Version:  2.0.40 2.2.26 2.4.37 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16

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

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us