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/module.h>
51 #include <linux/pci.h>
52 #include <linux/interrupt.h>
54 #include "../comedidev.h"
56 #include "comedi_fc.h"
60 #define PC236_DRIVER_NAME "amplc_pc236"
62 #define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
63 #define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
65 /* PCI236 PCI configuration register information */
66 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
67 #define PCI_DEVICE_ID_INVALID 0xffff
69 /* PC36AT / PCI236 registers */
71 #define PC236_IO_SIZE 4
72 #define PC236_LCR_IO_SIZE 128
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)
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)
89 * Board descriptions for Amplicon PC36AT and PCI236.
92 enum pc236_bustype { isa_bustype, pci_bustype };
93 enum pc236_model { pc36at_model, pci236_model, anypci_model };
98 enum pc236_bustype bustype;
99 enum pc236_model model;
101 static const struct pc236_board pc236_boards[] = {
105 .bustype = isa_bustype,
106 .model = pc36at_model,
112 .devid = PCI_DEVICE_ID_AMPLICON_PCI236,
113 .bustype = pci_bustype,
114 .model = pci236_model,
117 .name = PC236_DRIVER_NAME,
118 .devid = PCI_DEVICE_ID_INVALID,
119 .bustype = pci_bustype,
120 .model = anypci_model, /* wildcard */
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.
129 struct pc236_private {
130 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
134 /* test if ISA supported and this is an ISA board */
135 static inline bool is_isa_board(const struct pc236_board *board)
137 return DO_ISA && board->bustype == isa_bustype;
140 /* test if PCI supported and this is a PCI board */
141 static inline bool is_pci_board(const struct pc236_board *board)
143 return DO_PCI && board->bustype == pci_bustype;
147 * This function looks for a board matching the supplied PCI device.
149 static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
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];
161 * This function looks for a PCI device matching the requested board name,
164 static struct pci_dev *pc236_find_pci_dev(struct comedi_device *dev,
165 struct comedi_devconfig *it)
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];
172 for_each_pci_dev(pci_dev) {
174 if (bus != pci_dev->bus->number ||
175 slot != PCI_SLOT(pci_dev->devfn))
178 if (pci_dev->vendor != PCI_VENDOR_ID_AMPLICON)
181 if (thisboard->model == anypci_model) {
182 /* Wildcard board matches any supported PCI board. */
183 const struct pc236_board *foundboard;
185 foundboard = pc236_find_pci_board(pci_dev);
186 if (foundboard == NULL)
188 /* Replace wildcard board_ptr. */
189 dev->board_ptr = foundboard;
191 /* Match specific model name. */
192 if (pci_dev->device != thisboard->devid)
197 dev_err(dev->class_dev,
198 "No supported board found! (req. bus %d, slot %d)\n",
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!).
208 static void pc236_intr_disable(struct comedi_device *dev)
210 const struct pc236_board *thisboard = comedi_board(dev);
211 struct pc236_private *devpriv = dev->private;
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);
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!).
226 static void pc236_intr_enable(struct comedi_device *dev)
228 const struct pc236_board *thisboard = comedi_board(dev);
229 struct pc236_private *devpriv = dev->private;
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);
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
244 * Returns 0 if the interrupt should be ignored.
246 static int pc236_intr_check(struct comedi_device *dev)
248 const struct pc236_board *thisboard = comedi_board(dev);
249 struct pc236_private *devpriv = dev->private;
254 spin_lock_irqsave(&dev->spinlock, flags);
255 if (devpriv->enable_irq) {
257 if (is_pci_board(thisboard)) {
258 intcsr = inl(devpriv->lcr_iobase + PLX9052_INTCSR);
259 if (!(intcsr & PLX9052_INTCSR_LI1STAT)) {
262 /* Clear interrupt and keep it enabled. */
263 outl(PCI236_INTR_ENABLE,
264 devpriv->lcr_iobase + PLX9052_INTCSR);
268 spin_unlock_irqrestore(&dev->spinlock, flags);
274 * Input from subdevice 1.
275 * Copied from the comedi_parport driver.
277 static int pc236_intr_insn(struct comedi_device *dev,
278 struct comedi_subdevice *s, struct comedi_insn *insn,
286 * Subdevice 1 command test.
287 * Copied from the comedi_parport driver.
289 static int pc236_intr_cmdtest(struct comedi_device *dev,
290 struct comedi_subdevice *s,
291 struct comedi_cmd *cmd)
295 /* Step 1 : check if triggers are trivially valid */
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);
306 /* Step 2a : make sure trigger sources are unique */
307 /* Step 2b : and mutually compatible */
312 /* Step 3: check it arguments are trivially valid */
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, 1);
318 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
323 /* step 4: ignored */
332 * Subdevice 1 command.
334 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
336 pc236_intr_enable(dev);
342 * Subdevice 1 cancel command.
344 static int pc236_intr_cancel(struct comedi_device *dev,
345 struct comedi_subdevice *s)
347 pc236_intr_disable(dev);
353 * Interrupt service routine.
354 * Based on the comedi_parport driver.
356 static irqreturn_t pc236_interrupt(int irq, void *d)
358 struct comedi_device *dev = d;
359 struct comedi_subdevice *s = &dev->subdevices[1];
362 handled = pc236_intr_check(dev);
363 if (dev->attached && handled) {
364 comedi_buf_put(s->async, 0);
365 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
366 comedi_event(dev, s);
368 return IRQ_RETVAL(handled);
371 static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
373 const struct pc236_board *thisboard = comedi_board(dev);
374 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
378 if (is_isa_board(thisboard))
379 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
380 "(base %#lx) ", dev->iobase);
381 else if (is_pci_board(thisboard))
382 tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
383 "(pci %s) ", pci_name(pcidev));
387 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
389 (dev->irq ? "" : " UNAVAILABLE"));
391 tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
393 dev_info(dev->class_dev, "%s %sattached\n",
394 dev->board_name, tmpbuf);
397 static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
398 unsigned int irq, unsigned long req_irq_flags)
400 const struct pc236_board *thisboard = comedi_board(dev);
401 struct comedi_subdevice *s;
404 dev->board_name = thisboard->name;
405 dev->iobase = iobase;
407 ret = comedi_alloc_subdevices(dev, 2);
411 s = &dev->subdevices[0];
412 /* digital i/o subdevice (8255) */
413 ret = subdev_8255_init(dev, s, NULL, iobase);
415 dev_err(dev->class_dev, "error! out of memory!\n");
418 s = &dev->subdevices[1];
419 dev->read_subdev = s;
420 s->type = COMEDI_SUBD_UNUSED;
421 pc236_intr_disable(dev);
423 if (request_irq(irq, pc236_interrupt, req_irq_flags,
424 PC236_DRIVER_NAME, dev) >= 0) {
426 s->type = COMEDI_SUBD_DI;
427 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
430 s->range_table = &range_digital;
431 s->insn_bits = pc236_intr_insn;
432 s->do_cmdtest = pc236_intr_cmdtest;
433 s->do_cmd = pc236_intr_cmd;
434 s->cancel = pc236_intr_cancel;
437 pc236_report_attach(dev, irq);
441 static int pc236_pci_common_attach(struct comedi_device *dev,
442 struct pci_dev *pci_dev)
444 struct pc236_private *devpriv = dev->private;
445 unsigned long iobase;
448 comedi_set_hw_dev(dev, &pci_dev->dev);
450 ret = comedi_pci_enable(dev);
454 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
455 iobase = pci_resource_start(pci_dev, 2);
456 return pc236_common_attach(dev, iobase, pci_dev->irq, IRQF_SHARED);
460 * Attach is called by the Comedi core to configure the driver
461 * for a particular board. If you specified a board_name array
462 * in the driver structure, dev->board_ptr contains that
465 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
467 const struct pc236_board *thisboard = comedi_board(dev);
468 struct pc236_private *devpriv;
471 devpriv = comedi_alloc_devpriv(dev, sizeof(*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 = comedi_alloc_devpriv(dev, sizeof(*devpriv));
517 dev->board_ptr = pc236_find_pci_board(pci_dev);
518 if (dev->board_ptr == NULL) {
519 dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
523 * Need to 'get' the PCI device to match the 'put' in pc236_detach().
524 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
525 * support for manual attachment of PCI devices via pc236_attach()
528 pci_dev_get(pci_dev);
529 return pc236_pci_common_attach(dev, pci_dev);
532 static void pc236_detach(struct comedi_device *dev)
534 const struct pc236_board *thisboard = comedi_board(dev);
539 pc236_intr_disable(dev);
540 if (is_isa_board(thisboard)) {
541 comedi_legacy_detach(dev);
542 } else if (is_pci_board(thisboard)) {
543 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
545 free_irq(dev->irq, dev);
546 comedi_pci_disable(dev);
553 * The struct comedi_driver structure tells the Comedi core module
554 * which functions to call to configure/deconfigure (attach/detach)
555 * the board, and also about the kernel module that contains
558 static struct comedi_driver amplc_pc236_driver = {
559 .driver_name = PC236_DRIVER_NAME,
560 .module = THIS_MODULE,
561 .attach = pc236_attach,
562 .auto_attach = pc236_auto_attach,
563 .detach = pc236_detach,
564 .board_name = &pc236_boards[0].name,
565 .offset = sizeof(struct pc236_board),
566 .num_names = ARRAY_SIZE(pc236_boards),
570 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
571 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
575 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
577 static int amplc_pc236_pci_probe(struct pci_dev *dev,
578 const struct pci_device_id *id)
580 return comedi_pci_auto_config(dev, &lc_pc236_driver,
584 static struct pci_driver amplc_pc236_pci_driver = {
585 .name = PC236_DRIVER_NAME,
586 .id_table = pc236_pci_table,
587 .probe = &lc_pc236_pci_probe,
588 .remove = comedi_pci_auto_unconfig,
591 module_comedi_pci_driver(amplc_pc236_driver, amplc_pc236_pci_driver);
593 module_comedi_driver(amplc_pc236_driver);
596 MODULE_AUTHOR("Comedi http://www.comedi.org");
597 MODULE_DESCRIPTION("Comedi low-level driver");
598 MODULE_LICENSE("GPL");