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