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>
44 #include "comedi_fc.h"
49 /* boards constants */
51 #define PCLx1x_RANGE 16
53 /* #define outb(x,y) printk("OUTB(%x, 200+%d)\n", x,y-0x200); outb(x,y) */
55 /* INTEL 8254 counters */
59 /* R: counter read-back register W: counter control */
60 #define PCL816_CTRCTL 7
62 /* R: A/D high byte W: A/D range control */
63 #define PCL816_RANGE 9
64 /* W: clear INT request */
65 #define PCL816_CLRINT 10
66 /* R: next mux scan channel W: mux scan channel & range control pointer */
68 /* R/W: operation control register */
69 #define PCL816_CONTROL 12
71 /* R: return status byte W: set DMA/IRQ */
72 #define PCL816_STATUS 13
73 #define PCL816_STATUS_DRDY_MASK 0x80
75 /* R: low byte of A/D W: soft A/D trigger */
76 #define PCL816_AD_LO 8
77 /* R: high byte of A/D W: A/D range control */
78 #define PCL816_AD_HI 9
80 /* type of interrupt handler */
81 #define INT_TYPE_AI1_INT 1
82 #define INT_TYPE_AI1_DMA 2
83 #define INT_TYPE_AI3_INT 4
84 #define INT_TYPE_AI3_DMA 5
86 #define INT_TYPE_AI1_DMA_RTC 9
87 #define INT_TYPE_AI3_DMA_RTC 10
91 #define RTC_IO_EXTENT 0x10
94 #define MAGIC_DMA_WORD 0x5a5a
96 static const struct comedi_lrange range_pcl816 = { 8, {
108 struct pcl816_board {
110 const char *name; /* board name */
111 int n_ranges; /* len of range list */
112 int n_aichan; /* num of A/D chans in diferencial mode */
113 unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */
114 int n_aochan; /* num of D/A chans */
115 int n_dichan; /* num of DI chans */
116 int n_dochan; /* num of DO chans */
117 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
118 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
119 unsigned int io_range; /* len of IO space */
120 unsigned int IRQbits; /* allowed interrupts */
121 unsigned int DMAbits; /* allowed DMA chans */
122 int ai_maxdata; /* maxdata for A/D */
123 int ao_maxdata; /* maxdata for D/A */
124 int ai_chanlist; /* allowed len of channel list A/D */
125 int ao_chanlist; /* allowed len of channel list D/A */
126 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
129 #define devpriv ((struct pcl816_private *)dev->private)
132 static int RTC_lock; /* RTC lock */
133 static int RTC_timer_lock; /* RTC int lock */
136 struct pcl816_private {
138 unsigned int dma; /* used DMA, 0=don't use DMA */
139 int dma_rtc; /* 1=RTC used with DMA, 0=no RTC alloc */
141 unsigned long rtc_iobase; /* RTC port region */
142 unsigned int rtc_iosize;
143 unsigned int rtc_irq;
145 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
146 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
147 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
148 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
149 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
150 unsigned int last_top_dma; /* DMA pointer in last RTC int */
151 int next_dma_buf; /* which DMA buffer will be used next round */
152 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
153 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
155 unsigned int ai_scans; /* len of scanlist */
156 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
157 int irq_free; /* 1=have allocated IRQ */
158 int irq_blocked; /* 1=IRQ now uses any subdev */
160 int rtc_irq_blocked; /* 1=we now do AI with DMA&RTC */
162 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
163 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
164 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
165 int ai_act_scan; /* how many scans we finished */
166 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
167 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
168 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
169 unsigned int ai_n_chan; /* how many channels per scan */
170 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
171 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
173 struct timer_list rtc_irq_timer; /* timer for RTC sanity check */
174 unsigned long rtc_freq; /* RTC int freq */
179 ==============================================================================
181 static int check_channel_list(struct comedi_device *dev,
182 struct comedi_subdevice *s,
183 unsigned int *chanlist, unsigned int chanlen);
184 static void setup_channel_list(struct comedi_device *dev,
185 struct comedi_subdevice *s,
186 unsigned int *chanlist, unsigned int seglen);
187 static int pcl816_ai_cancel(struct comedi_device *dev,
188 struct comedi_subdevice *s);
189 static void start_pacer(struct comedi_device *dev, int mode,
190 unsigned int divisor1, unsigned int divisor2);
192 static int set_rtc_irq_bit(unsigned char bit);
195 static int pcl816_ai_cmdtest(struct comedi_device *dev,
196 struct comedi_subdevice *s,
197 struct comedi_cmd *cmd);
198 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
201 ==============================================================================
202 ANALOG INPUT MODE0, 816 cards, slow version
204 static int pcl816_ai_insn_read(struct comedi_device *dev,
205 struct comedi_subdevice *s,
206 struct comedi_insn *insn, unsigned int *data)
211 DPRINTK("mode 0 analog input\n");
212 /* software trigger, DMA and INT off */
213 outb(0, dev->iobase + PCL816_CONTROL);
214 /* clear INT (conversion end) flag */
215 outb(0, dev->iobase + PCL816_CLRINT);
217 /* Set the input channel */
218 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
220 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
222 for (n = 0; n < insn->n; n++) {
224 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
228 if (!(inb(dev->iobase + PCL816_STATUS) &
229 PCL816_STATUS_DRDY_MASK)) {
230 /* return read value */
233 PCL816_AD_HI) << 8) |
234 (inb(dev->iobase + PCL816_AD_LO)));
235 /* clear INT (conversion end) flag */
236 outb(0, dev->iobase + PCL816_CLRINT);
241 /* Return timeout error */
243 comedi_error(dev, "A/D insn timeout\n");
245 /* clear INT (conversion end) flag */
246 outb(0, dev->iobase + PCL816_CLRINT);
255 ==============================================================================
256 analog input interrupt mode 1 & 3, 818 cards
257 one sample per interrupt version
259 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
261 struct comedi_device *dev = d;
262 struct comedi_subdevice *s = &dev->subdevices[0];
264 int timeout = 50; /* wait max 50us */
267 if (!(inb(dev->iobase + PCL816_STATUS) &
268 PCL816_STATUS_DRDY_MASK))
272 if (!timeout) { /* timeout, bail error */
273 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
274 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
275 pcl816_ai_cancel(dev, s);
276 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
277 comedi_event(dev, s);
283 low = inb(dev->iobase + PCL816_AD_LO);
284 hi = inb(dev->iobase + PCL816_AD_HI);
286 comedi_buf_put(s->async, (hi << 8) | low);
288 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
290 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
291 devpriv->ai_act_chanlist_pos = 0;
293 s->async->cur_chan++;
294 if (s->async->cur_chan >= devpriv->ai_n_chan) {
295 s->async->cur_chan = 0;
296 devpriv->ai_act_scan++;
299 if (!devpriv->ai_neverending)
300 /* all data sampled */
301 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
302 /* all data sampled */
303 pcl816_ai_cancel(dev, s);
304 s->async->events |= COMEDI_CB_EOA;
306 comedi_event(dev, s);
311 ==============================================================================
312 analog input dma mode 1 & 3, 816 cards
314 static void transfer_from_dma_buf(struct comedi_device *dev,
315 struct comedi_subdevice *s, short *ptr,
316 unsigned int bufptr, unsigned int len)
320 s->async->events = 0;
322 for (i = 0; i < len; i++) {
324 comedi_buf_put(s->async, ptr[bufptr++]);
326 if (++devpriv->ai_act_chanlist_pos >=
327 devpriv->ai_act_chanlist_len) {
328 devpriv->ai_act_chanlist_pos = 0;
331 s->async->cur_chan++;
332 if (s->async->cur_chan >= devpriv->ai_n_chan) {
333 s->async->cur_chan = 0;
334 devpriv->ai_act_scan++;
337 if (!devpriv->ai_neverending)
338 /* all data sampled */
339 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
340 pcl816_ai_cancel(dev, s);
341 s->async->events |= COMEDI_CB_EOA;
342 s->async->events |= COMEDI_CB_BLOCK;
347 comedi_event(dev, s);
350 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
352 struct comedi_device *dev = d;
353 struct comedi_subdevice *s = &dev->subdevices[0];
354 int len, bufptr, this_dma_buf;
355 unsigned long dma_flags;
358 disable_dma(devpriv->dma);
359 this_dma_buf = devpriv->next_dma_buf;
361 /* switch dma bufs */
362 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
364 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
365 set_dma_mode(devpriv->dma, DMA_MODE_READ);
366 dma_flags = claim_dma_lock();
367 /* clear_dma_ff (devpriv->dma); */
368 set_dma_addr(devpriv->dma,
369 devpriv->hwdmaptr[devpriv->next_dma_buf]);
370 if (devpriv->dma_runs_to_end) {
371 set_dma_count(devpriv->dma,
372 devpriv->hwdmasize[devpriv->
375 set_dma_count(devpriv->dma, devpriv->last_dma_run);
377 release_dma_lock(dma_flags);
378 enable_dma(devpriv->dma);
381 devpriv->dma_runs_to_end--;
382 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
384 ptr = (short *)devpriv->dmabuf[this_dma_buf];
386 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
387 bufptr = devpriv->ai_poll_ptr;
388 devpriv->ai_poll_ptr = 0;
390 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
395 ==============================================================================
398 static irqreturn_t interrupt_pcl816(int irq, void *d)
400 struct comedi_device *dev = d;
403 if (!dev->attached) {
404 comedi_error(dev, "premature interrupt");
408 switch (devpriv->int816_mode) {
409 case INT_TYPE_AI1_DMA:
410 case INT_TYPE_AI3_DMA:
411 return interrupt_pcl816_ai_mode13_dma(irq, d);
412 case INT_TYPE_AI1_INT:
413 case INT_TYPE_AI3_INT:
414 return interrupt_pcl816_ai_mode13_int(irq, d);
417 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
418 if (!dev->irq || !devpriv->irq_free || !devpriv->irq_blocked ||
419 !devpriv->int816_mode) {
420 if (devpriv->irq_was_now_closed) {
421 devpriv->irq_was_now_closed = 0;
422 /* comedi_error(dev,"last IRQ.."); */
425 comedi_error(dev, "bad IRQ!");
428 comedi_error(dev, "IRQ from unknown source!");
433 ==============================================================================
436 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
438 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
439 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
440 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
441 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
442 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
443 cmd->stop_src, cmd->scan_end_src);
444 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
445 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
449 ==============================================================================
451 static int pcl816_ai_cmdtest(struct comedi_device *dev,
452 struct comedi_subdevice *s, struct comedi_cmd *cmd)
454 const struct pcl816_board *board = comedi_board(dev);
456 int tmp, divisor1 = 0, divisor2 = 0;
458 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
459 pcl816_cmdtest_out(-1, cmd);
462 /* Step 1 : check if triggers are trivially valid */
464 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
465 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
466 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
467 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
468 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
473 /* Step 2a : make sure trigger sources are unique */
475 err |= cfc_check_trigger_is_unique(cmd->convert_src);
476 err |= cfc_check_trigger_is_unique(cmd->stop_src);
478 /* Step 2b : and mutually compatible */
484 /* step 3: make sure arguments are trivially compatible */
485 if (cmd->start_arg != 0) {
490 if (cmd->scan_begin_arg != 0) {
491 cmd->scan_begin_arg = 0;
494 if (cmd->convert_src == TRIG_TIMER) {
495 if (cmd->convert_arg < board->ai_ns_min) {
496 cmd->convert_arg = board->ai_ns_min;
499 } else { /* TRIG_EXT */
500 if (cmd->convert_arg != 0) {
501 cmd->convert_arg = 0;
506 if (cmd->scan_end_arg != cmd->chanlist_len) {
507 cmd->scan_end_arg = cmd->chanlist_len;
510 if (cmd->stop_src == TRIG_COUNT) {
511 if (!cmd->stop_arg) {
515 } else { /* TRIG_NONE */
516 if (cmd->stop_arg != 0) {
526 /* step 4: fix up any arguments */
527 if (cmd->convert_src == TRIG_TIMER) {
528 tmp = cmd->convert_arg;
529 i8253_cascade_ns_to_timer(board->i8254_osc_base,
530 &divisor1, &divisor2,
532 cmd->flags & TRIG_ROUND_MASK);
533 if (cmd->convert_arg < board->ai_ns_min)
534 cmd->convert_arg = board->ai_ns_min;
535 if (tmp != cmd->convert_arg)
543 /* step 5: complain about special chanlist considerations */
546 if (!check_channel_list(dev, s, cmd->chanlist,
548 return 5; /* incorrect channels list */
554 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
556 const struct pcl816_board *board = comedi_board(dev);
557 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
558 struct comedi_cmd *cmd = &s->async->cmd;
561 if (cmd->start_src != TRIG_NOW)
563 if (cmd->scan_begin_src != TRIG_FOLLOW)
565 if (cmd->scan_end_src != TRIG_COUNT)
567 if (cmd->scan_end_arg != cmd->chanlist_len)
569 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
570 if (devpriv->irq_blocked)
573 if (cmd->convert_src == TRIG_TIMER) {
574 if (cmd->convert_arg < board->ai_ns_min)
575 cmd->convert_arg = board->ai_ns_min;
577 i8253_cascade_ns_to_timer(board->i8254_osc_base, &divisor1,
578 &divisor2, &cmd->convert_arg,
579 cmd->flags & TRIG_ROUND_MASK);
581 /* PCL816 crash if any divisor is set to 1 */
592 start_pacer(dev, -1, 0, 0); /* stop pacer */
594 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
597 setup_channel_list(dev, s, cmd->chanlist, seglen);
600 devpriv->ai_n_chan = cmd->chanlist_len;
601 devpriv->ai_act_scan = 0;
602 s->async->cur_chan = 0;
603 devpriv->irq_blocked = 1;
604 devpriv->ai_poll_ptr = 0;
605 devpriv->irq_was_now_closed = 0;
607 if (cmd->stop_src == TRIG_COUNT) {
608 devpriv->ai_scans = cmd->stop_arg;
609 devpriv->ai_neverending = 0;
611 devpriv->ai_scans = 0;
612 devpriv->ai_neverending = 1;
615 /* don't we want wake up every scan? */
616 if ((cmd->flags & TRIG_WAKE_EOS)) {
618 "pl816: You wankt WAKE_EOS but I dont want handle it");
619 /* devpriv->ai_eos=1; */
620 /* if (devpriv->ai_n_chan==1) */
621 /* devpriv->dma=0; // DMA is useless for this situation */
625 bytes = devpriv->hwdmasize[0];
626 if (!devpriv->ai_neverending) {
628 bytes = s->async->cmd.chanlist_len *
629 s->async->cmd.chanlist_len *
632 /* how many DMA pages we must fill */
633 devpriv->dma_runs_to_end = bytes /
634 devpriv->hwdmasize[0];
636 /* on last dma transfer must be moved */
637 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
638 devpriv->dma_runs_to_end--;
639 if (devpriv->dma_runs_to_end >= 0)
640 bytes = devpriv->hwdmasize[0];
642 devpriv->dma_runs_to_end = -1;
644 devpriv->next_dma_buf = 0;
645 set_dma_mode(devpriv->dma, DMA_MODE_READ);
646 dma_flags = claim_dma_lock();
647 clear_dma_ff(devpriv->dma);
648 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
649 set_dma_count(devpriv->dma, bytes);
650 release_dma_lock(dma_flags);
651 enable_dma(devpriv->dma);
654 start_pacer(dev, 1, divisor1, divisor2);
655 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
657 switch (cmd->convert_src) {
659 devpriv->int816_mode = INT_TYPE_AI1_DMA;
662 outb(0x32, dev->iobase + PCL816_CONTROL);
664 /* write irq and DMA to card */
665 outb(dmairq, dev->iobase + PCL816_STATUS);
669 devpriv->int816_mode = INT_TYPE_AI3_DMA;
671 /* Ext trig+IRQ+DMA */
672 outb(0x34, dev->iobase + PCL816_CONTROL);
674 /* write irq to card */
675 outb(dmairq, dev->iobase + PCL816_STATUS);
679 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
683 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
686 unsigned int top1, top2, i;
689 return 0; /* poll is valid only for DMA transfer */
691 spin_lock_irqsave(&dev->spinlock, flags);
693 for (i = 0; i < 20; i++) {
694 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
695 top2 = get_dma_residue(devpriv->dma);
700 spin_unlock_irqrestore(&dev->spinlock, flags);
704 /* where is now DMA in buffer */
705 top1 = devpriv->hwdmasize[0] - top1;
706 top1 >>= 1; /* sample position */
707 top2 = top1 - devpriv->ai_poll_ptr;
708 if (top2 < 1) { /* no new samples */
709 spin_unlock_irqrestore(&dev->spinlock, flags);
713 transfer_from_dma_buf(dev, s,
714 (short *)devpriv->dmabuf[devpriv->next_dma_buf],
715 devpriv->ai_poll_ptr, top2);
717 devpriv->ai_poll_ptr = top1; /* new buffer position */
718 spin_unlock_irqrestore(&dev->spinlock, flags);
720 return s->async->buf_write_count - s->async->buf_read_count;
724 ==============================================================================
725 cancel any mode 1-4 AI
727 static int pcl816_ai_cancel(struct comedi_device *dev,
728 struct comedi_subdevice *s)
730 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
732 if (devpriv->irq_blocked > 0) {
733 switch (devpriv->int816_mode) {
735 case INT_TYPE_AI1_DMA_RTC:
736 case INT_TYPE_AI3_DMA_RTC:
737 set_rtc_irq_bit(0); /* stop RTC */
738 del_timer(&devpriv->rtc_irq_timer);
740 case INT_TYPE_AI1_DMA:
741 case INT_TYPE_AI3_DMA:
742 disable_dma(devpriv->dma);
743 case INT_TYPE_AI1_INT:
744 case INT_TYPE_AI3_INT:
745 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
746 dev->iobase + PCL816_CONTROL); /* Stop A/D */
748 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
751 outb(0xb0, dev->iobase + PCL816_CTRCTL);
752 outb(0x70, dev->iobase + PCL816_CTRCTL);
753 outb(0, dev->iobase + PCL816_AD_LO);
754 inb(dev->iobase + PCL816_AD_LO);
755 inb(dev->iobase + PCL816_AD_HI);
757 /* clear INT request */
758 outb(0, dev->iobase + PCL816_CLRINT);
761 outb(0, dev->iobase + PCL816_CONTROL);
762 devpriv->irq_blocked = 0;
763 devpriv->irq_was_now_closed = devpriv->int816_mode;
764 devpriv->int816_mode = 0;
765 devpriv->last_int_sub = s;
771 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
776 ==============================================================================
779 static int pcl816_check(unsigned long iobase)
781 outb(0x00, iobase + PCL816_MUX);
783 if (inb(iobase + PCL816_MUX) != 0x00)
784 return 1; /* there isn't card */
785 outb(0x55, iobase + PCL816_MUX);
787 if (inb(iobase + PCL816_MUX) != 0x55)
788 return 1; /* there isn't card */
789 outb(0x00, iobase + PCL816_MUX);
791 outb(0x18, iobase + PCL816_CONTROL);
793 if (inb(iobase + PCL816_CONTROL) != 0x18)
794 return 1; /* there isn't card */
795 return 0; /* ok, card exist */
799 ==============================================================================
800 reset whole PCL-816 cards
802 static void pcl816_reset(struct comedi_device *dev)
804 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
805 /* outb (0, dev->iobase + PCL818_DA_HI); */
807 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
808 /* outb (0, dev->iobase + PCL818_DO_LO); */
810 outb(0, dev->iobase + PCL816_CONTROL);
811 outb(0, dev->iobase + PCL816_MUX);
812 outb(0, dev->iobase + PCL816_CLRINT);
813 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
814 outb(0x70, dev->iobase + PCL816_CTRCTL);
815 outb(0x30, dev->iobase + PCL816_CTRCTL);
816 outb(0, dev->iobase + PCL816_RANGE);
820 ==============================================================================
821 Start/stop pacer onboard pacer
824 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
825 unsigned int divisor2)
827 outb(0x32, dev->iobase + PCL816_CTRCTL);
828 outb(0xff, dev->iobase + PCL816_CTR0);
829 outb(0x00, dev->iobase + PCL816_CTR0);
832 /* set counter 2 as mode 3 */
833 outb(0xb4, dev->iobase + PCL816_CTRCTL);
834 /* set counter 1 as mode 3 */
835 outb(0x74, dev->iobase + PCL816_CTRCTL);
839 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
841 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
842 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
843 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
844 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
847 /* clear pending interrupts (just in case) */
848 /* outb(0, dev->iobase + PCL816_CLRINT); */
852 ==============================================================================
853 Check if channel list from user is builded correctly
854 If it's ok, then return non-zero length of repeated segment of channel list
857 check_channel_list(struct comedi_device *dev,
858 struct comedi_subdevice *s, unsigned int *chanlist,
859 unsigned int chanlen)
861 unsigned int chansegment[16];
862 unsigned int i, nowmustbechan, seglen, segpos;
864 /* correct channel and range number check itself comedi/range.c */
866 comedi_error(dev, "range/channel list is empty!");
871 /* first channel is every time ok */
872 chansegment[0] = chanlist[0];
873 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
874 /* build part of chanlist */
875 DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
876 CR_CHAN(chanlist[i]),
877 CR_RANGE(chanlist[i]));)
879 /* we detect loop, this must by finish */
880 if (chanlist[0] == chanlist[i])
883 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
884 if (nowmustbechan != CR_CHAN(chanlist[i])) {
885 /* channel list isn't continuous :-( */
887 "comedi%d: pcl816: channel list must "
888 "be continuous! chanlist[%i]=%d but "
889 "must be %d or %d!\n", dev->minor,
890 i, CR_CHAN(chanlist[i]), nowmustbechan,
891 CR_CHAN(chanlist[0]));
894 /* well, this is next correct channel in list */
895 chansegment[i] = chanlist[i];
898 /* check whole chanlist */
899 for (i = 0, segpos = 0; i < chanlen; i++) {
900 DEBUG(printk("%d %d=%d %d\n",
901 CR_CHAN(chansegment[i % seglen]),
902 CR_RANGE(chansegment[i % seglen]),
903 CR_CHAN(chanlist[i]),
904 CR_RANGE(chanlist[i]));)
905 if (chanlist[i] != chansegment[i % seglen]) {
907 "comedi%d: pcl816: bad channel or range"
908 " number! chanlist[%i]=%d,%d,%d and not"
909 " %d,%d,%d!\n", dev->minor, i,
910 CR_CHAN(chansegment[i]),
911 CR_RANGE(chansegment[i]),
912 CR_AREF(chansegment[i]),
913 CR_CHAN(chanlist[i % seglen]),
914 CR_RANGE(chanlist[i % seglen]),
915 CR_AREF(chansegment[i % seglen]));
916 return 0; /* chan/gain list is strange */
923 return seglen; /* we can serve this with MUX logic */
927 ==============================================================================
928 Program scan/gain logic with channel list.
931 setup_channel_list(struct comedi_device *dev,
932 struct comedi_subdevice *s, unsigned int *chanlist,
937 devpriv->ai_act_chanlist_len = seglen;
938 devpriv->ai_act_chanlist_pos = 0;
940 for (i = 0; i < seglen; i++) { /* store range list to card */
941 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
942 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
944 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
948 /* select channel interval to scan */
949 outb(devpriv->ai_act_chanlist[0] |
950 (devpriv->ai_act_chanlist[seglen - 1] << 4),
951 dev->iobase + PCL816_MUX);
956 ==============================================================================
957 Enable(1)/disable(0) periodic interrupts from RTC
959 static int set_rtc_irq_bit(unsigned char bit)
966 if (RTC_timer_lock > 1)
970 if (RTC_timer_lock < 0)
972 if (RTC_timer_lock > 0)
978 val = CMOS_READ(RTC_CONTROL);
984 CMOS_WRITE(val, RTC_CONTROL);
985 CMOS_READ(RTC_INTR_FLAGS);
986 restore_flags(flags);
991 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
993 const struct pcl816_board *board = comedi_board(dev);
995 unsigned long iobase;
996 unsigned int irq, dma;
999 struct comedi_subdevice *s;
1001 /* claim our I/O space */
1002 iobase = it->options[0];
1003 printk("comedi%d: pcl816: board=%s, ioport=0x%03lx", dev->minor,
1004 board->name, iobase);
1006 if (!request_region(iobase, board->io_range, "pcl816")) {
1007 printk("I/O port conflict\n");
1011 dev->iobase = iobase;
1013 if (pcl816_check(iobase)) {
1014 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
1018 ret = alloc_private(dev, sizeof(struct pcl816_private));
1020 return ret; /* Can't alloc mem */
1022 dev->board_name = board->name;
1026 if (board->IRQbits != 0) { /* board support IRQ */
1027 irq = it->options[1];
1028 if (irq) { /* we want to use IRQ */
1029 if (((1 << irq) & board->IRQbits) == 0) {
1031 (", IRQ %u is out of allowed range, "
1032 "DISABLING IT", irq);
1033 irq = 0; /* Bad IRQ */
1036 (irq, interrupt_pcl816, 0, "pcl816", dev)) {
1038 (", unable to allocate IRQ %u, "
1039 "DISABLING IT", irq);
1040 irq = 0; /* Can't use IRQ */
1042 printk(KERN_INFO ", irq=%u", irq);
1049 if (irq) /* 1=we have allocated irq */
1050 devpriv->irq_free = 1;
1052 devpriv->irq_free = 0;
1054 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
1055 devpriv->int816_mode = 0; /* mode of irq */
1058 /* grab RTC for DMA operations */
1059 devpriv->dma_rtc = 0;
1060 if (it->options[2] > 0) { /* we want to use DMA */
1061 if (RTC_lock == 0) {
1062 if (!request_region(RTC_PORT(0), RTC_IO_EXTENT,
1066 devpriv->rtc_iobase = RTC_PORT(0);
1067 devpriv->rtc_iosize = RTC_IO_EXTENT;
1069 #ifdef UNTESTED_CODE
1070 if (!request_irq(RTC_IRQ, interrupt_pcl816_ai_mode13_dma_rtc, 0,
1071 "pcl816 DMA (RTC)", dev)) {
1072 devpriv->dma_rtc = 1;
1073 devpriv->rtc_irq = RTC_IRQ;
1074 printk(", dma_irq=%u", devpriv->rtc_irq);
1077 if (RTC_lock == 0) {
1078 if (devpriv->rtc_iobase)
1079 release_region(devpriv->rtc_iobase,
1080 devpriv->rtc_iosize);
1082 devpriv->rtc_iobase = 0;
1083 devpriv->rtc_iosize = 0;
1086 printk("pcl816: RTC code missing");
1096 if ((devpriv->irq_free == 0) && (devpriv->dma_rtc == 0))
1097 goto no_dma; /* if we haven't IRQ, we can't use DMA */
1099 if (board->DMAbits != 0) { /* board support DMA */
1100 dma = it->options[2];
1102 goto no_dma; /* DMA disabled */
1104 if (((1 << dma) & board->DMAbits) == 0) {
1105 printk(", DMA is out of allowed range, FAIL!\n");
1106 return -EINVAL; /* Bad DMA */
1108 ret = request_dma(dma, "pcl816");
1111 ", unable to allocate DMA %u, FAIL!\n", dma);
1112 return -EBUSY; /* DMA isn't free */
1116 printk(KERN_INFO ", dma=%u", dma);
1117 pages = 2; /* we need 16KB */
1118 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
1120 if (!devpriv->dmabuf[0]) {
1121 printk(", unable to allocate DMA buffer, FAIL!\n");
1123 * maybe experiment with try_to_free_pages()
1126 return -EBUSY; /* no buffer :-( */
1128 devpriv->dmapages[0] = pages;
1129 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1130 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1131 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1133 if (devpriv->dma_rtc == 0) { /* we must do duble buff :-( */
1134 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1135 if (!devpriv->dmabuf[1]) {
1137 ", unable to allocate DMA buffer, "
1141 devpriv->dmapages[1] = pages;
1142 devpriv->hwdmaptr[1] =
1143 virt_to_bus((void *)devpriv->dmabuf[1]);
1144 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1150 /* if (board->n_aochan > 0)
1151 subdevs[1] = COMEDI_SUBD_AO;
1152 if (board->n_dichan > 0)
1153 subdevs[2] = COMEDI_SUBD_DI;
1154 if (board->n_dochan > 0)
1155 subdevs[3] = COMEDI_SUBD_DO;
1158 ret = comedi_alloc_subdevices(dev, 1);
1162 s = &dev->subdevices[0];
1163 if (board->n_aichan > 0) {
1164 s->type = COMEDI_SUBD_AI;
1165 devpriv->sub_ai = s;
1166 dev->read_subdev = s;
1167 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1168 s->n_chan = board->n_aichan;
1169 s->subdev_flags |= SDF_DIFF;
1170 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1171 s->maxdata = board->ai_maxdata;
1172 s->len_chanlist = board->ai_chanlist;
1173 s->range_table = board->ai_range_type;
1174 s->cancel = pcl816_ai_cancel;
1175 s->do_cmdtest = pcl816_ai_cmdtest;
1176 s->do_cmd = pcl816_ai_cmd;
1177 s->poll = pcl816_ai_poll;
1178 s->insn_read = pcl816_ai_insn_read;
1180 s->type = COMEDI_SUBD_UNUSED;
1184 case COMEDI_SUBD_AO:
1185 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1186 s->n_chan = board->n_aochan;
1187 s->maxdata = board->ao_maxdata;
1188 s->len_chanlist = board->ao_chanlist;
1189 s->range_table = board->ao_range_type;
1192 case COMEDI_SUBD_DI:
1193 s->subdev_flags = SDF_READABLE;
1194 s->n_chan = board->n_dichan;
1196 s->len_chanlist = board->n_dichan;
1197 s->range_table = &range_digital;
1200 case COMEDI_SUBD_DO:
1201 s->subdev_flags = SDF_WRITABLE;
1202 s->n_chan = board->n_dochan;
1204 s->len_chanlist = board->n_dochan;
1205 s->range_table = &range_digital;
1216 static void pcl816_detach(struct comedi_device *dev)
1218 const struct pcl816_board *board = comedi_board(dev);
1221 pcl816_ai_cancel(dev, devpriv->sub_ai);
1224 free_dma(devpriv->dma);
1225 if (devpriv->dmabuf[0])
1226 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1227 if (devpriv->dmabuf[1])
1228 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1230 if (devpriv->rtc_irq)
1231 free_irq(devpriv->rtc_irq, dev);
1232 if ((devpriv->dma_rtc) && (RTC_lock == 1)) {
1233 if (devpriv->rtc_iobase)
1234 release_region(devpriv->rtc_iobase,
1235 devpriv->rtc_iosize);
1240 free_irq(dev->irq, dev);
1242 release_region(dev->iobase, board->io_range);
1244 if (devpriv->dma_rtc)
1249 static const struct pcl816_board boardtypes[] = {
1250 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1251 &range_pcl816, PCLx1x_RANGE,
1252 0x00fc, /* IRQ mask */
1253 0x0a, /* DMA mask */
1254 0xffff, /* 16-bit card */
1255 0xffff, /* D/A maxdata */
1257 1, /* ao chan list */
1259 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1260 &range_pcl816, PCLx1x_RANGE,
1263 0x3fff, /* 14 bit card */
1270 static struct comedi_driver pcl816_driver = {
1271 .driver_name = "pcl816",
1272 .module = THIS_MODULE,
1273 .attach = pcl816_attach,
1274 .detach = pcl816_detach,
1275 .board_name = &boardtypes[0].name,
1276 .num_names = ARRAY_SIZE(boardtypes),
1277 .offset = sizeof(struct pcl816_board),
1279 module_comedi_driver(pcl816_driver);
1281 MODULE_AUTHOR("Comedi http://www.comedi.org");
1282 MODULE_DESCRIPTION("Comedi low-level driver");
1283 MODULE_LICENSE("GPL");