Merge branch 'fix/misc' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[pandora-kernel.git] / drivers / staging / comedi / drivers / amplc_pc236.c
1 /*
2     comedi/drivers/amplc_pc236.c
3     Driver for Amplicon PC36AT and PCI236 DIO boards.
4
5     Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
6
7     COMEDI - Linux Control and Measurement Device Interface
8     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
9
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 */
25 /*
26 Driver: amplc_pc236
27 Description: Amplicon PC36AT, PCI236
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC36AT (pc36at), PCI236 (pci236 or amplc_pc236)
30 Updated: Wed, 01 Apr 2009 15:41:25 +0100
31 Status: works
32
33 Configuration options - PC36AT:
34   [0] - I/O port base address
35   [1] - IRQ (optional)
36
37 Configuration options - PCI236:
38   [0] - PCI bus of device (optional)
39   [1] - PCI slot of device (optional)
40   If bus/slot is not specified, the first available PCI device will be
41   used.
42
43 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
44 as subdevice 0.
45
46 Subdevice 1 pretends to be a digital input device, but it always returns
47 0 when read. However, if you run a command with scan_begin_src=TRIG_EXT,
48 a rising edge on port C bit 3 acts as an external trigger, which can be
49 used to wake up tasks.  This is like the comedi_parport device, but the
50 only way to physically disable the interrupt on the PC36AT is to remove
51 the IRQ jumper.  If no interrupt is connected, then subdevice 1 is
52 unused.
53 */
54
55 #include <linux/interrupt.h>
56
57 #include "../comedidev.h"
58
59 #include "comedi_pci.h"
60
61 #include "8255.h"
62 #include "plx9052.h"
63
64 #define PC236_DRIVER_NAME       "amplc_pc236"
65
66 /* PCI236 PCI configuration register information */
67 #define PCI_VENDOR_ID_AMPLICON 0x14dc
68 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
69 #define PCI_DEVICE_ID_INVALID 0xffff
70
71 /* PC36AT / PCI236 registers */
72
73 #define PC236_IO_SIZE           4
74 #define PC236_LCR_IO_SIZE       128
75
76 /*
77  * INTCSR values for PCI236.
78  */
79 /* Disable interrupt, also clear any interrupt there */
80 #define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
81         | PLX9052_INTCSR_LI1POL_HIGH \
82         | PLX9052_INTCSR_LI2POL_HIGH \
83         | PLX9052_INTCSR_PCIENAB_DISABLED \
84         | PLX9052_INTCSR_LI1SEL_EDGE \
85         | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
86 /* Enable interrupt, also clear any interrupt there. */
87 #define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
88         | PLX9052_INTCSR_LI1POL_HIGH \
89         | PLX9052_INTCSR_LI2POL_HIGH \
90         | PLX9052_INTCSR_PCIENAB_ENABLED \
91         | PLX9052_INTCSR_LI1SEL_EDGE \
92         | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
93
94 /*
95  * Board descriptions for Amplicon PC36AT and PCI236.
96  */
97
98 enum pc236_bustype { isa_bustype, pci_bustype };
99 enum pc236_model { pc36at_model, pci236_model, anypci_model };
100
101 struct pc236_board {
102         const char *name;
103         const char *fancy_name;
104         unsigned short devid;
105         enum pc236_bustype bustype;
106         enum pc236_model model;
107 };
108 static const struct pc236_board pc236_boards[] = {
109         {
110          .name = "pc36at",
111          .fancy_name = "PC36AT",
112          .bustype = isa_bustype,
113          .model = pc36at_model,
114          },
115 #ifdef CONFIG_COMEDI_PCI
116         {
117          .name = "pci236",
118          .fancy_name = "PCI236",
119          .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
120          .bustype = pci_bustype,
121          .model = pci236_model,
122          },
123 #endif
124 #ifdef CONFIG_COMEDI_PCI
125         {
126          .name = PC236_DRIVER_NAME,
127          .fancy_name = PC236_DRIVER_NAME,
128          .devid = PCI_DEVICE_ID_INVALID,
129          .bustype = pci_bustype,
130          .model = anypci_model, /* wildcard */
131          },
132 #endif
133 };
134
135 #ifdef CONFIG_COMEDI_PCI
136 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
137         {
138         PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236,
139                     PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
140         0}
141 };
142
143 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
144 #endif /* CONFIG_COMEDI_PCI */
145
146 /*
147  * Useful for shorthand access to the particular board structure
148  */
149 #define thisboard ((const struct pc236_board *)dev->board_ptr)
150
151 /* this structure is for data unique to this hardware driver.  If
152    several hardware drivers keep similar information in this structure,
153    feel free to suggest moving the variable to the struct comedi_device struct.  */
154 struct pc236_private {
155 #ifdef CONFIG_COMEDI_PCI
156         /* PCI device */
157         struct pci_dev *pci_dev;
158         unsigned long lcr_iobase;       /* PLX PCI9052 config registers in PCIBAR1 */
159 #endif
160         int enable_irq;
161 };
162
163 #define devpriv ((struct pc236_private *)dev->private)
164
165 /*
166  * The struct comedi_driver structure tells the Comedi core module
167  * which functions to call to configure/deconfigure (attach/detach)
168  * the board, and also about the kernel module that contains
169  * the device code.
170  */
171 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
172 static int pc236_detach(struct comedi_device *dev);
173 static struct comedi_driver driver_amplc_pc236 = {
174         .driver_name = PC236_DRIVER_NAME,
175         .module = THIS_MODULE,
176         .attach = pc236_attach,
177         .detach = pc236_detach,
178         .board_name = &pc236_boards[0].name,
179         .offset = sizeof(struct pc236_board),
180         .num_names = ARRAY_SIZE(pc236_boards),
181 };
182
183 #ifdef CONFIG_COMEDI_PCI
184 COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
185 #else
186 COMEDI_INITCLEANUP(driver_amplc_pc236);
187 #endif
188
189 static int pc236_request_region(unsigned minor, unsigned long from,
190                                 unsigned long extent);
191 static void pc236_intr_disable(struct comedi_device *dev);
192 static void pc236_intr_enable(struct comedi_device *dev);
193 static int pc236_intr_check(struct comedi_device *dev);
194 static int pc236_intr_insn(struct comedi_device *dev,
195                            struct comedi_subdevice *s, struct comedi_insn *insn,
196                            unsigned int *data);
197 static int pc236_intr_cmdtest(struct comedi_device *dev,
198                               struct comedi_subdevice *s,
199                               struct comedi_cmd *cmd);
200 static int pc236_intr_cmd(struct comedi_device *dev,
201                           struct comedi_subdevice *s);
202 static int pc236_intr_cancel(struct comedi_device *dev,
203                              struct comedi_subdevice *s);
204 static irqreturn_t pc236_interrupt(int irq, void *d);
205
206 /*
207  * This function looks for a PCI device matching the requested board name,
208  * bus and slot.
209  */
210 #ifdef CONFIG_COMEDI_PCI
211 static int
212 pc236_find_pci(struct comedi_device *dev, int bus, int slot,
213                struct pci_dev **pci_dev_p)
214 {
215         struct pci_dev *pci_dev = NULL;
216
217         *pci_dev_p = NULL;
218
219         /* Look for matching PCI device. */
220         for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
221              pci_dev != NULL;
222              pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
223                                       PCI_ANY_ID, pci_dev)) {
224                 /* If bus/slot specified, check them. */
225                 if (bus || slot) {
226                         if (bus != pci_dev->bus->number
227                             || slot != PCI_SLOT(pci_dev->devfn))
228                                 continue;
229                 }
230                 if (thisboard->model == anypci_model) {
231                         /* Match any supported model. */
232                         int i;
233
234                         for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
235                                 if (pc236_boards[i].bustype != pci_bustype)
236                                         continue;
237                                 if (pci_dev->device == pc236_boards[i].devid) {
238                                         /* Change board_ptr to matched board. */
239                                         dev->board_ptr = &pc236_boards[i];
240                                         break;
241                                 }
242                         }
243                         if (i == ARRAY_SIZE(pc236_boards))
244                                 continue;
245                 } else {
246                         /* Match specific model name. */
247                         if (pci_dev->device != thisboard->devid)
248                                 continue;
249                 }
250
251                 /* Found a match. */
252                 *pci_dev_p = pci_dev;
253                 return 0;
254         }
255         /* No match found. */
256         if (bus || slot) {
257                 printk(KERN_ERR
258                        "comedi%d: error! no %s found at pci %02x:%02x!\n",
259                        dev->minor, thisboard->name, bus, slot);
260         } else {
261                 printk(KERN_ERR "comedi%d: error! no %s found!\n",
262                        dev->minor, thisboard->name);
263         }
264         return -EIO;
265 }
266 #endif
267
268 /*
269  * Attach is called by the Comedi core to configure the driver
270  * for a particular board.  If you specified a board_name array
271  * in the driver structure, dev->board_ptr contains that
272  * address.
273  */
274 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
275 {
276         struct comedi_subdevice *s;
277         unsigned long iobase = 0;
278         unsigned int irq = 0;
279 #ifdef CONFIG_COMEDI_PCI
280         struct pci_dev *pci_dev = NULL;
281         int bus = 0, slot = 0;
282 #endif
283         int share_irq = 0;
284         int ret;
285
286         printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
287                PC236_DRIVER_NAME);
288 /*
289  * Allocate the private structure area.  alloc_private() is a
290  * convenient macro defined in comedidev.h.
291  */
292         ret = alloc_private(dev, sizeof(struct pc236_private));
293         if (ret < 0) {
294                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
295                        dev->minor);
296                 return ret;
297         }
298         /* Process options. */
299         switch (thisboard->bustype) {
300         case isa_bustype:
301                 iobase = it->options[0];
302                 irq = it->options[1];
303                 share_irq = 0;
304                 break;
305 #ifdef CONFIG_COMEDI_PCI
306         case pci_bustype:
307                 bus = it->options[0];
308                 slot = it->options[1];
309                 share_irq = 1;
310
311                 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
312                 if (ret < 0)
313                         return ret;
314                 devpriv->pci_dev = pci_dev;
315                 break;
316 #endif /* CONFIG_COMEDI_PCI */
317         default:
318                 printk(KERN_ERR
319                        "comedi%d: %s: BUG! cannot determine board type!\n",
320                        dev->minor, PC236_DRIVER_NAME);
321                 return -EINVAL;
322                 break;
323         }
324
325 /*
326  * Initialize dev->board_name.
327  */
328         dev->board_name = thisboard->name;
329
330         /* Enable device and reserve I/O spaces. */
331 #ifdef CONFIG_COMEDI_PCI
332         if (pci_dev) {
333
334                 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
335                 if (ret < 0) {
336                         printk(KERN_ERR
337                                "comedi%d: error! cannot enable PCI device and request regions!\n",
338                                dev->minor);
339                         return ret;
340                 }
341                 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
342                 iobase = pci_resource_start(pci_dev, 2);
343                 irq = pci_dev->irq;
344         } else
345 #endif
346         {
347                 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
348                 if (ret < 0) {
349                         return ret;
350                 }
351         }
352         dev->iobase = iobase;
353
354 /*
355  * Allocate the subdevice structures.  alloc_subdevice() is a
356  * convenient macro defined in comedidev.h.
357  */
358         ret = alloc_subdevices(dev, 2);
359         if (ret < 0) {
360                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
361                        dev->minor);
362                 return ret;
363         }
364
365         s = dev->subdevices + 0;
366         /* digital i/o subdevice (8255) */
367         ret = subdev_8255_init(dev, s, NULL, iobase);
368         if (ret < 0) {
369                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
370                        dev->minor);
371                 return ret;
372         }
373         s = dev->subdevices + 1;
374         dev->read_subdev = s;
375         s->type = COMEDI_SUBD_UNUSED;
376         pc236_intr_disable(dev);
377         if (irq) {
378                 unsigned long flags = share_irq ? IRQF_SHARED : 0;
379
380                 if (request_irq(irq, pc236_interrupt, flags,
381                                 PC236_DRIVER_NAME, dev) >= 0) {
382                         dev->irq = irq;
383                         s->type = COMEDI_SUBD_DI;
384                         s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
385                         s->n_chan = 1;
386                         s->maxdata = 1;
387                         s->range_table = &range_digital;
388                         s->insn_bits = pc236_intr_insn;
389                         s->do_cmdtest = pc236_intr_cmdtest;
390                         s->do_cmd = pc236_intr_cmd;
391                         s->cancel = pc236_intr_cancel;
392                 }
393         }
394         printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
395         if (thisboard->bustype == isa_bustype) {
396                 printk("(base %#lx) ", iobase);
397         } else {
398 #ifdef CONFIG_COMEDI_PCI
399                 printk("(pci %s) ", pci_name(pci_dev));
400 #endif
401         }
402         if (irq) {
403                 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
404         } else {
405                 printk("(no irq) ");
406         }
407
408         printk("attached\n");
409
410         return 1;
411 }
412
413 /*
414  * _detach is called to deconfigure a device.  It should deallocate
415  * resources.
416  * This function is also called when _attach() fails, so it should be
417  * careful not to release resources that were not necessarily
418  * allocated by _attach().  dev->private and dev->subdevices are
419  * deallocated automatically by the core.
420  */
421 static int pc236_detach(struct comedi_device *dev)
422 {
423         printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
424                PC236_DRIVER_NAME);
425         if (devpriv) {
426                 pc236_intr_disable(dev);
427         }
428         if (dev->irq)
429                 free_irq(dev->irq, dev);
430         if (dev->subdevices) {
431                 subdev_8255_cleanup(dev, dev->subdevices + 0);
432         }
433         if (devpriv) {
434 #ifdef CONFIG_COMEDI_PCI
435                 if (devpriv->pci_dev) {
436                         if (dev->iobase) {
437                                 comedi_pci_disable(devpriv->pci_dev);
438                         }
439                         pci_dev_put(devpriv->pci_dev);
440                 } else
441 #endif
442                 {
443                         if (dev->iobase) {
444                                 release_region(dev->iobase, PC236_IO_SIZE);
445                         }
446                 }
447         }
448         if (dev->board_name) {
449                 printk(KERN_INFO "comedi%d: %s removed\n",
450                        dev->minor, dev->board_name);
451         }
452         return 0;
453 }
454
455 /*
456  * This function checks and requests an I/O region, reporting an error
457  * if there is a conflict.
458  */
459 static int pc236_request_region(unsigned minor, unsigned long from,
460                                 unsigned long extent)
461 {
462         if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
463                 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
464                        minor, from, extent);
465                 return -EIO;
466         }
467         return 0;
468 }
469
470 /*
471  * This function is called to mark the interrupt as disabled (no command
472  * configured on subdevice 1) and to physically disable the interrupt
473  * (not possible on the PC36AT, except by removing the IRQ jumper!).
474  */
475 static void pc236_intr_disable(struct comedi_device *dev)
476 {
477         unsigned long flags;
478
479         spin_lock_irqsave(&dev->spinlock, flags);
480         devpriv->enable_irq = 0;
481 #ifdef CONFIG_COMEDI_PCI
482         if (devpriv->lcr_iobase)
483                 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
484 #endif
485         spin_unlock_irqrestore(&dev->spinlock, flags);
486 }
487
488 /*
489  * This function is called to mark the interrupt as enabled (a command
490  * configured on subdevice 1) and to physically enable the interrupt
491  * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
492  */
493 static void pc236_intr_enable(struct comedi_device *dev)
494 {
495         unsigned long flags;
496
497         spin_lock_irqsave(&dev->spinlock, flags);
498         devpriv->enable_irq = 1;
499 #ifdef CONFIG_COMEDI_PCI
500         if (devpriv->lcr_iobase)
501                 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
502 #endif
503         spin_unlock_irqrestore(&dev->spinlock, flags);
504 }
505
506 /*
507  * This function is called when an interrupt occurs to check whether
508  * the interrupt has been marked as enabled and was generated by the
509  * board.  If so, the function prepares the hardware for the next
510  * interrupt.
511  * Returns 0 if the interrupt should be ignored.
512  */
513 static int pc236_intr_check(struct comedi_device *dev)
514 {
515         int retval = 0;
516         unsigned long flags;
517
518         spin_lock_irqsave(&dev->spinlock, flags);
519         if (devpriv->enable_irq) {
520                 retval = 1;
521 #ifdef CONFIG_COMEDI_PCI
522                 if (devpriv->lcr_iobase) {
523                         if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
524                              & PLX9052_INTCSR_LI1STAT_MASK)
525                             == PLX9052_INTCSR_LI1STAT_INACTIVE) {
526                                 retval = 0;
527                         } else {
528                                 /* Clear interrupt and keep it enabled. */
529                                 outl(PCI236_INTR_ENABLE,
530                                      devpriv->lcr_iobase + PLX9052_INTCSR);
531                         }
532                 }
533 #endif
534         }
535         spin_unlock_irqrestore(&dev->spinlock, flags);
536
537         return retval;
538 }
539
540 /*
541  * Input from subdevice 1.
542  * Copied from the comedi_parport driver.
543  */
544 static int pc236_intr_insn(struct comedi_device *dev,
545                            struct comedi_subdevice *s, struct comedi_insn *insn,
546                            unsigned int *data)
547 {
548         data[1] = 0;
549         return 2;
550 }
551
552 /*
553  * Subdevice 1 command test.
554  * Copied from the comedi_parport driver.
555  */
556 static int pc236_intr_cmdtest(struct comedi_device *dev,
557                               struct comedi_subdevice *s,
558                               struct comedi_cmd *cmd)
559 {
560         int err = 0;
561         int tmp;
562
563         /* step 1 */
564
565         tmp = cmd->start_src;
566         cmd->start_src &= TRIG_NOW;
567         if (!cmd->start_src || tmp != cmd->start_src)
568                 err++;
569
570         tmp = cmd->scan_begin_src;
571         cmd->scan_begin_src &= TRIG_EXT;
572         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
573                 err++;
574
575         tmp = cmd->convert_src;
576         cmd->convert_src &= TRIG_FOLLOW;
577         if (!cmd->convert_src || tmp != cmd->convert_src)
578                 err++;
579
580         tmp = cmd->scan_end_src;
581         cmd->scan_end_src &= TRIG_COUNT;
582         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
583                 err++;
584
585         tmp = cmd->stop_src;
586         cmd->stop_src &= TRIG_NONE;
587         if (!cmd->stop_src || tmp != cmd->stop_src)
588                 err++;
589
590         if (err)
591                 return 1;
592
593         /* step 2: ignored */
594
595         if (err)
596                 return 2;
597
598         /* step 3: */
599
600         if (cmd->start_arg != 0) {
601                 cmd->start_arg = 0;
602                 err++;
603         }
604         if (cmd->scan_begin_arg != 0) {
605                 cmd->scan_begin_arg = 0;
606                 err++;
607         }
608         if (cmd->convert_arg != 0) {
609                 cmd->convert_arg = 0;
610                 err++;
611         }
612         if (cmd->scan_end_arg != 1) {
613                 cmd->scan_end_arg = 1;
614                 err++;
615         }
616         if (cmd->stop_arg != 0) {
617                 cmd->stop_arg = 0;
618                 err++;
619         }
620
621         if (err)
622                 return 3;
623
624         /* step 4: ignored */
625
626         if (err)
627                 return 4;
628
629         return 0;
630 }
631
632 /*
633  * Subdevice 1 command.
634  */
635 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
636 {
637         pc236_intr_enable(dev);
638
639         return 0;
640 }
641
642 /*
643  * Subdevice 1 cancel command.
644  */
645 static int pc236_intr_cancel(struct comedi_device *dev,
646                              struct comedi_subdevice *s)
647 {
648         pc236_intr_disable(dev);
649
650         return 0;
651 }
652
653 /*
654  * Interrupt service routine.
655  * Based on the comedi_parport driver.
656  */
657 static irqreturn_t pc236_interrupt(int irq, void *d)
658 {
659         struct comedi_device *dev = d;
660         struct comedi_subdevice *s = dev->subdevices + 1;
661         int handled;
662
663         handled = pc236_intr_check(dev);
664         if (dev->attached && handled) {
665                 comedi_buf_put(s->async, 0);
666                 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
667                 comedi_event(dev, s);
668         }
669         return IRQ_RETVAL(handled);
670 }