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 /* fifo overflow for cio-das802/16 */
99 #define CIO_ENHF 0x90 /* interrupt fifo half full for cio-das802/16 */
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 struct das800_board {
113 const struct comedi_lrange *ai_range;
117 static const struct comedi_lrange range_das801_ai = {
131 static const struct comedi_lrange range_cio_das801_ai = {
145 static const struct comedi_lrange range_das802_ai = {
159 static const struct comedi_lrange range_das80216_ai = {
172 enum das800_boardinfo {
182 static const struct das800_board das800_boards[] = {
186 .ai_range = &range_bipolar5,
189 [BOARD_CIODAS800] = {
190 .name = "cio-das800",
192 .ai_range = &range_bipolar5,
198 .ai_range = &range_das801_ai,
201 [BOARD_CIODAS801] = {
202 .name = "cio-das801",
204 .ai_range = &range_cio_das801_ai,
210 .ai_range = &range_das802_ai,
213 [BOARD_CIODAS802] = {
214 .name = "cio-das802",
216 .ai_range = &range_das802_ai,
219 [BOARD_CIODAS80216] = {
220 .name = "cio-das802/16",
222 .ai_range = &range_das80216_ai,
227 struct das800_private {
228 unsigned int count; /* number of data points left to be taken */
229 int forever; /* flag that we should take data forever */
230 unsigned int divisor1; /* counter 1 value for timed conversions */
231 unsigned int divisor2; /* counter 2 value for timed conversions */
232 int do_bits; /* digital output bits */
235 static void das800_ind_write(struct comedi_device *dev,
236 unsigned val, unsigned reg)
239 * Select dev->iobase + 2 to be desired register
240 * then write to that register.
242 outb(reg, dev->iobase + DAS800_GAIN);
243 outb(val, dev->iobase + 2);
246 static unsigned das800_ind_read(struct comedi_device *dev, unsigned reg)
249 * Select dev->iobase + 7 to be desired register
250 * then read from that register.
252 outb(reg, dev->iobase + DAS800_GAIN);
253 return inb(dev->iobase + 7);
256 static void das800_enable(struct comedi_device *dev)
258 const struct das800_board *thisboard = comedi_board(dev);
259 struct das800_private *devpriv = dev->private;
260 unsigned long irq_flags;
262 spin_lock_irqsave(&dev->spinlock, irq_flags);
263 /* enable fifo-half full interrupts for cio-das802/16 */
264 if (thisboard->resolution == 16)
265 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
266 /* enable hardware triggering */
267 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
268 /* enable card's interrupt */
269 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
270 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
273 static void das800_disable(struct comedi_device *dev)
275 unsigned long irq_flags;
277 spin_lock_irqsave(&dev->spinlock, irq_flags);
278 /* disable hardware triggering of conversions */
279 das800_ind_write(dev, 0x0, CONV_CONTROL);
280 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
283 static int das800_set_frequency(struct comedi_device *dev)
285 struct das800_private *devpriv = dev->private;
288 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
290 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
298 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
300 struct das800_private *devpriv = dev->private;
302 devpriv->forever = 0;
308 static int das800_ai_do_cmdtest(struct comedi_device *dev,
309 struct comedi_subdevice *s,
310 struct comedi_cmd *cmd)
312 const struct das800_board *thisboard = comedi_board(dev);
313 struct das800_private *devpriv = dev->private;
316 /* Step 1 : check if triggers are trivially valid */
318 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
319 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
320 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
321 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
322 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
327 /* Step 2a : make sure trigger sources are unique */
329 err |= cfc_check_trigger_is_unique(cmd->start_src);
330 err |= cfc_check_trigger_is_unique(cmd->convert_src);
331 err |= cfc_check_trigger_is_unique(cmd->stop_src);
333 /* Step 2b : and mutually compatible */
338 /* Step 3: check if arguments are trivially valid */
340 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
342 if (cmd->convert_src == TRIG_TIMER)
343 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
344 thisboard->ai_speed);
346 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
347 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
349 if (cmd->stop_src == TRIG_COUNT)
350 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
352 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
357 /* step 4: fix up any arguments */
359 if (cmd->convert_src == TRIG_TIMER) {
360 int tmp = cmd->convert_arg;
362 /* calculate counter values that give desired timing */
363 i8253_cascade_ns_to_timer_2div(TIMER_BASE,
367 cmd->flags & TRIG_ROUND_MASK);
368 if (tmp != cmd->convert_arg)
375 /* check channel/gain list against card's limitations */
377 unsigned int chan = CR_CHAN(cmd->chanlist[0]);
378 unsigned int range = CR_RANGE(cmd->chanlist[0]);
382 for (i = 1; i < cmd->chanlist_len; i++) {
383 next = cmd->chanlist[i];
384 if (CR_CHAN(next) != (chan + i) % N_CHAN_AI) {
385 dev_err(dev->class_dev,
386 "chanlist must be consecutive, counting upwards\n");
389 if (CR_RANGE(next) != range) {
390 dev_err(dev->class_dev,
391 "chanlist must all have the same gain\n");
403 static int das800_ai_do_cmd(struct comedi_device *dev,
404 struct comedi_subdevice *s)
406 const struct das800_board *thisboard = comedi_board(dev);
407 struct das800_private *devpriv = dev->private;
408 int startChan, endChan, scan, gain;
410 unsigned long irq_flags;
411 struct comedi_async *async = s->async;
415 /* set channel scan limits */
416 startChan = CR_CHAN(async->cmd.chanlist[0]);
417 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
418 scan = (endChan << 3) | startChan;
420 spin_lock_irqsave(&dev->spinlock, irq_flags);
421 /* set scan limits */
422 das800_ind_write(dev, scan, SCAN_LIMITS);
423 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
426 gain = CR_RANGE(async->cmd.chanlist[0]);
427 if (thisboard->resolution == 12 && gain > 0)
430 outb(gain, dev->iobase + DAS800_GAIN);
432 switch (async->cmd.stop_src) {
434 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
435 devpriv->forever = 0;
438 devpriv->forever = 1;
445 /* enable auto channel scan, send interrupts on end of conversion
446 * and set clock source to internal or external
449 conv_bits |= EACS | IEOC;
450 if (async->cmd.start_src == TRIG_EXT)
452 switch (async->cmd.convert_src) {
454 conv_bits |= CASC | ITE;
455 /* set conversion frequency */
456 if (das800_set_frequency(dev) < 0) {
457 comedi_error(dev, "Error setting up counters");
467 spin_lock_irqsave(&dev->spinlock, irq_flags);
468 das800_ind_write(dev, conv_bits, CONV_CONTROL);
469 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
476 static irqreturn_t das800_interrupt(int irq, void *d)
478 short i; /* loop index */
480 struct comedi_device *dev = d;
481 const struct das800_board *thisboard = comedi_board(dev);
482 struct das800_private *devpriv = dev->private;
483 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
484 struct comedi_async *async;
486 unsigned long irq_flags;
487 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
490 int fifo_overflow = 0;
492 status = inb(dev->iobase + DAS800_STATUS);
493 /* if interrupt was not generated by board or driver not attached, quit */
496 if (!(dev->attached))
499 /* wait until here to initialize async, since we will get null dereference
500 * if interrupt occurs before driver is fully attached!
504 /* if hardware conversions are not enabled, then quit */
505 spin_lock_irqsave(&dev->spinlock, irq_flags);
506 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
507 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
509 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
513 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
514 for (i = 0; i < max_loops; i++) {
515 /* read 16 bits from dev->iobase and dev->iobase + 1 */
516 dataPoint = inb(dev->iobase + DAS800_LSB);
517 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
518 if (thisboard->resolution == 12) {
519 fifo_empty = dataPoint & FIFO_EMPTY;
520 fifo_overflow = dataPoint & FIFO_OVF;
524 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
528 /* strip off extraneous bits for 12 bit cards */
529 if (thisboard->resolution == 12)
530 dataPoint = (dataPoint >> 4) & 0xfff;
531 /* if there are more data points to collect */
532 if (devpriv->count > 0 || devpriv->forever == 1) {
533 /* write data point to buffer */
534 cfc_write_to_buffer(s, dataPoint);
535 if (devpriv->count > 0)
539 async->events |= COMEDI_CB_BLOCK;
540 /* check for fifo overflow */
541 if (thisboard->resolution == 12) {
542 fifo_overflow = dataPoint & FIFO_OVF;
543 /* else cio-das802/16 */
545 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
548 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
549 comedi_error(dev, "DAS800 FIFO overflow");
550 das800_cancel(dev, s);
551 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
552 comedi_event(dev, s);
556 if (devpriv->count > 0 || devpriv->forever == 1) {
557 /* Re-enable card's interrupt.
558 * We already have spinlock, so indirect addressing is safe */
559 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
561 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
562 /* otherwise, stop taking data */
564 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
566 async->events |= COMEDI_CB_EOA;
568 comedi_event(dev, s);
573 static int das800_ai_insn_read(struct comedi_device *dev,
574 struct comedi_subdevice *s,
575 struct comedi_insn *insn,
578 const struct das800_board *thisboard = comedi_board(dev);
579 struct das800_private *devpriv = dev->private;
585 unsigned long irq_flags;
589 /* set multiplexer */
590 chan = CR_CHAN(insn->chanspec);
592 spin_lock_irqsave(&dev->spinlock, irq_flags);
593 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
594 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
596 /* set gain / range */
597 range = CR_RANGE(insn->chanspec);
598 if (thisboard->resolution == 12 && range)
601 outb(range, dev->iobase + DAS800_GAIN);
605 for (n = 0; n < insn->n; n++) {
606 /* trigger conversion */
607 outb_p(0, dev->iobase + DAS800_MSB);
609 for (i = 0; i < timeout; i++) {
610 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
614 comedi_error(dev, "timeout");
617 lsb = inb(dev->iobase + DAS800_LSB);
618 msb = inb(dev->iobase + DAS800_MSB);
619 if (thisboard->resolution == 12) {
620 data[n] = (lsb >> 4) & 0xff;
621 data[n] |= (msb << 4);
623 data[n] = (msb << 8) | lsb;
630 static int das800_di_insn_bits(struct comedi_device *dev,
631 struct comedi_subdevice *s,
632 struct comedi_insn *insn,
635 data[1] = (inb(dev->iobase + DAS800_STATUS) >> 4) & 0x7;
640 static int das800_do_insn_bits(struct comedi_device *dev,
641 struct comedi_subdevice *s,
642 struct comedi_insn *insn,
645 struct das800_private *devpriv = dev->private;
646 unsigned int mask = data[0];
647 unsigned int bits = data[1];
648 unsigned long irq_flags;
652 s->state |= (bits & mask);
653 devpriv->do_bits = s->state << 4;
655 spin_lock_irqsave(&dev->spinlock, irq_flags);
656 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
658 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
666 static int das800_probe(struct comedi_device *dev)
668 const struct das800_board *thisboard = comedi_board(dev);
669 int board = thisboard ? thisboard - das800_boards : -EINVAL;
671 unsigned long irq_flags;
673 spin_lock_irqsave(&dev->spinlock, irq_flags);
674 id_bits = das800_ind_read(dev, ID) & 0x3;
675 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
679 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
681 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
682 board = BOARD_DAS800;
685 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
687 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
688 board = BOARD_DAS801;
691 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
692 board == BOARD_CIODAS80216)
694 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
695 board = BOARD_DAS802;
698 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
706 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
708 const struct das800_board *thisboard = comedi_board(dev);
709 struct das800_private *devpriv;
710 struct comedi_subdevice *s;
711 unsigned int irq = it->options[1];
712 unsigned long irq_flags;
716 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
719 dev->private = devpriv;
721 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
725 board = das800_probe(dev);
727 dev_dbg(dev->class_dev, "unable to determine board type\n");
730 dev->board_ptr = das800_boards + board;
731 thisboard = comedi_board(dev);
732 dev->board_name = thisboard->name;
734 if (irq > 1 && irq <= 7) {
735 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
741 ret = comedi_alloc_subdevices(dev, 3);
745 /* Analog Input subdevice */
746 s = &dev->subdevices[0];
747 dev->read_subdev = s;
748 s->type = COMEDI_SUBD_AI;
749 s->subdev_flags = SDF_READABLE | SDF_GROUND;
751 s->maxdata = (1 << thisboard->resolution) - 1;
752 s->range_table = thisboard->ai_range;
753 s->insn_read = das800_ai_insn_read;
755 s->subdev_flags |= SDF_CMD_READ;
757 s->do_cmdtest = das800_ai_do_cmdtest;
758 s->do_cmd = das800_ai_do_cmd;
759 s->cancel = das800_cancel;
762 /* Digital Input subdevice */
763 s = &dev->subdevices[1];
764 s->type = COMEDI_SUBD_DI;
765 s->subdev_flags = SDF_READABLE;
768 s->range_table = &range_digital;
769 s->insn_bits = das800_di_insn_bits;
771 /* Digital Output subdevice */
772 s = &dev->subdevices[2];
773 s->type = COMEDI_SUBD_DO;
774 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
777 s->range_table = &range_digital;
778 s->insn_bits = das800_do_insn_bits;
782 /* initialize digital out channels */
783 spin_lock_irqsave(&dev->spinlock, irq_flags);
784 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
785 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
790 static struct comedi_driver driver_das800 = {
791 .driver_name = "das800",
792 .module = THIS_MODULE,
793 .attach = das800_attach,
794 .detach = comedi_legacy_detach,
795 .num_names = ARRAY_SIZE(das800_boards),
796 .board_name = &das800_boards[0].name,
797 .offset = sizeof(struct das800_board),
799 module_comedi_driver(driver_das800);
801 MODULE_AUTHOR("Comedi http://www.comedi.org");
802 MODULE_DESCRIPTION("Comedi low-level driver");
803 MODULE_LICENSE("GPL");