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>
47 /* boards constants */
49 #define PCLx1x_RANGE 16
51 /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
53 /* INTEL 8254 counters */
57 /* R: counter read-back register W: counter control */
58 #define PCL816_CTRCTL 7
60 /* R: A/D high byte W: A/D range control */
61 #define PCL816_RANGE 9
62 /* W: clear INT request */
63 #define PCL816_CLRINT 10
64 /* R: next mux scan channel W: mux scan channel & range control pointer */
66 /* R/W: operation control register */
67 #define PCL816_CONTROL 12
69 /* R: return status byte W: set DMA/IRQ */
70 #define PCL816_STATUS 13
71 #define PCL816_STATUS_DRDY_MASK 0x80
73 /* R: low byte of A/D W: soft A/D trigger */
74 #define PCL816_AD_LO 8
75 /* R: high byte of A/D W: A/D range control */
76 #define PCL816_AD_HI 9
78 /* type of interrupt handler */
79 #define INT_TYPE_AI1_INT 1
80 #define INT_TYPE_AI1_DMA 2
81 #define INT_TYPE_AI3_INT 4
82 #define INT_TYPE_AI3_DMA 5
84 #define INT_TYPE_AI1_DMA_RTC 9
85 #define INT_TYPE_AI3_DMA_RTC 10
89 #define RTC_IO_EXTENT 0x10
92 #define MAGIC_DMA_WORD 0x5a5a
94 static const struct comedi_lrange range_pcl816 = { 8, {
106 struct pcl816_board {
108 const char *name; /* board name */
109 int n_ranges; /* len of range list */
110 int n_aichan; /* num of A/D chans in diferencial mode */
111 unsigned int ai_ns_min; /* minimal alllowed delay between samples (in ns) */
112 int n_aochan; /* num of D/A chans */
113 int n_dichan; /* num of DI chans */
114 int n_dochan; /* num of DO chans */
115 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
116 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
117 unsigned int io_range; /* len of IO space */
118 unsigned int IRQbits; /* allowed interrupts */
119 unsigned int DMAbits; /* allowed DMA chans */
120 int ai_maxdata; /* maxdata for A/D */
121 int ao_maxdata; /* maxdata for D/A */
122 int ai_chanlist; /* allowed len of channel list A/D */
123 int ao_chanlist; /* allowed len of channel list D/A */
124 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
127 static const struct pcl816_board boardtypes[] = {
128 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
129 &range_pcl816, PCLx1x_RANGE,
130 0x00fc, /* IRQ mask */
132 0xffff, /* 16-bit card */
133 0xffff, /* D/A maxdata */
135 1, /* ao chan list */
137 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
138 &range_pcl816, PCLx1x_RANGE,
141 0x3fff, /* 14 bit card */
148 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
149 #define devpriv ((struct pcl816_private *)dev->private)
150 #define this_board ((const struct pcl816_board *)dev->board_ptr)
152 static int pcl816_attach(struct comedi_device *dev,
153 struct comedi_devconfig *it);
154 static int pcl816_detach(struct comedi_device *dev);
157 static int RTC_lock = 0; /* RTC lock */
158 static int RTC_timer_lock = 0; /* RTC int lock */
161 static struct comedi_driver driver_pcl816 = {
162 .driver_name = "pcl816",
163 .module = THIS_MODULE,
164 .attach = pcl816_attach,
165 .detach = pcl816_detach,
166 .board_name = &boardtypes[0].name,
167 .num_names = n_boardtypes,
168 .offset = sizeof(struct pcl816_board),
171 COMEDI_INITCLEANUP(driver_pcl816);
173 struct pcl816_private {
175 unsigned int dma; /* used DMA, 0=don't use DMA */
176 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
178 unsigned long rtc_iobase; /* RTC port region */
179 unsigned int rtc_iosize;
180 unsigned int rtc_irq;
182 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
183 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
184 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
185 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
186 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
187 unsigned int last_top_dma; /* DMA pointer in last RTC int */
188 int next_dma_buf; /* which DMA buffer will be used next round */
189 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
190 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
192 unsigned int ai_scans; /* len of scanlist */
193 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
194 int irq_free; /* 1=have allocated IRQ */
195 int irq_blocked; /* 1=IRQ now uses any subdev */
197 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
199 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
200 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
201 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
202 int ai_act_scan; /* how many scans we finished */
203 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
204 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
205 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
206 unsigned int ai_n_chan; /* how many channels per scan */
207 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
208 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
210 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
211 unsigned long rtc_freq; /* RTC int freq */
216 ==============================================================================
218 static int check_channel_list(struct comedi_device *dev,
219 struct comedi_subdevice *s,
220 unsigned int *chanlist, unsigned int chanlen);
221 static void setup_channel_list(struct comedi_device *dev,
222 struct comedi_subdevice *s,
223 unsigned int *chanlist, unsigned int seglen);
224 static int pcl816_ai_cancel(struct comedi_device *dev,
225 struct comedi_subdevice *s);
226 static void start_pacer(struct comedi_device *dev, int mode,
227 unsigned int divisor1, unsigned int divisor2);
229 static int set_rtc_irq_bit(unsigned char bit);
232 static int pcl816_ai_cmdtest(struct comedi_device *dev,
233 struct comedi_subdevice *s,
234 struct comedi_cmd *cmd);
235 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
238 ==============================================================================
239 ANALOG INPUT MODE0, 816 cards, slow version
241 static int pcl816_ai_insn_read(struct comedi_device *dev,
242 struct comedi_subdevice *s,
243 struct comedi_insn *insn, unsigned int *data)
248 DPRINTK("mode 0 analog input\n");
249 /* software trigger, DMA and INT off */
250 outb(0, dev->iobase + PCL816_CONTROL);
251 /* clear INT (conversion end) flag */
252 outb(0, dev->iobase + PCL816_CLRINT);
254 /* Set the input channel */
255 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
256 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE); /* select gain */
258 for (n = 0; n < insn->n; n++) {
260 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
264 if (!(inb(dev->iobase + PCL816_STATUS) &
265 PCL816_STATUS_DRDY_MASK)) {
266 /* return read value */
269 PCL816_AD_HI) << 8) |
270 (inb(dev->iobase + PCL816_AD_LO)));
272 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
277 /* Return timeout error */
279 comedi_error(dev, "A/D insn timeout\n");
281 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT (conversion end) flag */
290 ==============================================================================
291 analog input interrupt mode 1 & 3, 818 cards
292 one sample per interrupt version
294 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
296 struct comedi_device *dev = d;
297 struct comedi_subdevice *s = dev->subdevices + 0;
299 int timeout = 50; /* wait max 50us */
302 if (!(inb(dev->iobase + PCL816_STATUS) &
303 PCL816_STATUS_DRDY_MASK))
307 if (!timeout) { /* timeout, bail error */
308 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
309 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
310 pcl816_ai_cancel(dev, s);
311 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
312 comedi_event(dev, s);
318 low = inb(dev->iobase + PCL816_AD_LO);
319 hi = inb(dev->iobase + PCL816_AD_HI);
321 comedi_buf_put(s->async, (hi << 8) | low);
323 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
325 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
326 devpriv->ai_act_chanlist_pos = 0;
328 s->async->cur_chan++;
329 if (s->async->cur_chan >= devpriv->ai_n_chan) {
330 s->async->cur_chan = 0;
331 devpriv->ai_act_scan++;
334 if (!devpriv->ai_neverending)
335 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
336 /* all data sampled */
337 pcl816_ai_cancel(dev, s);
338 s->async->events |= COMEDI_CB_EOA;
340 comedi_event(dev, s);
345 ==============================================================================
346 analog input dma mode 1 & 3, 816 cards
348 static void transfer_from_dma_buf(struct comedi_device *dev,
349 struct comedi_subdevice *s, short *ptr,
350 unsigned int bufptr, unsigned int len)
354 s->async->events = 0;
356 for (i = 0; i < len; i++) {
358 comedi_buf_put(s->async, ptr[bufptr++]);
360 if (++devpriv->ai_act_chanlist_pos >=
361 devpriv->ai_act_chanlist_len) {
362 devpriv->ai_act_chanlist_pos = 0;
365 s->async->cur_chan++;
366 if (s->async->cur_chan >= devpriv->ai_n_chan) {
367 s->async->cur_chan = 0;
368 devpriv->ai_act_scan++;
371 if (!devpriv->ai_neverending)
372 if (devpriv->ai_act_scan >= devpriv->ai_scans) { /* all data sampled */
373 pcl816_ai_cancel(dev, s);
374 s->async->events |= COMEDI_CB_EOA;
375 s->async->events |= COMEDI_CB_BLOCK;
380 comedi_event(dev, s);
383 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
385 struct comedi_device *dev = d;
386 struct comedi_subdevice *s = dev->subdevices + 0;
387 int len, bufptr, this_dma_buf;
388 unsigned long dma_flags;
391 disable_dma(devpriv->dma);
392 this_dma_buf = devpriv->next_dma_buf;
394 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) { /* switch dma bufs */
396 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
397 set_dma_mode(devpriv->dma, DMA_MODE_READ);
398 dma_flags = claim_dma_lock();
399 /* clear_dma_ff (devpriv->dma); */
400 set_dma_addr(devpriv->dma,
401 devpriv->hwdmaptr[devpriv->next_dma_buf]);
402 if (devpriv->dma_runs_to_end) {
403 set_dma_count(devpriv->dma,
404 devpriv->hwdmasize[devpriv->
407 set_dma_count(devpriv->dma, devpriv->last_dma_run);
409 release_dma_lock(dma_flags);
410 enable_dma(devpriv->dma);
413 devpriv->dma_runs_to_end--;
414 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
416 ptr = (short *)devpriv->dmabuf[this_dma_buf];
418 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
419 bufptr = devpriv->ai_poll_ptr;
420 devpriv->ai_poll_ptr = 0;
422 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
427 ==============================================================================
430 static irqreturn_t interrupt_pcl816(int irq, void *d)
432 struct comedi_device *dev = d;
435 if (!dev->attached) {
436 comedi_error(dev, "premature interrupt");
440 switch (devpriv->int816_mode) {
441 case INT_TYPE_AI1_DMA:
442 case INT_TYPE_AI3_DMA:
443 return interrupt_pcl816_ai_mode13_dma(irq, d);
444 case INT_TYPE_AI1_INT:
445 case INT_TYPE_AI3_INT:
446 return interrupt_pcl816_ai_mode13_int(irq, d);
449 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
450 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
451 (!devpriv->int816_mode)) {
452 if (devpriv->irq_was_now_closed) {
453 devpriv->irq_was_now_closed = 0;
454 /* comedi_error(dev,"last IRQ.."); */
457 comedi_error(dev, "bad IRQ!");
460 comedi_error(dev, "IRQ from unknown source!");
465 ==============================================================================
468 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
470 printk("pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
471 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
472 printk("pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
473 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
474 printk("pcl816 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
476 printk("pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n", e,
477 cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
481 ==============================================================================
483 static int pcl816_ai_cmdtest(struct comedi_device *dev,
484 struct comedi_subdevice *s, struct comedi_cmd *cmd)
487 int tmp, divisor1 = 0, divisor2 = 0;
489 DEBUG(printk("pcl816 pcl812_ai_cmdtest\n"); pcl816_cmdtest_out(-1, cmd);
492 /* step 1: make sure trigger sources are trivially valid */
493 tmp = cmd->start_src;
494 cmd->start_src &= TRIG_NOW;
495 if (!cmd->start_src || tmp != cmd->start_src)
498 tmp = cmd->scan_begin_src;
499 cmd->scan_begin_src &= TRIG_FOLLOW;
500 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
503 tmp = cmd->convert_src;
504 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
505 if (!cmd->convert_src || tmp != cmd->convert_src)
508 tmp = cmd->scan_end_src;
509 cmd->scan_end_src &= TRIG_COUNT;
510 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
514 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
515 if (!cmd->stop_src || tmp != cmd->stop_src)
522 /* step 2: make sure trigger sources are unique and mutually compatible */
524 if (cmd->start_src != TRIG_NOW) {
525 cmd->start_src = TRIG_NOW;
529 if (cmd->scan_begin_src != TRIG_FOLLOW) {
530 cmd->scan_begin_src = TRIG_FOLLOW;
534 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
535 cmd->convert_src = TRIG_TIMER;
539 if (cmd->scan_end_src != TRIG_COUNT) {
540 cmd->scan_end_src = TRIG_COUNT;
544 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
551 /* step 3: make sure arguments are trivially compatible */
552 if (cmd->start_arg != 0) {
557 if (cmd->scan_begin_arg != 0) {
558 cmd->scan_begin_arg = 0;
561 if (cmd->convert_src == TRIG_TIMER) {
562 if (cmd->convert_arg < this_board->ai_ns_min) {
563 cmd->convert_arg = this_board->ai_ns_min;
566 } else { /* TRIG_EXT */
567 if (cmd->convert_arg != 0) {
568 cmd->convert_arg = 0;
573 if (cmd->scan_end_arg != cmd->chanlist_len) {
574 cmd->scan_end_arg = cmd->chanlist_len;
577 if (cmd->stop_src == TRIG_COUNT) {
578 if (!cmd->stop_arg) {
582 } else { /* TRIG_NONE */
583 if (cmd->stop_arg != 0) {
593 /* step 4: fix up any arguments */
594 if (cmd->convert_src == TRIG_TIMER) {
595 tmp = cmd->convert_arg;
596 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
597 &divisor1, &divisor2,
599 cmd->flags & TRIG_ROUND_MASK);
600 if (cmd->convert_arg < this_board->ai_ns_min)
601 cmd->convert_arg = this_board->ai_ns_min;
602 if (tmp != cmd->convert_arg)
610 /* step 5: complain about special chanlist considerations */
613 if (!check_channel_list(dev, s, cmd->chanlist,
615 return 5; /* incorrect channels list */
621 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
623 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
624 struct comedi_cmd *cmd = &s->async->cmd;
627 if (cmd->start_src != TRIG_NOW)
629 if (cmd->scan_begin_src != TRIG_FOLLOW)
631 if (cmd->scan_end_src != TRIG_COUNT)
633 if (cmd->scan_end_arg != cmd->chanlist_len)
635 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
636 if (devpriv->irq_blocked)
639 if (cmd->convert_src == TRIG_TIMER) {
640 if (cmd->convert_arg < this_board->ai_ns_min)
641 cmd->convert_arg = this_board->ai_ns_min;
643 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
644 &divisor2, &cmd->convert_arg,
645 cmd->flags & TRIG_ROUND_MASK);
646 if (divisor1 == 1) { /* PCL816 crash if any divisor is set to 1 */
656 start_pacer(dev, -1, 0, 0); /* stop pacer */
658 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
661 setup_channel_list(dev, s, cmd->chanlist, seglen);
664 devpriv->ai_n_chan = cmd->chanlist_len;
665 devpriv->ai_act_scan = 0;
666 s->async->cur_chan = 0;
667 devpriv->irq_blocked = 1;
668 devpriv->ai_poll_ptr = 0;
669 devpriv->irq_was_now_closed = 0;
671 if (cmd->stop_src == TRIG_COUNT) {
672 devpriv->ai_scans = cmd->stop_arg;
673 devpriv->ai_neverending = 0;
675 devpriv->ai_scans = 0;
676 devpriv->ai_neverending = 1;
679 if ((cmd->flags & TRIG_WAKE_EOS)) { /* don't we want wake up every scan? */
680 printk("pl816: You wankt WAKE_EOS but I dont want handle it");
681 /* devpriv->ai_eos=1; */
682 /* if (devpriv->ai_n_chan==1) */
683 /* devpriv->dma=0; // DMA is useless for this situation */
687 bytes = devpriv->hwdmasize[0];
688 if (!devpriv->ai_neverending) {
689 bytes = s->async->cmd.chanlist_len * s->async->cmd.chanlist_len * sizeof(short); /* how many */
690 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize[0]; /* how many DMA pages we must fill */
691 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0]; /* on last dma transfer must be moved */
692 devpriv->dma_runs_to_end--;
693 if (devpriv->dma_runs_to_end >= 0)
694 bytes = devpriv->hwdmasize[0];
696 devpriv->dma_runs_to_end = -1;
698 devpriv->next_dma_buf = 0;
699 set_dma_mode(devpriv->dma, DMA_MODE_READ);
700 dma_flags = claim_dma_lock();
701 clear_dma_ff(devpriv->dma);
702 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
703 set_dma_count(devpriv->dma, bytes);
704 release_dma_lock(dma_flags);
705 enable_dma(devpriv->dma);
708 start_pacer(dev, 1, divisor1, divisor2);
709 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
711 switch (cmd->convert_src) {
713 devpriv->int816_mode = INT_TYPE_AI1_DMA;
714 outb(0x32, dev->iobase + PCL816_CONTROL); /* Pacer+IRQ+DMA */
715 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq and DMA to card */
719 devpriv->int816_mode = INT_TYPE_AI3_DMA;
720 outb(0x34, dev->iobase + PCL816_CONTROL); /* Ext trig+IRQ+DMA */
721 outb(dmairq, dev->iobase + PCL816_STATUS); /* write irq to card */
725 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
729 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
732 unsigned int top1, top2, i;
735 return 0; /* poll is valid only for DMA transfer */
737 spin_lock_irqsave(&dev->spinlock, flags);
739 for (i = 0; i < 20; i++) {
740 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
741 top2 = get_dma_residue(devpriv->dma);
746 spin_unlock_irqrestore(&dev->spinlock, flags);
750 top1 = devpriv->hwdmasize[0] - top1; /* where is now DMA in buffer */
751 top1 >>= 1; /* sample position */
752 top2 = top1 - devpriv->ai_poll_ptr;
753 if (top2 < 1) { /* no new samples */
754 spin_unlock_irqrestore(&dev->spinlock, flags);
758 transfer_from_dma_buf(dev, s,
759 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
760 devpriv->ai_poll_ptr, top2);
762 devpriv->ai_poll_ptr = top1; /* new buffer position */
763 spin_unlock_irqrestore(&dev->spinlock, flags);
765 return s->async->buf_write_count - s->async->buf_read_count;
769 ==============================================================================
770 cancel any mode 1-4 AI
772 static int pcl816_ai_cancel(struct comedi_device *dev,
773 struct comedi_subdevice *s)
775 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
777 if (devpriv->irq_blocked > 0) {
778 switch (devpriv->int816_mode) {
780 case INT_TYPE_AI1_DMA_RTC:
781 case INT_TYPE_AI3_DMA_RTC:
782 set_rtc_irq_bit(0); /* stop RTC */
783 del_timer(&devpriv->rtc_irq_timer);
785 case INT_TYPE_AI1_DMA:
786 case INT_TYPE_AI3_DMA:
787 disable_dma(devpriv->dma);
788 case INT_TYPE_AI1_INT:
789 case INT_TYPE_AI3_INT:
790 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73, dev->iobase + PCL816_CONTROL); /* Stop A/D */
792 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
793 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
794 outb(0x70, dev->iobase + PCL816_CTRCTL);
795 outb(0, dev->iobase + PCL816_AD_LO);
796 inb(dev->iobase + PCL816_AD_LO);
797 inb(dev->iobase + PCL816_AD_HI);
798 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
799 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
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);
869 outb(0xb4, dev->iobase + PCL816_CTRCTL); /* set counter 2 as mode 3 */
870 outb(0x74, dev->iobase + PCL816_CTRCTL); /* set counter 1 as mode 3 */
874 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
876 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
877 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
878 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
879 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
882 /* clear pending interrupts (just in case) */
883 /* outb(0, dev->iobase + PCL816_CLRINT); */
887 ==============================================================================
888 Check if channel list from user is builded correctly
889 If it's ok, then return non-zero length of repeated segment of channel list
892 check_channel_list(struct comedi_device *dev,
893 struct comedi_subdevice *s, unsigned int *chanlist,
894 unsigned int chanlen)
896 unsigned int chansegment[16];
897 unsigned int i, nowmustbechan, seglen, segpos;
899 /* correct channel and range number check itself comedi/range.c */
901 comedi_error(dev, "range/channel list is empty!");
906 chansegment[0] = chanlist[0]; /* first channel is everytime ok */
907 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
908 /* build part of chanlist */
909 DEBUG(printk("%d. %d %d\n", i, CR_CHAN(chanlist[i]),
910 CR_RANGE(chanlist[i]));)
911 if (chanlist[0] == chanlist[i])
912 break; /* we detect loop, this must by finish */
914 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
915 if (nowmustbechan != CR_CHAN(chanlist[i])) {
916 /* channel list isn't continous :-( */
918 ("comedi%d: pcl816: channel list must be continous! chanlist[%i]=%d but must be %d or %d!\n",
919 dev->minor, i, CR_CHAN(chanlist[i]),
920 nowmustbechan, CR_CHAN(chanlist[0]));
923 chansegment[i] = chanlist[i]; /* well, this is next correct channel in list */
926 for (i = 0, segpos = 0; i < chanlen; i++) { /* check whole chanlist */
927 DEBUG(printk("%d %d=%d %d\n",
928 CR_CHAN(chansegment[i % seglen]),
929 CR_RANGE(chansegment[i % seglen]),
930 CR_CHAN(chanlist[i]),
931 CR_RANGE(chanlist[i]));)
932 if (chanlist[i] != chansegment[i % seglen]) {
934 ("comedi%d: pcl816: bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
935 dev->minor, i, CR_CHAN(chansegment[i]),
936 CR_RANGE(chansegment[i]),
937 CR_AREF(chansegment[i]),
938 CR_CHAN(chanlist[i % seglen]),
939 CR_RANGE(chanlist[i % seglen]),
940 CR_AREF(chansegment[i % seglen]));
941 return 0; /* chan/gain list is strange */
948 return seglen; /* we can serve this with MUX logic */
952 ==============================================================================
953 Program scan/gain logic with channel list.
956 setup_channel_list(struct comedi_device *dev,
957 struct comedi_subdevice *s, unsigned int *chanlist,
962 devpriv->ai_act_chanlist_len = seglen;
963 devpriv->ai_act_chanlist_pos = 0;
965 for (i = 0; i < seglen; i++) { /* store range list to card */
966 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
967 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
968 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE); /* select gain */
973 outb(devpriv->ai_act_chanlist[0] | (devpriv->ai_act_chanlist[seglen - 1] << 4), dev->iobase + PCL816_MUX); /* select channel interval to scan */
978 ==============================================================================
979 Enable(1)/disable(0) periodic interrupts from RTC
981 static int set_rtc_irq_bit(unsigned char bit)
988 if (RTC_timer_lock > 1)
992 if (RTC_timer_lock < 0)
994 if (RTC_timer_lock > 0)
1000 val = CMOS_READ(RTC_CONTROL);
1006 CMOS_WRITE(val, RTC_CONTROL);
1007 CMOS_READ(RTC_INTR_FLAGS);
1008 restore_flags(flags);
1014 ==============================================================================
1015 Free any resources that we have claimed
1017 static void free_resources(struct comedi_device *dev)
1019 /* printk("free_resource()\n"); */
1021 pcl816_ai_cancel(dev, devpriv->sub_ai);
1024 free_dma(devpriv->dma);
1025 if (devpriv->dmabuf[0])
1026 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1027 if (devpriv->dmabuf[1])
1028 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1030 if (devpriv->rtc_irq)
1031 free_irq(devpriv->rtc_irq, dev);
1032 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1033 if (devpriv->rtc_iobase)
1034 release_region(devpriv->rtc_iobase,
1035 devpriv->rtc_iosize);
1041 free_irq(dev->irq, dev);
1043 release_region(dev->iobase, this_board->io_range);
1044 /* printk("free_resource() end\n"); */
1048 ==============================================================================
1053 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1056 unsigned long iobase;
1057 unsigned int irq, dma;
1058 unsigned long pages;
1060 struct comedi_subdevice *s;
1062 /* claim our I/O space */
1063 iobase = it->options[0];
1064 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1065 this_board->name, iobase);
1067 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1068 printk("I/O port conflict\n");
1072 dev->iobase = iobase;
1074 if (pcl816_check(iobase)) {
1075 printk(", I cann't detect board. FAIL!\n");
1079 ret = alloc_private(dev, sizeof(struct pcl816_private));
1081 return ret; /* Can't alloc mem */
1083 /* set up some name stuff */
1084 dev->board_name = this_board->name;
1088 if (this_board->IRQbits != 0) { /* board support IRQ */
1089 irq = it->options[1];
1090 if (irq) { /* we want to use IRQ */
1091 if (((1 << irq) & this_board->IRQbits) == 0) {
1093 (", IRQ %u is out of allowed range, DISABLING IT",
1095 irq = 0; /* Bad IRQ */
1098 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1100 (", unable to allocate IRQ %u, DISABLING IT",
1102 irq = 0; /* Can't use IRQ */
1104 printk(", irq=%u", irq);
1112 devpriv->irq_free = 1;
1113 } /* 1=we have allocated irq */
1115 devpriv->irq_free = 0;
1117 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1118 devpriv->int816_mode = 0; /* mode of irq */
1121 /* grab RTC for DMA operations */
1122 devpriv->dma_rtc = 0;
1123 if (it->options[2] > 0) { /* we want to use DMA */
1124 if (RTC_lock == 0) {
1125 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1129 devpriv->rtc_iobase = RTC_PORT(0);
1130 devpriv->rtc_iosize = RTC_IO_EXTENT;
1132 #ifdef UNTESTED_CODE
1133 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1134 "pcl816 DMA (RTC)", dev)) {
1135 devpriv->dma_rtc = 1;
1136 devpriv->rtc_irq = RTC_IRQ;
1137 printk(", dma_irq=%u", devpriv->rtc_irq);
1140 if (RTC_lock == 0) {
1141 if (devpriv->rtc_iobase)
1142 release_region(devpriv->rtc_iobase,
1143 devpriv->rtc_iosize);
1145 devpriv->rtc_iobase = 0;
1146 devpriv->rtc_iosize = 0;
1149 printk("pcl816: RTC code missing");
1159 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1160 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1162 if (this_board->DMAbits != 0) { /* board support DMA */
1163 dma = it->options[2];
1165 goto no_dma; /* DMA disabled */
1167 if (((1 << dma) & this_board->DMAbits) == 0) {
1168 printk(", DMA is out of allowed range, FAIL!\n");
1169 return -EINVAL; /* Bad DMA */
1171 ret = request_dma(dma, "pcl816");
1173 printk(", unable to allocate DMA %u, FAIL!\n", dma);
1174 return -EBUSY; /* DMA isn't free */
1178 printk(", dma=%u", dma);
1179 pages = 2; /* we need 16KB */
1180 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1182 if (!devpriv->dmabuf[0]) {
1183 printk(", unable to allocate DMA buffer, FAIL!\n");
1184 /* maybe experiment with try_to_free_pages() will help .... */
1185 return -EBUSY; /* no buffer :-( */
1187 devpriv->dmapages[0] = pages;
1188 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1189 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1190 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1192 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1193 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1194 if (!devpriv->dmabuf[1]) {
1196 (", unable to allocate DMA buffer, FAIL!\n");
1199 devpriv->dmapages[1] = pages;
1200 devpriv->hwdmaptr[1] =
1201 virt_to_bus((void *)devpriv->dmabuf[1]);
1202 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1208 /* if (this_board->n_aochan > 0)
1209 subdevs[1] = COMEDI_SUBD_AO;
1210 if (this_board->n_dichan > 0)
1211 subdevs[2] = COMEDI_SUBD_DI;
1212 if (this_board->n_dochan > 0)
1213 subdevs[3] = COMEDI_SUBD_DO;
1216 ret = alloc_subdevices(dev, 1);
1220 s = dev->subdevices + 0;
1221 if (this_board->n_aichan > 0) {
1222 s->type = COMEDI_SUBD_AI;
1223 devpriv->sub_ai = s;
1224 dev->read_subdev = s;
1225 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1226 s->n_chan = this_board->n_aichan;
1227 s->subdev_flags |= SDF_DIFF;
1228 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1229 s->maxdata = this_board->ai_maxdata;
1230 s->len_chanlist = this_board->ai_chanlist;
1231 s->range_table = this_board->ai_range_type;
1232 s->cancel = pcl816_ai_cancel;
1233 s->do_cmdtest = pcl816_ai_cmdtest;
1234 s->do_cmd = pcl816_ai_cmd;
1235 s->poll = pcl816_ai_poll;
1236 s->insn_read = pcl816_ai_insn_read;
1238 s->type = COMEDI_SUBD_UNUSED;
1242 case COMEDI_SUBD_AO:
1243 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1244 s->n_chan = this_board->n_aochan;
1245 s->maxdata = this_board->ao_maxdata;
1246 s->len_chanlist = this_board->ao_chanlist;
1247 s->range_table = this_board->ao_range_type;
1250 case COMEDI_SUBD_DI:
1251 s->subdev_flags = SDF_READABLE;
1252 s->n_chan = this_board->n_dichan;
1254 s->len_chanlist = this_board->n_dichan;
1255 s->range_table = &range_digital;
1258 case COMEDI_SUBD_DO:
1259 s->subdev_flags = SDF_WRITABLE;
1260 s->n_chan = this_board->n_dochan;
1262 s->len_chanlist = this_board->n_dochan;
1263 s->range_table = &range_digital;
1275 ==============================================================================
1278 static int pcl816_detach(struct comedi_device *dev)
1280 DEBUG(printk("comedi%d: pcl816: remove\n", dev->minor);)
1281 free_resources(dev);
1283 if (devpriv->dma_rtc)