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/delay.h>
66 #include "comedi_fc.h"
69 #define N_CHAN_AI 8 /* number of analog input channels */
71 /* Registers for the das800 */
74 #define FIFO_EMPTY 0x1
77 #define DAS800_CONTROL1 2
78 #define CONTROL1_INTE 0x8
79 #define DAS800_CONV_CONTROL 2
85 #define CONV_HCEN 0x80
86 #define DAS800_SCAN_LIMITS 2
87 #define DAS800_STATUS 2
91 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
92 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
94 #define CONV_CONTROL 0xa0
95 #define SCAN_LIMITS 0xc0
98 #define DAS800_STATUS2 7
99 #define STATUS2_HCEN 0x80
100 #define STATUS2_INTE 0X20
103 #define DAS802_16_HALF_FIFO_SZ 128
105 struct das800_board {
108 const struct comedi_lrange *ai_range;
112 static const struct comedi_lrange range_das801_ai = {
126 static const struct comedi_lrange range_cio_das801_ai = {
140 static const struct comedi_lrange range_das802_ai = {
154 static const struct comedi_lrange range_das80216_ai = {
167 enum das800_boardinfo {
177 static const struct das800_board das800_boards[] = {
181 .ai_range = &range_bipolar5,
184 [BOARD_CIODAS800] = {
185 .name = "cio-das800",
187 .ai_range = &range_bipolar5,
193 .ai_range = &range_das801_ai,
196 [BOARD_CIODAS801] = {
197 .name = "cio-das801",
199 .ai_range = &range_cio_das801_ai,
205 .ai_range = &range_das802_ai,
208 [BOARD_CIODAS802] = {
209 .name = "cio-das802",
211 .ai_range = &range_das802_ai,
214 [BOARD_CIODAS80216] = {
215 .name = "cio-das802/16",
217 .ai_range = &range_das80216_ai,
222 struct das800_private {
223 unsigned int count; /* number of data points left to be taken */
224 unsigned int divisor1; /* counter 1 value for timed conversions */
225 unsigned int divisor2; /* counter 2 value for timed conversions */
226 unsigned int do_bits; /* digital output bits */
227 bool forever; /* flag that we should take data forever */
230 static void das800_ind_write(struct comedi_device *dev,
231 unsigned val, unsigned reg)
234 * Select dev->iobase + 2 to be desired register
235 * then write to that register.
237 outb(reg, dev->iobase + DAS800_GAIN);
238 outb(val, dev->iobase + 2);
241 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
244 * Select dev->iobase + 7 to be desired register
245 * then read from that register.
247 outb(reg, dev->iobase + DAS800_GAIN);
248 return inb(dev->iobase + 7);
251 static void das800_enable(struct comedi_device *dev)
253 const struct das800_board *thisboard = comedi_board(dev);
254 struct das800_private *devpriv = dev->private;
255 unsigned long irq_flags;
257 spin_lock_irqsave(&dev->spinlock, irq_flags);
258 /* enable fifo-half full interrupts for cio-das802/16 */
259 if (thisboard->resolution == 16)
260 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
261 /* enable hardware triggering */
262 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
263 /* enable card's interrupt */
264 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
268 static void das800_disable(struct comedi_device *dev)
270 unsigned long irq_flags;
272 spin_lock_irqsave(&dev->spinlock, irq_flags);
273 /* disable hardware triggering of conversions */
274 das800_ind_write(dev, 0x0, CONV_CONTROL);
275 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
278 static int das800_set_frequency(struct comedi_device *dev)
280 struct das800_private *devpriv = dev->private;
283 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
285 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
293 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
295 struct das800_private *devpriv = dev->private;
297 devpriv->forever = false;
303 static int das800_ai_do_cmdtest(struct comedi_device *dev,
304 struct comedi_subdevice *s,
305 struct comedi_cmd *cmd)
307 const struct das800_board *thisboard = comedi_board(dev);
308 struct das800_private *devpriv = dev->private;
311 /* Step 1 : check if triggers are trivially valid */
313 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
314 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
315 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
316 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
317 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
322 /* Step 2a : make sure trigger sources are unique */
324 err |= cfc_check_trigger_is_unique(cmd->start_src);
325 err |= cfc_check_trigger_is_unique(cmd->convert_src);
326 err |= cfc_check_trigger_is_unique(cmd->stop_src);
328 /* Step 2b : and mutually compatible */
333 /* Step 3: check if arguments are trivially valid */
335 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
337 if (cmd->convert_src == TRIG_TIMER)
338 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
339 thisboard->ai_speed);
341 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
344 if (cmd->stop_src == TRIG_COUNT)
345 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
347 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
352 /* step 4: fix up any arguments */
354 if (cmd->convert_src == TRIG_TIMER) {
355 int tmp = cmd->convert_arg;
357 /* calculate counter values that give desired timing */
358 i8253_cascade_ns_to_timer(I8254_OSC_BASE_1MHZ,
361 &cmd->convert_arg, cmd->flags);
362 if (tmp != cmd->convert_arg)
369 /* check channel/gain list against card's limitations */
371 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
372 unsigned int range = CR_RANGE(cmd->chanlist[0]);
376 for (i = 1; i < cmd->chanlist_len; i++) {
377 next = cmd->chanlist[i];
378 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
379 dev_err(dev->class_dev,
380 "chanlist must be consecutive, counting upwards\n");
383 if (CR_RANGE(next) != range) {
384 dev_err(dev->class_dev,
385 "chanlist must all have the same gain\n");
397 static int das800_ai_do_cmd(struct comedi_device *dev,
398 struct comedi_subdevice *s)
400 const struct das800_board *thisboard = comedi_board(dev);
401 struct das800_private *devpriv = dev->private;
402 struct comedi_async *async = s->async;
403 unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
404 unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
405 unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
406 unsigned int scan_chans = (end_chan << 3) | start_chan;
408 unsigned long irq_flags;
412 spin_lock_irqsave(&dev->spinlock, irq_flags);
413 /* set scan limits */
414 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
415 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
418 if (thisboard->resolution == 12 && gain > 0)
421 outb(gain, dev->iobase + DAS800_GAIN);
423 switch (async->cmd.stop_src) {
425 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
426 devpriv->forever = false;
429 devpriv->forever = true;
436 /* enable auto channel scan, send interrupts on end of conversion
437 * and set clock source to internal or external
440 conv_bits |= EACS | IEOC;
441 if (async->cmd.start_src == TRIG_EXT)
443 switch (async->cmd.convert_src) {
445 conv_bits |= CASC | ITE;
446 /* set conversion frequency */
447 if (das800_set_frequency(dev) < 0) {
448 comedi_error(dev, "Error setting up counters");
458 spin_lock_irqsave(&dev->spinlock, irq_flags);
459 das800_ind_write(dev, conv_bits, CONV_CONTROL);
460 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
467 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
469 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
470 unsigned int msb = inb(dev->iobase + DAS800_MSB);
472 return (msb << 8) | lsb;
475 static irqreturn_t das800_interrupt(int irq, void *d)
477 struct comedi_device *dev = d;
478 struct das800_private *devpriv = dev->private;
479 struct comedi_subdevice *s = dev->read_subdev;
480 struct comedi_async *async = s ? s->async : NULL;
481 unsigned long irq_flags;
488 status = inb(dev->iobase + DAS800_STATUS);
494 spin_lock_irqsave(&dev->spinlock, irq_flags);
495 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
497 * Don't release spinlock yet since we want to make sure
498 * no one else disables hardware conversions.
501 /* if hardware conversions are not enabled, then quit */
503 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
507 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
508 val = das800_ai_get_sample(dev);
509 if (s->maxdata == 0x0fff) {
510 fifo_empty = !!(val & FIFO_EMPTY);
511 fifo_overflow = !!(val & FIFO_OVF);
513 /* cio-das802/16 has no fifo empty status bit */
515 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
518 if (fifo_empty || fifo_overflow)
521 if (s->maxdata == 0x0fff)
522 val >>= 4; /* 12-bit sample */
524 /* if there are more data points to collect */
525 if (devpriv->count > 0 || devpriv->forever) {
526 /* write data point to buffer */
527 cfc_write_to_buffer(s, val & s->maxdata);
531 async->events |= COMEDI_CB_BLOCK;
534 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
535 das800_cancel(dev, s);
536 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
537 comedi_event(dev, s);
542 if (devpriv->count > 0 || devpriv->forever) {
543 /* Re-enable card's interrupt.
544 * We already have spinlock, so indirect addressing is safe */
545 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
547 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
549 /* otherwise, stop taking data */
550 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
552 async->events |= COMEDI_CB_EOA;
554 comedi_event(dev, s);
559 static int das800_wait_for_conv(struct comedi_device *dev, int timeout)
563 for (i = 0; i < timeout; i++) {
564 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
570 static int das800_ai_insn_read(struct comedi_device *dev,
571 struct comedi_subdevice *s,
572 struct comedi_insn *insn,
575 struct das800_private *devpriv = dev->private;
576 unsigned int chan = CR_CHAN(insn->chanspec);
577 unsigned int range = CR_RANGE(insn->chanspec);
578 unsigned long irq_flags;
585 /* set multiplexer */
586 spin_lock_irqsave(&dev->spinlock, irq_flags);
587 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
588 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
590 /* set gain / range */
591 if (s->maxdata == 0x0fff && range)
594 outb(range, dev->iobase + DAS800_GAIN);
598 for (i = 0; i < insn->n; i++) {
599 /* trigger conversion */
600 outb_p(0, dev->iobase + DAS800_MSB);
602 ret = das800_wait_for_conv(dev, 1000);
606 val = das800_ai_get_sample(dev);
607 if (s->maxdata == 0x0fff)
608 val >>= 4; /* 12-bit sample */
609 data[i] = val & s->maxdata;
615 static int das800_di_insn_bits(struct comedi_device *dev,
616 struct comedi_subdevice *s,
617 struct comedi_insn *insn,
620 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
625 static int das800_do_insn_bits(struct comedi_device *dev,
626 struct comedi_subdevice *s,
627 struct comedi_insn *insn,
630 struct das800_private *devpriv = dev->private;
631 unsigned long irq_flags;
633 if (comedi_dio_update_state(s, data)) {
634 devpriv->do_bits = s->state << 4;
636 spin_lock_irqsave(&dev->spinlock, irq_flags);
637 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
639 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
647 static int das800_probe(struct comedi_device *dev)
649 const struct das800_board *thisboard = comedi_board(dev);
650 int board = thisboard ? thisboard - das800_boards : -EINVAL;
652 unsigned long irq_flags;
654 spin_lock_irqsave(&dev->spinlock, irq_flags);
655 id_bits = das800_ind_read(dev, ID) & 0x3;
656 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
660 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
662 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
663 board = BOARD_DAS800;
666 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
668 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
669 board = BOARD_DAS801;
672 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
673 board == BOARD_CIODAS80216)
675 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
676 board = BOARD_DAS802;
679 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
687 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
689 const struct das800_board *thisboard = comedi_board(dev);
690 struct das800_private *devpriv;
691 struct comedi_subdevice *s;
692 unsigned int irq = it->options[1];
693 unsigned long irq_flags;
697 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
701 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
705 board = das800_probe(dev);
707 dev_dbg(dev->class_dev, "unable to determine board type\n");
710 dev->board_ptr = das800_boards + board;
711 thisboard = comedi_board(dev);
712 dev->board_name = thisboard->name;
714 if (irq > 1 && irq <= 7) {
715 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
721 ret = comedi_alloc_subdevices(dev, 3);
725 /* Analog Input subdevice */
726 s = &dev->subdevices[0];
727 dev->read_subdev = s;
728 s->type = COMEDI_SUBD_AI;
729 s->subdev_flags = SDF_READABLE | SDF_GROUND;
731 s->maxdata = (1 << thisboard->resolution) - 1;
732 s->range_table = thisboard->ai_range;
733 s->insn_read = das800_ai_insn_read;
735 s->subdev_flags |= SDF_CMD_READ;
737 s->do_cmdtest = das800_ai_do_cmdtest;
738 s->do_cmd = das800_ai_do_cmd;
739 s->cancel = das800_cancel;
742 /* Digital Input subdevice */
743 s = &dev->subdevices[1];
744 s->type = COMEDI_SUBD_DI;
745 s->subdev_flags = SDF_READABLE;
748 s->range_table = &range_digital;
749 s->insn_bits = das800_di_insn_bits;
751 /* Digital Output subdevice */
752 s = &dev->subdevices[2];
753 s->type = COMEDI_SUBD_DO;
754 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
757 s->range_table = &range_digital;
758 s->insn_bits = das800_do_insn_bits;
762 /* initialize digital out channels */
763 spin_lock_irqsave(&dev->spinlock, irq_flags);
764 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
765 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
770 static struct comedi_driver driver_das800 = {
771 .driver_name = "das800",
772 .module = THIS_MODULE,
773 .attach = das800_attach,
774 .detach = comedi_legacy_detach,
775 .num_names = ARRAY_SIZE(das800_boards),
776 .board_name = &das800_boards[0].name,
777 .offset = sizeof(struct das800_board),
779 module_comedi_driver(driver_das800);
781 MODULE_AUTHOR("Comedi http://www.comedi.org");
782 MODULE_DESCRIPTION("Comedi low-level driver");
783 MODULE_LICENSE("GPL");