dyna_pci10xx.c revision 9901a4d75d007686e8f6473189cafc4b216b7449
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_auto_attach(struct comedi_device *dev,
181					      unsigned long context_unused)
182{
183	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
184	struct dyna_pci10xx_private *devpriv;
185	struct comedi_subdevice *s;
186	int ret;
187
188	dev->board_name = dev->driver->driver_name;
189
190	devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
191	if (!devpriv)
192		return -ENOMEM;
193	dev->private = devpriv;
194
195	ret = comedi_pci_enable(pcidev, dev->board_name);
196	if (ret)
197		return ret;
198	dev->iobase = pci_resource_start(pcidev, 2);
199	devpriv->BADR3 = pci_resource_start(pcidev, 3);
200
201	mutex_init(&devpriv->mutex);
202
203	ret = comedi_alloc_subdevices(dev, 4);
204	if (ret)
205		return ret;
206
207	/* analog input */
208	s = &dev->subdevices[0];
209	s->type = COMEDI_SUBD_AI;
210	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
211	s->n_chan = 16;
212	s->maxdata = 0x0FFF;
213	s->range_table = &range_pci1050_ai;
214	s->len_chanlist = 16;
215	s->insn_read = dyna_pci10xx_insn_read_ai;
216
217	/* analog output */
218	s = &dev->subdevices[1];
219	s->type = COMEDI_SUBD_AO;
220	s->subdev_flags = SDF_WRITABLE;
221	s->n_chan = 16;
222	s->maxdata = 0x0FFF;
223	s->range_table = &range_unipolar10;
224	s->len_chanlist = 16;
225	s->insn_write = dyna_pci10xx_insn_write_ao;
226
227	/* digital input */
228	s = &dev->subdevices[2];
229	s->type = COMEDI_SUBD_DI;
230	s->subdev_flags = SDF_READABLE | SDF_GROUND;
231	s->n_chan = 16;
232	s->maxdata = 1;
233	s->range_table = &range_digital;
234	s->len_chanlist = 16;
235	s->insn_bits = dyna_pci10xx_di_insn_bits;
236
237	/* digital output */
238	s = &dev->subdevices[3];
239	s->type = COMEDI_SUBD_DO;
240	s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
241	s->n_chan = 16;
242	s->maxdata = 1;
243	s->range_table = &range_digital;
244	s->len_chanlist = 16;
245	s->state = 0;
246	s->insn_bits = dyna_pci10xx_do_insn_bits;
247
248	dev_info(dev->class_dev, "%s attached\n", dev->board_name);
249
250	return 0;
251}
252
253static void dyna_pci10xx_detach(struct comedi_device *dev)
254{
255	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
256	struct dyna_pci10xx_private *devpriv = dev->private;
257
258	if (devpriv)
259		mutex_destroy(&devpriv->mutex);
260	if (pcidev) {
261		if (dev->iobase)
262			comedi_pci_disable(pcidev);
263	}
264}
265
266static struct comedi_driver dyna_pci10xx_driver = {
267	.driver_name	= "dyna_pci10xx",
268	.module		= THIS_MODULE,
269	.auto_attach	= dyna_pci10xx_auto_attach,
270	.detach		= dyna_pci10xx_detach,
271};
272
273static int dyna_pci10xx_pci_probe(struct pci_dev *dev,
274					    const struct pci_device_id *ent)
275{
276	return comedi_pci_auto_config(dev, &dyna_pci10xx_driver);
277}
278
279static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = {
280	{ PCI_DEVICE(PCI_VENDOR_ID_PLX, 0x1050) },
281	{ 0 }
282};
283MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
284
285static struct pci_driver dyna_pci10xx_pci_driver = {
286	.name		= "dyna_pci10xx",
287	.id_table	= dyna_pci10xx_pci_table,
288	.probe		= dyna_pci10xx_pci_probe,
289	.remove		= comedi_pci_auto_unconfig,
290};
291module_comedi_pci_driver(dyna_pci10xx_driver, dyna_pci10xx_pci_driver);
292
293MODULE_LICENSE("GPL");
294MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
295MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");
296