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 static const struct pcl816_board boardtypes[] = {
129 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
130 &range_pcl816, PCLx1x_RANGE,
131 0x00fc, /* IRQ mask */
133 0xffff, /* 16-bit card */
134 0xffff, /* D/A maxdata */
136 1, /* ao chan list */
138 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
139 &range_pcl816, PCLx1x_RANGE,
142 0x3fff, /* 14 bit card */
149 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl816_board))
150 #define devpriv ((struct pcl816_private *)dev->private)
151 #define this_board ((const struct pcl816_board *)dev->board_ptr)
153 static int pcl816_attach(struct comedi_device *dev,
154 struct comedi_devconfig *it);
155 static int pcl816_detach(struct comedi_device *dev);
158 static int RTC_lock = 0; /* RTC lock */
159 static int RTC_timer_lock = 0; /* RTC int lock */
162 static struct comedi_driver driver_pcl816 = {
163 .driver_name = "pcl816",
164 .module = THIS_MODULE,
165 .attach = pcl816_attach,
166 .detach = pcl816_detach,
167 .board_name = &boardtypes[0].name,
168 .num_names = n_boardtypes,
169 .offset = sizeof(struct pcl816_board),
172 static int __init driver_pcl816_init_module(void)
174 return comedi_driver_register(&driver_pcl816);
177 static void __exit driver_pcl816_cleanup_module(void)
179 comedi_driver_unregister(&driver_pcl816);
182 module_init(driver_pcl816_init_module);
183 module_exit(driver_pcl816_cleanup_module);
185 struct pcl816_private {
187 unsigned int dma; /* used DMA, 0=don't use DMA */
188 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
190 unsigned long rtc_iobase; /* RTC port region */
191 unsigned int rtc_iosize;
192 unsigned int rtc_irq;
194 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
195 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
196 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
197 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
198 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
199 unsigned int last_top_dma; /* DMA pointer in last RTC int */
200 int next_dma_buf; /* which DMA buffer will be used next round */
201 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
202 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
204 unsigned int ai_scans; /* len of scanlist */
205 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
206 int irq_free; /* 1=have allocated IRQ */
207 int irq_blocked; /* 1=IRQ now uses any subdev */
209 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
211 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
212 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
213 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
214 int ai_act_scan; /* how many scans we finished */
215 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
216 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
217 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
218 unsigned int ai_n_chan; /* how many channels per scan */
219 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
220 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
222 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
223 unsigned long rtc_freq; /* RTC int freq */
228 ==============================================================================
230 static int check_channel_list(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 unsigned int *chanlist, unsigned int chanlen);
233 static void setup_channel_list(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 unsigned int *chanlist, unsigned int seglen);
236 static int pcl816_ai_cancel(struct comedi_device *dev,
237 struct comedi_subdevice *s);
238 static void start_pacer(struct comedi_device *dev, int mode,
239 unsigned int divisor1, unsigned int divisor2);
241 static int set_rtc_irq_bit(unsigned char bit);
244 static int pcl816_ai_cmdtest(struct comedi_device *dev,
245 struct comedi_subdevice *s,
246 struct comedi_cmd *cmd);
247 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
250 ==============================================================================
251 ANALOG INPUT MODE0, 816 cards, slow version
253 static int pcl816_ai_insn_read(struct comedi_device *dev,
254 struct comedi_subdevice *s,
255 struct comedi_insn *insn, unsigned int *data)
260 DPRINTK("mode 0 analog input\n");
261 /* software trigger, DMA and INT off */
262 outb(0, dev->iobase + PCL816_CONTROL);
263 /* clear INT (conversion end) flag */
264 outb(0, dev->iobase + PCL816_CLRINT);
266 /* Set the input channel */
267 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
269 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
271 for (n = 0; n < insn->n; n++) {
273 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
277 if (!(inb(dev->iobase + PCL816_STATUS) &
278 PCL816_STATUS_DRDY_MASK)) {
279 /* return read value */
282 PCL816_AD_HI) << 8) |
283 (inb(dev->iobase + PCL816_AD_LO)));
284 /* clear INT (conversion end) flag */
285 outb(0, dev->iobase + PCL816_CLRINT);
290 /* Return timeout error */
292 comedi_error(dev, "A/D insn timeout\n");
294 /* clear INT (conversion end) flag */
295 outb(0, dev->iobase + PCL816_CLRINT);
304 ==============================================================================
305 analog input interrupt mode 1 & 3, 818 cards
306 one sample per interrupt version
308 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
310 struct comedi_device *dev = d;
311 struct comedi_subdevice *s = dev->subdevices + 0;
313 int timeout = 50; /* wait max 50us */
316 if (!(inb(dev->iobase + PCL816_STATUS) &
317 PCL816_STATUS_DRDY_MASK))
321 if (!timeout) { /* timeout, bail error */
322 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
323 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
324 pcl816_ai_cancel(dev, s);
325 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
326 comedi_event(dev, s);
332 low = inb(dev->iobase + PCL816_AD_LO);
333 hi = inb(dev->iobase + PCL816_AD_HI);
335 comedi_buf_put(s->async, (hi << 8) | low);
337 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
339 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
340 devpriv->ai_act_chanlist_pos = 0;
342 s->async->cur_chan++;
343 if (s->async->cur_chan >= devpriv->ai_n_chan) {
344 s->async->cur_chan = 0;
345 devpriv->ai_act_scan++;
348 if (!devpriv->ai_neverending)
349 /* all data sampled */
350 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
351 /* all data sampled */
352 pcl816_ai_cancel(dev, s);
353 s->async->events |= COMEDI_CB_EOA;
355 comedi_event(dev, s);
360 ==============================================================================
361 analog input dma mode 1 & 3, 816 cards
363 static void transfer_from_dma_buf(struct comedi_device *dev,
364 struct comedi_subdevice *s, short *ptr,
365 unsigned int bufptr, unsigned int len)
369 s->async->events = 0;
371 for (i = 0; i < len; i++) {
373 comedi_buf_put(s->async, ptr[bufptr++]);
375 if (++devpriv->ai_act_chanlist_pos >=
376 devpriv->ai_act_chanlist_len) {
377 devpriv->ai_act_chanlist_pos = 0;
380 s->async->cur_chan++;
381 if (s->async->cur_chan >= devpriv->ai_n_chan) {
382 s->async->cur_chan = 0;
383 devpriv->ai_act_scan++;
386 if (!devpriv->ai_neverending)
387 /* all data sampled */
388 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
389 pcl816_ai_cancel(dev, s);
390 s->async->events |= COMEDI_CB_EOA;
391 s->async->events |= COMEDI_CB_BLOCK;
396 comedi_event(dev, s);
399 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
401 struct comedi_device *dev = d;
402 struct comedi_subdevice *s = dev->subdevices + 0;
403 int len, bufptr, this_dma_buf;
404 unsigned long dma_flags;
407 disable_dma(devpriv->dma);
408 this_dma_buf = devpriv->next_dma_buf;
410 /* switch dma bufs */
411 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
413 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
414 set_dma_mode(devpriv->dma, DMA_MODE_READ);
415 dma_flags = claim_dma_lock();
416 /* clear_dma_ff (devpriv->dma); */
417 set_dma_addr(devpriv->dma,
418 devpriv->hwdmaptr[devpriv->next_dma_buf]);
419 if (devpriv->dma_runs_to_end) {
420 set_dma_count(devpriv->dma,
421 devpriv->hwdmasize[devpriv->
424 set_dma_count(devpriv->dma, devpriv->last_dma_run);
426 release_dma_lock(dma_flags);
427 enable_dma(devpriv->dma);
430 devpriv->dma_runs_to_end--;
431 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
433 ptr = (short *)devpriv->dmabuf[this_dma_buf];
435 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
436 bufptr = devpriv->ai_poll_ptr;
437 devpriv->ai_poll_ptr = 0;
439 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
444 ==============================================================================
447 static irqreturn_t interrupt_pcl816(int irq, void *d)
449 struct comedi_device *dev = d;
452 if (!dev->attached) {
453 comedi_error(dev, "premature interrupt");
457 switch (devpriv->int816_mode) {
458 case INT_TYPE_AI1_DMA:
459 case INT_TYPE_AI3_DMA:
460 return interrupt_pcl816_ai_mode13_dma(irq, d);
461 case INT_TYPE_AI1_INT:
462 case INT_TYPE_AI3_INT:
463 return interrupt_pcl816_ai_mode13_int(irq, d);
466 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
467 if ((!dev->irq) | (!devpriv->irq_free) | (!devpriv->irq_blocked) |
468 (!devpriv->int816_mode)) {
469 if (devpriv->irq_was_now_closed) {
470 devpriv->irq_was_now_closed = 0;
471 /* comedi_error(dev,"last IRQ.."); */
474 comedi_error(dev, "bad IRQ!");
477 comedi_error(dev, "IRQ from unknown source!");
482 ==============================================================================
485 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
487 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
488 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
489 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
490 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
491 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
492 cmd->stop_src, cmd->scan_end_src);
493 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
494 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
498 ==============================================================================
500 static int pcl816_ai_cmdtest(struct comedi_device *dev,
501 struct comedi_subdevice *s, struct comedi_cmd *cmd)
504 int tmp, divisor1 = 0, divisor2 = 0;
506 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
507 pcl816_cmdtest_out(-1, cmd);
510 /* step 1: make sure trigger sources are trivially valid */
511 tmp = cmd->start_src;
512 cmd->start_src &= TRIG_NOW;
513 if (!cmd->start_src || tmp != cmd->start_src)
516 tmp = cmd->scan_begin_src;
517 cmd->scan_begin_src &= TRIG_FOLLOW;
518 if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
521 tmp = cmd->convert_src;
522 cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
523 if (!cmd->convert_src || tmp != cmd->convert_src)
526 tmp = cmd->scan_end_src;
527 cmd->scan_end_src &= TRIG_COUNT;
528 if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
532 cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
533 if (!cmd->stop_src || tmp != cmd->stop_src)
541 * step 2: make sure trigger sources
542 * are unique and mutually compatible
545 if (cmd->start_src != TRIG_NOW) {
546 cmd->start_src = TRIG_NOW;
550 if (cmd->scan_begin_src != TRIG_FOLLOW) {
551 cmd->scan_begin_src = TRIG_FOLLOW;
555 if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
556 cmd->convert_src = TRIG_TIMER;
560 if (cmd->scan_end_src != TRIG_COUNT) {
561 cmd->scan_end_src = TRIG_COUNT;
565 if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
572 /* step 3: make sure arguments are trivially compatible */
573 if (cmd->start_arg != 0) {
578 if (cmd->scan_begin_arg != 0) {
579 cmd->scan_begin_arg = 0;
582 if (cmd->convert_src == TRIG_TIMER) {
583 if (cmd->convert_arg < this_board->ai_ns_min) {
584 cmd->convert_arg = this_board->ai_ns_min;
587 } else { /* TRIG_EXT */
588 if (cmd->convert_arg != 0) {
589 cmd->convert_arg = 0;
594 if (cmd->scan_end_arg != cmd->chanlist_len) {
595 cmd->scan_end_arg = cmd->chanlist_len;
598 if (cmd->stop_src == TRIG_COUNT) {
599 if (!cmd->stop_arg) {
603 } else { /* TRIG_NONE */
604 if (cmd->stop_arg != 0) {
614 /* step 4: fix up any arguments */
615 if (cmd->convert_src == TRIG_TIMER) {
616 tmp = cmd->convert_arg;
617 i8253_cascade_ns_to_timer(this_board->i8254_osc_base,
618 &divisor1, &divisor2,
620 cmd->flags & TRIG_ROUND_MASK);
621 if (cmd->convert_arg < this_board->ai_ns_min)
622 cmd->convert_arg = this_board->ai_ns_min;
623 if (tmp != cmd->convert_arg)
631 /* step 5: complain about special chanlist considerations */
634 if (!check_channel_list(dev, s, cmd->chanlist,
636 return 5; /* incorrect channels list */
642 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
644 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
645 struct comedi_cmd *cmd = &s->async->cmd;
648 if (cmd->start_src != TRIG_NOW)
650 if (cmd->scan_begin_src != TRIG_FOLLOW)
652 if (cmd->scan_end_src != TRIG_COUNT)
654 if (cmd->scan_end_arg != cmd->chanlist_len)
656 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
657 if (devpriv->irq_blocked)
660 if (cmd->convert_src == TRIG_TIMER) {
661 if (cmd->convert_arg < this_board->ai_ns_min)
662 cmd->convert_arg = this_board->ai_ns_min;
664 i8253_cascade_ns_to_timer(this_board->i8254_osc_base, &divisor1,
665 &divisor2, &cmd->convert_arg,
666 cmd->flags & TRIG_ROUND_MASK);
668 /* PCL816 crash if any divisor is set to 1 */
679 start_pacer(dev, -1, 0, 0); /* stop pacer */
681 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
684 setup_channel_list(dev, s, cmd->chanlist, seglen);
687 devpriv->ai_n_chan = cmd->chanlist_len;
688 devpriv->ai_act_scan = 0;
689 s->async->cur_chan = 0;
690 devpriv->irq_blocked = 1;
691 devpriv->ai_poll_ptr = 0;
692 devpriv->irq_was_now_closed = 0;
694 if (cmd->stop_src == TRIG_COUNT) {
695 devpriv->ai_scans = cmd->stop_arg;
696 devpriv->ai_neverending = 0;
698 devpriv->ai_scans = 0;
699 devpriv->ai_neverending = 1;
702 /* don't we want wake up every scan? */
703 if ((cmd->flags & TRIG_WAKE_EOS)) {
705 "pl816: You wankt WAKE_EOS but I dont want handle it");
706 /* devpriv->ai_eos=1; */
707 /* if (devpriv->ai_n_chan==1) */
708 /* devpriv->dma=0; // DMA is useless for this situation */
712 bytes = devpriv->hwdmasize[0];
713 if (!devpriv->ai_neverending) {
715 bytes = s->async->cmd.chanlist_len *
716 s->async->cmd.chanlist_len *
719 /* how many DMA pages we must fill */
720 devpriv->dma_runs_to_end = bytes /
721 devpriv->hwdmasize[0];
723 /* on last dma transfer must be moved */
724 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
725 devpriv->dma_runs_to_end--;
726 if (devpriv->dma_runs_to_end >= 0)
727 bytes = devpriv->hwdmasize[0];
729 devpriv->dma_runs_to_end = -1;
731 devpriv->next_dma_buf = 0;
732 set_dma_mode(devpriv->dma, DMA_MODE_READ);
733 dma_flags = claim_dma_lock();
734 clear_dma_ff(devpriv->dma);
735 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
736 set_dma_count(devpriv->dma, bytes);
737 release_dma_lock(dma_flags);
738 enable_dma(devpriv->dma);
741 start_pacer(dev, 1, divisor1, divisor2);
742 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
744 switch (cmd->convert_src) {
746 devpriv->int816_mode = INT_TYPE_AI1_DMA;
749 outb(0x32, dev->iobase + PCL816_CONTROL);
751 /* write irq and DMA to card */
752 outb(dmairq, dev->iobase + PCL816_STATUS);
756 devpriv->int816_mode = INT_TYPE_AI3_DMA;
758 /* Ext trig+IRQ+DMA */
759 outb(0x34, dev->iobase + PCL816_CONTROL);
761 /* write irq to card */
762 outb(dmairq, dev->iobase + PCL816_STATUS);
766 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
770 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
773 unsigned int top1, top2, i;
776 return 0; /* poll is valid only for DMA transfer */
778 spin_lock_irqsave(&dev->spinlock, flags);
780 for (i = 0; i < 20; i++) {
781 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
782 top2 = get_dma_residue(devpriv->dma);
787 spin_unlock_irqrestore(&dev->spinlock, flags);
791 /* where is now DMA in buffer */
792 top1 = devpriv->hwdmasize[0] - top1;
793 top1 >>= 1; /* sample position */
794 top2 = top1 - devpriv->ai_poll_ptr;
795 if (top2 < 1) { /* no new samples */
796 spin_unlock_irqrestore(&dev->spinlock, flags);
800 transfer_from_dma_buf(dev, s,
801 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
802 devpriv->ai_poll_ptr, top2);
804 devpriv->ai_poll_ptr = top1; /* new buffer position */
805 spin_unlock_irqrestore(&dev->spinlock, flags);
807 return s->async->buf_write_count - s->async->buf_read_count;
811 ==============================================================================
812 cancel any mode 1-4 AI
814 static int pcl816_ai_cancel(struct comedi_device *dev,
815 struct comedi_subdevice *s)
817 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
819 if (devpriv->irq_blocked > 0) {
820 switch (devpriv->int816_mode) {
822 case INT_TYPE_AI1_DMA_RTC:
823 case INT_TYPE_AI3_DMA_RTC:
824 set_rtc_irq_bit(0); /* stop RTC */
825 del_timer(&devpriv->rtc_irq_timer);
827 case INT_TYPE_AI1_DMA:
828 case INT_TYPE_AI3_DMA:
829 disable_dma(devpriv->dma);
830 case INT_TYPE_AI1_INT:
831 case INT_TYPE_AI3_INT:
832 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
833 dev->iobase + PCL816_CONTROL); /* Stop A/D */
835 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
838 outb(0xb0, dev->iobase + PCL816_CTRCTL);
839 outb(0x70, dev->iobase + PCL816_CTRCTL);
840 outb(0, dev->iobase + PCL816_AD_LO);
841 inb(dev->iobase + PCL816_AD_LO);
842 inb(dev->iobase + PCL816_AD_HI);
844 /* clear INT request */
845 outb(0, dev->iobase + PCL816_CLRINT);
848 outb(0, dev->iobase + PCL816_CONTROL);
849 devpriv->irq_blocked = 0;
850 devpriv->irq_was_now_closed = devpriv->int816_mode;
851 devpriv->int816_mode = 0;
852 devpriv->last_int_sub = s;
858 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
863 ==============================================================================
866 static int pcl816_check(unsigned long iobase)
868 outb(0x00, iobase + PCL816_MUX);
870 if (inb(iobase + PCL816_MUX) != 0x00)
871 return 1; /* there isn't card */
872 outb(0x55, iobase + PCL816_MUX);
874 if (inb(iobase + PCL816_MUX) != 0x55)
875 return 1; /* there isn't card */
876 outb(0x00, iobase + PCL816_MUX);
878 outb(0x18, iobase + PCL816_CONTROL);
880 if (inb(iobase + PCL816_CONTROL) != 0x18)
881 return 1; /* there isn't card */
882 return 0; /* ok, card exist */
886 ==============================================================================
887 reset whole PCL-816 cards
889 static void pcl816_reset(struct comedi_device *dev)
891 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
892 /* outb (0, dev->iobase + PCL818_DA_HI); */
894 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
895 /* outb (0, dev->iobase + PCL818_DO_LO); */
897 outb(0, dev->iobase + PCL816_CONTROL);
898 outb(0, dev->iobase + PCL816_MUX);
899 outb(0, dev->iobase + PCL816_CLRINT);
900 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
901 outb(0x70, dev->iobase + PCL816_CTRCTL);
902 outb(0x30, dev->iobase + PCL816_CTRCTL);
903 outb(0, dev->iobase + PCL816_RANGE);
907 ==============================================================================
908 Start/stop pacer onboard pacer
911 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
912 unsigned int divisor2)
914 outb(0x32, dev->iobase + PCL816_CTRCTL);
915 outb(0xff, dev->iobase + PCL816_CTR0);
916 outb(0x00, dev->iobase + PCL816_CTR0);
919 /* set counter 2 as mode 3 */
920 outb(0xb4, dev->iobase + PCL816_CTRCTL);
921 /* set counter 1 as mode 3 */
922 outb(0x74, dev->iobase + PCL816_CTRCTL);
926 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
928 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
929 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
930 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
931 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
934 /* clear pending interrupts (just in case) */
935 /* outb(0, dev->iobase + PCL816_CLRINT); */
939 ==============================================================================
940 Check if channel list from user is builded correctly
941 If it's ok, then return non-zero length of repeated segment of channel list
944 check_channel_list(struct comedi_device *dev,
945 struct comedi_subdevice *s, unsigned int *chanlist,
946 unsigned int chanlen)
948 unsigned int chansegment[16];
949 unsigned int i, nowmustbechan, seglen, segpos;
951 /* correct channel and range number check itself comedi/range.c */
953 comedi_error(dev, "range/channel list is empty!");
958 /* first channel is every time ok */
959 chansegment[0] = chanlist[0];
960 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
961 /* build part of chanlist */
962 DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
963 CR_CHAN(chanlist[i]),
964 CR_RANGE(chanlist[i]));)
966 /* we detect loop, this must by finish */
967 if (chanlist[0] == chanlist[i])
970 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
971 if (nowmustbechan != CR_CHAN(chanlist[i])) {
972 /* channel list isn't continuous :-( */
974 "comedi%d: pcl816: channel list must "
975 "be continuous! chanlist[%i]=%d but "
976 "must be %d or %d!\n", dev->minor,
977 i, CR_CHAN(chanlist[i]), nowmustbechan,
978 CR_CHAN(chanlist[0]));
981 /* well, this is next correct channel in list */
982 chansegment[i] = chanlist[i];
985 /* check whole chanlist */
986 for (i = 0, segpos = 0; i < chanlen; i++) {
987 DEBUG(printk("%d %d=%d %d\n",
988 CR_CHAN(chansegment[i % seglen]),
989 CR_RANGE(chansegment[i % seglen]),
990 CR_CHAN(chanlist[i]),
991 CR_RANGE(chanlist[i]));)
992 if (chanlist[i] != chansegment[i % seglen]) {
994 "comedi%d: pcl816: bad channel or range"
995 " number! chanlist[%i]=%d,%d,%d and not"
996 " %d,%d,%d!\n", dev->minor, i,
997 CR_CHAN(chansegment[i]),
998 CR_RANGE(chansegment[i]),
999 CR_AREF(chansegment[i]),
1000 CR_CHAN(chanlist[i % seglen]),
1001 CR_RANGE(chanlist[i % seglen]),
1002 CR_AREF(chansegment[i % seglen]));
1003 return 0; /* chan/gain list is strange */
1010 return seglen; /* we can serve this with MUX logic */
1014 ==============================================================================
1015 Program scan/gain logic with channel list.
1018 setup_channel_list(struct comedi_device *dev,
1019 struct comedi_subdevice *s, unsigned int *chanlist,
1020 unsigned int seglen)
1024 devpriv->ai_act_chanlist_len = seglen;
1025 devpriv->ai_act_chanlist_pos = 0;
1027 for (i = 0; i < seglen; i++) { /* store range list to card */
1028 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
1029 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
1031 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
1035 /* select channel interval to scan */
1036 outb(devpriv->ai_act_chanlist[0] |
1037 (devpriv->ai_act_chanlist[seglen - 1] << 4),
1038 dev->iobase + PCL816_MUX);
1043 ==============================================================================
1044 Enable(1)/disable(0) periodic interrupts from RTC
1046 static int set_rtc_irq_bit(unsigned char bit)
1049 unsigned long flags;
1053 if (RTC_timer_lock > 1)
1057 if (RTC_timer_lock < 0)
1059 if (RTC_timer_lock > 0)
1065 val = CMOS_READ(RTC_CONTROL);
1071 CMOS_WRITE(val, RTC_CONTROL);
1072 CMOS_READ(RTC_INTR_FLAGS);
1073 restore_flags(flags);
1079 ==============================================================================
1080 Free any resources that we have claimed
1082 static void free_resources(struct comedi_device *dev)
1084 /* printk("free_resource()\n"); */
1086 pcl816_ai_cancel(dev, devpriv->sub_ai);
1089 free_dma(devpriv->dma);
1090 if (devpriv->dmabuf[0])
1091 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1092 if (devpriv->dmabuf[1])
1093 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1095 if (devpriv->rtc_irq)
1096 free_irq(devpriv->rtc_irq, dev);
1097 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1098 if (devpriv->rtc_iobase)
1099 release_region(devpriv->rtc_iobase,
1100 devpriv->rtc_iosize);
1106 free_irq(dev->irq, dev);
1108 release_region(dev->iobase, this_board->io_range);
1109 /* printk("free_resource() end\n"); */
1113 ==============================================================================
1118 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1121 unsigned long iobase;
1122 unsigned int irq, dma;
1123 unsigned long pages;
1125 struct comedi_subdevice *s;
1127 /* claim our I/O space */
1128 iobase = it->options[0];
1129 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1130 this_board->name, iobase);
1132 if (!request_region(iobase, this_board->io_range, "pcl816")) {
1133 printk("I/O port conflict\n");
1137 dev->iobase = iobase;
1139 if (pcl816_check(iobase)) {
1140 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
1144 ret = alloc_private(dev, sizeof(struct pcl816_private));
1146 return ret; /* Can't alloc mem */
1148 /* set up some name stuff */
1149 dev->board_name = this_board->name;
1153 if (this_board->IRQbits != 0) { /* board support IRQ */
1154 irq = it->options[1];
1155 if (irq) { /* we want to use IRQ */
1156 if (((1 << irq) & this_board->IRQbits) == 0) {
1158 (", IRQ %u is out of allowed range, "
1159 "DISABLING IT", irq);
1160 irq = 0; /* Bad IRQ */
1163 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1165 (", unable to allocate IRQ %u, "
1166 "DISABLING IT", irq);
1167 irq = 0; /* Can't use IRQ */
1169 printk(KERN_INFO ", irq=%u", irq);
1176 if (irq) /* 1=we have allocated irq */
1177 devpriv->irq_free = 1;
1179 devpriv->irq_free = 0;
1181 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1182 devpriv->int816_mode = 0; /* mode of irq */
1185 /* grab RTC for DMA operations */
1186 devpriv->dma_rtc = 0;
1187 if (it->options[2] > 0) { /* we want to use DMA */
1188 if (RTC_lock == 0) {
1189 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1193 devpriv->rtc_iobase = RTC_PORT(0);
1194 devpriv->rtc_iosize = RTC_IO_EXTENT;
1196 #ifdef UNTESTED_CODE
1197 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1198 "pcl816 DMA (RTC)", dev)) {
1199 devpriv->dma_rtc = 1;
1200 devpriv->rtc_irq = RTC_IRQ;
1201 printk(", dma_irq=%u", devpriv->rtc_irq);
1204 if (RTC_lock == 0) {
1205 if (devpriv->rtc_iobase)
1206 release_region(devpriv->rtc_iobase,
1207 devpriv->rtc_iosize);
1209 devpriv->rtc_iobase = 0;
1210 devpriv->rtc_iosize = 0;
1213 printk("pcl816: RTC code missing");
1223 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1224 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1226 if (this_board->DMAbits != 0) { /* board support DMA */
1227 dma = it->options[2];
1229 goto no_dma; /* DMA disabled */
1231 if (((1 << dma) & this_board->DMAbits) == 0) {
1232 printk(", DMA is out of allowed range, FAIL!\n");
1233 return -EINVAL; /* Bad DMA */
1235 ret = request_dma(dma, "pcl816");
1238 ", unable to allocate DMA %u, FAIL!\n", dma);
1239 return -EBUSY; /* DMA isn't free */
1243 printk(KERN_INFO ", dma=%u", dma);
1244 pages = 2; /* we need 16KB */
1245 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1247 if (!devpriv->dmabuf[0]) {
1248 printk(", unable to allocate DMA buffer, FAIL!\n");
1250 * maybe experiment with try_to_free_pages()
1253 return -EBUSY; /* no buffer :-( */
1255 devpriv->dmapages[0] = pages;
1256 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1257 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1258 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1260 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1261 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1262 if (!devpriv->dmabuf[1]) {
1264 ", unable to allocate DMA buffer, "
1268 devpriv->dmapages[1] = pages;
1269 devpriv->hwdmaptr[1] =
1270 virt_to_bus((void *)devpriv->dmabuf[1]);
1271 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1277 /* if (this_board->n_aochan > 0)
1278 subdevs[1] = COMEDI_SUBD_AO;
1279 if (this_board->n_dichan > 0)
1280 subdevs[2] = COMEDI_SUBD_DI;
1281 if (this_board->n_dochan > 0)
1282 subdevs[3] = COMEDI_SUBD_DO;
1285 ret = alloc_subdevices(dev, 1);
1289 s = dev->subdevices + 0;
1290 if (this_board->n_aichan > 0) {
1291 s->type = COMEDI_SUBD_AI;
1292 devpriv->sub_ai = s;
1293 dev->read_subdev = s;
1294 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1295 s->n_chan = this_board->n_aichan;
1296 s->subdev_flags |= SDF_DIFF;
1297 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1298 s->maxdata = this_board->ai_maxdata;
1299 s->len_chanlist = this_board->ai_chanlist;
1300 s->range_table = this_board->ai_range_type;
1301 s->cancel = pcl816_ai_cancel;
1302 s->do_cmdtest = pcl816_ai_cmdtest;
1303 s->do_cmd = pcl816_ai_cmd;
1304 s->poll = pcl816_ai_poll;
1305 s->insn_read = pcl816_ai_insn_read;
1307 s->type = COMEDI_SUBD_UNUSED;
1311 case COMEDI_SUBD_AO:
1312 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1313 s->n_chan = this_board->n_aochan;
1314 s->maxdata = this_board->ao_maxdata;
1315 s->len_chanlist = this_board->ao_chanlist;
1316 s->range_table = this_board->ao_range_type;
1319 case COMEDI_SUBD_DI:
1320 s->subdev_flags = SDF_READABLE;
1321 s->n_chan = this_board->n_dichan;
1323 s->len_chanlist = this_board->n_dichan;
1324 s->range_table = &range_digital;
1327 case COMEDI_SUBD_DO:
1328 s->subdev_flags = SDF_WRITABLE;
1329 s->n_chan = this_board->n_dochan;
1331 s->len_chanlist = this_board->n_dochan;
1332 s->range_table = &range_digital;
1344 ==============================================================================
1347 static int pcl816_detach(struct comedi_device *dev)
1349 DEBUG(printk(KERN_INFO "comedi%d: pcl816: remove\n", dev->minor);)
1350 free_resources(dev);
1352 if (devpriv->dma_rtc)
1358 MODULE_AUTHOR("Comedi http://www.comedi.org");
1359 MODULE_DESCRIPTION("Comedi low-level driver");
1360 MODULE_LICENSE("GPL");