These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / aio_aio12_8.c
1 /*
2  * aio_aio12_8.c
3  * Driver for Access I/O Products PC-104 AIO12-8 Analog I/O 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_aio12_8
19  * Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board
20  * Author: Pablo Mejia <pablo.mejia@cctechnol.com>
21  * Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8),
22  *   [Access I/O] PC-104 AI12-8 (aio_ai12_8),
23  *   [Access I/O] PC-104 AO12-4 (aio_ao12_4)
24  * Status: experimental
25  *
26  * Configuration Options:
27  *   [0] - I/O port base address
28  *
29  * Notes:
30  * Only synchronous operations are supported.
31  */
32
33 #include <linux/module.h>
34 #include "../comedidev.h"
35
36 #include "comedi_8254.h"
37 #include "8255.h"
38
39 /*
40  * Register map
41  */
42 #define AIO12_8_STATUS_REG              0x00
43 #define AIO12_8_STATUS_ADC_EOC          BIT(7)
44 #define AIO12_8_STATUS_PORT_C_COS       BIT(6)
45 #define AIO12_8_STATUS_IRQ_ENA          BIT(2)
46 #define AIO12_8_INTERRUPT_REG           0x01
47 #define AIO12_8_INTERRUPT_ADC           BIT(7)
48 #define AIO12_8_INTERRUPT_COS           BIT(6)
49 #define AIO12_8_INTERRUPT_COUNTER1      BIT(5)
50 #define AIO12_8_INTERRUPT_PORT_C3       BIT(4)
51 #define AIO12_8_INTERRUPT_PORT_C0       BIT(3)
52 #define AIO12_8_INTERRUPT_ENA           BIT(2)
53 #define AIO12_8_ADC_REG                 0x02
54 #define AIO12_8_ADC_MODE(x)             (((x) & 0x3) << 6)
55 #define AIO12_8_ADC_MODE_NORMAL         AIO12_8_ADC_MODE(0)
56 #define AIO12_8_ADC_MODE_INT_CLK        AIO12_8_ADC_MODE(1)
57 #define AIO12_8_ADC_MODE_STANDBY        AIO12_8_ADC_MODE(2)
58 #define AIO12_8_ADC_MODE_POWERDOWN      AIO12_8_ADC_MODE(3)
59 #define AIO12_8_ADC_ACQ(x)              (((x) & 0x1) << 5)
60 #define AIO12_8_ADC_ACQ_3USEC           AIO12_8_ADC_ACQ(0)
61 #define AIO12_8_ADC_ACQ_PROGRAM         AIO12_8_ADC_ACQ(1)
62 #define AIO12_8_ADC_RANGE(x)            ((x) << 3)
63 #define AIO12_8_ADC_CHAN(x)             ((x) << 0)
64 #define AIO12_8_DAC_REG(x)              (0x04 + (x) * 2)
65 #define AIO12_8_8254_BASE_REG           0x0c
66 #define AIO12_8_8255_BASE_REG           0x10
67 #define AIO12_8_DIO_CONTROL_REG         0x14
68 #define AIO12_8_DIO_CONTROL_TST         BIT(0)
69 #define AIO12_8_ADC_TRIGGER_REG         0x15
70 #define AIO12_8_ADC_TRIGGER_RANGE(x)    ((x) << 3)
71 #define AIO12_8_ADC_TRIGGER_CHAN(x)     ((x) << 0)
72 #define AIO12_8_TRIGGER_REG             0x16
73 #define AIO12_8_TRIGGER_ADTRIG          BIT(1)
74 #define AIO12_8_TRIGGER_DACTRIG         BIT(0)
75 #define AIO12_8_COS_REG                 0x17
76 #define AIO12_8_DAC_ENABLE_REG          0x18
77 #define AIO12_8_DAC_ENABLE_REF_ENA      BIT(0)
78
79 static const struct comedi_lrange aio_aio12_8_range = {
80         4, {
81                 UNI_RANGE(5),
82                 BIP_RANGE(5),
83                 UNI_RANGE(10),
84                 BIP_RANGE(10)
85         }
86 };
87
88 struct aio12_8_boardtype {
89         const char *name;
90         unsigned int has_ai:1;
91         unsigned int has_ao:1;
92 };
93
94 static const struct aio12_8_boardtype board_types[] = {
95         {
96                 .name           = "aio_aio12_8",
97                 .has_ai         = 1,
98                 .has_ao         = 1,
99         }, {
100                 .name           = "aio_ai12_8",
101                 .has_ai         = 1,
102         }, {
103                 .name           = "aio_ao12_4",
104                 .has_ao         = 1,
105         },
106 };
107
108 static int aio_aio12_8_ai_eoc(struct comedi_device *dev,
109                               struct comedi_subdevice *s,
110                               struct comedi_insn *insn,
111                               unsigned long context)
112 {
113         unsigned int status;
114
115         status = inb(dev->iobase + AIO12_8_STATUS_REG);
116         if (status & AIO12_8_STATUS_ADC_EOC)
117                 return 0;
118         return -EBUSY;
119 }
120
121 static int aio_aio12_8_ai_read(struct comedi_device *dev,
122                                struct comedi_subdevice *s,
123                                struct comedi_insn *insn,
124                                unsigned int *data)
125 {
126         unsigned int chan = CR_CHAN(insn->chanspec);
127         unsigned int range = CR_RANGE(insn->chanspec);
128         unsigned int val;
129         unsigned char control;
130         int ret;
131         int i;
132
133         /*
134          * Setup the control byte for internal 2MHz clock, 3uS conversion,
135          * at the desired range of the requested channel.
136          */
137         control = AIO12_8_ADC_MODE_NORMAL | AIO12_8_ADC_ACQ_3USEC |
138                   AIO12_8_ADC_RANGE(range) | AIO12_8_ADC_CHAN(chan);
139
140         /* Read status to clear EOC latch */
141         inb(dev->iobase + AIO12_8_STATUS_REG);
142
143         for (i = 0; i < insn->n; i++) {
144                 /*  Setup and start conversion */
145                 outb(control, dev->iobase + AIO12_8_ADC_REG);
146
147                 /*  Wait for conversion to complete */
148                 ret = comedi_timeout(dev, s, insn, aio_aio12_8_ai_eoc, 0);
149                 if (ret)
150                         return ret;
151
152                 val = inw(dev->iobase + AIO12_8_ADC_REG) & s->maxdata;
153
154                 /* munge bipolar 2's complement data to offset binary */
155                 if (comedi_range_is_bipolar(s, range))
156                         val = comedi_offset_munge(s, val);
157
158                 data[i] = val;
159         }
160
161         return insn->n;
162 }
163
164 static int aio_aio12_8_ao_insn_write(struct comedi_device *dev,
165                                      struct comedi_subdevice *s,
166                                      struct comedi_insn *insn,
167                                      unsigned int *data)
168 {
169         unsigned int chan = CR_CHAN(insn->chanspec);
170         unsigned int val = s->readback[chan];
171         int i;
172
173         /* enable DACs */
174         outb(AIO12_8_DAC_ENABLE_REF_ENA, dev->iobase + AIO12_8_DAC_ENABLE_REG);
175
176         for (i = 0; i < insn->n; i++) {
177                 val = data[i];
178                 outw(val, dev->iobase + AIO12_8_DAC_REG(chan));
179         }
180         s->readback[chan] = val;
181
182         return insn->n;
183 }
184
185 static int aio_aio12_8_counter_insn_config(struct comedi_device *dev,
186                                            struct comedi_subdevice *s,
187                                            struct comedi_insn *insn,
188                                            unsigned int *data)
189 {
190         unsigned int chan = CR_CHAN(insn->chanspec);
191
192         switch (data[0]) {
193         case INSN_CONFIG_GET_CLOCK_SRC:
194                 /*
195                  * Channels 0 and 2 have external clock sources.
196                  * Channel 1 has a fixed 1 MHz clock source.
197                  */
198                 data[0] = 0;
199                 data[1] = (chan == 1) ? I8254_OSC_BASE_1MHZ : 0;
200                 break;
201         default:
202                 return -EINVAL;
203         }
204
205         return insn->n;
206 }
207
208 static int aio_aio12_8_attach(struct comedi_device *dev,
209                               struct comedi_devconfig *it)
210 {
211         const struct aio12_8_boardtype *board = dev->board_ptr;
212         struct comedi_subdevice *s;
213         int ret;
214
215         ret = comedi_request_region(dev, it->options[0], 32);
216         if (ret)
217                 return ret;
218
219         dev->pacer = comedi_8254_init(dev->iobase + AIO12_8_8254_BASE_REG,
220                                       0, I8254_IO8, 0);
221         if (!dev->pacer)
222                 return -ENOMEM;
223
224         ret = comedi_alloc_subdevices(dev, 4);
225         if (ret)
226                 return ret;
227
228         /* Analog Input subdevice */
229         s = &dev->subdevices[0];
230         if (board->has_ai) {
231                 s->type         = COMEDI_SUBD_AI;
232                 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
233                 s->n_chan       = 8;
234                 s->maxdata      = 0x0fff;
235                 s->range_table  = &aio_aio12_8_range;
236                 s->insn_read    = aio_aio12_8_ai_read;
237         } else {
238                 s->type = COMEDI_SUBD_UNUSED;
239         }
240
241         /* Analog Output subdevice */
242         s = &dev->subdevices[1];
243         if (board->has_ao) {
244                 s->type         = COMEDI_SUBD_AO;
245                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
246                 s->n_chan       = 4;
247                 s->maxdata      = 0x0fff;
248                 s->range_table  = &aio_aio12_8_range;
249                 s->insn_write   = aio_aio12_8_ao_insn_write;
250
251                 ret = comedi_alloc_subdev_readback(s);
252                 if (ret)
253                         return ret;
254         } else {
255                 s->type = COMEDI_SUBD_UNUSED;
256         }
257
258         /* Digital I/O subdevice (8255) */
259         s = &dev->subdevices[2];
260         ret = subdev_8255_init(dev, s, NULL, AIO12_8_8255_BASE_REG);
261         if (ret)
262                 return ret;
263
264         /* Counter subdevice (8254) */
265         s = &dev->subdevices[3];
266         comedi_8254_subdevice_init(s, dev->pacer);
267
268         dev->pacer->insn_config = aio_aio12_8_counter_insn_config;
269
270         return 0;
271 }
272
273 static struct comedi_driver aio_aio12_8_driver = {
274         .driver_name    = "aio_aio12_8",
275         .module         = THIS_MODULE,
276         .attach         = aio_aio12_8_attach,
277         .detach         = comedi_legacy_detach,
278         .board_name     = &board_types[0].name,
279         .num_names      = ARRAY_SIZE(board_types),
280         .offset         = sizeof(struct aio12_8_boardtype),
281 };
282 module_comedi_driver(aio_aio12_8_driver);
283
284 MODULE_AUTHOR("Comedi http://www.comedi.org");
285 MODULE_DESCRIPTION("Comedi driver for Access I/O AIO12-8 Analog I/O Board");
286 MODULE_LICENSE("GPL");