1 /*
2 * comedi/drivers/adv_pci1710.c
3 *
4 * Author: Michal Dobes <dobes@tesnet.cz>
5 *
6 * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn>
7 * for testing and information.
8 *
9 * hardware driver for Advantech cards:
10 * card: PCI-1710, PCI-1710HG, PCI-1711, PCI-1713, PCI-1720, PCI-1731
11 * driver: pci1710, pci1710hg, pci1711, pci1713, pci1720, pci1731
12 *
13 * Options:
14 * [0] - PCI bus number - if bus number and slot number are 0,
15 * then driver search for first unused card
16 * [1] - PCI slot number
17 *
18 */
19 /*
20 Driver: adv_pci1710
21 Description: Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713,
22 Advantech PCI-1720, PCI-1731
23 Author: Michal Dobes <dobes@tesnet.cz>
24 Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG (pci1710hg),
25 PCI-1711 (adv_pci1710), PCI-1713, PCI-1720,
26 PCI-1731
27 Status: works
28
29 This driver supports AI, AO, DI and DO subdevices.
30 AI subdevice supports cmd and insn interface,
31 other subdevices support only insn interface.
32
33 The PCI-1710 and PCI-1710HG have the same PCI device ID, so the
34 driver cannot distinguish between them, as would be normal for a
35 PCI driver.
36
37 Configuration options:
38 [0] - PCI bus of device (optional)
39 [1] - PCI slot of device (optional)
40 If bus/slot is not specified, the first available PCI
41 device will be used.
42 */
43
44 #include <linux/module.h>
45 #include <linux/interrupt.h>
46
47 #include "../comedi_pci.h"
48
49 #include "comedi_8254.h"
50 #include "amcc_s5933.h"
51
52 #define PCI171x_AD_DATA 0 /* R: A/D data */
53 #define PCI171x_SOFTTRG 0 /* W: soft trigger for A/D */
54 #define PCI171x_RANGE 2 /* W: A/D gain/range register */
55 #define PCI171x_MUX 4 /* W: A/D multiplexor control */
56 #define PCI171x_STATUS 6 /* R: status register */
57 #define PCI171x_CONTROL 6 /* W: control register */
58 #define PCI171x_CLRINT 8 /* W: clear interrupts request */
59 #define PCI171x_CLRFIFO 9 /* W: clear FIFO */
60 #define PCI171x_DA1 10 /* W: D/A register */
61 #define PCI171x_DA2 12 /* W: D/A register */
62 #define PCI171x_DAREF 14 /* W: D/A reference control */
63 #define PCI171x_DI 16 /* R: digi inputs */
64 #define PCI171x_DO 16 /* R: digi inputs */
65
66 #define PCI171X_TIMER_BASE 0x18
67
68 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
69 * reg) */
70 #define Status_FE 0x0100 /* 1=FIFO is empty */
71 #define Status_FH 0x0200 /* 1=FIFO is half full */
72 #define Status_FF 0x0400 /* 1=FIFO is full, fatal error */
73 #define Status_IRQ 0x0800 /* 1=IRQ occurred */
74 /* bits from control register (PCI171x_CONTROL) */
75 #define Control_CNT0 0x0040 /* 1=CNT0 have external source,
76 * 0=have internal 100kHz source */
77 #define Control_ONEFH 0x0020 /* 1=IRQ on FIFO is half full, 0=every sample */
78 #define Control_IRQEN 0x0010 /* 1=enable IRQ */
79 #define Control_GATE 0x0008 /* 1=enable external trigger GATE (8254?) */
80 #define Control_EXT 0x0004 /* 1=external trigger source */
81 #define Control_PACER 0x0002 /* 1=enable internal 8254 trigger source */
82 #define Control_SW 0x0001 /* 1=enable software trigger source */
83
84 #define PCI1720_DA0 0 /* W: D/A register 0 */
85 #define PCI1720_DA1 2 /* W: D/A register 1 */
86 #define PCI1720_DA2 4 /* W: D/A register 2 */
87 #define PCI1720_DA3 6 /* W: D/A register 3 */
88 #define PCI1720_RANGE 8 /* R/W: D/A range register */
89 #define PCI1720_SYNCOUT 9 /* W: D/A synchronized output register */
90 #define PCI1720_SYNCONT 15 /* R/W: D/A synchronized control */
91
92 /* D/A synchronized control (PCI1720_SYNCONT) */
93 #define Syncont_SC0 1 /* set synchronous output mode */
94
95 static const struct comedi_lrange range_pci1710_3 = {
96 9, {
97 BIP_RANGE(5),
98 BIP_RANGE(2.5),
99 BIP_RANGE(1.25),
100 BIP_RANGE(0.625),
101 BIP_RANGE(10),
102 UNI_RANGE(10),
103 UNI_RANGE(5),
104 UNI_RANGE(2.5),
105 UNI_RANGE(1.25)
106 }
107 };
108
109 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
110 0x10, 0x11, 0x12, 0x13 };
111
112 static const struct comedi_lrange range_pci1710hg = {
113 12, {
114 BIP_RANGE(5),
115 BIP_RANGE(0.5),
116 BIP_RANGE(0.05),
117 BIP_RANGE(0.005),
118 BIP_RANGE(10),
119 BIP_RANGE(1),
120 BIP_RANGE(0.1),
121 BIP_RANGE(0.01),
122 UNI_RANGE(10),
123 UNI_RANGE(1),
124 UNI_RANGE(0.1),
125 UNI_RANGE(0.01)
126 }
127 };
128
129 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
130 0x05, 0x06, 0x07, 0x10, 0x11,
131 0x12, 0x13 };
132
133 static const struct comedi_lrange range_pci17x1 = {
134 5, {
135 BIP_RANGE(10),
136 BIP_RANGE(5),
137 BIP_RANGE(2.5),
138 BIP_RANGE(1.25),
139 BIP_RANGE(0.625)
140 }
141 };
142
143 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
144
145 static const struct comedi_lrange pci1720_ao_range = {
146 4, {
147 UNI_RANGE(5),
148 UNI_RANGE(10),
149 BIP_RANGE(5),
150 BIP_RANGE(10)
151 }
152 };
153
154 static const struct comedi_lrange pci171x_ao_range = {
155 2, {
156 UNI_RANGE(5),
157 UNI_RANGE(10)
158 }
159 };
160
161 enum pci1710_boardid {
162 BOARD_PCI1710,
163 BOARD_PCI1710HG,
164 BOARD_PCI1711,
165 BOARD_PCI1713,
166 BOARD_PCI1720,
167 BOARD_PCI1731,
168 };
169
170 struct boardtype {
171 const char *name; /* board name */
172 int n_aichan; /* num of A/D chans */
173 const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
174 const char *rangecode_ai; /* range codes for programming */
175 unsigned int is_pci1713:1;
176 unsigned int is_pci1720:1;
177 unsigned int has_irq:1;
178 unsigned int has_large_fifo:1; /* 4K or 1K FIFO */
179 unsigned int has_diff_ai:1;
180 unsigned int has_ao:1;
181 unsigned int has_di_do:1;
182 unsigned int has_counter:1;
183 };
184
185 static const struct boardtype boardtypes[] = {
186 [BOARD_PCI1710] = {
187 .name = "pci1710",
188 .n_aichan = 16,
189 .rangelist_ai = &range_pci1710_3,
190 .rangecode_ai = range_codes_pci1710_3,
191 .has_irq = 1,
192 .has_large_fifo = 1,
193 .has_diff_ai = 1,
194 .has_ao = 1,
195 .has_di_do = 1,
196 .has_counter = 1,
197 },
198 [BOARD_PCI1710HG] = {
199 .name = "pci1710hg",
200 .n_aichan = 16,
201 .rangelist_ai = &range_pci1710hg,
202 .rangecode_ai = range_codes_pci1710hg,
203 .has_irq = 1,
204 .has_large_fifo = 1,
205 .has_diff_ai = 1,
206 .has_ao = 1,
207 .has_di_do = 1,
208 .has_counter = 1,
209 },
210 [BOARD_PCI1711] = {
211 .name = "pci1711",
212 .n_aichan = 16,
213 .rangelist_ai = &range_pci17x1,
214 .rangecode_ai = range_codes_pci17x1,
215 .has_irq = 1,
216 .has_ao = 1,
217 .has_di_do = 1,
218 .has_counter = 1,
219 },
220 [BOARD_PCI1713] = {
221 .name = "pci1713",
222 .n_aichan = 32,
223 .rangelist_ai = &range_pci1710_3,
224 .rangecode_ai = range_codes_pci1710_3,
225 .is_pci1713 = 1,
226 .has_irq = 1,
227 .has_large_fifo = 1,
228 .has_diff_ai = 1,
229 },
230 [BOARD_PCI1720] = {
231 .name = "pci1720",
232 .is_pci1720 = 1,
233 .has_ao = 1,
234 },
235 [BOARD_PCI1731] = {
236 .name = "pci1731",
237 .n_aichan = 16,
238 .rangelist_ai = &range_pci17x1,
239 .rangecode_ai = range_codes_pci17x1,
240 .has_irq = 1,
241 .has_di_do = 1,
242 },
243 };
244
245 struct pci1710_private {
246 unsigned int max_samples;
247 unsigned int CntrlReg; /* Control register */
248 unsigned char ai_et;
249 unsigned int ai_et_CntrlReg;
250 unsigned int ai_et_MuxVal;
251 unsigned int act_chanlist[32]; /* list of scanned channel */
252 unsigned char saved_seglen; /* len of the non-repeating chanlist */
253 unsigned char da_ranges; /* copy of D/A outpit range register */
254 };
255
pci171x_ai_check_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)256 static int pci171x_ai_check_chanlist(struct comedi_device *dev,
257 struct comedi_subdevice *s,
258 struct comedi_cmd *cmd)
259 {
260 struct pci1710_private *devpriv = dev->private;
261 unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
262 unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
263 unsigned int next_chan = (chan0 + 1) % s->n_chan;
264 unsigned int chansegment[32];
265 unsigned int seglen;
266 int i;
267
268 if (cmd->chanlist_len == 1) {
269 devpriv->saved_seglen = cmd->chanlist_len;
270 return 0;
271 }
272
273 /* first channel is always ok */
274 chansegment[0] = cmd->chanlist[0];
275
276 for (i = 1; i < cmd->chanlist_len; i++) {
277 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
278 unsigned int aref = CR_AREF(cmd->chanlist[i]);
279
280 if (cmd->chanlist[0] == cmd->chanlist[i])
281 break; /* we detected a loop, stop */
282
283 if (aref == AREF_DIFF && (chan & 1)) {
284 dev_err(dev->class_dev,
285 "Odd channel cannot be differential input!\n");
286 return -EINVAL;
287 }
288
289 if (last_aref == AREF_DIFF)
290 next_chan = (next_chan + 1) % s->n_chan;
291 if (chan != next_chan) {
292 dev_err(dev->class_dev,
293 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
294 i, chan, next_chan, chan0);
295 return -EINVAL;
296 }
297
298 /* next correct channel in list */
299 chansegment[i] = cmd->chanlist[i];
300 last_aref = aref;
301 }
302 seglen = i;
303
304 for (i = 0; i < cmd->chanlist_len; i++) {
305 if (cmd->chanlist[i] != chansegment[i % seglen]) {
306 dev_err(dev->class_dev,
307 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
308 i, CR_CHAN(chansegment[i]),
309 CR_RANGE(chansegment[i]),
310 CR_AREF(chansegment[i]),
311 CR_CHAN(cmd->chanlist[i % seglen]),
312 CR_RANGE(cmd->chanlist[i % seglen]),
313 CR_AREF(chansegment[i % seglen]));
314 return -EINVAL;
315 }
316 }
317 devpriv->saved_seglen = seglen;
318
319 return 0;
320 }
321
pci171x_ai_setup_chanlist(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int * chanlist,unsigned int n_chan,unsigned int seglen)322 static void pci171x_ai_setup_chanlist(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 unsigned int *chanlist,
325 unsigned int n_chan,
326 unsigned int seglen)
327 {
328 const struct boardtype *board = dev->board_ptr;
329 struct pci1710_private *devpriv = dev->private;
330 unsigned int first_chan = CR_CHAN(chanlist[0]);
331 unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]);
332 unsigned int i;
333
334 for (i = 0; i < seglen; i++) { /* store range list to card */
335 unsigned int chan = CR_CHAN(chanlist[i]);
336 unsigned int range = CR_RANGE(chanlist[i]);
337 unsigned int aref = CR_AREF(chanlist[i]);
338 unsigned int rangeval;
339
340 rangeval = board->rangecode_ai[range];
341 if (aref == AREF_DIFF)
342 rangeval |= 0x0020;
343
344 /* select channel and set range */
345 outw(chan | (chan << 8), dev->iobase + PCI171x_MUX);
346 outw(rangeval, dev->iobase + PCI171x_RANGE);
347
348 devpriv->act_chanlist[i] = chan;
349 }
350 for ( ; i < n_chan; i++) /* store remainder of channel list */
351 devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]);
352
353 /* select channel interval to scan */
354 devpriv->ai_et_MuxVal = first_chan | (last_chan << 8);
355 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
356 }
357
pci171x_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)358 static int pci171x_ai_eoc(struct comedi_device *dev,
359 struct comedi_subdevice *s,
360 struct comedi_insn *insn,
361 unsigned long context)
362 {
363 unsigned int status;
364
365 status = inw(dev->iobase + PCI171x_STATUS);
366 if ((status & Status_FE) == 0)
367 return 0;
368 return -EBUSY;
369 }
370
pci171x_ai_read_sample(struct comedi_device * dev,struct comedi_subdevice * s,unsigned int cur_chan,unsigned int * val)371 static int pci171x_ai_read_sample(struct comedi_device *dev,
372 struct comedi_subdevice *s,
373 unsigned int cur_chan,
374 unsigned int *val)
375 {
376 const struct boardtype *board = dev->board_ptr;
377 struct pci1710_private *devpriv = dev->private;
378 unsigned int sample;
379 unsigned int chan;
380
381 sample = inw(dev->iobase + PCI171x_AD_DATA);
382 if (!board->is_pci1713) {
383 /*
384 * The upper 4 bits of the 16-bit sample are the channel number
385 * that the sample was acquired from. Verify that this channel
386 * number matches the expected channel number.
387 */
388 chan = sample >> 12;
389 if (chan != devpriv->act_chanlist[cur_chan]) {
390 dev_err(dev->class_dev,
391 "A/D data droput: received from channel %d, expected %d\n",
392 chan, devpriv->act_chanlist[cur_chan]);
393 return -ENODATA;
394 }
395 }
396 *val = sample & s->maxdata;
397 return 0;
398 }
399
pci171x_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)400 static int pci171x_ai_insn_read(struct comedi_device *dev,
401 struct comedi_subdevice *s,
402 struct comedi_insn *insn,
403 unsigned int *data)
404 {
405 struct pci1710_private *devpriv = dev->private;
406 int ret = 0;
407 int i;
408
409 devpriv->CntrlReg &= Control_CNT0;
410 devpriv->CntrlReg |= Control_SW; /* set software trigger */
411 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
412 outb(0, dev->iobase + PCI171x_CLRFIFO);
413 outb(0, dev->iobase + PCI171x_CLRINT);
414
415 pci171x_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1);
416
417 for (i = 0; i < insn->n; i++) {
418 unsigned int val;
419
420 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
421
422 ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
423 if (ret)
424 break;
425
426 ret = pci171x_ai_read_sample(dev, s, 0, &val);
427 if (ret)
428 break;
429
430 data[i] = val;
431 }
432
433 outb(0, dev->iobase + PCI171x_CLRFIFO);
434 outb(0, dev->iobase + PCI171x_CLRINT);
435
436 return ret ? ret : insn->n;
437 }
438
pci171x_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)439 static int pci171x_ao_insn_write(struct comedi_device *dev,
440 struct comedi_subdevice *s,
441 struct comedi_insn *insn,
442 unsigned int *data)
443 {
444 struct pci1710_private *devpriv = dev->private;
445 unsigned int chan = CR_CHAN(insn->chanspec);
446 unsigned int range = CR_RANGE(insn->chanspec);
447 unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
448 unsigned int val = s->readback[chan];
449 int i;
450
451 devpriv->da_ranges &= ~(1 << (chan << 1));
452 devpriv->da_ranges |= (range << (chan << 1));
453 outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
454
455 for (i = 0; i < insn->n; i++) {
456 val = data[i];
457 outw(val, dev->iobase + reg);
458 }
459
460 s->readback[chan] = val;
461
462 return insn->n;
463 }
464
pci171x_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)465 static int pci171x_di_insn_bits(struct comedi_device *dev,
466 struct comedi_subdevice *s,
467 struct comedi_insn *insn,
468 unsigned int *data)
469 {
470 data[1] = inw(dev->iobase + PCI171x_DI);
471
472 return insn->n;
473 }
474
pci171x_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)475 static int pci171x_do_insn_bits(struct comedi_device *dev,
476 struct comedi_subdevice *s,
477 struct comedi_insn *insn,
478 unsigned int *data)
479 {
480 if (comedi_dio_update_state(s, data))
481 outw(s->state, dev->iobase + PCI171x_DO);
482
483 data[1] = s->state;
484
485 return insn->n;
486 }
487
pci1720_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)488 static int pci1720_ao_insn_write(struct comedi_device *dev,
489 struct comedi_subdevice *s,
490 struct comedi_insn *insn,
491 unsigned int *data)
492 {
493 struct pci1710_private *devpriv = dev->private;
494 unsigned int chan = CR_CHAN(insn->chanspec);
495 unsigned int range = CR_RANGE(insn->chanspec);
496 unsigned int val;
497 int i;
498
499 val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
500 val |= (range << (chan << 1));
501 if (val != devpriv->da_ranges) {
502 outb(val, dev->iobase + PCI1720_RANGE);
503 devpriv->da_ranges = val;
504 }
505
506 val = s->readback[chan];
507 for (i = 0; i < insn->n; i++) {
508 val = data[i];
509 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
510 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
511 }
512
513 s->readback[chan] = val;
514
515 return insn->n;
516 }
517
pci171x_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)518 static int pci171x_ai_cancel(struct comedi_device *dev,
519 struct comedi_subdevice *s)
520 {
521 struct pci1710_private *devpriv = dev->private;
522
523 devpriv->CntrlReg &= Control_CNT0;
524 devpriv->CntrlReg |= Control_SW;
525 /* reset any operations */
526 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
527 comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
528 outb(0, dev->iobase + PCI171x_CLRFIFO);
529 outb(0, dev->iobase + PCI171x_CLRINT);
530
531 return 0;
532 }
533
pci1710_handle_every_sample(struct comedi_device * dev,struct comedi_subdevice * s)534 static void pci1710_handle_every_sample(struct comedi_device *dev,
535 struct comedi_subdevice *s)
536 {
537 struct comedi_cmd *cmd = &s->async->cmd;
538 unsigned int status;
539 unsigned int val;
540 int ret;
541
542 status = inw(dev->iobase + PCI171x_STATUS);
543 if (status & Status_FE) {
544 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
545 s->async->events |= COMEDI_CB_ERROR;
546 return;
547 }
548 if (status & Status_FF) {
549 dev_dbg(dev->class_dev,
550 "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
551 s->async->events |= COMEDI_CB_ERROR;
552 return;
553 }
554
555 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
556
557 for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
558 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
559 if (ret) {
560 s->async->events |= COMEDI_CB_ERROR;
561 break;
562 }
563
564 comedi_buf_write_samples(s, &val, 1);
565
566 if (cmd->stop_src == TRIG_COUNT &&
567 s->async->scans_done >= cmd->stop_arg) {
568 s->async->events |= COMEDI_CB_EOA;
569 break;
570 }
571 }
572
573 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
574 }
575
pci1710_handle_fifo(struct comedi_device * dev,struct comedi_subdevice * s)576 static void pci1710_handle_fifo(struct comedi_device *dev,
577 struct comedi_subdevice *s)
578 {
579 struct pci1710_private *devpriv = dev->private;
580 struct comedi_async *async = s->async;
581 struct comedi_cmd *cmd = &async->cmd;
582 unsigned int status;
583 int i;
584
585 status = inw(dev->iobase + PCI171x_STATUS);
586 if (!(status & Status_FH)) {
587 dev_dbg(dev->class_dev, "A/D FIFO not half full!\n");
588 async->events |= COMEDI_CB_ERROR;
589 return;
590 }
591 if (status & Status_FF) {
592 dev_dbg(dev->class_dev,
593 "A/D FIFO Full status (Fatal Error!)\n");
594 async->events |= COMEDI_CB_ERROR;
595 return;
596 }
597
598 for (i = 0; i < devpriv->max_samples; i++) {
599 unsigned int val;
600 int ret;
601
602 ret = pci171x_ai_read_sample(dev, s, s->async->cur_chan, &val);
603 if (ret) {
604 s->async->events |= COMEDI_CB_ERROR;
605 break;
606 }
607
608 if (!comedi_buf_write_samples(s, &val, 1))
609 break;
610
611 if (cmd->stop_src == TRIG_COUNT &&
612 async->scans_done >= cmd->stop_arg) {
613 async->events |= COMEDI_CB_EOA;
614 break;
615 }
616 }
617
618 outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
619 }
620
interrupt_service_pci1710(int irq,void * d)621 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
622 {
623 struct comedi_device *dev = d;
624 struct pci1710_private *devpriv = dev->private;
625 struct comedi_subdevice *s;
626 struct comedi_cmd *cmd;
627
628 if (!dev->attached) /* is device attached? */
629 return IRQ_NONE; /* no, exit */
630
631 s = dev->read_subdev;
632 cmd = &s->async->cmd;
633
634 /* is this interrupt from our board? */
635 if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
636 return IRQ_NONE; /* no, exit */
637
638 if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
639 devpriv->ai_et = 0;
640 devpriv->CntrlReg &= Control_CNT0;
641 devpriv->CntrlReg |= Control_SW; /* set software trigger */
642 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
643 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
644 outb(0, dev->iobase + PCI171x_CLRFIFO);
645 outb(0, dev->iobase + PCI171x_CLRINT);
646 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
647 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
648 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
649 return IRQ_HANDLED;
650 }
651
652 if (cmd->flags & CMDF_WAKE_EOS)
653 pci1710_handle_every_sample(dev, s);
654 else
655 pci1710_handle_fifo(dev, s);
656
657 comedi_handle_events(dev, s);
658
659 return IRQ_HANDLED;
660 }
661
pci171x_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)662 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
663 {
664 struct pci1710_private *devpriv = dev->private;
665 struct comedi_cmd *cmd = &s->async->cmd;
666
667 pci171x_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len,
668 devpriv->saved_seglen);
669
670 outb(0, dev->iobase + PCI171x_CLRFIFO);
671 outb(0, dev->iobase + PCI171x_CLRINT);
672
673 devpriv->CntrlReg &= Control_CNT0;
674 if ((cmd->flags & CMDF_WAKE_EOS) == 0)
675 devpriv->CntrlReg |= Control_ONEFH;
676
677 if (cmd->convert_src == TRIG_TIMER) {
678 comedi_8254_update_divisors(dev->pacer);
679
680 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
681 if (cmd->start_src == TRIG_EXT) {
682 devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
683 devpriv->CntrlReg &=
684 ~(Control_PACER | Control_ONEFH | Control_GATE);
685 devpriv->CntrlReg |= Control_EXT;
686 devpriv->ai_et = 1;
687 } else { /* TRIG_NOW */
688 devpriv->ai_et = 0;
689 }
690 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
691
692 if (cmd->start_src == TRIG_NOW)
693 comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
694 } else { /* TRIG_EXT */
695 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
696 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
697 }
698
699 return 0;
700 }
701
pci171x_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)702 static int pci171x_ai_cmdtest(struct comedi_device *dev,
703 struct comedi_subdevice *s,
704 struct comedi_cmd *cmd)
705 {
706 int err = 0;
707
708 /* Step 1 : check if triggers are trivially valid */
709
710 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
711 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
712 err |= comedi_check_trigger_src(&cmd->convert_src,
713 TRIG_TIMER | TRIG_EXT);
714 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
715 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
716
717 if (err)
718 return 1;
719
720 /* step 2a: make sure trigger sources are unique */
721
722 err |= comedi_check_trigger_is_unique(cmd->start_src);
723 err |= comedi_check_trigger_is_unique(cmd->convert_src);
724 err |= comedi_check_trigger_is_unique(cmd->stop_src);
725
726 /* step 2b: and mutually compatible */
727
728 if (err)
729 return 2;
730
731 /* Step 3: check if arguments are trivially valid */
732
733 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
734 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
735
736 if (cmd->convert_src == TRIG_TIMER)
737 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000);
738 else /* TRIG_FOLLOW */
739 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
740
741 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
742 cmd->chanlist_len);
743
744 if (cmd->stop_src == TRIG_COUNT)
745 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1);
746 else /* TRIG_NONE */
747 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
748
749 if (err)
750 return 3;
751
752 /* step 4: fix up any arguments */
753
754 if (cmd->convert_src == TRIG_TIMER) {
755 unsigned int arg = cmd->convert_arg;
756
757 comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
758 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
759 }
760
761 if (err)
762 return 4;
763
764 /* Step 5: check channel list */
765
766 err |= pci171x_ai_check_chanlist(dev, s, cmd);
767
768 if (err)
769 return 5;
770
771 return 0;
772 }
773
pci171x_insn_counter_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)774 static int pci171x_insn_counter_config(struct comedi_device *dev,
775 struct comedi_subdevice *s,
776 struct comedi_insn *insn,
777 unsigned int *data)
778 {
779 struct pci1710_private *devpriv = dev->private;
780
781 switch (data[0]) {
782 case INSN_CONFIG_SET_CLOCK_SRC:
783 switch (data[1]) {
784 case 0: /* internal */
785 devpriv->ai_et_CntrlReg &= ~Control_CNT0;
786 break;
787 case 1: /* external */
788 devpriv->ai_et_CntrlReg |= Control_CNT0;
789 break;
790 default:
791 return -EINVAL;
792 }
793 outw(devpriv->ai_et_CntrlReg, dev->iobase + PCI171x_CONTROL);
794 break;
795 case INSN_CONFIG_GET_CLOCK_SRC:
796 if (devpriv->ai_et_CntrlReg & Control_CNT0) {
797 data[1] = 1;
798 data[2] = 0;
799 } else {
800 data[1] = 0;
801 data[2] = I8254_OSC_BASE_10MHZ;
802 }
803 break;
804 default:
805 return -EINVAL;
806 }
807
808 return insn->n;
809 }
810
pci171x_reset(struct comedi_device * dev)811 static int pci171x_reset(struct comedi_device *dev)
812 {
813 const struct boardtype *board = dev->board_ptr;
814 struct pci1710_private *devpriv = dev->private;
815
816 /* Software trigger, CNT0=external */
817 devpriv->CntrlReg = Control_SW | Control_CNT0;
818 /* reset any operations */
819 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
820 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
821 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
822 devpriv->da_ranges = 0;
823 if (board->has_ao) {
824 /* set DACs to 0..5V */
825 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
826 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
827 outw(0, dev->iobase + PCI171x_DA2);
828 }
829 outw(0, dev->iobase + PCI171x_DO); /* digital outputs to 0 */
830 outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
831 outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
832
833 return 0;
834 }
835
pci1720_reset(struct comedi_device * dev)836 static int pci1720_reset(struct comedi_device *dev)
837 {
838 struct pci1710_private *devpriv = dev->private;
839 /* set synchronous output mode */
840 outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
841 devpriv->da_ranges = 0xAA;
842 /* set all ranges to +/-5V */
843 outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
844 outw(0x0800, dev->iobase + PCI1720_DA0); /* set outputs to 0V */
845 outw(0x0800, dev->iobase + PCI1720_DA1);
846 outw(0x0800, dev->iobase + PCI1720_DA2);
847 outw(0x0800, dev->iobase + PCI1720_DA3);
848 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
849
850 return 0;
851 }
852
pci1710_reset(struct comedi_device * dev)853 static int pci1710_reset(struct comedi_device *dev)
854 {
855 const struct boardtype *board = dev->board_ptr;
856
857 if (board->is_pci1720)
858 return pci1720_reset(dev);
859
860 return pci171x_reset(dev);
861 }
862
pci1710_auto_attach(struct comedi_device * dev,unsigned long context)863 static int pci1710_auto_attach(struct comedi_device *dev,
864 unsigned long context)
865 {
866 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
867 const struct boardtype *board = NULL;
868 struct pci1710_private *devpriv;
869 struct comedi_subdevice *s;
870 int ret, subdev, n_subdevices;
871
872 if (context < ARRAY_SIZE(boardtypes))
873 board = &boardtypes[context];
874 if (!board)
875 return -ENODEV;
876 dev->board_ptr = board;
877 dev->board_name = board->name;
878
879 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
880 if (!devpriv)
881 return -ENOMEM;
882
883 ret = comedi_pci_enable(dev);
884 if (ret)
885 return ret;
886 dev->iobase = pci_resource_start(pcidev, 2);
887
888 dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE,
889 I8254_OSC_BASE_10MHZ, I8254_IO16, 0);
890 if (!dev->pacer)
891 return -ENOMEM;
892
893 n_subdevices = 0;
894 if (board->n_aichan)
895 n_subdevices++;
896 if (board->has_ao)
897 n_subdevices++;
898 if (board->has_di_do)
899 n_subdevices += 2;
900 if (board->has_counter)
901 n_subdevices++;
902
903 ret = comedi_alloc_subdevices(dev, n_subdevices);
904 if (ret)
905 return ret;
906
907 pci1710_reset(dev);
908
909 if (board->has_irq && pcidev->irq) {
910 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
911 IRQF_SHARED, dev->board_name, dev);
912 if (ret == 0)
913 dev->irq = pcidev->irq;
914 }
915
916 subdev = 0;
917
918 if (board->n_aichan) {
919 s = &dev->subdevices[subdev];
920 s->type = COMEDI_SUBD_AI;
921 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
922 if (board->has_diff_ai)
923 s->subdev_flags |= SDF_DIFF;
924 s->n_chan = board->n_aichan;
925 s->maxdata = 0x0fff;
926 s->range_table = board->rangelist_ai;
927 s->insn_read = pci171x_ai_insn_read;
928 if (dev->irq) {
929 dev->read_subdev = s;
930 s->subdev_flags |= SDF_CMD_READ;
931 s->len_chanlist = s->n_chan;
932 s->do_cmdtest = pci171x_ai_cmdtest;
933 s->do_cmd = pci171x_ai_cmd;
934 s->cancel = pci171x_ai_cancel;
935 }
936 subdev++;
937 }
938
939 if (board->has_ao) {
940 s = &dev->subdevices[subdev];
941 s->type = COMEDI_SUBD_AO;
942 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
943 s->maxdata = 0x0fff;
944 if (board->is_pci1720) {
945 s->n_chan = 4;
946 s->range_table = &pci1720_ao_range;
947 s->insn_write = pci1720_ao_insn_write;
948 } else {
949 s->n_chan = 2;
950 s->range_table = &pci171x_ao_range;
951 s->insn_write = pci171x_ao_insn_write;
952 }
953
954 ret = comedi_alloc_subdev_readback(s);
955 if (ret)
956 return ret;
957
958 /* initialize the readback values to match the board reset */
959 if (board->is_pci1720) {
960 int i;
961
962 for (i = 0; i < s->n_chan; i++)
963 s->readback[i] = 0x0800;
964 }
965
966 subdev++;
967 }
968
969 if (board->has_di_do) {
970 s = &dev->subdevices[subdev];
971 s->type = COMEDI_SUBD_DI;
972 s->subdev_flags = SDF_READABLE;
973 s->n_chan = 16;
974 s->maxdata = 1;
975 s->range_table = &range_digital;
976 s->insn_bits = pci171x_di_insn_bits;
977 subdev++;
978
979 s = &dev->subdevices[subdev];
980 s->type = COMEDI_SUBD_DO;
981 s->subdev_flags = SDF_WRITABLE;
982 s->n_chan = 16;
983 s->maxdata = 1;
984 s->range_table = &range_digital;
985 s->insn_bits = pci171x_do_insn_bits;
986 subdev++;
987 }
988
989 /* Counter subdevice (8254) */
990 if (board->has_counter) {
991 s = &dev->subdevices[subdev];
992 comedi_8254_subdevice_init(s, dev->pacer);
993
994 dev->pacer->insn_config = pci171x_insn_counter_config;
995
996 /* counters 1 and 2 are used internally for the pacer */
997 comedi_8254_set_busy(dev->pacer, 1, true);
998 comedi_8254_set_busy(dev->pacer, 2, true);
999
1000 subdev++;
1001 }
1002
1003 /* max_samples is half the FIFO size (2 bytes/sample) */
1004 devpriv->max_samples = (board->has_large_fifo) ? 2048 : 512;
1005
1006 return 0;
1007 }
1008
pci1710_detach(struct comedi_device * dev)1009 static void pci1710_detach(struct comedi_device *dev)
1010 {
1011 if (dev->iobase)
1012 pci1710_reset(dev);
1013 comedi_pci_detach(dev);
1014 }
1015
1016 static struct comedi_driver adv_pci1710_driver = {
1017 .driver_name = "adv_pci1710",
1018 .module = THIS_MODULE,
1019 .auto_attach = pci1710_auto_attach,
1020 .detach = pci1710_detach,
1021 };
1022
adv_pci1710_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)1023 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1024 const struct pci_device_id *id)
1025 {
1026 return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1027 id->driver_data);
1028 }
1029
1030 static const struct pci_device_id adv_pci1710_pci_table[] = {
1031 {
1032 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1033 PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1034 .driver_data = BOARD_PCI1710,
1035 }, {
1036 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1037 PCI_VENDOR_ID_ADVANTECH, 0x0000),
1038 .driver_data = BOARD_PCI1710,
1039 }, {
1040 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1041 PCI_VENDOR_ID_ADVANTECH, 0xb100),
1042 .driver_data = BOARD_PCI1710,
1043 }, {
1044 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1045 PCI_VENDOR_ID_ADVANTECH, 0xb200),
1046 .driver_data = BOARD_PCI1710,
1047 }, {
1048 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1049 PCI_VENDOR_ID_ADVANTECH, 0xc100),
1050 .driver_data = BOARD_PCI1710,
1051 }, {
1052 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1053 PCI_VENDOR_ID_ADVANTECH, 0xc200),
1054 .driver_data = BOARD_PCI1710,
1055 }, {
1056 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1057 .driver_data = BOARD_PCI1710,
1058 }, {
1059 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1060 PCI_VENDOR_ID_ADVANTECH, 0x0002),
1061 .driver_data = BOARD_PCI1710HG,
1062 }, {
1063 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1064 PCI_VENDOR_ID_ADVANTECH, 0xb102),
1065 .driver_data = BOARD_PCI1710HG,
1066 }, {
1067 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1068 PCI_VENDOR_ID_ADVANTECH, 0xb202),
1069 .driver_data = BOARD_PCI1710HG,
1070 }, {
1071 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1072 PCI_VENDOR_ID_ADVANTECH, 0xc102),
1073 .driver_data = BOARD_PCI1710HG,
1074 }, {
1075 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1076 PCI_VENDOR_ID_ADVANTECH, 0xc202),
1077 .driver_data = BOARD_PCI1710HG,
1078 }, {
1079 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1080 .driver_data = BOARD_PCI1710HG,
1081 },
1082 { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1083 { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1084 { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1085 { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1086 { 0 }
1087 };
1088 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1089
1090 static struct pci_driver adv_pci1710_pci_driver = {
1091 .name = "adv_pci1710",
1092 .id_table = adv_pci1710_pci_table,
1093 .probe = adv_pci1710_pci_probe,
1094 .remove = comedi_pci_auto_unconfig,
1095 };
1096 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1097
1098 MODULE_AUTHOR("Comedi http://www.comedi.org");
1099 MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
1100 MODULE_LICENSE("GPL");
1101