adv_pci1723.c revision 7f072f54ae5dc9965cbe450419b1389d13e2b849
1/*******************************************************************************
2   comedi/drivers/pci1723.c
3
4   COMEDI - Linux Control and Measurement Device Interface
5   Copyright (C) 2000 David A. Schleef <ds@schleef.org>
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21*******************************************************************************/
22/*
23Driver: adv_pci1723
24Description: Advantech PCI-1723
25Author: yonggang <rsmgnu@gmail.com>, Ian Abbott <abbotti@mev.co.uk>
26Devices: [Advantech] PCI-1723 (adv_pci1723)
27Updated: Mon, 14 Apr 2008 15:12:56 +0100
28Status: works
29
30Configuration Options:
31  [0] - PCI bus of device (optional)
32  [1] - PCI slot of device (optional)
33
34  If bus/slot is not specified, the first supported
35  PCI device found will be used.
36
37Subdevice 0 is 8-channel AO, 16-bit, range +/- 10 V.
38
39Subdevice 1 is 16-channel DIO.  The channels are configurable as input or
40output in 2 groups (0 to 7, 8 to 15).  Configuring any channel implicitly
41configures all channels in the same group.
42
43TODO:
44
451. Add the two milliamp ranges to the AO subdevice (0 to 20 mA, 4 to 20 mA).
462. Read the initial ranges and values of the AO subdevice at start-up instead
47   of reinitializing them.
483. Implement calibration.
49*/
50
51#include <linux/pci.h>
52
53#include "../comedidev.h"
54
55/* all the registers for the pci1723 board */
56#define PCI1723_DA(N)   ((N)<<1)	/* W: D/A register N (0 to 7) */
57
58#define PCI1723_SYN_SET  0x12		/* synchronized set register */
59#define PCI1723_ALL_CHNNELE_SYN_STROBE  0x12
60					/* synchronized status register */
61
62#define PCI1723_RANGE_CALIBRATION_MODE 0x14
63					/* range and calibration mode */
64#define PCI1723_RANGE_CALIBRATION_STATUS 0x14
65					/* range and calibration status */
66
67#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16
68						/*
69						 * SADC control command for
70						 * calibration function
71						 */
72#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16
73						/*
74						 * SADC control status for
75						 * calibration function
76						 */
77
78#define PCI1723_CALIBRATION_PARA_STROBE 0x18
79					/* Calibration parameter strobe */
80
81#define PCI1723_DIGITAL_IO_PORT_SET 0x1A	/* Digital I/O port setting */
82#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A	/* Digital I/O port mode */
83
84#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C
85					/* Write digital output command */
86#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C	/* Read digital input data */
87
88#define PCI1723_WRITE_CAL_CMD 0x1E		/* Write calibration command */
89#define PCI1723_READ_CAL_STATUS 0x1E		/* Read calibration status */
90
91#define PCI1723_SYN_STROBE 0x20			/* Synchronized strobe */
92
93#define PCI1723_RESET_ALL_CHN_STROBE 0x22
94					/* Reset all D/A channels strobe */
95
96#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24
97						/*
98						 * Reset the calibration
99						 * controller strobe
100						 */
101
102#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26
103						/*
104						 * Change D/A channels output
105						 * type strobe
106						 */
107
108#define PCI1723_SELECT_CALIBRATION 0x28	/* Select the calibration Ref_V */
109
110struct pci1723_private {
111	unsigned char da_range[8];	/* D/A output range for each channel */
112	short ao_data[8];	/* data output buffer */
113};
114
115/*
116 * The pci1723 card reset;
117 */
118static int pci1723_reset(struct comedi_device *dev)
119{
120	struct pci1723_private *devpriv = dev->private;
121	int i;
122
123	outw(0x01, dev->iobase + PCI1723_SYN_SET);
124					       /* set synchronous output mode */
125
126	for (i = 0; i < 8; i++) {
127		/* set all outputs to 0V */
128		devpriv->ao_data[i] = 0x8000;
129		outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i));
130		/* set all ranges to +/- 10V */
131		devpriv->da_range[i] = 0;
132		outw(((devpriv->da_range[i] << 4) | i),
133		     PCI1723_RANGE_CALIBRATION_MODE);
134	}
135
136	outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE);
137							    /* update ranges */
138	outw(0, dev->iobase + PCI1723_SYN_STROBE);	    /* update outputs */
139
140	/* set asynchronous output mode */
141	outw(0, dev->iobase + PCI1723_SYN_SET);
142
143	return 0;
144}
145
146static int pci1723_insn_read_ao(struct comedi_device *dev,
147				struct comedi_subdevice *s,
148				struct comedi_insn *insn, unsigned int *data)
149{
150	struct pci1723_private *devpriv = dev->private;
151	int n, chan;
152
153	chan = CR_CHAN(insn->chanspec);
154	for (n = 0; n < insn->n; n++)
155		data[n] = devpriv->ao_data[chan];
156
157	return n;
158}
159
160/*
161  analog data output;
162*/
163static int pci1723_ao_write_winsn(struct comedi_device *dev,
164				  struct comedi_subdevice *s,
165				  struct comedi_insn *insn, unsigned int *data)
166{
167	struct pci1723_private *devpriv = dev->private;
168	int n, chan;
169	chan = CR_CHAN(insn->chanspec);
170
171	for (n = 0; n < insn->n; n++) {
172
173		devpriv->ao_data[chan] = data[n];
174		outw(data[n], dev->iobase + PCI1723_DA(chan));
175	}
176
177	return n;
178}
179
180/*
181  digital i/o config/query
182*/
183static int pci1723_dio_insn_config(struct comedi_device *dev,
184				   struct comedi_subdevice *s,
185				   struct comedi_insn *insn, unsigned int *data)
186{
187	unsigned int mask;
188	unsigned int bits;
189	unsigned short dio_mode;
190
191	mask = 1 << CR_CHAN(insn->chanspec);
192	if (mask & 0x00FF)
193		bits = 0x00FF;
194	else
195		bits = 0xFF00;
196
197	switch (data[0]) {
198	case INSN_CONFIG_DIO_INPUT:
199		s->io_bits &= ~bits;
200		break;
201	case INSN_CONFIG_DIO_OUTPUT:
202		s->io_bits |= bits;
203		break;
204	case INSN_CONFIG_DIO_QUERY:
205		data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
206		return insn->n;
207	default:
208		return -EINVAL;
209	}
210
211	/* update hardware DIO mode */
212	dio_mode = 0x0000;	/* low byte output, high byte output */
213	if ((s->io_bits & 0x00FF) == 0)
214		dio_mode |= 0x0001;	/* low byte input */
215	if ((s->io_bits & 0xFF00) == 0)
216		dio_mode |= 0x0002;	/* high byte input */
217	outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET);
218	return 1;
219}
220
221/*
222  digital i/o bits read/write
223*/
224static int pci1723_dio_insn_bits(struct comedi_device *dev,
225				 struct comedi_subdevice *s,
226				 struct comedi_insn *insn, unsigned int *data)
227{
228	if (data[0]) {
229		s->state &= ~data[0];
230		s->state |= (data[0] & data[1]);
231		outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD);
232	}
233	data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
234	return insn->n;
235}
236
237static int pci1723_auto_attach(struct comedi_device *dev,
238					 unsigned long context_unused)
239{
240	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
241	struct pci1723_private *devpriv;
242	struct comedi_subdevice *s;
243	int ret;
244
245	dev->board_name = dev->driver->driver_name;
246
247	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
248	if (!devpriv)
249		return -ENOMEM;
250	dev->private = devpriv;
251
252	ret = comedi_pci_enable(pcidev, dev->board_name);
253	if (ret)
254		return ret;
255	dev->iobase = pci_resource_start(pcidev, 2);
256
257	ret = comedi_alloc_subdevices(dev, 2);
258	if (ret)
259		return ret;
260
261	s = &dev->subdevices[0];
262	dev->write_subdev = s;
263	s->type		= COMEDI_SUBD_AO;
264	s->subdev_flags	= SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
265	s->n_chan	= 8;
266	s->maxdata	= 0xffff;
267	s->len_chanlist	= 8;
268	s->range_table	= &range_bipolar10;
269	s->insn_write	= pci1723_ao_write_winsn;
270	s->insn_read	= pci1723_insn_read_ao;
271
272	s = &dev->subdevices[1];
273	s->type		= COMEDI_SUBD_DIO;
274	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
275	s->n_chan	= 16;
276	s->maxdata	= 1;
277	s->len_chanlist	= 16;
278	s->range_table	= &range_digital;
279	s->insn_config	= pci1723_dio_insn_config;
280	s->insn_bits	= pci1723_dio_insn_bits;
281
282	/* read DIO config */
283	switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) & 0x03) {
284	case 0x00:	/* low byte output, high byte output */
285		s->io_bits = 0xFFFF;
286		break;
287	case 0x01:	/* low byte input, high byte output */
288		s->io_bits = 0xFF00;
289		break;
290	case 0x02:	/* low byte output, high byte input */
291		s->io_bits = 0x00FF;
292		break;
293	case 0x03:	/* low byte input, high byte input */
294		s->io_bits = 0x0000;
295		break;
296	}
297	/* read DIO port state */
298	s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
299
300	pci1723_reset(dev);
301
302	dev_info(dev->class_dev, "%s attached\n", dev->board_name);
303
304	return 0;
305}
306
307static void pci1723_detach(struct comedi_device *dev)
308{
309	if (dev->iobase)
310		pci1723_reset(dev);
311	comedi_pci_disable(dev);
312}
313
314static struct comedi_driver adv_pci1723_driver = {
315	.driver_name	= "adv_pci1723",
316	.module		= THIS_MODULE,
317	.auto_attach	= pci1723_auto_attach,
318	.detach		= pci1723_detach,
319};
320
321static int adv_pci1723_pci_probe(struct pci_dev *dev,
322				 const struct pci_device_id *id)
323{
324	return comedi_pci_auto_config(dev, &adv_pci1723_driver,
325				      id->driver_data);
326}
327
328static DEFINE_PCI_DEVICE_TABLE(adv_pci1723_pci_table) = {
329	{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) },
330	{ 0 }
331};
332MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table);
333
334static struct pci_driver adv_pci1723_pci_driver = {
335	.name		= "adv_pci1723",
336	.id_table	= adv_pci1723_pci_table,
337	.probe		= adv_pci1723_pci_probe,
338	.remove		= comedi_pci_auto_unconfig,
339};
340module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver);
341
342MODULE_AUTHOR("Comedi http://www.comedi.org");
343MODULE_DESCRIPTION("Comedi low-level driver");
344MODULE_LICENSE("GPL");
345