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