1 // SPDX-License-Identifier: GPL-2.0+
4 * Comedi driver for Access I/O Products 104-IIRO-16 board
5 * Copyright (C) 2006 C&C Technologies, Inc.
10 * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
11 * Author: Zachary Ware <zach.ware@cctechnol.com>
12 * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
13 * Status: experimental
15 * Configuration Options:
16 * [0] - I/O port base address
17 * [1] - IRQ (optional)
19 * The board supports interrupts on change of state of the digital inputs.
20 * The sample data returned by the async command indicates which inputs
21 * changed state and the current state of the inputs:
23 * Bit 23 - IRQ Enable (1) / Disable (0)
24 * Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
25 * Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
26 * Bit 15 - Digital input 15
28 * Bit 0 - Digital input 0
31 #include <linux/module.h>
32 #include <linux/interrupt.h>
34 #include "../comedidev.h"
36 #define AIO_IIRO_16_RELAY_0_7 0x00
37 #define AIO_IIRO_16_INPUT_0_7 0x01
38 #define AIO_IIRO_16_IRQ 0x02
39 #define AIO_IIRO_16_RELAY_8_15 0x04
40 #define AIO_IIRO_16_INPUT_8_15 0x05
41 #define AIO_IIRO_16_STATUS 0x07
42 #define AIO_IIRO_16_STATUS_IRQE BIT(7)
43 #define AIO_IIRO_16_STATUS_INPUT_8_15 BIT(1)
44 #define AIO_IIRO_16_STATUS_INPUT_0_7 BIT(0)
46 static unsigned int aio_iiro_16_read_inputs(struct comedi_device
*dev
)
50 val
= inb(dev
->iobase
+ AIO_IIRO_16_INPUT_0_7
);
51 val
|= inb(dev
->iobase
+ AIO_IIRO_16_INPUT_8_15
) << 8;
56 static irqreturn_t
aio_iiro_16_cos(int irq
, void *d
)
58 struct comedi_device
*dev
= d
;
59 struct comedi_subdevice
*s
= dev
->read_subdev
;
63 status
= inb(dev
->iobase
+ AIO_IIRO_16_STATUS
);
64 if (!(status
& AIO_IIRO_16_STATUS_IRQE
))
67 val
= aio_iiro_16_read_inputs(dev
);
68 val
|= (status
<< 16);
70 comedi_buf_write_samples(s
, &val
, 1);
71 comedi_handle_events(dev
, s
);
76 static void aio_iiro_enable_irq(struct comedi_device
*dev
, bool enable
)
79 inb(dev
->iobase
+ AIO_IIRO_16_IRQ
);
81 outb(0, dev
->iobase
+ AIO_IIRO_16_IRQ
);
84 static int aio_iiro_16_cos_cancel(struct comedi_device
*dev
,
85 struct comedi_subdevice
*s
)
87 aio_iiro_enable_irq(dev
, false);
92 static int aio_iiro_16_cos_cmd(struct comedi_device
*dev
,
93 struct comedi_subdevice
*s
)
95 aio_iiro_enable_irq(dev
, true);
100 static int aio_iiro_16_cos_cmdtest(struct comedi_device
*dev
,
101 struct comedi_subdevice
*s
,
102 struct comedi_cmd
*cmd
)
106 /* Step 1 : check if triggers are trivially valid */
108 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
109 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_EXT
);
110 err
|= comedi_check_trigger_src(&cmd
->convert_src
, TRIG_FOLLOW
);
111 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
112 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_NONE
);
117 /* Step 2a : make sure trigger sources are unique */
118 /* Step 2b : and mutually compatible */
120 /* Step 3: check if arguments are trivially valid */
122 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
123 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
124 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
125 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
127 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
132 /* Step 4: fix up any arguments */
134 /* Step 5: check channel list if it exists */
139 static int aio_iiro_16_do_insn_bits(struct comedi_device
*dev
,
140 struct comedi_subdevice
*s
,
141 struct comedi_insn
*insn
,
144 if (comedi_dio_update_state(s
, data
)) {
145 outb(s
->state
& 0xff, dev
->iobase
+ AIO_IIRO_16_RELAY_0_7
);
146 outb((s
->state
>> 8) & 0xff,
147 dev
->iobase
+ AIO_IIRO_16_RELAY_8_15
);
155 static int aio_iiro_16_di_insn_bits(struct comedi_device
*dev
,
156 struct comedi_subdevice
*s
,
157 struct comedi_insn
*insn
,
160 data
[1] = aio_iiro_16_read_inputs(dev
);
165 static int aio_iiro_16_attach(struct comedi_device
*dev
,
166 struct comedi_devconfig
*it
)
168 struct comedi_subdevice
*s
;
171 ret
= comedi_request_region(dev
, it
->options
[0], 0x8);
175 aio_iiro_enable_irq(dev
, false);
178 * Digital input change of state interrupts are optionally supported
179 * using IRQ 2-7, 10-12, 14, or 15.
181 if ((1 << it
->options
[1]) & 0xdcfc) {
182 ret
= request_irq(it
->options
[1], aio_iiro_16_cos
, 0,
183 dev
->board_name
, dev
);
185 dev
->irq
= it
->options
[1];
188 ret
= comedi_alloc_subdevices(dev
, 2);
192 /* Digital Output subdevice */
193 s
= &dev
->subdevices
[0];
194 s
->type
= COMEDI_SUBD_DO
;
195 s
->subdev_flags
= SDF_WRITABLE
;
198 s
->range_table
= &range_digital
;
199 s
->insn_bits
= aio_iiro_16_do_insn_bits
;
201 /* get the initial state of the relays */
202 s
->state
= inb(dev
->iobase
+ AIO_IIRO_16_RELAY_0_7
) |
203 (inb(dev
->iobase
+ AIO_IIRO_16_RELAY_8_15
) << 8);
205 /* Digital Input subdevice */
206 s
= &dev
->subdevices
[1];
207 s
->type
= COMEDI_SUBD_DI
;
208 s
->subdev_flags
= SDF_READABLE
;
211 s
->range_table
= &range_digital
;
212 s
->insn_bits
= aio_iiro_16_di_insn_bits
;
214 dev
->read_subdev
= s
;
215 s
->subdev_flags
|= SDF_CMD_READ
| SDF_LSAMPL
;
217 s
->do_cmdtest
= aio_iiro_16_cos_cmdtest
;
218 s
->do_cmd
= aio_iiro_16_cos_cmd
;
219 s
->cancel
= aio_iiro_16_cos_cancel
;
225 static struct comedi_driver aio_iiro_16_driver
= {
226 .driver_name
= "aio_iiro_16",
227 .module
= THIS_MODULE
,
228 .attach
= aio_iiro_16_attach
,
229 .detach
= comedi_legacy_detach
,
231 module_comedi_driver(aio_iiro_16_driver
);
233 MODULE_AUTHOR("Comedi http://www.comedi.org");
234 MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
235 MODULE_LICENSE("GPL");