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