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/pci.h>
26 #include <linux/interrupt.h>
27 #include <linux/sched.h>
29 #include "../comedidev.h"
30 #include "comedi_fc.h"
31 #include "amcc_s5933.h"
32 #include "addi_watchdog.h"
34 struct apci1564_private
{
35 unsigned int amcc_iobase
; /* base of AMCC I/O registers */
36 unsigned int mode1
; /* riding-edge/high level channels */
37 unsigned int mode2
; /* falling-edge/low level channels */
38 unsigned int ctrl
; /* interrupt mode OR (edge) . AND (level) */
39 unsigned char timer_select_mode
;
40 struct task_struct
*tsk_current
;
43 #include "addi-data/hwdrv_apci1564.c"
45 static int apci1564_reset(struct comedi_device
*dev
)
47 struct apci1564_private
*devpriv
= dev
->private;
49 /* Disable the input interrupts and reset status register */
50 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
51 inl(devpriv
->amcc_iobase
+ APCI1564_DI_INT_STATUS_REG
);
52 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE1_REG
);
53 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE2_REG
);
55 /* Reset the output channels and disable interrupts */
56 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DO_REG
);
57 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DO_INT_CTRL_REG
);
59 /* Reset the watchdog registers */
60 addi_watchdog_reset(devpriv
->amcc_iobase
+ APCI1564_WDOG_REG
);
62 /* Reset the timer registers */
63 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_TIMER_CTRL_REG
);
64 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_TIMER_RELOAD_REG
);
66 /* Reset the counter registers */
67 outl(0x0, dev
->iobase
+ APCI1564_COUNTER_CTRL_REG(0));
68 outl(0x0, dev
->iobase
+ APCI1564_COUNTER_CTRL_REG(1));
69 outl(0x0, dev
->iobase
+ APCI1564_COUNTER_CTRL_REG(2));
74 static irqreturn_t
apci1564_interrupt(int irq
, void *d
)
76 struct comedi_device
*dev
= d
;
77 struct apci1564_private
*devpriv
= dev
->private;
78 struct comedi_subdevice
*s
= dev
->read_subdev
;
83 /* check interrupt is from this device */
84 if ((inl(devpriv
->amcc_iobase
+ AMCC_OP_REG_INTCSR
) &
85 INTCSR_INTR_ASSERTED
) == 0)
88 status
= inl(devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
89 if (status
& APCI1564_DI_INT_ENABLE
) {
90 /* disable the interrupt */
91 outl(status
& APCI1564_DI_INT_DISABLE
,
92 devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
94 s
->state
= inl(dev
->iobase
+ APCI1564_DI_INT_STATUS_REG
)
96 comedi_buf_write_samples(s
, &s
->state
, 1);
97 comedi_handle_events(dev
, s
);
99 /* enable the interrupt */
100 outl(status
, devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
103 status
= inl(devpriv
->amcc_iobase
+ APCI1564_TIMER_IRQ_REG
);
105 /* Disable Timer Interrupt */
106 ctrl
= inl(devpriv
->amcc_iobase
+ APCI1564_TIMER_CTRL_REG
);
107 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_TIMER_CTRL_REG
);
109 /* Send a signal to from kernel to user space */
110 send_sig(SIGIO
, devpriv
->tsk_current
, 0);
112 /* Enable Timer Interrupt */
113 outl(ctrl
, devpriv
->amcc_iobase
+ APCI1564_TIMER_CTRL_REG
);
116 for (chan
= 0; chan
< 4; chan
++) {
117 status
= inl(dev
->iobase
+ APCI1564_COUNTER_IRQ_REG(chan
));
119 /* Disable Counter Interrupt */
120 ctrl
= inl(dev
->iobase
+
121 APCI1564_COUNTER_CTRL_REG(chan
));
122 outl(0x0, dev
->iobase
+
123 APCI1564_COUNTER_CTRL_REG(chan
));
125 /* Send a signal to from kernel to user space */
126 send_sig(SIGIO
, devpriv
->tsk_current
, 0);
128 /* Enable Counter Interrupt */
129 outl(ctrl
, dev
->iobase
+
130 APCI1564_COUNTER_CTRL_REG(chan
));
137 static int apci1564_di_insn_bits(struct comedi_device
*dev
,
138 struct comedi_subdevice
*s
,
139 struct comedi_insn
*insn
,
142 struct apci1564_private
*devpriv
= dev
->private;
144 data
[1] = inl(devpriv
->amcc_iobase
+ APCI1564_DI_REG
);
149 static int apci1564_do_insn_bits(struct comedi_device
*dev
,
150 struct comedi_subdevice
*s
,
151 struct comedi_insn
*insn
,
154 struct apci1564_private
*devpriv
= dev
->private;
156 s
->state
= inl(devpriv
->amcc_iobase
+ APCI1564_DO_REG
);
158 if (comedi_dio_update_state(s
, data
))
159 outl(s
->state
, devpriv
->amcc_iobase
+ APCI1564_DO_REG
);
166 static int apci1564_diag_insn_bits(struct comedi_device
*dev
,
167 struct comedi_subdevice
*s
,
168 struct comedi_insn
*insn
,
171 struct apci1564_private
*devpriv
= dev
->private;
173 data
[1] = inl(devpriv
->amcc_iobase
+ APCI1564_DO_INT_STATUS_REG
) & 3;
179 * Change-Of-State (COS) interrupt configuration
181 * Channels 0 to 15 are interruptible. These channels can be configured
182 * to generate interrupts based on AND/OR logic for the desired channels.
185 * - reacts to rising or falling edges
186 * - interrupt is generated when any enabled channel
187 * meet the desired interrupt condition
190 * - reacts to changes in level of the selected inputs
191 * - interrupt is generated when all enabled channels
192 * meet the desired interrupt condition
193 * - after an interrupt, a change in level must occur on
194 * the selected inputs to release the IRQ logic
196 * The COS interrupt must be configured before it can be enabled.
198 * data[0] : INSN_CONFIG_DIGITAL_TRIG
199 * data[1] : trigger number (= 0)
200 * data[2] : configuration operation:
201 * COMEDI_DIGITAL_TRIG_DISABLE = no interrupts
202 * COMEDI_DIGITAL_TRIG_ENABLE_EDGES = OR (edge) interrupts
203 * COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = AND (level) interrupts
204 * data[3] : left-shift for data[4] and data[5]
205 * data[4] : rising-edge/high level channels
206 * data[5] : falling-edge/low level channels
208 static int apci1564_cos_insn_config(struct comedi_device
*dev
,
209 struct comedi_subdevice
*s
,
210 struct comedi_insn
*insn
,
213 struct apci1564_private
*devpriv
= dev
->private;
214 unsigned int shift
, oldmask
;
217 case INSN_CONFIG_DIGITAL_TRIG
:
221 oldmask
= (1U << shift
) - 1;
223 case COMEDI_DIGITAL_TRIG_DISABLE
:
227 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
228 inl(devpriv
->amcc_iobase
+ APCI1564_DI_INT_STATUS_REG
);
229 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE1_REG
);
230 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE2_REG
);
232 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES
:
233 if (devpriv
->ctrl
!= (APCI1564_DI_INT_ENABLE
|
234 APCI1564_DI_INT_OR
)) {
235 /* switching to 'OR' 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
;
250 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS
:
251 if (devpriv
->ctrl
!= (APCI1564_DI_INT_ENABLE
|
252 APCI1564_DI_INT_AND
)) {
253 /* switching to 'AND' mode */
254 devpriv
->ctrl
= APCI1564_DI_INT_ENABLE
|
256 /* wipe old channels */
260 /* preserve unspecified channels */
261 devpriv
->mode1
&= oldmask
;
262 devpriv
->mode2
&= oldmask
;
264 /* configure specified channels */
265 devpriv
->mode1
|= data
[4] << shift
;
266 devpriv
->mode2
|= data
[5] << shift
;
278 static int apci1564_cos_insn_bits(struct comedi_device
*dev
,
279 struct comedi_subdevice
*s
,
280 struct comedi_insn
*insn
,
288 static int apci1564_cos_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 */
308 /* Step 3: check if arguments are trivially valid */
310 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
311 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
312 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
313 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
314 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
319 /* Step 4: fix up any arguments */
321 /* Step 5: check channel list if it exists */
327 * Change-Of-State (COS) 'do_cmd' operation
329 * Enable the COS interrupt as configured by apci1564_cos_insn_config().
331 static int apci1564_cos_cmd(struct comedi_device
*dev
,
332 struct comedi_subdevice
*s
)
334 struct apci1564_private
*devpriv
= dev
->private;
336 if (!devpriv
->ctrl
) {
337 dev_warn(dev
->class_dev
,
338 "Interrupts disabled due to mode configuration!\n");
342 outl(devpriv
->mode1
, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE1_REG
);
343 outl(devpriv
->mode2
, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE2_REG
);
344 outl(devpriv
->ctrl
, devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
349 static int apci1564_cos_cancel(struct comedi_device
*dev
,
350 struct comedi_subdevice
*s
)
352 struct apci1564_private
*devpriv
= dev
->private;
354 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_IRQ_REG
);
355 inl(devpriv
->amcc_iobase
+ APCI1564_DI_INT_STATUS_REG
);
356 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE1_REG
);
357 outl(0x0, devpriv
->amcc_iobase
+ APCI1564_DI_INT_MODE2_REG
);
362 static int apci1564_auto_attach(struct comedi_device
*dev
,
363 unsigned long context_unused
)
365 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
366 struct apci1564_private
*devpriv
;
367 struct comedi_subdevice
*s
;
370 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
374 ret
= comedi_pci_enable(dev
);
378 dev
->iobase
= pci_resource_start(pcidev
, 1);
379 devpriv
->amcc_iobase
= pci_resource_start(pcidev
, 0);
383 if (pcidev
->irq
> 0) {
384 ret
= request_irq(pcidev
->irq
, apci1564_interrupt
, IRQF_SHARED
,
385 dev
->board_name
, dev
);
387 dev
->irq
= pcidev
->irq
;
390 ret
= comedi_alloc_subdevices(dev
, 6);
394 /* Allocate and Initialise DI Subdevice Structures */
395 s
= &dev
->subdevices
[0];
396 s
->type
= COMEDI_SUBD_DI
;
397 s
->subdev_flags
= SDF_READABLE
;
400 s
->range_table
= &range_digital
;
401 s
->insn_bits
= apci1564_di_insn_bits
;
403 /* Allocate and Initialise DO Subdevice Structures */
404 s
= &dev
->subdevices
[1];
405 s
->type
= COMEDI_SUBD_DO
;
406 s
->subdev_flags
= SDF_WRITABLE
;
409 s
->range_table
= &range_digital
;
410 s
->insn_bits
= apci1564_do_insn_bits
;
412 /* Change-Of-State (COS) interrupt subdevice */
413 s
= &dev
->subdevices
[2];
415 dev
->read_subdev
= s
;
416 s
->type
= COMEDI_SUBD_DI
;
417 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
420 s
->range_table
= &range_digital
;
422 s
->insn_config
= apci1564_cos_insn_config
;
423 s
->insn_bits
= apci1564_cos_insn_bits
;
424 s
->do_cmdtest
= apci1564_cos_cmdtest
;
425 s
->do_cmd
= apci1564_cos_cmd
;
426 s
->cancel
= apci1564_cos_cancel
;
428 s
->type
= COMEDI_SUBD_UNUSED
;
431 /* Allocate and Initialise Timer Subdevice Structures */
432 s
= &dev
->subdevices
[3];
433 s
->type
= COMEDI_SUBD_TIMER
;
434 s
->subdev_flags
= SDF_WRITABLE
;
437 s
->range_table
= &range_digital
;
438 s
->insn_write
= apci1564_timer_write
;
439 s
->insn_read
= apci1564_timer_read
;
440 s
->insn_config
= apci1564_timer_config
;
442 /* Initialize the watchdog subdevice */
443 s
= &dev
->subdevices
[4];
444 ret
= addi_watchdog_init(s
, devpriv
->amcc_iobase
+ APCI1564_WDOG_REG
);
448 /* Initialize the diagnostic status subdevice */
449 s
= &dev
->subdevices
[5];
450 s
->type
= COMEDI_SUBD_DI
;
451 s
->subdev_flags
= SDF_READABLE
;
454 s
->range_table
= &range_digital
;
455 s
->insn_bits
= apci1564_diag_insn_bits
;
460 static void apci1564_detach(struct comedi_device
*dev
)
464 comedi_pci_detach(dev
);
467 static struct comedi_driver apci1564_driver
= {
468 .driver_name
= "addi_apci_1564",
469 .module
= THIS_MODULE
,
470 .auto_attach
= apci1564_auto_attach
,
471 .detach
= apci1564_detach
,
474 static int apci1564_pci_probe(struct pci_dev
*dev
,
475 const struct pci_device_id
*id
)
477 return comedi_pci_auto_config(dev
, &apci1564_driver
, id
->driver_data
);
480 static const struct pci_device_id apci1564_pci_table
[] = {
481 { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA
, 0x1006) },
484 MODULE_DEVICE_TABLE(pci
, apci1564_pci_table
);
486 static struct pci_driver apci1564_pci_driver
= {
487 .name
= "addi_apci_1564",
488 .id_table
= apci1564_pci_table
,
489 .probe
= apci1564_pci_probe
,
490 .remove
= comedi_pci_auto_unconfig
,
492 module_comedi_pci_driver(apci1564_driver
, apci1564_pci_driver
);
494 MODULE_AUTHOR("Comedi http://www.comedi.org");
495 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
496 MODULE_LICENSE("GPL");