Merge branch 'for-4.0-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[linux-2.6-block.git] / drivers / staging / comedi / drivers / dt2811.c
CommitLineData
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/*
23Driver: dt2811
24Description: Data Translation DT2811
25Author: ds
26Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
27Status: works
28
29Configuration 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
47static 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
56static 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
65static 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
74static 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
83static 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
92static 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
196struct 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 204enum { card_2811_pgh, card_2811_pgl };
d89da617
BP
205
206struct 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 218static const struct comedi_lrange *dac_range_types[] = {
3c443716
DS
219 &range_bipolar5,
220 &range_bipolar2_5,
221 &range_unipolar5
222};
223
7c4ede3a
HS
224static 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
237static 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
259static 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
279static 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
288static 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 321static 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
448static 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
462static 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};
471module_comedi_driver(dt2811_driver);
90f703d3
AT
472
473MODULE_AUTHOR("Comedi http://www.comedi.org");
474MODULE_DESCRIPTION("Comedi low-level driver");
475MODULE_LICENSE("GPL");