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>
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, {
105 const char *name; // board name
106 int n_ranges; // len of range list
107 int n_aichan; // num of A/D chans in diferencial mode
108 unsigned int ai_ns_min; // minimal alllowed delay between samples (in ns)
109 int n_aochan; // num of D/A chans
110 int n_dichan; // num of DI chans
111 int n_dochan; // num of DO chans
112 const struct comedi_lrange *ai_range_type; // default A/D rangelist
113 const struct comedi_lrange *ao_range_type; // dafault D/A rangelist
114 unsigned int io_range; // len of IO space
115 unsigned int IRQbits; // allowed interrupts
116 unsigned int DMAbits; // allowed DMA chans
117 int ai_maxdata; // maxdata for A/D
118 int ao_maxdata; // maxdata for D/A
119 int ai_chanlist; // allowed len of channel list A/D
120 int ao_chanlist; // allowed len of channel list D/A
121 int i8254_osc_base; // 1/frequency of on board oscilator in ns
124 static const boardtype boardtypes[] = {
125 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
126 &range_pcl816, PCLx1x_RANGE,
129 0xffff, // 16-bit card
130 0xffff, // D/A maxdata
134 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
135 &range_pcl816, PCLx1x_RANGE,
138 0x3fff, /* 14 bit card */
145 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
146 #define devpriv ((pcl816_private *)dev->private)
147 #define this_board ((const boardtype *)dev->board_ptr)
149 static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it);
150 static int pcl816_detach(struct comedi_device * dev);
153 static int RTC_lock = 0; /* RTC lock */
154 static int RTC_timer_lock = 0; /* RTC int lock */
157 static struct comedi_driver driver_pcl816 = {
158 driver_name:"pcl816",
160 attach:pcl816_attach,
161 detach:pcl816_detach,
162 board_name:&boardtypes[0].name,
163 num_names:n_boardtypes,
164 offset:sizeof(boardtype),
167 COMEDI_INITCLEANUP(driver_pcl816);
170 unsigned int dma; // used DMA, 0=don't use DMA
171 int dma_rtc; // 1=RTC used with DMA, 0=no RTC alloc
173 unsigned long rtc_iobase; // RTC port region
174 unsigned int rtc_iosize;
175 unsigned int rtc_irq;
177 unsigned long dmabuf[2]; // pointers to begin of DMA buffers
178 unsigned int dmapages[2]; // len of DMA buffers in PAGE_SIZEs
179 unsigned int hwdmaptr[2]; // hardware address of DMA buffers
180 unsigned int hwdmasize[2]; // len of DMA buffers in Bytes
181 unsigned int dmasamplsize; // size in samples hwdmasize[0]/2
182 unsigned int last_top_dma; // DMA pointer in last RTC int
183 int next_dma_buf; // which DMA buffer will be used next round
184 long dma_runs_to_end; // how many we must permorm DMA transfer to end of record
185 unsigned long last_dma_run; // how many bytes we must transfer on last DMA page
187 unsigned int ai_scans; // len of scanlist
188 unsigned char ai_neverending; // if=1, then we do neverending record (you must use cancel())
189 int irq_free; // 1=have allocated IRQ
190 int irq_blocked; // 1=IRQ now uses any subdev
192 int rtc_irq_blocked; // 1=we now do AI with DMA&RTC
194 int irq_was_now_closed; // when IRQ finish, there's stored int816_mode for last interrupt
195 int int816_mode; // who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma
196 struct comedi_subdevice *last_int_sub; // ptr to subdevice which now finish
197 int ai_act_scan; // how many scans we finished
198 unsigned int ai_act_chanlist[16]; // MUX setting for actual AI operations
199 unsigned int ai_act_chanlist_len; // how long is actual MUX list
200 unsigned int ai_act_chanlist_pos; // actual position in MUX list
201 unsigned int ai_poll_ptr; // how many sampes transfer poll
202 struct comedi_subdevice *sub_ai; // ptr to AI subdevice
204 struct timer_list rtc_irq_timer; // timer for RTC sanity check
205 unsigned long rtc_freq; // RTC int freq
210 ==============================================================================
212 static int check_and_setup_channel_list(struct comedi_device * dev,
213 struct comedi_subdevice * s, unsigned int *chanlist, int chanlen);
214 static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
215 static void start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
216 unsigned int divisor2);
218 static int set_rtc_irq_bit(unsigned char bit);
221 static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
222 struct comedi_cmd * cmd);
223 static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s);
226 ==============================================================================
227 ANALOG INPUT MODE0, 816 cards, slow version
229 static int pcl816_ai_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
230 struct comedi_insn * insn, unsigned int * data)
235 DPRINTK("mode 0 analog input\n");
236 // software trigger, DMA and INT off
237 outb(0, dev->iobase + PCL816_CONTROL);
238 // clear INT (conversion end) flag
239 outb(0, dev->iobase + PCL816_CLRINT);
241 // Set the input channel
242 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
243 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
245 for (n = 0; n < insn->n; n++) {
247 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
251 if (!(inb(dev->iobase + PCL816_STATUS) &
252 PCL816_STATUS_DRDY_MASK)) {
256 PCL816_AD_HI) << 8) |
257 (inb(dev->iobase + PCL816_AD_LO)));
259 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
264 // Return timeout error
266 comedi_error(dev, "A/D insn timeout\n");
268 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
277 ==============================================================================
278 analog input interrupt mode 1 & 3, 818 cards
279 one sample per interrupt version
281 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
283 struct comedi_device *dev = d;
284 struct comedi_subdevice *s = dev->subdevices + 0;
286 int timeout = 50; /* wait max 50us */
289 if (!(inb(dev->iobase + PCL816_STATUS) &
290 PCL816_STATUS_DRDY_MASK))
294 if (!timeout) { // timeout, bail error
295 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
296 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
297 pcl816_ai_cancel(dev, s);
298 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
299 comedi_event(dev, s);
305 low = inb(dev->iobase + PCL816_AD_LO);
306 hi = inb(dev->iobase + PCL816_AD_HI);
308 comedi_buf_put(s->async, (hi << 8) | low);
310 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
312 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
313 devpriv->ai_act_chanlist_pos = 0;
315 if (s->async->cur_chan == 0) {
316 devpriv->ai_act_scan++;
319 if (!devpriv->ai_neverending)
320 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
321 /* all data sampled */
322 pcl816_ai_cancel(dev, s);
323 s->async->events |= COMEDI_CB_EOA;
325 comedi_event(dev, s);
330 ==============================================================================
331 analog input dma mode 1 & 3, 816 cards
333 static void transfer_from_dma_buf(struct comedi_device * dev, struct comedi_subdevice * s,
334 short * ptr, unsigned int bufptr, unsigned int len)
338 s->async->events = 0;
340 for (i = 0; i < len; i++) {
342 comedi_buf_put(s->async, ptr[bufptr++]);
344 if (++devpriv->ai_act_chanlist_pos >=
345 devpriv->ai_act_chanlist_len) {
346 devpriv->ai_act_chanlist_pos = 0;
347 devpriv->ai_act_scan++;
350 if (!devpriv->ai_neverending)
351 if (devpriv->ai_act_scan >= devpriv->ai_scans) { // all data sampled
352 pcl816_ai_cancel(dev, s);
353 s->async->events |= COMEDI_CB_EOA;
354 s->async->events |= COMEDI_CB_BLOCK;
359 comedi_event(dev, s);
362 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
364 struct comedi_device *dev = d;
365 struct comedi_subdevice *s = dev->subdevices + 0;
366 int len, bufptr, this_dma_buf;
367 unsigned long dma_flags;
370 disable_dma(devpriv->dma);
371 this_dma_buf = devpriv->next_dma_buf;
373 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { // switch dma bufs
375 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
376 set_dma_mode(devpriv->dma, DMA_MODE_READ);
377 dma_flags = claim_dma_lock();
378 // clear_dma_ff (devpriv->dma);
379 set_dma_addr(devpriv->dma,
380 devpriv->hwdmaptr[devpriv->next_dma_buf]);
381 if (devpriv->dma_runs_to_end) {
382 set_dma_count(devpriv->dma,
383 devpriv->hwdmasize[devpriv->next_dma_buf]);
385 set_dma_count(devpriv->dma, devpriv->last_dma_run);
387 release_dma_lock(dma_flags);
388 enable_dma(devpriv->dma);
391 devpriv->dma_runs_to_end--;
392 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
394 ptr = (short *) devpriv->dmabuf[this_dma_buf];
396 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
397 bufptr = devpriv->ai_poll_ptr;
398 devpriv->ai_poll_ptr = 0;
400 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
405 ==============================================================================
408 static irqreturn_t interrupt_pcl816(int irq, void *d PT_REGS_ARG)
410 struct comedi_device *dev = d;
413 if (!dev->attached) {
414 comedi_error(dev, "premature interrupt");
418 switch (devpriv->int816_mode) {
419 case INT_TYPE_AI1_DMA:
420 case INT_TYPE_AI3_DMA:
421 return interrupt_pcl816_ai_mode13_dma(irq, d);
422 case INT_TYPE_AI1_INT:
423 case INT_TYPE_AI3_INT:
424 return interrupt_pcl816_ai_mode13_int(irq, d);
427 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
428 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
429 (!devpriv->int816_mode)) {
430 if (devpriv->irq_was_now_closed) {
431 devpriv->irq_was_now_closed = 0;
432 // comedi_error(dev,"last IRQ..");
435 comedi_error(dev, "bad IRQ!");
438 comedi_error(dev, "IRQ from unknow source!");
443 ==============================================================================
446 static void pcl816_cmdtest_out(int e, struct comedi_cmd * cmd)
448 rt_printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
449 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
450 rt_printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
451 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
452 rt_printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
454 rt_printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
455 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
459 ==============================================================================
461 static int pcl816_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
462 struct comedi_cmd * cmd)
465 int tmp, divisor1, divisor2;
467 DEBUG(rt_printk("pcl816 pcl812_ai_cmdtest\n");
468 pcl816_cmdtest_out(-1, cmd););
470 /* step 1: make sure trigger sources are trivially valid */
471 tmp = cmd->start_src;
472 cmd->start_src &= TRIG_NOW;
473 if (!cmd->start_src || tmp != cmd->start_src)
476 tmp = cmd->scan_begin_src;
477 cmd->scan_begin_src &= TRIG_FOLLOW;
478 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
481 if (!cmd->convert_src & (TRIG_EXT | TRIG_TIMER))
484 tmp = cmd->scan_end_src;
485 cmd->scan_end_src &= TRIG_COUNT;
486 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
490 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
491 if (!cmd->stop_src || tmp != cmd->stop_src)
498 /* step 2: make sure trigger sources are unique and mutually compatible */
500 if (cmd->start_src != TRIG_NOW) {
501 cmd->start_src = TRIG_NOW;
505 if (cmd->scan_begin_src != TRIG_FOLLOW) {
506 cmd->scan_begin_src = TRIG_FOLLOW;
510 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
511 cmd->convert_src = TRIG_TIMER;
515 if (cmd->scan_end_src != TRIG_COUNT) {
516 cmd->scan_end_src = TRIG_COUNT;
520 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
527 /* step 3: make sure arguments are trivially compatible */
528 if (cmd->start_arg != 0) {
533 if (cmd->scan_begin_arg != 0) {
534 cmd->scan_begin_arg = 0;
537 if (cmd->convert_src == TRIG_TIMER) {
538 if (cmd->convert_arg < this_board->ai_ns_min) {
539 cmd->convert_arg = this_board->ai_ns_min;
542 } else { /* TRIG_EXT */
543 if (cmd->convert_arg != 0) {
544 cmd->convert_arg = 0;
549 if (!cmd->chanlist_len) {
550 cmd->chanlist_len = 1;
553 if (cmd->chanlist_len > this_board->n_aichan) {
554 cmd->chanlist_len = this_board->n_aichan;
557 if (cmd->scan_end_arg != cmd->chanlist_len) {
558 cmd->scan_end_arg = cmd->chanlist_len;
561 if (cmd->stop_src == TRIG_COUNT) {
562 if (!cmd->stop_arg) {
566 } else { /* TRIG_NONE */
567 if (cmd->stop_arg != 0) {
577 /* step 4: fix up any arguments */
578 if (cmd->convert_src == TRIG_TIMER) {
579 tmp = cmd->convert_arg;
580 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
581 &divisor1, &divisor2, &cmd->convert_arg,
582 cmd->flags & TRIG_ROUND_MASK);
583 if (cmd->convert_arg < this_board->ai_ns_min)
584 cmd->convert_arg = this_board->ai_ns_min;
585 if (tmp != cmd->convert_arg)
596 static int pcl816_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
598 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
599 struct comedi_cmd *cmd = &s->async->cmd;
601 if (cmd->start_src != TRIG_NOW)
603 if (cmd->scan_begin_src != TRIG_FOLLOW)
605 if (cmd->scan_end_src != TRIG_COUNT)
607 if (cmd->scan_end_arg != cmd->chanlist_len)
609 // if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL;
610 if (devpriv->irq_blocked)
613 if (cmd->convert_src == TRIG_TIMER) {
614 if (cmd->convert_arg < this_board->ai_ns_min)
615 cmd->convert_arg = this_board->ai_ns_min;
617 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
618 &divisor2, &cmd->convert_arg,
619 cmd->flags & TRIG_ROUND_MASK);
620 if (divisor1 == 1) { // PCL816 crash if any divisor is set to 1
630 start_pacer(dev, -1, 0, 0); // stop pacer
632 if (!check_and_setup_channel_list(dev, s, cmd->chanlist,
637 devpriv->ai_act_scan = 0;
638 s->async->cur_chan = 0;
639 devpriv->irq_blocked = 1;
640 devpriv->ai_poll_ptr = 0;
641 devpriv->irq_was_now_closed = 0;
643 if (cmd->stop_src == TRIG_COUNT) {
644 devpriv->ai_scans = cmd->stop_arg;
645 devpriv->ai_neverending = 0;
647 devpriv->ai_scans = 0;
648 devpriv->ai_neverending = 1;
651 if ((cmd->flags & TRIG_WAKE_EOS)) { // don't we want wake up every scan?
652 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
653 // devpriv->ai_eos=1;
654 //if (devpriv->ai_n_chan==1)
655 // devpriv->dma=0; // DMA is useless for this situation
659 bytes = devpriv->hwdmasize[0];
660 if (!devpriv->ai_neverending) {
661 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); // how many
662 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; // how many DMA pages we must fill
663 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; //on last dma transfer must be moved
664 devpriv->dma_runs_to_end--;
665 if (devpriv->dma_runs_to_end >= 0)
666 bytes = devpriv->hwdmasize[0];
668 devpriv->dma_runs_to_end = -1;
670 devpriv->next_dma_buf = 0;
671 set_dma_mode(devpriv->dma, DMA_MODE_READ);
672 dma_flags = claim_dma_lock();
673 clear_dma_ff(devpriv->dma);
674 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
675 set_dma_count(devpriv->dma, bytes);
676 release_dma_lock(dma_flags);
677 enable_dma(devpriv->dma);
680 start_pacer(dev, 1, divisor1, divisor2);
681 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
683 switch (cmd->convert_src) {
685 devpriv->int816_mode = INT_TYPE_AI1_DMA;
686 outb(0x32, dev->iobase + PCL816_CONTROL); // Pacer+IRQ+DMA
687 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq and DMA to card
691 devpriv->int816_mode = INT_TYPE_AI3_DMA;
692 outb(0x34, dev->iobase + PCL816_CONTROL); // Ext trig+IRQ+DMA
693 outb(dmairq, dev->iobase + PCL816_STATUS); // write irq to card
697 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
701 static int pcl816_ai_poll(struct comedi_device * dev, struct comedi_subdevice * s)
704 unsigned int top1, top2, i;
707 return 0; // poll is valid only for DMA transfer
709 comedi_spin_lock_irqsave(&dev->spinlock, flags);
711 for (i = 0; i < 20; i++) {
712 top1 = get_dma_residue(devpriv->dma); // where is now DMA
713 top2 = get_dma_residue(devpriv->dma);
718 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
722 top1 = devpriv->hwdmasize[0] - top1; // where is now DMA in buffer
723 top1 >>= 1; // sample position
724 top2 = top1 - devpriv->ai_poll_ptr;
725 if (top2 < 1) { // no new samples
726 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
730 transfer_from_dma_buf(dev, s,
731 (short *) devpriv->dmabuf[devpriv->next_dma_buf],
732 devpriv->ai_poll_ptr, top2);
734 devpriv->ai_poll_ptr = top1; // new buffer position
735 comedi_spin_unlock_irqrestore(&dev->spinlock, flags);
737 return s->async->buf_write_count - s->async->buf_read_count;
741 ==============================================================================
742 cancel any mode 1-4 AI
744 static int pcl816_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
746 // DEBUG(rt_printk("pcl816_ai_cancel()\n");)
748 if (devpriv->irq_blocked > 0) {
749 switch (devpriv->int816_mode) {
751 case INT_TYPE_AI1_DMA_RTC:
752 case INT_TYPE_AI3_DMA_RTC:
753 set_rtc_irq_bit(0); // stop RTC
754 del_timer(&devpriv->rtc_irq_timer);
756 case INT_TYPE_AI1_DMA:
757 case INT_TYPE_AI3_DMA:
758 disable_dma(devpriv->dma);
759 case INT_TYPE_AI1_INT:
760 case INT_TYPE_AI3_INT:
761 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
763 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
764 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
765 outb(0x70, dev->iobase + PCL816_CTRCTL);
766 outb(0, dev->iobase + PCL816_AD_LO);
767 inb(dev->iobase + PCL816_AD_LO);
768 inb(dev->iobase + PCL816_AD_HI);
769 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
770 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
771 devpriv->irq_blocked = 0;
772 devpriv->irq_was_now_closed = devpriv->int816_mode;
773 devpriv->int816_mode = 0;
774 devpriv->last_int_sub = s;
780 DEBUG(rt_printk("comedi: pcl816_ai_cancel() successful\n");
786 ==============================================================================
789 static int pcl816_check(unsigned long iobase)
791 outb(0x00, iobase + PCL816_MUX);
793 if (inb(iobase + PCL816_MUX) != 0x00)
794 return 1; //there isn't card
795 outb(0x55, iobase + PCL816_MUX);
797 if (inb(iobase + PCL816_MUX) != 0x55)
798 return 1; //there isn't card
799 outb(0x00, iobase + PCL816_MUX);
801 outb(0x18, iobase + PCL816_CONTROL);
803 if (inb(iobase + PCL816_CONTROL) != 0x18)
804 return 1; //there isn't card
805 return 0; // ok, card exist
809 ==============================================================================
810 reset whole PCL-816 cards
812 static void pcl816_reset(struct comedi_device * dev)
814 // outb (0, dev->iobase + PCL818_DA_LO); // DAC=0V
815 // outb (0, dev->iobase + PCL818_DA_HI);
816 // comedi_udelay (1);
817 // outb (0, dev->iobase + PCL818_DO_HI); // DO=$0000
818 // outb (0, dev->iobase + PCL818_DO_LO);
819 // comedi_udelay (1);
820 outb(0, dev->iobase + PCL816_CONTROL);
821 outb(0, dev->iobase + PCL816_MUX);
822 outb(0, dev->iobase + PCL816_CLRINT);
823 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
824 outb(0x70, dev->iobase + PCL816_CTRCTL);
825 outb(0x30, dev->iobase + PCL816_CTRCTL);
826 outb(0, dev->iobase + PCL816_RANGE);
830 ==============================================================================
831 Start/stop pacer onboard pacer
834 start_pacer(struct comedi_device * dev, int mode, unsigned int divisor1,
835 unsigned int divisor2)
837 outb(0x32, dev->iobase + PCL816_CTRCTL);
838 outb(0xff, dev->iobase + PCL816_CTR0);
839 outb(0x00, dev->iobase + PCL816_CTR0);
841 outb(0xb4, dev->iobase + PCL816_CTRCTL); // set counter 2 as mode 3
842 outb(0x74, dev->iobase + PCL816_CTRCTL); // set counter 1 as mode 3
846 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
848 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
849 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
850 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
851 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
854 /* clear pending interrupts (just in case) */
855 // outb(0, dev->iobase + PCL816_CLRINT);
859 ==============================================================================
860 Check if channel list from user is builded correctly
861 If it's ok, then program scan/gain logic
864 check_and_setup_channel_list(struct comedi_device * dev, struct comedi_subdevice * s,
865 unsigned int *chanlist, int chanlen)
867 unsigned int chansegment[16];
868 unsigned int i, nowmustbechan, seglen, segpos;
870 // correct channel and range number check itself comedi/range.c
872 comedi_error(dev, "range/channel list is empty!");
877 chansegment[0] = chanlist[0]; // first channel is everytime ok
878 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
879 // build part of chanlist
880 DEBUG(rt_printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
881 CR_RANGE(chanlist[i]));
883 if (chanlist[0] == chanlist[i])
884 break; // we detect loop, this must by finish
886 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
887 if (nowmustbechan != CR_CHAN(chanlist[i])) {
888 // channel list isn't continous :-(
890 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
891 dev->minor, i, CR_CHAN(chanlist[i]),
892 nowmustbechan, CR_CHAN(chanlist[0]));
895 chansegment[i] = chanlist[i]; // well, this is next correct channel in list
898 for (i = 0, segpos = 0; i < chanlen; i++) { // check whole chanlist
899 DEBUG(rt_printk("%d %d=%d %d\n",
900 CR_CHAN(chansegment[i % seglen]),
901 CR_RANGE(chansegment[i % seglen]),
902 CR_CHAN(chanlist[i]),
903 CR_RANGE(chanlist[i]));
905 if (chanlist[i] != chansegment[i % seglen]) {
907 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
908 dev->minor, i, CR_CHAN(chansegment[i]),
909 CR_RANGE(chansegment[i]),
910 CR_AREF(chansegment[i]),
911 CR_CHAN(chanlist[i % seglen]),
912 CR_RANGE(chanlist[i % seglen]),
913 CR_AREF(chansegment[i % seglen]));
914 return 0; // chan/gain list is strange
921 devpriv->ai_act_chanlist_len = seglen;
922 devpriv->ai_act_chanlist_pos = 0;
924 for (i = 0; i < seglen; i++) { // store range list to card
925 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
926 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
927 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
932 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
934 return 1; // we can serve this with MUX logic
939 ==============================================================================
940 Enable(1)/disable(0) periodic interrupts from RTC
942 static int set_rtc_irq_bit(unsigned char bit)
949 if (RTC_timer_lock > 1)
953 if (RTC_timer_lock < 0)
955 if (RTC_timer_lock > 0)
961 val = CMOS_READ(RTC_CONTROL);
967 CMOS_WRITE(val, RTC_CONTROL);
968 CMOS_READ(RTC_INTR_FLAGS);
969 restore_flags(flags);
975 ==============================================================================
976 Free any resources that we have claimed
978 static void free_resources(struct comedi_device * dev)
980 //rt_printk("free_resource()\n");
982 pcl816_ai_cancel(dev, devpriv->sub_ai);
985 free_dma(devpriv->dma);
986 if (devpriv->dmabuf[0])
987 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
988 if (devpriv->dmabuf[1])
989 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
991 if (devpriv->rtc_irq)
992 comedi_free_irq(devpriv->rtc_irq, dev);
993 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
994 if (devpriv->rtc_iobase)
995 release_region(devpriv->rtc_iobase,
996 devpriv->rtc_iosize);
1002 free_irq(dev->irq, dev);
1004 release_region(dev->iobase, this_board->io_range);
1005 //rt_printk("free_resource() end\n");
1009 ==============================================================================
1014 static int pcl816_attach(struct comedi_device * dev, struct comedi_devconfig * it)
1017 unsigned long iobase;
1018 unsigned int irq, dma;
1019 unsigned long pages;
1021 struct comedi_subdevice *s;
1023 /* claim our I/O space */
1024 iobase = it->options[0];
1025 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1026 this_board->name, iobase);
1028 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1029 rt_printk("I/O port conflict\n");
1033 dev->iobase = iobase;
1035 if (pcl816_check(iobase)) {
1036 rt_printk(", I cann't detect board. FAIL!\n");
1040 if ((ret = alloc_private(dev, sizeof(pcl816_private))) < 0)
1041 return ret; /* Can't alloc mem */
1043 /* set up some name stuff */
1044 dev->board_name = this_board->name;
1048 if (this_board->IRQbits != 0) { /* board support IRQ */
1049 irq = it->options[1];
1050 if (irq) { /* we want to use IRQ */
1051 if (((1 << irq) & this_board->IRQbits) == 0) {
1053 (", IRQ %u is out of allowed range, DISABLING IT",
1055 irq = 0; /* Bad IRQ */
1057 if (comedi_request_irq(irq, interrupt_pcl816, 0,
1060 (", unable to allocate IRQ %u, DISABLING IT",
1062 irq = 0; /* Can't use IRQ */
1064 rt_printk(", irq=%u", irq);
1072 devpriv->irq_free = 1;
1073 } /* 1=we have allocated irq */
1075 devpriv->irq_free = 0;
1077 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1078 devpriv->int816_mode = 0; /* mode of irq */
1081 /* grab RTC for DMA operations */
1082 devpriv->dma_rtc = 0;
1083 if (it->options[2] > 0) { // we want to use DMA
1084 if (RTC_lock == 0) {
1085 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1089 devpriv->rtc_iobase = RTC_PORT(0);
1090 devpriv->rtc_iosize = RTC_IO_EXTENT;
1092 #ifdef UNTESTED_CODE
1093 if (!comedi_request_irq(RTC_IRQ,
1094 interrupt_pcl816_ai_mode13_dma_rtc, 0,
1095 "pcl816 DMA (RTC)", dev)) {
1096 devpriv->dma_rtc = 1;
1097 devpriv->rtc_irq = RTC_IRQ;
1098 rt_printk(", dma_irq=%u", devpriv->rtc_irq);
1101 if (RTC_lock == 0) {
1102 if (devpriv->rtc_iobase)
1103 release_region(devpriv->rtc_iobase,
1104 devpriv->rtc_iosize);
1106 devpriv->rtc_iobase = 0;
1107 devpriv->rtc_iosize = 0;
1110 printk("pcl816: RTC code missing");
1120 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1121 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1123 if (this_board->DMAbits != 0) { /* board support DMA */
1124 dma = it->options[2];
1126 goto no_dma; /* DMA disabled */
1128 if (((1 << dma) & this_board->DMAbits) == 0) {
1129 rt_printk(", DMA is out of allowed range, FAIL!\n");
1130 return -EINVAL; /* Bad DMA */
1132 ret = request_dma(dma, "pcl816");
1134 rt_printk(", unable to allocate DMA %u, FAIL!\n", dma);
1135 return -EBUSY; /* DMA isn't free */
1139 rt_printk(", dma=%u", dma);
1140 pages = 2; /* we need 16KB */
1141 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1143 if (!devpriv->dmabuf[0]) {
1144 rt_printk(", unable to allocate DMA buffer, FAIL!\n");
1145 /* maybe experiment with try_to_free_pages() will help .... */
1146 return -EBUSY; /* no buffer :-( */
1148 devpriv->dmapages[0] = pages;
1149 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1150 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1151 //rt_printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE);
1153 if (devpriv->dma_rtc == 0) { // we must do duble buff :-(
1154 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1155 if (!devpriv->dmabuf[1]) {
1157 (", unable to allocate DMA buffer, FAIL!\n");
1160 devpriv->dmapages[1] = pages;
1161 devpriv->hwdmaptr[1] =
1162 virt_to_bus((void *)devpriv->dmabuf[1]);
1163 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1169 /* if (this_board->n_aochan > 0)
1170 subdevs[1] = COMEDI_SUBD_AO;
1171 if (this_board->n_dichan > 0)
1172 subdevs[2] = COMEDI_SUBD_DI;
1173 if (this_board->n_dochan > 0)
1174 subdevs[3] = COMEDI_SUBD_DO;
1176 if ((ret = alloc_subdevices(dev, 1)) < 0)
1179 s = dev->subdevices + 0;
1180 if (this_board->n_aichan > 0) {
1181 s->type = COMEDI_SUBD_AI;
1182 devpriv->sub_ai = s;
1183 dev->read_subdev = s;
1184 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1185 s->n_chan = this_board->n_aichan;
1186 s->subdev_flags |= SDF_DIFF;
1187 //printk (", %dchans DIFF DAC - %d", s->n_chan, i);
1188 s->maxdata = this_board->ai_maxdata;
1189 s->len_chanlist = this_board->ai_chanlist;
1190 s->range_table = this_board->ai_range_type;
1191 s->cancel = pcl816_ai_cancel;
1192 s->do_cmdtest = pcl816_ai_cmdtest;
1193 s->do_cmd = pcl816_ai_cmd;
1194 s->poll = pcl816_ai_poll;
1195 s->insn_read = pcl816_ai_insn_read;
1197 s->type = COMEDI_SUBD_UNUSED;
1201 case COMEDI_SUBD_AO:
1202 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1203 s->n_chan = this_board->n_aochan;
1204 s->maxdata = this_board->ao_maxdata;
1205 s->len_chanlist = this_board->ao_chanlist;
1206 s->range_table = this_board->ao_range_type;
1209 case COMEDI_SUBD_DI:
1210 s->subdev_flags = SDF_READABLE;
1211 s->n_chan = this_board->n_dichan;
1213 s->len_chanlist = this_board->n_dichan;
1214 s->range_table = &range_digital;
1217 case COMEDI_SUBD_DO:
1218 s->subdev_flags = SDF_WRITABLE;
1219 s->n_chan = this_board->n_dochan;
1221 s->len_chanlist = this_board->n_dochan;
1222 s->range_table = &range_digital;
1234 ==============================================================================
1237 static int pcl816_detach(struct comedi_device * dev)
1239 DEBUG(rt_printk("comedi%d: pcl816: remove\n", dev->minor);
1241 free_resources(dev);
1243 if (devpriv->dma_rtc)