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, {
105 struct pcl816_board {
107 const char *name; /* board name */
108 int n_ranges; /* len of range list */
109 int n_aichan; /* num of A/D chans in diferencial mode */
110 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
111 int n_aochan; /* num of D/A chans */
112 int n_dichan; /* num of DI chans */
113 int n_dochan; /* num of DO chans */
114 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
115 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
116 unsigned int io_range; /* len of IO space */
117 unsigned int IRQbits; /* allowed interrupts */
118 unsigned int DMAbits; /* allowed DMA chans */
119 int ai_maxdata; /* maxdata for A/D */
120 int ao_maxdata; /* maxdata for D/A */
121 int ai_chanlist; /* allowed len of channel list A/D */
122 int ao_chanlist; /* allowed len of channel list D/A */
123 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,
152 struct comedi_devconfig *it);
153 static int pcl816_detach(struct comedi_device *dev);
156 static int RTC_lock = 0; /* RTC lock */
157 static int RTC_timer_lock = 0; /* RTC int lock */
160 static struct comedi_driver driver_pcl816 = {
161 .driver_name = "pcl816",
162 .module = THIS_MODULE,
163 .attach = pcl816_attach,
164 .detach = pcl816_detach,
165 .board_name = &boardtypes[0].name,
166 .num_names = n_boardtypes,
167 .offset = sizeof(struct pcl816_board),
170 COMEDI_INITCLEANUP(driver_pcl816);
172 struct pcl816_private {
174 unsigned int dma; /* used DMA, 0=don't use DMA */
175 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
177 unsigned long rtc_iobase; /* RTC port region */
178 unsigned int rtc_iosize;
179 unsigned int rtc_irq;
181 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
182 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
183 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
184 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
185 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
186 unsigned int last_top_dma; /* DMA pointer in last RTC int */
187 int next_dma_buf; /* which DMA buffer will be used next round */
188 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
189 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
191 unsigned int ai_scans; /* len of scanlist */
192 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
193 int irq_free; /* 1=have allocated IRQ */
194 int irq_blocked; /* 1=IRQ now uses any subdev */
196 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
198 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
199 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
200 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
201 int ai_act_scan; /* how many scans we finished */
202 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
203 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
204 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
205 unsigned int ai_n_chan; /* how many channels per scan */
206 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
207 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
209 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
210 unsigned long rtc_freq; /* RTC int freq */
215 ==============================================================================
217 static int check_channel_list(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 unsigned int *chanlist, unsigned int chanlen);
220 static void setup_channel_list(struct comedi_device *dev,
221 struct comedi_subdevice *s,
222 unsigned int *chanlist, unsigned int seglen);
223 static int pcl816_ai_cancel(struct comedi_device *dev,
224 struct comedi_subdevice *s);
225 static void start_pacer(struct comedi_device *dev, int mode,
226 unsigned int divisor1, unsigned int divisor2);
228 static int set_rtc_irq_bit(unsigned char bit);
231 static int pcl816_ai_cmdtest(struct comedi_device *dev,
232 struct comedi_subdevice *s,
233 struct comedi_cmd *cmd);
234 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
237 ==============================================================================
238 ANALOG INPUT MODE0, 816 cards, slow version
240 static int pcl816_ai_insn_read(struct comedi_device *dev,
241 struct comedi_subdevice *s,
242 struct comedi_insn *insn, unsigned int *data)
247 DPRINTK("mode 0 analog input\n");
248 /* software trigger, DMA and INT off */
249 outb(0, dev->iobase + PCL816_CONTROL);
250 /* clear INT (conversion end) flag */
251 outb(0, dev->iobase + PCL816_CLRINT);
253 /* Set the input channel */
254 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
255 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
257 for (n = 0; n < insn->n; n++) {
259 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
263 if (!(inb(dev->iobase + PCL816_STATUS) &
264 PCL816_STATUS_DRDY_MASK)) {
265 /* return read value */
268 PCL816_AD_HI) << 8) |
269 (inb(dev->iobase + PCL816_AD_LO)));
271 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
276 /* Return timeout error */
278 comedi_error(dev, "A/D insn timeout\n");
280 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
289 ==============================================================================
290 analog input interrupt mode 1 & 3, 818 cards
291 one sample per interrupt version
293 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
295 struct comedi_device *dev = d;
296 struct comedi_subdevice *s = dev->subdevices + 0;
298 int timeout = 50; /* wait max 50us */
301 if (!(inb(dev->iobase + PCL816_STATUS) &
302 PCL816_STATUS_DRDY_MASK))
306 if (!timeout) { /* timeout, bail error */
307 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
308 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
309 pcl816_ai_cancel(dev, s);
310 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
311 comedi_event(dev, s);
317 low = inb(dev->iobase + PCL816_AD_LO);
318 hi = inb(dev->iobase + PCL816_AD_HI);
320 comedi_buf_put(s->async, (hi << 8) | low);
322 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
324 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
325 devpriv->ai_act_chanlist_pos = 0;
327 s->async->cur_chan++;
328 if (s->async->cur_chan >= devpriv->ai_n_chan) {
329 s->async->cur_chan = 0;
330 devpriv->ai_act_scan++;
333 if (!devpriv->ai_neverending)
334 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
335 /* all data sampled */
336 pcl816_ai_cancel(dev, s);
337 s->async->events |= COMEDI_CB_EOA;
339 comedi_event(dev, s);
344 ==============================================================================
345 analog input dma mode 1 & 3, 816 cards
347 static void transfer_from_dma_buf(struct comedi_device *dev,
348 struct comedi_subdevice *s, short *ptr,
349 unsigned int bufptr, unsigned int len)
353 s->async->events = 0;
355 for (i = 0; i < len; i++) {
357 comedi_buf_put(s->async, ptr[bufptr++]);
359 if (++devpriv->ai_act_chanlist_pos >=
360 devpriv->ai_act_chanlist_len) {
361 devpriv->ai_act_chanlist_pos = 0;
364 s->async->cur_chan++;
365 if (s->async->cur_chan >= devpriv->ai_n_chan) {
366 s->async->cur_chan = 0;
367 devpriv->ai_act_scan++;
370 if (!devpriv->ai_neverending)
371 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
372 pcl816_ai_cancel(dev, s);
373 s->async->events |= COMEDI_CB_EOA;
374 s->async->events |= COMEDI_CB_BLOCK;
379 comedi_event(dev, s);
382 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
384 struct comedi_device *dev = d;
385 struct comedi_subdevice *s = dev->subdevices + 0;
386 int len, bufptr, this_dma_buf;
387 unsigned long dma_flags;
390 disable_dma(devpriv->dma);
391 this_dma_buf = devpriv->next_dma_buf;
393 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
395 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
396 set_dma_mode(devpriv->dma, DMA_MODE_READ);
397 dma_flags = claim_dma_lock();
398 /* clear_dma_ff (devpriv->dma); */
399 set_dma_addr(devpriv->dma,
400 devpriv->hwdmaptr[devpriv->next_dma_buf]);
401 if (devpriv->dma_runs_to_end) {
402 set_dma_count(devpriv->dma,
403 devpriv->hwdmasize[devpriv->
406 set_dma_count(devpriv->dma, devpriv->last_dma_run);
408 release_dma_lock(dma_flags);
409 enable_dma(devpriv->dma);
412 devpriv->dma_runs_to_end--;
413 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
415 ptr = (short *)devpriv->dmabuf[this_dma_buf];
417 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
418 bufptr = devpriv->ai_poll_ptr;
419 devpriv->ai_poll_ptr = 0;
421 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
426 ==============================================================================
429 static irqreturn_t interrupt_pcl816(int irq, void *d)
431 struct comedi_device *dev = d;
434 if (!dev->attached) {
435 comedi_error(dev, "premature interrupt");
439 switch (devpriv->int816_mode) {
440 case INT_TYPE_AI1_DMA:
441 case INT_TYPE_AI3_DMA:
442 return interrupt_pcl816_ai_mode13_dma(irq, d);
443 case INT_TYPE_AI1_INT:
444 case INT_TYPE_AI3_INT:
445 return interrupt_pcl816_ai_mode13_int(irq, d);
448 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
449 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
450 (!devpriv->int816_mode)) {
451 if (devpriv->irq_was_now_closed) {
452 devpriv->irq_was_now_closed = 0;
453 /* comedi_error(dev,"last IRQ.."); */
456 comedi_error(dev, "bad IRQ!");
459 comedi_error(dev, "IRQ from unknown source!");
464 ==============================================================================
467 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
469 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
470 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
471 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
472 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
473 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
475 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
476 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
480 ==============================================================================
482 static int pcl816_ai_cmdtest(struct comedi_device *dev,
483 struct comedi_subdevice *s, struct comedi_cmd *cmd)
486 int tmp, divisor1 = 0, divisor2 = 0;
488 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
491 /* step 1: make sure trigger sources are trivially valid */
492 tmp = cmd->start_src;
493 cmd->start_src &= TRIG_NOW;
494 if (!cmd->start_src || tmp != cmd->start_src)
497 tmp = cmd->scan_begin_src;
498 cmd->scan_begin_src &= TRIG_FOLLOW;
499 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
502 tmp = cmd->convert_src;
503 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
504 if (!cmd->convert_src || tmp != cmd->convert_src)
507 tmp = cmd->scan_end_src;
508 cmd->scan_end_src &= TRIG_COUNT;
509 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
513 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
514 if (!cmd->stop_src || tmp != cmd->stop_src)
521 /* step 2: make sure trigger sources are unique and mutually compatible */
523 if (cmd->start_src != TRIG_NOW) {
524 cmd->start_src = TRIG_NOW;
528 if (cmd->scan_begin_src != TRIG_FOLLOW) {
529 cmd->scan_begin_src = TRIG_FOLLOW;
533 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
534 cmd->convert_src = TRIG_TIMER;
538 if (cmd->scan_end_src != TRIG_COUNT) {
539 cmd->scan_end_src = TRIG_COUNT;
543 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
550 /* step 3: make sure arguments are trivially compatible */
551 if (cmd->start_arg != 0) {
556 if (cmd->scan_begin_arg != 0) {
557 cmd->scan_begin_arg = 0;
560 if (cmd->convert_src == TRIG_TIMER) {
561 if (cmd->convert_arg < this_board->ai_ns_min) {
562 cmd->convert_arg = this_board->ai_ns_min;
565 } else { /* TRIG_EXT */
566 if (cmd->convert_arg != 0) {
567 cmd->convert_arg = 0;
572 if (cmd->scan_end_arg != cmd->chanlist_len) {
573 cmd->scan_end_arg = cmd->chanlist_len;
576 if (cmd->stop_src == TRIG_COUNT) {
577 if (!cmd->stop_arg) {
581 } else { /* TRIG_NONE */
582 if (cmd->stop_arg != 0) {
592 /* step 4: fix up any arguments */
593 if (cmd->convert_src == TRIG_TIMER) {
594 tmp = cmd->convert_arg;
595 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
596 &divisor1, &divisor2,
598 cmd->flags & TRIG_ROUND_MASK);
599 if (cmd->convert_arg < this_board->ai_ns_min)
600 cmd->convert_arg = this_board->ai_ns_min;
601 if (tmp != cmd->convert_arg)
609 /* step 5: complain about special chanlist considerations */
612 if (!check_channel_list(dev, s, cmd->chanlist,
614 return 5; /* incorrect channels list */
620 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
622 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
623 struct comedi_cmd *cmd = &s->async->cmd;
626 if (cmd->start_src != TRIG_NOW)
628 if (cmd->scan_begin_src != TRIG_FOLLOW)
630 if (cmd->scan_end_src != TRIG_COUNT)
632 if (cmd->scan_end_arg != cmd->chanlist_len)
634 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
635 if (devpriv->irq_blocked)
638 if (cmd->convert_src == TRIG_TIMER) {
639 if (cmd->convert_arg < this_board->ai_ns_min)
640 cmd->convert_arg = this_board->ai_ns_min;
642 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
643 &divisor2, &cmd->convert_arg,
644 cmd->flags & TRIG_ROUND_MASK);
645 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
655 start_pacer(dev, -1, 0, 0); /* stop pacer */
657 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
660 setup_channel_list(dev, s, cmd->chanlist, seglen);
663 devpriv->ai_n_chan = cmd->chanlist_len;
664 devpriv->ai_act_scan = 0;
665 s->async->cur_chan = 0;
666 devpriv->irq_blocked = 1;
667 devpriv->ai_poll_ptr = 0;
668 devpriv->irq_was_now_closed = 0;
670 if (cmd->stop_src == TRIG_COUNT) {
671 devpriv->ai_scans = cmd->stop_arg;
672 devpriv->ai_neverending = 0;
674 devpriv->ai_scans = 0;
675 devpriv->ai_neverending = 1;
678 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
679 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
680 /* devpriv->ai_eos=1; */
681 /* if (devpriv->ai_n_chan==1) */
682 /* devpriv->dma=0; // DMA is useless for this situation */
686 bytes = devpriv->hwdmasize[0];
687 if (!devpriv->ai_neverending) {
688 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
689 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
690 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
691 devpriv->dma_runs_to_end--;
692 if (devpriv->dma_runs_to_end >= 0)
693 bytes = devpriv->hwdmasize[0];
695 devpriv->dma_runs_to_end = -1;
697 devpriv->next_dma_buf = 0;
698 set_dma_mode(devpriv->dma, DMA_MODE_READ);
699 dma_flags = claim_dma_lock();
700 clear_dma_ff(devpriv->dma);
701 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
702 set_dma_count(devpriv->dma, bytes);
703 release_dma_lock(dma_flags);
704 enable_dma(devpriv->dma);
707 start_pacer(dev, 1, divisor1, divisor2);
708 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
710 switch (cmd->convert_src) {
712 devpriv->int816_mode = INT_TYPE_AI1_DMA;
713 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
714 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
718 devpriv->int816_mode = INT_TYPE_AI3_DMA;
719 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
720 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
724 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
728 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
731 unsigned int top1, top2, i;
734 return 0; /* poll is valid only for DMA transfer */
736 spin_lock_irqsave(&dev->spinlock, flags);
738 for (i = 0; i < 20; i++) {
739 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
740 top2 = get_dma_residue(devpriv->dma);
745 spin_unlock_irqrestore(&dev->spinlock, flags);
749 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
750 top1 >>= 1; /* sample position */
751 top2 = top1 - devpriv->ai_poll_ptr;
752 if (top2 < 1) { /* no new samples */
753 spin_unlock_irqrestore(&dev->spinlock, flags);
757 transfer_from_dma_buf(dev, s,
758 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
759 devpriv->ai_poll_ptr, top2);
761 devpriv->ai_poll_ptr = top1; /* new buffer position */
762 spin_unlock_irqrestore(&dev->spinlock, flags);
764 return s->async->buf_write_count - s->async->buf_read_count;
768 ==============================================================================
769 cancel any mode 1-4 AI
771 static int pcl816_ai_cancel(struct comedi_device *dev,
772 struct comedi_subdevice *s)
774 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
776 if (devpriv->irq_blocked > 0) {
777 switch (devpriv->int816_mode) {
779 case INT_TYPE_AI1_DMA_RTC:
780 case INT_TYPE_AI3_DMA_RTC:
781 set_rtc_irq_bit(0); /* stop RTC */
782 del_timer(&devpriv->rtc_irq_timer);
784 case INT_TYPE_AI1_DMA:
785 case INT_TYPE_AI3_DMA:
786 disable_dma(devpriv->dma);
787 case INT_TYPE_AI1_INT:
788 case INT_TYPE_AI3_INT:
789 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
791 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
792 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
793 outb(0x70, dev->iobase + PCL816_CTRCTL);
794 outb(0, dev->iobase + PCL816_AD_LO);
795 inb(dev->iobase + PCL816_AD_LO);
796 inb(dev->iobase + PCL816_AD_HI);
797 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
798 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
799 devpriv->irq_blocked = 0;
800 devpriv->irq_was_now_closed = devpriv->int816_mode;
801 devpriv->int816_mode = 0;
802 devpriv->last_int_sub = s;
808 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
813 ==============================================================================
816 static int pcl816_check(unsigned long iobase)
818 outb(0x00, iobase + PCL816_MUX);
820 if (inb(iobase + PCL816_MUX) != 0x00)
821 return 1; /* there isn't card */
822 outb(0x55, iobase + PCL816_MUX);
824 if (inb(iobase + PCL816_MUX) != 0x55)
825 return 1; /* there isn't card */
826 outb(0x00, iobase + PCL816_MUX);
828 outb(0x18, iobase + PCL816_CONTROL);
830 if (inb(iobase + PCL816_CONTROL) != 0x18)
831 return 1; /* there isn't card */
832 return 0; /* ok, card exist */
836 ==============================================================================
837 reset whole PCL-816 cards
839 static void pcl816_reset(struct comedi_device *dev)
841 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
842 /* outb (0, dev->iobase + PCL818_DA_HI); */
844 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
845 /* outb (0, dev->iobase + PCL818_DO_LO); */
847 outb(0, dev->iobase + PCL816_CONTROL);
848 outb(0, dev->iobase + PCL816_MUX);
849 outb(0, dev->iobase + PCL816_CLRINT);
850 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
851 outb(0x70, dev->iobase + PCL816_CTRCTL);
852 outb(0x30, dev->iobase + PCL816_CTRCTL);
853 outb(0, dev->iobase + PCL816_RANGE);
857 ==============================================================================
858 Start/stop pacer onboard pacer
861 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
862 unsigned int divisor2)
864 outb(0x32, dev->iobase + PCL816_CTRCTL);
865 outb(0xff, dev->iobase + PCL816_CTR0);
866 outb(0x00, dev->iobase + PCL816_CTR0);
868 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
869 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
873 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
875 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
876 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
877 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
878 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
881 /* clear pending interrupts (just in case) */
882 /* outb(0, dev->iobase + PCL816_CLRINT); */
886 ==============================================================================
887 Check if channel list from user is builded correctly
888 If it's ok, then return non-zero length of repeated segment of channel list
891 check_channel_list(struct comedi_device *dev,
892 struct comedi_subdevice *s, unsigned int *chanlist,
893 unsigned int chanlen)
895 unsigned int chansegment[16];
896 unsigned int i, nowmustbechan, seglen, segpos;
898 /* correct channel and range number check itself comedi/range.c */
900 comedi_error(dev, "range/channel list is empty!");
905 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
906 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
907 /* build part of chanlist */
908 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
909 CR_RANGE(chanlist[i]));)
910 if (chanlist[0] == chanlist[i])
911 break; /* we detect loop, this must by finish */
913 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
914 if (nowmustbechan != CR_CHAN(chanlist[i])) {
915 /* channel list isn't continous :-( */
917 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
918 dev->minor, i, CR_CHAN(chanlist[i]),
919 nowmustbechan, CR_CHAN(chanlist[0]));
922 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
925 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
926 DEBUG(printk("%d %d=%d %d\n",
927 CR_CHAN(chansegment[i % seglen]),
928 CR_RANGE(chansegment[i % seglen]),
929 CR_CHAN(chanlist[i]),
930 CR_RANGE(chanlist[i]));)
931 if (chanlist[i] != chansegment[i % seglen]) {
933 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
934 dev->minor, i, CR_CHAN(chansegment[i]),
935 CR_RANGE(chansegment[i]),
936 CR_AREF(chansegment[i]),
937 CR_CHAN(chanlist[i % seglen]),
938 CR_RANGE(chanlist[i % seglen]),
939 CR_AREF(chansegment[i % seglen]));
940 return 0; /* chan/gain list is strange */
947 return seglen; /* we can serve this with MUX logic */
951 ==============================================================================
952 Program scan/gain logic with channel list.
955 setup_channel_list(struct comedi_device *dev,
956 struct comedi_subdevice *s, unsigned int *chanlist,
961 devpriv->ai_act_chanlist_len = seglen;
962 devpriv->ai_act_chanlist_pos = 0;
964 for (i = 0; i < seglen; i++) { /* store range list to card */
965 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
966 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
967 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
972 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
977 ==============================================================================
978 Enable(1)/disable(0) periodic interrupts from RTC
980 static int set_rtc_irq_bit(unsigned char bit)
987 if (RTC_timer_lock > 1)
991 if (RTC_timer_lock < 0)
993 if (RTC_timer_lock > 0)
999 val = CMOS_READ(RTC_CONTROL);
1005 CMOS_WRITE(val, RTC_CONTROL);
1006 CMOS_READ(RTC_INTR_FLAGS);
1007 restore_flags(flags);
1013 ==============================================================================
1014 Free any resources that we have claimed
1016 static void free_resources(struct comedi_device *dev)
1018 /* printk("free_resource()\n"); */
1020 pcl816_ai_cancel(dev, devpriv->sub_ai);
1023 free_dma(devpriv->dma);
1024 if (devpriv->dmabuf[0])
1025 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1026 if (devpriv->dmabuf[1])
1027 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1029 if (devpriv->rtc_irq)
1030 free_irq(devpriv->rtc_irq, dev);
1031 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1032 if (devpriv->rtc_iobase)
1033 release_region(devpriv->rtc_iobase,
1034 devpriv->rtc_iosize);
1040 free_irq(dev->irq, dev);
1042 release_region(dev->iobase, this_board->io_range);
1043 /* printk("free_resource() end\n"); */
1047 ==============================================================================
1052 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1055 unsigned long iobase;
1056 unsigned int irq, dma;
1057 unsigned long pages;
1059 struct comedi_subdevice *s;
1061 /* claim our I/O space */
1062 iobase = it->options[0];
1063 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1064 this_board->name, iobase);
1066 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1067 printk("I/O port conflict\n");
1071 dev->iobase = iobase;
1073 if (pcl816_check(iobase)) {
1074 printk(", I cann't detect board. FAIL!\n");
1078 ret = alloc_private(dev, sizeof(struct pcl816_private));
1080 return ret; /* Can't alloc mem */
1082 /* set up some name stuff */
1083 dev->board_name = this_board->name;
1087 if (this_board->IRQbits != 0) { /* board support IRQ */
1088 irq = it->options[1];
1089 if (irq) { /* we want to use IRQ */
1090 if (((1 << irq) & this_board->IRQbits) == 0) {
1092 (", IRQ %u is out of allowed range, DISABLING IT",
1094 irq = 0; /* Bad IRQ */
1097 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1099 (", unable to allocate IRQ %u, DISABLING IT",
1101 irq = 0; /* Can't use IRQ */
1103 printk(", irq=%u", irq);
1111 devpriv->irq_free = 1;
1112 } /* 1=we have allocated irq */
1114 devpriv->irq_free = 0;
1116 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1117 devpriv->int816_mode = 0; /* mode of irq */
1120 /* grab RTC for DMA operations */
1121 devpriv->dma_rtc = 0;
1122 if (it->options[2] > 0) { /* we want to use DMA */
1123 if (RTC_lock == 0) {
1124 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1128 devpriv->rtc_iobase = RTC_PORT(0);
1129 devpriv->rtc_iosize = RTC_IO_EXTENT;
1131 #ifdef UNTESTED_CODE
1132 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1133 "pcl816 DMA (RTC)", dev)) {
1134 devpriv->dma_rtc = 1;
1135 devpriv->rtc_irq = RTC_IRQ;
1136 printk(", dma_irq=%u", devpriv->rtc_irq);
1139 if (RTC_lock == 0) {
1140 if (devpriv->rtc_iobase)
1141 release_region(devpriv->rtc_iobase,
1142 devpriv->rtc_iosize);
1144 devpriv->rtc_iobase = 0;
1145 devpriv->rtc_iosize = 0;
1148 printk("pcl816: RTC code missing");
1158 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1159 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1161 if (this_board->DMAbits != 0) { /* board support DMA */
1162 dma = it->options[2];
1164 goto no_dma; /* DMA disabled */
1166 if (((1 << dma) & this_board->DMAbits) == 0) {
1167 printk(", DMA is out of allowed range, FAIL!\n");
1168 return -EINVAL; /* Bad DMA */
1170 ret = request_dma(dma, "pcl816");
1172 printk(", unable to allocate DMA %u, FAIL!\n", dma);
1173 return -EBUSY; /* DMA isn't free */
1177 printk(", dma=%u", dma);
1178 pages = 2; /* we need 16KB */
1179 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1181 if (!devpriv->dmabuf[0]) {
1182 printk(", unable to allocate DMA buffer, FAIL!\n");
1183 /* maybe experiment with try_to_free_pages() will help .... */
1184 return -EBUSY; /* no buffer :-( */
1186 devpriv->dmapages[0] = pages;
1187 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1188 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1189 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1191 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1192 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1193 if (!devpriv->dmabuf[1]) {
1195 (", unable to allocate DMA buffer, FAIL!\n");
1198 devpriv->dmapages[1] = pages;
1199 devpriv->hwdmaptr[1] =
1200 virt_to_bus((void *)devpriv->dmabuf[1]);
1201 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1207 /* if (this_board->n_aochan > 0)
1208 subdevs[1] = COMEDI_SUBD_AO;
1209 if (this_board->n_dichan > 0)
1210 subdevs[2] = COMEDI_SUBD_DI;
1211 if (this_board->n_dochan > 0)
1212 subdevs[3] = COMEDI_SUBD_DO;
1215 ret = alloc_subdevices(dev, 1);
1219 s = dev->subdevices + 0;
1220 if (this_board->n_aichan > 0) {
1221 s->type = COMEDI_SUBD_AI;
1222 devpriv->sub_ai = s;
1223 dev->read_subdev = s;
1224 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1225 s->n_chan = this_board->n_aichan;
1226 s->subdev_flags |= SDF_DIFF;
1227 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1228 s->maxdata = this_board->ai_maxdata;
1229 s->len_chanlist = this_board->ai_chanlist;
1230 s->range_table = this_board->ai_range_type;
1231 s->cancel = pcl816_ai_cancel;
1232 s->do_cmdtest = pcl816_ai_cmdtest;
1233 s->do_cmd = pcl816_ai_cmd;
1234 s->poll = pcl816_ai_poll;
1235 s->insn_read = pcl816_ai_insn_read;
1237 s->type = COMEDI_SUBD_UNUSED;
1241 case COMEDI_SUBD_AO:
1242 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1243 s->n_chan = this_board->n_aochan;
1244 s->maxdata = this_board->ao_maxdata;
1245 s->len_chanlist = this_board->ao_chanlist;
1246 s->range_table = this_board->ao_range_type;
1249 case COMEDI_SUBD_DI:
1250 s->subdev_flags = SDF_READABLE;
1251 s->n_chan = this_board->n_dichan;
1253 s->len_chanlist = this_board->n_dichan;
1254 s->range_table = &range_digital;
1257 case COMEDI_SUBD_DO:
1258 s->subdev_flags = SDF_WRITABLE;
1259 s->n_chan = this_board->n_dochan;
1261 s->len_chanlist = this_board->n_dochan;
1262 s->range_table = &range_digital;
1274 ==============================================================================
1277 static int pcl816_detach(struct comedi_device *dev)
1279 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1280 free_resources(dev);
1282 if (devpriv->dma_rtc)