1 // SPDX-License-Identifier: GPL-2.0
4 * Comedi driver for Advantech PCI-1710 series boards
5 * Author: Michal Dobes <dobes@tesnet.cz>
7 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
8 * for testing and information.
13 * Description: Comedi driver for Advantech PCI-1710 series boards
14 * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
16 * Author: Michal Dobes <dobes@tesnet.cz>
17 * Updated: Fri, 29 Oct 2015 17:19:35 -0700
20 * Configuration options: not applicable, uses PCI auto config
22 * This driver supports AI, AO, DI and DO subdevices.
23 * AI subdevice supports cmd and insn interface,
24 * other subdevices support only insn interface.
26 * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
27 * driver cannot distinguish between them, as would be normal for a
31 #include <linux/module.h>
32 #include <linux/interrupt.h>
33 #include <linux/comedi/comedi_pci.h>
34 #include <linux/comedi/comedi_8254.h>
36 #include "amcc_s5933.h"
39 * PCI BAR2 Register map (dev->iobase)
41 #define PCI171X_AD_DATA_REG 0x00 /* R: A/D data */
42 #define PCI171X_SOFTTRG_REG 0x00 /* W: soft trigger for A/D */
43 #define PCI171X_RANGE_REG 0x02 /* W: A/D gain/range register */
44 #define PCI171X_RANGE_DIFF BIT(5)
45 #define PCI171X_RANGE_UNI BIT(4)
46 #define PCI171X_RANGE_GAIN(x) (((x) & 0x7) << 0)
47 #define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */
48 #define PCI171X_MUX_CHANH(x) (((x) & 0xff) << 8)
49 #define PCI171X_MUX_CHANL(x) (((x) & 0xff) << 0)
50 #define PCI171X_MUX_CHAN(x) (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x))
51 #define PCI171X_STATUS_REG 0x06 /* R: status register */
52 #define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */
53 #define PCI171X_STATUS_FF BIT(10) /* 1=FIFO is full, fatal error */
54 #define PCI171X_STATUS_FH BIT(9) /* 1=FIFO is half full */
55 #define PCI171X_STATUS_FE BIT(8) /* 1=FIFO is empty */
56 #define PCI171X_CTRL_REG 0x06 /* W: control register */
57 #define PCI171X_CTRL_CNT0 BIT(6) /* 1=ext. clk, 0=int. 100kHz clk */
58 #define PCI171X_CTRL_ONEFH BIT(5) /* 1=on FIFO half full, 0=on sample */
59 #define PCI171X_CTRL_IRQEN BIT(4) /* 1=enable IRQ */
60 #define PCI171X_CTRL_GATE BIT(3) /* 1=enable ext. trigger GATE (8254?) */
61 #define PCI171X_CTRL_EXT BIT(2) /* 1=enable ext. trigger source */
62 #define PCI171X_CTRL_PACER BIT(1) /* 1=enable int. 8254 trigger source */
63 #define PCI171X_CTRL_SW BIT(0) /* 1=enable software trigger source */
64 #define PCI171X_CLRINT_REG 0x08 /* W: clear interrupts request */
65 #define PCI171X_CLRFIFO_REG 0x09 /* W: clear FIFO */
66 #define PCI171X_DA_REG(x) (0x0a + ((x) * 2)) /* W: D/A register */
67 #define PCI171X_DAREF_REG 0x0e /* W: D/A reference control */
68 #define PCI171X_DAREF(c, r) (((r) & 0x3) << ((c) * 2))
69 #define PCI171X_DAREF_MASK(c) PCI171X_DAREF((c), 0x3)
70 #define PCI171X_DI_REG 0x10 /* R: digital inputs */
71 #define PCI171X_DO_REG 0x10 /* W: digital outputs */
72 #define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */
74 static const struct comedi_lrange pci1710_ai_range
= {
76 BIP_RANGE(5), /* gain 1 (0x00) */
77 BIP_RANGE(2.5), /* gain 2 (0x01) */
78 BIP_RANGE(1.25), /* gain 4 (0x02) */
79 BIP_RANGE(0.625), /* gain 8 (0x03) */
80 BIP_RANGE(10), /* gain 0.5 (0x04) */
81 UNI_RANGE(10), /* gain 1 (0x00 | UNI) */
82 UNI_RANGE(5), /* gain 2 (0x01 | UNI) */
83 UNI_RANGE(2.5), /* gain 4 (0x02 | UNI) */
84 UNI_RANGE(1.25) /* gain 8 (0x03 | UNI) */
88 static const struct comedi_lrange pci1710hg_ai_range
= {
90 BIP_RANGE(5), /* gain 1 (0x00) */
91 BIP_RANGE(0.5), /* gain 10 (0x01) */
92 BIP_RANGE(0.05), /* gain 100 (0x02) */
93 BIP_RANGE(0.005), /* gain 1000 (0x03) */
94 BIP_RANGE(10), /* gain 0.5 (0x04) */
95 BIP_RANGE(1), /* gain 5 (0x05) */
96 BIP_RANGE(0.1), /* gain 50 (0x06) */
97 BIP_RANGE(0.01), /* gain 500 (0x07) */
98 UNI_RANGE(10), /* gain 1 (0x00 | UNI) */
99 UNI_RANGE(1), /* gain 10 (0x01 | UNI) */
100 UNI_RANGE(0.1), /* gain 100 (0x02 | UNI) */
101 UNI_RANGE(0.01) /* gain 1000 (0x03 | UNI) */
105 static const struct comedi_lrange pci1711_ai_range
= {
107 BIP_RANGE(10), /* gain 1 (0x00) */
108 BIP_RANGE(5), /* gain 2 (0x01) */
109 BIP_RANGE(2.5), /* gain 4 (0x02) */
110 BIP_RANGE(1.25), /* gain 8 (0x03) */
111 BIP_RANGE(0.625) /* gain 16 (0x04) */
115 static const struct comedi_lrange pci171x_ao_range
= {
117 UNI_RANGE(5), /* internal -5V ref */
118 UNI_RANGE(10), /* internal -10V ref */
119 RANGE_ext(0, 1) /* external -Vref (+/-10V max) */
123 enum pci1710_boardid
{
133 const struct comedi_lrange
*ai_range
;
134 unsigned int is_pci1711
:1;
135 unsigned int is_pci1713
:1;
136 unsigned int has_ao
:1;
139 static const struct boardtype boardtypes
[] = {
142 .ai_range
= &pci1710_ai_range
,
145 [BOARD_PCI1710HG
] = {
147 .ai_range
= &pci1710hg_ai_range
,
152 .ai_range
= &pci1711_ai_range
,
158 .ai_range
= &pci1710_ai_range
,
163 .ai_range
= &pci1711_ai_range
,
168 struct pci1710_private
{
169 unsigned int max_samples
;
170 unsigned int ctrl
; /* control register value */
171 unsigned int ctrl_ext
; /* used to switch from TRIG_EXT to TRIG_xxx */
172 unsigned int mux_scan
; /* used to set the channel interval to scan */
174 unsigned int act_chanlist
[32]; /* list of scanned channel */
175 unsigned char saved_seglen
; /* len of the non-repeating chanlist */
176 unsigned char da_ranges
; /* copy of D/A outpit range register */
177 unsigned char unipolar_gain
; /* adjust for unipolar gain codes */
180 static int pci1710_ai_check_chanlist(struct comedi_device
*dev
,
181 struct comedi_subdevice
*s
,
182 struct comedi_cmd
*cmd
)
184 struct pci1710_private
*devpriv
= dev
->private;
185 unsigned int chan0
= CR_CHAN(cmd
->chanlist
[0]);
186 unsigned int last_aref
= CR_AREF(cmd
->chanlist
[0]);
187 unsigned int next_chan
= (chan0
+ 1) % s
->n_chan
;
188 unsigned int chansegment
[32];
192 if (cmd
->chanlist_len
== 1) {
193 devpriv
->saved_seglen
= cmd
->chanlist_len
;
197 /* first channel is always ok */
198 chansegment
[0] = cmd
->chanlist
[0];
200 for (i
= 1; i
< cmd
->chanlist_len
; i
++) {
201 unsigned int chan
= CR_CHAN(cmd
->chanlist
[i
]);
202 unsigned int aref
= CR_AREF(cmd
->chanlist
[i
]);
204 if (cmd
->chanlist
[0] == cmd
->chanlist
[i
])
205 break; /* we detected a loop, stop */
207 if (aref
== AREF_DIFF
&& (chan
& 1)) {
208 dev_err(dev
->class_dev
,
209 "Odd channel cannot be differential input!\n");
213 if (last_aref
== AREF_DIFF
)
214 next_chan
= (next_chan
+ 1) % s
->n_chan
;
215 if (chan
!= next_chan
) {
216 dev_err(dev
->class_dev
,
217 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
218 i
, chan
, next_chan
, chan0
);
222 /* next correct channel in list */
223 chansegment
[i
] = cmd
->chanlist
[i
];
228 for (i
= 0; i
< cmd
->chanlist_len
; i
++) {
229 if (cmd
->chanlist
[i
] != chansegment
[i
% seglen
]) {
230 dev_err(dev
->class_dev
,
231 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
232 i
, CR_CHAN(chansegment
[i
]),
233 CR_RANGE(chansegment
[i
]),
234 CR_AREF(chansegment
[i
]),
235 CR_CHAN(cmd
->chanlist
[i
% seglen
]),
236 CR_RANGE(cmd
->chanlist
[i
% seglen
]),
237 CR_AREF(chansegment
[i
% seglen
]));
241 devpriv
->saved_seglen
= seglen
;
246 static void pci1710_ai_setup_chanlist(struct comedi_device
*dev
,
247 struct comedi_subdevice
*s
,
248 unsigned int *chanlist
,
252 struct pci1710_private
*devpriv
= dev
->private;
253 unsigned int first_chan
= CR_CHAN(chanlist
[0]);
254 unsigned int last_chan
= CR_CHAN(chanlist
[seglen
- 1]);
257 for (i
= 0; i
< seglen
; i
++) { /* store range list to card */
258 unsigned int chan
= CR_CHAN(chanlist
[i
]);
259 unsigned int range
= CR_RANGE(chanlist
[i
]);
260 unsigned int aref
= CR_AREF(chanlist
[i
]);
261 unsigned int rangeval
= 0;
263 if (aref
== AREF_DIFF
)
264 rangeval
|= PCI171X_RANGE_DIFF
;
265 if (comedi_range_is_unipolar(s
, range
)) {
266 rangeval
|= PCI171X_RANGE_UNI
;
267 range
-= devpriv
->unipolar_gain
;
269 rangeval
|= PCI171X_RANGE_GAIN(range
);
271 /* select channel and set range */
272 outw(PCI171X_MUX_CHAN(chan
), dev
->iobase
+ PCI171X_MUX_REG
);
273 outw(rangeval
, dev
->iobase
+ PCI171X_RANGE_REG
);
275 devpriv
->act_chanlist
[i
] = chan
;
277 for ( ; i
< n_chan
; i
++) /* store remainder of channel list */
278 devpriv
->act_chanlist
[i
] = CR_CHAN(chanlist
[i
]);
280 /* select channel interval to scan */
281 devpriv
->mux_scan
= PCI171X_MUX_CHANL(first_chan
) |
282 PCI171X_MUX_CHANH(last_chan
);
283 outw(devpriv
->mux_scan
, dev
->iobase
+ PCI171X_MUX_REG
);
286 static int pci1710_ai_eoc(struct comedi_device
*dev
,
287 struct comedi_subdevice
*s
,
288 struct comedi_insn
*insn
,
289 unsigned long context
)
293 status
= inw(dev
->iobase
+ PCI171X_STATUS_REG
);
294 if ((status
& PCI171X_STATUS_FE
) == 0)
299 static int pci1710_ai_read_sample(struct comedi_device
*dev
,
300 struct comedi_subdevice
*s
,
301 unsigned int cur_chan
,
304 const struct boardtype
*board
= dev
->board_ptr
;
305 struct pci1710_private
*devpriv
= dev
->private;
306 unsigned short sample
;
309 sample
= inw(dev
->iobase
+ PCI171X_AD_DATA_REG
);
310 if (!board
->is_pci1713
) {
312 * The upper 4 bits of the 16-bit sample are the channel number
313 * that the sample was acquired from. Verify that this channel
314 * number matches the expected channel number.
317 if (chan
!= devpriv
->act_chanlist
[cur_chan
]) {
318 dev_err(dev
->class_dev
,
319 "A/D data dropout: received from channel %d, expected %d\n",
320 chan
, devpriv
->act_chanlist
[cur_chan
]);
324 *val
= sample
& s
->maxdata
;
328 static int pci1710_ai_insn_read(struct comedi_device
*dev
,
329 struct comedi_subdevice
*s
,
330 struct comedi_insn
*insn
,
333 struct pci1710_private
*devpriv
= dev
->private;
337 /* enable software trigger */
338 devpriv
->ctrl
|= PCI171X_CTRL_SW
;
339 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
341 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
342 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
344 pci1710_ai_setup_chanlist(dev
, s
, &insn
->chanspec
, 1, 1);
346 for (i
= 0; i
< insn
->n
; i
++) {
349 /* start conversion */
350 outw(0, dev
->iobase
+ PCI171X_SOFTTRG_REG
);
352 ret
= comedi_timeout(dev
, s
, insn
, pci1710_ai_eoc
, 0);
356 ret
= pci1710_ai_read_sample(dev
, s
, 0, &val
);
363 /* disable software trigger */
364 devpriv
->ctrl
&= ~PCI171X_CTRL_SW
;
365 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
367 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
368 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
370 return ret
? ret
: insn
->n
;
373 static int pci1710_ai_cancel(struct comedi_device
*dev
,
374 struct comedi_subdevice
*s
)
376 struct pci1710_private
*devpriv
= dev
->private;
378 /* disable A/D triggers and interrupt sources */
379 devpriv
->ctrl
&= PCI171X_CTRL_CNT0
; /* preserve counter 0 clk src */
380 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
383 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, false);
385 /* clear A/D FIFO and any pending interrutps */
386 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
387 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
392 static void pci1710_handle_every_sample(struct comedi_device
*dev
,
393 struct comedi_subdevice
*s
)
395 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
400 status
= inw(dev
->iobase
+ PCI171X_STATUS_REG
);
401 if (status
& PCI171X_STATUS_FE
) {
402 dev_dbg(dev
->class_dev
, "A/D FIFO empty (%4x)\n", status
);
403 s
->async
->events
|= COMEDI_CB_ERROR
;
406 if (status
& PCI171X_STATUS_FF
) {
407 dev_dbg(dev
->class_dev
,
408 "A/D FIFO Full status (Fatal Error!) (%4x)\n", status
);
409 s
->async
->events
|= COMEDI_CB_ERROR
;
413 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
415 for (; !(inw(dev
->iobase
+ PCI171X_STATUS_REG
) & PCI171X_STATUS_FE
);) {
416 ret
= pci1710_ai_read_sample(dev
, s
, s
->async
->cur_chan
, &val
);
418 s
->async
->events
|= COMEDI_CB_ERROR
;
422 comedi_buf_write_samples(s
, &val
, 1);
424 if (cmd
->stop_src
== TRIG_COUNT
&&
425 s
->async
->scans_done
>= cmd
->stop_arg
) {
426 s
->async
->events
|= COMEDI_CB_EOA
;
431 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
434 static void pci1710_handle_fifo(struct comedi_device
*dev
,
435 struct comedi_subdevice
*s
)
437 struct pci1710_private
*devpriv
= dev
->private;
438 struct comedi_async
*async
= s
->async
;
439 struct comedi_cmd
*cmd
= &async
->cmd
;
443 status
= inw(dev
->iobase
+ PCI171X_STATUS_REG
);
444 if (!(status
& PCI171X_STATUS_FH
)) {
445 dev_dbg(dev
->class_dev
, "A/D FIFO not half full!\n");
446 async
->events
|= COMEDI_CB_ERROR
;
449 if (status
& PCI171X_STATUS_FF
) {
450 dev_dbg(dev
->class_dev
,
451 "A/D FIFO Full status (Fatal Error!)\n");
452 async
->events
|= COMEDI_CB_ERROR
;
456 for (i
= 0; i
< devpriv
->max_samples
; i
++) {
460 ret
= pci1710_ai_read_sample(dev
, s
, s
->async
->cur_chan
, &val
);
462 s
->async
->events
|= COMEDI_CB_ERROR
;
466 if (!comedi_buf_write_samples(s
, &val
, 1))
469 if (cmd
->stop_src
== TRIG_COUNT
&&
470 async
->scans_done
>= cmd
->stop_arg
) {
471 async
->events
|= COMEDI_CB_EOA
;
476 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
479 static irqreturn_t
pci1710_irq_handler(int irq
, void *d
)
481 struct comedi_device
*dev
= d
;
482 struct pci1710_private
*devpriv
= dev
->private;
483 struct comedi_subdevice
*s
;
484 struct comedi_cmd
*cmd
;
486 if (!dev
->attached
) /* is device attached? */
487 return IRQ_NONE
; /* no, exit */
489 s
= dev
->read_subdev
;
490 cmd
= &s
->async
->cmd
;
492 /* is this interrupt from our board? */
493 if (!(inw(dev
->iobase
+ PCI171X_STATUS_REG
) & PCI171X_STATUS_IRQ
))
494 return IRQ_NONE
; /* no, exit */
496 if (devpriv
->ai_et
) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
498 devpriv
->ctrl
&= PCI171X_CTRL_CNT0
;
499 devpriv
->ctrl
|= PCI171X_CTRL_SW
; /* set software trigger */
500 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
501 devpriv
->ctrl
= devpriv
->ctrl_ext
;
502 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
503 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
504 /* no sample on this interrupt; reset the channel interval */
505 outw(devpriv
->mux_scan
, dev
->iobase
+ PCI171X_MUX_REG
);
506 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
507 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
511 if (cmd
->flags
& CMDF_WAKE_EOS
)
512 pci1710_handle_every_sample(dev
, s
);
514 pci1710_handle_fifo(dev
, s
);
516 comedi_handle_events(dev
, s
);
521 static int pci1710_ai_cmd(struct comedi_device
*dev
, struct comedi_subdevice
*s
)
523 struct pci1710_private
*devpriv
= dev
->private;
524 struct comedi_cmd
*cmd
= &s
->async
->cmd
;
526 pci1710_ai_setup_chanlist(dev
, s
, cmd
->chanlist
, cmd
->chanlist_len
,
527 devpriv
->saved_seglen
);
529 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
530 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
532 devpriv
->ctrl
&= PCI171X_CTRL_CNT0
;
533 if ((cmd
->flags
& CMDF_WAKE_EOS
) == 0)
534 devpriv
->ctrl
|= PCI171X_CTRL_ONEFH
;
536 if (cmd
->convert_src
== TRIG_TIMER
) {
537 comedi_8254_update_divisors(dev
->pacer
);
539 devpriv
->ctrl
|= PCI171X_CTRL_PACER
| PCI171X_CTRL_IRQEN
;
540 if (cmd
->start_src
== TRIG_EXT
) {
541 devpriv
->ctrl_ext
= devpriv
->ctrl
;
542 devpriv
->ctrl
&= ~(PCI171X_CTRL_PACER
|
545 devpriv
->ctrl
|= PCI171X_CTRL_EXT
;
547 } else { /* TRIG_NOW */
550 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
552 if (cmd
->start_src
== TRIG_NOW
)
553 comedi_8254_pacer_enable(dev
->pacer
, 1, 2, true);
554 } else { /* TRIG_EXT */
555 devpriv
->ctrl
|= PCI171X_CTRL_EXT
| PCI171X_CTRL_IRQEN
;
556 outw(devpriv
->ctrl
, dev
->iobase
+ PCI171X_CTRL_REG
);
562 static int pci1710_ai_cmdtest(struct comedi_device
*dev
,
563 struct comedi_subdevice
*s
,
564 struct comedi_cmd
*cmd
)
568 /* Step 1 : check if triggers are trivially valid */
570 err
|= comedi_check_trigger_src(&cmd
->start_src
, TRIG_NOW
| TRIG_EXT
);
571 err
|= comedi_check_trigger_src(&cmd
->scan_begin_src
, TRIG_FOLLOW
);
572 err
|= comedi_check_trigger_src(&cmd
->convert_src
,
573 TRIG_TIMER
| TRIG_EXT
);
574 err
|= comedi_check_trigger_src(&cmd
->scan_end_src
, TRIG_COUNT
);
575 err
|= comedi_check_trigger_src(&cmd
->stop_src
, TRIG_COUNT
| TRIG_NONE
);
580 /* step 2a: make sure trigger sources are unique */
582 err
|= comedi_check_trigger_is_unique(cmd
->start_src
);
583 err
|= comedi_check_trigger_is_unique(cmd
->convert_src
);
584 err
|= comedi_check_trigger_is_unique(cmd
->stop_src
);
586 /* step 2b: and mutually compatible */
591 /* Step 3: check if arguments are trivially valid */
593 err
|= comedi_check_trigger_arg_is(&cmd
->start_arg
, 0);
594 err
|= comedi_check_trigger_arg_is(&cmd
->scan_begin_arg
, 0);
596 if (cmd
->convert_src
== TRIG_TIMER
)
597 err
|= comedi_check_trigger_arg_min(&cmd
->convert_arg
, 10000);
598 else /* TRIG_FOLLOW */
599 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, 0);
601 err
|= comedi_check_trigger_arg_is(&cmd
->scan_end_arg
,
604 if (cmd
->stop_src
== TRIG_COUNT
)
605 err
|= comedi_check_trigger_arg_min(&cmd
->stop_arg
, 1);
607 err
|= comedi_check_trigger_arg_is(&cmd
->stop_arg
, 0);
612 /* step 4: fix up any arguments */
614 if (cmd
->convert_src
== TRIG_TIMER
) {
615 unsigned int arg
= cmd
->convert_arg
;
617 comedi_8254_cascade_ns_to_timer(dev
->pacer
, &arg
, cmd
->flags
);
618 err
|= comedi_check_trigger_arg_is(&cmd
->convert_arg
, arg
);
624 /* Step 5: check channel list */
626 err
|= pci1710_ai_check_chanlist(dev
, s
, cmd
);
634 static int pci1710_ao_insn_write(struct comedi_device
*dev
,
635 struct comedi_subdevice
*s
,
636 struct comedi_insn
*insn
,
639 struct pci1710_private
*devpriv
= dev
->private;
640 unsigned int chan
= CR_CHAN(insn
->chanspec
);
641 unsigned int range
= CR_RANGE(insn
->chanspec
);
642 unsigned int val
= s
->readback
[chan
];
645 devpriv
->da_ranges
&= ~PCI171X_DAREF_MASK(chan
);
646 devpriv
->da_ranges
|= PCI171X_DAREF(chan
, range
);
647 outw(devpriv
->da_ranges
, dev
->iobase
+ PCI171X_DAREF_REG
);
649 for (i
= 0; i
< insn
->n
; i
++) {
651 outw(val
, dev
->iobase
+ PCI171X_DA_REG(chan
));
654 s
->readback
[chan
] = val
;
659 static int pci1710_di_insn_bits(struct comedi_device
*dev
,
660 struct comedi_subdevice
*s
,
661 struct comedi_insn
*insn
,
664 data
[1] = inw(dev
->iobase
+ PCI171X_DI_REG
);
669 static int pci1710_do_insn_bits(struct comedi_device
*dev
,
670 struct comedi_subdevice
*s
,
671 struct comedi_insn
*insn
,
674 if (comedi_dio_update_state(s
, data
))
675 outw(s
->state
, dev
->iobase
+ PCI171X_DO_REG
);
682 static int pci1710_counter_insn_config(struct comedi_device
*dev
,
683 struct comedi_subdevice
*s
,
684 struct comedi_insn
*insn
,
687 struct pci1710_private
*devpriv
= dev
->private;
690 case INSN_CONFIG_SET_CLOCK_SRC
:
692 case 0: /* internal */
693 devpriv
->ctrl_ext
&= ~PCI171X_CTRL_CNT0
;
695 case 1: /* external */
696 devpriv
->ctrl_ext
|= PCI171X_CTRL_CNT0
;
701 outw(devpriv
->ctrl_ext
, dev
->iobase
+ PCI171X_CTRL_REG
);
703 case INSN_CONFIG_GET_CLOCK_SRC
:
704 if (devpriv
->ctrl_ext
& PCI171X_CTRL_CNT0
) {
709 data
[2] = I8254_OSC_BASE_1MHZ
;
719 static void pci1710_reset(struct comedi_device
*dev
)
721 const struct boardtype
*board
= dev
->board_ptr
;
724 * Disable A/D triggers and interrupt sources, set counter 0
725 * to use internal 1 MHz clock.
727 outw(0, dev
->iobase
+ PCI171X_CTRL_REG
);
729 /* clear A/D FIFO and any pending interrutps */
730 outb(0, dev
->iobase
+ PCI171X_CLRFIFO_REG
);
731 outb(0, dev
->iobase
+ PCI171X_CLRINT_REG
);
734 /* set DACs to 0..5V and outputs to 0V */
735 outb(0, dev
->iobase
+ PCI171X_DAREF_REG
);
736 outw(0, dev
->iobase
+ PCI171X_DA_REG(0));
737 outw(0, dev
->iobase
+ PCI171X_DA_REG(1));
740 /* set digital outputs to 0 */
741 outw(0, dev
->iobase
+ PCI171X_DO_REG
);
744 static int pci1710_auto_attach(struct comedi_device
*dev
,
745 unsigned long context
)
747 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
748 const struct boardtype
*board
= NULL
;
749 struct pci1710_private
*devpriv
;
750 struct comedi_subdevice
*s
;
751 int ret
, subdev
, n_subdevices
;
754 if (context
< ARRAY_SIZE(boardtypes
))
755 board
= &boardtypes
[context
];
758 dev
->board_ptr
= board
;
759 dev
->board_name
= board
->name
;
761 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
765 ret
= comedi_pci_enable(dev
);
768 dev
->iobase
= pci_resource_start(pcidev
, 2);
770 dev
->pacer
= comedi_8254_io_alloc(dev
->iobase
+ PCI171X_TIMER_BASE
,
771 I8254_OSC_BASE_10MHZ
, I8254_IO16
, 0);
772 if (IS_ERR(dev
->pacer
))
773 return PTR_ERR(dev
->pacer
);
775 n_subdevices
= 1; /* all boards have analog inputs */
778 if (!board
->is_pci1713
) {
780 * All other boards have digital inputs and outputs as
781 * well as a user counter.
786 ret
= comedi_alloc_subdevices(dev
, n_subdevices
);
793 ret
= request_irq(pcidev
->irq
, pci1710_irq_handler
,
794 IRQF_SHARED
, dev
->board_name
, dev
);
796 dev
->irq
= pcidev
->irq
;
801 /* Analog Input subdevice */
802 s
= &dev
->subdevices
[subdev
++];
803 s
->type
= COMEDI_SUBD_AI
;
804 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
805 if (!board
->is_pci1711
)
806 s
->subdev_flags
|= SDF_DIFF
;
807 s
->n_chan
= board
->is_pci1713
? 32 : 16;
809 s
->range_table
= board
->ai_range
;
810 s
->insn_read
= pci1710_ai_insn_read
;
812 dev
->read_subdev
= s
;
813 s
->subdev_flags
|= SDF_CMD_READ
;
814 s
->len_chanlist
= s
->n_chan
;
815 s
->do_cmdtest
= pci1710_ai_cmdtest
;
816 s
->do_cmd
= pci1710_ai_cmd
;
817 s
->cancel
= pci1710_ai_cancel
;
820 /* find the value needed to adjust for unipolar gain codes */
821 for (i
= 0; i
< s
->range_table
->length
; i
++) {
822 if (comedi_range_is_unipolar(s
, i
)) {
823 devpriv
->unipolar_gain
= i
;
829 /* Analog Output subdevice */
830 s
= &dev
->subdevices
[subdev
++];
831 s
->type
= COMEDI_SUBD_AO
;
832 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
835 s
->range_table
= &pci171x_ao_range
;
836 s
->insn_write
= pci1710_ao_insn_write
;
838 ret
= comedi_alloc_subdev_readback(s
);
843 if (!board
->is_pci1713
) {
844 /* Digital Input subdevice */
845 s
= &dev
->subdevices
[subdev
++];
846 s
->type
= COMEDI_SUBD_DI
;
847 s
->subdev_flags
= SDF_READABLE
;
850 s
->range_table
= &range_digital
;
851 s
->insn_bits
= pci1710_di_insn_bits
;
853 /* Digital Output subdevice */
854 s
= &dev
->subdevices
[subdev
++];
855 s
->type
= COMEDI_SUBD_DO
;
856 s
->subdev_flags
= SDF_WRITABLE
;
859 s
->range_table
= &range_digital
;
860 s
->insn_bits
= pci1710_do_insn_bits
;
862 /* Counter subdevice (8254) */
863 s
= &dev
->subdevices
[subdev
++];
864 comedi_8254_subdevice_init(s
, dev
->pacer
);
866 dev
->pacer
->insn_config
= pci1710_counter_insn_config
;
868 /* counters 1 and 2 are used internally for the pacer */
869 comedi_8254_set_busy(dev
->pacer
, 1, true);
870 comedi_8254_set_busy(dev
->pacer
, 2, true);
873 /* max_samples is half the FIFO size (2 bytes/sample) */
874 devpriv
->max_samples
= (board
->is_pci1711
) ? 512 : 2048;
879 static struct comedi_driver adv_pci1710_driver
= {
880 .driver_name
= "adv_pci1710",
881 .module
= THIS_MODULE
,
882 .auto_attach
= pci1710_auto_attach
,
883 .detach
= comedi_pci_detach
,
886 static int adv_pci1710_pci_probe(struct pci_dev
*dev
,
887 const struct pci_device_id
*id
)
889 return comedi_pci_auto_config(dev
, &adv_pci1710_driver
,
893 static const struct pci_device_id adv_pci1710_pci_table
[] = {
895 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
896 PCI_VENDOR_ID_PLX
, PCI_DEVICE_ID_PLX_9050
),
897 .driver_data
= BOARD_PCI1710
,
899 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
900 PCI_VENDOR_ID_ADVANTECH
, 0x0000),
901 .driver_data
= BOARD_PCI1710
,
903 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
904 PCI_VENDOR_ID_ADVANTECH
, 0xb100),
905 .driver_data
= BOARD_PCI1710
,
907 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
908 PCI_VENDOR_ID_ADVANTECH
, 0xb200),
909 .driver_data
= BOARD_PCI1710
,
911 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
912 PCI_VENDOR_ID_ADVANTECH
, 0xc100),
913 .driver_data
= BOARD_PCI1710
,
915 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
916 PCI_VENDOR_ID_ADVANTECH
, 0xc200),
917 .driver_data
= BOARD_PCI1710
,
919 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710, 0x1000, 0xd100),
920 .driver_data
= BOARD_PCI1710
,
922 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
923 PCI_VENDOR_ID_ADVANTECH
, 0x0002),
924 .driver_data
= BOARD_PCI1710HG
,
926 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
927 PCI_VENDOR_ID_ADVANTECH
, 0xb102),
928 .driver_data
= BOARD_PCI1710HG
,
930 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
931 PCI_VENDOR_ID_ADVANTECH
, 0xb202),
932 .driver_data
= BOARD_PCI1710HG
,
934 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
935 PCI_VENDOR_ID_ADVANTECH
, 0xc102),
936 .driver_data
= BOARD_PCI1710HG
,
938 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710,
939 PCI_VENDOR_ID_ADVANTECH
, 0xc202),
940 .driver_data
= BOARD_PCI1710HG
,
942 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH
, 0x1710, 0x1000, 0xd102),
943 .driver_data
= BOARD_PCI1710HG
,
945 { PCI_VDEVICE(ADVANTECH
, 0x1711), BOARD_PCI1711
},
946 { PCI_VDEVICE(ADVANTECH
, 0x1713), BOARD_PCI1713
},
947 { PCI_VDEVICE(ADVANTECH
, 0x1731), BOARD_PCI1731
},
950 MODULE_DEVICE_TABLE(pci
, adv_pci1710_pci_table
);
952 static struct pci_driver adv_pci1710_pci_driver
= {
953 .name
= "adv_pci1710",
954 .id_table
= adv_pci1710_pci_table
,
955 .probe
= adv_pci1710_pci_probe
,
956 .remove
= comedi_pci_auto_unconfig
,
958 module_comedi_pci_driver(adv_pci1710_driver
, adv_pci1710_pci_driver
);
960 MODULE_AUTHOR("Comedi https://www.comedi.org");
961 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
962 MODULE_LICENSE("GPL");