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