2 comedi/drivers/pcl816.c
4 Author: Juan Grigera <juan@grigera.com.ar>
5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
7 hardware driver for Advantech cards:
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
17 Updated: Tue, 2 Apr 2002 23:15:21 -0800
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
22 The driver support AI command mode, other subdevices not written.
24 Analog output and digital input and output are not supported.
26 Configuration Options:
28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
29 [2] - DMA (0=disable, 1, 3)
30 [3] - 0, 10=10MHz clock for 8254
31 1= 1MHz clock for 8254
35 #include <linux/module.h>
36 #include "../comedidev.h"
38 #include <linux/gfp.h>
39 #include <linux/delay.h>
41 #include <linux/interrupt.h>
44 #include "comedi_fc.h"
47 /* boards constants */
49 #define PCLx1x_RANGE 16
51 /* INTEL 8254 counters */
55 /* R: counter read-back register W: counter control */
56 #define PCL816_CTRCTL 7
58 /* R: A/D high byte W: A/D range control */
59 #define PCL816_RANGE 9
60 /* W: clear INT request */
61 #define PCL816_CLRINT 10
62 /* R: next mux scan channel W: mux scan channel & range control pointer */
64 /* R/W: operation control register */
65 #define PCL816_CONTROL 12
67 /* R: return status byte W: set DMA/IRQ */
68 #define PCL816_STATUS 13
69 #define PCL816_STATUS_DRDY_MASK 0x80
71 /* R: low byte of A/D W: soft A/D trigger */
72 #define PCL816_AD_LO 8
73 /* R: high byte of A/D W: A/D range control */
74 #define PCL816_AD_HI 9
76 /* type of interrupt handler */
77 #define INT_TYPE_AI1_INT 1
78 #define INT_TYPE_AI1_DMA 2
79 #define INT_TYPE_AI3_INT 4
80 #define INT_TYPE_AI3_DMA 5
82 #define MAGIC_DMA_WORD 0x5a5a
84 static const struct comedi_lrange range_pcl816
= { 8, {
98 const char *name
; /* board name */
99 int n_ranges
; /* len of range list */
100 int n_aichan
; /* num of A/D chans in diferencial mode */
101 unsigned int ai_ns_min
; /* minimal allowed delay between samples (in ns) */
102 int n_aochan
; /* num of D/A chans */
103 int n_dichan
; /* num of DI chans */
104 int n_dochan
; /* num of DO chans */
105 const struct comedi_lrange
*ai_range_type
; /* default A/D rangelist */
106 const struct comedi_lrange
*ao_range_type
; /* default D/A rangelist */
107 unsigned int io_range
; /* len of IO space */
108 unsigned int IRQbits
; /* allowed interrupts */
109 unsigned int DMAbits
; /* allowed DMA chans */
110 int ai_maxdata
; /* maxdata for A/D */
111 int ao_maxdata
; /* maxdata for D/A */
112 int ai_chanlist
; /* allowed len of channel list A/D */
113 int ao_chanlist
; /* allowed len of channel list D/A */
114 int i8254_osc_base
; /* 1/frequency of on board oscilator in ns */
117 struct pcl816_private
{
119 unsigned int dma
; /* used DMA, 0=don't use DMA */
120 unsigned long dmabuf
[2]; /* pointers to begin of DMA buffers */
121 unsigned int dmapages
[2]; /* len of DMA buffers in PAGE_SIZEs */
122 unsigned int hwdmaptr
[2]; /* hardware address of DMA buffers */
123 unsigned int hwdmasize
[2]; /* len of DMA buffers in Bytes */
124 unsigned int dmasamplsize
; /* size in samples hwdmasize[0]/2 */
125 int next_dma_buf
; /* which DMA buffer will be used next round */
126 long dma_runs_to_end
; /* how many we must permorm DMA transfer to end of record */
127 unsigned long last_dma_run
; /* how many bytes we must transfer on last DMA page */
129 unsigned int ai_scans
; /* len of scanlist */
130 unsigned char ai_neverending
; /* if=1, then we do neverending record (you must use cancel()) */
131 int irq_blocked
; /* 1=IRQ now uses any subdev */
132 int irq_was_now_closed
; /* when IRQ finish, there's stored int816_mode for last interrupt */
133 int int816_mode
; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
134 struct comedi_subdevice
*last_int_sub
; /* ptr to subdevice which now finish */
135 int ai_act_scan
; /* how many scans we finished */
136 unsigned int ai_act_chanlist
[16]; /* MUX setting for actual AI operations */
137 unsigned int ai_act_chanlist_len
; /* how long is actual MUX list */
138 unsigned int ai_act_chanlist_pos
; /* actual position in MUX list */
139 unsigned int ai_n_chan
; /* how many channels per scan */
140 unsigned int ai_poll_ptr
; /* how many sampes transfer poll */
141 struct comedi_subdevice
*sub_ai
; /* ptr to AI subdevice */
145 ==============================================================================
147 static int check_channel_list(struct comedi_device
*dev
,
148 struct comedi_subdevice
*s
,
149 unsigned int *chanlist
, unsigned int chanlen
);
150 static void setup_channel_list(struct comedi_device
*dev
,
151 struct comedi_subdevice
*s
,
152 unsigned int *chanlist
, unsigned int seglen
);
153 static int pcl816_ai_cancel(struct comedi_device
*dev
,
154 struct comedi_subdevice
*s
);
155 static void start_pacer(struct comedi_device
*dev
, int mode
,
156 unsigned int divisor1
, unsigned int divisor2
);
158 static int pcl816_ai_cmdtest(struct comedi_device
*dev
,
159 struct comedi_subdevice
*s
,
160 struct comedi_cmd
*cmd
);
161 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
);
164 ==============================================================================
165 ANALOG INPUT MODE0, 816 cards, slow version
167 static int pcl816_ai_insn_read(struct comedi_device
*dev
,
168 struct comedi_subdevice
*s
,
169 struct comedi_insn
*insn
, unsigned int *data
)
174 /* software trigger, DMA and INT off */
175 outb(0, dev
->iobase
+ PCL816_CONTROL
);
176 /* clear INT (conversion end) flag */
177 outb(0, dev
->iobase
+ PCL816_CLRINT
);
179 /* Set the input channel */
180 outb(CR_CHAN(insn
->chanspec
) & 0xf, dev
->iobase
+ PCL816_MUX
);
182 outb(CR_RANGE(insn
->chanspec
), dev
->iobase
+ PCL816_RANGE
);
184 for (n
= 0; n
< insn
->n
; n
++) {
186 outb(0, dev
->iobase
+ PCL816_AD_LO
); /* start conversion */
190 if (!(inb(dev
->iobase
+ PCL816_STATUS
) &
191 PCL816_STATUS_DRDY_MASK
)) {
192 /* return read value */
195 PCL816_AD_HI
) << 8) |
196 (inb(dev
->iobase
+ PCL816_AD_LO
)));
197 /* clear INT (conversion end) flag */
198 outb(0, dev
->iobase
+ PCL816_CLRINT
);
203 /* Return timeout error */
205 comedi_error(dev
, "A/D insn timeout\n");
207 /* clear INT (conversion end) flag */
208 outb(0, dev
->iobase
+ PCL816_CLRINT
);
217 ==============================================================================
218 analog input interrupt mode 1 & 3, 818 cards
219 one sample per interrupt version
221 static irqreturn_t
interrupt_pcl816_ai_mode13_int(int irq
, void *d
)
223 struct comedi_device
*dev
= d
;
224 struct pcl816_private
*devpriv
= dev
->private;
225 struct comedi_subdevice
*s
= &dev
->subdevices
[0];
226 unsigned char low
, hi
;
227 int timeout
= 50; /* wait max 50us */
230 if (!(inb(dev
->iobase
+ PCL816_STATUS
) &
231 PCL816_STATUS_DRDY_MASK
))
235 if (!timeout
) { /* timeout, bail error */
236 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
237 comedi_error(dev
, "A/D mode1/3 IRQ without DRDY!");
238 pcl816_ai_cancel(dev
, s
);
239 s
->async
->events
|= COMEDI_CB_EOA
| COMEDI_CB_ERROR
;
240 comedi_event(dev
, s
);
246 low
= inb(dev
->iobase
+ PCL816_AD_LO
);
247 hi
= inb(dev
->iobase
+ PCL816_AD_HI
);
249 comedi_buf_put(s
->async
, (hi
<< 8) | low
);
251 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
253 if (++devpriv
->ai_act_chanlist_pos
>= devpriv
->ai_act_chanlist_len
)
254 devpriv
->ai_act_chanlist_pos
= 0;
256 s
->async
->cur_chan
++;
257 if (s
->async
->cur_chan
>= devpriv
->ai_n_chan
) {
258 s
->async
->cur_chan
= 0;
259 devpriv
->ai_act_scan
++;
262 if (!devpriv
->ai_neverending
)
263 /* all data sampled */
264 if (devpriv
->ai_act_scan
>= devpriv
->ai_scans
) {
265 /* all data sampled */
266 pcl816_ai_cancel(dev
, s
);
267 s
->async
->events
|= COMEDI_CB_EOA
;
269 comedi_event(dev
, s
);
274 ==============================================================================
275 analog input dma mode 1 & 3, 816 cards
277 static void transfer_from_dma_buf(struct comedi_device
*dev
,
278 struct comedi_subdevice
*s
,
280 unsigned int bufptr
, unsigned int len
)
282 struct pcl816_private
*devpriv
= dev
->private;
285 s
->async
->events
= 0;
287 for (i
= 0; i
< len
; i
++) {
289 comedi_buf_put(s
->async
, ptr
[bufptr
++]);
291 if (++devpriv
->ai_act_chanlist_pos
>=
292 devpriv
->ai_act_chanlist_len
) {
293 devpriv
->ai_act_chanlist_pos
= 0;
296 s
->async
->cur_chan
++;
297 if (s
->async
->cur_chan
>= devpriv
->ai_n_chan
) {
298 s
->async
->cur_chan
= 0;
299 devpriv
->ai_act_scan
++;
302 if (!devpriv
->ai_neverending
)
303 /* all data sampled */
304 if (devpriv
->ai_act_scan
>= devpriv
->ai_scans
) {
305 pcl816_ai_cancel(dev
, s
);
306 s
->async
->events
|= COMEDI_CB_EOA
;
307 s
->async
->events
|= COMEDI_CB_BLOCK
;
312 comedi_event(dev
, s
);
315 static irqreturn_t
interrupt_pcl816_ai_mode13_dma(int irq
, void *d
)
317 struct comedi_device
*dev
= d
;
318 struct pcl816_private
*devpriv
= dev
->private;
319 struct comedi_subdevice
*s
= &dev
->subdevices
[0];
320 int len
, bufptr
, this_dma_buf
;
321 unsigned long dma_flags
;
324 disable_dma(devpriv
->dma
);
325 this_dma_buf
= devpriv
->next_dma_buf
;
327 /* switch dma bufs */
328 if ((devpriv
->dma_runs_to_end
> -1) || devpriv
->ai_neverending
) {
330 devpriv
->next_dma_buf
= 1 - devpriv
->next_dma_buf
;
331 set_dma_mode(devpriv
->dma
, DMA_MODE_READ
);
332 dma_flags
= claim_dma_lock();
333 /* clear_dma_ff (devpriv->dma); */
334 set_dma_addr(devpriv
->dma
,
335 devpriv
->hwdmaptr
[devpriv
->next_dma_buf
]);
336 if (devpriv
->dma_runs_to_end
) {
337 set_dma_count(devpriv
->dma
,
338 devpriv
->hwdmasize
[devpriv
->
341 set_dma_count(devpriv
->dma
, devpriv
->last_dma_run
);
343 release_dma_lock(dma_flags
);
344 enable_dma(devpriv
->dma
);
347 devpriv
->dma_runs_to_end
--;
348 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
350 ptr
= (unsigned short *)devpriv
->dmabuf
[this_dma_buf
];
352 len
= (devpriv
->hwdmasize
[0] >> 1) - devpriv
->ai_poll_ptr
;
353 bufptr
= devpriv
->ai_poll_ptr
;
354 devpriv
->ai_poll_ptr
= 0;
356 transfer_from_dma_buf(dev
, s
, ptr
, bufptr
, len
);
361 ==============================================================================
364 static irqreturn_t
interrupt_pcl816(int irq
, void *d
)
366 struct comedi_device
*dev
= d
;
367 struct pcl816_private
*devpriv
= dev
->private;
369 if (!dev
->attached
) {
370 comedi_error(dev
, "premature interrupt");
374 switch (devpriv
->int816_mode
) {
375 case INT_TYPE_AI1_DMA
:
376 case INT_TYPE_AI3_DMA
:
377 return interrupt_pcl816_ai_mode13_dma(irq
, d
);
378 case INT_TYPE_AI1_INT
:
379 case INT_TYPE_AI3_INT
:
380 return interrupt_pcl816_ai_mode13_int(irq
, d
);
383 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
384 if (!dev
->irq
|| !devpriv
->irq_blocked
|| !devpriv
->int816_mode
) {
385 if (devpriv
->irq_was_now_closed
) {
386 devpriv
->irq_was_now_closed
= 0;
387 /* comedi_error(dev,"last IRQ.."); */
390 comedi_error(dev
, "bad IRQ!");
393 comedi_error(dev
, "IRQ from unknown source!");
398 ==============================================================================
400 static int pcl816_ai_cmdtest(struct comedi_device
*dev
,
401 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
403 const struct pcl816_board
*board
= comedi_board(dev
);
405 int tmp
, divisor1
= 0, divisor2
= 0;
407 /* Step 1 : check if triggers are trivially valid */
409 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
410 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
411 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_EXT
| TRIG_TIMER
);
412 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
413 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
418 /* Step 2a : make sure trigger sources are unique */
420 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
421 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
423 /* Step 2b : and mutually compatible */
429 /* Step 3: check if arguments are trivially valid */
431 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
432 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
434 if (cmd
->convert_src
== TRIG_TIMER
)
435 err
|= cfc_check_trigger_arg_min(&cmd
->convert_arg
,
438 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
440 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
442 if (cmd
->stop_src
== TRIG_COUNT
)
443 err
|= cfc_check_trigger_arg_min(&cmd
->stop_arg
, 1);
445 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
451 /* step 4: fix up any arguments */
452 if (cmd
->convert_src
== TRIG_TIMER
) {
453 tmp
= cmd
->convert_arg
;
454 i8253_cascade_ns_to_timer(board
->i8254_osc_base
,
455 &divisor1
, &divisor2
,
456 &cmd
->convert_arg
, cmd
->flags
);
457 if (cmd
->convert_arg
< board
->ai_ns_min
)
458 cmd
->convert_arg
= board
->ai_ns_min
;
459 if (tmp
!= cmd
->convert_arg
)
467 /* step 5: complain about special chanlist considerations */
470 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
472 return 5; /* incorrect channels list */
478 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
480 const struct pcl816_board
*board
= comedi_board(dev
);
481 struct pcl816_private
*devpriv
= dev
->private;
482 unsigned int divisor1
= 0, divisor2
= 0, dma_flags
, bytes
, dmairq
;
483 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
486 if (cmd
->start_src
!= TRIG_NOW
)
488 if (cmd
->scan_begin_src
!= TRIG_FOLLOW
)
490 if (cmd
->scan_end_src
!= TRIG_COUNT
)
492 if (cmd
->scan_end_arg
!= cmd
->chanlist_len
)
494 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
495 if (devpriv
->irq_blocked
)
498 if (cmd
->convert_src
== TRIG_TIMER
) {
499 if (cmd
->convert_arg
< board
->ai_ns_min
)
500 cmd
->convert_arg
= board
->ai_ns_min
;
502 i8253_cascade_ns_to_timer(board
->i8254_osc_base
,
503 &divisor1
, &divisor2
,
504 &cmd
->convert_arg
, cmd
->flags
);
506 /* PCL816 crash if any divisor is set to 1 */
517 start_pacer(dev
, -1, 0, 0); /* stop pacer */
519 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
522 setup_channel_list(dev
, s
, cmd
->chanlist
, seglen
);
525 devpriv
->ai_n_chan
= cmd
->chanlist_len
;
526 devpriv
->ai_act_scan
= 0;
527 s
->async
->cur_chan
= 0;
528 devpriv
->irq_blocked
= 1;
529 devpriv
->ai_poll_ptr
= 0;
530 devpriv
->irq_was_now_closed
= 0;
532 if (cmd
->stop_src
== TRIG_COUNT
) {
533 devpriv
->ai_scans
= cmd
->stop_arg
;
534 devpriv
->ai_neverending
= 0;
536 devpriv
->ai_scans
= 0;
537 devpriv
->ai_neverending
= 1;
541 bytes
= devpriv
->hwdmasize
[0];
542 if (!devpriv
->ai_neverending
) {
544 bytes
= s
->async
->cmd
.chanlist_len
*
545 s
->async
->cmd
.chanlist_len
*
548 /* how many DMA pages we must fill */
549 devpriv
->dma_runs_to_end
= bytes
/
550 devpriv
->hwdmasize
[0];
552 /* on last dma transfer must be moved */
553 devpriv
->last_dma_run
= bytes
% devpriv
->hwdmasize
[0];
554 devpriv
->dma_runs_to_end
--;
555 if (devpriv
->dma_runs_to_end
>= 0)
556 bytes
= devpriv
->hwdmasize
[0];
558 devpriv
->dma_runs_to_end
= -1;
560 devpriv
->next_dma_buf
= 0;
561 set_dma_mode(devpriv
->dma
, DMA_MODE_READ
);
562 dma_flags
= claim_dma_lock();
563 clear_dma_ff(devpriv
->dma
);
564 set_dma_addr(devpriv
->dma
, devpriv
->hwdmaptr
[0]);
565 set_dma_count(devpriv
->dma
, bytes
);
566 release_dma_lock(dma_flags
);
567 enable_dma(devpriv
->dma
);
570 start_pacer(dev
, 1, divisor1
, divisor2
);
571 dmairq
= ((devpriv
->dma
& 0x3) << 4) | (dev
->irq
& 0x7);
573 switch (cmd
->convert_src
) {
575 devpriv
->int816_mode
= INT_TYPE_AI1_DMA
;
578 outb(0x32, dev
->iobase
+ PCL816_CONTROL
);
580 /* write irq and DMA to card */
581 outb(dmairq
, dev
->iobase
+ PCL816_STATUS
);
585 devpriv
->int816_mode
= INT_TYPE_AI3_DMA
;
587 /* Ext trig+IRQ+DMA */
588 outb(0x34, dev
->iobase
+ PCL816_CONTROL
);
590 /* write irq to card */
591 outb(dmairq
, dev
->iobase
+ PCL816_STATUS
);
598 static int pcl816_ai_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
600 struct pcl816_private
*devpriv
= dev
->private;
602 unsigned int top1
, top2
, i
;
605 return 0; /* poll is valid only for DMA transfer */
607 spin_lock_irqsave(&dev
->spinlock
, flags
);
609 for (i
= 0; i
< 20; i
++) {
610 top1
= get_dma_residue(devpriv
->dma
); /* where is now DMA */
611 top2
= get_dma_residue(devpriv
->dma
);
616 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
620 /* where is now DMA in buffer */
621 top1
= devpriv
->hwdmasize
[0] - top1
;
622 top1
>>= 1; /* sample position */
623 top2
= top1
- devpriv
->ai_poll_ptr
;
624 if (top2
< 1) { /* no new samples */
625 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
629 transfer_from_dma_buf(dev
, s
,
630 (unsigned short *)devpriv
->dmabuf
[devpriv
->
632 devpriv
->ai_poll_ptr
, top2
);
634 devpriv
->ai_poll_ptr
= top1
; /* new buffer position */
635 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
637 return s
->async
->buf_write_count
- s
->async
->buf_read_count
;
641 ==============================================================================
642 cancel any mode 1-4 AI
644 static int pcl816_ai_cancel(struct comedi_device
*dev
,
645 struct comedi_subdevice
*s
)
647 struct pcl816_private
*devpriv
= dev
->private;
649 if (devpriv
->irq_blocked
> 0) {
650 switch (devpriv
->int816_mode
) {
651 case INT_TYPE_AI1_DMA
:
652 case INT_TYPE_AI3_DMA
:
653 disable_dma(devpriv
->dma
);
654 case INT_TYPE_AI1_INT
:
655 case INT_TYPE_AI3_INT
:
656 outb(inb(dev
->iobase
+ PCL816_CONTROL
) & 0x73,
657 dev
->iobase
+ PCL816_CONTROL
); /* Stop A/D */
659 outb(0, dev
->iobase
+ PCL816_CONTROL
); /* Stop A/D */
662 outb(0xb0, dev
->iobase
+ PCL816_CTRCTL
);
663 outb(0x70, dev
->iobase
+ PCL816_CTRCTL
);
664 outb(0, dev
->iobase
+ PCL816_AD_LO
);
665 inb(dev
->iobase
+ PCL816_AD_LO
);
666 inb(dev
->iobase
+ PCL816_AD_HI
);
668 /* clear INT request */
669 outb(0, dev
->iobase
+ PCL816_CLRINT
);
672 outb(0, dev
->iobase
+ PCL816_CONTROL
);
673 devpriv
->irq_blocked
= 0;
674 devpriv
->irq_was_now_closed
= devpriv
->int816_mode
;
675 devpriv
->int816_mode
= 0;
676 devpriv
->last_int_sub
= s
;
685 ==============================================================================
688 static int pcl816_check(unsigned long iobase
)
690 outb(0x00, iobase
+ PCL816_MUX
);
692 if (inb(iobase
+ PCL816_MUX
) != 0x00)
693 return 1; /* there isn't card */
694 outb(0x55, iobase
+ PCL816_MUX
);
696 if (inb(iobase
+ PCL816_MUX
) != 0x55)
697 return 1; /* there isn't card */
698 outb(0x00, iobase
+ PCL816_MUX
);
700 outb(0x18, iobase
+ PCL816_CONTROL
);
702 if (inb(iobase
+ PCL816_CONTROL
) != 0x18)
703 return 1; /* there isn't card */
704 return 0; /* ok, card exist */
708 ==============================================================================
709 reset whole PCL-816 cards
711 static void pcl816_reset(struct comedi_device
*dev
)
713 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
714 /* outb (0, dev->iobase + PCL818_DA_HI); */
716 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
717 /* outb (0, dev->iobase + PCL818_DO_LO); */
719 outb(0, dev
->iobase
+ PCL816_CONTROL
);
720 outb(0, dev
->iobase
+ PCL816_MUX
);
721 outb(0, dev
->iobase
+ PCL816_CLRINT
);
722 outb(0xb0, dev
->iobase
+ PCL816_CTRCTL
); /* Stop pacer */
723 outb(0x70, dev
->iobase
+ PCL816_CTRCTL
);
724 outb(0x30, dev
->iobase
+ PCL816_CTRCTL
);
725 outb(0, dev
->iobase
+ PCL816_RANGE
);
729 ==============================================================================
730 Start/stop pacer onboard pacer
733 start_pacer(struct comedi_device
*dev
, int mode
, unsigned int divisor1
,
734 unsigned int divisor2
)
736 outb(0x32, dev
->iobase
+ PCL816_CTRCTL
);
737 outb(0xff, dev
->iobase
+ PCL816_CTR0
);
738 outb(0x00, dev
->iobase
+ PCL816_CTR0
);
741 /* set counter 2 as mode 3 */
742 outb(0xb4, dev
->iobase
+ PCL816_CTRCTL
);
743 /* set counter 1 as mode 3 */
744 outb(0x74, dev
->iobase
+ PCL816_CTRCTL
);
748 dev_dbg(dev
->class_dev
, "mode %d, divisor1 %d, divisor2 %d\n",
749 mode
, divisor1
, divisor2
);
750 outb(divisor2
& 0xff, dev
->iobase
+ PCL816_CTR2
);
751 outb((divisor2
>> 8) & 0xff, dev
->iobase
+ PCL816_CTR2
);
752 outb(divisor1
& 0xff, dev
->iobase
+ PCL816_CTR1
);
753 outb((divisor1
>> 8) & 0xff, dev
->iobase
+ PCL816_CTR1
);
756 /* clear pending interrupts (just in case) */
757 /* outb(0, dev->iobase + PCL816_CLRINT); */
761 ==============================================================================
762 Check if channel list from user is built correctly
763 If it's ok, then return non-zero length of repeated segment of channel list
766 check_channel_list(struct comedi_device
*dev
,
767 struct comedi_subdevice
*s
, unsigned int *chanlist
,
768 unsigned int chanlen
)
770 unsigned int chansegment
[16];
771 unsigned int i
, nowmustbechan
, seglen
, segpos
;
773 /* correct channel and range number check itself comedi/range.c */
775 comedi_error(dev
, "range/channel list is empty!");
780 /* first channel is every time ok */
781 chansegment
[0] = chanlist
[0];
782 for (i
= 1, seglen
= 1; i
< chanlen
; i
++, seglen
++) {
783 /* we detect loop, this must by finish */
784 if (chanlist
[0] == chanlist
[i
])
787 (CR_CHAN(chansegment
[i
- 1]) + 1) % chanlen
;
788 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
789 /* channel list isn't continuous :-( */
790 dev_dbg(dev
->class_dev
,
791 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
792 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
793 CR_CHAN(chanlist
[0]));
796 /* well, this is next correct channel in list */
797 chansegment
[i
] = chanlist
[i
];
800 /* check whole chanlist */
801 for (i
= 0, segpos
= 0; i
< chanlen
; i
++) {
802 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
803 dev_dbg(dev
->class_dev
,
804 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
805 i
, CR_CHAN(chansegment
[i
]),
806 CR_RANGE(chansegment
[i
]),
807 CR_AREF(chansegment
[i
]),
808 CR_CHAN(chanlist
[i
% seglen
]),
809 CR_RANGE(chanlist
[i
% seglen
]),
810 CR_AREF(chansegment
[i
% seglen
]));
811 return 0; /* chan/gain list is strange */
818 return seglen
; /* we can serve this with MUX logic */
822 ==============================================================================
823 Program scan/gain logic with channel list.
826 setup_channel_list(struct comedi_device
*dev
,
827 struct comedi_subdevice
*s
, unsigned int *chanlist
,
830 struct pcl816_private
*devpriv
= dev
->private;
833 devpriv
->ai_act_chanlist_len
= seglen
;
834 devpriv
->ai_act_chanlist_pos
= 0;
836 for (i
= 0; i
< seglen
; i
++) { /* store range list to card */
837 devpriv
->ai_act_chanlist
[i
] = CR_CHAN(chanlist
[i
]);
838 outb(CR_CHAN(chanlist
[0]) & 0xf, dev
->iobase
+ PCL816_MUX
);
840 outb(CR_RANGE(chanlist
[0]), dev
->iobase
+ PCL816_RANGE
);
844 /* select channel interval to scan */
845 outb(devpriv
->ai_act_chanlist
[0] |
846 (devpriv
->ai_act_chanlist
[seglen
- 1] << 4),
847 dev
->iobase
+ PCL816_MUX
);
850 static int pcl816_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
852 const struct pcl816_board
*board
= comedi_board(dev
);
853 struct pcl816_private
*devpriv
;
858 struct comedi_subdevice
*s
;
860 ret
= comedi_request_region(dev
, it
->options
[0], board
->io_range
);
864 if (pcl816_check(dev
->iobase
)) {
865 dev_err(dev
->class_dev
, "I can't detect board. FAIL!\n");
869 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
873 if ((1 << it
->options
[1]) & board
->IRQbits
) {
874 ret
= request_irq(it
->options
[1], interrupt_pcl816
, 0,
875 dev
->board_name
, dev
);
877 dev
->irq
= it
->options
[1];
880 devpriv
->irq_blocked
= 0; /* number of subdevice which use IRQ */
881 devpriv
->int816_mode
= 0; /* mode of irq */
887 goto no_dma
; /* if we haven't IRQ, we can't use DMA */
889 if (board
->DMAbits
!= 0) { /* board support DMA */
890 dma
= it
->options
[2];
892 goto no_dma
; /* DMA disabled */
894 if (((1 << dma
) & board
->DMAbits
) == 0) {
895 dev_err(dev
->class_dev
,
896 "DMA is out of allowed range, FAIL!\n");
897 return -EINVAL
; /* Bad DMA */
899 ret
= request_dma(dma
, dev
->board_name
);
901 dev_err(dev
->class_dev
,
902 "unable to allocate DMA %u, FAIL!\n", dma
);
903 return -EBUSY
; /* DMA isn't free */
907 pages
= 2; /* we need 16KB */
908 devpriv
->dmabuf
[0] = __get_dma_pages(GFP_KERNEL
, pages
);
910 if (!devpriv
->dmabuf
[0]) {
911 dev_err(dev
->class_dev
,
912 "unable to allocate DMA buffer, FAIL!\n");
914 * maybe experiment with try_to_free_pages()
917 return -EBUSY
; /* no buffer :-( */
919 devpriv
->dmapages
[0] = pages
;
920 devpriv
->hwdmaptr
[0] = virt_to_bus((void *)devpriv
->dmabuf
[0]);
921 devpriv
->hwdmasize
[0] = (1 << pages
) * PAGE_SIZE
;
923 devpriv
->dmabuf
[1] = __get_dma_pages(GFP_KERNEL
, pages
);
924 if (!devpriv
->dmabuf
[1]) {
925 dev_err(dev
->class_dev
,
926 "unable to allocate DMA buffer, FAIL!\n");
929 devpriv
->dmapages
[1] = pages
;
930 devpriv
->hwdmaptr
[1] = virt_to_bus((void *)devpriv
->dmabuf
[1]);
931 devpriv
->hwdmasize
[1] = (1 << pages
) * PAGE_SIZE
;
936 /* if (board->n_aochan > 0)
937 subdevs[1] = COMEDI_SUBD_AO;
938 if (board->n_dichan > 0)
939 subdevs[2] = COMEDI_SUBD_DI;
940 if (board->n_dochan > 0)
941 subdevs[3] = COMEDI_SUBD_DO;
944 ret
= comedi_alloc_subdevices(dev
, 1);
948 s
= &dev
->subdevices
[0];
949 if (board
->n_aichan
> 0) {
950 s
->type
= COMEDI_SUBD_AI
;
952 s
->subdev_flags
= SDF_CMD_READ
| SDF_DIFF
;
953 s
->n_chan
= board
->n_aichan
;
954 s
->maxdata
= board
->ai_maxdata
;
955 s
->range_table
= board
->ai_range_type
;
956 s
->insn_read
= pcl816_ai_insn_read
;
958 dev
->read_subdev
= s
;
959 s
->subdev_flags
|= SDF_CMD_READ
;
960 s
->len_chanlist
= board
->ai_chanlist
;
961 s
->do_cmdtest
= pcl816_ai_cmdtest
;
962 s
->do_cmd
= pcl816_ai_cmd
;
963 s
->poll
= pcl816_ai_poll
;
964 s
->cancel
= pcl816_ai_cancel
;
967 s
->type
= COMEDI_SUBD_UNUSED
;
972 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
973 s
->n_chan
= board
->n_aochan
;
974 s
->maxdata
= board
->ao_maxdata
;
975 s
->len_chanlist
= board
->ao_chanlist
;
976 s
->range_table
= board
->ao_range_type
;
980 s
->subdev_flags
= SDF_READABLE
;
981 s
->n_chan
= board
->n_dichan
;
983 s
->len_chanlist
= board
->n_dichan
;
984 s
->range_table
= &range_digital
;
988 s
->subdev_flags
= SDF_WRITABLE
;
989 s
->n_chan
= board
->n_dochan
;
991 s
->len_chanlist
= board
->n_dochan
;
992 s
->range_table
= &range_digital
;
1001 static void pcl816_detach(struct comedi_device
*dev
)
1003 struct pcl816_private
*devpriv
= dev
->private;
1006 pcl816_ai_cancel(dev
, devpriv
->sub_ai
);
1009 free_dma(devpriv
->dma
);
1010 if (devpriv
->dmabuf
[0])
1011 free_pages(devpriv
->dmabuf
[0], devpriv
->dmapages
[0]);
1012 if (devpriv
->dmabuf
[1])
1013 free_pages(devpriv
->dmabuf
[1], devpriv
->dmapages
[1]);
1015 comedi_legacy_detach(dev
);
1018 static const struct pcl816_board boardtypes
[] = {
1019 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816
,
1020 &range_pcl816
, PCLx1x_RANGE
,
1021 0x00fc, /* IRQ mask */
1022 0x0a, /* DMA mask */
1023 0xffff, /* 16-bit card */
1024 0xffff, /* D/A maxdata */
1026 1, /* ao chan list */
1027 I8254_OSC_BASE_10MHZ
},
1028 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816
,
1029 &range_pcl816
, PCLx1x_RANGE
,
1032 0x3fff, /* 14 bit card */
1036 I8254_OSC_BASE_10MHZ
},
1039 static struct comedi_driver pcl816_driver
= {
1040 .driver_name
= "pcl816",
1041 .module
= THIS_MODULE
,
1042 .attach
= pcl816_attach
,
1043 .detach
= pcl816_detach
,
1044 .board_name
= &boardtypes
[0].name
,
1045 .num_names
= ARRAY_SIZE(boardtypes
),
1046 .offset
= sizeof(struct pcl816_board
),
1048 module_comedi_driver(pcl816_driver
);
1050 MODULE_AUTHOR("Comedi http://www.comedi.org");
1051 MODULE_DESCRIPTION("Comedi low-level driver");
1052 MODULE_LICENSE("GPL");