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 */
148 static inline short munge_sample(short data
)
150 return (data
>> 4) & 0xfff;
153 static void munge_sample_array(short *array
, unsigned int num_elements
)
157 for (i
= 0; i
< num_elements
; i
++)
158 array
[i
] = munge_sample(array
[i
]);
161 static int das16m1_cmd_test(struct comedi_device
*dev
,
162 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
164 struct das16m1_private_struct
*devpriv
= dev
->private;
165 unsigned int err
= 0, tmp
, i
;
167 /* Step 1 : check if triggers are trivially valid */
169 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
| TRIG_EXT
);
170 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
171 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_TIMER
| TRIG_EXT
);
172 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
173 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
178 /* Step 2a : make sure trigger sources are unique */
180 err
|= cfc_check_trigger_is_unique(cmd
->start_src
);
181 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
182 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
184 /* Step 2b : and mutually compatible */
189 /* Step 3: check if arguments are trivially valid */
191 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
193 if (cmd
->scan_begin_src
== TRIG_FOLLOW
) /* internal trigger */
194 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
196 if (cmd
->convert_src
== TRIG_TIMER
)
197 err
|= cfc_check_trigger_arg_min(&cmd
->convert_arg
, 1000);
199 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
201 if (cmd
->stop_src
== TRIG_COUNT
) {
202 /* any count is allowed */
205 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
211 /* step 4: fix up arguments */
213 if (cmd
->convert_src
== TRIG_TIMER
) {
214 tmp
= cmd
->convert_arg
;
215 /* calculate counter values that give desired timing */
216 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL
,
217 &(devpriv
->divisor1
),
218 &(devpriv
->divisor2
),
220 cmd
->flags
& TRIG_ROUND_MASK
);
221 if (tmp
!= cmd
->convert_arg
)
228 /* check chanlist against board's peculiarities */
229 if (cmd
->chanlist
&& cmd
->chanlist_len
> 1) {
230 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
231 /* even/odd channels must go into even/odd queue addresses */
232 if ((i
% 2) != (CR_CHAN(cmd
->chanlist
[i
]) % 2)) {
233 comedi_error(dev
, "bad chanlist:\n"
234 " even/odd channels must go have even/odd chanlist indices");
238 if ((cmd
->chanlist_len
% 2) != 0) {
240 "chanlist must be of even length or length 1");
251 /* This function takes a time in nanoseconds and sets the *
252 * 2 pacer clocks to the closest frequency possible. It also *
253 * returns the actual sampling period. */
254 static unsigned int das16m1_set_pacer(struct comedi_device
*dev
,
255 unsigned int ns
, int rounding_flags
)
257 struct das16m1_private_struct
*devpriv
= dev
->private;
259 i8253_cascade_ns_to_timer_2div(DAS16M1_XTAL
, &(devpriv
->divisor1
),
260 &(devpriv
->divisor2
), &ns
,
261 rounding_flags
& TRIG_ROUND_MASK
);
263 /* Write the values of ctr1 and ctr2 into counters 1 and 2 */
264 i8254_load(dev
->iobase
+ DAS16M1_8254_SECOND
, 0, 1, devpriv
->divisor1
,
266 i8254_load(dev
->iobase
+ DAS16M1_8254_SECOND
, 0, 2, devpriv
->divisor2
,
272 static int das16m1_cmd_exec(struct comedi_device
*dev
,
273 struct comedi_subdevice
*s
)
275 struct das16m1_private_struct
*devpriv
= dev
->private;
276 struct comedi_async
*async
= s
->async
;
277 struct comedi_cmd
*cmd
= &async
->cmd
;
278 unsigned int byte
, i
;
281 comedi_error(dev
, "irq required to execute comedi_cmd");
285 /* disable interrupts and internal pacer */
286 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
287 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
289 /* set software count */
290 devpriv
->adc_count
= 0;
291 /* Initialize lower half of hardware counter, used to determine how
292 * many samples are in fifo. Value doesn't actually load into counter
293 * until counter's next clock (the next a/d conversion) */
294 i8254_load(dev
->iobase
+ DAS16M1_8254_FIRST
, 0, 1, 0, 2);
295 /* remember current reading of counter so we know when counter has
296 * actually been loaded */
297 devpriv
->initial_hw_count
=
298 i8254_read(dev
->iobase
+ DAS16M1_8254_FIRST
, 0, 1);
299 /* setup channel/gain queue */
300 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
301 outb(i
, dev
->iobase
+ DAS16M1_QUEUE_ADDR
);
303 Q_CHAN(CR_CHAN(cmd
->chanlist
[i
])) |
304 Q_RANGE(CR_RANGE(cmd
->chanlist
[i
]));
305 outb(byte
, dev
->iobase
+ DAS16M1_QUEUE_DATA
);
308 /* set counter mode and counts */
310 das16m1_set_pacer(dev
, cmd
->convert_arg
,
311 cmd
->flags
& TRIG_ROUND_MASK
);
313 /* set control & status register */
315 /* if we are using external start trigger (also board dislikes having
316 * both start and conversion triggers external simultaneously) */
317 if (cmd
->start_src
== TRIG_EXT
&& cmd
->convert_src
!= TRIG_EXT
)
318 byte
|= EXT_TRIG_BIT
;
320 outb(byte
, dev
->iobase
+ DAS16M1_CS
);
321 /* clear interrupt bit */
322 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
324 /* enable interrupts and internal pacer */
325 devpriv
->control_state
&= ~PACER_MASK
;
326 if (cmd
->convert_src
== TRIG_TIMER
)
327 devpriv
->control_state
|= INT_PACER
;
329 devpriv
->control_state
|= EXT_PACER
;
331 devpriv
->control_state
|= INTE
;
332 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
337 static int das16m1_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
339 struct das16m1_private_struct
*devpriv
= dev
->private;
341 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
342 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
347 static int das16m1_ai_rinsn(struct comedi_device
*dev
,
348 struct comedi_subdevice
*s
,
349 struct comedi_insn
*insn
, unsigned int *data
)
351 struct das16m1_private_struct
*devpriv
= dev
->private;
354 const int timeout
= 1000;
356 /* disable interrupts and internal pacer */
357 devpriv
->control_state
&= ~INTE
& ~PACER_MASK
;
358 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
360 /* setup channel/gain queue */
361 outb(0, dev
->iobase
+ DAS16M1_QUEUE_ADDR
);
363 Q_CHAN(CR_CHAN(insn
->chanspec
)) | Q_RANGE(CR_RANGE(insn
->chanspec
));
364 outb(byte
, dev
->iobase
+ DAS16M1_QUEUE_DATA
);
366 for (n
= 0; n
< insn
->n
; n
++) {
367 /* clear IRQDATA bit */
368 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
369 /* trigger conversion */
370 outb(0, dev
->iobase
);
372 for (i
= 0; i
< timeout
; i
++) {
373 if (inb(dev
->iobase
+ DAS16M1_CS
) & IRQDATA
)
377 comedi_error(dev
, "timeout");
380 data
[n
] = munge_sample(inw(dev
->iobase
));
386 static int das16m1_di_rbits(struct comedi_device
*dev
,
387 struct comedi_subdevice
*s
,
388 struct comedi_insn
*insn
, unsigned int *data
)
392 bits
= inb(dev
->iobase
+ DAS16M1_DIO
) & 0xf;
399 static int das16m1_do_wbits(struct comedi_device
*dev
,
400 struct comedi_subdevice
*s
,
401 struct comedi_insn
*insn
, unsigned int *data
)
403 struct das16m1_private_struct
*devpriv
= dev
->private;
406 /* only set bits that have been masked */
408 wbits
= devpriv
->do_bits
;
409 /* zero bits that have been masked */
411 /* set masked bits */
412 wbits
|= data
[0] & data
[1];
413 devpriv
->do_bits
= wbits
;
416 outb(devpriv
->do_bits
, dev
->iobase
+ DAS16M1_DIO
);
421 static void das16m1_handler(struct comedi_device
*dev
, unsigned int status
)
423 struct das16m1_private_struct
*devpriv
= dev
->private;
424 struct comedi_subdevice
*s
;
425 struct comedi_async
*async
;
426 struct comedi_cmd
*cmd
;
430 s
= dev
->read_subdev
;
435 /* figure out how many samples are in fifo */
436 hw_counter
= i8254_read(dev
->iobase
+ DAS16M1_8254_FIRST
, 0, 1);
437 /* make sure hardware counter reading is not bogus due to initial value
438 * not having been loaded yet */
439 if (devpriv
->adc_count
== 0 && hw_counter
== devpriv
->initial_hw_count
) {
442 /* The calculation of num_samples looks odd, but it uses the following facts.
443 * 16 bit hardware counter is initialized with value of zero (which really
444 * means 0x1000). The counter decrements by one on each conversion
445 * (when the counter decrements from zero it goes to 0xffff). num_samples
446 * is a 16 bit variable, so it will roll over in a similar fashion to the
447 * hardware counter. Work it out, and this is what you get. */
448 num_samples
= -hw_counter
- devpriv
->adc_count
;
450 /* check if we only need some of the points */
451 if (cmd
->stop_src
== TRIG_COUNT
) {
452 if (num_samples
> cmd
->stop_arg
* cmd
->chanlist_len
)
453 num_samples
= cmd
->stop_arg
* cmd
->chanlist_len
;
455 /* make sure we dont try to get too many points if fifo has overrun */
456 if (num_samples
> FIFO_SIZE
)
457 num_samples
= FIFO_SIZE
;
458 insw(dev
->iobase
, devpriv
->ai_buffer
, num_samples
);
459 munge_sample_array(devpriv
->ai_buffer
, num_samples
);
460 cfc_write_array_to_buffer(s
, devpriv
->ai_buffer
,
461 num_samples
* sizeof(short));
462 devpriv
->adc_count
+= num_samples
;
464 if (cmd
->stop_src
== TRIG_COUNT
) {
465 if (devpriv
->adc_count
>= cmd
->stop_arg
* cmd
->chanlist_len
) { /* end of acquisition */
466 das16m1_cancel(dev
, s
);
467 async
->events
|= COMEDI_CB_EOA
;
471 /* this probably won't catch overruns since the card doesn't generate
472 * overrun interrupts, but we might as well try */
473 if (status
& OVRUN
) {
474 das16m1_cancel(dev
, s
);
475 async
->events
|= COMEDI_CB_EOA
| COMEDI_CB_ERROR
;
476 comedi_error(dev
, "fifo overflow");
479 comedi_event(dev
, s
);
483 static int das16m1_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
488 /* prevent race with interrupt handler */
489 spin_lock_irqsave(&dev
->spinlock
, flags
);
490 status
= inb(dev
->iobase
+ DAS16M1_CS
);
491 das16m1_handler(dev
, status
);
492 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
494 return s
->async
->buf_write_count
- s
->async
->buf_read_count
;
497 static irqreturn_t
das16m1_interrupt(int irq
, void *d
)
500 struct comedi_device
*dev
= d
;
502 if (dev
->attached
== 0) {
503 comedi_error(dev
, "premature interrupt");
506 /* prevent race with comedi_poll() */
507 spin_lock(&dev
->spinlock
);
509 status
= inb(dev
->iobase
+ DAS16M1_CS
);
511 if ((status
& (IRQDATA
| OVRUN
)) == 0) {
512 comedi_error(dev
, "spurious interrupt");
513 spin_unlock(&dev
->spinlock
);
517 das16m1_handler(dev
, status
);
519 /* clear interrupt */
520 outb(0, dev
->iobase
+ DAS16M1_CLEAR_INTR
);
522 spin_unlock(&dev
->spinlock
);
526 static int das16m1_irq_bits(unsigned int irq
)
567 static int das16m1_attach(struct comedi_device
*dev
,
568 struct comedi_devconfig
*it
)
570 struct das16m1_private_struct
*devpriv
;
571 struct comedi_subdevice
*s
;
574 unsigned long iobase
;
576 dev
->board_name
= dev
->driver
->driver_name
;
578 iobase
= it
->options
[0];
580 devpriv
= kzalloc(sizeof(*devpriv
), GFP_KERNEL
);
583 dev
->private = devpriv
;
585 if (!request_region(iobase
, DAS16M1_SIZE
, dev
->board_name
)) {
586 comedi_error(dev
, "I/O port conflict\n");
589 if (!request_region(iobase
+ DAS16M1_82C55
, DAS16M1_SIZE2
,
591 release_region(iobase
, DAS16M1_SIZE
);
592 comedi_error(dev
, "I/O port conflict\n");
595 dev
->iobase
= iobase
;
597 /* now for the irq */
598 irq
= it
->options
[1];
599 /* make sure it is valid */
600 if (das16m1_irq_bits(irq
) >= 0) {
601 ret
= request_irq(irq
, das16m1_interrupt
, 0,
602 dev
->driver
->driver_name
, dev
);
608 } else if (irq
== 0) {
612 comedi_error(dev
, "invalid irq\n"
613 " valid irqs are 2, 3, 5, 7, 10, 11, 12, or 15\n");
617 ret
= comedi_alloc_subdevices(dev
, 4);
621 s
= &dev
->subdevices
[0];
622 dev
->read_subdev
= s
;
624 s
->type
= COMEDI_SUBD_AI
;
625 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
627 s
->subdev_flags
= SDF_DIFF
;
628 s
->len_chanlist
= 256;
629 s
->maxdata
= (1 << 12) - 1;
630 s
->range_table
= &range_das16m1
;
631 s
->insn_read
= das16m1_ai_rinsn
;
632 s
->do_cmdtest
= das16m1_cmd_test
;
633 s
->do_cmd
= das16m1_cmd_exec
;
634 s
->cancel
= das16m1_cancel
;
635 s
->poll
= das16m1_poll
;
637 s
= &dev
->subdevices
[1];
639 s
->type
= COMEDI_SUBD_DI
;
640 s
->subdev_flags
= SDF_READABLE
;
643 s
->range_table
= &range_digital
;
644 s
->insn_bits
= das16m1_di_rbits
;
646 s
= &dev
->subdevices
[2];
648 s
->type
= COMEDI_SUBD_DO
;
649 s
->subdev_flags
= SDF_WRITABLE
| SDF_READABLE
;
652 s
->range_table
= &range_digital
;
653 s
->insn_bits
= das16m1_do_wbits
;
655 s
= &dev
->subdevices
[3];
657 subdev_8255_init(dev
, s
, NULL
, dev
->iobase
+ DAS16M1_82C55
);
659 /* disable upper half of hardware conversion counter so it doesn't mess with us */
660 outb(TOTAL_CLEAR
, dev
->iobase
+ DAS16M1_8254_FIRST_CNTRL
);
662 /* initialize digital output lines */
663 outb(devpriv
->do_bits
, dev
->iobase
+ DAS16M1_DIO
);
665 /* set the interrupt level */
667 devpriv
->control_state
= das16m1_irq_bits(dev
->irq
);
669 devpriv
->control_state
= 0;
670 outb(devpriv
->control_state
, dev
->iobase
+ DAS16M1_INTR_CONTROL
);
675 static void das16m1_detach(struct comedi_device
*dev
)
678 subdev_8255_cleanup(dev
, &dev
->subdevices
[3]);
680 free_irq(dev
->irq
, dev
);
682 release_region(dev
->iobase
, DAS16M1_SIZE
);
683 release_region(dev
->iobase
+ DAS16M1_82C55
, DAS16M1_SIZE2
);
687 static struct comedi_driver das16m1_driver
= {
688 .driver_name
= "das16m1",
689 .module
= THIS_MODULE
,
690 .attach
= das16m1_attach
,
691 .detach
= das16m1_detach
,
693 module_comedi_driver(das16m1_driver
);
695 MODULE_AUTHOR("Comedi http://www.comedi.org");
696 MODULE_DESCRIPTION("Comedi low-level driver");
697 MODULE_LICENSE("GPL");