2 comedi/drivers/pcl818.c
4 Author: Michal Dobes <dobes@tesnet.cz>
6 hardware driver for Advantech cards:
7 card: PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818, PCL-718
8 driver: pcl818l, pcl818h, pcl818hd, pcl818hg, pcl818, pcl718
12 Description: Advantech PCL-818 cards, PCL-718
13 Author: Michal Dobes <dobes@tesnet.cz>
14 Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
15 PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
19 All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
20 Differences are only at maximal sample speed, range list and FIFO
22 The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
23 only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
24 PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
25 but this code is untested.
26 A word or two about DMA. Driver support DMA operations at two ways:
27 1) DMA uses two buffers and after one is filled then is generated
28 INT and DMA restart with second buffer. With this mode I'm unable run
29 more that 80Ksamples/secs without data dropouts on K6/233.
30 2) DMA uses one buffer and run in autoinit mode and the data are
31 from DMA buffer moved on the fly with 2kHz interrupts from RTC.
32 This mode is used if the interrupt 8 is available for allocation.
33 If not, then first DMA mode is used. With this I can run at
34 full speed one card (100ksamples/secs) or two cards with
35 60ksamples/secs each (more is problem on account of ISA limitations).
36 To use this mode you must have compiled kernel with disabled
37 "Enhanced Real Time Clock Support".
38 Maybe you can have problems if you use xntpd or similar.
39 If you've data dropouts with DMA mode 2 then:
41 b) switch text mode console to fb.
45 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
46 [2] - DMA (0=disable, 1, 3)
47 [3] - 0, 10=10MHz clock for 8254
48 1= 1MHz clock for 8254
49 [4] - 0, 5=A/D input -5V.. +5V
50 1, 10=A/D input -10V..+10V
51 [5] - 0, 5=D/A output 0-5V (internal reference -5V)
52 1, 10=D/A output 0-10V (internal reference -10V)
53 2 =D/A output unknown (external reference)
55 Options for PCL-818, PCL-818H:
57 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
58 [2] - DMA (0=disable, 1, 3)
59 [3] - 0, 10=10MHz clock for 8254
60 1= 1MHz clock for 8254
61 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
62 1, 10=D/A output 0-10V (internal reference -10V)
63 2 =D/A output unknown (external reference)
65 Options for PCL-818HD, PCL-818HG:
67 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
68 [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
69 1=use DMA ch 1, 3=use DMA ch 3)
70 [3] - 0, 10=10MHz clock for 8254
71 1= 1MHz clock for 8254
72 [4] - 0, 5=D/A output 0-5V (internal reference -5V)
73 1, 10=D/A output 0-10V (internal reference -10V)
74 2 =D/A output unknown (external reference)
78 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
79 [2] - DMA (0=disable, 1, 3)
80 [3] - 0, 10=10MHz clock for 8254
81 1= 1MHz clock for 8254
82 [4] - 0=A/D Range is +/-10V
87 5= user defined bipolar
92 10= user defined unipolar
93 [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
94 1, 10=D/A outputs 0-10V (internal reference -10V)
95 2=D/A outputs unknown (external reference)
96 [6] - 0, 60=max 60kHz A/D sampling
97 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
101 #include <linux/module.h>
102 #include <linux/gfp.h>
103 #include <linux/delay.h>
104 #include <linux/io.h>
105 #include <linux/interrupt.h>
108 #include "../comedidev.h"
110 #include "comedi_fc.h"
113 /* boards constants */
115 #define boardPCL818L 0
116 #define boardPCL818H 1
117 #define boardPCL818HD 2
118 #define boardPCL818HG 3
119 #define boardPCL818 4
120 #define boardPCL718 5
122 /* W: clear INT request */
123 #define PCL818_CLRINT 8
124 /* R: return status byte */
125 #define PCL818_STATUS 8
126 /* R: A/D high byte W: A/D range control */
127 #define PCL818_RANGE 1
128 /* R: next mux scan channel W: mux scan channel & range control pointer */
130 /* R/W: operation control register */
131 #define PCL818_CONTROL 9
132 /* W: counter enable */
133 #define PCL818_CNTENABLE 10
135 /* R: low byte of A/D W: soft A/D trigger */
136 #define PCL818_AD_LO 0
137 /* R: high byte of A/D W: A/D range control */
138 #define PCL818_AD_HI 1
139 /* W: D/A low&high byte */
140 #define PCL818_DA_LO 4
141 #define PCL818_DA_HI 5
142 /* R: low&high byte of DI */
143 #define PCL818_DI_LO 3
144 #define PCL818_DI_HI 11
145 /* W: low&high byte of DO */
146 #define PCL818_DO_LO 3
147 #define PCL818_DO_HI 11
148 /* W: PCL718 second D/A */
149 #define PCL718_DA2_LO 6
150 #define PCL718_DA2_HI 7
152 #define PCL818_TIMER_BASE 0x0c
154 /* W: fifo enable/disable */
155 #define PCL818_FI_ENABLE 6
156 /* W: fifo interrupt clear */
157 #define PCL818_FI_INTCLR 20
158 /* W: fifo interrupt clear */
159 #define PCL818_FI_FLUSH 25
161 #define PCL818_FI_STATUS 25
162 /* R: one record from FIFO */
163 #define PCL818_FI_DATALO 23
164 #define PCL818_FI_DATAHI 23
166 /* type of interrupt handler */
167 #define INT_TYPE_AI1_INT 1
168 #define INT_TYPE_AI1_DMA 2
169 #define INT_TYPE_AI1_FIFO 3
170 #define INT_TYPE_AI3_INT 4
171 #define INT_TYPE_AI3_DMA 5
172 #define INT_TYPE_AI3_FIFO 6
174 #define MAGIC_DMA_WORD 0x5a5a
176 static const struct comedi_lrange range_pcl818h_ai = {
190 static const struct comedi_lrange range_pcl818hg_ai = {
207 static const struct comedi_lrange range_pcl818l_l_ai = {
216 static const struct comedi_lrange range_pcl818l_h_ai = {
225 static const struct comedi_lrange range718_bipolar1 = {
231 static const struct comedi_lrange range718_bipolar0_5 = {
237 static const struct comedi_lrange range718_unipolar2 = {
243 static const struct comedi_lrange range718_unipolar1 = {
249 struct pcl818_board {
253 const struct comedi_lrange *ai_range_type;
254 unsigned int has_dma:1;
255 unsigned int has_fifo:1;
256 unsigned int is_818:1;
259 static const struct pcl818_board boardtypes[] = {
264 .ai_range_type = &range_pcl818l_l_ai,
271 .ai_range_type = &range_pcl818h_ai,
278 .ai_range_type = &range_pcl818h_ai,
286 .ai_range_type = &range_pcl818hg_ai,
294 .ai_range_type = &range_pcl818h_ai,
301 .ai_range_type = &range_unipolar5,
306 .ai_range_type = &range_pcl818h_ai,
312 struct pcl818_private {
313 unsigned int dma; /* used DMA, 0=don't use DMA */
314 unsigned int dmapages;
315 unsigned int hwdmasize;
316 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
317 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
318 int next_dma_buf; /* which DMA buffer will be used next round */
319 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
320 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
321 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
322 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
323 int ai_mode; /* who now uses IRQ - 1=AI1 int, 2=AI1 dma, 3=AI3 int, 4AI3 dma */
324 int ai_act_scan; /* how many scans we finished */
325 int ai_act_chan; /* actual position in actual scan */
326 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
327 unsigned int act_chanlist_len; /* how long is actual MUX list */
328 unsigned int act_chanlist_pos; /* actual position in MUX list */
329 unsigned int ai_data_len; /* len of data buffer */
330 unsigned int ao_readback[2];
331 unsigned int divisor1;
332 unsigned int divisor2;
333 unsigned int usefifo:1;
334 unsigned int ai_cmd_running:1;
335 unsigned int irq_was_now_closed:1;
336 unsigned int neverending_ai:1;
339 static const unsigned int muxonechan[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, /* used for gain list programming */
340 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
343 static void setup_channel_list(struct comedi_device *dev,
344 struct comedi_subdevice *s,
345 unsigned int *chanlist, unsigned int n_chan,
346 unsigned int seglen);
347 static int check_channel_list(struct comedi_device *dev,
348 struct comedi_subdevice *s,
349 unsigned int *chanlist, unsigned int n_chan);
351 static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
353 struct pcl818_private *devpriv = dev->private;
354 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
356 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
357 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
361 i8254_write(timer_base, 0, 2, devpriv->divisor2);
362 i8254_write(timer_base, 0, 1, devpriv->divisor1);
366 static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
367 struct comedi_subdevice *s,
372 val = inb(dev->iobase + PCL818_AD_HI) << 8;
373 val |= inb(dev->iobase + PCL818_AD_LO);
378 return (val >> 4) & s->maxdata;
381 static int pcl818_ai_eoc(struct comedi_device *dev,
382 struct comedi_subdevice *s,
383 struct comedi_insn *insn,
384 unsigned long context)
388 status = inb(dev->iobase + PCL818_STATUS);
394 static int pcl818_ai_insn_read(struct comedi_device *dev,
395 struct comedi_subdevice *s,
396 struct comedi_insn *insn, unsigned int *data)
401 /* software trigger, DMA and INT off */
402 outb(0, dev->iobase + PCL818_CONTROL);
405 outb(muxonechan[CR_CHAN(insn->chanspec)], dev->iobase + PCL818_MUX);
408 outb(CR_RANGE(insn->chanspec), dev->iobase + PCL818_RANGE);
410 for (n = 0; n < insn->n; n++) {
412 /* clear INT (conversion end) flag */
413 outb(0, dev->iobase + PCL818_CLRINT);
415 /* start conversion */
416 outb(0, dev->iobase + PCL818_AD_LO);
418 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
420 /* clear INT (conversion end) flag */
421 outb(0, dev->iobase + PCL818_CLRINT);
425 data[n] = pcl818_ai_get_sample(dev, s, NULL);
431 static int pcl818_ao_insn_read(struct comedi_device *dev,
432 struct comedi_subdevice *s,
433 struct comedi_insn *insn, unsigned int *data)
435 struct pcl818_private *devpriv = dev->private;
437 int chan = CR_CHAN(insn->chanspec);
439 for (n = 0; n < insn->n; n++)
440 data[n] = devpriv->ao_readback[chan];
445 static int pcl818_ao_insn_write(struct comedi_device *dev,
446 struct comedi_subdevice *s,
447 struct comedi_insn *insn, unsigned int *data)
449 struct pcl818_private *devpriv = dev->private;
451 int chan = CR_CHAN(insn->chanspec);
453 for (n = 0; n < insn->n; n++) {
454 devpriv->ao_readback[chan] = data[n];
455 outb((data[n] & 0x000f) << 4, dev->iobase +
456 (chan ? PCL718_DA2_LO : PCL818_DA_LO));
457 outb((data[n] & 0x0ff0) >> 4, dev->iobase +
458 (chan ? PCL718_DA2_HI : PCL818_DA_HI));
464 static int pcl818_di_insn_bits(struct comedi_device *dev,
465 struct comedi_subdevice *s,
466 struct comedi_insn *insn, unsigned int *data)
468 data[1] = inb(dev->iobase + PCL818_DI_LO) |
469 (inb(dev->iobase + PCL818_DI_HI) << 8);
474 static int pcl818_do_insn_bits(struct comedi_device *dev,
475 struct comedi_subdevice *s,
476 struct comedi_insn *insn,
479 if (comedi_dio_update_state(s, data)) {
480 outb(s->state & 0xff, dev->iobase + PCL818_DO_LO);
481 outb((s->state >> 8), dev->iobase + PCL818_DO_HI);
489 static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
491 struct comedi_device *dev = d;
492 struct pcl818_private *devpriv = dev->private;
493 struct comedi_subdevice *s = dev->read_subdev;
494 struct comedi_cmd *cmd = &s->async->cmd;
496 int timeout = 50; /* wait max 50us */
499 if (inb(dev->iobase + PCL818_STATUS) & 0x10)
503 outb(0, dev->iobase + PCL818_STATUS); /* clear INT request */
504 comedi_error(dev, "A/D mode1/3 IRQ without DRDY!");
506 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
507 comedi_event(dev, s);
511 comedi_buf_put(s->async, pcl818_ai_get_sample(dev, s, &chan));
512 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
514 if (chan != devpriv->act_chanlist[devpriv->act_chanlist_pos]) {
515 dev_dbg(dev->class_dev,
516 "A/D mode1/3 IRQ - channel dropout %x!=%x !\n",
518 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
520 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
521 comedi_event(dev, s);
524 devpriv->act_chanlist_pos++;
525 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
526 devpriv->act_chanlist_pos = 0;
528 s->async->cur_chan++;
529 if (s->async->cur_chan >= cmd->chanlist_len) {
530 s->async->cur_chan = 0;
531 devpriv->ai_act_scan--;
534 if (!devpriv->neverending_ai) {
535 if (devpriv->ai_act_scan == 0) { /* all data sampled */
537 s->async->events |= COMEDI_CB_EOA;
540 comedi_event(dev, s);
544 static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
546 struct comedi_device *dev = d;
547 struct pcl818_private *devpriv = dev->private;
548 struct comedi_subdevice *s = dev->read_subdev;
549 struct comedi_cmd *cmd = &s->async->cmd;
554 disable_dma(devpriv->dma);
555 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
556 if ((devpriv->dma_runs_to_end) > -1 || devpriv->neverending_ai) { /* switch dma bufs */
557 set_dma_mode(devpriv->dma, DMA_MODE_READ);
558 flags = claim_dma_lock();
559 set_dma_addr(devpriv->dma,
560 devpriv->hwdmaptr[devpriv->next_dma_buf]);
561 if (devpriv->dma_runs_to_end || devpriv->neverending_ai)
562 set_dma_count(devpriv->dma, devpriv->hwdmasize);
564 set_dma_count(devpriv->dma, devpriv->last_dma_run);
565 release_dma_lock(flags);
566 enable_dma(devpriv->dma);
569 devpriv->dma_runs_to_end--;
570 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
571 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
573 len = devpriv->hwdmasize >> 1;
576 for (i = 0; i < len; i++) {
577 if ((ptr[bufptr] & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
578 dev_dbg(dev->class_dev,
579 "A/D mode1/3 DMA - channel dropout %d(card)!=%d(chanlist) at %d !\n",
581 devpriv->act_chanlist[devpriv->act_chanlist_pos],
582 devpriv->act_chanlist_pos);
584 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
585 comedi_event(dev, s);
589 comedi_buf_put(s->async, ptr[bufptr++] >> 4); /* get one sample */
591 devpriv->act_chanlist_pos++;
592 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
593 devpriv->act_chanlist_pos = 0;
595 s->async->cur_chan++;
596 if (s->async->cur_chan >= cmd->chanlist_len) {
597 s->async->cur_chan = 0;
598 devpriv->ai_act_scan--;
601 if (!devpriv->neverending_ai)
602 if (devpriv->ai_act_scan == 0) { /* all data sampled */
604 s->async->events |= COMEDI_CB_EOA;
605 comedi_event(dev, s);
611 comedi_event(dev, s);
615 static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
617 struct comedi_device *dev = d;
618 struct pcl818_private *devpriv = dev->private;
619 struct comedi_subdevice *s = dev->read_subdev;
620 struct comedi_cmd *cmd = &s->async->cmd;
624 outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
626 lo = inb(dev->iobase + PCL818_FI_STATUS);
629 comedi_error(dev, "A/D mode1/3 FIFO overflow!");
631 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
632 comedi_event(dev, s);
637 comedi_error(dev, "A/D mode1/3 FIFO interrupt without data!");
639 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
640 comedi_event(dev, s);
649 for (i = 0; i < len; i++) {
650 lo = inb(dev->iobase + PCL818_FI_DATALO);
651 if ((lo & 0xf) != devpriv->act_chanlist[devpriv->act_chanlist_pos]) { /* dropout! */
652 dev_dbg(dev->class_dev,
653 "A/D mode1/3 FIFO - channel dropout %d!=%d !\n",
655 devpriv->act_chanlist[devpriv->act_chanlist_pos]);
657 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
658 comedi_event(dev, s);
662 comedi_buf_put(s->async, (lo >> 4) | (inb(dev->iobase + PCL818_FI_DATAHI) << 4)); /* get one sample */
664 devpriv->act_chanlist_pos++;
665 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
666 devpriv->act_chanlist_pos = 0;
668 s->async->cur_chan++;
669 if (s->async->cur_chan >= cmd->chanlist_len) {
670 s->async->cur_chan = 0;
671 devpriv->ai_act_scan--;
674 if (!devpriv->neverending_ai)
675 if (devpriv->ai_act_scan == 0) { /* all data sampled */
677 s->async->events |= COMEDI_CB_EOA;
678 comedi_event(dev, s);
684 comedi_event(dev, s);
688 static irqreturn_t interrupt_pcl818(int irq, void *d)
690 struct comedi_device *dev = d;
691 struct pcl818_private *devpriv = dev->private;
692 struct comedi_subdevice *s = dev->read_subdev;
694 if (!dev->attached) {
695 comedi_error(dev, "premature interrupt");
699 if (devpriv->ai_cmd_running && devpriv->irq_was_now_closed) {
700 if ((devpriv->neverending_ai || (!devpriv->neverending_ai &&
701 devpriv->ai_act_scan > 0)) &&
702 (devpriv->ai_mode == INT_TYPE_AI1_DMA ||
703 devpriv->ai_mode == INT_TYPE_AI3_DMA)) {
704 /* The cleanup from ai_cancel() has been delayed
705 until now because the card doesn't seem to like
706 being reprogrammed while a DMA transfer is in
709 devpriv->ai_act_scan = 0;
710 devpriv->neverending_ai = 0;
714 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
719 switch (devpriv->ai_mode) {
720 case INT_TYPE_AI1_DMA:
721 case INT_TYPE_AI3_DMA:
722 return interrupt_pcl818_ai_mode13_dma(irq, d);
723 case INT_TYPE_AI1_INT:
724 case INT_TYPE_AI3_INT:
725 return interrupt_pcl818_ai_mode13_int(irq, d);
726 case INT_TYPE_AI1_FIFO:
727 case INT_TYPE_AI3_FIFO:
728 return interrupt_pcl818_ai_mode13_fifo(irq, d);
733 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
735 if (!devpriv->ai_cmd_running || !devpriv->ai_mode) {
736 comedi_error(dev, "bad IRQ!");
740 comedi_error(dev, "IRQ from unknown source!");
744 static void pcl818_ai_mode13dma_int(int mode, struct comedi_device *dev,
745 struct comedi_subdevice *s)
747 struct pcl818_private *devpriv = dev->private;
748 struct comedi_cmd *cmd = &s->async->cmd;
752 disable_dma(devpriv->dma); /* disable dma */
753 bytes = devpriv->hwdmasize;
754 if (!devpriv->neverending_ai) {
755 bytes = cmd->chanlist_len * cmd->stop_arg * sizeof(short);
756 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
757 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
758 devpriv->dma_runs_to_end--;
759 if (devpriv->dma_runs_to_end >= 0)
760 bytes = devpriv->hwdmasize;
763 devpriv->next_dma_buf = 0;
764 set_dma_mode(devpriv->dma, DMA_MODE_READ);
765 flags = claim_dma_lock();
766 clear_dma_ff(devpriv->dma);
767 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
768 set_dma_count(devpriv->dma, bytes);
769 release_dma_lock(flags);
770 enable_dma(devpriv->dma);
773 devpriv->ai_mode = INT_TYPE_AI1_DMA;
774 outb(0x87 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Pacer+IRQ+DMA */
776 devpriv->ai_mode = INT_TYPE_AI3_DMA;
777 outb(0x86 | (dev->irq << 4), dev->iobase + PCL818_CONTROL); /* Ext trig+IRQ+DMA */
781 static int pcl818_ai_cmd_mode(int mode, struct comedi_device *dev,
782 struct comedi_subdevice *s)
784 struct pcl818_private *devpriv = dev->private;
785 struct comedi_cmd *cmd = &s->async->cmd;
788 if (devpriv->ai_cmd_running)
791 pcl818_start_pacer(dev, false);
793 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
796 setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len, seglen);
800 devpriv->ai_act_scan = cmd->stop_arg;
801 devpriv->ai_act_chan = 0;
802 devpriv->ai_cmd_running = 1;
803 devpriv->irq_was_now_closed = 0;
804 devpriv->act_chanlist_pos = 0;
805 devpriv->dma_runs_to_end = 0;
807 outb(0, dev->iobase + PCL818_CNTENABLE); /* enable pacer */
809 switch (devpriv->dma) {
812 pcl818_ai_mode13dma_int(mode, dev, s);
815 if (!devpriv->usefifo) {
818 devpriv->ai_mode = INT_TYPE_AI1_INT;
820 outb(0x83 | (dev->irq << 4),
821 dev->iobase + PCL818_CONTROL);
823 devpriv->ai_mode = INT_TYPE_AI3_INT;
825 outb(0x82 | (dev->irq << 4),
826 dev->iobase + PCL818_CONTROL);
831 outb(1, dev->iobase + PCL818_FI_ENABLE);
833 devpriv->ai_mode = INT_TYPE_AI1_FIFO;
835 outb(0x03, dev->iobase + PCL818_CONTROL);
837 devpriv->ai_mode = INT_TYPE_AI3_FIFO;
838 outb(0x02, dev->iobase + PCL818_CONTROL);
843 pcl818_start_pacer(dev, mode == 1);
848 static int check_channel_list(struct comedi_device *dev,
849 struct comedi_subdevice *s,
850 unsigned int *chanlist, unsigned int n_chan)
852 unsigned int chansegment[16];
853 unsigned int i, nowmustbechan, seglen, segpos;
855 /* correct channel and range number check itself comedi/range.c */
857 comedi_error(dev, "range/channel list is empty!");
862 /* first channel is every time ok */
863 chansegment[0] = chanlist[0];
864 /* build part of chanlist */
865 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
866 /* we detect loop, this must by finish */
868 if (chanlist[0] == chanlist[i])
871 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
872 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
873 dev_dbg(dev->class_dev,
874 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
875 i, CR_CHAN(chanlist[i]), nowmustbechan,
876 CR_CHAN(chanlist[0]));
879 /* well, this is next correct channel in list */
880 chansegment[i] = chanlist[i];
883 /* check whole chanlist */
884 for (i = 0, segpos = 0; i < n_chan; i++) {
885 if (chanlist[i] != chansegment[i % seglen]) {
886 dev_dbg(dev->class_dev,
887 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
888 i, CR_CHAN(chansegment[i]),
889 CR_RANGE(chansegment[i]),
890 CR_AREF(chansegment[i]),
891 CR_CHAN(chanlist[i % seglen]),
892 CR_RANGE(chanlist[i % seglen]),
893 CR_AREF(chansegment[i % seglen]));
894 return 0; /* chan/gain list is strange */
903 static void setup_channel_list(struct comedi_device *dev,
904 struct comedi_subdevice *s,
905 unsigned int *chanlist, unsigned int n_chan,
908 struct pcl818_private *devpriv = dev->private;
911 devpriv->act_chanlist_len = seglen;
912 devpriv->act_chanlist_pos = 0;
914 for (i = 0; i < seglen; i++) { /* store range list to card */
915 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
916 outb(muxonechan[CR_CHAN(chanlist[i])], dev->iobase + PCL818_MUX); /* select channel */
917 outb(CR_RANGE(chanlist[i]), dev->iobase + PCL818_RANGE); /* select gain */
922 /* select channel interval to scan */
923 outb(devpriv->act_chanlist[0] | (devpriv->act_chanlist[seglen -
925 dev->iobase + PCL818_MUX);
928 static int check_single_ended(unsigned int port)
930 if (inb(port + PCL818_STATUS) & 0x20)
935 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
936 struct comedi_cmd *cmd)
938 const struct pcl818_board *board = comedi_board(dev);
939 struct pcl818_private *devpriv = dev->private;
943 /* Step 1 : check if triggers are trivially valid */
945 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
946 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
947 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
948 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
949 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
954 /* Step 2a : make sure trigger sources are unique */
956 err |= cfc_check_trigger_is_unique(cmd->convert_src);
957 err |= cfc_check_trigger_is_unique(cmd->stop_src);
959 /* Step 2b : and mutually compatible */
964 /* Step 3: check if arguments are trivially valid */
966 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
967 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
969 if (cmd->convert_src == TRIG_TIMER)
970 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
973 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
975 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
977 if (cmd->stop_src == TRIG_COUNT)
978 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
980 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
985 /* step 4: fix up any arguments */
987 if (cmd->convert_src == TRIG_TIMER) {
988 tmp = cmd->convert_arg;
989 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
992 &cmd->convert_arg, cmd->flags);
993 if (cmd->convert_arg < board->ns_min)
994 cmd->convert_arg = board->ns_min;
995 if (tmp != cmd->convert_arg)
1002 /* step 5: complain about special chanlist considerations */
1004 if (cmd->chanlist) {
1005 if (!check_channel_list(dev, s, cmd->chanlist,
1007 return 5; /* incorrect channels list */
1013 static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
1015 struct pcl818_private *devpriv = dev->private;
1016 struct comedi_cmd *cmd = &s->async->cmd;
1019 devpriv->ai_data_len = s->async->prealloc_bufsz;
1021 if (cmd->stop_src == TRIG_COUNT)
1022 devpriv->neverending_ai = 0;
1024 devpriv->neverending_ai = 1;
1026 if (cmd->scan_begin_src == TRIG_FOLLOW) { /* mode 1, 3 */
1027 if (cmd->convert_src == TRIG_TIMER) { /* mode 1 */
1028 retval = pcl818_ai_cmd_mode(1, dev, s);
1031 if (cmd->convert_src == TRIG_EXT) { /* mode 3 */
1032 return pcl818_ai_cmd_mode(3, dev, s);
1039 static int pcl818_ai_cancel(struct comedi_device *dev,
1040 struct comedi_subdevice *s)
1042 struct pcl818_private *devpriv = dev->private;
1044 if (devpriv->ai_cmd_running) {
1045 devpriv->irq_was_now_closed = 1;
1047 switch (devpriv->ai_mode) {
1048 case INT_TYPE_AI1_DMA:
1049 case INT_TYPE_AI3_DMA:
1050 if (devpriv->neverending_ai ||
1051 (!devpriv->neverending_ai &&
1052 devpriv->ai_act_scan > 0)) {
1053 /* wait for running dma transfer to end, do cleanup in interrupt */
1056 disable_dma(devpriv->dma);
1057 case INT_TYPE_AI1_INT:
1058 case INT_TYPE_AI3_INT:
1059 case INT_TYPE_AI1_FIFO:
1060 case INT_TYPE_AI3_FIFO:
1061 outb(inb(dev->iobase + PCL818_CONTROL) & 0x73, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1063 pcl818_start_pacer(dev, false);
1064 outb(0, dev->iobase + PCL818_AD_LO);
1065 pcl818_ai_get_sample(dev, s, NULL);
1066 outb(0, dev->iobase + PCL818_CLRINT); /* clear INT request */
1067 outb(0, dev->iobase + PCL818_CONTROL); /* Stop A/D */
1068 if (devpriv->usefifo) { /* FIFO shutdown */
1069 outb(0, dev->iobase + PCL818_FI_INTCLR);
1070 outb(0, dev->iobase + PCL818_FI_FLUSH);
1071 outb(0, dev->iobase + PCL818_FI_ENABLE);
1073 devpriv->ai_cmd_running = 0;
1074 devpriv->neverending_ai = 0;
1075 devpriv->ai_mode = 0;
1076 devpriv->irq_was_now_closed = 0;
1085 static void pcl818_reset(struct comedi_device *dev)
1087 const struct pcl818_board *board = comedi_board(dev);
1088 struct pcl818_private *devpriv = dev->private;
1089 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
1091 if (devpriv->usefifo) { /* FIFO shutdown */
1092 outb(0, dev->iobase + PCL818_FI_INTCLR);
1093 outb(0, dev->iobase + PCL818_FI_FLUSH);
1094 outb(0, dev->iobase + PCL818_FI_ENABLE);
1096 outb(0, dev->iobase + PCL818_DA_LO); /* DAC=0V */
1097 outb(0, dev->iobase + PCL818_DA_HI);
1099 outb(0, dev->iobase + PCL818_DO_HI); /* DO=$0000 */
1100 outb(0, dev->iobase + PCL818_DO_LO);
1102 outb(0, dev->iobase + PCL818_CONTROL);
1103 outb(0, dev->iobase + PCL818_CNTENABLE);
1104 outb(0, dev->iobase + PCL818_MUX);
1105 outb(0, dev->iobase + PCL818_CLRINT);
1108 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1109 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1110 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1112 if (board->is_818) {
1113 outb(0, dev->iobase + PCL818_RANGE);
1115 outb(0, dev->iobase + PCL718_DA2_LO);
1116 outb(0, dev->iobase + PCL718_DA2_HI);
1120 static void pcl818_set_ai_range_table(struct comedi_device *dev,
1121 struct comedi_subdevice *s,
1122 struct comedi_devconfig *it)
1124 const struct pcl818_board *board = comedi_board(dev);
1126 /* default to the range table from the boardinfo */
1127 s->range_table = board->ai_range_type;
1129 /* now check the user config option based on the boardtype */
1130 if (board->is_818) {
1131 if (it->options[4] == 1 || it->options[4] == 10) {
1132 /* secondary range list jumper selectable */
1133 s->range_table = &range_pcl818l_h_ai;
1136 switch (it->options[4]) {
1138 s->range_table = &range_bipolar10;
1141 s->range_table = &range_bipolar5;
1144 s->range_table = &range_bipolar2_5;
1147 s->range_table = &range718_bipolar1;
1150 s->range_table = &range718_bipolar0_5;
1153 s->range_table = &range_unipolar10;
1156 s->range_table = &range_unipolar5;
1159 s->range_table = &range718_unipolar2;
1162 s->range_table = &range718_unipolar1;
1165 s->range_table = &range_unknown;
1171 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1173 const struct pcl818_board *board = comedi_board(dev);
1174 struct pcl818_private *devpriv;
1175 struct comedi_subdevice *s;
1179 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1183 /* should we use the FIFO? */
1184 if (board->has_fifo && it->options[2] == -1)
1185 devpriv->usefifo = 1;
1187 ret = comedi_request_region(dev, it->options[0],
1188 devpriv->usefifo ? 0x20 : 0x10);
1192 /* we can use IRQ 2-7 for async command support */
1193 if (it->options[1] >= 2 && it->options[1] <= 7) {
1194 ret = request_irq(it->options[1], interrupt_pcl818, 0,
1195 dev->board_name, dev);
1197 dev->irq = it->options[1];
1200 /* we need an IRQ to do DMA on channel 3 or 1 */
1201 if (dev->irq && board->has_dma &&
1202 (it->options[2] == 3 || it->options[2] == 1)) {
1203 ret = request_dma(it->options[2], dev->board_name);
1205 dev_err(dev->class_dev,
1206 "unable to request DMA channel %d\n",
1210 devpriv->dma = it->options[2];
1212 devpriv->dmapages = 2; /* we need 16KB */
1213 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
1215 for (i = 0; i < 2; i++) {
1216 unsigned long dmabuf;
1218 dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
1222 devpriv->dmabuf[i] = dmabuf;
1223 devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
1227 ret = comedi_alloc_subdevices(dev, 4);
1231 s = &dev->subdevices[0];
1232 s->type = COMEDI_SUBD_AI;
1233 s->subdev_flags = SDF_READABLE;
1234 if (check_single_ended(dev->iobase)) {
1236 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1239 s->subdev_flags |= SDF_DIFF;
1241 s->maxdata = 0x0fff;
1243 pcl818_set_ai_range_table(dev, s, it);
1245 s->insn_read = pcl818_ai_insn_read;
1247 dev->read_subdev = s;
1248 s->subdev_flags |= SDF_CMD_READ;
1249 s->len_chanlist = s->n_chan;
1250 s->do_cmdtest = ai_cmdtest;
1252 s->cancel = pcl818_ai_cancel;
1255 s = &dev->subdevices[1];
1256 if (!board->n_aochan) {
1257 s->type = COMEDI_SUBD_UNUSED;
1259 s->type = COMEDI_SUBD_AO;
1260 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1261 s->n_chan = board->n_aochan;
1262 s->maxdata = 0x0fff;
1263 s->range_table = &range_unipolar5;
1264 s->insn_read = pcl818_ao_insn_read;
1265 s->insn_write = pcl818_ao_insn_write;
1266 if (board->is_818) {
1267 if ((it->options[4] == 1) || (it->options[4] == 10))
1268 s->range_table = &range_unipolar10;
1269 if (it->options[4] == 2)
1270 s->range_table = &range_unknown;
1272 if ((it->options[5] == 1) || (it->options[5] == 10))
1273 s->range_table = &range_unipolar10;
1274 if (it->options[5] == 2)
1275 s->range_table = &range_unknown;
1279 /* Digital Input subdevice */
1280 s = &dev->subdevices[2];
1281 s->type = COMEDI_SUBD_DI;
1282 s->subdev_flags = SDF_READABLE;
1285 s->range_table = &range_digital;
1286 s->insn_bits = pcl818_di_insn_bits;
1288 /* Digital Output subdevice */
1289 s = &dev->subdevices[3];
1290 s->type = COMEDI_SUBD_DO;
1291 s->subdev_flags = SDF_WRITABLE;
1294 s->range_table = &range_digital;
1295 s->insn_bits = pcl818_do_insn_bits;
1297 /* select 1/10MHz oscilator */
1298 if ((it->options[3] == 0) || (it->options[3] == 10))
1299 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
1301 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
1303 /* max sampling speed */
1304 devpriv->ns_min = board->ns_min;
1306 if (!board->is_818) {
1307 if ((it->options[6] == 1) || (it->options[6] == 100))
1308 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1316 static void pcl818_detach(struct comedi_device *dev)
1318 struct pcl818_private *devpriv = dev->private;
1321 pcl818_ai_cancel(dev, dev->read_subdev);
1324 free_dma(devpriv->dma);
1325 if (devpriv->dmabuf[0])
1326 free_pages(devpriv->dmabuf[0], devpriv->dmapages);
1327 if (devpriv->dmabuf[1])
1328 free_pages(devpriv->dmabuf[1], devpriv->dmapages);
1330 comedi_legacy_detach(dev);
1333 static struct comedi_driver pcl818_driver = {
1334 .driver_name = "pcl818",
1335 .module = THIS_MODULE,
1336 .attach = pcl818_attach,
1337 .detach = pcl818_detach,
1338 .board_name = &boardtypes[0].name,
1339 .num_names = ARRAY_SIZE(boardtypes),
1340 .offset = sizeof(struct pcl818_board),
1342 module_comedi_driver(pcl818_driver);
1344 MODULE_AUTHOR("Comedi http://www.comedi.org");
1345 MODULE_DESCRIPTION("Comedi low-level driver");
1346 MODULE_LICENSE("GPL");