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