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