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.
47 cmd triggers supported:
48 start_src: TRIG_NOW | TRIG_EXT
49 scan_begin_src: TRIG_FOLLOW
50 scan_end_src: TRIG_COUNT
51 convert_src: TRIG_TIMER | TRIG_EXT
52 stop_src: TRIG_NONE | TRIG_COUNT
55 #include <linux/module.h>
56 #include <linux/interrupt.h>
57 #include <linux/delay.h>
59 #include "../comedidev.h"
61 #include "comedi_8254.h"
63 #define N_CHAN_AI 8 /* number of analog input channels */
65 /* Registers for the das800 */
68 #define FIFO_EMPTY 0x1
71 #define DAS800_CONTROL1 2
72 #define CONTROL1_INTE 0x8
73 #define DAS800_CONV_CONTROL 2
79 #define CONV_HCEN 0x80
80 #define DAS800_SCAN_LIMITS 2
81 #define DAS800_STATUS 2
85 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
86 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
88 #define CONV_CONTROL 0xa0
89 #define SCAN_LIMITS 0xc0
92 #define DAS800_STATUS2 7
93 #define STATUS2_HCEN 0x80
94 #define STATUS2_INTE 0X20
97 #define DAS802_16_HALF_FIFO_SZ 128
102 const struct comedi_lrange *ai_range;
106 static const struct comedi_lrange range_das801_ai = {
120 static const struct comedi_lrange range_cio_das801_ai = {
134 static const struct comedi_lrange range_das802_ai = {
148 static const struct comedi_lrange range_das80216_ai = {
161 enum das800_boardinfo {
171 static const struct das800_board das800_boards[] = {
175 .ai_range = &range_bipolar5,
178 [BOARD_CIODAS800] = {
179 .name = "cio-das800",
181 .ai_range = &range_bipolar5,
187 .ai_range = &range_das801_ai,
190 [BOARD_CIODAS801] = {
191 .name = "cio-das801",
193 .ai_range = &range_cio_das801_ai,
199 .ai_range = &range_das802_ai,
202 [BOARD_CIODAS802] = {
203 .name = "cio-das802",
205 .ai_range = &range_das802_ai,
208 [BOARD_CIODAS80216] = {
209 .name = "cio-das802/16",
211 .ai_range = &range_das80216_ai,
216 struct das800_private {
217 unsigned int do_bits; /* digital output bits */
220 static void das800_ind_write(struct comedi_device *dev,
221 unsigned val, unsigned reg)
224 * Select dev->iobase + 2 to be desired register
225 * then write to that register.
227 outb(reg, dev->iobase + DAS800_GAIN);
228 outb(val, dev->iobase + 2);
231 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
234 * Select dev->iobase + 7 to be desired register
235 * then read from that register.
237 outb(reg, dev->iobase + DAS800_GAIN);
238 return inb(dev->iobase + 7);
241 static void das800_enable(struct comedi_device *dev)
243 const struct das800_board *board = dev->board_ptr;
244 struct das800_private *devpriv = dev->private;
245 unsigned long irq_flags;
247 spin_lock_irqsave(&dev->spinlock, irq_flags);
248 /* enable fifo-half full interrupts for cio-das802/16 */
249 if (board->resolution == 16)
250 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
251 /* enable hardware triggering */
252 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
253 /* enable card's interrupt */
254 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
255 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
258 static void das800_disable(struct comedi_device *dev)
260 unsigned long irq_flags;
262 spin_lock_irqsave(&dev->spinlock, irq_flags);
263 /* disable hardware triggering of conversions */
264 das800_ind_write(dev, 0x0, CONV_CONTROL);
265 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
268 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
274 static int das800_ai_check_chanlist(struct comedi_device *dev,
275 struct comedi_subdevice *s,
276 struct comedi_cmd *cmd)
278 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
279 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
282 for (i = 1; i < cmd->chanlist_len; i++) {
283 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
284 unsigned int range = CR_RANGE(cmd->chanlist[i]);
286 if (chan != (chan0 + i) % s->n_chan) {
287 dev_dbg(dev->class_dev,
288 "chanlist must be consecutive, counting upwards\n");
292 if (range != range0) {
293 dev_dbg(dev->class_dev,
294 "chanlist must all have the same gain\n");
302 static int das800_ai_do_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 struct comedi_cmd *cmd)
306 const struct das800_board *board = dev->board_ptr;
309 /* Step 1 : check if triggers are trivially valid */
311 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
312 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
313 err |= comedi_check_trigger_src(&cmd->convert_src,
314 TRIG_TIMER | TRIG_EXT);
315 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
316 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
321 /* Step 2a : make sure trigger sources are unique */
323 err |= comedi_check_trigger_is_unique(cmd->start_src);
324 err |= comedi_check_trigger_is_unique(cmd->convert_src);
325 err |= comedi_check_trigger_is_unique(cmd->stop_src);
327 /* Step 2b : and mutually compatible */
332 /* Step 3: check if arguments are trivially valid */
334 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
336 if (cmd->convert_src == TRIG_TIMER) {
337 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
341 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
342 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
345 if (cmd->stop_src == TRIG_COUNT)
346 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
348 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
353 /* step 4: fix up any arguments */
355 if (cmd->convert_src == TRIG_TIMER) {
356 unsigned int arg = cmd->convert_arg;
358 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
359 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
365 /* Step 5: check channel list if it exists */
366 if (cmd->chanlist && cmd->chanlist_len > 0)
367 err |= das800_ai_check_chanlist(dev, s, cmd);
375 static int das800_ai_do_cmd(struct comedi_device *dev,
376 struct comedi_subdevice *s)
378 const struct das800_board *board = dev->board_ptr;
379 struct comedi_async *async = s->async;
380 struct comedi_cmd *cmd = &async->cmd;
381 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
382 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
383 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
384 unsigned int scan_chans = (end_chan << 3) | start_chan;
386 unsigned long irq_flags;
390 spin_lock_irqsave(&dev->spinlock, irq_flags);
391 /* set scan limits */
392 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
393 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
396 if (board->resolution == 12 && gain > 0)
399 outb(gain, dev->iobase + DAS800_GAIN);
401 /* enable auto channel scan, send interrupts on end of conversion
402 * and set clock source to internal or external
405 conv_bits |= EACS | IEOC;
406 if (cmd->start_src == TRIG_EXT)
408 if (cmd->convert_src == TRIG_TIMER) {
409 conv_bits |= CASC | ITE;
410 comedi_8254_update_divisors(dev->pacer);
411 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
414 spin_lock_irqsave(&dev->spinlock, irq_flags);
415 das800_ind_write(dev, conv_bits, CONV_CONTROL);
416 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
422 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
424 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
425 unsigned int msb = inb(dev->iobase + DAS800_MSB);
427 return (msb << 8) | lsb;
430 static irqreturn_t das800_interrupt(int irq, void *d)
432 struct comedi_device *dev = d;
433 struct das800_private *devpriv = dev->private;
434 struct comedi_subdevice *s = dev->read_subdev;
435 struct comedi_async *async;
436 struct comedi_cmd *cmd;
437 unsigned long irq_flags;
444 status = inb(dev->iobase + DAS800_STATUS);
453 spin_lock_irqsave(&dev->spinlock, irq_flags);
454 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
456 * Don't release spinlock yet since we want to make sure
457 * no one else disables hardware conversions.
460 /* if hardware conversions are not enabled, then quit */
462 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
466 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
467 val = das800_ai_get_sample(dev);
468 if (s->maxdata == 0x0fff) {
469 fifo_empty = !!(val & FIFO_EMPTY);
470 fifo_overflow = !!(val & FIFO_OVF);
472 /* cio-das802/16 has no fifo empty status bit */
474 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
477 if (fifo_empty || fifo_overflow)
480 if (s->maxdata == 0x0fff)
481 val >>= 4; /* 12-bit sample */
484 comedi_buf_write_samples(s, &val, 1);
486 if (cmd->stop_src == TRIG_COUNT &&
487 async->scans_done >= cmd->stop_arg) {
488 async->events |= COMEDI_CB_EOA;
494 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
495 async->events |= COMEDI_CB_ERROR;
496 comedi_handle_events(dev, s);
500 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
502 * Re-enable card's interrupt.
503 * We already have spinlock, so indirect addressing is safe
505 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
507 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
509 /* otherwise, stop taking data */
510 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
513 comedi_handle_events(dev, s);
517 static int das800_ai_eoc(struct comedi_device *dev,
518 struct comedi_subdevice *s,
519 struct comedi_insn *insn,
520 unsigned long context)
524 status = inb(dev->iobase + DAS800_STATUS);
525 if ((status & BUSY) == 0)
530 static int das800_ai_insn_read(struct comedi_device *dev,
531 struct comedi_subdevice *s,
532 struct comedi_insn *insn,
535 struct das800_private *devpriv = dev->private;
536 unsigned int chan = CR_CHAN(insn->chanspec);
537 unsigned int range = CR_RANGE(insn->chanspec);
538 unsigned long irq_flags;
545 /* set multiplexer */
546 spin_lock_irqsave(&dev->spinlock, irq_flags);
547 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
548 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
550 /* set gain / range */
551 if (s->maxdata == 0x0fff && range)
554 outb(range, dev->iobase + DAS800_GAIN);
558 for (i = 0; i < insn->n; i++) {
559 /* trigger conversion */
560 outb_p(0, dev->iobase + DAS800_MSB);
562 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
566 val = das800_ai_get_sample(dev);
567 if (s->maxdata == 0x0fff)
568 val >>= 4; /* 12-bit sample */
569 data[i] = val & s->maxdata;
575 static int das800_di_insn_bits(struct comedi_device *dev,
576 struct comedi_subdevice *s,
577 struct comedi_insn *insn,
580 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
585 static int das800_do_insn_bits(struct comedi_device *dev,
586 struct comedi_subdevice *s,
587 struct comedi_insn *insn,
590 struct das800_private *devpriv = dev->private;
591 unsigned long irq_flags;
593 if (comedi_dio_update_state(s, data)) {
594 devpriv->do_bits = s->state << 4;
596 spin_lock_irqsave(&dev->spinlock, irq_flags);
597 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
599 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
607 static const struct das800_board *das800_probe(struct comedi_device *dev)
609 const struct das800_board *board = dev->board_ptr;
610 int index = board ? board - das800_boards : -EINVAL;
612 unsigned long irq_flags;
615 * The dev->board_ptr will be set by comedi_device_attach() if the
616 * board name provided by the user matches a board->name in this
617 * driver. If so, this function sanity checks the id_bits to verify
618 * that the board is correct.
620 * If the dev->board_ptr is not set, the user is trying to attach
621 * an unspecified board to this driver. In this case the id_bits
622 * are used to 'probe' for the correct dev->board_ptr.
624 spin_lock_irqsave(&dev->spinlock, irq_flags);
625 id_bits = das800_ind_read(dev, ID) & 0x3;
626 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
630 if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
632 index = BOARD_DAS800;
635 if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
637 index = BOARD_DAS801;
640 if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
641 index == BOARD_CIODAS80216)
643 index = BOARD_DAS802;
646 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
650 dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
651 das800_boards[index].name);
653 return &das800_boards[index];
656 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
658 const struct das800_board *board;
659 struct das800_private *devpriv;
660 struct comedi_subdevice *s;
661 unsigned int irq = it->options[1];
662 unsigned long irq_flags;
665 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
669 ret = comedi_request_region(dev, it->options[0], 0x8);
673 board = das800_probe(dev);
676 dev->board_ptr = board;
677 dev->board_name = board->name;
679 if (irq > 1 && irq <= 7) {
680 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
686 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
687 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
691 ret = comedi_alloc_subdevices(dev, 3);
695 /* Analog Input subdevice */
696 s = &dev->subdevices[0];
697 dev->read_subdev = s;
698 s->type = COMEDI_SUBD_AI;
699 s->subdev_flags = SDF_READABLE | SDF_GROUND;
701 s->maxdata = (1 << board->resolution) - 1;
702 s->range_table = board->ai_range;
703 s->insn_read = das800_ai_insn_read;
705 s->subdev_flags |= SDF_CMD_READ;
707 s->do_cmdtest = das800_ai_do_cmdtest;
708 s->do_cmd = das800_ai_do_cmd;
709 s->cancel = das800_cancel;
712 /* Digital Input subdevice */
713 s = &dev->subdevices[1];
714 s->type = COMEDI_SUBD_DI;
715 s->subdev_flags = SDF_READABLE;
718 s->range_table = &range_digital;
719 s->insn_bits = das800_di_insn_bits;
721 /* Digital Output subdevice */
722 s = &dev->subdevices[2];
723 s->type = COMEDI_SUBD_DO;
724 s->subdev_flags = SDF_WRITABLE;
727 s->range_table = &range_digital;
728 s->insn_bits = das800_do_insn_bits;
732 /* initialize digital out channels */
733 spin_lock_irqsave(&dev->spinlock, irq_flags);
734 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
735 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
740 static struct comedi_driver driver_das800 = {
741 .driver_name = "das800",
742 .module = THIS_MODULE,
743 .attach = das800_attach,
744 .detach = comedi_legacy_detach,
745 .num_names = ARRAY_SIZE(das800_boards),
746 .board_name = &das800_boards[0].name,
747 .offset = sizeof(struct das800_board),
749 module_comedi_driver(driver_das800);
751 MODULE_AUTHOR("Comedi http://www.comedi.org");
752 MODULE_DESCRIPTION("Comedi low-level driver");
753 MODULE_LICENSE("GPL");