staging: comedi: dt282x: use dev->read_subdev
[deliverable/linux.git] / drivers / staging / comedi / drivers / dt282x.c
1 /*
2 comedi/drivers/dt282x.c
3 Hardware driver for Data Translation DT2821 series
4
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1997-8 David A. Schleef <ds@schleef.org>
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 */
18 /*
19 Driver: dt282x
20 Description: Data Translation DT2821 series (including DT-EZ)
21 Author: ds
22 Devices: [Data Translation] DT2821 (dt2821),
23 DT2821-F-16SE (dt2821-f), DT2821-F-8DI (dt2821-f),
24 DT2821-G-16SE (dt2821-f), DT2821-G-8DI (dt2821-g),
25 DT2823 (dt2823),
26 DT2824-PGH (dt2824-pgh), DT2824-PGL (dt2824-pgl), DT2825 (dt2825),
27 DT2827 (dt2827), DT2828 (dt2828), DT21-EZ (dt21-ez), DT23-EZ (dt23-ez),
28 DT24-EZ (dt24-ez), DT24-EZ-PGL (dt24-ez-pgl)
29 Status: complete
30 Updated: Wed, 22 Aug 2001 17:11:34 -0700
31
32 Configuration options:
33 [0] - I/O port base address
34 [1] - IRQ
35 [2] - DMA 1
36 [3] - DMA 2
37 [4] - AI jumpered for 0=single ended, 1=differential
38 [5] - AI jumpered for 0=straight binary, 1=2's complement
39 [6] - AO 0 jumpered for 0=straight binary, 1=2's complement
40 [7] - AO 1 jumpered for 0=straight binary, 1=2's complement
41 [8] - AI jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5]
42 [9] - AO 0 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
43 4=[-2.5,2.5]
44 [10]- A0 1 jumpered for 0=[-10,10]V, 1=[0,10], 2=[-5,5], 3=[0,5],
45 4=[-2.5,2.5]
46
47 Notes:
48 - AO commands might be broken.
49 - If you try to run a command on both the AI and AO subdevices
50 simultaneously, bad things will happen. The driver needs to
51 be fixed to check for this situation and return an error.
52 */
53
54 #include <linux/module.h>
55 #include "../comedidev.h"
56
57 #include <linux/delay.h>
58 #include <linux/gfp.h>
59 #include <linux/interrupt.h>
60 #include <linux/io.h>
61
62 #include <asm/dma.h>
63
64 #include "comedi_fc.h"
65
66 #define DT2821_TIMEOUT 100 /* 500 us */
67 #define DT2821_SIZE 0x10
68
69 /*
70 * Registers in the DT282x
71 */
72
73 #define DT2821_ADCSR 0x00 /* A/D Control/Status */
74 #define DT2821_CHANCSR 0x02 /* Channel Control/Status */
75 #define DT2821_ADDAT 0x04 /* A/D data */
76 #define DT2821_DACSR 0x06 /* D/A Control/Status */
77 #define DT2821_DADAT 0x08 /* D/A data */
78 #define DT2821_DIODAT 0x0a /* digital data */
79 #define DT2821_SUPCSR 0x0c /* Supervisor Control/Status */
80 #define DT2821_TMRCTR 0x0e /* Timer/Counter */
81
82 /*
83 * At power up, some registers are in a well-known state. The
84 * masks and values are as follows:
85 */
86
87 #define DT2821_ADCSR_MASK 0xfff0
88 #define DT2821_ADCSR_VAL 0x7c00
89
90 #define DT2821_CHANCSR_MASK 0xf0f0
91 #define DT2821_CHANCSR_VAL 0x70f0
92
93 #define DT2821_DACSR_MASK 0x7c93
94 #define DT2821_DACSR_VAL 0x7c90
95
96 #define DT2821_SUPCSR_MASK 0xf8ff
97 #define DT2821_SUPCSR_VAL 0x0000
98
99 #define DT2821_TMRCTR_MASK 0xff00
100 #define DT2821_TMRCTR_VAL 0xf000
101
102 /*
103 * Bit fields of each register
104 */
105
106 /* ADCSR */
107
108 #define DT2821_ADERR 0x8000 /* (R) 1 for A/D error */
109 #define DT2821_ADCLK 0x0200 /* (R/W) A/D clock enable */
110 /* 0x7c00 read as 1's */
111 #define DT2821_MUXBUSY 0x0100 /* (R) multiplexer busy */
112 #define DT2821_ADDONE 0x0080 /* (R) A/D done */
113 #define DT2821_IADDONE 0x0040 /* (R/W) interrupt on A/D done */
114 /* 0x0030 gain select */
115 /* 0x000f channel select */
116
117 /* CHANCSR */
118
119 #define DT2821_LLE 0x8000 /* (R/W) Load List Enable */
120 /* 0x7000 read as 1's */
121 /* 0x0f00 (R) present address */
122 /* 0x00f0 read as 1's */
123 /* 0x000f (R) number of entries - 1 */
124
125 /* DACSR */
126
127 #define DT2821_DAERR 0x8000 /* (R) D/A error */
128 #define DT2821_YSEL 0x0200 /* (R/W) DAC 1 select */
129 #define DT2821_SSEL 0x0100 /* (R/W) single channel select */
130 #define DT2821_DACRDY 0x0080 /* (R) DAC ready */
131 #define DT2821_IDARDY 0x0040 /* (R/W) interrupt on DAC ready */
132 #define DT2821_DACLK 0x0020 /* (R/W) D/A clock enable */
133 #define DT2821_HBOE 0x0002 /* (R/W) DIO high byte output enable */
134 #define DT2821_LBOE 0x0001 /* (R/W) DIO low byte output enable */
135
136 /* SUPCSR */
137
138 #define DT2821_DMAD 0x8000 /* (R) DMA done */
139 #define DT2821_ERRINTEN 0x4000 /* (R/W) interrupt on error */
140 #define DT2821_CLRDMADNE 0x2000 /* (W) clear DMA done */
141 #define DT2821_DDMA 0x1000 /* (R/W) dual DMA */
142 #define DT2821_DS1 0x0800 /* (R/W) DMA select 1 */
143 #define DT2821_DS0 0x0400 /* (R/W) DMA select 0 */
144 #define DT2821_BUFFB 0x0200 /* (R/W) buffer B selected */
145 #define DT2821_SCDN 0x0100 /* (R) scan done */
146 #define DT2821_DACON 0x0080 /* (W) DAC single conversion */
147 #define DT2821_ADCINIT 0x0040 /* (W) A/D initialize */
148 #define DT2821_DACINIT 0x0020 /* (W) D/A initialize */
149 #define DT2821_PRLD 0x0010 /* (W) preload multiplexer */
150 #define DT2821_STRIG 0x0008 /* (W) software trigger */
151 #define DT2821_XTRIG 0x0004 /* (R/W) external trigger enable */
152 #define DT2821_XCLK 0x0002 /* (R/W) external clock enable */
153 #define DT2821_BDINIT 0x0001 /* (W) initialize board */
154
155 static const struct comedi_lrange range_dt282x_ai_lo_bipolar = {
156 4, {
157 RANGE(-10, 10),
158 RANGE(-5, 5),
159 RANGE(-2.5, 2.5),
160 RANGE(-1.25, 1.25)
161 }
162 };
163
164 static const struct comedi_lrange range_dt282x_ai_lo_unipolar = {
165 4, {
166 RANGE(0, 10),
167 RANGE(0, 5),
168 RANGE(0, 2.5),
169 RANGE(0, 1.25)
170 }
171 };
172
173 static const struct comedi_lrange range_dt282x_ai_5_bipolar = {
174 4, {
175 RANGE(-5, 5),
176 RANGE(-2.5, 2.5),
177 RANGE(-1.25, 1.25),
178 RANGE(-0.625, 0.625)
179 }
180 };
181
182 static const struct comedi_lrange range_dt282x_ai_5_unipolar = {
183 4, {
184 RANGE(0, 5),
185 RANGE(0, 2.5),
186 RANGE(0, 1.25),
187 RANGE(0, 0.625),
188 }
189 };
190
191 static const struct comedi_lrange range_dt282x_ai_hi_bipolar = {
192 4, {
193 RANGE(-10, 10),
194 RANGE(-1, 1),
195 RANGE(-0.1, 0.1),
196 RANGE(-0.02, 0.02)
197 }
198 };
199
200 static const struct comedi_lrange range_dt282x_ai_hi_unipolar = {
201 4, {
202 RANGE(0, 10),
203 RANGE(0, 1),
204 RANGE(0, 0.1),
205 RANGE(0, 0.02)
206 }
207 };
208
209 struct dt282x_board {
210 const char *name;
211 int adbits;
212 int adchan_se;
213 int adchan_di;
214 int ai_speed;
215 int ispgl;
216 int dachan;
217 int dabits;
218 };
219
220 struct dt282x_private {
221 int ad_2scomp; /* we have 2's comp jumper set */
222 int da0_2scomp; /* same, for DAC0 */
223 int da1_2scomp; /* same, for DAC1 */
224
225 const struct comedi_lrange *darangelist[2];
226
227 unsigned short ao[2];
228
229 volatile int dacsr; /* software copies of registers */
230 volatile int adcsr;
231 volatile int supcsr;
232
233 volatile int ntrig;
234 volatile int nread;
235
236 struct {
237 int chan;
238 unsigned short *buf; /* DMA buffer */
239 volatile int size; /* size of current transfer */
240 } dma[2];
241 int dma_maxsize; /* max size of DMA transfer (in bytes) */
242 int usedma; /* driver uses DMA */
243 volatile int current_dma_index;
244 int dma_dir;
245 };
246
247 /*
248 * Some useless abstractions
249 */
250 #define chan_to_DAC(a) ((a)&1)
251 #define mux_busy() (inw(dev->iobase+DT2821_ADCSR)&DT2821_MUXBUSY)
252 #define ad_done() (inw(dev->iobase+DT2821_ADCSR)&DT2821_ADDONE)
253
254 /*
255 * danger! macro abuse... a is the expression to wait on, and b is
256 * the statement(s) to execute if it doesn't happen.
257 */
258 #define wait_for(a, b) \
259 do { \
260 int _i; \
261 for (_i = 0; _i < DT2821_TIMEOUT; _i++) { \
262 if (a) { \
263 _i = 0; \
264 break; \
265 } \
266 udelay(5); \
267 } \
268 if (_i) { \
269 b \
270 } \
271 } while (0)
272
273 static int prep_ai_dma(struct comedi_device *dev, int chan, int size);
274 static int prep_ao_dma(struct comedi_device *dev, int chan, int size);
275 static int dt282x_ai_cancel(struct comedi_device *dev,
276 struct comedi_subdevice *s);
277 static int dt282x_ao_cancel(struct comedi_device *dev,
278 struct comedi_subdevice *s);
279 static int dt282x_ns_to_timer(int *nanosec, int round_mode);
280 static void dt282x_disable_dma(struct comedi_device *dev);
281
282 static int dt282x_grab_dma(struct comedi_device *dev, int dma1, int dma2);
283
284 static void dt282x_munge(struct comedi_device *dev, unsigned short *buf,
285 unsigned int nbytes)
286 {
287 const struct dt282x_board *board = comedi_board(dev);
288 struct dt282x_private *devpriv = dev->private;
289 unsigned int i;
290 unsigned short mask = (1 << board->adbits) - 1;
291 unsigned short sign = 1 << (board->adbits - 1);
292 int n;
293
294 if (devpriv->ad_2scomp)
295 sign = 1 << (board->adbits - 1);
296 else
297 sign = 0;
298
299 if (nbytes % 2)
300 comedi_error(dev, "bug! odd number of bytes from dma xfer");
301 n = nbytes / 2;
302 for (i = 0; i < n; i++)
303 buf[i] = (buf[i] & mask) ^ sign;
304 }
305
306 static void dt282x_ao_dma_interrupt(struct comedi_device *dev)
307 {
308 struct dt282x_private *devpriv = dev->private;
309 void *ptr;
310 int size;
311 int i;
312 struct comedi_subdevice *s = &dev->subdevices[1];
313
314 outw(devpriv->supcsr | DT2821_CLRDMADNE, dev->iobase + DT2821_SUPCSR);
315
316 if (!s->async->prealloc_buf) {
317 dev_err(dev->class_dev, "no buffer in %s\n", __func__);
318 return;
319 }
320
321 i = devpriv->current_dma_index;
322 ptr = devpriv->dma[i].buf;
323
324 disable_dma(devpriv->dma[i].chan);
325
326 devpriv->current_dma_index = 1 - i;
327
328 size = cfc_read_array_from_buffer(s, ptr, devpriv->dma_maxsize);
329 if (size == 0) {
330 dev_err(dev->class_dev, "AO underrun\n");
331 dt282x_ao_cancel(dev, s);
332 s->async->events |= COMEDI_CB_OVERFLOW;
333 return;
334 }
335 prep_ao_dma(dev, i, size);
336 return;
337 }
338
339 static void dt282x_ai_dma_interrupt(struct comedi_device *dev)
340 {
341 struct dt282x_private *devpriv = dev->private;
342 struct comedi_subdevice *s = dev->read_subdev;
343 void *ptr;
344 int size;
345 int i;
346 int ret;
347
348 outw(devpriv->supcsr | DT2821_CLRDMADNE, dev->iobase + DT2821_SUPCSR);
349
350 if (!s->async->prealloc_buf) {
351 dev_err(dev->class_dev, "no buffer in %s\n", __func__);
352 return;
353 }
354
355 i = devpriv->current_dma_index;
356 ptr = devpriv->dma[i].buf;
357 size = devpriv->dma[i].size;
358
359 disable_dma(devpriv->dma[i].chan);
360
361 devpriv->current_dma_index = 1 - i;
362
363 dt282x_munge(dev, ptr, size);
364 ret = cfc_write_array_to_buffer(s, ptr, size);
365 if (ret != size) {
366 dt282x_ai_cancel(dev, s);
367 return;
368 }
369 devpriv->nread -= size / 2;
370
371 if (devpriv->nread < 0) {
372 dev_info(dev->class_dev, "nread off by one\n");
373 devpriv->nread = 0;
374 }
375 if (!devpriv->nread) {
376 dt282x_ai_cancel(dev, s);
377 s->async->events |= COMEDI_CB_EOA;
378 return;
379 }
380 #if 0
381 /* clear the dual dma flag, making this the last dma segment */
382 /* XXX probably wrong */
383 if (!devpriv->ntrig) {
384 devpriv->supcsr &= ~(DT2821_DDMA);
385 outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR);
386 }
387 #endif
388 /* restart the channel */
389 prep_ai_dma(dev, i, 0);
390 }
391
392 static int prep_ai_dma(struct comedi_device *dev, int dma_index, int n)
393 {
394 struct dt282x_private *devpriv = dev->private;
395 int dma_chan;
396 unsigned long dma_ptr;
397 unsigned long flags;
398
399 if (!devpriv->ntrig)
400 return 0;
401
402 if (n == 0)
403 n = devpriv->dma_maxsize;
404 if (n > devpriv->ntrig * 2)
405 n = devpriv->ntrig * 2;
406 devpriv->ntrig -= n / 2;
407
408 devpriv->dma[dma_index].size = n;
409 dma_chan = devpriv->dma[dma_index].chan;
410 dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
411
412 set_dma_mode(dma_chan, DMA_MODE_READ);
413 flags = claim_dma_lock();
414 clear_dma_ff(dma_chan);
415 set_dma_addr(dma_chan, dma_ptr);
416 set_dma_count(dma_chan, n);
417 release_dma_lock(flags);
418
419 enable_dma(dma_chan);
420
421 return n;
422 }
423
424 static int prep_ao_dma(struct comedi_device *dev, int dma_index, int n)
425 {
426 struct dt282x_private *devpriv = dev->private;
427 int dma_chan;
428 unsigned long dma_ptr;
429 unsigned long flags;
430
431 devpriv->dma[dma_index].size = n;
432 dma_chan = devpriv->dma[dma_index].chan;
433 dma_ptr = virt_to_bus(devpriv->dma[dma_index].buf);
434
435 set_dma_mode(dma_chan, DMA_MODE_WRITE);
436 flags = claim_dma_lock();
437 clear_dma_ff(dma_chan);
438 set_dma_addr(dma_chan, dma_ptr);
439 set_dma_count(dma_chan, n);
440 release_dma_lock(flags);
441
442 enable_dma(dma_chan);
443
444 return n;
445 }
446
447 static irqreturn_t dt282x_interrupt(int irq, void *d)
448 {
449 struct comedi_device *dev = d;
450 struct dt282x_private *devpriv = dev->private;
451 struct comedi_subdevice *s = dev->read_subdev;
452 struct comedi_subdevice *s_ao;
453 unsigned int supcsr, adcsr, dacsr;
454 int handled = 0;
455
456 if (!dev->attached) {
457 comedi_error(dev, "spurious interrupt");
458 return IRQ_HANDLED;
459 }
460
461 s_ao = &dev->subdevices[1];
462 adcsr = inw(dev->iobase + DT2821_ADCSR);
463 dacsr = inw(dev->iobase + DT2821_DACSR);
464 supcsr = inw(dev->iobase + DT2821_SUPCSR);
465 if (supcsr & DT2821_DMAD) {
466 if (devpriv->dma_dir == DMA_MODE_READ)
467 dt282x_ai_dma_interrupt(dev);
468 else
469 dt282x_ao_dma_interrupt(dev);
470 handled = 1;
471 }
472 if (adcsr & DT2821_ADERR) {
473 if (devpriv->nread != 0) {
474 comedi_error(dev, "A/D error");
475 dt282x_ai_cancel(dev, s);
476 s->async->events |= COMEDI_CB_ERROR;
477 }
478 handled = 1;
479 }
480 if (dacsr & DT2821_DAERR) {
481 comedi_error(dev, "D/A error");
482 dt282x_ao_cancel(dev, s_ao);
483 s->async->events |= COMEDI_CB_ERROR;
484 handled = 1;
485 }
486 #if 0
487 if (adcsr & DT2821_ADDONE) {
488 int ret;
489 unsigned short data;
490
491 data = inw(dev->iobase + DT2821_ADDAT);
492 data &= (1 << board->adbits) - 1;
493
494 if (devpriv->ad_2scomp)
495 data ^= 1 << (board->adbits - 1);
496 ret = comedi_buf_put(s->async, data);
497
498 if (ret == 0)
499 s->async->events |= COMEDI_CB_OVERFLOW;
500
501 devpriv->nread--;
502 if (!devpriv->nread) {
503 s->async->events |= COMEDI_CB_EOA;
504 } else {
505 if (supcsr & DT2821_SCDN)
506 outw(devpriv->supcsr | DT2821_STRIG,
507 dev->iobase + DT2821_SUPCSR);
508 }
509 handled = 1;
510 }
511 #endif
512 comedi_event(dev, s);
513
514 return IRQ_RETVAL(handled);
515 }
516
517 static void dt282x_load_changain(struct comedi_device *dev, int n,
518 unsigned int *chanlist)
519 {
520 struct dt282x_private *devpriv = dev->private;
521 unsigned int i;
522 unsigned int chan, range;
523
524 outw(DT2821_LLE | (n - 1), dev->iobase + DT2821_CHANCSR);
525 for (i = 0; i < n; i++) {
526 chan = CR_CHAN(chanlist[i]);
527 range = CR_RANGE(chanlist[i]);
528 outw(devpriv->adcsr | (range << 4) | chan,
529 dev->iobase + DT2821_ADCSR);
530 }
531 outw(n - 1, dev->iobase + DT2821_CHANCSR);
532 }
533
534 /*
535 * Performs a single A/D conversion.
536 * - Put channel/gain into channel-gain list
537 * - preload multiplexer
538 * - trigger conversion and wait for it to finish
539 */
540 static int dt282x_ai_insn_read(struct comedi_device *dev,
541 struct comedi_subdevice *s,
542 struct comedi_insn *insn, unsigned int *data)
543 {
544 const struct dt282x_board *board = comedi_board(dev);
545 struct dt282x_private *devpriv = dev->private;
546 int i;
547
548 /* XXX should we really be enabling the ad clock here? */
549 devpriv->adcsr = DT2821_ADCLK;
550 outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR);
551
552 dt282x_load_changain(dev, 1, &insn->chanspec);
553
554 outw(devpriv->supcsr | DT2821_PRLD, dev->iobase + DT2821_SUPCSR);
555 wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME;);
556
557 for (i = 0; i < insn->n; i++) {
558 outw(devpriv->supcsr | DT2821_STRIG,
559 dev->iobase + DT2821_SUPCSR);
560 wait_for(ad_done(), comedi_error(dev, "timeout\n");
561 return -ETIME;);
562
563 data[i] =
564 inw(dev->iobase +
565 DT2821_ADDAT) & ((1 << board->adbits) - 1);
566 if (devpriv->ad_2scomp)
567 data[i] ^= (1 << (board->adbits - 1));
568 }
569
570 return i;
571 }
572
573 static int dt282x_ai_cmdtest(struct comedi_device *dev,
574 struct comedi_subdevice *s, struct comedi_cmd *cmd)
575 {
576 const struct dt282x_board *board = comedi_board(dev);
577 int err = 0;
578 int tmp;
579
580 /* Step 1 : check if triggers are trivially valid */
581
582 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
583 err |= cfc_check_trigger_src(&cmd->scan_begin_src,
584 TRIG_FOLLOW | TRIG_EXT);
585 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
586 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
587 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
588
589 if (err)
590 return 1;
591
592 /* Step 2a : make sure trigger sources are unique */
593
594 err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
595 err |= cfc_check_trigger_is_unique(cmd->stop_src);
596
597 /* Step 2b : and mutually compatible */
598
599 if (err)
600 return 2;
601
602 /* Step 3: check if arguments are trivially valid */
603
604 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
605
606 if (cmd->scan_begin_src == TRIG_FOLLOW) {
607 /* internal trigger */
608 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
609 } else {
610 /* external trigger */
611 /* should be level/edge, hi/lo specification here */
612 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
613 }
614
615 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 4000);
616
617 #define SLOWEST_TIMER (250*(1<<15)*255)
618 err |= cfc_check_trigger_arg_max(&cmd->convert_arg, SLOWEST_TIMER);
619 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, board->ai_speed);
620 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
621
622 if (cmd->stop_src == TRIG_COUNT) {
623 /* any count is allowed */
624 } else { /* TRIG_NONE */
625 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
626 }
627
628 if (err)
629 return 3;
630
631 /* step 4: fix up any arguments */
632
633 tmp = cmd->convert_arg;
634 dt282x_ns_to_timer(&cmd->convert_arg, cmd->flags & TRIG_ROUND_MASK);
635 if (tmp != cmd->convert_arg)
636 err++;
637
638 if (err)
639 return 4;
640
641 return 0;
642 }
643
644 static int dt282x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
645 {
646 const struct dt282x_board *board = comedi_board(dev);
647 struct dt282x_private *devpriv = dev->private;
648 struct comedi_cmd *cmd = &s->async->cmd;
649 int timer;
650
651 if (devpriv->usedma == 0) {
652 comedi_error(dev,
653 "driver requires 2 dma channels"
654 " to execute command");
655 return -EIO;
656 }
657
658 dt282x_disable_dma(dev);
659
660 if (cmd->convert_arg < board->ai_speed)
661 cmd->convert_arg = board->ai_speed;
662 timer = dt282x_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_NEAREST);
663 outw(timer, dev->iobase + DT2821_TMRCTR);
664
665 if (cmd->scan_begin_src == TRIG_FOLLOW) {
666 /* internal trigger */
667 devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0;
668 } else {
669 /* external trigger */
670 devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS0 | DT2821_DS1;
671 }
672 outw(devpriv->supcsr | DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_ADCINIT,
673 dev->iobase + DT2821_SUPCSR);
674
675 devpriv->ntrig = cmd->stop_arg * cmd->scan_end_arg;
676 devpriv->nread = devpriv->ntrig;
677
678 devpriv->dma_dir = DMA_MODE_READ;
679 devpriv->current_dma_index = 0;
680 prep_ai_dma(dev, 0, 0);
681 if (devpriv->ntrig) {
682 prep_ai_dma(dev, 1, 0);
683 devpriv->supcsr |= DT2821_DDMA;
684 outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR);
685 }
686
687 devpriv->adcsr = 0;
688
689 dt282x_load_changain(dev, cmd->chanlist_len, cmd->chanlist);
690
691 devpriv->adcsr = DT2821_ADCLK | DT2821_IADDONE;
692 outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR);
693
694 outw(devpriv->supcsr | DT2821_PRLD, dev->iobase + DT2821_SUPCSR);
695 wait_for(!mux_busy(), comedi_error(dev, "timeout\n"); return -ETIME;);
696
697 if (cmd->scan_begin_src == TRIG_FOLLOW) {
698 outw(devpriv->supcsr | DT2821_STRIG,
699 dev->iobase + DT2821_SUPCSR);
700 } else {
701 devpriv->supcsr |= DT2821_XTRIG;
702 outw(devpriv->supcsr, dev->iobase + DT2821_SUPCSR);
703 }
704
705 return 0;
706 }
707
708 static void dt282x_disable_dma(struct comedi_device *dev)
709 {
710 struct dt282x_private *devpriv = dev->private;
711
712 if (devpriv->usedma) {
713 disable_dma(devpriv->dma[0].chan);
714 disable_dma(devpriv->dma[1].chan);
715 }
716 }
717
718 static int dt282x_ai_cancel(struct comedi_device *dev,
719 struct comedi_subdevice *s)
720 {
721 struct dt282x_private *devpriv = dev->private;
722
723 dt282x_disable_dma(dev);
724
725 devpriv->adcsr = 0;
726 outw(devpriv->adcsr, dev->iobase + DT2821_ADCSR);
727
728 devpriv->supcsr = 0;
729 outw(devpriv->supcsr | DT2821_ADCINIT, dev->iobase + DT2821_SUPCSR);
730
731 return 0;
732 }
733
734 static int dt282x_ns_to_timer(int *nanosec, int round_mode)
735 {
736 int prescale, base, divider;
737
738 for (prescale = 0; prescale < 16; prescale++) {
739 if (prescale == 1)
740 continue;
741 base = 250 * (1 << prescale);
742 switch (round_mode) {
743 case TRIG_ROUND_NEAREST:
744 default:
745 divider = (*nanosec + base / 2) / base;
746 break;
747 case TRIG_ROUND_DOWN:
748 divider = (*nanosec) / base;
749 break;
750 case TRIG_ROUND_UP:
751 divider = (*nanosec + base - 1) / base;
752 break;
753 }
754 if (divider < 256) {
755 *nanosec = divider * base;
756 return (prescale << 8) | (255 - divider);
757 }
758 }
759 base = 250 * (1 << 15);
760 divider = 255;
761 *nanosec = divider * base;
762 return (15 << 8) | (255 - divider);
763 }
764
765 /*
766 * Analog output routine. Selects single channel conversion,
767 * selects correct channel, converts from 2's compliment to
768 * offset binary if necessary, loads the data into the DAC
769 * data register, and performs the conversion.
770 */
771 static int dt282x_ao_insn_read(struct comedi_device *dev,
772 struct comedi_subdevice *s,
773 struct comedi_insn *insn, unsigned int *data)
774 {
775 struct dt282x_private *devpriv = dev->private;
776
777 data[0] = devpriv->ao[CR_CHAN(insn->chanspec)];
778
779 return 1;
780 }
781
782 static int dt282x_ao_insn_write(struct comedi_device *dev,
783 struct comedi_subdevice *s,
784 struct comedi_insn *insn, unsigned int *data)
785 {
786 const struct dt282x_board *board = comedi_board(dev);
787 struct dt282x_private *devpriv = dev->private;
788 unsigned short d;
789 unsigned int chan;
790
791 chan = CR_CHAN(insn->chanspec);
792 d = data[0];
793 d &= (1 << board->dabits) - 1;
794 devpriv->ao[chan] = d;
795
796 devpriv->dacsr |= DT2821_SSEL;
797
798 if (chan) {
799 /* select channel */
800 devpriv->dacsr |= DT2821_YSEL;
801 if (devpriv->da0_2scomp)
802 d ^= (1 << (board->dabits - 1));
803 } else {
804 devpriv->dacsr &= ~DT2821_YSEL;
805 if (devpriv->da1_2scomp)
806 d ^= (1 << (board->dabits - 1));
807 }
808
809 outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
810
811 outw(d, dev->iobase + DT2821_DADAT);
812
813 outw(devpriv->supcsr | DT2821_DACON, dev->iobase + DT2821_SUPCSR);
814
815 return 1;
816 }
817
818 static int dt282x_ao_cmdtest(struct comedi_device *dev,
819 struct comedi_subdevice *s, struct comedi_cmd *cmd)
820 {
821 int err = 0;
822 int tmp;
823
824 /* Step 1 : check if triggers are trivially valid */
825
826 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
827 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
828 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
829 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
830 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
831
832 if (err)
833 return 1;
834
835 /* Step 2a : make sure trigger sources are unique */
836
837 err |= cfc_check_trigger_is_unique(cmd->stop_src);
838
839 /* Step 2b : and mutually compatible */
840
841 if (err)
842 return 2;
843
844 /* Step 3: check if arguments are trivially valid */
845
846 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
847 err |= cfc_check_trigger_arg_min(&cmd->scan_begin_arg, 5000);
848 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
849 err |= cfc_check_trigger_arg_max(&cmd->scan_end_arg, 2);
850
851 if (cmd->stop_src == TRIG_COUNT) {
852 /* any count is allowed */
853 } else { /* TRIG_NONE */
854 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
855 }
856
857 if (err)
858 return 3;
859
860 /* step 4: fix up any arguments */
861
862 tmp = cmd->scan_begin_arg;
863 dt282x_ns_to_timer(&cmd->scan_begin_arg, cmd->flags & TRIG_ROUND_MASK);
864 if (tmp != cmd->scan_begin_arg)
865 err++;
866
867 if (err)
868 return 4;
869
870 return 0;
871
872 }
873
874 static int dt282x_ao_inttrig(struct comedi_device *dev,
875 struct comedi_subdevice *s, unsigned int x)
876 {
877 struct dt282x_private *devpriv = dev->private;
878 int size;
879
880 if (x != 0)
881 return -EINVAL;
882
883 size = cfc_read_array_from_buffer(s, devpriv->dma[0].buf,
884 devpriv->dma_maxsize);
885 if (size == 0) {
886 dev_err(dev->class_dev, "AO underrun\n");
887 return -EPIPE;
888 }
889 prep_ao_dma(dev, 0, size);
890
891 size = cfc_read_array_from_buffer(s, devpriv->dma[1].buf,
892 devpriv->dma_maxsize);
893 if (size == 0) {
894 dev_err(dev->class_dev, "AO underrun\n");
895 return -EPIPE;
896 }
897 prep_ao_dma(dev, 1, size);
898
899 outw(devpriv->supcsr | DT2821_STRIG, dev->iobase + DT2821_SUPCSR);
900 s->async->inttrig = NULL;
901
902 return 1;
903 }
904
905 static int dt282x_ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
906 {
907 struct dt282x_private *devpriv = dev->private;
908 int timer;
909 struct comedi_cmd *cmd = &s->async->cmd;
910
911 if (devpriv->usedma == 0) {
912 comedi_error(dev,
913 "driver requires 2 dma channels"
914 " to execute command");
915 return -EIO;
916 }
917
918 dt282x_disable_dma(dev);
919
920 devpriv->supcsr = DT2821_ERRINTEN | DT2821_DS1 | DT2821_DDMA;
921 outw(devpriv->supcsr | DT2821_CLRDMADNE | DT2821_BUFFB | DT2821_DACINIT,
922 dev->iobase + DT2821_SUPCSR);
923
924 devpriv->ntrig = cmd->stop_arg * cmd->chanlist_len;
925 devpriv->nread = devpriv->ntrig;
926
927 devpriv->dma_dir = DMA_MODE_WRITE;
928 devpriv->current_dma_index = 0;
929
930 timer = dt282x_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_NEAREST);
931 outw(timer, dev->iobase + DT2821_TMRCTR);
932
933 devpriv->dacsr = DT2821_SSEL | DT2821_DACLK | DT2821_IDARDY;
934 outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
935
936 s->async->inttrig = dt282x_ao_inttrig;
937
938 return 0;
939 }
940
941 static int dt282x_ao_cancel(struct comedi_device *dev,
942 struct comedi_subdevice *s)
943 {
944 struct dt282x_private *devpriv = dev->private;
945
946 dt282x_disable_dma(dev);
947
948 devpriv->dacsr = 0;
949 outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
950
951 devpriv->supcsr = 0;
952 outw(devpriv->supcsr | DT2821_DACINIT, dev->iobase + DT2821_SUPCSR);
953
954 return 0;
955 }
956
957 static int dt282x_dio_insn_bits(struct comedi_device *dev,
958 struct comedi_subdevice *s,
959 struct comedi_insn *insn,
960 unsigned int *data)
961 {
962 if (comedi_dio_update_state(s, data))
963 outw(s->state, dev->iobase + DT2821_DIODAT);
964
965 data[1] = inw(dev->iobase + DT2821_DIODAT);
966
967 return insn->n;
968 }
969
970 static int dt282x_dio_insn_config(struct comedi_device *dev,
971 struct comedi_subdevice *s,
972 struct comedi_insn *insn,
973 unsigned int *data)
974 {
975 struct dt282x_private *devpriv = dev->private;
976 unsigned int chan = CR_CHAN(insn->chanspec);
977 unsigned int mask;
978 int ret;
979
980 if (chan < 8)
981 mask = 0x00ff;
982 else
983 mask = 0xff00;
984
985 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
986 if (ret)
987 return ret;
988
989 devpriv->dacsr &= ~(DT2821_LBOE | DT2821_HBOE);
990 if (s->io_bits & 0x00ff)
991 devpriv->dacsr |= DT2821_LBOE;
992 if (s->io_bits & 0xff00)
993 devpriv->dacsr |= DT2821_HBOE;
994
995 outw(devpriv->dacsr, dev->iobase + DT2821_DACSR);
996
997 return insn->n;
998 }
999
1000 static const struct comedi_lrange *const ai_range_table[] = {
1001 &range_dt282x_ai_lo_bipolar,
1002 &range_dt282x_ai_lo_unipolar,
1003 &range_dt282x_ai_5_bipolar,
1004 &range_dt282x_ai_5_unipolar
1005 };
1006
1007 static const struct comedi_lrange *const ai_range_pgl_table[] = {
1008 &range_dt282x_ai_hi_bipolar,
1009 &range_dt282x_ai_hi_unipolar
1010 };
1011
1012 static const struct comedi_lrange *opt_ai_range_lkup(int ispgl, int x)
1013 {
1014 if (ispgl) {
1015 if (x < 0 || x >= 2)
1016 x = 0;
1017 return ai_range_pgl_table[x];
1018 } else {
1019 if (x < 0 || x >= 4)
1020 x = 0;
1021 return ai_range_table[x];
1022 }
1023 }
1024
1025 static const struct comedi_lrange *const ao_range_table[] = {
1026 &range_bipolar10,
1027 &range_unipolar10,
1028 &range_bipolar5,
1029 &range_unipolar5,
1030 &range_bipolar2_5
1031 };
1032
1033 static const struct comedi_lrange *opt_ao_range_lkup(int x)
1034 {
1035 if (x < 0 || x >= 5)
1036 x = 0;
1037 return ao_range_table[x];
1038 }
1039
1040 enum { /* i/o base, irq, dma channels */
1041 opt_iobase = 0, opt_irq, opt_dma1, opt_dma2,
1042 opt_diff, /* differential */
1043 opt_ai_twos, opt_ao0_twos, opt_ao1_twos, /* twos comp */
1044 opt_ai_range, opt_ao0_range, opt_ao1_range, /* range */
1045 };
1046
1047 static int dt282x_grab_dma(struct comedi_device *dev, int dma1, int dma2)
1048 {
1049 struct dt282x_private *devpriv = dev->private;
1050 int ret;
1051
1052 devpriv->usedma = 0;
1053
1054 if (!dma1 && !dma2)
1055 return 0;
1056
1057 if (dma1 == dma2 || dma1 < 5 || dma2 < 5 || dma1 > 7 || dma2 > 7)
1058 return -EINVAL;
1059
1060 if (dma2 < dma1) {
1061 int i;
1062 i = dma1;
1063 dma1 = dma2;
1064 dma2 = i;
1065 }
1066
1067 ret = request_dma(dma1, "dt282x A");
1068 if (ret)
1069 return -EBUSY;
1070 devpriv->dma[0].chan = dma1;
1071
1072 ret = request_dma(dma2, "dt282x B");
1073 if (ret)
1074 return -EBUSY;
1075 devpriv->dma[1].chan = dma2;
1076
1077 devpriv->dma_maxsize = PAGE_SIZE;
1078 devpriv->dma[0].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1079 devpriv->dma[1].buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
1080 if (!devpriv->dma[0].buf || !devpriv->dma[1].buf)
1081 return -ENOMEM;
1082
1083 devpriv->usedma = 1;
1084
1085 return 0;
1086 }
1087
1088 /*
1089 options:
1090 0 i/o base
1091 1 irq
1092 2 dma1
1093 3 dma2
1094 4 0=single ended, 1=differential
1095 5 ai 0=straight binary, 1=2's comp
1096 6 ao0 0=straight binary, 1=2's comp
1097 7 ao1 0=straight binary, 1=2's comp
1098 8 ai 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V
1099 9 ao0 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1100 10 ao1 0=±10 V, 1=0-10 V, 2=±5 V, 3=0-5 V, 4=±2.5 V
1101 */
1102 static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1103 {
1104 const struct dt282x_board *board = comedi_board(dev);
1105 struct dt282x_private *devpriv;
1106 struct comedi_subdevice *s;
1107 int ret;
1108 int i;
1109
1110 ret = comedi_request_region(dev, it->options[0], DT2821_SIZE);
1111 if (ret)
1112 return ret;
1113
1114 outw(DT2821_BDINIT, dev->iobase + DT2821_SUPCSR);
1115 i = inw(dev->iobase + DT2821_ADCSR);
1116
1117 if (((inw(dev->iobase + DT2821_ADCSR) & DT2821_ADCSR_MASK)
1118 != DT2821_ADCSR_VAL) ||
1119 ((inw(dev->iobase + DT2821_CHANCSR) & DT2821_CHANCSR_MASK)
1120 != DT2821_CHANCSR_VAL) ||
1121 ((inw(dev->iobase + DT2821_DACSR) & DT2821_DACSR_MASK)
1122 != DT2821_DACSR_VAL) ||
1123 ((inw(dev->iobase + DT2821_SUPCSR) & DT2821_SUPCSR_MASK)
1124 != DT2821_SUPCSR_VAL) ||
1125 ((inw(dev->iobase + DT2821_TMRCTR) & DT2821_TMRCTR_MASK)
1126 != DT2821_TMRCTR_VAL)) {
1127 dev_err(dev->class_dev, "board not found\n");
1128 return -EIO;
1129 }
1130 /* should do board test */
1131
1132 if (it->options[opt_irq] > 0) {
1133 ret = request_irq(it->options[opt_irq], dt282x_interrupt, 0,
1134 dev->board_name, dev);
1135 if (ret == 0)
1136 dev->irq = it->options[opt_irq];
1137 }
1138
1139 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1140 if (!devpriv)
1141 return -ENOMEM;
1142
1143 if (dev->irq) {
1144 ret = dt282x_grab_dma(dev, it->options[opt_dma1],
1145 it->options[opt_dma2]);
1146 if (ret < 0)
1147 return ret;
1148 }
1149
1150 ret = comedi_alloc_subdevices(dev, 3);
1151 if (ret)
1152 return ret;
1153
1154 s = &dev->subdevices[0];
1155
1156 /* ai subdevice */
1157 s->type = COMEDI_SUBD_AI;
1158 s->subdev_flags = SDF_READABLE |
1159 ((it->options[opt_diff]) ? SDF_DIFF : SDF_COMMON);
1160 s->n_chan =
1161 (it->options[opt_diff]) ? board->adchan_di : board->adchan_se;
1162 s->insn_read = dt282x_ai_insn_read;
1163 s->maxdata = (1 << board->adbits) - 1;
1164 s->range_table =
1165 opt_ai_range_lkup(board->ispgl, it->options[opt_ai_range]);
1166 devpriv->ad_2scomp = it->options[opt_ai_twos];
1167 if (dev->irq) {
1168 dev->read_subdev = s;
1169 s->subdev_flags |= SDF_CMD_READ;
1170 s->len_chanlist = 16;
1171 s->do_cmdtest = dt282x_ai_cmdtest;
1172 s->do_cmd = dt282x_ai_cmd;
1173 s->cancel = dt282x_ai_cancel;
1174 }
1175
1176 s = &dev->subdevices[1];
1177
1178 s->n_chan = board->dachan;
1179 if (s->n_chan) {
1180 /* ao subsystem */
1181 s->type = COMEDI_SUBD_AO;
1182 s->subdev_flags = SDF_WRITABLE;
1183 s->insn_read = dt282x_ao_insn_read;
1184 s->insn_write = dt282x_ao_insn_write;
1185 s->maxdata = (1 << board->dabits) - 1;
1186 s->range_table_list = devpriv->darangelist;
1187 devpriv->darangelist[0] =
1188 opt_ao_range_lkup(it->options[opt_ao0_range]);
1189 devpriv->darangelist[1] =
1190 opt_ao_range_lkup(it->options[opt_ao1_range]);
1191 devpriv->da0_2scomp = it->options[opt_ao0_twos];
1192 devpriv->da1_2scomp = it->options[opt_ao1_twos];
1193 if (dev->irq) {
1194 dev->write_subdev = s;
1195 s->subdev_flags |= SDF_CMD_WRITE;
1196 s->len_chanlist = 2;
1197 s->do_cmdtest = dt282x_ao_cmdtest;
1198 s->do_cmd = dt282x_ao_cmd;
1199 s->cancel = dt282x_ao_cancel;
1200 }
1201 } else {
1202 s->type = COMEDI_SUBD_UNUSED;
1203 }
1204
1205 s = &dev->subdevices[2];
1206 /* dio subsystem */
1207 s->type = COMEDI_SUBD_DIO;
1208 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1209 s->n_chan = 16;
1210 s->insn_bits = dt282x_dio_insn_bits;
1211 s->insn_config = dt282x_dio_insn_config;
1212 s->maxdata = 1;
1213 s->range_table = &range_digital;
1214
1215 return 0;
1216 }
1217
1218 static void dt282x_detach(struct comedi_device *dev)
1219 {
1220 struct dt282x_private *devpriv = dev->private;
1221
1222 if (dev->private) {
1223 if (devpriv->dma[0].chan)
1224 free_dma(devpriv->dma[0].chan);
1225 if (devpriv->dma[1].chan)
1226 free_dma(devpriv->dma[1].chan);
1227 if (devpriv->dma[0].buf)
1228 free_page((unsigned long)devpriv->dma[0].buf);
1229 if (devpriv->dma[1].buf)
1230 free_page((unsigned long)devpriv->dma[1].buf);
1231 }
1232 comedi_legacy_detach(dev);
1233 }
1234
1235 static const struct dt282x_board boardtypes[] = {
1236 {
1237 .name = "dt2821",
1238 .adbits = 12,
1239 .adchan_se = 16,
1240 .adchan_di = 8,
1241 .ai_speed = 20000,
1242 .ispgl = 0,
1243 .dachan = 2,
1244 .dabits = 12,
1245 }, {
1246 .name = "dt2821-f",
1247 .adbits = 12,
1248 .adchan_se = 16,
1249 .adchan_di = 8,
1250 .ai_speed = 6500,
1251 .ispgl = 0,
1252 .dachan = 2,
1253 .dabits = 12,
1254 }, {
1255 .name = "dt2821-g",
1256 .adbits = 12,
1257 .adchan_se = 16,
1258 .adchan_di = 8,
1259 .ai_speed = 4000,
1260 .ispgl = 0,
1261 .dachan = 2,
1262 .dabits = 12,
1263 }, {
1264 .name = "dt2823",
1265 .adbits = 16,
1266 .adchan_se = 0,
1267 .adchan_di = 4,
1268 .ai_speed = 10000,
1269 .ispgl = 0,
1270 .dachan = 2,
1271 .dabits = 16,
1272 }, {
1273 .name = "dt2824-pgh",
1274 .adbits = 12,
1275 .adchan_se = 16,
1276 .adchan_di = 8,
1277 .ai_speed = 20000,
1278 .ispgl = 0,
1279 .dachan = 0,
1280 .dabits = 0,
1281 }, {
1282 .name = "dt2824-pgl",
1283 .adbits = 12,
1284 .adchan_se = 16,
1285 .adchan_di = 8,
1286 .ai_speed = 20000,
1287 .ispgl = 1,
1288 .dachan = 0,
1289 .dabits = 0,
1290 }, {
1291 .name = "dt2825",
1292 .adbits = 12,
1293 .adchan_se = 16,
1294 .adchan_di = 8,
1295 .ai_speed = 20000,
1296 .ispgl = 1,
1297 .dachan = 2,
1298 .dabits = 12,
1299 }, {
1300 .name = "dt2827",
1301 .adbits = 16,
1302 .adchan_se = 0,
1303 .adchan_di = 4,
1304 .ai_speed = 10000,
1305 .ispgl = 0,
1306 .dachan = 2,
1307 .dabits = 12,
1308 }, {
1309 .name = "dt2828",
1310 .adbits = 12,
1311 .adchan_se = 4,
1312 .adchan_di = 0,
1313 .ai_speed = 10000,
1314 .ispgl = 0,
1315 .dachan = 2,
1316 .dabits = 12,
1317 }, {
1318 .name = "dt2829",
1319 .adbits = 16,
1320 .adchan_se = 8,
1321 .adchan_di = 0,
1322 .ai_speed = 33250,
1323 .ispgl = 0,
1324 .dachan = 2,
1325 .dabits = 16,
1326 }, {
1327 .name = "dt21-ez",
1328 .adbits = 12,
1329 .adchan_se = 16,
1330 .adchan_di = 8,
1331 .ai_speed = 10000,
1332 .ispgl = 0,
1333 .dachan = 2,
1334 .dabits = 12,
1335 }, {
1336 .name = "dt23-ez",
1337 .adbits = 16,
1338 .adchan_se = 16,
1339 .adchan_di = 8,
1340 .ai_speed = 10000,
1341 .ispgl = 0,
1342 .dachan = 0,
1343 .dabits = 0,
1344 }, {
1345 .name = "dt24-ez",
1346 .adbits = 12,
1347 .adchan_se = 16,
1348 .adchan_di = 8,
1349 .ai_speed = 10000,
1350 .ispgl = 0,
1351 .dachan = 0,
1352 .dabits = 0,
1353 }, {
1354 .name = "dt24-ez-pgl",
1355 .adbits = 12,
1356 .adchan_se = 16,
1357 .adchan_di = 8,
1358 .ai_speed = 10000,
1359 .ispgl = 1,
1360 .dachan = 0,
1361 .dabits = 0,
1362 },
1363 };
1364
1365 static struct comedi_driver dt282x_driver = {
1366 .driver_name = "dt282x",
1367 .module = THIS_MODULE,
1368 .attach = dt282x_attach,
1369 .detach = dt282x_detach,
1370 .board_name = &boardtypes[0].name,
1371 .num_names = ARRAY_SIZE(boardtypes),
1372 .offset = sizeof(struct dt282x_board),
1373 };
1374 module_comedi_driver(dt282x_driver);
1375
1376 MODULE_AUTHOR("Comedi http://www.comedi.org");
1377 MODULE_DESCRIPTION("Comedi low-level driver");
1378 MODULE_LICENSE("GPL");
This page took 0.093866 seconds and 5 git commands to generate.