1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48#include <linux/interrupt.h>
49#include "../comedidev.h"
50
51#include <linux/ioport.h>
52
53static const char *driver_name = "dt2811";
54
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 }
62};
63
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 }
71};
72
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 }
80};
81
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 }
89};
90
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 }
98};
99
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 }
107};
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
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
195
196
197
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
206struct dt2811_board {
207
208 const char *name;
209 const struct comedi_lrange *bip_5;
210 const struct comedi_lrange *bip_2_5;
211 const struct comedi_lrange *unip_5;
212};
213
214#define this_board ((const struct dt2811_board *)dev->board_ptr)
215
216enum { card_2811_pgh, card_2811_pgl };
217
218struct dt2811_private {
219 int ntrig;
220 int curadchan;
221 enum {
222 adc_singleended, adc_diff, adc_pseudo_diff
223 } adc_mux;
224 enum {
225 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
226 } dac_range[2];
227 const struct comedi_lrange *range_type_list[2];
228 unsigned int ao_readback[2];
229};
230
231#define devpriv ((struct dt2811_private *)dev->private)
232
233static const struct comedi_lrange *dac_range_types[] = {
234 &range_bipolar5,
235 &range_bipolar2_5,
236 &range_unipolar5
237};
238
239#define DT2811_TIMEOUT 5
240
241#if 0
242static irqreturn_t dt2811_interrupt(int irq, void *d)
243{
244 int lo, hi;
245 int data;
246 struct comedi_device *dev = d;
247
248 if (!dev->attached) {
249 comedi_error(dev, "spurious interrupt");
250 return IRQ_HANDLED;
251 }
252
253 lo = inb(dev->iobase + DT2811_ADDATLO);
254 hi = inb(dev->iobase + DT2811_ADDATHI);
255
256 data = lo + (hi << 8);
257
258 if (!(--devpriv->ntrig)) {
259
260 s->async->events |= COMEDI_SB_EOA;
261 }
262 comedi_event(dev, s);
263 return IRQ_HANDLED;
264}
265#endif
266
267static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
268 struct comedi_insn *insn, unsigned int *data)
269{
270 int chan = CR_CHAN(insn->chanspec);
271 int timeout = DT2811_TIMEOUT;
272 int i;
273
274 for (i = 0; i < insn->n; i++) {
275 outb(chan, dev->iobase + DT2811_ADGCR);
276
277 while (timeout
278 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
279 timeout--;
280 if (!timeout)
281 return -ETIME;
282
283 data[i] = inb(dev->iobase + DT2811_ADDATLO);
284 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
285 data[i] &= 0xfff;
286 }
287
288 return i;
289}
290
291#if 0
292
293
294int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
295{
296 struct comedi_device *dev = comedi_devices + minor;
297
298 if (adtrig->n < 1)
299 return 0;
300 dev->curadchan = adtrig->chan;
301 switch (dev->i_admode) {
302 case COMEDI_MDEMAND:
303 dev->ntrig = adtrig->n - 1;
304
305
306
307
308 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
309 do_gettimeofday(&trigtime);
310 break;
311 case COMEDI_MCONTS:
312 dev->ntrig = adtrig->n;
313 break;
314 }
315
316 return 0;
317}
318#endif
319
320static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
321 struct comedi_insn *insn, unsigned int *data)
322{
323 int i;
324 int chan;
325
326 chan = CR_CHAN(insn->chanspec);
327
328 for (i = 0; i < insn->n; i++) {
329 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
330 outb((data[i] >> 8) & 0xff,
331 dev->iobase + DT2811_DADAT0HI + 2 * chan);
332 devpriv->ao_readback[chan] = data[i];
333 }
334
335 return i;
336}
337
338static int dt2811_ao_insn_read(struct comedi_device *dev,
339 struct comedi_subdevice *s,
340 struct comedi_insn *insn, unsigned int *data)
341{
342 int i;
343 int chan;
344
345 chan = CR_CHAN(insn->chanspec);
346
347 for (i = 0; i < insn->n; i++)
348 data[i] = devpriv->ao_readback[chan];
349
350 return i;
351}
352
353static int dt2811_di_insn_bits(struct comedi_device *dev,
354 struct comedi_subdevice *s,
355 struct comedi_insn *insn, unsigned int *data)
356{
357 if (insn->n != 2)
358 return -EINVAL;
359
360 data[1] = inb(dev->iobase + DT2811_DIO);
361
362 return 2;
363}
364
365static int dt2811_do_insn_bits(struct comedi_device *dev,
366 struct comedi_subdevice *s,
367 struct comedi_insn *insn, unsigned int *data)
368{
369 if (insn->n != 2)
370 return -EINVAL;
371
372 s->state &= ~data[0];
373 s->state |= data[0] & data[1];
374 outb(s->state, dev->iobase + DT2811_DIO);
375
376 data[1] = s->state;
377
378 return 2;
379}
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
402{
403
404
405
406
407 int ret;
408 struct comedi_subdevice *s;
409 unsigned long iobase;
410
411 iobase = it->options[0];
412
413 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
414
415 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
416 printk(KERN_ERR "I/O port conflict\n");
417 return -EIO;
418 }
419
420 dev->iobase = iobase;
421 dev->board_name = this_board->name;
422
423#if 0
424 outb(0, dev->iobase + DT2811_ADCSR);
425 udelay(100);
426 i = inb(dev->iobase + DT2811_ADDATLO);
427 i = inb(dev->iobase + DT2811_ADDATHI);
428#endif
429
430#if 0
431 irq = it->options[1];
432 if (irq < 0) {
433 save_flags(flags);
434 sti();
435 irqs = probe_irq_on();
436
437 outb(DT2811_CLRERROR | DT2811_INTENB,
438 dev->iobase + DT2811_ADCSR);
439 outb(0, dev->iobase + DT2811_ADGCR);
440
441 udelay(100);
442
443 irq = probe_irq_off(irqs);
444 restore_flags(flags);
445
446
447
448
449 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
450 printk(KERN_ERR "error probing irq (bad)\n");
451 dev->irq = 0;
452 if (irq > 0) {
453 i = inb(dev->iobase + DT2811_ADDATLO);
454 i = inb(dev->iobase + DT2811_ADDATHI);
455 printk(KERN_INFO "(irq = %d)\n", irq);
456 ret = request_irq(irq, dt2811_interrupt, 0,
457 driver_name, dev);
458 if (ret < 0)
459 return -EIO;
460 dev->irq = irq;
461 } else if (irq == 0) {
462 printk(KERN_INFO "(no irq)\n");
463 } else {
464 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
465 }
466 }
467#endif
468
469 ret = alloc_subdevices(dev, 4);
470 if (ret < 0)
471 return ret;
472
473 ret = alloc_private(dev, sizeof(struct dt2811_private));
474 if (ret < 0)
475 return ret;
476
477 switch (it->options[2]) {
478 case 0:
479 devpriv->adc_mux = adc_singleended;
480 break;
481 case 1:
482 devpriv->adc_mux = adc_diff;
483 break;
484 case 2:
485 devpriv->adc_mux = adc_pseudo_diff;
486 break;
487 default:
488 devpriv->adc_mux = adc_singleended;
489 break;
490 }
491 switch (it->options[4]) {
492 case 0:
493 devpriv->dac_range[0] = dac_bipolar_5;
494 break;
495 case 1:
496 devpriv->dac_range[0] = dac_bipolar_2_5;
497 break;
498 case 2:
499 devpriv->dac_range[0] = dac_unipolar_5;
500 break;
501 default:
502 devpriv->dac_range[0] = dac_bipolar_5;
503 break;
504 }
505 switch (it->options[5]) {
506 case 0:
507 devpriv->dac_range[1] = dac_bipolar_5;
508 break;
509 case 1:
510 devpriv->dac_range[1] = dac_bipolar_2_5;
511 break;
512 case 2:
513 devpriv->dac_range[1] = dac_unipolar_5;
514 break;
515 default:
516 devpriv->dac_range[1] = dac_bipolar_5;
517 break;
518 }
519
520 s = dev->subdevices + 0;
521
522 s->type = COMEDI_SUBD_AI;
523 s->subdev_flags = SDF_READABLE | SDF_GROUND;
524 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
525 s->insn_read = dt2811_ai_insn;
526 s->maxdata = 0xfff;
527 switch (it->options[3]) {
528 case 0:
529 default:
530 s->range_table = this_board->bip_5;
531 break;
532 case 1:
533 s->range_table = this_board->bip_2_5;
534 break;
535 case 2:
536 s->range_table = this_board->unip_5;
537 break;
538 }
539
540 s = dev->subdevices + 1;
541
542 s->type = COMEDI_SUBD_AO;
543 s->subdev_flags = SDF_WRITABLE;
544 s->n_chan = 2;
545 s->insn_write = dt2811_ao_insn;
546 s->insn_read = dt2811_ao_insn_read;
547 s->maxdata = 0xfff;
548 s->range_table_list = devpriv->range_type_list;
549 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
550 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
551
552 s = dev->subdevices + 2;
553
554 s->type = COMEDI_SUBD_DI;
555 s->subdev_flags = SDF_READABLE;
556 s->n_chan = 8;
557 s->insn_bits = dt2811_di_insn_bits;
558 s->maxdata = 1;
559 s->range_table = &range_digital;
560
561 s = dev->subdevices + 3;
562
563 s->type = COMEDI_SUBD_DO;
564 s->subdev_flags = SDF_WRITABLE;
565 s->n_chan = 8;
566 s->insn_bits = dt2811_do_insn_bits;
567 s->maxdata = 1;
568 s->state = 0;
569 s->range_table = &range_digital;
570
571 return 0;
572}
573
574static void dt2811_detach(struct comedi_device *dev)
575{
576 if (dev->irq)
577 free_irq(dev->irq, dev);
578 if (dev->iobase)
579 release_region(dev->iobase, DT2811_SIZE);
580}
581
582static const struct dt2811_board boardtypes[] = {
583 {
584 .name = "dt2811-pgh",
585 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
586 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
587 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
588 }, {
589 .name = "dt2811-pgl",
590 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
591 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
592 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
593 },
594};
595
596static struct comedi_driver dt2811_driver = {
597 .driver_name = "dt2811",
598 .module = THIS_MODULE,
599 .attach = dt2811_attach,
600 .detach = dt2811_detach,
601 .board_name = &boardtypes[0].name,
602 .num_names = ARRAY_SIZE(boardtypes),
603 .offset = sizeof(struct dt2811_board),
604};
605module_comedi_driver(dt2811_driver);
606
607MODULE_AUTHOR("Comedi http://www.comedi.org");
608MODULE_DESCRIPTION("Comedi low-level driver");
609MODULE_LICENSE("GPL");
610