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 &&
415 hw_counter == devpriv->initial_hw_count) {
418 /* The calculation of num_samples looks odd, but it uses the
419 * following facts. 16 bit hardware counter is initialized with
420 * value of zero (which really means 0x1000). The counter
421 * decrements by one on each conversion (when the counter
422 * decrements from zero it goes to 0xffff). num_samples is a
423 * 16 bit variable, so it will roll over in a similar fashion
424 * to the hardware counter. Work it out, and this is what you
426 num_samples = -hw_counter - devpriv->adc_count;
428 /* check if we only need some of the points */
429 if (cmd->stop_src == TRIG_COUNT) {
430 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
431 num_samples = cmd->stop_arg * cmd->chanlist_len;
433 /* make sure we dont try to get too many points if fifo has overrun */
434 if (num_samples > FIFO_SIZE)
435 num_samples = FIFO_SIZE;
436 insw(dev->iobase, devpriv->ai_buffer, num_samples);
437 munge_sample_array(devpriv->ai_buffer, num_samples);
438 comedi_buf_write_samples(s, devpriv->ai_buffer, num_samples);
439 devpriv->adc_count += num_samples;
441 if (cmd->stop_src == TRIG_COUNT) {
442 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) {
443 /* end of acquisition */
444 async->events |= COMEDI_CB_EOA;
448 /* this probably won't catch overruns since the card doesn't generate
449 * overrun interrupts, but we might as well try */
450 if (status & OVRUN) {
451 async->events |= COMEDI_CB_ERROR;
452 dev_err(dev->class_dev, "fifo overflow\n");
455 comedi_handle_events(dev, s);
458 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
463 /* prevent race with interrupt handler */
464 spin_lock_irqsave(&dev->spinlock, flags);
465 status = inb(dev->iobase + DAS16M1_CS);
466 das16m1_handler(dev, status);
467 spin_unlock_irqrestore(&dev->spinlock, flags);
469 return comedi_buf_n_bytes_ready(s);
472 static irqreturn_t das16m1_interrupt(int irq, void *d)
475 struct comedi_device *dev = d;
477 if (!dev->attached) {
478 dev_err(dev->class_dev, "premature interrupt\n");
481 /* prevent race with comedi_poll() */
482 spin_lock(&dev->spinlock);
484 status = inb(dev->iobase + DAS16M1_CS);
486 if ((status & (IRQDATA | OVRUN)) == 0) {
487 dev_err(dev->class_dev, "spurious interrupt\n");
488 spin_unlock(&dev->spinlock);
492 das16m1_handler(dev, status);
494 /* clear interrupt */
495 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
497 spin_unlock(&dev->spinlock);
501 static int das16m1_irq_bits(unsigned int irq)
530 static int das16m1_attach(struct comedi_device *dev,
531 struct comedi_devconfig *it)
533 struct das16m1_private_struct *devpriv;
534 struct comedi_subdevice *s;
537 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
541 ret = comedi_request_region(dev, it->options[0], 0x10);
544 /* Request an additional region for the 8255 */
545 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
549 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
551 /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
552 if ((1 << it->options[1]) & 0xdcfc) {
553 ret = request_irq(it->options[1], das16m1_interrupt, 0,
554 dev->board_name, dev);
556 dev->irq = it->options[1];
559 dev->pacer = comedi_8254_init(dev->iobase + DAS16M1_8254_SECOND,
560 I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
564 devpriv->counter = comedi_8254_init(dev->iobase + DAS16M1_8254_FIRST,
566 if (!devpriv->counter)
569 ret = comedi_alloc_subdevices(dev, 4);
573 s = &dev->subdevices[0];
575 s->type = COMEDI_SUBD_AI;
576 s->subdev_flags = SDF_READABLE | SDF_DIFF;
578 s->maxdata = (1 << 12) - 1;
579 s->range_table = &range_das16m1;
580 s->insn_read = das16m1_ai_rinsn;
582 dev->read_subdev = s;
583 s->subdev_flags |= SDF_CMD_READ;
584 s->len_chanlist = 256;
585 s->do_cmdtest = das16m1_cmd_test;
586 s->do_cmd = das16m1_cmd_exec;
587 s->cancel = das16m1_cancel;
588 s->poll = das16m1_poll;
591 s = &dev->subdevices[1];
593 s->type = COMEDI_SUBD_DI;
594 s->subdev_flags = SDF_READABLE;
597 s->range_table = &range_digital;
598 s->insn_bits = das16m1_di_rbits;
600 s = &dev->subdevices[2];
602 s->type = COMEDI_SUBD_DO;
603 s->subdev_flags = SDF_WRITABLE;
606 s->range_table = &range_digital;
607 s->insn_bits = das16m1_do_wbits;
609 s = &dev->subdevices[3];
611 ret = subdev_8255_init(dev, s, NULL, DAS16M1_82C55);
615 /* initialize digital output lines */
616 outb(0, dev->iobase + DAS16M1_DIO);
618 /* set the interrupt level */
619 devpriv->control_state = das16m1_irq_bits(dev->irq) << 4;
620 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
625 static void das16m1_detach(struct comedi_device *dev)
627 struct das16m1_private_struct *devpriv = dev->private;
630 if (devpriv->extra_iobase)
631 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
632 kfree(devpriv->counter);
634 comedi_legacy_detach(dev);
637 static struct comedi_driver das16m1_driver = {
638 .driver_name = "das16m1",
639 .module = THIS_MODULE,
640 .attach = das16m1_attach,
641 .detach = das16m1_detach,
643 module_comedi_driver(das16m1_driver);
645 MODULE_AUTHOR("Comedi http://www.comedi.org");
646 MODULE_DESCRIPTION("Comedi low-level driver");
647 MODULE_LICENSE("GPL");