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/interrupt.h>
60 #include "../comedidev.h"
62 #include <linux/ioport.h>
63 #include <linux/delay.h>
66 #include "comedi_fc.h"
69 #define TIMER_BASE 1000
70 #define N_CHAN_AI 8 /* number of analog input channels */
72 /* Registers for the das800 */
75 #define FIFO_EMPTY 0x1
78 #define DAS800_CONTROL1 2
79 #define CONTROL1_INTE 0x8
80 #define DAS800_CONV_CONTROL 2
86 #define CONV_HCEN 0x80
87 #define DAS800_SCAN_LIMITS 2
88 #define DAS800_STATUS 2
92 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
93 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
95 #define CONV_CONTROL 0xa0
96 #define SCAN_LIMITS 0xc0
99 #define DAS800_STATUS2 7
100 #define STATUS2_HCEN 0x80
101 #define STATUS2_INTE 0X20
104 #define DAS802_16_HALF_FIFO_SZ 128
106 struct das800_board {
109 const struct comedi_lrange *ai_range;
113 static const struct comedi_lrange range_das801_ai = {
127 static const struct comedi_lrange range_cio_das801_ai = {
141 static const struct comedi_lrange range_das802_ai = {
155 static const struct comedi_lrange range_das80216_ai = {
168 enum das800_boardinfo {
178 static const struct das800_board das800_boards[] = {
182 .ai_range = &range_bipolar5,
185 [BOARD_CIODAS800] = {
186 .name = "cio-das800",
188 .ai_range = &range_bipolar5,
194 .ai_range = &range_das801_ai,
197 [BOARD_CIODAS801] = {
198 .name = "cio-das801",
200 .ai_range = &range_cio_das801_ai,
206 .ai_range = &range_das802_ai,
209 [BOARD_CIODAS802] = {
210 .name = "cio-das802",
212 .ai_range = &range_das802_ai,
215 [BOARD_CIODAS80216] = {
216 .name = "cio-das802/16",
218 .ai_range = &range_das80216_ai,
223 struct das800_private {
224 unsigned int count; /* number of data points left to be taken */
225 unsigned int divisor1; /* counter 1 value for timed conversions */
226 unsigned int divisor2; /* counter 2 value for timed conversions */
227 unsigned int do_bits; /* digital output bits */
228 bool forever; /* flag that we should take data forever */
231 static void das800_ind_write(struct comedi_device *dev,
232 unsigned val, unsigned reg)
235 * Select dev->iobase + 2 to be desired register
236 * then write to that register.
238 outb(reg, dev->iobase + DAS800_GAIN);
239 outb(val, dev->iobase + 2);
242 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
245 * Select dev->iobase + 7 to be desired register
246 * then read from that register.
248 outb(reg, dev->iobase + DAS800_GAIN);
249 return inb(dev->iobase + 7);
252 static void das800_enable(struct comedi_device *dev)
254 const struct das800_board *thisboard = comedi_board(dev);
255 struct das800_private *devpriv = dev->private;
256 unsigned long irq_flags;
258 spin_lock_irqsave(&dev->spinlock, irq_flags);
259 /* enable fifo-half full interrupts for cio-das802/16 */
260 if (thisboard->resolution == 16)
261 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
262 /* enable hardware triggering */
263 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
264 /* enable card's interrupt */
265 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
266 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
269 static void das800_disable(struct comedi_device *dev)
271 unsigned long irq_flags;
273 spin_lock_irqsave(&dev->spinlock, irq_flags);
274 /* disable hardware triggering of conversions */
275 das800_ind_write(dev, 0x0, CONV_CONTROL);
276 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
279 static int das800_set_frequency(struct comedi_device *dev)
281 struct das800_private *devpriv = dev->private;
284 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
286 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
294 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
296 struct das800_private *devpriv = dev->private;
298 devpriv->forever = false;
304 static int das800_ai_do_cmdtest(struct comedi_device *dev,
305 struct comedi_subdevice *s,
306 struct comedi_cmd *cmd)
308 const struct das800_board *thisboard = comedi_board(dev);
309 struct das800_private *devpriv = dev->private;
312 /* Step 1 : check if triggers are trivially valid */
314 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
315 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
316 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
317 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
318 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
323 /* Step 2a : make sure trigger sources are unique */
325 err |= cfc_check_trigger_is_unique(cmd->start_src);
326 err |= cfc_check_trigger_is_unique(cmd->convert_src);
327 err |= cfc_check_trigger_is_unique(cmd->stop_src);
329 /* Step 2b : and mutually compatible */
334 /* Step 3: check if arguments are trivially valid */
336 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
338 if (cmd->convert_src == TRIG_TIMER)
339 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
340 thisboard->ai_speed);
342 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
343 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
345 if (cmd->stop_src == TRIG_COUNT)
346 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
348 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
353 /* step 4: fix up any arguments */
355 if (cmd->convert_src == TRIG_TIMER) {
356 int tmp = cmd->convert_arg;
358 /* calculate counter values that give desired timing */
359 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
363 cmd->flags & TRIG_ROUND_MASK);
364 if (tmp != cmd->convert_arg)
371 /* check channel/gain list against card's limitations */
373 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
374 unsigned int range = CR_RANGE(cmd->chanlist[0]);
378 for (i = 1; i < cmd->chanlist_len; i++) {
379 next = cmd->chanlist[i];
380 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
381 dev_err(dev->class_dev,
382 "chanlist must be consecutive, counting upwards\n");
385 if (CR_RANGE(next) != range) {
386 dev_err(dev->class_dev,
387 "chanlist must all have the same gain\n");
399 static int das800_ai_do_cmd(struct comedi_device *dev,
400 struct comedi_subdevice *s)
402 const struct das800_board *thisboard = comedi_board(dev);
403 struct das800_private *devpriv = dev->private;
404 struct comedi_async *async = s->async;
405 unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
406 unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
407 unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
408 unsigned int scan_chans = (end_chan << 3) | start_chan;
410 unsigned long irq_flags;
414 spin_lock_irqsave(&dev->spinlock, irq_flags);
415 /* set scan limits */
416 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
417 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
420 if (thisboard->resolution == 12 && gain > 0)
423 outb(gain, dev->iobase + DAS800_GAIN);
425 switch (async->cmd.stop_src) {
427 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
428 devpriv->forever = false;
431 devpriv->forever = true;
438 /* enable auto channel scan, send interrupts on end of conversion
439 * and set clock source to internal or external
442 conv_bits |= EACS | IEOC;
443 if (async->cmd.start_src == TRIG_EXT)
445 switch (async->cmd.convert_src) {
447 conv_bits |= CASC | ITE;
448 /* set conversion frequency */
449 if (das800_set_frequency(dev) < 0) {
450 comedi_error(dev, "Error setting up counters");
460 spin_lock_irqsave(&dev->spinlock, irq_flags);
461 das800_ind_write(dev, conv_bits, CONV_CONTROL);
462 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
469 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
471 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
472 unsigned int msb = inb(dev->iobase + DAS800_MSB);
474 return (msb << 8) | lsb;
477 static irqreturn_t das800_interrupt(int irq, void *d)
479 struct comedi_device *dev = d;
480 struct das800_private *devpriv = dev->private;
481 struct comedi_subdevice *s = dev->read_subdev;
482 struct comedi_async *async = s ? s->async : NULL;
483 unsigned long irq_flags;
490 status = inb(dev->iobase + DAS800_STATUS);
496 spin_lock_irqsave(&dev->spinlock, irq_flags);
497 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
499 * Don't release spinlock yet since we want to make sure
500 * no one else disables hardware conversions.
503 /* if hardware conversions are not enabled, then quit */
505 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
509 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
510 val = das800_ai_get_sample(dev);
511 if (s->maxdata == 0x0fff) {
512 fifo_empty = !!(val & FIFO_EMPTY);
513 fifo_overflow = !!(val & FIFO_OVF);
515 /* cio-das802/16 has no fifo empty status bit */
517 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
520 if (fifo_empty || fifo_overflow)
523 if (s->maxdata == 0x0fff)
524 val >>= 4; /* 12-bit sample */
526 /* if there are more data points to collect */
527 if (devpriv->count > 0 || devpriv->forever) {
528 /* write data point to buffer */
529 cfc_write_to_buffer(s, val & s->maxdata);
533 async->events |= COMEDI_CB_BLOCK;
536 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
537 das800_cancel(dev, s);
538 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
539 comedi_event(dev, s);
544 if (devpriv->count > 0 || devpriv->forever) {
545 /* Re-enable card's interrupt.
546 * We already have spinlock, so indirect addressing is safe */
547 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
549 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
551 /* otherwise, stop taking data */
552 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
554 async->events |= COMEDI_CB_EOA;
556 comedi_event(dev, s);
561 static int das800_wait_for_conv(struct comedi_device *dev, int timeout)
565 for (i = 0; i < timeout; i++) {
566 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
572 static int das800_ai_insn_read(struct comedi_device *dev,
573 struct comedi_subdevice *s,
574 struct comedi_insn *insn,
577 struct das800_private *devpriv = dev->private;
578 unsigned int chan = CR_CHAN(insn->chanspec);
579 unsigned int range = CR_RANGE(insn->chanspec);
580 unsigned long irq_flags;
587 /* set multiplexer */
588 spin_lock_irqsave(&dev->spinlock, irq_flags);
589 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
590 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
592 /* set gain / range */
593 if (s->maxdata == 0x0fff && range)
596 outb(range, dev->iobase + DAS800_GAIN);
600 for (i = 0; i < insn->n; i++) {
601 /* trigger conversion */
602 outb_p(0, dev->iobase + DAS800_MSB);
604 ret = das800_wait_for_conv(dev, 1000);
608 val = das800_ai_get_sample(dev);
609 if (s->maxdata == 0x0fff)
610 val >>= 4; /* 12-bit sample */
611 data[i] = val & s->maxdata;
617 static int das800_di_insn_bits(struct comedi_device *dev,
618 struct comedi_subdevice *s,
619 struct comedi_insn *insn,
622 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
627 static int das800_do_insn_bits(struct comedi_device *dev,
628 struct comedi_subdevice *s,
629 struct comedi_insn *insn,
632 struct das800_private *devpriv = dev->private;
633 unsigned int mask = data[0];
634 unsigned int bits = data[1];
635 unsigned long irq_flags;
639 s->state |= (bits & mask);
640 devpriv->do_bits = s->state << 4;
642 spin_lock_irqsave(&dev->spinlock, irq_flags);
643 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
645 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
653 static int das800_probe(struct comedi_device *dev)
655 const struct das800_board *thisboard = comedi_board(dev);
656 int board = thisboard ? thisboard - das800_boards : -EINVAL;
658 unsigned long irq_flags;
660 spin_lock_irqsave(&dev->spinlock, irq_flags);
661 id_bits = das800_ind_read(dev, ID) & 0x3;
662 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
666 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
668 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
669 board = BOARD_DAS800;
672 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
674 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
675 board = BOARD_DAS801;
678 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
679 board == BOARD_CIODAS80216)
681 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
682 board = BOARD_DAS802;
685 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
693 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
695 const struct das800_board *thisboard = comedi_board(dev);
696 struct das800_private *devpriv;
697 struct comedi_subdevice *s;
698 unsigned int irq = it->options[1];
699 unsigned long irq_flags;
703 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
706 dev->private = 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");