2 * comedi/drivers/pcl818.c
5 * Description: Advantech PCL-818 cards, PCL-718
6 * Author: Michal Dobes <dobes@tesnet.cz>
7 * Devices: [Advantech] PCL-818L (pcl818l), PCL-818H (pcl818h),
8 * PCL-818HD (pcl818hd), PCL-818HG (pcl818hg), PCL-818 (pcl818),
12 * All cards have 16 SE/8 DIFF ADCs, one or two DACs, 16 DI and 16 DO.
13 * Differences are only at maximal sample speed, range list and FIFO
15 * The driver support AI mode 0, 1, 3 other subdevices (AO, DI, DO) support
16 * only mode 0. If DMA/FIFO/INT are disabled then AI support only mode 0.
17 * PCL-818HD and PCL-818HG support 1kword FIFO. Driver support this FIFO
18 * but this code is untested.
19 * A word or two about DMA. Driver support DMA operations at two ways:
20 * 1) DMA uses two buffers and after one is filled then is generated
21 * INT and DMA restart with second buffer. With this mode I'm unable run
22 * more that 80Ksamples/secs without data dropouts on K6/233.
23 * 2) DMA uses one buffer and run in autoinit mode and the data are
24 * from DMA buffer moved on the fly with 2kHz interrupts from RTC.
25 * This mode is used if the interrupt 8 is available for allocation.
26 * If not, then first DMA mode is used. With this I can run at
27 * full speed one card (100ksamples/secs) or two cards with
28 * 60ksamples/secs each (more is problem on account of ISA limitations).
29 * To use this mode you must have compiled kernel with disabled
30 * "Enhanced Real Time Clock Support".
31 * Maybe you can have problems if you use xntpd or similar.
32 * If you've data dropouts with DMA mode 2 then:
34 * b) switch text mode console to fb.
36 * Options for PCL-818L:
38 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
39 * [2] - DMA (0=disable, 1, 3)
40 * [3] - 0, 10=10MHz clock for 8254
41 * 1= 1MHz clock for 8254
42 * [4] - 0, 5=A/D input -5V.. +5V
43 * 1, 10=A/D input -10V..+10V
44 * [5] - 0, 5=D/A output 0-5V (internal reference -5V)
45 * 1, 10=D/A output 0-10V (internal reference -10V)
46 * 2 =D/A output unknown (external reference)
48 * Options for PCL-818, PCL-818H:
50 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
51 * [2] - DMA (0=disable, 1, 3)
52 * [3] - 0, 10=10MHz clock for 8254
53 * 1= 1MHz clock for 8254
54 * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
55 * 1, 10=D/A output 0-10V (internal reference -10V)
56 * 2 =D/A output unknown (external reference)
58 * Options for PCL-818HD, PCL-818HG:
60 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
61 * [2] - DMA/FIFO (-1=use FIFO, 0=disable both FIFO and DMA,
62 * 1=use DMA ch 1, 3=use DMA ch 3)
63 * [3] - 0, 10=10MHz clock for 8254
64 * 1= 1MHz clock for 8254
65 * [4] - 0, 5=D/A output 0-5V (internal reference -5V)
66 * 1, 10=D/A output 0-10V (internal reference -10V)
67 * 2 =D/A output unknown (external reference)
69 * Options for PCL-718:
71 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
72 * [2] - DMA (0=disable, 1, 3)
73 * [3] - 0, 10=10MHz clock for 8254
74 * 1= 1MHz clock for 8254
75 * [4] - 0=A/D Range is +/-10V
80 * 5= user defined bipolar
85 * 10= user defined unipolar
86 * [5] - 0, 5=D/A outputs 0-5V (internal reference -5V)
87 * 1, 10=D/A outputs 0-10V (internal reference -10V)
88 * 2=D/A outputs unknown (external reference)
89 * [6] - 0, 60=max 60kHz A/D sampling
90 * 1,100=max 100kHz A/D sampling (PCL-718 with Option 001 installed)
94 #include <linux/module.h>
95 #include <linux/gfp.h>
96 #include <linux/delay.h>
98 #include <linux/interrupt.h>
100 #include "../comedidev.h"
102 #include "comedi_isadma.h"
103 #include "comedi_8254.h"
105 /* boards constants */
107 #define boardPCL818L 0
108 #define boardPCL818H 1
109 #define boardPCL818HD 2
110 #define boardPCL818HG 3
111 #define boardPCL818 4
112 #define boardPCL718 5
117 #define PCL818_AI_LSB_REG 0x00
118 #define PCL818_AI_MSB_REG 0x01
119 #define PCL818_RANGE_REG 0x01
120 #define PCL818_MUX_REG 0x02
121 #define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
122 #define PCL818_DO_DI_LSB_REG 0x03
123 #define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
124 #define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
125 #define PCL818_STATUS_REG 0x08
126 #define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
127 #define PCL818_STATUS_INT (1 << 4)
128 #define PCL818_STATUS_MUX (1 << 5)
129 #define PCL818_STATUS_UNI (1 << 6)
130 #define PCL818_STATUS_EOC (1 << 7)
131 #define PCL818_CTRL_REG 0x09
132 #define PCL818_CTRL_DISABLE_TRIG (0 << 0)
133 #define PCL818_CTRL_SOFT_TRIG (1 << 0)
134 #define PCL818_CTRL_EXT_TRIG (2 << 0)
135 #define PCL818_CTRL_PACER_TRIG (3 << 0)
136 #define PCL818_CTRL_DMAE (1 << 2)
137 #define PCL818_CTRL_IRQ(x) ((x) << 4)
138 #define PCL818_CTRL_INTE (1 << 7)
139 #define PCL818_CNTENABLE_REG 0x0a
140 #define PCL818_CNTENABLE_PACER_ENA (0 << 0)
141 #define PCL818_CNTENABLE_PACER_TRIG0 (1 << 0)
142 #define PCL818_CNTENABLE_CNT0_EXT_CLK (0 << 1)
143 #define PCL818_CNTENABLE_CNT0_INT_CLK (1 << 1)
144 #define PCL818_DO_DI_MSB_REG 0x0b
145 #define PCL818_TIMER_BASE 0x0c
147 /* W: fifo enable/disable */
148 #define PCL818_FI_ENABLE 6
149 /* W: fifo interrupt clear */
150 #define PCL818_FI_INTCLR 20
151 /* W: fifo interrupt clear */
152 #define PCL818_FI_FLUSH 25
154 #define PCL818_FI_STATUS 25
155 /* R: one record from FIFO */
156 #define PCL818_FI_DATALO 23
157 #define PCL818_FI_DATAHI 24
159 #define MAGIC_DMA_WORD 0x5a5a
161 static const struct comedi_lrange range_pcl818h_ai
= {
175 static const struct comedi_lrange range_pcl818hg_ai
= {
192 static const struct comedi_lrange range_pcl818l_l_ai
= {
201 static const struct comedi_lrange range_pcl818l_h_ai
= {
210 static const struct comedi_lrange range718_bipolar1
= {
216 static const struct comedi_lrange range718_bipolar0_5
= {
222 static const struct comedi_lrange range718_unipolar2
= {
228 static const struct comedi_lrange range718_unipolar1
= {
234 struct pcl818_board
{
238 const struct comedi_lrange
*ai_range_type
;
239 unsigned int has_dma
:1;
240 unsigned int has_fifo
:1;
241 unsigned int is_818
:1;
244 static const struct pcl818_board boardtypes
[] = {
249 .ai_range_type
= &range_pcl818l_l_ai
,
256 .ai_range_type
= &range_pcl818h_ai
,
263 .ai_range_type
= &range_pcl818h_ai
,
271 .ai_range_type
= &range_pcl818hg_ai
,
279 .ai_range_type
= &range_pcl818h_ai
,
286 .ai_range_type
= &range_unipolar5
,
291 .ai_range_type
= &range_pcl818h_ai
,
297 struct pcl818_private
{
298 struct comedi_isadma
*dma
;
299 /* manimal allowed delay between samples (in us) for actual card */
301 /* MUX setting for actual AI operations */
302 unsigned int act_chanlist
[16];
303 unsigned int act_chanlist_len
; /* how long is actual MUX list */
304 unsigned int act_chanlist_pos
; /* actual position in MUX list */
305 unsigned int usefifo
:1;
306 unsigned int ai_cmd_running
:1;
307 unsigned int ai_cmd_canceled
:1;
310 static void pcl818_ai_setup_dma(struct comedi_device
*dev
,
311 struct comedi_subdevice
*s
,
312 unsigned int unread_samples
)
314 struct pcl818_private
*devpriv
= dev
->private;
315 struct comedi_isadma
*dma
= devpriv
->dma
;
316 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
317 unsigned int max_samples
= comedi_bytes_to_samples(s
, desc
->maxsize
);
318 unsigned int nsamples
;
320 comedi_isadma_disable(dma
->chan
);
323 * Determine dma size based on the buffer maxsize plus the number of
324 * unread samples and the number of samples remaining in the command.
326 nsamples
= comedi_nsamples_left(s
, max_samples
+ unread_samples
);
327 if (nsamples
> unread_samples
) {
328 nsamples
-= unread_samples
;
329 desc
->size
= comedi_samples_to_bytes(s
, nsamples
);
330 comedi_isadma_program(desc
);
334 static void pcl818_ai_set_chan_range(struct comedi_device
*dev
,
338 outb(chan
, dev
->iobase
+ PCL818_MUX_REG
);
339 outb(range
, dev
->iobase
+ PCL818_RANGE_REG
);
342 static void pcl818_ai_set_chan_scan(struct comedi_device
*dev
,
343 unsigned int first_chan
,
344 unsigned int last_chan
)
346 outb(PCL818_MUX_SCAN(first_chan
, last_chan
),
347 dev
->iobase
+ PCL818_MUX_REG
);
350 static void pcl818_ai_setup_chanlist(struct comedi_device
*dev
,
351 unsigned int *chanlist
,
354 struct pcl818_private
*devpriv
= dev
->private;
355 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
356 unsigned int last_chan
;
360 devpriv
->act_chanlist_len
= seglen
;
361 devpriv
->act_chanlist_pos
= 0;
363 /* store range list to card */
364 for (i
= 0; i
< seglen
; i
++) {
365 last_chan
= CR_CHAN(chanlist
[i
]);
366 range
= CR_RANGE(chanlist
[i
]);
368 devpriv
->act_chanlist
[i
] = last_chan
;
370 pcl818_ai_set_chan_range(dev
, last_chan
, range
);
375 pcl818_ai_set_chan_scan(dev
, first_chan
, last_chan
);
378 static void pcl818_ai_clear_eoc(struct comedi_device
*dev
)
380 /* writing any value clears the interrupt request */
381 outb(0, dev
->iobase
+ PCL818_STATUS_REG
);
384 static void pcl818_ai_soft_trig(struct comedi_device
*dev
)
386 /* writing any value triggers a software conversion */
387 outb(0, dev
->iobase
+ PCL818_AI_LSB_REG
);
390 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device
*dev
,
391 struct comedi_subdevice
*s
,
396 val
= inb(dev
->iobase
+ PCL818_FI_DATALO
);
397 val
|= (inb(dev
->iobase
+ PCL818_FI_DATAHI
) << 8);
402 return (val
>> 4) & s
->maxdata
;
405 static unsigned int pcl818_ai_get_sample(struct comedi_device
*dev
,
406 struct comedi_subdevice
*s
,
411 val
= inb(dev
->iobase
+ PCL818_AI_MSB_REG
) << 8;
412 val
|= inb(dev
->iobase
+ PCL818_AI_LSB_REG
);
417 return (val
>> 4) & s
->maxdata
;
420 static int pcl818_ai_eoc(struct comedi_device
*dev
,
421 struct comedi_subdevice
*s
,
422 struct comedi_insn
*insn
,
423 unsigned long context
)
427 status
= inb(dev
->iobase
+ PCL818_STATUS_REG
);
428 if (status
& PCL818_STATUS_INT
)
433 static bool pcl818_ai_write_sample(struct comedi_device
*dev
,
434 struct comedi_subdevice
*s
,
435 unsigned int chan
, unsigned int val
)
437 struct pcl818_private
*devpriv
= dev
->private;
438 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
439 unsigned int expected_chan
;
441 expected_chan
= devpriv
->act_chanlist
[devpriv
->act_chanlist_pos
];
442 if (chan
!= expected_chan
) {
443 dev_dbg(dev
->class_dev
,
444 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
445 (devpriv
->dma
) ? "DMA" :
446 (devpriv
->usefifo
) ? "FIFO" : "IRQ",
447 chan
, expected_chan
);
448 s
->async
->events
|= COMEDI_CB_ERROR
;
452 comedi_buf_write_samples(s
, &val
, 1);
454 devpriv
->act_chanlist_pos
++;
455 if (devpriv
->act_chanlist_pos
>= devpriv
->act_chanlist_len
)
456 devpriv
->act_chanlist_pos
= 0;
458 if (cmd
->stop_src
== TRIG_COUNT
&&
459 s
->async
->scans_done
>= cmd
->stop_arg
) {
460 s
->async
->events
|= COMEDI_CB_EOA
;
467 static void pcl818_handle_eoc(struct comedi_device
*dev
,
468 struct comedi_subdevice
*s
)
473 if (pcl818_ai_eoc(dev
, s
, NULL
, 0)) {
474 dev_err(dev
->class_dev
, "A/D mode1/3 IRQ without DRDY!\n");
475 s
->async
->events
|= COMEDI_CB_ERROR
;
479 val
= pcl818_ai_get_sample(dev
, s
, &chan
);
480 pcl818_ai_write_sample(dev
, s
, chan
, val
);
483 static void pcl818_handle_dma(struct comedi_device
*dev
,
484 struct comedi_subdevice
*s
)
486 struct pcl818_private
*devpriv
= dev
->private;
487 struct comedi_isadma
*dma
= devpriv
->dma
;
488 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
489 unsigned short *ptr
= desc
->virt_addr
;
490 unsigned int nsamples
= comedi_bytes_to_samples(s
, desc
->size
);
495 /* restart dma with the next buffer */
496 dma
->cur_dma
= 1 - dma
->cur_dma
;
497 pcl818_ai_setup_dma(dev
, s
, nsamples
);
499 for (i
= 0; i
< nsamples
; i
++) {
502 val
= (val
>> 4) & s
->maxdata
;
503 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
508 static void pcl818_handle_fifo(struct comedi_device
*dev
,
509 struct comedi_subdevice
*s
)
516 status
= inb(dev
->iobase
+ PCL818_FI_STATUS
);
519 dev_err(dev
->class_dev
, "A/D mode1/3 FIFO overflow!\n");
520 s
->async
->events
|= COMEDI_CB_ERROR
;
525 dev_err(dev
->class_dev
,
526 "A/D mode1/3 FIFO interrupt without data!\n");
527 s
->async
->events
|= COMEDI_CB_ERROR
;
536 for (i
= 0; i
< len
; i
++) {
537 val
= pcl818_ai_get_fifo_sample(dev
, s
, &chan
);
538 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
543 static irqreturn_t
pcl818_interrupt(int irq
, void *d
)
545 struct comedi_device
*dev
= d
;
546 struct pcl818_private
*devpriv
= dev
->private;
547 struct comedi_subdevice
*s
= dev
->read_subdev
;
548 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
550 if (!dev
->attached
|| !devpriv
->ai_cmd_running
) {
551 pcl818_ai_clear_eoc(dev
);
555 if (devpriv
->ai_cmd_canceled
) {
557 * The cleanup from ai_cancel() has been delayed
558 * until now because the card doesn't seem to like
559 * being reprogrammed while a DMA transfer is in
562 s
->async
->scans_done
= cmd
->stop_arg
;
568 pcl818_handle_dma(dev
, s
);
569 else if (devpriv
->usefifo
)
570 pcl818_handle_fifo(dev
, s
);
572 pcl818_handle_eoc(dev
, s
);
574 pcl818_ai_clear_eoc(dev
);
576 comedi_handle_events(dev
, s
);
580 static int check_channel_list(struct comedi_device
*dev
,
581 struct comedi_subdevice
*s
,
582 unsigned int *chanlist
, unsigned int n_chan
)
584 unsigned int chansegment
[16];
585 unsigned int i
, nowmustbechan
, seglen
, segpos
;
587 /* correct channel and range number check itself comedi/range.c */
589 dev_err(dev
->class_dev
, "range/channel list is empty!\n");
594 /* first channel is every time ok */
595 chansegment
[0] = chanlist
[0];
596 /* build part of chanlist */
597 for (i
= 1, seglen
= 1; i
< n_chan
; i
++, seglen
++) {
598 /* we detect loop, this must by finish */
600 if (chanlist
[0] == chanlist
[i
])
603 (CR_CHAN(chansegment
[i
- 1]) + 1) % s
->n_chan
;
604 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
605 /* channel list isn't continuous :-( */
606 dev_dbg(dev
->class_dev
,
607 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
608 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
609 CR_CHAN(chanlist
[0]));
612 /* well, this is next correct channel in list */
613 chansegment
[i
] = chanlist
[i
];
616 /* check whole chanlist */
617 for (i
= 0, segpos
= 0; i
< n_chan
; i
++) {
618 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
619 dev_dbg(dev
->class_dev
,
620 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
621 i
, CR_CHAN(chansegment
[i
]),
622 CR_RANGE(chansegment
[i
]),
623 CR_AREF(chansegment
[i
]),
624 CR_CHAN(chanlist
[i
% seglen
]),
625 CR_RANGE(chanlist
[i
% seglen
]),
626 CR_AREF(chansegment
[i
% seglen
]));
627 return 0; /* chan/gain list is strange */
636 static int check_single_ended(unsigned int port
)
638 if (inb(port
+ PCL818_STATUS_REG
) & PCL818_STATUS_MUX
)
643 static int ai_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
644 struct comedi_cmd
*cmd
)
646 const struct pcl818_board
*board
= dev
->board_ptr
;
649 /* Step 1 : check if triggers are trivially valid */
651 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
652 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
653 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
654 TRIG_TIMER
| TRIG_EXT
);
655 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
656 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
661 /* Step 2a : make sure trigger sources are unique */
663 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
664 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
666 /* Step 2b : and mutually compatible */
671 /* Step 3: check if arguments are trivially valid */
673 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
674 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
676 if (cmd
->convert_src
== TRIG_TIMER
) {
677 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
,
679 } else { /* TRIG_EXT */
680 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
683 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
686 if (cmd
->stop_src
== TRIG_COUNT
)
687 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
689 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
694 /* step 4: fix up any arguments */
696 if (cmd
->convert_src
== TRIG_TIMER
) {
697 unsigned int arg
= cmd
->convert_arg
;
699 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
700 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
706 /* step 5: complain about special chanlist considerations */
709 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
711 return 5; /* incorrect channels list */
717 static int pcl818_ai_cmd(struct comedi_device
*dev
,
718 struct comedi_subdevice
*s
)
720 struct pcl818_private
*devpriv
= dev
->private;
721 struct comedi_isadma
*dma
= devpriv
->dma
;
722 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
723 unsigned int ctrl
= 0;
726 if (devpriv
->ai_cmd_running
)
729 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
732 pcl818_ai_setup_chanlist(dev
, cmd
->chanlist
, seglen
);
734 devpriv
->ai_cmd_running
= 1;
735 devpriv
->ai_cmd_canceled
= 0;
736 devpriv
->act_chanlist_pos
= 0;
738 if (cmd
->convert_src
== TRIG_TIMER
)
739 ctrl
|= PCL818_CTRL_PACER_TRIG
;
741 ctrl
|= PCL818_CTRL_EXT_TRIG
;
743 outb(PCL818_CNTENABLE_PACER_ENA
, dev
->iobase
+ PCL818_CNTENABLE_REG
);
746 /* setup and enable dma for the first buffer */
748 pcl818_ai_setup_dma(dev
, s
, 0);
750 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
) |
752 } else if (devpriv
->usefifo
) {
754 outb(1, dev
->iobase
+ PCL818_FI_ENABLE
);
756 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
);
758 outb(ctrl
, dev
->iobase
+ PCL818_CTRL_REG
);
760 if (cmd
->convert_src
== TRIG_TIMER
) {
761 comedi_8254_update_divisors(dev
->pacer
);
762 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
768 static int pcl818_ai_cancel(struct comedi_device
*dev
,
769 struct comedi_subdevice
*s
)
771 struct pcl818_private
*devpriv
= dev
->private;
772 struct comedi_isadma
*dma
= devpriv
->dma
;
773 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
775 if (!devpriv
->ai_cmd_running
)
779 if (cmd
->stop_src
== TRIG_NONE
||
780 (cmd
->stop_src
== TRIG_COUNT
&&
781 s
->async
->scans_done
< cmd
->stop_arg
)) {
782 if (!devpriv
->ai_cmd_canceled
) {
784 * Wait for running dma transfer to end,
785 * do cleanup in interrupt.
787 devpriv
->ai_cmd_canceled
= 1;
791 comedi_isadma_disable(dma
->chan
);
794 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
795 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, false);
796 pcl818_ai_clear_eoc(dev
);
798 if (devpriv
->usefifo
) { /* FIFO shutdown */
799 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
800 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
801 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
803 devpriv
->ai_cmd_running
= 0;
804 devpriv
->ai_cmd_canceled
= 0;
809 static int pcl818_ai_insn_read(struct comedi_device
*dev
,
810 struct comedi_subdevice
*s
,
811 struct comedi_insn
*insn
,
814 unsigned int chan
= CR_CHAN(insn
->chanspec
);
815 unsigned int range
= CR_RANGE(insn
->chanspec
);
819 outb(PCL818_CTRL_SOFT_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
821 pcl818_ai_set_chan_range(dev
, chan
, range
);
822 pcl818_ai_set_chan_scan(dev
, chan
, chan
);
824 for (i
= 0; i
< insn
->n
; i
++) {
825 pcl818_ai_clear_eoc(dev
);
826 pcl818_ai_soft_trig(dev
);
828 ret
= comedi_timeout(dev
, s
, insn
, pcl818_ai_eoc
, 0);
832 data
[i
] = pcl818_ai_get_sample(dev
, s
, NULL
);
834 pcl818_ai_clear_eoc(dev
);
836 return ret
? ret
: insn
->n
;
839 static int pcl818_ao_insn_write(struct comedi_device
*dev
,
840 struct comedi_subdevice
*s
,
841 struct comedi_insn
*insn
,
844 unsigned int chan
= CR_CHAN(insn
->chanspec
);
845 unsigned int val
= s
->readback
[chan
];
848 for (i
= 0; i
< insn
->n
; i
++) {
850 outb((val
& 0x000f) << 4,
851 dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
852 outb((val
& 0x0ff0) >> 4,
853 dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
855 s
->readback
[chan
] = val
;
860 static int pcl818_di_insn_bits(struct comedi_device
*dev
,
861 struct comedi_subdevice
*s
,
862 struct comedi_insn
*insn
,
865 data
[1] = inb(dev
->iobase
+ PCL818_DO_DI_LSB_REG
) |
866 (inb(dev
->iobase
+ PCL818_DO_DI_MSB_REG
) << 8);
871 static int pcl818_do_insn_bits(struct comedi_device
*dev
,
872 struct comedi_subdevice
*s
,
873 struct comedi_insn
*insn
,
876 if (comedi_dio_update_state(s
, data
)) {
877 outb(s
->state
& 0xff, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
878 outb((s
->state
>> 8), dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
886 static void pcl818_reset(struct comedi_device
*dev
)
888 const struct pcl818_board
*board
= dev
->board_ptr
;
891 /* flush and disable the FIFO */
892 if (board
->has_fifo
) {
893 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
894 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
895 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
898 /* disable analog input trigger */
899 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
900 pcl818_ai_clear_eoc(dev
);
902 pcl818_ai_set_chan_range(dev
, 0, 0);
905 outb(PCL818_CNTENABLE_PACER_ENA
, dev
->iobase
+ PCL818_CNTENABLE_REG
);
907 /* set analog output channels to 0V */
908 for (chan
= 0; chan
< board
->n_aochan
; chan
++) {
909 outb(0, dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
910 outb(0, dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
913 /* set all digital outputs low */
914 outb(0, dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
915 outb(0, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
918 static void pcl818_set_ai_range_table(struct comedi_device
*dev
,
919 struct comedi_subdevice
*s
,
920 struct comedi_devconfig
*it
)
922 const struct pcl818_board
*board
= dev
->board_ptr
;
924 /* default to the range table from the boardinfo */
925 s
->range_table
= board
->ai_range_type
;
927 /* now check the user config option based on the boardtype */
929 if (it
->options
[4] == 1 || it
->options
[4] == 10) {
930 /* secondary range list jumper selectable */
931 s
->range_table
= &range_pcl818l_h_ai
;
934 switch (it
->options
[4]) {
936 s
->range_table
= &range_bipolar10
;
939 s
->range_table
= &range_bipolar5
;
942 s
->range_table
= &range_bipolar2_5
;
945 s
->range_table
= &range718_bipolar1
;
948 s
->range_table
= &range718_bipolar0_5
;
951 s
->range_table
= &range_unipolar10
;
954 s
->range_table
= &range_unipolar5
;
957 s
->range_table
= &range718_unipolar2
;
960 s
->range_table
= &range718_unipolar1
;
963 s
->range_table
= &range_unknown
;
969 static void pcl818_alloc_dma(struct comedi_device
*dev
, unsigned int dma_chan
)
971 struct pcl818_private
*devpriv
= dev
->private;
973 /* only DMA channels 3 and 1 are valid */
974 if (!(dma_chan
== 3 || dma_chan
== 1))
977 /* DMA uses two 16K buffers */
978 devpriv
->dma
= comedi_isadma_alloc(dev
, 2, dma_chan
, dma_chan
,
979 PAGE_SIZE
* 4, COMEDI_ISADMA_READ
);
982 static void pcl818_free_dma(struct comedi_device
*dev
)
984 struct pcl818_private
*devpriv
= dev
->private;
987 comedi_isadma_free(devpriv
->dma
);
990 static int pcl818_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
992 const struct pcl818_board
*board
= dev
->board_ptr
;
993 struct pcl818_private
*devpriv
;
994 struct comedi_subdevice
*s
;
995 unsigned int osc_base
;
998 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
1002 ret
= comedi_request_region(dev
, it
->options
[0],
1003 board
->has_fifo
? 0x20 : 0x10);
1007 /* we can use IRQ 2-7 for async command support */
1008 if (it
->options
[1] >= 2 && it
->options
[1] <= 7) {
1009 ret
= request_irq(it
->options
[1], pcl818_interrupt
, 0,
1010 dev
->board_name
, dev
);
1012 dev
->irq
= it
->options
[1];
1015 /* should we use the FIFO? */
1016 if (dev
->irq
&& board
->has_fifo
&& it
->options
[2] == -1)
1017 devpriv
->usefifo
= 1;
1019 /* we need an IRQ to do DMA on channel 3 or 1 */
1020 if (dev
->irq
&& board
->has_dma
)
1021 pcl818_alloc_dma(dev
, it
->options
[2]);
1023 /* use 1MHz or 10MHz oscilator */
1024 if ((it
->options
[3] == 0) || (it
->options
[3] == 10))
1025 osc_base
= I8254_OSC_BASE_10MHZ
;
1027 osc_base
= I8254_OSC_BASE_1MHZ
;
1029 dev
->pacer
= comedi_8254_init(dev
->iobase
+ PCL818_TIMER_BASE
,
1030 osc_base
, I8254_IO8
, 0);
1034 /* max sampling speed */
1035 devpriv
->ns_min
= board
->ns_min
;
1036 if (!board
->is_818
) {
1037 /* extended PCL718 to 100kHz DAC */
1038 if ((it
->options
[6] == 1) || (it
->options
[6] == 100))
1039 devpriv
->ns_min
= 10000;
1042 ret
= comedi_alloc_subdevices(dev
, 4);
1046 s
= &dev
->subdevices
[0];
1047 s
->type
= COMEDI_SUBD_AI
;
1048 s
->subdev_flags
= SDF_READABLE
;
1049 if (check_single_ended(dev
->iobase
)) {
1051 s
->subdev_flags
|= SDF_COMMON
| SDF_GROUND
;
1054 s
->subdev_flags
|= SDF_DIFF
;
1056 s
->maxdata
= 0x0fff;
1058 pcl818_set_ai_range_table(dev
, s
, it
);
1060 s
->insn_read
= pcl818_ai_insn_read
;
1062 dev
->read_subdev
= s
;
1063 s
->subdev_flags
|= SDF_CMD_READ
;
1064 s
->len_chanlist
= s
->n_chan
;
1065 s
->do_cmdtest
= ai_cmdtest
;
1066 s
->do_cmd
= pcl818_ai_cmd
;
1067 s
->cancel
= pcl818_ai_cancel
;
1070 /* Analog Output subdevice */
1071 s
= &dev
->subdevices
[1];
1072 if (board
->n_aochan
) {
1073 s
->type
= COMEDI_SUBD_AO
;
1074 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
1075 s
->n_chan
= board
->n_aochan
;
1076 s
->maxdata
= 0x0fff;
1077 s
->range_table
= &range_unipolar5
;
1078 if (board
->is_818
) {
1079 if ((it
->options
[4] == 1) || (it
->options
[4] == 10))
1080 s
->range_table
= &range_unipolar10
;
1081 if (it
->options
[4] == 2)
1082 s
->range_table
= &range_unknown
;
1084 if ((it
->options
[5] == 1) || (it
->options
[5] == 10))
1085 s
->range_table
= &range_unipolar10
;
1086 if (it
->options
[5] == 2)
1087 s
->range_table
= &range_unknown
;
1089 s
->insn_write
= pcl818_ao_insn_write
;
1091 ret
= comedi_alloc_subdev_readback(s
);
1095 s
->type
= COMEDI_SUBD_UNUSED
;
1098 /* Digital Input subdevice */
1099 s
= &dev
->subdevices
[2];
1100 s
->type
= COMEDI_SUBD_DI
;
1101 s
->subdev_flags
= SDF_READABLE
;
1104 s
->range_table
= &range_digital
;
1105 s
->insn_bits
= pcl818_di_insn_bits
;
1107 /* Digital Output subdevice */
1108 s
= &dev
->subdevices
[3];
1109 s
->type
= COMEDI_SUBD_DO
;
1110 s
->subdev_flags
= SDF_WRITABLE
;
1113 s
->range_table
= &range_digital
;
1114 s
->insn_bits
= pcl818_do_insn_bits
;
1121 static void pcl818_detach(struct comedi_device
*dev
)
1123 struct pcl818_private
*devpriv
= dev
->private;
1126 pcl818_ai_cancel(dev
, dev
->read_subdev
);
1129 pcl818_free_dma(dev
);
1130 comedi_legacy_detach(dev
);
1133 static struct comedi_driver pcl818_driver
= {
1134 .driver_name
= "pcl818",
1135 .module
= THIS_MODULE
,
1136 .attach
= pcl818_attach
,
1137 .detach
= pcl818_detach
,
1138 .board_name
= &boardtypes
[0].name
,
1139 .num_names
= ARRAY_SIZE(boardtypes
),
1140 .offset
= sizeof(struct pcl818_board
),
1142 module_comedi_driver(pcl818_driver
);
1144 MODULE_AUTHOR("Comedi http://www.comedi.org");
1145 MODULE_DESCRIPTION("Comedi low-level driver");
1146 MODULE_LICENSE("GPL");