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 "../comedidev.h"
37 #include <linux/ioport.h>
38 #include <linux/mc146818rtc.h>
39 #include <linux/delay.h>
46 /* boards constants */
48 #define PCLx1x_RANGE 16
50 /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
52 /* INTEL 8254 counters */
56 /* R: counter read-back register W: counter control */
57 #define PCL816_CTRCTL 7
59 /* R: A/D high byte W: A/D range control */
60 #define PCL816_RANGE 9
61 /* W: clear INT request */
62 #define PCL816_CLRINT 10
63 /* R: next mux scan channel W: mux scan channel & range control pointer */
65 /* R/W: operation control register */
66 #define PCL816_CONTROL 12
68 /* R: return status byte W: set DMA/IRQ */
69 #define PCL816_STATUS 13
70 #define PCL816_STATUS_DRDY_MASK 0x80
72 /* R: low byte of A/D W: soft A/D trigger */
73 #define PCL816_AD_LO 8
74 /* R: high byte of A/D W: A/D range control */
75 #define PCL816_AD_HI 9
77 /* type of interrupt handler */
78 #define INT_TYPE_AI1_INT 1
79 #define INT_TYPE_AI1_DMA 2
80 #define INT_TYPE_AI3_INT 4
81 #define INT_TYPE_AI3_DMA 5
83 #define INT_TYPE_AI1_DMA_RTC 9
84 #define INT_TYPE_AI3_DMA_RTC 10
88 #define RTC_IO_EXTENT 0x10
91 #define MAGIC_DMA_WORD 0x5a5a
93 static const struct comedi_lrange range_pcl816
= { 8, {
104 struct pcl816_board
{
106 const char *name
; /* board name */
107 int n_ranges
; /* len of range list */
108 int n_aichan
; /* num of A/D chans in diferencial mode */
109 unsigned int ai_ns_min
; /* minimal alllowed delay between samples (in ns) */
110 int n_aochan
; /* num of D/A chans */
111 int n_dichan
; /* num of DI chans */
112 int n_dochan
; /* num of DO chans */
113 const struct comedi_lrange
*ai_range_type
; /* default A/D rangelist */
114 const struct comedi_lrange
*ao_range_type
; /* dafault D/A rangelist */
115 unsigned int io_range
; /* len of IO space */
116 unsigned int IRQbits
; /* allowed interrupts */
117 unsigned int DMAbits
; /* allowed DMA chans */
118 int ai_maxdata
; /* maxdata for A/D */
119 int ao_maxdata
; /* maxdata for D/A */
120 int ai_chanlist
; /* allowed len of channel list A/D */
121 int ao_chanlist
; /* allowed len of channel list D/A */
122 int i8254_osc_base
; /* 1/frequency of on board oscilator in ns */
126 static const struct pcl816_board boardtypes
[] = {
127 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816
,
128 &range_pcl816
, PCLx1x_RANGE
,
129 0x00fc, /* IRQ mask */
131 0xffff, /* 16-bit card */
132 0xffff, /* D/A maxdata */
134 1, /* ao chan list */
136 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816
,
137 &range_pcl816
, PCLx1x_RANGE
,
140 0x3fff, /* 14 bit card */
147 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
148 #define devpriv ((struct pcl816_private *)dev->private)
149 #define this_board ((const struct pcl816_board *)dev->board_ptr)
151 static int pcl816_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
);
152 static int pcl816_detach(struct comedi_device
*dev
);
155 static int RTC_lock
= 0; /* RTC lock */
156 static int RTC_timer_lock
= 0; /* RTC int lock */
159 static struct comedi_driver driver_pcl816
= {
160 .driver_name
= "pcl816",
161 .module
= THIS_MODULE
,
162 .attach
= pcl816_attach
,
163 .detach
= pcl816_detach
,
164 .board_name
= &boardtypes
[0].name
,
165 .num_names
= n_boardtypes
,
166 .offset
= sizeof(struct pcl816_board
),
169 COMEDI_INITCLEANUP(driver_pcl816
);
171 struct pcl816_private
{
173 unsigned int dma
; /* used DMA, 0=don't use DMA */
174 int dma_rtc
; /* 1=RTC used with DMA, 0=no RTC alloc */
176 unsigned long rtc_iobase
; /* RTC port region */
177 unsigned int rtc_iosize
;
178 unsigned int rtc_irq
;
180 unsigned long dmabuf
[2]; /* pointers to begin of DMA buffers */
181 unsigned int dmapages
[2]; /* len of DMA buffers in PAGE_SIZEs */
182 unsigned int hwdmaptr
[2]; /* hardware address of DMA buffers */
183 unsigned int hwdmasize
[2]; /* len of DMA buffers in Bytes */
184 unsigned int dmasamplsize
; /* size in samples hwdmasize[0]/2 */
185 unsigned int last_top_dma
; /* DMA pointer in last RTC int */
186 int next_dma_buf
; /* which DMA buffer will be used next round */
187 long dma_runs_to_end
; /* how many we must permorm DMA transfer to end of record */
188 unsigned long last_dma_run
; /* how many bytes we must transfer on last DMA page */
190 unsigned int ai_scans
; /* len of scanlist */
191 unsigned char ai_neverending
; /* if=1, then we do neverending record (you must use cancel()) */
192 int irq_free
; /* 1=have allocated IRQ */
193 int irq_blocked
; /* 1=IRQ now uses any subdev */
195 int rtc_irq_blocked
; /* 1=we now do AI with DMA&RTC */
197 int irq_was_now_closed
; /* when IRQ finish, there's stored int816_mode for last interrupt */
198 int int816_mode
; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
199 struct comedi_subdevice
*last_int_sub
; /* ptr to subdevice which now finish */
200 int ai_act_scan
; /* how many scans we finished */
201 unsigned int ai_act_chanlist
[16]; /* MUX setting for actual AI operations */
202 unsigned int ai_act_chanlist_len
; /* how long is actual MUX list */
203 unsigned int ai_act_chanlist_pos
; /* actual position in MUX list */
204 unsigned int ai_poll_ptr
; /* how many sampes transfer poll */
205 struct comedi_subdevice
*sub_ai
; /* ptr to AI subdevice */
207 struct timer_list rtc_irq_timer
; /* timer for RTC sanity check */
208 unsigned long rtc_freq
; /* RTC int freq */
214 ==============================================================================
216 static int check_and_setup_channel_list(struct comedi_device
*dev
,
217 struct comedi_subdevice
*s
, unsigned int *chanlist
, int chanlen
);
218 static int pcl816_ai_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
);
219 static void start_pacer(struct comedi_device
*dev
, int mode
, unsigned int divisor1
,
220 unsigned int divisor2
);
222 static int set_rtc_irq_bit(unsigned char bit
);
225 static int pcl816_ai_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
226 struct comedi_cmd
*cmd
);
227 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
);
230 ==============================================================================
231 ANALOG INPUT MODE0, 816 cards, slow version
233 static int pcl816_ai_insn_read(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
234 struct comedi_insn
*insn
, unsigned int *data
)
239 DPRINTK("mode 0 analog input\n");
240 /* software trigger, DMA and INT off */
241 outb(0, dev
->iobase
+ PCL816_CONTROL
);
242 /* clear INT (conversion end) flag */
243 outb(0, dev
->iobase
+ PCL816_CLRINT
);
245 /* Set the input channel */
246 outb(CR_CHAN(insn
->chanspec
) & 0xf, dev
->iobase
+ PCL816_MUX
);
247 outb(CR_RANGE(insn
->chanspec
), dev
->iobase
+ PCL816_RANGE
); /* select gain */
249 for (n
= 0; n
< insn
->n
; n
++) {
251 outb(0, dev
->iobase
+ PCL816_AD_LO
); /* start conversion */
255 if (!(inb(dev
->iobase
+ PCL816_STATUS
) &
256 PCL816_STATUS_DRDY_MASK
)) {
257 /* return read value */
260 PCL816_AD_HI
) << 8) |
261 (inb(dev
->iobase
+ PCL816_AD_LO
)));
263 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT (conversion end) flag */
268 /* Return timeout error */
270 comedi_error(dev
, "A/D insn timeout\n");
272 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT (conversion end) flag */
281 ==============================================================================
282 analog input interrupt mode 1 & 3, 818 cards
283 one sample per interrupt version
285 static irqreturn_t
interrupt_pcl816_ai_mode13_int(int irq
, void *d
)
287 struct comedi_device
*dev
= d
;
288 struct comedi_subdevice
*s
= dev
->subdevices
+ 0;
290 int timeout
= 50; /* wait max 50us */
293 if (!(inb(dev
->iobase
+ PCL816_STATUS
) &
294 PCL816_STATUS_DRDY_MASK
))
298 if (!timeout
) { /* timeout, bail error */
299 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
300 comedi_error(dev
, "A/D mode1/3 IRQ without DRDY!");
301 pcl816_ai_cancel(dev
, s
);
302 s
->async
->events
|= COMEDI_CB_EOA
| COMEDI_CB_ERROR
;
303 comedi_event(dev
, s
);
309 low
= inb(dev
->iobase
+ PCL816_AD_LO
);
310 hi
= inb(dev
->iobase
+ PCL816_AD_HI
);
312 comedi_buf_put(s
->async
, (hi
<< 8) | low
);
314 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
316 if (++devpriv
->ai_act_chanlist_pos
>= devpriv
->ai_act_chanlist_len
)
317 devpriv
->ai_act_chanlist_pos
= 0;
319 if (s
->async
->cur_chan
== 0) {
320 devpriv
->ai_act_scan
++;
323 if (!devpriv
->ai_neverending
)
324 if (devpriv
->ai_act_scan
>= devpriv
->ai_scans
) { /* all data sampled */
325 /* all data sampled */
326 pcl816_ai_cancel(dev
, s
);
327 s
->async
->events
|= COMEDI_CB_EOA
;
329 comedi_event(dev
, s
);
334 ==============================================================================
335 analog input dma mode 1 & 3, 816 cards
337 static void transfer_from_dma_buf(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
338 short *ptr
, unsigned int bufptr
, unsigned int len
)
342 s
->async
->events
= 0;
344 for (i
= 0; i
< len
; i
++) {
346 comedi_buf_put(s
->async
, ptr
[bufptr
++]);
348 if (++devpriv
->ai_act_chanlist_pos
>=
349 devpriv
->ai_act_chanlist_len
) {
350 devpriv
->ai_act_chanlist_pos
= 0;
351 devpriv
->ai_act_scan
++;
354 if (!devpriv
->ai_neverending
)
355 if (devpriv
->ai_act_scan
>= devpriv
->ai_scans
) { /* all data sampled */
356 pcl816_ai_cancel(dev
, s
);
357 s
->async
->events
|= COMEDI_CB_EOA
;
358 s
->async
->events
|= COMEDI_CB_BLOCK
;
363 comedi_event(dev
, s
);
366 static irqreturn_t
interrupt_pcl816_ai_mode13_dma(int irq
, void *d
)
368 struct comedi_device
*dev
= d
;
369 struct comedi_subdevice
*s
= dev
->subdevices
+ 0;
370 int len
, bufptr
, this_dma_buf
;
371 unsigned long dma_flags
;
374 disable_dma(devpriv
->dma
);
375 this_dma_buf
= devpriv
->next_dma_buf
;
377 if ((devpriv
->dma_runs_to_end
> -1) || devpriv
->ai_neverending
) { /* switch dma bufs */
379 devpriv
->next_dma_buf
= 1 - devpriv
->next_dma_buf
;
380 set_dma_mode(devpriv
->dma
, DMA_MODE_READ
);
381 dma_flags
= claim_dma_lock();
382 /* clear_dma_ff (devpriv->dma); */
383 set_dma_addr(devpriv
->dma
,
384 devpriv
->hwdmaptr
[devpriv
->next_dma_buf
]);
385 if (devpriv
->dma_runs_to_end
) {
386 set_dma_count(devpriv
->dma
,
387 devpriv
->hwdmasize
[devpriv
->next_dma_buf
]);
389 set_dma_count(devpriv
->dma
, devpriv
->last_dma_run
);
391 release_dma_lock(dma_flags
);
392 enable_dma(devpriv
->dma
);
395 devpriv
->dma_runs_to_end
--;
396 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
398 ptr
= (short *) devpriv
->dmabuf
[this_dma_buf
];
400 len
= (devpriv
->hwdmasize
[0] >> 1) - devpriv
->ai_poll_ptr
;
401 bufptr
= devpriv
->ai_poll_ptr
;
402 devpriv
->ai_poll_ptr
= 0;
404 transfer_from_dma_buf(dev
, s
, ptr
, bufptr
, len
);
409 ==============================================================================
412 static irqreturn_t
interrupt_pcl816(int irq
, void *d
)
414 struct comedi_device
*dev
= d
;
417 if (!dev
->attached
) {
418 comedi_error(dev
, "premature interrupt");
422 switch (devpriv
->int816_mode
) {
423 case INT_TYPE_AI1_DMA
:
424 case INT_TYPE_AI3_DMA
:
425 return interrupt_pcl816_ai_mode13_dma(irq
, d
);
426 case INT_TYPE_AI1_INT
:
427 case INT_TYPE_AI3_INT
:
428 return interrupt_pcl816_ai_mode13_int(irq
, d
);
431 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
432 if ((!dev
->irq
) | (!devpriv
->irq_free
) | (!devpriv
->irq_blocked
) |
433 (!devpriv
->int816_mode
)) {
434 if (devpriv
->irq_was_now_closed
) {
435 devpriv
->irq_was_now_closed
= 0;
436 /* comedi_error(dev,"last IRQ.."); */
439 comedi_error(dev
, "bad IRQ!");
442 comedi_error(dev
, "IRQ from unknow source!");
447 ==============================================================================
450 static void pcl816_cmdtest_out(int e
, struct comedi_cmd
*cmd
)
452 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e
,
453 cmd
->start_src
, cmd
->scan_begin_src
, cmd
->convert_src
);
454 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e
,
455 cmd
->start_arg
, cmd
->scan_begin_arg
, cmd
->convert_arg
);
456 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e
, cmd
->stop_src
,
458 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e
,
459 cmd
->stop_arg
, cmd
->scan_end_arg
, cmd
->chanlist_len
);
463 ==============================================================================
465 static int pcl816_ai_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
466 struct comedi_cmd
*cmd
)
469 int tmp
, divisor1
, divisor2
;
471 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n");
472 pcl816_cmdtest_out(-1, cmd
););
474 /* step 1: make sure trigger sources are trivially valid */
475 tmp
= cmd
->start_src
;
476 cmd
->start_src
&= TRIG_NOW
;
477 if (!cmd
->start_src
|| tmp
!= cmd
->start_src
)
480 tmp
= cmd
->scan_begin_src
;
481 cmd
->scan_begin_src
&= TRIG_FOLLOW
;
482 if (!cmd
->scan_begin_src
|| tmp
!= cmd
->scan_begin_src
)
485 if (!cmd
->convert_src
& (TRIG_EXT
| TRIG_TIMER
))
488 tmp
= cmd
->scan_end_src
;
489 cmd
->scan_end_src
&= TRIG_COUNT
;
490 if (!cmd
->scan_end_src
|| tmp
!= cmd
->scan_end_src
)
494 cmd
->stop_src
&= TRIG_COUNT
| TRIG_NONE
;
495 if (!cmd
->stop_src
|| tmp
!= cmd
->stop_src
)
502 /* step 2: make sure trigger sources are unique and mutually compatible */
504 if (cmd
->start_src
!= TRIG_NOW
) {
505 cmd
->start_src
= TRIG_NOW
;
509 if (cmd
->scan_begin_src
!= TRIG_FOLLOW
) {
510 cmd
->scan_begin_src
= TRIG_FOLLOW
;
514 if (cmd
->convert_src
!= TRIG_EXT
&& cmd
->convert_src
!= TRIG_TIMER
) {
515 cmd
->convert_src
= TRIG_TIMER
;
519 if (cmd
->scan_end_src
!= TRIG_COUNT
) {
520 cmd
->scan_end_src
= TRIG_COUNT
;
524 if (cmd
->stop_src
!= TRIG_NONE
&& cmd
->stop_src
!= TRIG_COUNT
)
531 /* step 3: make sure arguments are trivially compatible */
532 if (cmd
->start_arg
!= 0) {
537 if (cmd
->scan_begin_arg
!= 0) {
538 cmd
->scan_begin_arg
= 0;
541 if (cmd
->convert_src
== TRIG_TIMER
) {
542 if (cmd
->convert_arg
< this_board
->ai_ns_min
) {
543 cmd
->convert_arg
= this_board
->ai_ns_min
;
546 } else { /* TRIG_EXT */
547 if (cmd
->convert_arg
!= 0) {
548 cmd
->convert_arg
= 0;
553 if (!cmd
->chanlist_len
) {
554 cmd
->chanlist_len
= 1;
557 if (cmd
->chanlist_len
> this_board
->n_aichan
) {
558 cmd
->chanlist_len
= this_board
->n_aichan
;
561 if (cmd
->scan_end_arg
!= cmd
->chanlist_len
) {
562 cmd
->scan_end_arg
= cmd
->chanlist_len
;
565 if (cmd
->stop_src
== TRIG_COUNT
) {
566 if (!cmd
->stop_arg
) {
570 } else { /* TRIG_NONE */
571 if (cmd
->stop_arg
!= 0) {
581 /* step 4: fix up any arguments */
582 if (cmd
->convert_src
== TRIG_TIMER
) {
583 tmp
= cmd
->convert_arg
;
584 i8253_cascade_ns_to_timer(this_board
->i8254_osc_base
,
585 &divisor1
, &divisor2
, &cmd
->convert_arg
,
586 cmd
->flags
& TRIG_ROUND_MASK
);
587 if (cmd
->convert_arg
< this_board
->ai_ns_min
)
588 cmd
->convert_arg
= this_board
->ai_ns_min
;
589 if (tmp
!= cmd
->convert_arg
)
600 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
602 unsigned int divisor1
= 0, divisor2
= 0, dma_flags
, bytes
, dmairq
;
603 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
605 if (cmd
->start_src
!= TRIG_NOW
)
607 if (cmd
->scan_begin_src
!= TRIG_FOLLOW
)
609 if (cmd
->scan_end_src
!= TRIG_COUNT
)
611 if (cmd
->scan_end_arg
!= cmd
->chanlist_len
)
613 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
614 if (devpriv
->irq_blocked
)
617 if (cmd
->convert_src
== TRIG_TIMER
) {
618 if (cmd
->convert_arg
< this_board
->ai_ns_min
)
619 cmd
->convert_arg
= this_board
->ai_ns_min
;
621 i8253_cascade_ns_to_timer(this_board
->i8254_osc_base
, &divisor1
,
622 &divisor2
, &cmd
->convert_arg
,
623 cmd
->flags
& TRIG_ROUND_MASK
);
624 if (divisor1
== 1) { /* PCL816 crash if any divisor is set to 1 */
634 start_pacer(dev
, -1, 0, 0); /* stop pacer */
636 if (!check_and_setup_channel_list(dev
, s
, cmd
->chanlist
,
641 devpriv
->ai_act_scan
= 0;
642 s
->async
->cur_chan
= 0;
643 devpriv
->irq_blocked
= 1;
644 devpriv
->ai_poll_ptr
= 0;
645 devpriv
->irq_was_now_closed
= 0;
647 if (cmd
->stop_src
== TRIG_COUNT
) {
648 devpriv
->ai_scans
= cmd
->stop_arg
;
649 devpriv
->ai_neverending
= 0;
651 devpriv
->ai_scans
= 0;
652 devpriv
->ai_neverending
= 1;
655 if ((cmd
->flags
& TRIG_WAKE_EOS
)) { /* don't we want wake up every scan? */
656 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
657 /* devpriv->ai_eos=1; */
658 /* if (devpriv->ai_n_chan==1) */
659 /* devpriv->dma=0; // DMA is useless for this situation */
663 bytes
= devpriv
->hwdmasize
[0];
664 if (!devpriv
->ai_neverending
) {
665 bytes
= s
->async
->cmd
.chanlist_len
* s
->async
->cmd
.chanlist_len
* sizeof(short); /* how many */
666 devpriv
->dma_runs_to_end
= bytes
/ devpriv
->hwdmasize
[0]; /* how many DMA pages we must fill */
667 devpriv
->last_dma_run
= bytes
% devpriv
->hwdmasize
[0]; /* on last dma transfer must be moved */
668 devpriv
->dma_runs_to_end
--;
669 if (devpriv
->dma_runs_to_end
>= 0)
670 bytes
= devpriv
->hwdmasize
[0];
672 devpriv
->dma_runs_to_end
= -1;
674 devpriv
->next_dma_buf
= 0;
675 set_dma_mode(devpriv
->dma
, DMA_MODE_READ
);
676 dma_flags
= claim_dma_lock();
677 clear_dma_ff(devpriv
->dma
);
678 set_dma_addr(devpriv
->dma
, devpriv
->hwdmaptr
[0]);
679 set_dma_count(devpriv
->dma
, bytes
);
680 release_dma_lock(dma_flags
);
681 enable_dma(devpriv
->dma
);
684 start_pacer(dev
, 1, divisor1
, divisor2
);
685 dmairq
= ((devpriv
->dma
& 0x3) << 4) | (dev
->irq
& 0x7);
687 switch (cmd
->convert_src
) {
689 devpriv
->int816_mode
= INT_TYPE_AI1_DMA
;
690 outb(0x32, dev
->iobase
+ PCL816_CONTROL
); /* Pacer+IRQ+DMA */
691 outb(dmairq
, dev
->iobase
+ PCL816_STATUS
); /* write irq and DMA to card */
695 devpriv
->int816_mode
= INT_TYPE_AI3_DMA
;
696 outb(0x34, dev
->iobase
+ PCL816_CONTROL
); /* Ext trig+IRQ+DMA */
697 outb(dmairq
, dev
->iobase
+ PCL816_STATUS
); /* write irq to card */
701 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
705 static int pcl816_ai_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
708 unsigned int top1
, top2
, i
;
711 return 0; /* poll is valid only for DMA transfer */
713 spin_lock_irqsave(&dev
->spinlock
, flags
);
715 for (i
= 0; i
< 20; i
++) {
716 top1
= get_dma_residue(devpriv
->dma
); /* where is now DMA */
717 top2
= get_dma_residue(devpriv
->dma
);
722 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
726 top1
= devpriv
->hwdmasize
[0] - top1
; /* where is now DMA in buffer */
727 top1
>>= 1; /* sample position */
728 top2
= top1
- devpriv
->ai_poll_ptr
;
729 if (top2
< 1) { /* no new samples */
730 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
734 transfer_from_dma_buf(dev
, s
,
735 (short *) devpriv
->dmabuf
[devpriv
->next_dma_buf
],
736 devpriv
->ai_poll_ptr
, top2
);
738 devpriv
->ai_poll_ptr
= top1
; /* new buffer position */
739 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
741 return s
->async
->buf_write_count
- s
->async
->buf_read_count
;
745 ==============================================================================
746 cancel any mode 1-4 AI
748 static int pcl816_ai_cancel(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
750 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
752 if (devpriv
->irq_blocked
> 0) {
753 switch (devpriv
->int816_mode
) {
755 case INT_TYPE_AI1_DMA_RTC
:
756 case INT_TYPE_AI3_DMA_RTC
:
757 set_rtc_irq_bit(0); /* stop RTC */
758 del_timer(&devpriv
->rtc_irq_timer
);
760 case INT_TYPE_AI1_DMA
:
761 case INT_TYPE_AI3_DMA
:
762 disable_dma(devpriv
->dma
);
763 case INT_TYPE_AI1_INT
:
764 case INT_TYPE_AI3_INT
:
765 outb(inb(dev
->iobase
+ PCL816_CONTROL
) & 0x73, dev
->iobase
+ PCL816_CONTROL
); /* Stop A/D */
767 outb(0, dev
->iobase
+ PCL816_CONTROL
); /* Stop A/D */
768 outb(0xb0, dev
->iobase
+ PCL816_CTRCTL
); /* Stop pacer */
769 outb(0x70, dev
->iobase
+ PCL816_CTRCTL
);
770 outb(0, dev
->iobase
+ PCL816_AD_LO
);
771 inb(dev
->iobase
+ PCL816_AD_LO
);
772 inb(dev
->iobase
+ PCL816_AD_HI
);
773 outb(0, dev
->iobase
+ PCL816_CLRINT
); /* clear INT request */
774 outb(0, dev
->iobase
+ PCL816_CONTROL
); /* Stop A/D */
775 devpriv
->irq_blocked
= 0;
776 devpriv
->irq_was_now_closed
= devpriv
->int816_mode
;
777 devpriv
->int816_mode
= 0;
778 devpriv
->last_int_sub
= s
;
784 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");
790 ==============================================================================
793 static int pcl816_check(unsigned long iobase
)
795 outb(0x00, iobase
+ PCL816_MUX
);
797 if (inb(iobase
+ PCL816_MUX
) != 0x00)
798 return 1; /* there isn't card */
799 outb(0x55, iobase
+ PCL816_MUX
);
801 if (inb(iobase
+ PCL816_MUX
) != 0x55)
802 return 1; /* there isn't card */
803 outb(0x00, iobase
+ PCL816_MUX
);
805 outb(0x18, iobase
+ PCL816_CONTROL
);
807 if (inb(iobase
+ PCL816_CONTROL
) != 0x18)
808 return 1; /* there isn't card */
809 return 0; /* ok, card exist */
813 ==============================================================================
814 reset whole PCL-816 cards
816 static void pcl816_reset(struct comedi_device
*dev
)
818 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
819 /* outb (0, dev->iobase + PCL818_DA_HI); */
821 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
822 /* outb (0, dev->iobase + PCL818_DO_LO); */
824 outb(0, dev
->iobase
+ PCL816_CONTROL
);
825 outb(0, dev
->iobase
+ PCL816_MUX
);
826 outb(0, dev
->iobase
+ PCL816_CLRINT
);
827 outb(0xb0, dev
->iobase
+ PCL816_CTRCTL
); /* Stop pacer */
828 outb(0x70, dev
->iobase
+ PCL816_CTRCTL
);
829 outb(0x30, dev
->iobase
+ PCL816_CTRCTL
);
830 outb(0, dev
->iobase
+ PCL816_RANGE
);
834 ==============================================================================
835 Start/stop pacer onboard pacer
838 start_pacer(struct comedi_device
*dev
, int mode
, unsigned int divisor1
,
839 unsigned int divisor2
)
841 outb(0x32, dev
->iobase
+ PCL816_CTRCTL
);
842 outb(0xff, dev
->iobase
+ PCL816_CTR0
);
843 outb(0x00, dev
->iobase
+ PCL816_CTR0
);
845 outb(0xb4, dev
->iobase
+ PCL816_CTRCTL
); /* set counter 2 as mode 3 */
846 outb(0x74, dev
->iobase
+ PCL816_CTRCTL
); /* set counter 1 as mode 3 */
850 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode
, divisor1
,
852 outb(divisor2
& 0xff, dev
->iobase
+ PCL816_CTR2
);
853 outb((divisor2
>> 8) & 0xff, dev
->iobase
+ PCL816_CTR2
);
854 outb(divisor1
& 0xff, dev
->iobase
+ PCL816_CTR1
);
855 outb((divisor1
>> 8) & 0xff, dev
->iobase
+ PCL816_CTR1
);
858 /* clear pending interrupts (just in case) */
859 /* outb(0, dev->iobase + PCL816_CLRINT); */
863 ==============================================================================
864 Check if channel list from user is builded correctly
865 If it's ok, then program scan/gain logic
868 check_and_setup_channel_list(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
869 unsigned int *chanlist
, int chanlen
)
871 unsigned int chansegment
[16];
872 unsigned int i
, nowmustbechan
, seglen
, segpos
;
874 /* correct channel and range number check itself comedi/range.c */
876 comedi_error(dev
, "range/channel list is empty!");
881 chansegment
[0] = chanlist
[0]; /* first channel is everytime ok */
882 for (i
= 1, seglen
= 1; i
< chanlen
; i
++, seglen
++) {
883 /* build part of chanlist */
884 DEBUG(printk("%d. %d %d\n", i
, CR_CHAN(chanlist
[i
]),
885 CR_RANGE(chanlist
[i
]));
887 if (chanlist
[0] == chanlist
[i
])
888 break; /* we detect loop, this must by finish */
890 (CR_CHAN(chansegment
[i
- 1]) + 1) % chanlen
;
891 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
892 /* channel list isn't continous :-( */
894 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
895 dev
->minor
, i
, CR_CHAN(chanlist
[i
]),
896 nowmustbechan
, CR_CHAN(chanlist
[0]));
899 chansegment
[i
] = chanlist
[i
]; /* well, this is next correct channel in list */
902 for (i
= 0, segpos
= 0; i
< chanlen
; i
++) { /* check whole chanlist */
903 DEBUG(printk("%d %d=%d %d\n",
904 CR_CHAN(chansegment
[i
% seglen
]),
905 CR_RANGE(chansegment
[i
% seglen
]),
906 CR_CHAN(chanlist
[i
]),
907 CR_RANGE(chanlist
[i
]));
909 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
911 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
912 dev
->minor
, i
, CR_CHAN(chansegment
[i
]),
913 CR_RANGE(chansegment
[i
]),
914 CR_AREF(chansegment
[i
]),
915 CR_CHAN(chanlist
[i
% seglen
]),
916 CR_RANGE(chanlist
[i
% seglen
]),
917 CR_AREF(chansegment
[i
% seglen
]));
918 return 0; /* chan/gain list is strange */
925 devpriv
->ai_act_chanlist_len
= seglen
;
926 devpriv
->ai_act_chanlist_pos
= 0;
928 for (i
= 0; i
< seglen
; i
++) { /* store range list to card */
929 devpriv
->ai_act_chanlist
[i
] = CR_CHAN(chanlist
[i
]);
930 outb(CR_CHAN(chanlist
[0]) & 0xf, dev
->iobase
+ PCL816_MUX
);
931 outb(CR_RANGE(chanlist
[0]), dev
->iobase
+ PCL816_RANGE
); /* select gain */
936 outb(devpriv
->ai_act_chanlist
[0] | (devpriv
->ai_act_chanlist
[seglen
- 1] << 4), dev
->iobase
+ PCL816_MUX
); /* select channel interval to scan */
938 return 1; /* we can serve this with MUX logic */
943 ==============================================================================
944 Enable(1)/disable(0) periodic interrupts from RTC
946 static int set_rtc_irq_bit(unsigned char bit
)
953 if (RTC_timer_lock
> 1)
957 if (RTC_timer_lock
< 0)
959 if (RTC_timer_lock
> 0)
965 val
= CMOS_READ(RTC_CONTROL
);
971 CMOS_WRITE(val
, RTC_CONTROL
);
972 CMOS_READ(RTC_INTR_FLAGS
);
973 restore_flags(flags
);
979 ==============================================================================
980 Free any resources that we have claimed
982 static void free_resources(struct comedi_device
*dev
)
984 /* printk("free_resource()\n"); */
986 pcl816_ai_cancel(dev
, devpriv
->sub_ai
);
989 free_dma(devpriv
->dma
);
990 if (devpriv
->dmabuf
[0])
991 free_pages(devpriv
->dmabuf
[0], devpriv
->dmapages
[0]);
992 if (devpriv
->dmabuf
[1])
993 free_pages(devpriv
->dmabuf
[1], devpriv
->dmapages
[1]);
995 if (devpriv
->rtc_irq
)
996 free_irq(devpriv
->rtc_irq
, dev
);
997 if ((devpriv
->dma_rtc
) && (RTC_lock
== 1)) {
998 if (devpriv
->rtc_iobase
)
999 release_region(devpriv
->rtc_iobase
,
1000 devpriv
->rtc_iosize
);
1006 free_irq(dev
->irq
, dev
);
1008 release_region(dev
->iobase
, this_board
->io_range
);
1009 /* printk("free_resource() end\n"); */
1013 ==============================================================================
1018 static int pcl816_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
1021 unsigned long iobase
;
1022 unsigned int irq
, dma
;
1023 unsigned long pages
;
1025 struct comedi_subdevice
*s
;
1027 /* claim our I/O space */
1028 iobase
= it
->options
[0];
1029 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev
->minor
,
1030 this_board
->name
, iobase
);
1032 if (!request_region(iobase
, this_board
->io_range
, "pcl816")) {
1033 printk("I/O port conflict\n");
1037 dev
->iobase
= iobase
;
1039 if (pcl816_check(iobase
)) {
1040 printk(", I cann't detect board. FAIL!\n");
1044 ret
= alloc_private(dev
, sizeof(struct pcl816_private
));
1046 return ret
; /* Can't alloc mem */
1048 /* set up some name stuff */
1049 dev
->board_name
= this_board
->name
;
1053 if (this_board
->IRQbits
!= 0) { /* board support IRQ */
1054 irq
= it
->options
[1];
1055 if (irq
) { /* we want to use IRQ */
1056 if (((1 << irq
) & this_board
->IRQbits
) == 0) {
1058 (", IRQ %u is out of allowed range, DISABLING IT",
1060 irq
= 0; /* Bad IRQ */
1062 if (request_irq(irq
, interrupt_pcl816
, 0, "pcl816", dev
)) {
1064 (", unable to allocate IRQ %u, DISABLING IT",
1066 irq
= 0; /* Can't use IRQ */
1068 printk(", irq=%u", irq
);
1076 devpriv
->irq_free
= 1;
1077 } /* 1=we have allocated irq */
1079 devpriv
->irq_free
= 0;
1081 devpriv
->irq_blocked
= 0; /* number of subdevice which use IRQ */
1082 devpriv
->int816_mode
= 0; /* mode of irq */
1085 /* grab RTC for DMA operations */
1086 devpriv
->dma_rtc
= 0;
1087 if (it
->options
[2] > 0) { /* we want to use DMA */
1088 if (RTC_lock
== 0) {
1089 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT
,
1093 devpriv
->rtc_iobase
= RTC_PORT(0);
1094 devpriv
->rtc_iosize
= RTC_IO_EXTENT
;
1096 #ifdef UNTESTED_CODE
1097 if (!request_irq(RTC_IRQ
, interrupt_pcl816_ai_mode13_dma_rtc
, 0,
1098 "pcl816 DMA (RTC)", dev
)) {
1099 devpriv
->dma_rtc
= 1;
1100 devpriv
->rtc_irq
= RTC_IRQ
;
1101 printk(", dma_irq=%u", devpriv
->rtc_irq
);
1104 if (RTC_lock
== 0) {
1105 if (devpriv
->rtc_iobase
)
1106 release_region(devpriv
->rtc_iobase
,
1107 devpriv
->rtc_iosize
);
1109 devpriv
->rtc_iobase
= 0;
1110 devpriv
->rtc_iosize
= 0;
1113 printk("pcl816: RTC code missing");
1123 if ((devpriv
->irq_free
== 0) && (devpriv
->dma_rtc
== 0))
1124 goto no_dma
; /* if we haven't IRQ, we can't use DMA */
1126 if (this_board
->DMAbits
!= 0) { /* board support DMA */
1127 dma
= it
->options
[2];
1129 goto no_dma
; /* DMA disabled */
1131 if (((1 << dma
) & this_board
->DMAbits
) == 0) {
1132 printk(", DMA is out of allowed range, FAIL!\n");
1133 return -EINVAL
; /* Bad DMA */
1135 ret
= request_dma(dma
, "pcl816");
1137 printk(", unable to allocate DMA %u, FAIL!\n", dma
);
1138 return -EBUSY
; /* DMA isn't free */
1142 printk(", dma=%u", dma
);
1143 pages
= 2; /* we need 16KB */
1144 devpriv
->dmabuf
[0] = __get_dma_pages(GFP_KERNEL
, pages
);
1146 if (!devpriv
->dmabuf
[0]) {
1147 printk(", unable to allocate DMA buffer, FAIL!\n");
1148 /* maybe experiment with try_to_free_pages() will help .... */
1149 return -EBUSY
; /* no buffer :-( */
1151 devpriv
->dmapages
[0] = pages
;
1152 devpriv
->hwdmaptr
[0] = virt_to_bus((void *)devpriv
->dmabuf
[0]);
1153 devpriv
->hwdmasize
[0] = (1 << pages
) * PAGE_SIZE
;
1154 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1156 if (devpriv
->dma_rtc
== 0) { /* we must do duble buff :-( */
1157 devpriv
->dmabuf
[1] = __get_dma_pages(GFP_KERNEL
, pages
);
1158 if (!devpriv
->dmabuf
[1]) {
1160 (", unable to allocate DMA buffer, FAIL!\n");
1163 devpriv
->dmapages
[1] = pages
;
1164 devpriv
->hwdmaptr
[1] =
1165 virt_to_bus((void *)devpriv
->dmabuf
[1]);
1166 devpriv
->hwdmasize
[1] = (1 << pages
) * PAGE_SIZE
;
1172 /* if (this_board->n_aochan > 0)
1173 subdevs[1] = COMEDI_SUBD_AO;
1174 if (this_board->n_dichan > 0)
1175 subdevs[2] = COMEDI_SUBD_DI;
1176 if (this_board->n_dochan > 0)
1177 subdevs[3] = COMEDI_SUBD_DO;
1180 ret
= alloc_subdevices(dev
, 1);
1184 s
= dev
->subdevices
+ 0;
1185 if (this_board
->n_aichan
> 0) {
1186 s
->type
= COMEDI_SUBD_AI
;
1187 devpriv
->sub_ai
= s
;
1188 dev
->read_subdev
= s
;
1189 s
->subdev_flags
= SDF_READABLE
| SDF_CMD_READ
;
1190 s
->n_chan
= this_board
->n_aichan
;
1191 s
->subdev_flags
|= SDF_DIFF
;
1192 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1193 s
->maxdata
= this_board
->ai_maxdata
;
1194 s
->len_chanlist
= this_board
->ai_chanlist
;
1195 s
->range_table
= this_board
->ai_range_type
;
1196 s
->cancel
= pcl816_ai_cancel
;
1197 s
->do_cmdtest
= pcl816_ai_cmdtest
;
1198 s
->do_cmd
= pcl816_ai_cmd
;
1199 s
->poll
= pcl816_ai_poll
;
1200 s
->insn_read
= pcl816_ai_insn_read
;
1202 s
->type
= COMEDI_SUBD_UNUSED
;
1206 case COMEDI_SUBD_AO
:
1207 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
1208 s
->n_chan
= this_board
->n_aochan
;
1209 s
->maxdata
= this_board
->ao_maxdata
;
1210 s
->len_chanlist
= this_board
->ao_chanlist
;
1211 s
->range_table
= this_board
->ao_range_type
;
1214 case COMEDI_SUBD_DI
:
1215 s
->subdev_flags
= SDF_READABLE
;
1216 s
->n_chan
= this_board
->n_dichan
;
1218 s
->len_chanlist
= this_board
->n_dichan
;
1219 s
->range_table
= &range_digital
;
1222 case COMEDI_SUBD_DO
:
1223 s
->subdev_flags
= SDF_WRITABLE
;
1224 s
->n_chan
= this_board
->n_dochan
;
1226 s
->len_chanlist
= this_board
->n_dochan
;
1227 s
->range_table
= &range_digital
;
1239 ==============================================================================
1242 static int pcl816_detach(struct comedi_device
*dev
)
1244 DEBUG(printk("comedi%d: pcl816: remove\n", dev
->minor
);
1246 free_resources(dev
);
1248 if (devpriv
->dma_rtc
)