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, ciodas800, das801, ciodas801, das802, ciodas802, ciodas80216 };
174 static const struct das800_board das800_boards[] = {
178 .ai_range = &range_bipolar5,
182 .name = "cio-das800",
184 .ai_range = &range_bipolar5,
190 .ai_range = &range_das801_ai,
194 .name = "cio-das801",
196 .ai_range = &range_cio_das801_ai,
202 .ai_range = &range_das802_ai,
206 .name = "cio-das802",
208 .ai_range = &range_das802_ai,
212 .name = "cio-das802/16",
214 .ai_range = &range_das80216_ai,
219 struct das800_private {
220 volatile unsigned int count; /* number of data points left to be taken */
221 volatile int forever; /* flag indicating whether we should take data forever */
222 unsigned int divisor1; /* value to load into board's counter 1 for timed conversions */
223 unsigned int divisor2; /* value to load into board's counter 2 for timed conversions */
224 volatile 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 /* enable_das800 makes the card start taking hardware triggered conversions */
249 static void enable_das800(struct comedi_device *dev)
251 const struct das800_board *thisboard = comedi_board(dev);
252 struct das800_private *devpriv = dev->private;
253 unsigned long irq_flags;
255 spin_lock_irqsave(&dev->spinlock, irq_flags);
256 /* enable fifo-half full interrupts for cio-das802/16 */
257 if (thisboard->resolution == 16)
258 outb(CIO_ENHF, dev->iobase + DAS800_GAIN);
259 /* enable hardware triggering */
260 das800_ind_write(dev, CONV_HCEN, CONV_CONTROL);
261 /* enable card's interrupt */
262 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
263 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
266 /* disable_das800 stops hardware triggered conversions */
267 static void disable_das800(struct comedi_device *dev)
269 unsigned long irq_flags;
271 spin_lock_irqsave(&dev->spinlock, irq_flags);
272 /* disable hardware triggering of conversions */
273 das800_ind_write(dev, 0x0, CONV_CONTROL);
274 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
277 static int das800_set_frequency(struct comedi_device *dev)
279 struct das800_private *devpriv = dev->private;
282 if (i8254_load(dev->iobase + DAS800_8254, 0, 1, devpriv->divisor1, 2))
284 if (i8254_load(dev->iobase + DAS800_8254, 0, 2, devpriv->divisor2, 2))
292 static int das800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
294 struct das800_private *devpriv = dev->private;
296 devpriv->forever = 0;
302 static int das800_ai_do_cmdtest(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 struct comedi_cmd *cmd)
306 const struct das800_board *thisboard = comedi_board(dev);
307 struct das800_private *devpriv = dev->private;
313 /* Step 1 : check if triggers are trivially valid */
315 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
316 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
317 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
318 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
319 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
324 /* Step 2a : make sure trigger sources are unique */
326 err |= cfc_check_trigger_is_unique(cmd->start_src);
327 err |= cfc_check_trigger_is_unique(cmd->convert_src);
328 err |= cfc_check_trigger_is_unique(cmd->stop_src);
330 /* Step 2b : and mutually compatible */
335 /* Step 3: check if arguments are trivially valid */
337 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
339 if (cmd->convert_src == TRIG_TIMER)
340 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
341 thisboard->ai_speed);
343 err |= cfc_check_trigger_arg_min(&cmd->chanlist_len, 1);
344 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
346 if (cmd->stop_src == TRIG_COUNT)
347 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
349 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
354 /* step 4: fix up any arguments */
356 if (cmd->convert_src == TRIG_TIMER) {
357 tmp = cmd->convert_arg;
358 /* calculate counter values that give desired timing */
359 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
360 &(devpriv->divisor2),
362 cmd->flags & TRIG_ROUND_MASK);
363 if (tmp != cmd->convert_arg)
370 /* check channel/gain list against card's limitations */
372 gain = CR_RANGE(cmd->chanlist[0]);
373 startChan = CR_CHAN(cmd->chanlist[0]);
374 for (i = 1; i < cmd->chanlist_len; i++) {
375 if (CR_CHAN(cmd->chanlist[i]) !=
376 (startChan + i) % N_CHAN_AI) {
378 "entries in chanlist must be consecutive channels, counting upwards\n");
381 if (CR_RANGE(cmd->chanlist[i]) != gain) {
383 "entries in chanlist must all have the same gain\n");
395 static int das800_ai_do_cmd(struct comedi_device *dev,
396 struct comedi_subdevice *s)
398 const struct das800_board *thisboard = comedi_board(dev);
399 struct das800_private *devpriv = dev->private;
400 int startChan, endChan, scan, gain;
402 unsigned long irq_flags;
403 struct comedi_async *async = s->async;
407 "no irq assigned for das-800, cannot do hardware conversions");
413 /* set channel scan limits */
414 startChan = CR_CHAN(async->cmd.chanlist[0]);
415 endChan = (startChan + async->cmd.chanlist_len - 1) % 8;
416 scan = (endChan << 3) | startChan;
418 spin_lock_irqsave(&dev->spinlock, irq_flags);
419 /* set scan limits */
420 das800_ind_write(dev, scan, SCAN_LIMITS);
421 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
424 gain = CR_RANGE(async->cmd.chanlist[0]);
425 if (thisboard->resolution == 12 && gain > 0)
428 outb(gain, dev->iobase + DAS800_GAIN);
430 switch (async->cmd.stop_src) {
432 devpriv->count = async->cmd.stop_arg * async->cmd.chanlist_len;
433 devpriv->forever = 0;
436 devpriv->forever = 1;
443 /* enable auto channel scan, send interrupts on end of conversion
444 * and set clock source to internal or external
447 conv_bits |= EACS | IEOC;
448 if (async->cmd.start_src == TRIG_EXT)
450 switch (async->cmd.convert_src) {
452 conv_bits |= CASC | ITE;
453 /* set conversion frequency */
454 i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
455 &(devpriv->divisor2),
456 &(async->cmd.convert_arg),
458 flags & TRIG_ROUND_MASK);
459 if (das800_set_frequency(dev) < 0) {
460 comedi_error(dev, "Error setting up counters");
470 spin_lock_irqsave(&dev->spinlock, irq_flags);
471 das800_ind_write(dev, conv_bits, CONV_CONTROL);
472 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
479 static irqreturn_t das800_interrupt(int irq, void *d)
481 short i; /* loop index */
483 struct comedi_device *dev = d;
484 const struct das800_board *thisboard = comedi_board(dev);
485 struct das800_private *devpriv = dev->private;
486 struct comedi_subdevice *s = dev->read_subdev; /* analog input subdevice */
487 struct comedi_async *async;
489 unsigned long irq_flags;
490 static const int max_loops = 128; /* half-fifo size for cio-das802/16 */
493 int fifo_overflow = 0;
495 status = inb(dev->iobase + DAS800_STATUS);
496 /* if interrupt was not generated by board or driver not attached, quit */
499 if (!(dev->attached))
502 /* wait until here to initialize async, since we will get null dereference
503 * if interrupt occurs before driver is fully attached!
507 /* if hardware conversions are not enabled, then quit */
508 spin_lock_irqsave(&dev->spinlock, irq_flags);
509 status = das800_ind_read(dev, CONTROL1) & STATUS2_HCEN;
510 /* don't release spinlock yet since we want to make sure no one else disables hardware conversions */
512 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
516 /* loop while card's fifo is not empty (and limit to half fifo for cio-das802/16) */
517 for (i = 0; i < max_loops; i++) {
518 /* read 16 bits from dev->iobase and dev->iobase + 1 */
519 dataPoint = inb(dev->iobase + DAS800_LSB);
520 dataPoint += inb(dev->iobase + DAS800_MSB) << 8;
521 if (thisboard->resolution == 12) {
522 fifo_empty = dataPoint & FIFO_EMPTY;
523 fifo_overflow = dataPoint & FIFO_OVF;
527 fifo_empty = 0; /* cio-das802/16 has no fifo empty status bit */
531 /* strip off extraneous bits for 12 bit cards */
532 if (thisboard->resolution == 12)
533 dataPoint = (dataPoint >> 4) & 0xfff;
534 /* if there are more data points to collect */
535 if (devpriv->count > 0 || devpriv->forever == 1) {
536 /* write data point to buffer */
537 cfc_write_to_buffer(s, dataPoint);
538 if (devpriv->count > 0)
542 async->events |= COMEDI_CB_BLOCK;
543 /* check for fifo overflow */
544 if (thisboard->resolution == 12) {
545 fifo_overflow = dataPoint & FIFO_OVF;
546 /* else cio-das802/16 */
548 fifo_overflow = inb(dev->iobase + DAS800_GAIN) & CIO_FFOV;
551 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
552 comedi_error(dev, "DAS800 FIFO overflow");
553 das800_cancel(dev, s);
554 async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
555 comedi_event(dev, s);
559 if (devpriv->count > 0 || devpriv->forever == 1) {
560 /* Re-enable card's interrupt.
561 * We already have spinlock, so indirect addressing is safe */
562 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits,
564 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
565 /* otherwise, stop taking data */
567 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
568 disable_das800(dev); /* disable hardware triggered conversions */
569 async->events |= COMEDI_CB_EOA;
571 comedi_event(dev, s);
576 static int das800_ai_rinsn(struct comedi_device *dev,
577 struct comedi_subdevice *s, struct comedi_insn *insn,
580 const struct das800_board *thisboard = comedi_board(dev);
581 struct das800_private *devpriv = dev->private;
587 unsigned long irq_flags;
589 disable_das800(dev); /* disable hardware conversions (enables software conversions) */
591 /* set multiplexer */
592 chan = CR_CHAN(insn->chanspec);
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 range = CR_RANGE(insn->chanspec);
600 if (thisboard->resolution == 12 && range)
603 outb(range, dev->iobase + DAS800_GAIN);
607 for (n = 0; n < insn->n; n++) {
608 /* trigger conversion */
609 outb_p(0, dev->iobase + DAS800_MSB);
611 for (i = 0; i < timeout; i++) {
612 if (!(inb(dev->iobase + DAS800_STATUS) & BUSY))
616 comedi_error(dev, "timeout");
619 lsb = inb(dev->iobase + DAS800_LSB);
620 msb = inb(dev->iobase + DAS800_MSB);
621 if (thisboard->resolution == 12) {
622 data[n] = (lsb >> 4) & 0xff;
623 data[n] |= (msb << 4);
625 data[n] = (msb << 8) | lsb;
632 static int das800_di_rbits(struct comedi_device *dev,
633 struct comedi_subdevice *s, struct comedi_insn *insn,
638 bits = inb(dev->iobase + DAS800_STATUS) >> 4;
646 static int das800_do_wbits(struct comedi_device *dev,
647 struct comedi_subdevice *s, struct comedi_insn *insn,
650 struct das800_private *devpriv = dev->private;
652 unsigned long irq_flags;
654 /* only set bits that have been masked */
656 wbits = devpriv->do_bits >> 4;
658 wbits |= data[0] & data[1];
659 devpriv->do_bits = wbits << 4;
661 spin_lock_irqsave(&dev->spinlock, irq_flags);
662 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
663 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
670 static int das800_probe(struct comedi_device *dev)
672 const struct das800_board *thisboard = comedi_board(dev);
674 unsigned long irq_flags;
677 spin_lock_irqsave(&dev->spinlock, irq_flags);
678 id_bits = das800_ind_read(dev, ID) & 0x3;
679 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
681 board = thisboard - das800_boards;
685 if (board == das800) {
686 dev_dbg(dev->class_dev, "Board model: DAS-800\n");
689 if (board == ciodas800) {
690 dev_dbg(dev->class_dev, "Board model: CIO-DAS800\n");
693 dev_dbg(dev->class_dev, "Board model (probed): DAS-800\n");
697 if (board == das801) {
698 dev_dbg(dev->class_dev, "Board model: DAS-801\n");
701 if (board == ciodas801) {
702 dev_dbg(dev->class_dev, "Board model: CIO-DAS801\n");
705 dev_dbg(dev->class_dev, "Board model (probed): DAS-801\n");
709 if (board == das802) {
710 dev_dbg(dev->class_dev, "Board model: DAS-802\n");
713 if (board == ciodas802) {
714 dev_dbg(dev->class_dev, "Board model: CIO-DAS802\n");
717 if (board == ciodas80216) {
718 dev_dbg(dev->class_dev, "Board model: CIO-DAS802/16\n");
721 dev_dbg(dev->class_dev, "Board model (probed): DAS-802\n");
725 dev_dbg(dev->class_dev,
726 "Board model: probe returned 0x%x (unknown)\n",
734 static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
736 const struct das800_board *thisboard = comedi_board(dev);
737 struct das800_private *devpriv;
738 struct comedi_subdevice *s;
739 unsigned int irq = it->options[1];
740 unsigned long irq_flags;
744 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
747 dev->private = devpriv;
749 ret = comedi_request_region(dev, it->options[0], DAS800_SIZE);
753 board = das800_probe(dev);
755 dev_dbg(dev->class_dev, "unable to determine board type\n");
758 dev->board_ptr = das800_boards + board;
759 thisboard = comedi_board(dev);
762 if (irq == 1 || irq > 7) {
763 dev_err(dev->class_dev, "irq out of range\n");
767 if (request_irq(irq, das800_interrupt, 0, "das800", dev)) {
768 dev_err(dev->class_dev, "unable to allocate irq %u\n",
775 dev->board_name = thisboard->name;
777 ret = comedi_alloc_subdevices(dev, 3);
781 /* analog input subdevice */
782 s = &dev->subdevices[0];
783 dev->read_subdev = s;
784 s->type = COMEDI_SUBD_AI;
785 s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
788 s->maxdata = (1 << thisboard->resolution) - 1;
789 s->range_table = thisboard->ai_range;
790 s->do_cmd = das800_ai_do_cmd;
791 s->do_cmdtest = das800_ai_do_cmdtest;
792 s->insn_read = das800_ai_rinsn;
793 s->cancel = das800_cancel;
796 s = &dev->subdevices[1];
797 s->type = COMEDI_SUBD_DI;
798 s->subdev_flags = SDF_READABLE;
801 s->range_table = &range_digital;
802 s->insn_bits = das800_di_rbits;
805 s = &dev->subdevices[2];
806 s->type = COMEDI_SUBD_DO;
807 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
810 s->range_table = &range_digital;
811 s->insn_bits = das800_do_wbits;
815 /* initialize digital out channels */
816 spin_lock_irqsave(&dev->spinlock, irq_flags);
817 das800_ind_write(dev, CONTROL1_INTE | devpriv->do_bits, CONTROL1);
818 spin_unlock_irqrestore(&dev->spinlock, irq_flags);
823 static struct comedi_driver driver_das800 = {
824 .driver_name = "das800",
825 .module = THIS_MODULE,
826 .attach = das800_attach,
827 .detach = comedi_legacy_detach,
828 .num_names = ARRAY_SIZE(das800_boards),
829 .board_name = &das800_boards[0].name,
830 .offset = sizeof(struct das800_board),
832 module_comedi_driver(driver_das800);
834 MODULE_AUTHOR("Comedi http://www.comedi.org");
835 MODULE_DESCRIPTION("Comedi low-level driver");
836 MODULE_LICENSE("GPL");