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