2 comedi/drivers/amplc_pc236.c
3 Driver for Amplicon PC36AT and PCI236 DIO boards.
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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
28 Configuration options - PC36AT:
29 [0] - I/O port base address
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
38 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
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
50 #include <linux/pci.h>
51 #include <linux/interrupt.h>
53 #include "../comedidev.h"
55 #include "comedi_fc.h"
59 #define PC236_DRIVER_NAME "amplc_pc236"
61 #define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
62 #define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
64 /* PCI236 PCI configuration register information */
65 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
66 #define PCI_DEVICE_ID_INVALID 0xffff
68 /* PC36AT / PCI236 registers */
70 #define PC236_IO_SIZE 4
71 #define PC236_LCR_IO_SIZE 128
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)
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)
88 * Board descriptions for Amplicon PC36AT and PCI236.
91 enum pc236_bustype { isa_bustype, pci_bustype };
92 enum pc236_model { pc36at_model, pci236_model, anypci_model };
97 enum pc236_bustype bustype;
98 enum pc236_model model;
100 static const struct pc236_board pc236_boards[] = {
104 .bustype = isa_bustype,
105 .model = pc36at_model,
111 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
112 .bustype = pci_bustype,
113 .model = pci236_model,
116 .name = PC236_DRIVER_NAME,
117 .devid = PCI_DEVICE_ID_INVALID,
118 .bustype = pci_bustype,
119 .model = anypci_model, /* wildcard */
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.
128 struct pc236_private {
129 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
133 /* test if ISA supported and this is an ISA board */
134 static inline bool is_isa_board(const struct pc236_board *board)
136 return DO_ISA && board->bustype == isa_bustype;
139 /* test if PCI supported and this is a PCI board */
140 static inline bool is_pci_board(const struct pc236_board *board)
142 return DO_PCI && board->bustype == pci_bustype;
146 * This function looks for a board matching the supplied PCI device.
148 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
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];
160 * This function looks for a PCI device matching the requested board name,
163 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
164 struct comedi_devconfig *it)
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];
171 for_each_pci_dev(pci_dev) {
173 if (bus != pci_dev->bus->number ||
174 slot != PCI_SLOT(pci_dev->devfn))
177 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
180 if (thisboard->model == anypci_model) {
181 /* Wildcard board matches any supported PCI board. */
182 const struct pc236_board *foundboard;
184 foundboard = pc236_find_pci_board(pci_dev);
185 if (foundboard == NULL)
187 /* Replace wildcard board_ptr. */
188 dev->board_ptr = foundboard;
190 /* Match specific model name. */
191 if (pci_dev->device != thisboard->devid)
196 dev_err(dev->class_dev,
197 "No supported board found! (req. bus %d, slot %d)\n",
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!).
207 static void pc236_intr_disable(struct comedi_device *dev)
209 const struct pc236_board *thisboard = comedi_board(dev);
210 struct pc236_private *devpriv = dev->private;
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);
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!).
225 static void pc236_intr_enable(struct comedi_device *dev)
227 const struct pc236_board *thisboard = comedi_board(dev);
228 struct pc236_private *devpriv = dev->private;
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);
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
243 * Returns 0 if the interrupt should be ignored.
245 static int pc236_intr_check(struct comedi_device *dev)
247 const struct pc236_board *thisboard = comedi_board(dev);
248 struct pc236_private *devpriv = dev->private;
253 spin_lock_irqsave(&dev->spinlock, flags);
254 if (devpriv->enable_irq) {
256 if (is_pci_board(thisboard)) {
257 intcsr = inl(devpriv->lcr_iobase + PLX9052_INTCSR);
258 if (!(intcsr & PLX9052_INTCSR_LI1STAT)) {
261 /* Clear interrupt and keep it enabled. */
262 outl(PCI236_INTR_ENABLE,
263 devpriv->lcr_iobase + PLX9052_INTCSR);
267 spin_unlock_irqrestore(&dev->spinlock, flags);
273 * Input from subdevice 1.
274 * Copied from the comedi_parport driver.
276 static int pc236_intr_insn(struct comedi_device *dev,
277 struct comedi_subdevice *s, struct comedi_insn *insn,
285 * Subdevice 1 command test.
286 * Copied from the comedi_parport driver.
288 static int pc236_intr_cmdtest(struct comedi_device *dev,
289 struct comedi_subdevice *s,
290 struct comedi_cmd *cmd)
294 /* Step 1 : check if triggers are trivially valid */
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);
305 /* Step 2a : make sure trigger sources are unique */
306 /* Step 2b : and mutually compatible */
311 /* Step 3: check it arguments are trivially valid */
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);
322 /* step 4: ignored */
331 * Subdevice 1 command.
333 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
335 pc236_intr_enable(dev);
341 * Subdevice 1 cancel command.
343 static int pc236_intr_cancel(struct comedi_device *dev,
344 struct comedi_subdevice *s)
346 pc236_intr_disable(dev);
352 * Interrupt service routine.
353 * Based on the comedi_parport driver.
355 static irqreturn_t pc236_interrupt(int irq, void *d)
357 struct comedi_device *dev = d;
358 struct comedi_subdevice *s = &dev->subdevices[1];
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);
367 return IRQ_RETVAL(handled);
370 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
372 const struct pc236_board *thisboard = comedi_board(dev);
373 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
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));
386 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
388 (dev->irq ? "" : " UNAVAILABLE"));
390 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
392 dev_info(dev->class_dev, "%s %sattached\n",
393 dev->board_name, tmpbuf);
396 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
397 unsigned int irq, unsigned long req_irq_flags)
399 const struct pc236_board *thisboard = comedi_board(dev);
400 struct comedi_subdevice *s;
403 dev->board_name = thisboard->name;
404 dev->iobase = iobase;
406 ret = comedi_alloc_subdevices(dev, 2);
410 s = &dev->subdevices[0];
411 /* digital i/o subdevice (8255) */
412 ret = subdev_8255_init(dev, s, NULL, iobase);
414 dev_err(dev->class_dev, "error! out of memory!\n");
417 s = &dev->subdevices[1];
418 dev->read_subdev = s;
419 s->type = COMEDI_SUBD_UNUSED;
420 pc236_intr_disable(dev);
422 if (request_irq(irq, pc236_interrupt, req_irq_flags,
423 PC236_DRIVER_NAME, dev) >= 0) {
425 s->type = COMEDI_SUBD_DI;
426 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
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;
436 pc236_report_attach(dev, irq);
440 static int pc236_pci_common_attach(struct comedi_device *dev,
441 struct pci_dev *pci_dev)
443 struct pc236_private *devpriv = dev->private;
444 unsigned long iobase;
447 comedi_set_hw_dev(dev, &pci_dev->dev);
449 ret = comedi_pci_enable(dev);
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);
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
464 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
466 const struct pc236_board *thisboard = comedi_board(dev);
467 struct pc236_private *devpriv;
470 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
473 dev->private = devpriv;
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);
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;
485 pci_dev = pc236_find_pci_dev(dev, it);
488 return pc236_pci_common_attach(dev, pci_dev);
490 dev_err(dev->class_dev, PC236_DRIVER_NAME
491 ": BUG! cannot determine board type!\n");
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.
501 static int pc236_auto_attach(struct comedi_device *dev,
502 unsigned long context_unused)
504 struct pci_dev *pci_dev = comedi_to_pci_dev(dev);
505 struct pc236_private *devpriv;
510 dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
513 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
516 dev->private = devpriv;
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");
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()
529 pci_dev_get(pci_dev);
530 return pc236_pci_common_attach(dev, pci_dev);
533 static void pc236_detach(struct comedi_device *dev)
535 const struct pc236_board *thisboard = comedi_board(dev);
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);
546 free_irq(dev->irq, dev);
547 comedi_pci_disable(dev);
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
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),
571 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
572 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
576 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
578 static int amplc_pc236_pci_probe(struct pci_dev *dev,
579 const struct pci_device_id *id)
581 return comedi_pci_auto_config(dev, &lc_pc236_driver,
585 static struct pci_driver amplc_pc236_pci_driver = {
586 .name = PC236_DRIVER_NAME,
587 .id_table = pc236_pci_table,
588 .probe = &lc_pc236_pci_probe,
589 .remove = comedi_pci_auto_unconfig,
592 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
594 module_comedi_driver(amplc_pc236_driver);
597 MODULE_AUTHOR("Comedi http://www.comedi.org");
598 MODULE_DESCRIPTION("Comedi low-level driver");
599 MODULE_LICENSE("GPL");