2 comedi/drivers/pcl816.c
4 Author: Juan Grigera <juan@grigera.com.ar>
5 based on pcl818 by Michal Dobes <dobes@tesnet.cz> and bits of pcl812
7 hardware driver for Advantech cards:
13 Description: Advantech PCL-816 cards, PCL-814
14 Author: Juan Grigera <juan@grigera.com.ar>
15 Devices: [Advantech] PCL-816 (pcl816), PCL-814B (pcl814b)
17 Updated: Tue, 2 Apr 2002 23:15:21 -0800
19 PCL 816 and 814B have 16 SE/DIFF ADCs, 16 DACs, 16 DI and 16 DO.
20 Differences are at resolution (16 vs 12 bits).
22 The driver support AI command mode, other subdevices not written.
24 Analog output and digital input and output are not supported.
26 Configuration Options:
28 [1] - IRQ (0=disable, 2, 3, 4, 5, 6, 7)
29 [2] - DMA (0=disable, 1, 3)
30 [3] - 0, 10=10MHz clock for 8254
31 1= 1MHz clock for 8254
35 #include <linux/module.h>
36 #include <linux/gfp.h>
37 #include <linux/delay.h>
39 #include <linux/interrupt.h>
41 #include "../comedidev.h"
43 #include "comedi_isadma.h"
44 #include "comedi_8254.h"
49 #define PCL816_DO_DI_LSB_REG 0x00
50 #define PCL816_DO_DI_MSB_REG 0x01
51 #define PCL816_TIMER_BASE 0x04
52 #define PCL816_AI_LSB_REG 0x08
53 #define PCL816_AI_MSB_REG 0x09
54 #define PCL816_RANGE_REG 0x09
55 #define PCL816_CLRINT_REG 0x0a
56 #define PCL816_MUX_REG 0x0b
57 #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
58 #define PCL816_CTRL_REG 0x0c
59 #define PCL816_CTRL_DISABLE_TRIG (0 << 0)
60 #define PCL816_CTRL_SOFT_TRIG (1 << 0)
61 #define PCL816_CTRL_PACER_TRIG (1 << 1)
62 #define PCL816_CTRL_EXT_TRIG (1 << 2)
63 #define PCL816_CTRL_POE (1 << 3)
64 #define PCL816_CTRL_DMAEN (1 << 4)
65 #define PCL816_CTRL_INTEN (1 << 5)
66 #define PCL816_CTRL_DMASRC_SLOT0 (0 << 6)
67 #define PCL816_CTRL_DMASRC_SLOT1 (1 << 6)
68 #define PCL816_CTRL_DMASRC_SLOT2 (2 << 6)
69 #define PCL816_STATUS_REG 0x0d
70 #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
71 #define PCL816_STATUS_INTSRC_MASK (3 << 4)
72 #define PCL816_STATUS_INTSRC_SLOT0 (0 << 4)
73 #define PCL816_STATUS_INTSRC_SLOT1 (1 << 4)
74 #define PCL816_STATUS_INTSRC_SLOT2 (2 << 4)
75 #define PCL816_STATUS_INTSRC_DMA (3 << 4)
76 #define PCL816_STATUS_INTACT (1 << 6)
77 #define PCL816_STATUS_DRDY (1 << 7)
79 #define MAGIC_DMA_WORD 0x5a5a
81 static const struct comedi_lrange range_pcl816 = {
101 static const struct pcl816_board boardtypes[] = {
104 .ai_maxdata = 0xffff,
105 .ao_maxdata = 0xffff,
109 .ai_maxdata = 0x3fff,
110 .ao_maxdata = 0x3fff,
115 struct pcl816_private {
116 struct comedi_isadma *dma;
117 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
118 unsigned int ai_cmd_running:1;
119 unsigned int ai_cmd_canceled:1;
122 static void pcl816_ai_setup_dma(struct comedi_device *dev,
123 struct comedi_subdevice *s,
124 unsigned int unread_samples)
126 struct pcl816_private *devpriv = dev->private;
127 struct comedi_isadma *dma = devpriv->dma;
128 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
129 unsigned int max_samples = comedi_bytes_to_samples(s, desc->maxsize);
130 unsigned int nsamples;
132 comedi_isadma_disable(dma->chan);
135 * Determine dma size based on the buffer maxsize plus the number of
136 * unread samples and the number of samples remaining in the command.
138 nsamples = comedi_nsamples_left(s, max_samples + unread_samples);
139 if (nsamples > unread_samples) {
140 nsamples -= unread_samples;
141 desc->size = comedi_samples_to_bytes(s, nsamples);
142 comedi_isadma_program(desc);
146 static void pcl816_ai_set_chan_range(struct comedi_device *dev,
150 outb(chan, dev->iobase + PCL816_MUX_REG);
151 outb(range, dev->iobase + PCL816_RANGE_REG);
154 static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
155 unsigned int first_chan,
156 unsigned int last_chan)
158 outb(PCL816_MUX_SCAN(first_chan, last_chan),
159 dev->iobase + PCL816_MUX_REG);
162 static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
163 unsigned int *chanlist,
166 unsigned int first_chan = CR_CHAN(chanlist[0]);
167 unsigned int last_chan;
171 /* store range list to card */
172 for (i = 0; i < seglen; i++) {
173 last_chan = CR_CHAN(chanlist[i]);
174 range = CR_RANGE(chanlist[i]);
176 pcl816_ai_set_chan_range(dev, last_chan, range);
181 pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
184 static void pcl816_ai_clear_eoc(struct comedi_device *dev)
186 /* writing any value clears the interrupt request */
187 outb(0, dev->iobase + PCL816_CLRINT_REG);
190 static void pcl816_ai_soft_trig(struct comedi_device *dev)
192 /* writing any value triggers a software conversion */
193 outb(0, dev->iobase + PCL816_AI_LSB_REG);
196 static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
197 struct comedi_subdevice *s)
201 val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
202 val |= inb(dev->iobase + PCL816_AI_LSB_REG);
204 return val & s->maxdata;
207 static int pcl816_ai_eoc(struct comedi_device *dev,
208 struct comedi_subdevice *s,
209 struct comedi_insn *insn,
210 unsigned long context)
214 status = inb(dev->iobase + PCL816_STATUS_REG);
215 if ((status & PCL816_STATUS_DRDY) == 0)
220 static bool pcl816_ai_next_chan(struct comedi_device *dev,
221 struct comedi_subdevice *s)
223 struct comedi_cmd *cmd = &s->async->cmd;
225 if (cmd->stop_src == TRIG_COUNT &&
226 s->async->scans_done >= cmd->stop_arg) {
227 s->async->events |= COMEDI_CB_EOA;
234 static void transfer_from_dma_buf(struct comedi_device *dev,
235 struct comedi_subdevice *s,
237 unsigned int bufptr, unsigned int len)
242 for (i = 0; i < len; i++) {
244 comedi_buf_write_samples(s, &val, 1);
246 if (!pcl816_ai_next_chan(dev, s))
251 static irqreturn_t pcl816_interrupt(int irq, void *d)
253 struct comedi_device *dev = d;
254 struct comedi_subdevice *s = dev->read_subdev;
255 struct pcl816_private *devpriv = dev->private;
256 struct comedi_isadma *dma = devpriv->dma;
257 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
258 unsigned int nsamples;
261 if (!dev->attached || !devpriv->ai_cmd_running) {
262 pcl816_ai_clear_eoc(dev);
266 if (devpriv->ai_cmd_canceled) {
267 devpriv->ai_cmd_canceled = 0;
268 pcl816_ai_clear_eoc(dev);
272 nsamples = comedi_bytes_to_samples(s, desc->size) -
273 devpriv->ai_poll_ptr;
274 bufptr = devpriv->ai_poll_ptr;
275 devpriv->ai_poll_ptr = 0;
277 /* restart dma with the next buffer */
278 dma->cur_dma = 1 - dma->cur_dma;
279 pcl816_ai_setup_dma(dev, s, nsamples);
281 transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
283 pcl816_ai_clear_eoc(dev);
285 comedi_handle_events(dev, s);
289 static int check_channel_list(struct comedi_device *dev,
290 struct comedi_subdevice *s,
291 unsigned int *chanlist,
292 unsigned int chanlen)
294 unsigned int chansegment[16];
295 unsigned int i, nowmustbechan, seglen, segpos;
297 /* correct channel and range number check itself comedi/range.c */
299 dev_err(dev->class_dev, "range/channel list is empty!\n");
304 /* first channel is every time ok */
305 chansegment[0] = chanlist[0];
306 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
307 /* we detect loop, this must by finish */
308 if (chanlist[0] == chanlist[i])
311 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
312 if (nowmustbechan != CR_CHAN(chanlist[i])) {
313 /* channel list isn't continuous :-( */
314 dev_dbg(dev->class_dev,
315 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
316 i, CR_CHAN(chanlist[i]), nowmustbechan,
317 CR_CHAN(chanlist[0]));
320 /* well, this is next correct channel in list */
321 chansegment[i] = chanlist[i];
324 /* check whole chanlist */
325 for (i = 0, segpos = 0; i < chanlen; i++) {
326 if (chanlist[i] != chansegment[i % seglen]) {
327 dev_dbg(dev->class_dev,
328 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
329 i, CR_CHAN(chansegment[i]),
330 CR_RANGE(chansegment[i]),
331 CR_AREF(chansegment[i]),
332 CR_CHAN(chanlist[i % seglen]),
333 CR_RANGE(chanlist[i % seglen]),
334 CR_AREF(chansegment[i % seglen]));
335 return 0; /* chan/gain list is strange */
342 return seglen; /* we can serve this with MUX logic */
345 static int pcl816_ai_cmdtest(struct comedi_device *dev,
346 struct comedi_subdevice *s, struct comedi_cmd *cmd)
350 /* Step 1 : check if triggers are trivially valid */
352 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
353 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
354 err |= comedi_check_trigger_src(&cmd->convert_src,
355 TRIG_EXT | TRIG_TIMER);
356 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
357 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
362 /* Step 2a : make sure trigger sources are unique */
364 err |= comedi_check_trigger_is_unique(cmd->convert_src);
365 err |= comedi_check_trigger_is_unique(cmd->stop_src);
367 /* Step 2b : and mutually compatible */
372 /* Step 3: check if arguments are trivially valid */
374 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
375 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
377 if (cmd->convert_src == TRIG_TIMER)
378 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
380 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
382 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
385 if (cmd->stop_src == TRIG_COUNT)
386 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
388 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
393 /* step 4: fix up any arguments */
394 if (cmd->convert_src == TRIG_TIMER) {
395 unsigned int arg = cmd->convert_arg;
397 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
398 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
404 /* step 5: complain about special chanlist considerations */
407 if (!check_channel_list(dev, s, cmd->chanlist,
409 return 5; /* incorrect channels list */
415 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
417 struct pcl816_private *devpriv = dev->private;
418 struct comedi_isadma *dma = devpriv->dma;
419 struct comedi_cmd *cmd = &s->async->cmd;
423 if (devpriv->ai_cmd_running)
426 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
429 pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
432 devpriv->ai_cmd_running = 1;
433 devpriv->ai_poll_ptr = 0;
434 devpriv->ai_cmd_canceled = 0;
436 /* setup and enable dma for the first buffer */
438 pcl816_ai_setup_dma(dev, s, 0);
440 comedi_8254_set_mode(dev->pacer, 0, I8254_MODE1 | I8254_BINARY);
441 comedi_8254_write(dev->pacer, 0, 0x0ff);
443 comedi_8254_update_divisors(dev->pacer);
444 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
446 ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
447 if (cmd->convert_src == TRIG_TIMER)
448 ctrl |= PCL816_CTRL_PACER_TRIG;
450 ctrl |= PCL816_CTRL_EXT_TRIG;
452 outb(ctrl, dev->iobase + PCL816_CTRL_REG);
453 outb((dma->chan << 4) | dev->irq,
454 dev->iobase + PCL816_STATUS_REG);
459 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
461 struct pcl816_private *devpriv = dev->private;
462 struct comedi_isadma *dma = devpriv->dma;
463 struct comedi_isadma_desc *desc;
468 spin_lock_irqsave(&dev->spinlock, flags);
470 poll = comedi_isadma_poll(dma);
471 poll = comedi_bytes_to_samples(s, poll);
472 if (poll > devpriv->ai_poll_ptr) {
473 desc = &dma->desc[dma->cur_dma];
474 transfer_from_dma_buf(dev, s, desc->virt_addr,
475 devpriv->ai_poll_ptr,
476 poll - devpriv->ai_poll_ptr);
477 /* new buffer position */
478 devpriv->ai_poll_ptr = poll;
480 comedi_handle_events(dev, s);
482 ret = comedi_buf_n_bytes_ready(s);
487 spin_unlock_irqrestore(&dev->spinlock, flags);
492 static int pcl816_ai_cancel(struct comedi_device *dev,
493 struct comedi_subdevice *s)
495 struct pcl816_private *devpriv = dev->private;
497 if (!devpriv->ai_cmd_running)
500 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
501 pcl816_ai_clear_eoc(dev);
503 comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
505 devpriv->ai_cmd_running = 0;
506 devpriv->ai_cmd_canceled = 1;
511 static int pcl816_ai_insn_read(struct comedi_device *dev,
512 struct comedi_subdevice *s,
513 struct comedi_insn *insn,
516 unsigned int chan = CR_CHAN(insn->chanspec);
517 unsigned int range = CR_RANGE(insn->chanspec);
521 outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
523 pcl816_ai_set_chan_range(dev, chan, range);
524 pcl816_ai_set_chan_scan(dev, chan, chan);
526 for (i = 0; i < insn->n; i++) {
527 pcl816_ai_clear_eoc(dev);
528 pcl816_ai_soft_trig(dev);
530 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
534 data[i] = pcl816_ai_get_sample(dev, s);
536 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
537 pcl816_ai_clear_eoc(dev);
539 return ret ? ret : insn->n;
542 static int pcl816_di_insn_bits(struct comedi_device *dev,
543 struct comedi_subdevice *s,
544 struct comedi_insn *insn,
547 data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
548 (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
553 static int pcl816_do_insn_bits(struct comedi_device *dev,
554 struct comedi_subdevice *s,
555 struct comedi_insn *insn,
558 if (comedi_dio_update_state(s, data)) {
559 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
560 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
568 static void pcl816_reset(struct comedi_device *dev)
570 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
571 pcl816_ai_set_chan_range(dev, 0, 0);
572 pcl816_ai_clear_eoc(dev);
574 /* set all digital outputs low */
575 outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
576 outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
579 static void pcl816_alloc_irq_and_dma(struct comedi_device *dev,
580 struct comedi_devconfig *it)
582 struct pcl816_private *devpriv = dev->private;
583 unsigned int irq_num = it->options[1];
584 unsigned int dma_chan = it->options[2];
586 /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
587 if (!(irq_num >= 2 && irq_num <= 7) ||
588 !(dma_chan == 3 || dma_chan == 1))
591 if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev))
594 /* DMA uses two 16K buffers */
595 devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
596 PAGE_SIZE * 4, COMEDI_ISADMA_READ);
598 free_irq(irq_num, dev);
603 static void pcl816_free_dma(struct comedi_device *dev)
605 struct pcl816_private *devpriv = dev->private;
608 comedi_isadma_free(devpriv->dma);
611 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
613 const struct pcl816_board *board = dev->board_ptr;
614 struct pcl816_private *devpriv;
615 struct comedi_subdevice *s;
618 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
622 ret = comedi_request_region(dev, it->options[0], 0x10);
626 /* an IRQ and DMA are required to support async commands */
627 pcl816_alloc_irq_and_dma(dev, it);
629 dev->pacer = comedi_8254_init(dev->iobase + PCL816_TIMER_BASE,
630 I8254_OSC_BASE_10MHZ, I8254_IO8, 0);
634 ret = comedi_alloc_subdevices(dev, 4);
638 s = &dev->subdevices[0];
639 s->type = COMEDI_SUBD_AI;
640 s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
642 s->maxdata = board->ai_maxdata;
643 s->range_table = &range_pcl816;
644 s->insn_read = pcl816_ai_insn_read;
646 dev->read_subdev = s;
647 s->subdev_flags |= SDF_CMD_READ;
648 s->len_chanlist = board->ai_chanlist;
649 s->do_cmdtest = pcl816_ai_cmdtest;
650 s->do_cmd = pcl816_ai_cmd;
651 s->poll = pcl816_ai_poll;
652 s->cancel = pcl816_ai_cancel;
655 /* Analog OUtput subdevice */
656 s = &dev->subdevices[2];
657 s->type = COMEDI_SUBD_UNUSED;
659 subdevs[1] = COMEDI_SUBD_AO;
660 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
662 s->maxdata = board->ao_maxdata;
663 s->range_table = &range_pcl816;
666 /* Digital Input subdevice */
667 s = &dev->subdevices[2];
668 s->type = COMEDI_SUBD_DI;
669 s->subdev_flags = SDF_READABLE;
672 s->range_table = &range_digital;
673 s->insn_bits = pcl816_di_insn_bits;
675 /* Digital Output subdevice */
676 s = &dev->subdevices[3];
677 s->type = COMEDI_SUBD_DO;
678 s->subdev_flags = SDF_WRITABLE;
681 s->range_table = &range_digital;
682 s->insn_bits = pcl816_do_insn_bits;
689 static void pcl816_detach(struct comedi_device *dev)
692 pcl816_ai_cancel(dev, dev->read_subdev);
695 pcl816_free_dma(dev);
696 comedi_legacy_detach(dev);
699 static struct comedi_driver pcl816_driver = {
700 .driver_name = "pcl816",
701 .module = THIS_MODULE,
702 .attach = pcl816_attach,
703 .detach = pcl816_detach,
704 .board_name = &boardtypes[0].name,
705 .num_names = ARRAY_SIZE(boardtypes),
706 .offset = sizeof(struct pcl816_board),
708 module_comedi_driver(pcl816_driver);
710 MODULE_AUTHOR("Comedi http://www.comedi.org");
711 MODULE_DESCRIPTION("Comedi low-level driver");
712 MODULE_LICENSE("GPL");