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