3 * Comedi driver for Advantech PCL-816 cards
5 * Author: Juan Grigera <juan@grigera.com.ar>
6 * based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
11 * Description: Advantech PCL-816 cards, PCL-814
12 * Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
13 * Author: Juan Grigera <juan@grigera.com.ar>
15 * Updated: Tue, 2 Apr 2002 23:15:21 -0800
17 * PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
18 * Differences are at resolution (16 vs 12 bits).
20 * The driver support AI command mode, other subdevices not written.
22 * Analog output and digital input and output are not supported.
24 * Configuration Options:
26 * [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
27 * [2] - DMA (0=disable, 1, 3)
28 * [3] - 0, 10=10MHz clock for 8254
29 * 1= 1MHz clock for 8254
32 #include <linux/module.h>
33 #include <linux/gfp.h>
34 #include <linux/delay.h>
36 #include <linux/interrupt.h>
38 #include "../comedidev.h"
40 #include "comedi_isadma.h"
41 #include "comedi_8254.h"
46 #define PCL816_DO_DI_LSB_REG 0x00
47 #define PCL816_DO_DI_MSB_REG 0x01
48 #define PCL816_TIMER_BASE 0x04
49 #define PCL816_AI_LSB_REG 0x08
50 #define PCL816_AI_MSB_REG 0x09
51 #define PCL816_RANGE_REG 0x09
52 #define PCL816_CLRINT_REG 0x0a
53 #define PCL816_MUX_REG 0x0b
54 #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
55 #define PCL816_CTRL_REG 0x0c
56 #define PCL816_CTRL_SOFT_TRIG BIT(0)
57 #define PCL816_CTRL_PACER_TRIG BIT(1)
58 #define PCL816_CTRL_EXT_TRIG BIT(2)
59 #define PCL816_CTRL_POE BIT(3)
60 #define PCL816_CTRL_DMAEN BIT(4)
61 #define PCL816_CTRL_INTEN BIT(5)
62 #define PCL816_CTRL_DMASRC_SLOT(x) (((x) & 0x3) << 6)
63 #define PCL816_STATUS_REG 0x0d
64 #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
65 #define PCL816_STATUS_INTSRC_SLOT(x) (((x) & 0x3) << 4)
66 #define PCL816_STATUS_INTSRC_DMA PCL816_STATUS_INTSRC_SLOT(3)
67 #define PCL816_STATUS_INTSRC_MASK PCL816_STATUS_INTSRC_SLOT(3)
68 #define PCL816_STATUS_INTACT BIT(6)
69 #define PCL816_STATUS_DRDY BIT(7)
71 #define MAGIC_DMA_WORD 0x5a5a
73 static const struct comedi_lrange range_pcl816
= {
92 static const struct pcl816_board boardtypes
[] = {
104 struct pcl816_private
{
105 struct comedi_isadma
*dma
;
106 unsigned int ai_poll_ptr
; /* how many sampes transfer poll */
107 unsigned int ai_cmd_running
:1;
108 unsigned int ai_cmd_canceled
:1;
111 static void pcl816_ai_setup_dma(struct comedi_device
*dev
,
112 struct comedi_subdevice
*s
,
113 unsigned int unread_samples
)
115 struct pcl816_private
*devpriv
= dev
->private;
116 struct comedi_isadma
*dma
= devpriv
->dma
;
117 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
118 unsigned int max_samples
= comedi_bytes_to_samples(s
, desc
->maxsize
);
119 unsigned int nsamples
;
121 comedi_isadma_disable(dma
->chan
);
124 * Determine dma size based on the buffer maxsize plus the number of
125 * unread samples and the number of samples remaining in the command.
127 nsamples
= comedi_nsamples_left(s
, max_samples
+ unread_samples
);
128 if (nsamples
> unread_samples
) {
129 nsamples
-= unread_samples
;
130 desc
->size
= comedi_samples_to_bytes(s
, nsamples
);
131 comedi_isadma_program(desc
);
135 static void pcl816_ai_set_chan_range(struct comedi_device
*dev
,
139 outb(chan
, dev
->iobase
+ PCL816_MUX_REG
);
140 outb(range
, dev
->iobase
+ PCL816_RANGE_REG
);
143 static void pcl816_ai_set_chan_scan(struct comedi_device
*dev
,
144 unsigned int first_chan
,
145 unsigned int last_chan
)
147 outb(PCL816_MUX_SCAN(first_chan
, last_chan
),
148 dev
->iobase
+ PCL816_MUX_REG
);
151 static void pcl816_ai_setup_chanlist(struct comedi_device
*dev
,
152 unsigned int *chanlist
,
155 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
156 unsigned int last_chan
;
160 /* store range list to card */
161 for (i
= 0; i
< seglen
; i
++) {
162 last_chan
= CR_CHAN(chanlist
[i
]);
163 range
= CR_RANGE(chanlist
[i
]);
165 pcl816_ai_set_chan_range(dev
, last_chan
, range
);
170 pcl816_ai_set_chan_scan(dev
, first_chan
, last_chan
);
173 static void pcl816_ai_clear_eoc(struct comedi_device
*dev
)
175 /* writing any value clears the interrupt request */
176 outb(0, dev
->iobase
+ PCL816_CLRINT_REG
);
179 static void pcl816_ai_soft_trig(struct comedi_device
*dev
)
181 /* writing any value triggers a software conversion */
182 outb(0, dev
->iobase
+ PCL816_AI_LSB_REG
);
185 static unsigned int pcl816_ai_get_sample(struct comedi_device
*dev
,
186 struct comedi_subdevice
*s
)
190 val
= inb(dev
->iobase
+ PCL816_AI_MSB_REG
) << 8;
191 val
|= inb(dev
->iobase
+ PCL816_AI_LSB_REG
);
193 return val
& s
->maxdata
;
196 static int pcl816_ai_eoc(struct comedi_device
*dev
,
197 struct comedi_subdevice
*s
,
198 struct comedi_insn
*insn
,
199 unsigned long context
)
203 status
= inb(dev
->iobase
+ PCL816_STATUS_REG
);
204 if ((status
& PCL816_STATUS_DRDY
) == 0)
209 static bool pcl816_ai_next_chan(struct comedi_device
*dev
,
210 struct comedi_subdevice
*s
)
212 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
214 if (cmd
->stop_src
== TRIG_COUNT
&&
215 s
->async
->scans_done
>= cmd
->stop_arg
) {
216 s
->async
->events
|= COMEDI_CB_EOA
;
223 static void transfer_from_dma_buf(struct comedi_device
*dev
,
224 struct comedi_subdevice
*s
,
226 unsigned int bufptr
, unsigned int len
)
231 for (i
= 0; i
< len
; i
++) {
233 comedi_buf_write_samples(s
, &val
, 1);
235 if (!pcl816_ai_next_chan(dev
, s
))
240 static irqreturn_t
pcl816_interrupt(int irq
, void *d
)
242 struct comedi_device
*dev
= d
;
243 struct comedi_subdevice
*s
= dev
->read_subdev
;
244 struct pcl816_private
*devpriv
= dev
->private;
245 struct comedi_isadma
*dma
= devpriv
->dma
;
246 struct comedi_isadma_desc
*desc
= &dma
->desc
[dma
->cur_dma
];
247 unsigned int nsamples
;
250 if (!dev
->attached
|| !devpriv
->ai_cmd_running
) {
251 pcl816_ai_clear_eoc(dev
);
255 if (devpriv
->ai_cmd_canceled
) {
256 devpriv
->ai_cmd_canceled
= 0;
257 pcl816_ai_clear_eoc(dev
);
261 nsamples
= comedi_bytes_to_samples(s
, desc
->size
) -
262 devpriv
->ai_poll_ptr
;
263 bufptr
= devpriv
->ai_poll_ptr
;
264 devpriv
->ai_poll_ptr
= 0;
266 /* restart dma with the next buffer */
267 dma
->cur_dma
= 1 - dma
->cur_dma
;
268 pcl816_ai_setup_dma(dev
, s
, nsamples
);
270 transfer_from_dma_buf(dev
, s
, desc
->virt_addr
, bufptr
, nsamples
);
272 pcl816_ai_clear_eoc(dev
);
274 comedi_handle_events(dev
, s
);
278 static int check_channel_list(struct comedi_device
*dev
,
279 struct comedi_subdevice
*s
,
280 unsigned int *chanlist
,
281 unsigned int chanlen
)
283 unsigned int chansegment
[16];
284 unsigned int i
, nowmustbechan
, seglen
, segpos
;
286 /* correct channel and range number check itself comedi/range.c */
288 dev_err(dev
->class_dev
, "range/channel list is empty!\n");
293 /* first channel is every time ok */
294 chansegment
[0] = chanlist
[0];
295 for (i
= 1, seglen
= 1; i
< chanlen
; i
++, seglen
++) {
296 /* we detect loop, this must by finish */
297 if (chanlist
[0] == chanlist
[i
])
300 (CR_CHAN(chansegment
[i
- 1]) + 1) % chanlen
;
301 if (nowmustbechan
!= CR_CHAN(chanlist
[i
])) {
302 /* channel list isn't continuous :-( */
303 dev_dbg(dev
->class_dev
,
304 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
305 i
, CR_CHAN(chanlist
[i
]), nowmustbechan
,
306 CR_CHAN(chanlist
[0]));
309 /* well, this is next correct channel in list */
310 chansegment
[i
] = chanlist
[i
];
313 /* check whole chanlist */
314 for (i
= 0, segpos
= 0; i
< chanlen
; i
++) {
315 if (chanlist
[i
] != chansegment
[i
% seglen
]) {
316 dev_dbg(dev
->class_dev
,
317 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
318 i
, CR_CHAN(chansegment
[i
]),
319 CR_RANGE(chansegment
[i
]),
320 CR_AREF(chansegment
[i
]),
321 CR_CHAN(chanlist
[i
% seglen
]),
322 CR_RANGE(chanlist
[i
% seglen
]),
323 CR_AREF(chansegment
[i
% seglen
]));
324 return 0; /* chan/gain list is strange */
331 return seglen
; /* we can serve this with MUX logic */
334 static int pcl816_ai_cmdtest(struct comedi_device
*dev
,
335 struct comedi_subdevice
*s
, struct comedi_cmd
*cmd
)
339 /* Step 1 : check if triggers are trivially valid */
341 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
);
342 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
343 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
344 TRIG_EXT
| TRIG_TIMER
);
345 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
346 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
351 /* Step 2a : make sure trigger sources are unique */
353 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
354 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
356 /* Step 2b : and mutually compatible */
361 /* Step 3: check if arguments are trivially valid */
363 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
364 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
366 if (cmd
->convert_src
== TRIG_TIMER
)
367 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
, 10000);
369 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
371 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
374 if (cmd
->stop_src
== TRIG_COUNT
)
375 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
377 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
382 /* step 4: fix up any arguments */
383 if (cmd
->convert_src
== TRIG_TIMER
) {
384 unsigned int arg
= cmd
->convert_arg
;
386 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
387 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
393 /* step 5: complain about special chanlist considerations */
396 if (!check_channel_list(dev
, s
, cmd
->chanlist
,
398 return 5; /* incorrect channels list */
404 static int pcl816_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
406 struct pcl816_private
*devpriv
= dev
->private;
407 struct comedi_isadma
*dma
= devpriv
->dma
;
408 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
412 if (devpriv
->ai_cmd_running
)
415 seglen
= check_channel_list(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
);
418 pcl816_ai_setup_chanlist(dev
, cmd
->chanlist
, seglen
);
421 devpriv
->ai_cmd_running
= 1;
422 devpriv
->ai_poll_ptr
= 0;
423 devpriv
->ai_cmd_canceled
= 0;
425 /* setup and enable dma for the first buffer */
427 pcl816_ai_setup_dma(dev
, s
, 0);
429 comedi_8254_set_mode(dev
->pacer
, 0, I8254_MODE1
| I8254_BINARY
);
430 comedi_8254_write(dev
->pacer
, 0, 0x0ff);
432 comedi_8254_update_divisors(dev
->pacer
);
433 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
435 ctrl
= PCL816_CTRL_INTEN
| PCL816_CTRL_DMAEN
|
436 PCL816_CTRL_DMASRC_SLOT(0);
437 if (cmd
->convert_src
== TRIG_TIMER
)
438 ctrl
|= PCL816_CTRL_PACER_TRIG
;
440 ctrl
|= PCL816_CTRL_EXT_TRIG
;
442 outb(ctrl
, dev
->iobase
+ PCL816_CTRL_REG
);
443 outb((dma
->chan
<< 4) | dev
->irq
,
444 dev
->iobase
+ PCL816_STATUS_REG
);
449 static int pcl816_ai_poll(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
451 struct pcl816_private
*devpriv
= dev
->private;
452 struct comedi_isadma
*dma
= devpriv
->dma
;
453 struct comedi_isadma_desc
*desc
;
458 spin_lock_irqsave(&dev
->spinlock
, flags
);
460 poll
= comedi_isadma_poll(dma
);
461 poll
= comedi_bytes_to_samples(s
, poll
);
462 if (poll
> devpriv
->ai_poll_ptr
) {
463 desc
= &dma
->desc
[dma
->cur_dma
];
464 transfer_from_dma_buf(dev
, s
, desc
->virt_addr
,
465 devpriv
->ai_poll_ptr
,
466 poll
- devpriv
->ai_poll_ptr
);
467 /* new buffer position */
468 devpriv
->ai_poll_ptr
= poll
;
470 comedi_handle_events(dev
, s
);
472 ret
= comedi_buf_n_bytes_ready(s
);
477 spin_unlock_irqrestore(&dev
->spinlock
, flags
);
482 static int pcl816_ai_cancel(struct comedi_device
*dev
,
483 struct comedi_subdevice
*s
)
485 struct pcl816_private
*devpriv
= dev
->private;
487 if (!devpriv
->ai_cmd_running
)
490 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
491 pcl816_ai_clear_eoc(dev
);
493 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, false);
495 devpriv
->ai_cmd_running
= 0;
496 devpriv
->ai_cmd_canceled
= 1;
501 static int pcl816_ai_insn_read(struct comedi_device
*dev
,
502 struct comedi_subdevice
*s
,
503 struct comedi_insn
*insn
,
506 unsigned int chan
= CR_CHAN(insn
->chanspec
);
507 unsigned int range
= CR_RANGE(insn
->chanspec
);
511 outb(PCL816_CTRL_SOFT_TRIG
, dev
->iobase
+ PCL816_CTRL_REG
);
513 pcl816_ai_set_chan_range(dev
, chan
, range
);
514 pcl816_ai_set_chan_scan(dev
, chan
, chan
);
516 for (i
= 0; i
< insn
->n
; i
++) {
517 pcl816_ai_clear_eoc(dev
);
518 pcl816_ai_soft_trig(dev
);
520 ret
= comedi_timeout(dev
, s
, insn
, pcl816_ai_eoc
, 0);
524 data
[i
] = pcl816_ai_get_sample(dev
, s
);
526 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
527 pcl816_ai_clear_eoc(dev
);
529 return ret
? ret
: insn
->n
;
532 static int pcl816_di_insn_bits(struct comedi_device
*dev
,
533 struct comedi_subdevice
*s
,
534 struct comedi_insn
*insn
,
537 data
[1] = inb(dev
->iobase
+ PCL816_DO_DI_LSB_REG
) |
538 (inb(dev
->iobase
+ PCL816_DO_DI_MSB_REG
) << 8);
543 static int pcl816_do_insn_bits(struct comedi_device
*dev
,
544 struct comedi_subdevice
*s
,
545 struct comedi_insn
*insn
,
548 if (comedi_dio_update_state(s
, data
)) {
549 outb(s
->state
& 0xff, dev
->iobase
+ PCL816_DO_DI_LSB_REG
);
550 outb((s
->state
>> 8), dev
->iobase
+ PCL816_DO_DI_MSB_REG
);
558 static void pcl816_reset(struct comedi_device
*dev
)
560 outb(0, dev
->iobase
+ PCL816_CTRL_REG
);
561 pcl816_ai_set_chan_range(dev
, 0, 0);
562 pcl816_ai_clear_eoc(dev
);
564 /* set all digital outputs low */
565 outb(0, dev
->iobase
+ PCL816_DO_DI_LSB_REG
);
566 outb(0, dev
->iobase
+ PCL816_DO_DI_MSB_REG
);
569 static void pcl816_alloc_irq_and_dma(struct comedi_device
*dev
,
570 struct comedi_devconfig
*it
)
572 struct pcl816_private
*devpriv
= dev
->private;
573 unsigned int irq_num
= it
->options
[1];
574 unsigned int dma_chan
= it
->options
[2];
576 /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
577 if (!(irq_num
>= 2 && irq_num
<= 7) ||
578 !(dma_chan
== 3 || dma_chan
== 1))
581 if (request_irq(irq_num
, pcl816_interrupt
, 0, dev
->board_name
, dev
))
584 /* DMA uses two 16K buffers */
585 devpriv
->dma
= comedi_isadma_alloc(dev
, 2, dma_chan
, dma_chan
,
586 PAGE_SIZE
* 4, COMEDI_ISADMA_READ
);
588 free_irq(irq_num
, dev
);
593 static void pcl816_free_dma(struct comedi_device
*dev
)
595 struct pcl816_private
*devpriv
= dev
->private;
598 comedi_isadma_free(devpriv
->dma
);
601 static int pcl816_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
603 const struct pcl816_board
*board
= dev
->board_ptr
;
604 struct pcl816_private
*devpriv
;
605 struct comedi_subdevice
*s
;
608 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
612 ret
= comedi_request_region(dev
, it
->options
[0], 0x10);
616 /* an IRQ and DMA are required to support async commands */
617 pcl816_alloc_irq_and_dma(dev
, it
);
619 dev
->pacer
= comedi_8254_init(dev
->iobase
+ PCL816_TIMER_BASE
,
620 I8254_OSC_BASE_10MHZ
, I8254_IO8
, 0);
624 ret
= comedi_alloc_subdevices(dev
, 4);
628 s
= &dev
->subdevices
[0];
629 s
->type
= COMEDI_SUBD_AI
;
630 s
->subdev_flags
= SDF_CMD_READ
| SDF_DIFF
;
632 s
->maxdata
= board
->ai_maxdata
;
633 s
->range_table
= &range_pcl816
;
634 s
->insn_read
= pcl816_ai_insn_read
;
636 dev
->read_subdev
= s
;
637 s
->subdev_flags
|= SDF_CMD_READ
;
638 s
->len_chanlist
= board
->ai_chanlist
;
639 s
->do_cmdtest
= pcl816_ai_cmdtest
;
640 s
->do_cmd
= pcl816_ai_cmd
;
641 s
->poll
= pcl816_ai_poll
;
642 s
->cancel
= pcl816_ai_cancel
;
645 /* Piggyback Slot1 subdevice */
646 s
= &dev
->subdevices
[1];
647 s
->type
= COMEDI_SUBD_UNUSED
;
649 /* Digital Input subdevice */
650 s
= &dev
->subdevices
[2];
651 s
->type
= COMEDI_SUBD_DI
;
652 s
->subdev_flags
= SDF_READABLE
;
655 s
->range_table
= &range_digital
;
656 s
->insn_bits
= pcl816_di_insn_bits
;
658 /* Digital Output subdevice */
659 s
= &dev
->subdevices
[3];
660 s
->type
= COMEDI_SUBD_DO
;
661 s
->subdev_flags
= SDF_WRITABLE
;
664 s
->range_table
= &range_digital
;
665 s
->insn_bits
= pcl816_do_insn_bits
;
672 static void pcl816_detach(struct comedi_device
*dev
)
675 pcl816_ai_cancel(dev
, dev
->read_subdev
);
678 pcl816_free_dma(dev
);
679 comedi_legacy_detach(dev
);
682 static struct comedi_driver pcl816_driver
= {
683 .driver_name
= "pcl816",
684 .module
= THIS_MODULE
,
685 .attach
= pcl816_attach
,
686 .detach
= pcl816_detach
,
687 .board_name
= &boardtypes
[0].name
,
688 .num_names
= ARRAY_SIZE(boardtypes
),
689 .offset
= sizeof(struct pcl816_board
),
691 module_comedi_driver(pcl816_driver
);
693 MODULE_AUTHOR("Comedi http://www.comedi.org");
694 MODULE_DESCRIPTION("Comedi low-level driver");
695 MODULE_LICENSE("GPL");