dyna_pci10xx.c revision de9f2db41e6037fc4b9944edc62025327e4b9589
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 "comedi_pci.h"
42#include <linux/mutex.h>
43
44#define PCI_VENDOR_ID_DYNALOG		0x10b5
45#define DRV_NAME			"dyna_pci10xx"
46
47#define READ_TIMEOUT 50
48
49static DEFINE_MUTEX(start_stop_sem);
50
51static const struct comedi_lrange range_pci1050_ai = { 3, {
52							  BIP_RANGE(10),
53							  BIP_RANGE(5),
54							  UNI_RANGE(10)
55							  }
56};
57
58static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
59
60static const struct comedi_lrange range_pci1050_ao = { 1, {
61							  UNI_RANGE(10)
62							  }
63};
64
65static const char range_codes_pci1050_ao[] = { 0x00 };
66
67struct boardtype {
68	const char *name;
69	int device_id;
70	int ai_chans;
71	int ai_bits;
72	int ao_chans;
73	int ao_bits;
74	int di_chans;
75	int di_bits;
76	int do_chans;
77	int do_bits;
78	const struct comedi_lrange *range_ai;
79	const char *range_codes_ai;
80	const struct comedi_lrange *range_ao;
81	const char *range_codes_ao;
82};
83
84static const struct boardtype boardtypes[] = {
85	{
86	.name = "dyna_pci1050",
87	.device_id = 0x1050,
88	.ai_chans = 16,
89	.ai_bits = 12,
90	.ao_chans = 16,
91	.ao_bits = 12,
92	.di_chans = 16,
93	.di_bits = 16,
94	.do_chans = 16,
95	.do_bits = 16,
96	.range_ai = &range_pci1050_ai,
97	.range_codes_ai = range_codes_pci1050_ai,
98	.range_ao = &range_pci1050_ao,
99	.range_codes_ao = range_codes_pci1050_ao,
100	},
101	/*  dummy entry corresponding to driver name */
102	{.name = DRV_NAME},
103};
104
105struct dyna_pci10xx_private {
106	struct pci_dev *pci_dev;	/*  ptr to PCI device */
107	char valid;			/*  card is usable */
108	struct mutex mutex;
109
110	/* device base address registers */
111	unsigned long BADR0, BADR1, BADR2, BADR3, BADR4, BADR5;
112};
113
114#define thisboard ((const struct boardtype *)dev->board_ptr)
115#define devpriv ((struct dyna_pci10xx_private *)dev->private)
116
117/******************************************************************************/
118/************************** READ WRITE FUNCTIONS ******************************/
119/******************************************************************************/
120
121/* analog input callback */
122static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
123			struct comedi_subdevice *s,
124			struct comedi_insn *insn, unsigned int *data)
125{
126	int n, counter;
127	u16 d = 0;
128	unsigned int chan, range;
129
130	/* get the channel number and range */
131	chan = CR_CHAN(insn->chanspec);
132	range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
133
134	mutex_lock(&devpriv->mutex);
135	/* convert n samples */
136	for (n = 0; n < insn->n; n++) {
137		/* trigger conversion */
138		smp_mb();
139		outw_p(0x0000 + range + chan, devpriv->BADR2 + 2);
140		udelay(10);
141		/* read data */
142		for (counter = 0; counter < READ_TIMEOUT; counter++) {
143			d = inw_p(devpriv->BADR2);
144
145			/* check if read is successfull if the EOC bit is set */
146			if (d & (1 << 15))
147				goto conv_finish;
148		}
149		data[n] = 0;
150		printk(KERN_DEBUG "comedi: dyna_pci10xx: "
151			"timeout reading analog input\n");
152		continue;
153conv_finish:
154		/* mask the first 4 bits - EOC bits */
155		d &= 0x0FFF;
156		data[n] = d;
157	}
158	mutex_unlock(&devpriv->mutex);
159
160	/* return the number of samples read/written */
161	return n;
162}
163
164/* analog output callback */
165static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
166				 struct comedi_subdevice *s,
167				 struct comedi_insn *insn, unsigned int *data)
168{
169	int n;
170	unsigned int chan, range;
171
172	chan = CR_CHAN(insn->chanspec);
173	range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
174
175	mutex_lock(&devpriv->mutex);
176	for (n = 0; n < insn->n; n++) {
177		smp_mb();
178		/* trigger conversion and write data */
179		outw_p(data[n], devpriv->BADR2);
180		udelay(10);
181	}
182	mutex_unlock(&devpriv->mutex);
183	return n;
184}
185
186/* digital input bit interface */
187static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
188			      struct comedi_subdevice *s,
189			      struct comedi_insn *insn, unsigned int *data)
190{
191	u16 d = 0;
192
193	if (insn->n != 2)
194		return -EINVAL;
195
196	mutex_lock(&devpriv->mutex);
197	smp_mb();
198	d = inw_p(devpriv->BADR3);
199	udelay(10);
200
201	/* on return the data[0] contains output and data[1] contains input */
202	data[1] = d;
203	data[0] = s->state;
204	mutex_unlock(&devpriv->mutex);
205	return 2;
206}
207
208/* digital output bit interface */
209static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
210			      struct comedi_subdevice *s,
211			      struct comedi_insn *insn, unsigned int *data)
212{
213	if (insn->n != 2)
214		return -EINVAL;
215
216	/* The insn data is a mask in data[0] and the new data
217	 * in data[1], each channel cooresponding to a bit.
218	 * s->state contains the previous write data
219	 */
220	mutex_lock(&devpriv->mutex);
221	if (data[0]) {
222		s->state &= ~data[0];
223		s->state |= (data[0] & data[1]);
224		smp_mb();
225		outw_p(s->state, devpriv->BADR3);
226		udelay(10);
227	}
228
229	/*
230	 * On return, data[1] contains the value of the digital
231	 * input and output lines. We just return the software copy of the
232	 * output values if it was a purely digital output subdevice.
233	 */
234	data[1] = s->state;
235	mutex_unlock(&devpriv->mutex);
236	return 2;
237}
238
239/******************************************************************************/
240/*********************** INITIALIZATION FUNCTIONS *****************************/
241/******************************************************************************/
242
243static int dyna_pci10xx_attach(struct comedi_device *dev,
244			  struct comedi_devconfig *it)
245{
246	struct comedi_subdevice *s;
247	struct pci_dev *pcidev;
248	unsigned int opt_bus, opt_slot;
249	int board_index, i;
250
251	mutex_lock(&start_stop_sem);
252
253	if (alloc_private(dev, sizeof(struct dyna_pci10xx_private)) < 0) {
254		printk(KERN_ERR "comedi: dyna_pci10xx: "
255			"failed to allocate memory!\n");
256		mutex_unlock(&start_stop_sem);
257		return -ENOMEM;
258	}
259
260	opt_bus = it->options[0];
261	opt_slot = it->options[1];
262	dev->board_name = thisboard->name;
263	dev->irq = 0;
264
265	/*
266	 * Probe the PCI bus and located the matching device
267	 */
268	for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
269		pcidev != NULL;
270		pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
271
272		board_index = -1;
273		for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
274			if ((pcidev->vendor == PCI_VENDOR_ID_DYNALOG) &&
275				(pcidev->device == boardtypes[i].device_id)) {
276					board_index = i;
277					break;
278				}
279		}
280		if (board_index < 0)
281			continue;
282
283		/* Found matching vendor/device. */
284		if (opt_bus || opt_slot) {
285			/* Check bus/slot. */
286			if (opt_bus != pcidev->bus->number
287			    || opt_slot != PCI_SLOT(pcidev->devfn))
288				continue;	/* no match */
289		}
290
291		goto found;
292	}
293	printk(KERN_ERR "comedi: dyna_pci10xx: no supported device found!\n");
294	mutex_unlock(&start_stop_sem);
295	return -EIO;
296
297found:
298
299	if (!pcidev) {
300		if (opt_bus || opt_slot) {
301			printk(KERN_ERR "comedi: dyna_pci10xx: "
302				"invalid PCI device at b:s %d:%d\n",
303				opt_bus, opt_slot);
304		} else {
305			printk(KERN_ERR "comedi: dyna_pci10xx: "
306				"invalid PCI device\n");
307		}
308		mutex_unlock(&start_stop_sem);
309		return -EIO;
310	}
311
312	if (comedi_pci_enable(pcidev, DRV_NAME)) {
313		printk(KERN_ERR "comedi: dyna_pci10xx: "
314			"failed to enable PCI device and request regions!");
315		mutex_unlock(&start_stop_sem);
316		return -EIO;
317	}
318
319	mutex_init(&devpriv->mutex);
320	dev->board_ptr = &boardtypes[board_index];
321	devpriv->pci_dev = pcidev;
322
323	printk(KERN_INFO "comedi: dyna_pci10xx: device found!\n");
324
325	/* initialize device base address registers */
326	devpriv->BADR0 = pci_resource_start(pcidev, 0);
327	devpriv->BADR1 = pci_resource_start(pcidev, 1);
328	devpriv->BADR2 = pci_resource_start(pcidev, 2);
329	devpriv->BADR3 = pci_resource_start(pcidev, 3);
330	devpriv->BADR4 = pci_resource_start(pcidev, 4);
331	devpriv->BADR5 = pci_resource_start(pcidev, 5);
332
333	if (alloc_subdevices(dev, 4) < 0) {
334		printk(KERN_ERR "comedi: dyna_pci10xx: "
335			"failed allocating subdevices\n");
336		mutex_unlock(&start_stop_sem);
337		return -ENOMEM;
338	}
339
340	/* analog input */
341	s = dev->subdevices + 0;
342	s->type = COMEDI_SUBD_AI;
343	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
344	s->n_chan = thisboard->ai_chans;
345	s->maxdata = 0x0FFF;
346	s->range_table = thisboard->range_ai;
347	s->len_chanlist = 16;
348	s->insn_read = dyna_pci10xx_insn_read_ai;
349
350	/* analog output */
351	s = dev->subdevices + 1;
352	s->type = COMEDI_SUBD_AO;
353	s->subdev_flags = SDF_WRITABLE;
354	s->n_chan = thisboard->ao_chans;
355	s->maxdata = 0x0FFF;
356	s->range_table = thisboard->range_ao;
357	s->len_chanlist = 16;
358	s->insn_write = dyna_pci10xx_insn_write_ao;
359
360	/* digital input */
361	s = dev->subdevices + 2;
362	s->type = COMEDI_SUBD_DI;
363	s->subdev_flags = SDF_READABLE | SDF_GROUND;
364	s->n_chan = thisboard->di_chans;
365	s->maxdata = 1;
366	s->range_table = &range_digital;
367	s->len_chanlist = thisboard->di_chans;
368	s->insn_bits = dyna_pci10xx_di_insn_bits;
369
370	/* digital output */
371	s = dev->subdevices + 3;
372	s->type = COMEDI_SUBD_DO;
373	s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
374	s->n_chan = thisboard->do_chans;
375	s->maxdata = 1;
376	s->range_table = &range_digital;
377	s->len_chanlist = thisboard->do_chans;
378	s->state = 0;
379	s->insn_bits = dyna_pci10xx_do_insn_bits;
380
381	devpriv->valid = 1;
382	mutex_unlock(&start_stop_sem);
383
384	printk(KERN_INFO "comedi: dyna_pci10xx: %s - device setup completed!\n",
385		boardtypes[board_index].name);
386
387	return 1;
388}
389
390static int dyna_pci10xx_detach(struct comedi_device *dev)
391{
392	if (devpriv && devpriv->pci_dev) {
393		comedi_pci_disable(devpriv->pci_dev);
394		mutex_destroy(&devpriv->mutex);
395	}
396
397	return 0;
398}
399
400static struct comedi_driver driver_dyna_pci10xx = {
401	.driver_name	= DRV_NAME,
402	.module		= THIS_MODULE,
403	.attach		= dyna_pci10xx_attach,
404	.detach		= dyna_pci10xx_detach,
405	.board_name	= &boardtypes[0].name,
406	.offset		= sizeof(struct boardtype),
407	.num_names	= ARRAY_SIZE(boardtypes),
408};
409
410static int __devinit driver_dyna_pci10xx_pci_probe(struct pci_dev *dev,
411					      const struct pci_device_id *ent)
412{
413	return comedi_pci_auto_config(dev, &driver_dyna_pci10xx);
414}
415
416static void __devexit driver_dyna_pci10xx_pci_remove(struct pci_dev *dev)
417{
418	comedi_pci_auto_unconfig(dev);
419}
420
421static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = {
422	{ PCI_DEVICE(PCI_VENDOR_ID_DYNALOG, 0x1050) },
423	{ 0 }
424};
425MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
426
427static struct pci_driver driver_dyna_pci10xx_pci_driver = {
428	.id_table	= dyna_pci10xx_pci_table,
429	.probe		= driver_dyna_pci10xx_pci_probe,
430	.remove		= __devexit_p(driver_dyna_pci10xx_pci_remove),
431};
432
433static int __init driver_dyna_pci10xx_init_module(void)
434{
435	int retval;
436
437	retval = comedi_driver_register(&driver_dyna_pci10xx);
438	if (retval < 0)
439		return retval;
440
441	driver_dyna_pci10xx_pci_driver.name =
442		(char *)driver_dyna_pci10xx.driver_name;
443	return pci_register_driver(&driver_dyna_pci10xx_pci_driver);
444}
445module_init(driver_dyna_pci10xx_init_module);
446
447static void __exit driver_dyna_pci10xx_cleanup_module(void)
448{
449	pci_unregister_driver(&driver_dyna_pci10xx_pci_driver);
450	comedi_driver_unregister(&driver_dyna_pci10xx);
451}
452module_exit(driver_dyna_pci10xx_cleanup_module);
453
454MODULE_LICENSE("GPL");
455MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
456MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");
457