1/*
2    comedi/drivers/das16m1.c
3    CIO-DAS16/M1 driver
4    Author: Frank Mori Hess, based on code from the das16
5      driver.
6    Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
7
8    COMEDI - Linux Control and Measurement Device Interface
9    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20*/
21/*
22Driver: das16m1
23Description: CIO-DAS16/M1
24Author: Frank Mori Hess <fmhess@users.sourceforge.net>
25Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
26Status: works
27
28This driver supports a single board - the CIO-DAS16/M1.
29As far as I know, there are no other boards that have
30the same register layout.  Even the CIO-DAS16/M1/16 is
31significantly different.
32
33I was _barely_ able to reach the full 1 MHz capability
34of this board, using a hard real-time interrupt
35(set the TRIG_RT flag in your struct comedi_cmd and use
36rtlinux or RTAI).  The board can't do dma, so the bottleneck is
37pulling the data across the ISA bus.  I timed the interrupt
38handler, and it took my computer ~470 microseconds to pull 512
39samples from the board.  So at 1 Mhz sampling rate,
40expect your CPU to be spending almost all of its
41time in the interrupt handler.
42
43This board has some unusual restrictions for its channel/gain list.  If the
44list has 2 or more channels in it, then two conditions must be satisfied:
45(1) - even/odd channels must appear at even/odd indices in the list
46(2) - the list must have an even number of entries.
47
48Options:
49        [0] - base io address
50        [1] - irq (optional, but you probably want it)
51
52irq can be omitted, although the cmd interface will not work without it.
53*/
54
55#include <linux/module.h>
56#include <linux/interrupt.h>
57#include "../comedidev.h"
58
59#include "8255.h"
60#include "8253.h"
61#include "comedi_fc.h"
62
63#define DAS16M1_SIZE2 8
64
65#define FIFO_SIZE 1024		/*  1024 sample fifo */
66
67/*
68    CIO-DAS16_M1.pdf
69
70    "cio-das16/m1"
71
72  0	a/d bits 0-3, mux		start 12 bit
73  1	a/d bits 4-11		unused
74  2	status		control
75  3	di 4 bit		do 4 bit
76  4	unused			clear interrupt
77  5	interrupt, pacer
78  6	channel/gain queue address
79  7	channel/gain queue data
80  89ab	8254
81  cdef	8254
82  400	8255
83  404-407 	8254
84
85*/
86
87#define DAS16M1_AI             0	/*  16-bit wide register */
88#define   AI_CHAN(x)             ((x) & 0xf)
89#define DAS16M1_CS             2
90#define   EXT_TRIG_BIT           0x1
91#define   OVRUN                  0x20
92#define   IRQDATA                0x80
93#define DAS16M1_DIO            3
94#define DAS16M1_CLEAR_INTR     4
95#define DAS16M1_INTR_CONTROL   5
96#define   EXT_PACER              0x2
97#define   INT_PACER              0x3
98#define   PACER_MASK             0x3
99#define   INTE                   0x80
100#define DAS16M1_QUEUE_ADDR     6
101#define DAS16M1_QUEUE_DATA     7
102#define   Q_CHAN(x)              ((x) & 0x7)
103#define   Q_RANGE(x)             (((x) & 0xf) << 4)
104#define   UNIPOLAR               0x40
105#define DAS16M1_8254_FIRST             0x8
106#define DAS16M1_8254_FIRST_CNTRL       0xb
107#define   TOTAL_CLEAR                    0x30
108#define DAS16M1_8254_SECOND            0xc
109#define DAS16M1_82C55                  0x400
110#define DAS16M1_8254_THIRD             0x404
111
112static const struct comedi_lrange range_das16m1 = {
113	9, {
114		BIP_RANGE(5),
115		BIP_RANGE(2.5),
116		BIP_RANGE(1.25),
117		BIP_RANGE(0.625),
118		UNI_RANGE(10),
119		UNI_RANGE(5),
120		UNI_RANGE(2.5),
121		UNI_RANGE(1.25),
122		BIP_RANGE(10)
123	}
124};
125
126struct das16m1_private_struct {
127	unsigned int control_state;
128	unsigned int adc_count;	/*  number of samples completed */
129	/* initial value in lower half of hardware conversion counter,
130	 * needed to keep track of whether new count has been loaded into
131	 * counter yet (loaded by first sample conversion) */
132	u16 initial_hw_count;
133	unsigned short ai_buffer[FIFO_SIZE];
134	unsigned int divisor1;	/*  divides master clock to obtain conversion speed */
135	unsigned int divisor2;	/*  divides master clock to obtain conversion speed */
136	unsigned long extra_iobase;
137};
138
139static inline unsigned short munge_sample(unsigned short data)
140{
141	return (data >> 4) & 0xfff;
142}
143
144static void munge_sample_array(unsigned short *array, unsigned int num_elements)
145{
146	unsigned int i;
147
148	for (i = 0; i < num_elements; i++)
149		array[i] = munge_sample(array[i]);
150}
151
152static int das16m1_ai_check_chanlist(struct comedi_device *dev,
153				     struct comedi_subdevice *s,
154				     struct comedi_cmd *cmd)
155{
156	int i;
157
158	if (cmd->chanlist_len == 1)
159		return 0;
160
161	if ((cmd->chanlist_len % 2) != 0) {
162		dev_dbg(dev->class_dev,
163			"chanlist must be of even length or length 1\n");
164		return -EINVAL;
165	}
166
167	for (i = 0; i < cmd->chanlist_len; i++) {
168		unsigned int chan = CR_CHAN(cmd->chanlist[i]);
169
170		if ((i % 2) != (chan % 2)) {
171			dev_dbg(dev->class_dev,
172				 "even/odd channels must go have even/odd chanlist indices\n");
173			return -EINVAL;
174		}
175	}
176
177	return 0;
178}
179
180static int das16m1_cmd_test(struct comedi_device *dev,
181			    struct comedi_subdevice *s, struct comedi_cmd *cmd)
182{
183	struct das16m1_private_struct *devpriv = dev->private;
184	int err = 0;
185	unsigned int arg;
186
187	/* Step 1 : check if triggers are trivially valid */
188
189	err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
190	err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
191	err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
192	err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
193	err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
194
195	if (err)
196		return 1;
197
198	/* Step 2a : make sure trigger sources are unique */
199
200	err |= cfc_check_trigger_is_unique(cmd->start_src);
201	err |= cfc_check_trigger_is_unique(cmd->convert_src);
202	err |= cfc_check_trigger_is_unique(cmd->stop_src);
203
204	/* Step 2b : and mutually compatible */
205
206	if (err)
207		return 2;
208
209	/* Step 3: check if arguments are trivially valid */
210
211	err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
212
213	if (cmd->scan_begin_src == TRIG_FOLLOW)	/* internal trigger */
214		err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
215
216	if (cmd->convert_src == TRIG_TIMER)
217		err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 1000);
218
219	err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
220
221	if (cmd->stop_src == TRIG_COUNT)
222		err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
223	else	/* TRIG_NONE */
224		err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
225
226	if (err)
227		return 3;
228
229	/* step 4: fix up arguments */
230
231	if (cmd->convert_src == TRIG_TIMER) {
232		arg = cmd->convert_arg;
233		i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
234					  &devpriv->divisor1,
235					  &devpriv->divisor2,
236					  &arg, cmd->flags);
237		err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
238	}
239
240	if (err)
241		return 4;
242
243	/* Step 5: check channel list if it exists */
244	if (cmd->chanlist && cmd->chanlist_len > 0)
245		err |= das16m1_ai_check_chanlist(dev, s, cmd);
246
247	if (err)
248		return 5;
249
250	return 0;
251}
252
253static void das16m1_set_pacer(struct comedi_device *dev)
254{
255	struct das16m1_private_struct *devpriv = dev->private;
256	unsigned long timer_base = dev->iobase + DAS16M1_8254_SECOND;
257
258	i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
259	i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
260
261	i8254_write(timer_base, 0, 1, devpriv->divisor1);
262	i8254_write(timer_base, 0, 2, devpriv->divisor2);
263}
264
265static int das16m1_cmd_exec(struct comedi_device *dev,
266			    struct comedi_subdevice *s)
267{
268	struct das16m1_private_struct *devpriv = dev->private;
269	struct comedi_async *async = s->async;
270	struct comedi_cmd *cmd = &async->cmd;
271	unsigned long timer_base = dev->iobase + DAS16M1_8254_FIRST;
272	unsigned int byte, i;
273
274	/* disable interrupts and internal pacer */
275	devpriv->control_state &= ~INTE & ~PACER_MASK;
276	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
277
278	/*  set software count */
279	devpriv->adc_count = 0;
280	/* Initialize lower half of hardware counter, used to determine how
281	 * many samples are in fifo.  Value doesn't actually load into counter
282	 * until counter's next clock (the next a/d conversion) */
283	i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
284	i8254_write(timer_base, 0, 1, 0);
285	/* remember current reading of counter so we know when counter has
286	 * actually been loaded */
287	devpriv->initial_hw_count = i8254_read(timer_base, 0, 1);
288	/* setup channel/gain queue */
289	for (i = 0; i < cmd->chanlist_len; i++) {
290		outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
291		byte =
292		    Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
293		    Q_RANGE(CR_RANGE(cmd->chanlist[i]));
294		outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
295	}
296
297	/* enable interrupts and set internal pacer counter mode and counts */
298	devpriv->control_state &= ~PACER_MASK;
299	if (cmd->convert_src == TRIG_TIMER) {
300		das16m1_set_pacer(dev);
301		devpriv->control_state |= INT_PACER;
302	} else {	/* TRIG_EXT */
303		devpriv->control_state |= EXT_PACER;
304	}
305
306	/*  set control & status register */
307	byte = 0;
308	/* if we are using external start trigger (also board dislikes having
309	 * both start and conversion triggers external simultaneously) */
310	if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
311		byte |= EXT_TRIG_BIT;
312
313	outb(byte, dev->iobase + DAS16M1_CS);
314	/* clear interrupt bit */
315	outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
316
317	devpriv->control_state |= INTE;
318	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
319
320	return 0;
321}
322
323static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
324{
325	struct das16m1_private_struct *devpriv = dev->private;
326
327	devpriv->control_state &= ~INTE & ~PACER_MASK;
328	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
329
330	return 0;
331}
332
333static int das16m1_ai_eoc(struct comedi_device *dev,
334			  struct comedi_subdevice *s,
335			  struct comedi_insn *insn,
336			  unsigned long context)
337{
338	unsigned int status;
339
340	status = inb(dev->iobase + DAS16M1_CS);
341	if (status & IRQDATA)
342		return 0;
343	return -EBUSY;
344}
345
346static int das16m1_ai_rinsn(struct comedi_device *dev,
347			    struct comedi_subdevice *s,
348			    struct comedi_insn *insn, unsigned int *data)
349{
350	struct das16m1_private_struct *devpriv = dev->private;
351	int ret;
352	int n;
353	int byte;
354
355	/* disable interrupts and internal pacer */
356	devpriv->control_state &= ~INTE & ~PACER_MASK;
357	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
358
359	/* setup channel/gain queue */
360	outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
361	byte =
362	    Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
363	outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
364
365	for (n = 0; n < insn->n; n++) {
366		/* clear IRQDATA bit */
367		outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
368		/* trigger conversion */
369		outb(0, dev->iobase);
370
371		ret = comedi_timeout(dev, s, insn, das16m1_ai_eoc, 0);
372		if (ret)
373			return ret;
374
375		data[n] = munge_sample(inw(dev->iobase));
376	}
377
378	return n;
379}
380
381static int das16m1_di_rbits(struct comedi_device *dev,
382			    struct comedi_subdevice *s,
383			    struct comedi_insn *insn, unsigned int *data)
384{
385	unsigned int bits;
386
387	bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
388	data[1] = bits;
389	data[0] = 0;
390
391	return insn->n;
392}
393
394static int das16m1_do_wbits(struct comedi_device *dev,
395			    struct comedi_subdevice *s,
396			    struct comedi_insn *insn,
397			    unsigned int *data)
398{
399	if (comedi_dio_update_state(s, data))
400		outb(s->state, dev->iobase + DAS16M1_DIO);
401
402	data[1] = s->state;
403
404	return insn->n;
405}
406
407static void das16m1_handler(struct comedi_device *dev, unsigned int status)
408{
409	struct das16m1_private_struct *devpriv = dev->private;
410	struct comedi_subdevice *s;
411	struct comedi_async *async;
412	struct comedi_cmd *cmd;
413	u16 num_samples;
414	u16 hw_counter;
415
416	s = dev->read_subdev;
417	async = s->async;
418	cmd = &async->cmd;
419
420	/*  figure out how many samples are in fifo */
421	hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
422	/* make sure hardware counter reading is not bogus due to initial value
423	 * not having been loaded yet */
424	if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
425		num_samples = 0;
426	} else {
427		/* The calculation of num_samples looks odd, but it uses the following facts.
428		 * 16 bit hardware counter is initialized with value of zero (which really
429		 * means 0x1000).  The counter decrements by one on each conversion
430		 * (when the counter decrements from zero it goes to 0xffff).  num_samples
431		 * is a 16 bit variable, so it will roll over in a similar fashion to the
432		 * hardware counter.  Work it out, and this is what you get. */
433		num_samples = -hw_counter - devpriv->adc_count;
434	}
435	/*  check if we only need some of the points */
436	if (cmd->stop_src == TRIG_COUNT) {
437		if (num_samples > cmd->stop_arg * cmd->chanlist_len)
438			num_samples = cmd->stop_arg * cmd->chanlist_len;
439	}
440	/*  make sure we dont try to get too many points if fifo has overrun */
441	if (num_samples > FIFO_SIZE)
442		num_samples = FIFO_SIZE;
443	insw(dev->iobase, devpriv->ai_buffer, num_samples);
444	munge_sample_array(devpriv->ai_buffer, num_samples);
445	cfc_write_array_to_buffer(s, devpriv->ai_buffer,
446				  num_samples * sizeof(short));
447	devpriv->adc_count += num_samples;
448
449	if (cmd->stop_src == TRIG_COUNT) {
450		if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
451			/* end of acquisition */
452			async->events |= COMEDI_CB_EOA;
453		}
454	}
455
456	/* this probably won't catch overruns since the card doesn't generate
457	 * overrun interrupts, but we might as well try */
458	if (status & OVRUN) {
459		async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
460		dev_err(dev->class_dev, "fifo overflow\n");
461	}
462
463	cfc_handle_events(dev, s);
464}
465
466static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
467{
468	unsigned long flags;
469	unsigned int status;
470
471	/*  prevent race with interrupt handler */
472	spin_lock_irqsave(&dev->spinlock, flags);
473	status = inb(dev->iobase + DAS16M1_CS);
474	das16m1_handler(dev, status);
475	spin_unlock_irqrestore(&dev->spinlock, flags);
476
477	return comedi_buf_n_bytes_ready(s);
478}
479
480static irqreturn_t das16m1_interrupt(int irq, void *d)
481{
482	int status;
483	struct comedi_device *dev = d;
484
485	if (!dev->attached) {
486		dev_err(dev->class_dev, "premature interrupt\n");
487		return IRQ_HANDLED;
488	}
489	/*  prevent race with comedi_poll() */
490	spin_lock(&dev->spinlock);
491
492	status = inb(dev->iobase + DAS16M1_CS);
493
494	if ((status & (IRQDATA | OVRUN)) == 0) {
495		dev_err(dev->class_dev, "spurious interrupt\n");
496		spin_unlock(&dev->spinlock);
497		return IRQ_NONE;
498	}
499
500	das16m1_handler(dev, status);
501
502	/* clear interrupt */
503	outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
504
505	spin_unlock(&dev->spinlock);
506	return IRQ_HANDLED;
507}
508
509static int das16m1_irq_bits(unsigned int irq)
510{
511	switch (irq) {
512	case 10:
513		return 0x0;
514	case 11:
515		return 0x1;
516	case 12:
517		return 0x2;
518	case 15:
519		return 0x3;
520	case 2:
521		return 0x4;
522	case 3:
523		return 0x5;
524	case 5:
525		return 0x6;
526	case 7:
527		return 0x7;
528	default:
529		return 0x0;
530	}
531}
532
533/*
534 * Options list:
535 *   0  I/O base
536 *   1  IRQ
537 */
538static int das16m1_attach(struct comedi_device *dev,
539			  struct comedi_devconfig *it)
540{
541	struct das16m1_private_struct *devpriv;
542	struct comedi_subdevice *s;
543	int ret;
544
545	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
546	if (!devpriv)
547		return -ENOMEM;
548
549	ret = comedi_request_region(dev, it->options[0], 0x10);
550	if (ret)
551		return ret;
552	/* Request an additional region for the 8255 */
553	ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
554				      DAS16M1_SIZE2);
555	if (ret)
556		return ret;
557	devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
558
559	/* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
560	if ((1 << it->options[1]) & 0xdcfc) {
561		ret = request_irq(it->options[1], das16m1_interrupt, 0,
562				  dev->board_name, dev);
563		if (ret == 0)
564			dev->irq = it->options[1];
565	}
566
567	ret = comedi_alloc_subdevices(dev, 4);
568	if (ret)
569		return ret;
570
571	s = &dev->subdevices[0];
572	/* ai */
573	s->type = COMEDI_SUBD_AI;
574	s->subdev_flags = SDF_READABLE | SDF_DIFF;
575	s->n_chan = 8;
576	s->maxdata = (1 << 12) - 1;
577	s->range_table = &range_das16m1;
578	s->insn_read = das16m1_ai_rinsn;
579	if (dev->irq) {
580		dev->read_subdev = s;
581		s->subdev_flags |= SDF_CMD_READ;
582		s->len_chanlist = 256;
583		s->do_cmdtest = das16m1_cmd_test;
584		s->do_cmd = das16m1_cmd_exec;
585		s->cancel = das16m1_cancel;
586		s->poll = das16m1_poll;
587	}
588
589	s = &dev->subdevices[1];
590	/* di */
591	s->type = COMEDI_SUBD_DI;
592	s->subdev_flags = SDF_READABLE;
593	s->n_chan = 4;
594	s->maxdata = 1;
595	s->range_table = &range_digital;
596	s->insn_bits = das16m1_di_rbits;
597
598	s = &dev->subdevices[2];
599	/* do */
600	s->type = COMEDI_SUBD_DO;
601	s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
602	s->n_chan = 4;
603	s->maxdata = 1;
604	s->range_table = &range_digital;
605	s->insn_bits = das16m1_do_wbits;
606
607	s = &dev->subdevices[3];
608	/* 8255 */
609	ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
610	if (ret)
611		return ret;
612
613	/*  disable upper half of hardware conversion counter so it doesn't mess with us */
614	outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
615
616	/*  initialize digital output lines */
617	outb(0, dev->iobase + DAS16M1_DIO);
618
619	/* set the interrupt level */
620	devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
621	outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
622
623	return 0;
624}
625
626static void das16m1_detach(struct comedi_device *dev)
627{
628	struct das16m1_private_struct *devpriv = dev->private;
629
630	if (devpriv && devpriv->extra_iobase)
631		release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
632	comedi_legacy_detach(dev);
633}
634
635static struct comedi_driver das16m1_driver = {
636	.driver_name	= "das16m1",
637	.module		= THIS_MODULE,
638	.attach		= das16m1_attach,
639	.detach		= das16m1_detach,
640};
641module_comedi_driver(das16m1_driver);
642
643MODULE_AUTHOR("Comedi http://www.comedi.org");
644MODULE_DESCRIPTION("Comedi low-level driver");
645MODULE_LICENSE("GPL");
646