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 static const struct comedi_lrange range_das801_ai = {
132 static const struct comedi_lrange range_cio_das801_ai = {
142 RANGE(-0.005, 0.005),
147 static const struct comedi_lrange range_das802_ai = {
157 RANGE(-0.625, 0.625),
162 static const struct comedi_lrange range_das80216_ai = {
176 enum { das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
178 static const struct das800_board das800_boards[] = {
182 .ai_range = &range_bipolar5,
186 .name = "cio-das800",
188 .ai_range = &range_bipolar5,
194 .ai_range = &range_das801_ai,
198 .name = "cio-das801",
200 .ai_range = &range_cio_das801_ai,
206 .ai_range = &range_das802_ai,
210 .name = "cio-das802",
212 .ai_range = &range_das802_ai,
216 .name = "cio-das802/16",
218 .ai_range = &range_das80216_ai,
223 struct das800_private {
224 volatile unsigned int count; /* number of data points left to be taken */
225 volatile int forever; /* flag indicating whether we should take data forever */
226 unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
227 unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
228 volatile int do_bits; /* digital output bits */
231 static int das800_attach(struct comedi_device *dev,
232 struct comedi_devconfig *it);
233 static void das800_detach(struct comedi_device *dev);
234 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
236 static struct comedi_driver driver_das800 = {
237 .driver_name = "das800",
238 .module = THIS_MODULE,
239 .attach = das800_attach,
240 .detach = das800_detach,
241 .num_names = ARRAY_SIZE(das800_boards),
242 .board_name = &das800_boards[0].name,
243 .offset = sizeof(struct das800_board),
246 static irqreturn_t das800_interrupt(int irq, void *d);
247 static void enable_das800(struct comedi_device *dev);
248 static void disable_das800(struct comedi_device *dev);
249 static int das800_ai_do_cmdtest(struct comedi_device *dev,
250 struct comedi_subdevice *s,
251 struct comedi_cmd *cmd);
252 static int das800_ai_do_cmd(struct comedi_device *dev,
253 struct comedi_subdevice *s);
254 static int das800_ai_rinsn(struct comedi_device *dev,
255 struct comedi_subdevice *s, struct comedi_insn *insn,
257 static int das800_di_rbits(struct comedi_device *dev,
258 struct comedi_subdevice *s, struct comedi_insn *insn,
260 static int das800_do_wbits(struct comedi_device *dev,
261 struct comedi_subdevice *s, struct comedi_insn *insn,
263 static int das800_probe(struct comedi_device *dev);
264 static int das800_set_frequency(struct comedi_device *dev);
266 /* checks and probes das-800 series board type */
267 static int das800_probe(struct comedi_device *dev)
269 const struct das800_board *thisboard = comedi_board(dev);
271 unsigned long irq_flags;
274 /* 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
275 spin_lock_irqsave(&dev->spinlock, irq_flags);
276 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
277 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
278 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
280 board = thisboard - das800_boards;
284 if (board == das800) {
285 dev_dbg(dev->class_dev, "Board model: DAS-800\n");
288 if (board == ciodas800) {
289 dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
292 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
296 if (board == das801) {
297 dev_dbg(dev->class_dev, "Board model: DAS-801\n");
300 if (board == ciodas801) {
301 dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
304 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
308 if (board == das802) {
309 dev_dbg(dev->class_dev, "Board model: DAS-802\n");
312 if (board == ciodas802) {
313 dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
316 if (board == ciodas80216) {
317 dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
320 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
324 dev_dbg(dev->class_dev,
325 "Board model: probe returned 0x%x (unknown)\n",
333 module_comedi_driver(driver_das800);
335 /* interrupt service routine */
336 static irqreturn_t das800_interrupt(int irq, void *d)
338 short i; /* loop index */
340 struct comedi_device *dev = d;
341 const struct das800_board *thisboard = comedi_board(dev);
342 struct das800_private *devpriv = dev->private;
343 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
344 struct comedi_async *async;
346 unsigned long irq_flags;
347 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
350 int fifo_overflow = 0;
352 status = inb(dev->iobase + DAS800_STATUS);
353 /* if interrupt was not generated by board or driver not attached, quit */
356 if (!(dev->attached))
359 /* wait until here to initialize async, since we will get null dereference
360 * if interrupt occurs before driver is fully attached!
364 /* if hardware conversions are not enabled, then quit */
365 spin_lock_irqsave(&dev->spinlock, irq_flags);
366 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
367 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
368 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
370 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
374 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
375 for (i = 0; i < max_loops; i++) {
376 /* read 16 bits from dev->iobase and dev->iobase + 1 */
377 dataPoint = inb(dev->iobase + DAS800_LSB);
378 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
379 if (thisboard->resolution == 12) {
380 fifo_empty = dataPoint & FIFO_EMPTY;
381 fifo_overflow = dataPoint & FIFO_OVF;
385 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
389 /* strip off extraneous bits for 12 bit cards */
390 if (thisboard->resolution == 12)
391 dataPoint = (dataPoint >> 4) & 0xfff;
392 /* if there are more data points to collect */
393 if (devpriv->count > 0 || devpriv->forever == 1) {
394 /* write data point to buffer */
395 cfc_write_to_buffer(s, dataPoint);
396 if (devpriv->count > 0)
400 async->events |= COMEDI_CB_BLOCK;
401 /* check for fifo overflow */
402 if (thisboard->resolution == 12) {
403 fifo_overflow = dataPoint & FIFO_OVF;
404 /* else cio-das802/16 */
406 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
409 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
410 comedi_error(dev, "DAS800 FIFO overflow");
411 das800_cancel(dev, s);
412 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
413 comedi_event(dev, s);
417 if (devpriv->count > 0 || devpriv->forever == 1) {
418 /* Re-enable card's interrupt.
419 * We already have spinlock, so indirect addressing is safe */
420 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
421 outb(CONTROL1_INTE | devpriv->do_bits,
422 dev->iobase + DAS800_CONTROL1);
423 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
424 /* otherwise, stop taking data */
426 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
427 disable_das800(dev); /* disable hardware triggered conversions */
428 async->events |= COMEDI_CB_EOA;
430 comedi_event(dev, s);
435 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
437 const struct das800_board *thisboard = comedi_board(dev);
438 struct das800_private *devpriv;
439 struct comedi_subdevice *s;
440 unsigned int irq = it->options[1];
441 unsigned long irq_flags;
445 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
448 dev->private = devpriv;
450 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
454 board = das800_probe(dev);
456 dev_dbg(dev->class_dev, "unable to determine board type\n");
459 dev->board_ptr = das800_boards + board;
460 thisboard = comedi_board(dev);
463 if (irq == 1 || irq > 7) {
464 dev_err(dev->class_dev, "irq out of range\n");
468 if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
469 dev_err(dev->class_dev, "unable to allocate irq %u\n",
476 dev->board_name = thisboard->name;
478 ret = comedi_alloc_subdevices(dev, 3);
482 /* analog input subdevice */
483 s = &dev->subdevices[0];
484 dev->read_subdev = s;
485 s->type = COMEDI_SUBD_AI;
486 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
489 s->maxdata = (1 << thisboard->resolution) - 1;
490 s->range_table = thisboard->ai_range;
491 s->do_cmd = das800_ai_do_cmd;
492 s->do_cmdtest = das800_ai_do_cmdtest;
493 s->insn_read = das800_ai_rinsn;
494 s->cancel = das800_cancel;
497 s = &dev->subdevices[1];
498 s->type = COMEDI_SUBD_DI;
499 s->subdev_flags = SDF_READABLE;
502 s->range_table = &range_digital;
503 s->insn_bits = das800_di_rbits;
506 s = &dev->subdevices[2];
507 s->type = COMEDI_SUBD_DO;
508 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
511 s->range_table = &range_digital;
512 s->insn_bits = das800_do_wbits;
516 /* initialize digital out channels */
517 spin_lock_irqsave(&dev->spinlock, irq_flags);
518 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
519 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
520 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
525 static void das800_detach(struct comedi_device *dev)
528 release_region(dev->iobase, DAS800_SIZE);
530 free_irq(dev->irq, dev);
533 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
535 struct das800_private *devpriv = dev->private;
537 devpriv->forever = 0;
543 /* enable_das800 makes the card start taking hardware triggered conversions */
544 static void enable_das800(struct comedi_device *dev)
546 const struct das800_board *thisboard = comedi_board(dev);
547 struct das800_private *devpriv = dev->private;
548 unsigned long irq_flags;
550 spin_lock_irqsave(&dev->spinlock, irq_flags);
551 /* enable fifo-half full interrupts for cio-das802/16 */
552 if (thisboard->resolution == 16)
553 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
554 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
555 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
556 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
557 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
558 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
561 /* disable_das800 stops hardware triggered conversions */
562 static void disable_das800(struct comedi_device *dev)
564 unsigned long irq_flags;
565 spin_lock_irqsave(&dev->spinlock, irq_flags);
566 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
567 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
568 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
571 static int das800_ai_do_cmdtest(struct comedi_device *dev,
572 struct comedi_subdevice *s,
573 struct comedi_cmd *cmd)
575 const struct das800_board *thisboard = comedi_board(dev);
576 struct das800_private *devpriv = dev->private;
582 /* Step 1 : check if triggers are trivially valid */
584 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
585 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
586 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
587 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
588 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
593 /* Step 2a : make sure trigger sources are unique */
595 err |= cfc_check_trigger_is_unique(cmd->start_src);
596 err |= cfc_check_trigger_is_unique(cmd->convert_src);
597 err |= cfc_check_trigger_is_unique(cmd->stop_src);
599 /* Step 2b : and mutually compatible */
604 /* Step 3: check if arguments are trivially valid */
606 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
608 if (cmd->convert_src == TRIG_TIMER)
609 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
610 thisboard->ai_speed);
612 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
613 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
615 if (cmd->stop_src == TRIG_COUNT)
616 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
618 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
623 /* step 4: fix up any arguments */
625 if (cmd->convert_src == TRIG_TIMER) {
626 tmp = cmd->convert_arg;
627 /* calculate counter values that give desired timing */
628 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
629 &(devpriv->divisor2),
631 cmd->flags & TRIG_ROUND_MASK);
632 if (tmp != cmd->convert_arg)
639 /* check channel/gain list against card's limitations */
641 gain = CR_RANGE(cmd->chanlist[0]);
642 startChan = CR_CHAN(cmd->chanlist[0]);
643 for (i = 1; i < cmd->chanlist_len; i++) {
644 if (CR_CHAN(cmd->chanlist[i]) !=
645 (startChan + i) % N_CHAN_AI) {
647 "entries in chanlist must be consecutive channels, counting upwards\n");
650 if (CR_RANGE(cmd->chanlist[i]) != gain) {
652 "entries in chanlist must all have the same gain\n");
664 static int das800_ai_do_cmd(struct comedi_device *dev,
665 struct comedi_subdevice *s)
667 const struct das800_board *thisboard = comedi_board(dev);
668 struct das800_private *devpriv = dev->private;
669 int startChan, endChan, scan, gain;
671 unsigned long irq_flags;
672 struct comedi_async *async = s->async;
676 "no irq assigned for das-800, cannot do hardware conversions");
682 /* set channel scan limits */
683 startChan = CR_CHAN(async->cmd.chanlist[0]);
684 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
685 scan = (endChan << 3) | startChan;
687 spin_lock_irqsave(&dev->spinlock, irq_flags);
688 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
689 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
690 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
693 gain = CR_RANGE(async->cmd.chanlist[0]);
694 if (thisboard->resolution == 12 && gain > 0)
697 outb(gain, dev->iobase + DAS800_GAIN);
699 switch (async->cmd.stop_src) {
701 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
702 devpriv->forever = 0;
705 devpriv->forever = 1;
712 /* enable auto channel scan, send interrupts on end of conversion
713 * and set clock source to internal or external
716 conv_bits |= EACS | IEOC;
717 if (async->cmd.start_src == TRIG_EXT)
719 switch (async->cmd.convert_src) {
721 conv_bits |= CASC | ITE;
722 /* set conversion frequency */
723 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
724 &(devpriv->divisor2),
725 &(async->cmd.convert_arg),
727 flags & TRIG_ROUND_MASK);
728 if (das800_set_frequency(dev) < 0) {
729 comedi_error(dev, "Error setting up counters");
739 spin_lock_irqsave(&dev->spinlock, irq_flags);
740 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
741 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
742 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
748 static int das800_ai_rinsn(struct comedi_device *dev,
749 struct comedi_subdevice *s, struct comedi_insn *insn,
752 const struct das800_board *thisboard = comedi_board(dev);
753 struct das800_private *devpriv = dev->private;
759 unsigned long irq_flags;
761 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
763 /* set multiplexer */
764 chan = CR_CHAN(insn->chanspec);
766 spin_lock_irqsave(&dev->spinlock, irq_flags);
767 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
768 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
769 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
771 /* set gain / range */
772 range = CR_RANGE(insn->chanspec);
773 if (thisboard->resolution == 12 && range)
776 outb(range, dev->iobase + DAS800_GAIN);
780 for (n = 0; n < insn->n; n++) {
781 /* trigger conversion */
782 outb_p(0, dev->iobase + DAS800_MSB);
784 for (i = 0; i < timeout; i++) {
785 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
789 comedi_error(dev, "timeout");
792 lsb = inb(dev->iobase + DAS800_LSB);
793 msb = inb(dev->iobase + DAS800_MSB);
794 if (thisboard->resolution == 12) {
795 data[n] = (lsb >> 4) & 0xff;
796 data[n] |= (msb << 4);
798 data[n] = (msb << 8) | lsb;
805 static int das800_di_rbits(struct comedi_device *dev,
806 struct comedi_subdevice *s, struct comedi_insn *insn,
811 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
819 static int das800_do_wbits(struct comedi_device *dev,
820 struct comedi_subdevice *s, struct comedi_insn *insn,
823 struct das800_private *devpriv = dev->private;
825 unsigned long irq_flags;
827 /* only set bits that have been masked */
829 wbits = devpriv->do_bits >> 4;
831 wbits |= data[0] & data[1];
832 devpriv->do_bits = wbits << 4;
834 spin_lock_irqsave(&dev->spinlock, irq_flags);
835 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
836 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
837 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
844 /* loads counters with divisor1, divisor2 from private structure */
845 static int das800_set_frequency(struct comedi_device *dev)
847 struct das800_private *devpriv = dev->private;
850 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
852 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
860 MODULE_AUTHOR("Comedi http://www.comedi.org");
861 MODULE_DESCRIPTION("Comedi low-level driver");
862 MODULE_LICENSE("GPL");