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_fc.h"
106 /* boards constants */
108 #define boardPCL818L 0
109 #define boardPCL818H 1
110 #define boardPCL818HD 2
111 #define boardPCL818HG 3
112 #define boardPCL818 4
113 #define boardPCL718 5
118 #define PCL818_AI_LSB_REG 0x00
119 #define PCL818_AI_MSB_REG 0x01
120 #define PCL818_RANGE_REG 0x01
121 #define PCL818_MUX_REG 0x02
122 #define PCL818_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
123 #define PCL818_DO_DI_LSB_REG 0x03
124 #define PCL818_AO_LSB_REG(x) (0x04 + ((x) * 2))
125 #define PCL818_AO_MSB_REG(x) (0x05 + ((x) * 2))
126 #define PCL818_STATUS_REG 0x08
127 #define PCL818_STATUS_NEXT_CHAN_MASK (0xf << 0)
128 #define PCL818_STATUS_INT (1 << 4)
129 #define PCL818_STATUS_MUX (1 << 5)
130 #define PCL818_STATUS_UNI (1 << 6)
131 #define PCL818_STATUS_EOC (1 << 7)
132 #define PCL818_CTRL_REG 0x09
133 #define PCL818_CTRL_DISABLE_TRIG (0 << 0)
134 #define PCL818_CTRL_SOFT_TRIG (1 << 0)
135 #define PCL818_CTRL_EXT_TRIG (2 << 0)
136 #define PCL818_CTRL_PACER_TRIG (3 << 0)
137 #define PCL818_CTRL_DMAE (1 << 2)
138 #define PCL818_CTRL_IRQ(x) ((x) << 4)
139 #define PCL818_CTRL_INTE (1 << 7)
140 #define PCL818_CNTENABLE_REG 0x0a
141 #define PCL818_CNTENABLE_PACER_ENA (0 << 0)
142 #define PCL818_CNTENABLE_PACER_TRIG0 (1 << 0)
143 #define PCL818_CNTENABLE_CNT0_EXT_CLK (0 << 1)
144 #define PCL818_CNTENABLE_CNT0_INT_CLK (1 << 1)
145 #define PCL818_DO_DI_MSB_REG 0x0b
146 #define PCL818_TIMER_BASE 0x0c
148 /* W: fifo enable/disable */
149 #define PCL818_FI_ENABLE 6
150 /* W: fifo interrupt clear */
151 #define PCL818_FI_INTCLR 20
152 /* W: fifo interrupt clear */
153 #define PCL818_FI_FLUSH 25
155 #define PCL818_FI_STATUS 25
156 /* R: one record from FIFO */
157 #define PCL818_FI_DATALO 23
158 #define PCL818_FI_DATAHI 24
160 #define MAGIC_DMA_WORD 0x5a5a
162 static const struct comedi_lrange range_pcl818h_ai
= {
176 static const struct comedi_lrange range_pcl818hg_ai
= {
193 static const struct comedi_lrange range_pcl818l_l_ai
= {
202 static const struct comedi_lrange range_pcl818l_h_ai
= {
211 static const struct comedi_lrange range718_bipolar1
= {
217 static const struct comedi_lrange range718_bipolar0_5
= {
223 static const struct comedi_lrange range718_unipolar2
= {
229 static const struct comedi_lrange range718_unipolar1
= {
235 struct pcl818_board
{
239 const struct comedi_lrange
*ai_range_type
;
240 unsigned int has_dma
:1;
241 unsigned int has_fifo
:1;
242 unsigned int is_818
:1;
245 static const struct pcl818_board boardtypes
[] = {
250 .ai_range_type
= &range_pcl818l_l_ai
,
257 .ai_range_type
= &range_pcl818h_ai
,
264 .ai_range_type
= &range_pcl818h_ai
,
272 .ai_range_type
= &range_pcl818hg_ai
,
280 .ai_range_type
= &range_pcl818h_ai
,
287 .ai_range_type
= &range_unipolar5
,
292 .ai_range_type
= &range_pcl818h_ai
,
298 struct pcl818_private
{
299 struct comedi_isadma
*dma
;
300 /* manimal allowed delay between samples (in us) for actual card */
302 int i8253_osc_base
; /* 1/frequency of on board oscilator in ns */
303 /* MUX setting for actual AI operations */
304 unsigned int act_chanlist
[16];
305 unsigned int act_chanlist_len
; /* how long is actual MUX list */
306 unsigned int act_chanlist_pos
; /* actual position in MUX list */
307 unsigned int divisor1
;
308 unsigned int divisor2
;
309 unsigned int usefifo
:1;
310 unsigned int ai_cmd_running
:1;
311 unsigned int ai_cmd_canceled
:1;
314 static void pcl818_start_pacer(struct comedi_device
*dev
, bool load_counters
)
316 struct pcl818_private
*devpriv
= dev
->private;
317 unsigned long timer_base
= dev
->iobase
+ PCL818_TIMER_BASE
;
319 i8254_set_mode(timer_base
, 0, 2, I8254_MODE2
| I8254_BINARY
);
320 i8254_set_mode(timer_base
, 0, 1, I8254_MODE2
| I8254_BINARY
);
324 i8254_write(timer_base
, 0, 2, devpriv
->divisor2
);
325 i8254_write(timer_base
, 0, 1, devpriv
->divisor1
);
329 static void pcl818_ai_setup_dma(struct comedi_device
*dev
,
330 struct comedi_subdevice
*s
,
331 unsigned int unread_samples
)
333 struct pcl818_private
*devpriv
= dev
->private;
334 struct comedi_isadma
*dma
= devpriv
->dma
;
335 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
336 unsigned int max_samples
= comedi_bytes_to_samples(s
, desc
->maxsize
);
337 unsigned int nsamples
;
339 comedi_isadma_disable(dma
->chan
);
342 * Determine dma size based on the buffer maxsize plus the number of
343 * unread samples and the number of samples remaining in the command.
345 nsamples
= comedi_nsamples_left(s
, max_samples
+ unread_samples
);
346 if (nsamples
> unread_samples
) {
347 nsamples
-= unread_samples
;
348 desc
->size
= comedi_samples_to_bytes(s
, nsamples
);
349 comedi_isadma_program(desc
);
353 static void pcl818_ai_set_chan_range(struct comedi_device
*dev
,
357 outb(chan
, dev
->iobase
+ PCL818_MUX_REG
);
358 outb(range
, dev
->iobase
+ PCL818_RANGE_REG
);
361 static void pcl818_ai_set_chan_scan(struct comedi_device
*dev
,
362 unsigned int first_chan
,
363 unsigned int last_chan
)
365 outb(PCL818_MUX_SCAN(first_chan
, last_chan
),
366 dev
->iobase
+ PCL818_MUX_REG
);
369 static void pcl818_ai_setup_chanlist(struct comedi_device
*dev
,
370 unsigned int *chanlist
,
373 struct pcl818_private
*devpriv
= dev
->private;
374 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
375 unsigned int last_chan
;
379 devpriv
->act_chanlist_len
= seglen
;
380 devpriv
->act_chanlist_pos
= 0;
382 /* store range list to card */
383 for (i
= 0; i
< seglen
; i
++) {
384 last_chan
= CR_CHAN(chanlist
[i
]);
385 range
= CR_RANGE(chanlist
[i
]);
387 devpriv
->act_chanlist
[i
] = last_chan
;
389 pcl818_ai_set_chan_range(dev
, last_chan
, range
);
394 pcl818_ai_set_chan_scan(dev
, first_chan
, last_chan
);
397 static void pcl818_ai_clear_eoc(struct comedi_device
*dev
)
399 /* writing any value clears the interrupt request */
400 outb(0, dev
->iobase
+ PCL818_STATUS_REG
);
403 static void pcl818_ai_soft_trig(struct comedi_device
*dev
)
405 /* writing any value triggers a software conversion */
406 outb(0, dev
->iobase
+ PCL818_AI_LSB_REG
);
409 static unsigned int pcl818_ai_get_fifo_sample(struct comedi_device
*dev
,
410 struct comedi_subdevice
*s
,
415 val
= inb(dev
->iobase
+ PCL818_FI_DATALO
);
416 val
|= (inb(dev
->iobase
+ PCL818_FI_DATAHI
) << 8);
421 return (val
>> 4) & s
->maxdata
;
424 static unsigned int pcl818_ai_get_sample(struct comedi_device
*dev
,
425 struct comedi_subdevice
*s
,
430 val
= inb(dev
->iobase
+ PCL818_AI_MSB_REG
) << 8;
431 val
|= inb(dev
->iobase
+ PCL818_AI_LSB_REG
);
436 return (val
>> 4) & s
->maxdata
;
439 static int pcl818_ai_eoc(struct comedi_device
*dev
,
440 struct comedi_subdevice
*s
,
441 struct comedi_insn
*insn
,
442 unsigned long context
)
446 status
= inb(dev
->iobase
+ PCL818_STATUS_REG
);
447 if (status
& PCL818_STATUS_INT
)
452 static bool pcl818_ai_write_sample(struct comedi_device
*dev
,
453 struct comedi_subdevice
*s
,
454 unsigned int chan
, unsigned int val
)
456 struct pcl818_private
*devpriv
= dev
->private;
457 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
458 unsigned int expected_chan
;
460 expected_chan
= devpriv
->act_chanlist
[devpriv
->act_chanlist_pos
];
461 if (chan
!= expected_chan
) {
462 dev_dbg(dev
->class_dev
,
463 "A/D mode1/3 %s - channel dropout %d!=%d !\n",
464 (devpriv
->dma
) ? "DMA" :
465 (devpriv
->usefifo
) ? "FIFO" : "IRQ",
466 chan
, expected_chan
);
467 s
->async
->events
|= COMEDI_CB_ERROR
;
471 comedi_buf_write_samples(s
, &val
, 1);
473 devpriv
->act_chanlist_pos
++;
474 if (devpriv
->act_chanlist_pos
>= devpriv
->act_chanlist_len
)
475 devpriv
->act_chanlist_pos
= 0;
477 if (cmd
->stop_src
== TRIG_COUNT
&&
478 s
->async
->scans_done
>= cmd
->stop_arg
) {
479 s
->async
->events
|= COMEDI_CB_EOA
;
486 static void pcl818_handle_eoc(struct comedi_device
*dev
,
487 struct comedi_subdevice
*s
)
492 if (pcl818_ai_eoc(dev
, s
, NULL
, 0)) {
493 dev_err(dev
->class_dev
, "A/D mode1/3 IRQ without DRDY!\n");
494 s
->async
->events
|= COMEDI_CB_ERROR
;
498 val
= pcl818_ai_get_sample(dev
, s
, &chan
);
499 pcl818_ai_write_sample(dev
, s
, chan
, val
);
502 static void pcl818_handle_dma(struct comedi_device
*dev
,
503 struct comedi_subdevice
*s
)
505 struct pcl818_private
*devpriv
= dev
->private;
506 struct comedi_isadma
*dma
= devpriv
->dma
;
507 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
508 unsigned short *ptr
= desc
->virt_addr
;
509 unsigned int nsamples
= comedi_bytes_to_samples(s
, desc
->size
);
514 /* restart dma with the next buffer */
515 dma
->cur_dma
= 1 - dma
->cur_dma
;
516 pcl818_ai_setup_dma(dev
, s
, nsamples
);
518 for (i
= 0; i
< nsamples
; i
++) {
521 val
= (val
>> 4) & s
->maxdata
;
522 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
527 static void pcl818_handle_fifo(struct comedi_device
*dev
,
528 struct comedi_subdevice
*s
)
535 status
= inb(dev
->iobase
+ PCL818_FI_STATUS
);
538 dev_err(dev
->class_dev
, "A/D mode1/3 FIFO overflow!\n");
539 s
->async
->events
|= COMEDI_CB_ERROR
;
544 dev_err(dev
->class_dev
,
545 "A/D mode1/3 FIFO interrupt without data!\n");
546 s
->async
->events
|= COMEDI_CB_ERROR
;
555 for (i
= 0; i
< len
; i
++) {
556 val
= pcl818_ai_get_fifo_sample(dev
, s
, &chan
);
557 if (!pcl818_ai_write_sample(dev
, s
, chan
, val
))
562 static irqreturn_t
pcl818_interrupt(int irq
, void *d
)
564 struct comedi_device
*dev
= d
;
565 struct pcl818_private
*devpriv
= dev
->private;
566 struct comedi_subdevice
*s
= dev
->read_subdev
;
567 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
569 if (!dev
->attached
|| !devpriv
->ai_cmd_running
) {
570 pcl818_ai_clear_eoc(dev
);
574 if (devpriv
->ai_cmd_canceled
) {
576 * The cleanup from ai_cancel() has been delayed
577 * until now because the card doesn't seem to like
578 * being reprogrammed while a DMA transfer is in
581 s
->async
->scans_done
= cmd
->stop_arg
;
587 pcl818_handle_dma(dev
, s
);
588 else if (devpriv
->usefifo
)
589 pcl818_handle_fifo(dev
, s
);
591 pcl818_handle_eoc(dev
, s
);
593 pcl818_ai_clear_eoc(dev
);
595 comedi_handle_events(dev
, s
);
599 static int check_channel_list(struct comedi_device
*dev
,
600 struct comedi_subdevice
*s
,
601 unsigned int *chanlist
, unsigned int n_chan
)
603 unsigned int chansegment
[16];
604 unsigned int i
, nowmustbechan
, seglen
, segpos
;
606 /* correct channel and range number check itself comedi/range.c */
608 dev_err(dev
->class_dev
, "range/channel list is empty!\n");
613 /* first channel is every time ok */
614 chansegment
[0] = chanlist
[0];
615 /* build part of chanlist */
616 for (i
= 1, seglen
= 1; i
< n_chan
; i
++, seglen
++) {
617 /* we detect loop, this must by finish */
619 if (chanlist
[0] == chanlist
[i
])
622 (CR_CHAN(chansegment
[i
- 1]) + 1) % s
->n_chan
;
623 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
624 /* channel list isn't continuous :-( */
625 dev_dbg(dev
->class_dev
,
626 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
627 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
628 CR_CHAN(chanlist
[0]));
631 /* well, this is next correct channel in list */
632 chansegment
[i
] = chanlist
[i
];
635 /* check whole chanlist */
636 for (i
= 0, segpos
= 0; i
< n_chan
; i
++) {
637 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
638 dev_dbg(dev
->class_dev
,
639 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
640 i
, CR_CHAN(chansegment
[i
]),
641 CR_RANGE(chansegment
[i
]),
642 CR_AREF(chansegment
[i
]),
643 CR_CHAN(chanlist
[i
% seglen
]),
644 CR_RANGE(chanlist
[i
% seglen
]),
645 CR_AREF(chansegment
[i
% seglen
]));
646 return 0; /* chan/gain list is strange */
655 static int check_single_ended(unsigned int port
)
657 if (inb(port
+ PCL818_STATUS_REG
) & PCL818_STATUS_MUX
)
662 static int ai_cmdtest(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
663 struct comedi_cmd
*cmd
)
665 const struct pcl818_board
*board
= dev
->board_ptr
;
666 struct pcl818_private
*devpriv
= dev
->private;
670 /* Step 1 : check if triggers are trivially valid */
672 err
|= cfc_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
673 err
|= cfc_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
674 err
|= cfc_check_trigger_src(&cmd
->convert_src
, TRIG_TIMER
| TRIG_EXT
);
675 err
|= cfc_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
676 err
|= cfc_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
681 /* Step 2a : make sure trigger sources are unique */
683 err
|= cfc_check_trigger_is_unique(cmd
->convert_src
);
684 err
|= cfc_check_trigger_is_unique(cmd
->stop_src
);
686 /* Step 2b : and mutually compatible */
691 /* Step 3: check if arguments are trivially valid */
693 err
|= cfc_check_trigger_arg_is(&cmd
->start_arg
, 0);
694 err
|= cfc_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
696 if (cmd
->convert_src
== TRIG_TIMER
)
697 err
|= cfc_check_trigger_arg_min(&cmd
->convert_arg
,
700 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, 0);
702 err
|= cfc_check_trigger_arg_is(&cmd
->scan_end_arg
, cmd
->chanlist_len
);
704 if (cmd
->stop_src
== TRIG_COUNT
)
705 err
|= cfc_check_trigger_arg_min(&cmd
->stop_arg
, 1);
707 err
|= cfc_check_trigger_arg_is(&cmd
->stop_arg
, 0);
712 /* step 4: fix up any arguments */
714 if (cmd
->convert_src
== TRIG_TIMER
) {
715 arg
= cmd
->convert_arg
;
716 i8253_cascade_ns_to_timer(devpriv
->i8253_osc_base
,
720 err
|= cfc_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
726 /* step 5: complain about special chanlist considerations */
729 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
731 return 5; /* incorrect channels list */
737 static int pcl818_ai_cmd(struct comedi_device
*dev
,
738 struct comedi_subdevice
*s
)
740 struct pcl818_private
*devpriv
= dev
->private;
741 struct comedi_isadma
*dma
= devpriv
->dma
;
742 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
743 unsigned int ctrl
= 0;
746 if (devpriv
->ai_cmd_running
)
749 pcl818_start_pacer(dev
, false);
751 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
754 pcl818_ai_setup_chanlist(dev
, cmd
->chanlist
, seglen
);
756 devpriv
->ai_cmd_running
= 1;
757 devpriv
->ai_cmd_canceled
= 0;
758 devpriv
->act_chanlist_pos
= 0;
760 if (cmd
->convert_src
== TRIG_TIMER
)
761 ctrl
|= PCL818_CTRL_PACER_TRIG
;
763 ctrl
|= PCL818_CTRL_EXT_TRIG
;
765 outb(PCL818_CNTENABLE_PACER_ENA
, dev
->iobase
+ PCL818_CNTENABLE_REG
);
768 /* setup and enable dma for the first buffer */
770 pcl818_ai_setup_dma(dev
, s
, 0);
772 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
) |
774 } else if (devpriv
->usefifo
) {
776 outb(1, dev
->iobase
+ PCL818_FI_ENABLE
);
778 ctrl
|= PCL818_CTRL_INTE
| PCL818_CTRL_IRQ(dev
->irq
);
780 outb(ctrl
, dev
->iobase
+ PCL818_CTRL_REG
);
782 if (cmd
->convert_src
== TRIG_TIMER
)
783 pcl818_start_pacer(dev
, true);
788 static int pcl818_ai_cancel(struct comedi_device
*dev
,
789 struct comedi_subdevice
*s
)
791 struct pcl818_private
*devpriv
= dev
->private;
792 struct comedi_isadma
*dma
= devpriv
->dma
;
793 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
795 if (!devpriv
->ai_cmd_running
)
799 if (cmd
->stop_src
== TRIG_NONE
||
800 (cmd
->stop_src
== TRIG_COUNT
&&
801 s
->async
->scans_done
< cmd
->stop_arg
)) {
802 if (!devpriv
->ai_cmd_canceled
) {
804 * Wait for running dma transfer to end,
805 * do cleanup in interrupt.
807 devpriv
->ai_cmd_canceled
= 1;
811 comedi_isadma_disable(dma
->chan
);
814 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
815 pcl818_start_pacer(dev
, false);
816 pcl818_ai_clear_eoc(dev
);
818 if (devpriv
->usefifo
) { /* FIFO shutdown */
819 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
820 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
821 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
823 devpriv
->ai_cmd_running
= 0;
824 devpriv
->ai_cmd_canceled
= 0;
829 static int pcl818_ai_insn_read(struct comedi_device
*dev
,
830 struct comedi_subdevice
*s
,
831 struct comedi_insn
*insn
,
834 unsigned int chan
= CR_CHAN(insn
->chanspec
);
835 unsigned int range
= CR_RANGE(insn
->chanspec
);
839 outb(PCL818_CTRL_SOFT_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
841 pcl818_ai_set_chan_range(dev
, chan
, range
);
842 pcl818_ai_set_chan_scan(dev
, chan
, chan
);
844 for (i
= 0; i
< insn
->n
; i
++) {
845 pcl818_ai_clear_eoc(dev
);
846 pcl818_ai_soft_trig(dev
);
848 ret
= comedi_timeout(dev
, s
, insn
, pcl818_ai_eoc
, 0);
852 data
[i
] = pcl818_ai_get_sample(dev
, s
, NULL
);
854 pcl818_ai_clear_eoc(dev
);
856 return ret
? ret
: insn
->n
;
859 static int pcl818_ao_insn_write(struct comedi_device
*dev
,
860 struct comedi_subdevice
*s
,
861 struct comedi_insn
*insn
,
864 unsigned int chan
= CR_CHAN(insn
->chanspec
);
865 unsigned int val
= s
->readback
[chan
];
868 for (i
= 0; i
< insn
->n
; i
++) {
870 outb((val
& 0x000f) << 4,
871 dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
872 outb((val
& 0x0ff0) >> 4,
873 dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
875 s
->readback
[chan
] = val
;
880 static int pcl818_di_insn_bits(struct comedi_device
*dev
,
881 struct comedi_subdevice
*s
,
882 struct comedi_insn
*insn
,
885 data
[1] = inb(dev
->iobase
+ PCL818_DO_DI_LSB_REG
) |
886 (inb(dev
->iobase
+ PCL818_DO_DI_MSB_REG
) << 8);
891 static int pcl818_do_insn_bits(struct comedi_device
*dev
,
892 struct comedi_subdevice
*s
,
893 struct comedi_insn
*insn
,
896 if (comedi_dio_update_state(s
, data
)) {
897 outb(s
->state
& 0xff, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
898 outb((s
->state
>> 8), dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
906 static void pcl818_reset(struct comedi_device
*dev
)
908 const struct pcl818_board
*board
= dev
->board_ptr
;
909 unsigned long timer_base
= dev
->iobase
+ PCL818_TIMER_BASE
;
912 /* flush and disable the FIFO */
913 if (board
->has_fifo
) {
914 outb(0, dev
->iobase
+ PCL818_FI_INTCLR
);
915 outb(0, dev
->iobase
+ PCL818_FI_FLUSH
);
916 outb(0, dev
->iobase
+ PCL818_FI_ENABLE
);
919 /* disable analog input trigger */
920 outb(PCL818_CTRL_DISABLE_TRIG
, dev
->iobase
+ PCL818_CTRL_REG
);
921 pcl818_ai_clear_eoc(dev
);
923 pcl818_ai_set_chan_range(dev
, 0, 0);
926 outb(PCL818_CNTENABLE_PACER_ENA
, dev
->iobase
+ PCL818_CNTENABLE_REG
);
927 i8254_set_mode(timer_base
, 0, 2, I8254_MODE0
| I8254_BINARY
);
928 i8254_set_mode(timer_base
, 0, 1, I8254_MODE0
| I8254_BINARY
);
929 i8254_set_mode(timer_base
, 0, 0, I8254_MODE0
| I8254_BINARY
);
931 /* set analog output channels to 0V */
932 for (chan
= 0; chan
< board
->n_aochan
; chan
++) {
933 outb(0, dev
->iobase
+ PCL818_AO_LSB_REG(chan
));
934 outb(0, dev
->iobase
+ PCL818_AO_MSB_REG(chan
));
937 /* set all digital outputs low */
938 outb(0, dev
->iobase
+ PCL818_DO_DI_MSB_REG
);
939 outb(0, dev
->iobase
+ PCL818_DO_DI_LSB_REG
);
942 static void pcl818_set_ai_range_table(struct comedi_device
*dev
,
943 struct comedi_subdevice
*s
,
944 struct comedi_devconfig
*it
)
946 const struct pcl818_board
*board
= dev
->board_ptr
;
948 /* default to the range table from the boardinfo */
949 s
->range_table
= board
->ai_range_type
;
951 /* now check the user config option based on the boardtype */
953 if (it
->options
[4] == 1 || it
->options
[4] == 10) {
954 /* secondary range list jumper selectable */
955 s
->range_table
= &range_pcl818l_h_ai
;
958 switch (it
->options
[4]) {
960 s
->range_table
= &range_bipolar10
;
963 s
->range_table
= &range_bipolar5
;
966 s
->range_table
= &range_bipolar2_5
;
969 s
->range_table
= &range718_bipolar1
;
972 s
->range_table
= &range718_bipolar0_5
;
975 s
->range_table
= &range_unipolar10
;
978 s
->range_table
= &range_unipolar5
;
981 s
->range_table
= &range718_unipolar2
;
984 s
->range_table
= &range718_unipolar1
;
987 s
->range_table
= &range_unknown
;
993 static void pcl818_alloc_dma(struct comedi_device
*dev
, unsigned int dma_chan
)
995 struct pcl818_private
*devpriv
= dev
->private;
997 /* only DMA channels 3 and 1 are valid */
998 if (!(dma_chan
== 3 || dma_chan
== 1))
1001 /* DMA uses two 16K buffers */
1002 devpriv
->dma
= comedi_isadma_alloc(dev
, 2, dma_chan
, dma_chan
,
1003 PAGE_SIZE
* 4, COMEDI_ISADMA_READ
);
1006 static void pcl818_free_dma(struct comedi_device
*dev
)
1008 struct pcl818_private
*devpriv
= dev
->private;
1011 comedi_isadma_free(devpriv
->dma
);
1014 static int pcl818_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
1016 const struct pcl818_board
*board
= dev
->board_ptr
;
1017 struct pcl818_private
*devpriv
;
1018 struct comedi_subdevice
*s
;
1021 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
1025 ret
= comedi_request_region(dev
, it
->options
[0],
1026 board
->has_fifo
? 0x20 : 0x10);
1030 /* we can use IRQ 2-7 for async command support */
1031 if (it
->options
[1] >= 2 && it
->options
[1] <= 7) {
1032 ret
= request_irq(it
->options
[1], pcl818_interrupt
, 0,
1033 dev
->board_name
, dev
);
1035 dev
->irq
= it
->options
[1];
1038 /* should we use the FIFO? */
1039 if (dev
->irq
&& board
->has_fifo
&& it
->options
[2] == -1)
1040 devpriv
->usefifo
= 1;
1042 /* we need an IRQ to do DMA on channel 3 or 1 */
1043 if (dev
->irq
&& board
->has_dma
)
1044 pcl818_alloc_dma(dev
, it
->options
[2]);
1046 ret
= comedi_alloc_subdevices(dev
, 4);
1050 s
= &dev
->subdevices
[0];
1051 s
->type
= COMEDI_SUBD_AI
;
1052 s
->subdev_flags
= SDF_READABLE
;
1053 if (check_single_ended(dev
->iobase
)) {
1055 s
->subdev_flags
|= SDF_COMMON
| SDF_GROUND
;
1058 s
->subdev_flags
|= SDF_DIFF
;
1060 s
->maxdata
= 0x0fff;
1062 pcl818_set_ai_range_table(dev
, s
, it
);
1064 s
->insn_read
= pcl818_ai_insn_read
;
1066 dev
->read_subdev
= s
;
1067 s
->subdev_flags
|= SDF_CMD_READ
;
1068 s
->len_chanlist
= s
->n_chan
;
1069 s
->do_cmdtest
= ai_cmdtest
;
1070 s
->do_cmd
= pcl818_ai_cmd
;
1071 s
->cancel
= pcl818_ai_cancel
;
1074 /* Analog Output subdevice */
1075 s
= &dev
->subdevices
[1];
1076 if (board
->n_aochan
) {
1077 s
->type
= COMEDI_SUBD_AO
;
1078 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
1079 s
->n_chan
= board
->n_aochan
;
1080 s
->maxdata
= 0x0fff;
1081 s
->range_table
= &range_unipolar5
;
1082 if (board
->is_818
) {
1083 if ((it
->options
[4] == 1) || (it
->options
[4] == 10))
1084 s
->range_table
= &range_unipolar10
;
1085 if (it
->options
[4] == 2)
1086 s
->range_table
= &range_unknown
;
1088 if ((it
->options
[5] == 1) || (it
->options
[5] == 10))
1089 s
->range_table
= &range_unipolar10
;
1090 if (it
->options
[5] == 2)
1091 s
->range_table
= &range_unknown
;
1093 s
->insn_write
= pcl818_ao_insn_write
;
1095 ret
= comedi_alloc_subdev_readback(s
);
1099 s
->type
= COMEDI_SUBD_UNUSED
;
1102 /* Digital Input subdevice */
1103 s
= &dev
->subdevices
[2];
1104 s
->type
= COMEDI_SUBD_DI
;
1105 s
->subdev_flags
= SDF_READABLE
;
1108 s
->range_table
= &range_digital
;
1109 s
->insn_bits
= pcl818_di_insn_bits
;
1111 /* Digital Output subdevice */
1112 s
= &dev
->subdevices
[3];
1113 s
->type
= COMEDI_SUBD_DO
;
1114 s
->subdev_flags
= SDF_WRITABLE
;
1117 s
->range_table
= &range_digital
;
1118 s
->insn_bits
= pcl818_do_insn_bits
;
1120 /* select 1/10MHz oscilator */
1121 if ((it
->options
[3] == 0) || (it
->options
[3] == 10))
1122 devpriv
->i8253_osc_base
= I8254_OSC_BASE_10MHZ
;
1124 devpriv
->i8253_osc_base
= I8254_OSC_BASE_1MHZ
;
1126 /* max sampling speed */
1127 devpriv
->ns_min
= board
->ns_min
;
1129 if (!board
->is_818
) {
1130 if ((it
->options
[6] == 1) || (it
->options
[6] == 100)) {
1131 /* extended PCL718 to 100kHz DAC */
1132 devpriv
->ns_min
= 10000;
1141 static void pcl818_detach(struct comedi_device
*dev
)
1143 struct pcl818_private
*devpriv
= dev
->private;
1146 pcl818_ai_cancel(dev
, dev
->read_subdev
);
1149 pcl818_free_dma(dev
);
1150 comedi_legacy_detach(dev
);
1153 static struct comedi_driver pcl818_driver
= {
1154 .driver_name
= "pcl818",
1155 .module
= THIS_MODULE
,
1156 .attach
= pcl818_attach
,
1157 .detach
= pcl818_detach
,
1158 .board_name
= &boardtypes
[0].name
,
1159 .num_names
= ARRAY_SIZE(boardtypes
),
1160 .offset
= sizeof(struct pcl818_board
),
1162 module_comedi_driver(pcl818_driver
);
1164 MODULE_AUTHOR("Comedi http://www.comedi.org");
1165 MODULE_DESCRIPTION("Comedi low-level driver");
1166 MODULE_LICENSE("GPL");