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