Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
[cascardo/linux.git] / drivers / staging / comedi / drivers / dyna_pci10xx.c
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 "comedi_pci.h"
42 #include <linux/mutex.h>
43
44 #define PCI_VENDOR_ID_DYNALOG           0x10b5
45 #define DRV_NAME                        "dyna_pci10xx"
46
47 #define READ_TIMEOUT 50
48
49 static DEFINE_MUTEX(start_stop_sem);
50
51 static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table) = {
52         { PCI_DEVICE(PCI_VENDOR_ID_DYNALOG, 0x1050) },
53         { 0 }
54 };
55
56 MODULE_DEVICE_TABLE(pci, dyna_pci10xx_pci_table);
57
58 static int dyna_pci10xx_attach(struct comedi_device *dev,
59                           struct comedi_devconfig *it);
60 static int dyna_pci10xx_detach(struct comedi_device *dev);
61
62 static const struct comedi_lrange range_pci1050_ai = { 3, {
63                                                           BIP_RANGE(10),
64                                                           BIP_RANGE(5),
65                                                           UNI_RANGE(10)
66                                                           }
67 };
68
69 static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
70
71 static const struct comedi_lrange range_pci1050_ao = { 1, {
72                                                           UNI_RANGE(10)
73                                                           }
74 };
75
76 static const char range_codes_pci1050_ao[] = { 0x00 };
77
78 struct boardtype {
79         const char *name;
80         int device_id;
81         int ai_chans;
82         int ai_bits;
83         int ao_chans;
84         int ao_bits;
85         int di_chans;
86         int di_bits;
87         int do_chans;
88         int do_bits;
89         const struct comedi_lrange *range_ai;
90         const char *range_codes_ai;
91         const struct comedi_lrange *range_ao;
92         const char *range_codes_ao;
93 };
94
95 static const struct boardtype boardtypes[] = {
96         {
97         .name = "dyna_pci1050",
98         .device_id = 0x1050,
99         .ai_chans = 16,
100         .ai_bits = 12,
101         .ao_chans = 16,
102         .ao_bits = 12,
103         .di_chans = 16,
104         .di_bits = 16,
105         .do_chans = 16,
106         .do_bits = 16,
107         .range_ai = &range_pci1050_ai,
108         .range_codes_ai = range_codes_pci1050_ai,
109         .range_ao = &range_pci1050_ao,
110         .range_codes_ao = range_codes_pci1050_ao,
111         },
112         /*  dummy entry corresponding to driver name */
113         {.name = DRV_NAME},
114 };
115
116 static struct comedi_driver driver_dyna_pci10xx = {
117         .driver_name = DRV_NAME,
118         .module = THIS_MODULE,
119         .attach = dyna_pci10xx_attach,
120         .detach = dyna_pci10xx_detach,
121         .board_name = &boardtypes[0].name,
122         .offset = sizeof(struct boardtype),
123         .num_names = ARRAY_SIZE(boardtypes),
124 };
125
126 struct dyna_pci10xx_private {
127         struct pci_dev *pci_dev;        /*  ptr to PCI device */
128         char valid;                     /*  card is usable */
129         struct mutex mutex;
130
131         /* device base address registers */
132         unsigned long BADR0, BADR1, BADR2, BADR3, BADR4, BADR5;
133 };
134
135 #define thisboard ((const struct boardtype *)dev->board_ptr)
136 #define devpriv ((struct dyna_pci10xx_private *)dev->private)
137
138 /******************************************************************************/
139 /************************** READ WRITE FUNCTIONS ******************************/
140 /******************************************************************************/
141
142 /* analog input callback */
143 static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
144                         struct comedi_subdevice *s,
145                         struct comedi_insn *insn, unsigned int *data)
146 {
147         int n, counter;
148         u16 d = 0;
149         unsigned int chan, range;
150
151         /* get the channel number and range */
152         chan = CR_CHAN(insn->chanspec);
153         range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
154
155         mutex_lock(&devpriv->mutex);
156         /* convert n samples */
157         for (n = 0; n < insn->n; n++) {
158                 /* trigger conversion */
159                 smp_mb();
160                 outw_p(0x0000 + range + chan, devpriv->BADR2 + 2);
161                 udelay(10);
162                 /* read data */
163                 for (counter = 0; counter < READ_TIMEOUT; counter++) {
164                         d = inw_p(devpriv->BADR2);
165
166                         /* check if read is successfull if the EOC bit is set */
167                         if (d & (1 << 15))
168                                 goto conv_finish;
169                 }
170                 data[n] = 0;
171                 printk(KERN_DEBUG "comedi: dyna_pci10xx: "
172                         "timeout reading analog input\n");
173                 continue;
174 conv_finish:
175                 /* mask the first 4 bits - EOC bits */
176                 d &= 0x0FFF;
177                 data[n] = d;
178         }
179         mutex_unlock(&devpriv->mutex);
180
181         /* return the number of samples read/written */
182         return n;
183 }
184
185 /* analog output callback */
186 static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
187                                  struct comedi_subdevice *s,
188                                  struct comedi_insn *insn, unsigned int *data)
189 {
190         int n;
191         unsigned int chan, range;
192
193         chan = CR_CHAN(insn->chanspec);
194         range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
195
196         mutex_lock(&devpriv->mutex);
197         for (n = 0; n < insn->n; n++) {
198                 smp_mb();
199                 /* trigger conversion and write data */
200                 outw_p(data[n], devpriv->BADR2);
201                 udelay(10);
202         }
203         mutex_unlock(&devpriv->mutex);
204         return n;
205 }
206
207 /* digital input bit interface */
208 static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
209                               struct comedi_subdevice *s,
210                               struct comedi_insn *insn, unsigned int *data)
211 {
212         u16 d = 0;
213
214         if (insn->n != 2)
215                 return -EINVAL;
216
217         mutex_lock(&devpriv->mutex);
218         smp_mb();
219         d = inw_p(devpriv->BADR3);
220         udelay(10);
221
222         /* on return the data[0] contains output and data[1] contains input */
223         data[1] = d;
224         data[0] = s->state;
225         mutex_unlock(&devpriv->mutex);
226         return 2;
227 }
228
229 /* digital output bit interface */
230 static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
231                               struct comedi_subdevice *s,
232                               struct comedi_insn *insn, unsigned int *data)
233 {
234         if (insn->n != 2)
235                 return -EINVAL;
236
237         /* The insn data is a mask in data[0] and the new data
238          * in data[1], each channel cooresponding to a bit.
239          * s->state contains the previous write data
240          */
241         mutex_lock(&devpriv->mutex);
242         if (data[0]) {
243                 s->state &= ~data[0];
244                 s->state |= (data[0] & data[1]);
245                 smp_mb();
246                 outw_p(s->state, devpriv->BADR3);
247                 udelay(10);
248         }
249
250         /*
251          * On return, data[1] contains the value of the digital
252          * input and output lines. We just return the software copy of the
253          * output values if it was a purely digital output subdevice.
254          */
255         data[1] = s->state;
256         mutex_unlock(&devpriv->mutex);
257         return 2;
258 }
259
260 /******************************************************************************/
261 /*********************** INITIALIZATION FUNCTIONS *****************************/
262 /******************************************************************************/
263
264 static int dyna_pci10xx_attach(struct comedi_device *dev,
265                           struct comedi_devconfig *it)
266 {
267         struct comedi_subdevice *s;
268         struct pci_dev *pcidev;
269         unsigned int opt_bus, opt_slot;
270         int board_index, i;
271
272         mutex_lock(&start_stop_sem);
273
274         if (alloc_private(dev, sizeof(struct dyna_pci10xx_private)) < 0) {
275                 printk(KERN_ERR "comedi: dyna_pci10xx: "
276                         "failed to allocate memory!\n");
277                 mutex_unlock(&start_stop_sem);
278                 return -ENOMEM;
279         }
280
281         opt_bus = it->options[0];
282         opt_slot = it->options[1];
283         dev->board_name = thisboard->name;
284         dev->irq = 0;
285
286         /*
287          * Probe the PCI bus and located the matching device
288          */
289         for (pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
290                 pcidev != NULL;
291                 pcidev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pcidev)) {
292
293                 board_index = -1;
294                 for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
295                         if ((pcidev->vendor == PCI_VENDOR_ID_DYNALOG) &&
296                                 (pcidev->device == boardtypes[i].device_id)) {
297                                         board_index = i;
298                                         break;
299                                 }
300                 }
301                 if (board_index < 0)
302                         continue;
303
304                 /* Found matching vendor/device. */
305                 if (opt_bus || opt_slot) {
306                         /* Check bus/slot. */
307                         if (opt_bus != pcidev->bus->number
308                             || opt_slot != PCI_SLOT(pcidev->devfn))
309                                 continue;       /* no match */
310                 }
311
312                 goto found;
313         }
314         printk(KERN_ERR "comedi: dyna_pci10xx: no supported device found!\n");
315         mutex_unlock(&start_stop_sem);
316         return -EIO;
317
318 found:
319
320         if (!pcidev) {
321                 if (opt_bus || opt_slot) {
322                         printk(KERN_ERR "comedi: dyna_pci10xx: "
323                                 "invalid PCI device at b:s %d:%d\n",
324                                 opt_bus, opt_slot);
325                 } else {
326                         printk(KERN_ERR "comedi: dyna_pci10xx: "
327                                 "invalid PCI device\n");
328                 }
329                 mutex_unlock(&start_stop_sem);
330                 return -EIO;
331         }
332
333         if (comedi_pci_enable(pcidev, DRV_NAME)) {
334                 printk(KERN_ERR "comedi: dyna_pci10xx: "
335                         "failed to enable PCI device and request regions!");
336                 mutex_unlock(&start_stop_sem);
337                 return -EIO;
338         }
339
340         mutex_init(&devpriv->mutex);
341         dev->board_ptr = &boardtypes[board_index];
342         devpriv->pci_dev = pcidev;
343
344         printk(KERN_INFO "comedi: dyna_pci10xx: device found!\n");
345
346         /* initialize device base address registers */
347         devpriv->BADR0 = pci_resource_start(pcidev, 0);
348         devpriv->BADR1 = pci_resource_start(pcidev, 1);
349         devpriv->BADR2 = pci_resource_start(pcidev, 2);
350         devpriv->BADR3 = pci_resource_start(pcidev, 3);
351         devpriv->BADR4 = pci_resource_start(pcidev, 4);
352         devpriv->BADR5 = pci_resource_start(pcidev, 5);
353
354         if (alloc_subdevices(dev, 4) < 0) {
355                 printk(KERN_ERR "comedi: dyna_pci10xx: "
356                         "failed allocating subdevices\n");
357                 mutex_unlock(&start_stop_sem);
358                 return -ENOMEM;
359         }
360
361         /* analog input */
362         s = dev->subdevices + 0;
363         s->type = COMEDI_SUBD_AI;
364         s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
365         s->n_chan = thisboard->ai_chans;
366         s->maxdata = 0x0FFF;
367         s->range_table = thisboard->range_ai;
368         s->len_chanlist = 16;
369         s->insn_read = dyna_pci10xx_insn_read_ai;
370
371         /* analog output */
372         s = dev->subdevices + 1;
373         s->type = COMEDI_SUBD_AO;
374         s->subdev_flags = SDF_WRITABLE;
375         s->n_chan = thisboard->ao_chans;
376         s->maxdata = 0x0FFF;
377         s->range_table = thisboard->range_ao;
378         s->len_chanlist = 16;
379         s->insn_write = dyna_pci10xx_insn_write_ao;
380
381         /* digital input */
382         s = dev->subdevices + 2;
383         s->type = COMEDI_SUBD_DI;
384         s->subdev_flags = SDF_READABLE | SDF_GROUND;
385         s->n_chan = thisboard->di_chans;
386         s->maxdata = 1;
387         s->range_table = &range_digital;
388         s->len_chanlist = thisboard->di_chans;
389         s->insn_bits = dyna_pci10xx_di_insn_bits;
390
391         /* digital output */
392         s = dev->subdevices + 3;
393         s->type = COMEDI_SUBD_DO;
394         s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
395         s->n_chan = thisboard->do_chans;
396         s->maxdata = 1;
397         s->range_table = &range_digital;
398         s->len_chanlist = thisboard->do_chans;
399         s->state = 0;
400         s->insn_bits = dyna_pci10xx_do_insn_bits;
401
402         devpriv->valid = 1;
403         mutex_unlock(&start_stop_sem);
404
405         printk(KERN_INFO "comedi: dyna_pci10xx: %s - device setup completed!\n",
406                 boardtypes[board_index].name);
407
408         return 1;
409 }
410
411 static int dyna_pci10xx_detach(struct comedi_device *dev)
412 {
413         if (devpriv && devpriv->pci_dev) {
414                 comedi_pci_disable(devpriv->pci_dev);
415                 mutex_destroy(&devpriv->mutex);
416         }
417
418         return 0;
419 }
420
421 static int __devinit driver_dyna_pci10xx_pci_probe(struct pci_dev *dev,
422                                               const struct pci_device_id *ent)
423 {
424         return comedi_pci_auto_config(dev, driver_dyna_pci10xx.driver_name);
425 }
426
427 static void __devexit driver_dyna_pci10xx_pci_remove(struct pci_dev *dev)
428 {
429         comedi_pci_auto_unconfig(dev);
430 }
431
432 static struct pci_driver driver_dyna_pci10xx_pci_driver = {
433         .id_table = dyna_pci10xx_pci_table,
434         .probe = &driver_dyna_pci10xx_pci_probe,
435         .remove = __devexit_p(&driver_dyna_pci10xx_pci_remove)
436 };
437
438 static int __init driver_dyna_pci10xx_init_module(void)
439 {
440         int retval;
441
442         retval = comedi_driver_register(&driver_dyna_pci10xx);
443         if (retval < 0)
444                 return retval;
445
446         driver_dyna_pci10xx_pci_driver.name =
447                 (char *)driver_dyna_pci10xx.driver_name;
448         return pci_register_driver(&driver_dyna_pci10xx_pci_driver);
449 }
450
451 static void __exit driver_dyna_pci10xx_cleanup_module(void)
452 {
453         pci_unregister_driver(&driver_dyna_pci10xx_pci_driver);
454         comedi_driver_unregister(&driver_dyna_pci10xx);
455 }
456
457 module_init(driver_dyna_pci10xx_init_module);
458 module_exit(driver_dyna_pci10xx_cleanup_module);
459
460 MODULE_LICENSE("GPL");
461 MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
462 MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");