Commit | Line | Data |
---|---|---|
7623199a | 1 | /* |
471c5d6c HS |
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 | */ | |
7623199a PM |
16 | |
17 | /* | |
471c5d6c HS |
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), | |
fbea1876 | 23 | * [Access I/O] PC-104 AO12-4 (aio_ao12_4) |
471c5d6c HS |
24 | * Status: experimental |
25 | * | |
26 | * Configuration Options: | |
27 | * [0] - I/O port base address | |
28 | * | |
29 | * Notes: | |
30 | * Only synchronous operations are supported. | |
31 | */ | |
7623199a | 32 | |
ce157f80 | 33 | #include <linux/module.h> |
7623199a | 34 | #include "../comedidev.h" |
ffe4a317 HS |
35 | |
36 | #include "comedi_8254.h" | |
7623199a PM |
37 | #include "8255.h" |
38 | ||
d4fec101 HS |
39 | /* |
40 | * Register map | |
41 | */ | |
42 | #define AIO12_8_STATUS_REG 0x00 | |
4dc4f282 HS |
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) | |
d4fec101 | 46 | #define AIO12_8_INTERRUPT_REG 0x01 |
4dc4f282 HS |
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) | |
d4fec101 | 53 | #define AIO12_8_ADC_REG 0x02 |
4dc4f282 HS |
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) | |
d4fec101 HS |
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 | |
4dc4f282 | 68 | #define AIO12_8_DIO_CONTROL_TST BIT(0) |
d4fec101 HS |
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 | |
4dc4f282 HS |
73 | #define AIO12_8_TRIGGER_ADTRIG BIT(1) |
74 | #define AIO12_8_TRIGGER_DACTRIG BIT(0) | |
d4fec101 HS |
75 | #define AIO12_8_COS_REG 0x17 |
76 | #define AIO12_8_DAC_ENABLE_REG 0x18 | |
4dc4f282 | 77 | #define AIO12_8_DAC_ENABLE_REF_ENA BIT(0) |
7623199a | 78 | |
c5fcb7ca HS |
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 | ||
74753712 | 88 | struct aio12_8_boardtype { |
7623199a | 89 | const char *name; |
6db70e39 HS |
90 | unsigned int has_ai:1; |
91 | unsigned int has_ao:1; | |
74753712 | 92 | }; |
7623199a | 93 | |
74753712 | 94 | static const struct aio12_8_boardtype board_types[] = { |
7623199a | 95 | { |
862edd6b | 96 | .name = "aio_aio12_8", |
6db70e39 HS |
97 | .has_ai = 1, |
98 | .has_ao = 1, | |
862edd6b HS |
99 | }, { |
100 | .name = "aio_ai12_8", | |
6db70e39 | 101 | .has_ai = 1, |
862edd6b | 102 | }, { |
fbea1876 | 103 | .name = "aio_ao12_4", |
6db70e39 | 104 | .has_ao = 1, |
862edd6b | 105 | }, |
7623199a PM |
106 | }; |
107 | ||
0862a46f HS |
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 | ||
0a85b6f0 MT |
121 | static int aio_aio12_8_ai_read(struct comedi_device *dev, |
122 | struct comedi_subdevice *s, | |
ee4c7709 HS |
123 | struct comedi_insn *insn, |
124 | unsigned int *data) | |
7623199a | 125 | { |
d4fec101 HS |
126 | unsigned int chan = CR_CHAN(insn->chanspec); |
127 | unsigned int range = CR_RANGE(insn->chanspec); | |
ee4c7709 | 128 | unsigned int val; |
d4fec101 | 129 | unsigned char control; |
0862a46f | 130 | int ret; |
ee4c7709 | 131 | int i; |
7623199a | 132 | |
d4fec101 HS |
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); | |
7623199a | 142 | |
ee4c7709 | 143 | for (i = 0; i < insn->n; i++) { |
2696fb57 | 144 | /* Setup and start conversion */ |
d4fec101 | 145 | outb(control, dev->iobase + AIO12_8_ADC_REG); |
7623199a | 146 | |
2696fb57 | 147 | /* Wait for conversion to complete */ |
0862a46f | 148 | ret = comedi_timeout(dev, s, insn, aio_aio12_8_ai_eoc, 0); |
22ca19d9 | 149 | if (ret) |
0862a46f | 150 | return ret; |
d4fec101 | 151 | |
ee4c7709 HS |
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; | |
7623199a | 159 | } |
d4fec101 HS |
160 | |
161 | return insn->n; | |
7623199a PM |
162 | } |
163 | ||
9ac58133 HS |
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) | |
7623199a | 168 | { |
d4fec101 | 169 | unsigned int chan = CR_CHAN(insn->chanspec); |
9ac58133 | 170 | unsigned int val = s->readback[chan]; |
7623199a | 171 | int i; |
7623199a | 172 | |
2696fb57 | 173 | /* enable DACs */ |
d4fec101 | 174 | outb(AIO12_8_DAC_ENABLE_REF_ENA, dev->iobase + AIO12_8_DAC_ENABLE_REG); |
7623199a PM |
175 | |
176 | for (i = 0; i < insn->n; i++) { | |
d4fec101 | 177 | val = data[i]; |
9ac58133 | 178 | outw(val, dev->iobase + AIO12_8_DAC_REG(chan)); |
7623199a | 179 | } |
9ac58133 | 180 | s->readback[chan] = val; |
d4fec101 | 181 | |
7623199a PM |
182 | return insn->n; |
183 | } | |
184 | ||
ffe4a317 HS |
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 | ||
0a85b6f0 MT |
208 | static int aio_aio12_8_attach(struct comedi_device *dev, |
209 | struct comedi_devconfig *it) | |
7623199a | 210 | { |
b37e1d6e | 211 | const struct aio12_8_boardtype *board = dev->board_ptr; |
34c43922 | 212 | struct comedi_subdevice *s; |
8b6c5694 | 213 | int ret; |
7623199a | 214 | |
a5958514 HS |
215 | ret = comedi_request_region(dev, it->options[0], 32); |
216 | if (ret) | |
217 | return ret; | |
7623199a | 218 | |
ffe4a317 HS |
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 | ||
862edd6b | 224 | ret = comedi_alloc_subdevices(dev, 4); |
8b6c5694 HS |
225 | if (ret) |
226 | return ret; | |
7623199a | 227 | |
6db70e39 | 228 | /* Analog Input subdevice */ |
ef83beae | 229 | s = &dev->subdevices[0]; |
6db70e39 | 230 | if (board->has_ai) { |
862edd6b HS |
231 | s->type = COMEDI_SUBD_AI; |
232 | s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF; | |
6db70e39 | 233 | s->n_chan = 8; |
862edd6b | 234 | s->maxdata = 0x0fff; |
c5fcb7ca | 235 | s->range_table = &aio_aio12_8_range; |
862edd6b HS |
236 | s->insn_read = aio_aio12_8_ai_read; |
237 | } else { | |
238 | s->type = COMEDI_SUBD_UNUSED; | |
239 | } | |
240 | ||
6db70e39 | 241 | /* Analog Output subdevice */ |
ef83beae | 242 | s = &dev->subdevices[1]; |
6db70e39 | 243 | if (board->has_ao) { |
862edd6b | 244 | s->type = COMEDI_SUBD_AO; |
1198f6b0 | 245 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; |
862edd6b HS |
246 | s->n_chan = 4; |
247 | s->maxdata = 0x0fff; | |
c5fcb7ca | 248 | s->range_table = &aio_aio12_8_range; |
9ac58133 | 249 | s->insn_write = aio_aio12_8_ao_insn_write; |
9ac58133 HS |
250 | |
251 | ret = comedi_alloc_subdev_readback(s); | |
252 | if (ret) | |
253 | return ret; | |
862edd6b HS |
254 | } else { |
255 | s->type = COMEDI_SUBD_UNUSED; | |
256 | } | |
257 | ||
9f4d30dd | 258 | /* Digital I/O subdevice (8255) */ |
ef83beae | 259 | s = &dev->subdevices[2]; |
4085e93b | 260 | ret = subdev_8255_init(dev, s, NULL, AIO12_8_8255_BASE_REG); |
862edd6b HS |
261 | if (ret) |
262 | return ret; | |
263 | ||
ffe4a317 | 264 | /* Counter subdevice (8254) */ |
ef83beae | 265 | s = &dev->subdevices[3]; |
ffe4a317 HS |
266 | comedi_8254_subdevice_init(s, dev->pacer); |
267 | ||
268 | dev->pacer->insn_config = aio_aio12_8_counter_insn_config; | |
862edd6b | 269 | |
7623199a PM |
270 | return 0; |
271 | } | |
272 | ||
294f930d HS |
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, | |
588ba6dc | 277 | .detach = comedi_legacy_detach, |
294f930d HS |
278 | .board_name = &board_types[0].name, |
279 | .num_names = ARRAY_SIZE(board_types), | |
280 | .offset = sizeof(struct aio12_8_boardtype), | |
7623199a | 281 | }; |
294f930d | 282 | module_comedi_driver(aio_aio12_8_driver); |
90f703d3 AT |
283 | |
284 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
180480ed | 285 | MODULE_DESCRIPTION("Comedi driver for Access I/O AIO12-8 Analog I/O Board"); |
90f703d3 | 286 | MODULE_LICENSE("GPL"); |