das800.c revision 25436dc9d84f1be60ff549c9ab712bba2835f284
1/*
2    comedi/drivers/das800.c
3    Driver for Keitley das800 series boards and compatibles
4    Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5
6    COMEDI - Linux Control and Measurement Device Interface
7    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23************************************************************************
24*/
25/*
26Driver: das800
27Description: Keithley Metrabyte DAS800 (& compatibles)
28Author: Frank Mori Hess <fmhess@users.sourceforge.net>
29Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
30  DAS-802 (das-802),
31  [Measurement Computing] CIO-DAS800 (cio-das800),
32  CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
33  CIO-DAS802/16 (cio-das802/16)
34Status: works, cio-das802/16 untested - email me if you have tested it
35
36Configuration options:
37  [0] - I/O port base address
38  [1] - IRQ (optional, required for timed or externally triggered conversions)
39
40Notes:
41	IRQ can be omitted, although the cmd interface will not work without it.
42
43	All entries in the channel/gain list must use the same gain and be
44	consecutive channels counting upwards in channel number (these are
45	hardware limitations.)
46
47	I've never tested the gain setting stuff since I only have a
48	DAS-800 board with fixed gain.
49
50	The cio-das802/16 does not have a fifo-empty status bit!  Therefore
51	only fifo-half-full transfers are possible with this card.
52*/
53/*
54
55cmd triggers supported:
56	start_src:      TRIG_NOW | TRIG_EXT
57	scan_begin_src: TRIG_FOLLOW
58	scan_end_src:   TRIG_COUNT
59	convert_src:    TRIG_TIMER | TRIG_EXT
60	stop_src:       TRIG_NONE | TRIG_COUNT
61
62
63*/
64
65#include <linux/interrupt.h>
66#include "../comedidev.h"
67
68#include <linux/ioport.h>
69#include <linux/delay.h>
70
71#include "8253.h"
72#include "comedi_fc.h"
73
74#define DAS800_SIZE           8
75#define TIMER_BASE            1000
76#define N_CHAN_AI             8	/*  number of analog input channels */
77
78/* Registers for the das800 */
79
80#define DAS800_LSB            0
81#define   FIFO_EMPTY            0x1
82#define   FIFO_OVF              0x2
83#define DAS800_MSB            1
84#define DAS800_CONTROL1       2
85#define   CONTROL1_INTE         0x8
86#define DAS800_CONV_CONTROL   2
87#define   ITE                   0x1
88#define   CASC                  0x2
89#define   DTEN                  0x4
90#define   IEOC                  0x8
91#define   EACS                  0x10
92#define   CONV_HCEN             0x80
93#define DAS800_SCAN_LIMITS    2
94#define DAS800_STATUS         2
95#define   IRQ                   0x8
96#define   BUSY                  0x80
97#define DAS800_GAIN           3
98#define   CIO_FFOV              0x8	/*  fifo overflow for cio-das802/16 */
99#define   CIO_ENHF              0x90	/*  interrupt fifo half full for cio-das802/16 */
100#define   CONTROL1              0x80
101#define   CONV_CONTROL          0xa0
102#define   SCAN_LIMITS           0xc0
103#define   ID                    0xe0
104#define DAS800_8254           4
105#define DAS800_STATUS2        7
106#define   STATUS2_HCEN          0x80
107#define   STATUS2_INTE          0X20
108#define DAS800_ID             7
109
110struct das800_board {
111	const char *name;
112	int ai_speed;
113	const struct comedi_lrange *ai_range;
114	int resolution;
115};
116
117/* analog input ranges */
118static const struct comedi_lrange range_das800_ai = {
119	1,
120	{
121			RANGE(-5, 5),
122		}
123};
124
125static const struct comedi_lrange range_das801_ai = {
126	9,
127	{
128			RANGE(-5, 5),
129			RANGE(-10, 10),
130			RANGE(0, 10),
131			RANGE(-0.5, 0.5),
132			RANGE(0, 1),
133			RANGE(-0.05, 0.05),
134			RANGE(0, 0.1),
135			RANGE(-0.01, 0.01),
136			RANGE(0, 0.02),
137		}
138};
139
140static const struct comedi_lrange range_cio_das801_ai = {
141	9,
142	{
143			RANGE(-5, 5),
144			RANGE(-10, 10),
145			RANGE(0, 10),
146			RANGE(-0.5, 0.5),
147			RANGE(0, 1),
148			RANGE(-0.05, 0.05),
149			RANGE(0, 0.1),
150			RANGE(-0.005, 0.005),
151			RANGE(0, 0.01),
152		}
153};
154
155static const struct comedi_lrange range_das802_ai = {
156	9,
157	{
158			RANGE(-5, 5),
159			RANGE(-10, 10),
160			RANGE(0, 10),
161			RANGE(-2.5, 2.5),
162			RANGE(0, 5),
163			RANGE(-1.25, 1.25),
164			RANGE(0, 2.5),
165			RANGE(-0.625, 0.625),
166			RANGE(0, 1.25),
167		}
168};
169
170static const struct comedi_lrange range_das80216_ai = {
171	8,
172	{
173			RANGE(-10, 10),
174			RANGE(0, 10),
175			RANGE(-5, 5),
176			RANGE(0, 5),
177			RANGE(-2.5, 2.5),
178			RANGE(0, 2.5),
179			RANGE(-1.25, 1.25),
180			RANGE(0, 1.25),
181		}
182};
183
184enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
185
186static const struct das800_board das800_boards[] = {
187	{
188	.name = "das-800",
189	.ai_speed = 25000,
190	.ai_range = &range_das800_ai,
191	.resolution = 12,
192		},
193	{
194	.name = "cio-das800",
195	.ai_speed = 20000,
196	.ai_range = &range_das800_ai,
197	.resolution = 12,
198		},
199	{
200	.name = "das-801",
201	.ai_speed = 25000,
202	.ai_range = &range_das801_ai,
203	.resolution = 12,
204		},
205	{
206	.name = "cio-das801",
207	.ai_speed = 20000,
208	.ai_range = &range_cio_das801_ai,
209	.resolution = 12,
210		},
211	{
212	.name = "das-802",
213	.ai_speed = 25000,
214	.ai_range = &range_das802_ai,
215	.resolution = 12,
216		},
217	{
218	.name = "cio-das802",
219	.ai_speed = 20000,
220	.ai_range = &range_das802_ai,
221	.resolution = 12,
222		},
223	{
224	.name = "cio-das802/16",
225	.ai_speed = 10000,
226	.ai_range = &range_das80216_ai,
227	.resolution = 16,
228		},
229};
230
231/*
232 * Useful for shorthand access to the particular board structure
233 */
234#define thisboard ((const struct das800_board *)dev->board_ptr)
235
236struct das800_private {
237	volatile unsigned int count;	/* number of data points left to be taken */
238	volatile int forever;	/* flag indicating whether we should take data forever */
239	unsigned int divisor1;	/* value to load into board's counter 1 for timed conversions */
240	unsigned int divisor2;	/* value to load into board's counter 2 for timed conversions */
241	volatile int do_bits;	/* digital output bits */
242};
243
244#define devpriv ((struct das800_private *)dev->private)
245
246static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it);
247static int das800_detach(struct comedi_device *dev);
248static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
249
250static struct comedi_driver driver_das800 = {
251	.driver_name = "das800",
252	.module = THIS_MODULE,
253	.attach = das800_attach,
254	.detach = das800_detach,
255	.num_names = ARRAY_SIZE(das800_boards),
256	.board_name = &das800_boards[0].name,
257	.offset = sizeof(struct das800_board),
258};
259
260static irqreturn_t das800_interrupt(int irq, void *d);
261static void enable_das800(struct comedi_device *dev);
262static void disable_das800(struct comedi_device *dev);
263static int das800_ai_do_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
264	struct comedi_cmd *cmd);
265static int das800_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
266static int das800_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
267	struct comedi_insn *insn, unsigned int *data);
268static int das800_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
269	struct comedi_insn *insn, unsigned int *data);
270static int das800_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
271	struct comedi_insn *insn, unsigned int *data);
272static int das800_probe(struct comedi_device *dev);
273static int das800_set_frequency(struct comedi_device *dev);
274
275/* checks and probes das-800 series board type */
276static int das800_probe(struct comedi_device *dev)
277{
278	int id_bits;
279	unsigned long irq_flags;
280	int board;
281
282	/*  'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
283	spin_lock_irqsave(&dev->spinlock, irq_flags);
284	outb(ID, dev->iobase + DAS800_GAIN);	/* select base address + 7 to be ID register */
285	id_bits = inb(dev->iobase + DAS800_ID) & 0x3;	/* get id bits */
286	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
287
288	board = thisboard - das800_boards;
289
290	switch (id_bits) {
291	case 0x0:
292		if (board == das800) {
293			printk(" Board model: DAS-800\n");
294			return board;
295		}
296		if (board == ciodas800) {
297			printk(" Board model: CIO-DAS800\n");
298			return board;
299		}
300		printk(" Board model (probed): DAS-800\n");
301		return das800;
302		break;
303	case 0x2:
304		if (board == das801) {
305			printk(" Board model: DAS-801\n");
306			return board;
307		}
308		if (board == ciodas801) {
309			printk(" Board model: CIO-DAS801\n");
310			return board;
311		}
312		printk(" Board model (probed): DAS-801\n");
313		return das801;
314		break;
315	case 0x3:
316		if (board == das802) {
317			printk(" Board model: DAS-802\n");
318			return board;
319		}
320		if (board == ciodas802) {
321			printk(" Board model: CIO-DAS802\n");
322			return board;
323		}
324		if (board == ciodas80216) {
325			printk(" Board model: CIO-DAS802/16\n");
326			return board;
327		}
328		printk(" Board model (probed): DAS-802\n");
329		return das802;
330		break;
331	default:
332		printk(" Board model: probe returned 0x%x (unknown)\n",
333			id_bits);
334		return board;
335		break;
336	}
337	return -1;
338}
339
340/*
341 * A convenient macro that defines init_module() and cleanup_module(),
342 * as necessary.
343 */
344COMEDI_INITCLEANUP(driver_das800);
345
346/* interrupt service routine */
347static irqreturn_t das800_interrupt(int irq, void *d)
348{
349	short i;		/* loop index */
350	short dataPoint = 0;
351	struct comedi_device *dev = d;
352	struct comedi_subdevice *s = dev->read_subdev;	/* analog input subdevice */
353	struct comedi_async *async;
354	int status;
355	unsigned long irq_flags;
356	static const int max_loops = 128;	/*  half-fifo size for cio-das802/16 */
357	/*  flags */
358	int fifo_empty = 0;
359	int fifo_overflow = 0;
360
361	status = inb(dev->iobase + DAS800_STATUS);
362	/* if interrupt was not generated by board or driver not attached, quit */
363	if (!(status & IRQ))
364		return IRQ_NONE;
365	if (!(dev->attached))
366		return IRQ_HANDLED;
367
368	/* wait until here to initialize async, since we will get null dereference
369	 * if interrupt occurs before driver is fully attached!
370	 */
371	async = s->async;
372
373	/*  if hardware conversions are not enabled, then quit */
374	spin_lock_irqsave(&dev->spinlock, irq_flags);
375	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select base address + 7 to be STATUS2 register */
376	status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
377	/* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
378	if (status == 0) {
379		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
380		return IRQ_HANDLED;
381	}
382
383	/* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
384	for (i = 0; i < max_loops; i++) {
385		/* read 16 bits from dev->iobase and dev->iobase + 1 */
386		dataPoint = inb(dev->iobase + DAS800_LSB);
387		dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
388		if (thisboard->resolution == 12) {
389			fifo_empty = dataPoint & FIFO_EMPTY;
390			fifo_overflow = dataPoint & FIFO_OVF;
391			if (fifo_overflow)
392				break;
393		} else {
394			fifo_empty = 0;	/*  cio-das802/16 has no fifo empty status bit */
395		}
396		if (fifo_empty) {
397			break;
398		}
399		/* strip off extraneous bits for 12 bit cards */
400		if (thisboard->resolution == 12)
401			dataPoint = (dataPoint >> 4) & 0xfff;
402		/* if there are more data points to collect */
403		if (devpriv->count > 0 || devpriv->forever == 1) {
404			/* write data point to buffer */
405			cfc_write_to_buffer(s, dataPoint);
406			if (devpriv->count > 0)
407				devpriv->count--;
408		}
409	}
410	async->events |= COMEDI_CB_BLOCK;
411	/* check for fifo overflow */
412	if (thisboard->resolution == 12) {
413		fifo_overflow = dataPoint & FIFO_OVF;
414		/*  else cio-das802/16 */
415	} else {
416		fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
417	}
418	if (fifo_overflow) {
419		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
420		comedi_error(dev, "DAS800 FIFO overflow");
421		das800_cancel(dev, dev->subdevices + 0);
422		async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
423		comedi_event(dev, s);
424		async->events = 0;
425		return IRQ_HANDLED;
426	}
427	if (devpriv->count > 0 || devpriv->forever == 1) {
428		/* Re-enable card's interrupt.
429		 * We already have spinlock, so indirect addressing is safe */
430		outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
431		outb(CONTROL1_INTE | devpriv->do_bits,
432			dev->iobase + DAS800_CONTROL1);
433		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
434		/* otherwise, stop taking data */
435	} else {
436		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
437		disable_das800(dev);	/* diable hardware triggered conversions */
438		async->events |= COMEDI_CB_EOA;
439	}
440	comedi_event(dev, s);
441	async->events = 0;
442	return IRQ_HANDLED;
443}
444
445static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
446{
447	struct comedi_subdevice *s;
448	unsigned long iobase = it->options[0];
449	unsigned int irq = it->options[1];
450	unsigned long irq_flags;
451	int board;
452
453	printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
454	if (irq) {
455		printk(", irq %u", irq);
456	}
457	printk("\n");
458
459	/* allocate and initialize dev->private */
460	if (alloc_private(dev, sizeof(struct das800_private)) < 0)
461		return -ENOMEM;
462
463	if (iobase == 0) {
464		printk("io base address required for das800\n");
465		return -EINVAL;
466	}
467
468	/* check if io addresses are available */
469	if (!request_region(iobase, DAS800_SIZE, "das800")) {
470		printk("I/O port conflict\n");
471		return -EIO;
472	}
473	dev->iobase = iobase;
474
475	board = das800_probe(dev);
476	if (board < 0) {
477		printk("unable to determine board type\n");
478		return -ENODEV;
479	}
480	dev->board_ptr = das800_boards + board;
481
482	/* grab our IRQ */
483	if (irq == 1 || irq > 7) {
484		printk("irq out of range\n");
485		return -EINVAL;
486	}
487	if (irq) {
488		if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
489			printk("unable to allocate irq %u\n", irq);
490			return -EINVAL;
491		}
492	}
493	dev->irq = irq;
494
495	dev->board_name = thisboard->name;
496
497	if (alloc_subdevices(dev, 3) < 0)
498		return -ENOMEM;
499
500	/* analog input subdevice */
501	s = dev->subdevices + 0;
502	dev->read_subdev = s;
503	s->type = COMEDI_SUBD_AI;
504	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
505	s->n_chan = 8;
506	s->len_chanlist = 8;
507	s->maxdata = (1 << thisboard->resolution) - 1;
508	s->range_table = thisboard->ai_range;
509	s->do_cmd = das800_ai_do_cmd;
510	s->do_cmdtest = das800_ai_do_cmdtest;
511	s->insn_read = das800_ai_rinsn;
512	s->cancel = das800_cancel;
513
514	/* di */
515	s = dev->subdevices + 1;
516	s->type = COMEDI_SUBD_DI;
517	s->subdev_flags = SDF_READABLE;
518	s->n_chan = 3;
519	s->maxdata = 1;
520	s->range_table = &range_digital;
521	s->insn_bits = das800_di_rbits;
522
523	/* do */
524	s = dev->subdevices + 2;
525	s->type = COMEDI_SUBD_DO;
526	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
527	s->n_chan = 4;
528	s->maxdata = 1;
529	s->range_table = &range_digital;
530	s->insn_bits = das800_do_wbits;
531
532	disable_das800(dev);
533
534	/* initialize digital out channels */
535	spin_lock_irqsave(&dev->spinlock, irq_flags);
536	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
537	outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
538	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
539
540	return 0;
541};
542
543static int das800_detach(struct comedi_device *dev)
544{
545	printk("comedi%d: das800: remove\n", dev->minor);
546
547	/* only free stuff if it has been allocated by _attach */
548	if (dev->iobase)
549		release_region(dev->iobase, DAS800_SIZE);
550	if (dev->irq)
551		free_irq(dev->irq, dev);
552	return 0;
553};
554
555static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
556{
557	devpriv->forever = 0;
558	devpriv->count = 0;
559	disable_das800(dev);
560	return 0;
561}
562
563/* enable_das800 makes the card start taking hardware triggered conversions */
564static void enable_das800(struct comedi_device *dev)
565{
566	unsigned long irq_flags;
567	spin_lock_irqsave(&dev->spinlock, irq_flags);
568	/*  enable fifo-half full interrupts for cio-das802/16 */
569	if (thisboard->resolution == 16)
570		outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
571	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
572	outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL);	/* enable hardware triggering */
573	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
574	outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);	/* enable card's interrupt */
575	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
576}
577
578/* disable_das800 stops hardware triggered conversions */
579static void disable_das800(struct comedi_device *dev)
580{
581	unsigned long irq_flags;
582	spin_lock_irqsave(&dev->spinlock, irq_flags);
583	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
584	outb(0x0, dev->iobase + DAS800_CONV_CONTROL);	/* disable hardware triggering of conversions */
585	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
586}
587
588static int das800_ai_do_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
589	struct comedi_cmd *cmd)
590{
591	int err = 0;
592	int tmp;
593	int gain, startChan;
594	int i;
595
596	/* step 1: make sure trigger sources are trivially valid */
597
598	tmp = cmd->start_src;
599	cmd->start_src &= TRIG_NOW | TRIG_EXT;
600	if (!cmd->start_src || tmp != cmd->start_src)
601		err++;
602
603	tmp = cmd->scan_begin_src;
604	cmd->scan_begin_src &= TRIG_FOLLOW;
605	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
606		err++;
607
608	tmp = cmd->convert_src;
609	cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
610	if (!cmd->convert_src || tmp != cmd->convert_src)
611		err++;
612
613	tmp = cmd->scan_end_src;
614	cmd->scan_end_src &= TRIG_COUNT;
615	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
616		err++;
617
618	tmp = cmd->stop_src;
619	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
620	if (!cmd->stop_src || tmp != cmd->stop_src)
621		err++;
622
623	if (err)
624		return 1;
625
626	/* step 2: make sure trigger sources are unique and mutually compatible */
627
628	if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
629		err++;
630	if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
631		err++;
632	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
633		err++;
634
635	if (err)
636		return 2;
637
638	/* step 3: make sure arguments are trivially compatible */
639
640	if (cmd->start_arg != 0) {
641		cmd->start_arg = 0;
642		err++;
643	}
644	if (cmd->convert_src == TRIG_TIMER) {
645		if (cmd->convert_arg < thisboard->ai_speed) {
646			cmd->convert_arg = thisboard->ai_speed;
647			err++;
648		}
649	}
650	if (!cmd->chanlist_len) {
651		cmd->chanlist_len = 1;
652		err++;
653	}
654	if (cmd->scan_end_arg != cmd->chanlist_len) {
655		cmd->scan_end_arg = cmd->chanlist_len;
656		err++;
657	}
658	if (cmd->stop_src == TRIG_COUNT) {
659		if (!cmd->stop_arg) {
660			cmd->stop_arg = 1;
661			err++;
662		}
663	} else {		/* TRIG_NONE */
664		if (cmd->stop_arg != 0) {
665			cmd->stop_arg = 0;
666			err++;
667		}
668	}
669
670	if (err)
671		return 3;
672
673	/* step 4: fix up any arguments */
674
675	if (cmd->convert_src == TRIG_TIMER) {
676		tmp = cmd->convert_arg;
677		/* calculate counter values that give desired timing */
678		i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
679			&(devpriv->divisor2), &(cmd->convert_arg),
680			cmd->flags & TRIG_ROUND_MASK);
681		if (tmp != cmd->convert_arg)
682			err++;
683	}
684
685	if (err)
686		return 4;
687
688	/*  check channel/gain list against card's limitations */
689	if (cmd->chanlist) {
690		gain = CR_RANGE(cmd->chanlist[0]);
691		startChan = CR_CHAN(cmd->chanlist[0]);
692		for (i = 1; i < cmd->chanlist_len; i++) {
693			if (CR_CHAN(cmd->chanlist[i]) !=
694				(startChan + i) % N_CHAN_AI) {
695				comedi_error(dev,
696					"entries in chanlist must be consecutive channels, counting upwards\n");
697				err++;
698			}
699			if (CR_RANGE(cmd->chanlist[i]) != gain) {
700				comedi_error(dev,
701					"entries in chanlist must all have the same gain\n");
702				err++;
703			}
704		}
705	}
706
707	if (err)
708		return 5;
709
710	return 0;
711}
712
713static int das800_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
714{
715	int startChan, endChan, scan, gain;
716	int conv_bits;
717	unsigned long irq_flags;
718	struct comedi_async *async = s->async;
719
720	if (!dev->irq) {
721		comedi_error(dev,
722			"no irq assigned for das-800, cannot do hardware conversions");
723		return -1;
724	}
725
726	disable_das800(dev);
727
728	/* set channel scan limits */
729	startChan = CR_CHAN(async->cmd.chanlist[0]);
730	endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
731	scan = (endChan << 3) | startChan;
732
733	spin_lock_irqsave(&dev->spinlock, irq_flags);
734	outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN);	/* select base address + 2 to be scan limits register */
735	outb(scan, dev->iobase + DAS800_SCAN_LIMITS);	/* set scan limits */
736	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
737
738	/* set gain */
739	gain = CR_RANGE(async->cmd.chanlist[0]);
740	if (thisboard->resolution == 12 && gain > 0)
741		gain += 0x7;
742	gain &= 0xf;
743	outb(gain, dev->iobase + DAS800_GAIN);
744
745	switch (async->cmd.stop_src) {
746	case TRIG_COUNT:
747		devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
748		devpriv->forever = 0;
749		break;
750	case TRIG_NONE:
751		devpriv->forever = 1;
752		devpriv->count = 0;
753		break;
754	default:
755		break;
756	}
757
758	/* enable auto channel scan, send interrupts on end of conversion
759	 * and set clock source to internal or external
760	 */
761	conv_bits = 0;
762	conv_bits |= EACS | IEOC;
763	if (async->cmd.start_src == TRIG_EXT)
764		conv_bits |= DTEN;
765	switch (async->cmd.convert_src) {
766	case TRIG_TIMER:
767		conv_bits |= CASC | ITE;
768		/* set conversion frequency */
769		i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
770			&(devpriv->divisor2), &(async->cmd.convert_arg),
771			async->cmd.flags & TRIG_ROUND_MASK);
772		if (das800_set_frequency(dev) < 0) {
773			comedi_error(dev, "Error setting up counters");
774			return -1;
775		}
776		break;
777	case TRIG_EXT:
778		break;
779	default:
780		break;
781	}
782
783	spin_lock_irqsave(&dev->spinlock, irq_flags);
784	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
785	outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
786	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
787	async->events = 0;
788	enable_das800(dev);
789	return 0;
790}
791
792static int das800_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
793	struct comedi_insn *insn, unsigned int *data)
794{
795	int i, n;
796	int chan;
797	int range;
798	int lsb, msb;
799	int timeout = 1000;
800	unsigned long irq_flags;
801
802	disable_das800(dev);	/* disable hardware conversions (enables software conversions) */
803
804	/* set multiplexer */
805	chan = CR_CHAN(insn->chanspec);
806
807	spin_lock_irqsave(&dev->spinlock, irq_flags);
808	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
809	outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
810	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
811
812	/* set gain / range */
813	range = CR_RANGE(insn->chanspec);
814	if (thisboard->resolution == 12 && range)
815		range += 0x7;
816	range &= 0xf;
817	outb(range, dev->iobase + DAS800_GAIN);
818
819	udelay(5);
820
821	for (n = 0; n < insn->n; n++) {
822		/* trigger conversion */
823		outb_p(0, dev->iobase + DAS800_MSB);
824
825		for (i = 0; i < timeout; i++) {
826			if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
827				break;
828		}
829		if (i == timeout) {
830			comedi_error(dev, "timeout");
831			return -ETIME;
832		}
833		lsb = inb(dev->iobase + DAS800_LSB);
834		msb = inb(dev->iobase + DAS800_MSB);
835		if (thisboard->resolution == 12) {
836			data[n] = (lsb >> 4) & 0xff;
837			data[n] |= (msb << 4);
838		} else {
839			data[n] = (msb << 8) | lsb;
840		}
841	}
842
843	return n;
844}
845
846static int das800_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
847	struct comedi_insn *insn, unsigned int *data)
848{
849	unsigned int bits;
850
851	bits = inb(dev->iobase + DAS800_STATUS) >> 4;
852	bits &= 0x7;
853	data[1] = bits;
854	data[0] = 0;
855
856	return 2;
857}
858
859static int das800_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
860	struct comedi_insn *insn, unsigned int *data)
861{
862	int wbits;
863	unsigned long irq_flags;
864
865	/*  only set bits that have been masked */
866	data[0] &= 0xf;
867	wbits = devpriv->do_bits >> 4;
868	wbits &= ~data[0];
869	wbits |= data[0] & data[1];
870	devpriv->do_bits = wbits << 4;
871
872	spin_lock_irqsave(&dev->spinlock, irq_flags);
873	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
874	outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
875	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
876
877	data[1] = wbits;
878
879	return 2;
880}
881
882/* loads counters with divisor1, divisor2 from private structure */
883static int das800_set_frequency(struct comedi_device *dev)
884{
885	int err = 0;
886
887	if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
888		err++;
889	if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
890		err++;
891	if (err)
892		return -1;
893
894	return 0;
895}
896