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 static int das800_attach(struct comedi_device *dev,
245 struct comedi_devconfig *it);
246 static void das800_detach(struct comedi_device *dev);
247 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
249 static struct comedi_driver driver_das800 = {
250 .driver_name = "das800",
251 .module = THIS_MODULE,
252 .attach = das800_attach,
253 .detach = das800_detach,
254 .num_names = ARRAY_SIZE(das800_boards),
255 .board_name = &das800_boards[0].name,
256 .offset = sizeof(struct das800_board),
259 static irqreturn_t das800_interrupt(int irq, void *d);
260 static void enable_das800(struct comedi_device *dev);
261 static void disable_das800(struct comedi_device *dev);
262 static int das800_ai_do_cmdtest(struct comedi_device *dev,
263 struct comedi_subdevice *s,
264 struct comedi_cmd *cmd);
265 static int das800_ai_do_cmd(struct comedi_device *dev,
266 struct comedi_subdevice *s);
267 static int das800_ai_rinsn(struct comedi_device *dev,
268 struct comedi_subdevice *s, struct comedi_insn *insn,
270 static int das800_di_rbits(struct comedi_device *dev,
271 struct comedi_subdevice *s, struct comedi_insn *insn,
273 static int das800_do_wbits(struct comedi_device *dev,
274 struct comedi_subdevice *s, struct comedi_insn *insn,
276 static int das800_probe(struct comedi_device *dev);
277 static int das800_set_frequency(struct comedi_device *dev);
279 /* checks and probes das-800 series board type */
280 static int das800_probe(struct comedi_device *dev)
283 unsigned long irq_flags;
286 /* 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing */
287 spin_lock_irqsave(&dev->spinlock, irq_flags);
288 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
289 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
290 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
292 board = thisboard - das800_boards;
296 if (board == das800) {
297 dev_dbg(dev->class_dev, "Board model: DAS-800\n");
300 if (board == ciodas800) {
301 dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
304 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
308 if (board == das801) {
309 dev_dbg(dev->class_dev, "Board model: DAS-801\n");
312 if (board == ciodas801) {
313 dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
316 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
320 if (board == das802) {
321 dev_dbg(dev->class_dev, "Board model: DAS-802\n");
324 if (board == ciodas802) {
325 dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
328 if (board == ciodas80216) {
329 dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
332 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
336 dev_dbg(dev->class_dev,
337 "Board model: probe returned 0x%x (unknown)\n",
345 module_comedi_driver(driver_das800);
347 /* interrupt service routine */
348 static irqreturn_t das800_interrupt(int irq, void *d)
350 short i; /* loop index */
352 struct comedi_device *dev = d;
353 struct das800_private *devpriv = dev->private;
354 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
355 struct comedi_async *async;
357 unsigned long irq_flags;
358 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
361 int fifo_overflow = 0;
363 status = inb(dev->iobase + DAS800_STATUS);
364 /* if interrupt was not generated by board or driver not attached, quit */
367 if (!(dev->attached))
370 /* wait until here to initialize async, since we will get null dereference
371 * if interrupt occurs before driver is fully attached!
375 /* if hardware conversions are not enabled, then quit */
376 spin_lock_irqsave(&dev->spinlock, irq_flags);
377 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
378 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
379 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
381 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
385 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
386 for (i = 0; i < max_loops; i++) {
387 /* read 16 bits from dev->iobase and dev->iobase + 1 */
388 dataPoint = inb(dev->iobase + DAS800_LSB);
389 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
390 if (thisboard->resolution == 12) {
391 fifo_empty = dataPoint & FIFO_EMPTY;
392 fifo_overflow = dataPoint & FIFO_OVF;
396 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
400 /* strip off extraneous bits for 12 bit cards */
401 if (thisboard->resolution == 12)
402 dataPoint = (dataPoint >> 4) & 0xfff;
403 /* if there are more data points to collect */
404 if (devpriv->count > 0 || devpriv->forever == 1) {
405 /* write data point to buffer */
406 cfc_write_to_buffer(s, dataPoint);
407 if (devpriv->count > 0)
411 async->events |= COMEDI_CB_BLOCK;
412 /* check for fifo overflow */
413 if (thisboard->resolution == 12) {
414 fifo_overflow = dataPoint & FIFO_OVF;
415 /* else cio-das802/16 */
417 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
420 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
421 comedi_error(dev, "DAS800 FIFO overflow");
422 das800_cancel(dev, s);
423 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
424 comedi_event(dev, s);
428 if (devpriv->count > 0 || devpriv->forever == 1) {
429 /* Re-enable card's interrupt.
430 * We already have spinlock, so indirect addressing is safe */
431 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
432 outb(CONTROL1_INTE | devpriv->do_bits,
433 dev->iobase + DAS800_CONTROL1);
434 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
435 /* otherwise, stop taking data */
437 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
438 disable_das800(dev); /* disable hardware triggered conversions */
439 async->events |= COMEDI_CB_EOA;
441 comedi_event(dev, s);
446 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
448 struct das800_private *devpriv;
449 struct comedi_subdevice *s;
450 unsigned long iobase = it->options[0];
451 unsigned int irq = it->options[1];
452 unsigned long irq_flags;
456 dev_info(dev->class_dev, "das800: io 0x%lx\n", iobase);
458 dev_dbg(dev->class_dev, "irq %u\n", irq);
460 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
463 dev->private = devpriv;
466 dev_err(dev->class_dev,
467 "io base address required for das800\n");
471 /* check if io addresses are available */
472 if (!request_region(iobase, DAS800_SIZE, "das800")) {
473 dev_err(dev->class_dev, "I/O port conflict\n");
476 dev->iobase = iobase;
478 board = das800_probe(dev);
480 dev_dbg(dev->class_dev, "unable to determine board type\n");
483 dev->board_ptr = das800_boards + board;
486 if (irq == 1 || irq > 7) {
487 dev_err(dev->class_dev, "irq out of range\n");
491 if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
492 dev_err(dev->class_dev, "unable to allocate irq %u\n",
499 dev->board_name = thisboard->name;
501 ret = comedi_alloc_subdevices(dev, 3);
505 /* analog input subdevice */
506 s = &dev->subdevices[0];
507 dev->read_subdev = s;
508 s->type = COMEDI_SUBD_AI;
509 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
512 s->maxdata = (1 << thisboard->resolution) - 1;
513 s->range_table = thisboard->ai_range;
514 s->do_cmd = das800_ai_do_cmd;
515 s->do_cmdtest = das800_ai_do_cmdtest;
516 s->insn_read = das800_ai_rinsn;
517 s->cancel = das800_cancel;
520 s = &dev->subdevices[1];
521 s->type = COMEDI_SUBD_DI;
522 s->subdev_flags = SDF_READABLE;
525 s->range_table = &range_digital;
526 s->insn_bits = das800_di_rbits;
529 s = &dev->subdevices[2];
530 s->type = COMEDI_SUBD_DO;
531 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
534 s->range_table = &range_digital;
535 s->insn_bits = das800_do_wbits;
539 /* initialize digital out channels */
540 spin_lock_irqsave(&dev->spinlock, irq_flags);
541 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
542 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
543 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
548 static void das800_detach(struct comedi_device *dev)
551 release_region(dev->iobase, DAS800_SIZE);
553 free_irq(dev->irq, dev);
556 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
558 struct das800_private *devpriv = dev->private;
560 devpriv->forever = 0;
566 /* enable_das800 makes the card start taking hardware triggered conversions */
567 static void enable_das800(struct comedi_device *dev)
569 struct das800_private *devpriv = dev->private;
570 unsigned long irq_flags;
572 spin_lock_irqsave(&dev->spinlock, irq_flags);
573 /* enable fifo-half full interrupts for cio-das802/16 */
574 if (thisboard->resolution == 16)
575 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
576 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
577 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
578 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
579 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
580 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
583 /* disable_das800 stops hardware triggered conversions */
584 static void disable_das800(struct comedi_device *dev)
586 unsigned long irq_flags;
587 spin_lock_irqsave(&dev->spinlock, irq_flags);
588 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
589 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
590 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
593 static int das800_ai_do_cmdtest(struct comedi_device *dev,
594 struct comedi_subdevice *s,
595 struct comedi_cmd *cmd)
597 struct das800_private *devpriv = dev->private;
603 /* Step 1 : check if triggers are trivially valid */
605 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
606 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
607 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
608 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
609 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
614 /* Step 2a : make sure trigger sources are unique */
616 err |= cfc_check_trigger_is_unique(cmd->start_src);
617 err |= cfc_check_trigger_is_unique(cmd->convert_src);
618 err |= cfc_check_trigger_is_unique(cmd->stop_src);
620 /* Step 2b : and mutually compatible */
625 /* Step 3: check if arguments are trivially valid */
627 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
629 if (cmd->convert_src == TRIG_TIMER)
630 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
631 thisboard->ai_speed);
633 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
634 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
636 if (cmd->stop_src == TRIG_COUNT)
637 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
639 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
644 /* step 4: fix up any arguments */
646 if (cmd->convert_src == TRIG_TIMER) {
647 tmp = cmd->convert_arg;
648 /* calculate counter values that give desired timing */
649 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
650 &(devpriv->divisor2),
652 cmd->flags & TRIG_ROUND_MASK);
653 if (tmp != cmd->convert_arg)
660 /* check channel/gain list against card's limitations */
662 gain = CR_RANGE(cmd->chanlist[0]);
663 startChan = CR_CHAN(cmd->chanlist[0]);
664 for (i = 1; i < cmd->chanlist_len; i++) {
665 if (CR_CHAN(cmd->chanlist[i]) !=
666 (startChan + i) % N_CHAN_AI) {
668 "entries in chanlist must be consecutive channels, counting upwards\n");
671 if (CR_RANGE(cmd->chanlist[i]) != gain) {
673 "entries in chanlist must all have the same gain\n");
685 static int das800_ai_do_cmd(struct comedi_device *dev,
686 struct comedi_subdevice *s)
688 struct das800_private *devpriv = dev->private;
689 int startChan, endChan, scan, gain;
691 unsigned long irq_flags;
692 struct comedi_async *async = s->async;
696 "no irq assigned for das-800, cannot do hardware conversions");
702 /* set channel scan limits */
703 startChan = CR_CHAN(async->cmd.chanlist[0]);
704 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
705 scan = (endChan << 3) | startChan;
707 spin_lock_irqsave(&dev->spinlock, irq_flags);
708 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
709 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
710 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
713 gain = CR_RANGE(async->cmd.chanlist[0]);
714 if (thisboard->resolution == 12 && gain > 0)
717 outb(gain, dev->iobase + DAS800_GAIN);
719 switch (async->cmd.stop_src) {
721 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
722 devpriv->forever = 0;
725 devpriv->forever = 1;
732 /* enable auto channel scan, send interrupts on end of conversion
733 * and set clock source to internal or external
736 conv_bits |= EACS | IEOC;
737 if (async->cmd.start_src == TRIG_EXT)
739 switch (async->cmd.convert_src) {
741 conv_bits |= CASC | ITE;
742 /* set conversion frequency */
743 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
744 &(devpriv->divisor2),
745 &(async->cmd.convert_arg),
747 flags & TRIG_ROUND_MASK);
748 if (das800_set_frequency(dev) < 0) {
749 comedi_error(dev, "Error setting up counters");
759 spin_lock_irqsave(&dev->spinlock, irq_flags);
760 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
761 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
762 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
768 static int das800_ai_rinsn(struct comedi_device *dev,
769 struct comedi_subdevice *s, struct comedi_insn *insn,
772 struct das800_private *devpriv = dev->private;
778 unsigned long irq_flags;
780 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
782 /* set multiplexer */
783 chan = CR_CHAN(insn->chanspec);
785 spin_lock_irqsave(&dev->spinlock, irq_flags);
786 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
787 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
788 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
790 /* set gain / range */
791 range = CR_RANGE(insn->chanspec);
792 if (thisboard->resolution == 12 && range)
795 outb(range, dev->iobase + DAS800_GAIN);
799 for (n = 0; n < insn->n; n++) {
800 /* trigger conversion */
801 outb_p(0, dev->iobase + DAS800_MSB);
803 for (i = 0; i < timeout; i++) {
804 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
808 comedi_error(dev, "timeout");
811 lsb = inb(dev->iobase + DAS800_LSB);
812 msb = inb(dev->iobase + DAS800_MSB);
813 if (thisboard->resolution == 12) {
814 data[n] = (lsb >> 4) & 0xff;
815 data[n] |= (msb << 4);
817 data[n] = (msb << 8) | lsb;
824 static int das800_di_rbits(struct comedi_device *dev,
825 struct comedi_subdevice *s, struct comedi_insn *insn,
830 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
838 static int das800_do_wbits(struct comedi_device *dev,
839 struct comedi_subdevice *s, struct comedi_insn *insn,
842 struct das800_private *devpriv = dev->private;
844 unsigned long irq_flags;
846 /* only set bits that have been masked */
848 wbits = devpriv->do_bits >> 4;
850 wbits |= data[0] & data[1];
851 devpriv->do_bits = wbits << 4;
853 spin_lock_irqsave(&dev->spinlock, irq_flags);
854 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
855 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
856 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
863 /* loads counters with divisor1, divisor2 from private structure */
864 static int das800_set_frequency(struct comedi_device *dev)
866 struct das800_private *devpriv = dev->private;
869 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
871 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
879 MODULE_AUTHOR("Comedi http://www.comedi.org");
880 MODULE_DESCRIPTION("Comedi low-level driver");
881 MODULE_LICENSE("GPL");