1 #include <linux/module.h>
4 #include "../comedidev.h"
6 #include "amcc_s5933.h"
7 #include "addi_watchdog.h"
9 #include "addi-data/addi_common.h"
11 struct apci1564_private {
12 unsigned int amcc_iobase; /* base of AMCC I/O registers */
13 unsigned int mode1; /* riding-edge/high level channels */
14 unsigned int mode2; /* falling-edge/low level channels */
15 unsigned int ctrl; /* interrupt mode OR (edge) . AND (level) */
16 unsigned int do_int_type;
17 unsigned char timer_select_mode;
18 unsigned char mode_select_register;
19 struct task_struct *tsk_current;
22 #include "addi-data/hwdrv_apci1564.c"
24 static int apci1564_reset(struct comedi_device *dev)
26 struct apci1564_private *devpriv = dev->private;
28 devpriv->do_int_type = 0;
30 /* Disable the input interrupts and reset status register */
31 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
32 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
33 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
34 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
36 /* Reset the output channels and disable interrupts */
37 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_REG);
38 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_INT_CTRL_REG);
40 /* Reset the watchdog registers */
41 addi_watchdog_reset(devpriv->amcc_iobase + APCI1564_WDOG_REG);
43 /* Reset the timer registers */
44 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
45 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_RELOAD_REG);
47 /* Reset the counter registers */
48 outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER1));
49 outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER2));
50 outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER3));
51 outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(APCI1564_COUNTER4));
56 static irqreturn_t apci1564_interrupt(int irq, void *d)
58 struct comedi_device *dev = d;
59 struct apci1564_private *devpriv = dev->private;
60 struct comedi_subdevice *s = dev->read_subdev;
65 /* check interrupt is from this device */
66 if ((inl(devpriv->amcc_iobase + AMCC_OP_REG_INTCSR) &
67 INTCSR_INTR_ASSERTED) == 0)
70 status = inl(devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
71 if (status & APCI1564_DI_INT_ENABLE) {
72 /* disable the interrupt */
73 outl(status & APCI1564_DI_INT_DISABLE,
74 devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
76 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG)
78 comedi_buf_put(s, s->state);
79 s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
82 /* enable the interrupt */
83 outl(status, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
86 status = inl(devpriv->amcc_iobase + APCI1564_DO_IRQ_REG);
88 /* Check for Digital Output interrupt Type */
89 /* 1: VCC interrupt */
91 devpriv->do_int_type = inl(devpriv->amcc_iobase +
92 APCI1564_DO_INT_STATUS_REG) & 0x3;
93 /* Disable the Interrupt */
94 outl(0x0, devpriv->amcc_iobase + APCI1564_DO_INT_CTRL_REG);
96 /* Sends signal to user space */
97 send_sig(SIGIO, devpriv->tsk_current, 0);
100 status = inl(devpriv->amcc_iobase + APCI1564_TIMER_IRQ_REG);
102 /* Disable Timer Interrupt */
103 ctrl = inl(devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
104 outl(0x0, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
106 /* Send a signal to from kernel to user space */
107 send_sig(SIGIO, devpriv->tsk_current, 0);
109 /* Enable Timer Interrupt */
110 outl(ctrl, devpriv->amcc_iobase + APCI1564_TIMER_CTRL_REG);
113 for (chan = 0; chan < 4; chan++) {
114 status = inl(dev->iobase + APCI1564_TCW_IRQ_REG(chan));
116 /* Disable Counter Interrupt */
117 ctrl = inl(dev->iobase + APCI1564_TCW_CTRL_REG(chan));
118 outl(0x0, dev->iobase + APCI1564_TCW_CTRL_REG(chan));
120 /* Send a signal to from kernel to user space */
121 send_sig(SIGIO, devpriv->tsk_current, 0);
123 /* Enable Counter Interrupt */
124 outl(ctrl, dev->iobase + APCI1564_TCW_CTRL_REG(chan));
131 static int apci1564_di_insn_bits(struct comedi_device *dev,
132 struct comedi_subdevice *s,
133 struct comedi_insn *insn,
136 struct apci1564_private *devpriv = dev->private;
138 data[1] = inl(devpriv->amcc_iobase + APCI1564_DI_REG);
143 static int apci1564_do_insn_bits(struct comedi_device *dev,
144 struct comedi_subdevice *s,
145 struct comedi_insn *insn,
148 struct apci1564_private *devpriv = dev->private;
150 s->state = inl(devpriv->amcc_iobase + APCI1564_DO_REG);
152 if (comedi_dio_update_state(s, data))
153 outl(s->state, devpriv->amcc_iobase + APCI1564_DO_REG);
161 * Change-Of-State (COS) interrupt configuration
163 * Channels 0 to 15 are interruptible. These channels can be configured
164 * to generate interrupts based on AND/OR logic for the desired channels.
167 * - reacts to rising or falling edges
168 * - interrupt is generated when any enabled channel
169 * meet the desired interrupt condition
172 * - reacts to changes in level of the selected inputs
173 * - interrupt is generated when all enabled channels
174 * meet the desired interrupt condition
175 * - after an interrupt, a change in level must occur on
176 * the selected inputs to release the IRQ logic
178 * The COS interrupt must be configured before it can be enabled.
180 * data[0] : INSN_CONFIG_DIGITAL_TRIG
181 * data[1] : trigger number (= 0)
182 * data[2] : configuration operation:
183 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
184 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
185 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
186 * data[3] : left-shift for data[4] and data[5]
187 * data[4] : rising-edge/high level channels
188 * data[5] : falling-edge/low level channels
190 static int apci1564_cos_insn_config(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 struct comedi_insn *insn,
195 struct apci1564_private *devpriv = dev->private;
196 unsigned int shift, oldmask;
199 case INSN_CONFIG_DIGITAL_TRIG:
203 oldmask = (1U << shift) - 1;
205 case COMEDI_DIGITAL_TRIG_DISABLE:
209 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
210 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
211 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
212 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
214 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
215 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
216 APCI1564_DI_INT_OR)) {
217 /* switching to 'OR' mode */
218 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
220 /* wipe old channels */
224 /* preserve unspecified channels */
225 devpriv->mode1 &= oldmask;
226 devpriv->mode2 &= oldmask;
228 /* configure specified channels */
229 devpriv->mode1 |= data[4] << shift;
230 devpriv->mode2 |= data[5] << shift;
232 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
233 if (devpriv->ctrl != (APCI1564_DI_INT_ENABLE |
234 APCI1564_DI_INT_AND)) {
235 /* switching to 'AND' mode */
236 devpriv->ctrl = APCI1564_DI_INT_ENABLE |
238 /* wipe old channels */
242 /* preserve unspecified channels */
243 devpriv->mode1 &= oldmask;
244 devpriv->mode2 &= oldmask;
246 /* configure specified channels */
247 devpriv->mode1 |= data[4] << shift;
248 devpriv->mode2 |= data[5] << shift;
260 static int apci1564_cos_insn_bits(struct comedi_device *dev,
261 struct comedi_subdevice *s,
262 struct comedi_insn *insn,
270 static int apci1564_cos_cmdtest(struct comedi_device *dev,
271 struct comedi_subdevice *s,
272 struct comedi_cmd *cmd)
276 /* Step 1 : check if triggers are trivially valid */
278 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
279 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
280 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
281 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
282 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
287 /* Step 2a : make sure trigger sources are unique */
288 /* Step 2b : and mutually compatible */
293 /* Step 3: check if arguments are trivially valid */
295 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
296 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
297 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
298 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
299 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
304 /* step 4: ignored */
313 * Change-Of-State (COS) 'do_cmd' operation
315 * Enable the COS interrupt as configured by apci1564_cos_insn_config().
317 static int apci1564_cos_cmd(struct comedi_device *dev,
318 struct comedi_subdevice *s)
320 struct apci1564_private *devpriv = dev->private;
322 if (!devpriv->ctrl) {
323 dev_warn(dev->class_dev,
324 "Interrupts disabled due to mode configuration!\n");
328 outl(devpriv->mode1, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
329 outl(devpriv->mode2, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
330 outl(devpriv->ctrl, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
335 static int apci1564_cos_cancel(struct comedi_device *dev,
336 struct comedi_subdevice *s)
338 struct apci1564_private *devpriv = dev->private;
340 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_IRQ_REG);
341 inl(devpriv->amcc_iobase + APCI1564_DI_INT_STATUS_REG);
342 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE1_REG);
343 outl(0x0, devpriv->amcc_iobase + APCI1564_DI_INT_MODE2_REG);
348 static int apci1564_auto_attach(struct comedi_device *dev,
349 unsigned long context_unused)
351 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
352 struct apci1564_private *devpriv;
353 struct comedi_subdevice *s;
356 dev->board_name = dev->driver->driver_name;
358 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
362 ret = comedi_pci_enable(dev);
366 dev->iobase = pci_resource_start(pcidev, 1);
367 devpriv->amcc_iobase = pci_resource_start(pcidev, 0);
371 if (pcidev->irq > 0) {
372 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
373 dev->board_name, dev);
375 dev->irq = pcidev->irq;
378 ret = comedi_alloc_subdevices(dev, 4);
382 /* Allocate and Initialise DI Subdevice Structures */
383 s = &dev->subdevices[0];
384 s->type = COMEDI_SUBD_DI;
385 s->subdev_flags = SDF_READABLE;
388 s->len_chanlist = 32;
389 s->range_table = &range_digital;
390 s->insn_bits = apci1564_di_insn_bits;
392 /* Allocate and Initialise DO Subdevice Structures */
393 s = &dev->subdevices[1];
394 s->type = COMEDI_SUBD_DO;
395 s->subdev_flags = SDF_WRITEABLE;
397 s->maxdata = 0xffffffff;
398 s->len_chanlist = 32;
399 s->range_table = &range_digital;
400 s->insn_config = apci1564_do_config;
401 s->insn_bits = apci1564_do_insn_bits;
402 s->insn_read = apci1564_do_read;
404 /* Allocate and Initialise Timer Subdevice Structures */
405 s = &dev->subdevices[2];
406 s->type = COMEDI_SUBD_TIMER;
407 s->subdev_flags = SDF_WRITEABLE;
411 s->range_table = &range_digital;
412 s->insn_write = apci1564_timer_write;
413 s->insn_read = apci1564_timer_read;
414 s->insn_config = apci1564_timer_config;
416 /* Change-Of-State (COS) interrupt subdevice */
417 s = &dev->subdevices[3];
419 dev->read_subdev = s;
420 s->type = COMEDI_SUBD_DI;
421 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
424 s->range_table = &range_digital;
426 s->insn_config = apci1564_cos_insn_config;
427 s->insn_bits = apci1564_cos_insn_bits;
428 s->do_cmdtest = apci1564_cos_cmdtest;
429 s->do_cmd = apci1564_cos_cmd;
430 s->cancel = apci1564_cos_cancel;
432 s->type = COMEDI_SUBD_UNUSED;
438 static void apci1564_detach(struct comedi_device *dev)
440 struct apci1564_private *devpriv = dev->private;
446 free_irq(dev->irq, dev);
448 comedi_pci_disable(dev);
451 static struct comedi_driver apci1564_driver = {
452 .driver_name = "addi_apci_1564",
453 .module = THIS_MODULE,
454 .auto_attach = apci1564_auto_attach,
455 .detach = apci1564_detach,
458 static int apci1564_pci_probe(struct pci_dev *dev,
459 const struct pci_device_id *id)
461 return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
464 static const struct pci_device_id apci1564_pci_table[] = {
465 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
468 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
470 static struct pci_driver apci1564_pci_driver = {
471 .name = "addi_apci_1564",
472 .id_table = apci1564_pci_table,
473 .probe = apci1564_pci_probe,
474 .remove = comedi_pci_auto_unconfig,
476 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
478 MODULE_AUTHOR("Comedi http://www.comedi.org");
479 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
480 MODULE_LICENSE("GPL");