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>
6 COMEDI - Linux Control and Measurement Device Interface
7 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
23 ************************************************************************
27 Description: Keithley Metrabyte DAS800 (& compatibles)
28 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
29 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
31 [Measurement Computing] CIO-DAS800 (cio-das800),
32 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
33 CIO-DAS802/16 (cio-das802/16)
34 Status: works, cio-das802/16 untested - email me if you have tested it
36 Configuration options:
37 [0] - I/O port base address
38 [1] - IRQ (optional, required for timed or externally triggered conversions)
41 IRQ can be omitted, although the cmd interface will not work without it.
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.)
47 I've never tested the gain setting stuff since I only have a
48 DAS-800 board with fixed gain.
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.
55 cmd 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
65 #include <linux/interrupt.h>
66 #include "../comedidev.h"
68 #include <linux/ioport.h>
69 #include <linux/delay.h>
72 #include "comedi_fc.h"
75 #define TIMER_BASE 1000
76 #define N_CHAN_AI 8 /* number of analog input channels */
78 /* Registers for the das800 */
81 #define FIFO_EMPTY 0x1
84 #define DAS800_CONTROL1 2
85 #define CONTROL1_INTE 0x8
86 #define DAS800_CONV_CONTROL 2
92 #define CONV_HCEN 0x80
93 #define DAS800_SCAN_LIMITS 2
94 #define DAS800_STATUS 2
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
104 #define DAS800_8254 4
105 #define DAS800_STATUS2 7
106 #define STATUS2_HCEN 0x80
107 #define STATUS2_INTE 0X20
110 struct das800_board {
113 const struct comedi_lrange *ai_range;
117 /* analog input ranges */
118 static const struct comedi_lrange range_das800_ai = {
125 static const struct comedi_lrange range_das801_ai = {
140 static const struct comedi_lrange range_cio_das801_ai = {
150 RANGE(-0.005, 0.005),
155 static const struct comedi_lrange range_das802_ai = {
165 RANGE(-0.625, 0.625),
170 static const struct comedi_lrange range_das80216_ai = {
184 enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
186 static const struct das800_board das800_boards[] = {
190 .ai_range = &range_das800_ai,
194 .name = "cio-das800",
196 .ai_range = &range_das800_ai,
202 .ai_range = &range_das801_ai,
206 .name = "cio-das801",
208 .ai_range = &range_cio_das801_ai,
214 .ai_range = &range_das802_ai,
218 .name = "cio-das802",
220 .ai_range = &range_das802_ai,
224 .name = "cio-das802/16",
226 .ai_range = &range_das80216_ai,
232 * Useful for shorthand access to the particular board structure
234 #define thisboard ((const struct das800_board *)dev->board_ptr)
236 struct 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 */
244 #define devpriv ((struct das800_private *)dev->private)
246 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it);
247 static int das800_detach(struct comedi_device *dev);
248 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
250 static struct comedi_driver driver_das800 = {
251 .driver_name = "das800",
252 .module = THIS_MODULE,
253 .attach = das800_attach,
254 .detach = das800_detach,
255 .num_names = ARRAY_SIZE(das800_boards),
256 .board_name = &das800_boards[0].name,
257 .offset = sizeof(struct das800_board),
260 static irqreturn_t das800_interrupt(int irq, void *d);
261 static void enable_das800(struct comedi_device *dev);
262 static void disable_das800(struct comedi_device *dev);
263 static int das800_ai_do_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
264 struct comedi_cmd *cmd);
265 static int das800_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
266 static int das800_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
267 struct comedi_insn *insn, unsigned int *data);
268 static int das800_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
269 struct comedi_insn *insn, unsigned int *data);
270 static int das800_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
271 struct comedi_insn *insn, unsigned int *data);
272 static int das800_probe(struct comedi_device *dev);
273 static int das800_set_frequency(struct comedi_device *dev);
275 /* checks and probes das-800 series board type */
276 static int das800_probe(struct comedi_device *dev)
279 unsigned long irq_flags;
282 /* 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
283 spin_lock_irqsave(&dev->spinlock, irq_flags);
284 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
285 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
286 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
288 board = thisboard - das800_boards;
292 if (board == das800) {
293 printk(" Board model: DAS-800\n");
296 if (board == ciodas800) {
297 printk(" Board model: CIO-DAS800\n");
300 printk(" Board model (probed): DAS-800\n");
304 if (board == das801) {
305 printk(" Board model: DAS-801\n");
308 if (board == ciodas801) {
309 printk(" Board model: CIO-DAS801\n");
312 printk(" Board model (probed): DAS-801\n");
316 if (board == das802) {
317 printk(" Board model: DAS-802\n");
320 if (board == ciodas802) {
321 printk(" Board model: CIO-DAS802\n");
324 if (board == ciodas80216) {
325 printk(" Board model: CIO-DAS802/16\n");
328 printk(" Board model (probed): DAS-802\n");
332 printk(" Board model: probe returned 0x%x (unknown)\n",
341 * A convenient macro that defines init_module() and cleanup_module(),
344 COMEDI_INITCLEANUP(driver_das800);
346 /* interrupt service routine */
347 static irqreturn_t das800_interrupt(int irq, void *d)
349 short i; /* loop index */
351 struct comedi_device *dev = d;
352 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
353 struct comedi_async *async;
355 unsigned long irq_flags;
356 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
359 int fifo_overflow = 0;
361 status = inb(dev->iobase + DAS800_STATUS);
362 /* if interrupt was not generated by board or driver not attached, quit */
365 if (!(dev->attached))
368 /* wait until here to initialize async, since we will get null dereference
369 * if interrupt occurs before driver is fully attached!
373 /* if hardware conversions are not enabled, then quit */
374 spin_lock_irqsave(&dev->spinlock, irq_flags);
375 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
376 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
377 /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
379 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
383 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
384 for (i = 0; i < max_loops; i++) {
385 /* read 16 bits from dev->iobase and dev->iobase + 1 */
386 dataPoint = inb(dev->iobase + DAS800_LSB);
387 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
388 if (thisboard->resolution == 12) {
389 fifo_empty = dataPoint & FIFO_EMPTY;
390 fifo_overflow = dataPoint & FIFO_OVF;
394 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
399 /* strip off extraneous bits for 12 bit cards */
400 if (thisboard->resolution == 12)
401 dataPoint = (dataPoint >> 4) & 0xfff;
402 /* if there are more data points to collect */
403 if (devpriv->count > 0 || devpriv->forever == 1) {
404 /* write data point to buffer */
405 cfc_write_to_buffer(s, dataPoint);
406 if (devpriv->count > 0)
410 async->events |= COMEDI_CB_BLOCK;
411 /* check for fifo overflow */
412 if (thisboard->resolution == 12) {
413 fifo_overflow = dataPoint & FIFO_OVF;
414 /* else cio-das802/16 */
416 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
419 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
420 comedi_error(dev, "DAS800 FIFO overflow");
421 das800_cancel(dev, dev->subdevices + 0);
422 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
423 comedi_event(dev, s);
427 if (devpriv->count > 0 || devpriv->forever == 1) {
428 /* Re-enable card's interrupt.
429 * We already have spinlock, so indirect addressing is safe */
430 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
431 outb(CONTROL1_INTE | devpriv->do_bits,
432 dev->iobase + DAS800_CONTROL1);
433 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
434 /* otherwise, stop taking data */
436 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
437 disable_das800(dev); /* diable hardware triggered conversions */
438 async->events |= COMEDI_CB_EOA;
440 comedi_event(dev, s);
445 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
447 struct comedi_subdevice *s;
448 unsigned long iobase = it->options[0];
449 unsigned int irq = it->options[1];
450 unsigned long irq_flags;
453 printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
455 printk(", irq %u", irq);
459 /* allocate and initialize dev->private */
460 if (alloc_private(dev, sizeof(struct das800_private)) < 0)
464 printk("io base address required for das800\n");
468 /* check if io addresses are available */
469 if (!request_region(iobase, DAS800_SIZE, "das800")) {
470 printk("I/O port conflict\n");
473 dev->iobase = iobase;
475 board = das800_probe(dev);
477 printk("unable to determine board type\n");
480 dev->board_ptr = das800_boards + board;
483 if (irq == 1 || irq > 7) {
484 printk("irq out of range\n");
488 if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
489 printk("unable to allocate irq %u\n", irq);
495 dev->board_name = thisboard->name;
497 if (alloc_subdevices(dev, 3) < 0)
500 /* analog input subdevice */
501 s = dev->subdevices + 0;
502 dev->read_subdev = s;
503 s->type = COMEDI_SUBD_AI;
504 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
507 s->maxdata = (1 << thisboard->resolution) - 1;
508 s->range_table = thisboard->ai_range;
509 s->do_cmd = das800_ai_do_cmd;
510 s->do_cmdtest = das800_ai_do_cmdtest;
511 s->insn_read = das800_ai_rinsn;
512 s->cancel = das800_cancel;
515 s = dev->subdevices + 1;
516 s->type = COMEDI_SUBD_DI;
517 s->subdev_flags = SDF_READABLE;
520 s->range_table = &range_digital;
521 s->insn_bits = das800_di_rbits;
524 s = dev->subdevices + 2;
525 s->type = COMEDI_SUBD_DO;
526 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
529 s->range_table = &range_digital;
530 s->insn_bits = das800_do_wbits;
534 /* initialize digital out channels */
535 spin_lock_irqsave(&dev->spinlock, irq_flags);
536 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
537 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
538 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
543 static int das800_detach(struct comedi_device *dev)
545 printk("comedi%d: das800: remove\n", dev->minor);
547 /* only free stuff if it has been allocated by _attach */
549 release_region(dev->iobase, DAS800_SIZE);
551 free_irq(dev->irq, dev);
555 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
557 devpriv->forever = 0;
563 /* enable_das800 makes the card start taking hardware triggered conversions */
564 static void enable_das800(struct comedi_device *dev)
566 unsigned long irq_flags;
567 spin_lock_irqsave(&dev->spinlock, irq_flags);
568 /* enable fifo-half full interrupts for cio-das802/16 */
569 if (thisboard->resolution == 16)
570 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
571 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
572 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
573 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
574 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
575 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
578 /* disable_das800 stops hardware triggered conversions */
579 static void disable_das800(struct comedi_device *dev)
581 unsigned long irq_flags;
582 spin_lock_irqsave(&dev->spinlock, irq_flags);
583 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
584 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
585 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
588 static int das800_ai_do_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
589 struct comedi_cmd *cmd)
596 /* step 1: make sure trigger sources are trivially valid */
598 tmp = cmd->start_src;
599 cmd->start_src &= TRIG_NOW | TRIG_EXT;
600 if (!cmd->start_src || tmp != cmd->start_src)
603 tmp = cmd->scan_begin_src;
604 cmd->scan_begin_src &= TRIG_FOLLOW;
605 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
608 tmp = cmd->convert_src;
609 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
610 if (!cmd->convert_src || tmp != cmd->convert_src)
613 tmp = cmd->scan_end_src;
614 cmd->scan_end_src &= TRIG_COUNT;
615 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
619 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
620 if (!cmd->stop_src || tmp != cmd->stop_src)
626 /* step 2: make sure trigger sources are unique and mutually compatible */
628 if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
630 if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
632 if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
638 /* step 3: make sure arguments are trivially compatible */
640 if (cmd->start_arg != 0) {
644 if (cmd->convert_src == TRIG_TIMER) {
645 if (cmd->convert_arg < thisboard->ai_speed) {
646 cmd->convert_arg = thisboard->ai_speed;
650 if (!cmd->chanlist_len) {
651 cmd->chanlist_len = 1;
654 if (cmd->scan_end_arg != cmd->chanlist_len) {
655 cmd->scan_end_arg = cmd->chanlist_len;
658 if (cmd->stop_src == TRIG_COUNT) {
659 if (!cmd->stop_arg) {
663 } else { /* TRIG_NONE */
664 if (cmd->stop_arg != 0) {
673 /* step 4: fix up any arguments */
675 if (cmd->convert_src == TRIG_TIMER) {
676 tmp = cmd->convert_arg;
677 /* calculate counter values that give desired timing */
678 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
679 &(devpriv->divisor2), &(cmd->convert_arg),
680 cmd->flags & TRIG_ROUND_MASK);
681 if (tmp != cmd->convert_arg)
688 /* check channel/gain list against card's limitations */
690 gain = CR_RANGE(cmd->chanlist[0]);
691 startChan = CR_CHAN(cmd->chanlist[0]);
692 for (i = 1; i < cmd->chanlist_len; i++) {
693 if (CR_CHAN(cmd->chanlist[i]) !=
694 (startChan + i) % N_CHAN_AI) {
696 "entries in chanlist must be consecutive channels, counting upwards\n");
699 if (CR_RANGE(cmd->chanlist[i]) != gain) {
701 "entries in chanlist must all have the same gain\n");
713 static int das800_ai_do_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
715 int startChan, endChan, scan, gain;
717 unsigned long irq_flags;
718 struct comedi_async *async = s->async;
722 "no irq assigned for das-800, cannot do hardware conversions");
728 /* set channel scan limits */
729 startChan = CR_CHAN(async->cmd.chanlist[0]);
730 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
731 scan = (endChan << 3) | startChan;
733 spin_lock_irqsave(&dev->spinlock, irq_flags);
734 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
735 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
736 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
739 gain = CR_RANGE(async->cmd.chanlist[0]);
740 if (thisboard->resolution == 12 && gain > 0)
743 outb(gain, dev->iobase + DAS800_GAIN);
745 switch (async->cmd.stop_src) {
747 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
748 devpriv->forever = 0;
751 devpriv->forever = 1;
758 /* enable auto channel scan, send interrupts on end of conversion
759 * and set clock source to internal or external
762 conv_bits |= EACS | IEOC;
763 if (async->cmd.start_src == TRIG_EXT)
765 switch (async->cmd.convert_src) {
767 conv_bits |= CASC | ITE;
768 /* set conversion frequency */
769 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
770 &(devpriv->divisor2), &(async->cmd.convert_arg),
771 async->cmd.flags & TRIG_ROUND_MASK);
772 if (das800_set_frequency(dev) < 0) {
773 comedi_error(dev, "Error setting up counters");
783 spin_lock_irqsave(&dev->spinlock, irq_flags);
784 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
785 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
786 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
792 static int das800_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
793 struct comedi_insn *insn, unsigned int *data)
800 unsigned long irq_flags;
802 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
804 /* set multiplexer */
805 chan = CR_CHAN(insn->chanspec);
807 spin_lock_irqsave(&dev->spinlock, irq_flags);
808 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
809 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
810 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
812 /* set gain / range */
813 range = CR_RANGE(insn->chanspec);
814 if (thisboard->resolution == 12 && range)
817 outb(range, dev->iobase + DAS800_GAIN);
821 for (n = 0; n < insn->n; n++) {
822 /* trigger conversion */
823 outb_p(0, dev->iobase + DAS800_MSB);
825 for (i = 0; i < timeout; i++) {
826 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
830 comedi_error(dev, "timeout");
833 lsb = inb(dev->iobase + DAS800_LSB);
834 msb = inb(dev->iobase + DAS800_MSB);
835 if (thisboard->resolution == 12) {
836 data[n] = (lsb >> 4) & 0xff;
837 data[n] |= (msb << 4);
839 data[n] = (msb << 8) | lsb;
846 static int das800_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
847 struct comedi_insn *insn, unsigned int *data)
851 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
859 static int das800_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
860 struct comedi_insn *insn, unsigned int *data)
863 unsigned long irq_flags;
865 /* only set bits that have been masked */
867 wbits = devpriv->do_bits >> 4;
869 wbits |= data[0] & data[1];
870 devpriv->do_bits = wbits << 4;
872 spin_lock_irqsave(&dev->spinlock, irq_flags);
873 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
874 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
875 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
882 /* loads counters with divisor1, divisor2 from private structure */
883 static int das800_set_frequency(struct comedi_device *dev)
887 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
889 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))