dyna_pci10xx.c revision 8fda437d8924d2f9c864d805862b43ff36020c5b
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 const struct comedi_lrange range_pci1050_ai = { 3, {
49							  BIP_RANGE(10),
50							  BIP_RANGE(5),
51							  UNI_RANGE(10)
52							  }
53};
54
55static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
56
57struct boardtype {
58	const char *name;
59	int device_id;
60};
61
62static const struct boardtype boardtypes[] = {
63	{
64	.name = "dyna_pci1050",
65	.device_id = 0x1050,
66	},
67	/*  dummy entry corresponding to driver name */
68	{.name = DRV_NAME},
69};
70
71struct dyna_pci10xx_private {
72	struct mutex mutex;
73	unsigned long BADR3;
74};
75
76/******************************************************************************/
77/************************** READ WRITE FUNCTIONS ******************************/
78/******************************************************************************/
79
80/* analog input callback */
81static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
82			struct comedi_subdevice *s,
83			struct comedi_insn *insn, unsigned int *data)
84{
85	struct dyna_pci10xx_private *devpriv = dev->private;
86	int n, counter;
87	u16 d = 0;
88	unsigned int chan, range;
89
90	/* get the channel number and range */
91	chan = CR_CHAN(insn->chanspec);
92	range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
93
94	mutex_lock(&devpriv->mutex);
95	/* convert n samples */
96	for (n = 0; n < insn->n; n++) {
97		/* trigger conversion */
98		smp_mb();
99		outw_p(0x0000 + range + chan, dev->iobase + 2);
100		udelay(10);
101		/* read data */
102		for (counter = 0; counter < READ_TIMEOUT; counter++) {
103			d = inw_p(dev->iobase);
104
105			/* check if read is successful if the EOC bit is set */
106			if (d & (1 << 15))
107				goto conv_finish;
108		}
109		data[n] = 0;
110		printk(KERN_DEBUG "comedi: dyna_pci10xx: "
111			"timeout reading analog input\n");
112		continue;
113conv_finish:
114		/* mask the first 4 bits - EOC bits */
115		d &= 0x0FFF;
116		data[n] = d;
117	}
118	mutex_unlock(&devpriv->mutex);
119
120	/* return the number of samples read/written */
121	return n;
122}
123
124/* analog output callback */
125static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
126				 struct comedi_subdevice *s,
127				 struct comedi_insn *insn, unsigned int *data)
128{
129	struct dyna_pci10xx_private *devpriv = dev->private;
130	int n;
131	unsigned int chan, range;
132
133	chan = CR_CHAN(insn->chanspec);
134	range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
135
136	mutex_lock(&devpriv->mutex);
137	for (n = 0; n < insn->n; n++) {
138		smp_mb();
139		/* trigger conversion and write data */
140		outw_p(data[n], dev->iobase);
141		udelay(10);
142	}
143	mutex_unlock(&devpriv->mutex);
144	return n;
145}
146
147/* digital input bit interface */
148static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
149			      struct comedi_subdevice *s,
150			      struct comedi_insn *insn, unsigned int *data)
151{
152	struct dyna_pci10xx_private *devpriv = dev->private;
153	u16 d = 0;
154
155	mutex_lock(&devpriv->mutex);
156	smp_mb();
157	d = inw_p(devpriv->BADR3);
158	udelay(10);
159
160	/* on return the data[0] contains output and data[1] contains input */
161	data[1] = d;
162	data[0] = s->state;
163	mutex_unlock(&devpriv->mutex);
164	return insn->n;
165}
166
167/* digital output bit interface */
168static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
169			      struct comedi_subdevice *s,
170			      struct comedi_insn *insn, unsigned int *data)
171{
172	struct dyna_pci10xx_private *devpriv = dev->private;
173
174	/* The insn data is a mask in data[0] and the new data
175	 * in data[1], each channel cooresponding to a bit.
176	 * s->state contains the previous write data
177	 */
178	mutex_lock(&devpriv->mutex);
179	if (data[0]) {
180		s->state &= ~data[0];
181		s->state |= (data[0] & data[1]);
182		smp_mb();
183		outw_p(s->state, devpriv->BADR3);
184		udelay(10);
185	}
186
187	/*
188	 * On return, data[1] contains the value of the digital
189	 * input and output lines. We just return the software copy of the
190	 * output values if it was a purely digital output subdevice.
191	 */
192	data[1] = s->state;
193	mutex_unlock(&devpriv->mutex);
194	return insn->n;
195}
196
197static const void *dyna_pci10xx_find_boardinfo(struct comedi_device *dev,
198					       struct pci_dev *pcidev)
199{
200	const struct boardtype *thisboard;
201	int i;
202
203	for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
204		thisboard = &boardtypes[i];
205		if (pcidev->device != thisboard->device_id)
206			return thisboard;
207	}
208	return NULL;
209}
210
211static int dyna_pci10xx_attach_pci(struct comedi_device *dev,
212				   struct pci_dev *pcidev)
213{
214	const struct boardtype *thisboard;
215	struct dyna_pci10xx_private *devpriv;
216	struct comedi_subdevice *s;
217	int ret;
218
219	comedi_set_hw_dev(dev, &pcidev->dev);
220
221	thisboard = dyna_pci10xx_find_boardinfo(dev, pcidev);
222	if (!thisboard)
223		return -ENODEV;
224	dev->board_ptr = thisboard;
225	dev->board_name = thisboard->name;
226
227	ret = alloc_private(dev, sizeof(*devpriv));
228	if (ret)
229		return ret;
230	devpriv = dev->private;
231
232	ret = comedi_pci_enable(pcidev, dev->board_name);
233	if (ret)
234		return ret;
235	dev->iobase = pci_resource_start(pcidev, 2);
236	devpriv->BADR3 = pci_resource_start(pcidev, 3);
237
238	mutex_init(&devpriv->mutex);
239
240	ret = comedi_alloc_subdevices(dev, 4);
241	if (ret)
242		return ret;
243
244	/* analog input */
245	s = dev->subdevices + 0;
246	s->type = COMEDI_SUBD_AI;
247	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
248	s->n_chan = 16;
249	s->maxdata = 0x0FFF;
250	s->range_table = &range_pci1050_ai;
251	s->len_chanlist = 16;
252	s->insn_read = dyna_pci10xx_insn_read_ai;
253
254	/* analog output */
255	s = dev->subdevices + 1;
256	s->type = COMEDI_SUBD_AO;
257	s->subdev_flags = SDF_WRITABLE;
258	s->n_chan = 16;
259	s->maxdata = 0x0FFF;
260	s->range_table = &range_unipolar10;
261	s->len_chanlist = 16;
262	s->insn_write = dyna_pci10xx_insn_write_ao;
263
264	/* digital input */
265	s = dev->subdevices + 2;
266	s->type = COMEDI_SUBD_DI;
267	s->subdev_flags = SDF_READABLE | SDF_GROUND;
268	s->n_chan = 16;
269	s->maxdata = 1;
270	s->range_table = &range_digital;
271	s->len_chanlist = 16;
272	s->insn_bits = dyna_pci10xx_di_insn_bits;
273
274	/* digital output */
275	s = dev->subdevices + 3;
276	s->type = COMEDI_SUBD_DO;
277	s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
278	s->n_chan = 16;
279	s->maxdata = 1;
280	s->range_table = &range_digital;
281	s->len_chanlist = 16;
282	s->state = 0;
283	s->insn_bits = dyna_pci10xx_do_insn_bits;
284
285	dev_info(dev->class_dev, "%s: %s attached\n",
286		dev->driver->driver_name, dev->board_name);
287
288	return 0;
289}
290
291static int dyna_pci10xx_attach(struct comedi_device *dev,
292			       struct comedi_devconfig *it)
293{
294	dev_warn(dev->class_dev,
295		"This driver does not support attach using comedi_config\n");
296
297	return -ENOSYS;
298}
299
300static void dyna_pci10xx_detach(struct comedi_device *dev)
301{
302	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
303	struct dyna_pci10xx_private *devpriv = dev->private;
304
305	if (devpriv)
306		mutex_destroy(&devpriv->mutex);
307	if (pcidev) {
308		if (dev->iobase)
309			comedi_pci_disable(pcidev);
310	}
311}
312
313static struct comedi_driver dyna_pci10xx_driver = {
314	.driver_name	= "dyna_pci10xx",
315	.module		= THIS_MODULE,
316	.attach		= dyna_pci10xx_attach,
317	.attach_pci	= dyna_pci10xx_attach_pci,
318	.detach		= dyna_pci10xx_detach,
319};
320
321static int __devinit dyna_pci10xx_pci_probe(struct pci_dev *dev,
322					    const struct pci_device_id *ent)
323{
324	return comedi_pci_auto_config(dev, &dyna_pci10xx_driver);
325}
326
327static void __devexit dyna_pci10xx_pci_remove(struct pci_dev *dev)
328{
329	comedi_pci_auto_unconfig(dev);
330}
331
332static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = {
333	{ PCI_DEVICE(PCI_VENDOR_ID_DYNALOG, 0x1050) },
334	{ 0 }
335};
336MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
337
338static struct pci_driver dyna_pci10xx_pci_driver = {
339	.name		= "dyna_pci10xx",
340	.id_table	= dyna_pci10xx_pci_table,
341	.probe		= dyna_pci10xx_pci_probe,
342	.remove		= __devexit_p(dyna_pci10xx_pci_remove),
343};
344module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver);
345
346MODULE_LICENSE("GPL");
347MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
348MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");
349