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