das800.c revision 90f703d30dd3e0c16ff80f35e34e511385a05ad5
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		/* strip off extraneous bits for 12 bit cards */
405		if (thisboard->resolution == 12)
406			dataPoint = (dataPoint >> 4) & 0xfff;
407		/* if there are more data points to collect */
408		if (devpriv->count > 0 || devpriv->forever == 1) {
409			/* write data point to buffer */
410			cfc_write_to_buffer(s, dataPoint);
411			if (devpriv->count > 0)
412				devpriv->count--;
413		}
414	}
415	async->events |= COMEDI_CB_BLOCK;
416	/* check for fifo overflow */
417	if (thisboard->resolution == 12) {
418		fifo_overflow = dataPoint & FIFO_OVF;
419		/*  else cio-das802/16 */
420	} else {
421		fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
422	}
423	if (fifo_overflow) {
424		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
425		comedi_error(dev, "DAS800 FIFO overflow");
426		das800_cancel(dev, dev->subdevices + 0);
427		async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
428		comedi_event(dev, s);
429		async->events = 0;
430		return IRQ_HANDLED;
431	}
432	if (devpriv->count > 0 || devpriv->forever == 1) {
433		/* Re-enable card's interrupt.
434		 * We already have spinlock, so indirect addressing is safe */
435		outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
436		outb(CONTROL1_INTE | devpriv->do_bits,
437		     dev->iobase + DAS800_CONTROL1);
438		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
439		/* otherwise, stop taking data */
440	} else {
441		spin_unlock_irqrestore(&dev->spinlock, irq_flags);
442		disable_das800(dev);	/* diable hardware triggered conversions */
443		async->events |= COMEDI_CB_EOA;
444	}
445	comedi_event(dev, s);
446	async->events = 0;
447	return IRQ_HANDLED;
448}
449
450static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
451{
452	struct comedi_subdevice *s;
453	unsigned long iobase = it->options[0];
454	unsigned int irq = it->options[1];
455	unsigned long irq_flags;
456	int board;
457
458	printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
459	if (irq)
460		printk(", irq %u", irq);
461	printk("\n");
462
463	/* allocate and initialize dev->private */
464	if (alloc_private(dev, sizeof(struct das800_private)) < 0)
465		return -ENOMEM;
466
467	if (iobase == 0) {
468		printk("io base address required for das800\n");
469		return -EINVAL;
470	}
471
472	/* check if io addresses are available */
473	if (!request_region(iobase, DAS800_SIZE, "das800")) {
474		printk("I/O port conflict\n");
475		return -EIO;
476	}
477	dev->iobase = iobase;
478
479	board = das800_probe(dev);
480	if (board < 0) {
481		printk("unable to determine board type\n");
482		return -ENODEV;
483	}
484	dev->board_ptr = das800_boards + board;
485
486	/* grab our IRQ */
487	if (irq == 1 || irq > 7) {
488		printk("irq out of range\n");
489		return -EINVAL;
490	}
491	if (irq) {
492		if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
493			printk("unable to allocate irq %u\n", irq);
494			return -EINVAL;
495		}
496	}
497	dev->irq = irq;
498
499	dev->board_name = thisboard->name;
500
501	if (alloc_subdevices(dev, 3) < 0)
502		return -ENOMEM;
503
504	/* analog input subdevice */
505	s = dev->subdevices + 0;
506	dev->read_subdev = s;
507	s->type = COMEDI_SUBD_AI;
508	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
509	s->n_chan = 8;
510	s->len_chanlist = 8;
511	s->maxdata = (1 << thisboard->resolution) - 1;
512	s->range_table = thisboard->ai_range;
513	s->do_cmd = das800_ai_do_cmd;
514	s->do_cmdtest = das800_ai_do_cmdtest;
515	s->insn_read = das800_ai_rinsn;
516	s->cancel = das800_cancel;
517
518	/* di */
519	s = dev->subdevices + 1;
520	s->type = COMEDI_SUBD_DI;
521	s->subdev_flags = SDF_READABLE;
522	s->n_chan = 3;
523	s->maxdata = 1;
524	s->range_table = &range_digital;
525	s->insn_bits = das800_di_rbits;
526
527	/* do */
528	s = dev->subdevices + 2;
529	s->type = COMEDI_SUBD_DO;
530	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
531	s->n_chan = 4;
532	s->maxdata = 1;
533	s->range_table = &range_digital;
534	s->insn_bits = das800_do_wbits;
535
536	disable_das800(dev);
537
538	/* initialize digital out channels */
539	spin_lock_irqsave(&dev->spinlock, irq_flags);
540	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
541	outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
542	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
543
544	return 0;
545};
546
547static int das800_detach(struct comedi_device *dev)
548{
549	printk("comedi%d: das800: remove\n", dev->minor);
550
551	/* only free stuff if it has been allocated by _attach */
552	if (dev->iobase)
553		release_region(dev->iobase, DAS800_SIZE);
554	if (dev->irq)
555		free_irq(dev->irq, dev);
556	return 0;
557};
558
559static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
560{
561	devpriv->forever = 0;
562	devpriv->count = 0;
563	disable_das800(dev);
564	return 0;
565}
566
567/* enable_das800 makes the card start taking hardware triggered conversions */
568static void enable_das800(struct comedi_device *dev)
569{
570	unsigned long irq_flags;
571	spin_lock_irqsave(&dev->spinlock, irq_flags);
572	/*  enable fifo-half full interrupts for cio-das802/16 */
573	if (thisboard->resolution == 16)
574		outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
575	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
576	outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL);	/* enable hardware triggering */
577	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
578	outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);	/* enable card's interrupt */
579	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
580}
581
582/* disable_das800 stops hardware triggered conversions */
583static void disable_das800(struct comedi_device *dev)
584{
585	unsigned long irq_flags;
586	spin_lock_irqsave(&dev->spinlock, irq_flags);
587	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
588	outb(0x0, dev->iobase + DAS800_CONV_CONTROL);	/* disable hardware triggering of conversions */
589	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
590}
591
592static int das800_ai_do_cmdtest(struct comedi_device *dev,
593				struct comedi_subdevice *s,
594				struct comedi_cmd *cmd)
595{
596	int err = 0;
597	int tmp;
598	int gain, startChan;
599	int i;
600
601	/* step 1: make sure trigger sources are trivially valid */
602
603	tmp = cmd->start_src;
604	cmd->start_src &= TRIG_NOW | TRIG_EXT;
605	if (!cmd->start_src || tmp != cmd->start_src)
606		err++;
607
608	tmp = cmd->scan_begin_src;
609	cmd->scan_begin_src &= TRIG_FOLLOW;
610	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
611		err++;
612
613	tmp = cmd->convert_src;
614	cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
615	if (!cmd->convert_src || tmp != cmd->convert_src)
616		err++;
617
618	tmp = cmd->scan_end_src;
619	cmd->scan_end_src &= TRIG_COUNT;
620	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
621		err++;
622
623	tmp = cmd->stop_src;
624	cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
625	if (!cmd->stop_src || tmp != cmd->stop_src)
626		err++;
627
628	if (err)
629		return 1;
630
631	/* step 2: make sure trigger sources are unique and mutually compatible */
632
633	if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
634		err++;
635	if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
636		err++;
637	if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
638		err++;
639
640	if (err)
641		return 2;
642
643	/* step 3: make sure arguments are trivially compatible */
644
645	if (cmd->start_arg != 0) {
646		cmd->start_arg = 0;
647		err++;
648	}
649	if (cmd->convert_src == TRIG_TIMER) {
650		if (cmd->convert_arg < thisboard->ai_speed) {
651			cmd->convert_arg = thisboard->ai_speed;
652			err++;
653		}
654	}
655	if (!cmd->chanlist_len) {
656		cmd->chanlist_len = 1;
657		err++;
658	}
659	if (cmd->scan_end_arg != cmd->chanlist_len) {
660		cmd->scan_end_arg = cmd->chanlist_len;
661		err++;
662	}
663	if (cmd->stop_src == TRIG_COUNT) {
664		if (!cmd->stop_arg) {
665			cmd->stop_arg = 1;
666			err++;
667		}
668	} else {		/* TRIG_NONE */
669		if (cmd->stop_arg != 0) {
670			cmd->stop_arg = 0;
671			err++;
672		}
673	}
674
675	if (err)
676		return 3;
677
678	/* step 4: fix up any arguments */
679
680	if (cmd->convert_src == TRIG_TIMER) {
681		tmp = cmd->convert_arg;
682		/* calculate counter values that give desired timing */
683		i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
684					       &(devpriv->divisor2),
685					       &(cmd->convert_arg),
686					       cmd->flags & TRIG_ROUND_MASK);
687		if (tmp != cmd->convert_arg)
688			err++;
689	}
690
691	if (err)
692		return 4;
693
694	/*  check channel/gain list against card's limitations */
695	if (cmd->chanlist) {
696		gain = CR_RANGE(cmd->chanlist[0]);
697		startChan = CR_CHAN(cmd->chanlist[0]);
698		for (i = 1; i < cmd->chanlist_len; i++) {
699			if (CR_CHAN(cmd->chanlist[i]) !=
700			    (startChan + i) % N_CHAN_AI) {
701				comedi_error(dev,
702					     "entries in chanlist must be consecutive channels, counting upwards\n");
703				err++;
704			}
705			if (CR_RANGE(cmd->chanlist[i]) != gain) {
706				comedi_error(dev,
707					     "entries in chanlist must all have the same gain\n");
708				err++;
709			}
710		}
711	}
712
713	if (err)
714		return 5;
715
716	return 0;
717}
718
719static int das800_ai_do_cmd(struct comedi_device *dev,
720			    struct comedi_subdevice *s)
721{
722	int startChan, endChan, scan, gain;
723	int conv_bits;
724	unsigned long irq_flags;
725	struct comedi_async *async = s->async;
726
727	if (!dev->irq) {
728		comedi_error(dev,
729			     "no irq assigned for das-800, cannot do hardware conversions");
730		return -1;
731	}
732
733	disable_das800(dev);
734
735	/* set channel scan limits */
736	startChan = CR_CHAN(async->cmd.chanlist[0]);
737	endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
738	scan = (endChan << 3) | startChan;
739
740	spin_lock_irqsave(&dev->spinlock, irq_flags);
741	outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN);	/* select base address + 2 to be scan limits register */
742	outb(scan, dev->iobase + DAS800_SCAN_LIMITS);	/* set scan limits */
743	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
744
745	/* set gain */
746	gain = CR_RANGE(async->cmd.chanlist[0]);
747	if (thisboard->resolution == 12 && gain > 0)
748		gain += 0x7;
749	gain &= 0xf;
750	outb(gain, dev->iobase + DAS800_GAIN);
751
752	switch (async->cmd.stop_src) {
753	case TRIG_COUNT:
754		devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
755		devpriv->forever = 0;
756		break;
757	case TRIG_NONE:
758		devpriv->forever = 1;
759		devpriv->count = 0;
760		break;
761	default:
762		break;
763	}
764
765	/* enable auto channel scan, send interrupts on end of conversion
766	 * and set clock source to internal or external
767	 */
768	conv_bits = 0;
769	conv_bits |= EACS | IEOC;
770	if (async->cmd.start_src == TRIG_EXT)
771		conv_bits |= DTEN;
772	switch (async->cmd.convert_src) {
773	case TRIG_TIMER:
774		conv_bits |= CASC | ITE;
775		/* set conversion frequency */
776		i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
777					       &(devpriv->divisor2),
778					       &(async->cmd.convert_arg),
779					       async->cmd.
780					       flags & TRIG_ROUND_MASK);
781		if (das800_set_frequency(dev) < 0) {
782			comedi_error(dev, "Error setting up counters");
783			return -1;
784		}
785		break;
786	case TRIG_EXT:
787		break;
788	default:
789		break;
790	}
791
792	spin_lock_irqsave(&dev->spinlock, irq_flags);
793	outb(CONV_CONTROL, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be conversion control register */
794	outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
795	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
796	async->events = 0;
797	enable_das800(dev);
798	return 0;
799}
800
801static int das800_ai_rinsn(struct comedi_device *dev,
802			   struct comedi_subdevice *s, struct comedi_insn *insn,
803			   unsigned int *data)
804{
805	int i, n;
806	int chan;
807	int range;
808	int lsb, msb;
809	int timeout = 1000;
810	unsigned long irq_flags;
811
812	disable_das800(dev);	/* disable hardware conversions (enables software conversions) */
813
814	/* set multiplexer */
815	chan = CR_CHAN(insn->chanspec);
816
817	spin_lock_irqsave(&dev->spinlock, irq_flags);
818	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
819	outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
820	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
821
822	/* set gain / range */
823	range = CR_RANGE(insn->chanspec);
824	if (thisboard->resolution == 12 && range)
825		range += 0x7;
826	range &= 0xf;
827	outb(range, dev->iobase + DAS800_GAIN);
828
829	udelay(5);
830
831	for (n = 0; n < insn->n; n++) {
832		/* trigger conversion */
833		outb_p(0, dev->iobase + DAS800_MSB);
834
835		for (i = 0; i < timeout; i++) {
836			if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
837				break;
838		}
839		if (i == timeout) {
840			comedi_error(dev, "timeout");
841			return -ETIME;
842		}
843		lsb = inb(dev->iobase + DAS800_LSB);
844		msb = inb(dev->iobase + DAS800_MSB);
845		if (thisboard->resolution == 12) {
846			data[n] = (lsb >> 4) & 0xff;
847			data[n] |= (msb << 4);
848		} else {
849			data[n] = (msb << 8) | lsb;
850		}
851	}
852
853	return n;
854}
855
856static int das800_di_rbits(struct comedi_device *dev,
857			   struct comedi_subdevice *s, struct comedi_insn *insn,
858			   unsigned int *data)
859{
860	unsigned int bits;
861
862	bits = inb(dev->iobase + DAS800_STATUS) >> 4;
863	bits &= 0x7;
864	data[1] = bits;
865	data[0] = 0;
866
867	return 2;
868}
869
870static int das800_do_wbits(struct comedi_device *dev,
871			   struct comedi_subdevice *s, struct comedi_insn *insn,
872			   unsigned int *data)
873{
874	int wbits;
875	unsigned long irq_flags;
876
877	/*  only set bits that have been masked */
878	data[0] &= 0xf;
879	wbits = devpriv->do_bits >> 4;
880	wbits &= ~data[0];
881	wbits |= data[0] & data[1];
882	devpriv->do_bits = wbits << 4;
883
884	spin_lock_irqsave(&dev->spinlock, irq_flags);
885	outb(CONTROL1, dev->iobase + DAS800_GAIN);	/* select dev->iobase + 2 to be control register 1 */
886	outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
887	spin_unlock_irqrestore(&dev->spinlock, irq_flags);
888
889	data[1] = wbits;
890
891	return 2;
892}
893
894/* loads counters with divisor1, divisor2 from private structure */
895static int das800_set_frequency(struct comedi_device *dev)
896{
897	int err = 0;
898
899	if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
900		err++;
901	if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
902		err++;
903	if (err)
904		return -1;
905
906	return 0;
907}
908
909MODULE_AUTHOR("Comedi http://www.comedi.org");
910MODULE_DESCRIPTION("Comedi low-level driver");
911MODULE_LICENSE("GPL");
912