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