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.
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.
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
33 Configuration options - PC36AT:
34 [0] - I/O port base address
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
43 The PC36AT ISA board and PCI236 PCI board have a single 8255 appearing
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
55 #include <linux/interrupt.h>
57 #include "../comedidev.h"
59 #include "comedi_pci.h"
64 #define PC236_DRIVER_NAME "amplc_pc236"
66 /* PCI236 PCI configuration register information */
67 #define PCI_VENDOR_ID_AMPLICON 0x14dc
68 #define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
69 #define PCI_DEVICE_ID_INVALID 0xffff
71 /* PC36AT / PCI236 registers */
73 #define PC236_IO_SIZE 4
74 #define PC236_LCR_IO_SIZE 128
77 * INTCSR values for PCI236.
79 /* Disable interrupt, also clear any interrupt there */
80 #define PCI236_INTR_DISABLE ( PLX9052_INTCSR_LI1ENAB_DISABLED \
81 | PLX9052_INTCSR_LI1POL_HIGH \
82 | PLX9052_INTCSR_LI2POL_HIGH \
83 | PLX9052_INTCSR_PCIENAB_DISABLED \
84 | PLX9052_INTCSR_LI1SEL_EDGE \
85 | PLX9052_INTCSR_LI1CLRINT_ASSERTED )
86 /* Enable interrupt, also clear any interrupt there. */
87 #define PCI236_INTR_ENABLE ( PLX9052_INTCSR_LI1ENAB_ENABLED \
88 | PLX9052_INTCSR_LI1POL_HIGH \
89 | PLX9052_INTCSR_LI2POL_HIGH \
90 | PLX9052_INTCSR_PCIENAB_ENABLED \
91 | PLX9052_INTCSR_LI1SEL_EDGE \
92 | PLX9052_INTCSR_LI1CLRINT_ASSERTED )
95 * Board descriptions for Amplicon PC36AT and PCI236.
98 enum pc236_bustype { isa_bustype, pci_bustype };
99 enum pc236_model { pc36at_model, pci236_model, anypci_model };
103 const char *fancy_name;
104 unsigned short devid;
105 enum pc236_bustype bustype;
106 enum pc236_model model;
108 static const struct pc236_board pc236_boards[] = {
112 bustype: isa_bustype,
115 #ifdef CONFIG_COMEDI_PCI
119 devid: PCI_DEVICE_ID_AMPLICON_PCI236,
120 bustype: pci_bustype,
124 #ifdef CONFIG_COMEDI_PCI
126 name: PC236_DRIVER_NAME,
127 fancy_name:PC236_DRIVER_NAME,
128 devid: PCI_DEVICE_ID_INVALID,
129 bustype: pci_bustype,
130 model: anypci_model, /* wildcard */
135 #ifdef CONFIG_COMEDI_PCI
136 static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
137 {PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236, PCI_ANY_ID,
138 PCI_ANY_ID, 0, 0, 0},
142 MODULE_DEVICE_TABLE(pci, pc236_pci_table);
143 #endif /* CONFIG_COMEDI_PCI */
146 * Useful for shorthand access to the particular board structure
148 #define thisboard ((const struct pc236_board *)dev->board_ptr)
150 /* this structure is for data unique to this hardware driver. If
151 several hardware drivers keep similar information in this structure,
152 feel free to suggest moving the variable to the struct comedi_device struct. */
153 struct pc236_private {
154 #ifdef CONFIG_COMEDI_PCI
156 struct pci_dev *pci_dev;
157 unsigned long lcr_iobase; /* PLX PCI9052 config registers in PCIBAR1 */
162 #define devpriv ((struct pc236_private *)dev->private)
165 * The struct comedi_driver structure tells the Comedi core module
166 * which functions to call to configure/deconfigure (attach/detach)
167 * the board, and also about the kernel module that contains
170 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it);
171 static int pc236_detach(struct comedi_device *dev);
172 static struct comedi_driver driver_amplc_pc236 = {
173 driver_name:PC236_DRIVER_NAME,
177 board_name:&pc236_boards[0].name,
178 offset:sizeof(struct pc236_board),
179 num_names:sizeof(pc236_boards) / sizeof(struct pc236_board),
182 #ifdef CONFIG_COMEDI_PCI
183 COMEDI_PCI_INITCLEANUP(driver_amplc_pc236, pc236_pci_table);
185 COMEDI_INITCLEANUP(driver_amplc_pc236);
188 static int pc236_request_region(unsigned minor, unsigned long from,
189 unsigned long extent);
190 static void pc236_intr_disable(struct comedi_device * dev);
191 static void pc236_intr_enable(struct comedi_device * dev);
192 static int pc236_intr_check(struct comedi_device * dev);
193 static int pc236_intr_insn(struct comedi_device * dev, struct comedi_subdevice * s,
194 struct comedi_insn * insn, unsigned int * data);
195 static int pc236_intr_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
196 struct comedi_cmd * cmd);
197 static int pc236_intr_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
198 static int pc236_intr_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
199 static irqreturn_t pc236_interrupt(int irq, void *d);
202 * This function looks for a PCI device matching the requested board name,
205 #ifdef CONFIG_COMEDI_PCI
207 pc236_find_pci(struct comedi_device *dev, int bus, int slot,
208 struct pci_dev **pci_dev_p)
210 struct pci_dev *pci_dev = NULL;
214 /* Look for matching PCI device. */
215 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
217 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
218 PCI_ANY_ID, pci_dev)) {
219 /* If bus/slot specified, check them. */
221 if (bus != pci_dev->bus->number
222 || slot != PCI_SLOT(pci_dev->devfn))
225 if (thisboard->model == anypci_model) {
226 /* Match any supported model. */
229 for (i = 0; i < ARRAY_SIZE(pc236_boards); i++) {
230 if (pc236_boards[i].bustype != pci_bustype)
232 if (pci_dev->device == pc236_boards[i].devid) {
233 /* Change board_ptr to matched board. */
234 dev->board_ptr = &pc236_boards[i];
238 if (i == ARRAY_SIZE(pc236_boards))
241 /* Match specific model name. */
242 if (pci_dev->device != thisboard->devid)
247 *pci_dev_p = pci_dev;
250 /* No match found. */
253 "comedi%d: error! no %s found at pci %02x:%02x!\n",
254 dev->minor, thisboard->name, bus, slot);
256 printk(KERN_ERR "comedi%d: error! no %s found!\n",
257 dev->minor, thisboard->name);
264 * Attach is called by the Comedi core to configure the driver
265 * for a particular board. If you specified a board_name array
266 * in the driver structure, dev->board_ptr contains that
269 static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
271 struct comedi_subdevice *s;
272 unsigned long iobase = 0;
273 unsigned int irq = 0;
274 #ifdef CONFIG_COMEDI_PCI
275 struct pci_dev *pci_dev = NULL;
276 int bus = 0, slot = 0;
281 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
284 * Allocate the private structure area. alloc_private() is a
285 * convenient macro defined in comedidev.h.
287 if ((ret = alloc_private(dev, sizeof(struct pc236_private))) < 0) {
288 printk(KERN_ERR "comedi%d: error! out of memory!\n",
292 /* Process options. */
293 switch (thisboard->bustype) {
295 iobase = it->options[0];
296 irq = it->options[1];
299 #ifdef CONFIG_COMEDI_PCI
301 bus = it->options[0];
302 slot = it->options[1];
305 if ((ret = pc236_find_pci(dev, bus, slot, &pci_dev)) < 0)
307 devpriv->pci_dev = pci_dev;
309 #endif /* CONFIG_COMEDI_PCI */
312 "comedi%d: %s: BUG! cannot determine board type!\n",
313 dev->minor, PC236_DRIVER_NAME);
319 * Initialize dev->board_name.
321 dev->board_name = thisboard->name;
323 /* Enable device and reserve I/O spaces. */
324 #ifdef CONFIG_COMEDI_PCI
326 if ((ret = comedi_pci_enable(pci_dev, PC236_DRIVER_NAME)) < 0) {
328 "comedi%d: error! cannot enable PCI device and request regions!\n",
332 devpriv->lcr_iobase = pci_resource_start(pci_dev, 1);
333 iobase = pci_resource_start(pci_dev, 2);
338 ret = pc236_request_region(dev->minor, iobase, PC236_IO_SIZE);
343 dev->iobase = iobase;
346 * Allocate the subdevice structures. alloc_subdevice() is a
347 * convenient macro defined in comedidev.h.
349 if ((ret = alloc_subdevices(dev, 2)) < 0) {
350 printk(KERN_ERR "comedi%d: error! out of memory!\n",
355 s = dev->subdevices + 0;
356 /* digital i/o subdevice (8255) */
357 if ((ret = subdev_8255_init(dev, s, NULL, iobase)) < 0) {
358 printk(KERN_ERR "comedi%d: error! out of memory!\n",
362 s = dev->subdevices + 1;
363 dev->read_subdev = s;
364 s->type = COMEDI_SUBD_UNUSED;
365 pc236_intr_disable(dev);
367 unsigned long flags = share_irq ? IRQF_SHARED : 0;
369 if (comedi_request_irq(irq, pc236_interrupt, flags,
370 PC236_DRIVER_NAME, dev) >= 0) {
372 s->type = COMEDI_SUBD_DI;
373 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
376 s->range_table = &range_digital;
377 s->insn_bits = pc236_intr_insn;
378 s->do_cmdtest = pc236_intr_cmdtest;
379 s->do_cmd = pc236_intr_cmd;
380 s->cancel = pc236_intr_cancel;
383 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
384 if (thisboard->bustype == isa_bustype) {
385 printk("(base %#lx) ", iobase);
387 #ifdef CONFIG_COMEDI_PCI
388 printk("(pci %s) ", pci_name(pci_dev));
392 printk("(irq %u%s) ", irq, (dev->irq ? "" : " UNAVAILABLE"));
397 printk("attached\n");
403 * _detach is called to deconfigure a device. It should deallocate
405 * This function is also called when _attach() fails, so it should be
406 * careful not to release resources that were not necessarily
407 * allocated by _attach(). dev->private and dev->subdevices are
408 * deallocated automatically by the core.
410 static int pc236_detach(struct comedi_device *dev)
412 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
415 pc236_intr_disable(dev);
418 comedi_free_irq(dev->irq, dev);
419 if (dev->subdevices) {
420 subdev_8255_cleanup(dev, dev->subdevices + 0);
423 #ifdef CONFIG_COMEDI_PCI
424 if (devpriv->pci_dev) {
426 comedi_pci_disable(devpriv->pci_dev);
428 pci_dev_put(devpriv->pci_dev);
433 release_region(dev->iobase, PC236_IO_SIZE);
437 if (dev->board_name) {
438 printk(KERN_INFO "comedi%d: %s removed\n",
439 dev->minor, dev->board_name);
445 * This function checks and requests an I/O region, reporting an error
446 * if there is a conflict.
448 static int pc236_request_region(unsigned minor, unsigned long from,
449 unsigned long extent)
451 if (!from || !request_region(from, extent, PC236_DRIVER_NAME)) {
452 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
453 minor, from, extent);
460 * This function is called to mark the interrupt as disabled (no command
461 * configured on subdevice 1) and to physically disable the interrupt
462 * (not possible on the PC36AT, except by removing the IRQ jumper!).
464 static void pc236_intr_disable(struct comedi_device *dev)
468 comedi_spin_lock_irqsave(&dev->spinlock, flags);
469 devpriv->enable_irq = 0;
470 #ifdef CONFIG_COMEDI_PCI
471 if (devpriv->lcr_iobase)
472 outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
474 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
478 * This function is called to mark the interrupt as enabled (a command
479 * configured on subdevice 1) and to physically enable the interrupt
480 * (not possible on the PC36AT, except by (re)connecting the IRQ jumper!).
482 static void pc236_intr_enable(struct comedi_device *dev)
486 comedi_spin_lock_irqsave(&dev->spinlock, flags);
487 devpriv->enable_irq = 1;
488 #ifdef CONFIG_COMEDI_PCI
489 if (devpriv->lcr_iobase)
490 outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
492 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
496 * This function is called when an interrupt occurs to check whether
497 * the interrupt has been marked as enabled and was generated by the
498 * board. If so, the function prepares the hardware for the next
500 * Returns 0 if the interrupt should be ignored.
502 static int pc236_intr_check(struct comedi_device *dev)
507 comedi_spin_lock_irqsave(&dev->spinlock, flags);
508 if (devpriv->enable_irq) {
510 #ifdef CONFIG_COMEDI_PCI
511 if (devpriv->lcr_iobase) {
512 if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
513 & PLX9052_INTCSR_LI1STAT_MASK)
514 == PLX9052_INTCSR_LI1STAT_INACTIVE) {
517 /* Clear interrupt and keep it enabled. */
518 outl(PCI236_INTR_ENABLE,
519 devpriv->lcr_iobase + PLX9052_INTCSR);
524 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
530 * Input from subdevice 1.
531 * Copied from the comedi_parport driver.
533 static int pc236_intr_insn(struct comedi_device *dev, struct comedi_subdevice *s,
534 struct comedi_insn *insn, unsigned int *data)
541 * Subdevice 1 command test.
542 * Copied from the comedi_parport driver.
544 static int pc236_intr_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
545 struct comedi_cmd *cmd)
552 tmp = cmd->start_src;
553 cmd->start_src &= TRIG_NOW;
554 if (!cmd->start_src || tmp != cmd->start_src)
557 tmp = cmd->scan_begin_src;
558 cmd->scan_begin_src &= TRIG_EXT;
559 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
562 tmp = cmd->convert_src;
563 cmd->convert_src &= TRIG_FOLLOW;
564 if (!cmd->convert_src || tmp != cmd->convert_src)
567 tmp = cmd->scan_end_src;
568 cmd->scan_end_src &= TRIG_COUNT;
569 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
573 cmd->stop_src &= TRIG_NONE;
574 if (!cmd->stop_src || tmp != cmd->stop_src)
580 /* step 2: ignored */
587 if (cmd->start_arg != 0) {
591 if (cmd->scan_begin_arg != 0) {
592 cmd->scan_begin_arg = 0;
595 if (cmd->convert_arg != 0) {
596 cmd->convert_arg = 0;
599 if (cmd->scan_end_arg != 1) {
600 cmd->scan_end_arg = 1;
603 if (cmd->stop_arg != 0) {
611 /* step 4: ignored */
620 * Subdevice 1 command.
622 static int pc236_intr_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
624 pc236_intr_enable(dev);
630 * Subdevice 1 cancel command.
632 static int pc236_intr_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
634 pc236_intr_disable(dev);
640 * Interrupt service routine.
641 * Based on the comedi_parport driver.
643 static irqreturn_t pc236_interrupt(int irq, void *d)
645 struct comedi_device *dev = d;
646 struct comedi_subdevice *s = dev->subdevices + 1;
649 handled = pc236_intr_check(dev);
650 if (dev->attached && handled) {
651 comedi_buf_put(s->async, 0);
652 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
653 comedi_event(dev, s);
655 return IRQ_RETVAL(handled);