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.
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
33 0 = signle-ended
34 1 = differential
35 2 = pseudo-differential (common reference)
36 [3] - A/D range
37 0 = [-5, 5]
38 1 = [-2.5, 2.5]
39 2 = [0, 5]
40 [4] - D/A 0 range (same choices)
41 [4] - D/A 1 range (same choices)
42 */
43
44 #include <linux/module.h>
45 #include "../comedidev.h"
46
47 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
48 4, {
49 UNI_RANGE(5),
50 UNI_RANGE(2.5),
51 UNI_RANGE(1.25),
52 UNI_RANGE(0.625)
53 }
54 };
55
56 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
57 4, {
58 BIP_RANGE(2.5),
59 BIP_RANGE(1.25),
60 BIP_RANGE(0.625),
61 BIP_RANGE(0.3125)
62 }
63 };
64
65 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
66 4, {
67 BIP_RANGE(5),
68 BIP_RANGE(2.5),
69 BIP_RANGE(1.25),
70 BIP_RANGE(0.625)
71 }
72 };
73
74 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
75 4, {
76 UNI_RANGE(5),
77 UNI_RANGE(0.5),
78 UNI_RANGE(0.05),
79 UNI_RANGE(0.01)
80 }
81 };
82
83 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
84 4, {
85 BIP_RANGE(2.5),
86 BIP_RANGE(0.25),
87 BIP_RANGE(0.025),
88 BIP_RANGE(0.005)
89 }
90 };
91
92 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
93 4, {
94 BIP_RANGE(5),
95 BIP_RANGE(0.5),
96 BIP_RANGE(0.05),
97 BIP_RANGE(0.01)
98 }
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
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
196 struct dt2811_board {
197 const char *name;
198 const struct comedi_lrange *bip_5;
199 const struct comedi_lrange *bip_2_5;
200 const struct comedi_lrange *unip_5;
201 };
202
203 enum { card_2811_pgh, card_2811_pgl };
204
205 struct dt2811_private {
206 int ntrig;
207 int curadchan;
208 enum {
209 adc_singleended, adc_diff, adc_pseudo_diff
210 } adc_mux;
211 enum {
212 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
213 } dac_range[2];
214 const struct comedi_lrange *range_type_list[2];
215 };
216
217 static const struct comedi_lrange *dac_range_types[] = {
218 &range_bipolar5,
219 &range_bipolar2_5,
220 &range_unipolar5
221 };
222
dt2811_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)223 static int dt2811_ai_eoc(struct comedi_device *dev,
224 struct comedi_subdevice *s,
225 struct comedi_insn *insn,
226 unsigned long context)
227 {
228 unsigned int status;
229
230 status = inb(dev->iobase + DT2811_ADCSR);
231 if ((status & DT2811_ADBUSY) == 0)
232 return 0;
233 return -EBUSY;
234 }
235
dt2811_ai_insn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)236 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
237 struct comedi_insn *insn, unsigned int *data)
238 {
239 int chan = CR_CHAN(insn->chanspec);
240 int ret;
241 int i;
242
243 for (i = 0; i < insn->n; i++) {
244 outb(chan, dev->iobase + DT2811_ADGCR);
245
246 ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0);
247 if (ret)
248 return ret;
249
250 data[i] = inb(dev->iobase + DT2811_ADDATLO);
251 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
252 data[i] &= 0xfff;
253 }
254
255 return i;
256 }
257
dt2811_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)258 static int dt2811_ao_insn_write(struct comedi_device *dev,
259 struct comedi_subdevice *s,
260 struct comedi_insn *insn,
261 unsigned int *data)
262 {
263 unsigned int chan = CR_CHAN(insn->chanspec);
264 unsigned int val = s->readback[chan];
265 int i;
266
267 for (i = 0; i < insn->n; i++) {
268 val = data[i];
269 outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
270 outb((val >> 8) & 0xff,
271 dev->iobase + DT2811_DADAT0HI + 2 * chan);
272 }
273 s->readback[chan] = val;
274
275 return insn->n;
276 }
277
dt2811_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)278 static int dt2811_di_insn_bits(struct comedi_device *dev,
279 struct comedi_subdevice *s,
280 struct comedi_insn *insn, unsigned int *data)
281 {
282 data[1] = inb(dev->iobase + DT2811_DIO);
283
284 return insn->n;
285 }
286
dt2811_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)287 static int dt2811_do_insn_bits(struct comedi_device *dev,
288 struct comedi_subdevice *s,
289 struct comedi_insn *insn,
290 unsigned int *data)
291 {
292 if (comedi_dio_update_state(s, data))
293 outb(s->state, dev->iobase + DT2811_DIO);
294
295 data[1] = s->state;
296
297 return insn->n;
298 }
299
300 /*
301 options[0] Board base address
302 options[1] IRQ
303 options[2] Input configuration
304 0 == single-ended
305 1 == differential
306 2 == pseudo-differential
307 options[3] Analog input range configuration
308 0 == bipolar 5 (-5V -- +5V)
309 1 == bipolar 2.5V (-2.5V -- +2.5V)
310 2 == unipolar 5V (0V -- +5V)
311 options[4] Analog output 0 range configuration
312 0 == bipolar 5 (-5V -- +5V)
313 1 == bipolar 2.5V (-2.5V -- +2.5V)
314 2 == unipolar 5V (0V -- +5V)
315 options[5] Analog output 1 range configuration
316 0 == bipolar 5 (-5V -- +5V)
317 1 == bipolar 2.5V (-2.5V -- +2.5V)
318 2 == unipolar 5V (0V -- +5V)
319 */
dt2811_attach(struct comedi_device * dev,struct comedi_devconfig * it)320 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
321 {
322 /* int i; */
323 const struct dt2811_board *board = dev->board_ptr;
324 struct dt2811_private *devpriv;
325 int ret;
326 struct comedi_subdevice *s;
327
328 ret = comedi_request_region(dev, it->options[0], 0x8);
329 if (ret)
330 return ret;
331
332 #if 0
333 outb(0, dev->iobase + DT2811_ADCSR);
334 udelay(100);
335 i = inb(dev->iobase + DT2811_ADDATLO);
336 i = inb(dev->iobase + DT2811_ADDATHI);
337 #endif
338
339 ret = comedi_alloc_subdevices(dev, 4);
340 if (ret)
341 return ret;
342
343 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
344 if (!devpriv)
345 return -ENOMEM;
346
347 switch (it->options[2]) {
348 case 0:
349 devpriv->adc_mux = adc_singleended;
350 break;
351 case 1:
352 devpriv->adc_mux = adc_diff;
353 break;
354 case 2:
355 devpriv->adc_mux = adc_pseudo_diff;
356 break;
357 default:
358 devpriv->adc_mux = adc_singleended;
359 break;
360 }
361 switch (it->options[4]) {
362 case 0:
363 devpriv->dac_range[0] = dac_bipolar_5;
364 break;
365 case 1:
366 devpriv->dac_range[0] = dac_bipolar_2_5;
367 break;
368 case 2:
369 devpriv->dac_range[0] = dac_unipolar_5;
370 break;
371 default:
372 devpriv->dac_range[0] = dac_bipolar_5;
373 break;
374 }
375 switch (it->options[5]) {
376 case 0:
377 devpriv->dac_range[1] = dac_bipolar_5;
378 break;
379 case 1:
380 devpriv->dac_range[1] = dac_bipolar_2_5;
381 break;
382 case 2:
383 devpriv->dac_range[1] = dac_unipolar_5;
384 break;
385 default:
386 devpriv->dac_range[1] = dac_bipolar_5;
387 break;
388 }
389
390 s = &dev->subdevices[0];
391 /* initialize the ADC subdevice */
392 s->type = COMEDI_SUBD_AI;
393 s->subdev_flags = SDF_READABLE | SDF_GROUND;
394 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
395 s->insn_read = dt2811_ai_insn;
396 s->maxdata = 0xfff;
397 switch (it->options[3]) {
398 case 0:
399 default:
400 s->range_table = board->bip_5;
401 break;
402 case 1:
403 s->range_table = board->bip_2_5;
404 break;
405 case 2:
406 s->range_table = board->unip_5;
407 break;
408 }
409
410 s = &dev->subdevices[1];
411 /* ao subdevice */
412 s->type = COMEDI_SUBD_AO;
413 s->subdev_flags = SDF_WRITABLE;
414 s->n_chan = 2;
415 s->maxdata = 0xfff;
416 s->range_table_list = devpriv->range_type_list;
417 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
418 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
419 s->insn_write = dt2811_ao_insn_write;
420
421 ret = comedi_alloc_subdev_readback(s);
422 if (ret)
423 return ret;
424
425 s = &dev->subdevices[2];
426 /* di subdevice */
427 s->type = COMEDI_SUBD_DI;
428 s->subdev_flags = SDF_READABLE;
429 s->n_chan = 8;
430 s->insn_bits = dt2811_di_insn_bits;
431 s->maxdata = 1;
432 s->range_table = &range_digital;
433
434 s = &dev->subdevices[3];
435 /* do subdevice */
436 s->type = COMEDI_SUBD_DO;
437 s->subdev_flags = SDF_WRITABLE;
438 s->n_chan = 8;
439 s->insn_bits = dt2811_do_insn_bits;
440 s->maxdata = 1;
441 s->state = 0;
442 s->range_table = &range_digital;
443
444 return 0;
445 }
446
447 static const struct dt2811_board boardtypes[] = {
448 {
449 .name = "dt2811-pgh",
450 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
451 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
452 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
453 }, {
454 .name = "dt2811-pgl",
455 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
456 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
457 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
458 },
459 };
460
461 static struct comedi_driver dt2811_driver = {
462 .driver_name = "dt2811",
463 .module = THIS_MODULE,
464 .attach = dt2811_attach,
465 .detach = comedi_legacy_detach,
466 .board_name = &boardtypes[0].name,
467 .num_names = ARRAY_SIZE(boardtypes),
468 .offset = sizeof(struct dt2811_board),
469 };
470 module_comedi_driver(dt2811_driver);
471
472 MODULE_AUTHOR("Comedi http://www.comedi.org");
473 MODULE_DESCRIPTION("Comedi low-level driver");
474 MODULE_LICENSE("GPL");
475