adv_pci1723.c revision 8258d3923c7d8f9a28c43612c3f91c7b9d61191b
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 "../comedidev.h"
52
53#define PCI_VENDOR_ID_ADVANTECH		0x13fe	/* Advantech PCI vendor ID */
54
55/* hardware types of the cards */
56#define TYPE_PCI1723 0
57
58#define IORANGE_1723  0x2A
59
60/* all the registers for the pci1723 board */
61#define PCI1723_DA(N)   ((N)<<1)	/* W: D/A register N (0 to 7) */
62
63#define PCI1723_SYN_SET  0x12		/* synchronized set register */
64#define PCI1723_ALL_CHNNELE_SYN_STROBE  0x12
65					/* synchronized status register */
66
67#define PCI1723_RANGE_CALIBRATION_MODE 0x14
68					/* range and calibration mode */
69#define PCI1723_RANGE_CALIBRATION_STATUS 0x14
70					/* range and calibration status */
71
72#define PCI1723_CONTROL_CMD_CALIBRATION_FUN 0x16
73						/*
74						 * SADC control command for
75						 * calibration function
76						 */
77#define PCI1723_STATUS_CMD_CALIBRATION_FUN 0x16
78						/*
79						 * SADC control status for
80						 * calibration function
81						 */
82
83#define PCI1723_CALIBRATION_PARA_STROBE 0x18
84					/* Calibration parameter strobe */
85
86#define PCI1723_DIGITAL_IO_PORT_SET 0x1A	/* Digital I/O port setting */
87#define PCI1723_DIGITAL_IO_PORT_MODE 0x1A	/* Digital I/O port mode */
88
89#define PCI1723_WRITE_DIGITAL_OUTPUT_CMD 0x1C
90					/* Write digital output command */
91#define PCI1723_READ_DIGITAL_INPUT_DATA 0x1C	/* Read digital input data */
92
93#define PCI1723_WRITE_CAL_CMD 0x1E		/* Write calibration command */
94#define PCI1723_READ_CAL_STATUS 0x1E		/* Read calibration status */
95
96#define PCI1723_SYN_STROBE 0x20			/* Synchronized strobe */
97
98#define PCI1723_RESET_ALL_CHN_STROBE 0x22
99					/* Reset all D/A channels strobe */
100
101#define PCI1723_RESET_CAL_CONTROL_STROBE 0x24
102						/*
103						 * Reset the calibration
104						 * controller strobe
105						 */
106
107#define PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE 0x26
108						/*
109						 * Change D/A channels output
110						 * type strobe
111						 */
112
113#define PCI1723_SELECT_CALIBRATION 0x28	/* Select the calibration Ref_V */
114
115/* static unsigned short pci_list_builded=0;      =1 list of card is know */
116
117static const struct comedi_lrange range_pci1723 = { 1, {
118							BIP_RANGE(10)
119							}
120};
121
122/*
123 * Board descriptions for pci1723 boards.
124 */
125struct pci1723_board {
126	const char *name;
127	int vendor_id;		/* PCI vendor a device ID of card */
128	int device_id;
129	int iorange;
130	char cardtype;
131	int n_aochan;		/* num of D/A chans */
132	int n_diochan;		/* num of DIO chans */
133	int ao_maxdata;		/* resolution of D/A */
134	const struct comedi_lrange *rangelist_ao;	/* rangelist for D/A */
135};
136
137static const struct pci1723_board boardtypes[] = {
138	{
139	 .name = "pci1723",
140	 .vendor_id = PCI_VENDOR_ID_ADVANTECH,
141	 .device_id = 0x1723,
142	 .iorange = IORANGE_1723,
143	 .cardtype = TYPE_PCI1723,
144	 .n_aochan = 8,
145	 .n_diochan = 16,
146	 .ao_maxdata = 0xffff,
147	 .rangelist_ao = &range_pci1723,
148	 },
149};
150
151/* This structure is for data unique to this hardware driver. */
152struct pci1723_private {
153	int valid;		/* card is usable; */
154
155	unsigned char da_range[8];	/* D/A output range for each channel */
156
157	short ao_data[8];	/* data output buffer */
158};
159
160/* The following macro to make it easy to access the private structure. */
161#define devpriv ((struct pci1723_private *)dev->private)
162
163#define this_board boardtypes
164
165/*
166 * The pci1723 card reset;
167 */
168static int pci1723_reset(struct comedi_device *dev)
169{
170	int i;
171	DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n");
172
173	outw(0x01, dev->iobase + PCI1723_SYN_SET);
174					       /* set synchronous output mode */
175
176	for (i = 0; i < 8; i++) {
177		/* set all outputs to 0V */
178		devpriv->ao_data[i] = 0x8000;
179		outw(devpriv->ao_data[i], dev->iobase + PCI1723_DA(i));
180		/* set all ranges to +/- 10V */
181		devpriv->da_range[i] = 0;
182		outw(((devpriv->da_range[i] << 4) | i),
183		     PCI1723_RANGE_CALIBRATION_MODE);
184	}
185
186	outw(0, dev->iobase + PCI1723_CHANGE_CHA_OUTPUT_TYPE_STROBE);
187							    /* update ranges */
188	outw(0, dev->iobase + PCI1723_SYN_STROBE);	    /* update outputs */
189
190	/* set asynchronous output mode */
191	outw(0, dev->iobase + PCI1723_SYN_SET);
192
193	DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n");
194	return 0;
195}
196
197static int pci1723_insn_read_ao(struct comedi_device *dev,
198				struct comedi_subdevice *s,
199				struct comedi_insn *insn, unsigned int *data)
200{
201	int n, chan;
202
203	chan = CR_CHAN(insn->chanspec);
204	DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() -----\n");
205	for (n = 0; n < insn->n; n++)
206		data[n] = devpriv->ao_data[chan];
207
208	return n;
209}
210
211/*
212  analog data output;
213*/
214static int pci1723_ao_write_winsn(struct comedi_device *dev,
215				  struct comedi_subdevice *s,
216				  struct comedi_insn *insn, unsigned int *data)
217{
218	int n, chan;
219	chan = CR_CHAN(insn->chanspec);
220
221	DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n");
222
223	for (n = 0; n < insn->n; n++) {
224
225		devpriv->ao_data[chan] = data[n];
226		outw(data[n], dev->iobase + PCI1723_DA(chan));
227	}
228
229	return n;
230}
231
232/*
233  digital i/o config/query
234*/
235static int pci1723_dio_insn_config(struct comedi_device *dev,
236				   struct comedi_subdevice *s,
237				   struct comedi_insn *insn, unsigned int *data)
238{
239	unsigned int mask;
240	unsigned int bits;
241	unsigned short dio_mode;
242
243	mask = 1 << CR_CHAN(insn->chanspec);
244	if (mask & 0x00FF)
245		bits = 0x00FF;
246	else
247		bits = 0xFF00;
248
249	switch (data[0]) {
250	case INSN_CONFIG_DIO_INPUT:
251		s->io_bits &= ~bits;
252		break;
253	case INSN_CONFIG_DIO_OUTPUT:
254		s->io_bits |= bits;
255		break;
256	case INSN_CONFIG_DIO_QUERY:
257		data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT;
258		return insn->n;
259	default:
260		return -EINVAL;
261	}
262
263	/* update hardware DIO mode */
264	dio_mode = 0x0000;	/* low byte output, high byte output */
265	if ((s->io_bits & 0x00FF) == 0)
266		dio_mode |= 0x0001;	/* low byte input */
267	if ((s->io_bits & 0xFF00) == 0)
268		dio_mode |= 0x0002;	/* high byte input */
269	outw(dio_mode, dev->iobase + PCI1723_DIGITAL_IO_PORT_SET);
270	return 1;
271}
272
273/*
274  digital i/o bits read/write
275*/
276static int pci1723_dio_insn_bits(struct comedi_device *dev,
277				 struct comedi_subdevice *s,
278				 struct comedi_insn *insn, unsigned int *data)
279{
280	if (data[0]) {
281		s->state &= ~data[0];
282		s->state |= (data[0] & data[1]);
283		outw(s->state, dev->iobase + PCI1723_WRITE_DIGITAL_OUTPUT_CMD);
284	}
285	data[1] = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
286	return insn->n;
287}
288
289static struct pci_dev *pci1723_find_pci_dev(struct comedi_device *dev,
290					    struct comedi_devconfig *it)
291{
292	struct pci_dev *pcidev = NULL;
293	int bus = it->options[0];
294	int slot = it->options[1];
295
296	for_each_pci_dev(pcidev) {
297		if (bus || slot) {
298			if (bus != pcidev->bus->number ||
299			    slot != PCI_SLOT(pcidev->devfn))
300				continue;
301		}
302		if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
303			continue;
304		return pcidev;
305	}
306	dev_err(dev->class_dev,
307		"No supported board found! (req. bus %d, slot %d)\n",
308		bus, slot);
309	return NULL;
310}
311
312static int pci1723_attach(struct comedi_device *dev,
313			  struct comedi_devconfig *it)
314{
315	struct pci_dev *pcidev;
316	struct comedi_subdevice *s;
317	int ret, subdev, n_subdevices;
318
319	printk(KERN_ERR "comedi%d: adv_pci1723: board=%s",
320						dev->minor, this_board->name);
321
322	ret = alloc_private(dev, sizeof(struct pci1723_private));
323	if (ret < 0) {
324		printk(" - Allocation failed!\n");
325		return -ENOMEM;
326	}
327
328	pcidev = pci1723_find_pci_dev(dev, it);
329	if (!pcidev)
330		return -EIO;
331	comedi_set_hw_dev(dev, &pcidev->dev);
332
333	ret = comedi_pci_enable(pcidev, "adv_pci1723");
334	if (ret)
335		return ret;
336
337	dev->iobase = pci_resource_start(pcidev, 2);
338
339	dev->board_name = this_board->name;
340
341	n_subdevices = 0;
342
343	if (this_board->n_aochan)
344		n_subdevices++;
345	if (this_board->n_diochan)
346		n_subdevices++;
347
348	ret = comedi_alloc_subdevices(dev, n_subdevices);
349	if (ret)
350		return ret;
351
352	pci1723_reset(dev);
353	subdev = 0;
354	if (this_board->n_aochan) {
355		s = dev->subdevices + subdev;
356		dev->write_subdev = s;
357		s->type = COMEDI_SUBD_AO;
358		s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
359		s->n_chan = this_board->n_aochan;
360		s->maxdata = this_board->ao_maxdata;
361		s->len_chanlist = this_board->n_aochan;
362		s->range_table = this_board->rangelist_ao;
363
364		s->insn_write = pci1723_ao_write_winsn;
365		s->insn_read = pci1723_insn_read_ao;
366
367		/* read DIO config */
368		switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE)
369								       & 0x03) {
370		case 0x00:	/* low byte output, high byte output */
371			s->io_bits = 0xFFFF;
372			break;
373		case 0x01:	/* low byte input, high byte output */
374			s->io_bits = 0xFF00;
375			break;
376		case 0x02:	/* low byte output, high byte input */
377			s->io_bits = 0x00FF;
378			break;
379		case 0x03:	/* low byte input, high byte input */
380			s->io_bits = 0x0000;
381			break;
382		}
383		/* read DIO port state */
384		s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
385
386		subdev++;
387	}
388
389	if (this_board->n_diochan) {
390		s = dev->subdevices + subdev;
391		s->type = COMEDI_SUBD_DIO;
392		s->subdev_flags =
393		    SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
394		s->n_chan = this_board->n_diochan;
395		s->maxdata = 1;
396		s->len_chanlist = this_board->n_diochan;
397		s->range_table = &range_digital;
398		s->insn_config = pci1723_dio_insn_config;
399		s->insn_bits = pci1723_dio_insn_bits;
400		subdev++;
401	}
402
403	devpriv->valid = 1;
404
405	pci1723_reset(dev);
406
407	return 0;
408}
409
410static void pci1723_detach(struct comedi_device *dev)
411{
412	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
413
414	if (dev->private) {
415		if (devpriv->valid)
416			pci1723_reset(dev);
417	}
418	if (pcidev) {
419		if (dev->iobase)
420			comedi_pci_disable(pcidev);
421		pci_dev_put(pcidev);
422	}
423}
424
425static struct comedi_driver adv_pci1723_driver = {
426	.driver_name	= "adv_pci1723",
427	.module		= THIS_MODULE,
428	.attach		= pci1723_attach,
429	.detach		= pci1723_detach,
430};
431
432static int __devinit adv_pci1723_pci_probe(struct pci_dev *dev,
433					   const struct pci_device_id *ent)
434{
435	return comedi_pci_auto_config(dev, &adv_pci1723_driver);
436}
437
438static void __devexit adv_pci1723_pci_remove(struct pci_dev *dev)
439{
440	comedi_pci_auto_unconfig(dev);
441}
442
443static DEFINE_PCI_DEVICE_TABLE(adv_pci1723_pci_table) = {
444	{ PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1723) },
445	{ 0 }
446};
447MODULE_DEVICE_TABLE(pci, adv_pci1723_pci_table);
448
449static struct pci_driver adv_pci1723_pci_driver = {
450	.name		= "adv_pci1723",
451	.id_table	= adv_pci1723_pci_table,
452	.probe		= adv_pci1723_pci_probe,
453	.remove		= __devexit_p(adv_pci1723_pci_remove),
454};
455module_comedi_pci_driver(adv_pci1723_driver, adv_pci1723_pci_driver);
456
457MODULE_AUTHOR("Comedi http://www.comedi.org");
458MODULE_DESCRIPTION("Comedi low-level driver");
459MODULE_LICENSE("GPL");
460