dyna_pci10xx.c revision 0e4039f3112326d73f66b00fd18468a3804ed29e
1/*
2 * comedi/drivers/dyna_pci10xx.c
3 * Copyright (C) 2011 Prashant Shah, pshah.mumbai@gmail.com
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20/*
21 Driver: dyna_pci10xx
22 Devices: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/
23 Author: Prashant Shah <pshah.mumbai@gmail.com>
24 Developed at Automation Labs, Chemical Dept., IIT Bombay, India.
25 Prof. Kannan Moudgalya <kannan@iitb.ac.in>
26 http://www.iitb.ac.in
27 Status: Stable
28 Version: 1.0
29 Device Supported :
30 - Dynalog PCI 1050
31
32 Notes :
33 - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and
34 they are using the PLX Technlogies Vendor ID since that is the PCI Chip used
35 in the card.
36 - Dynalog India Pvt. Ltd. has provided the internal register specification for
37 their cards in their manuals.
38*/
39
40#include "../comedidev.h"
41#include <linux/mutex.h>
42
43#define PCI_VENDOR_ID_DYNALOG		0x10b5
44#define DRV_NAME			"dyna_pci10xx"
45
46#define READ_TIMEOUT 50
47
48static DEFINE_MUTEX(start_stop_sem);
49
50static const struct comedi_lrange range_pci1050_ai = { 3, {
51							  BIP_RANGE(10),
52							  BIP_RANGE(5),
53							  UNI_RANGE(10)
54							  }
55};
56
57static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
58
59static const struct comedi_lrange range_pci1050_ao = { 1, {
60							  UNI_RANGE(10)
61							  }
62};
63
64static const char range_codes_pci1050_ao[] = { 0x00 };
65
66struct boardtype {
67	const char *name;
68	int device_id;
69	int ai_chans;
70	int ai_bits;
71	int ao_chans;
72	int ao_bits;
73	int di_chans;
74	int di_bits;
75	int do_chans;
76	int do_bits;
77	const struct comedi_lrange *range_ai;
78	const char *range_codes_ai;
79	const struct comedi_lrange *range_ao;
80	const char *range_codes_ao;
81};
82
83static const struct boardtype boardtypes[] = {
84	{
85	.name = "dyna_pci1050",
86	.device_id = 0x1050,
87	.ai_chans = 16,
88	.ai_bits = 12,
89	.ao_chans = 16,
90	.ao_bits = 12,
91	.di_chans = 16,
92	.di_bits = 16,
93	.do_chans = 16,
94	.do_bits = 16,
95	.range_ai = &range_pci1050_ai,
96	.range_codes_ai = range_codes_pci1050_ai,
97	.range_ao = &range_pci1050_ao,
98	.range_codes_ao = range_codes_pci1050_ao,
99	},
100	/*  dummy entry corresponding to driver name */
101	{.name = DRV_NAME},
102};
103
104struct dyna_pci10xx_private {
105	struct pci_dev *pci_dev;	/*  ptr to PCI device */
106	char valid;			/*  card is usable */
107	struct mutex mutex;
108
109	/* device base address registers */
110	unsigned long BADR0, BADR1, BADR2, BADR3, BADR4, BADR5;
111};
112
113#define thisboard ((const struct boardtype *)dev->board_ptr)
114#define devpriv ((struct dyna_pci10xx_private *)dev->private)
115
116/******************************************************************************/
117/************************** READ WRITE FUNCTIONS ******************************/
118/******************************************************************************/
119
120/* analog input callback */
121static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
122			struct comedi_subdevice *s,
123			struct comedi_insn *insn, unsigned int *data)
124{
125	int n, counter;
126	u16 d = 0;
127	unsigned int chan, range;
128
129	/* get the channel number and range */
130	chan = CR_CHAN(insn->chanspec);
131	range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
132
133	mutex_lock(&devpriv->mutex);
134	/* convert n samples */
135	for (n = 0; n < insn->n; n++) {
136		/* trigger conversion */
137		smp_mb();
138		outw_p(0x0000 + range + chan, devpriv->BADR2 + 2);
139		udelay(10);
140		/* read data */
141		for (counter = 0; counter < READ_TIMEOUT; counter++) {
142			d = inw_p(devpriv->BADR2);
143
144			/* check if read is successful if the EOC bit is set */
145			if (d & (1 << 15))
146				goto conv_finish;
147		}
148		data[n] = 0;
149		printk(KERN_DEBUG "comedi: dyna_pci10xx: "
150			"timeout reading analog input\n");
151		continue;
152conv_finish:
153		/* mask the first 4 bits - EOC bits */
154		d &= 0x0FFF;
155		data[n] = d;
156	}
157	mutex_unlock(&devpriv->mutex);
158
159	/* return the number of samples read/written */
160	return n;
161}
162
163/* analog output callback */
164static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
165				 struct comedi_subdevice *s,
166				 struct comedi_insn *insn, unsigned int *data)
167{
168	int n;
169	unsigned int chan, range;
170
171	chan = CR_CHAN(insn->chanspec);
172	range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
173
174	mutex_lock(&devpriv->mutex);
175	for (n = 0; n < insn->n; n++) {
176		smp_mb();
177		/* trigger conversion and write data */
178		outw_p(data[n], devpriv->BADR2);
179		udelay(10);
180	}
181	mutex_unlock(&devpriv->mutex);
182	return n;
183}
184
185/* digital input bit interface */
186static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
187			      struct comedi_subdevice *s,
188			      struct comedi_insn *insn, unsigned int *data)
189{
190	u16 d = 0;
191
192	if (insn->n != 2)
193		return -EINVAL;
194
195	mutex_lock(&devpriv->mutex);
196	smp_mb();
197	d = inw_p(devpriv->BADR3);
198	udelay(10);
199
200	/* on return the data[0] contains output and data[1] contains input */
201	data[1] = d;
202	data[0] = s->state;
203	mutex_unlock(&devpriv->mutex);
204	return 2;
205}
206
207/* digital output bit interface */
208static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
209			      struct comedi_subdevice *s,
210			      struct comedi_insn *insn, unsigned int *data)
211{
212	if (insn->n != 2)
213		return -EINVAL;
214
215	/* The insn data is a mask in data[0] and the new data
216	 * in data[1], each channel cooresponding to a bit.
217	 * s->state contains the previous write data
218	 */
219	mutex_lock(&devpriv->mutex);
220	if (data[0]) {
221		s->state &= ~data[0];
222		s->state |= (data[0] & data[1]);
223		smp_mb();
224		outw_p(s->state, devpriv->BADR3);
225		udelay(10);
226	}
227
228	/*
229	 * On return, data[1] contains the value of the digital
230	 * input and output lines. We just return the software copy of the
231	 * output values if it was a purely digital output subdevice.
232	 */
233	data[1] = s->state;
234	mutex_unlock(&devpriv->mutex);
235	return 2;
236}
237
238/******************************************************************************/
239/*********************** INITIALIZATION FUNCTIONS *****************************/
240/******************************************************************************/
241
242static int dyna_pci10xx_attach(struct comedi_device *dev,
243			  struct comedi_devconfig *it)
244{
245	struct comedi_subdevice *s;
246	struct pci_dev *pcidev;
247	unsigned int opt_bus, opt_slot;
248	int board_index, i;
249
250	mutex_lock(&start_stop_sem);
251
252	if (alloc_private(dev, sizeof(struct dyna_pci10xx_private)) < 0) {
253		printk(KERN_ERR "comedi: dyna_pci10xx: "
254			"failed to allocate memory!\n");
255		mutex_unlock(&start_stop_sem);
256		return -ENOMEM;
257	}
258
259	opt_bus = it->options[0];
260	opt_slot = it->options[1];
261	dev->board_name = thisboard->name;
262	dev->irq = 0;
263
264	/*
265	 * Probe the PCI bus and located the matching device
266	 */
267	for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
268		pcidev != NULL;
269		pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
270
271		board_index = -1;
272		for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
273			if ((pcidev->vendor == PCI_VENDOR_ID_DYNALOG) &&
274				(pcidev->device == boardtypes[i].device_id)) {
275					board_index = i;
276					break;
277				}
278		}
279		if (board_index < 0)
280			continue;
281
282		/* Found matching vendor/device. */
283		if (opt_bus || opt_slot) {
284			/* Check bus/slot. */
285			if (opt_bus != pcidev->bus->number
286			    || opt_slot != PCI_SLOT(pcidev->devfn))
287				continue;	/* no match */
288		}
289
290		goto found;
291	}
292	printk(KERN_ERR "comedi: dyna_pci10xx: no supported device found!\n");
293	mutex_unlock(&start_stop_sem);
294	return -EIO;
295
296found:
297
298	if (!pcidev) {
299		if (opt_bus || opt_slot) {
300			printk(KERN_ERR "comedi: dyna_pci10xx: "
301				"invalid PCI device at b:s %d:%d\n",
302				opt_bus, opt_slot);
303		} else {
304			printk(KERN_ERR "comedi: dyna_pci10xx: "
305				"invalid PCI device\n");
306		}
307		mutex_unlock(&start_stop_sem);
308		return -EIO;
309	}
310
311	if (comedi_pci_enable(pcidev, DRV_NAME)) {
312		printk(KERN_ERR "comedi: dyna_pci10xx: "
313			"failed to enable PCI device and request regions!");
314		mutex_unlock(&start_stop_sem);
315		return -EIO;
316	}
317
318	mutex_init(&devpriv->mutex);
319	dev->board_ptr = &boardtypes[board_index];
320	devpriv->pci_dev = pcidev;
321
322	printk(KERN_INFO "comedi: dyna_pci10xx: device found!\n");
323
324	/* initialize device base address registers */
325	devpriv->BADR0 = pci_resource_start(pcidev, 0);
326	devpriv->BADR1 = pci_resource_start(pcidev, 1);
327	devpriv->BADR2 = pci_resource_start(pcidev, 2);
328	devpriv->BADR3 = pci_resource_start(pcidev, 3);
329	devpriv->BADR4 = pci_resource_start(pcidev, 4);
330	devpriv->BADR5 = pci_resource_start(pcidev, 5);
331
332	if (comedi_alloc_subdevices(dev, 4) < 0) {
333		mutex_unlock(&start_stop_sem);
334		return -ENOMEM;
335	}
336
337	/* analog input */
338	s = dev->subdevices + 0;
339	s->type = COMEDI_SUBD_AI;
340	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
341	s->n_chan = thisboard->ai_chans;
342	s->maxdata = 0x0FFF;
343	s->range_table = thisboard->range_ai;
344	s->len_chanlist = 16;
345	s->insn_read = dyna_pci10xx_insn_read_ai;
346
347	/* analog output */
348	s = dev->subdevices + 1;
349	s->type = COMEDI_SUBD_AO;
350	s->subdev_flags = SDF_WRITABLE;
351	s->n_chan = thisboard->ao_chans;
352	s->maxdata = 0x0FFF;
353	s->range_table = thisboard->range_ao;
354	s->len_chanlist = 16;
355	s->insn_write = dyna_pci10xx_insn_write_ao;
356
357	/* digital input */
358	s = dev->subdevices + 2;
359	s->type = COMEDI_SUBD_DI;
360	s->subdev_flags = SDF_READABLE | SDF_GROUND;
361	s->n_chan = thisboard->di_chans;
362	s->maxdata = 1;
363	s->range_table = &range_digital;
364	s->len_chanlist = thisboard->di_chans;
365	s->insn_bits = dyna_pci10xx_di_insn_bits;
366
367	/* digital output */
368	s = dev->subdevices + 3;
369	s->type = COMEDI_SUBD_DO;
370	s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
371	s->n_chan = thisboard->do_chans;
372	s->maxdata = 1;
373	s->range_table = &range_digital;
374	s->len_chanlist = thisboard->do_chans;
375	s->state = 0;
376	s->insn_bits = dyna_pci10xx_do_insn_bits;
377
378	devpriv->valid = 1;
379	mutex_unlock(&start_stop_sem);
380
381	printk(KERN_INFO "comedi: dyna_pci10xx: %s - device setup completed!\n",
382		boardtypes[board_index].name);
383
384	return 1;
385}
386
387static void dyna_pci10xx_detach(struct comedi_device *dev)
388{
389	if (devpriv && devpriv->pci_dev) {
390		comedi_pci_disable(devpriv->pci_dev);
391		mutex_destroy(&devpriv->mutex);
392	}
393}
394
395static struct comedi_driver dyna_pci10xx_driver = {
396	.driver_name	= "dyna_pci10xx",
397	.module		= THIS_MODULE,
398	.attach		= dyna_pci10xx_attach,
399	.detach		= dyna_pci10xx_detach,
400	.board_name	= &boardtypes[0].name,
401	.offset		= sizeof(struct boardtype),
402	.num_names	= ARRAY_SIZE(boardtypes),
403};
404
405static int __devinit dyna_pci10xx_pci_probe(struct pci_dev *dev,
406					    const struct pci_device_id *ent)
407{
408	return comedi_pci_auto_config(dev, &dyna_pci10xx_driver);
409}
410
411static void __devexit dyna_pci10xx_pci_remove(struct pci_dev *dev)
412{
413	comedi_pci_auto_unconfig(dev);
414}
415
416static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = {
417	{ PCI_DEVICE(PCI_VENDOR_ID_DYNALOG, 0x1050) },
418	{ 0 }
419};
420MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
421
422static struct pci_driver dyna_pci10xx_pci_driver = {
423	.name		= "dyna_pci10xx",
424	.id_table	= dyna_pci10xx_pci_table,
425	.probe		= dyna_pci10xx_pci_probe,
426	.remove		= __devexit_p(dyna_pci10xx_pci_remove),
427};
428module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver);
429
430MODULE_LICENSE("GPL");
431MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
432MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");
433