2 comedi/drivers/das16m1.c
4 Author: Frank Mori Hess, based on code from the das16
6 Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
23 Description: CIO-DAS16/M1
24 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
25 Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
28 This driver supports a single board - the CIO-DAS16/M1.
29 As far as I know, there are no other boards that have
30 the same register layout. Even the CIO-DAS16/M1/16 is
31 significantly different.
33 I was _barely_ able to reach the full 1 MHz capability
34 of this board, using a hard real-time interrupt
35 (set the TRIG_RT flag in your struct comedi_cmd and use
36 rtlinux or RTAI). The board can't do dma, so the bottleneck is
37 pulling the data across the ISA bus. I timed the interrupt
38 handler, and it took my computer ~470 microseconds to pull 512
39 samples from the board. So at 1 Mhz sampling rate,
40 expect your CPU to be spending almost all of its
41 time in the interrupt handler.
43 This board has some unusual restrictions for its channel/gain list. If the
44 list 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.
50 [1] - irq (optional, but you probably want it)
52 irq can be omitted, although the cmd interface will not work without it.
55 #include <linux/module.h>
56 #include <linux/slab.h>
57 #include <linux/interrupt.h>
58 #include "../comedidev.h"
61 #include "comedi_8254.h"
63 #define DAS16M1_SIZE2 8
65 #define FIFO_SIZE 1024 /* 1024 sample fifo */
72 0 a/d bits 0-3, mux start 12 bit
73 1 a/d bits 4-11 unused
76 4 unused clear interrupt
78 6 channel/gain queue address
79 7 channel/gain queue data
87 #define DAS16M1_AI 0 /* 16-bit wide register */
88 #define AI_CHAN(x) ((x) & 0xf)
90 #define EXT_TRIG_BIT 0x1
94 #define DAS16M1_CLEAR_INTR 4
95 #define DAS16M1_INTR_CONTROL 5
98 #define PACER_MASK 0x3
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_SECOND 0xc
107 #define DAS16M1_82C55 0x400
108 #define DAS16M1_8254_THIRD 0x404
110 static const struct comedi_lrange range_das16m1 = {
124 struct das16m1_private_struct {
125 struct comedi_8254 *counter;
126 unsigned int control_state;
127 unsigned int adc_count; /* number of samples completed */
128 /* initial value in lower half of hardware conversion counter,
129 * needed to keep track of whether new count has been loaded into
130 * counter yet (loaded by first sample conversion) */
131 u16 initial_hw_count;
132 unsigned short ai_buffer[FIFO_SIZE];
133 unsigned long extra_iobase;
136 static inline unsigned short munge_sample(unsigned short data)
138 return (data >> 4) & 0xfff;
141 static void munge_sample_array(unsigned short *array, unsigned int num_elements)
145 for (i = 0; i < num_elements; i++)
146 array[i] = munge_sample(array[i]);
149 static int das16m1_ai_check_chanlist(struct comedi_device *dev,
150 struct comedi_subdevice *s,
151 struct comedi_cmd *cmd)
155 if (cmd->chanlist_len == 1)
158 if ((cmd->chanlist_len % 2) != 0) {
159 dev_dbg(dev->class_dev,
160 "chanlist must be of even length or length 1\n");
164 for (i = 0; i < cmd->chanlist_len; i++) {
165 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
167 if ((i % 2) != (chan % 2)) {
168 dev_dbg(dev->class_dev,
169 "even/odd channels must go have even/odd chanlist indices\n");
177 static int das16m1_cmd_test(struct comedi_device *dev,
178 struct comedi_subdevice *s, struct comedi_cmd *cmd)
182 /* Step 1 : check if triggers are trivially valid */
184 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
185 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
186 err |= comedi_check_trigger_src(&cmd->convert_src,
187 TRIG_TIMER | TRIG_EXT);
188 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
189 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
194 /* Step 2a : make sure trigger sources are unique */
196 err |= comedi_check_trigger_is_unique(cmd->start_src);
197 err |= comedi_check_trigger_is_unique(cmd->convert_src);
198 err |= comedi_check_trigger_is_unique(cmd->stop_src);
200 /* Step 2b : and mutually compatible */
205 /* Step 3: check if arguments are trivially valid */
207 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
209 if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
210 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
212 if (cmd->convert_src == TRIG_TIMER)
213 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 1000);
215 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
218 if (cmd->stop_src == TRIG_COUNT)
219 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
221 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
226 /* step 4: fix up arguments */
228 if (cmd->convert_src == TRIG_TIMER) {
229 unsigned int arg = cmd->convert_arg;
231 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
232 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
238 /* Step 5: check channel list if it exists */
239 if (cmd->chanlist && cmd->chanlist_len > 0)
240 err |= das16m1_ai_check_chanlist(dev, s, cmd);
248 static int das16m1_cmd_exec(struct comedi_device *dev,
249 struct comedi_subdevice *s)
251 struct das16m1_private_struct *devpriv = dev->private;
252 struct comedi_async *async = s->async;
253 struct comedi_cmd *cmd = &async->cmd;
254 unsigned int byte, i;
256 /* disable interrupts and internal pacer */
257 devpriv->control_state &= ~INTE & ~PACER_MASK;
258 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
260 /* set software count */
261 devpriv->adc_count = 0;
264 * Initialize lower half of hardware counter, used to determine how
265 * many samples are in fifo. Value doesn't actually load into counter
266 * until counter's next clock (the next a/d conversion).
268 comedi_8254_set_mode(devpriv->counter, 1, I8254_MODE2 | I8254_BINARY);
269 comedi_8254_write(devpriv->counter, 1, 0);
272 * Remember current reading of counter so we know when counter has
273 * actually been loaded.
275 devpriv->initial_hw_count = comedi_8254_read(devpriv->counter, 1);
277 /* setup channel/gain queue */
278 for (i = 0; i < cmd->chanlist_len; i++) {
279 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
281 Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
282 Q_RANGE(CR_RANGE(cmd->chanlist[i]));
283 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
286 /* enable interrupts and set internal pacer counter mode and counts */
287 devpriv->control_state &= ~PACER_MASK;
288 if (cmd->convert_src == TRIG_TIMER) {
289 comedi_8254_update_divisors(dev->pacer);
290 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
291 devpriv->control_state |= INT_PACER;
292 } else { /* TRIG_EXT */
293 devpriv->control_state |= EXT_PACER;
296 /* set control & status register */
298 /* if we are using external start trigger (also board dislikes having
299 * both start and conversion triggers external simultaneously) */
300 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
301 byte |= EXT_TRIG_BIT;
303 outb(byte, dev->iobase + DAS16M1_CS);
304 /* clear interrupt bit */
305 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
307 devpriv->control_state |= INTE;
308 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
313 static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
315 struct das16m1_private_struct *devpriv = dev->private;
317 devpriv->control_state &= ~INTE & ~PACER_MASK;
318 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
323 static int das16m1_ai_eoc(struct comedi_device *dev,
324 struct comedi_subdevice *s,
325 struct comedi_insn *insn,
326 unsigned long context)
330 status = inb(dev->iobase + DAS16M1_CS);
331 if (status & IRQDATA)
336 static int das16m1_ai_rinsn(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn, unsigned int *data)
340 struct das16m1_private_struct *devpriv = dev->private;
345 /* disable interrupts and internal pacer */
346 devpriv->control_state &= ~INTE & ~PACER_MASK;
347 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
349 /* setup channel/gain queue */
350 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
352 Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
353 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
355 for (n = 0; n < insn->n; n++) {
356 /* clear IRQDATA bit */
357 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
358 /* trigger conversion */
359 outb(0, dev->iobase);
361 ret = comedi_timeout(dev, s, insn, das16m1_ai_eoc, 0);
365 data[n] = munge_sample(inw(dev->iobase));
371 static int das16m1_di_rbits(struct comedi_device *dev,
372 struct comedi_subdevice *s,
373 struct comedi_insn *insn, unsigned int *data)
377 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
384 static int das16m1_do_wbits(struct comedi_device *dev,
385 struct comedi_subdevice *s,
386 struct comedi_insn *insn,
389 if (comedi_dio_update_state(s, data))
390 outb(s->state, dev->iobase + DAS16M1_DIO);
397 static void das16m1_handler(struct comedi_device *dev, unsigned int status)
399 struct das16m1_private_struct *devpriv = dev->private;
400 struct comedi_subdevice *s;
401 struct comedi_async *async;
402 struct comedi_cmd *cmd;
406 s = dev->read_subdev;
410 /* figure out how many samples are in fifo */
411 hw_counter = comedi_8254_read(devpriv->counter, 1);
412 /* make sure hardware counter reading is not bogus due to initial value
413 * not having been loaded yet */
414 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
417 /* The calculation of num_samples looks odd, but it uses the following facts.
418 * 16 bit hardware counter is initialized with value of zero (which really
419 * means 0x1000). The counter decrements by one on each conversion
420 * (when the counter decrements from zero it goes to 0xffff). num_samples
421 * is a 16 bit variable, so it will roll over in a similar fashion to the
422 * hardware counter. Work it out, and this is what you get. */
423 num_samples = -hw_counter - devpriv->adc_count;
425 /* check if we only need some of the points */
426 if (cmd->stop_src == TRIG_COUNT) {
427 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
428 num_samples = cmd->stop_arg * cmd->chanlist_len;
430 /* make sure we dont try to get too many points if fifo has overrun */
431 if (num_samples > FIFO_SIZE)
432 num_samples = FIFO_SIZE;
433 insw(dev->iobase, devpriv->ai_buffer, num_samples);
434 munge_sample_array(devpriv->ai_buffer, num_samples);
435 comedi_buf_write_samples(s, devpriv->ai_buffer, num_samples);
436 devpriv->adc_count += num_samples;
438 if (cmd->stop_src == TRIG_COUNT) {
439 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
440 /* end of acquisition */
441 async->events |= COMEDI_CB_EOA;
445 /* this probably won't catch overruns since the card doesn't generate
446 * overrun interrupts, but we might as well try */
447 if (status & OVRUN) {
448 async->events |= COMEDI_CB_ERROR;
449 dev_err(dev->class_dev, "fifo overflow\n");
452 comedi_handle_events(dev, s);
455 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
460 /* prevent race with interrupt handler */
461 spin_lock_irqsave(&dev->spinlock, flags);
462 status = inb(dev->iobase + DAS16M1_CS);
463 das16m1_handler(dev, status);
464 spin_unlock_irqrestore(&dev->spinlock, flags);
466 return comedi_buf_n_bytes_ready(s);
469 static irqreturn_t das16m1_interrupt(int irq, void *d)
472 struct comedi_device *dev = d;
474 if (!dev->attached) {
475 dev_err(dev->class_dev, "premature interrupt\n");
478 /* prevent race with comedi_poll() */
479 spin_lock(&dev->spinlock);
481 status = inb(dev->iobase + DAS16M1_CS);
483 if ((status & (IRQDATA | OVRUN)) == 0) {
484 dev_err(dev->class_dev, "spurious interrupt\n");
485 spin_unlock(&dev->spinlock);
489 das16m1_handler(dev, status);
491 /* clear interrupt */
492 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
494 spin_unlock(&dev->spinlock);
498 static int das16m1_irq_bits(unsigned int irq)
527 static int das16m1_attach(struct comedi_device *dev,
528 struct comedi_devconfig *it)
530 struct das16m1_private_struct *devpriv;
531 struct comedi_subdevice *s;
534 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
538 ret = comedi_request_region(dev, it->options[0], 0x10);
541 /* Request an additional region for the 8255 */
542 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
546 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
548 /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
549 if ((1 << it->options[1]) & 0xdcfc) {
550 ret = request_irq(it->options[1], das16m1_interrupt, 0,
551 dev->board_name, dev);
553 dev->irq = it->options[1];
556 dev->pacer = comedi_8254_init(dev->iobase + DAS16M1_8254_SECOND,
557 I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
561 devpriv->counter = comedi_8254_init(dev->iobase + DAS16M1_8254_FIRST,
563 if (!devpriv->counter)
566 ret = comedi_alloc_subdevices(dev, 4);
570 s = &dev->subdevices[0];
572 s->type = COMEDI_SUBD_AI;
573 s->subdev_flags = SDF_READABLE | SDF_DIFF;
575 s->maxdata = (1 << 12) - 1;
576 s->range_table = &range_das16m1;
577 s->insn_read = das16m1_ai_rinsn;
579 dev->read_subdev = s;
580 s->subdev_flags |= SDF_CMD_READ;
581 s->len_chanlist = 256;
582 s->do_cmdtest = das16m1_cmd_test;
583 s->do_cmd = das16m1_cmd_exec;
584 s->cancel = das16m1_cancel;
585 s->poll = das16m1_poll;
588 s = &dev->subdevices[1];
590 s->type = COMEDI_SUBD_DI;
591 s->subdev_flags = SDF_READABLE;
594 s->range_table = &range_digital;
595 s->insn_bits = das16m1_di_rbits;
597 s = &dev->subdevices[2];
599 s->type = COMEDI_SUBD_DO;
600 s->subdev_flags = SDF_WRITABLE;
603 s->range_table = &range_digital;
604 s->insn_bits = das16m1_do_wbits;
606 s = &dev->subdevices[3];
608 ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
612 /* initialize digital output lines */
613 outb(0, dev->iobase + DAS16M1_DIO);
615 /* set the interrupt level */
616 devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
617 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
622 static void das16m1_detach(struct comedi_device *dev)
624 struct das16m1_private_struct *devpriv = dev->private;
627 if (devpriv->extra_iobase)
628 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
629 kfree(devpriv->counter);
631 comedi_legacy_detach(dev);
634 static struct comedi_driver das16m1_driver = {
635 .driver_name = "das16m1",
636 .module = THIS_MODULE,
637 .attach = das16m1_attach,
638 .detach = das16m1_detach,
640 module_comedi_driver(das16m1_driver);
642 MODULE_AUTHOR("Comedi http://www.comedi.org");
643 MODULE_DESCRIPTION("Comedi low-level driver");
644 MODULE_LICENSE("GPL");