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/interrupt.h>
57 #include "../comedidev.h"
61 #include "comedi_fc.h"
63 #define DAS16M1_SIZE 16
64 #define DAS16M1_SIZE2 8
66 #define FIFO_SIZE 1024 /* 1024 sample fifo */
73 0 a/d bits 0-3, mux start 12 bit
74 1 a/d bits 4-11 unused
77 4 unused clear interrupt
79 6 channel/gain queue address
80 7 channel/gain queue data
88 #define DAS16M1_AI 0 /* 16-bit wide register */
89 #define AI_CHAN(x) ((x) & 0xf)
91 #define EXT_TRIG_BIT 0x1
95 #define DAS16M1_CLEAR_INTR 4
96 #define DAS16M1_INTR_CONTROL 5
99 #define PACER_MASK 0x3
101 #define DAS16M1_QUEUE_ADDR 6
102 #define DAS16M1_QUEUE_DATA 7
103 #define Q_CHAN(x) ((x) & 0x7)
104 #define Q_RANGE(x) (((x) & 0xf) << 4)
105 #define UNIPOLAR 0x40
106 #define DAS16M1_8254_FIRST 0x8
107 #define DAS16M1_8254_FIRST_CNTRL 0xb
108 #define TOTAL_CLEAR 0x30
109 #define DAS16M1_8254_SECOND 0xc
110 #define DAS16M1_82C55 0x400
111 #define DAS16M1_8254_THIRD 0x404
113 static const struct comedi_lrange range_das16m1 = { 9,
127 struct das16m1_private_struct {
128 unsigned int control_state;
129 volatile unsigned int adc_count; /* number of samples completed */
130 /* initial value in lower half of hardware conversion counter,
131 * needed to keep track of whether new count has been loaded into
132 * counter yet (loaded by first sample conversion) */
133 u16 initial_hw_count;
134 unsigned short ai_buffer[FIFO_SIZE];
135 unsigned int divisor1; /* divides master clock to obtain conversion speed */
136 unsigned int divisor2; /* divides master clock to obtain conversion speed */
137 unsigned long extra_iobase;
140 static inline unsigned short munge_sample(unsigned short data)
142 return (data >> 4) & 0xfff;
145 static void munge_sample_array(unsigned short *array, unsigned int num_elements)
149 for (i = 0; i < num_elements; i++)
150 array[i] = munge_sample(array[i]);
153 static int das16m1_cmd_test(struct comedi_device *dev,
154 struct comedi_subdevice *s, struct comedi_cmd *cmd)
156 struct das16m1_private_struct *devpriv = dev->private;
157 unsigned int err = 0, tmp, i;
159 /* Step 1 : check if triggers are trivially valid */
161 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
162 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
163 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
164 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
165 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
170 /* Step 2a : make sure trigger sources are unique */
172 err |= cfc_check_trigger_is_unique(cmd->start_src);
173 err |= cfc_check_trigger_is_unique(cmd->convert_src);
174 err |= cfc_check_trigger_is_unique(cmd->stop_src);
176 /* Step 2b : and mutually compatible */
181 /* Step 3: check if arguments are trivially valid */
183 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
185 if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
186 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
188 if (cmd->convert_src == TRIG_TIMER)
189 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 1000);
191 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
193 if (cmd->stop_src == TRIG_COUNT) {
194 /* any count is allowed */
197 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
203 /* step 4: fix up arguments */
205 if (cmd->convert_src == TRIG_TIMER) {
206 tmp = cmd->convert_arg;
207 /* calculate counter values that give desired timing */
208 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
211 &cmd->convert_arg, cmd->flags);
212 if (tmp != cmd->convert_arg)
219 /* check chanlist against board's peculiarities */
220 if (cmd->chanlist && cmd->chanlist_len > 1) {
221 for (i = 0; i < cmd->chanlist_len; i++) {
222 /* even/odd channels must go into even/odd queue addresses */
223 if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
224 comedi_error(dev, "bad chanlist:\n"
225 " even/odd channels must go have even/odd chanlist indices");
229 if ((cmd->chanlist_len % 2) != 0) {
231 "chanlist must be of even length or length 1");
242 /* This function takes a time in nanoseconds and sets the *
243 * 2 pacer clocks to the closest frequency possible. It also *
244 * returns the actual sampling period. */
245 static unsigned int das16m1_set_pacer(struct comedi_device *dev,
246 unsigned int ns, int rounding_flags)
248 struct das16m1_private_struct *devpriv = dev->private;
250 i8253_cascade_ns_to_timer_2div(I8254_OSC_BASE_10MHZ,
253 &ns, rounding_flags);
255 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
256 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
258 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
264 static int das16m1_cmd_exec(struct comedi_device *dev,
265 struct comedi_subdevice *s)
267 struct das16m1_private_struct *devpriv = dev->private;
268 struct comedi_async *async = s->async;
269 struct comedi_cmd *cmd = &async->cmd;
270 unsigned int byte, i;
273 comedi_error(dev, "irq required to execute comedi_cmd");
277 /* disable interrupts and internal pacer */
278 devpriv->control_state &= ~INTE & ~PACER_MASK;
279 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
281 /* set software count */
282 devpriv->adc_count = 0;
283 /* Initialize lower half of hardware counter, used to determine how
284 * many samples are in fifo. Value doesn't actually load into counter
285 * until counter's next clock (the next a/d conversion) */
286 i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
287 /* remember current reading of counter so we know when counter has
288 * actually been loaded */
289 devpriv->initial_hw_count =
290 i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
291 /* setup channel/gain queue */
292 for (i = 0; i < cmd->chanlist_len; i++) {
293 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
295 Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
296 Q_RANGE(CR_RANGE(cmd->chanlist[i]));
297 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
300 /* set counter mode and counts */
302 das16m1_set_pacer(dev, cmd->convert_arg,
303 cmd->flags & TRIG_ROUND_MASK);
305 /* set control & status register */
307 /* if we are using external start trigger (also board dislikes having
308 * both start and conversion triggers external simultaneously) */
309 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
310 byte |= EXT_TRIG_BIT;
312 outb(byte, dev->iobase + DAS16M1_CS);
313 /* clear interrupt bit */
314 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
316 /* enable interrupts and internal pacer */
317 devpriv->control_state &= ~PACER_MASK;
318 if (cmd->convert_src == TRIG_TIMER)
319 devpriv->control_state |= INT_PACER;
321 devpriv->control_state |= EXT_PACER;
323 devpriv->control_state |= INTE;
324 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
329 static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
331 struct das16m1_private_struct *devpriv = dev->private;
333 devpriv->control_state &= ~INTE & ~PACER_MASK;
334 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
339 static int das16m1_ai_rinsn(struct comedi_device *dev,
340 struct comedi_subdevice *s,
341 struct comedi_insn *insn, unsigned int *data)
343 struct das16m1_private_struct *devpriv = dev->private;
346 const int timeout = 1000;
348 /* disable interrupts and internal pacer */
349 devpriv->control_state &= ~INTE & ~PACER_MASK;
350 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
352 /* setup channel/gain queue */
353 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
355 Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
356 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
358 for (n = 0; n < insn->n; n++) {
359 /* clear IRQDATA bit */
360 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
361 /* trigger conversion */
362 outb(0, dev->iobase);
364 for (i = 0; i < timeout; i++) {
365 if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
369 comedi_error(dev, "timeout");
372 data[n] = munge_sample(inw(dev->iobase));
378 static int das16m1_di_rbits(struct comedi_device *dev,
379 struct comedi_subdevice *s,
380 struct comedi_insn *insn, unsigned int *data)
384 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
391 static int das16m1_do_wbits(struct comedi_device *dev,
392 struct comedi_subdevice *s,
393 struct comedi_insn *insn,
396 if (comedi_dio_update_state(s, data))
397 outb(s->state, dev->iobase + DAS16M1_DIO);
404 static void das16m1_handler(struct comedi_device *dev, unsigned int status)
406 struct das16m1_private_struct *devpriv = dev->private;
407 struct comedi_subdevice *s;
408 struct comedi_async *async;
409 struct comedi_cmd *cmd;
413 s = dev->read_subdev;
418 /* figure out how many samples are in fifo */
419 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
420 /* make sure hardware counter reading is not bogus due to initial value
421 * not having been loaded yet */
422 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
425 /* The calculation of num_samples looks odd, but it uses the following facts.
426 * 16 bit hardware counter is initialized with value of zero (which really
427 * means 0x1000). The counter decrements by one on each conversion
428 * (when the counter decrements from zero it goes to 0xffff). num_samples
429 * is a 16 bit variable, so it will roll over in a similar fashion to the
430 * hardware counter. Work it out, and this is what you get. */
431 num_samples = -hw_counter - devpriv->adc_count;
433 /* check if we only need some of the points */
434 if (cmd->stop_src == TRIG_COUNT) {
435 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
436 num_samples = cmd->stop_arg * cmd->chanlist_len;
438 /* make sure we dont try to get too many points if fifo has overrun */
439 if (num_samples > FIFO_SIZE)
440 num_samples = FIFO_SIZE;
441 insw(dev->iobase, devpriv->ai_buffer, num_samples);
442 munge_sample_array(devpriv->ai_buffer, num_samples);
443 cfc_write_array_to_buffer(s, devpriv->ai_buffer,
444 num_samples * sizeof(short));
445 devpriv->adc_count += num_samples;
447 if (cmd->stop_src == TRIG_COUNT) {
448 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
449 das16m1_cancel(dev, s);
450 async->events |= COMEDI_CB_EOA;
454 /* this probably won't catch overruns since the card doesn't generate
455 * overrun interrupts, but we might as well try */
456 if (status & OVRUN) {
457 das16m1_cancel(dev, s);
458 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
459 comedi_error(dev, "fifo overflow");
462 comedi_event(dev, s);
466 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
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);
477 return s->async->buf_write_count - s->async->buf_read_count;
480 static irqreturn_t das16m1_interrupt(int irq, void *d)
483 struct comedi_device *dev = d;
485 if (!dev->attached) {
486 comedi_error(dev, "premature interrupt");
489 /* prevent race with comedi_poll() */
490 spin_lock(&dev->spinlock);
492 status = inb(dev->iobase + DAS16M1_CS);
494 if ((status & (IRQDATA | OVRUN)) == 0) {
495 comedi_error(dev, "spurious interrupt");
496 spin_unlock(&dev->spinlock);
500 das16m1_handler(dev, status);
502 /* clear interrupt */
503 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
505 spin_unlock(&dev->spinlock);
509 static int das16m1_irq_bits(unsigned int irq)
550 static int das16m1_attach(struct comedi_device *dev,
551 struct comedi_devconfig *it)
553 struct das16m1_private_struct *devpriv;
554 struct comedi_subdevice *s;
558 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
562 ret = comedi_request_region(dev, it->options[0], DAS16M1_SIZE);
565 /* Request an additional region for the 8255 */
566 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
570 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
572 /* now for the irq */
573 irq = it->options[1];
574 /* make sure it is valid */
575 if (das16m1_irq_bits(irq) >= 0) {
576 ret = request_irq(irq, das16m1_interrupt, 0,
577 dev->driver->driver_name, dev);
583 } else if (irq == 0) {
587 comedi_error(dev, "invalid irq\n"
588 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
592 ret = comedi_alloc_subdevices(dev, 4);
596 s = &dev->subdevices[0];
597 dev->read_subdev = s;
599 s->type = COMEDI_SUBD_AI;
600 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
602 s->subdev_flags = SDF_DIFF;
603 s->len_chanlist = 256;
604 s->maxdata = (1 << 12) - 1;
605 s->range_table = &range_das16m1;
606 s->insn_read = das16m1_ai_rinsn;
607 s->do_cmdtest = das16m1_cmd_test;
608 s->do_cmd = das16m1_cmd_exec;
609 s->cancel = das16m1_cancel;
610 s->poll = das16m1_poll;
612 s = &dev->subdevices[1];
614 s->type = COMEDI_SUBD_DI;
615 s->subdev_flags = SDF_READABLE;
618 s->range_table = &range_digital;
619 s->insn_bits = das16m1_di_rbits;
621 s = &dev->subdevices[2];
623 s->type = COMEDI_SUBD_DO;
624 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
627 s->range_table = &range_digital;
628 s->insn_bits = das16m1_do_wbits;
630 s = &dev->subdevices[3];
632 ret = subdev_8255_init(dev, s, NULL, devpriv->extra_iobase);
636 /* disable upper half of hardware conversion counter so it doesn't mess with us */
637 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
639 /* initialize digital output lines */
640 outb(0, dev->iobase + DAS16M1_DIO);
642 /* set the interrupt level */
644 devpriv->control_state = das16m1_irq_bits(dev->irq);
646 devpriv->control_state = 0;
647 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
652 static void das16m1_detach(struct comedi_device *dev)
654 struct das16m1_private_struct *devpriv = dev->private;
656 if (devpriv && devpriv->extra_iobase)
657 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
658 comedi_legacy_detach(dev);
661 static struct comedi_driver das16m1_driver = {
662 .driver_name = "das16m1",
663 .module = THIS_MODULE,
664 .attach = das16m1_attach,
665 .detach = das16m1_detach,
667 module_comedi_driver(das16m1_driver);
669 MODULE_AUTHOR("Comedi http://www.comedi.org");
670 MODULE_DESCRIPTION("Comedi low-level driver");
671 MODULE_LICENSE("GPL");