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