1 // SPDX-License-Identifier: GPL-2.0+
3 * comedi/drivers/das800.c
4 * Driver for Keitley das800 series boards and compatibles
5 * Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
7 * COMEDI - Linux Control and Measurement Device Interface
8 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
12 * Description: Keithley Metrabyte DAS800 (& compatibles)
13 * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
14 * Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
16 * [Measurement Computing] CIO-DAS800 (cio-das800),
17 * CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
18 * CIO-DAS802/16 (cio-das802/16)
19 * Status: works, cio-das802/16 untested - email me if you have tested it
21 * Configuration options:
22 * [0] - I/O port base address
23 * [1] - IRQ (optional, required for timed or externally triggered conversions)
26 * IRQ can be omitted, although the cmd interface will not work without it.
28 * All entries in the channel/gain list must use the same gain and be
29 * consecutive channels counting upwards in channel number (these are
30 * hardware limitations.)
32 * I've never tested the gain setting stuff since I only have a
33 * DAS-800 board with fixed gain.
35 * The cio-das802/16 does not have a fifo-empty status bit! Therefore
36 * only fifo-half-full transfers are possible with this card.
38 * cmd triggers supported:
39 * start_src: TRIG_NOW | TRIG_EXT
40 * scan_begin_src: TRIG_FOLLOW
41 * scan_end_src: TRIG_COUNT
42 * convert_src: TRIG_TIMER | TRIG_EXT
43 * stop_src: TRIG_NONE | TRIG_COUNT
46 #include <linux/module.h>
47 #include <linux/interrupt.h>
48 #include <linux/delay.h>
50 #include "../comedidev.h"
52 #include "comedi_8254.h"
54 #define N_CHAN_AI 8 /* number of analog input channels */
56 /* Registers for the das800 */
59 #define FIFO_EMPTY 0x1
62 #define DAS800_CONTROL1 2
63 #define CONTROL1_INTE 0x8
64 #define DAS800_CONV_CONTROL 2
70 #define CONV_HCEN 0x80
71 #define DAS800_SCAN_LIMITS 2
72 #define DAS800_STATUS 2
76 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
77 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
79 #define CONV_CONTROL 0xa0
80 #define SCAN_LIMITS 0xc0
83 #define DAS800_STATUS2 7
84 #define STATUS2_HCEN 0x80
85 #define STATUS2_INTE 0X20
88 #define DAS802_16_HALF_FIFO_SZ 128
93 const struct comedi_lrange *ai_range;
97 static const struct comedi_lrange range_das801_ai = {
111 static const struct comedi_lrange range_cio_das801_ai = {
125 static const struct comedi_lrange range_das802_ai = {
139 static const struct comedi_lrange range_das80216_ai = {
152 enum das800_boardinfo {
162 static const struct das800_board das800_boards[] = {
166 .ai_range = &range_bipolar5,
169 [BOARD_CIODAS800] = {
170 .name = "cio-das800",
172 .ai_range = &range_bipolar5,
178 .ai_range = &range_das801_ai,
181 [BOARD_CIODAS801] = {
182 .name = "cio-das801",
184 .ai_range = &range_cio_das801_ai,
190 .ai_range = &range_das802_ai,
193 [BOARD_CIODAS802] = {
194 .name = "cio-das802",
196 .ai_range = &range_das802_ai,
199 [BOARD_CIODAS80216] = {
200 .name = "cio-das802/16",
202 .ai_range = &range_das80216_ai,
207 struct das800_private {
208 unsigned int do_bits; /* digital output bits */
211 static void das800_ind_write(struct comedi_device *dev,
212 unsigned int val, unsigned int reg)
215 * Select dev->iobase + 2 to be desired register
216 * then write to that register.
218 outb(reg, dev->iobase + DAS800_GAIN);
219 outb(val, dev->iobase + 2);
222 static unsigned int das800_ind_read(struct comedi_device *dev, unsigned int reg)
225 * Select dev->iobase + 7 to be desired register
226 * then read from that register.
228 outb(reg, dev->iobase + DAS800_GAIN);
229 return inb(dev->iobase + 7);
232 static void das800_enable(struct comedi_device *dev)
234 const struct das800_board *board = dev->board_ptr;
235 struct das800_private *devpriv = dev->private;
236 unsigned long irq_flags;
238 spin_lock_irqsave(&dev->spinlock, irq_flags);
239 /* enable fifo-half full interrupts for cio-das802/16 */
240 if (board->resolution == 16)
241 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
242 /* enable hardware triggering */
243 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
244 /* enable card's interrupt */
245 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
246 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
249 static void das800_disable(struct comedi_device *dev)
251 unsigned long irq_flags;
253 spin_lock_irqsave(&dev->spinlock, irq_flags);
254 /* disable hardware triggering of conversions */
255 das800_ind_write(dev, 0x0, CONV_CONTROL);
256 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
259 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
265 static int das800_ai_check_chanlist(struct comedi_device *dev,
266 struct comedi_subdevice *s,
267 struct comedi_cmd *cmd)
269 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
270 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
273 for (i = 1; i < cmd->chanlist_len; i++) {
274 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
275 unsigned int range = CR_RANGE(cmd->chanlist[i]);
277 if (chan != (chan0 + i) % s->n_chan) {
278 dev_dbg(dev->class_dev,
279 "chanlist must be consecutive, counting upwards\n");
283 if (range != range0) {
284 dev_dbg(dev->class_dev,
285 "chanlist must all have the same gain\n");
293 static int das800_ai_do_cmdtest(struct comedi_device *dev,
294 struct comedi_subdevice *s,
295 struct comedi_cmd *cmd)
297 const struct das800_board *board = dev->board_ptr;
300 /* Step 1 : check if triggers are trivially valid */
302 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
303 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
304 err |= comedi_check_trigger_src(&cmd->convert_src,
305 TRIG_TIMER | TRIG_EXT);
306 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
307 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
312 /* Step 2a : make sure trigger sources are unique */
314 err |= comedi_check_trigger_is_unique(cmd->start_src);
315 err |= comedi_check_trigger_is_unique(cmd->convert_src);
316 err |= comedi_check_trigger_is_unique(cmd->stop_src);
318 /* Step 2b : and mutually compatible */
323 /* Step 3: check if arguments are trivially valid */
325 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
327 if (cmd->convert_src == TRIG_TIMER) {
328 err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
332 err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
333 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
336 if (cmd->stop_src == TRIG_COUNT)
337 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
339 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
344 /* step 4: fix up any arguments */
346 if (cmd->convert_src == TRIG_TIMER) {
347 unsigned int arg = cmd->convert_arg;
349 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
350 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
356 /* Step 5: check channel list if it exists */
357 if (cmd->chanlist && cmd->chanlist_len > 0)
358 err |= das800_ai_check_chanlist(dev, s, cmd);
366 static int das800_ai_do_cmd(struct comedi_device *dev,
367 struct comedi_subdevice *s)
369 const struct das800_board *board = dev->board_ptr;
370 struct comedi_async *async = s->async;
371 struct comedi_cmd *cmd = &async->cmd;
372 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
373 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
374 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
375 unsigned int scan_chans = (end_chan << 3) | start_chan;
377 unsigned long irq_flags;
381 spin_lock_irqsave(&dev->spinlock, irq_flags);
382 /* set scan limits */
383 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
384 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
387 if (board->resolution == 12 && gain > 0)
390 outb(gain, dev->iobase + DAS800_GAIN);
392 /* enable auto channel scan, send interrupts on end of conversion
393 * and set clock source to internal or external
396 conv_bits |= EACS | IEOC;
397 if (cmd->start_src == TRIG_EXT)
399 if (cmd->convert_src == TRIG_TIMER) {
400 conv_bits |= CASC | ITE;
401 comedi_8254_update_divisors(dev->pacer);
402 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
405 spin_lock_irqsave(&dev->spinlock, irq_flags);
406 das800_ind_write(dev, conv_bits, CONV_CONTROL);
407 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
413 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
415 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
416 unsigned int msb = inb(dev->iobase + DAS800_MSB);
418 return (msb << 8) | lsb;
421 static irqreturn_t das800_interrupt(int irq, void *d)
423 struct comedi_device *dev = d;
424 struct das800_private *devpriv = dev->private;
425 struct comedi_subdevice *s = dev->read_subdev;
426 struct comedi_async *async;
427 struct comedi_cmd *cmd;
428 unsigned long irq_flags;
435 status = inb(dev->iobase + DAS800_STATUS);
444 spin_lock_irqsave(&dev->spinlock, irq_flags);
445 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
447 * Don't release spinlock yet since we want to make sure
448 * no one else disables hardware conversions.
451 /* if hardware conversions are not enabled, then quit */
453 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
457 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
458 val = das800_ai_get_sample(dev);
459 if (s->maxdata == 0x0fff) {
460 fifo_empty = !!(val & FIFO_EMPTY);
461 fifo_overflow = !!(val & FIFO_OVF);
463 /* cio-das802/16 has no fifo empty status bit */
465 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
468 if (fifo_empty || fifo_overflow)
471 if (s->maxdata == 0x0fff)
472 val >>= 4; /* 12-bit sample */
475 comedi_buf_write_samples(s, &val, 1);
477 if (cmd->stop_src == TRIG_COUNT &&
478 async->scans_done >= cmd->stop_arg) {
479 async->events |= COMEDI_CB_EOA;
485 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
486 async->events |= COMEDI_CB_ERROR;
487 comedi_handle_events(dev, s);
491 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
493 * Re-enable card's interrupt.
494 * We already have spinlock, so indirect addressing is safe
496 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
498 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
500 /* otherwise, stop taking data */
501 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
504 comedi_handle_events(dev, s);
508 static int das800_ai_eoc(struct comedi_device *dev,
509 struct comedi_subdevice *s,
510 struct comedi_insn *insn,
511 unsigned long context)
515 status = inb(dev->iobase + DAS800_STATUS);
516 if ((status & BUSY) == 0)
521 static int das800_ai_insn_read(struct comedi_device *dev,
522 struct comedi_subdevice *s,
523 struct comedi_insn *insn,
526 struct das800_private *devpriv = dev->private;
527 unsigned int chan = CR_CHAN(insn->chanspec);
528 unsigned int range = CR_RANGE(insn->chanspec);
529 unsigned long irq_flags;
536 /* set multiplexer */
537 spin_lock_irqsave(&dev->spinlock, irq_flags);
538 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
539 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
541 /* set gain / range */
542 if (s->maxdata == 0x0fff && range)
545 outb(range, dev->iobase + DAS800_GAIN);
549 for (i = 0; i < insn->n; i++) {
550 /* trigger conversion */
551 outb_p(0, dev->iobase + DAS800_MSB);
553 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
557 val = das800_ai_get_sample(dev);
558 if (s->maxdata == 0x0fff)
559 val >>= 4; /* 12-bit sample */
560 data[i] = val & s->maxdata;
566 static int das800_di_insn_bits(struct comedi_device *dev,
567 struct comedi_subdevice *s,
568 struct comedi_insn *insn,
571 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
576 static int das800_do_insn_bits(struct comedi_device *dev,
577 struct comedi_subdevice *s,
578 struct comedi_insn *insn,
581 struct das800_private *devpriv = dev->private;
582 unsigned long irq_flags;
584 if (comedi_dio_update_state(s, data)) {
585 devpriv->do_bits = s->state << 4;
587 spin_lock_irqsave(&dev->spinlock, irq_flags);
588 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
590 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
598 static const struct das800_board *das800_probe(struct comedi_device *dev)
600 const struct das800_board *board = dev->board_ptr;
601 int index = board ? board - das800_boards : -EINVAL;
603 unsigned long irq_flags;
606 * The dev->board_ptr will be set by comedi_device_attach() if the
607 * board name provided by the user matches a board->name in this
608 * driver. If so, this function sanity checks the id_bits to verify
609 * that the board is correct.
611 * If the dev->board_ptr is not set, the user is trying to attach
612 * an unspecified board to this driver. In this case the id_bits
613 * are used to 'probe' for the correct dev->board_ptr.
615 spin_lock_irqsave(&dev->spinlock, irq_flags);
616 id_bits = das800_ind_read(dev, ID) & 0x3;
617 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
621 if (index == BOARD_DAS800 || index == BOARD_CIODAS800)
623 index = BOARD_DAS800;
626 if (index == BOARD_DAS801 || index == BOARD_CIODAS801)
628 index = BOARD_DAS801;
631 if (index == BOARD_DAS802 || index == BOARD_CIODAS802 ||
632 index == BOARD_CIODAS80216)
634 index = BOARD_DAS802;
637 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
641 dev_dbg(dev->class_dev, "Board model (probed): %s series\n",
642 das800_boards[index].name);
644 return &das800_boards[index];
647 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
649 const struct das800_board *board;
650 struct das800_private *devpriv;
651 struct comedi_subdevice *s;
652 unsigned int irq = it->options[1];
653 unsigned long irq_flags;
656 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
660 ret = comedi_request_region(dev, it->options[0], 0x8);
664 board = das800_probe(dev);
667 dev->board_ptr = board;
668 dev->board_name = board->name;
670 if (irq > 1 && irq <= 7) {
671 ret = request_irq(irq, das800_interrupt, 0, "das800",
677 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
678 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
682 ret = comedi_alloc_subdevices(dev, 3);
686 /* Analog Input subdevice */
687 s = &dev->subdevices[0];
688 dev->read_subdev = s;
689 s->type = COMEDI_SUBD_AI;
690 s->subdev_flags = SDF_READABLE | SDF_GROUND;
692 s->maxdata = (1 << board->resolution) - 1;
693 s->range_table = board->ai_range;
694 s->insn_read = das800_ai_insn_read;
696 s->subdev_flags |= SDF_CMD_READ;
698 s->do_cmdtest = das800_ai_do_cmdtest;
699 s->do_cmd = das800_ai_do_cmd;
700 s->cancel = das800_cancel;
703 /* Digital Input subdevice */
704 s = &dev->subdevices[1];
705 s->type = COMEDI_SUBD_DI;
706 s->subdev_flags = SDF_READABLE;
709 s->range_table = &range_digital;
710 s->insn_bits = das800_di_insn_bits;
712 /* Digital Output subdevice */
713 s = &dev->subdevices[2];
714 s->type = COMEDI_SUBD_DO;
715 s->subdev_flags = SDF_WRITABLE;
718 s->range_table = &range_digital;
719 s->insn_bits = das800_do_insn_bits;
723 /* initialize digital out channels */
724 spin_lock_irqsave(&dev->spinlock, irq_flags);
725 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
726 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
731 static struct comedi_driver driver_das800 = {
732 .driver_name = "das800",
733 .module = THIS_MODULE,
734 .attach = das800_attach,
735 .detach = comedi_legacy_detach,
736 .num_names = ARRAY_SIZE(das800_boards),
737 .board_name = &das800_boards[0].name,
738 .offset = sizeof(struct das800_board),
740 module_comedi_driver(driver_das800);
742 MODULE_AUTHOR("Comedi https://www.comedi.org");
743 MODULE_DESCRIPTION("Comedi low-level driver");
744 MODULE_LICENSE("GPL");