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
125 #define PCL818_AI_LSB_REG 0x00
126 #define PCL818_AI_MSB_REG 0x01
127 #define PCL818_RANGE_REG 0x01
128 #define PCL818_MUX_REG 0x02
129 #define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
130 #define PCL818_DO_DI_LSB_REG 0x03
131 #define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
132 #define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
133 #define PCL818_STATUS_REG 0x08
134 #define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
135 #define PCL818_STATUS_INT (1 << 4)
136 #define PCL818_STATUS_MUX (1 << 5)
137 #define PCL818_STATUS_UNI (1 << 6)
138 #define PCL818_STATUS_EOC (1 << 7)
139 #define PCL818_CTRL_REG 0x09
140 #define PCL818_CTRL_DISABLE_TRIG (0 << 0)
141 #define PCL818_CTRL_SOFT_TRIG (1 << 0)
142 #define PCL818_CTRL_EXT_TRIG (2 << 0)
143 #define PCL818_CTRL_PACER_TRIG (3 << 0)
144 #define PCL818_CTRL_DMAE (1 << 2)
145 #define PCL818_CTRL_IRQ(x) ((x) << 4)
146 #define PCL818_CTRL_INTE (1 << 7)
147 #define PCL818_CNTENABLE_REG 0x0a
148 #define PCL818_CNTENABLE_PACER_ENA (0 << 0)
149 #define PCL818_CNTENABLE_PACER_TRIG0 (1 << 0)
150 #define PCL818_CNTENABLE_CNT0_EXT_CLK (0 << 1)
151 #define PCL818_CNTENABLE_CNT0_INT_CLK (1 << 1)
152 #define PCL818_DO_DI_MSB_REG 0x0b
153 #define PCL818_TIMER_BASE 0x0c
155 /* W: fifo enable/disable */
156 #define PCL818_FI_ENABLE 6
157 /* W: fifo interrupt clear */
158 #define PCL818_FI_INTCLR 20
159 /* W: fifo interrupt clear */
160 #define PCL818_FI_FLUSH 25
162 #define PCL818_FI_STATUS 25
163 /* R: one record from FIFO */
164 #define PCL818_FI_DATALO 23
165 #define PCL818_FI_DATAHI 24
167 #define MAGIC_DMA_WORD 0x5a5a
169 static const struct comedi_lrange range_pcl818h_ai = {
183 static const struct comedi_lrange range_pcl818hg_ai = {
200 static const struct comedi_lrange range_pcl818l_l_ai = {
209 static const struct comedi_lrange range_pcl818l_h_ai = {
218 static const struct comedi_lrange range718_bipolar1 = {
224 static const struct comedi_lrange range718_bipolar0_5 = {
230 static const struct comedi_lrange range718_unipolar2 = {
236 static const struct comedi_lrange range718_unipolar1 = {
242 struct pcl818_board {
246 const struct comedi_lrange *ai_range_type;
247 unsigned int has_dma:1;
248 unsigned int has_fifo:1;
249 unsigned int is_818:1;
252 static const struct pcl818_board boardtypes[] = {
257 .ai_range_type = &range_pcl818l_l_ai,
264 .ai_range_type = &range_pcl818h_ai,
271 .ai_range_type = &range_pcl818h_ai,
279 .ai_range_type = &range_pcl818hg_ai,
287 .ai_range_type = &range_pcl818h_ai,
294 .ai_range_type = &range_unipolar5,
299 .ai_range_type = &range_pcl818h_ai,
305 struct pcl818_dma_desc {
306 void *virt_addr; /* virtual address of DMA buffer */
307 dma_addr_t hw_addr; /* hardware (bus) address of DMA buffer */
308 unsigned int size; /* transfer size (in bytes) */
311 struct pcl818_private {
312 unsigned int dma; /* used DMA, 0=don't use DMA */
313 unsigned int hwdmasize;
314 struct pcl818_dma_desc dma_desc[2];
316 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
317 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
318 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
319 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
320 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
321 unsigned int act_chanlist_len; /* how long is actual MUX list */
322 unsigned int act_chanlist_pos; /* actual position in MUX list */
323 unsigned int ai_data_len; /* len of data buffer */
324 unsigned int divisor1;
325 unsigned int divisor2;
326 unsigned int usefifo:1;
327 unsigned int ai_cmd_running:1;
328 unsigned int ai_cmd_canceled:1;
331 static void pcl818_isadma_program(unsigned int dma_chan,
332 struct pcl818_dma_desc *dma)
336 flags = claim_dma_lock();
337 clear_dma_ff(dma_chan);
338 set_dma_mode(dma_chan, DMA_MODE_READ);
339 set_dma_addr(dma_chan, dma->hw_addr);
340 set_dma_count(dma_chan, dma->size);
341 enable_dma(dma_chan);
342 release_dma_lock(flags);
345 static void pcl818_isadma_disable(unsigned int dma_chan)
349 flags = claim_dma_lock();
350 disable_dma(dma_chan);
351 release_dma_lock(flags);
354 static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
356 struct pcl818_private *devpriv = dev->private;
357 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
359 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
360 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
364 i8254_write(timer_base, 0, 2, devpriv->divisor2);
365 i8254_write(timer_base, 0, 1, devpriv->divisor1);
369 static void pcl818_ai_setup_dma(struct comedi_device *dev,
370 struct comedi_subdevice *s)
372 struct pcl818_private *devpriv = dev->private;
373 struct pcl818_dma_desc *dma = &devpriv->dma_desc[0];
374 struct comedi_cmd *cmd = &s->async->cmd;
376 pcl818_isadma_disable(devpriv->dma);
377 if (cmd->stop_src == TRIG_COUNT) {
378 dma->size = cmd->stop_arg * comedi_bytes_per_scan(s);
379 devpriv->dma_runs_to_end = dma->size / devpriv->hwdmasize;
380 devpriv->last_dma_run = dma->size % devpriv->hwdmasize;
381 devpriv->dma_runs_to_end--;
382 if (devpriv->dma_runs_to_end >= 0)
383 dma->size = devpriv->hwdmasize;
385 dma->size = devpriv->hwdmasize;
388 devpriv->cur_dma = 0;
390 pcl818_isadma_program(devpriv->dma, dma);
393 static void pcl818_ai_setup_next_dma(struct comedi_device *dev,
394 struct comedi_subdevice *s)
396 struct pcl818_private *devpriv = dev->private;
397 struct comedi_cmd *cmd = &s->async->cmd;
398 struct pcl818_dma_desc *dma;
400 pcl818_isadma_disable(devpriv->dma);
401 devpriv->cur_dma = 1 - devpriv->cur_dma;
402 if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
403 /* switch dma bufs */
404 dma = &devpriv->dma_desc[devpriv->cur_dma];
405 if (devpriv->dma_runs_to_end || cmd->stop_src == TRIG_NONE)
406 dma->size = devpriv->hwdmasize;
408 dma->size = devpriv->last_dma_run;
410 pcl818_isadma_program(devpriv->dma, dma);
413 devpriv->dma_runs_to_end--;
416 static void pcl818_ai_set_chan_range(struct comedi_device *dev,
420 outb(chan, dev->iobase + PCL818_MUX_REG);
421 outb(range, dev->iobase + PCL818_RANGE_REG);
424 static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
425 unsigned int first_chan,
426 unsigned int last_chan)
428 outb(PCL818_MUX_SCAN(first_chan, last_chan),
429 dev->iobase + PCL818_MUX_REG);
432 static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
433 unsigned int *chanlist,
436 struct pcl818_private *devpriv = dev->private;
437 unsigned int first_chan = CR_CHAN(chanlist[0]);
438 unsigned int last_chan;
442 devpriv->act_chanlist_len = seglen;
443 devpriv->act_chanlist_pos = 0;
445 /* store range list to card */
446 for (i = 0; i < seglen; i++) {
447 last_chan = CR_CHAN(chanlist[i]);
448 range = CR_RANGE(chanlist[i]);
450 devpriv->act_chanlist[i] = last_chan;
452 pcl818_ai_set_chan_range(dev, last_chan, range);
457 pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
460 static void pcl818_ai_clear_eoc(struct comedi_device *dev)
462 /* writing any value clears the interrupt request */
463 outb(0, dev->iobase + PCL818_STATUS_REG);
466 static void pcl818_ai_soft_trig(struct comedi_device *dev)
468 /* writing any value triggers a software conversion */
469 outb(0, dev->iobase + PCL818_AI_LSB_REG);
472 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
473 struct comedi_subdevice *s,
478 val = inb(dev->iobase + PCL818_FI_DATALO);
479 val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
484 return (val >> 4) & s->maxdata;
487 static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
488 struct comedi_subdevice *s,
493 val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
494 val |= inb(dev->iobase + PCL818_AI_LSB_REG);
499 return (val >> 4) & s->maxdata;
502 static int pcl818_ai_eoc(struct comedi_device *dev,
503 struct comedi_subdevice *s,
504 struct comedi_insn *insn,
505 unsigned long context)
509 status = inb(dev->iobase + PCL818_STATUS_REG);
510 if (status & PCL818_STATUS_INT)
515 static bool pcl818_ai_dropout(struct comedi_device *dev,
516 struct comedi_subdevice *s,
519 struct pcl818_private *devpriv = dev->private;
520 unsigned int expected_chan;
522 expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
523 if (chan != expected_chan) {
524 dev_dbg(dev->class_dev,
525 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
526 (devpriv->dma) ? "DMA" :
527 (devpriv->usefifo) ? "FIFO" : "IRQ",
528 chan, expected_chan);
529 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
535 static bool pcl818_ai_next_chan(struct comedi_device *dev,
536 struct comedi_subdevice *s)
538 struct pcl818_private *devpriv = dev->private;
539 struct comedi_cmd *cmd = &s->async->cmd;
541 devpriv->act_chanlist_pos++;
542 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
543 devpriv->act_chanlist_pos = 0;
545 if (cmd->stop_src == TRIG_COUNT &&
546 s->async->scans_done >= cmd->stop_arg) {
547 s->async->events |= COMEDI_CB_EOA;
554 static void pcl818_handle_eoc(struct comedi_device *dev,
555 struct comedi_subdevice *s)
560 if (pcl818_ai_eoc(dev, s, NULL, 0)) {
561 dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
562 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
566 val = pcl818_ai_get_sample(dev, s, &chan);
568 if (pcl818_ai_dropout(dev, s, chan))
571 comedi_buf_write_samples(s, &val, 1);
573 pcl818_ai_next_chan(dev, s);
576 static void pcl818_handle_dma(struct comedi_device *dev,
577 struct comedi_subdevice *s)
579 struct pcl818_private *devpriv = dev->private;
580 struct pcl818_dma_desc *dma = &devpriv->dma_desc[devpriv->cur_dma];
581 unsigned short *ptr = dma->virt_addr;
582 unsigned int nsamples = comedi_bytes_to_samples(s, dma->size);
587 pcl818_ai_setup_next_dma(dev, s);
589 for (i = 0; i < nsamples; i++) {
592 val = (val >> 4) & s->maxdata;
594 if (pcl818_ai_dropout(dev, s, chan))
597 comedi_buf_write_samples(s, &val, 1);
599 if (!pcl818_ai_next_chan(dev, s))
604 static void pcl818_handle_fifo(struct comedi_device *dev,
605 struct comedi_subdevice *s)
612 status = inb(dev->iobase + PCL818_FI_STATUS);
615 dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
616 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
621 dev_err(dev->class_dev,
622 "A/D mode1/3 FIFO interrupt without data!\n");
623 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
632 for (i = 0; i < len; i++) {
633 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
635 if (pcl818_ai_dropout(dev, s, chan))
638 comedi_buf_write_samples(s, &val, 1);
640 if (!pcl818_ai_next_chan(dev, s))
645 static irqreturn_t pcl818_interrupt(int irq, void *d)
647 struct comedi_device *dev = d;
648 struct pcl818_private *devpriv = dev->private;
649 struct comedi_subdevice *s = dev->read_subdev;
650 struct comedi_cmd *cmd = &s->async->cmd;
652 if (!dev->attached || !devpriv->ai_cmd_running) {
653 pcl818_ai_clear_eoc(dev);
657 if (devpriv->ai_cmd_canceled) {
659 * The cleanup from ai_cancel() has been delayed
660 * until now because the card doesn't seem to like
661 * being reprogrammed while a DMA transfer is in
664 s->async->scans_done = cmd->stop_arg;
670 pcl818_handle_dma(dev, s);
671 else if (devpriv->usefifo)
672 pcl818_handle_fifo(dev, s);
674 pcl818_handle_eoc(dev, s);
676 pcl818_ai_clear_eoc(dev);
678 comedi_handle_events(dev, s);
682 static int check_channel_list(struct comedi_device *dev,
683 struct comedi_subdevice *s,
684 unsigned int *chanlist, unsigned int n_chan)
686 unsigned int chansegment[16];
687 unsigned int i, nowmustbechan, seglen, segpos;
689 /* correct channel and range number check itself comedi/range.c */
691 dev_err(dev->class_dev, "range/channel list is empty!\n");
696 /* first channel is every time ok */
697 chansegment[0] = chanlist[0];
698 /* build part of chanlist */
699 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
700 /* we detect loop, this must by finish */
702 if (chanlist[0] == chanlist[i])
705 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
706 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
707 dev_dbg(dev->class_dev,
708 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
709 i, CR_CHAN(chanlist[i]), nowmustbechan,
710 CR_CHAN(chanlist[0]));
713 /* well, this is next correct channel in list */
714 chansegment[i] = chanlist[i];
717 /* check whole chanlist */
718 for (i = 0, segpos = 0; i < n_chan; i++) {
719 if (chanlist[i] != chansegment[i % seglen]) {
720 dev_dbg(dev->class_dev,
721 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
722 i, CR_CHAN(chansegment[i]),
723 CR_RANGE(chansegment[i]),
724 CR_AREF(chansegment[i]),
725 CR_CHAN(chanlist[i % seglen]),
726 CR_RANGE(chanlist[i % seglen]),
727 CR_AREF(chansegment[i % seglen]));
728 return 0; /* chan/gain list is strange */
737 static int check_single_ended(unsigned int port)
739 if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
744 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
745 struct comedi_cmd *cmd)
747 const struct pcl818_board *board = dev->board_ptr;
748 struct pcl818_private *devpriv = dev->private;
752 /* Step 1 : check if triggers are trivially valid */
754 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
755 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
756 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
757 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
758 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
763 /* Step 2a : make sure trigger sources are unique */
765 err |= cfc_check_trigger_is_unique(cmd->convert_src);
766 err |= cfc_check_trigger_is_unique(cmd->stop_src);
768 /* Step 2b : and mutually compatible */
773 /* Step 3: check if arguments are trivially valid */
775 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
776 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
778 if (cmd->convert_src == TRIG_TIMER)
779 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
782 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
784 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
786 if (cmd->stop_src == TRIG_COUNT)
787 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
789 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
794 /* step 4: fix up any arguments */
796 if (cmd->convert_src == TRIG_TIMER) {
797 arg = cmd->convert_arg;
798 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
802 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
808 /* step 5: complain about special chanlist considerations */
811 if (!check_channel_list(dev, s, cmd->chanlist,
813 return 5; /* incorrect channels list */
819 static int pcl818_ai_cmd(struct comedi_device *dev,
820 struct comedi_subdevice *s)
822 struct pcl818_private *devpriv = dev->private;
823 struct comedi_cmd *cmd = &s->async->cmd;
824 unsigned int ctrl = 0;
827 if (devpriv->ai_cmd_running)
830 pcl818_start_pacer(dev, false);
832 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
835 pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
837 devpriv->ai_data_len = s->async->prealloc_bufsz;
838 devpriv->ai_cmd_running = 1;
839 devpriv->ai_cmd_canceled = 0;
840 devpriv->act_chanlist_pos = 0;
841 devpriv->dma_runs_to_end = 0;
843 if (cmd->convert_src == TRIG_TIMER)
844 ctrl |= PCL818_CTRL_PACER_TRIG;
846 ctrl |= PCL818_CTRL_EXT_TRIG;
848 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
851 pcl818_ai_setup_dma(dev, s);
853 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
855 } else if (devpriv->usefifo) {
857 outb(1, dev->iobase + PCL818_FI_ENABLE);
859 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
861 outb(ctrl, dev->iobase + PCL818_CTRL_REG);
863 if (cmd->convert_src == TRIG_TIMER)
864 pcl818_start_pacer(dev, true);
869 static int pcl818_ai_cancel(struct comedi_device *dev,
870 struct comedi_subdevice *s)
872 struct pcl818_private *devpriv = dev->private;
873 struct comedi_cmd *cmd = &s->async->cmd;
875 if (!devpriv->ai_cmd_running)
879 if (cmd->stop_src == TRIG_NONE ||
880 (cmd->stop_src == TRIG_COUNT &&
881 s->async->scans_done < cmd->stop_arg)) {
882 if (!devpriv->ai_cmd_canceled) {
884 * Wait for running dma transfer to end,
885 * do cleanup in interrupt.
887 devpriv->ai_cmd_canceled = 1;
891 pcl818_isadma_disable(devpriv->dma);
894 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
895 pcl818_start_pacer(dev, false);
896 pcl818_ai_clear_eoc(dev);
898 if (devpriv->usefifo) { /* FIFO shutdown */
899 outb(0, dev->iobase + PCL818_FI_INTCLR);
900 outb(0, dev->iobase + PCL818_FI_FLUSH);
901 outb(0, dev->iobase + PCL818_FI_ENABLE);
903 devpriv->ai_cmd_running = 0;
904 devpriv->ai_cmd_canceled = 0;
909 static int pcl818_ai_insn_read(struct comedi_device *dev,
910 struct comedi_subdevice *s,
911 struct comedi_insn *insn,
914 unsigned int chan = CR_CHAN(insn->chanspec);
915 unsigned int range = CR_RANGE(insn->chanspec);
919 outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
921 pcl818_ai_set_chan_range(dev, chan, range);
922 pcl818_ai_set_chan_scan(dev, chan, chan);
924 for (i = 0; i < insn->n; i++) {
925 pcl818_ai_clear_eoc(dev);
926 pcl818_ai_soft_trig(dev);
928 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
932 data[i] = pcl818_ai_get_sample(dev, s, NULL);
934 pcl818_ai_clear_eoc(dev);
936 return ret ? ret : insn->n;
939 static int pcl818_ao_insn_write(struct comedi_device *dev,
940 struct comedi_subdevice *s,
941 struct comedi_insn *insn,
944 unsigned int chan = CR_CHAN(insn->chanspec);
945 unsigned int val = s->readback[chan];
948 for (i = 0; i < insn->n; i++) {
950 outb((val & 0x000f) << 4,
951 dev->iobase + PCL818_AO_LSB_REG(chan));
952 outb((val & 0x0ff0) >> 4,
953 dev->iobase + PCL818_AO_MSB_REG(chan));
955 s->readback[chan] = val;
960 static int pcl818_di_insn_bits(struct comedi_device *dev,
961 struct comedi_subdevice *s,
962 struct comedi_insn *insn,
965 data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
966 (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
971 static int pcl818_do_insn_bits(struct comedi_device *dev,
972 struct comedi_subdevice *s,
973 struct comedi_insn *insn,
976 if (comedi_dio_update_state(s, data)) {
977 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
978 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
986 static void pcl818_reset(struct comedi_device *dev)
988 const struct pcl818_board *board = dev->board_ptr;
989 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
992 /* flush and disable the FIFO */
993 if (board->has_fifo) {
994 outb(0, dev->iobase + PCL818_FI_INTCLR);
995 outb(0, dev->iobase + PCL818_FI_FLUSH);
996 outb(0, dev->iobase + PCL818_FI_ENABLE);
999 /* disable analog input trigger */
1000 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
1001 pcl818_ai_clear_eoc(dev);
1003 pcl818_ai_set_chan_range(dev, 0, 0);
1006 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
1007 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1008 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1009 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1011 /* set analog output channels to 0V */
1012 for (chan = 0; chan < board->n_aochan; chan++) {
1013 outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
1014 outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
1017 /* set all digital outputs low */
1018 outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
1019 outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
1022 static void pcl818_set_ai_range_table(struct comedi_device *dev,
1023 struct comedi_subdevice *s,
1024 struct comedi_devconfig *it)
1026 const struct pcl818_board *board = dev->board_ptr;
1028 /* default to the range table from the boardinfo */
1029 s->range_table = board->ai_range_type;
1031 /* now check the user config option based on the boardtype */
1032 if (board->is_818) {
1033 if (it->options[4] == 1 || it->options[4] == 10) {
1034 /* secondary range list jumper selectable */
1035 s->range_table = &range_pcl818l_h_ai;
1038 switch (it->options[4]) {
1040 s->range_table = &range_bipolar10;
1043 s->range_table = &range_bipolar5;
1046 s->range_table = &range_bipolar2_5;
1049 s->range_table = &range718_bipolar1;
1052 s->range_table = &range718_bipolar0_5;
1055 s->range_table = &range_unipolar10;
1058 s->range_table = &range_unipolar5;
1061 s->range_table = &range718_unipolar2;
1064 s->range_table = &range718_unipolar1;
1067 s->range_table = &range_unknown;
1073 static int pcl818_alloc_dma(struct comedi_device *dev, unsigned int dma_chan)
1075 struct pcl818_private *devpriv = dev->private;
1076 struct pcl818_dma_desc *dma;
1079 if (!(dma_chan == 3 || dma_chan == 1))
1082 if (request_dma(dma_chan, dev->board_name))
1084 devpriv->dma = dma_chan;
1086 devpriv->hwdmasize = PAGE_SIZE * 4; /* we need 16KB */
1088 for (i = 0; i < 2; i++) {
1089 dma = &devpriv->dma_desc[i];
1091 dma->virt_addr = dma_alloc_coherent(NULL, devpriv->hwdmasize,
1092 &dma->hw_addr, GFP_KERNEL);
1093 if (!dma->virt_addr)
1099 static void pcl818_free_dma(struct comedi_device *dev)
1101 struct pcl818_private *devpriv = dev->private;
1102 struct pcl818_dma_desc *dma;
1109 free_dma(devpriv->dma);
1110 for (i = 0; i < 2; i++) {
1111 dma = &devpriv->dma_desc[i];
1113 dma_free_coherent(NULL, devpriv->hwdmasize,
1114 dma->virt_addr, dma->hw_addr);
1118 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1120 const struct pcl818_board *board = dev->board_ptr;
1121 struct pcl818_private *devpriv;
1122 struct comedi_subdevice *s;
1125 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1129 ret = comedi_request_region(dev, it->options[0],
1130 board->has_fifo ? 0x20 : 0x10);
1134 /* we can use IRQ 2-7 for async command support */
1135 if (it->options[1] >= 2 && it->options[1] <= 7) {
1136 ret = request_irq(it->options[1], pcl818_interrupt, 0,
1137 dev->board_name, dev);
1139 dev->irq = it->options[1];
1142 /* should we use the FIFO? */
1143 if (dev->irq && board->has_fifo && it->options[2] == -1)
1144 devpriv->usefifo = 1;
1146 /* we need an IRQ to do DMA on channel 3 or 1 */
1147 if (dev->irq && board->has_dma) {
1148 ret = pcl818_alloc_dma(dev, it->options[2]);
1153 ret = comedi_alloc_subdevices(dev, 4);
1157 s = &dev->subdevices[0];
1158 s->type = COMEDI_SUBD_AI;
1159 s->subdev_flags = SDF_READABLE;
1160 if (check_single_ended(dev->iobase)) {
1162 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1165 s->subdev_flags |= SDF_DIFF;
1167 s->maxdata = 0x0fff;
1169 pcl818_set_ai_range_table(dev, s, it);
1171 s->insn_read = pcl818_ai_insn_read;
1173 dev->read_subdev = s;
1174 s->subdev_flags |= SDF_CMD_READ;
1175 s->len_chanlist = s->n_chan;
1176 s->do_cmdtest = ai_cmdtest;
1177 s->do_cmd = pcl818_ai_cmd;
1178 s->cancel = pcl818_ai_cancel;
1181 /* Analog Output subdevice */
1182 s = &dev->subdevices[1];
1183 if (board->n_aochan) {
1184 s->type = COMEDI_SUBD_AO;
1185 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1186 s->n_chan = board->n_aochan;
1187 s->maxdata = 0x0fff;
1188 s->range_table = &range_unipolar5;
1189 if (board->is_818) {
1190 if ((it->options[4] == 1) || (it->options[4] == 10))
1191 s->range_table = &range_unipolar10;
1192 if (it->options[4] == 2)
1193 s->range_table = &range_unknown;
1195 if ((it->options[5] == 1) || (it->options[5] == 10))
1196 s->range_table = &range_unipolar10;
1197 if (it->options[5] == 2)
1198 s->range_table = &range_unknown;
1200 s->insn_write = pcl818_ao_insn_write;
1202 ret = comedi_alloc_subdev_readback(s);
1206 s->type = COMEDI_SUBD_UNUSED;
1209 /* Digital Input subdevice */
1210 s = &dev->subdevices[2];
1211 s->type = COMEDI_SUBD_DI;
1212 s->subdev_flags = SDF_READABLE;
1215 s->range_table = &range_digital;
1216 s->insn_bits = pcl818_di_insn_bits;
1218 /* Digital Output subdevice */
1219 s = &dev->subdevices[3];
1220 s->type = COMEDI_SUBD_DO;
1221 s->subdev_flags = SDF_WRITABLE;
1224 s->range_table = &range_digital;
1225 s->insn_bits = pcl818_do_insn_bits;
1227 /* select 1/10MHz oscilator */
1228 if ((it->options[3] == 0) || (it->options[3] == 10))
1229 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
1231 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
1233 /* max sampling speed */
1234 devpriv->ns_min = board->ns_min;
1236 if (!board->is_818) {
1237 if ((it->options[6] == 1) || (it->options[6] == 100))
1238 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1246 static void pcl818_detach(struct comedi_device *dev)
1248 struct pcl818_private *devpriv = dev->private;
1251 pcl818_ai_cancel(dev, dev->read_subdev);
1254 pcl818_free_dma(dev);
1255 comedi_legacy_detach(dev);
1258 static struct comedi_driver pcl818_driver = {
1259 .driver_name = "pcl818",
1260 .module = THIS_MODULE,
1261 .attach = pcl818_attach,
1262 .detach = pcl818_detach,
1263 .board_name = &boardtypes[0].name,
1264 .num_names = ARRAY_SIZE(boardtypes),
1265 .offset = sizeof(struct pcl818_board),
1267 module_comedi_driver(pcl818_driver);
1269 MODULE_AUTHOR("Comedi http://www.comedi.org");
1270 MODULE_DESCRIPTION("Comedi low-level driver");
1271 MODULE_LICENSE("GPL");