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.
21 Description: Keithley Metrabyte DAS800 (& compatibles)
22 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
23 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
25 [Measurement Computing] CIO-DAS800 (cio-das800),
26 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
27 CIO-DAS802/16 (cio-das802/16)
28 Status: works, cio-das802/16 untested - email me if you have tested it
30 Configuration options:
31 [0] - I/O port base address
32 [1] - IRQ (optional, required for timed or externally triggered conversions)
35 IRQ can be omitted, although the cmd interface will not work without it.
37 All entries in the channel/gain list must use the same gain and be
38 consecutive channels counting upwards in channel number (these are
39 hardware limitations.)
41 I've never tested the gain setting stuff since I only have a
42 DAS-800 board with fixed gain.
44 The cio-das802/16 does not have a fifo-empty status bit! Therefore
45 only fifo-half-full transfers are possible with this card.
49 cmd triggers supported:
50 start_src: TRIG_NOW | TRIG_EXT
51 scan_begin_src: TRIG_FOLLOW
52 scan_end_src: TRIG_COUNT
53 convert_src: TRIG_TIMER | TRIG_EXT
54 stop_src: TRIG_NONE | TRIG_COUNT
59 #include <linux/module.h>
60 #include <linux/interrupt.h>
61 #include "../comedidev.h"
63 #include <linux/ioport.h>
64 #include <linux/delay.h>
67 #include "comedi_fc.h"
70 #define TIMER_BASE 1000
71 #define N_CHAN_AI 8 /* number of analog input channels */
73 /* Registers for the das800 */
76 #define FIFO_EMPTY 0x1
79 #define DAS800_CONTROL1 2
80 #define CONTROL1_INTE 0x8
81 #define DAS800_CONV_CONTROL 2
87 #define CONV_HCEN 0x80
88 #define DAS800_SCAN_LIMITS 2
89 #define DAS800_STATUS 2
93 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
94 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
96 #define CONV_CONTROL 0xa0
97 #define SCAN_LIMITS 0xc0
100 #define DAS800_STATUS2 7
101 #define STATUS2_HCEN 0x80
102 #define STATUS2_INTE 0X20
105 #define DAS802_16_HALF_FIFO_SZ 128
107 struct das800_board {
110 const struct comedi_lrange *ai_range;
114 static const struct comedi_lrange range_das801_ai = {
128 static const struct comedi_lrange range_cio_das801_ai = {
142 static const struct comedi_lrange range_das802_ai = {
156 static const struct comedi_lrange range_das80216_ai = {
169 enum das800_boardinfo {
179 static const struct das800_board das800_boards[] = {
183 .ai_range = &range_bipolar5,
186 [BOARD_CIODAS800] = {
187 .name = "cio-das800",
189 .ai_range = &range_bipolar5,
195 .ai_range = &range_das801_ai,
198 [BOARD_CIODAS801] = {
199 .name = "cio-das801",
201 .ai_range = &range_cio_das801_ai,
207 .ai_range = &range_das802_ai,
210 [BOARD_CIODAS802] = {
211 .name = "cio-das802",
213 .ai_range = &range_das802_ai,
216 [BOARD_CIODAS80216] = {
217 .name = "cio-das802/16",
219 .ai_range = &range_das80216_ai,
224 struct das800_private {
225 unsigned int count; /* number of data points left to be taken */
226 unsigned int divisor1; /* counter 1 value for timed conversions */
227 unsigned int divisor2; /* counter 2 value for timed conversions */
228 unsigned int do_bits; /* digital output bits */
229 bool forever; /* flag that we should take data forever */
232 static void das800_ind_write(struct comedi_device *dev,
233 unsigned val, unsigned reg)
236 * Select dev->iobase + 2 to be desired register
237 * then write to that register.
239 outb(reg, dev->iobase + DAS800_GAIN);
240 outb(val, dev->iobase + 2);
243 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
246 * Select dev->iobase + 7 to be desired register
247 * then read from that register.
249 outb(reg, dev->iobase + DAS800_GAIN);
250 return inb(dev->iobase + 7);
253 static void das800_enable(struct comedi_device *dev)
255 const struct das800_board *thisboard = comedi_board(dev);
256 struct das800_private *devpriv = dev->private;
257 unsigned long irq_flags;
259 spin_lock_irqsave(&dev->spinlock, irq_flags);
260 /* enable fifo-half full interrupts for cio-das802/16 */
261 if (thisboard->resolution == 16)
262 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
263 /* enable hardware triggering */
264 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
265 /* enable card's interrupt */
266 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
267 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
270 static void das800_disable(struct comedi_device *dev)
272 unsigned long irq_flags;
274 spin_lock_irqsave(&dev->spinlock, irq_flags);
275 /* disable hardware triggering of conversions */
276 das800_ind_write(dev, 0x0, CONV_CONTROL);
277 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
280 static int das800_set_frequency(struct comedi_device *dev)
282 struct das800_private *devpriv = dev->private;
285 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
287 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
295 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
297 struct das800_private *devpriv = dev->private;
299 devpriv->forever = false;
305 static int das800_ai_do_cmdtest(struct comedi_device *dev,
306 struct comedi_subdevice *s,
307 struct comedi_cmd *cmd)
309 const struct das800_board *thisboard = comedi_board(dev);
310 struct das800_private *devpriv = dev->private;
313 /* Step 1 : check if triggers are trivially valid */
315 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
316 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
317 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
318 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
319 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
324 /* Step 2a : make sure trigger sources are unique */
326 err |= cfc_check_trigger_is_unique(cmd->start_src);
327 err |= cfc_check_trigger_is_unique(cmd->convert_src);
328 err |= cfc_check_trigger_is_unique(cmd->stop_src);
330 /* Step 2b : and mutually compatible */
335 /* Step 3: check if arguments are trivially valid */
337 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
339 if (cmd->convert_src == TRIG_TIMER)
340 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
341 thisboard->ai_speed);
343 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
344 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
346 if (cmd->stop_src == TRIG_COUNT)
347 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
349 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
354 /* step 4: fix up any arguments */
356 if (cmd->convert_src == TRIG_TIMER) {
357 int tmp = cmd->convert_arg;
359 /* calculate counter values that give desired timing */
360 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
364 cmd->flags & TRIG_ROUND_MASK);
365 if (tmp != cmd->convert_arg)
372 /* check channel/gain list against card's limitations */
374 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
375 unsigned int range = CR_RANGE(cmd->chanlist[0]);
379 for (i = 1; i < cmd->chanlist_len; i++) {
380 next = cmd->chanlist[i];
381 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
382 dev_err(dev->class_dev,
383 "chanlist must be consecutive, counting upwards\n");
386 if (CR_RANGE(next) != range) {
387 dev_err(dev->class_dev,
388 "chanlist must all have the same gain\n");
400 static int das800_ai_do_cmd(struct comedi_device *dev,
401 struct comedi_subdevice *s)
403 const struct das800_board *thisboard = comedi_board(dev);
404 struct das800_private *devpriv = dev->private;
405 struct comedi_async *async = s->async;
406 unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
407 unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
408 unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
409 unsigned int scan_chans = (end_chan << 3) | start_chan;
411 unsigned long irq_flags;
415 spin_lock_irqsave(&dev->spinlock, irq_flags);
416 /* set scan limits */
417 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
418 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
421 if (thisboard->resolution == 12 && gain > 0)
424 outb(gain, dev->iobase + DAS800_GAIN);
426 switch (async->cmd.stop_src) {
428 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
429 devpriv->forever = false;
432 devpriv->forever = true;
439 /* enable auto channel scan, send interrupts on end of conversion
440 * and set clock source to internal or external
443 conv_bits |= EACS | IEOC;
444 if (async->cmd.start_src == TRIG_EXT)
446 switch (async->cmd.convert_src) {
448 conv_bits |= CASC | ITE;
449 /* set conversion frequency */
450 if (das800_set_frequency(dev) < 0) {
451 comedi_error(dev, "Error setting up counters");
461 spin_lock_irqsave(&dev->spinlock, irq_flags);
462 das800_ind_write(dev, conv_bits, CONV_CONTROL);
463 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
470 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
472 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
473 unsigned int msb = inb(dev->iobase + DAS800_MSB);
475 return (msb << 8) | lsb;
478 static irqreturn_t das800_interrupt(int irq, void *d)
480 struct comedi_device *dev = d;
481 struct das800_private *devpriv = dev->private;
482 struct comedi_subdevice *s = dev->read_subdev;
483 struct comedi_async *async = s ? s->async : NULL;
484 unsigned long irq_flags;
491 status = inb(dev->iobase + DAS800_STATUS);
497 spin_lock_irqsave(&dev->spinlock, irq_flags);
498 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
500 * Don't release spinlock yet since we want to make sure
501 * no one else disables hardware conversions.
504 /* if hardware conversions are not enabled, then quit */
506 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
510 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
511 val = das800_ai_get_sample(dev);
512 if (s->maxdata == 0x0fff) {
513 fifo_empty = !!(val & FIFO_EMPTY);
514 fifo_overflow = !!(val & FIFO_OVF);
516 /* cio-das802/16 has no fifo empty status bit */
518 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
521 if (fifo_empty || fifo_overflow)
524 if (s->maxdata == 0x0fff)
525 val >>= 4; /* 12-bit sample */
527 /* if there are more data points to collect */
528 if (devpriv->count > 0 || devpriv->forever) {
529 /* write data point to buffer */
530 cfc_write_to_buffer(s, val & s->maxdata);
534 async->events |= COMEDI_CB_BLOCK;
537 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
538 das800_cancel(dev, s);
539 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
540 comedi_event(dev, s);
545 if (devpriv->count > 0 || devpriv->forever) {
546 /* Re-enable card's interrupt.
547 * We already have spinlock, so indirect addressing is safe */
548 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
550 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
552 /* otherwise, stop taking data */
553 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
555 async->events |= COMEDI_CB_EOA;
557 comedi_event(dev, s);
562 static int das800_wait_for_conv(struct comedi_device *dev, int timeout)
566 for (i = 0; i < timeout; i++) {
567 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
573 static int das800_ai_insn_read(struct comedi_device *dev,
574 struct comedi_subdevice *s,
575 struct comedi_insn *insn,
578 struct das800_private *devpriv = dev->private;
579 unsigned int chan = CR_CHAN(insn->chanspec);
580 unsigned int range = CR_RANGE(insn->chanspec);
581 unsigned long irq_flags;
588 /* set multiplexer */
589 spin_lock_irqsave(&dev->spinlock, irq_flags);
590 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
591 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
593 /* set gain / range */
594 if (s->maxdata == 0x0fff && range)
597 outb(range, dev->iobase + DAS800_GAIN);
601 for (i = 0; i < insn->n; i++) {
602 /* trigger conversion */
603 outb_p(0, dev->iobase + DAS800_MSB);
605 ret = das800_wait_for_conv(dev, 1000);
609 val = das800_ai_get_sample(dev);
610 if (s->maxdata == 0x0fff)
611 val >>= 4; /* 12-bit sample */
612 data[i] = val & s->maxdata;
618 static int das800_di_insn_bits(struct comedi_device *dev,
619 struct comedi_subdevice *s,
620 struct comedi_insn *insn,
623 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
628 static int das800_do_insn_bits(struct comedi_device *dev,
629 struct comedi_subdevice *s,
630 struct comedi_insn *insn,
633 struct das800_private *devpriv = dev->private;
634 unsigned int mask = data[0];
635 unsigned int bits = data[1];
636 unsigned long irq_flags;
640 s->state |= (bits & mask);
641 devpriv->do_bits = s->state << 4;
643 spin_lock_irqsave(&dev->spinlock, irq_flags);
644 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
646 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
654 static int das800_probe(struct comedi_device *dev)
656 const struct das800_board *thisboard = comedi_board(dev);
657 int board = thisboard ? thisboard - das800_boards : -EINVAL;
659 unsigned long irq_flags;
661 spin_lock_irqsave(&dev->spinlock, irq_flags);
662 id_bits = das800_ind_read(dev, ID) & 0x3;
663 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
667 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
669 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
670 board = BOARD_DAS800;
673 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
675 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
676 board = BOARD_DAS801;
679 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
680 board == BOARD_CIODAS80216)
682 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
683 board = BOARD_DAS802;
686 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
694 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
696 const struct das800_board *thisboard = comedi_board(dev);
697 struct das800_private *devpriv;
698 struct comedi_subdevice *s;
699 unsigned int irq = it->options[1];
700 unsigned long irq_flags;
704 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
708 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
712 board = das800_probe(dev);
714 dev_dbg(dev->class_dev, "unable to determine board type\n");
717 dev->board_ptr = das800_boards + board;
718 thisboard = comedi_board(dev);
719 dev->board_name = thisboard->name;
721 if (irq > 1 && irq <= 7) {
722 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
728 ret = comedi_alloc_subdevices(dev, 3);
732 /* Analog Input subdevice */
733 s = &dev->subdevices[0];
734 dev->read_subdev = s;
735 s->type = COMEDI_SUBD_AI;
736 s->subdev_flags = SDF_READABLE | SDF_GROUND;
738 s->maxdata = (1 << thisboard->resolution) - 1;
739 s->range_table = thisboard->ai_range;
740 s->insn_read = das800_ai_insn_read;
742 s->subdev_flags |= SDF_CMD_READ;
744 s->do_cmdtest = das800_ai_do_cmdtest;
745 s->do_cmd = das800_ai_do_cmd;
746 s->cancel = das800_cancel;
749 /* Digital Input subdevice */
750 s = &dev->subdevices[1];
751 s->type = COMEDI_SUBD_DI;
752 s->subdev_flags = SDF_READABLE;
755 s->range_table = &range_digital;
756 s->insn_bits = das800_di_insn_bits;
758 /* Digital Output subdevice */
759 s = &dev->subdevices[2];
760 s->type = COMEDI_SUBD_DO;
761 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
764 s->range_table = &range_digital;
765 s->insn_bits = das800_do_insn_bits;
769 /* initialize digital out channels */
770 spin_lock_irqsave(&dev->spinlock, irq_flags);
771 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
772 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
777 static struct comedi_driver driver_das800 = {
778 .driver_name = "das800",
779 .module = THIS_MODULE,
780 .attach = das800_attach,
781 .detach = comedi_legacy_detach,
782 .num_names = ARRAY_SIZE(das800_boards),
783 .board_name = &das800_boards[0].name,
784 .offset = sizeof(struct das800_board),
786 module_comedi_driver(driver_das800);
788 MODULE_AUTHOR("Comedi http://www.comedi.org");
789 MODULE_DESCRIPTION("Comedi low-level driver");
790 MODULE_LICENSE("GPL");