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