staging: comedi: propogate error code from comedi_alloc_subdevices
[deliverable/linux.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.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26/*
27Driver: dt2811
28Description: Data Translation DT2811
29Author: ds
30Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
31Status: works
32
33Configuration options:
34 [0] - I/O port base address
35 [1] - IRQ, although this is currently unused
36 [2] - A/D reference
3b9fdcd5
IC
37 0 = signle-ended
38 1 = differential
3c443716
DS
39 2 = pseudo-differential (common reference)
40 [3] - A/D range
3b9fdcd5
IC
41 0 = [-5, 5]
42 1 = [-2.5, 2.5]
43 2 = [0, 5]
3c443716
DS
44 [4] - D/A 0 range (same choices)
45 [4] - D/A 1 range (same choices)
46*/
47
25436dc9 48#include <linux/interrupt.h>
3c443716
DS
49#include "../comedidev.h"
50
51#include <linux/ioport.h>
52
53static const char *driver_name = "dt2811";
54
3b9fdcd5
IC
55static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
56 4, {
57 RANGE(0, 5),
58 RANGE(0, 2.5),
59 RANGE(0, 1.25),
60 RANGE(0, 0.625)
61 }
3c443716 62};
0a85b6f0 63
3b9fdcd5
IC
64static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
65 4, {
66 RANGE(-2.5, 2.5),
67 RANGE(-1.25, 1.25),
68 RANGE(-0.625, 0.625),
69 RANGE(-0.3125, 0.3125)
70 }
3c443716 71};
0a85b6f0 72
3b9fdcd5
IC
73static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
74 4, {
75 RANGE(-5, 5),
76 RANGE(-2.5, 2.5),
77 RANGE(-1.25, 1.25),
78 RANGE(-0.625, 0.625)
79 }
3c443716 80};
0a85b6f0 81
3b9fdcd5
IC
82static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
83 4, {
84 RANGE(0, 5),
85 RANGE(0, 0.5),
86 RANGE(0, 0.05),
87 RANGE(0, 0.01)
88 }
3c443716 89};
0a85b6f0 90
3b9fdcd5
IC
91static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
92 4, {
93 RANGE(-2.5, 2.5),
94 RANGE(-0.25, 0.25),
95 RANGE(-0.025, 0.025),
96 RANGE(-0.005, 0.005)
97 }
3c443716 98};
0a85b6f0 99
3b9fdcd5
IC
100static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
101 4, {
102 RANGE(-5, 5),
103 RANGE(-0.5, 0.5),
104 RANGE(-0.05, 0.05),
105 RANGE(-0.01, 0.01)
106 }
3c443716
DS
107};
108
109/*
110
111 0x00 ADCSR R/W A/D Control/Status Register
112 bit 7 - (R) 1 indicates A/D conversion done
113 reading ADDAT clears bit
114 (W) ignored
115 bit 6 - (R) 1 indicates A/D error
116 (W) ignored
117 bit 5 - (R) 1 indicates A/D busy, cleared at end
118 of conversion
119 (W) ignored
120 bit 4 - (R) 0
121 (W)
122 bit 3 - (R) 0
123 bit 2 - (R/W) 1 indicates interrupts enabled
124 bits 1,0 - (R/W) mode bits
125 00 single conversion on ADGCR load
126 01 continuous conversion, internal clock,
127 (clock enabled on ADGCR load)
128 10 continuous conversion, internal clock,
129 external trigger
130 11 continuous conversion, external clock,
131 external trigger
132
133 0x01 ADGCR R/W A/D Gain/Channel Register
134 bit 6,7 - (R/W) gain select
135 00 gain=1, both PGH, PGL models
136 01 gain=2 PGH, 10 PGL
137 10 gain=4 PGH, 100 PGL
138 11 gain=8 PGH, 500 PGL
139 bit 4,5 - reserved
140 bit 3-0 - (R/W) channel select
141 channel number from 0-15
142
143 0x02,0x03 (R) ADDAT A/D Data Register
144 (W) DADAT0 D/A Data Register 0
145 0x02 low byte
146 0x03 high byte
147
148 0x04,0x05 (W) DADAT0 D/A Data Register 1
149
150 0x06 (R) DIO0 Digital Input Port 0
151 (W) DIO1 Digital Output Port 1
152
153 0x07 TMRCTR (R/W) Timer/Counter Register
154 bits 6,7 - reserved
155 bits 5-3 - Timer frequency control (mantissa)
156 543 divisor freqency (kHz)
157 000 1 600
158 001 10 60
159 010 2 300
160 011 3 200
161 100 4 150
162 101 5 120
163 110 6 100
164 111 12 50
165 bits 2-0 - Timer frequency control (exponent)
166 210 multiply divisor/divide frequency by
167 000 1
168 001 10
169 010 100
170 011 1000
171 100 10000
172 101 100000
173 110 1000000
174 111 10000000
175
176 */
177
178#define TIMEOUT 10000
179
180#define DT2811_SIZE 8
181
182#define DT2811_ADCSR 0
183#define DT2811_ADGCR 1
184#define DT2811_ADDATLO 2
185#define DT2811_ADDATHI 3
186#define DT2811_DADAT0LO 2
187#define DT2811_DADAT0HI 3
188#define DT2811_DADAT1LO 4
189#define DT2811_DADAT1HI 5
190#define DT2811_DIO 6
191#define DT2811_TMRCTR 7
192
193/*
194 * flags
195 */
196
197/* ADCSR */
198
199#define DT2811_ADDONE 0x80
200#define DT2811_ADERROR 0x40
201#define DT2811_ADBUSY 0x20
202#define DT2811_CLRERROR 0x10
203#define DT2811_INTENB 0x04
204#define DT2811_ADMODE 0x03
205
42f1884d
BP
206struct dt2811_board {
207
3c443716 208 const char *name;
9ced1de6
BP
209 const struct comedi_lrange *bip_5;
210 const struct comedi_lrange *bip_2_5;
211 const struct comedi_lrange *unip_5;
42f1884d
BP
212};
213
3c443716 214enum { card_2811_pgh, card_2811_pgl };
d89da617
BP
215
216struct dt2811_private {
3c443716
DS
217 int ntrig;
218 int curadchan;
219 enum {
220 adc_singleended, adc_diff, adc_pseudo_diff
221 } adc_mux;
222 enum {
223 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
224 } dac_range[2];
9ced1de6 225 const struct comedi_lrange *range_type_list[2];
790c5541 226 unsigned int ao_readback[2];
d89da617 227};
3c443716 228
d89da617 229#define devpriv ((struct dt2811_private *)dev->private)
3c443716 230
9ced1de6 231static const struct comedi_lrange *dac_range_types[] = {
3c443716
DS
232 &range_bipolar5,
233 &range_bipolar2_5,
234 &range_unipolar5
235};
236
237#define DT2811_TIMEOUT 5
238
239#if 0
70265d24 240static irqreturn_t dt2811_interrupt(int irq, void *d)
3c443716
DS
241{
242 int lo, hi;
243 int data;
71b5f4f1 244 struct comedi_device *dev = d;
3c443716
DS
245
246 if (!dev->attached) {
247 comedi_error(dev, "spurious interrupt");
248 return IRQ_HANDLED;
249 }
250
251 lo = inb(dev->iobase + DT2811_ADDATLO);
252 hi = inb(dev->iobase + DT2811_ADDATHI);
253
254 data = lo + (hi << 8);
255
256 if (!(--devpriv->ntrig)) {
257 /* how to turn off acquisition */
258 s->async->events |= COMEDI_SB_EOA;
259 }
260 comedi_event(dev, s);
261 return IRQ_HANDLED;
262}
263#endif
264
5675d899
HS
265static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
266 struct comedi_insn *insn, unsigned int *data)
267{
268 int chan = CR_CHAN(insn->chanspec);
269 int timeout = DT2811_TIMEOUT;
270 int i;
271
272 for (i = 0; i < insn->n; i++) {
273 outb(chan, dev->iobase + DT2811_ADGCR);
274
275 while (timeout
276 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
277 timeout--;
278 if (!timeout)
279 return -ETIME;
280
281 data[i] = inb(dev->iobase + DT2811_ADDATLO);
282 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
283 data[i] &= 0xfff;
284 }
285
286 return i;
287}
288
289#if 0
290/* Wow. This is code from the Comedi stone age. But it hasn't been
291 * replaced, so I'll let it stay. */
292int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
293{
294 struct comedi_device *dev = comedi_devices + minor;
295
296 if (adtrig->n < 1)
297 return 0;
298 dev->curadchan = adtrig->chan;
299 switch (dev->i_admode) {
300 case COMEDI_MDEMAND:
301 dev->ntrig = adtrig->n - 1;
302 /* not necessary */
303 /*printk("dt2811: AD soft trigger\n"); */
304 /*outb(DT2811_CLRERROR|DT2811_INTENB,
305 dev->iobase+DT2811_ADCSR); */
306 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
307 do_gettimeofday(&trigtime);
308 break;
309 case COMEDI_MCONTS:
310 dev->ntrig = adtrig->n;
311 break;
312 }
313
314 return 0;
315}
316#endif
317
318static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
319 struct comedi_insn *insn, unsigned int *data)
320{
321 int i;
322 int chan;
323
324 chan = CR_CHAN(insn->chanspec);
325
326 for (i = 0; i < insn->n; i++) {
327 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
328 outb((data[i] >> 8) & 0xff,
329 dev->iobase + DT2811_DADAT0HI + 2 * chan);
330 devpriv->ao_readback[chan] = data[i];
331 }
332
333 return i;
334}
335
336static int dt2811_ao_insn_read(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn, unsigned int *data)
339{
340 int i;
341 int chan;
342
343 chan = CR_CHAN(insn->chanspec);
344
345 for (i = 0; i < insn->n; i++)
346 data[i] = devpriv->ao_readback[chan];
347
348 return i;
349}
350
351static int dt2811_di_insn_bits(struct comedi_device *dev,
352 struct comedi_subdevice *s,
353 struct comedi_insn *insn, unsigned int *data)
354{
355 if (insn->n != 2)
356 return -EINVAL;
357
358 data[1] = inb(dev->iobase + DT2811_DIO);
359
360 return 2;
361}
362
363static int dt2811_do_insn_bits(struct comedi_device *dev,
364 struct comedi_subdevice *s,
365 struct comedi_insn *insn, unsigned int *data)
366{
367 if (insn->n != 2)
368 return -EINVAL;
369
370 s->state &= ~data[0];
371 s->state |= data[0] & data[1];
372 outb(s->state, dev->iobase + DT2811_DIO);
373
374 data[1] = s->state;
375
376 return 2;
377}
378
3c443716
DS
379/*
380 options[0] Board base address
381 options[1] IRQ
382 options[2] Input configuration
3b9fdcd5
IC
383 0 == single-ended
384 1 == differential
385 2 == pseudo-differential
3c443716 386 options[3] Analog input range configuration
3b9fdcd5
IC
387 0 == bipolar 5 (-5V -- +5V)
388 1 == bipolar 2.5V (-2.5V -- +2.5V)
389 2 == unipolar 5V (0V -- +5V)
3c443716 390 options[4] Analog output 0 range configuration
3b9fdcd5
IC
391 0 == bipolar 5 (-5V -- +5V)
392 1 == bipolar 2.5V (-2.5V -- +2.5V)
393 2 == unipolar 5V (0V -- +5V)
3c443716 394 options[5] Analog output 1 range configuration
3b9fdcd5
IC
395 0 == bipolar 5 (-5V -- +5V)
396 1 == bipolar 2.5V (-2.5V -- +2.5V)
397 2 == unipolar 5V (0V -- +5V)
3c443716 398*/
da91b269 399static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
3c443716 400{
2696fb57
BP
401 /* int i, irq; */
402 /* unsigned long irqs; */
403 /* long flags; */
404
5a011d61 405 const struct dt2811_board *board = comedi_board(dev);
3c443716 406 int ret;
34c43922 407 struct comedi_subdevice *s;
3c443716
DS
408 unsigned long iobase;
409
410 iobase = it->options[0];
411
3b9fdcd5 412 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
3c443716
DS
413
414 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
3b9fdcd5 415 printk(KERN_ERR "I/O port conflict\n");
3c443716
DS
416 return -EIO;
417 }
418
419 dev->iobase = iobase;
5a011d61 420 dev->board_name = board->name;
3c443716
DS
421
422#if 0
423 outb(0, dev->iobase + DT2811_ADCSR);
5f74ea14 424 udelay(100);
3c443716
DS
425 i = inb(dev->iobase + DT2811_ADDATLO);
426 i = inb(dev->iobase + DT2811_ADDATHI);
427#endif
428
429#if 0
430 irq = it->options[1];
431 if (irq < 0) {
432 save_flags(flags);
433 sti();
434 irqs = probe_irq_on();
435
436 outb(DT2811_CLRERROR | DT2811_INTENB,
0a85b6f0 437 dev->iobase + DT2811_ADCSR);
3c443716
DS
438 outb(0, dev->iobase + DT2811_ADGCR);
439
5f74ea14 440 udelay(100);
3c443716
DS
441
442 irq = probe_irq_off(irqs);
443 restore_flags(flags);
444
3b9fdcd5
IC
445 /*outb(DT2811_CLRERROR|DT2811_INTENB,
446 dev->iobase+DT2811_ADCSR);*/
3c443716 447
3b9fdcd5
IC
448 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
449 printk(KERN_ERR "error probing irq (bad)\n");
3c443716
DS
450 dev->irq = 0;
451 if (irq > 0) {
452 i = inb(dev->iobase + DT2811_ADDATLO);
453 i = inb(dev->iobase + DT2811_ADDATHI);
3b9fdcd5 454 printk(KERN_INFO "(irq = %d)\n", irq);
5f74ea14 455 ret = request_irq(irq, dt2811_interrupt, 0,
0a85b6f0 456 driver_name, dev);
3c443716
DS
457 if (ret < 0)
458 return -EIO;
459 dev->irq = irq;
460 } else if (irq == 0) {
3b9fdcd5 461 printk(KERN_INFO "(no irq)\n");
3c443716 462 } else {
3b9fdcd5 463 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
3c443716
DS
464 }
465 }
466#endif
467
2f0b9d08 468 ret = comedi_alloc_subdevices(dev, 4);
8b6c5694 469 if (ret)
3c443716 470 return ret;
c3744138
BP
471
472 ret = alloc_private(dev, sizeof(struct dt2811_private));
473 if (ret < 0)
3c443716 474 return ret;
c3744138 475
3c443716
DS
476 switch (it->options[2]) {
477 case 0:
478 devpriv->adc_mux = adc_singleended;
479 break;
480 case 1:
481 devpriv->adc_mux = adc_diff;
482 break;
483 case 2:
484 devpriv->adc_mux = adc_pseudo_diff;
485 break;
486 default:
487 devpriv->adc_mux = adc_singleended;
488 break;
489 }
490 switch (it->options[4]) {
491 case 0:
492 devpriv->dac_range[0] = dac_bipolar_5;
493 break;
494 case 1:
495 devpriv->dac_range[0] = dac_bipolar_2_5;
496 break;
497 case 2:
498 devpriv->dac_range[0] = dac_unipolar_5;
499 break;
500 default:
501 devpriv->dac_range[0] = dac_bipolar_5;
502 break;
503 }
504 switch (it->options[5]) {
505 case 0:
506 devpriv->dac_range[1] = dac_bipolar_5;
507 break;
508 case 1:
509 devpriv->dac_range[1] = dac_bipolar_2_5;
510 break;
511 case 2:
512 devpriv->dac_range[1] = dac_unipolar_5;
513 break;
514 default:
515 devpriv->dac_range[1] = dac_bipolar_5;
516 break;
517 }
518
519 s = dev->subdevices + 0;
520 /* initialize the ADC subdevice */
521 s->type = COMEDI_SUBD_AI;
522 s->subdev_flags = SDF_READABLE | SDF_GROUND;
523 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
524 s->insn_read = dt2811_ai_insn;
525 s->maxdata = 0xfff;
526 switch (it->options[3]) {
527 case 0:
528 default:
5a011d61 529 s->range_table = board->bip_5;
3c443716
DS
530 break;
531 case 1:
5a011d61 532 s->range_table = board->bip_2_5;
3c443716
DS
533 break;
534 case 2:
5a011d61 535 s->range_table = board->unip_5;
3c443716
DS
536 break;
537 }
538
539 s = dev->subdevices + 1;
540 /* ao subdevice */
541 s->type = COMEDI_SUBD_AO;
542 s->subdev_flags = SDF_WRITABLE;
543 s->n_chan = 2;
544 s->insn_write = dt2811_ao_insn;
545 s->insn_read = dt2811_ao_insn_read;
546 s->maxdata = 0xfff;
547 s->range_table_list = devpriv->range_type_list;
548 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
549 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
550
551 s = dev->subdevices + 2;
552 /* di subdevice */
553 s->type = COMEDI_SUBD_DI;
554 s->subdev_flags = SDF_READABLE;
555 s->n_chan = 8;
556 s->insn_bits = dt2811_di_insn_bits;
557 s->maxdata = 1;
558 s->range_table = &range_digital;
559
560 s = dev->subdevices + 3;
561 /* do subdevice */
562 s->type = COMEDI_SUBD_DO;
563 s->subdev_flags = SDF_WRITABLE;
564 s->n_chan = 8;
565 s->insn_bits = dt2811_do_insn_bits;
566 s->maxdata = 1;
567 s->state = 0;
568 s->range_table = &range_digital;
569
570 return 0;
571}
572
484ecc95 573static void dt2811_detach(struct comedi_device *dev)
3c443716 574{
3b9fdcd5 575 if (dev->irq)
5f74ea14 576 free_irq(dev->irq, dev);
3b9fdcd5 577 if (dev->iobase)
3c443716 578 release_region(dev->iobase, DT2811_SIZE);
3c443716
DS
579}
580
5675d899
HS
581static const struct dt2811_board boardtypes[] = {
582 {
583 .name = "dt2811-pgh",
584 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
585 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
586 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
587 }, {
588 .name = "dt2811-pgl",
589 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
590 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
591 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
592 },
593};
3c443716 594
5675d899
HS
595static struct comedi_driver dt2811_driver = {
596 .driver_name = "dt2811",
597 .module = THIS_MODULE,
598 .attach = dt2811_attach,
599 .detach = dt2811_detach,
600 .board_name = &boardtypes[0].name,
601 .num_names = ARRAY_SIZE(boardtypes),
602 .offset = sizeof(struct dt2811_board),
603};
604module_comedi_driver(dt2811_driver);
90f703d3
AT
605
606MODULE_AUTHOR("Comedi http://www.comedi.org");
607MODULE_DESCRIPTION("Comedi low-level driver");
608MODULE_LICENSE("GPL");
This page took 0.453185 seconds and 5 git commands to generate.