2 * Comedi driver for CIO-DAS16/M1
3 * Author: Frank Mori Hess, based on code from the das16 driver.
4 * Copyright (C) 2001 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.
22 * Description: CIO-DAS16/M1
23 * Author: Frank Mori Hess <fmhess@users.sourceforge.net>
24 * Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
27 * This driver supports a single board - the CIO-DAS16/M1. As far as I know,
28 * there are no other boards that have the same register layout. Even the
29 * CIO-DAS16/M1/16 is significantly different.
31 * I was _barely_ able to reach the full 1 MHz capability of this board, using
32 * a hard real-time interrupt (set the TRIG_RT flag in your struct comedi_cmd
33 * and use rtlinux or RTAI). The board can't do dma, so the bottleneck is
34 * pulling the data across the ISA bus. I timed the interrupt handler, and it
35 * took my computer ~470 microseconds to pull 512 samples from the board. So
36 * at 1 Mhz sampling rate, expect your CPU to be spending almost all of its
37 * time in the interrupt handler.
39 * This board has some unusual restrictions for its channel/gain list. If the
40 * list has 2 or more channels in it, then two conditions must be satisfied:
41 * (1) - even/odd channels must appear at even/odd indices in the list
42 * (2) - the list must have an even number of entries.
44 * Configuration options:
45 * [0] - base io address
46 * [1] - irq (optional, but you probably want it)
48 * irq can be omitted, although the cmd interface will not work without it.
51 #include <linux/module.h>
52 #include <linux/slab.h>
53 #include <linux/interrupt.h>
54 #include "../comedidev.h"
57 #include "comedi_8254.h"
59 #define DAS16M1_SIZE2 8
61 #define FIFO_SIZE 1024 /* 1024 sample fifo */
64 * Register map (dev->iobase)
66 #define DAS16M1_AI_REG 0x00 /* 16-bit register */
67 #define DAS16M1_AI_TO_CHAN(x) (((x) >> 0) & 0xf)
68 #define DAS16M1_AI_TO_SAMPLE(x) (((x) >> 4) & 0xfff)
69 #define DAS16M1_CS_REG 0x02
70 #define DAS16M1_CS_EXT_TRIG BIT(0)
71 #define DAS16M1_CS_OVRUN BIT(5)
72 #define DAS16M1_CS_IRQDATA BIT(7)
73 #define DAS16M1_DI_REG 0x03
74 #define DAS16M1_DO_REG 0x03
75 #define DAS16M1_CLR_INTR_REG 0x04
76 #define DAS16M1_INTR_CTRL_REG 0x05
77 #define DAS16M1_INTR_CTRL_PACER(x) (((x) & 0x3) << 0)
78 #define DAS16M1_INTR_CTRL_PACER_EXT DAS16M1_INTR_CTRL_PACER(2)
79 #define DAS16M1_INTR_CTRL_PACER_INT DAS16M1_INTR_CTRL_PACER(3)
80 #define DAS16M1_INTR_CTRL_PACER_MASK DAS16M1_INTR_CTRL_PACER(3)
81 #define DAS16M1_INTR_CTRL_IRQ(x) (((x) & 0x7) << 4)
82 #define DAS16M1_INTR_CTRL_INTE BIT(7)
83 #define DAS16M1_Q_ADDR_REG 0x06
84 #define DAS16M1_Q_REG 0x07
85 #define DAS16M1_Q_CHAN(x) (((x) & 0x7) << 0)
86 #define DAS16M1_Q_RANGE(x) (((x) & 0xf) << 4)
87 #define DAS16M1_8254_FIRST 0x8
88 #define DAS16M1_8254_SECOND 0xc
89 #define DAS16M1_82C55 0x400
90 #define DAS16M1_8254_THIRD 0x404
92 static const struct comedi_lrange range_das16m1
= {
106 struct das16m1_private_struct
{
107 struct comedi_8254
*counter
;
108 unsigned int intr_ctrl
;
109 unsigned int adc_count
;
110 u16 initial_hw_count
;
111 unsigned short ai_buffer
[FIFO_SIZE
];
112 unsigned long extra_iobase
;
115 static void munge_sample_array(unsigned short *array
, unsigned int num_elements
)
119 for (i
= 0; i
< num_elements
; i
++)
120 array
[i
] = DAS16M1_AI_TO_SAMPLE(array
[i
]);
123 static int das16m1_ai_check_chanlist(struct comedi_device
*dev
,
124 struct comedi_subdevice
*s
,
125 struct comedi_cmd
*cmd
)
129 if (cmd
->chanlist_len
== 1)
132 if ((cmd
->chanlist_len
% 2) != 0) {
133 dev_dbg(dev
->class_dev
,
134 "chanlist must be of even length or length 1\n");
138 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
139 unsigned int chan
= CR_CHAN(cmd
->chanlist
[i
]);
141 if ((i
% 2) != (chan
% 2)) {
142 dev_dbg(dev
->class_dev
,
143 "even/odd channels must go have even/odd chanlist indices\n");
151 static int das16m1_cmd_test(struct comedi_device
*dev
,
152 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
156 /* Step 1 : check if triggers are trivially valid */
158 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
| TRIG_EXT
);
159 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
160 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
161 TRIG_TIMER
| TRIG_EXT
);
162 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
163 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
168 /* Step 2a : make sure trigger sources are unique */
170 err
|= comedi_check_trigger_is_unique(cmd
->start_src
);
171 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
172 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
174 /* Step 2b : and mutually compatible */
179 /* Step 3: check if arguments are trivially valid */
181 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
183 if (cmd
->scan_begin_src
== TRIG_FOLLOW
) /* internal trigger */
184 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
186 if (cmd
->convert_src
== TRIG_TIMER
)
187 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
, 1000);
189 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
192 if (cmd
->stop_src
== TRIG_COUNT
)
193 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
195 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
200 /* step 4: fix up arguments */
202 if (cmd
->convert_src
== TRIG_TIMER
) {
203 unsigned int arg
= cmd
->convert_arg
;
205 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
206 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
212 /* Step 5: check channel list if it exists */
213 if (cmd
->chanlist
&& cmd
->chanlist_len
> 0)
214 err
|= das16m1_ai_check_chanlist(dev
, s
, cmd
);
222 static int das16m1_cmd_exec(struct comedi_device
*dev
,
223 struct comedi_subdevice
*s
)
225 struct das16m1_private_struct
*devpriv
= dev
->private;
226 struct comedi_async
*async
= s
->async
;
227 struct comedi_cmd
*cmd
= &async
->cmd
;
228 unsigned int byte
, i
;
230 /* set software count */
231 devpriv
->adc_count
= 0;
234 * Initialize lower half of hardware counter, used to determine how
235 * many samples are in fifo. Value doesn't actually load into counter
236 * until counter's next clock (the next a/d conversion).
238 comedi_8254_set_mode(devpriv
->counter
, 1, I8254_MODE2
| I8254_BINARY
);
239 comedi_8254_write(devpriv
->counter
, 1, 0);
242 * Remember current reading of counter so we know when counter has
243 * actually been loaded.
245 devpriv
->initial_hw_count
= comedi_8254_read(devpriv
->counter
, 1);
247 /* setup channel/gain queue */
248 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
249 outb(i
, dev
->iobase
+ DAS16M1_Q_ADDR_REG
);
251 DAS16M1_Q_CHAN(CR_CHAN(cmd
->chanlist
[i
])) |
252 DAS16M1_Q_RANGE(CR_RANGE(cmd
->chanlist
[i
]));
253 outb(byte
, dev
->iobase
+ DAS16M1_Q_REG
);
256 /* enable interrupts and set internal pacer counter mode and counts */
257 devpriv
->intr_ctrl
&= ~DAS16M1_INTR_CTRL_PACER_MASK
;
258 if (cmd
->convert_src
== TRIG_TIMER
) {
259 comedi_8254_update_divisors(dev
->pacer
);
260 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
261 devpriv
->intr_ctrl
|= DAS16M1_INTR_CTRL_PACER_INT
;
262 } else { /* TRIG_EXT */
263 devpriv
->intr_ctrl
|= DAS16M1_INTR_CTRL_PACER_EXT
;
266 /* set control & status register */
269 * If we are using external start trigger (also board dislikes having
270 * both start and conversion triggers external simultaneously).
272 if (cmd
->start_src
== TRIG_EXT
&& cmd
->convert_src
!= TRIG_EXT
)
273 byte
|= DAS16M1_CS_EXT_TRIG
;
275 outb(byte
, dev
->iobase
+ DAS16M1_CS_REG
);
277 /* clear interrupt */
278 outb(0, dev
->iobase
+ DAS16M1_CLR_INTR_REG
);
280 devpriv
->intr_ctrl
|= DAS16M1_INTR_CTRL_INTE
;
281 outb(devpriv
->intr_ctrl
, dev
->iobase
+ DAS16M1_INTR_CTRL_REG
);
286 static int das16m1_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
288 struct das16m1_private_struct
*devpriv
= dev
->private;
290 /* disable interrupts and pacer */
291 devpriv
->intr_ctrl
&= ~(DAS16M1_INTR_CTRL_INTE
|
292 DAS16M1_INTR_CTRL_PACER_MASK
);
293 outb(devpriv
->intr_ctrl
, dev
->iobase
+ DAS16M1_INTR_CTRL_REG
);
298 static int das16m1_ai_eoc(struct comedi_device
*dev
,
299 struct comedi_subdevice
*s
,
300 struct comedi_insn
*insn
,
301 unsigned long context
)
305 status
= inb(dev
->iobase
+ DAS16M1_CS_REG
);
306 if (status
& DAS16M1_CS_IRQDATA
)
311 static int das16m1_ai_rinsn(struct comedi_device
*dev
,
312 struct comedi_subdevice
*s
,
313 struct comedi_insn
*insn
, unsigned int *data
)
319 /* setup channel/gain queue */
320 outb(0, dev
->iobase
+ DAS16M1_Q_ADDR_REG
);
321 byte
= DAS16M1_Q_CHAN(CR_CHAN(insn
->chanspec
)) |
322 DAS16M1_Q_RANGE(CR_RANGE(insn
->chanspec
));
323 outb(byte
, dev
->iobase
+ DAS16M1_Q_REG
);
325 for (n
= 0; n
< insn
->n
; n
++) {
328 /* clear interrupt */
329 outb(0, dev
->iobase
+ DAS16M1_CLR_INTR_REG
);
330 /* trigger conversion */
331 outb(0, dev
->iobase
+ DAS16M1_AI_REG
);
333 ret
= comedi_timeout(dev
, s
, insn
, das16m1_ai_eoc
, 0);
337 val
= inw(dev
->iobase
+ DAS16M1_AI_REG
);
338 data
[n
] = DAS16M1_AI_TO_SAMPLE(val
);
344 static int das16m1_di_rbits(struct comedi_device
*dev
,
345 struct comedi_subdevice
*s
,
346 struct comedi_insn
*insn
, unsigned int *data
)
350 bits
= inb(dev
->iobase
+ DAS16M1_DI_REG
) & 0xf;
357 static int das16m1_do_wbits(struct comedi_device
*dev
,
358 struct comedi_subdevice
*s
,
359 struct comedi_insn
*insn
,
362 if (comedi_dio_update_state(s
, data
))
363 outb(s
->state
, dev
->iobase
+ DAS16M1_DO_REG
);
370 static void das16m1_handler(struct comedi_device
*dev
, unsigned int status
)
372 struct das16m1_private_struct
*devpriv
= dev
->private;
373 struct comedi_subdevice
*s
;
374 struct comedi_async
*async
;
375 struct comedi_cmd
*cmd
;
379 s
= dev
->read_subdev
;
383 /* figure out how many samples are in fifo */
384 hw_counter
= comedi_8254_read(devpriv
->counter
, 1);
386 * Make sure hardware counter reading is not bogus due to initial
387 * value not having been loaded yet.
389 if (devpriv
->adc_count
== 0 &&
390 hw_counter
== devpriv
->initial_hw_count
) {
394 * The calculation of num_samples looks odd, but it uses the
395 * following facts. 16 bit hardware counter is initialized with
396 * value of zero (which really means 0x1000). The counter
397 * decrements by one on each conversion (when the counter
398 * decrements from zero it goes to 0xffff). num_samples is a
399 * 16 bit variable, so it will roll over in a similar fashion
400 * to the hardware counter. Work it out, and this is what you
403 num_samples
= -hw_counter
- devpriv
->adc_count
;
405 /* check if we only need some of the points */
406 if (cmd
->stop_src
== TRIG_COUNT
) {
407 if (num_samples
> cmd
->stop_arg
* cmd
->chanlist_len
)
408 num_samples
= cmd
->stop_arg
* cmd
->chanlist_len
;
410 /* make sure we dont try to get too many points if fifo has overrun */
411 if (num_samples
> FIFO_SIZE
)
412 num_samples
= FIFO_SIZE
;
413 insw(dev
->iobase
, devpriv
->ai_buffer
, num_samples
);
414 munge_sample_array(devpriv
->ai_buffer
, num_samples
);
415 comedi_buf_write_samples(s
, devpriv
->ai_buffer
, num_samples
);
416 devpriv
->adc_count
+= num_samples
;
418 if (cmd
->stop_src
== TRIG_COUNT
) {
419 if (devpriv
->adc_count
>= cmd
->stop_arg
* cmd
->chanlist_len
) {
420 /* end of acquisition */
421 async
->events
|= COMEDI_CB_EOA
;
426 * This probably won't catch overruns since the card doesn't generate
427 * overrun interrupts, but we might as well try.
429 if (status
& DAS16M1_CS_OVRUN
) {
430 async
->events
|= COMEDI_CB_ERROR
;
431 dev_err(dev
->class_dev
, "fifo overflow\n");
434 comedi_handle_events(dev
, s
);
437 static int das16m1_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
442 /* prevent race with interrupt handler */
443 spin_lock_irqsave(&dev
->spinlock
, flags
);
444 status
= inb(dev
->iobase
+ DAS16M1_CS_REG
);
445 das16m1_handler(dev
, status
);
446 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
448 return comedi_buf_n_bytes_ready(s
);
451 static irqreturn_t
das16m1_interrupt(int irq
, void *d
)
454 struct comedi_device
*dev
= d
;
456 if (!dev
->attached
) {
457 dev_err(dev
->class_dev
, "premature interrupt\n");
460 /* prevent race with comedi_poll() */
461 spin_lock(&dev
->spinlock
);
463 status
= inb(dev
->iobase
+ DAS16M1_CS_REG
);
465 if ((status
& (DAS16M1_CS_IRQDATA
| DAS16M1_CS_OVRUN
)) == 0) {
466 dev_err(dev
->class_dev
, "spurious interrupt\n");
467 spin_unlock(&dev
->spinlock
);
471 das16m1_handler(dev
, status
);
473 /* clear interrupt */
474 outb(0, dev
->iobase
+ DAS16M1_CLR_INTR_REG
);
476 spin_unlock(&dev
->spinlock
);
480 static int das16m1_irq_bits(unsigned int irq
)
509 static int das16m1_attach(struct comedi_device
*dev
,
510 struct comedi_devconfig
*it
)
512 struct das16m1_private_struct
*devpriv
;
513 struct comedi_subdevice
*s
;
516 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
520 ret
= comedi_request_region(dev
, it
->options
[0], 0x10);
523 /* Request an additional region for the 8255 */
524 ret
= __comedi_request_region(dev
, dev
->iobase
+ DAS16M1_82C55
,
528 devpriv
->extra_iobase
= dev
->iobase
+ DAS16M1_82C55
;
530 /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
531 if ((1 << it
->options
[1]) & 0xdcfc) {
532 ret
= request_irq(it
->options
[1], das16m1_interrupt
, 0,
533 dev
->board_name
, dev
);
535 dev
->irq
= it
->options
[1];
538 dev
->pacer
= comedi_8254_init(dev
->iobase
+ DAS16M1_8254_SECOND
,
539 I8254_OSC_BASE_10MHZ
, I8254_IO8
, 0);
543 devpriv
->counter
= comedi_8254_init(dev
->iobase
+ DAS16M1_8254_FIRST
,
545 if (!devpriv
->counter
)
548 ret
= comedi_alloc_subdevices(dev
, 4);
552 s
= &dev
->subdevices
[0];
554 s
->type
= COMEDI_SUBD_AI
;
555 s
->subdev_flags
= SDF_READABLE
| SDF_DIFF
;
557 s
->maxdata
= (1 << 12) - 1;
558 s
->range_table
= &range_das16m1
;
559 s
->insn_read
= das16m1_ai_rinsn
;
561 dev
->read_subdev
= s
;
562 s
->subdev_flags
|= SDF_CMD_READ
;
563 s
->len_chanlist
= 256;
564 s
->do_cmdtest
= das16m1_cmd_test
;
565 s
->do_cmd
= das16m1_cmd_exec
;
566 s
->cancel
= das16m1_cancel
;
567 s
->poll
= das16m1_poll
;
570 s
= &dev
->subdevices
[1];
572 s
->type
= COMEDI_SUBD_DI
;
573 s
->subdev_flags
= SDF_READABLE
;
576 s
->range_table
= &range_digital
;
577 s
->insn_bits
= das16m1_di_rbits
;
579 s
= &dev
->subdevices
[2];
581 s
->type
= COMEDI_SUBD_DO
;
582 s
->subdev_flags
= SDF_WRITABLE
;
585 s
->range_table
= &range_digital
;
586 s
->insn_bits
= das16m1_do_wbits
;
588 s
= &dev
->subdevices
[3];
590 ret
= subdev_8255_init(dev
, s
, NULL
, DAS16M1_82C55
);
594 /* initialize digital output lines */
595 outb(0, dev
->iobase
+ DAS16M1_DO_REG
);
597 /* set the interrupt level */
598 devpriv
->intr_ctrl
= DAS16M1_INTR_CTRL_IRQ(das16m1_irq_bits(dev
->irq
));
599 outb(devpriv
->intr_ctrl
, dev
->iobase
+ DAS16M1_INTR_CTRL_REG
);
604 static void das16m1_detach(struct comedi_device
*dev
)
606 struct das16m1_private_struct
*devpriv
= dev
->private;
609 if (devpriv
->extra_iobase
)
610 release_region(devpriv
->extra_iobase
, DAS16M1_SIZE2
);
611 kfree(devpriv
->counter
);
613 comedi_legacy_detach(dev
);
616 static struct comedi_driver das16m1_driver
= {
617 .driver_name
= "das16m1",
618 .module
= THIS_MODULE
,
619 .attach
= das16m1_attach
,
620 .detach
= das16m1_detach
,
622 module_comedi_driver(das16m1_driver
);
624 MODULE_AUTHOR("Comedi http://www.comedi.org");
625 MODULE_DESCRIPTION("Comedi low-level driver");
626 MODULE_LICENSE("GPL");