3 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
8 * Tel: +19(0)7223/9493-0
9 * Fax: +49(0)7223/9493-92
10 * http://www.addi-data.com
13 * This program is free software; you can redistribute it and/or modify it under
14 * the terms of the GNU General Public License as published by the Free Software
15 * Foundation; either version 2 of the License, or (at your option) any later
18 * This program is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
24 #include <linux/module.h>
25 #include <linux/interrupt.h>
26 #include <linux/sched.h>
28 #include "../comedi_pci.h"
30 #include "addi_watchdog.h"
35 * PLD Revision 1.0 I/O Mapping
37 * 0x04 - 0x18 Timer 12-Bit
39 * PLD Revision 2.x I/O Mapping
41 * 0x04 - 0x14 Digital Input
42 * 0x18 - 0x25 Digital Output
43 * 0x28 - 0x44 Watchdog 8-Bit
44 * 0x48 - 0x64 Timer 12-Bit
46 #define APCI1564_EEPROM_REG 0x00
47 #define APCI1564_EEPROM_VCC_STATUS (1 << 8)
48 #define APCI1564_EEPROM_TO_REV(x) (((x) >> 4) & 0xf)
49 #define APCI1564_EEPROM_DI (1 << 3)
50 #define APCI1564_EEPROM_DO (1 << 2)
51 #define APCI1564_EEPROM_CS (1 << 1)
52 #define APCI1564_EEPROM_CLK (1 << 0)
53 #define APCI1564_REV1_TIMER_IOBASE 0x04
54 #define APCI1564_REV2_MAIN_IOBASE 0x04
55 #define APCI1564_REV2_TIMER_IOBASE 0x48
60 * PLD Revision 1.0 I/O Mapping
61 * 0x00 - 0x10 Digital Input
62 * 0x14 - 0x20 Digital Output
63 * 0x24 - 0x3c Watchdog 8-Bit
65 * PLD Revision 2.x I/O Mapping
70 #define APCI1564_REV1_MAIN_IOBASE 0x00
73 * dev->iobase Register Map
74 * PLD Revision 1.0 - PCI BAR 1 + 0x00
75 * PLD Revision 2.x - PCI BAR 0 + 0x04
77 #define APCI1564_DI_REG 0x00
78 #define APCI1564_DI_INT_MODE1_REG 0x04
79 #define APCI1564_DI_INT_MODE2_REG 0x08
80 #define APCI1564_DI_INT_STATUS_REG 0x0c
81 #define APCI1564_DI_IRQ_REG 0x10
82 #define APCI1564_DO_REG 0x14
83 #define APCI1564_DO_INT_CTRL_REG 0x18
84 #define APCI1564_DO_INT_STATUS_REG 0x1c
85 #define APCI1564_DO_IRQ_REG 0x20
86 #define APCI1564_WDOG_REG 0x24
87 #define APCI1564_WDOG_RELOAD_REG 0x28
88 #define APCI1564_WDOG_TIMEBASE_REG 0x2c
89 #define APCI1564_WDOG_CTRL_REG 0x30
90 #define APCI1564_WDOG_STATUS_REG 0x34
91 #define APCI1564_WDOG_IRQ_REG 0x38
92 #define APCI1564_WDOG_WARN_TIMEVAL_REG 0x3c
93 #define APCI1564_WDOG_WARN_TIMEBASE_REG 0x40
96 * devpriv->timer Register Map (see addi_tcw.h for register/bit defines)
97 * PLD Revision 1.0 - PCI BAR 0 + 0x04
98 * PLD Revision 2.x - PCI BAR 0 + 0x48
102 * devpriv->counters Register Map (see addi_tcw.h for register/bit defines)
103 * PLD Revision 2.x - PCI BAR 1 + 0x00
105 #define APCI1564_COUNTER(x) ((x) * 0x20)
107 struct apci1564_private {
108 unsigned long eeprom; /* base address of EEPROM register */
109 unsigned long timer; /* base address of 12-bit timer */
110 unsigned long counters; /* base address of 32-bit counters */
111 unsigned int mode1; /* riding-edge/high level channels */
112 unsigned int mode2; /* falling-edge/low level channels */
113 unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
114 struct task_struct *tsk_current;
117 #include "addi-data/hwdrv_apci1564.c"
119 static int apci1564_reset(struct comedi_device *dev)
121 struct apci1564_private *devpriv = dev->private;
123 /* Disable the input interrupts and reset status register */
124 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
125 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
126 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
127 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
129 /* Reset the output channels and disable interrupts */
130 outl(0x0, dev->iobase + APCI1564_DO_REG);
131 outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
133 /* Reset the watchdog registers */
134 addi_watchdog_reset(dev->iobase + APCI1564_WDOG_REG);
136 /* Reset the timer registers */
137 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
138 outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
140 if (devpriv->counters) {
141 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
143 /* Reset the counter registers */
144 outl(0x0, iobase + APCI1564_COUNTER(0));
145 outl(0x0, iobase + APCI1564_COUNTER(1));
146 outl(0x0, iobase + APCI1564_COUNTER(2));
152 static irqreturn_t apci1564_interrupt(int irq, void *d)
154 struct comedi_device *dev = d;
155 struct apci1564_private *devpriv = dev->private;
156 struct comedi_subdevice *s = dev->read_subdev;
161 status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
162 if (status & APCI1564_DI_INT_ENABLE) {
163 /* disable the interrupt */
164 outl(status & APCI1564_DI_INT_DISABLE,
165 dev->iobase + APCI1564_DI_IRQ_REG);
167 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG) &
169 comedi_buf_write_samples(s, &s->state, 1);
170 comedi_handle_events(dev, s);
172 /* enable the interrupt */
173 outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
176 status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
178 /* Disable Timer Interrupt */
179 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
180 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
182 /* Send a signal to from kernel to user space */
183 send_sig(SIGIO, devpriv->tsk_current, 0);
185 /* Enable Timer Interrupt */
186 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
189 if (devpriv->counters) {
190 for (chan = 0; chan < 4; chan++) {
191 unsigned long iobase;
193 iobase = devpriv->counters + APCI1564_COUNTER(chan);
195 status = inl(iobase + ADDI_TCW_IRQ_REG);
197 /* Disable Counter Interrupt */
198 ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
199 outl(0x0, iobase + ADDI_TCW_CTRL_REG);
201 /* Send a signal to from kernel to user space */
202 send_sig(SIGIO, devpriv->tsk_current, 0);
204 /* Enable Counter Interrupt */
205 outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
213 static int apci1564_di_insn_bits(struct comedi_device *dev,
214 struct comedi_subdevice *s,
215 struct comedi_insn *insn,
218 data[1] = inl(dev->iobase + APCI1564_DI_REG);
223 static int apci1564_do_insn_bits(struct comedi_device *dev,
224 struct comedi_subdevice *s,
225 struct comedi_insn *insn,
228 s->state = inl(dev->iobase + APCI1564_DO_REG);
230 if (comedi_dio_update_state(s, data))
231 outl(s->state, dev->iobase + APCI1564_DO_REG);
238 static int apci1564_diag_insn_bits(struct comedi_device *dev,
239 struct comedi_subdevice *s,
240 struct comedi_insn *insn,
243 data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
249 * Change-Of-State (COS) interrupt configuration
251 * Channels 0 to 15 are interruptible. These channels can be configured
252 * to generate interrupts based on AND/OR logic for the desired channels.
255 * - reacts to rising or falling edges
256 * - interrupt is generated when any enabled channel
257 * meet the desired interrupt condition
260 * - reacts to changes in level of the selected inputs
261 * - interrupt is generated when all enabled channels
262 * meet the desired interrupt condition
263 * - after an interrupt, a change in level must occur on
264 * the selected inputs to release the IRQ logic
266 * The COS interrupt must be configured before it can be enabled.
268 * data[0] : INSN_CONFIG_DIGITAL_TRIG
269 * data[1] : trigger number (= 0)
270 * data[2] : configuration operation:
271 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
272 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
273 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
274 * data[3] : left-shift for data[4] and data[5]
275 * data[4] : rising-edge/high level channels
276 * data[5] : falling-edge/low level channels
278 static int apci1564_cos_insn_config(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 struct comedi_insn *insn,
283 struct apci1564_private *devpriv = dev->private;
284 unsigned int shift, oldmask;
287 case INSN_CONFIG_DIGITAL_TRIG:
291 oldmask = (1U << shift) - 1;
293 case COMEDI_DIGITAL_TRIG_DISABLE:
297 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
298 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
299 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
300 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
302 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
303 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
304 APCI1564_DI_INT_OR)) {
305 /* switching to 'OR' mode */
306 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
308 /* wipe old channels */
312 /* preserve unspecified channels */
313 devpriv->mode1 &= oldmask;
314 devpriv->mode2 &= oldmask;
316 /* configure specified channels */
317 devpriv->mode1 |= data[4] << shift;
318 devpriv->mode2 |= data[5] << shift;
320 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
321 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
322 APCI1564_DI_INT_AND)) {
323 /* switching to 'AND' mode */
324 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
326 /* wipe old channels */
330 /* preserve unspecified channels */
331 devpriv->mode1 &= oldmask;
332 devpriv->mode2 &= oldmask;
334 /* configure specified channels */
335 devpriv->mode1 |= data[4] << shift;
336 devpriv->mode2 |= data[5] << shift;
348 static int apci1564_cos_insn_bits(struct comedi_device *dev,
349 struct comedi_subdevice *s,
350 struct comedi_insn *insn,
358 static int apci1564_cos_cmdtest(struct comedi_device *dev,
359 struct comedi_subdevice *s,
360 struct comedi_cmd *cmd)
364 /* Step 1 : check if triggers are trivially valid */
366 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
367 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
368 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
369 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
370 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
375 /* Step 2a : make sure trigger sources are unique */
376 /* Step 2b : and mutually compatible */
378 /* Step 3: check if arguments are trivially valid */
380 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
381 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
382 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
383 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
385 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
390 /* Step 4: fix up any arguments */
392 /* Step 5: check channel list if it exists */
398 * Change-Of-State (COS) 'do_cmd' operation
400 * Enable the COS interrupt as configured by apci1564_cos_insn_config().
402 static int apci1564_cos_cmd(struct comedi_device *dev,
403 struct comedi_subdevice *s)
405 struct apci1564_private *devpriv = dev->private;
407 if (!devpriv->ctrl) {
408 dev_warn(dev->class_dev,
409 "Interrupts disabled due to mode configuration!\n");
413 outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
414 outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
415 outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
420 static int apci1564_cos_cancel(struct comedi_device *dev,
421 struct comedi_subdevice *s)
423 outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
424 inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
425 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
426 outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
431 static int apci1564_auto_attach(struct comedi_device *dev,
432 unsigned long context_unused)
434 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
435 struct apci1564_private *devpriv;
436 struct comedi_subdevice *s;
440 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
444 ret = comedi_pci_enable(dev);
448 /* read the EEPROM register and check the I/O map revision */
449 devpriv->eeprom = pci_resource_start(pcidev, 0);
450 val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
451 if (APCI1564_EEPROM_TO_REV(val) == 0) {
452 /* PLD Revision 1.0 I/O Mapping */
453 dev->iobase = pci_resource_start(pcidev, 1) +
454 APCI1564_REV1_MAIN_IOBASE;
455 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
457 /* PLD Revision 2.x I/O Mapping */
458 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
459 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
460 devpriv->counters = pci_resource_start(pcidev, 1);
465 if (pcidev->irq > 0) {
466 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
467 dev->board_name, dev);
469 dev->irq = pcidev->irq;
472 ret = comedi_alloc_subdevices(dev, 7);
476 /* Allocate and Initialise DI Subdevice Structures */
477 s = &dev->subdevices[0];
478 s->type = COMEDI_SUBD_DI;
479 s->subdev_flags = SDF_READABLE;
482 s->range_table = &range_digital;
483 s->insn_bits = apci1564_di_insn_bits;
485 /* Allocate and Initialise DO Subdevice Structures */
486 s = &dev->subdevices[1];
487 s->type = COMEDI_SUBD_DO;
488 s->subdev_flags = SDF_WRITABLE;
491 s->range_table = &range_digital;
492 s->insn_bits = apci1564_do_insn_bits;
494 /* Change-Of-State (COS) interrupt subdevice */
495 s = &dev->subdevices[2];
497 dev->read_subdev = s;
498 s->type = COMEDI_SUBD_DI;
499 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
502 s->range_table = &range_digital;
504 s->insn_config = apci1564_cos_insn_config;
505 s->insn_bits = apci1564_cos_insn_bits;
506 s->do_cmdtest = apci1564_cos_cmdtest;
507 s->do_cmd = apci1564_cos_cmd;
508 s->cancel = apci1564_cos_cancel;
510 s->type = COMEDI_SUBD_UNUSED;
513 /* Timer subdevice */
514 s = &dev->subdevices[3];
515 s->type = COMEDI_SUBD_TIMER;
516 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
519 s->range_table = &range_digital;
520 s->insn_config = apci1564_timer_insn_config;
521 s->insn_write = apci1564_timer_insn_write;
522 s->insn_read = apci1564_timer_insn_read;
524 /* Counter subdevice */
525 s = &dev->subdevices[4];
526 if (devpriv->counters) {
527 s->type = COMEDI_SUBD_COUNTER;
528 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
530 s->maxdata = 0xffffffff;
531 s->range_table = &range_digital;
532 s->insn_config = apci1564_counter_insn_config;
533 s->insn_write = apci1564_counter_insn_write;
534 s->insn_read = apci1564_counter_insn_read;
536 s->type = COMEDI_SUBD_UNUSED;
539 /* Initialize the watchdog subdevice */
540 s = &dev->subdevices[5];
541 ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_REG);
545 /* Initialize the diagnostic status subdevice */
546 s = &dev->subdevices[6];
547 s->type = COMEDI_SUBD_DI;
548 s->subdev_flags = SDF_READABLE;
551 s->range_table = &range_digital;
552 s->insn_bits = apci1564_diag_insn_bits;
557 static void apci1564_detach(struct comedi_device *dev)
561 comedi_pci_detach(dev);
564 static struct comedi_driver apci1564_driver = {
565 .driver_name = "addi_apci_1564",
566 .module = THIS_MODULE,
567 .auto_attach = apci1564_auto_attach,
568 .detach = apci1564_detach,
571 static int apci1564_pci_probe(struct pci_dev *dev,
572 const struct pci_device_id *id)
574 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
577 static const struct pci_device_id apci1564_pci_table[] = {
578 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
581 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
583 static struct pci_driver apci1564_pci_driver = {
584 .name = "addi_apci_1564",
585 .id_table = apci1564_pci_table,
586 .probe = apci1564_pci_probe,
587 .remove = comedi_pci_auto_unconfig,
589 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
591 MODULE_AUTHOR("Comedi http://www.comedi.org");
592 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
593 MODULE_LICENSE("GPL");