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 *thisboard = 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 (thisboard->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 *thisboard = 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,
338 thisboard->ai_speed);
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 *thisboard = 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 (thisboard->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 int das800_probe(struct comedi_device *dev)
609 const struct das800_board *thisboard = dev->board_ptr;
610 int board = thisboard ? thisboard - das800_boards : -EINVAL;
612 unsigned long irq_flags;
614 spin_lock_irqsave(&dev->spinlock, irq_flags);
615 id_bits = das800_ind_read(dev, ID) & 0x3;
616 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
620 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
622 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
623 board = BOARD_DAS800;
626 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
628 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
629 board = BOARD_DAS801;
632 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
633 board == BOARD_CIODAS80216)
635 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
636 board = BOARD_DAS802;
639 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
647 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
649 const struct das800_board *thisboard;
650 struct das800_private *devpriv;
651 struct comedi_subdevice *s;
652 unsigned int irq = it->options[1];
653 unsigned long irq_flags;
657 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
661 ret = comedi_request_region(dev, it->options[0], 0x8);
665 board = das800_probe(dev);
667 dev_dbg(dev->class_dev, "unable to determine board type\n");
670 dev->board_ptr = das800_boards + board;
671 thisboard = dev->board_ptr;
672 dev->board_name = thisboard->name;
674 if (irq > 1 && irq <= 7) {
675 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
681 dev->pacer = comedi_8254_init(dev->iobase + DAS800_8254,
682 I8254_OSC_BASE_1MHZ, I8254_IO8, 0);
686 ret = comedi_alloc_subdevices(dev, 3);
690 /* Analog Input subdevice */
691 s = &dev->subdevices[0];
692 dev->read_subdev = s;
693 s->type = COMEDI_SUBD_AI;
694 s->subdev_flags = SDF_READABLE | SDF_GROUND;
696 s->maxdata = (1 << thisboard->resolution) - 1;
697 s->range_table = thisboard->ai_range;
698 s->insn_read = das800_ai_insn_read;
700 s->subdev_flags |= SDF_CMD_READ;
702 s->do_cmdtest = das800_ai_do_cmdtest;
703 s->do_cmd = das800_ai_do_cmd;
704 s->cancel = das800_cancel;
707 /* Digital Input subdevice */
708 s = &dev->subdevices[1];
709 s->type = COMEDI_SUBD_DI;
710 s->subdev_flags = SDF_READABLE;
713 s->range_table = &range_digital;
714 s->insn_bits = das800_di_insn_bits;
716 /* Digital Output subdevice */
717 s = &dev->subdevices[2];
718 s->type = COMEDI_SUBD_DO;
719 s->subdev_flags = SDF_WRITABLE;
722 s->range_table = &range_digital;
723 s->insn_bits = das800_do_insn_bits;
727 /* initialize digital out channels */
728 spin_lock_irqsave(&dev->spinlock, irq_flags);
729 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
730 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
735 static struct comedi_driver driver_das800 = {
736 .driver_name = "das800",
737 .module = THIS_MODULE,
738 .attach = das800_attach,
739 .detach = comedi_legacy_detach,
740 .num_names = ARRAY_SIZE(das800_boards),
741 .board_name = &das800_boards[0].name,
742 .offset = sizeof(struct das800_board),
744 module_comedi_driver(driver_das800);
746 MODULE_AUTHOR("Comedi http://www.comedi.org");
747 MODULE_DESCRIPTION("Comedi low-level driver");
748 MODULE_LICENSE("GPL");