Commit | Line | Data |
---|---|---|
3c443716 DS |
1 | /* |
2 | comedi/drivers/dt2811.c | |
3 | Hardware driver for Data Translation DT2811 | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | History: | |
7 | Base Version - David A. Schleef <ds@schleef.org> | |
8 | December 1998 - Updated to work. David does not have a DT2811 | |
9 | board any longer so this was suffering from bitrot. | |
10 | Updated performed by ... | |
11 | ||
12 | This program is free software; you can redistribute it and/or modify | |
13 | it under the terms of the GNU General Public License as published by | |
14 | the Free Software Foundation; either version 2 of the License, or | |
15 | (at your option) any later version. | |
16 | ||
17 | This program is distributed in the hope that it will be useful, | |
18 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | GNU General Public License for more details. | |
3c443716 DS |
21 | */ |
22 | /* | |
23 | Driver: dt2811 | |
24 | Description: Data Translation DT2811 | |
25 | Author: ds | |
26 | Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh) | |
27 | Status: works | |
28 | ||
29 | Configuration options: | |
30 | [0] - I/O port base address | |
31 | [1] - IRQ, although this is currently unused | |
32 | [2] - A/D reference | |
3b9fdcd5 IC |
33 | 0 = signle-ended |
34 | 1 = differential | |
3c443716 DS |
35 | 2 = pseudo-differential (common reference) |
36 | [3] - A/D range | |
3b9fdcd5 IC |
37 | 0 = [-5, 5] |
38 | 1 = [-2.5, 2.5] | |
39 | 2 = [0, 5] | |
3c443716 DS |
40 | [4] - D/A 0 range (same choices) |
41 | [4] - D/A 1 range (same choices) | |
42 | */ | |
43 | ||
ce157f80 | 44 | #include <linux/module.h> |
3c443716 DS |
45 | #include "../comedidev.h" |
46 | ||
3b9fdcd5 IC |
47 | static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { |
48 | 4, { | |
3cdabd5d HS |
49 | UNI_RANGE(5), |
50 | UNI_RANGE(2.5), | |
51 | UNI_RANGE(1.25), | |
52 | UNI_RANGE(0.625) | |
3b9fdcd5 | 53 | } |
3c443716 | 54 | }; |
0a85b6f0 | 55 | |
3b9fdcd5 IC |
56 | static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { |
57 | 4, { | |
3cdabd5d HS |
58 | BIP_RANGE(2.5), |
59 | BIP_RANGE(1.25), | |
60 | BIP_RANGE(0.625), | |
61 | BIP_RANGE(0.3125) | |
3b9fdcd5 | 62 | } |
3c443716 | 63 | }; |
0a85b6f0 | 64 | |
3b9fdcd5 IC |
65 | static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { |
66 | 4, { | |
3cdabd5d HS |
67 | BIP_RANGE(5), |
68 | BIP_RANGE(2.5), | |
69 | BIP_RANGE(1.25), | |
70 | BIP_RANGE(0.625) | |
3b9fdcd5 | 71 | } |
3c443716 | 72 | }; |
0a85b6f0 | 73 | |
3b9fdcd5 IC |
74 | static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { |
75 | 4, { | |
3cdabd5d HS |
76 | UNI_RANGE(5), |
77 | UNI_RANGE(0.5), | |
78 | UNI_RANGE(0.05), | |
79 | UNI_RANGE(0.01) | |
3b9fdcd5 | 80 | } |
3c443716 | 81 | }; |
0a85b6f0 | 82 | |
3b9fdcd5 IC |
83 | static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { |
84 | 4, { | |
3cdabd5d HS |
85 | BIP_RANGE(2.5), |
86 | BIP_RANGE(0.25), | |
87 | BIP_RANGE(0.025), | |
88 | BIP_RANGE(0.005) | |
3b9fdcd5 | 89 | } |
3c443716 | 90 | }; |
0a85b6f0 | 91 | |
3b9fdcd5 IC |
92 | static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { |
93 | 4, { | |
3cdabd5d HS |
94 | BIP_RANGE(5), |
95 | BIP_RANGE(0.5), | |
96 | BIP_RANGE(0.05), | |
97 | BIP_RANGE(0.01) | |
3b9fdcd5 | 98 | } |
3c443716 DS |
99 | }; |
100 | ||
101 | /* | |
102 | ||
103 | 0x00 ADCSR R/W A/D Control/Status Register | |
104 | bit 7 - (R) 1 indicates A/D conversion done | |
105 | reading ADDAT clears bit | |
106 | (W) ignored | |
107 | bit 6 - (R) 1 indicates A/D error | |
108 | (W) ignored | |
109 | bit 5 - (R) 1 indicates A/D busy, cleared at end | |
110 | of conversion | |
111 | (W) ignored | |
112 | bit 4 - (R) 0 | |
113 | (W) | |
114 | bit 3 - (R) 0 | |
115 | bit 2 - (R/W) 1 indicates interrupts enabled | |
116 | bits 1,0 - (R/W) mode bits | |
117 | 00 single conversion on ADGCR load | |
118 | 01 continuous conversion, internal clock, | |
119 | (clock enabled on ADGCR load) | |
120 | 10 continuous conversion, internal clock, | |
121 | external trigger | |
122 | 11 continuous conversion, external clock, | |
123 | external trigger | |
124 | ||
125 | 0x01 ADGCR R/W A/D Gain/Channel Register | |
126 | bit 6,7 - (R/W) gain select | |
127 | 00 gain=1, both PGH, PGL models | |
128 | 01 gain=2 PGH, 10 PGL | |
129 | 10 gain=4 PGH, 100 PGL | |
130 | 11 gain=8 PGH, 500 PGL | |
131 | bit 4,5 - reserved | |
132 | bit 3-0 - (R/W) channel select | |
133 | channel number from 0-15 | |
134 | ||
135 | 0x02,0x03 (R) ADDAT A/D Data Register | |
136 | (W) DADAT0 D/A Data Register 0 | |
137 | 0x02 low byte | |
138 | 0x03 high byte | |
139 | ||
140 | 0x04,0x05 (W) DADAT0 D/A Data Register 1 | |
141 | ||
142 | 0x06 (R) DIO0 Digital Input Port 0 | |
143 | (W) DIO1 Digital Output Port 1 | |
144 | ||
145 | 0x07 TMRCTR (R/W) Timer/Counter Register | |
146 | bits 6,7 - reserved | |
147 | bits 5-3 - Timer frequency control (mantissa) | |
148 | 543 divisor freqency (kHz) | |
149 | 000 1 600 | |
150 | 001 10 60 | |
151 | 010 2 300 | |
152 | 011 3 200 | |
153 | 100 4 150 | |
154 | 101 5 120 | |
155 | 110 6 100 | |
156 | 111 12 50 | |
157 | bits 2-0 - Timer frequency control (exponent) | |
158 | 210 multiply divisor/divide frequency by | |
159 | 000 1 | |
160 | 001 10 | |
161 | 010 100 | |
162 | 011 1000 | |
163 | 100 10000 | |
164 | 101 100000 | |
165 | 110 1000000 | |
166 | 111 10000000 | |
167 | ||
168 | */ | |
169 | ||
170 | #define TIMEOUT 10000 | |
171 | ||
3c443716 DS |
172 | #define DT2811_ADCSR 0 |
173 | #define DT2811_ADGCR 1 | |
174 | #define DT2811_ADDATLO 2 | |
175 | #define DT2811_ADDATHI 3 | |
176 | #define DT2811_DADAT0LO 2 | |
177 | #define DT2811_DADAT0HI 3 | |
178 | #define DT2811_DADAT1LO 4 | |
179 | #define DT2811_DADAT1HI 5 | |
180 | #define DT2811_DIO 6 | |
181 | #define DT2811_TMRCTR 7 | |
182 | ||
183 | /* | |
184 | * flags | |
185 | */ | |
186 | ||
187 | /* ADCSR */ | |
188 | ||
189 | #define DT2811_ADDONE 0x80 | |
190 | #define DT2811_ADERROR 0x40 | |
191 | #define DT2811_ADBUSY 0x20 | |
192 | #define DT2811_CLRERROR 0x10 | |
193 | #define DT2811_INTENB 0x04 | |
194 | #define DT2811_ADMODE 0x03 | |
195 | ||
42f1884d BP |
196 | struct dt2811_board { |
197 | ||
3c443716 | 198 | const char *name; |
9ced1de6 BP |
199 | const struct comedi_lrange *bip_5; |
200 | const struct comedi_lrange *bip_2_5; | |
201 | const struct comedi_lrange *unip_5; | |
42f1884d BP |
202 | }; |
203 | ||
3c443716 | 204 | enum { card_2811_pgh, card_2811_pgl }; |
d89da617 BP |
205 | |
206 | struct dt2811_private { | |
3c443716 DS |
207 | int ntrig; |
208 | int curadchan; | |
209 | enum { | |
210 | adc_singleended, adc_diff, adc_pseudo_diff | |
211 | } adc_mux; | |
212 | enum { | |
213 | dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5 | |
214 | } dac_range[2]; | |
9ced1de6 | 215 | const struct comedi_lrange *range_type_list[2]; |
d89da617 | 216 | }; |
3c443716 | 217 | |
9ced1de6 | 218 | static const struct comedi_lrange *dac_range_types[] = { |
3c443716 DS |
219 | &range_bipolar5, |
220 | &range_bipolar2_5, | |
221 | &range_unipolar5 | |
222 | }; | |
223 | ||
7c4ede3a HS |
224 | static int dt2811_ai_eoc(struct comedi_device *dev, |
225 | struct comedi_subdevice *s, | |
226 | struct comedi_insn *insn, | |
227 | unsigned long context) | |
228 | { | |
229 | unsigned int status; | |
230 | ||
231 | status = inb(dev->iobase + DT2811_ADCSR); | |
232 | if ((status & DT2811_ADBUSY) == 0) | |
233 | return 0; | |
234 | return -EBUSY; | |
235 | } | |
3c443716 | 236 | |
5675d899 HS |
237 | static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, |
238 | struct comedi_insn *insn, unsigned int *data) | |
239 | { | |
240 | int chan = CR_CHAN(insn->chanspec); | |
7c4ede3a | 241 | int ret; |
5675d899 HS |
242 | int i; |
243 | ||
244 | for (i = 0; i < insn->n; i++) { | |
245 | outb(chan, dev->iobase + DT2811_ADGCR); | |
246 | ||
7c4ede3a HS |
247 | ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0); |
248 | if (ret) | |
249 | return ret; | |
5675d899 HS |
250 | |
251 | data[i] = inb(dev->iobase + DT2811_ADDATLO); | |
252 | data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; | |
253 | data[i] &= 0xfff; | |
254 | } | |
255 | ||
256 | return i; | |
257 | } | |
258 | ||
b33bad98 HS |
259 | static int dt2811_ao_insn_write(struct comedi_device *dev, |
260 | struct comedi_subdevice *s, | |
261 | struct comedi_insn *insn, | |
262 | unsigned int *data) | |
5675d899 | 263 | { |
b33bad98 HS |
264 | unsigned int chan = CR_CHAN(insn->chanspec); |
265 | unsigned int val = s->readback[chan]; | |
5675d899 | 266 | int i; |
5675d899 HS |
267 | |
268 | for (i = 0; i < insn->n; i++) { | |
b33bad98 HS |
269 | val = data[i]; |
270 | outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); | |
271 | outb((val >> 8) & 0xff, | |
5675d899 | 272 | dev->iobase + DT2811_DADAT0HI + 2 * chan); |
5675d899 | 273 | } |
b33bad98 | 274 | s->readback[chan] = val; |
5675d899 | 275 | |
b33bad98 | 276 | return insn->n; |
5675d899 HS |
277 | } |
278 | ||
279 | static int dt2811_di_insn_bits(struct comedi_device *dev, | |
280 | struct comedi_subdevice *s, | |
281 | struct comedi_insn *insn, unsigned int *data) | |
282 | { | |
5675d899 HS |
283 | data[1] = inb(dev->iobase + DT2811_DIO); |
284 | ||
a2714e3e | 285 | return insn->n; |
5675d899 HS |
286 | } |
287 | ||
288 | static int dt2811_do_insn_bits(struct comedi_device *dev, | |
289 | struct comedi_subdevice *s, | |
97f4289a HS |
290 | struct comedi_insn *insn, |
291 | unsigned int *data) | |
5675d899 | 292 | { |
97f4289a HS |
293 | if (comedi_dio_update_state(s, data)) |
294 | outb(s->state, dev->iobase + DT2811_DIO); | |
5675d899 HS |
295 | |
296 | data[1] = s->state; | |
297 | ||
a2714e3e | 298 | return insn->n; |
5675d899 HS |
299 | } |
300 | ||
3c443716 DS |
301 | /* |
302 | options[0] Board base address | |
303 | options[1] IRQ | |
304 | options[2] Input configuration | |
3b9fdcd5 IC |
305 | 0 == single-ended |
306 | 1 == differential | |
307 | 2 == pseudo-differential | |
3c443716 | 308 | options[3] Analog input range configuration |
3b9fdcd5 IC |
309 | 0 == bipolar 5 (-5V -- +5V) |
310 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
311 | 2 == unipolar 5V (0V -- +5V) | |
3c443716 | 312 | options[4] Analog output 0 range configuration |
3b9fdcd5 IC |
313 | 0 == bipolar 5 (-5V -- +5V) |
314 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
315 | 2 == unipolar 5V (0V -- +5V) | |
3c443716 | 316 | options[5] Analog output 1 range configuration |
3b9fdcd5 IC |
317 | 0 == bipolar 5 (-5V -- +5V) |
318 | 1 == bipolar 2.5V (-2.5V -- +2.5V) | |
319 | 2 == unipolar 5V (0V -- +5V) | |
3c443716 | 320 | */ |
da91b269 | 321 | static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
3c443716 | 322 | { |
d7bfe8e8 | 323 | /* int i; */ |
a5a74074 | 324 | const struct dt2811_board *board = dev->board_ptr; |
9a1a6cf8 | 325 | struct dt2811_private *devpriv; |
3c443716 | 326 | int ret; |
34c43922 | 327 | struct comedi_subdevice *s; |
3c443716 | 328 | |
862755ec | 329 | ret = comedi_request_region(dev, it->options[0], 0x8); |
6ca3f28b HS |
330 | if (ret) |
331 | return ret; | |
3c443716 DS |
332 | |
333 | #if 0 | |
334 | outb(0, dev->iobase + DT2811_ADCSR); | |
5f74ea14 | 335 | udelay(100); |
3c443716 DS |
336 | i = inb(dev->iobase + DT2811_ADDATLO); |
337 | i = inb(dev->iobase + DT2811_ADDATHI); | |
338 | #endif | |
339 | ||
2f0b9d08 | 340 | ret = comedi_alloc_subdevices(dev, 4); |
8b6c5694 | 341 | if (ret) |
3c443716 | 342 | return ret; |
c3744138 | 343 | |
0bdab509 | 344 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
345 | if (!devpriv) |
346 | return -ENOMEM; | |
c3744138 | 347 | |
3c443716 DS |
348 | switch (it->options[2]) { |
349 | case 0: | |
350 | devpriv->adc_mux = adc_singleended; | |
351 | break; | |
352 | case 1: | |
353 | devpriv->adc_mux = adc_diff; | |
354 | break; | |
355 | case 2: | |
356 | devpriv->adc_mux = adc_pseudo_diff; | |
357 | break; | |
358 | default: | |
359 | devpriv->adc_mux = adc_singleended; | |
360 | break; | |
361 | } | |
362 | switch (it->options[4]) { | |
363 | case 0: | |
364 | devpriv->dac_range[0] = dac_bipolar_5; | |
365 | break; | |
366 | case 1: | |
367 | devpriv->dac_range[0] = dac_bipolar_2_5; | |
368 | break; | |
369 | case 2: | |
370 | devpriv->dac_range[0] = dac_unipolar_5; | |
371 | break; | |
372 | default: | |
373 | devpriv->dac_range[0] = dac_bipolar_5; | |
374 | break; | |
375 | } | |
376 | switch (it->options[5]) { | |
377 | case 0: | |
378 | devpriv->dac_range[1] = dac_bipolar_5; | |
379 | break; | |
380 | case 1: | |
381 | devpriv->dac_range[1] = dac_bipolar_2_5; | |
382 | break; | |
383 | case 2: | |
384 | devpriv->dac_range[1] = dac_unipolar_5; | |
385 | break; | |
386 | default: | |
387 | devpriv->dac_range[1] = dac_bipolar_5; | |
388 | break; | |
389 | } | |
390 | ||
4ea49896 | 391 | s = &dev->subdevices[0]; |
3c443716 DS |
392 | /* initialize the ADC subdevice */ |
393 | s->type = COMEDI_SUBD_AI; | |
394 | s->subdev_flags = SDF_READABLE | SDF_GROUND; | |
395 | s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16; | |
396 | s->insn_read = dt2811_ai_insn; | |
397 | s->maxdata = 0xfff; | |
398 | switch (it->options[3]) { | |
399 | case 0: | |
400 | default: | |
5a011d61 | 401 | s->range_table = board->bip_5; |
3c443716 DS |
402 | break; |
403 | case 1: | |
5a011d61 | 404 | s->range_table = board->bip_2_5; |
3c443716 DS |
405 | break; |
406 | case 2: | |
5a011d61 | 407 | s->range_table = board->unip_5; |
3c443716 DS |
408 | break; |
409 | } | |
410 | ||
4ea49896 | 411 | s = &dev->subdevices[1]; |
3c443716 DS |
412 | /* ao subdevice */ |
413 | s->type = COMEDI_SUBD_AO; | |
414 | s->subdev_flags = SDF_WRITABLE; | |
415 | s->n_chan = 2; | |
3c443716 DS |
416 | s->maxdata = 0xfff; |
417 | s->range_table_list = devpriv->range_type_list; | |
418 | devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]]; | |
419 | devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]]; | |
b33bad98 | 420 | s->insn_write = dt2811_ao_insn_write; |
b33bad98 HS |
421 | |
422 | ret = comedi_alloc_subdev_readback(s); | |
423 | if (ret) | |
424 | return ret; | |
3c443716 | 425 | |
4ea49896 | 426 | s = &dev->subdevices[2]; |
3c443716 DS |
427 | /* di subdevice */ |
428 | s->type = COMEDI_SUBD_DI; | |
429 | s->subdev_flags = SDF_READABLE; | |
430 | s->n_chan = 8; | |
431 | s->insn_bits = dt2811_di_insn_bits; | |
432 | s->maxdata = 1; | |
433 | s->range_table = &range_digital; | |
434 | ||
4ea49896 | 435 | s = &dev->subdevices[3]; |
3c443716 DS |
436 | /* do subdevice */ |
437 | s->type = COMEDI_SUBD_DO; | |
438 | s->subdev_flags = SDF_WRITABLE; | |
439 | s->n_chan = 8; | |
440 | s->insn_bits = dt2811_do_insn_bits; | |
441 | s->maxdata = 1; | |
442 | s->state = 0; | |
443 | s->range_table = &range_digital; | |
444 | ||
445 | return 0; | |
446 | } | |
447 | ||
5675d899 HS |
448 | static const struct dt2811_board boardtypes[] = { |
449 | { | |
450 | .name = "dt2811-pgh", | |
451 | .bip_5 = &range_dt2811_pgh_ai_5_bipolar, | |
452 | .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar, | |
453 | .unip_5 = &range_dt2811_pgh_ai_5_unipolar, | |
454 | }, { | |
455 | .name = "dt2811-pgl", | |
456 | .bip_5 = &range_dt2811_pgl_ai_5_bipolar, | |
457 | .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar, | |
458 | .unip_5 = &range_dt2811_pgl_ai_5_unipolar, | |
459 | }, | |
460 | }; | |
3c443716 | 461 | |
5675d899 HS |
462 | static struct comedi_driver dt2811_driver = { |
463 | .driver_name = "dt2811", | |
464 | .module = THIS_MODULE, | |
465 | .attach = dt2811_attach, | |
3d1fe3f7 | 466 | .detach = comedi_legacy_detach, |
5675d899 HS |
467 | .board_name = &boardtypes[0].name, |
468 | .num_names = ARRAY_SIZE(boardtypes), | |
469 | .offset = sizeof(struct dt2811_board), | |
470 | }; | |
471 | module_comedi_driver(dt2811_driver); | |
90f703d3 AT |
472 | |
473 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
474 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
475 | MODULE_LICENSE("GPL"); |