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/gfp.h>
40 #include <linux/delay.h>
48 /* boards constants */
50 #define PCLx1x_RANGE 16
52 /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
54 /* INTEL 8254 counters */
58 /* R: counter read-back register W: counter control */
59 #define PCL816_CTRCTL 7
61 /* R: A/D high byte W: A/D range control */
62 #define PCL816_RANGE 9
63 /* W: clear INT request */
64 #define PCL816_CLRINT 10
65 /* R: next mux scan channel W: mux scan channel & range control pointer */
67 /* R/W: operation control register */
68 #define PCL816_CONTROL 12
70 /* R: return status byte W: set DMA/IRQ */
71 #define PCL816_STATUS 13
72 #define PCL816_STATUS_DRDY_MASK 0x80
74 /* R: low byte of A/D W: soft A/D trigger */
75 #define PCL816_AD_LO 8
76 /* R: high byte of A/D W: A/D range control */
77 #define PCL816_AD_HI 9
79 /* type of interrupt handler */
80 #define INT_TYPE_AI1_INT 1
81 #define INT_TYPE_AI1_DMA 2
82 #define INT_TYPE_AI3_INT 4
83 #define INT_TYPE_AI3_DMA 5
85 #define INT_TYPE_AI1_DMA_RTC 9
86 #define INT_TYPE_AI3_DMA_RTC 10
90 #define RTC_IO_EXTENT 0x10
93 #define MAGIC_DMA_WORD 0x5a5a
95 static const struct comedi_lrange range_pcl816 = { 8, {
107 struct pcl816_board {
109 const char *name; /* board name */
110 int n_ranges; /* len of range list */
111 int n_aichan; /* num of A/D chans in diferencial mode */
112 unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */
113 int n_aochan; /* num of D/A chans */
114 int n_dichan; /* num of DI chans */
115 int n_dochan; /* num of DO chans */
116 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
117 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
118 unsigned int io_range; /* len of IO space */
119 unsigned int IRQbits; /* allowed interrupts */
120 unsigned int DMAbits; /* allowed DMA chans */
121 int ai_maxdata; /* maxdata for A/D */
122 int ao_maxdata; /* maxdata for D/A */
123 int ai_chanlist; /* allowed len of channel list A/D */
124 int ao_chanlist; /* allowed len of channel list D/A */
125 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
128 #define devpriv ((struct pcl816_private *)dev->private)
129 #define this_board ((const struct pcl816_board *)dev->board_ptr)
132 static int RTC_lock; /* RTC lock */
133 static int RTC_timer_lock; /* RTC int lock */
136 struct pcl816_private {
138 unsigned int dma; /* used DMA, 0=don't use DMA */
139 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
141 unsigned long rtc_iobase; /* RTC port region */
142 unsigned int rtc_iosize;
143 unsigned int rtc_irq;
145 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
146 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
147 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
148 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
149 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
150 unsigned int last_top_dma; /* DMA pointer in last RTC int */
151 int next_dma_buf; /* which DMA buffer will be used next round */
152 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
153 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
155 unsigned int ai_scans; /* len of scanlist */
156 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
157 int irq_free; /* 1=have allocated IRQ */
158 int irq_blocked; /* 1=IRQ now uses any subdev */
160 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
162 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
163 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
164 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
165 int ai_act_scan; /* how many scans we finished */
166 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
167 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
168 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
169 unsigned int ai_n_chan; /* how many channels per scan */
170 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
171 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
173 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
174 unsigned long rtc_freq; /* RTC int freq */
179 ==============================================================================
181 static int check_channel_list(struct comedi_device *dev,
182 struct comedi_subdevice *s,
183 unsigned int *chanlist, unsigned int chanlen);
184 static void setup_channel_list(struct comedi_device *dev,
185 struct comedi_subdevice *s,
186 unsigned int *chanlist, unsigned int seglen);
187 static int pcl816_ai_cancel(struct comedi_device *dev,
188 struct comedi_subdevice *s);
189 static void start_pacer(struct comedi_device *dev, int mode,
190 unsigned int divisor1, unsigned int divisor2);
192 static int set_rtc_irq_bit(unsigned char bit);
195 static int pcl816_ai_cmdtest(struct comedi_device *dev,
196 struct comedi_subdevice *s,
197 struct comedi_cmd *cmd);
198 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
201 ==============================================================================
202 ANALOG INPUT MODE0, 816 cards, slow version
204 static int pcl816_ai_insn_read(struct comedi_device *dev,
205 struct comedi_subdevice *s,
206 struct comedi_insn *insn, unsigned int *data)
211 DPRINTK("mode 0 analog input\n");
212 /* software trigger, DMA and INT off */
213 outb(0, dev->iobase + PCL816_CONTROL);
214 /* clear INT (conversion end) flag */
215 outb(0, dev->iobase + PCL816_CLRINT);
217 /* Set the input channel */
218 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
220 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
222 for (n = 0; n < insn->n; n++) {
224 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
228 if (!(inb(dev->iobase + PCL816_STATUS) &
229 PCL816_STATUS_DRDY_MASK)) {
230 /* return read value */
233 PCL816_AD_HI) << 8) |
234 (inb(dev->iobase + PCL816_AD_LO)));
235 /* clear INT (conversion end) flag */
236 outb(0, dev->iobase + PCL816_CLRINT);
241 /* Return timeout error */
243 comedi_error(dev, "A/D insn timeout\n");
245 /* clear INT (conversion end) flag */
246 outb(0, dev->iobase + PCL816_CLRINT);
255 ==============================================================================
256 analog input interrupt mode 1 & 3, 818 cards
257 one sample per interrupt version
259 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
261 struct comedi_device *dev = d;
262 struct comedi_subdevice *s = dev->subdevices + 0;
264 int timeout = 50; /* wait max 50us */
267 if (!(inb(dev->iobase + PCL816_STATUS) &
268 PCL816_STATUS_DRDY_MASK))
272 if (!timeout) { /* timeout, bail error */
273 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
274 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
275 pcl816_ai_cancel(dev, s);
276 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
277 comedi_event(dev, s);
283 low = inb(dev->iobase + PCL816_AD_LO);
284 hi = inb(dev->iobase + PCL816_AD_HI);
286 comedi_buf_put(s->async, (hi << 8) | low);
288 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
290 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
291 devpriv->ai_act_chanlist_pos = 0;
293 s->async->cur_chan++;
294 if (s->async->cur_chan >= devpriv->ai_n_chan) {
295 s->async->cur_chan = 0;
296 devpriv->ai_act_scan++;
299 if (!devpriv->ai_neverending)
300 /* all data sampled */
301 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
302 /* all data sampled */
303 pcl816_ai_cancel(dev, s);
304 s->async->events |= COMEDI_CB_EOA;
306 comedi_event(dev, s);
311 ==============================================================================
312 analog input dma mode 1 & 3, 816 cards
314 static void transfer_from_dma_buf(struct comedi_device *dev,
315 struct comedi_subdevice *s, short *ptr,
316 unsigned int bufptr, unsigned int len)
320 s->async->events = 0;
322 for (i = 0; i < len; i++) {
324 comedi_buf_put(s->async, ptr[bufptr++]);
326 if (++devpriv->ai_act_chanlist_pos >=
327 devpriv->ai_act_chanlist_len) {
328 devpriv->ai_act_chanlist_pos = 0;
331 s->async->cur_chan++;
332 if (s->async->cur_chan >= devpriv->ai_n_chan) {
333 s->async->cur_chan = 0;
334 devpriv->ai_act_scan++;
337 if (!devpriv->ai_neverending)
338 /* all data sampled */
339 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
340 pcl816_ai_cancel(dev, s);
341 s->async->events |= COMEDI_CB_EOA;
342 s->async->events |= COMEDI_CB_BLOCK;
347 comedi_event(dev, s);
350 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
352 struct comedi_device *dev = d;
353 struct comedi_subdevice *s = dev->subdevices + 0;
354 int len, bufptr, this_dma_buf;
355 unsigned long dma_flags;
358 disable_dma(devpriv->dma);
359 this_dma_buf = devpriv->next_dma_buf;
361 /* switch dma bufs */
362 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
364 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
365 set_dma_mode(devpriv->dma, DMA_MODE_READ);
366 dma_flags = claim_dma_lock();
367 /* clear_dma_ff (devpriv->dma); */
368 set_dma_addr(devpriv->dma,
369 devpriv->hwdmaptr[devpriv->next_dma_buf]);
370 if (devpriv->dma_runs_to_end) {
371 set_dma_count(devpriv->dma,
372 devpriv->hwdmasize[devpriv->
375 set_dma_count(devpriv->dma, devpriv->last_dma_run);
377 release_dma_lock(dma_flags);
378 enable_dma(devpriv->dma);
381 devpriv->dma_runs_to_end--;
382 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
384 ptr = (short *)devpriv->dmabuf[this_dma_buf];
386 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
387 bufptr = devpriv->ai_poll_ptr;
388 devpriv->ai_poll_ptr = 0;
390 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
395 ==============================================================================
398 static irqreturn_t interrupt_pcl816(int irq, void *d)
400 struct comedi_device *dev = d;
403 if (!dev->attached) {
404 comedi_error(dev, "premature interrupt");
408 switch (devpriv->int816_mode) {
409 case INT_TYPE_AI1_DMA:
410 case INT_TYPE_AI3_DMA:
411 return interrupt_pcl816_ai_mode13_dma(irq, d);
412 case INT_TYPE_AI1_INT:
413 case INT_TYPE_AI3_INT:
414 return interrupt_pcl816_ai_mode13_int(irq, d);
417 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
418 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
419 (!devpriv->int816_mode)) {
420 if (devpriv->irq_was_now_closed) {
421 devpriv->irq_was_now_closed = 0;
422 /* comedi_error(dev,"last IRQ.."); */
425 comedi_error(dev, "bad IRQ!");
428 comedi_error(dev, "IRQ from unknown source!");
433 ==============================================================================
436 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
438 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
439 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
440 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
441 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
442 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
443 cmd->stop_src, cmd->scan_end_src);
444 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
445 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
449 ==============================================================================
451 static int pcl816_ai_cmdtest(struct comedi_device *dev,
452 struct comedi_subdevice *s, struct comedi_cmd *cmd)
455 int tmp, divisor1 = 0, divisor2 = 0;
457 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
458 pcl816_cmdtest_out(-1, cmd);
461 /* step 1: make sure trigger sources are trivially valid */
462 tmp = cmd->start_src;
463 cmd->start_src &= TRIG_NOW;
464 if (!cmd->start_src || tmp != cmd->start_src)
467 tmp = cmd->scan_begin_src;
468 cmd->scan_begin_src &= TRIG_FOLLOW;
469 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
472 tmp = cmd->convert_src;
473 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
474 if (!cmd->convert_src || tmp != cmd->convert_src)
477 tmp = cmd->scan_end_src;
478 cmd->scan_end_src &= TRIG_COUNT;
479 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
483 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
484 if (!cmd->stop_src || tmp != cmd->stop_src)
492 * step 2: make sure trigger sources
493 * are unique and mutually compatible
496 if (cmd->start_src != TRIG_NOW) {
497 cmd->start_src = TRIG_NOW;
501 if (cmd->scan_begin_src != TRIG_FOLLOW) {
502 cmd->scan_begin_src = TRIG_FOLLOW;
506 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
507 cmd->convert_src = TRIG_TIMER;
511 if (cmd->scan_end_src != TRIG_COUNT) {
512 cmd->scan_end_src = TRIG_COUNT;
516 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
523 /* step 3: make sure arguments are trivially compatible */
524 if (cmd->start_arg != 0) {
529 if (cmd->scan_begin_arg != 0) {
530 cmd->scan_begin_arg = 0;
533 if (cmd->convert_src == TRIG_TIMER) {
534 if (cmd->convert_arg < this_board->ai_ns_min) {
535 cmd->convert_arg = this_board->ai_ns_min;
538 } else { /* TRIG_EXT */
539 if (cmd->convert_arg != 0) {
540 cmd->convert_arg = 0;
545 if (cmd->scan_end_arg != cmd->chanlist_len) {
546 cmd->scan_end_arg = cmd->chanlist_len;
549 if (cmd->stop_src == TRIG_COUNT) {
550 if (!cmd->stop_arg) {
554 } else { /* TRIG_NONE */
555 if (cmd->stop_arg != 0) {
565 /* step 4: fix up any arguments */
566 if (cmd->convert_src == TRIG_TIMER) {
567 tmp = cmd->convert_arg;
568 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
569 &divisor1, &divisor2,
571 cmd->flags & TRIG_ROUND_MASK);
572 if (cmd->convert_arg < this_board->ai_ns_min)
573 cmd->convert_arg = this_board->ai_ns_min;
574 if (tmp != cmd->convert_arg)
582 /* step 5: complain about special chanlist considerations */
585 if (!check_channel_list(dev, s, cmd->chanlist,
587 return 5; /* incorrect channels list */
593 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
595 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
596 struct comedi_cmd *cmd = &s->async->cmd;
599 if (cmd->start_src != TRIG_NOW)
601 if (cmd->scan_begin_src != TRIG_FOLLOW)
603 if (cmd->scan_end_src != TRIG_COUNT)
605 if (cmd->scan_end_arg != cmd->chanlist_len)
607 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
608 if (devpriv->irq_blocked)
611 if (cmd->convert_src == TRIG_TIMER) {
612 if (cmd->convert_arg < this_board->ai_ns_min)
613 cmd->convert_arg = this_board->ai_ns_min;
615 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
616 &divisor2, &cmd->convert_arg,
617 cmd->flags & TRIG_ROUND_MASK);
619 /* PCL816 crash if any divisor is set to 1 */
630 start_pacer(dev, -1, 0, 0); /* stop pacer */
632 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
635 setup_channel_list(dev, s, cmd->chanlist, seglen);
638 devpriv->ai_n_chan = cmd->chanlist_len;
639 devpriv->ai_act_scan = 0;
640 s->async->cur_chan = 0;
641 devpriv->irq_blocked = 1;
642 devpriv->ai_poll_ptr = 0;
643 devpriv->irq_was_now_closed = 0;
645 if (cmd->stop_src == TRIG_COUNT) {
646 devpriv->ai_scans = cmd->stop_arg;
647 devpriv->ai_neverending = 0;
649 devpriv->ai_scans = 0;
650 devpriv->ai_neverending = 1;
653 /* don't we want wake up every scan? */
654 if ((cmd->flags & TRIG_WAKE_EOS)) {
656 "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) {
666 bytes = s->async->cmd.chanlist_len *
667 s->async->cmd.chanlist_len *
670 /* how many DMA pages we must fill */
671 devpriv->dma_runs_to_end = bytes /
672 devpriv->hwdmasize[0];
674 /* on last dma transfer must be moved */
675 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
676 devpriv->dma_runs_to_end--;
677 if (devpriv->dma_runs_to_end >= 0)
678 bytes = devpriv->hwdmasize[0];
680 devpriv->dma_runs_to_end = -1;
682 devpriv->next_dma_buf = 0;
683 set_dma_mode(devpriv->dma, DMA_MODE_READ);
684 dma_flags = claim_dma_lock();
685 clear_dma_ff(devpriv->dma);
686 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
687 set_dma_count(devpriv->dma, bytes);
688 release_dma_lock(dma_flags);
689 enable_dma(devpriv->dma);
692 start_pacer(dev, 1, divisor1, divisor2);
693 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
695 switch (cmd->convert_src) {
697 devpriv->int816_mode = INT_TYPE_AI1_DMA;
700 outb(0x32, dev->iobase + PCL816_CONTROL);
702 /* write irq and DMA to card */
703 outb(dmairq, dev->iobase + PCL816_STATUS);
707 devpriv->int816_mode = INT_TYPE_AI3_DMA;
709 /* Ext trig+IRQ+DMA */
710 outb(0x34, dev->iobase + PCL816_CONTROL);
712 /* write irq to card */
713 outb(dmairq, dev->iobase + PCL816_STATUS);
717 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
721 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
724 unsigned int top1, top2, i;
727 return 0; /* poll is valid only for DMA transfer */
729 spin_lock_irqsave(&dev->spinlock, flags);
731 for (i = 0; i < 20; i++) {
732 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
733 top2 = get_dma_residue(devpriv->dma);
738 spin_unlock_irqrestore(&dev->spinlock, flags);
742 /* where is now DMA in buffer */
743 top1 = devpriv->hwdmasize[0] - top1;
744 top1 >>= 1; /* sample position */
745 top2 = top1 - devpriv->ai_poll_ptr;
746 if (top2 < 1) { /* no new samples */
747 spin_unlock_irqrestore(&dev->spinlock, flags);
751 transfer_from_dma_buf(dev, s,
752 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
753 devpriv->ai_poll_ptr, top2);
755 devpriv->ai_poll_ptr = top1; /* new buffer position */
756 spin_unlock_irqrestore(&dev->spinlock, flags);
758 return s->async->buf_write_count - s->async->buf_read_count;
762 ==============================================================================
763 cancel any mode 1-4 AI
765 static int pcl816_ai_cancel(struct comedi_device *dev,
766 struct comedi_subdevice *s)
768 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
770 if (devpriv->irq_blocked > 0) {
771 switch (devpriv->int816_mode) {
773 case INT_TYPE_AI1_DMA_RTC:
774 case INT_TYPE_AI3_DMA_RTC:
775 set_rtc_irq_bit(0); /* stop RTC */
776 del_timer(&devpriv->rtc_irq_timer);
778 case INT_TYPE_AI1_DMA:
779 case INT_TYPE_AI3_DMA:
780 disable_dma(devpriv->dma);
781 case INT_TYPE_AI1_INT:
782 case INT_TYPE_AI3_INT:
783 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
784 dev->iobase + PCL816_CONTROL); /* Stop A/D */
786 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
789 outb(0xb0, dev->iobase + PCL816_CTRCTL);
790 outb(0x70, dev->iobase + PCL816_CTRCTL);
791 outb(0, dev->iobase + PCL816_AD_LO);
792 inb(dev->iobase + PCL816_AD_LO);
793 inb(dev->iobase + PCL816_AD_HI);
795 /* clear INT request */
796 outb(0, dev->iobase + PCL816_CLRINT);
799 outb(0, dev->iobase + PCL816_CONTROL);
800 devpriv->irq_blocked = 0;
801 devpriv->irq_was_now_closed = devpriv->int816_mode;
802 devpriv->int816_mode = 0;
803 devpriv->last_int_sub = s;
809 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
814 ==============================================================================
817 static int pcl816_check(unsigned long iobase)
819 outb(0x00, iobase + PCL816_MUX);
821 if (inb(iobase + PCL816_MUX) != 0x00)
822 return 1; /* there isn't card */
823 outb(0x55, iobase + PCL816_MUX);
825 if (inb(iobase + PCL816_MUX) != 0x55)
826 return 1; /* there isn't card */
827 outb(0x00, iobase + PCL816_MUX);
829 outb(0x18, iobase + PCL816_CONTROL);
831 if (inb(iobase + PCL816_CONTROL) != 0x18)
832 return 1; /* there isn't card */
833 return 0; /* ok, card exist */
837 ==============================================================================
838 reset whole PCL-816 cards
840 static void pcl816_reset(struct comedi_device *dev)
842 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
843 /* outb (0, dev->iobase + PCL818_DA_HI); */
845 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
846 /* outb (0, dev->iobase + PCL818_DO_LO); */
848 outb(0, dev->iobase + PCL816_CONTROL);
849 outb(0, dev->iobase + PCL816_MUX);
850 outb(0, dev->iobase + PCL816_CLRINT);
851 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
852 outb(0x70, dev->iobase + PCL816_CTRCTL);
853 outb(0x30, dev->iobase + PCL816_CTRCTL);
854 outb(0, dev->iobase + PCL816_RANGE);
858 ==============================================================================
859 Start/stop pacer onboard pacer
862 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
863 unsigned int divisor2)
865 outb(0x32, dev->iobase + PCL816_CTRCTL);
866 outb(0xff, dev->iobase + PCL816_CTR0);
867 outb(0x00, dev->iobase + PCL816_CTR0);
870 /* set counter 2 as mode 3 */
871 outb(0xb4, dev->iobase + PCL816_CTRCTL);
872 /* set counter 1 as mode 3 */
873 outb(0x74, dev->iobase + PCL816_CTRCTL);
877 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
879 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
880 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
881 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
882 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
885 /* clear pending interrupts (just in case) */
886 /* outb(0, dev->iobase + PCL816_CLRINT); */
890 ==============================================================================
891 Check if channel list from user is builded correctly
892 If it's ok, then return non-zero length of repeated segment of channel list
895 check_channel_list(struct comedi_device *dev,
896 struct comedi_subdevice *s, unsigned int *chanlist,
897 unsigned int chanlen)
899 unsigned int chansegment[16];
900 unsigned int i, nowmustbechan, seglen, segpos;
902 /* correct channel and range number check itself comedi/range.c */
904 comedi_error(dev, "range/channel list is empty!");
909 /* first channel is every time ok */
910 chansegment[0] = chanlist[0];
911 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
912 /* build part of chanlist */
913 DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
914 CR_CHAN(chanlist[i]),
915 CR_RANGE(chanlist[i]));)
917 /* we detect loop, this must by finish */
918 if (chanlist[0] == chanlist[i])
921 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
922 if (nowmustbechan != CR_CHAN(chanlist[i])) {
923 /* channel list isn't continuous :-( */
925 "comedi%d: pcl816: channel list must "
926 "be continuous! chanlist[%i]=%d but "
927 "must be %d or %d!\n", dev->minor,
928 i, CR_CHAN(chanlist[i]), nowmustbechan,
929 CR_CHAN(chanlist[0]));
932 /* well, this is next correct channel in list */
933 chansegment[i] = chanlist[i];
936 /* check whole chanlist */
937 for (i = 0, segpos = 0; i < chanlen; i++) {
938 DEBUG(printk("%d %d=%d %d\n",
939 CR_CHAN(chansegment[i % seglen]),
940 CR_RANGE(chansegment[i % seglen]),
941 CR_CHAN(chanlist[i]),
942 CR_RANGE(chanlist[i]));)
943 if (chanlist[i] != chansegment[i % seglen]) {
945 "comedi%d: pcl816: bad channel or range"
946 " number! chanlist[%i]=%d,%d,%d and not"
947 " %d,%d,%d!\n", dev->minor, i,
948 CR_CHAN(chansegment[i]),
949 CR_RANGE(chansegment[i]),
950 CR_AREF(chansegment[i]),
951 CR_CHAN(chanlist[i % seglen]),
952 CR_RANGE(chanlist[i % seglen]),
953 CR_AREF(chansegment[i % seglen]));
954 return 0; /* chan/gain list is strange */
961 return seglen; /* we can serve this with MUX logic */
965 ==============================================================================
966 Program scan/gain logic with channel list.
969 setup_channel_list(struct comedi_device *dev,
970 struct comedi_subdevice *s, unsigned int *chanlist,
975 devpriv->ai_act_chanlist_len = seglen;
976 devpriv->ai_act_chanlist_pos = 0;
978 for (i = 0; i < seglen; i++) { /* store range list to card */
979 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
980 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
982 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
986 /* select channel interval to scan */
987 outb(devpriv->ai_act_chanlist[0] |
988 (devpriv->ai_act_chanlist[seglen - 1] << 4),
989 dev->iobase + PCL816_MUX);
994 ==============================================================================
995 Enable(1)/disable(0) periodic interrupts from RTC
997 static int set_rtc_irq_bit(unsigned char bit)
1000 unsigned long flags;
1004 if (RTC_timer_lock > 1)
1008 if (RTC_timer_lock < 0)
1010 if (RTC_timer_lock > 0)
1016 val = CMOS_READ(RTC_CONTROL);
1022 CMOS_WRITE(val, RTC_CONTROL);
1023 CMOS_READ(RTC_INTR_FLAGS);
1024 restore_flags(flags);
1029 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1032 unsigned long iobase;
1033 unsigned int irq, dma;
1034 unsigned long pages;
1036 struct comedi_subdevice *s;
1038 /* claim our I/O space */
1039 iobase = it->options[0];
1040 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1041 this_board->name, iobase);
1043 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1044 printk("I/O port conflict\n");
1048 dev->iobase = iobase;
1050 if (pcl816_check(iobase)) {
1051 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
1055 ret = alloc_private(dev, sizeof(struct pcl816_private));
1057 return ret; /* Can't alloc mem */
1059 /* set up some name stuff */
1060 dev->board_name = this_board->name;
1064 if (this_board->IRQbits != 0) { /* board support IRQ */
1065 irq = it->options[1];
1066 if (irq) { /* we want to use IRQ */
1067 if (((1 << irq) & this_board->IRQbits) == 0) {
1069 (", IRQ %u is out of allowed range, "
1070 "DISABLING IT", irq);
1071 irq = 0; /* Bad IRQ */
1074 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1076 (", unable to allocate IRQ %u, "
1077 "DISABLING IT", irq);
1078 irq = 0; /* Can't use IRQ */
1080 printk(KERN_INFO ", irq=%u", irq);
1087 if (irq) /* 1=we have allocated irq */
1088 devpriv->irq_free = 1;
1090 devpriv->irq_free = 0;
1092 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1093 devpriv->int816_mode = 0; /* mode of irq */
1096 /* grab RTC for DMA operations */
1097 devpriv->dma_rtc = 0;
1098 if (it->options[2] > 0) { /* we want to use DMA */
1099 if (RTC_lock == 0) {
1100 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1104 devpriv->rtc_iobase = RTC_PORT(0);
1105 devpriv->rtc_iosize = RTC_IO_EXTENT;
1107 #ifdef UNTESTED_CODE
1108 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1109 "pcl816 DMA (RTC)", dev)) {
1110 devpriv->dma_rtc = 1;
1111 devpriv->rtc_irq = RTC_IRQ;
1112 printk(", dma_irq=%u", devpriv->rtc_irq);
1115 if (RTC_lock == 0) {
1116 if (devpriv->rtc_iobase)
1117 release_region(devpriv->rtc_iobase,
1118 devpriv->rtc_iosize);
1120 devpriv->rtc_iobase = 0;
1121 devpriv->rtc_iosize = 0;
1124 printk("pcl816: RTC code missing");
1134 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1135 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1137 if (this_board->DMAbits != 0) { /* board support DMA */
1138 dma = it->options[2];
1140 goto no_dma; /* DMA disabled */
1142 if (((1 << dma) & this_board->DMAbits) == 0) {
1143 printk(", DMA is out of allowed range, FAIL!\n");
1144 return -EINVAL; /* Bad DMA */
1146 ret = request_dma(dma, "pcl816");
1149 ", unable to allocate DMA %u, FAIL!\n", dma);
1150 return -EBUSY; /* DMA isn't free */
1154 printk(KERN_INFO ", dma=%u", dma);
1155 pages = 2; /* we need 16KB */
1156 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1158 if (!devpriv->dmabuf[0]) {
1159 printk(", unable to allocate DMA buffer, FAIL!\n");
1161 * maybe experiment with try_to_free_pages()
1164 return -EBUSY; /* no buffer :-( */
1166 devpriv->dmapages[0] = pages;
1167 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1168 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1169 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1171 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1172 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1173 if (!devpriv->dmabuf[1]) {
1175 ", unable to allocate DMA buffer, "
1179 devpriv->dmapages[1] = pages;
1180 devpriv->hwdmaptr[1] =
1181 virt_to_bus((void *)devpriv->dmabuf[1]);
1182 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1188 /* if (this_board->n_aochan > 0)
1189 subdevs[1] = COMEDI_SUBD_AO;
1190 if (this_board->n_dichan > 0)
1191 subdevs[2] = COMEDI_SUBD_DI;
1192 if (this_board->n_dochan > 0)
1193 subdevs[3] = COMEDI_SUBD_DO;
1196 ret = alloc_subdevices(dev, 1);
1200 s = dev->subdevices + 0;
1201 if (this_board->n_aichan > 0) {
1202 s->type = COMEDI_SUBD_AI;
1203 devpriv->sub_ai = s;
1204 dev->read_subdev = s;
1205 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1206 s->n_chan = this_board->n_aichan;
1207 s->subdev_flags |= SDF_DIFF;
1208 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1209 s->maxdata = this_board->ai_maxdata;
1210 s->len_chanlist = this_board->ai_chanlist;
1211 s->range_table = this_board->ai_range_type;
1212 s->cancel = pcl816_ai_cancel;
1213 s->do_cmdtest = pcl816_ai_cmdtest;
1214 s->do_cmd = pcl816_ai_cmd;
1215 s->poll = pcl816_ai_poll;
1216 s->insn_read = pcl816_ai_insn_read;
1218 s->type = COMEDI_SUBD_UNUSED;
1222 case COMEDI_SUBD_AO:
1223 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1224 s->n_chan = this_board->n_aochan;
1225 s->maxdata = this_board->ao_maxdata;
1226 s->len_chanlist = this_board->ao_chanlist;
1227 s->range_table = this_board->ao_range_type;
1230 case COMEDI_SUBD_DI:
1231 s->subdev_flags = SDF_READABLE;
1232 s->n_chan = this_board->n_dichan;
1234 s->len_chanlist = this_board->n_dichan;
1235 s->range_table = &range_digital;
1238 case COMEDI_SUBD_DO:
1239 s->subdev_flags = SDF_WRITABLE;
1240 s->n_chan = this_board->n_dochan;
1242 s->len_chanlist = this_board->n_dochan;
1243 s->range_table = &range_digital;
1254 static void pcl816_detach(struct comedi_device *dev)
1257 pcl816_ai_cancel(dev, devpriv->sub_ai);
1260 free_dma(devpriv->dma);
1261 if (devpriv->dmabuf[0])
1262 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1263 if (devpriv->dmabuf[1])
1264 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1266 if (devpriv->rtc_irq)
1267 free_irq(devpriv->rtc_irq, dev);
1268 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1269 if (devpriv->rtc_iobase)
1270 release_region(devpriv->rtc_iobase,
1271 devpriv->rtc_iosize);
1276 free_irq(dev->irq, dev);
1278 release_region(dev->iobase, this_board->io_range);
1280 if (devpriv->dma_rtc)
1285 static const struct pcl816_board boardtypes[] = {
1286 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1287 &range_pcl816, PCLx1x_RANGE,
1288 0x00fc, /* IRQ mask */
1289 0x0a, /* DMA mask */
1290 0xffff, /* 16-bit card */
1291 0xffff, /* D/A maxdata */
1293 1, /* ao chan list */
1295 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1296 &range_pcl816, PCLx1x_RANGE,
1299 0x3fff, /* 14 bit card */
1306 static struct comedi_driver pcl816_driver = {
1307 .driver_name = "pcl816",
1308 .module = THIS_MODULE,
1309 .attach = pcl816_attach,
1310 .detach = pcl816_detach,
1311 .board_name = &boardtypes[0].name,
1312 .num_names = ARRAY_SIZE(boardtypes),
1313 .offset = sizeof(struct pcl816_board),
1315 module_comedi_driver(pcl816_driver);
1317 MODULE_AUTHOR("Comedi http://www.comedi.org");
1318 MODULE_DESCRIPTION("Comedi low-level driver");
1319 MODULE_LICENSE("GPL");