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