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