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 int val, unsigned int 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 int das800_ind_read(struct comedi_device *dev, unsigned int 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");