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