Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[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         {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236, PCI_ANY_ID,
138                 PCI_ANY_ID, 0, 0, 0},
139         {0}
140 };
141
142 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
143 #endif /* CONFIG_COMEDI_PCI */
144
145 /*
146  * Useful for shorthand access to the particular board structure
147  */
148 #define thisboard ((const struct pc236_board *)dev->board_ptr)
149
150 /* this structure is for data unique to this hardware driver.  If
151    several hardware drivers keep similar information in this structure,
152    feel free to suggest moving the variable to the struct comedi_device struct.  */
153 struct pc236_private {
154 #ifdef CONFIG_COMEDI_PCI
155         /* PCI device */
156         struct pci_dev *pci_dev;
157         unsigned long lcr_iobase;       /* PLX PCI9052 config registers in PCIBAR1 */
158 #endif
159         int enable_irq;
160 };
161
162 #define devpriv ((struct pc236_private *)dev->private)
163
164 /*
165  * The struct comedi_driver structure tells the Comedi core module
166  * which functions to call to configure/deconfigure (attach/detach)
167  * the board, and also about the kernel module that contains
168  * the device code.
169  */
170 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
171 static int pc236_detach(struct comedi_device *dev);
172 static struct comedi_driver driver_amplc_pc236 = {
173         .driver_name = PC236_DRIVER_NAME,
174         .module = THIS_MODULE,
175         .attach = pc236_attach,
176         .detach = pc236_detach,
177         .board_name = &pc236_boards[0].name,
178         .offset = sizeof(struct pc236_board),
179         .num_names = ARRAY_SIZE(pc236_boards),
180 };
181
182 #ifdef CONFIG_COMEDI_PCI
183 COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
184 #else
185 COMEDI_INITCLEANUP(driver_amplc_pc236);
186 #endif
187
188 static int pc236_request_region(unsigned minor, unsigned long from,
189         unsigned long extent);
190 static void pc236_intr_disable(struct comedi_device *dev);
191 static void pc236_intr_enable(struct comedi_device *dev);
192 static int pc236_intr_check(struct comedi_device *dev);
193 static int pc236_intr_insn(struct comedi_device *dev, struct comedi_subdevice *s,
194         struct comedi_insn *insn, unsigned int *data);
195 static int pc236_intr_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
196         struct comedi_cmd *cmd);
197 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
198 static int pc236_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
199 static irqreturn_t pc236_interrupt(int irq, void *d);
200
201 /*
202  * This function looks for a PCI device matching the requested board name,
203  * bus and slot.
204  */
205 #ifdef CONFIG_COMEDI_PCI
206 static int
207 pc236_find_pci(struct comedi_device *dev, int bus, int slot,
208         struct pci_dev **pci_dev_p)
209 {
210         struct pci_dev *pci_dev = NULL;
211
212         *pci_dev_p = NULL;
213
214         /* Look for matching PCI device. */
215         for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
216                 pci_dev != NULL;
217                 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
218                         PCI_ANY_ID, pci_dev)) {
219                 /* If bus/slot specified, check them. */
220                 if (bus || slot) {
221                         if (bus != pci_dev->bus->number
222                                 || slot != PCI_SLOT(pci_dev->devfn))
223                                 continue;
224                 }
225                 if (thisboard->model == anypci_model) {
226                         /* Match any supported model. */
227                         int i;
228
229                         for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
230                                 if (pc236_boards[i].bustype != pci_bustype)
231                                         continue;
232                                 if (pci_dev->device == pc236_boards[i].devid) {
233                                         /* Change board_ptr to matched board. */
234                                         dev->board_ptr = &pc236_boards[i];
235                                         break;
236                                 }
237                         }
238                         if (i == ARRAY_SIZE(pc236_boards))
239                                 continue;
240                 } else {
241                         /* Match specific model name. */
242                         if (pci_dev->device != thisboard->devid)
243                                 continue;
244                 }
245
246                 /* Found a match. */
247                 *pci_dev_p = pci_dev;
248                 return 0;
249         }
250         /* No match found. */
251         if (bus || slot) {
252                 printk(KERN_ERR
253                         "comedi%d: error! no %s found at pci %02x:%02x!\n",
254                         dev->minor, thisboard->name, bus, slot);
255         } else {
256                 printk(KERN_ERR "comedi%d: error! no %s found!\n",
257                         dev->minor, thisboard->name);
258         }
259         return -EIO;
260 }
261 #endif
262
263 /*
264  * Attach is called by the Comedi core to configure the driver
265  * for a particular board.  If you specified a board_name array
266  * in the driver structure, dev->board_ptr contains that
267  * address.
268  */
269 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
270 {
271         struct comedi_subdevice *s;
272         unsigned long iobase = 0;
273         unsigned int irq = 0;
274 #ifdef CONFIG_COMEDI_PCI
275         struct pci_dev *pci_dev = NULL;
276         int bus = 0, slot = 0;
277 #endif
278         int share_irq = 0;
279         int ret;
280
281         printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
282                 PC236_DRIVER_NAME);
283 /*
284  * Allocate the private structure area.  alloc_private() is a
285  * convenient macro defined in comedidev.h.
286  */
287         ret = alloc_private(dev, sizeof(struct pc236_private));
288         if (ret < 0) {
289                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
290                         dev->minor);
291                 return ret;
292         }
293         /* Process options. */
294         switch (thisboard->bustype) {
295         case isa_bustype:
296                 iobase = it->options[0];
297                 irq = it->options[1];
298                 share_irq = 0;
299                 break;
300 #ifdef CONFIG_COMEDI_PCI
301         case pci_bustype:
302                 bus = it->options[0];
303                 slot = it->options[1];
304                 share_irq = 1;
305
306                 ret = pc236_find_pci(dev, bus, slot, &pci_dev);
307                 if (ret < 0)
308                         return ret;
309                 devpriv->pci_dev = pci_dev;
310                 break;
311 #endif /* CONFIG_COMEDI_PCI */
312         default:
313                 printk(KERN_ERR
314                         "comedi%d: %s: BUG! cannot determine board type!\n",
315                         dev->minor, PC236_DRIVER_NAME);
316                 return -EINVAL;
317                 break;
318         }
319
320 /*
321  * Initialize dev->board_name.
322  */
323         dev->board_name = thisboard->name;
324
325         /* Enable device and reserve I/O spaces. */
326 #ifdef CONFIG_COMEDI_PCI
327         if (pci_dev) {
328
329                 ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
330                 if (ret < 0) {
331                         printk(KERN_ERR
332                                 "comedi%d: error! cannot enable PCI device and request regions!\n",
333                                 dev->minor);
334                         return ret;
335                 }
336                 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
337                 iobase = pci_resource_start(pci_dev, 2);
338                 irq = pci_dev->irq;
339         } else
340 #endif
341         {
342                 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
343                 if (ret < 0) {
344                         return ret;
345                 }
346         }
347         dev->iobase = iobase;
348
349 /*
350  * Allocate the subdevice structures.  alloc_subdevice() is a
351  * convenient macro defined in comedidev.h.
352  */
353         ret = alloc_subdevices(dev, 2);
354         if (ret < 0) {
355                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
356                         dev->minor);
357                 return ret;
358         }
359
360         s = dev->subdevices + 0;
361         /* digital i/o subdevice (8255) */
362         ret = subdev_8255_init(dev, s, NULL, iobase);
363         if (ret < 0) {
364                 printk(KERN_ERR "comedi%d: error! out of memory!\n",
365                         dev->minor);
366                 return ret;
367         }
368         s = dev->subdevices + 1;
369         dev->read_subdev = s;
370         s->type = COMEDI_SUBD_UNUSED;
371         pc236_intr_disable(dev);
372         if (irq) {
373                 unsigned long flags = share_irq ? IRQF_SHARED : 0;
374
375                 if (request_irq(irq, pc236_interrupt, flags,
376                                 PC236_DRIVER_NAME, dev) >= 0) {
377                         dev->irq = irq;
378                         s->type = COMEDI_SUBD_DI;
379                         s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
380                         s->n_chan = 1;
381                         s->maxdata = 1;
382                         s->range_table = &range_digital;
383                         s->insn_bits = pc236_intr_insn;
384                         s->do_cmdtest = pc236_intr_cmdtest;
385                         s->do_cmd = pc236_intr_cmd;
386                         s->cancel = pc236_intr_cancel;
387                 }
388         }
389         printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
390         if (thisboard->bustype == isa_bustype) {
391                 printk("(base %#lx) ", iobase);
392         } else {
393 #ifdef CONFIG_COMEDI_PCI
394                 printk("(pci %s) ", pci_name(pci_dev));
395 #endif
396         }
397         if (irq) {
398                 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
399         } else {
400                 printk("(no irq) ");
401         }
402
403         printk("attached\n");
404
405         return 1;
406 }
407
408 /*
409  * _detach is called to deconfigure a device.  It should deallocate
410  * resources.
411  * This function is also called when _attach() fails, so it should be
412  * careful not to release resources that were not necessarily
413  * allocated by _attach().  dev->private and dev->subdevices are
414  * deallocated automatically by the core.
415  */
416 static int pc236_detach(struct comedi_device *dev)
417 {
418         printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
419                 PC236_DRIVER_NAME);
420         if (devpriv) {
421                 pc236_intr_disable(dev);
422         }
423         if (dev->irq)
424                 free_irq(dev->irq, dev);
425         if (dev->subdevices) {
426                 subdev_8255_cleanup(dev, dev->subdevices + 0);
427         }
428         if (devpriv) {
429 #ifdef CONFIG_COMEDI_PCI
430                 if (devpriv->pci_dev) {
431                         if (dev->iobase) {
432                                 comedi_pci_disable(devpriv->pci_dev);
433                         }
434                         pci_dev_put(devpriv->pci_dev);
435                 } else
436 #endif
437                 {
438                         if (dev->iobase) {
439                                 release_region(dev->iobase, PC236_IO_SIZE);
440                         }
441                 }
442         }
443         if (dev->board_name) {
444                 printk(KERN_INFO "comedi%d: %s removed\n",
445                         dev->minor, dev->board_name);
446         }
447         return 0;
448 }
449
450 /*
451  * This function checks and requests an I/O region, reporting an error
452  * if there is a conflict.
453  */
454 static int pc236_request_region(unsigned minor, unsigned long from,
455         unsigned long extent)
456 {
457         if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
458                 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
459                         minor, from, extent);
460                 return -EIO;
461         }
462         return 0;
463 }
464
465 /*
466  * This function is called to mark the interrupt as disabled (no command
467  * configured on subdevice 1) and to physically disable the interrupt
468  * (not possible on the PC36AT, except by removing the IRQ jumper!).
469  */
470 static void pc236_intr_disable(struct comedi_device *dev)
471 {
472         unsigned long flags;
473
474         spin_lock_irqsave(&dev->spinlock, flags);
475         devpriv->enable_irq = 0;
476 #ifdef CONFIG_COMEDI_PCI
477         if (devpriv->lcr_iobase)
478                 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
479 #endif
480         spin_unlock_irqrestore(&dev->spinlock, flags);
481 }
482
483 /*
484  * This function is called to mark the interrupt as enabled (a command
485  * configured on subdevice 1) and to physically enable the interrupt
486  * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
487  */
488 static void pc236_intr_enable(struct comedi_device *dev)
489 {
490         unsigned long flags;
491
492         spin_lock_irqsave(&dev->spinlock, flags);
493         devpriv->enable_irq = 1;
494 #ifdef CONFIG_COMEDI_PCI
495         if (devpriv->lcr_iobase)
496                 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
497 #endif
498         spin_unlock_irqrestore(&dev->spinlock, flags);
499 }
500
501 /*
502  * This function is called when an interrupt occurs to check whether
503  * the interrupt has been marked as enabled and was generated by the
504  * board.  If so, the function prepares the hardware for the next
505  * interrupt.
506  * Returns 0 if the interrupt should be ignored.
507  */
508 static int pc236_intr_check(struct comedi_device *dev)
509 {
510         int retval = 0;
511         unsigned long flags;
512
513         spin_lock_irqsave(&dev->spinlock, flags);
514         if (devpriv->enable_irq) {
515                 retval = 1;
516 #ifdef CONFIG_COMEDI_PCI
517                 if (devpriv->lcr_iobase) {
518                         if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
519                                         & PLX9052_INTCSR_LI1STAT_MASK)
520                                 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
521                                 retval = 0;
522                         } else {
523                                 /* Clear interrupt and keep it enabled. */
524                                 outl(PCI236_INTR_ENABLE,
525                                         devpriv->lcr_iobase + PLX9052_INTCSR);
526                         }
527                 }
528 #endif
529         }
530         spin_unlock_irqrestore(&dev->spinlock, flags);
531
532         return retval;
533 }
534
535 /*
536  * Input from subdevice 1.
537  * Copied from the comedi_parport driver.
538  */
539 static int pc236_intr_insn(struct comedi_device *dev, struct comedi_subdevice *s,
540         struct comedi_insn *insn, unsigned int *data)
541 {
542         data[1] = 0;
543         return 2;
544 }
545
546 /*
547  * Subdevice 1 command test.
548  * Copied from the comedi_parport driver.
549  */
550 static int pc236_intr_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
551         struct comedi_cmd *cmd)
552 {
553         int err = 0;
554         int tmp;
555
556         /* step 1 */
557
558         tmp = cmd->start_src;
559         cmd->start_src &= TRIG_NOW;
560         if (!cmd->start_src || tmp != cmd->start_src)
561                 err++;
562
563         tmp = cmd->scan_begin_src;
564         cmd->scan_begin_src &= TRIG_EXT;
565         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
566                 err++;
567
568         tmp = cmd->convert_src;
569         cmd->convert_src &= TRIG_FOLLOW;
570         if (!cmd->convert_src || tmp != cmd->convert_src)
571                 err++;
572
573         tmp = cmd->scan_end_src;
574         cmd->scan_end_src &= TRIG_COUNT;
575         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
576                 err++;
577
578         tmp = cmd->stop_src;
579         cmd->stop_src &= TRIG_NONE;
580         if (!cmd->stop_src || tmp != cmd->stop_src)
581                 err++;
582
583         if (err)
584                 return 1;
585
586         /* step 2: ignored */
587
588         if (err)
589                 return 2;
590
591         /* step 3: */
592
593         if (cmd->start_arg != 0) {
594                 cmd->start_arg = 0;
595                 err++;
596         }
597         if (cmd->scan_begin_arg != 0) {
598                 cmd->scan_begin_arg = 0;
599                 err++;
600         }
601         if (cmd->convert_arg != 0) {
602                 cmd->convert_arg = 0;
603                 err++;
604         }
605         if (cmd->scan_end_arg != 1) {
606                 cmd->scan_end_arg = 1;
607                 err++;
608         }
609         if (cmd->stop_arg != 0) {
610                 cmd->stop_arg = 0;
611                 err++;
612         }
613
614         if (err)
615                 return 3;
616
617         /* step 4: ignored */
618
619         if (err)
620                 return 4;
621
622         return 0;
623 }
624
625 /*
626  * Subdevice 1 command.
627  */
628 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
629 {
630         pc236_intr_enable(dev);
631
632         return 0;
633 }
634
635 /*
636  * Subdevice 1 cancel command.
637  */
638 static int pc236_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
639 {
640         pc236_intr_disable(dev);
641
642         return 0;
643 }
644
645 /*
646  * Interrupt service routine.
647  * Based on the comedi_parport driver.
648  */
649 static irqreturn_t pc236_interrupt(int irq, void *d)
650 {
651         struct comedi_device *dev = d;
652         struct comedi_subdevice *s = dev->subdevices + 1;
653         int handled;
654
655         handled = pc236_intr_check(dev);
656         if (dev->attached && handled) {
657                 comedi_buf_put(s->async, 0);
658                 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
659                 comedi_event(dev, s);
660         }
661         return IRQ_RETVAL(handled);
662 }