2 das800.c driver for Keitley das800 series boards and compatibles
3 Copyright (C) 2000 Frank Mori Hess <fmhess@users.sourceforge.net>
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 ************************************************************************
26 Description: Keithley Metrabyte DAS800 (& compatibles)
27 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
28 Devices: [Keithley Metrabyte] DAS-800 (das-800), DAS-801 (das-801),
30 [Measurement Computing] CIO-DAS800 (cio-das800),
31 CIO-DAS801 (cio-das801), CIO-DAS802 (cio-das802),
32 CIO-DAS802/16 (cio-das802/16)
33 Status: works, cio-das802/16 untested - email me if you have tested it
35 Configuration options:
36 [0] - I/O port base address
37 [1] - IRQ (optional, required for timed or externally triggered conversions)
40 IRQ can be omitted, although the cmd interface will not work without it.
42 All entries in the channel/gain list must use the same gain and be
43 consecutive channels counting upwards in channel number (these are
44 hardware limitations.)
46 I've never tested the gain setting stuff since I only have a
47 DAS-800 board with fixed gain.
49 The cio-das802/16 does not have a fifo-empty status bit! Therefore
50 only fifo-half-full transfers are possible with this card.
54 cmd triggers supported:
55 start_src: TRIG_NOW | TRIG_EXT
56 scan_begin_src: TRIG_FOLLOW
57 scan_end_src: TRIG_COUNT
58 convert_src: TRIG_TIMER | TRIG_EXT
59 stop_src: TRIG_NONE | TRIG_COUNT
64 #include <linux/comedidev.h>
66 #include <linux/ioport.h>
67 #include <linux/delay.h>
70 #include "comedi_fc.h"
73 #define TIMER_BASE 1000
74 #define N_CHAN_AI 8 // number of analog input channels
76 /* Registers for the das800 */
79 #define FIFO_EMPTY 0x1
82 #define DAS800_CONTROL1 2
83 #define CONTROL1_INTE 0x8
84 #define DAS800_CONV_CONTROL 2
90 #define CONV_HCEN 0x80
91 #define DAS800_SCAN_LIMITS 2
92 #define DAS800_STATUS 2
96 #define CIO_FFOV 0x8 // fifo overflow for cio-das802/16
97 #define CIO_ENHF 0x90 // interrupt fifo half full for cio-das802/16
99 #define CONV_CONTROL 0xa0
100 #define SCAN_LIMITS 0xc0
102 #define DAS800_8254 4
103 #define DAS800_STATUS2 7
104 #define STATUS2_HCEN 0x80
105 #define STATUS2_INTE 0X20
108 typedef struct das800_board_struct{
111 comedi_lrange *ai_range;
115 //analog input ranges
116 static comedi_lrange range_das800_ai = {
123 static comedi_lrange range_das801_ai = {
138 static comedi_lrange range_cio_das801_ai = {
148 RANGE(-0.005, 0.005),
153 static comedi_lrange range_das802_ai = {
163 RANGE(-0.625, 0.625),
168 static comedi_lrange range_das80216_ai = {
182 enum{das800, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216};
184 static das800_board das800_boards[] =
189 ai_range: &range_das800_ai,
195 ai_range: &range_das800_ai,
201 ai_range: &range_das801_ai,
207 ai_range: &range_cio_das801_ai,
213 ai_range: &range_das802_ai,
219 ai_range: &range_das802_ai,
223 name: "cio-das802/16",
225 ai_range: &range_das80216_ai,
230 * Useful for shorthand access to the particular board structure
232 #define thisboard ((das800_board *)dev->board_ptr)
235 volatile unsigned int count; /* number of data points left to be taken */
236 volatile int forever; /* flag indicating whether we should take data forever */
237 unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
238 unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
239 volatile int do_bits; /* digital output bits */
242 #define devpriv ((das800_private *)dev->private)
244 static int das800_attach(comedi_device *dev,comedi_devconfig *it);
245 static int das800_detach(comedi_device *dev);
246 static int das800_cancel(comedi_device *dev, comedi_subdevice *s);
248 static comedi_driver driver_das800={
249 driver_name: "das800",
251 attach: das800_attach,
252 detach: das800_detach,
253 num_names: sizeof(das800_boards) / sizeof(das800_board),
254 board_name: &das800_boards[0].name,
255 offset: sizeof(das800_board),
258 static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG);
259 static void enable_das800(comedi_device *dev);
260 static void disable_das800(comedi_device *dev);
261 static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd);
262 static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s);
263 static int das800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
264 static int das800_di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
265 static int das800_do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data);
266 static int das800_probe(comedi_device *dev);
267 static int das800_set_frequency(comedi_device *dev);
269 /* checks and probes das-800 series board type */
270 static int das800_probe(comedi_device *dev)
273 unsigned long irq_flags;
276 // 'comedi spin lock irqsave' disables even rt interrupts, we use them to protect indirect addressing
277 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
278 outb(ID, dev->iobase + DAS800_GAIN); /* select base address + 7 to be ID register */
279 id_bits = inb(dev->iobase + DAS800_ID) & 0x3; /* get id bits */
280 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
282 board = thisboard - das800_boards;
289 printk(" Board model: DAS-800\n");
292 if(board == ciodas800)
294 printk(" Board model: CIO-DAS800\n");
297 printk(" Board model (probed): DAS-800\n");
303 printk(" Board model: DAS-801\n");
306 if(board == ciodas801)
308 printk(" Board model: CIO-DAS801\n");
311 printk(" Board model (probed): DAS-801\n");
317 printk(" Board model: DAS-802\n");
320 if(board == ciodas802)
322 printk(" Board model: CIO-DAS802\n");
325 if(board == ciodas80216)
327 printk(" Board model: CIO-DAS802/16\n");
330 printk(" Board model (probed): DAS-802\n");
334 printk(" Board model: probe returned 0x%x (unknown)\n", id_bits);
342 * A convenient macro that defines init_module() and cleanup_module(),
345 COMEDI_INITCLEANUP(driver_das800);
347 /* interrupt service routine */
348 static irqreturn_t das800_interrupt(int irq, void *d PT_REGS_ARG)
350 short i; /* loop index */
351 sampl_t dataPoint = 0;
352 comedi_device *dev = d;
353 comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
356 unsigned long irq_flags;
357 static const int max_loops = 128; // half-fifo size for cio-das802/16
360 int fifo_overflow = 0;
362 status = inb(dev->iobase + DAS800_STATUS);
363 /* if interrupt was not generated by board or driver not attached, quit */
369 /* wait until here to initialize async, since we will get null dereference
370 * if interrupt occurs before driver is fully attached!
374 // if hardware conversions are not enabled, then quit
375 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
376 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select base address + 7 to be STATUS2 register */
377 status = inb(dev->iobase + DAS800_STATUS2) & STATUS2_HCEN;
378 /* don't release spinlock yet since we want to make sure noone else disables hardware conversions */
381 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
385 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
386 for(i = 0; i < max_loops; i++)
388 /* read 16 bits from dev->iobase and dev->iobase + 1 */
389 dataPoint = inb(dev->iobase + DAS800_LSB);
390 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
391 if(thisboard->resolution == 12)
393 fifo_empty = dataPoint & FIFO_EMPTY;
394 fifo_overflow = dataPoint & FIFO_OVF;
395 if(fifo_overflow) break;
398 fifo_empty = 0; // cio-das802/16 has no fifo empty status bit
404 /* strip off extraneous bits for 12 bit cards*/
405 if(thisboard->resolution == 12)
406 dataPoint = (dataPoint >> 4) & 0xfff;
407 /* if there are more data points to collect */
408 if(devpriv->count > 0 || devpriv->forever == 1)
410 /* write data point to buffer */
411 cfc_write_to_buffer( s, dataPoint);
412 if(devpriv->count > 0) devpriv->count--;
415 async->events |= COMEDI_CB_BLOCK;
416 /* check for fifo overflow */
417 if(thisboard->resolution == 12)
419 fifo_overflow = dataPoint & FIFO_OVF;
420 // else cio-das802/16
423 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
427 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
428 comedi_error(dev, "DAS800 FIFO overflow");
429 das800_cancel(dev, dev->subdevices + 0);
430 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
431 comedi_event(dev, s, async->events);
435 if(devpriv->count > 0 || devpriv->forever == 1)
437 /* Re-enable card's interrupt.
438 * We already have spinlock, so indirect addressing is safe */
439 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
440 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
441 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
442 /* otherwise, stop taking data */
445 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
446 disable_das800(dev); /* diable hardware triggered conversions */
447 async->events |= COMEDI_CB_EOA;
449 comedi_event(dev, s, async->events);
454 static int das800_attach(comedi_device *dev, comedi_devconfig *it)
457 unsigned long iobase = it->options[0];
458 unsigned int irq = it->options[1];
459 unsigned long irq_flags;
462 printk("comedi%d: das800: io 0x%lx", dev->minor, iobase);
465 printk(", irq %u", irq);
469 /* allocate and initialize dev->private */
470 if(alloc_private(dev, sizeof(das800_private)) < 0)
475 printk("io base address required for das800\n");
479 /* check if io addresses are available */
480 if(!request_region(iobase, DAS800_SIZE, "das800"))
482 printk("I/O port conflict\n");
485 dev->iobase = iobase;
487 board = das800_probe(dev);
490 printk("unable to determine board type\n");
493 dev->board_ptr = das800_boards + board;
496 if(irq == 1 || irq > 7)
498 printk("irq out of range\n");
503 if(comedi_request_irq( irq, das800_interrupt, 0, "das800", dev ))
505 printk( "unable to allocate irq %u\n", irq);
511 dev->board_name = thisboard->name;
513 if(alloc_subdevices(dev, 3) < 0)
516 /* analog input subdevice */
517 s = dev->subdevices + 0;
518 dev->read_subdev = s;
519 s->type = COMEDI_SUBD_AI;
520 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
523 s->maxdata = (1 << thisboard->resolution) - 1;
524 s->range_table = thisboard->ai_range;
525 s->do_cmd = das800_ai_do_cmd;
526 s->do_cmdtest = das800_ai_do_cmdtest;
527 s->insn_read = das800_ai_rinsn;
528 s->cancel = das800_cancel;
531 s = dev->subdevices + 1;
532 s->type=COMEDI_SUBD_DI;
533 s->subdev_flags = SDF_READABLE;
536 s->range_table = &range_digital;
537 s->insn_bits = das800_di_rbits;
540 s = dev->subdevices + 2;
541 s->type=COMEDI_SUBD_DO;
542 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
545 s->range_table = &range_digital;
546 s->insn_bits = das800_do_wbits;
550 /* initialize digital out channels */
551 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
552 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
553 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
554 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
559 static int das800_detach(comedi_device *dev)
561 printk("comedi%d: das800: remove\n", dev->minor);
563 /* only free stuff if it has been allocated by _attach */
565 release_region(dev->iobase, DAS800_SIZE);
567 comedi_free_irq(dev->irq, dev);
571 static int das800_cancel(comedi_device *dev, comedi_subdevice *s)
573 devpriv->forever = 0;
579 /* enable_das800 makes the card start taking hardware triggered conversions */
580 static void enable_das800(comedi_device *dev)
582 unsigned long irq_flags;
583 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
584 // enable fifo-half full interrupts for cio-das802/16
585 if(thisboard->resolution == 16)
586 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
587 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
588 outb(CONV_HCEN, dev->iobase + DAS800_CONV_CONTROL); /* enable hardware triggering */
589 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
590 outb(CONTROL1_INTE | devpriv->do_bits, dev->iobase + DAS800_CONTROL1); /* enable card's interrupt */
591 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
594 /* disable_das800 stops hardware triggered conversions */
595 static void disable_das800(comedi_device *dev)
597 unsigned long irq_flags;
598 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
599 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
600 outb(0x0, dev->iobase + DAS800_CONV_CONTROL); /* disable hardware triggering of conversions */
601 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
604 static int das800_ai_do_cmdtest(comedi_device *dev,comedi_subdevice *s,comedi_cmd *cmd)
611 /* step 1: make sure trigger sources are trivially valid */
613 tmp = cmd->start_src;
614 cmd->start_src &= TRIG_NOW | TRIG_EXT;
615 if(!cmd->start_src || tmp != cmd->start_src) err++;
617 tmp = cmd->scan_begin_src;
618 cmd->scan_begin_src &= TRIG_FOLLOW;
619 if(!cmd->scan_begin_src || tmp != cmd->scan_begin_src) err++;
621 tmp = cmd->convert_src;
622 cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
623 if(!cmd->convert_src || tmp != cmd->convert_src) err++;
625 tmp = cmd->scan_end_src;
626 cmd->scan_end_src &= TRIG_COUNT;
627 if(!cmd->scan_end_src || tmp != cmd->scan_end_src) err++;
630 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
631 if(!cmd->stop_src || tmp!=cmd->stop_src) err++;
635 /* step 2: make sure trigger sources are unique and mutually compatible */
637 if(cmd->start_src != TRIG_NOW &&
638 cmd->start_src != TRIG_EXT) err++;
639 if(cmd->convert_src != TRIG_TIMER &&
640 cmd->convert_src != TRIG_EXT) err++;
641 if(cmd->stop_src != TRIG_COUNT &&
642 cmd->stop_src != TRIG_NONE) err++;
646 /* step 3: make sure arguments are trivially compatible */
648 if(cmd->start_arg != 0)
653 if(cmd->convert_src == TRIG_TIMER)
655 if(cmd->convert_arg < thisboard->ai_speed)
657 cmd->convert_arg = thisboard->ai_speed;
661 if(!cmd->chanlist_len)
663 cmd->chanlist_len = 1;
666 if(cmd->scan_end_arg != cmd->chanlist_len)
668 cmd->scan_end_arg = cmd->chanlist_len;
671 if(cmd->stop_src == TRIG_COUNT)
680 if(cmd->stop_arg != 0)
689 /* step 4: fix up any arguments */
691 if(cmd->convert_src == TRIG_TIMER)
693 tmp = cmd->convert_arg;
694 /* calculate counter values that give desired timing */
695 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(cmd->convert_arg), cmd->flags & TRIG_ROUND_MASK);
696 if(tmp != cmd->convert_arg) err++;
701 // check channel/gain list against card's limitations
704 gain = CR_RANGE(cmd->chanlist[0]);
705 startChan = CR_CHAN(cmd->chanlist[0]);
706 for(i = 1; i < cmd->chanlist_len; i++)
708 if(CR_CHAN(cmd->chanlist[i]) != (startChan + i) % N_CHAN_AI)
710 comedi_error(dev, "entries in chanlist must be consecutive channels, counting upwards\n");
713 if(CR_RANGE(cmd->chanlist[i]) != gain)
715 comedi_error(dev, "entries in chanlist must all have the same gain\n");
726 static int das800_ai_do_cmd(comedi_device *dev, comedi_subdevice *s)
728 int startChan, endChan, scan, gain;
730 unsigned long irq_flags;
731 comedi_async *async = s->async;
735 comedi_error(dev, "no irq assigned for das-800, cannot do hardware conversions");
741 /* set channel scan limits */
742 startChan = CR_CHAN(async->cmd.chanlist[0]);
743 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
744 scan = (endChan << 3) | startChan;
746 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
747 outb(SCAN_LIMITS, dev->iobase + DAS800_GAIN); /* select base address + 2 to be scan limits register */
748 outb(scan, dev->iobase + DAS800_SCAN_LIMITS); /* set scan limits */
749 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
752 gain = CR_RANGE(async->cmd.chanlist[0]);
753 if( thisboard->resolution == 12 && gain > 0)
756 outb(gain, dev->iobase + DAS800_GAIN);
758 switch(async->cmd.stop_src)
761 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
762 devpriv->forever = 0;
765 devpriv->forever = 1;
772 /* enable auto channel scan, send interrupts on end of conversion
773 * and set clock source to internal or external
776 conv_bits |= EACS | IEOC;
777 if(async->cmd.start_src == TRIG_EXT)
779 switch(async->cmd.convert_src)
782 conv_bits |= CASC | ITE;
783 /* set conversion frequency */
784 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1), &(devpriv->divisor2), &(async->cmd.convert_arg), async->cmd.flags & TRIG_ROUND_MASK);
785 if(das800_set_frequency(dev) < 0)
787 comedi_error(dev, "Error setting up counters");
797 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
798 outb(CONV_CONTROL, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be conversion control register */
799 outb(conv_bits, dev->iobase + DAS800_CONV_CONTROL);
800 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
806 static int das800_ai_rinsn(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
813 unsigned long irq_flags;
815 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
817 /* set multiplexer */
818 chan = CR_CHAN(insn->chanspec);
820 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
821 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
822 outb(chan | devpriv->do_bits, dev->iobase + DAS800_CONTROL1);
823 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
825 /* set gain / range */
826 range = CR_RANGE(insn->chanspec);
827 if(thisboard->resolution == 12 && range)
830 outb(range, dev->iobase + DAS800_GAIN);
834 for(n = 0; n < insn->n; n++)
836 /* trigger conversion */
837 outb_p(0, dev->iobase + DAS800_MSB);
839 for(i = 0; i < timeout; i++)
841 if(!(inb(dev->iobase + DAS800_STATUS) & BUSY))
846 comedi_error(dev, "timeout");
849 lsb = inb(dev->iobase + DAS800_LSB);
850 msb = inb(dev->iobase + DAS800_MSB);
851 if(thisboard->resolution == 12)
853 data[n] = (lsb >> 4) & 0xff;
854 data[n] |= (msb << 4);
857 data[n] = (msb << 8) | lsb;
864 static int das800_di_rbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
868 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
876 static int das800_do_wbits(comedi_device *dev, comedi_subdevice *s, comedi_insn *insn, lsampl_t *data)
879 unsigned long irq_flags;
882 // only set bits that have been masked
884 wbits = devpriv->do_bits >> 4;
886 wbits |= data[0] & data[1];
887 devpriv->do_bits = wbits << 4;
889 comedi_spin_lock_irqsave(&dev->spinlock, irq_flags);
890 outb(CONTROL1, dev->iobase + DAS800_GAIN); /* select dev->iobase + 2 to be control register 1 */
891 outb(devpriv->do_bits | CONTROL1_INTE, dev->iobase + DAS800_CONTROL1);
892 comedi_spin_unlock_irqrestore(&dev->spinlock, irq_flags);
899 /* loads counters with divisor1, divisor2 from private structure */
900 static int das800_set_frequency(comedi_device *dev)
904 if(i8254_load(dev->iobase + DAS800_8254, 1, devpriv->divisor1, 2)) err++;
905 if(i8254_load(dev->iobase + DAS800_8254, 2, devpriv->divisor2, 2)) err++;