Merge branch 'for-3.6-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[cascardo/linux.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 "8255.h"
60 #include "plx9052.h"
61
62 #define PC236_DRIVER_NAME       "amplc_pc236"
63
64 /* PCI236 PCI configuration register information */
65 #define PCI_VENDOR_ID_AMPLICON 0x14dc
66 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
67 #define PCI_DEVICE_ID_INVALID 0xffff
68
69 /* PC36AT / PCI236 registers */
70
71 #define PC236_IO_SIZE           4
72 #define PC236_LCR_IO_SIZE       128
73
74 /*
75  * INTCSR values for PCI236.
76  */
77 /* Disable interrupt, also clear any interrupt there */
78 #define PCI236_INTR_DISABLE (PLX9052_INTCSR_LI1ENAB_DISABLED \
79         | PLX9052_INTCSR_LI1POL_HIGH \
80         | PLX9052_INTCSR_LI2POL_HIGH \
81         | PLX9052_INTCSR_PCIENAB_DISABLED \
82         | PLX9052_INTCSR_LI1SEL_EDGE \
83         | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
84 /* Enable interrupt, also clear any interrupt there. */
85 #define PCI236_INTR_ENABLE (PLX9052_INTCSR_LI1ENAB_ENABLED \
86         | PLX9052_INTCSR_LI1POL_HIGH \
87         | PLX9052_INTCSR_LI2POL_HIGH \
88         | PLX9052_INTCSR_PCIENAB_ENABLED \
89         | PLX9052_INTCSR_LI1SEL_EDGE \
90         | PLX9052_INTCSR_LI1CLRINT_ASSERTED)
91
92 /*
93  * Board descriptions for Amplicon PC36AT and PCI236.
94  */
95
96 enum pc236_bustype { isa_bustype, pci_bustype };
97 enum pc236_model { pc36at_model, pci236_model, anypci_model };
98
99 struct pc236_board {
100         const char *name;
101         unsigned short devid;
102         enum pc236_bustype bustype;
103         enum pc236_model model;
104 };
105 static const struct pc236_board pc236_boards[] = {
106 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
107         {
108                 .name = "pc36at",
109                 .bustype = isa_bustype,
110                 .model = pc36at_model,
111         },
112 #endif
113 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
114         {
115                 .name = "pci236",
116                 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
117                 .bustype = pci_bustype,
118                 .model = pci236_model,
119         },
120         {
121                 .name = PC236_DRIVER_NAME,
122                 .devid = PCI_DEVICE_ID_INVALID,
123                 .bustype = pci_bustype,
124                 .model = anypci_model,  /* wildcard */
125         },
126 #endif
127 };
128
129 /* this structure is for data unique to this hardware driver.  If
130    several hardware drivers keep similar information in this structure,
131    feel free to suggest moving the variable to the struct comedi_device struct.
132  */
133 struct pc236_private {
134         unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
135         int enable_irq;
136 };
137
138 /*
139  * This function looks for a board matching the supplied PCI device.
140  */
141 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
142 {
143         unsigned int i;
144
145         for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
146                 if (pc236_boards[i].bustype == pci_bustype &&
147                     pci_dev->device == pc236_boards[i].devid)
148                         return &pc236_boards[i];
149         return NULL;
150 }
151
152 /*
153  * This function looks for a PCI device matching the requested board name,
154  * bus and slot.
155  */
156 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
157                                           struct comedi_devconfig *it)
158 {
159         const struct pc236_board *thisboard = comedi_board(dev);
160         struct pci_dev *pci_dev = NULL;
161         int bus = it->options[0];
162         int slot = it->options[1];
163
164         for_each_pci_dev(pci_dev) {
165                 if (bus || slot) {
166                         if (bus != pci_dev->bus->number ||
167                             slot != PCI_SLOT(pci_dev->devfn))
168                                 continue;
169                 }
170                 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
171                         continue;
172
173                 if (thisboard->model == anypci_model) {
174                         /* Wildcard board matches any supported PCI board. */
175                         const struct pc236_board *foundboard;
176
177                         foundboard = pc236_find_pci_board(pci_dev);
178                         if (foundboard == NULL)
179                                 continue;
180                         /* Replace wildcard board_ptr. */
181                         dev->board_ptr = foundboard;
182                 } else {
183                         /* Match specific model name. */
184                         if (pci_dev->device != thisboard->devid)
185                                 continue;
186                 }
187                 return pci_dev;
188         }
189         dev_err(dev->class_dev,
190                 "No supported board found! (req. bus %d, slot %d)\n",
191                 bus, slot);
192         return NULL;
193 }
194
195 /*
196  * This function checks and requests an I/O region, reporting an error
197  * if there is a conflict.
198  */
199 static int pc236_request_region(struct comedi_device *dev, unsigned long from,
200                                 unsigned long extent)
201 {
202         if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
203                 dev_err(dev->class_dev, "I/O port conflict (%#lx,%lu)!\n",
204                        from, extent);
205                 return -EIO;
206         }
207         return 0;
208 }
209
210 /*
211  * This function is called to mark the interrupt as disabled (no command
212  * configured on subdevice 1) and to physically disable the interrupt
213  * (not possible on the PC36AT, except by removing the IRQ jumper!).
214  */
215 static void pc236_intr_disable(struct comedi_device *dev)
216 {
217         struct pc236_private *devpriv = dev->private;
218         unsigned long flags;
219
220         spin_lock_irqsave(&dev->spinlock, flags);
221         devpriv->enable_irq = 0;
222         if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
223                 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
224         spin_unlock_irqrestore(&dev->spinlock, flags);
225 }
226
227 /*
228  * This function is called to mark the interrupt as enabled (a command
229  * configured on subdevice 1) and to physically enable the interrupt
230  * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
231  */
232 static void pc236_intr_enable(struct comedi_device *dev)
233 {
234         struct pc236_private *devpriv = dev->private;
235         unsigned long flags;
236
237         spin_lock_irqsave(&dev->spinlock, flags);
238         devpriv->enable_irq = 1;
239         if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
240                 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
241         spin_unlock_irqrestore(&dev->spinlock, flags);
242 }
243
244 /*
245  * This function is called when an interrupt occurs to check whether
246  * the interrupt has been marked as enabled and was generated by the
247  * board.  If so, the function prepares the hardware for the next
248  * interrupt.
249  * Returns 0 if the interrupt should be ignored.
250  */
251 static int pc236_intr_check(struct comedi_device *dev)
252 {
253         struct pc236_private *devpriv = dev->private;
254         int retval = 0;
255         unsigned long flags;
256
257         spin_lock_irqsave(&dev->spinlock, flags);
258         if (devpriv->enable_irq) {
259                 retval = 1;
260                 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
261                     devpriv->lcr_iobase) {
262                         if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
263                              & PLX9052_INTCSR_LI1STAT_MASK)
264                             == PLX9052_INTCSR_LI1STAT_INACTIVE) {
265                                 retval = 0;
266                         } else {
267                                 /* Clear interrupt and keep it enabled. */
268                                 outl(PCI236_INTR_ENABLE,
269                                      devpriv->lcr_iobase + PLX9052_INTCSR);
270                         }
271                 }
272         }
273         spin_unlock_irqrestore(&dev->spinlock, flags);
274
275         return retval;
276 }
277
278 /*
279  * Input from subdevice 1.
280  * Copied from the comedi_parport driver.
281  */
282 static int pc236_intr_insn(struct comedi_device *dev,
283                            struct comedi_subdevice *s, struct comedi_insn *insn,
284                            unsigned int *data)
285 {
286         data[1] = 0;
287         return insn->n;
288 }
289
290 /*
291  * Subdevice 1 command test.
292  * Copied from the comedi_parport driver.
293  */
294 static int pc236_intr_cmdtest(struct comedi_device *dev,
295                               struct comedi_subdevice *s,
296                               struct comedi_cmd *cmd)
297 {
298         int err = 0;
299         int tmp;
300
301         /* step 1 */
302
303         tmp = cmd->start_src;
304         cmd->start_src &= TRIG_NOW;
305         if (!cmd->start_src || tmp != cmd->start_src)
306                 err++;
307
308         tmp = cmd->scan_begin_src;
309         cmd->scan_begin_src &= TRIG_EXT;
310         if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
311                 err++;
312
313         tmp = cmd->convert_src;
314         cmd->convert_src &= TRIG_FOLLOW;
315         if (!cmd->convert_src || tmp != cmd->convert_src)
316                 err++;
317
318         tmp = cmd->scan_end_src;
319         cmd->scan_end_src &= TRIG_COUNT;
320         if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
321                 err++;
322
323         tmp = cmd->stop_src;
324         cmd->stop_src &= TRIG_NONE;
325         if (!cmd->stop_src || tmp != cmd->stop_src)
326                 err++;
327
328         if (err)
329                 return 1;
330
331         /* step 2: ignored */
332
333         if (err)
334                 return 2;
335
336         /* step 3: */
337
338         if (cmd->start_arg != 0) {
339                 cmd->start_arg = 0;
340                 err++;
341         }
342         if (cmd->scan_begin_arg != 0) {
343                 cmd->scan_begin_arg = 0;
344                 err++;
345         }
346         if (cmd->convert_arg != 0) {
347                 cmd->convert_arg = 0;
348                 err++;
349         }
350         if (cmd->scan_end_arg != 1) {
351                 cmd->scan_end_arg = 1;
352                 err++;
353         }
354         if (cmd->stop_arg != 0) {
355                 cmd->stop_arg = 0;
356                 err++;
357         }
358
359         if (err)
360                 return 3;
361
362         /* step 4: ignored */
363
364         if (err)
365                 return 4;
366
367         return 0;
368 }
369
370 /*
371  * Subdevice 1 command.
372  */
373 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
374 {
375         pc236_intr_enable(dev);
376
377         return 0;
378 }
379
380 /*
381  * Subdevice 1 cancel command.
382  */
383 static int pc236_intr_cancel(struct comedi_device *dev,
384                              struct comedi_subdevice *s)
385 {
386         pc236_intr_disable(dev);
387
388         return 0;
389 }
390
391 /*
392  * Interrupt service routine.
393  * Based on the comedi_parport driver.
394  */
395 static irqreturn_t pc236_interrupt(int irq, void *d)
396 {
397         struct comedi_device *dev = d;
398         struct comedi_subdevice *s = dev->subdevices + 1;
399         int handled;
400
401         handled = pc236_intr_check(dev);
402         if (dev->attached && handled) {
403                 comedi_buf_put(s->async, 0);
404                 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
405                 comedi_event(dev, s);
406         }
407         return IRQ_RETVAL(handled);
408 }
409
410 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
411 {
412         const struct pc236_board *thisboard = comedi_board(dev);
413         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
414         char tmpbuf[60];
415         int tmplen;
416
417         if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
418             thisboard->bustype == isa_bustype)
419                 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
420                                    "(base %#lx) ", dev->iobase);
421         else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
422                  thisboard->bustype == pci_bustype) {
423                 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
424                                    "(pci %s) ", pci_name(pcidev));
425         } else
426                 tmplen = 0;
427         if (irq)
428                 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
429                                     "(irq %u%s) ", irq,
430                                     (dev->irq ? "" : " UNAVAILABLE"));
431         else
432                 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
433                                     "(no irq) ");
434         dev_info(dev->class_dev, "%s %sattached\n",
435                  dev->board_name, tmpbuf);
436 }
437
438 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
439                                unsigned int irq, unsigned long req_irq_flags)
440 {
441         const struct pc236_board *thisboard = comedi_board(dev);
442         struct comedi_subdevice *s;
443         int ret;
444
445         dev->board_name = thisboard->name;
446         dev->iobase = iobase;
447
448         ret = comedi_alloc_subdevices(dev, 2);
449         if (ret)
450                 return ret;
451
452         s = dev->subdevices + 0;
453         /* digital i/o subdevice (8255) */
454         ret = subdev_8255_init(dev, s, NULL, iobase);
455         if (ret < 0) {
456                 dev_err(dev->class_dev, "error! out of memory!\n");
457                 return ret;
458         }
459         s = dev->subdevices + 1;
460         dev->read_subdev = s;
461         s->type = COMEDI_SUBD_UNUSED;
462         pc236_intr_disable(dev);
463         if (irq) {
464                 if (request_irq(irq, pc236_interrupt, req_irq_flags,
465                                 PC236_DRIVER_NAME, dev) >= 0) {
466                         dev->irq = irq;
467                         s->type = COMEDI_SUBD_DI;
468                         s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
469                         s->n_chan = 1;
470                         s->maxdata = 1;
471                         s->range_table = &range_digital;
472                         s->insn_bits = pc236_intr_insn;
473                         s->do_cmdtest = pc236_intr_cmdtest;
474                         s->do_cmd = pc236_intr_cmd;
475                         s->cancel = pc236_intr_cancel;
476                 }
477         }
478         pc236_report_attach(dev, irq);
479         return 1;
480 }
481
482 static int pc236_pci_common_attach(struct comedi_device *dev,
483                                    struct pci_dev *pci_dev)
484 {
485         struct pc236_private *devpriv = dev->private;
486         unsigned long iobase;
487         int ret;
488
489         comedi_set_hw_dev(dev, &pci_dev->dev);
490
491         ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME);
492         if (ret < 0) {
493                 dev_err(dev->class_dev,
494                         "error! cannot enable PCI device and request regions!\n");
495                 return ret;
496         }
497         devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
498         iobase = pci_resource_start(pci_dev, 2);
499         return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
500 }
501
502 /*
503  * Attach is called by the Comedi core to configure the driver
504  * for a particular board.  If you specified a board_name array
505  * in the driver structure, dev->board_ptr contains that
506  * address.
507  */
508 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
509 {
510         const struct pc236_board *thisboard = comedi_board(dev);
511         int ret;
512
513         dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach\n");
514         ret = alloc_private(dev, sizeof(struct pc236_private));
515         if (ret < 0) {
516                 dev_err(dev->class_dev, "error! out of memory!\n");
517                 return ret;
518         }
519         /* Process options according to bus type. */
520         if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
521             thisboard->bustype == isa_bustype) {
522                 unsigned long iobase = it->options[0];
523                 unsigned int irq = it->options[1];
524                 ret = pc236_request_region(dev, iobase, PC236_IO_SIZE);
525                 if (ret < 0)
526                         return ret;
527                 return pc236_common_attach(dev, iobase, irq, 0);
528         } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
529                    thisboard->bustype == pci_bustype) {
530                 struct pci_dev *pci_dev;
531
532                 pci_dev = pc236_find_pci_dev(dev, it);
533                 if (!pci_dev)
534                         return -EIO;
535                 return pc236_pci_common_attach(dev, pci_dev);
536         } else {
537                 dev_err(dev->class_dev, PC236_DRIVER_NAME
538                         ": BUG! cannot determine board type!\n");
539                 return -EINVAL;
540         }
541 }
542
543 /*
544  * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
545  * to the "manual" attach hook.  dev->board_ptr is NULL on entry.  There should
546  * be a board entry matching the supplied PCI device.
547  */
548 static int __devinit pc236_attach_pci(struct comedi_device *dev,
549                                       struct pci_dev *pci_dev)
550 {
551         int ret;
552
553         if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI))
554                 return -EINVAL;
555
556         dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
557                  pci_name(pci_dev));
558         ret = alloc_private(dev, sizeof(struct pc236_private));
559         if (ret < 0) {
560                 dev_err(dev->class_dev, "error! out of memory!\n");
561                 return ret;
562         }
563         dev->board_ptr = pc236_find_pci_board(pci_dev);
564         if (dev->board_ptr == NULL) {
565                 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
566                 return -EINVAL;
567         }
568         /*
569          * Need to 'get' the PCI device to match the 'put' in pc236_detach().
570          * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
571          * support for manual attachment of PCI devices via pc236_attach()
572          * has been removed.
573          */
574         pci_dev_get(pci_dev);
575         return pc236_pci_common_attach(dev, pci_dev);
576 }
577
578 static void pc236_detach(struct comedi_device *dev)
579 {
580         struct pc236_private *devpriv = dev->private;
581         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
582
583         if (devpriv)
584                 pc236_intr_disable(dev);
585         if (dev->irq)
586                 free_irq(dev->irq, dev);
587         if (dev->subdevices)
588                 subdev_8255_cleanup(dev, dev->subdevices + 0);
589         if (pcidev) {
590                 if (dev->iobase)
591                         comedi_pci_disable(pcidev);
592                 pci_dev_put(pcidev);
593         } else {
594                 if (dev->iobase)
595                         release_region(dev->iobase, PC236_IO_SIZE);
596         }
597 }
598
599 /*
600  * The struct comedi_driver structure tells the Comedi core module
601  * which functions to call to configure/deconfigure (attach/detach)
602  * the board, and also about the kernel module that contains
603  * the device code.
604  */
605 static struct comedi_driver amplc_pc236_driver = {
606         .driver_name = PC236_DRIVER_NAME,
607         .module = THIS_MODULE,
608         .attach = pc236_attach,
609         .attach_pci = pc236_attach_pci,
610         .detach = pc236_detach,
611         .board_name = &pc236_boards[0].name,
612         .offset = sizeof(struct pc236_board),
613         .num_names = ARRAY_SIZE(pc236_boards),
614 };
615
616 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
617 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
618         { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
619         {0}
620 };
621
622 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
623
624 static int __devinit amplc_pc236_pci_probe(struct pci_dev *dev,
625                                            const struct pci_device_id *ent)
626 {
627         return comedi_pci_auto_config(dev, &amplc_pc236_driver);
628 }
629
630 static void __devexit amplc_pc236_pci_remove(struct pci_dev *dev)
631 {
632         comedi_pci_auto_unconfig(dev);
633 }
634
635 static struct pci_driver amplc_pc236_pci_driver = {
636         .name = PC236_DRIVER_NAME,
637         .id_table = pc236_pci_table,
638         .probe = &amplc_pc236_pci_probe,
639         .remove = __devexit_p(&amplc_pc236_pci_remove)
640 };
641
642 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
643 #else
644 module_comedi_driver(amplc_pc236_driver);
645 #endif
646
647 MODULE_AUTHOR("Comedi http://www.comedi.org");
648 MODULE_DESCRIPTION("Comedi low-level driver");
649 MODULE_LICENSE("GPL");