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 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
457 &(devpriv->divisor2),
458 &(async->cmd.convert_arg),
460 flags & TRIG_ROUND_MASK);
461 if (das800_set_frequency(dev) < 0) {
462 comedi_error(dev, "Error setting up counters");
472 spin_lock_irqsave(&dev->spinlock, irq_flags);
473 das800_ind_write(dev, conv_bits, CONV_CONTROL);
474 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
481 static irqreturn_t das800_interrupt(int irq, void *d)
483 short i; /* loop index */
485 struct comedi_device *dev = d;
486 const struct das800_board *thisboard = comedi_board(dev);
487 struct das800_private *devpriv = dev->private;
488 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
489 struct comedi_async *async;
491 unsigned long irq_flags;
492 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
495 int fifo_overflow = 0;
497 status = inb(dev->iobase + DAS800_STATUS);
498 /* if interrupt was not generated by board or driver not attached, quit */
501 if (!(dev->attached))
504 /* wait until here to initialize async, since we will get null dereference
505 * if interrupt occurs before driver is fully attached!
509 /* if hardware conversions are not enabled, then quit */
510 spin_lock_irqsave(&dev->spinlock, irq_flags);
511 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
512 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
514 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
518 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
519 for (i = 0; i < max_loops; i++) {
520 /* read 16 bits from dev->iobase and dev->iobase + 1 */
521 dataPoint = inb(dev->iobase + DAS800_LSB);
522 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
523 if (thisboard->resolution == 12) {
524 fifo_empty = dataPoint & FIFO_EMPTY;
525 fifo_overflow = dataPoint & FIFO_OVF;
529 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
533 /* strip off extraneous bits for 12 bit cards */
534 if (thisboard->resolution == 12)
535 dataPoint = (dataPoint >> 4) & 0xfff;
536 /* if there are more data points to collect */
537 if (devpriv->count > 0 || devpriv->forever == 1) {
538 /* write data point to buffer */
539 cfc_write_to_buffer(s, dataPoint);
540 if (devpriv->count > 0)
544 async->events |= COMEDI_CB_BLOCK;
545 /* check for fifo overflow */
546 if (thisboard->resolution == 12) {
547 fifo_overflow = dataPoint & FIFO_OVF;
548 /* else cio-das802/16 */
550 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
553 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
554 comedi_error(dev, "DAS800 FIFO overflow");
555 das800_cancel(dev, s);
556 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
557 comedi_event(dev, s);
561 if (devpriv->count > 0 || devpriv->forever == 1) {
562 /* Re-enable card's interrupt.
563 * We already have spinlock, so indirect addressing is safe */
564 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
566 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
567 /* otherwise, stop taking data */
569 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
571 async->events |= COMEDI_CB_EOA;
573 comedi_event(dev, s);
578 static int das800_ai_insn_read(struct comedi_device *dev,
579 struct comedi_subdevice *s,
580 struct comedi_insn *insn,
583 const struct das800_board *thisboard = comedi_board(dev);
584 struct das800_private *devpriv = dev->private;
590 unsigned long irq_flags;
594 /* set multiplexer */
595 chan = CR_CHAN(insn->chanspec);
597 spin_lock_irqsave(&dev->spinlock, irq_flags);
598 das800_ind_write(dev, chan | devpriv->do_bits, CONTROL1);
599 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
601 /* set gain / range */
602 range = CR_RANGE(insn->chanspec);
603 if (thisboard->resolution == 12 && range)
606 outb(range, dev->iobase + DAS800_GAIN);
610 for (n = 0; n < insn->n; n++) {
611 /* trigger conversion */
612 outb_p(0, dev->iobase + DAS800_MSB);
614 for (i = 0; i < timeout; i++) {
615 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
619 comedi_error(dev, "timeout");
622 lsb = inb(dev->iobase + DAS800_LSB);
623 msb = inb(dev->iobase + DAS800_MSB);
624 if (thisboard->resolution == 12) {
625 data[n] = (lsb >> 4) & 0xff;
626 data[n] |= (msb << 4);
628 data[n] = (msb << 8) | lsb;
635 static int das800_di_insn_bits(struct comedi_device *dev,
636 struct comedi_subdevice *s,
637 struct comedi_insn *insn,
642 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
650 static int das800_do_insn_bits(struct comedi_device *dev,
651 struct comedi_subdevice *s,
652 struct comedi_insn *insn,
655 struct das800_private *devpriv = dev->private;
657 unsigned long irq_flags;
659 /* only set bits that have been masked */
661 wbits = devpriv->do_bits >> 4;
663 wbits |= data[0] & data[1];
664 devpriv->do_bits = wbits << 4;
666 spin_lock_irqsave(&dev->spinlock, irq_flags);
667 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
668 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
675 static int das800_probe(struct comedi_device *dev)
677 const struct das800_board *thisboard = comedi_board(dev);
678 int board = thisboard ? thisboard - das800_boards : -EINVAL;
680 unsigned long irq_flags;
682 spin_lock_irqsave(&dev->spinlock, irq_flags);
683 id_bits = das800_ind_read(dev, ID) & 0x3;
684 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
688 if (board == BOARD_DAS800 || board == BOARD_CIODAS800)
690 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
691 board = BOARD_DAS800;
694 if (board == BOARD_DAS801 || board == BOARD_CIODAS801)
696 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
697 board = BOARD_DAS801;
700 if (board == BOARD_DAS802 || board == BOARD_CIODAS802 ||
701 board == BOARD_CIODAS80216)
703 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
704 board = BOARD_DAS802;
707 dev_dbg(dev->class_dev, "Board model: 0x%x (unknown)\n",
715 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
717 const struct das800_board *thisboard = comedi_board(dev);
718 struct das800_private *devpriv;
719 struct comedi_subdevice *s;
720 unsigned int irq = it->options[1];
721 unsigned long irq_flags;
725 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
728 dev->private = devpriv;
730 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
734 board = das800_probe(dev);
736 dev_dbg(dev->class_dev, "unable to determine board type\n");
739 dev->board_ptr = das800_boards + board;
740 thisboard = comedi_board(dev);
741 dev->board_name = thisboard->name;
743 if (irq > 1 && irq <= 7) {
744 ret = request_irq(irq, das800_interrupt, 0, dev->board_name,
750 ret = comedi_alloc_subdevices(dev, 3);
754 /* Analog Input subdevice */
755 s = &dev->subdevices[0];
756 dev->read_subdev = s;
757 s->type = COMEDI_SUBD_AI;
758 s->subdev_flags = SDF_READABLE | SDF_GROUND;
760 s->maxdata = (1 << thisboard->resolution) - 1;
761 s->range_table = thisboard->ai_range;
762 s->insn_read = das800_ai_insn_read;
764 s->subdev_flags |= SDF_CMD_READ;
766 s->do_cmdtest = das800_ai_do_cmdtest;
767 s->do_cmd = das800_ai_do_cmd;
768 s->cancel = das800_cancel;
771 /* Digital Input subdevice */
772 s = &dev->subdevices[1];
773 s->type = COMEDI_SUBD_DI;
774 s->subdev_flags = SDF_READABLE;
777 s->range_table = &range_digital;
778 s->insn_bits = das800_di_insn_bits;
780 /* Digital Output subdevice */
781 s = &dev->subdevices[2];
782 s->type = COMEDI_SUBD_DO;
783 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
786 s->range_table = &range_digital;
787 s->insn_bits = das800_do_insn_bits;
791 /* initialize digital out channels */
792 spin_lock_irqsave(&dev->spinlock, irq_flags);
793 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
794 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
799 static struct comedi_driver driver_das800 = {
800 .driver_name = "das800",
801 .module = THIS_MODULE,
802 .attach = das800_attach,
803 .detach = comedi_legacy_detach,
804 .num_names = ARRAY_SIZE(das800_boards),
805 .board_name = &das800_boards[0].name,
806 .offset = sizeof(struct das800_board),
808 module_comedi_driver(driver_das800);
810 MODULE_AUTHOR("Comedi http://www.comedi.org");
811 MODULE_DESCRIPTION("Comedi low-level driver");
812 MODULE_LICENSE("GPL");