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.
23 Description: CIO-DAS16/M1
24 Author: Frank Mori Hess <fmhess@users.sourceforge.net>
25 Devices: [Measurement Computing] CIO-DAS16/M1 (das16m1)
28 This driver supports a single board - the CIO-DAS16/M1.
29 As far as I know, there are no other boards that have
30 the same register layout. Even the CIO-DAS16/M1/16 is
31 significantly different.
33 I was _barely_ able to reach the full 1 MHz capability
34 of this board, using a hard real-time interrupt
35 (set the TRIG_RT flag in your struct comedi_cmd and use
36 rtlinux or RTAI). The board can't do dma, so the bottleneck is
37 pulling the data across the ISA bus. I timed the interrupt
38 handler, and it took my computer ~470 microseconds to pull 512
39 samples from the board. So at 1 Mhz sampling rate,
40 expect your CPU to be spending almost all of its
41 time in the interrupt handler.
43 This board has some unusual restrictions for its channel/gain list. If the
44 list has 2 or more channels in it, then two conditions must be satisfied:
45 (1) - even/odd channels must appear at even/odd indices in the list
46 (2) - the list must have an even number of entries.
50 [1] - irq (optional, but you probably want it)
52 irq can be omitted, although the cmd interface will not work without it.
55 #include <linux/module.h>
56 #include <linux/slab.h>
57 #include <linux/interrupt.h>
58 #include "../comedidev.h"
61 #include "comedi_8254.h"
63 #define DAS16M1_SIZE2 8
65 #define FIFO_SIZE 1024 /* 1024 sample fifo */
72 0 a/d bits 0-3, mux start 12 bit
73 1 a/d bits 4-11 unused
76 4 unused clear interrupt
78 6 channel/gain queue address
79 7 channel/gain queue data
87 #define DAS16M1_AI 0 /* 16-bit wide register */
88 #define AI_CHAN(x) ((x) & 0xf)
90 #define EXT_TRIG_BIT 0x1
94 #define DAS16M1_CLEAR_INTR 4
95 #define DAS16M1_INTR_CONTROL 5
98 #define PACER_MASK 0x3
100 #define DAS16M1_QUEUE_ADDR 6
101 #define DAS16M1_QUEUE_DATA 7
102 #define Q_CHAN(x) ((x) & 0x7)
103 #define Q_RANGE(x) (((x) & 0xf) << 4)
104 #define UNIPOLAR 0x40
105 #define DAS16M1_8254_FIRST 0x8
106 #define DAS16M1_8254_SECOND 0xc
107 #define DAS16M1_82C55 0x400
108 #define DAS16M1_8254_THIRD 0x404
110 static const struct comedi_lrange range_das16m1
= {
124 struct das16m1_private_struct
{
125 struct comedi_8254
*counter
;
126 unsigned int control_state
;
127 unsigned int adc_count
; /* number of samples completed */
128 /* initial value in lower half of hardware conversion counter,
129 * needed to keep track of whether new count has been loaded into
130 * counter yet (loaded by first sample conversion) */
131 u16 initial_hw_count
;
132 unsigned short ai_buffer
[FIFO_SIZE
];
133 unsigned long extra_iobase
;
136 static inline unsigned short munge_sample(unsigned short data
)
138 return (data
>> 4) & 0xfff;
141 static void munge_sample_array(unsigned short *array
, unsigned int num_elements
)
145 for (i
= 0; i
< num_elements
; i
++)
146 array
[i
] = munge_sample(array
[i
]);
149 static int das16m1_ai_check_chanlist(struct comedi_device
*dev
,
150 struct comedi_subdevice
*s
,
151 struct comedi_cmd
*cmd
)
155 if (cmd
->chanlist_len
== 1)
158 if ((cmd
->chanlist_len
% 2) != 0) {
159 dev_dbg(dev
->class_dev
,
160 "chanlist must be of even length or length 1\n");
164 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
165 unsigned int chan
= CR_CHAN(cmd
->chanlist
[i
]);
167 if ((i
% 2) != (chan
% 2)) {
168 dev_dbg(dev
->class_dev
,
169 "even/odd channels must go have even/odd chanlist indices\n");
177 static int das16m1_cmd_test(struct comedi_device
*dev
,
178 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
182 /* Step 1 : check if triggers are trivially valid */
184 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
| TRIG_EXT
);
185 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
186 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
187 TRIG_TIMER
| TRIG_EXT
);
188 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
189 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
194 /* Step 2a : make sure trigger sources are unique */
196 err
|= comedi_check_trigger_is_unique(cmd
->start_src
);
197 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
198 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
200 /* Step 2b : and mutually compatible */
205 /* Step 3: check if arguments are trivially valid */
207 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
209 if (cmd
->scan_begin_src
== TRIG_FOLLOW
) /* internal trigger */
210 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
212 if (cmd
->convert_src
== TRIG_TIMER
)
213 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
, 1000);
215 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
218 if (cmd
->stop_src
== TRIG_COUNT
)
219 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
221 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
226 /* step 4: fix up arguments */
228 if (cmd
->convert_src
== TRIG_TIMER
) {
229 unsigned int arg
= cmd
->convert_arg
;
231 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
232 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
238 /* Step 5: check channel list if it exists */
239 if (cmd
->chanlist
&& cmd
->chanlist_len
> 0)
240 err
|= das16m1_ai_check_chanlist(dev
, s
, cmd
);
248 static int das16m1_cmd_exec(struct comedi_device
*dev
,
249 struct comedi_subdevice
*s
)
251 struct das16m1_private_struct
*devpriv
= dev
->private;
252 struct comedi_async
*async
= s
->async
;
253 struct comedi_cmd
*cmd
= &async
->cmd
;
254 unsigned int byte
, i
;
256 /* disable interrupts and internal pacer */
257 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
258 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
260 /* set software count */
261 devpriv
->adc_count
= 0;
264 * Initialize lower half of hardware counter, used to determine how
265 * many samples are in fifo. Value doesn't actually load into counter
266 * until counter's next clock (the next a/d conversion).
268 comedi_8254_set_mode(devpriv
->counter
, 1, I8254_MODE2
| I8254_BINARY
);
269 comedi_8254_write(devpriv
->counter
, 1, 0);
272 * Remember current reading of counter so we know when counter has
273 * actually been loaded.
275 devpriv
->initial_hw_count
= comedi_8254_read(devpriv
->counter
, 1);
277 /* setup channel/gain queue */
278 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
279 outb(i
, dev
->iobase
+ DAS16M1_QUEUE_ADDR
);
281 Q_CHAN(CR_CHAN(cmd
->chanlist
[i
])) |
282 Q_RANGE(CR_RANGE(cmd
->chanlist
[i
]));
283 outb(byte
, dev
->iobase
+ DAS16M1_QUEUE_DATA
);
286 /* enable interrupts and set internal pacer counter mode and counts */
287 devpriv
->control_state
&= ~PACER_MASK
;
288 if (cmd
->convert_src
== TRIG_TIMER
) {
289 comedi_8254_update_divisors(dev
->pacer
);
290 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
291 devpriv
->control_state
|= INT_PACER
;
292 } else { /* TRIG_EXT */
293 devpriv
->control_state
|= EXT_PACER
;
296 /* set control & status register */
298 /* if we are using external start trigger (also board dislikes having
299 * both start and conversion triggers external simultaneously) */
300 if (cmd
->start_src
== TRIG_EXT
&& cmd
->convert_src
!= TRIG_EXT
)
301 byte
|= EXT_TRIG_BIT
;
303 outb(byte
, dev
->iobase
+ DAS16M1_CS
);
304 /* clear interrupt bit */
305 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
307 devpriv
->control_state
|= INTE
;
308 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
313 static int das16m1_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
315 struct das16m1_private_struct
*devpriv
= dev
->private;
317 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
318 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
323 static int das16m1_ai_eoc(struct comedi_device
*dev
,
324 struct comedi_subdevice
*s
,
325 struct comedi_insn
*insn
,
326 unsigned long context
)
330 status
= inb(dev
->iobase
+ DAS16M1_CS
);
331 if (status
& IRQDATA
)
336 static int das16m1_ai_rinsn(struct comedi_device
*dev
,
337 struct comedi_subdevice
*s
,
338 struct comedi_insn
*insn
, unsigned int *data
)
340 struct das16m1_private_struct
*devpriv
= dev
->private;
345 /* disable interrupts and internal pacer */
346 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
347 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
349 /* setup channel/gain queue */
350 outb(0, dev
->iobase
+ DAS16M1_QUEUE_ADDR
);
352 Q_CHAN(CR_CHAN(insn
->chanspec
)) | Q_RANGE(CR_RANGE(insn
->chanspec
));
353 outb(byte
, dev
->iobase
+ DAS16M1_QUEUE_DATA
);
355 for (n
= 0; n
< insn
->n
; n
++) {
356 /* clear IRQDATA bit */
357 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
358 /* trigger conversion */
359 outb(0, dev
->iobase
);
361 ret
= comedi_timeout(dev
, s
, insn
, das16m1_ai_eoc
, 0);
365 data
[n
] = munge_sample(inw(dev
->iobase
));
371 static int das16m1_di_rbits(struct comedi_device
*dev
,
372 struct comedi_subdevice
*s
,
373 struct comedi_insn
*insn
, unsigned int *data
)
377 bits
= inb(dev
->iobase
+ DAS16M1_DIO
) & 0xf;
384 static int das16m1_do_wbits(struct comedi_device
*dev
,
385 struct comedi_subdevice
*s
,
386 struct comedi_insn
*insn
,
389 if (comedi_dio_update_state(s
, data
))
390 outb(s
->state
, dev
->iobase
+ DAS16M1_DIO
);
397 static void das16m1_handler(struct comedi_device
*dev
, unsigned int status
)
399 struct das16m1_private_struct
*devpriv
= dev
->private;
400 struct comedi_subdevice
*s
;
401 struct comedi_async
*async
;
402 struct comedi_cmd
*cmd
;
406 s
= dev
->read_subdev
;
410 /* figure out how many samples are in fifo */
411 hw_counter
= comedi_8254_read(devpriv
->counter
, 1);
412 /* make sure hardware counter reading is not bogus due to initial value
413 * not having been loaded yet */
414 if (devpriv
->adc_count
== 0 && hw_counter
== devpriv
->initial_hw_count
) {
417 /* The calculation of num_samples looks odd, but it uses the following facts.
418 * 16 bit hardware counter is initialized with value of zero (which really
419 * means 0x1000). The counter decrements by one on each conversion
420 * (when the counter decrements from zero it goes to 0xffff). num_samples
421 * is a 16 bit variable, so it will roll over in a similar fashion to the
422 * hardware counter. Work it out, and this is what you get. */
423 num_samples
= -hw_counter
- devpriv
->adc_count
;
425 /* check if we only need some of the points */
426 if (cmd
->stop_src
== TRIG_COUNT
) {
427 if (num_samples
> cmd
->stop_arg
* cmd
->chanlist_len
)
428 num_samples
= cmd
->stop_arg
* cmd
->chanlist_len
;
430 /* make sure we dont try to get too many points if fifo has overrun */
431 if (num_samples
> FIFO_SIZE
)
432 num_samples
= FIFO_SIZE
;
433 insw(dev
->iobase
, devpriv
->ai_buffer
, num_samples
);
434 munge_sample_array(devpriv
->ai_buffer
, num_samples
);
435 comedi_buf_write_samples(s
, devpriv
->ai_buffer
, num_samples
);
436 devpriv
->adc_count
+= num_samples
;
438 if (cmd
->stop_src
== TRIG_COUNT
) {
439 if (devpriv
->adc_count
>= cmd
->stop_arg
* cmd
->chanlist_len
) {
440 /* end of acquisition */
441 async
->events
|= COMEDI_CB_EOA
;
445 /* this probably won't catch overruns since the card doesn't generate
446 * overrun interrupts, but we might as well try */
447 if (status
& OVRUN
) {
448 async
->events
|= COMEDI_CB_ERROR
;
449 dev_err(dev
->class_dev
, "fifo overflow\n");
452 comedi_handle_events(dev
, s
);
455 static int das16m1_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
460 /* prevent race with interrupt handler */
461 spin_lock_irqsave(&dev
->spinlock
, flags
);
462 status
= inb(dev
->iobase
+ DAS16M1_CS
);
463 das16m1_handler(dev
, status
);
464 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
466 return comedi_buf_n_bytes_ready(s
);
469 static irqreturn_t
das16m1_interrupt(int irq
, void *d
)
472 struct comedi_device
*dev
= d
;
474 if (!dev
->attached
) {
475 dev_err(dev
->class_dev
, "premature interrupt\n");
478 /* prevent race with comedi_poll() */
479 spin_lock(&dev
->spinlock
);
481 status
= inb(dev
->iobase
+ DAS16M1_CS
);
483 if ((status
& (IRQDATA
| OVRUN
)) == 0) {
484 dev_err(dev
->class_dev
, "spurious interrupt\n");
485 spin_unlock(&dev
->spinlock
);
489 das16m1_handler(dev
, status
);
491 /* clear interrupt */
492 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
494 spin_unlock(&dev
->spinlock
);
498 static int das16m1_irq_bits(unsigned int irq
)
527 static int das16m1_attach(struct comedi_device
*dev
,
528 struct comedi_devconfig
*it
)
530 struct das16m1_private_struct
*devpriv
;
531 struct comedi_subdevice
*s
;
534 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
538 ret
= comedi_request_region(dev
, it
->options
[0], 0x10);
541 /* Request an additional region for the 8255 */
542 ret
= __comedi_request_region(dev
, dev
->iobase
+ DAS16M1_82C55
,
546 devpriv
->extra_iobase
= dev
->iobase
+ DAS16M1_82C55
;
548 /* only irqs 2, 3, 4, 5, 6, 7, 10, 11, 12, 14, and 15 are valid */
549 if ((1 << it
->options
[1]) & 0xdcfc) {
550 ret
= request_irq(it
->options
[1], das16m1_interrupt
, 0,
551 dev
->board_name
, dev
);
553 dev
->irq
= it
->options
[1];
556 dev
->pacer
= comedi_8254_init(dev
->iobase
+ DAS16M1_8254_SECOND
,
557 I8254_OSC_BASE_10MHZ
, I8254_IO8
, 0);
561 devpriv
->counter
= comedi_8254_init(dev
->iobase
+ DAS16M1_8254_FIRST
,
563 if (!devpriv
->counter
)
566 ret
= comedi_alloc_subdevices(dev
, 4);
570 s
= &dev
->subdevices
[0];
572 s
->type
= COMEDI_SUBD_AI
;
573 s
->subdev_flags
= SDF_READABLE
| SDF_DIFF
;
575 s
->maxdata
= (1 << 12) - 1;
576 s
->range_table
= &range_das16m1
;
577 s
->insn_read
= das16m1_ai_rinsn
;
579 dev
->read_subdev
= s
;
580 s
->subdev_flags
|= SDF_CMD_READ
;
581 s
->len_chanlist
= 256;
582 s
->do_cmdtest
= das16m1_cmd_test
;
583 s
->do_cmd
= das16m1_cmd_exec
;
584 s
->cancel
= das16m1_cancel
;
585 s
->poll
= das16m1_poll
;
588 s
= &dev
->subdevices
[1];
590 s
->type
= COMEDI_SUBD_DI
;
591 s
->subdev_flags
= SDF_READABLE
;
594 s
->range_table
= &range_digital
;
595 s
->insn_bits
= das16m1_di_rbits
;
597 s
= &dev
->subdevices
[2];
599 s
->type
= COMEDI_SUBD_DO
;
600 s
->subdev_flags
= SDF_WRITABLE
;
603 s
->range_table
= &range_digital
;
604 s
->insn_bits
= das16m1_do_wbits
;
606 s
= &dev
->subdevices
[3];
608 ret
= subdev_8255_init(dev
, s
, NULL
, DAS16M1_82C55
);
612 /* initialize digital output lines */
613 outb(0, dev
->iobase
+ DAS16M1_DIO
);
615 /* set the interrupt level */
616 devpriv
->control_state
= das16m1_irq_bits(dev
->irq
) << 4;
617 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
622 static void das16m1_detach(struct comedi_device
*dev
)
624 struct das16m1_private_struct
*devpriv
= dev
->private;
627 if (devpriv
->extra_iobase
)
628 release_region(devpriv
->extra_iobase
, DAS16M1_SIZE2
);
629 kfree(devpriv
->counter
);
631 comedi_legacy_detach(dev
);
634 static struct comedi_driver das16m1_driver
= {
635 .driver_name
= "das16m1",
636 .module
= THIS_MODULE
,
637 .attach
= das16m1_attach
,
638 .detach
= das16m1_detach
,
640 module_comedi_driver(das16m1_driver
);
642 MODULE_AUTHOR("Comedi http://www.comedi.org");
643 MODULE_DESCRIPTION("Comedi low-level driver");
644 MODULE_LICENSE("GPL");