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_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
233 static irqreturn_t das800_interrupt(int irq, void *d);
234 static void enable_das800(struct comedi_device *dev);
235 static void disable_das800(struct comedi_device *dev);
236 static int das800_set_frequency(struct comedi_device *dev);
238 /* checks and probes das-800 series board type */
239 static int das800_probe(struct comedi_device *dev)
241 const struct das800_board *thisboard = comedi_board(dev);
243 unsigned long irq_flags;
246 /* 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
247 spin_lock_irqsave(&dev->spinlock, irq_flags);
248 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
249 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
250 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
252 board = thisboard - das800_boards;
256 if (board == das800) {
257 dev_dbg(dev->class_dev, "Board model: DAS-800\n");
260 if (board == ciodas800) {
261 dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
264 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
268 if (board == das801) {
269 dev_dbg(dev->class_dev, "Board model: DAS-801\n");
272 if (board == ciodas801) {
273 dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
276 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
280 if (board == das802) {
281 dev_dbg(dev->class_dev, "Board model: DAS-802\n");
284 if (board == ciodas802) {
285 dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
288 if (board == ciodas80216) {
289 dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
292 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
296 dev_dbg(dev->class_dev,
297 "Board model: probe returned 0x%x (unknown)\n",
305 /* interrupt service routine */
306 static irqreturn_t das800_interrupt(int irq, void *d)
308 short i; /* loop index */
310 struct comedi_device *dev = d;
311 const struct das800_board *thisboard = comedi_board(dev);
312 struct das800_private *devpriv = dev->private;
313 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
314 struct comedi_async *async;
316 unsigned long irq_flags;
317 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
320 int fifo_overflow = 0;
322 status = inb(dev->iobase + DAS800_STATUS);
323 /* if interrupt was not generated by board or driver not attached, quit */
326 if (!(dev->attached))
329 /* wait until here to initialize async, since we will get null dereference
330 * if interrupt occurs before driver is fully attached!
334 /* if hardware conversions are not enabled, then quit */
335 spin_lock_irqsave(&dev->spinlock, irq_flags);
336 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
337 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
338 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
340 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
344 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
345 for (i = 0; i < max_loops; i++) {
346 /* read 16 bits from dev->iobase and dev->iobase + 1 */
347 dataPoint = inb(dev->iobase + DAS800_LSB);
348 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
349 if (thisboard->resolution == 12) {
350 fifo_empty = dataPoint & FIFO_EMPTY;
351 fifo_overflow = dataPoint & FIFO_OVF;
355 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
359 /* strip off extraneous bits for 12 bit cards */
360 if (thisboard->resolution == 12)
361 dataPoint = (dataPoint >> 4) & 0xfff;
362 /* if there are more data points to collect */
363 if (devpriv->count > 0 || devpriv->forever == 1) {
364 /* write data point to buffer */
365 cfc_write_to_buffer(s, dataPoint);
366 if (devpriv->count > 0)
370 async->events |= COMEDI_CB_BLOCK;
371 /* check for fifo overflow */
372 if (thisboard->resolution == 12) {
373 fifo_overflow = dataPoint & FIFO_OVF;
374 /* else cio-das802/16 */
376 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
379 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
380 comedi_error(dev, "DAS800 FIFO overflow");
381 das800_cancel(dev, s);
382 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
383 comedi_event(dev, s);
387 if (devpriv->count > 0 || devpriv->forever == 1) {
388 /* Re-enable card's interrupt.
389 * We already have spinlock, so indirect addressing is safe */
390 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
391 outb(CONTROL1_INTE | devpriv->do_bits,
392 dev->iobase + DAS800_CONTROL1);
393 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
394 /* otherwise, stop taking data */
396 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
397 disable_das800(dev); /* disable hardware triggered conversions */
398 async->events |= COMEDI_CB_EOA;
400 comedi_event(dev, s);
405 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
407 struct das800_private *devpriv = dev->private;
409 devpriv->forever = 0;
415 /* enable_das800 makes the card start taking hardware triggered conversions */
416 static void enable_das800(struct comedi_device *dev)
418 const struct das800_board *thisboard = comedi_board(dev);
419 struct das800_private *devpriv = dev->private;
420 unsigned long irq_flags;
422 spin_lock_irqsave(&dev->spinlock, irq_flags);
423 /* enable fifo-half full interrupts for cio-das802/16 */
424 if (thisboard->resolution == 16)
425 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
426 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
427 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
428 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
429 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
430 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
433 /* disable_das800 stops hardware triggered conversions */
434 static void disable_das800(struct comedi_device *dev)
436 unsigned long irq_flags;
437 spin_lock_irqsave(&dev->spinlock, irq_flags);
438 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
439 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
440 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
443 static int das800_ai_do_cmdtest(struct comedi_device *dev,
444 struct comedi_subdevice *s,
445 struct comedi_cmd *cmd)
447 const struct das800_board *thisboard = comedi_board(dev);
448 struct das800_private *devpriv = dev->private;
454 /* Step 1 : check if triggers are trivially valid */
456 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
457 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
458 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
459 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
460 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
465 /* Step 2a : make sure trigger sources are unique */
467 err |= cfc_check_trigger_is_unique(cmd->start_src);
468 err |= cfc_check_trigger_is_unique(cmd->convert_src);
469 err |= cfc_check_trigger_is_unique(cmd->stop_src);
471 /* Step 2b : and mutually compatible */
476 /* Step 3: check if arguments are trivially valid */
478 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
480 if (cmd->convert_src == TRIG_TIMER)
481 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
482 thisboard->ai_speed);
484 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
485 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
487 if (cmd->stop_src == TRIG_COUNT)
488 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
490 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
495 /* step 4: fix up any arguments */
497 if (cmd->convert_src == TRIG_TIMER) {
498 tmp = cmd->convert_arg;
499 /* calculate counter values that give desired timing */
500 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
501 &(devpriv->divisor2),
503 cmd->flags & TRIG_ROUND_MASK);
504 if (tmp != cmd->convert_arg)
511 /* check channel/gain list against card's limitations */
513 gain = CR_RANGE(cmd->chanlist[0]);
514 startChan = CR_CHAN(cmd->chanlist[0]);
515 for (i = 1; i < cmd->chanlist_len; i++) {
516 if (CR_CHAN(cmd->chanlist[i]) !=
517 (startChan + i) % N_CHAN_AI) {
519 "entries in chanlist must be consecutive channels, counting upwards\n");
522 if (CR_RANGE(cmd->chanlist[i]) != gain) {
524 "entries in chanlist must all have the same gain\n");
536 static int das800_ai_do_cmd(struct comedi_device *dev,
537 struct comedi_subdevice *s)
539 const struct das800_board *thisboard = comedi_board(dev);
540 struct das800_private *devpriv = dev->private;
541 int startChan, endChan, scan, gain;
543 unsigned long irq_flags;
544 struct comedi_async *async = s->async;
548 "no irq assigned for das-800, cannot do hardware conversions");
554 /* set channel scan limits */
555 startChan = CR_CHAN(async->cmd.chanlist[0]);
556 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
557 scan = (endChan << 3) | startChan;
559 spin_lock_irqsave(&dev->spinlock, irq_flags);
560 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
561 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
562 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
565 gain = CR_RANGE(async->cmd.chanlist[0]);
566 if (thisboard->resolution == 12 && gain > 0)
569 outb(gain, dev->iobase + DAS800_GAIN);
571 switch (async->cmd.stop_src) {
573 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
574 devpriv->forever = 0;
577 devpriv->forever = 1;
584 /* enable auto channel scan, send interrupts on end of conversion
585 * and set clock source to internal or external
588 conv_bits |= EACS | IEOC;
589 if (async->cmd.start_src == TRIG_EXT)
591 switch (async->cmd.convert_src) {
593 conv_bits |= CASC | ITE;
594 /* set conversion frequency */
595 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
596 &(devpriv->divisor2),
597 &(async->cmd.convert_arg),
599 flags & TRIG_ROUND_MASK);
600 if (das800_set_frequency(dev) < 0) {
601 comedi_error(dev, "Error setting up counters");
611 spin_lock_irqsave(&dev->spinlock, irq_flags);
612 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
613 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
614 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
620 static int das800_ai_rinsn(struct comedi_device *dev,
621 struct comedi_subdevice *s, struct comedi_insn *insn,
624 const struct das800_board *thisboard = comedi_board(dev);
625 struct das800_private *devpriv = dev->private;
631 unsigned long irq_flags;
633 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
635 /* set multiplexer */
636 chan = CR_CHAN(insn->chanspec);
638 spin_lock_irqsave(&dev->spinlock, irq_flags);
639 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
640 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
641 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
643 /* set gain / range */
644 range = CR_RANGE(insn->chanspec);
645 if (thisboard->resolution == 12 && range)
648 outb(range, dev->iobase + DAS800_GAIN);
652 for (n = 0; n < insn->n; n++) {
653 /* trigger conversion */
654 outb_p(0, dev->iobase + DAS800_MSB);
656 for (i = 0; i < timeout; i++) {
657 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
661 comedi_error(dev, "timeout");
664 lsb = inb(dev->iobase + DAS800_LSB);
665 msb = inb(dev->iobase + DAS800_MSB);
666 if (thisboard->resolution == 12) {
667 data[n] = (lsb >> 4) & 0xff;
668 data[n] |= (msb << 4);
670 data[n] = (msb << 8) | lsb;
677 static int das800_di_rbits(struct comedi_device *dev,
678 struct comedi_subdevice *s, struct comedi_insn *insn,
683 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
691 static int das800_do_wbits(struct comedi_device *dev,
692 struct comedi_subdevice *s, struct comedi_insn *insn,
695 struct das800_private *devpriv = dev->private;
697 unsigned long irq_flags;
699 /* only set bits that have been masked */
701 wbits = devpriv->do_bits >> 4;
703 wbits |= data[0] & data[1];
704 devpriv->do_bits = wbits << 4;
706 spin_lock_irqsave(&dev->spinlock, irq_flags);
707 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
708 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
709 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
716 /* loads counters with divisor1, divisor2 from private structure */
717 static int das800_set_frequency(struct comedi_device *dev)
719 struct das800_private *devpriv = dev->private;
722 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
724 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
732 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
734 const struct das800_board *thisboard = comedi_board(dev);
735 struct das800_private *devpriv;
736 struct comedi_subdevice *s;
737 unsigned int irq = it->options[1];
738 unsigned long irq_flags;
742 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
745 dev->private = devpriv;
747 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
751 board = das800_probe(dev);
753 dev_dbg(dev->class_dev, "unable to determine board type\n");
756 dev->board_ptr = das800_boards + board;
757 thisboard = comedi_board(dev);
760 if (irq == 1 || irq > 7) {
761 dev_err(dev->class_dev, "irq out of range\n");
765 if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
766 dev_err(dev->class_dev, "unable to allocate irq %u\n",
773 dev->board_name = thisboard->name;
775 ret = comedi_alloc_subdevices(dev, 3);
779 /* analog input subdevice */
780 s = &dev->subdevices[0];
781 dev->read_subdev = s;
782 s->type = COMEDI_SUBD_AI;
783 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
786 s->maxdata = (1 << thisboard->resolution) - 1;
787 s->range_table = thisboard->ai_range;
788 s->do_cmd = das800_ai_do_cmd;
789 s->do_cmdtest = das800_ai_do_cmdtest;
790 s->insn_read = das800_ai_rinsn;
791 s->cancel = das800_cancel;
794 s = &dev->subdevices[1];
795 s->type = COMEDI_SUBD_DI;
796 s->subdev_flags = SDF_READABLE;
799 s->range_table = &range_digital;
800 s->insn_bits = das800_di_rbits;
803 s = &dev->subdevices[2];
804 s->type = COMEDI_SUBD_DO;
805 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
808 s->range_table = &range_digital;
809 s->insn_bits = das800_do_wbits;
813 /* initialize digital out channels */
814 spin_lock_irqsave(&dev->spinlock, irq_flags);
815 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
816 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
817 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
822 static struct comedi_driver driver_das800 = {
823 .driver_name = "das800",
824 .module = THIS_MODULE,
825 .attach = das800_attach,
826 .detach = comedi_legacy_detach,
827 .num_names = ARRAY_SIZE(das800_boards),
828 .board_name = &das800_boards[0].name,
829 .offset = sizeof(struct das800_board),
831 module_comedi_driver(driver_das800);
833 MODULE_AUTHOR("Comedi http://www.comedi.org");
834 MODULE_DESCRIPTION("Comedi low-level driver");
835 MODULE_LICENSE("GPL");