1 // SPDX-License-Identifier: GPL-2.0
4 * Comedi driver for Advantech PCL-816 cards
6 * Author: Juan Grigera <juan@grigera.com.ar>
7 * based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
12 * Description: Advantech PCL-816 cards, PCL-814
13 * Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
14 * Author: Juan Grigera <juan@grigera.com.ar>
16 * Updated: Tue, 2 Apr 2002 23:15:21 -0800
18 * PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
19 * Differences are at resolution (16 vs 12 bits).
21 * The driver support AI command mode, other subdevices not written.
23 * Analog output and digital input and output are not supported.
25 * Configuration Options:
27 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
28 * [2] - DMA (0=disable, 1, 3)
29 * [3] - 0, 10=10MHz clock for 8254
30 * 1= 1MHz clock for 8254
33 #include <linux/module.h>
34 #include <linux/gfp.h>
35 #include <linux/delay.h>
37 #include <linux/interrupt.h>
39 #include "../comedidev.h"
41 #include "comedi_isadma.h"
42 #include "comedi_8254.h"
47 #define PCL816_DO_DI_LSB_REG 0x00
48 #define PCL816_DO_DI_MSB_REG 0x01
49 #define PCL816_TIMER_BASE 0x04
50 #define PCL816_AI_LSB_REG 0x08
51 #define PCL816_AI_MSB_REG 0x09
52 #define PCL816_RANGE_REG 0x09
53 #define PCL816_CLRINT_REG 0x0a
54 #define PCL816_MUX_REG 0x0b
55 #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
56 #define PCL816_CTRL_REG 0x0c
57 #define PCL816_CTRL_SOFT_TRIG BIT(0)
58 #define PCL816_CTRL_PACER_TRIG BIT(1)
59 #define PCL816_CTRL_EXT_TRIG BIT(2)
60 #define PCL816_CTRL_POE BIT(3)
61 #define PCL816_CTRL_DMAEN BIT(4)
62 #define PCL816_CTRL_INTEN BIT(5)
63 #define PCL816_CTRL_DMASRC_SLOT(x) (((x) & 0x3) << 6)
64 #define PCL816_STATUS_REG 0x0d
65 #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
66 #define PCL816_STATUS_INTSRC_SLOT(x) (((x) & 0x3) << 4)
67 #define PCL816_STATUS_INTSRC_DMA PCL816_STATUS_INTSRC_SLOT(3)
68 #define PCL816_STATUS_INTSRC_MASK PCL816_STATUS_INTSRC_SLOT(3)
69 #define PCL816_STATUS_INTACT BIT(6)
70 #define PCL816_STATUS_DRDY BIT(7)
72 #define MAGIC_DMA_WORD 0x5a5a
74 static const struct comedi_lrange range_pcl816
= {
93 static const struct pcl816_board boardtypes
[] = {
100 .ai_maxdata
= 0x3fff,
105 struct pcl816_private
{
106 struct comedi_isadma
*dma
;
107 unsigned int ai_poll_ptr
; /* how many sampes transfer poll */
108 unsigned int ai_cmd_running
:1;
109 unsigned int ai_cmd_canceled
:1;
112 static void pcl816_ai_setup_dma(struct comedi_device
*dev
,
113 struct comedi_subdevice
*s
,
114 unsigned int unread_samples
)
116 struct pcl816_private
*devpriv
= dev
->private;
117 struct comedi_isadma
*dma
= devpriv
->dma
;
118 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
119 unsigned int max_samples
= comedi_bytes_to_samples(s
, desc
->maxsize
);
120 unsigned int nsamples
;
122 comedi_isadma_disable(dma
->chan
);
125 * Determine dma size based on the buffer maxsize plus the number of
126 * unread samples and the number of samples remaining in the command.
128 nsamples
= comedi_nsamples_left(s
, max_samples
+ unread_samples
);
129 if (nsamples
> unread_samples
) {
130 nsamples
-= unread_samples
;
131 desc
->size
= comedi_samples_to_bytes(s
, nsamples
);
132 comedi_isadma_program(desc
);
136 static void pcl816_ai_set_chan_range(struct comedi_device
*dev
,
140 outb(chan
, dev
->iobase
+ PCL816_MUX_REG
);
141 outb(range
, dev
->iobase
+ PCL816_RANGE_REG
);
144 static void pcl816_ai_set_chan_scan(struct comedi_device
*dev
,
145 unsigned int first_chan
,
146 unsigned int last_chan
)
148 outb(PCL816_MUX_SCAN(first_chan
, last_chan
),
149 dev
->iobase
+ PCL816_MUX_REG
);
152 static void pcl816_ai_setup_chanlist(struct comedi_device
*dev
,
153 unsigned int *chanlist
,
156 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
157 unsigned int last_chan
;
161 /* store range list to card */
162 for (i
= 0; i
< seglen
; i
++) {
163 last_chan
= CR_CHAN(chanlist
[i
]);
164 range
= CR_RANGE(chanlist
[i
]);
166 pcl816_ai_set_chan_range(dev
, last_chan
, range
);
171 pcl816_ai_set_chan_scan(dev
, first_chan
, last_chan
);
174 static void pcl816_ai_clear_eoc(struct comedi_device
*dev
)
176 /* writing any value clears the interrupt request */
177 outb(0, dev
->iobase
+ PCL816_CLRINT_REG
);
180 static void pcl816_ai_soft_trig(struct comedi_device
*dev
)
182 /* writing any value triggers a software conversion */
183 outb(0, dev
->iobase
+ PCL816_AI_LSB_REG
);
186 static unsigned int pcl816_ai_get_sample(struct comedi_device
*dev
,
187 struct comedi_subdevice
*s
)
191 val
= inb(dev
->iobase
+ PCL816_AI_MSB_REG
) << 8;
192 val
|= inb(dev
->iobase
+ PCL816_AI_LSB_REG
);
194 return val
& s
->maxdata
;
197 static int pcl816_ai_eoc(struct comedi_device
*dev
,
198 struct comedi_subdevice
*s
,
199 struct comedi_insn
*insn
,
200 unsigned long context
)
204 status
= inb(dev
->iobase
+ PCL816_STATUS_REG
);
205 if ((status
& PCL816_STATUS_DRDY
) == 0)
210 static bool pcl816_ai_next_chan(struct comedi_device
*dev
,
211 struct comedi_subdevice
*s
)
213 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
215 if (cmd
->stop_src
== TRIG_COUNT
&&
216 s
->async
->scans_done
>= cmd
->stop_arg
) {
217 s
->async
->events
|= COMEDI_CB_EOA
;
224 static void transfer_from_dma_buf(struct comedi_device
*dev
,
225 struct comedi_subdevice
*s
,
227 unsigned int bufptr
, unsigned int len
)
232 for (i
= 0; i
< len
; i
++) {
234 comedi_buf_write_samples(s
, &val
, 1);
236 if (!pcl816_ai_next_chan(dev
, s
))
241 static irqreturn_t
pcl816_interrupt(int irq
, void *d
)
243 struct comedi_device
*dev
= d
;
244 struct comedi_subdevice
*s
= dev
->read_subdev
;
245 struct pcl816_private
*devpriv
= dev
->private;
246 struct comedi_isadma
*dma
= devpriv
->dma
;
247 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
248 unsigned int nsamples
;
251 if (!dev
->attached
|| !devpriv
->ai_cmd_running
) {
252 pcl816_ai_clear_eoc(dev
);
256 if (devpriv
->ai_cmd_canceled
) {
257 devpriv
->ai_cmd_canceled
= 0;
258 pcl816_ai_clear_eoc(dev
);
262 nsamples
= comedi_bytes_to_samples(s
, desc
->size
) -
263 devpriv
->ai_poll_ptr
;
264 bufptr
= devpriv
->ai_poll_ptr
;
265 devpriv
->ai_poll_ptr
= 0;
267 /* restart dma with the next buffer */
268 dma
->cur_dma
= 1 - dma
->cur_dma
;
269 pcl816_ai_setup_dma(dev
, s
, nsamples
);
271 transfer_from_dma_buf(dev
, s
, desc
->virt_addr
, bufptr
, nsamples
);
273 pcl816_ai_clear_eoc(dev
);
275 comedi_handle_events(dev
, s
);
279 static int check_channel_list(struct comedi_device
*dev
,
280 struct comedi_subdevice
*s
,
281 unsigned int *chanlist
,
282 unsigned int chanlen
)
284 unsigned int chansegment
[16];
285 unsigned int i
, nowmustbechan
, seglen
;
287 /* correct channel and range number check itself comedi/range.c */
289 dev_err(dev
->class_dev
, "range/channel list is empty!\n");
294 /* first channel is every time ok */
295 chansegment
[0] = chanlist
[0];
296 for (i
= 1, seglen
= 1; i
< chanlen
; i
++, seglen
++) {
297 /* we detect loop, this must by finish */
298 if (chanlist
[0] == chanlist
[i
])
301 (CR_CHAN(chansegment
[i
- 1]) + 1) % chanlen
;
302 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
303 /* channel list isn't continuous :-( */
304 dev_dbg(dev
->class_dev
,
305 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
306 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
307 CR_CHAN(chanlist
[0]));
310 /* well, this is next correct channel in list */
311 chansegment
[i
] = chanlist
[i
];
314 /* check whole chanlist */
315 for (i
= 0; i
< chanlen
; i
++) {
316 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
317 dev_dbg(dev
->class_dev
,
318 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
319 i
, CR_CHAN(chansegment
[i
]),
320 CR_RANGE(chansegment
[i
]),
321 CR_AREF(chansegment
[i
]),
322 CR_CHAN(chanlist
[i
% seglen
]),
323 CR_RANGE(chanlist
[i
% seglen
]),
324 CR_AREF(chansegment
[i
% seglen
]));
325 return 0; /* chan/gain list is strange */
332 return seglen
; /* we can serve this with MUX logic */
335 static int pcl816_ai_cmdtest(struct comedi_device
*dev
,
336 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
340 /* Step 1 : check if triggers are trivially valid */
342 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
343 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
344 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
345 TRIG_EXT
| TRIG_TIMER
);
346 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
347 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
352 /* Step 2a : make sure trigger sources are unique */
354 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
355 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
357 /* Step 2b : and mutually compatible */
362 /* Step 3: check if arguments are trivially valid */
364 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
365 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
367 if (cmd
->convert_src
== TRIG_TIMER
)
368 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
, 10000);
370 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
372 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
375 if (cmd
->stop_src
== TRIG_COUNT
)
376 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
378 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
383 /* step 4: fix up any arguments */
384 if (cmd
->convert_src
== TRIG_TIMER
) {
385 unsigned int arg
= cmd
->convert_arg
;
387 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
388 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
394 /* step 5: complain about special chanlist considerations */
397 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
399 return 5; /* incorrect channels list */
405 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
407 struct pcl816_private
*devpriv
= dev
->private;
408 struct comedi_isadma
*dma
= devpriv
->dma
;
409 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
413 if (devpriv
->ai_cmd_running
)
416 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
419 pcl816_ai_setup_chanlist(dev
, cmd
->chanlist
, seglen
);
422 devpriv
->ai_cmd_running
= 1;
423 devpriv
->ai_poll_ptr
= 0;
424 devpriv
->ai_cmd_canceled
= 0;
426 /* setup and enable dma for the first buffer */
428 pcl816_ai_setup_dma(dev
, s
, 0);
430 comedi_8254_set_mode(dev
->pacer
, 0, I8254_MODE1
| I8254_BINARY
);
431 comedi_8254_write(dev
->pacer
, 0, 0x0ff);
433 comedi_8254_update_divisors(dev
->pacer
);
434 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
436 ctrl
= PCL816_CTRL_INTEN
| PCL816_CTRL_DMAEN
|
437 PCL816_CTRL_DMASRC_SLOT(0);
438 if (cmd
->convert_src
== TRIG_TIMER
)
439 ctrl
|= PCL816_CTRL_PACER_TRIG
;
441 ctrl
|= PCL816_CTRL_EXT_TRIG
;
443 outb(ctrl
, dev
->iobase
+ PCL816_CTRL_REG
);
444 outb((dma
->chan
<< 4) | dev
->irq
,
445 dev
->iobase
+ PCL816_STATUS_REG
);
450 static int pcl816_ai_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
452 struct pcl816_private
*devpriv
= dev
->private;
453 struct comedi_isadma
*dma
= devpriv
->dma
;
454 struct comedi_isadma_desc
*desc
;
459 spin_lock_irqsave(&dev
->spinlock
, flags
);
461 poll
= comedi_isadma_poll(dma
);
462 poll
= comedi_bytes_to_samples(s
, poll
);
463 if (poll
> devpriv
->ai_poll_ptr
) {
464 desc
= &dma
->desc
[dma
->cur_dma
];
465 transfer_from_dma_buf(dev
, s
, desc
->virt_addr
,
466 devpriv
->ai_poll_ptr
,
467 poll
- devpriv
->ai_poll_ptr
);
468 /* new buffer position */
469 devpriv
->ai_poll_ptr
= poll
;
471 comedi_handle_events(dev
, s
);
473 ret
= comedi_buf_n_bytes_ready(s
);
478 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
483 static int pcl816_ai_cancel(struct comedi_device
*dev
,
484 struct comedi_subdevice
*s
)
486 struct pcl816_private
*devpriv
= dev
->private;
488 if (!devpriv
->ai_cmd_running
)
491 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
492 pcl816_ai_clear_eoc(dev
);
494 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, false);
496 devpriv
->ai_cmd_running
= 0;
497 devpriv
->ai_cmd_canceled
= 1;
502 static int pcl816_ai_insn_read(struct comedi_device
*dev
,
503 struct comedi_subdevice
*s
,
504 struct comedi_insn
*insn
,
507 unsigned int chan
= CR_CHAN(insn
->chanspec
);
508 unsigned int range
= CR_RANGE(insn
->chanspec
);
512 outb(PCL816_CTRL_SOFT_TRIG
, dev
->iobase
+ PCL816_CTRL_REG
);
514 pcl816_ai_set_chan_range(dev
, chan
, range
);
515 pcl816_ai_set_chan_scan(dev
, chan
, chan
);
517 for (i
= 0; i
< insn
->n
; i
++) {
518 pcl816_ai_clear_eoc(dev
);
519 pcl816_ai_soft_trig(dev
);
521 ret
= comedi_timeout(dev
, s
, insn
, pcl816_ai_eoc
, 0);
525 data
[i
] = pcl816_ai_get_sample(dev
, s
);
527 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
528 pcl816_ai_clear_eoc(dev
);
530 return ret
? ret
: insn
->n
;
533 static int pcl816_di_insn_bits(struct comedi_device
*dev
,
534 struct comedi_subdevice
*s
,
535 struct comedi_insn
*insn
,
538 data
[1] = inb(dev
->iobase
+ PCL816_DO_DI_LSB_REG
) |
539 (inb(dev
->iobase
+ PCL816_DO_DI_MSB_REG
) << 8);
544 static int pcl816_do_insn_bits(struct comedi_device
*dev
,
545 struct comedi_subdevice
*s
,
546 struct comedi_insn
*insn
,
549 if (comedi_dio_update_state(s
, data
)) {
550 outb(s
->state
& 0xff, dev
->iobase
+ PCL816_DO_DI_LSB_REG
);
551 outb((s
->state
>> 8), dev
->iobase
+ PCL816_DO_DI_MSB_REG
);
559 static void pcl816_reset(struct comedi_device
*dev
)
561 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
562 pcl816_ai_set_chan_range(dev
, 0, 0);
563 pcl816_ai_clear_eoc(dev
);
565 /* set all digital outputs low */
566 outb(0, dev
->iobase
+ PCL816_DO_DI_LSB_REG
);
567 outb(0, dev
->iobase
+ PCL816_DO_DI_MSB_REG
);
570 static void pcl816_alloc_irq_and_dma(struct comedi_device
*dev
,
571 struct comedi_devconfig
*it
)
573 struct pcl816_private
*devpriv
= dev
->private;
574 unsigned int irq_num
= it
->options
[1];
575 unsigned int dma_chan
= it
->options
[2];
577 /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
578 if (!(irq_num
>= 2 && irq_num
<= 7) ||
579 !(dma_chan
== 3 || dma_chan
== 1))
582 if (request_irq(irq_num
, pcl816_interrupt
, 0, dev
->board_name
, dev
))
585 /* DMA uses two 16K buffers */
586 devpriv
->dma
= comedi_isadma_alloc(dev
, 2, dma_chan
, dma_chan
,
587 PAGE_SIZE
* 4, COMEDI_ISADMA_READ
);
589 free_irq(irq_num
, dev
);
594 static void pcl816_free_dma(struct comedi_device
*dev
)
596 struct pcl816_private
*devpriv
= dev
->private;
599 comedi_isadma_free(devpriv
->dma
);
602 static int pcl816_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
604 const struct pcl816_board
*board
= dev
->board_ptr
;
605 struct pcl816_private
*devpriv
;
606 struct comedi_subdevice
*s
;
609 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
613 ret
= comedi_request_region(dev
, it
->options
[0], 0x10);
617 /* an IRQ and DMA are required to support async commands */
618 pcl816_alloc_irq_and_dma(dev
, it
);
620 dev
->pacer
= comedi_8254_init(dev
->iobase
+ PCL816_TIMER_BASE
,
621 I8254_OSC_BASE_10MHZ
, I8254_IO8
, 0);
625 ret
= comedi_alloc_subdevices(dev
, 4);
629 s
= &dev
->subdevices
[0];
630 s
->type
= COMEDI_SUBD_AI
;
631 s
->subdev_flags
= SDF_CMD_READ
| SDF_DIFF
;
633 s
->maxdata
= board
->ai_maxdata
;
634 s
->range_table
= &range_pcl816
;
635 s
->insn_read
= pcl816_ai_insn_read
;
637 dev
->read_subdev
= s
;
638 s
->subdev_flags
|= SDF_CMD_READ
;
639 s
->len_chanlist
= board
->ai_chanlist
;
640 s
->do_cmdtest
= pcl816_ai_cmdtest
;
641 s
->do_cmd
= pcl816_ai_cmd
;
642 s
->poll
= pcl816_ai_poll
;
643 s
->cancel
= pcl816_ai_cancel
;
646 /* Piggyback Slot1 subdevice */
647 s
= &dev
->subdevices
[1];
648 s
->type
= COMEDI_SUBD_UNUSED
;
650 /* Digital Input subdevice */
651 s
= &dev
->subdevices
[2];
652 s
->type
= COMEDI_SUBD_DI
;
653 s
->subdev_flags
= SDF_READABLE
;
656 s
->range_table
= &range_digital
;
657 s
->insn_bits
= pcl816_di_insn_bits
;
659 /* Digital Output subdevice */
660 s
= &dev
->subdevices
[3];
661 s
->type
= COMEDI_SUBD_DO
;
662 s
->subdev_flags
= SDF_WRITABLE
;
665 s
->range_table
= &range_digital
;
666 s
->insn_bits
= pcl816_do_insn_bits
;
673 static void pcl816_detach(struct comedi_device
*dev
)
676 pcl816_ai_cancel(dev
, dev
->read_subdev
);
679 pcl816_free_dma(dev
);
680 comedi_legacy_detach(dev
);
683 static struct comedi_driver pcl816_driver
= {
684 .driver_name
= "pcl816",
685 .module
= THIS_MODULE
,
686 .attach
= pcl816_attach
,
687 .detach
= pcl816_detach
,
688 .board_name
= &boardtypes
[0].name
,
689 .num_names
= ARRAY_SIZE(boardtypes
),
690 .offset
= sizeof(struct pcl816_board
),
692 module_comedi_driver(pcl816_driver
);
694 MODULE_AUTHOR("Comedi http://www.comedi.org");
695 MODULE_DESCRIPTION("Comedi low-level driver");
696 MODULE_LICENSE("GPL");