4b0735ad57065d228e9af53327ebbd036a73aa8e
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / adv_pci1710.c
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 informations.
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/pci.h>
46 #include <linux/interrupt.h>
47
48 #include "../comedidev.h"
49
50 #include "comedi_fc.h"
51 #include "8253.h"
52 #include "amcc_s5933.h"
53
54 /* hardware types of the cards */
55 #define TYPE_PCI171X    0
56 #define TYPE_PCI1713    2
57 #define TYPE_PCI1720    3
58
59 #define PCI171x_AD_DATA  0      /* R:   A/D data */
60 #define PCI171x_SOFTTRG  0      /* W:   soft trigger for A/D */
61 #define PCI171x_RANGE    2      /* W:   A/D gain/range register */
62 #define PCI171x_MUX      4      /* W:   A/D multiplexor control */
63 #define PCI171x_STATUS   6      /* R:   status register */
64 #define PCI171x_CONTROL  6      /* W:   control register */
65 #define PCI171x_CLRINT   8      /* W:   clear interrupts request */
66 #define PCI171x_CLRFIFO  9      /* W:   clear FIFO */
67 #define PCI171x_DA1     10      /* W:   D/A register */
68 #define PCI171x_DA2     12      /* W:   D/A register */
69 #define PCI171x_DAREF   14      /* W:   D/A reference control */
70 #define PCI171x_DI      16      /* R:   digi inputs */
71 #define PCI171x_DO      16      /* R:   digi inputs */
72
73 #define PCI171X_TIMER_BASE      0x18
74
75 #define PCI171x_CNT0    24      /* R/W: 8254 counter 0 */
76 #define PCI171x_CNT1    26      /* R/W: 8254 counter 1 */
77 #define PCI171x_CNT2    28      /* R/W: 8254 counter 2 */
78 #define PCI171x_CNTCTRL 30      /* W:   8254 counter control */
79
80 /* upper bits from status register (PCI171x_STATUS) (lower is same with control
81  * reg) */
82 #define Status_FE       0x0100  /* 1=FIFO is empty */
83 #define Status_FH       0x0200  /* 1=FIFO is half full */
84 #define Status_FF       0x0400  /* 1=FIFO is full, fatal error */
85 #define Status_IRQ      0x0800  /* 1=IRQ occurred */
86 /* bits from control register (PCI171x_CONTROL) */
87 #define Control_CNT0    0x0040  /* 1=CNT0 have external source,
88                                  * 0=have internal 100kHz source */
89 #define Control_ONEFH   0x0020  /* 1=IRQ on FIFO is half full, 0=every sample */
90 #define Control_IRQEN   0x0010  /* 1=enable IRQ */
91 #define Control_GATE    0x0008  /* 1=enable external trigger GATE (8254?) */
92 #define Control_EXT     0x0004  /* 1=external trigger source */
93 #define Control_PACER   0x0002  /* 1=enable internal 8254 trigger source */
94 #define Control_SW      0x0001  /* 1=enable software trigger source */
95 /* bits from counter control register (PCI171x_CNTCTRL) */
96 #define Counter_BCD     0x0001  /* 0 = binary counter, 1 = BCD counter */
97 #define Counter_M0      0x0002  /* M0-M2 select modes 0-5 */
98 #define Counter_M1      0x0004  /* 000 = mode 0, 010 = mode 2 ... */
99 #define Counter_M2      0x0008
100 #define Counter_RW0     0x0010  /* RW0/RW1 select read/write mode */
101 #define Counter_RW1     0x0020
102 #define Counter_SC0     0x0040  /* Select Counter. Only 00 or 11 may */
103 #define Counter_SC1     0x0080  /* be used, 00 for CNT0,
104                                  * 11 for read-back command */
105
106 #define PCI1720_DA0      0      /* W:   D/A register 0 */
107 #define PCI1720_DA1      2      /* W:   D/A register 1 */
108 #define PCI1720_DA2      4      /* W:   D/A register 2 */
109 #define PCI1720_DA3      6      /* W:   D/A register 3 */
110 #define PCI1720_RANGE    8      /* R/W: D/A range register */
111 #define PCI1720_SYNCOUT  9      /* W:   D/A synchronized output register */
112 #define PCI1720_SYNCONT 15      /* R/W: D/A synchronized control */
113
114 /* D/A synchronized control (PCI1720_SYNCONT) */
115 #define Syncont_SC0      1      /* set synchronous output mode */
116
117 static const struct comedi_lrange range_pci1710_3 = {
118         9, {
119                 BIP_RANGE(5),
120                 BIP_RANGE(2.5),
121                 BIP_RANGE(1.25),
122                 BIP_RANGE(0.625),
123                 BIP_RANGE(10),
124                 UNI_RANGE(10),
125                 UNI_RANGE(5),
126                 UNI_RANGE(2.5),
127                 UNI_RANGE(1.25)
128         }
129 };
130
131 static const char range_codes_pci1710_3[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
132                                               0x10, 0x11, 0x12, 0x13 };
133
134 static const struct comedi_lrange range_pci1710hg = {
135         12, {
136                 BIP_RANGE(5),
137                 BIP_RANGE(0.5),
138                 BIP_RANGE(0.05),
139                 BIP_RANGE(0.005),
140                 BIP_RANGE(10),
141                 BIP_RANGE(1),
142                 BIP_RANGE(0.1),
143                 BIP_RANGE(0.01),
144                 UNI_RANGE(10),
145                 UNI_RANGE(1),
146                 UNI_RANGE(0.1),
147                 UNI_RANGE(0.01)
148         }
149 };
150
151 static const char range_codes_pci1710hg[] = { 0x00, 0x01, 0x02, 0x03, 0x04,
152                                               0x05, 0x06, 0x07, 0x10, 0x11,
153                                               0x12, 0x13 };
154
155 static const struct comedi_lrange range_pci17x1 = {
156         5, {
157                 BIP_RANGE(10),
158                 BIP_RANGE(5),
159                 BIP_RANGE(2.5),
160                 BIP_RANGE(1.25),
161                 BIP_RANGE(0.625)
162         }
163 };
164
165 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
166
167 static const struct comedi_lrange range_pci1720 = {
168         4, {
169                 UNI_RANGE(5),
170                 UNI_RANGE(10),
171                 BIP_RANGE(5),
172                 BIP_RANGE(10)
173         }
174 };
175
176 static const struct comedi_lrange range_pci171x_da = {
177         2, {
178                 UNI_RANGE(5),
179                 UNI_RANGE(10)
180         }
181 };
182
183 enum pci1710_boardid {
184         BOARD_PCI1710,
185         BOARD_PCI1710HG,
186         BOARD_PCI1711,
187         BOARD_PCI1713,
188         BOARD_PCI1720,
189         BOARD_PCI1731,
190 };
191
192 struct boardtype {
193         const char *name;       /*  board name */
194         char cardtype;          /*  0=1710& co. 2=1713, ... */
195         int n_aichan;           /*  num of A/D chans */
196         const struct comedi_lrange *rangelist_ai;       /*  rangelist for A/D */
197         const char *rangecode_ai;       /*  range codes for programming */
198         const struct comedi_lrange *rangelist_ao;       /*  rangelist for D/A */
199         unsigned int has_irq:1;
200         unsigned int has_large_fifo:1;  /* 4K or 1K FIFO */
201         unsigned int has_diff_ai:1;
202         unsigned int has_ao:1;
203         unsigned int has_di_do:1;
204         unsigned int has_counter:1;
205 };
206
207 static const struct boardtype boardtypes[] = {
208         [BOARD_PCI1710] = {
209                 .name           = "pci1710",
210                 .cardtype       = TYPE_PCI171X,
211                 .n_aichan       = 16,
212                 .rangelist_ai   = &range_pci1710_3,
213                 .rangecode_ai   = range_codes_pci1710_3,
214                 .rangelist_ao   = &range_pci171x_da,
215                 .has_irq        = 1,
216                 .has_large_fifo = 1,
217                 .has_diff_ai    = 1,
218                 .has_ao         = 1,
219                 .has_di_do      = 1,
220                 .has_counter    = 1,
221         },
222         [BOARD_PCI1710HG] = {
223                 .name           = "pci1710hg",
224                 .cardtype       = TYPE_PCI171X,
225                 .n_aichan       = 16,
226                 .rangelist_ai   = &range_pci1710hg,
227                 .rangecode_ai   = range_codes_pci1710hg,
228                 .rangelist_ao   = &range_pci171x_da,
229                 .has_irq        = 1,
230                 .has_large_fifo = 1,
231                 .has_diff_ai    = 1,
232                 .has_ao         = 1,
233                 .has_di_do      = 1,
234                 .has_counter    = 1,
235         },
236         [BOARD_PCI1711] = {
237                 .name           = "pci1711",
238                 .cardtype       = TYPE_PCI171X,
239                 .n_aichan       = 16,
240                 .rangelist_ai   = &range_pci17x1,
241                 .rangecode_ai   = range_codes_pci17x1,
242                 .rangelist_ao   = &range_pci171x_da,
243                 .has_irq        = 1,
244                 .has_ao         = 1,
245                 .has_di_do      = 1,
246                 .has_counter    = 1,
247         },
248         [BOARD_PCI1713] = {
249                 .name           = "pci1713",
250                 .cardtype       = TYPE_PCI1713,
251                 .n_aichan       = 32,
252                 .rangelist_ai   = &range_pci1710_3,
253                 .rangecode_ai   = range_codes_pci1710_3,
254                 .has_irq        = 1,
255                 .has_large_fifo = 1,
256                 .has_diff_ai    = 1,
257         },
258         [BOARD_PCI1720] = {
259                 .name           = "pci1720",
260                 .cardtype       = TYPE_PCI1720,
261                 .rangelist_ao   = &range_pci1720,
262                 .has_ao         = 1,
263         },
264         [BOARD_PCI1731] = {
265                 .name           = "pci1731",
266                 .cardtype       = TYPE_PCI171X,
267                 .n_aichan       = 16,
268                 .rangelist_ai   = &range_pci17x1,
269                 .rangecode_ai   = range_codes_pci17x1,
270                 .has_irq        = 1,
271                 .has_di_do      = 1,
272         },
273 };
274
275 struct pci1710_private {
276         unsigned int max_samples;
277         unsigned int CntrlReg;  /*  Control register */
278         unsigned char ai_et;
279         unsigned int ai_et_CntrlReg;
280         unsigned int ai_et_MuxVal;
281         unsigned int next_divisor1;
282         unsigned int next_divisor2;
283         unsigned int divisor1;
284         unsigned int divisor2;
285         unsigned int act_chanlist[32];  /*  list of scanned channel */
286         unsigned char saved_seglen;     /* len of the non-repeating chanlist */
287         unsigned char da_ranges;        /*  copy of D/A outpit range register */
288         unsigned int cnt0_write_wait;   /* after a write, wait for update of the
289                                          * internal state */
290 };
291
292 /*  used for gain list programming */
293 static const unsigned int muxonechan[] = {
294         0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
295         0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
296         0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
297         0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
298 };
299
300 static int pci171x_ai_dropout(struct comedi_device *dev,
301                               struct comedi_subdevice *s,
302                               unsigned int chan,
303                               unsigned int val)
304 {
305         const struct boardtype *board = dev->board_ptr;
306         struct pci1710_private *devpriv = dev->private;
307
308         if (board->cardtype != TYPE_PCI1713) {
309                 if ((val & 0xf000) != devpriv->act_chanlist[chan]) {
310                         dev_err(dev->class_dev,
311                                 "A/D data droput: received from channel %d, expected %d\n",
312                                 (val >> 12) & 0xf,
313                                 (devpriv->act_chanlist[chan] >> 12) & 0xf);
314                         return -ENODATA;
315                 }
316         }
317         return 0;
318 }
319
320 static int pci171x_ai_check_chanlist(struct comedi_device *dev,
321                                      struct comedi_subdevice *s,
322                                      struct comedi_cmd *cmd)
323 {
324         struct pci1710_private *devpriv = dev->private;
325         unsigned int chan0 = CR_CHAN(cmd->chanlist[0]);
326         unsigned int last_aref = CR_AREF(cmd->chanlist[0]);
327         unsigned int next_chan = (chan0 + 1) % s->n_chan;
328         unsigned int chansegment[32];
329         unsigned int seglen;
330         int i;
331
332         if (cmd->chanlist_len == 1) {
333                 devpriv->saved_seglen = cmd->chanlist_len;
334                 return 0;
335         }
336
337         /* first channel is always ok */
338         chansegment[0] = cmd->chanlist[0];
339
340         for (i = 1; i < cmd->chanlist_len; i++) {
341                 unsigned int chan = CR_CHAN(cmd->chanlist[i]);
342                 unsigned int aref = CR_AREF(cmd->chanlist[i]);
343
344                 if (cmd->chanlist[0] == cmd->chanlist[i])
345                         break;  /*  we detected a loop, stop */
346
347                 if (aref == AREF_DIFF && (chan & 1)) {
348                         dev_err(dev->class_dev,
349                                 "Odd channel cannot be differential input!\n");
350                         return -EINVAL;
351                 }
352
353                 if (last_aref == AREF_DIFF)
354                         next_chan = (next_chan + 1) % s->n_chan;
355                 if (chan != next_chan) {
356                         dev_err(dev->class_dev,
357                                 "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
358                                 i, chan, next_chan, chan0);
359                         return -EINVAL;
360                 }
361
362                 /* next correct channel in list */
363                 chansegment[i] = cmd->chanlist[i];
364                 last_aref = aref;
365         }
366         seglen = i;
367
368         for (i = 0; i < cmd->chanlist_len; i++) {
369                 if (cmd->chanlist[i] != chansegment[i % seglen]) {
370                         dev_err(dev->class_dev,
371                                 "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
372                                 i, CR_CHAN(chansegment[i]),
373                                 CR_RANGE(chansegment[i]),
374                                 CR_AREF(chansegment[i]),
375                                 CR_CHAN(cmd->chanlist[i % seglen]),
376                                 CR_RANGE(cmd->chanlist[i % seglen]),
377                                 CR_AREF(chansegment[i % seglen]));
378                         return -EINVAL;
379                 }
380         }
381         devpriv->saved_seglen = seglen;
382
383         return 0;
384 }
385
386 static void setup_channel_list(struct comedi_device *dev,
387                                struct comedi_subdevice *s,
388                                unsigned int *chanlist, unsigned int n_chan,
389                                unsigned int seglen)
390 {
391         const struct boardtype *this_board = dev->board_ptr;
392         struct pci1710_private *devpriv = dev->private;
393         unsigned int i, range, chanprog;
394
395         for (i = 0; i < seglen; i++) {  /*  store range list to card */
396                 chanprog = muxonechan[CR_CHAN(chanlist[i])];
397                 outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
398                 range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
399                 if (CR_AREF(chanlist[i]) == AREF_DIFF)
400                         range |= 0x0020;
401                 outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
402                 devpriv->act_chanlist[i] =
403                         (CR_CHAN(chanlist[i]) << 12) & 0xf000;
404         }
405         for ( ; i < n_chan; i++) { /* store remainder of channel list */
406                 devpriv->act_chanlist[i] =
407                         (CR_CHAN(chanlist[i]) << 12) & 0xf000;
408         }
409
410         devpriv->ai_et_MuxVal =
411                 CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
412         /* select channel interval to scan */
413         outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
414 }
415
416 static int pci171x_ai_eoc(struct comedi_device *dev,
417                           struct comedi_subdevice *s,
418                           struct comedi_insn *insn,
419                           unsigned long context)
420 {
421         unsigned int status;
422
423         status = inw(dev->iobase + PCI171x_STATUS);
424         if ((status & Status_FE) == 0)
425                 return 0;
426         return -EBUSY;
427 }
428
429 static int pci171x_insn_read_ai(struct comedi_device *dev,
430                                 struct comedi_subdevice *s,
431                                 struct comedi_insn *insn, unsigned int *data)
432 {
433         struct pci1710_private *devpriv = dev->private;
434         unsigned int chan = CR_CHAN(insn->chanspec);
435         int ret = 0;
436         int i;
437
438         devpriv->CntrlReg &= Control_CNT0;
439         devpriv->CntrlReg |= Control_SW;        /*  set software trigger */
440         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
441         outb(0, dev->iobase + PCI171x_CLRFIFO);
442         outb(0, dev->iobase + PCI171x_CLRINT);
443
444         setup_channel_list(dev, s, &insn->chanspec, 1, 1);
445
446         for (i = 0; i < insn->n; i++) {
447                 unsigned int val;
448
449                 outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
450
451                 ret = comedi_timeout(dev, s, insn, pci171x_ai_eoc, 0);
452                 if (ret)
453                         break;
454
455                 val = inw(dev->iobase + PCI171x_AD_DATA);
456                 ret = pci171x_ai_dropout(dev, s, chan, val);
457                 if (ret)
458                         break;
459
460                 data[i] = val & s->maxdata;
461         }
462
463         outb(0, dev->iobase + PCI171x_CLRFIFO);
464         outb(0, dev->iobase + PCI171x_CLRINT);
465
466         return ret ? ret : insn->n;
467 }
468
469 static int pci171x_ao_insn_write(struct comedi_device *dev,
470                                  struct comedi_subdevice *s,
471                                  struct comedi_insn *insn,
472                                  unsigned int *data)
473 {
474         struct pci1710_private *devpriv = dev->private;
475         unsigned int chan = CR_CHAN(insn->chanspec);
476         unsigned int range = CR_RANGE(insn->chanspec);
477         unsigned int reg = chan ? PCI171x_DA2 : PCI171x_DA1;
478         unsigned int val = s->readback[chan];
479         int i;
480
481         devpriv->da_ranges &= ~(1 << (chan << 1));
482         devpriv->da_ranges |= (range << (chan << 1));
483         outw(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
484
485         for (i = 0; i < insn->n; i++) {
486                 val = data[i];
487                 outw(val, dev->iobase + reg);
488         }
489
490         s->readback[chan] = val;
491
492         return insn->n;
493 }
494
495 /*
496 ==============================================================================
497 */
498 static int pci171x_insn_bits_di(struct comedi_device *dev,
499                                 struct comedi_subdevice *s,
500                                 struct comedi_insn *insn, unsigned int *data)
501 {
502         data[1] = inw(dev->iobase + PCI171x_DI);
503
504         return insn->n;
505 }
506
507 static int pci171x_insn_bits_do(struct comedi_device *dev,
508                                 struct comedi_subdevice *s,
509                                 struct comedi_insn *insn,
510                                 unsigned int *data)
511 {
512         if (comedi_dio_update_state(s, data))
513                 outw(s->state, dev->iobase + PCI171x_DO);
514
515         data[1] = s->state;
516
517         return insn->n;
518 }
519
520 static void pci171x_start_pacer(struct comedi_device *dev,
521                                 bool load_counters)
522 {
523         struct pci1710_private *devpriv = dev->private;
524         unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
525
526         i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
527         i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
528
529         if (load_counters) {
530                 i8254_write(timer_base, 1, 2, devpriv->divisor2);
531                 i8254_write(timer_base, 1, 1, devpriv->divisor1);
532         }
533 }
534
535 static int pci171x_counter_insn_read(struct comedi_device *dev,
536                                      struct comedi_subdevice *s,
537                                      struct comedi_insn *insn,
538                                      unsigned int *data)
539 {
540         unsigned int msb, lsb, ccntrl;
541         int i;
542
543         ccntrl = 0xD2;          /* count only */
544         for (i = 0; i < insn->n; i++) {
545                 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
546
547                 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
548                 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
549
550                 data[0] = lsb | (msb << 8);
551         }
552
553         return insn->n;
554 }
555
556 static int pci171x_counter_insn_write(struct comedi_device *dev,
557                                       struct comedi_subdevice *s,
558                                       struct comedi_insn *insn,
559                                       unsigned int *data)
560 {
561         struct pci1710_private *devpriv = dev->private;
562         uint msb, lsb, ccntrl, status;
563
564         lsb = data[0] & 0x00FF;
565         msb = (data[0] & 0xFF00) >> 8;
566
567         /* write lsb, then msb */
568         outw(lsb, dev->iobase + PCI171x_CNT0);
569         outw(msb, dev->iobase + PCI171x_CNT0);
570
571         if (devpriv->cnt0_write_wait) {
572                 /* wait for the new count to be loaded */
573                 ccntrl = 0xE2;
574                 do {
575                         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
576                         status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
577                 } while (status & 0x40);
578         }
579
580         return insn->n;
581 }
582
583 static int pci171x_counter_insn_config(struct comedi_device *dev,
584                                        struct comedi_subdevice *s,
585                                        struct comedi_insn *insn,
586                                        unsigned int *data)
587 {
588 #ifdef unused
589         /* This doesn't work like a normal Comedi counter config */
590         struct pci1710_private *devpriv = dev->private;
591         uint ccntrl = 0;
592
593         devpriv->cnt0_write_wait = data[0] & 0x20;
594
595         /* internal or external clock? */
596         if (!(data[0] & 0x10)) {        /* internal */
597                 devpriv->CntrlReg &= ~Control_CNT0;
598         } else {
599                 devpriv->CntrlReg |= Control_CNT0;
600         }
601         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
602
603         if (data[0] & 0x01)
604                 ccntrl |= Counter_M0;
605         if (data[0] & 0x02)
606                 ccntrl |= Counter_M1;
607         if (data[0] & 0x04)
608                 ccntrl |= Counter_M2;
609         if (data[0] & 0x08)
610                 ccntrl |= Counter_BCD;
611         ccntrl |= Counter_RW0;  /* set read/write mode */
612         ccntrl |= Counter_RW1;
613         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
614 #endif
615
616         return 1;
617 }
618
619 static int pci1720_ao_insn_write(struct comedi_device *dev,
620                                  struct comedi_subdevice *s,
621                                  struct comedi_insn *insn,
622                                  unsigned int *data)
623 {
624         struct pci1710_private *devpriv = dev->private;
625         unsigned int chan = CR_CHAN(insn->chanspec);
626         unsigned int range = CR_RANGE(insn->chanspec);
627         unsigned int val;
628         int i;
629
630         val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
631         val |= (range << (chan << 1));
632         if (val != devpriv->da_ranges) {
633                 outb(val, dev->iobase + PCI1720_RANGE);
634                 devpriv->da_ranges = val;
635         }
636
637         val = s->readback[chan];
638         for (i = 0; i < insn->n; i++) {
639                 val = data[i];
640                 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
641                 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
642         }
643
644         s->readback[chan] = val;
645
646         return insn->n;
647 }
648
649 /*
650 ==============================================================================
651 */
652 static int pci171x_ai_cancel(struct comedi_device *dev,
653                              struct comedi_subdevice *s)
654 {
655         const struct boardtype *this_board = dev->board_ptr;
656         struct pci1710_private *devpriv = dev->private;
657
658         switch (this_board->cardtype) {
659         default:
660                 devpriv->CntrlReg &= Control_CNT0;
661                 devpriv->CntrlReg |= Control_SW;
662                 /* reset any operations */
663                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
664                 pci171x_start_pacer(dev, false);
665                 outb(0, dev->iobase + PCI171x_CLRFIFO);
666                 outb(0, dev->iobase + PCI171x_CLRINT);
667                 break;
668         }
669
670         return 0;
671 }
672
673 static void pci1710_handle_every_sample(struct comedi_device *dev,
674                                         struct comedi_subdevice *s)
675 {
676         struct comedi_cmd *cmd = &s->async->cmd;
677         unsigned int status;
678         unsigned int val;
679         int ret;
680
681         status = inw(dev->iobase + PCI171x_STATUS);
682         if (status & Status_FE) {
683                 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
684                 s->async->events |= COMEDI_CB_ERROR;
685                 comedi_handle_events(dev, s);
686                 return;
687         }
688         if (status & Status_FF) {
689                 dev_dbg(dev->class_dev,
690                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
691                 s->async->events |= COMEDI_CB_ERROR;
692                 comedi_handle_events(dev, s);
693                 return;
694         }
695
696         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
697
698         for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
699                 val = inw(dev->iobase + PCI171x_AD_DATA);
700                 ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
701                 if (ret) {
702                         s->async->events |= COMEDI_CB_ERROR;
703                         break;
704                 }
705
706                 val &= s->maxdata;
707                 comedi_buf_write_samples(s, &val, 1);
708
709                 if (cmd->stop_src == TRIG_COUNT &&
710                     s->async->scans_done >= cmd->stop_arg) {
711                         s->async->events |= COMEDI_CB_EOA;
712                         break;
713                 }
714         }
715
716         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
717
718         comedi_handle_events(dev, s);
719 }
720
721 /*
722 ==============================================================================
723 */
724 static int move_block_from_fifo(struct comedi_device *dev,
725                                 struct comedi_subdevice *s, int n, int turn)
726 {
727         unsigned int val;
728         int ret;
729         int i;
730
731         for (i = 0; i < n; i++) {
732                 val = inw(dev->iobase + PCI171x_AD_DATA);
733
734                 ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
735                 if (ret) {
736                         s->async->events |= COMEDI_CB_ERROR;
737                         return ret;
738                 }
739
740                 val &= s->maxdata;
741                 comedi_buf_write_samples(s, &val, 1);
742         }
743         return 0;
744 }
745
746 static void pci1710_handle_fifo(struct comedi_device *dev,
747                                 struct comedi_subdevice *s)
748 {
749         struct pci1710_private *devpriv = dev->private;
750         struct comedi_cmd *cmd = &s->async->cmd;
751         unsigned int nsamples;
752         unsigned int m;
753
754         m = inw(dev->iobase + PCI171x_STATUS);
755         if (!(m & Status_FH)) {
756                 dev_dbg(dev->class_dev, "A/D FIFO not half full! (%4x)\n", m);
757                 s->async->events |= COMEDI_CB_ERROR;
758                 comedi_handle_events(dev, s);
759                 return;
760         }
761         if (m & Status_FF) {
762                 dev_dbg(dev->class_dev,
763                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", m);
764                 s->async->events |= COMEDI_CB_ERROR;
765                 comedi_handle_events(dev, s);
766                 return;
767         }
768
769         nsamples = devpriv->max_samples;
770         if (comedi_samples_to_bytes(s, nsamples) >= s->async->prealloc_bufsz) {
771                 m = comedi_bytes_to_samples(s, s->async->prealloc_bufsz);
772                 if (move_block_from_fifo(dev, s, m, 0))
773                         return;
774                 nsamples -= m;
775         }
776
777         if (nsamples) {
778                 if (move_block_from_fifo(dev, s, nsamples, 1))
779                         return;
780         }
781
782         if (cmd->stop_src == TRIG_COUNT &&
783             s->async->scans_done >= cmd->stop_arg) {
784                 s->async->events |= COMEDI_CB_EOA;
785                 comedi_handle_events(dev, s);
786                 return;
787         }
788         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
789
790         comedi_handle_events(dev, s);
791 }
792
793 /*
794 ==============================================================================
795 */
796 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
797 {
798         struct comedi_device *dev = d;
799         struct pci1710_private *devpriv = dev->private;
800         struct comedi_subdevice *s;
801         struct comedi_cmd *cmd;
802
803         if (!dev->attached)     /*  is device attached? */
804                 return IRQ_NONE;        /*  no, exit */
805
806         s = dev->read_subdev;
807         cmd = &s->async->cmd;
808
809         /*  is this interrupt from our board? */
810         if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
811                 return IRQ_NONE;        /*  no, exit */
812
813         if (devpriv->ai_et) {   /*  Switch from initial TRIG_EXT to TRIG_xxx. */
814                 devpriv->ai_et = 0;
815                 devpriv->CntrlReg &= Control_CNT0;
816                 devpriv->CntrlReg |= Control_SW; /* set software trigger */
817                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
818                 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
819                 outb(0, dev->iobase + PCI171x_CLRFIFO);
820                 outb(0, dev->iobase + PCI171x_CLRINT);
821                 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
822                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
823                 pci171x_start_pacer(dev, true);
824                 return IRQ_HANDLED;
825         }
826
827         if (cmd->flags & CMDF_WAKE_EOS)
828                 pci1710_handle_every_sample(dev, s);
829         else
830                 pci1710_handle_fifo(dev, s);
831
832         return IRQ_HANDLED;
833 }
834
835 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
836 {
837         struct pci1710_private *devpriv = dev->private;
838         struct comedi_cmd *cmd = &s->async->cmd;
839
840         pci171x_start_pacer(dev, false);
841
842         setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len,
843                            devpriv->saved_seglen);
844
845         outb(0, dev->iobase + PCI171x_CLRFIFO);
846         outb(0, dev->iobase + PCI171x_CLRINT);
847
848         devpriv->CntrlReg &= Control_CNT0;
849         if ((cmd->flags & CMDF_WAKE_EOS) == 0)
850                 devpriv->CntrlReg |= Control_ONEFH;
851
852         devpriv->divisor1 = devpriv->next_divisor1;
853         devpriv->divisor2 = devpriv->next_divisor2;
854
855         if (cmd->convert_src == TRIG_TIMER) {
856                 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
857                 if (cmd->start_src == TRIG_EXT) {
858                         devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
859                         devpriv->CntrlReg &=
860                             ~(Control_PACER | Control_ONEFH | Control_GATE);
861                         devpriv->CntrlReg |= Control_EXT;
862                         devpriv->ai_et = 1;
863                 } else {        /* TRIG_NOW */
864                         devpriv->ai_et = 0;
865                 }
866                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
867
868                 if (cmd->start_src == TRIG_NOW)
869                         pci171x_start_pacer(dev, true);
870         } else {        /* TRIG_EXT */
871                 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
872                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
873         }
874
875         return 0;
876 }
877
878 /*
879 ==============================================================================
880 */
881 static int pci171x_ai_cmdtest(struct comedi_device *dev,
882                               struct comedi_subdevice *s,
883                               struct comedi_cmd *cmd)
884 {
885         struct pci1710_private *devpriv = dev->private;
886         int err = 0;
887         unsigned int arg;
888
889         /* Step 1 : check if triggers are trivially valid */
890
891         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
892         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
893         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
894         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
895         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
896
897         if (err)
898                 return 1;
899
900         /* step 2a: make sure trigger sources are unique */
901
902         err |= cfc_check_trigger_is_unique(cmd->start_src);
903         err |= cfc_check_trigger_is_unique(cmd->convert_src);
904         err |= cfc_check_trigger_is_unique(cmd->stop_src);
905
906         /* step 2b: and mutually compatible */
907
908         if (err)
909                 return 2;
910
911         /* Step 3: check if arguments are trivially valid */
912
913         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
914         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
915
916         if (cmd->convert_src == TRIG_TIMER)
917                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
918         else    /* TRIG_FOLLOW */
919                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
920
921         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
922
923         if (cmd->stop_src == TRIG_COUNT)
924                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
925         else    /* TRIG_NONE */
926                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
927
928         if (err)
929                 return 3;
930
931         /* step 4: fix up any arguments */
932
933         if (cmd->convert_src == TRIG_TIMER) {
934                 arg = cmd->convert_arg;
935                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
936                                           &devpriv->next_divisor1,
937                                           &devpriv->next_divisor2,
938                                           &arg, cmd->flags);
939                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
940         }
941
942         if (err)
943                 return 4;
944
945         /* Step 5: check channel list */
946
947         err |= pci171x_ai_check_chanlist(dev, s, cmd);
948
949         if (err)
950                 return 5;
951
952         return 0;
953 }
954
955 /*
956 ==============================================================================
957 */
958 static int pci171x_reset(struct comedi_device *dev)
959 {
960         const struct boardtype *this_board = dev->board_ptr;
961         struct pci1710_private *devpriv = dev->private;
962
963         outw(0x30, dev->iobase + PCI171x_CNTCTRL);
964         /* Software trigger, CNT0=external */
965         devpriv->CntrlReg = Control_SW | Control_CNT0;
966         /* reset any operations */
967         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
968         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
969         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
970         pci171x_start_pacer(dev, false);
971         devpriv->da_ranges = 0;
972         if (this_board->has_ao) {
973                 /* set DACs to 0..5V */
974                 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
975                 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
976                 outw(0, dev->iobase + PCI171x_DA2);
977         }
978         outw(0, dev->iobase + PCI171x_DO);      /*  digital outputs to 0 */
979         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
980         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
981
982         return 0;
983 }
984
985 /*
986 ==============================================================================
987 */
988 static int pci1720_reset(struct comedi_device *dev)
989 {
990         struct pci1710_private *devpriv = dev->private;
991         /* set synchronous output mode */
992         outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
993         devpriv->da_ranges = 0xAA;
994         /* set all ranges to +/-5V */
995         outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
996         outw(0x0800, dev->iobase + PCI1720_DA0);        /*  set outputs to 0V */
997         outw(0x0800, dev->iobase + PCI1720_DA1);
998         outw(0x0800, dev->iobase + PCI1720_DA2);
999         outw(0x0800, dev->iobase + PCI1720_DA3);
1000         outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
1001
1002         return 0;
1003 }
1004
1005 /*
1006 ==============================================================================
1007 */
1008 static int pci1710_reset(struct comedi_device *dev)
1009 {
1010         const struct boardtype *this_board = dev->board_ptr;
1011
1012         switch (this_board->cardtype) {
1013         case TYPE_PCI1720:
1014                 return pci1720_reset(dev);
1015         default:
1016                 return pci171x_reset(dev);
1017         }
1018 }
1019
1020 static int pci1710_auto_attach(struct comedi_device *dev,
1021                                unsigned long context)
1022 {
1023         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1024         const struct boardtype *this_board = NULL;
1025         struct pci1710_private *devpriv;
1026         struct comedi_subdevice *s;
1027         int ret, subdev, n_subdevices;
1028
1029         if (context < ARRAY_SIZE(boardtypes))
1030                 this_board = &boardtypes[context];
1031         if (!this_board)
1032                 return -ENODEV;
1033         dev->board_ptr = this_board;
1034         dev->board_name = this_board->name;
1035
1036         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1037         if (!devpriv)
1038                 return -ENOMEM;
1039
1040         ret = comedi_pci_enable(dev);
1041         if (ret)
1042                 return ret;
1043         dev->iobase = pci_resource_start(pcidev, 2);
1044
1045         n_subdevices = 0;
1046         if (this_board->n_aichan)
1047                 n_subdevices++;
1048         if (this_board->has_ao)
1049                 n_subdevices++;
1050         if (this_board->has_di_do)
1051                 n_subdevices += 2;
1052         if (this_board->has_counter)
1053                 n_subdevices++;
1054
1055         ret = comedi_alloc_subdevices(dev, n_subdevices);
1056         if (ret)
1057                 return ret;
1058
1059         pci1710_reset(dev);
1060
1061         if (this_board->has_irq && pcidev->irq) {
1062                 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1063                                   IRQF_SHARED, dev->board_name, dev);
1064                 if (ret == 0)
1065                         dev->irq = pcidev->irq;
1066         }
1067
1068         subdev = 0;
1069
1070         if (this_board->n_aichan) {
1071                 s = &dev->subdevices[subdev];
1072                 s->type = COMEDI_SUBD_AI;
1073                 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1074                 if (this_board->has_diff_ai)
1075                         s->subdev_flags |= SDF_DIFF;
1076                 s->n_chan = this_board->n_aichan;
1077                 s->maxdata = 0x0fff;
1078                 s->range_table = this_board->rangelist_ai;
1079                 s->insn_read = pci171x_insn_read_ai;
1080                 if (dev->irq) {
1081                         dev->read_subdev = s;
1082                         s->subdev_flags |= SDF_CMD_READ;
1083                         s->len_chanlist = s->n_chan;
1084                         s->do_cmdtest = pci171x_ai_cmdtest;
1085                         s->do_cmd = pci171x_ai_cmd;
1086                         s->cancel = pci171x_ai_cancel;
1087                 }
1088                 subdev++;
1089         }
1090
1091         if (this_board->has_ao) {
1092                 s = &dev->subdevices[subdev];
1093                 s->type = COMEDI_SUBD_AO;
1094                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1095                 s->maxdata = 0x0fff;
1096                 s->range_table = this_board->rangelist_ao;
1097                 switch (this_board->cardtype) {
1098                 case TYPE_PCI1720:
1099                         s->n_chan = 4;
1100                         s->insn_write = pci1720_ao_insn_write;
1101                         break;
1102                 default:
1103                         s->n_chan = 2;
1104                         s->insn_write = pci171x_ao_insn_write;
1105                         break;
1106                 }
1107
1108                 ret = comedi_alloc_subdev_readback(s);
1109                 if (ret)
1110                         return ret;
1111
1112                 /* initialize the readback values to match the board reset */
1113                 if (this_board->cardtype == TYPE_PCI1720) {
1114                         int i;
1115
1116                         for (i = 0; i < s->n_chan; i++)
1117                                 s->readback[i] = 0x0800;
1118                 }
1119
1120                 subdev++;
1121         }
1122
1123         if (this_board->has_di_do) {
1124                 s = &dev->subdevices[subdev];
1125                 s->type = COMEDI_SUBD_DI;
1126                 s->subdev_flags = SDF_READABLE;
1127                 s->n_chan = 16;
1128                 s->maxdata = 1;
1129                 s->range_table = &range_digital;
1130                 s->insn_bits = pci171x_insn_bits_di;
1131                 subdev++;
1132
1133                 s = &dev->subdevices[subdev];
1134                 s->type = COMEDI_SUBD_DO;
1135                 s->subdev_flags = SDF_WRITABLE;
1136                 s->n_chan = 16;
1137                 s->maxdata = 1;
1138                 s->range_table = &range_digital;
1139                 s->insn_bits = pci171x_insn_bits_do;
1140                 subdev++;
1141         }
1142
1143         if (this_board->has_counter) {
1144                 s = &dev->subdevices[subdev];
1145                 s->type         = COMEDI_SUBD_COUNTER;
1146                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1147                 s->n_chan       = 1;
1148                 s->maxdata      = 0xffff;
1149                 s->range_table  = &range_unknown;
1150                 s->insn_read    = pci171x_counter_insn_read;
1151                 s->insn_write   = pci171x_counter_insn_write;
1152                 s->insn_config  = pci171x_counter_insn_config;
1153                 subdev++;
1154         }
1155
1156         /* max_samples is half the FIFO size (2 bytes/sample) */
1157         devpriv->max_samples = (this_board->has_large_fifo) ? 2048 : 512;
1158
1159         return 0;
1160 }
1161
1162 static void pci1710_detach(struct comedi_device *dev)
1163 {
1164         if (dev->iobase)
1165                 pci1710_reset(dev);
1166         comedi_pci_detach(dev);
1167 }
1168
1169 static struct comedi_driver adv_pci1710_driver = {
1170         .driver_name    = "adv_pci1710",
1171         .module         = THIS_MODULE,
1172         .auto_attach    = pci1710_auto_attach,
1173         .detach         = pci1710_detach,
1174 };
1175
1176 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1177                                  const struct pci_device_id *id)
1178 {
1179         return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1180                                       id->driver_data);
1181 }
1182
1183 static const struct pci_device_id adv_pci1710_pci_table[] = {
1184         {
1185                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1186                                PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1187                 .driver_data = BOARD_PCI1710,
1188         }, {
1189                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1190                                PCI_VENDOR_ID_ADVANTECH, 0x0000),
1191                 .driver_data = BOARD_PCI1710,
1192         }, {
1193                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1194                                PCI_VENDOR_ID_ADVANTECH, 0xb100),
1195                 .driver_data = BOARD_PCI1710,
1196         }, {
1197                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1198                                PCI_VENDOR_ID_ADVANTECH, 0xb200),
1199                 .driver_data = BOARD_PCI1710,
1200         }, {
1201                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1202                                PCI_VENDOR_ID_ADVANTECH, 0xc100),
1203                 .driver_data = BOARD_PCI1710,
1204         }, {
1205                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1206                                PCI_VENDOR_ID_ADVANTECH, 0xc200),
1207                 .driver_data = BOARD_PCI1710,
1208         }, {
1209                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1210                 .driver_data = BOARD_PCI1710,
1211         }, {
1212                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1213                                PCI_VENDOR_ID_ADVANTECH, 0x0002),
1214                 .driver_data = BOARD_PCI1710HG,
1215         }, {
1216                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1217                                PCI_VENDOR_ID_ADVANTECH, 0xb102),
1218                 .driver_data = BOARD_PCI1710HG,
1219         }, {
1220                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1221                                PCI_VENDOR_ID_ADVANTECH, 0xb202),
1222                 .driver_data = BOARD_PCI1710HG,
1223         }, {
1224                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1225                                PCI_VENDOR_ID_ADVANTECH, 0xc102),
1226                 .driver_data = BOARD_PCI1710HG,
1227         }, {
1228                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1229                                PCI_VENDOR_ID_ADVANTECH, 0xc202),
1230                 .driver_data = BOARD_PCI1710HG,
1231         }, {
1232                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1233                 .driver_data = BOARD_PCI1710HG,
1234         },
1235         { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1236         { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1237         { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1238         { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1239         { 0 }
1240 };
1241 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1242
1243 static struct pci_driver adv_pci1710_pci_driver = {
1244         .name           = "adv_pci1710",
1245         .id_table       = adv_pci1710_pci_table,
1246         .probe          = adv_pci1710_pci_probe,
1247         .remove         = comedi_pci_auto_unconfig,
1248 };
1249 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1250
1251 MODULE_AUTHOR("Comedi http://www.comedi.org");
1252 MODULE_DESCRIPTION("Comedi low-level driver");
1253 MODULE_LICENSE("GPL");