3 * Comedi driver for Advantech PCI-1710 series boards
4 * Author: Michal Dobes <dobes@tesnet.cz>
6 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7 * for testing and information.
12 * Description: Comedi driver for Advantech PCI-1710 series boards
13 * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
14 * PCI-1713, PCI-1720, PCI-1731
15 * Author: Michal Dobes <dobes@tesnet.cz>
18 * Configuration options: not applicable, uses PCI auto config
20 * This driver supports AI, AO, DI and DO subdevices.
21 * AI subdevice supports cmd and insn interface,
22 * other subdevices support only insn interface.
24 * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
25 * driver cannot distinguish between them, as would be normal for a
29 #include <linux/module.h>
30 #include <linux/interrupt.h>
32 #include "../comedi_pci.h"
34 #include "comedi_8254.h"
35 #include "amcc_s5933.h"
38 * PCI BAR2 Register map (dev->iobase)
40 #define PCI171X_AD_DATA_REG 0x00 /* R: A/D data */
41 #define PCI171X_SOFTTRG_REG 0x00 /* W: soft trigger for A/D */
42 #define PCI171X_RANGE_REG 0x02 /* W: A/D gain/range register */
43 #define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */
44 #define PCI171X_STATUS_REG 0x06 /* R: status register */
45 #define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */
46 #define PCI171X_STATUS_FF BIT(10) /* 1=FIFO is full, fatal error */
47 #define PCI171X_STATUS_FH BIT(9) /* 1=FIFO is half full */
48 #define PCI171X_STATUS_FE BIT(8) /* 1=FIFO is empty */
49 #define PCI171X_CTRL_REG 0x06 /* W: control register */
50 #define PCI171X_CTRL_CNT0 BIT(6) /* 1=ext. clk, 0=int. 100kHz clk */
51 #define PCI171X_CTRL_ONEFH BIT(5) /* 1=on FIFO half full, 0=on sample */
52 #define PCI171X_CTRL_IRQEN BIT(4) /* 1=enable IRQ */
53 #define PCI171X_CTRL_GATE BIT(3) /* 1=enable ext. trigger GATE (8254?) */
54 #define PCI171X_CTRL_EXT BIT(2) /* 1=enable ext. trigger source */
55 #define PCI171X_CTRL_PACER BIT(1) /* 1=enable int. 8254 trigger source */
56 #define PCI171X_CTRL_SW BIT(0) /* 1=enable software trigger source */
57 #define PCI171X_CLRINT_REG 0x08 /* W: clear interrupts request */
58 #define PCI171X_CLRFIFO_REG 0x09 /* W: clear FIFO */
59 #define PCI171X_DA_REG(x) (0x0a + ((x) * 2)) /* W: D/A register */
60 #define PCI171X_DAREF_REG 0x0e /* W: D/A reference control */
61 #define PCI171X_DI_REG 0x10 /* R: digital inputs */
62 #define PCI171X_DO_REG 0x10 /* W: digital outputs */
63 #define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */
66 * PCI-1720 only has analog outputs and has a different
67 * register map (dev->iobase)
69 #define PCI1720_DA_REG(x) (0x00 + ((x) * 2)) /* W: D/A registers */
70 #define PCI1720_RANGE_REG 0x08 /* R/W: D/A range register */
71 #define PCI1720_SYNC_REG 0x09 /* W: D/A synchronized output */
72 #define PCI1720_SYNC_CTRL_REG 0x0f /* R/W: D/A synchronized control */
73 #define PCI1720_SYNC_CTRL_SC0 BIT(0) /* set synchronous output mode */
75 static const struct comedi_lrange range_pci1710_3 = {
89 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
90 0x10, 0x11, 0x12, 0x13 };
92 static const struct comedi_lrange range_pci1710hg = {
109 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
110 0x05, 0x06, 0x07, 0x10, 0x11,
113 static const struct comedi_lrange range_pci17x1 = {
123 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
125 static const struct comedi_lrange pci1720_ao_range = {
134 static const struct comedi_lrange pci171x_ao_range = {
141 enum pci1710_boardid {
151 const char *name; /* board name */
152 int n_aichan; /* num of A/D chans */
153 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
154 const char *rangecode_ai; /* range codes for programming */
155 unsigned int is_pci1713:1;
156 unsigned int is_pci1720:1;
157 unsigned int has_irq:1;
158 unsigned int has_large_fifo:1; /* 4K or 1K FIFO */
159 unsigned int has_diff_ai:1;
160 unsigned int has_ao:1;
161 unsigned int has_di_do:1;
162 unsigned int has_counter:1;
165 static const struct boardtype boardtypes[] = {
169 .rangelist_ai = &range_pci1710_3,
170 .rangecode_ai = range_codes_pci1710_3,
178 [BOARD_PCI1710HG] = {
181 .rangelist_ai = &range_pci1710hg,
182 .rangecode_ai = range_codes_pci1710hg,
193 .rangelist_ai = &range_pci17x1,
194 .rangecode_ai = range_codes_pci17x1,
203 .rangelist_ai = &range_pci1710_3,
204 .rangecode_ai = range_codes_pci1710_3,
218 .rangelist_ai = &range_pci17x1,
219 .rangecode_ai = range_codes_pci17x1,
225 struct pci1710_private {
226 unsigned int max_samples;
227 unsigned int ctrl; /* control register value */
228 unsigned int ctrl_ext; /* used to switch from TRIG_EXT to TRIG_xxx */
229 unsigned int mux_ext; /* used to set the channel interval to scan */
231 unsigned int act_chanlist[32]; /* list of scanned channel */
232 unsigned char saved_seglen; /* len of the non-repeating chanlist */
233 unsigned char da_ranges; /* copy of D/A outpit range register */
236 static int pci171x_ai_check_chanlist(struct comedi_device *dev,
237 struct comedi_subdevice *s,
238 struct comedi_cmd *cmd)
240 struct pci1710_private *devpriv = dev->private;
241 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
242 unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
243 unsigned int next_chan = (chan0 + 1) % s->n_chan;
244 unsigned int chansegment[32];
248 if (cmd->chanlist_len == 1) {
249 devpriv->saved_seglen = cmd->chanlist_len;
253 /* first channel is always ok */
254 chansegment[0] = cmd->chanlist[0];
256 for (i = 1; i < cmd->chanlist_len; i++) {
257 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
258 unsigned int aref = CR_AREF(cmd->chanlist[i]);
260 if (cmd->chanlist[0] == cmd->chanlist[i])
261 break; /* we detected a loop, stop */
263 if (aref == AREF_DIFF && (chan & 1)) {
264 dev_err(dev->class_dev,
265 "Odd channel cannot be differential input!\n");
269 if (last_aref == AREF_DIFF)
270 next_chan = (next_chan + 1) % s->n_chan;
271 if (chan != next_chan) {
272 dev_err(dev->class_dev,
273 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
274 i, chan, next_chan, chan0);
278 /* next correct channel in list */
279 chansegment[i] = cmd->chanlist[i];
284 for (i = 0; i < cmd->chanlist_len; i++) {
285 if (cmd->chanlist[i] != chansegment[i % seglen]) {
286 dev_err(dev->class_dev,
287 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
288 i, CR_CHAN(chansegment[i]),
289 CR_RANGE(chansegment[i]),
290 CR_AREF(chansegment[i]),
291 CR_CHAN(cmd->chanlist[i % seglen]),
292 CR_RANGE(cmd->chanlist[i % seglen]),
293 CR_AREF(chansegment[i % seglen]));
297 devpriv->saved_seglen = seglen;
302 static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
303 struct comedi_subdevice *s,
304 unsigned int *chanlist,
308 const struct boardtype *board = dev->board_ptr;
309 struct pci1710_private *devpriv = dev->private;
310 unsigned int first_chan = CR_CHAN(chanlist[0]);
311 unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
314 for (i = 0; i < seglen; i++) { /* store range list to card */
315 unsigned int chan = CR_CHAN(chanlist[i]);
316 unsigned int range = CR_RANGE(chanlist[i]);
317 unsigned int aref = CR_AREF(chanlist[i]);
318 unsigned int rangeval;
320 rangeval = board->rangecode_ai[range];
321 if (aref == AREF_DIFF)
324 /* select channel and set range */
325 outw(chan | (chan << 8), dev->iobase + PCI171X_MUX_REG);
326 outw(rangeval, dev->iobase + PCI171X_RANGE_REG);
328 devpriv->act_chanlist[i] = chan;
330 for ( ; i < n_chan; i++) /* store remainder of channel list */
331 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
333 /* select channel interval to scan */
334 devpriv->mux_ext = first_chan | (last_chan << 8);
335 outw(devpriv->mux_ext, dev->iobase + PCI171X_MUX_REG);
338 static int pci171x_ai_eoc(struct comedi_device *dev,
339 struct comedi_subdevice *s,
340 struct comedi_insn *insn,
341 unsigned long context)
345 status = inw(dev->iobase + PCI171X_STATUS_REG);
346 if ((status & PCI171X_STATUS_FE) == 0)
351 static int pci171x_ai_read_sample(struct comedi_device *dev,
352 struct comedi_subdevice *s,
353 unsigned int cur_chan,
356 const struct boardtype *board = dev->board_ptr;
357 struct pci1710_private *devpriv = dev->private;
361 sample = inw(dev->iobase + PCI171X_AD_DATA_REG);
362 if (!board->is_pci1713) {
364 * The upper 4 bits of the 16-bit sample are the channel number
365 * that the sample was acquired from. Verify that this channel
366 * number matches the expected channel number.
369 if (chan != devpriv->act_chanlist[cur_chan]) {
370 dev_err(dev->class_dev,
371 "A/D data droput: received from channel %d, expected %d\n",
372 chan, devpriv->act_chanlist[cur_chan]);
376 *val = sample & s->maxdata;
380 static int pci171x_ai_insn_read(struct comedi_device *dev,
381 struct comedi_subdevice *s,
382 struct comedi_insn *insn,
385 struct pci1710_private *devpriv = dev->private;
389 devpriv->ctrl &= PCI171X_CTRL_CNT0;
390 devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
391 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
392 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
393 outb(0, dev->iobase + PCI171X_CLRINT_REG);
395 pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
397 for (i = 0; i < insn->n; i++) {
400 /* start conversion */
401 outw(0, dev->iobase + PCI171X_SOFTTRG_REG);
403 ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
407 ret = pci171x_ai_read_sample(dev, s, 0, &val);
414 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
415 outb(0, dev->iobase + PCI171X_CLRINT_REG);
417 return ret ? ret : insn->n;
420 static int pci171x_ao_insn_write(struct comedi_device *dev,
421 struct comedi_subdevice *s,
422 struct comedi_insn *insn,
425 struct pci1710_private *devpriv = dev->private;
426 unsigned int chan = CR_CHAN(insn->chanspec);
427 unsigned int range = CR_RANGE(insn->chanspec);
428 unsigned int val = s->readback[chan];
431 devpriv->da_ranges &= ~(1 << (chan << 1));
432 devpriv->da_ranges |= (range << (chan << 1));
433 outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
435 for (i = 0; i < insn->n; i++) {
437 outw(val, dev->iobase + PCI171X_DA_REG(chan));
440 s->readback[chan] = val;
445 static int pci171x_di_insn_bits(struct comedi_device *dev,
446 struct comedi_subdevice *s,
447 struct comedi_insn *insn,
450 data[1] = inw(dev->iobase + PCI171X_DI_REG);
455 static int pci171x_do_insn_bits(struct comedi_device *dev,
456 struct comedi_subdevice *s,
457 struct comedi_insn *insn,
460 if (comedi_dio_update_state(s, data))
461 outw(s->state, dev->iobase + PCI171X_DO_REG);
468 static int pci1720_ao_insn_write(struct comedi_device *dev,
469 struct comedi_subdevice *s,
470 struct comedi_insn *insn,
473 struct pci1710_private *devpriv = dev->private;
474 unsigned int chan = CR_CHAN(insn->chanspec);
475 unsigned int range = CR_RANGE(insn->chanspec);
479 val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
480 val |= (range << (chan << 1));
481 if (val != devpriv->da_ranges) {
482 outb(val, dev->iobase + PCI1720_RANGE_REG);
483 devpriv->da_ranges = val;
486 val = s->readback[chan];
487 for (i = 0; i < insn->n; i++) {
489 outw(val, dev->iobase + PCI1720_DA_REG(chan));
490 outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
493 s->readback[chan] = val;
498 static int pci171x_ai_cancel(struct comedi_device *dev,
499 struct comedi_subdevice *s)
501 struct pci1710_private *devpriv = dev->private;
503 devpriv->ctrl &= PCI171X_CTRL_CNT0;
504 devpriv->ctrl |= PCI171X_CTRL_SW;
505 /* reset any operations */
506 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
507 comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
508 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
509 outb(0, dev->iobase + PCI171X_CLRINT_REG);
514 static void pci1710_handle_every_sample(struct comedi_device *dev,
515 struct comedi_subdevice *s)
517 struct comedi_cmd *cmd = &s->async->cmd;
522 status = inw(dev->iobase + PCI171X_STATUS_REG);
523 if (status & PCI171X_STATUS_FE) {
524 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
525 s->async->events |= COMEDI_CB_ERROR;
528 if (status & PCI171X_STATUS_FF) {
529 dev_dbg(dev->class_dev,
530 "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
531 s->async->events |= COMEDI_CB_ERROR;
535 outb(0, dev->iobase + PCI171X_CLRINT_REG);
537 for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) {
538 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
540 s->async->events |= COMEDI_CB_ERROR;
544 comedi_buf_write_samples(s, &val, 1);
546 if (cmd->stop_src == TRIG_COUNT &&
547 s->async->scans_done >= cmd->stop_arg) {
548 s->async->events |= COMEDI_CB_EOA;
553 outb(0, dev->iobase + PCI171X_CLRINT_REG);
556 static void pci1710_handle_fifo(struct comedi_device *dev,
557 struct comedi_subdevice *s)
559 struct pci1710_private *devpriv = dev->private;
560 struct comedi_async *async = s->async;
561 struct comedi_cmd *cmd = &async->cmd;
565 status = inw(dev->iobase + PCI171X_STATUS_REG);
566 if (!(status & PCI171X_STATUS_FH)) {
567 dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
568 async->events |= COMEDI_CB_ERROR;
571 if (status & PCI171X_STATUS_FF) {
572 dev_dbg(dev->class_dev,
573 "A/D FIFO Full status (Fatal Error!)\n");
574 async->events |= COMEDI_CB_ERROR;
578 for (i = 0; i < devpriv->max_samples; i++) {
582 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
584 s->async->events |= COMEDI_CB_ERROR;
588 if (!comedi_buf_write_samples(s, &val, 1))
591 if (cmd->stop_src == TRIG_COUNT &&
592 async->scans_done >= cmd->stop_arg) {
593 async->events |= COMEDI_CB_EOA;
598 outb(0, dev->iobase + PCI171X_CLRINT_REG);
601 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
603 struct comedi_device *dev = d;
604 struct pci1710_private *devpriv = dev->private;
605 struct comedi_subdevice *s;
606 struct comedi_cmd *cmd;
608 if (!dev->attached) /* is device attached? */
609 return IRQ_NONE; /* no, exit */
611 s = dev->read_subdev;
612 cmd = &s->async->cmd;
614 /* is this interrupt from our board? */
615 if (!(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ))
616 return IRQ_NONE; /* no, exit */
618 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
620 devpriv->ctrl &= PCI171X_CTRL_CNT0;
621 devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
622 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
623 devpriv->ctrl = devpriv->ctrl_ext;
624 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
625 outb(0, dev->iobase + PCI171X_CLRINT_REG);
626 /* no sample on this interrupt; reset the channel interval */
627 outw(devpriv->mux_ext, dev->iobase + PCI171X_MUX_REG);
628 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
629 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
633 if (cmd->flags & CMDF_WAKE_EOS)
634 pci1710_handle_every_sample(dev, s);
636 pci1710_handle_fifo(dev, s);
638 comedi_handle_events(dev, s);
643 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
645 struct pci1710_private *devpriv = dev->private;
646 struct comedi_cmd *cmd = &s->async->cmd;
648 pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
649 devpriv->saved_seglen);
651 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
652 outb(0, dev->iobase + PCI171X_CLRINT_REG);
654 devpriv->ctrl &= PCI171X_CTRL_CNT0;
655 if ((cmd->flags & CMDF_WAKE_EOS) == 0)
656 devpriv->ctrl |= PCI171X_CTRL_ONEFH;
658 if (cmd->convert_src == TRIG_TIMER) {
659 comedi_8254_update_divisors(dev->pacer);
661 devpriv->ctrl |= PCI171X_CTRL_PACER | PCI171X_CTRL_IRQEN;
662 if (cmd->start_src == TRIG_EXT) {
663 devpriv->ctrl_ext = devpriv->ctrl;
664 devpriv->ctrl &= ~(PCI171X_CTRL_PACER |
667 devpriv->ctrl |= PCI171X_CTRL_EXT;
669 } else { /* TRIG_NOW */
672 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
674 if (cmd->start_src == TRIG_NOW)
675 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
676 } else { /* TRIG_EXT */
677 devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN;
678 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
684 static int pci171x_ai_cmdtest(struct comedi_device *dev,
685 struct comedi_subdevice *s,
686 struct comedi_cmd *cmd)
690 /* Step 1 : check if triggers are trivially valid */
692 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
693 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
694 err |= comedi_check_trigger_src(&cmd->convert_src,
695 TRIG_TIMER | TRIG_EXT);
696 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
697 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
702 /* step 2a: make sure trigger sources are unique */
704 err |= comedi_check_trigger_is_unique(cmd->start_src);
705 err |= comedi_check_trigger_is_unique(cmd->convert_src);
706 err |= comedi_check_trigger_is_unique(cmd->stop_src);
708 /* step 2b: and mutually compatible */
713 /* Step 3: check if arguments are trivially valid */
715 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
716 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
718 if (cmd->convert_src == TRIG_TIMER)
719 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
720 else /* TRIG_FOLLOW */
721 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
723 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
726 if (cmd->stop_src == TRIG_COUNT)
727 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
729 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
734 /* step 4: fix up any arguments */
736 if (cmd->convert_src == TRIG_TIMER) {
737 unsigned int arg = cmd->convert_arg;
739 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
740 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
746 /* Step 5: check channel list */
748 err |= pci171x_ai_check_chanlist(dev, s, cmd);
756 static int pci171x_insn_counter_config(struct comedi_device *dev,
757 struct comedi_subdevice *s,
758 struct comedi_insn *insn,
761 struct pci1710_private *devpriv = dev->private;
764 case INSN_CONFIG_SET_CLOCK_SRC:
766 case 0: /* internal */
767 devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0;
769 case 1: /* external */
770 devpriv->ctrl_ext |= PCI171X_CTRL_CNT0;
775 outw(devpriv->ctrl_ext, dev->iobase + PCI171X_CTRL_REG);
777 case INSN_CONFIG_GET_CLOCK_SRC:
778 if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) {
783 data[2] = I8254_OSC_BASE_10MHZ;
793 static int pci171x_reset(struct comedi_device *dev)
795 const struct boardtype *board = dev->board_ptr;
796 struct pci1710_private *devpriv = dev->private;
798 /* Software trigger, CNT0=external */
799 devpriv->ctrl = PCI171X_CTRL_SW | PCI171X_CTRL_CNT0;
800 /* reset any operations */
801 outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG);
802 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
803 outb(0, dev->iobase + PCI171X_CLRINT_REG);
804 devpriv->da_ranges = 0;
806 /* set DACs to 0..5V and outputs to 0V */
807 outb(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG);
808 outw(0, dev->iobase + PCI171X_DA_REG(0));
809 outw(0, dev->iobase + PCI171X_DA_REG(1));
811 outw(0, dev->iobase + PCI171X_DO_REG); /* digital outputs to 0 */
812 outb(0, dev->iobase + PCI171X_CLRFIFO_REG);
813 outb(0, dev->iobase + PCI171X_CLRINT_REG);
818 static int pci1720_reset(struct comedi_device *dev)
820 struct pci1710_private *devpriv = dev->private;
821 /* set synchronous output mode */
822 outb(PCI1720_SYNC_CTRL_SC0, dev->iobase + PCI1720_SYNC_CTRL_REG);
823 devpriv->da_ranges = 0xAA;
824 /* set all ranges to +/-5V and outputs to 0V */
825 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE_REG);
826 outw(0x0800, dev->iobase + PCI1720_DA_REG(0));
827 outw(0x0800, dev->iobase + PCI1720_DA_REG(1));
828 outw(0x0800, dev->iobase + PCI1720_DA_REG(2));
829 outw(0x0800, dev->iobase + PCI1720_DA_REG(3));
830 outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
835 static int pci1710_reset(struct comedi_device *dev)
837 const struct boardtype *board = dev->board_ptr;
839 if (board->is_pci1720)
840 return pci1720_reset(dev);
842 return pci171x_reset(dev);
845 static int pci1710_auto_attach(struct comedi_device *dev,
846 unsigned long context)
848 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
849 const struct boardtype *board = NULL;
850 struct pci1710_private *devpriv;
851 struct comedi_subdevice *s;
852 int ret, subdev, n_subdevices;
854 if (context < ARRAY_SIZE(boardtypes))
855 board = &boardtypes[context];
858 dev->board_ptr = board;
859 dev->board_name = board->name;
861 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
865 ret = comedi_pci_enable(dev);
868 dev->iobase = pci_resource_start(pcidev, 2);
870 dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
871 I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
880 if (board->has_di_do)
882 if (board->has_counter)
885 ret = comedi_alloc_subdevices(dev, n_subdevices);
891 if (board->has_irq && pcidev->irq) {
892 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
893 IRQF_SHARED, dev->board_name, dev);
895 dev->irq = pcidev->irq;
900 if (board->n_aichan) {
901 s = &dev->subdevices[subdev];
902 s->type = COMEDI_SUBD_AI;
903 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
904 if (board->has_diff_ai)
905 s->subdev_flags |= SDF_DIFF;
906 s->n_chan = board->n_aichan;
908 s->range_table = board->rangelist_ai;
909 s->insn_read = pci171x_ai_insn_read;
911 dev->read_subdev = s;
912 s->subdev_flags |= SDF_CMD_READ;
913 s->len_chanlist = s->n_chan;
914 s->do_cmdtest = pci171x_ai_cmdtest;
915 s->do_cmd = pci171x_ai_cmd;
916 s->cancel = pci171x_ai_cancel;
922 s = &dev->subdevices[subdev];
923 s->type = COMEDI_SUBD_AO;
924 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
926 if (board->is_pci1720) {
928 s->range_table = &pci1720_ao_range;
929 s->insn_write = pci1720_ao_insn_write;
932 s->range_table = &pci171x_ao_range;
933 s->insn_write = pci171x_ao_insn_write;
936 ret = comedi_alloc_subdev_readback(s);
940 /* initialize the readback values to match the board reset */
941 if (board->is_pci1720) {
944 for (i = 0; i < s->n_chan; i++)
945 s->readback[i] = 0x0800;
951 if (board->has_di_do) {
952 s = &dev->subdevices[subdev];
953 s->type = COMEDI_SUBD_DI;
954 s->subdev_flags = SDF_READABLE;
957 s->range_table = &range_digital;
958 s->insn_bits = pci171x_di_insn_bits;
961 s = &dev->subdevices[subdev];
962 s->type = COMEDI_SUBD_DO;
963 s->subdev_flags = SDF_WRITABLE;
966 s->range_table = &range_digital;
967 s->insn_bits = pci171x_do_insn_bits;
971 /* Counter subdevice (8254) */
972 if (board->has_counter) {
973 s = &dev->subdevices[subdev];
974 comedi_8254_subdevice_init(s, dev->pacer);
976 dev->pacer->insn_config = pci171x_insn_counter_config;
978 /* counters 1 and 2 are used internally for the pacer */
979 comedi_8254_set_busy(dev->pacer, 1, true);
980 comedi_8254_set_busy(dev->pacer, 2, true);
985 /* max_samples is half the FIFO size (2 bytes/sample) */
986 devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
991 static void pci1710_detach(struct comedi_device *dev)
995 comedi_pci_detach(dev);
998 static struct comedi_driver adv_pci1710_driver = {
999 .driver_name = "adv_pci1710",
1000 .module = THIS_MODULE,
1001 .auto_attach = pci1710_auto_attach,
1002 .detach = pci1710_detach,
1005 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1006 const struct pci_device_id *id)
1008 return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1012 static const struct pci_device_id adv_pci1710_pci_table[] = {
1014 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1015 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1016 .driver_data = BOARD_PCI1710,
1018 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1019 PCI_VENDOR_ID_ADVANTECH, 0x0000),
1020 .driver_data = BOARD_PCI1710,
1022 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1023 PCI_VENDOR_ID_ADVANTECH, 0xb100),
1024 .driver_data = BOARD_PCI1710,
1026 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1027 PCI_VENDOR_ID_ADVANTECH, 0xb200),
1028 .driver_data = BOARD_PCI1710,
1030 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1031 PCI_VENDOR_ID_ADVANTECH, 0xc100),
1032 .driver_data = BOARD_PCI1710,
1034 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1035 PCI_VENDOR_ID_ADVANTECH, 0xc200),
1036 .driver_data = BOARD_PCI1710,
1038 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1039 .driver_data = BOARD_PCI1710,
1041 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1042 PCI_VENDOR_ID_ADVANTECH, 0x0002),
1043 .driver_data = BOARD_PCI1710HG,
1045 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1046 PCI_VENDOR_ID_ADVANTECH, 0xb102),
1047 .driver_data = BOARD_PCI1710HG,
1049 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1050 PCI_VENDOR_ID_ADVANTECH, 0xb202),
1051 .driver_data = BOARD_PCI1710HG,
1053 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1054 PCI_VENDOR_ID_ADVANTECH, 0xc102),
1055 .driver_data = BOARD_PCI1710HG,
1057 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1058 PCI_VENDOR_ID_ADVANTECH, 0xc202),
1059 .driver_data = BOARD_PCI1710HG,
1061 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1062 .driver_data = BOARD_PCI1710HG,
1064 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1065 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1066 { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1067 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1070 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1072 static struct pci_driver adv_pci1710_pci_driver = {
1073 .name = "adv_pci1710",
1074 .id_table = adv_pci1710_pci_table,
1075 .probe = adv_pci1710_pci_probe,
1076 .remove = comedi_pci_auto_unconfig,
1078 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1080 MODULE_AUTHOR("Comedi http://www.comedi.org");
1081 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
1082 MODULE_LICENSE("GPL");