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