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 <linux/module.h>
36 #include "../comedidev.h"
38 #include <linux/gfp.h>
39 #include <linux/delay.h>
41 #include <linux/interrupt.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 MAGIC_DMA_WORD 0x5a5a
88 static const struct comedi_lrange range_pcl816 = { 8, {
100 struct pcl816_board {
102 const char *name; /* board name */
103 int n_ranges; /* len of range list */
104 int n_aichan; /* num of A/D chans in diferencial mode */
105 unsigned int ai_ns_min; /* minimal allowed delay between samples (in ns) */
106 int n_aochan; /* num of D/A chans */
107 int n_dichan; /* num of DI chans */
108 int n_dochan; /* num of DO chans */
109 const struct comedi_lrange *ai_range_type; /* default A/D rangelist */
110 const struct comedi_lrange *ao_range_type; /* default D/A rangelist */
111 unsigned int io_range; /* len of IO space */
112 unsigned int IRQbits; /* allowed interrupts */
113 unsigned int DMAbits; /* allowed DMA chans */
114 int ai_maxdata; /* maxdata for A/D */
115 int ao_maxdata; /* maxdata for D/A */
116 int ai_chanlist; /* allowed len of channel list A/D */
117 int ao_chanlist; /* allowed len of channel list D/A */
118 int i8254_osc_base; /* 1/frequency of on board oscilator in ns */
121 struct pcl816_private {
123 unsigned int dma; /* used DMA, 0=don't use DMA */
124 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
125 unsigned int dmapages[2]; /* len of DMA buffers in PAGE_SIZEs */
126 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
127 unsigned int hwdmasize[2]; /* len of DMA buffers in Bytes */
128 unsigned int dmasamplsize; /* size in samples hwdmasize[0]/2 */
129 int next_dma_buf; /* which DMA buffer will be used next round */
130 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
131 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
133 unsigned int ai_scans; /* len of scanlist */
134 unsigned char ai_neverending; /* if=1, then we do neverending record (you must use cancel()) */
135 int irq_free; /* 1=have allocated IRQ */
136 int irq_blocked; /* 1=IRQ now uses any subdev */
137 int irq_was_now_closed; /* when IRQ finish, there's stored int816_mode for last interrupt */
138 int int816_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
139 struct comedi_subdevice *last_int_sub; /* ptr to subdevice which now finish */
140 int ai_act_scan; /* how many scans we finished */
141 unsigned int ai_act_chanlist[16]; /* MUX setting for actual AI operations */
142 unsigned int ai_act_chanlist_len; /* how long is actual MUX list */
143 unsigned int ai_act_chanlist_pos; /* actual position in MUX list */
144 unsigned int ai_n_chan; /* how many channels per scan */
145 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
146 struct comedi_subdevice *sub_ai; /* ptr to AI subdevice */
150 ==============================================================================
152 static int check_channel_list(struct comedi_device *dev,
153 struct comedi_subdevice *s,
154 unsigned int *chanlist, unsigned int chanlen);
155 static void setup_channel_list(struct comedi_device *dev,
156 struct comedi_subdevice *s,
157 unsigned int *chanlist, unsigned int seglen);
158 static int pcl816_ai_cancel(struct comedi_device *dev,
159 struct comedi_subdevice *s);
160 static void start_pacer(struct comedi_device *dev, int mode,
161 unsigned int divisor1, unsigned int divisor2);
163 static int pcl816_ai_cmdtest(struct comedi_device *dev,
164 struct comedi_subdevice *s,
165 struct comedi_cmd *cmd);
166 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s);
169 ==============================================================================
170 ANALOG INPUT MODE0, 816 cards, slow version
172 static int pcl816_ai_insn_read(struct comedi_device *dev,
173 struct comedi_subdevice *s,
174 struct comedi_insn *insn, unsigned int *data)
179 DPRINTK("mode 0 analog input\n");
180 /* software trigger, DMA and INT off */
181 outb(0, dev->iobase + PCL816_CONTROL);
182 /* clear INT (conversion end) flag */
183 outb(0, dev->iobase + PCL816_CLRINT);
185 /* Set the input channel */
186 outb(CR_CHAN(insn->chanspec) & 0xf, dev->iobase + PCL816_MUX);
188 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL816_RANGE);
190 for (n = 0; n < insn->n; n++) {
192 outb(0, dev->iobase + PCL816_AD_LO); /* start conversion */
196 if (!(inb(dev->iobase + PCL816_STATUS) &
197 PCL816_STATUS_DRDY_MASK)) {
198 /* return read value */
201 PCL816_AD_HI) << 8) |
202 (inb(dev->iobase + PCL816_AD_LO)));
203 /* clear INT (conversion end) flag */
204 outb(0, dev->iobase + PCL816_CLRINT);
209 /* Return timeout error */
211 comedi_error(dev, "A/D insn timeout\n");
213 /* clear INT (conversion end) flag */
214 outb(0, dev->iobase + PCL816_CLRINT);
223 ==============================================================================
224 analog input interrupt mode 1 & 3, 818 cards
225 one sample per interrupt version
227 static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
229 struct comedi_device *dev = d;
230 struct pcl816_private *devpriv = dev->private;
231 struct comedi_subdevice *s = &dev->subdevices[0];
232 unsigned char low, hi;
233 int timeout = 50; /* wait max 50us */
236 if (!(inb(dev->iobase + PCL816_STATUS) &
237 PCL816_STATUS_DRDY_MASK))
241 if (!timeout) { /* timeout, bail error */
242 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
243 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
244 pcl816_ai_cancel(dev, s);
245 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
246 comedi_event(dev, s);
252 low = inb(dev->iobase + PCL816_AD_LO);
253 hi = inb(dev->iobase + PCL816_AD_HI);
255 comedi_buf_put(s->async, (hi << 8) | low);
257 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
259 if (++devpriv->ai_act_chanlist_pos >= devpriv->ai_act_chanlist_len)
260 devpriv->ai_act_chanlist_pos = 0;
262 s->async->cur_chan++;
263 if (s->async->cur_chan >= devpriv->ai_n_chan) {
264 s->async->cur_chan = 0;
265 devpriv->ai_act_scan++;
268 if (!devpriv->ai_neverending)
269 /* all data sampled */
270 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
271 /* all data sampled */
272 pcl816_ai_cancel(dev, s);
273 s->async->events |= COMEDI_CB_EOA;
275 comedi_event(dev, s);
280 ==============================================================================
281 analog input dma mode 1 & 3, 816 cards
283 static void transfer_from_dma_buf(struct comedi_device *dev,
284 struct comedi_subdevice *s,
286 unsigned int bufptr, unsigned int len)
288 struct pcl816_private *devpriv = dev->private;
291 s->async->events = 0;
293 for (i = 0; i < len; i++) {
295 comedi_buf_put(s->async, ptr[bufptr++]);
297 if (++devpriv->ai_act_chanlist_pos >=
298 devpriv->ai_act_chanlist_len) {
299 devpriv->ai_act_chanlist_pos = 0;
302 s->async->cur_chan++;
303 if (s->async->cur_chan >= devpriv->ai_n_chan) {
304 s->async->cur_chan = 0;
305 devpriv->ai_act_scan++;
308 if (!devpriv->ai_neverending)
309 /* all data sampled */
310 if (devpriv->ai_act_scan >= devpriv->ai_scans) {
311 pcl816_ai_cancel(dev, s);
312 s->async->events |= COMEDI_CB_EOA;
313 s->async->events |= COMEDI_CB_BLOCK;
318 comedi_event(dev, s);
321 static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
323 struct comedi_device *dev = d;
324 struct pcl816_private *devpriv = dev->private;
325 struct comedi_subdevice *s = &dev->subdevices[0];
326 int len, bufptr, this_dma_buf;
327 unsigned long dma_flags;
330 disable_dma(devpriv->dma);
331 this_dma_buf = devpriv->next_dma_buf;
333 /* switch dma bufs */
334 if ((devpriv->dma_runs_to_end > -1) || devpriv->ai_neverending) {
336 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
337 set_dma_mode(devpriv->dma, DMA_MODE_READ);
338 dma_flags = claim_dma_lock();
339 /* clear_dma_ff (devpriv->dma); */
340 set_dma_addr(devpriv->dma,
341 devpriv->hwdmaptr[devpriv->next_dma_buf]);
342 if (devpriv->dma_runs_to_end) {
343 set_dma_count(devpriv->dma,
344 devpriv->hwdmasize[devpriv->
347 set_dma_count(devpriv->dma, devpriv->last_dma_run);
349 release_dma_lock(dma_flags);
350 enable_dma(devpriv->dma);
353 devpriv->dma_runs_to_end--;
354 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
356 ptr = (unsigned short *)devpriv->dmabuf[this_dma_buf];
358 len = (devpriv->hwdmasize[0] >> 1) - devpriv->ai_poll_ptr;
359 bufptr = devpriv->ai_poll_ptr;
360 devpriv->ai_poll_ptr = 0;
362 transfer_from_dma_buf(dev, s, ptr, bufptr, len);
367 ==============================================================================
370 static irqreturn_t interrupt_pcl816(int irq, void *d)
372 struct comedi_device *dev = d;
373 struct pcl816_private *devpriv = dev->private;
377 if (!dev->attached) {
378 comedi_error(dev, "premature interrupt");
382 switch (devpriv->int816_mode) {
383 case INT_TYPE_AI1_DMA:
384 case INT_TYPE_AI3_DMA:
385 return interrupt_pcl816_ai_mode13_dma(irq, d);
386 case INT_TYPE_AI1_INT:
387 case INT_TYPE_AI3_INT:
388 return interrupt_pcl816_ai_mode13_int(irq, d);
391 outb(0, dev->iobase + PCL816_CLRINT); /* clear INT request */
392 if (!dev->irq || !devpriv->irq_free || !devpriv->irq_blocked ||
393 !devpriv->int816_mode) {
394 if (devpriv->irq_was_now_closed) {
395 devpriv->irq_was_now_closed = 0;
396 /* comedi_error(dev,"last IRQ.."); */
399 comedi_error(dev, "bad IRQ!");
402 comedi_error(dev, "IRQ from unknown source!");
407 ==============================================================================
410 static void pcl816_cmdtest_out(int e, struct comedi_cmd *cmd)
412 printk(KERN_INFO "pcl816 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
413 cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
414 printk(KERN_INFO "pcl816 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
415 cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
416 printk(KERN_INFO "pcl816 e=%d stopsrc=%x scanend=%x\n", e,
417 cmd->stop_src, cmd->scan_end_src);
418 printk(KERN_INFO "pcl816 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
419 e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
423 ==============================================================================
425 static int pcl816_ai_cmdtest(struct comedi_device *dev,
426 struct comedi_subdevice *s, struct comedi_cmd *cmd)
428 const struct pcl816_board *board = comedi_board(dev);
430 int tmp, divisor1 = 0, divisor2 = 0;
432 DEBUG(printk(KERN_INFO "pcl816 pcl812_ai_cmdtest\n");
433 pcl816_cmdtest_out(-1, cmd);
436 /* Step 1 : check if triggers are trivially valid */
438 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
439 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
440 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
441 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
442 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
447 /* Step 2a : make sure trigger sources are unique */
449 err |= cfc_check_trigger_is_unique(cmd->convert_src);
450 err |= cfc_check_trigger_is_unique(cmd->stop_src);
452 /* Step 2b : and mutually compatible */
458 /* Step 3: check if arguments are trivially valid */
460 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
461 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
463 if (cmd->convert_src == TRIG_TIMER)
464 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
467 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
469 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
471 if (cmd->stop_src == TRIG_COUNT)
472 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
474 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
480 /* step 4: fix up any arguments */
481 if (cmd->convert_src == TRIG_TIMER) {
482 tmp = cmd->convert_arg;
483 i8253_cascade_ns_to_timer(board->i8254_osc_base,
484 &divisor1, &divisor2,
485 &cmd->convert_arg, cmd->flags);
486 if (cmd->convert_arg < board->ai_ns_min)
487 cmd->convert_arg = board->ai_ns_min;
488 if (tmp != cmd->convert_arg)
496 /* step 5: complain about special chanlist considerations */
499 if (!check_channel_list(dev, s, cmd->chanlist,
501 return 5; /* incorrect channels list */
507 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
509 const struct pcl816_board *board = comedi_board(dev);
510 struct pcl816_private *devpriv = dev->private;
511 unsigned int divisor1 = 0, divisor2 = 0, dma_flags, bytes, dmairq;
512 struct comedi_cmd *cmd = &s->async->cmd;
515 if (cmd->start_src != TRIG_NOW)
517 if (cmd->scan_begin_src != TRIG_FOLLOW)
519 if (cmd->scan_end_src != TRIG_COUNT)
521 if (cmd->scan_end_arg != cmd->chanlist_len)
523 /* if(cmd->chanlist_len>MAX_CHANLIST_LEN) return -EINVAL; */
524 if (devpriv->irq_blocked)
527 if (cmd->convert_src == TRIG_TIMER) {
528 if (cmd->convert_arg < board->ai_ns_min)
529 cmd->convert_arg = board->ai_ns_min;
531 i8253_cascade_ns_to_timer(board->i8254_osc_base,
532 &divisor1, &divisor2,
533 &cmd->convert_arg, cmd->flags);
535 /* PCL816 crash if any divisor is set to 1 */
546 start_pacer(dev, -1, 0, 0); /* stop pacer */
548 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
551 setup_channel_list(dev, s, cmd->chanlist, seglen);
554 devpriv->ai_n_chan = cmd->chanlist_len;
555 devpriv->ai_act_scan = 0;
556 s->async->cur_chan = 0;
557 devpriv->irq_blocked = 1;
558 devpriv->ai_poll_ptr = 0;
559 devpriv->irq_was_now_closed = 0;
561 if (cmd->stop_src == TRIG_COUNT) {
562 devpriv->ai_scans = cmd->stop_arg;
563 devpriv->ai_neverending = 0;
565 devpriv->ai_scans = 0;
566 devpriv->ai_neverending = 1;
569 /* don't we want wake up every scan? */
570 if ((cmd->flags & TRIG_WAKE_EOS)) {
572 "pl816: You wankt WAKE_EOS but I dont want handle it");
573 /* devpriv->ai_eos=1; */
574 /* if (devpriv->ai_n_chan==1) */
575 /* devpriv->dma=0; // DMA is useless for this situation */
579 bytes = devpriv->hwdmasize[0];
580 if (!devpriv->ai_neverending) {
582 bytes = s->async->cmd.chanlist_len *
583 s->async->cmd.chanlist_len *
586 /* how many DMA pages we must fill */
587 devpriv->dma_runs_to_end = bytes /
588 devpriv->hwdmasize[0];
590 /* on last dma transfer must be moved */
591 devpriv->last_dma_run = bytes % devpriv->hwdmasize[0];
592 devpriv->dma_runs_to_end--;
593 if (devpriv->dma_runs_to_end >= 0)
594 bytes = devpriv->hwdmasize[0];
596 devpriv->dma_runs_to_end = -1;
598 devpriv->next_dma_buf = 0;
599 set_dma_mode(devpriv->dma, DMA_MODE_READ);
600 dma_flags = claim_dma_lock();
601 clear_dma_ff(devpriv->dma);
602 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
603 set_dma_count(devpriv->dma, bytes);
604 release_dma_lock(dma_flags);
605 enable_dma(devpriv->dma);
608 start_pacer(dev, 1, divisor1, divisor2);
609 dmairq = ((devpriv->dma & 0x3) << 4) | (dev->irq & 0x7);
611 switch (cmd->convert_src) {
613 devpriv->int816_mode = INT_TYPE_AI1_DMA;
616 outb(0x32, dev->iobase + PCL816_CONTROL);
618 /* write irq and DMA to card */
619 outb(dmairq, dev->iobase + PCL816_STATUS);
623 devpriv->int816_mode = INT_TYPE_AI3_DMA;
625 /* Ext trig+IRQ+DMA */
626 outb(0x34, dev->iobase + PCL816_CONTROL);
628 /* write irq to card */
629 outb(dmairq, dev->iobase + PCL816_STATUS);
633 DPRINTK("pcl816 END: pcl812_ai_cmd()\n");
637 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
639 struct pcl816_private *devpriv = dev->private;
641 unsigned int top1, top2, i;
644 return 0; /* poll is valid only for DMA transfer */
646 spin_lock_irqsave(&dev->spinlock, flags);
648 for (i = 0; i < 20; i++) {
649 top1 = get_dma_residue(devpriv->dma); /* where is now DMA */
650 top2 = get_dma_residue(devpriv->dma);
655 spin_unlock_irqrestore(&dev->spinlock, flags);
659 /* where is now DMA in buffer */
660 top1 = devpriv->hwdmasize[0] - top1;
661 top1 >>= 1; /* sample position */
662 top2 = top1 - devpriv->ai_poll_ptr;
663 if (top2 < 1) { /* no new samples */
664 spin_unlock_irqrestore(&dev->spinlock, flags);
668 transfer_from_dma_buf(dev, s,
669 (unsigned short *)devpriv->dmabuf[devpriv->
671 devpriv->ai_poll_ptr, top2);
673 devpriv->ai_poll_ptr = top1; /* new buffer position */
674 spin_unlock_irqrestore(&dev->spinlock, flags);
676 return s->async->buf_write_count - s->async->buf_read_count;
680 ==============================================================================
681 cancel any mode 1-4 AI
683 static int pcl816_ai_cancel(struct comedi_device *dev,
684 struct comedi_subdevice *s)
686 struct pcl816_private *devpriv = dev->private;
688 /* DEBUG(printk("pcl816_ai_cancel()\n");) */
690 if (devpriv->irq_blocked > 0) {
691 switch (devpriv->int816_mode) {
692 case INT_TYPE_AI1_DMA:
693 case INT_TYPE_AI3_DMA:
694 disable_dma(devpriv->dma);
695 case INT_TYPE_AI1_INT:
696 case INT_TYPE_AI3_INT:
697 outb(inb(dev->iobase + PCL816_CONTROL) & 0x73,
698 dev->iobase + PCL816_CONTROL); /* Stop A/D */
700 outb(0, dev->iobase + PCL816_CONTROL); /* Stop A/D */
703 outb(0xb0, dev->iobase + PCL816_CTRCTL);
704 outb(0x70, dev->iobase + PCL816_CTRCTL);
705 outb(0, dev->iobase + PCL816_AD_LO);
706 inb(dev->iobase + PCL816_AD_LO);
707 inb(dev->iobase + PCL816_AD_HI);
709 /* clear INT request */
710 outb(0, dev->iobase + PCL816_CLRINT);
713 outb(0, dev->iobase + PCL816_CONTROL);
714 devpriv->irq_blocked = 0;
715 devpriv->irq_was_now_closed = devpriv->int816_mode;
716 devpriv->int816_mode = 0;
717 devpriv->last_int_sub = s;
723 DEBUG(printk("comedi: pcl816_ai_cancel() successful\n");)
728 ==============================================================================
731 static int pcl816_check(unsigned long iobase)
733 outb(0x00, iobase + PCL816_MUX);
735 if (inb(iobase + PCL816_MUX) != 0x00)
736 return 1; /* there isn't card */
737 outb(0x55, iobase + PCL816_MUX);
739 if (inb(iobase + PCL816_MUX) != 0x55)
740 return 1; /* there isn't card */
741 outb(0x00, iobase + PCL816_MUX);
743 outb(0x18, iobase + PCL816_CONTROL);
745 if (inb(iobase + PCL816_CONTROL) != 0x18)
746 return 1; /* there isn't card */
747 return 0; /* ok, card exist */
751 ==============================================================================
752 reset whole PCL-816 cards
754 static void pcl816_reset(struct comedi_device *dev)
756 /* outb (0, dev->iobase + PCL818_DA_LO); DAC=0V */
757 /* outb (0, dev->iobase + PCL818_DA_HI); */
759 /* outb (0, dev->iobase + PCL818_DO_HI); DO=$0000 */
760 /* outb (0, dev->iobase + PCL818_DO_LO); */
762 outb(0, dev->iobase + PCL816_CONTROL);
763 outb(0, dev->iobase + PCL816_MUX);
764 outb(0, dev->iobase + PCL816_CLRINT);
765 outb(0xb0, dev->iobase + PCL816_CTRCTL); /* Stop pacer */
766 outb(0x70, dev->iobase + PCL816_CTRCTL);
767 outb(0x30, dev->iobase + PCL816_CTRCTL);
768 outb(0, dev->iobase + PCL816_RANGE);
772 ==============================================================================
773 Start/stop pacer onboard pacer
776 start_pacer(struct comedi_device *dev, int mode, unsigned int divisor1,
777 unsigned int divisor2)
779 outb(0x32, dev->iobase + PCL816_CTRCTL);
780 outb(0xff, dev->iobase + PCL816_CTR0);
781 outb(0x00, dev->iobase + PCL816_CTR0);
784 /* set counter 2 as mode 3 */
785 outb(0xb4, dev->iobase + PCL816_CTRCTL);
786 /* set counter 1 as mode 3 */
787 outb(0x74, dev->iobase + PCL816_CTRCTL);
791 DPRINTK("mode %d, divisor1 %d, divisor2 %d\n", mode, divisor1,
793 outb(divisor2 & 0xff, dev->iobase + PCL816_CTR2);
794 outb((divisor2 >> 8) & 0xff, dev->iobase + PCL816_CTR2);
795 outb(divisor1 & 0xff, dev->iobase + PCL816_CTR1);
796 outb((divisor1 >> 8) & 0xff, dev->iobase + PCL816_CTR1);
799 /* clear pending interrupts (just in case) */
800 /* outb(0, dev->iobase + PCL816_CLRINT); */
804 ==============================================================================
805 Check if channel list from user is built correctly
806 If it's ok, then return non-zero length of repeated segment of channel list
809 check_channel_list(struct comedi_device *dev,
810 struct comedi_subdevice *s, unsigned int *chanlist,
811 unsigned int chanlen)
813 unsigned int chansegment[16];
814 unsigned int i, nowmustbechan, seglen, segpos;
816 /* correct channel and range number check itself comedi/range.c */
818 comedi_error(dev, "range/channel list is empty!");
823 /* first channel is every time ok */
824 chansegment[0] = chanlist[0];
825 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
826 /* build part of chanlist */
827 DEBUG(printk(KERN_INFO "%d. %d %d\n", i,
828 CR_CHAN(chanlist[i]),
829 CR_RANGE(chanlist[i]));)
831 /* we detect loop, this must by finish */
832 if (chanlist[0] == chanlist[i])
835 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
836 if (nowmustbechan != CR_CHAN(chanlist[i])) {
837 /* channel list isn't continuous :-( */
839 "comedi%d: pcl816: channel list must "
840 "be continuous! chanlist[%i]=%d but "
841 "must be %d or %d!\n", dev->minor,
842 i, CR_CHAN(chanlist[i]), nowmustbechan,
843 CR_CHAN(chanlist[0]));
846 /* well, this is next correct channel in list */
847 chansegment[i] = chanlist[i];
850 /* check whole chanlist */
851 for (i = 0, segpos = 0; i < chanlen; i++) {
852 DEBUG(printk("%d %d=%d %d\n",
853 CR_CHAN(chansegment[i % seglen]),
854 CR_RANGE(chansegment[i % seglen]),
855 CR_CHAN(chanlist[i]),
856 CR_RANGE(chanlist[i]));)
857 if (chanlist[i] != chansegment[i % seglen]) {
859 "comedi%d: pcl816: bad channel or range"
860 " number! chanlist[%i]=%d,%d,%d and not"
861 " %d,%d,%d!\n", dev->minor, i,
862 CR_CHAN(chansegment[i]),
863 CR_RANGE(chansegment[i]),
864 CR_AREF(chansegment[i]),
865 CR_CHAN(chanlist[i % seglen]),
866 CR_RANGE(chanlist[i % seglen]),
867 CR_AREF(chansegment[i % seglen]));
868 return 0; /* chan/gain list is strange */
875 return seglen; /* we can serve this with MUX logic */
879 ==============================================================================
880 Program scan/gain logic with channel list.
883 setup_channel_list(struct comedi_device *dev,
884 struct comedi_subdevice *s, unsigned int *chanlist,
887 struct pcl816_private *devpriv = dev->private;
890 devpriv->ai_act_chanlist_len = seglen;
891 devpriv->ai_act_chanlist_pos = 0;
893 for (i = 0; i < seglen; i++) { /* store range list to card */
894 devpriv->ai_act_chanlist[i] = CR_CHAN(chanlist[i]);
895 outb(CR_CHAN(chanlist[0]) & 0xf, dev->iobase + PCL816_MUX);
897 outb(CR_RANGE(chanlist[0]), dev->iobase + PCL816_RANGE);
901 /* select channel interval to scan */
902 outb(devpriv->ai_act_chanlist[0] |
903 (devpriv->ai_act_chanlist[seglen - 1] << 4),
904 dev->iobase + PCL816_MUX);
907 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
909 const struct pcl816_board *board = comedi_board(dev);
910 struct pcl816_private *devpriv;
912 unsigned int irq, dma;
915 struct comedi_subdevice *s;
917 ret = comedi_request_region(dev, it->options[0], board->io_range);
921 if (pcl816_check(dev->iobase)) {
922 printk(KERN_ERR ", I cann't detect board. FAIL!\n");
926 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
932 if (board->IRQbits != 0) { /* board support IRQ */
933 irq = it->options[1];
934 if (irq) { /* we want to use IRQ */
935 if (((1 << irq) & board->IRQbits) == 0) {
937 (", IRQ %u is out of allowed range, "
938 "DISABLING IT", irq);
939 irq = 0; /* Bad IRQ */
941 if (request_irq(irq, interrupt_pcl816, 0,
942 dev->board_name, dev)) {
944 (", unable to allocate IRQ %u, "
945 "DISABLING IT", irq);
946 irq = 0; /* Can't use IRQ */
948 printk(KERN_INFO ", irq=%u", irq);
955 if (irq) /* 1=we have allocated irq */
956 devpriv->irq_free = 1;
958 devpriv->irq_free = 0;
960 devpriv->irq_blocked = 0; /* number of subdevice which use IRQ */
961 devpriv->int816_mode = 0; /* mode of irq */
966 if (!devpriv->irq_free)
967 goto no_dma; /* if we haven't IRQ, we can't use DMA */
969 if (board->DMAbits != 0) { /* board support DMA */
970 dma = it->options[2];
972 goto no_dma; /* DMA disabled */
974 if (((1 << dma) & board->DMAbits) == 0) {
975 printk(", DMA is out of allowed range, FAIL!\n");
976 return -EINVAL; /* Bad DMA */
978 ret = request_dma(dma, dev->board_name);
981 ", unable to allocate DMA %u, FAIL!\n", dma);
982 return -EBUSY; /* DMA isn't free */
986 printk(KERN_INFO ", dma=%u", dma);
987 pages = 2; /* we need 16KB */
988 devpriv->dmabuf[0] = __get_dma_pages(GFP_KERNEL, pages);
990 if (!devpriv->dmabuf[0]) {
991 printk(", unable to allocate DMA buffer, FAIL!\n");
993 * maybe experiment with try_to_free_pages()
996 return -EBUSY; /* no buffer :-( */
998 devpriv->dmapages[0] = pages;
999 devpriv->hwdmaptr[0] = virt_to_bus((void *)devpriv->dmabuf[0]);
1000 devpriv->hwdmasize[0] = (1 << pages) * PAGE_SIZE;
1001 /* printk("%d %d %ld, ",devpriv->dmapages[0],devpriv->hwdmasize[0],PAGE_SIZE); */
1003 devpriv->dmabuf[1] = __get_dma_pages(GFP_KERNEL, pages);
1004 if (!devpriv->dmabuf[1]) {
1006 ", unable to allocate DMA buffer, "
1010 devpriv->dmapages[1] = pages;
1011 devpriv->hwdmaptr[1] = virt_to_bus((void *)devpriv->dmabuf[1]);
1012 devpriv->hwdmasize[1] = (1 << pages) * PAGE_SIZE;
1017 /* if (board->n_aochan > 0)
1018 subdevs[1] = COMEDI_SUBD_AO;
1019 if (board->n_dichan > 0)
1020 subdevs[2] = COMEDI_SUBD_DI;
1021 if (board->n_dochan > 0)
1022 subdevs[3] = COMEDI_SUBD_DO;
1025 ret = comedi_alloc_subdevices(dev, 1);
1029 s = &dev->subdevices[0];
1030 if (board->n_aichan > 0) {
1031 s->type = COMEDI_SUBD_AI;
1032 devpriv->sub_ai = s;
1033 dev->read_subdev = s;
1034 s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
1035 s->n_chan = board->n_aichan;
1036 s->subdev_flags |= SDF_DIFF;
1037 /* printk (", %dchans DIFF DAC - %d", s->n_chan, i); */
1038 s->maxdata = board->ai_maxdata;
1039 s->len_chanlist = board->ai_chanlist;
1040 s->range_table = board->ai_range_type;
1041 s->cancel = pcl816_ai_cancel;
1042 s->do_cmdtest = pcl816_ai_cmdtest;
1043 s->do_cmd = pcl816_ai_cmd;
1044 s->poll = pcl816_ai_poll;
1045 s->insn_read = pcl816_ai_insn_read;
1047 s->type = COMEDI_SUBD_UNUSED;
1051 case COMEDI_SUBD_AO:
1052 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1053 s->n_chan = board->n_aochan;
1054 s->maxdata = board->ao_maxdata;
1055 s->len_chanlist = board->ao_chanlist;
1056 s->range_table = board->ao_range_type;
1059 case COMEDI_SUBD_DI:
1060 s->subdev_flags = SDF_READABLE;
1061 s->n_chan = board->n_dichan;
1063 s->len_chanlist = board->n_dichan;
1064 s->range_table = &range_digital;
1067 case COMEDI_SUBD_DO:
1068 s->subdev_flags = SDF_WRITABLE;
1069 s->n_chan = board->n_dochan;
1071 s->len_chanlist = board->n_dochan;
1072 s->range_table = &range_digital;
1083 static void pcl816_detach(struct comedi_device *dev)
1085 struct pcl816_private *devpriv = dev->private;
1088 pcl816_ai_cancel(dev, devpriv->sub_ai);
1091 free_dma(devpriv->dma);
1092 if (devpriv->dmabuf[0])
1093 free_pages(devpriv->dmabuf[0], devpriv->dmapages[0]);
1094 if (devpriv->dmabuf[1])
1095 free_pages(devpriv->dmabuf[1], devpriv->dmapages[1]);
1097 comedi_legacy_detach(dev);
1100 static const struct pcl816_board boardtypes[] = {
1101 {"pcl816", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1102 &range_pcl816, PCLx1x_RANGE,
1103 0x00fc, /* IRQ mask */
1104 0x0a, /* DMA mask */
1105 0xffff, /* 16-bit card */
1106 0xffff, /* D/A maxdata */
1108 1, /* ao chan list */
1109 I8254_OSC_BASE_10MHZ},
1110 {"pcl814b", 8, 16, 10000, 1, 16, 16, &range_pcl816,
1111 &range_pcl816, PCLx1x_RANGE,
1114 0x3fff, /* 14 bit card */
1118 I8254_OSC_BASE_10MHZ},
1121 static struct comedi_driver pcl816_driver = {
1122 .driver_name = "pcl816",
1123 .module = THIS_MODULE,
1124 .attach = pcl816_attach,
1125 .detach = pcl816_detach,
1126 .board_name = &boardtypes[0].name,
1127 .num_names = ARRAY_SIZE(boardtypes),
1128 .offset = sizeof(struct pcl816_board),
1130 module_comedi_driver(pcl816_driver);
1132 MODULE_AUTHOR("Comedi http://www.comedi.org");
1133 MODULE_DESCRIPTION("Comedi low-level driver");
1134 MODULE_LICENSE("GPL");