2 comedi/drivers/das16m1.c
4 Author: Frank Mori Hess, based on code from the das16
6 Copyright (C) 2001 Frank Mori Hess <fmhess@users.sourceforge.net>
8 COMEDI - Linux Control and Measurement Device Interface
9 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 ************************************************************************
29 Description: CIO-DAS16/M1
30 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
31 Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
34 This driver supports a single board - the CIO-DAS16/M1.
35 As far as I know, there are no other boards that have
36 the same register layout. Even the CIO-DAS16/M1/16 is
37 significantly different.
39 I was _barely_ able to reach the full 1 MHz capability
40 of this board, using a hard real-time interrupt
41 (set the TRIG_RT flag in your struct comedi_cmd and use
42 rtlinux or RTAI). The board can't do dma, so the bottleneck is
43 pulling the data across the ISA bus. I timed the interrupt
44 handler, and it took my computer ~470 microseconds to pull 512
45 samples from the board. So at 1 Mhz sampling rate,
46 expect your CPU to be spending almost all of its
47 time in the interrupt handler.
49 This board has some unusual restrictions for its channel/gain list. If the
50 list has 2 or more channels in it, then two conditions must be satisfied:
51 (1) - even/odd channels must appear at even/odd indices in the list
52 (2) - the list must have an even number of entries.
56 [1] - irq (optional, but you probably want it)
58 irq can be omitted, although the cmd interface will not work without it.
61 #include <linux/ioport.h>
62 #include <linux/interrupt.h>
63 #include "../comedidev.h"
67 #include "comedi_fc.h"
69 #define DAS16M1_SIZE 16
70 #define DAS16M1_SIZE2 8
72 #define DAS16M1_XTAL 100 /* 10 MHz master clock */
74 #define FIFO_SIZE 1024 /* 1024 sample fifo */
81 0 a/d bits 0-3, mux start 12 bit
82 1 a/d bits 4-11 unused
85 4 unused clear interrupt
87 6 channel/gain queue address
88 7 channel/gain queue data
96 #define DAS16M1_AI 0 /* 16-bit wide register */
97 #define AI_CHAN(x) ((x) & 0xf)
99 #define EXT_TRIG_BIT 0x1
102 #define DAS16M1_DIO 3
103 #define DAS16M1_CLEAR_INTR 4
104 #define DAS16M1_INTR_CONTROL 5
105 #define EXT_PACER 0x2
106 #define INT_PACER 0x3
107 #define PACER_MASK 0x3
109 #define DAS16M1_QUEUE_ADDR 6
110 #define DAS16M1_QUEUE_DATA 7
111 #define Q_CHAN(x) ((x) & 0x7)
112 #define Q_RANGE(x) (((x) & 0xf) << 4)
113 #define UNIPOLAR 0x40
114 #define DAS16M1_8254_FIRST 0x8
115 #define DAS16M1_8254_FIRST_CNTRL 0xb
116 #define TOTAL_CLEAR 0x30
117 #define DAS16M1_8254_SECOND 0xc
118 #define DAS16M1_82C55 0x400
119 #define DAS16M1_8254_THIRD 0x404
121 static const struct comedi_lrange range_das16m1 = { 9,
135 struct das16m1_private_struct {
136 unsigned int control_state;
137 volatile unsigned int adc_count; /* number of samples completed */
138 /* initial value in lower half of hardware conversion counter,
139 * needed to keep track of whether new count has been loaded into
140 * counter yet (loaded by first sample conversion) */
141 u16 initial_hw_count;
142 short ai_buffer[FIFO_SIZE];
143 unsigned int do_bits; /* saves status of digital output bits */
144 unsigned int divisor1; /* divides master clock to obtain conversion speed */
145 unsigned int divisor2; /* divides master clock to obtain conversion speed */
146 unsigned long extra_iobase;
149 static inline short munge_sample(short data)
151 return (data >> 4) & 0xfff;
154 static void munge_sample_array(short *array, unsigned int num_elements)
158 for (i = 0; i < num_elements; i++)
159 array[i] = munge_sample(array[i]);
162 static int das16m1_cmd_test(struct comedi_device *dev,
163 struct comedi_subdevice *s, struct comedi_cmd *cmd)
165 struct das16m1_private_struct *devpriv = dev->private;
166 unsigned int err = 0, tmp, i;
168 /* Step 1 : check if triggers are trivially valid */
170 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
171 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
172 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
173 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
174 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
179 /* Step 2a : make sure trigger sources are unique */
181 err |= cfc_check_trigger_is_unique(cmd->start_src);
182 err |= cfc_check_trigger_is_unique(cmd->convert_src);
183 err |= cfc_check_trigger_is_unique(cmd->stop_src);
185 /* Step 2b : and mutually compatible */
190 /* Step 3: check if arguments are trivially valid */
192 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
194 if (cmd->scan_begin_src == TRIG_FOLLOW) /* internal trigger */
195 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
197 if (cmd->convert_src == TRIG_TIMER)
198 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 1000);
200 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
202 if (cmd->stop_src == TRIG_COUNT) {
203 /* any count is allowed */
206 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
212 /* step 4: fix up arguments */
214 if (cmd->convert_src == TRIG_TIMER) {
215 tmp = cmd->convert_arg;
216 /* calculate counter values that give desired timing */
217 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL,
218 &(devpriv->divisor1),
219 &(devpriv->divisor2),
221 cmd->flags & TRIG_ROUND_MASK);
222 if (tmp != cmd->convert_arg)
229 /* check chanlist against board's peculiarities */
230 if (cmd->chanlist && cmd->chanlist_len > 1) {
231 for (i = 0; i < cmd->chanlist_len; i++) {
232 /* even/odd channels must go into even/odd queue addresses */
233 if ((i % 2) != (CR_CHAN(cmd->chanlist[i]) % 2)) {
234 comedi_error(dev, "bad chanlist:\n"
235 " even/odd channels must go have even/odd chanlist indices");
239 if ((cmd->chanlist_len % 2) != 0) {
241 "chanlist must be of even length or length 1");
252 /* This function takes a time in nanoseconds and sets the *
253 * 2 pacer clocks to the closest frequency possible. It also *
254 * returns the actual sampling period. */
255 static unsigned int das16m1_set_pacer(struct comedi_device *dev,
256 unsigned int ns, int rounding_flags)
258 struct das16m1_private_struct *devpriv = dev->private;
260 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL, &(devpriv->divisor1),
261 &(devpriv->divisor2), &ns,
262 rounding_flags & TRIG_ROUND_MASK);
264 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
265 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 1, devpriv->divisor1,
267 i8254_load(dev->iobase + DAS16M1_8254_SECOND, 0, 2, devpriv->divisor2,
273 static int das16m1_cmd_exec(struct comedi_device *dev,
274 struct comedi_subdevice *s)
276 struct das16m1_private_struct *devpriv = dev->private;
277 struct comedi_async *async = s->async;
278 struct comedi_cmd *cmd = &async->cmd;
279 unsigned int byte, i;
282 comedi_error(dev, "irq required to execute comedi_cmd");
286 /* disable interrupts and internal pacer */
287 devpriv->control_state &= ~INTE & ~PACER_MASK;
288 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
290 /* set software count */
291 devpriv->adc_count = 0;
292 /* Initialize lower half of hardware counter, used to determine how
293 * many samples are in fifo. Value doesn't actually load into counter
294 * until counter's next clock (the next a/d conversion) */
295 i8254_load(dev->iobase + DAS16M1_8254_FIRST, 0, 1, 0, 2);
296 /* remember current reading of counter so we know when counter has
297 * actually been loaded */
298 devpriv->initial_hw_count =
299 i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
300 /* setup channel/gain queue */
301 for (i = 0; i < cmd->chanlist_len; i++) {
302 outb(i, dev->iobase + DAS16M1_QUEUE_ADDR);
304 Q_CHAN(CR_CHAN(cmd->chanlist[i])) |
305 Q_RANGE(CR_RANGE(cmd->chanlist[i]));
306 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
309 /* set counter mode and counts */
311 das16m1_set_pacer(dev, cmd->convert_arg,
312 cmd->flags & TRIG_ROUND_MASK);
314 /* set control & status register */
316 /* if we are using external start trigger (also board dislikes having
317 * both start and conversion triggers external simultaneously) */
318 if (cmd->start_src == TRIG_EXT && cmd->convert_src != TRIG_EXT)
319 byte |= EXT_TRIG_BIT;
321 outb(byte, dev->iobase + DAS16M1_CS);
322 /* clear interrupt bit */
323 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
325 /* enable interrupts and internal pacer */
326 devpriv->control_state &= ~PACER_MASK;
327 if (cmd->convert_src == TRIG_TIMER)
328 devpriv->control_state |= INT_PACER;
330 devpriv->control_state |= EXT_PACER;
332 devpriv->control_state |= INTE;
333 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
338 static int das16m1_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
340 struct das16m1_private_struct *devpriv = dev->private;
342 devpriv->control_state &= ~INTE & ~PACER_MASK;
343 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
348 static int das16m1_ai_rinsn(struct comedi_device *dev,
349 struct comedi_subdevice *s,
350 struct comedi_insn *insn, unsigned int *data)
352 struct das16m1_private_struct *devpriv = dev->private;
355 const int timeout = 1000;
357 /* disable interrupts and internal pacer */
358 devpriv->control_state &= ~INTE & ~PACER_MASK;
359 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
361 /* setup channel/gain queue */
362 outb(0, dev->iobase + DAS16M1_QUEUE_ADDR);
364 Q_CHAN(CR_CHAN(insn->chanspec)) | Q_RANGE(CR_RANGE(insn->chanspec));
365 outb(byte, dev->iobase + DAS16M1_QUEUE_DATA);
367 for (n = 0; n < insn->n; n++) {
368 /* clear IRQDATA bit */
369 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
370 /* trigger conversion */
371 outb(0, dev->iobase);
373 for (i = 0; i < timeout; i++) {
374 if (inb(dev->iobase + DAS16M1_CS) & IRQDATA)
378 comedi_error(dev, "timeout");
381 data[n] = munge_sample(inw(dev->iobase));
387 static int das16m1_di_rbits(struct comedi_device *dev,
388 struct comedi_subdevice *s,
389 struct comedi_insn *insn, unsigned int *data)
393 bits = inb(dev->iobase + DAS16M1_DIO) & 0xf;
400 static int das16m1_do_wbits(struct comedi_device *dev,
401 struct comedi_subdevice *s,
402 struct comedi_insn *insn, unsigned int *data)
404 struct das16m1_private_struct *devpriv = dev->private;
407 /* only set bits that have been masked */
409 wbits = devpriv->do_bits;
410 /* zero bits that have been masked */
412 /* set masked bits */
413 wbits |= data[0] & data[1];
414 devpriv->do_bits = wbits;
417 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
422 static void das16m1_handler(struct comedi_device *dev, unsigned int status)
424 struct das16m1_private_struct *devpriv = dev->private;
425 struct comedi_subdevice *s;
426 struct comedi_async *async;
427 struct comedi_cmd *cmd;
431 s = dev->read_subdev;
436 /* figure out how many samples are in fifo */
437 hw_counter = i8254_read(dev->iobase + DAS16M1_8254_FIRST, 0, 1);
438 /* make sure hardware counter reading is not bogus due to initial value
439 * not having been loaded yet */
440 if (devpriv->adc_count == 0 && hw_counter == devpriv->initial_hw_count) {
443 /* The calculation of num_samples looks odd, but it uses the following facts.
444 * 16 bit hardware counter is initialized with value of zero (which really
445 * means 0x1000). The counter decrements by one on each conversion
446 * (when the counter decrements from zero it goes to 0xffff). num_samples
447 * is a 16 bit variable, so it will roll over in a similar fashion to the
448 * hardware counter. Work it out, and this is what you get. */
449 num_samples = -hw_counter - devpriv->adc_count;
451 /* check if we only need some of the points */
452 if (cmd->stop_src == TRIG_COUNT) {
453 if (num_samples > cmd->stop_arg * cmd->chanlist_len)
454 num_samples = cmd->stop_arg * cmd->chanlist_len;
456 /* make sure we dont try to get too many points if fifo has overrun */
457 if (num_samples > FIFO_SIZE)
458 num_samples = FIFO_SIZE;
459 insw(dev->iobase, devpriv->ai_buffer, num_samples);
460 munge_sample_array(devpriv->ai_buffer, num_samples);
461 cfc_write_array_to_buffer(s, devpriv->ai_buffer,
462 num_samples * sizeof(short));
463 devpriv->adc_count += num_samples;
465 if (cmd->stop_src == TRIG_COUNT) {
466 if (devpriv->adc_count >= cmd->stop_arg * cmd->chanlist_len) { /* end of acquisition */
467 das16m1_cancel(dev, s);
468 async->events |= COMEDI_CB_EOA;
472 /* this probably won't catch overruns since the card doesn't generate
473 * overrun interrupts, but we might as well try */
474 if (status & OVRUN) {
475 das16m1_cancel(dev, s);
476 async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
477 comedi_error(dev, "fifo overflow");
480 comedi_event(dev, s);
484 static int das16m1_poll(struct comedi_device *dev, struct comedi_subdevice *s)
489 /* prevent race with interrupt handler */
490 spin_lock_irqsave(&dev->spinlock, flags);
491 status = inb(dev->iobase + DAS16M1_CS);
492 das16m1_handler(dev, status);
493 spin_unlock_irqrestore(&dev->spinlock, flags);
495 return s->async->buf_write_count - s->async->buf_read_count;
498 static irqreturn_t das16m1_interrupt(int irq, void *d)
501 struct comedi_device *dev = d;
503 if (!dev->attached) {
504 comedi_error(dev, "premature interrupt");
507 /* prevent race with comedi_poll() */
508 spin_lock(&dev->spinlock);
510 status = inb(dev->iobase + DAS16M1_CS);
512 if ((status & (IRQDATA | OVRUN)) == 0) {
513 comedi_error(dev, "spurious interrupt");
514 spin_unlock(&dev->spinlock);
518 das16m1_handler(dev, status);
520 /* clear interrupt */
521 outb(0, dev->iobase + DAS16M1_CLEAR_INTR);
523 spin_unlock(&dev->spinlock);
527 static int das16m1_irq_bits(unsigned int irq)
568 static int das16m1_attach(struct comedi_device *dev,
569 struct comedi_devconfig *it)
571 struct das16m1_private_struct *devpriv;
572 struct comedi_subdevice *s;
576 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
579 dev->private = devpriv;
581 ret = comedi_request_region(dev, it->options[0], DAS16M1_SIZE);
584 /* Request an additional region for the 8255 */
585 ret = __comedi_request_region(dev, dev->iobase + DAS16M1_82C55,
589 devpriv->extra_iobase = dev->iobase + DAS16M1_82C55;
591 /* now for the irq */
592 irq = it->options[1];
593 /* make sure it is valid */
594 if (das16m1_irq_bits(irq) >= 0) {
595 ret = request_irq(irq, das16m1_interrupt, 0,
596 dev->driver->driver_name, dev);
602 } else if (irq == 0) {
606 comedi_error(dev, "invalid irq\n"
607 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
611 ret = comedi_alloc_subdevices(dev, 4);
615 s = &dev->subdevices[0];
616 dev->read_subdev = s;
618 s->type = COMEDI_SUBD_AI;
619 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
621 s->subdev_flags = SDF_DIFF;
622 s->len_chanlist = 256;
623 s->maxdata = (1 << 12) - 1;
624 s->range_table = &range_das16m1;
625 s->insn_read = das16m1_ai_rinsn;
626 s->do_cmdtest = das16m1_cmd_test;
627 s->do_cmd = das16m1_cmd_exec;
628 s->cancel = das16m1_cancel;
629 s->poll = das16m1_poll;
631 s = &dev->subdevices[1];
633 s->type = COMEDI_SUBD_DI;
634 s->subdev_flags = SDF_READABLE;
637 s->range_table = &range_digital;
638 s->insn_bits = das16m1_di_rbits;
640 s = &dev->subdevices[2];
642 s->type = COMEDI_SUBD_DO;
643 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
646 s->range_table = &range_digital;
647 s->insn_bits = das16m1_do_wbits;
649 s = &dev->subdevices[3];
651 ret = subdev_8255_init(dev, s, NULL, devpriv->extra_iobase);
655 /* disable upper half of hardware conversion counter so it doesn't mess with us */
656 outb(TOTAL_CLEAR, dev->iobase + DAS16M1_8254_FIRST_CNTRL);
658 /* initialize digital output lines */
659 outb(devpriv->do_bits, dev->iobase + DAS16M1_DIO);
661 /* set the interrupt level */
663 devpriv->control_state = das16m1_irq_bits(dev->irq);
665 devpriv->control_state = 0;
666 outb(devpriv->control_state, dev->iobase + DAS16M1_INTR_CONTROL);
671 static void das16m1_detach(struct comedi_device *dev)
673 struct das16m1_private_struct *devpriv = dev->private;
675 comedi_spriv_free(dev, 3);
676 if (devpriv && devpriv->extra_iobase)
677 release_region(devpriv->extra_iobase, DAS16M1_SIZE2);
678 comedi_legacy_detach(dev);
681 static struct comedi_driver das16m1_driver = {
682 .driver_name = "das16m1",
683 .module = THIS_MODULE,
684 .attach = das16m1_attach,
685 .detach = das16m1_detach,
687 module_comedi_driver(das16m1_driver);
689 MODULE_AUTHOR("Comedi http://www.comedi.org");
690 MODULE_DESCRIPTION("Comedi low-level driver");
691 MODULE_LICENSE("GPL");