1 // SPDX-License-Identifier: GPL-2.0
3 * comedi/drivers/pcl818.c
6 * Description: Advantech PCL-818 cards, PCL-718
7 * Author: Michal Dobes <dobes@tesnet.cz>
8 * Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
9 * PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
13 * All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
14 * Differences are only at maximal sample speed, range list and FIFO
16 * The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
17 * only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
18 * PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
19 * but this code is untested.
20 * A word or two about DMA. Driver support DMA operations at two ways:
21 * 1) DMA uses two buffers and after one is filled then is generated
22 * INT and DMA restart with second buffer. With this mode I'm unable run
23 * more that 80Ksamples/secs without data dropouts on K6/233.
24 * 2) DMA uses one buffer and run in autoinit mode and the data are
25 * from DMA buffer moved on the fly with 2kHz interrupts from RTC.
26 * This mode is used if the interrupt 8 is available for allocation.
27 * If not, then first DMA mode is used. With this I can run at
28 * full speed one card (100ksamples/secs) or two cards with
29 * 60ksamples/secs each (more is problem on account of ISA limitations).
30 * To use this mode you must have compiled kernel with disabled
31 * "Enhanced Real Time Clock Support".
32 * Maybe you can have problems if you use xntpd or similar.
33 * If you've data dropouts with DMA mode 2 then:
35 * b) switch text mode console to fb.
37 * Options for PCL-818L:
39 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
40 * [2] - DMA (0=disable, 1, 3)
41 * [3] - 0, 10=10MHz clock for 8254
42 * 1= 1MHz clock for 8254
43 * [4] - 0, 5=A/D input -5V.. +5V
44 * 1, 10=A/D input -10V..+10V
45 * [5] - 0, 5=D/A output 0-5V (internal reference -5V)
46 * 1, 10=D/A output 0-10V (internal reference -10V)
47 * 2 =D/A output unknown (external reference)
49 * Options for PCL-818, PCL-818H:
51 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
52 * [2] - DMA (0=disable, 1, 3)
53 * [3] - 0, 10=10MHz clock for 8254
54 * 1= 1MHz clock for 8254
55 * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
56 * 1, 10=D/A output 0-10V (internal reference -10V)
57 * 2 =D/A output unknown (external reference)
59 * Options for PCL-818HD, PCL-818HG:
61 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
62 * [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
63 * 1=use DMA ch 1, 3=use DMA ch 3)
64 * [3] - 0, 10=10MHz clock for 8254
65 * 1= 1MHz clock for 8254
66 * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
67 * 1, 10=D/A output 0-10V (internal reference -10V)
68 * 2 =D/A output unknown (external reference)
70 * Options for PCL-718:
72 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
73 * [2] - DMA (0=disable, 1, 3)
74 * [3] - 0, 10=10MHz clock for 8254
75 * 1= 1MHz clock for 8254
76 * [4] - 0=A/D Range is +/-10V
81 * 5= user defined bipolar
86 * 10= user defined unipolar
87 * [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
88 * 1, 10=D/A outputs 0-10V (internal reference -10V)
89 * 2=D/A outputs unknown (external reference)
90 * [6] - 0, 60=max 60kHz A/D sampling
91 * 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
95 #include <linux/module.h>
96 #include <linux/gfp.h>
97 #include <linux/delay.h>
99 #include <linux/interrupt.h>
101 #include "../comedidev.h"
103 #include "comedi_isadma.h"
104 #include "comedi_8254.h"
109 #define PCL818_AI_LSB_REG 0x00
110 #define PCL818_AI_MSB_REG 0x01
111 #define PCL818_RANGE_REG 0x01
112 #define PCL818_MUX_REG 0x02
113 #define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
114 #define PCL818_DO_DI_LSB_REG 0x03
115 #define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
116 #define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
117 #define PCL818_STATUS_REG 0x08
118 #define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
119 #define PCL818_STATUS_INT BIT(4)
120 #define PCL818_STATUS_MUX BIT(5)
121 #define PCL818_STATUS_UNI BIT(6)
122 #define PCL818_STATUS_EOC BIT(7)
123 #define PCL818_CTRL_REG 0x09
124 #define PCL818_CTRL_TRIG(x) (((x) & 0x3) << 0)
125 #define PCL818_CTRL_DISABLE_TRIG PCL818_CTRL_TRIG(0)
126 #define PCL818_CTRL_SOFT_TRIG PCL818_CTRL_TRIG(1)
127 #define PCL818_CTRL_EXT_TRIG PCL818_CTRL_TRIG(2)
128 #define PCL818_CTRL_PACER_TRIG PCL818_CTRL_TRIG(3)
129 #define PCL818_CTRL_DMAE BIT(2)
130 #define PCL818_CTRL_IRQ(x) ((x) << 4)
131 #define PCL818_CTRL_INTE BIT(7)
132 #define PCL818_CNTENABLE_REG 0x0a
133 #define PCL818_CNTENABLE_PACER_TRIG0 BIT(0)
134 #define PCL818_CNTENABLE_CNT0_INT_CLK BIT(1) /* 0=ext clk */
135 #define PCL818_DO_DI_MSB_REG 0x0b
136 #define PCL818_TIMER_BASE 0x0c
138 /* W: fifo enable/disable */
139 #define PCL818_FI_ENABLE 6
140 /* W: fifo interrupt clear */
141 #define PCL818_FI_INTCLR 20
142 /* W: fifo interrupt clear */
143 #define PCL818_FI_FLUSH 25
145 #define PCL818_FI_STATUS 25
146 /* R: one record from FIFO */
147 #define PCL818_FI_DATALO 23
148 #define PCL818_FI_DATAHI 24
150 #define MAGIC_DMA_WORD 0x5a5a
152 static const struct comedi_lrange range_pcl818h_ai
= {
166 static const struct comedi_lrange range_pcl818hg_ai
= {
183 static const struct comedi_lrange range_pcl818l_l_ai
= {
192 static const struct comedi_lrange range_pcl818l_h_ai
= {
201 static const struct comedi_lrange range718_bipolar1
= {
207 static const struct comedi_lrange range718_bipolar0_5
= {
213 static const struct comedi_lrange range718_unipolar2
= {
219 static const struct comedi_lrange range718_unipolar1
= {
225 struct pcl818_board
{
229 const struct comedi_lrange
*ai_range_type
;
230 unsigned int has_dma
:1;
231 unsigned int has_fifo
:1;
232 unsigned int is_818
:1;
235 static const struct pcl818_board boardtypes
[] = {
240 .ai_range_type
= &range_pcl818l_l_ai
,
247 .ai_range_type
= &range_pcl818h_ai
,
254 .ai_range_type
= &range_pcl818h_ai
,
262 .ai_range_type
= &range_pcl818hg_ai
,
270 .ai_range_type
= &range_pcl818h_ai
,
277 .ai_range_type
= &range_unipolar5
,
282 .ai_range_type
= &range_pcl818h_ai
,
288 struct pcl818_private
{
289 struct comedi_isadma
*dma
;
290 /* manimal allowed delay between samples (in us) for actual card */
292 /* MUX setting for actual AI operations */
293 unsigned int act_chanlist
[16];
294 unsigned int act_chanlist_len
; /* how long is actual MUX list */
295 unsigned int act_chanlist_pos
; /* actual position in MUX list */
296 unsigned int usefifo
:1;
297 unsigned int ai_cmd_running
:1;
298 unsigned int ai_cmd_canceled
:1;
301 static void pcl818_ai_setup_dma(struct comedi_device
*dev
,
302 struct comedi_subdevice
*s
,
303 unsigned int unread_samples
)
305 struct pcl818_private
*devpriv
= dev
->private;
306 struct comedi_isadma
*dma
= devpriv
->dma
;
307 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
308 unsigned int max_samples
= comedi_bytes_to_samples(s
, desc
->maxsize
);
309 unsigned int nsamples
;
311 comedi_isadma_disable(dma
->chan
);
314 * Determine dma size based on the buffer maxsize plus the number of
315 * unread samples and the number of samples remaining in the command.
317 nsamples
= comedi_nsamples_left(s
, max_samples
+ unread_samples
);
318 if (nsamples
> unread_samples
) {
319 nsamples
-= unread_samples
;
320 desc
->size
= comedi_samples_to_bytes(s
, nsamples
);
321 comedi_isadma_program(desc
);
325 static void pcl818_ai_set_chan_range(struct comedi_device
*dev
,
329 outb(chan
, dev
->iobase
+ PCL818_MUX_REG
);
330 outb(range
, dev
->iobase
+ PCL818_RANGE_REG
);
333 static void pcl818_ai_set_chan_scan(struct comedi_device
*dev
,
334 unsigned int first_chan
,
335 unsigned int last_chan
)
337 outb(PCL818_MUX_SCAN(first_chan
, last_chan
),
338 dev
->iobase
+ PCL818_MUX_REG
);
341 static void pcl818_ai_setup_chanlist(struct comedi_device
*dev
,
342 unsigned int *chanlist
,
345 struct pcl818_private
*devpriv
= dev
->private;
346 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
347 unsigned int last_chan
;
351 devpriv
->act_chanlist_len
= seglen
;
352 devpriv
->act_chanlist_pos
= 0;
354 /* store range list to card */
355 for (i
= 0; i
< seglen
; i
++) {
356 last_chan
= CR_CHAN(chanlist
[i
]);
357 range
= CR_RANGE(chanlist
[i
]);
359 devpriv
->act_chanlist
[i
] = last_chan
;
361 pcl818_ai_set_chan_range(dev
, last_chan
, range
);
366 pcl818_ai_set_chan_scan(dev
, first_chan
, last_chan
);
369 static void pcl818_ai_clear_eoc(struct comedi_device
*dev
)
371 /* writing any value clears the interrupt request */
372 outb(0, dev
->iobase
+ PCL818_STATUS_REG
);
375 static void pcl818_ai_soft_trig(struct comedi_device
*dev
)
377 /* writing any value triggers a software conversion */
378 outb(0, dev
->iobase
+ PCL818_AI_LSB_REG
);
381 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device
*dev
,
382 struct comedi_subdevice
*s
,
387 val
= inb(dev
->iobase
+ PCL818_FI_DATALO
);
388 val
|= (inb(dev
->iobase
+ PCL818_FI_DATAHI
) << 8);
393 return (val
>> 4) & s
->maxdata
;
396 static unsigned int pcl818_ai_get_sample(struct comedi_device
*dev
,
397 struct comedi_subdevice
*s
,
402 val
= inb(dev
->iobase
+ PCL818_AI_MSB_REG
) << 8;
403 val
|= inb(dev
->iobase
+ PCL818_AI_LSB_REG
);
408 return (val
>> 4) & s
->maxdata
;
411 static int pcl818_ai_eoc(struct comedi_device
*dev
,
412 struct comedi_subdevice
*s
,
413 struct comedi_insn
*insn
,
414 unsigned long context
)
418 status
= inb(dev
->iobase
+ PCL818_STATUS_REG
);
419 if (status
& PCL818_STATUS_INT
)
424 static bool pcl818_ai_write_sample(struct comedi_device
*dev
,
425 struct comedi_subdevice
*s
,
426 unsigned int chan
, unsigned int val
)
428 struct pcl818_private
*devpriv
= dev
->private;
429 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
430 unsigned int expected_chan
;
432 expected_chan
= devpriv
->act_chanlist
[devpriv
->act_chanlist_pos
];
433 if (chan
!= expected_chan
) {
434 dev_dbg(dev
->class_dev
,
435 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
436 (devpriv
->dma
) ? "DMA" :
437 (devpriv
->usefifo
) ? "FIFO" : "IRQ",
438 chan
, expected_chan
);
439 s
->async
->events
|= COMEDI_CB_ERROR
;
443 comedi_buf_write_samples(s
, &val
, 1);
445 devpriv
->act_chanlist_pos
++;
446 if (devpriv
->act_chanlist_pos
>= devpriv
->act_chanlist_len
)
447 devpriv
->act_chanlist_pos
= 0;
449 if (cmd
->stop_src
== TRIG_COUNT
&&
450 s
->async
->scans_done
>= cmd
->stop_arg
) {
451 s
->async
->events
|= COMEDI_CB_EOA
;
458 static void pcl818_handle_eoc(struct comedi_device
*dev
,
459 struct comedi_subdevice
*s
)
464 if (pcl818_ai_eoc(dev
, s
, NULL
, 0)) {
465 dev_err(dev
->class_dev
, "A/D mode1/3 IRQ without DRDY!\n");
466 s
->async
->events
|= COMEDI_CB_ERROR
;
470 val
= pcl818_ai_get_sample(dev
, s
, &chan
);
471 pcl818_ai_write_sample(dev
, s
, chan
, val
);
474 static void pcl818_handle_dma(struct comedi_device
*dev
,
475 struct comedi_subdevice
*s
)
477 struct pcl818_private
*devpriv
= dev
->private;
478 struct comedi_isadma
*dma
= devpriv
->dma
;
479 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
480 unsigned short *ptr
= desc
->virt_addr
;
481 unsigned int nsamples
= comedi_bytes_to_samples(s
, desc
->size
);
486 /* restart dma with the next buffer */
487 dma
->cur_dma
= 1 - dma
->cur_dma
;
488 pcl818_ai_setup_dma(dev
, s
, nsamples
);
490 for (i
= 0; i
< nsamples
; i
++) {
493 val
= (val
>> 4) & s
->maxdata
;
494 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
499 static void pcl818_handle_fifo(struct comedi_device
*dev
,
500 struct comedi_subdevice
*s
)
507 status
= inb(dev
->iobase
+ PCL818_FI_STATUS
);
510 dev_err(dev
->class_dev
, "A/D mode1/3 FIFO overflow!\n");
511 s
->async
->events
|= COMEDI_CB_ERROR
;
516 dev_err(dev
->class_dev
,
517 "A/D mode1/3 FIFO interrupt without data!\n");
518 s
->async
->events
|= COMEDI_CB_ERROR
;
527 for (i
= 0; i
< len
; i
++) {
528 val
= pcl818_ai_get_fifo_sample(dev
, s
, &chan
);
529 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
534 static irqreturn_t
pcl818_interrupt(int irq
, void *d
)
536 struct comedi_device
*dev
= d
;
537 struct pcl818_private
*devpriv
= dev
->private;
538 struct comedi_subdevice
*s
= dev
->read_subdev
;
539 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
541 if (!dev
->attached
|| !devpriv
->ai_cmd_running
) {
542 pcl818_ai_clear_eoc(dev
);
546 if (devpriv
->ai_cmd_canceled
) {
548 * The cleanup from ai_cancel() has been delayed
549 * until now because the card doesn't seem to like
550 * being reprogrammed while a DMA transfer is in
553 s
->async
->scans_done
= cmd
->stop_arg
;
559 pcl818_handle_dma(dev
, s
);
560 else if (devpriv
->usefifo
)
561 pcl818_handle_fifo(dev
, s
);
563 pcl818_handle_eoc(dev
, s
);
565 pcl818_ai_clear_eoc(dev
);
567 comedi_handle_events(dev
, s
);
571 static int check_channel_list(struct comedi_device
*dev
,
572 struct comedi_subdevice
*s
,
573 unsigned int *chanlist
, unsigned int n_chan
)
575 unsigned int chansegment
[16];
576 unsigned int i
, nowmustbechan
, seglen
;
578 /* correct channel and range number check itself comedi/range.c */
580 dev_err(dev
->class_dev
, "range/channel list is empty!\n");
585 /* first channel is every time ok */
586 chansegment
[0] = chanlist
[0];
587 /* build part of chanlist */
588 for (i
= 1, seglen
= 1; i
< n_chan
; i
++, seglen
++) {
589 /* we detect loop, this must by finish */
591 if (chanlist
[0] == chanlist
[i
])
594 (CR_CHAN(chansegment
[i
- 1]) + 1) % s
->n_chan
;
595 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
596 /* channel list isn't continuous :-( */
597 dev_dbg(dev
->class_dev
,
598 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
599 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
600 CR_CHAN(chanlist
[0]));
603 /* well, this is next correct channel in list */
604 chansegment
[i
] = chanlist
[i
];
607 /* check whole chanlist */
608 for (i
= 0; i
< n_chan
; i
++) {
609 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
610 dev_dbg(dev
->class_dev
,
611 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
612 i
, CR_CHAN(chansegment
[i
]),
613 CR_RANGE(chansegment
[i
]),
614 CR_AREF(chansegment
[i
]),
615 CR_CHAN(chanlist
[i
% seglen
]),
616 CR_RANGE(chanlist
[i
% seglen
]),
617 CR_AREF(chansegment
[i
% seglen
]));
618 return 0; /* chan/gain list is strange */
627 static int check_single_ended(unsigned int port
)
629 if (inb(port
+ PCL818_STATUS_REG
) & PCL818_STATUS_MUX
)
634 static int ai_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
635 struct comedi_cmd
*cmd
)
637 const struct pcl818_board
*board
= dev
->board_ptr
;
640 /* Step 1 : check if triggers are trivially valid */
642 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
643 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
644 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
645 TRIG_TIMER
| TRIG_EXT
);
646 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
647 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
652 /* Step 2a : make sure trigger sources are unique */
654 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
655 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
657 /* Step 2b : and mutually compatible */
662 /* Step 3: check if arguments are trivially valid */
664 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
665 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
667 if (cmd
->convert_src
== TRIG_TIMER
) {
668 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
,
670 } else { /* TRIG_EXT */
671 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
674 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
677 if (cmd
->stop_src
== TRIG_COUNT
)
678 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
680 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
685 /* step 4: fix up any arguments */
687 if (cmd
->convert_src
== TRIG_TIMER
) {
688 unsigned int arg
= cmd
->convert_arg
;
690 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
691 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
697 /* step 5: complain about special chanlist considerations */
700 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
702 return 5; /* incorrect channels list */
708 static int pcl818_ai_cmd(struct comedi_device
*dev
,
709 struct comedi_subdevice
*s
)
711 struct pcl818_private
*devpriv
= dev
->private;
712 struct comedi_isadma
*dma
= devpriv
->dma
;
713 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
714 unsigned int ctrl
= 0;
717 if (devpriv
->ai_cmd_running
)
720 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
723 pcl818_ai_setup_chanlist(dev
, cmd
->chanlist
, seglen
);
725 devpriv
->ai_cmd_running
= 1;
726 devpriv
->ai_cmd_canceled
= 0;
727 devpriv
->act_chanlist_pos
= 0;
729 if (cmd
->convert_src
== TRIG_TIMER
)
730 ctrl
|= PCL818_CTRL_PACER_TRIG
;
732 ctrl
|= PCL818_CTRL_EXT_TRIG
;
734 outb(0, dev
->iobase
+ PCL818_CNTENABLE_REG
);
737 /* setup and enable dma for the first buffer */
739 pcl818_ai_setup_dma(dev
, s
, 0);
741 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
) |
743 } else if (devpriv
->usefifo
) {
745 outb(1, dev
->iobase
+ PCL818_FI_ENABLE
);
747 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
);
749 outb(ctrl
, dev
->iobase
+ PCL818_CTRL_REG
);
751 if (cmd
->convert_src
== TRIG_TIMER
) {
752 comedi_8254_update_divisors(dev
->pacer
);
753 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
759 static int pcl818_ai_cancel(struct comedi_device
*dev
,
760 struct comedi_subdevice
*s
)
762 struct pcl818_private
*devpriv
= dev
->private;
763 struct comedi_isadma
*dma
= devpriv
->dma
;
764 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
766 if (!devpriv
->ai_cmd_running
)
770 if (cmd
->stop_src
== TRIG_NONE
||
771 (cmd
->stop_src
== TRIG_COUNT
&&
772 s
->async
->scans_done
< cmd
->stop_arg
)) {
773 if (!devpriv
->ai_cmd_canceled
) {
775 * Wait for running dma transfer to end,
776 * do cleanup in interrupt.
778 devpriv
->ai_cmd_canceled
= 1;
782 comedi_isadma_disable(dma
->chan
);
785 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
786 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, false);
787 pcl818_ai_clear_eoc(dev
);
789 if (devpriv
->usefifo
) { /* FIFO shutdown */
790 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
791 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
792 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
794 devpriv
->ai_cmd_running
= 0;
795 devpriv
->ai_cmd_canceled
= 0;
800 static int pcl818_ai_insn_read(struct comedi_device
*dev
,
801 struct comedi_subdevice
*s
,
802 struct comedi_insn
*insn
,
805 unsigned int chan
= CR_CHAN(insn
->chanspec
);
806 unsigned int range
= CR_RANGE(insn
->chanspec
);
810 outb(PCL818_CTRL_SOFT_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
812 pcl818_ai_set_chan_range(dev
, chan
, range
);
813 pcl818_ai_set_chan_scan(dev
, chan
, chan
);
815 for (i
= 0; i
< insn
->n
; i
++) {
816 pcl818_ai_clear_eoc(dev
);
817 pcl818_ai_soft_trig(dev
);
819 ret
= comedi_timeout(dev
, s
, insn
, pcl818_ai_eoc
, 0);
823 data
[i
] = pcl818_ai_get_sample(dev
, s
, NULL
);
825 pcl818_ai_clear_eoc(dev
);
827 return ret
? ret
: insn
->n
;
830 static int pcl818_ao_insn_write(struct comedi_device
*dev
,
831 struct comedi_subdevice
*s
,
832 struct comedi_insn
*insn
,
835 unsigned int chan
= CR_CHAN(insn
->chanspec
);
836 unsigned int val
= s
->readback
[chan
];
839 for (i
= 0; i
< insn
->n
; i
++) {
841 outb((val
& 0x000f) << 4,
842 dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
843 outb((val
& 0x0ff0) >> 4,
844 dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
846 s
->readback
[chan
] = val
;
851 static int pcl818_di_insn_bits(struct comedi_device
*dev
,
852 struct comedi_subdevice
*s
,
853 struct comedi_insn
*insn
,
856 data
[1] = inb(dev
->iobase
+ PCL818_DO_DI_LSB_REG
) |
857 (inb(dev
->iobase
+ PCL818_DO_DI_MSB_REG
) << 8);
862 static int pcl818_do_insn_bits(struct comedi_device
*dev
,
863 struct comedi_subdevice
*s
,
864 struct comedi_insn
*insn
,
867 if (comedi_dio_update_state(s
, data
)) {
868 outb(s
->state
& 0xff, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
869 outb((s
->state
>> 8), dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
877 static void pcl818_reset(struct comedi_device
*dev
)
879 const struct pcl818_board
*board
= dev
->board_ptr
;
882 /* flush and disable the FIFO */
883 if (board
->has_fifo
) {
884 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
885 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
886 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
889 /* disable analog input trigger */
890 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
891 pcl818_ai_clear_eoc(dev
);
893 pcl818_ai_set_chan_range(dev
, 0, 0);
896 outb(0, dev
->iobase
+ PCL818_CNTENABLE_REG
);
898 /* set analog output channels to 0V */
899 for (chan
= 0; chan
< board
->n_aochan
; chan
++) {
900 outb(0, dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
901 outb(0, dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
904 /* set all digital outputs low */
905 outb(0, dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
906 outb(0, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
909 static void pcl818_set_ai_range_table(struct comedi_device
*dev
,
910 struct comedi_subdevice
*s
,
911 struct comedi_devconfig
*it
)
913 const struct pcl818_board
*board
= dev
->board_ptr
;
915 /* default to the range table from the boardinfo */
916 s
->range_table
= board
->ai_range_type
;
918 /* now check the user config option based on the boardtype */
920 if (it
->options
[4] == 1 || it
->options
[4] == 10) {
921 /* secondary range list jumper selectable */
922 s
->range_table
= &range_pcl818l_h_ai
;
925 switch (it
->options
[4]) {
927 s
->range_table
= &range_bipolar10
;
930 s
->range_table
= &range_bipolar5
;
933 s
->range_table
= &range_bipolar2_5
;
936 s
->range_table
= &range718_bipolar1
;
939 s
->range_table
= &range718_bipolar0_5
;
942 s
->range_table
= &range_unipolar10
;
945 s
->range_table
= &range_unipolar5
;
948 s
->range_table
= &range718_unipolar2
;
951 s
->range_table
= &range718_unipolar1
;
954 s
->range_table
= &range_unknown
;
960 static void pcl818_alloc_dma(struct comedi_device
*dev
, unsigned int dma_chan
)
962 struct pcl818_private
*devpriv
= dev
->private;
964 /* only DMA channels 3 and 1 are valid */
965 if (!(dma_chan
== 3 || dma_chan
== 1))
968 /* DMA uses two 16K buffers */
969 devpriv
->dma
= comedi_isadma_alloc(dev
, 2, dma_chan
, dma_chan
,
970 PAGE_SIZE
* 4, COMEDI_ISADMA_READ
);
973 static void pcl818_free_dma(struct comedi_device
*dev
)
975 struct pcl818_private
*devpriv
= dev
->private;
978 comedi_isadma_free(devpriv
->dma
);
981 static int pcl818_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
983 const struct pcl818_board
*board
= dev
->board_ptr
;
984 struct pcl818_private
*devpriv
;
985 struct comedi_subdevice
*s
;
986 unsigned int osc_base
;
989 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
993 ret
= comedi_request_region(dev
, it
->options
[0],
994 board
->has_fifo
? 0x20 : 0x10);
998 /* we can use IRQ 2-7 for async command support */
999 if (it
->options
[1] >= 2 && it
->options
[1] <= 7) {
1000 ret
= request_irq(it
->options
[1], pcl818_interrupt
, 0,
1001 dev
->board_name
, dev
);
1003 dev
->irq
= it
->options
[1];
1006 /* should we use the FIFO? */
1007 if (dev
->irq
&& board
->has_fifo
&& it
->options
[2] == -1)
1008 devpriv
->usefifo
= 1;
1010 /* we need an IRQ to do DMA on channel 3 or 1 */
1011 if (dev
->irq
&& board
->has_dma
)
1012 pcl818_alloc_dma(dev
, it
->options
[2]);
1014 /* use 1MHz or 10MHz oscilator */
1015 if ((it
->options
[3] == 0) || (it
->options
[3] == 10))
1016 osc_base
= I8254_OSC_BASE_10MHZ
;
1018 osc_base
= I8254_OSC_BASE_1MHZ
;
1020 dev
->pacer
= comedi_8254_init(dev
->iobase
+ PCL818_TIMER_BASE
,
1021 osc_base
, I8254_IO8
, 0);
1025 /* max sampling speed */
1026 devpriv
->ns_min
= board
->ns_min
;
1027 if (!board
->is_818
) {
1028 /* extended PCL718 to 100kHz DAC */
1029 if ((it
->options
[6] == 1) || (it
->options
[6] == 100))
1030 devpriv
->ns_min
= 10000;
1033 ret
= comedi_alloc_subdevices(dev
, 4);
1037 s
= &dev
->subdevices
[0];
1038 s
->type
= COMEDI_SUBD_AI
;
1039 s
->subdev_flags
= SDF_READABLE
;
1040 if (check_single_ended(dev
->iobase
)) {
1042 s
->subdev_flags
|= SDF_COMMON
| SDF_GROUND
;
1045 s
->subdev_flags
|= SDF_DIFF
;
1047 s
->maxdata
= 0x0fff;
1049 pcl818_set_ai_range_table(dev
, s
, it
);
1051 s
->insn_read
= pcl818_ai_insn_read
;
1053 dev
->read_subdev
= s
;
1054 s
->subdev_flags
|= SDF_CMD_READ
;
1055 s
->len_chanlist
= s
->n_chan
;
1056 s
->do_cmdtest
= ai_cmdtest
;
1057 s
->do_cmd
= pcl818_ai_cmd
;
1058 s
->cancel
= pcl818_ai_cancel
;
1061 /* Analog Output subdevice */
1062 s
= &dev
->subdevices
[1];
1063 if (board
->n_aochan
) {
1064 s
->type
= COMEDI_SUBD_AO
;
1065 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
1066 s
->n_chan
= board
->n_aochan
;
1067 s
->maxdata
= 0x0fff;
1068 s
->range_table
= &range_unipolar5
;
1069 if (board
->is_818
) {
1070 if ((it
->options
[4] == 1) || (it
->options
[4] == 10))
1071 s
->range_table
= &range_unipolar10
;
1072 if (it
->options
[4] == 2)
1073 s
->range_table
= &range_unknown
;
1075 if ((it
->options
[5] == 1) || (it
->options
[5] == 10))
1076 s
->range_table
= &range_unipolar10
;
1077 if (it
->options
[5] == 2)
1078 s
->range_table
= &range_unknown
;
1080 s
->insn_write
= pcl818_ao_insn_write
;
1082 ret
= comedi_alloc_subdev_readback(s
);
1086 s
->type
= COMEDI_SUBD_UNUSED
;
1089 /* Digital Input subdevice */
1090 s
= &dev
->subdevices
[2];
1091 s
->type
= COMEDI_SUBD_DI
;
1092 s
->subdev_flags
= SDF_READABLE
;
1095 s
->range_table
= &range_digital
;
1096 s
->insn_bits
= pcl818_di_insn_bits
;
1098 /* Digital Output subdevice */
1099 s
= &dev
->subdevices
[3];
1100 s
->type
= COMEDI_SUBD_DO
;
1101 s
->subdev_flags
= SDF_WRITABLE
;
1104 s
->range_table
= &range_digital
;
1105 s
->insn_bits
= pcl818_do_insn_bits
;
1112 static void pcl818_detach(struct comedi_device
*dev
)
1114 struct pcl818_private
*devpriv
= dev
->private;
1117 pcl818_ai_cancel(dev
, dev
->read_subdev
);
1120 pcl818_free_dma(dev
);
1121 comedi_legacy_detach(dev
);
1124 static struct comedi_driver pcl818_driver
= {
1125 .driver_name
= "pcl818",
1126 .module
= THIS_MODULE
,
1127 .attach
= pcl818_attach
,
1128 .detach
= pcl818_detach
,
1129 .board_name
= &boardtypes
[0].name
,
1130 .num_names
= ARRAY_SIZE(boardtypes
),
1131 .offset
= sizeof(struct pcl818_board
),
1133 module_comedi_driver(pcl818_driver
);
1135 MODULE_AUTHOR("Comedi http://www.comedi.org");
1136 MODULE_DESCRIPTION("Comedi low-level driver");
1137 MODULE_LICENSE("GPL");