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_private {
306 unsigned int dma; /* used DMA, 0=don't use DMA */
307 unsigned int dmapages;
308 unsigned int hwdmasize;
309 unsigned long dmabuf[2]; /* pointers to begin of DMA buffers */
310 unsigned int hwdmaptr[2]; /* hardware address of DMA buffers */
311 int next_dma_buf; /* which DMA buffer will be used next round */
312 long dma_runs_to_end; /* how many we must permorm DMA transfer to end of record */
313 unsigned long last_dma_run; /* how many bytes we must transfer on last DMA page */
314 unsigned int ns_min; /* manimal allowed delay between samples (in us) for actual card */
315 int i8253_osc_base; /* 1/frequency of on board oscilator in ns */
316 int ai_act_scan; /* how many scans we finished */
317 int ai_act_chan; /* actual position in actual scan */
318 unsigned int act_chanlist[16]; /* MUX setting for actual AI operations */
319 unsigned int act_chanlist_len; /* how long is actual MUX list */
320 unsigned int act_chanlist_pos; /* actual position in MUX list */
321 unsigned int ai_data_len; /* len of data buffer */
322 unsigned int divisor1;
323 unsigned int divisor2;
324 unsigned int usefifo:1;
325 unsigned int ai_cmd_running:1;
326 unsigned int ai_cmd_canceled:1;
329 static void pcl818_start_pacer(struct comedi_device *dev, bool load_counters)
331 struct pcl818_private *devpriv = dev->private;
332 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
334 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
335 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
339 i8254_write(timer_base, 0, 2, devpriv->divisor2);
340 i8254_write(timer_base, 0, 1, devpriv->divisor1);
344 static void pcl818_ai_setup_dma(struct comedi_device *dev,
345 struct comedi_subdevice *s)
347 struct pcl818_private *devpriv = dev->private;
348 struct comedi_cmd *cmd = &s->async->cmd;
352 disable_dma(devpriv->dma); /* disable dma */
353 bytes = devpriv->hwdmasize;
354 if (cmd->stop_src == TRIG_COUNT) {
355 bytes = cmd->stop_arg * cfc_bytes_per_scan(s);
356 devpriv->dma_runs_to_end = bytes / devpriv->hwdmasize;
357 devpriv->last_dma_run = bytes % devpriv->hwdmasize;
358 devpriv->dma_runs_to_end--;
359 if (devpriv->dma_runs_to_end >= 0)
360 bytes = devpriv->hwdmasize;
363 devpriv->next_dma_buf = 0;
364 set_dma_mode(devpriv->dma, DMA_MODE_READ);
365 flags = claim_dma_lock();
366 clear_dma_ff(devpriv->dma);
367 set_dma_addr(devpriv->dma, devpriv->hwdmaptr[0]);
368 set_dma_count(devpriv->dma, bytes);
369 release_dma_lock(flags);
370 enable_dma(devpriv->dma);
373 static void pcl818_ai_setup_next_dma(struct comedi_device *dev,
374 struct comedi_subdevice *s)
376 struct pcl818_private *devpriv = dev->private;
377 struct comedi_cmd *cmd = &s->async->cmd;
380 disable_dma(devpriv->dma);
381 devpriv->next_dma_buf = 1 - devpriv->next_dma_buf;
382 if (devpriv->dma_runs_to_end > -1 || cmd->stop_src == TRIG_NONE) {
383 /* switch dma bufs */
384 set_dma_mode(devpriv->dma, DMA_MODE_READ);
385 flags = claim_dma_lock();
386 set_dma_addr(devpriv->dma,
387 devpriv->hwdmaptr[devpriv->next_dma_buf]);
388 if (devpriv->dma_runs_to_end || cmd->stop_src == TRIG_NONE)
389 set_dma_count(devpriv->dma, devpriv->hwdmasize);
391 set_dma_count(devpriv->dma, devpriv->last_dma_run);
392 release_dma_lock(flags);
393 enable_dma(devpriv->dma);
396 devpriv->dma_runs_to_end--;
399 static void pcl818_ai_set_chan_range(struct comedi_device *dev,
403 outb(chan, dev->iobase + PCL818_MUX_REG);
404 outb(range, dev->iobase + PCL818_RANGE_REG);
407 static void pcl818_ai_set_chan_scan(struct comedi_device *dev,
408 unsigned int first_chan,
409 unsigned int last_chan)
411 outb(PCL818_MUX_SCAN(first_chan, last_chan),
412 dev->iobase + PCL818_MUX_REG);
415 static void pcl818_ai_setup_chanlist(struct comedi_device *dev,
416 unsigned int *chanlist,
419 struct pcl818_private *devpriv = dev->private;
420 unsigned int first_chan = CR_CHAN(chanlist[0]);
421 unsigned int last_chan;
425 devpriv->act_chanlist_len = seglen;
426 devpriv->act_chanlist_pos = 0;
428 /* store range list to card */
429 for (i = 0; i < seglen; i++) {
430 last_chan = CR_CHAN(chanlist[i]);
431 range = CR_RANGE(chanlist[i]);
433 devpriv->act_chanlist[i] = last_chan;
435 pcl818_ai_set_chan_range(dev, last_chan, range);
440 pcl818_ai_set_chan_scan(dev, first_chan, last_chan);
443 static void pcl818_ai_clear_eoc(struct comedi_device *dev)
445 /* writing any value clears the interrupt request */
446 outb(0, dev->iobase + PCL818_STATUS_REG);
449 static void pcl818_ai_soft_trig(struct comedi_device *dev)
451 /* writing any value triggers a software conversion */
452 outb(0, dev->iobase + PCL818_AI_LSB_REG);
455 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device *dev,
456 struct comedi_subdevice *s,
461 val = inb(dev->iobase + PCL818_FI_DATALO);
462 val |= (inb(dev->iobase + PCL818_FI_DATAHI) << 8);
467 return (val >> 4) & s->maxdata;
470 static unsigned int pcl818_ai_get_sample(struct comedi_device *dev,
471 struct comedi_subdevice *s,
476 val = inb(dev->iobase + PCL818_AI_MSB_REG) << 8;
477 val |= inb(dev->iobase + PCL818_AI_LSB_REG);
482 return (val >> 4) & s->maxdata;
485 static int pcl818_ai_eoc(struct comedi_device *dev,
486 struct comedi_subdevice *s,
487 struct comedi_insn *insn,
488 unsigned long context)
492 status = inb(dev->iobase + PCL818_STATUS_REG);
493 if (status & PCL818_STATUS_INT)
498 static bool pcl818_ai_dropout(struct comedi_device *dev,
499 struct comedi_subdevice *s,
502 struct pcl818_private *devpriv = dev->private;
503 unsigned int expected_chan;
505 expected_chan = devpriv->act_chanlist[devpriv->act_chanlist_pos];
506 if (chan != expected_chan) {
507 dev_dbg(dev->class_dev,
508 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
509 (devpriv->dma) ? "DMA" :
510 (devpriv->usefifo) ? "FIFO" : "IRQ",
511 chan, expected_chan);
512 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
518 static bool pcl818_ai_next_chan(struct comedi_device *dev,
519 struct comedi_subdevice *s)
521 struct pcl818_private *devpriv = dev->private;
522 struct comedi_cmd *cmd = &s->async->cmd;
524 s->async->events |= COMEDI_CB_BLOCK;
526 devpriv->act_chanlist_pos++;
527 if (devpriv->act_chanlist_pos >= devpriv->act_chanlist_len)
528 devpriv->act_chanlist_pos = 0;
530 s->async->cur_chan++;
531 if (s->async->cur_chan >= cmd->chanlist_len) {
532 s->async->cur_chan = 0;
533 devpriv->ai_act_scan--;
534 s->async->events |= COMEDI_CB_EOS;
537 if (cmd->stop_src == TRIG_COUNT && devpriv->ai_act_scan == 0) {
538 /* all data sampled */
539 s->async->events |= COMEDI_CB_EOA;
546 static void pcl818_handle_eoc(struct comedi_device *dev,
547 struct comedi_subdevice *s)
552 if (pcl818_ai_eoc(dev, s, NULL, 0)) {
553 dev_err(dev->class_dev, "A/D mode1/3 IRQ without DRDY!\n");
554 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
558 val = pcl818_ai_get_sample(dev, s, &chan);
560 if (pcl818_ai_dropout(dev, s, chan))
563 comedi_buf_put(s, val);
565 pcl818_ai_next_chan(dev, s);
568 static void pcl818_handle_dma(struct comedi_device *dev,
569 struct comedi_subdevice *s)
571 struct pcl818_private *devpriv = dev->private;
577 pcl818_ai_setup_next_dma(dev, s);
579 ptr = (unsigned short *)devpriv->dmabuf[1 - devpriv->next_dma_buf];
581 len = devpriv->hwdmasize >> 1;
584 for (i = 0; i < len; i++) {
587 val = (val >> 4) & s->maxdata;
589 if (pcl818_ai_dropout(dev, s, chan))
592 comedi_buf_put(s, val);
594 if (!pcl818_ai_next_chan(dev, s))
599 static void pcl818_handle_fifo(struct comedi_device *dev,
600 struct comedi_subdevice *s)
607 status = inb(dev->iobase + PCL818_FI_STATUS);
610 dev_err(dev->class_dev, "A/D mode1/3 FIFO overflow!\n");
611 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
616 dev_err(dev->class_dev,
617 "A/D mode1/3 FIFO interrupt without data!\n");
618 s->async->events |= COMEDI_CB_EOA | COMEDI_CB_ERROR;
627 for (i = 0; i < len; i++) {
628 val = pcl818_ai_get_fifo_sample(dev, s, &chan);
630 if (pcl818_ai_dropout(dev, s, chan))
633 comedi_buf_put(s, val);
635 if (!pcl818_ai_next_chan(dev, s))
640 static irqreturn_t pcl818_interrupt(int irq, void *d)
642 struct comedi_device *dev = d;
643 struct pcl818_private *devpriv = dev->private;
644 struct comedi_subdevice *s = dev->read_subdev;
646 if (!dev->attached || !devpriv->ai_cmd_running) {
647 pcl818_ai_clear_eoc(dev);
651 if (devpriv->ai_cmd_canceled) {
653 * The cleanup from ai_cancel() has been delayed
654 * until now because the card doesn't seem to like
655 * being reprogrammed while a DMA transfer is in
658 devpriv->ai_act_scan = 0;
664 pcl818_handle_dma(dev, s);
665 else if (devpriv->usefifo)
666 pcl818_handle_fifo(dev, s);
668 pcl818_handle_eoc(dev, s);
670 pcl818_ai_clear_eoc(dev);
672 cfc_handle_events(dev, s);
676 static int check_channel_list(struct comedi_device *dev,
677 struct comedi_subdevice *s,
678 unsigned int *chanlist, unsigned int n_chan)
680 unsigned int chansegment[16];
681 unsigned int i, nowmustbechan, seglen, segpos;
683 /* correct channel and range number check itself comedi/range.c */
685 dev_err(dev->class_dev, "range/channel list is empty!\n");
690 /* first channel is every time ok */
691 chansegment[0] = chanlist[0];
692 /* build part of chanlist */
693 for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
694 /* we detect loop, this must by finish */
696 if (chanlist[0] == chanlist[i])
699 (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
700 if (nowmustbechan != CR_CHAN(chanlist[i])) { /* channel list isn't continuous :-( */
701 dev_dbg(dev->class_dev,
702 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
703 i, CR_CHAN(chanlist[i]), nowmustbechan,
704 CR_CHAN(chanlist[0]));
707 /* well, this is next correct channel in list */
708 chansegment[i] = chanlist[i];
711 /* check whole chanlist */
712 for (i = 0, segpos = 0; i < n_chan; i++) {
713 if (chanlist[i] != chansegment[i % seglen]) {
714 dev_dbg(dev->class_dev,
715 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
716 i, CR_CHAN(chansegment[i]),
717 CR_RANGE(chansegment[i]),
718 CR_AREF(chansegment[i]),
719 CR_CHAN(chanlist[i % seglen]),
720 CR_RANGE(chanlist[i % seglen]),
721 CR_AREF(chansegment[i % seglen]));
722 return 0; /* chan/gain list is strange */
731 static int check_single_ended(unsigned int port)
733 if (inb(port + PCL818_STATUS_REG) & PCL818_STATUS_MUX)
738 static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
739 struct comedi_cmd *cmd)
741 const struct pcl818_board *board = dev->board_ptr;
742 struct pcl818_private *devpriv = dev->private;
746 /* Step 1 : check if triggers are trivially valid */
748 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
749 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
750 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
751 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
752 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
757 /* Step 2a : make sure trigger sources are unique */
759 err |= cfc_check_trigger_is_unique(cmd->convert_src);
760 err |= cfc_check_trigger_is_unique(cmd->stop_src);
762 /* Step 2b : and mutually compatible */
767 /* Step 3: check if arguments are trivially valid */
769 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
770 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
772 if (cmd->convert_src == TRIG_TIMER)
773 err |= cfc_check_trigger_arg_min(&cmd->convert_arg,
776 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
778 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
780 if (cmd->stop_src == TRIG_COUNT)
781 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
783 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
788 /* step 4: fix up any arguments */
790 if (cmd->convert_src == TRIG_TIMER) {
791 arg = cmd->convert_arg;
792 i8253_cascade_ns_to_timer(devpriv->i8253_osc_base,
796 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
802 /* step 5: complain about special chanlist considerations */
805 if (!check_channel_list(dev, s, cmd->chanlist,
807 return 5; /* incorrect channels list */
813 static int pcl818_ai_cmd(struct comedi_device *dev,
814 struct comedi_subdevice *s)
816 struct pcl818_private *devpriv = dev->private;
817 struct comedi_cmd *cmd = &s->async->cmd;
818 unsigned int ctrl = 0;
821 if (devpriv->ai_cmd_running)
824 pcl818_start_pacer(dev, false);
826 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
829 pcl818_ai_setup_chanlist(dev, cmd->chanlist, seglen);
831 devpriv->ai_data_len = s->async->prealloc_bufsz;
832 devpriv->ai_act_scan = cmd->stop_arg;
833 devpriv->ai_act_chan = 0;
834 devpriv->ai_cmd_running = 1;
835 devpriv->ai_cmd_canceled = 0;
836 devpriv->act_chanlist_pos = 0;
837 devpriv->dma_runs_to_end = 0;
839 if (cmd->convert_src == TRIG_TIMER)
840 ctrl |= PCL818_CTRL_PACER_TRIG;
842 ctrl |= PCL818_CTRL_EXT_TRIG;
844 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
847 pcl818_ai_setup_dma(dev, s);
849 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq) |
851 } else if (devpriv->usefifo) {
853 outb(1, dev->iobase + PCL818_FI_ENABLE);
855 ctrl |= PCL818_CTRL_INTE | PCL818_CTRL_IRQ(dev->irq);
857 outb(ctrl, dev->iobase + PCL818_CTRL_REG);
859 if (cmd->convert_src == TRIG_TIMER)
860 pcl818_start_pacer(dev, true);
865 static int pcl818_ai_cancel(struct comedi_device *dev,
866 struct comedi_subdevice *s)
868 struct pcl818_private *devpriv = dev->private;
869 struct comedi_cmd *cmd = &s->async->cmd;
871 if (!devpriv->ai_cmd_running)
875 if (cmd->stop_src == TRIG_NONE ||
876 (cmd->stop_src == TRIG_COUNT && devpriv->ai_act_scan > 0)) {
877 if (!devpriv->ai_cmd_canceled) {
879 * Wait for running dma transfer to end,
880 * do cleanup in interrupt.
882 devpriv->ai_cmd_canceled = 1;
886 disable_dma(devpriv->dma);
889 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
890 pcl818_start_pacer(dev, false);
891 pcl818_ai_clear_eoc(dev);
893 if (devpriv->usefifo) { /* FIFO shutdown */
894 outb(0, dev->iobase + PCL818_FI_INTCLR);
895 outb(0, dev->iobase + PCL818_FI_FLUSH);
896 outb(0, dev->iobase + PCL818_FI_ENABLE);
898 devpriv->ai_cmd_running = 0;
899 devpriv->ai_cmd_canceled = 0;
904 static int pcl818_ai_insn_read(struct comedi_device *dev,
905 struct comedi_subdevice *s,
906 struct comedi_insn *insn,
909 unsigned int chan = CR_CHAN(insn->chanspec);
910 unsigned int range = CR_RANGE(insn->chanspec);
914 outb(PCL818_CTRL_SOFT_TRIG, dev->iobase + PCL818_CTRL_REG);
916 pcl818_ai_set_chan_range(dev, chan, range);
917 pcl818_ai_set_chan_scan(dev, chan, chan);
919 for (i = 0; i < insn->n; i++) {
920 pcl818_ai_clear_eoc(dev);
921 pcl818_ai_soft_trig(dev);
923 ret = comedi_timeout(dev, s, insn, pcl818_ai_eoc, 0);
927 data[i] = pcl818_ai_get_sample(dev, s, NULL);
929 pcl818_ai_clear_eoc(dev);
931 return ret ? ret : insn->n;
934 static int pcl818_ao_insn_write(struct comedi_device *dev,
935 struct comedi_subdevice *s,
936 struct comedi_insn *insn,
939 unsigned int chan = CR_CHAN(insn->chanspec);
940 unsigned int val = s->readback[chan];
943 for (i = 0; i < insn->n; i++) {
945 outb((val & 0x000f) << 4,
946 dev->iobase + PCL818_AO_LSB_REG(chan));
947 outb((val & 0x0ff0) >> 4,
948 dev->iobase + PCL818_AO_MSB_REG(chan));
950 s->readback[chan] = val;
955 static int pcl818_di_insn_bits(struct comedi_device *dev,
956 struct comedi_subdevice *s,
957 struct comedi_insn *insn,
960 data[1] = inb(dev->iobase + PCL818_DO_DI_LSB_REG) |
961 (inb(dev->iobase + PCL818_DO_DI_MSB_REG) << 8);
966 static int pcl818_do_insn_bits(struct comedi_device *dev,
967 struct comedi_subdevice *s,
968 struct comedi_insn *insn,
971 if (comedi_dio_update_state(s, data)) {
972 outb(s->state & 0xff, dev->iobase + PCL818_DO_DI_LSB_REG);
973 outb((s->state >> 8), dev->iobase + PCL818_DO_DI_MSB_REG);
981 static void pcl818_reset(struct comedi_device *dev)
983 const struct pcl818_board *board = dev->board_ptr;
984 unsigned long timer_base = dev->iobase + PCL818_TIMER_BASE;
987 /* flush and disable the FIFO */
988 if (board->has_fifo) {
989 outb(0, dev->iobase + PCL818_FI_INTCLR);
990 outb(0, dev->iobase + PCL818_FI_FLUSH);
991 outb(0, dev->iobase + PCL818_FI_ENABLE);
994 /* disable analog input trigger */
995 outb(PCL818_CTRL_DISABLE_TRIG, dev->iobase + PCL818_CTRL_REG);
996 pcl818_ai_clear_eoc(dev);
998 pcl818_ai_set_chan_range(dev, 0, 0);
1001 outb(PCL818_CNTENABLE_PACER_ENA, dev->iobase + PCL818_CNTENABLE_REG);
1002 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
1003 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
1004 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
1006 /* set analog output channels to 0V */
1007 for (chan = 0; chan < board->n_aochan; chan++) {
1008 outb(0, dev->iobase + PCL818_AO_LSB_REG(chan));
1009 outb(0, dev->iobase + PCL818_AO_MSB_REG(chan));
1012 /* set all digital outputs low */
1013 outb(0, dev->iobase + PCL818_DO_DI_MSB_REG);
1014 outb(0, dev->iobase + PCL818_DO_DI_LSB_REG);
1017 static void pcl818_set_ai_range_table(struct comedi_device *dev,
1018 struct comedi_subdevice *s,
1019 struct comedi_devconfig *it)
1021 const struct pcl818_board *board = dev->board_ptr;
1023 /* default to the range table from the boardinfo */
1024 s->range_table = board->ai_range_type;
1026 /* now check the user config option based on the boardtype */
1027 if (board->is_818) {
1028 if (it->options[4] == 1 || it->options[4] == 10) {
1029 /* secondary range list jumper selectable */
1030 s->range_table = &range_pcl818l_h_ai;
1033 switch (it->options[4]) {
1035 s->range_table = &range_bipolar10;
1038 s->range_table = &range_bipolar5;
1041 s->range_table = &range_bipolar2_5;
1044 s->range_table = &range718_bipolar1;
1047 s->range_table = &range718_bipolar0_5;
1050 s->range_table = &range_unipolar10;
1053 s->range_table = &range_unipolar5;
1056 s->range_table = &range718_unipolar2;
1059 s->range_table = &range718_unipolar1;
1062 s->range_table = &range_unknown;
1068 static int pcl818_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1070 const struct pcl818_board *board = dev->board_ptr;
1071 struct pcl818_private *devpriv;
1072 struct comedi_subdevice *s;
1076 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1080 ret = comedi_request_region(dev, it->options[0],
1081 board->has_fifo ? 0x20 : 0x10);
1085 /* we can use IRQ 2-7 for async command support */
1086 if (it->options[1] >= 2 && it->options[1] <= 7) {
1087 ret = request_irq(it->options[1], pcl818_interrupt, 0,
1088 dev->board_name, dev);
1090 dev->irq = it->options[1];
1093 /* should we use the FIFO? */
1094 if (dev->irq && board->has_fifo && it->options[2] == -1)
1095 devpriv->usefifo = 1;
1097 /* we need an IRQ to do DMA on channel 3 or 1 */
1098 if (dev->irq && board->has_dma &&
1099 (it->options[2] == 3 || it->options[2] == 1)) {
1100 ret = request_dma(it->options[2], dev->board_name);
1102 dev_err(dev->class_dev,
1103 "unable to request DMA channel %d\n",
1107 devpriv->dma = it->options[2];
1109 devpriv->dmapages = 2; /* we need 16KB */
1110 devpriv->hwdmasize = (1 << devpriv->dmapages) * PAGE_SIZE;
1112 for (i = 0; i < 2; i++) {
1113 unsigned long dmabuf;
1115 dmabuf = __get_dma_pages(GFP_KERNEL, devpriv->dmapages);
1119 devpriv->dmabuf[i] = dmabuf;
1120 devpriv->hwdmaptr[i] = virt_to_bus((void *)dmabuf);
1124 ret = comedi_alloc_subdevices(dev, 4);
1128 s = &dev->subdevices[0];
1129 s->type = COMEDI_SUBD_AI;
1130 s->subdev_flags = SDF_READABLE;
1131 if (check_single_ended(dev->iobase)) {
1133 s->subdev_flags |= SDF_COMMON | SDF_GROUND;
1136 s->subdev_flags |= SDF_DIFF;
1138 s->maxdata = 0x0fff;
1140 pcl818_set_ai_range_table(dev, s, it);
1142 s->insn_read = pcl818_ai_insn_read;
1144 dev->read_subdev = s;
1145 s->subdev_flags |= SDF_CMD_READ;
1146 s->len_chanlist = s->n_chan;
1147 s->do_cmdtest = ai_cmdtest;
1148 s->do_cmd = pcl818_ai_cmd;
1149 s->cancel = pcl818_ai_cancel;
1152 /* Analog Output subdevice */
1153 s = &dev->subdevices[1];
1154 if (board->n_aochan) {
1155 s->type = COMEDI_SUBD_AO;
1156 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
1157 s->n_chan = board->n_aochan;
1158 s->maxdata = 0x0fff;
1159 s->range_table = &range_unipolar5;
1160 if (board->is_818) {
1161 if ((it->options[4] == 1) || (it->options[4] == 10))
1162 s->range_table = &range_unipolar10;
1163 if (it->options[4] == 2)
1164 s->range_table = &range_unknown;
1166 if ((it->options[5] == 1) || (it->options[5] == 10))
1167 s->range_table = &range_unipolar10;
1168 if (it->options[5] == 2)
1169 s->range_table = &range_unknown;
1171 s->insn_write = pcl818_ao_insn_write;
1172 s->insn_read = comedi_readback_insn_read;
1174 ret = comedi_alloc_subdev_readback(s);
1178 s->type = COMEDI_SUBD_UNUSED;
1181 /* Digital Input subdevice */
1182 s = &dev->subdevices[2];
1183 s->type = COMEDI_SUBD_DI;
1184 s->subdev_flags = SDF_READABLE;
1187 s->range_table = &range_digital;
1188 s->insn_bits = pcl818_di_insn_bits;
1190 /* Digital Output subdevice */
1191 s = &dev->subdevices[3];
1192 s->type = COMEDI_SUBD_DO;
1193 s->subdev_flags = SDF_WRITABLE;
1196 s->range_table = &range_digital;
1197 s->insn_bits = pcl818_do_insn_bits;
1199 /* select 1/10MHz oscilator */
1200 if ((it->options[3] == 0) || (it->options[3] == 10))
1201 devpriv->i8253_osc_base = I8254_OSC_BASE_10MHZ;
1203 devpriv->i8253_osc_base = I8254_OSC_BASE_1MHZ;
1205 /* max sampling speed */
1206 devpriv->ns_min = board->ns_min;
1208 if (!board->is_818) {
1209 if ((it->options[6] == 1) || (it->options[6] == 100))
1210 devpriv->ns_min = 10000; /* extended PCL718 to 100kHz DAC */
1218 static void pcl818_detach(struct comedi_device *dev)
1220 struct pcl818_private *devpriv = dev->private;
1223 pcl818_ai_cancel(dev, dev->read_subdev);
1226 free_dma(devpriv->dma);
1227 if (devpriv->dmabuf[0])
1228 free_pages(devpriv->dmabuf[0], devpriv->dmapages);
1229 if (devpriv->dmabuf[1])
1230 free_pages(devpriv->dmabuf[1], devpriv->dmapages);
1232 comedi_legacy_detach(dev);
1235 static struct comedi_driver pcl818_driver = {
1236 .driver_name = "pcl818",
1237 .module = THIS_MODULE,
1238 .attach = pcl818_attach,
1239 .detach = pcl818_detach,
1240 .board_name = &boardtypes[0].name,
1241 .num_names = ARRAY_SIZE(boardtypes),
1242 .offset = sizeof(struct pcl818_board),
1244 module_comedi_driver(pcl818_driver);
1246 MODULE_AUTHOR("Comedi http://www.comedi.org");
1247 MODULE_DESCRIPTION("Comedi low-level driver");
1248 MODULE_LICENSE("GPL");