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/module.h>
60 #include <linux/interrupt.h>
61 #include "../comedidev.h"
63 #include <linux/delay.h>
66 #include "comedi_fc.h"
68 #define N_CHAN_AI 8 /* number of analog input channels */
70 /* Registers for the das800 */
73 #define FIFO_EMPTY 0x1
76 #define DAS800_CONTROL1 2
77 #define CONTROL1_INTE 0x8
78 #define DAS800_CONV_CONTROL 2
84 #define CONV_HCEN 0x80
85 #define DAS800_SCAN_LIMITS 2
86 #define DAS800_STATUS 2
90 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
91 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
93 #define CONV_CONTROL 0xa0
94 #define SCAN_LIMITS 0xc0
97 #define DAS800_STATUS2 7
98 #define STATUS2_HCEN 0x80
99 #define STATUS2_INTE 0X20
102 #define DAS802_16_HALF_FIFO_SZ 128
104 struct das800_board {
107 const struct comedi_lrange *ai_range;
111 static const struct comedi_lrange range_das801_ai = {
125 static const struct comedi_lrange range_cio_das801_ai = {
139 static const struct comedi_lrange range_das802_ai = {
153 static const struct comedi_lrange range_das80216_ai = {
166 enum das800_boardinfo {
176 static const struct das800_board das800_boards[] = {
180 .ai_range = &range_bipolar5,
183 [BOARD_CIODAS800] = {
184 .name = "cio-das800",
186 .ai_range = &range_bipolar5,
192 .ai_range = &range_das801_ai,
195 [BOARD_CIODAS801] = {
196 .name = "cio-das801",
198 .ai_range = &range_cio_das801_ai,
204 .ai_range = &range_das802_ai,
207 [BOARD_CIODAS802] = {
208 .name = "cio-das802",
210 .ai_range = &range_das802_ai,
213 [BOARD_CIODAS80216] = {
214 .name = "cio-das802/16",
216 .ai_range = &range_das80216_ai,
221 struct das800_private {
222 unsigned int divisor1; /* counter 1 value for timed conversions */
223 unsigned int divisor2; /* counter 2 value for timed conversions */
224 unsigned int do_bits; /* digital output bits */
227 static void das800_ind_write(struct comedi_device *dev,
228 unsigned val, unsigned reg)
231 * Select dev->iobase + 2 to be desired register
232 * then write to that register.
234 outb(reg, dev->iobase + DAS800_GAIN);
235 outb(val, dev->iobase + 2);
238 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
241 * Select dev->iobase + 7 to be desired register
242 * then read from that register.
244 outb(reg, dev->iobase + DAS800_GAIN);
245 return inb(dev->iobase + 7);
248 static void das800_enable(struct comedi_device *dev)
250 const struct das800_board *thisboard = dev->board_ptr;
251 struct das800_private *devpriv = dev->private;
252 unsigned long irq_flags;
254 spin_lock_irqsave(&dev->spinlock, irq_flags);
255 /* enable fifo-half full interrupts for cio-das802/16 */
256 if (thisboard->resolution == 16)
257 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
258 /* enable hardware triggering */
259 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
260 /* enable card's interrupt */
261 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
262 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
265 static void das800_disable(struct comedi_device *dev)
267 unsigned long irq_flags;
269 spin_lock_irqsave(&dev->spinlock, irq_flags);
270 /* disable hardware triggering of conversions */
271 das800_ind_write(dev, 0x0, CONV_CONTROL);
272 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
275 static void das800_set_frequency(struct comedi_device *dev)
277 struct das800_private *devpriv = dev->private;
278 unsigned long timer_base = dev->iobase + DAS800_8254;
280 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
281 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
282 i8254_write(timer_base, 0, 1, devpriv->divisor1);
283 i8254_write(timer_base, 0, 2, devpriv->divisor2);
286 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
292 static int das800_ai_check_chanlist(struct comedi_device *dev,
293 struct comedi_subdevice *s,
294 struct comedi_cmd *cmd)
296 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
297 unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
300 for (i = 1; i < cmd->chanlist_len; i++) {
301 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
302 unsigned int range = CR_RANGE(cmd->chanlist[i]);
304 if (chan != (chan0 + i) % s->n_chan) {
305 dev_dbg(dev->class_dev,
306 "chanlist must be consecutive, counting upwards\n");
310 if (range != range0) {
311 dev_dbg(dev->class_dev,
312 "chanlist must all have the same gain\n");
320 static int das800_ai_do_cmdtest(struct comedi_device *dev,
321 struct comedi_subdevice *s,
322 struct comedi_cmd *cmd)
324 const struct das800_board *thisboard = dev->board_ptr;
325 struct das800_private *devpriv = dev->private;
329 /* Step 1 : check if triggers are trivially valid */
331 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
332 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
333 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
334 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
335 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
340 /* Step 2a : make sure trigger sources are unique */
342 err |= cfc_check_trigger_is_unique(cmd->start_src);
343 err |= cfc_check_trigger_is_unique(cmd->convert_src);
344 err |= cfc_check_trigger_is_unique(cmd->stop_src);
346 /* Step 2b : and mutually compatible */
351 /* Step 3: check if arguments are trivially valid */
353 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
355 if (cmd->convert_src == TRIG_TIMER)
356 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
357 thisboard->ai_speed);
359 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
360 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
362 if (cmd->stop_src == TRIG_COUNT)
363 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
365 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
370 /* step 4: fix up any arguments */
372 if (cmd->convert_src == TRIG_TIMER) {
373 arg = cmd->convert_arg;
374 i8253_cascade_ns_to_timer(I8254_OSC_BASE_1MHZ,
378 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
384 /* Step 5: check channel list if it exists */
385 if (cmd->chanlist && cmd->chanlist_len > 0)
386 err |= das800_ai_check_chanlist(dev, s, cmd);
394 static int das800_ai_do_cmd(struct comedi_device *dev,
395 struct comedi_subdevice *s)
397 const struct das800_board *thisboard = dev->board_ptr;
398 struct comedi_async *async = s->async;
399 struct comedi_cmd *cmd = &async->cmd;
400 unsigned int gain = CR_RANGE(cmd->chanlist[0]);
401 unsigned int start_chan = CR_CHAN(cmd->chanlist[0]);
402 unsigned int end_chan = (start_chan + cmd->chanlist_len - 1) % 8;
403 unsigned int scan_chans = (end_chan << 3) | start_chan;
405 unsigned long irq_flags;
409 spin_lock_irqsave(&dev->spinlock, irq_flags);
410 /* set scan limits */
411 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
412 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
415 if (thisboard->resolution == 12 && gain > 0)
418 outb(gain, dev->iobase + DAS800_GAIN);
420 /* enable auto channel scan, send interrupts on end of conversion
421 * and set clock source to internal or external
424 conv_bits |= EACS | IEOC;
425 if (cmd->start_src == TRIG_EXT)
427 if (cmd->convert_src == TRIG_TIMER) {
428 conv_bits |= CASC | ITE;
429 /* set conversion frequency */
430 das800_set_frequency(dev);
433 spin_lock_irqsave(&dev->spinlock, irq_flags);
434 das800_ind_write(dev, conv_bits, CONV_CONTROL);
435 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
441 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
443 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
444 unsigned int msb = inb(dev->iobase + DAS800_MSB);
446 return (msb << 8) | lsb;
449 static irqreturn_t das800_interrupt(int irq, void *d)
451 struct comedi_device *dev = d;
452 struct das800_private *devpriv = dev->private;
453 struct comedi_subdevice *s = dev->read_subdev;
454 struct comedi_async *async;
455 struct comedi_cmd *cmd;
456 unsigned long irq_flags;
463 status = inb(dev->iobase + DAS800_STATUS);
472 spin_lock_irqsave(&dev->spinlock, irq_flags);
473 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
475 * Don't release spinlock yet since we want to make sure
476 * no one else disables hardware conversions.
479 /* if hardware conversions are not enabled, then quit */
481 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
485 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
486 val = das800_ai_get_sample(dev);
487 if (s->maxdata == 0x0fff) {
488 fifo_empty = !!(val & FIFO_EMPTY);
489 fifo_overflow = !!(val & FIFO_OVF);
491 /* cio-das802/16 has no fifo empty status bit */
493 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
496 if (fifo_empty || fifo_overflow)
499 if (s->maxdata == 0x0fff)
500 val >>= 4; /* 12-bit sample */
503 comedi_buf_write_samples(s, &val, 1);
505 if (cmd->stop_src == TRIG_COUNT &&
506 async->scans_done >= cmd->stop_arg) {
507 async->events |= COMEDI_CB_EOA;
513 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
514 async->events |= COMEDI_CB_ERROR;
515 comedi_handle_events(dev, s);
519 if (!(async->events & COMEDI_CB_CANCEL_MASK)) {
521 * Re-enable card's interrupt.
522 * We already have spinlock, so indirect addressing is safe
524 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
526 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
528 /* otherwise, stop taking data */
529 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
532 comedi_handle_events(dev, s);
536 static int das800_ai_eoc(struct comedi_device *dev,
537 struct comedi_subdevice *s,
538 struct comedi_insn *insn,
539 unsigned long context)
543 status = inb(dev->iobase + DAS800_STATUS);
544 if ((status & BUSY) == 0)
549 static int das800_ai_insn_read(struct comedi_device *dev,
550 struct comedi_subdevice *s,
551 struct comedi_insn *insn,
554 struct das800_private *devpriv = dev->private;
555 unsigned int chan = CR_CHAN(insn->chanspec);
556 unsigned int range = CR_RANGE(insn->chanspec);
557 unsigned long irq_flags;
564 /* set multiplexer */
565 spin_lock_irqsave(&dev->spinlock, irq_flags);
566 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
567 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
569 /* set gain / range */
570 if (s->maxdata == 0x0fff && range)
573 outb(range, dev->iobase + DAS800_GAIN);
577 for (i = 0; i < insn->n; i++) {
578 /* trigger conversion */
579 outb_p(0, dev->iobase + DAS800_MSB);
581 ret = comedi_timeout(dev, s, insn, das800_ai_eoc, 0);
585 val = das800_ai_get_sample(dev);
586 if (s->maxdata == 0x0fff)
587 val >>= 4; /* 12-bit sample */
588 data[i] = val & s->maxdata;
594 static int das800_di_insn_bits(struct comedi_device *dev,
595 struct comedi_subdevice *s,
596 struct comedi_insn *insn,
599 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
604 static int das800_do_insn_bits(struct comedi_device *dev,
605 struct comedi_subdevice *s,
606 struct comedi_insn *insn,
609 struct das800_private *devpriv = dev->private;
610 unsigned long irq_flags;
612 if (comedi_dio_update_state(s, data)) {
613 devpriv->do_bits = s->state << 4;
615 spin_lock_irqsave(&dev->spinlock, irq_flags);
616 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
618 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
626 static int das800_probe(struct comedi_device *dev)
628 const struct das800_board *thisboard = dev->board_ptr;
629 int board = thisboard ? thisboard - das800_boards : -EINVAL;
631 unsigned long irq_flags;
633 spin_lock_irqsave(&dev->spinlock, irq_flags);
634 id_bits = das800_ind_read(dev, ID) & 0x3;
635 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
639 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
641 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
642 board = BOARD_DAS800;
645 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
647 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
648 board = BOARD_DAS801;
651 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
652 board == BOARD_CIODAS80216)
654 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
655 board = BOARD_DAS802;
658 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
666 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
668 const struct das800_board *thisboard;
669 struct das800_private *devpriv;
670 struct comedi_subdevice *s;
671 unsigned int irq = it->options[1];
672 unsigned long irq_flags;
676 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
680 ret = comedi_request_region(dev, it->options[0], 0x8);
684 board = das800_probe(dev);
686 dev_dbg(dev->class_dev, "unable to determine board type\n");
689 dev->board_ptr = das800_boards + board;
690 thisboard = dev->board_ptr;
691 dev->board_name = thisboard->name;
693 if (irq > 1 && irq <= 7) {
694 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
700 ret = comedi_alloc_subdevices(dev, 3);
704 /* Analog Input subdevice */
705 s = &dev->subdevices[0];
706 dev->read_subdev = s;
707 s->type = COMEDI_SUBD_AI;
708 s->subdev_flags = SDF_READABLE | SDF_GROUND;
710 s->maxdata = (1 << thisboard->resolution) - 1;
711 s->range_table = thisboard->ai_range;
712 s->insn_read = das800_ai_insn_read;
714 s->subdev_flags |= SDF_CMD_READ;
716 s->do_cmdtest = das800_ai_do_cmdtest;
717 s->do_cmd = das800_ai_do_cmd;
718 s->cancel = das800_cancel;
721 /* Digital Input subdevice */
722 s = &dev->subdevices[1];
723 s->type = COMEDI_SUBD_DI;
724 s->subdev_flags = SDF_READABLE;
727 s->range_table = &range_digital;
728 s->insn_bits = das800_di_insn_bits;
730 /* Digital Output subdevice */
731 s = &dev->subdevices[2];
732 s->type = COMEDI_SUBD_DO;
733 s->subdev_flags = SDF_WRITABLE;
736 s->range_table = &range_digital;
737 s->insn_bits = das800_do_insn_bits;
741 /* initialize digital out channels */
742 spin_lock_irqsave(&dev->spinlock, irq_flags);
743 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
744 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
749 static struct comedi_driver driver_das800 = {
750 .driver_name = "das800",
751 .module = THIS_MODULE,
752 .attach = das800_attach,
753 .detach = comedi_legacy_detach,
754 .num_names = ARRAY_SIZE(das800_boards),
755 .board_name = &das800_boards[0].name,
756 .offset = sizeof(struct das800_board),
758 module_comedi_driver(driver_das800);
760 MODULE_AUTHOR("Comedi http://www.comedi.org");
761 MODULE_DESCRIPTION("Comedi low-level driver");
762 MODULE_LICENSE("GPL");