Merge remote-tracking branch 'asoc/fix/max98357a' into asoc-linus
[linux-2.6-block.git] / drivers / staging / comedi / drivers / aio_iiro_16.c
CommitLineData
56af390d 1/*
9cdb432c
HS
2 * aio_iiro_16.c
3 * Comedi driver for Access I/O Products 104-IIRO-16 board
4 * Copyright (C) 2006 C&C Technologies, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
56af390d
ZW
16
17/*
9cdb432c
HS
18 * Driver: aio_iiro_16
19 * Description: Access I/O Products PC/104 Isolated Input/Relay Output Board
20 * Author: Zachary Ware <zach.ware@cctechnol.com>
21 * Devices: [Access I/O] 104-IIRO-16 (aio_iiro_16)
22 * Status: experimental
23 *
24 * Configuration Options:
25 * [0] - I/O port base address
ad7a370c
HS
26 * [1] - IRQ (optional)
27 *
28 * The board supports interrupts on change of state of the digital inputs.
29 * The sample data returned by the async command indicates which inputs
53c0bee8 30 * changed state and the current state of the inputs:
ad7a370c 31 *
53c0bee8
HS
32 * Bit 23 - IRQ Enable (1) / Disable (0)
33 * Bit 17 - Input 8-15 Changed State (1 = Changed, 0 = No Change)
34 * Bit 16 - Input 0-7 Changed State (1 = Changed, 0 = No Change)
35 * Bit 15 - Digital input 15
36 * ...
37 * Bit 0 - Digital input 0
9cdb432c 38 */
56af390d 39
ce157f80 40#include <linux/module.h>
ad7a370c
HS
41#include <linux/interrupt.h>
42
56af390d 43#include "../comedidev.h"
56af390d 44
ad7a370c
HS
45#include "comedi_fc.h"
46
47#define AIO_IIRO_16_RELAY_0_7 0x00
48#define AIO_IIRO_16_INPUT_0_7 0x01
49#define AIO_IIRO_16_IRQ 0x02
50#define AIO_IIRO_16_RELAY_8_15 0x04
51#define AIO_IIRO_16_INPUT_8_15 0x05
52#define AIO_IIRO_16_STATUS 0x07
53#define AIO_IIRO_16_STATUS_IRQE BIT(7)
54#define AIO_IIRO_16_STATUS_INPUT_8_15 BIT(1)
55#define AIO_IIRO_16_STATUS_INPUT_0_7 BIT(0)
56
53c0bee8
HS
57static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
58{
59 unsigned int val;
60
61 val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
62 val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
63
64 return val;
65}
66
ad7a370c
HS
67static irqreturn_t aio_iiro_16_cos(int irq, void *d)
68{
69 struct comedi_device *dev = d;
70 struct comedi_subdevice *s = dev->read_subdev;
71 unsigned int status;
53c0bee8 72 unsigned int val;
ad7a370c
HS
73
74 status = inb(dev->iobase + AIO_IIRO_16_STATUS);
75 if (!(status & AIO_IIRO_16_STATUS_IRQE))
76 return IRQ_NONE;
77
53c0bee8
HS
78 val = aio_iiro_16_read_inputs(dev);
79 val |= (status << 16);
80
81 comedi_buf_write_samples(s, &val, 1);
ad7a370c
HS
82 comedi_handle_events(dev, s);
83
84 return IRQ_HANDLED;
85}
56af390d 86
ffe1cb9a
HS
87static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
88{
89 if (enable)
90 inb(dev->iobase + AIO_IIRO_16_IRQ);
91 else
92 outb(0, dev->iobase + AIO_IIRO_16_IRQ);
93}
94
ad7a370c
HS
95static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
96 struct comedi_subdevice *s)
97{
98 aio_iiro_enable_irq(dev, false);
99
100 return 0;
101}
102
103static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
104 struct comedi_subdevice *s)
105{
106 aio_iiro_enable_irq(dev, true);
107
108 return 0;
109}
110
111static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
112 struct comedi_subdevice *s,
113 struct comedi_cmd *cmd)
114{
115 int err = 0;
116
117 /* Step 1 : check if triggers are trivially valid */
118
119 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
120 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
121 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
122 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
123 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
124
125 if (err)
126 return 1;
127
128 /* Step 2a : make sure trigger sources are unique */
129 /* Step 2b : and mutually compatible */
130
131 /* Step 3: check if arguments are trivially valid */
132
133 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
134 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
135 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
136 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
137 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
138
139 if (err)
140 return 3;
141
142 /* Step 4: fix up any arguments */
143
144 /* Step 5: check channel list if it exists */
145
146 return 0;
147}
148
3b78602f
HS
149static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_insn *insn,
152 unsigned int *data)
6f97a28d 153{
97f4289a 154 if (comedi_dio_update_state(s, data)) {
6f97a28d
HS
155 outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
156 outb((s->state >> 8) & 0xff,
157 dev->iobase + AIO_IIRO_16_RELAY_8_15);
158 }
56af390d 159
6f97a28d
HS
160 data[1] = s->state;
161
a2714e3e 162 return insn->n;
6f97a28d 163}
56af390d 164
aba97d74
HS
165static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
166 struct comedi_subdevice *s,
167 struct comedi_insn *insn,
168 unsigned int *data)
6f97a28d 169{
53c0bee8 170 data[1] = aio_iiro_16_read_inputs(dev);
6f97a28d 171
a2714e3e 172 return insn->n;
6f97a28d 173}
56af390d 174
0a85b6f0
MT
175static int aio_iiro_16_attach(struct comedi_device *dev,
176 struct comedi_devconfig *it)
56af390d 177{
34c43922 178 struct comedi_subdevice *s;
8b6c5694 179 int ret;
56af390d 180
862755ec 181 ret = comedi_request_region(dev, it->options[0], 0x8);
c8c90a73
HS
182 if (ret)
183 return ret;
56af390d 184
ffe1cb9a
HS
185 aio_iiro_enable_irq(dev, false);
186
ad7a370c
HS
187 /*
188 * Digital input change of state interrupts are optionally supported
189 * using IRQ 2-7, 10-12, 14, or 15.
190 */
191 if ((1 << it->options[1]) & 0xdcfc) {
192 ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
193 dev->board_name, dev);
194 if (ret == 0)
195 dev->irq = it->options[1];
196 }
197
8b6c5694
HS
198 ret = comedi_alloc_subdevices(dev, 2);
199 if (ret)
200 return ret;
56af390d 201
0f0d2b1b 202 /* Digital Output subdevice */
40c4db8b 203 s = &dev->subdevices[0];
0f0d2b1b
HS
204 s->type = COMEDI_SUBD_DO;
205 s->subdev_flags = SDF_WRITABLE;
206 s->n_chan = 16;
207 s->maxdata = 1;
208 s->range_table = &range_digital;
209 s->insn_bits = aio_iiro_16_do_insn_bits;
210
21f1801f
HS
211 /* get the initial state of the relays */
212 s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
213 (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
214
0f0d2b1b 215 /* Digital Input subdevice */
40c4db8b 216 s = &dev->subdevices[1];
0f0d2b1b
HS
217 s->type = COMEDI_SUBD_DI;
218 s->subdev_flags = SDF_READABLE;
219 s->n_chan = 16;
220 s->maxdata = 1;
221 s->range_table = &range_digital;
222 s->insn_bits = aio_iiro_16_di_insn_bits;
ad7a370c
HS
223 if (dev->irq) {
224 dev->read_subdev = s;
53c0bee8 225 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
ad7a370c
HS
226 s->len_chanlist = 1;
227 s->do_cmdtest = aio_iiro_16_cos_cmdtest;
228 s->do_cmd = aio_iiro_16_cos_cmd;
229 s->cancel = aio_iiro_16_cos_cancel;
230 }
56af390d 231
fb780d21 232 return 0;
56af390d
ZW
233}
234
294f930d 235static struct comedi_driver aio_iiro_16_driver = {
6f97a28d
HS
236 .driver_name = "aio_iiro_16",
237 .module = THIS_MODULE,
238 .attach = aio_iiro_16_attach,
21208519 239 .detach = comedi_legacy_detach,
6f97a28d 240};
294f930d 241module_comedi_driver(aio_iiro_16_driver);
90f703d3
AT
242
243MODULE_AUTHOR("Comedi http://www.comedi.org");
a52355ee 244MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
90f703d3 245MODULE_LICENSE("GPL");