Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / aio_iiro_16.c
1 /*
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  */
16
17 /*
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
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
30  * changed state and the current state of the inputs:
31  *
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
38  */
39
40 #include <linux/module.h>
41 #include <linux/interrupt.h>
42
43 #include "../comedidev.h"
44
45 #define AIO_IIRO_16_RELAY_0_7           0x00
46 #define AIO_IIRO_16_INPUT_0_7           0x01
47 #define AIO_IIRO_16_IRQ                 0x02
48 #define AIO_IIRO_16_RELAY_8_15          0x04
49 #define AIO_IIRO_16_INPUT_8_15          0x05
50 #define AIO_IIRO_16_STATUS              0x07
51 #define AIO_IIRO_16_STATUS_IRQE         BIT(7)
52 #define AIO_IIRO_16_STATUS_INPUT_8_15   BIT(1)
53 #define AIO_IIRO_16_STATUS_INPUT_0_7    BIT(0)
54
55 static unsigned int aio_iiro_16_read_inputs(struct comedi_device *dev)
56 {
57         unsigned int val;
58
59         val = inb(dev->iobase + AIO_IIRO_16_INPUT_0_7);
60         val |= inb(dev->iobase + AIO_IIRO_16_INPUT_8_15) << 8;
61
62         return val;
63 }
64
65 static irqreturn_t aio_iiro_16_cos(int irq, void *d)
66 {
67         struct comedi_device *dev = d;
68         struct comedi_subdevice *s = dev->read_subdev;
69         unsigned int status;
70         unsigned int val;
71
72         status = inb(dev->iobase + AIO_IIRO_16_STATUS);
73         if (!(status & AIO_IIRO_16_STATUS_IRQE))
74                 return IRQ_NONE;
75
76         val = aio_iiro_16_read_inputs(dev);
77         val |= (status << 16);
78
79         comedi_buf_write_samples(s, &val, 1);
80         comedi_handle_events(dev, s);
81
82         return IRQ_HANDLED;
83 }
84
85 static void aio_iiro_enable_irq(struct comedi_device *dev, bool enable)
86 {
87         if (enable)
88                 inb(dev->iobase + AIO_IIRO_16_IRQ);
89         else
90                 outb(0, dev->iobase + AIO_IIRO_16_IRQ);
91 }
92
93 static int aio_iiro_16_cos_cancel(struct comedi_device *dev,
94                                   struct comedi_subdevice *s)
95 {
96         aio_iiro_enable_irq(dev, false);
97
98         return 0;
99 }
100
101 static int aio_iiro_16_cos_cmd(struct comedi_device *dev,
102                                struct comedi_subdevice *s)
103 {
104         aio_iiro_enable_irq(dev, true);
105
106         return 0;
107 }
108
109 static int aio_iiro_16_cos_cmdtest(struct comedi_device *dev,
110                                    struct comedi_subdevice *s,
111                                    struct comedi_cmd *cmd)
112 {
113         int err = 0;
114
115         /* Step 1 : check if triggers are trivially valid */
116
117         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
118         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
119         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
120         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
121         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
122
123         if (err)
124                 return 1;
125
126         /* Step 2a : make sure trigger sources are unique */
127         /* Step 2b : and mutually compatible */
128
129         /* Step 3: check if arguments are trivially valid */
130
131         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
132         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
133         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
134         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
135                                            cmd->chanlist_len);
136         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
137
138         if (err)
139                 return 3;
140
141         /* Step 4: fix up any arguments */
142
143         /* Step 5: check channel list if it exists */
144
145         return 0;
146 }
147
148 static int aio_iiro_16_do_insn_bits(struct comedi_device *dev,
149                                     struct comedi_subdevice *s,
150                                     struct comedi_insn *insn,
151                                     unsigned int *data)
152 {
153         if (comedi_dio_update_state(s, data)) {
154                 outb(s->state & 0xff, dev->iobase + AIO_IIRO_16_RELAY_0_7);
155                 outb((s->state >> 8) & 0xff,
156                      dev->iobase + AIO_IIRO_16_RELAY_8_15);
157         }
158
159         data[1] = s->state;
160
161         return insn->n;
162 }
163
164 static int aio_iiro_16_di_insn_bits(struct comedi_device *dev,
165                                     struct comedi_subdevice *s,
166                                     struct comedi_insn *insn,
167                                     unsigned int *data)
168 {
169         data[1] = aio_iiro_16_read_inputs(dev);
170
171         return insn->n;
172 }
173
174 static int aio_iiro_16_attach(struct comedi_device *dev,
175                               struct comedi_devconfig *it)
176 {
177         struct comedi_subdevice *s;
178         int ret;
179
180         ret = comedi_request_region(dev, it->options[0], 0x8);
181         if (ret)
182                 return ret;
183
184         aio_iiro_enable_irq(dev, false);
185
186         /*
187          * Digital input change of state interrupts are optionally supported
188          * using IRQ 2-7, 10-12, 14, or 15.
189          */
190         if ((1 << it->options[1]) & 0xdcfc) {
191                 ret = request_irq(it->options[1], aio_iiro_16_cos, 0,
192                                   dev->board_name, dev);
193                 if (ret == 0)
194                         dev->irq = it->options[1];
195         }
196
197         ret = comedi_alloc_subdevices(dev, 2);
198         if (ret)
199                 return ret;
200
201         /* Digital Output subdevice */
202         s = &dev->subdevices[0];
203         s->type         = COMEDI_SUBD_DO;
204         s->subdev_flags = SDF_WRITABLE;
205         s->n_chan       = 16;
206         s->maxdata      = 1;
207         s->range_table  = &range_digital;
208         s->insn_bits    = aio_iiro_16_do_insn_bits;
209
210         /* get the initial state of the relays */
211         s->state = inb(dev->iobase + AIO_IIRO_16_RELAY_0_7) |
212                    (inb(dev->iobase + AIO_IIRO_16_RELAY_8_15) << 8);
213
214         /* Digital Input subdevice */
215         s = &dev->subdevices[1];
216         s->type         = COMEDI_SUBD_DI;
217         s->subdev_flags = SDF_READABLE;
218         s->n_chan       = 16;
219         s->maxdata      = 1;
220         s->range_table  = &range_digital;
221         s->insn_bits    = aio_iiro_16_di_insn_bits;
222         if (dev->irq) {
223                 dev->read_subdev = s;
224                 s->subdev_flags |= SDF_CMD_READ | SDF_LSAMPL;
225                 s->len_chanlist = 1;
226                 s->do_cmdtest   = aio_iiro_16_cos_cmdtest;
227                 s->do_cmd       = aio_iiro_16_cos_cmd;
228                 s->cancel       = aio_iiro_16_cos_cancel;
229         }
230
231         return 0;
232 }
233
234 static struct comedi_driver aio_iiro_16_driver = {
235         .driver_name    = "aio_iiro_16",
236         .module         = THIS_MODULE,
237         .attach         = aio_iiro_16_attach,
238         .detach         = comedi_legacy_detach,
239 };
240 module_comedi_driver(aio_iiro_16_driver);
241
242 MODULE_AUTHOR("Comedi http://www.comedi.org");
243 MODULE_DESCRIPTION("Comedi driver for Access I/O Products 104-IIRO-16 board");
244 MODULE_LICENSE("GPL");