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