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_fc.h"
50 #define PCL816_DO_DI_LSB_REG 0x00
51 #define PCL816_DO_DI_MSB_REG 0x01
52 #define PCL816_TIMER_BASE 0x04
53 #define PCL816_AI_LSB_REG 0x08
54 #define PCL816_AI_MSB_REG 0x09
55 #define PCL816_RANGE_REG 0x09
56 #define PCL816_CLRINT_REG 0x0a
57 #define PCL816_MUX_REG 0x0b
58 #define PCL816_MUX_SCAN(_first, _last) (((_last) << 4) | (_first))
59 #define PCL816_CTRL_REG 0x0c
60 #define PCL816_CTRL_DISABLE_TRIG (0 << 0)
61 #define PCL816_CTRL_SOFT_TRIG (1 << 0)
62 #define PCL816_CTRL_PACER_TRIG (1 << 1)
63 #define PCL816_CTRL_EXT_TRIG (1 << 2)
64 #define PCL816_CTRL_POE (1 << 3)
65 #define PCL816_CTRL_DMAEN (1 << 4)
66 #define PCL816_CTRL_INTEN (1 << 5)
67 #define PCL816_CTRL_DMASRC_SLOT0 (0 << 6)
68 #define PCL816_CTRL_DMASRC_SLOT1 (1 << 6)
69 #define PCL816_CTRL_DMASRC_SLOT2 (2 << 6)
70 #define PCL816_STATUS_REG 0x0d
71 #define PCL816_STATUS_NEXT_CHAN_MASK (0xf << 0)
72 #define PCL816_STATUS_INTSRC_MASK (3 << 4)
73 #define PCL816_STATUS_INTSRC_SLOT0 (0 << 4)
74 #define PCL816_STATUS_INTSRC_SLOT1 (1 << 4)
75 #define PCL816_STATUS_INTSRC_SLOT2 (2 << 4)
76 #define PCL816_STATUS_INTSRC_DMA (3 << 4)
77 #define PCL816_STATUS_INTACT (1 << 6)
78 #define PCL816_STATUS_DRDY (1 << 7)
80 #define MAGIC_DMA_WORD 0x5a5a
82 static const struct comedi_lrange range_pcl816 = {
102 static const struct pcl816_board boardtypes[] = {
105 .ai_maxdata = 0xffff,
106 .ao_maxdata = 0xffff,
110 .ai_maxdata = 0x3fff,
111 .ao_maxdata = 0x3fff,
116 struct pcl816_private {
117 struct comedi_isadma *dma;
118 unsigned int ai_poll_ptr; /* how many sampes transfer poll */
119 unsigned int divisor1;
120 unsigned int divisor2;
121 unsigned int ai_cmd_running:1;
122 unsigned int ai_cmd_canceled:1;
125 static void pcl816_start_pacer(struct comedi_device *dev, bool load_counters)
127 struct pcl816_private *devpriv = dev->private;
128 unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
130 i8254_set_mode(timer_base, 0, 0, I8254_MODE1 | I8254_BINARY);
131 i8254_write(timer_base, 0, 0, 0x00ff);
134 i8254_set_mode(timer_base, 0, 2, I8254_MODE2 | I8254_BINARY);
135 i8254_set_mode(timer_base, 0, 1, I8254_MODE2 | I8254_BINARY);
139 i8254_write(timer_base, 0, 2, devpriv->divisor2);
140 i8254_write(timer_base, 0, 1, devpriv->divisor1);
144 static void pcl816_ai_setup_dma(struct comedi_device *dev,
145 struct comedi_subdevice *s)
147 struct pcl816_private *devpriv = dev->private;
148 struct comedi_isadma *dma = devpriv->dma;
149 struct comedi_isadma_desc *desc = &dma->desc[0];
150 unsigned int nsamples;
155 * Determine dma size based on the buffer maxsize and the number of
156 * samples remaining in the command.
158 nsamples = comedi_bytes_to_samples(s, desc->maxsize);
159 nsamples = comedi_nsamples_left(s, nsamples);
160 desc->size = comedi_samples_to_bytes(s, nsamples);
161 comedi_isadma_program(desc);
164 static void pcl816_ai_setup_next_dma(struct comedi_device *dev,
165 struct comedi_subdevice *s,
166 unsigned int unread_samples)
168 struct pcl816_private *devpriv = dev->private;
169 struct comedi_isadma *dma = devpriv->dma;
170 struct comedi_isadma_desc *desc;
171 unsigned int max_samples;
172 unsigned int nsamples;
174 comedi_isadma_disable(dma->chan);
176 dma->cur_dma = 1 - dma->cur_dma;
177 desc = &dma->desc[dma->cur_dma];
180 * Determine dma size based on the buffer maxsize plus the number of
181 * unread samples and the number of samples remaining in the command.
183 max_samples = comedi_bytes_to_samples(s, desc->maxsize);
184 nsamples = max_samples + unread_samples;
185 nsamples = comedi_nsamples_left(s, nsamples);
186 if (nsamples > unread_samples) {
187 nsamples -= unread_samples;
188 desc->size = comedi_samples_to_bytes(s, nsamples);
189 comedi_isadma_program(desc);
193 static void pcl816_ai_set_chan_range(struct comedi_device *dev,
197 outb(chan, dev->iobase + PCL816_MUX_REG);
198 outb(range, dev->iobase + PCL816_RANGE_REG);
201 static void pcl816_ai_set_chan_scan(struct comedi_device *dev,
202 unsigned int first_chan,
203 unsigned int last_chan)
205 outb(PCL816_MUX_SCAN(first_chan, last_chan),
206 dev->iobase + PCL816_MUX_REG);
209 static void pcl816_ai_setup_chanlist(struct comedi_device *dev,
210 unsigned int *chanlist,
213 unsigned int first_chan = CR_CHAN(chanlist[0]);
214 unsigned int last_chan;
218 /* store range list to card */
219 for (i = 0; i < seglen; i++) {
220 last_chan = CR_CHAN(chanlist[i]);
221 range = CR_RANGE(chanlist[i]);
223 pcl816_ai_set_chan_range(dev, last_chan, range);
228 pcl816_ai_set_chan_scan(dev, first_chan, last_chan);
231 static void pcl816_ai_clear_eoc(struct comedi_device *dev)
233 /* writing any value clears the interrupt request */
234 outb(0, dev->iobase + PCL816_CLRINT_REG);
237 static void pcl816_ai_soft_trig(struct comedi_device *dev)
239 /* writing any value triggers a software conversion */
240 outb(0, dev->iobase + PCL816_AI_LSB_REG);
243 static unsigned int pcl816_ai_get_sample(struct comedi_device *dev,
244 struct comedi_subdevice *s)
248 val = inb(dev->iobase + PCL816_AI_MSB_REG) << 8;
249 val |= inb(dev->iobase + PCL816_AI_LSB_REG);
251 return val & s->maxdata;
254 static int pcl816_ai_eoc(struct comedi_device *dev,
255 struct comedi_subdevice *s,
256 struct comedi_insn *insn,
257 unsigned long context)
261 status = inb(dev->iobase + PCL816_STATUS_REG);
262 if ((status & PCL816_STATUS_DRDY) == 0)
267 static bool pcl816_ai_next_chan(struct comedi_device *dev,
268 struct comedi_subdevice *s)
270 struct comedi_cmd *cmd = &s->async->cmd;
272 if (cmd->stop_src == TRIG_COUNT &&
273 s->async->scans_done >= cmd->stop_arg) {
274 s->async->events |= COMEDI_CB_EOA;
281 static void transfer_from_dma_buf(struct comedi_device *dev,
282 struct comedi_subdevice *s,
284 unsigned int bufptr, unsigned int len)
289 for (i = 0; i < len; i++) {
291 comedi_buf_write_samples(s, &val, 1);
293 if (!pcl816_ai_next_chan(dev, s))
298 static irqreturn_t pcl816_interrupt(int irq, void *d)
300 struct comedi_device *dev = d;
301 struct comedi_subdevice *s = dev->read_subdev;
302 struct pcl816_private *devpriv = dev->private;
303 struct comedi_isadma *dma = devpriv->dma;
304 struct comedi_isadma_desc *desc = &dma->desc[dma->cur_dma];
305 unsigned int nsamples;
308 if (!dev->attached || !devpriv->ai_cmd_running) {
309 pcl816_ai_clear_eoc(dev);
313 if (devpriv->ai_cmd_canceled) {
314 devpriv->ai_cmd_canceled = 0;
315 pcl816_ai_clear_eoc(dev);
319 nsamples = comedi_bytes_to_samples(s, desc->size) -
320 devpriv->ai_poll_ptr;
321 bufptr = devpriv->ai_poll_ptr;
322 devpriv->ai_poll_ptr = 0;
324 pcl816_ai_setup_next_dma(dev, s, nsamples);
326 transfer_from_dma_buf(dev, s, desc->virt_addr, bufptr, nsamples);
328 pcl816_ai_clear_eoc(dev);
330 comedi_handle_events(dev, s);
334 static int check_channel_list(struct comedi_device *dev,
335 struct comedi_subdevice *s,
336 unsigned int *chanlist,
337 unsigned int chanlen)
339 unsigned int chansegment[16];
340 unsigned int i, nowmustbechan, seglen, segpos;
342 /* correct channel and range number check itself comedi/range.c */
344 dev_err(dev->class_dev, "range/channel list is empty!\n");
349 /* first channel is every time ok */
350 chansegment[0] = chanlist[0];
351 for (i = 1, seglen = 1; i < chanlen; i++, seglen++) {
352 /* we detect loop, this must by finish */
353 if (chanlist[0] == chanlist[i])
356 (CR_CHAN(chansegment[i - 1]) + 1) % chanlen;
357 if (nowmustbechan != CR_CHAN(chanlist[i])) {
358 /* channel list isn't continuous :-( */
359 dev_dbg(dev->class_dev,
360 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
361 i, CR_CHAN(chanlist[i]), nowmustbechan,
362 CR_CHAN(chanlist[0]));
365 /* well, this is next correct channel in list */
366 chansegment[i] = chanlist[i];
369 /* check whole chanlist */
370 for (i = 0, segpos = 0; i < chanlen; i++) {
371 if (chanlist[i] != chansegment[i % seglen]) {
372 dev_dbg(dev->class_dev,
373 "bad channel or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
374 i, CR_CHAN(chansegment[i]),
375 CR_RANGE(chansegment[i]),
376 CR_AREF(chansegment[i]),
377 CR_CHAN(chanlist[i % seglen]),
378 CR_RANGE(chanlist[i % seglen]),
379 CR_AREF(chansegment[i % seglen]));
380 return 0; /* chan/gain list is strange */
387 return seglen; /* we can serve this with MUX logic */
390 static int pcl816_ai_cmdtest(struct comedi_device *dev,
391 struct comedi_subdevice *s, struct comedi_cmd *cmd)
393 struct pcl816_private *devpriv = dev->private;
397 /* Step 1 : check if triggers are trivially valid */
399 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
400 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
401 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
402 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
403 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
408 /* Step 2a : make sure trigger sources are unique */
410 err |= cfc_check_trigger_is_unique(cmd->convert_src);
411 err |= cfc_check_trigger_is_unique(cmd->stop_src);
413 /* Step 2b : and mutually compatible */
419 /* Step 3: check if arguments are trivially valid */
421 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
422 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
424 if (cmd->convert_src == TRIG_TIMER)
425 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
427 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
429 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
431 if (cmd->stop_src == TRIG_COUNT)
432 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
434 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
440 /* step 4: fix up any arguments */
441 if (cmd->convert_src == TRIG_TIMER) {
442 arg = cmd->convert_arg;
443 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
447 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
454 /* step 5: complain about special chanlist considerations */
457 if (!check_channel_list(dev, s, cmd->chanlist,
459 return 5; /* incorrect channels list */
465 static int pcl816_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
467 struct pcl816_private *devpriv = dev->private;
468 struct comedi_isadma *dma = devpriv->dma;
469 struct comedi_cmd *cmd = &s->async->cmd;
473 if (devpriv->ai_cmd_running)
476 pcl816_start_pacer(dev, false);
478 seglen = check_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len);
481 pcl816_ai_setup_chanlist(dev, cmd->chanlist, seglen);
484 devpriv->ai_cmd_running = 1;
485 devpriv->ai_poll_ptr = 0;
486 devpriv->ai_cmd_canceled = 0;
488 pcl816_ai_setup_dma(dev, s);
490 pcl816_start_pacer(dev, true);
492 ctrl = PCL816_CTRL_INTEN | PCL816_CTRL_DMAEN | PCL816_CTRL_DMASRC_SLOT0;
493 if (cmd->convert_src == TRIG_TIMER)
494 ctrl |= PCL816_CTRL_PACER_TRIG;
496 ctrl |= PCL816_CTRL_EXT_TRIG;
498 outb(ctrl, dev->iobase + PCL816_CTRL_REG);
499 outb((dma->chan << 4) | dev->irq,
500 dev->iobase + PCL816_STATUS_REG);
505 static int pcl816_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
507 struct pcl816_private *devpriv = dev->private;
508 struct comedi_isadma *dma = devpriv->dma;
509 struct comedi_isadma_desc *desc;
514 spin_lock_irqsave(&dev->spinlock, flags);
516 poll = comedi_isadma_poll(dma);
517 poll = comedi_bytes_to_samples(s, poll);
518 if (poll > devpriv->ai_poll_ptr) {
519 desc = &dma->desc[dma->cur_dma];
520 transfer_from_dma_buf(dev, s, desc->virt_addr,
521 devpriv->ai_poll_ptr,
522 poll - devpriv->ai_poll_ptr);
523 /* new buffer position */
524 devpriv->ai_poll_ptr = poll;
526 comedi_handle_events(dev, s);
528 ret = comedi_buf_n_bytes_ready(s);
533 spin_unlock_irqrestore(&dev->spinlock, flags);
538 static int pcl816_ai_cancel(struct comedi_device *dev,
539 struct comedi_subdevice *s)
541 struct pcl816_private *devpriv = dev->private;
543 if (!devpriv->ai_cmd_running)
546 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
547 pcl816_ai_clear_eoc(dev);
550 i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
551 2, I8254_MODE0 | I8254_BINARY);
552 i8254_set_mode(dev->iobase + PCL816_TIMER_BASE, 0,
553 1, I8254_MODE0 | I8254_BINARY);
555 devpriv->ai_cmd_running = 0;
556 devpriv->ai_cmd_canceled = 1;
561 static int pcl816_ai_insn_read(struct comedi_device *dev,
562 struct comedi_subdevice *s,
563 struct comedi_insn *insn,
566 unsigned int chan = CR_CHAN(insn->chanspec);
567 unsigned int range = CR_RANGE(insn->chanspec);
571 outb(PCL816_CTRL_SOFT_TRIG, dev->iobase + PCL816_CTRL_REG);
573 pcl816_ai_set_chan_range(dev, chan, range);
574 pcl816_ai_set_chan_scan(dev, chan, chan);
576 for (i = 0; i < insn->n; i++) {
577 pcl816_ai_clear_eoc(dev);
578 pcl816_ai_soft_trig(dev);
580 ret = comedi_timeout(dev, s, insn, pcl816_ai_eoc, 0);
584 data[i] = pcl816_ai_get_sample(dev, s);
586 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
587 pcl816_ai_clear_eoc(dev);
589 return ret ? ret : insn->n;
592 static int pcl816_di_insn_bits(struct comedi_device *dev,
593 struct comedi_subdevice *s,
594 struct comedi_insn *insn,
597 data[1] = inb(dev->iobase + PCL816_DO_DI_LSB_REG) |
598 (inb(dev->iobase + PCL816_DO_DI_MSB_REG) << 8);
603 static int pcl816_do_insn_bits(struct comedi_device *dev,
604 struct comedi_subdevice *s,
605 struct comedi_insn *insn,
608 if (comedi_dio_update_state(s, data)) {
609 outb(s->state & 0xff, dev->iobase + PCL816_DO_DI_LSB_REG);
610 outb((s->state >> 8), dev->iobase + PCL816_DO_DI_MSB_REG);
618 static void pcl816_reset(struct comedi_device *dev)
620 unsigned long timer_base = dev->iobase + PCL816_TIMER_BASE;
622 outb(PCL816_CTRL_DISABLE_TRIG, dev->iobase + PCL816_CTRL_REG);
623 pcl816_ai_set_chan_range(dev, 0, 0);
624 pcl816_ai_clear_eoc(dev);
627 i8254_set_mode(timer_base, 0, 2, I8254_MODE0 | I8254_BINARY);
628 i8254_set_mode(timer_base, 0, 1, I8254_MODE0 | I8254_BINARY);
629 i8254_set_mode(timer_base, 0, 0, I8254_MODE0 | I8254_BINARY);
631 /* set all digital outputs low */
632 outb(0, dev->iobase + PCL816_DO_DI_LSB_REG);
633 outb(0, dev->iobase + PCL816_DO_DI_MSB_REG);
636 static void pcl816_alloc_irq_and_dma(struct comedi_device *dev,
637 struct comedi_devconfig *it)
639 struct pcl816_private *devpriv = dev->private;
640 unsigned int irq_num = it->options[1];
641 unsigned int dma_chan = it->options[2];
643 /* only IRQs 2-7 and DMA channels 3 and 1 are valid */
644 if (!(irq_num >= 2 && irq_num <= 7) ||
645 !(dma_chan == 3 || dma_chan == 1))
648 if (request_irq(irq_num, pcl816_interrupt, 0, dev->board_name, dev))
651 /* DMA uses two 16K buffers */
652 devpriv->dma = comedi_isadma_alloc(dev, 2, dma_chan, dma_chan,
653 PAGE_SIZE * 4, COMEDI_ISADMA_READ);
655 free_irq(irq_num, dev);
660 static void pcl816_free_dma(struct comedi_device *dev)
662 struct pcl816_private *devpriv = dev->private;
665 comedi_isadma_free(devpriv->dma);
668 static int pcl816_attach(struct comedi_device *dev, struct comedi_devconfig *it)
670 const struct pcl816_board *board = dev->board_ptr;
671 struct pcl816_private *devpriv;
672 struct comedi_subdevice *s;
675 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
679 ret = comedi_request_region(dev, it->options[0], 0x10);
683 /* an IRQ and DMA are required to support async commands */
684 pcl816_alloc_irq_and_dma(dev, it);
686 ret = comedi_alloc_subdevices(dev, 4);
690 s = &dev->subdevices[0];
691 s->type = COMEDI_SUBD_AI;
692 s->subdev_flags = SDF_CMD_READ | SDF_DIFF;
694 s->maxdata = board->ai_maxdata;
695 s->range_table = &range_pcl816;
696 s->insn_read = pcl816_ai_insn_read;
698 dev->read_subdev = s;
699 s->subdev_flags |= SDF_CMD_READ;
700 s->len_chanlist = board->ai_chanlist;
701 s->do_cmdtest = pcl816_ai_cmdtest;
702 s->do_cmd = pcl816_ai_cmd;
703 s->poll = pcl816_ai_poll;
704 s->cancel = pcl816_ai_cancel;
707 /* Analog OUtput subdevice */
708 s = &dev->subdevices[2];
709 s->type = COMEDI_SUBD_UNUSED;
711 subdevs[1] = COMEDI_SUBD_AO;
712 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
714 s->maxdata = board->ao_maxdata;
715 s->range_table = &range_pcl816;
718 /* Digital Input subdevice */
719 s = &dev->subdevices[2];
720 s->type = COMEDI_SUBD_DI;
721 s->subdev_flags = SDF_READABLE;
724 s->range_table = &range_digital;
725 s->insn_bits = pcl816_di_insn_bits;
727 /* Digital Output subdevice */
728 s = &dev->subdevices[3];
729 s->type = COMEDI_SUBD_DO;
730 s->subdev_flags = SDF_WRITABLE;
733 s->range_table = &range_digital;
734 s->insn_bits = pcl816_do_insn_bits;
741 static void pcl816_detach(struct comedi_device *dev)
744 pcl816_ai_cancel(dev, dev->read_subdev);
747 pcl816_free_dma(dev);
748 comedi_legacy_detach(dev);
751 static struct comedi_driver pcl816_driver = {
752 .driver_name = "pcl816",
753 .module = THIS_MODULE,
754 .attach = pcl816_attach,
755 .detach = pcl816_detach,
756 .board_name = &boardtypes[0].name,
757 .num_names = ARRAY_SIZE(boardtypes),
758 .offset = sizeof(struct pcl816_board),
760 module_comedi_driver(pcl816_driver);
762 MODULE_AUTHOR("Comedi http://www.comedi.org");
763 MODULE_DESCRIPTION("Comedi low-level driver");
764 MODULE_LICENSE("GPL");