1// SPDX-License-Identifier: GPL-2.0
2/*
3 * adv_pci1710.c
4 * Comedi driver for Advantech PCI-1710 series boards
5 * Author: Michal Dobes <dobes@tesnet.cz>
6 *
7 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
8 * for testing and information.
9 */
10
11/*
12 * Driver: adv_pci1710
13 * Description: Comedi driver for Advantech PCI-1710 series boards
14 * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
15 * PCI-1713, PCI-1731
16 * Author: Michal Dobes <dobes@tesnet.cz>
17 * Updated: Fri, 29 Oct 2015 17:19:35 -0700
18 * Status: works
19 *
20 * Configuration options: not applicable, uses PCI auto config
21 *
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.
25 *
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
28 * PCI driver.
29 */
30
31#include <linux/module.h>
32#include <linux/interrupt.h>
33#include <linux/comedi/comedi_pci.h>
34#include <linux/comedi/comedi_8254.h>
35
36#include "amcc_s5933.h"
37
38/*
39 * PCI BAR2 Register map (dev->iobase)
40 */
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 */
73
74static const struct comedi_lrange pci1710_ai_range = {
75 9, {
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) */
85 }
86};
87
88static const struct comedi_lrange pci1710hg_ai_range = {
89 12, {
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) */
102 }
103};
104
105static const struct comedi_lrange pci1711_ai_range = {
106 5, {
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) */
112 }
113};
114
115static const struct comedi_lrange pci171x_ao_range = {
116 3, {
117 UNI_RANGE(5), /* internal -5V ref */
118 UNI_RANGE(10), /* internal -10V ref */
119 RANGE_ext(0, 1) /* external -Vref (+/-10V max) */
120 }
121};
122
123enum pci1710_boardid {
124 BOARD_PCI1710,
125 BOARD_PCI1710HG,
126 BOARD_PCI1711,
127 BOARD_PCI1713,
128 BOARD_PCI1731,
129};
130
131struct boardtype {
132 const char *name;
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;
137};
138
139static const struct boardtype boardtypes[] = {
140 [BOARD_PCI1710] = {
141 .name = "pci1710",
142 .ai_range = &pci1710_ai_range,
143 .has_ao = 1,
144 },
145 [BOARD_PCI1710HG] = {
146 .name = "pci1710hg",
147 .ai_range = &pci1710hg_ai_range,
148 .has_ao = 1,
149 },
150 [BOARD_PCI1711] = {
151 .name = "pci1711",
152 .ai_range = &pci1711_ai_range,
153 .is_pci1711 = 1,
154 .has_ao = 1,
155 },
156 [BOARD_PCI1713] = {
157 .name = "pci1713",
158 .ai_range = &pci1710_ai_range,
159 .is_pci1713 = 1,
160 },
161 [BOARD_PCI1731] = {
162 .name = "pci1731",
163 .ai_range = &pci1711_ai_range,
164 .is_pci1711 = 1,
165 },
166};
167
168struct 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 */
173 unsigned char ai_et;
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 */
178};
179
180static int pci1710_ai_check_chanlist(struct comedi_device *dev,
181 struct comedi_subdevice *s,
182 struct comedi_cmd *cmd)
183{
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];
189 unsigned int seglen;
190 int i;
191
192 if (cmd->chanlist_len == 1) {
193 devpriv->saved_seglen = cmd->chanlist_len;
194 return 0;
195 }
196
197 /* first channel is always ok */
198 chansegment[0] = cmd->chanlist[0];
199
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]);
203
204 if (cmd->chanlist[0] == cmd->chanlist[i])
205 break; /* we detected a loop, stop */
206
207 if (aref == AREF_DIFF && (chan & 1)) {
208 dev_err(dev->class_dev,
209 "Odd channel cannot be differential input!\n");
210 return -EINVAL;
211 }
212
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);
219 return -EINVAL;
220 }
221
222 /* next correct channel in list */
223 chansegment[i] = cmd->chanlist[i];
224 last_aref = aref;
225 }
226 seglen = i;
227
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]));
238 return -EINVAL;
239 }
240 }
241 devpriv->saved_seglen = seglen;
242
243 return 0;
244}
245
246static void pci1710_ai_setup_chanlist(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 unsigned int *chanlist,
249 unsigned int n_chan,
250 unsigned int seglen)
251{
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]);
255 unsigned int i;
256
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;
262
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;
268 }
269 rangeval |= PCI171X_RANGE_GAIN(range);
270
271 /* select channel and set range */
272 outw(PCI171X_MUX_CHAN(chan), port: dev->iobase + PCI171X_MUX_REG);
273 outw(value: rangeval, port: dev->iobase + PCI171X_RANGE_REG);
274
275 devpriv->act_chanlist[i] = chan;
276 }
277 for ( ; i < n_chan; i++) /* store remainder of channel list */
278 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
279
280 /* select channel interval to scan */
281 devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) |
282 PCI171X_MUX_CHANH(last_chan);
283 outw(value: devpriv->mux_scan, port: dev->iobase + PCI171X_MUX_REG);
284}
285
286static int pci1710_ai_eoc(struct comedi_device *dev,
287 struct comedi_subdevice *s,
288 struct comedi_insn *insn,
289 unsigned long context)
290{
291 unsigned int status;
292
293 status = inw(port: dev->iobase + PCI171X_STATUS_REG);
294 if ((status & PCI171X_STATUS_FE) == 0)
295 return 0;
296 return -EBUSY;
297}
298
299static int pci1710_ai_read_sample(struct comedi_device *dev,
300 struct comedi_subdevice *s,
301 unsigned int cur_chan,
302 unsigned short *val)
303{
304 const struct boardtype *board = dev->board_ptr;
305 struct pci1710_private *devpriv = dev->private;
306 unsigned short sample;
307 unsigned int chan;
308
309 sample = inw(port: dev->iobase + PCI171X_AD_DATA_REG);
310 if (!board->is_pci1713) {
311 /*
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.
315 */
316 chan = sample >> 12;
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]);
321 return -ENODATA;
322 }
323 }
324 *val = sample & s->maxdata;
325 return 0;
326}
327
328static int pci1710_ai_insn_read(struct comedi_device *dev,
329 struct comedi_subdevice *s,
330 struct comedi_insn *insn,
331 unsigned int *data)
332{
333 struct pci1710_private *devpriv = dev->private;
334 int ret = 0;
335 int i;
336
337 /* enable software trigger */
338 devpriv->ctrl |= PCI171X_CTRL_SW;
339 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
340
341 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
342 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
343
344 pci1710_ai_setup_chanlist(dev, s, chanlist: &insn->chanspec, n_chan: 1, seglen: 1);
345
346 for (i = 0; i < insn->n; i++) {
347 unsigned short val;
348
349 /* start conversion */
350 outw(value: 0, port: dev->iobase + PCI171X_SOFTTRG_REG);
351
352 ret = comedi_timeout(dev, s, insn, cb: pci1710_ai_eoc, context: 0);
353 if (ret)
354 break;
355
356 ret = pci1710_ai_read_sample(dev, s, cur_chan: 0, val: &val);
357 if (ret)
358 break;
359
360 data[i] = val;
361 }
362
363 /* disable software trigger */
364 devpriv->ctrl &= ~PCI171X_CTRL_SW;
365 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
366
367 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
368 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
369
370 return ret ? ret : insn->n;
371}
372
373static int pci1710_ai_cancel(struct comedi_device *dev,
374 struct comedi_subdevice *s)
375{
376 struct pci1710_private *devpriv = dev->private;
377
378 /* disable A/D triggers and interrupt sources */
379 devpriv->ctrl &= PCI171X_CTRL_CNT0; /* preserve counter 0 clk src */
380 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
381
382 /* disable pacer */
383 comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: false);
384
385 /* clear A/D FIFO and any pending interrutps */
386 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
387 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
388
389 return 0;
390}
391
392static void pci1710_handle_every_sample(struct comedi_device *dev,
393 struct comedi_subdevice *s)
394{
395 struct comedi_cmd *cmd = &s->async->cmd;
396 unsigned int status;
397 unsigned short val;
398 int ret;
399
400 status = inw(port: 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;
404 return;
405 }
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;
410 return;
411 }
412
413 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
414
415 for (; !(inw(port: dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) {
416 ret = pci1710_ai_read_sample(dev, s, cur_chan: s->async->cur_chan, val: &val);
417 if (ret) {
418 s->async->events |= COMEDI_CB_ERROR;
419 break;
420 }
421
422 comedi_buf_write_samples(s, data: &val, nsamples: 1);
423
424 if (cmd->stop_src == TRIG_COUNT &&
425 s->async->scans_done >= cmd->stop_arg) {
426 s->async->events |= COMEDI_CB_EOA;
427 break;
428 }
429 }
430
431 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
432}
433
434static void pci1710_handle_fifo(struct comedi_device *dev,
435 struct comedi_subdevice *s)
436{
437 struct pci1710_private *devpriv = dev->private;
438 struct comedi_async *async = s->async;
439 struct comedi_cmd *cmd = &async->cmd;
440 unsigned int status;
441 int i;
442
443 status = inw(port: 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;
447 return;
448 }
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;
453 return;
454 }
455
456 for (i = 0; i < devpriv->max_samples; i++) {
457 unsigned short val;
458 int ret;
459
460 ret = pci1710_ai_read_sample(dev, s, cur_chan: s->async->cur_chan, val: &val);
461 if (ret) {
462 s->async->events |= COMEDI_CB_ERROR;
463 break;
464 }
465
466 if (!comedi_buf_write_samples(s, data: &val, nsamples: 1))
467 break;
468
469 if (cmd->stop_src == TRIG_COUNT &&
470 async->scans_done >= cmd->stop_arg) {
471 async->events |= COMEDI_CB_EOA;
472 break;
473 }
474 }
475
476 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
477}
478
479static irqreturn_t pci1710_irq_handler(int irq, void *d)
480{
481 struct comedi_device *dev = d;
482 struct pci1710_private *devpriv = dev->private;
483 struct comedi_subdevice *s;
484 struct comedi_cmd *cmd;
485
486 if (!dev->attached) /* is device attached? */
487 return IRQ_NONE; /* no, exit */
488
489 s = dev->read_subdev;
490 cmd = &s->async->cmd;
491
492 /* is this interrupt from our board? */
493 if (!(inw(port: dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ))
494 return IRQ_NONE; /* no, exit */
495
496 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
497 devpriv->ai_et = 0;
498 devpriv->ctrl &= PCI171X_CTRL_CNT0;
499 devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */
500 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
501 devpriv->ctrl = devpriv->ctrl_ext;
502 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
503 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
504 /* no sample on this interrupt; reset the channel interval */
505 outw(value: devpriv->mux_scan, port: dev->iobase + PCI171X_MUX_REG);
506 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
507 comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: true);
508 return IRQ_HANDLED;
509 }
510
511 if (cmd->flags & CMDF_WAKE_EOS)
512 pci1710_handle_every_sample(dev, s);
513 else
514 pci1710_handle_fifo(dev, s);
515
516 comedi_handle_events(dev, s);
517
518 return IRQ_HANDLED;
519}
520
521static int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
522{
523 struct pci1710_private *devpriv = dev->private;
524 struct comedi_cmd *cmd = &s->async->cmd;
525
526 pci1710_ai_setup_chanlist(dev, s, chanlist: cmd->chanlist, n_chan: cmd->chanlist_len,
527 seglen: devpriv->saved_seglen);
528
529 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
530 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
531
532 devpriv->ctrl &= PCI171X_CTRL_CNT0;
533 if ((cmd->flags & CMDF_WAKE_EOS) == 0)
534 devpriv->ctrl |= PCI171X_CTRL_ONEFH;
535
536 if (cmd->convert_src == TRIG_TIMER) {
537 comedi_8254_update_divisors(i8254: dev->pacer);
538
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 |
543 PCI171X_CTRL_ONEFH |
544 PCI171X_CTRL_GATE);
545 devpriv->ctrl |= PCI171X_CTRL_EXT;
546 devpriv->ai_et = 1;
547 } else { /* TRIG_NOW */
548 devpriv->ai_et = 0;
549 }
550 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
551
552 if (cmd->start_src == TRIG_NOW)
553 comedi_8254_pacer_enable(i8254: dev->pacer, counter1: 1, counter2: 2, enable: true);
554 } else { /* TRIG_EXT */
555 devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN;
556 outw(value: devpriv->ctrl, port: dev->iobase + PCI171X_CTRL_REG);
557 }
558
559 return 0;
560}
561
562static int pci1710_ai_cmdtest(struct comedi_device *dev,
563 struct comedi_subdevice *s,
564 struct comedi_cmd *cmd)
565{
566 int err = 0;
567
568 /* Step 1 : check if triggers are trivially valid */
569
570 err |= comedi_check_trigger_src(src: &cmd->start_src, TRIG_NOW | TRIG_EXT);
571 err |= comedi_check_trigger_src(src: &cmd->scan_begin_src, TRIG_FOLLOW);
572 err |= comedi_check_trigger_src(src: &cmd->convert_src,
573 TRIG_TIMER | TRIG_EXT);
574 err |= comedi_check_trigger_src(src: &cmd->scan_end_src, TRIG_COUNT);
575 err |= comedi_check_trigger_src(src: &cmd->stop_src, TRIG_COUNT | TRIG_NONE);
576
577 if (err)
578 return 1;
579
580 /* step 2a: make sure trigger sources are unique */
581
582 err |= comedi_check_trigger_is_unique(src: cmd->start_src);
583 err |= comedi_check_trigger_is_unique(src: cmd->convert_src);
584 err |= comedi_check_trigger_is_unique(src: cmd->stop_src);
585
586 /* step 2b: and mutually compatible */
587
588 if (err)
589 return 2;
590
591 /* Step 3: check if arguments are trivially valid */
592
593 err |= comedi_check_trigger_arg_is(arg: &cmd->start_arg, val: 0);
594 err |= comedi_check_trigger_arg_is(arg: &cmd->scan_begin_arg, val: 0);
595
596 if (cmd->convert_src == TRIG_TIMER)
597 err |= comedi_check_trigger_arg_min(arg: &cmd->convert_arg, val: 10000);
598 else /* TRIG_FOLLOW */
599 err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: 0);
600
601 err |= comedi_check_trigger_arg_is(arg: &cmd->scan_end_arg,
602 val: cmd->chanlist_len);
603
604 if (cmd->stop_src == TRIG_COUNT)
605 err |= comedi_check_trigger_arg_min(arg: &cmd->stop_arg, val: 1);
606 else /* TRIG_NONE */
607 err |= comedi_check_trigger_arg_is(arg: &cmd->stop_arg, val: 0);
608
609 if (err)
610 return 3;
611
612 /* step 4: fix up any arguments */
613
614 if (cmd->convert_src == TRIG_TIMER) {
615 unsigned int arg = cmd->convert_arg;
616
617 comedi_8254_cascade_ns_to_timer(i8254: dev->pacer, nanosec: &arg, flags: cmd->flags);
618 err |= comedi_check_trigger_arg_is(arg: &cmd->convert_arg, val: arg);
619 }
620
621 if (err)
622 return 4;
623
624 /* Step 5: check channel list */
625
626 err |= pci1710_ai_check_chanlist(dev, s, cmd);
627
628 if (err)
629 return 5;
630
631 return 0;
632}
633
634static int pci1710_ao_insn_write(struct comedi_device *dev,
635 struct comedi_subdevice *s,
636 struct comedi_insn *insn,
637 unsigned int *data)
638{
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];
643 int i;
644
645 devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan);
646 devpriv->da_ranges |= PCI171X_DAREF(chan, range);
647 outw(value: devpriv->da_ranges, port: dev->iobase + PCI171X_DAREF_REG);
648
649 for (i = 0; i < insn->n; i++) {
650 val = data[i];
651 outw(value: val, port: dev->iobase + PCI171X_DA_REG(chan));
652 }
653
654 s->readback[chan] = val;
655
656 return insn->n;
657}
658
659static int pci1710_di_insn_bits(struct comedi_device *dev,
660 struct comedi_subdevice *s,
661 struct comedi_insn *insn,
662 unsigned int *data)
663{
664 data[1] = inw(port: dev->iobase + PCI171X_DI_REG);
665
666 return insn->n;
667}
668
669static int pci1710_do_insn_bits(struct comedi_device *dev,
670 struct comedi_subdevice *s,
671 struct comedi_insn *insn,
672 unsigned int *data)
673{
674 if (comedi_dio_update_state(s, data))
675 outw(value: s->state, port: dev->iobase + PCI171X_DO_REG);
676
677 data[1] = s->state;
678
679 return insn->n;
680}
681
682static int pci1710_counter_insn_config(struct comedi_device *dev,
683 struct comedi_subdevice *s,
684 struct comedi_insn *insn,
685 unsigned int *data)
686{
687 struct pci1710_private *devpriv = dev->private;
688
689 switch (data[0]) {
690 case INSN_CONFIG_SET_CLOCK_SRC:
691 switch (data[1]) {
692 case 0: /* internal */
693 devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0;
694 break;
695 case 1: /* external */
696 devpriv->ctrl_ext |= PCI171X_CTRL_CNT0;
697 break;
698 default:
699 return -EINVAL;
700 }
701 outw(value: devpriv->ctrl_ext, port: dev->iobase + PCI171X_CTRL_REG);
702 break;
703 case INSN_CONFIG_GET_CLOCK_SRC:
704 if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) {
705 data[1] = 1;
706 data[2] = 0;
707 } else {
708 data[1] = 0;
709 data[2] = I8254_OSC_BASE_1MHZ;
710 }
711 break;
712 default:
713 return -EINVAL;
714 }
715
716 return insn->n;
717}
718
719static void pci1710_reset(struct comedi_device *dev)
720{
721 const struct boardtype *board = dev->board_ptr;
722
723 /*
724 * Disable A/D triggers and interrupt sources, set counter 0
725 * to use internal 1 MHz clock.
726 */
727 outw(value: 0, port: dev->iobase + PCI171X_CTRL_REG);
728
729 /* clear A/D FIFO and any pending interrutps */
730 outb(value: 0, port: dev->iobase + PCI171X_CLRFIFO_REG);
731 outb(value: 0, port: dev->iobase + PCI171X_CLRINT_REG);
732
733 if (board->has_ao) {
734 /* set DACs to 0..5V and outputs to 0V */
735 outb(value: 0, port: dev->iobase + PCI171X_DAREF_REG);
736 outw(value: 0, port: dev->iobase + PCI171X_DA_REG(0));
737 outw(value: 0, port: dev->iobase + PCI171X_DA_REG(1));
738 }
739
740 /* set digital outputs to 0 */
741 outw(value: 0, port: dev->iobase + PCI171X_DO_REG);
742}
743
744static int pci1710_auto_attach(struct comedi_device *dev,
745 unsigned long context)
746{
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;
752 int i;
753
754 if (context < ARRAY_SIZE(boardtypes))
755 board = &boardtypes[context];
756 if (!board)
757 return -ENODEV;
758 dev->board_ptr = board;
759 dev->board_name = board->name;
760
761 devpriv = comedi_alloc_devpriv(dev, size: sizeof(*devpriv));
762 if (!devpriv)
763 return -ENOMEM;
764
765 ret = comedi_pci_enable(dev);
766 if (ret)
767 return ret;
768 dev->iobase = pci_resource_start(pcidev, 2);
769
770 dev->pacer = comedi_8254_io_alloc(iobase: dev->iobase + PCI171X_TIMER_BASE,
771 I8254_OSC_BASE_10MHZ, I8254_IO16, regshift: 0);
772 if (IS_ERR(ptr: dev->pacer))
773 return PTR_ERR(ptr: dev->pacer);
774
775 n_subdevices = 1; /* all boards have analog inputs */
776 if (board->has_ao)
777 n_subdevices++;
778 if (!board->is_pci1713) {
779 /*
780 * All other boards have digital inputs and outputs as
781 * well as a user counter.
782 */
783 n_subdevices += 3;
784 }
785
786 ret = comedi_alloc_subdevices(dev, num_subdevices: n_subdevices);
787 if (ret)
788 return ret;
789
790 pci1710_reset(dev);
791
792 if (pcidev->irq) {
793 ret = request_irq(irq: pcidev->irq, handler: pci1710_irq_handler,
794 IRQF_SHARED, name: dev->board_name, dev);
795 if (ret == 0)
796 dev->irq = pcidev->irq;
797 }
798
799 subdev = 0;
800
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;
808 s->maxdata = 0x0fff;
809 s->range_table = board->ai_range;
810 s->insn_read = pci1710_ai_insn_read;
811 if (dev->irq) {
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;
818 }
819
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, range: i)) {
823 devpriv->unipolar_gain = i;
824 break;
825 }
826 }
827
828 if (board->has_ao) {
829 /* Analog Output subdevice */
830 s = &dev->subdevices[subdev++];
831 s->type = COMEDI_SUBD_AO;
832 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
833 s->n_chan = 2;
834 s->maxdata = 0x0fff;
835 s->range_table = &pci171x_ao_range;
836 s->insn_write = pci1710_ao_insn_write;
837
838 ret = comedi_alloc_subdev_readback(s);
839 if (ret)
840 return ret;
841 }
842
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;
848 s->n_chan = 16;
849 s->maxdata = 1;
850 s->range_table = &range_digital;
851 s->insn_bits = pci1710_di_insn_bits;
852
853 /* Digital Output subdevice */
854 s = &dev->subdevices[subdev++];
855 s->type = COMEDI_SUBD_DO;
856 s->subdev_flags = SDF_WRITABLE;
857 s->n_chan = 16;
858 s->maxdata = 1;
859 s->range_table = &range_digital;
860 s->insn_bits = pci1710_do_insn_bits;
861
862 /* Counter subdevice (8254) */
863 s = &dev->subdevices[subdev++];
864 comedi_8254_subdevice_init(s, i8254: dev->pacer);
865
866 dev->pacer->insn_config = pci1710_counter_insn_config;
867
868 /* counters 1 and 2 are used internally for the pacer */
869 comedi_8254_set_busy(i8254: dev->pacer, counter: 1, busy: true);
870 comedi_8254_set_busy(i8254: dev->pacer, counter: 2, busy: true);
871 }
872
873 /* max_samples is half the FIFO size (2 bytes/sample) */
874 devpriv->max_samples = (board->is_pci1711) ? 512 : 2048;
875
876 return 0;
877}
878
879static 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,
884};
885
886static int adv_pci1710_pci_probe(struct pci_dev *dev,
887 const struct pci_device_id *id)
888{
889 return comedi_pci_auto_config(pcidev: dev, driver: &adv_pci1710_driver,
890 context: id->driver_data);
891}
892
893static const struct pci_device_id adv_pci1710_pci_table[] = {
894 {
895 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
896 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
897 .driver_data = BOARD_PCI1710,
898 }, {
899 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
900 PCI_VENDOR_ID_ADVANTECH, 0x0000),
901 .driver_data = BOARD_PCI1710,
902 }, {
903 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
904 PCI_VENDOR_ID_ADVANTECH, 0xb100),
905 .driver_data = BOARD_PCI1710,
906 }, {
907 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
908 PCI_VENDOR_ID_ADVANTECH, 0xb200),
909 .driver_data = BOARD_PCI1710,
910 }, {
911 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
912 PCI_VENDOR_ID_ADVANTECH, 0xc100),
913 .driver_data = BOARD_PCI1710,
914 }, {
915 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
916 PCI_VENDOR_ID_ADVANTECH, 0xc200),
917 .driver_data = BOARD_PCI1710,
918 }, {
919 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
920 .driver_data = BOARD_PCI1710,
921 }, {
922 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
923 PCI_VENDOR_ID_ADVANTECH, 0x0002),
924 .driver_data = BOARD_PCI1710HG,
925 }, {
926 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
927 PCI_VENDOR_ID_ADVANTECH, 0xb102),
928 .driver_data = BOARD_PCI1710HG,
929 }, {
930 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
931 PCI_VENDOR_ID_ADVANTECH, 0xb202),
932 .driver_data = BOARD_PCI1710HG,
933 }, {
934 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
935 PCI_VENDOR_ID_ADVANTECH, 0xc102),
936 .driver_data = BOARD_PCI1710HG,
937 }, {
938 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
939 PCI_VENDOR_ID_ADVANTECH, 0xc202),
940 .driver_data = BOARD_PCI1710HG,
941 }, {
942 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
943 .driver_data = BOARD_PCI1710HG,
944 },
945 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
946 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
947 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
948 { 0 }
949};
950MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
951
952static 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,
957};
958module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
959
960MODULE_AUTHOR("Comedi https://www.comedi.org");
961MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
962MODULE_LICENSE("GPL");
963

source code of linux/drivers/comedi/drivers/adv_pci1710.c