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.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 ************************************************************************
27 Description: Keithley Metrabyte DAS800 (& compatibles)
28 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
29 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
31 [Measurement Computing] CIO-DAS800 (cio-das800),
32 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
33 CIO-DAS802/16 (cio-das802/16)
34 Status: works, cio-das802/16 untested - email me if you have tested it
36 Configuration options:
37 [0] - I/O port base address
38 [1] - IRQ (optional, required for timed or externally triggered conversions)
41 IRQ can be omitted, although the cmd interface will not work without it.
43 All entries in the channel/gain list must use the same gain and be
44 consecutive channels counting upwards in channel number (these are
45 hardware limitations.)
47 I've never tested the gain setting stuff since I only have a
48 DAS-800 board with fixed gain.
50 The cio-das802/16 does not have a fifo-empty status bit! Therefore
51 only fifo-half-full transfers are possible with this card.
55 cmd triggers supported:
56 start_src: TRIG_NOW | TRIG_EXT
57 scan_begin_src: TRIG_FOLLOW
58 scan_end_src: TRIG_COUNT
59 convert_src: TRIG_TIMER | TRIG_EXT
60 stop_src: TRIG_NONE | TRIG_COUNT
65 #include <linux/interrupt.h>
66 #include "../comedidev.h"
68 #include <linux/ioport.h>
69 #include <linux/delay.h>
72 #include "comedi_fc.h"
75 #define TIMER_BASE 1000
76 #define N_CHAN_AI 8 /* number of analog input channels */
78 /* Registers for the das800 */
81 #define FIFO_EMPTY 0x1
84 #define DAS800_CONTROL1 2
85 #define CONTROL1_INTE 0x8
86 #define DAS800_CONV_CONTROL 2
92 #define CONV_HCEN 0x80
93 #define DAS800_SCAN_LIMITS 2
94 #define DAS800_STATUS 2
98 #define CIO_FFOV 0x8 /* cio-das802/16 fifo overflow */
99 #define CIO_ENHF 0x90 /* cio-das802/16 fifo half full int ena */
100 #define CONTROL1 0x80
101 #define CONV_CONTROL 0xa0
102 #define SCAN_LIMITS 0xc0
104 #define DAS800_8254 4
105 #define DAS800_STATUS2 7
106 #define STATUS2_HCEN 0x80
107 #define STATUS2_INTE 0X20
110 #define DAS802_16_HALF_FIFO_SZ 128
112 struct das800_board {
115 const struct comedi_lrange *ai_range;
119 static const struct comedi_lrange range_das801_ai = {
133 static const struct comedi_lrange range_cio_das801_ai = {
147 static const struct comedi_lrange range_das802_ai = {
161 static const struct comedi_lrange range_das80216_ai = {
174 enum das800_boardinfo {
184 static const struct das800_board das800_boards[] = {
188 .ai_range = &range_bipolar5,
191 [BOARD_CIODAS800] = {
192 .name = "cio-das800",
194 .ai_range = &range_bipolar5,
200 .ai_range = &range_das801_ai,
203 [BOARD_CIODAS801] = {
204 .name = "cio-das801",
206 .ai_range = &range_cio_das801_ai,
212 .ai_range = &range_das802_ai,
215 [BOARD_CIODAS802] = {
216 .name = "cio-das802",
218 .ai_range = &range_das802_ai,
221 [BOARD_CIODAS80216] = {
222 .name = "cio-das802/16",
224 .ai_range = &range_das80216_ai,
229 struct das800_private {
230 unsigned int count; /* number of data points left to be taken */
231 unsigned int divisor1; /* counter 1 value for timed conversions */
232 unsigned int divisor2; /* counter 2 value for timed conversions */
233 unsigned int do_bits; /* digital output bits */
234 bool forever; /* flag that we should take data forever */
237 static void das800_ind_write(struct comedi_device *dev,
238 unsigned val, unsigned reg)
241 * Select dev->iobase + 2 to be desired register
242 * then write to that register.
244 outb(reg, dev->iobase + DAS800_GAIN);
245 outb(val, dev->iobase + 2);
248 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
251 * Select dev->iobase + 7 to be desired register
252 * then read from that register.
254 outb(reg, dev->iobase + DAS800_GAIN);
255 return inb(dev->iobase + 7);
258 static void das800_enable(struct comedi_device *dev)
260 const struct das800_board *thisboard = comedi_board(dev);
261 struct das800_private *devpriv = dev->private;
262 unsigned long irq_flags;
264 spin_lock_irqsave(&dev->spinlock, irq_flags);
265 /* enable fifo-half full interrupts for cio-das802/16 */
266 if (thisboard->resolution == 16)
267 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
268 /* enable hardware triggering */
269 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
270 /* enable card's interrupt */
271 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
272 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
275 static void das800_disable(struct comedi_device *dev)
277 unsigned long irq_flags;
279 spin_lock_irqsave(&dev->spinlock, irq_flags);
280 /* disable hardware triggering of conversions */
281 das800_ind_write(dev, 0x0, CONV_CONTROL);
282 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
285 static int das800_set_frequency(struct comedi_device *dev)
287 struct das800_private *devpriv = dev->private;
290 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
292 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
300 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
302 struct das800_private *devpriv = dev->private;
304 devpriv->forever = false;
310 static int das800_ai_do_cmdtest(struct comedi_device *dev,
311 struct comedi_subdevice *s,
312 struct comedi_cmd *cmd)
314 const struct das800_board *thisboard = comedi_board(dev);
315 struct das800_private *devpriv = dev->private;
318 /* Step 1 : check if triggers are trivially valid */
320 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
321 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
322 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
323 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
324 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
329 /* Step 2a : make sure trigger sources are unique */
331 err |= cfc_check_trigger_is_unique(cmd->start_src);
332 err |= cfc_check_trigger_is_unique(cmd->convert_src);
333 err |= cfc_check_trigger_is_unique(cmd->stop_src);
335 /* Step 2b : and mutually compatible */
340 /* Step 3: check if arguments are trivially valid */
342 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
344 if (cmd->convert_src == TRIG_TIMER)
345 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
346 thisboard->ai_speed);
348 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
349 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
351 if (cmd->stop_src == TRIG_COUNT)
352 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
354 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
359 /* step 4: fix up any arguments */
361 if (cmd->convert_src == TRIG_TIMER) {
362 int tmp = cmd->convert_arg;
364 /* calculate counter values that give desired timing */
365 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
369 cmd->flags & TRIG_ROUND_MASK);
370 if (tmp != cmd->convert_arg)
377 /* check channel/gain list against card's limitations */
379 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
380 unsigned int range = CR_RANGE(cmd->chanlist[0]);
384 for (i = 1; i < cmd->chanlist_len; i++) {
385 next = cmd->chanlist[i];
386 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
387 dev_err(dev->class_dev,
388 "chanlist must be consecutive, counting upwards\n");
391 if (CR_RANGE(next) != range) {
392 dev_err(dev->class_dev,
393 "chanlist must all have the same gain\n");
405 static int das800_ai_do_cmd(struct comedi_device *dev,
406 struct comedi_subdevice *s)
408 const struct das800_board *thisboard = comedi_board(dev);
409 struct das800_private *devpriv = dev->private;
410 struct comedi_async *async = s->async;
411 unsigned int gain = CR_RANGE(async->cmd.chanlist[0]);
412 unsigned int start_chan = CR_CHAN(async->cmd.chanlist[0]);
413 unsigned int end_chan = (start_chan + async->cmd.chanlist_len - 1) % 8;
414 unsigned int scan_chans = (end_chan << 3) | start_chan;
416 unsigned long irq_flags;
420 spin_lock_irqsave(&dev->spinlock, irq_flags);
421 /* set scan limits */
422 das800_ind_write(dev, scan_chans, SCAN_LIMITS);
423 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
426 if (thisboard->resolution == 12 && gain > 0)
429 outb(gain, dev->iobase + DAS800_GAIN);
431 switch (async->cmd.stop_src) {
433 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
434 devpriv->forever = false;
437 devpriv->forever = true;
444 /* enable auto channel scan, send interrupts on end of conversion
445 * and set clock source to internal or external
448 conv_bits |= EACS | IEOC;
449 if (async->cmd.start_src == TRIG_EXT)
451 switch (async->cmd.convert_src) {
453 conv_bits |= CASC | ITE;
454 /* set conversion frequency */
455 if (das800_set_frequency(dev) < 0) {
456 comedi_error(dev, "Error setting up counters");
466 spin_lock_irqsave(&dev->spinlock, irq_flags);
467 das800_ind_write(dev, conv_bits, CONV_CONTROL);
468 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
475 static unsigned int das800_ai_get_sample(struct comedi_device *dev)
477 unsigned int lsb = inb(dev->iobase + DAS800_LSB);
478 unsigned int msb = inb(dev->iobase + DAS800_MSB);
480 return (msb << 8) | lsb;
483 static irqreturn_t das800_interrupt(int irq, void *d)
485 struct comedi_device *dev = d;
486 struct das800_private *devpriv = dev->private;
487 struct comedi_subdevice *s = dev->read_subdev;
488 struct comedi_async *async = s ? s->async : NULL;
489 unsigned long irq_flags;
496 status = inb(dev->iobase + DAS800_STATUS);
502 spin_lock_irqsave(&dev->spinlock, irq_flags);
503 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
505 * Don't release spinlock yet since we want to make sure
506 * no one else disables hardware conversions.
509 /* if hardware conversions are not enabled, then quit */
511 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
515 for (i = 0; i < DAS802_16_HALF_FIFO_SZ; i++) {
516 val = das800_ai_get_sample(dev);
517 if (s->maxdata == 0x0fff) {
518 fifo_empty = !!(val & FIFO_EMPTY);
519 fifo_overflow = !!(val & FIFO_OVF);
521 /* cio-das802/16 has no fifo empty status bit */
523 fifo_overflow = !!(inb(dev->iobase + DAS800_GAIN) &
526 if (fifo_empty || fifo_overflow)
529 if (s->maxdata == 0x0fff)
530 val >>= 4; /* 12-bit sample */
532 /* if there are more data points to collect */
533 if (devpriv->count > 0 || devpriv->forever) {
534 /* write data point to buffer */
535 cfc_write_to_buffer(s, val & s->maxdata);
539 async->events |= COMEDI_CB_BLOCK;
542 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
543 das800_cancel(dev, s);
544 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
545 comedi_event(dev, s);
550 if (devpriv->count > 0 || devpriv->forever) {
551 /* Re-enable card's interrupt.
552 * We already have spinlock, so indirect addressing is safe */
553 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
555 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
557 /* otherwise, stop taking data */
558 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
560 async->events |= COMEDI_CB_EOA;
562 comedi_event(dev, s);
567 static int das800_wait_for_conv(struct comedi_device *dev, int timeout)
571 for (i = 0; i < timeout; i++) {
572 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
578 static int das800_ai_insn_read(struct comedi_device *dev,
579 struct comedi_subdevice *s,
580 struct comedi_insn *insn,
583 struct das800_private *devpriv = dev->private;
584 unsigned int chan = CR_CHAN(insn->chanspec);
585 unsigned int range = CR_RANGE(insn->chanspec);
586 unsigned long irq_flags;
593 /* set multiplexer */
594 spin_lock_irqsave(&dev->spinlock, irq_flags);
595 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
596 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
598 /* set gain / range */
599 if (s->maxdata == 0x0fff && range)
602 outb(range, dev->iobase + DAS800_GAIN);
606 for (i = 0; i < insn->n; i++) {
607 /* trigger conversion */
608 outb_p(0, dev->iobase + DAS800_MSB);
610 ret = das800_wait_for_conv(dev, 1000);
614 val = das800_ai_get_sample(dev);
615 if (s->maxdata == 0x0fff)
616 val >>= 4; /* 12-bit sample */
617 data[i] = val & s->maxdata;
623 static int das800_di_insn_bits(struct comedi_device *dev,
624 struct comedi_subdevice *s,
625 struct comedi_insn *insn,
628 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
633 static int das800_do_insn_bits(struct comedi_device *dev,
634 struct comedi_subdevice *s,
635 struct comedi_insn *insn,
638 struct das800_private *devpriv = dev->private;
639 unsigned int mask = data[0];
640 unsigned int bits = data[1];
641 unsigned long irq_flags;
645 s->state |= (bits & mask);
646 devpriv->do_bits = s->state << 4;
648 spin_lock_irqsave(&dev->spinlock, irq_flags);
649 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
651 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
659 static int das800_probe(struct comedi_device *dev)
661 const struct das800_board *thisboard = comedi_board(dev);
662 int board = thisboard ? thisboard - das800_boards : -EINVAL;
664 unsigned long irq_flags;
666 spin_lock_irqsave(&dev->spinlock, irq_flags);
667 id_bits = das800_ind_read(dev, ID) & 0x3;
668 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
672 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
674 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
675 board = BOARD_DAS800;
678 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
680 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
681 board = BOARD_DAS801;
684 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
685 board == BOARD_CIODAS80216)
687 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
688 board = BOARD_DAS802;
691 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
699 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
701 const struct das800_board *thisboard = comedi_board(dev);
702 struct das800_private *devpriv;
703 struct comedi_subdevice *s;
704 unsigned int irq = it->options[1];
705 unsigned long irq_flags;
709 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
712 dev->private = devpriv;
714 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
718 board = das800_probe(dev);
720 dev_dbg(dev->class_dev, "unable to determine board type\n");
723 dev->board_ptr = das800_boards + board;
724 thisboard = comedi_board(dev);
725 dev->board_name = thisboard->name;
727 if (irq > 1 && irq <= 7) {
728 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
734 ret = comedi_alloc_subdevices(dev, 3);
738 /* Analog Input subdevice */
739 s = &dev->subdevices[0];
740 dev->read_subdev = s;
741 s->type = COMEDI_SUBD_AI;
742 s->subdev_flags = SDF_READABLE | SDF_GROUND;
744 s->maxdata = (1 << thisboard->resolution) - 1;
745 s->range_table = thisboard->ai_range;
746 s->insn_read = das800_ai_insn_read;
748 s->subdev_flags |= SDF_CMD_READ;
750 s->do_cmdtest = das800_ai_do_cmdtest;
751 s->do_cmd = das800_ai_do_cmd;
752 s->cancel = das800_cancel;
755 /* Digital Input subdevice */
756 s = &dev->subdevices[1];
757 s->type = COMEDI_SUBD_DI;
758 s->subdev_flags = SDF_READABLE;
761 s->range_table = &range_digital;
762 s->insn_bits = das800_di_insn_bits;
764 /* Digital Output subdevice */
765 s = &dev->subdevices[2];
766 s->type = COMEDI_SUBD_DO;
767 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
770 s->range_table = &range_digital;
771 s->insn_bits = das800_do_insn_bits;
775 /* initialize digital out channels */
776 spin_lock_irqsave(&dev->spinlock, irq_flags);
777 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
778 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
783 static struct comedi_driver driver_das800 = {
784 .driver_name = "das800",
785 .module = THIS_MODULE,
786 .attach = das800_attach,
787 .detach = comedi_legacy_detach,
788 .num_names = ARRAY_SIZE(das800_boards),
789 .board_name = &das800_boards[0].name,
790 .offset = sizeof(struct das800_board),
792 module_comedi_driver(driver_das800);
794 MODULE_AUTHOR("Comedi http://www.comedi.org");
795 MODULE_DESCRIPTION("Comedi low-level driver");
796 MODULE_LICENSE("GPL");