staging: comedi: adv_pci1710: tidy up digital input and output subdevice init
[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 static int pci171x_di_insn_bits(struct comedi_device *dev,
496                                 struct comedi_subdevice *s,
497                                 struct comedi_insn *insn,
498                                 unsigned int *data)
499 {
500         data[1] = inw(dev->iobase + PCI171x_DI);
501
502         return insn->n;
503 }
504
505 static int pci171x_do_insn_bits(struct comedi_device *dev,
506                                 struct comedi_subdevice *s,
507                                 struct comedi_insn *insn,
508                                 unsigned int *data)
509 {
510         if (comedi_dio_update_state(s, data))
511                 outw(s->state, dev->iobase + PCI171x_DO);
512
513         data[1] = s->state;
514
515         return insn->n;
516 }
517
518 static void pci171x_start_pacer(struct comedi_device *dev,
519                                 bool load_counters)
520 {
521         struct pci1710_private *devpriv = dev->private;
522         unsigned long timer_base = dev->iobase + PCI171X_TIMER_BASE;
523
524         i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
525         i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
526
527         if (load_counters) {
528                 i8254_write(timer_base, 1, 2, devpriv->divisor2);
529                 i8254_write(timer_base, 1, 1, devpriv->divisor1);
530         }
531 }
532
533 static int pci171x_counter_insn_read(struct comedi_device *dev,
534                                      struct comedi_subdevice *s,
535                                      struct comedi_insn *insn,
536                                      unsigned int *data)
537 {
538         unsigned int msb, lsb, ccntrl;
539         int i;
540
541         ccntrl = 0xD2;          /* count only */
542         for (i = 0; i < insn->n; i++) {
543                 outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
544
545                 lsb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
546                 msb = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
547
548                 data[0] = lsb | (msb << 8);
549         }
550
551         return insn->n;
552 }
553
554 static int pci171x_counter_insn_write(struct comedi_device *dev,
555                                       struct comedi_subdevice *s,
556                                       struct comedi_insn *insn,
557                                       unsigned int *data)
558 {
559         struct pci1710_private *devpriv = dev->private;
560         uint msb, lsb, ccntrl, status;
561
562         lsb = data[0] & 0x00FF;
563         msb = (data[0] & 0xFF00) >> 8;
564
565         /* write lsb, then msb */
566         outw(lsb, dev->iobase + PCI171x_CNT0);
567         outw(msb, dev->iobase + PCI171x_CNT0);
568
569         if (devpriv->cnt0_write_wait) {
570                 /* wait for the new count to be loaded */
571                 ccntrl = 0xE2;
572                 do {
573                         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
574                         status = inw(dev->iobase + PCI171x_CNT0) & 0xFF;
575                 } while (status & 0x40);
576         }
577
578         return insn->n;
579 }
580
581 static int pci171x_counter_insn_config(struct comedi_device *dev,
582                                        struct comedi_subdevice *s,
583                                        struct comedi_insn *insn,
584                                        unsigned int *data)
585 {
586 #ifdef unused
587         /* This doesn't work like a normal Comedi counter config */
588         struct pci1710_private *devpriv = dev->private;
589         uint ccntrl = 0;
590
591         devpriv->cnt0_write_wait = data[0] & 0x20;
592
593         /* internal or external clock? */
594         if (!(data[0] & 0x10)) {        /* internal */
595                 devpriv->CntrlReg &= ~Control_CNT0;
596         } else {
597                 devpriv->CntrlReg |= Control_CNT0;
598         }
599         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
600
601         if (data[0] & 0x01)
602                 ccntrl |= Counter_M0;
603         if (data[0] & 0x02)
604                 ccntrl |= Counter_M1;
605         if (data[0] & 0x04)
606                 ccntrl |= Counter_M2;
607         if (data[0] & 0x08)
608                 ccntrl |= Counter_BCD;
609         ccntrl |= Counter_RW0;  /* set read/write mode */
610         ccntrl |= Counter_RW1;
611         outw(ccntrl, dev->iobase + PCI171x_CNTCTRL);
612 #endif
613
614         return 1;
615 }
616
617 static int pci1720_ao_insn_write(struct comedi_device *dev,
618                                  struct comedi_subdevice *s,
619                                  struct comedi_insn *insn,
620                                  unsigned int *data)
621 {
622         struct pci1710_private *devpriv = dev->private;
623         unsigned int chan = CR_CHAN(insn->chanspec);
624         unsigned int range = CR_RANGE(insn->chanspec);
625         unsigned int val;
626         int i;
627
628         val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
629         val |= (range << (chan << 1));
630         if (val != devpriv->da_ranges) {
631                 outb(val, dev->iobase + PCI1720_RANGE);
632                 devpriv->da_ranges = val;
633         }
634
635         val = s->readback[chan];
636         for (i = 0; i < insn->n; i++) {
637                 val = data[i];
638                 outw(val, dev->iobase + PCI1720_DA0 + (chan << 1));
639                 outb(0, dev->iobase + PCI1720_SYNCOUT); /* update outputs */
640         }
641
642         s->readback[chan] = val;
643
644         return insn->n;
645 }
646
647 /*
648 ==============================================================================
649 */
650 static int pci171x_ai_cancel(struct comedi_device *dev,
651                              struct comedi_subdevice *s)
652 {
653         const struct boardtype *this_board = dev->board_ptr;
654         struct pci1710_private *devpriv = dev->private;
655
656         switch (this_board->cardtype) {
657         default:
658                 devpriv->CntrlReg &= Control_CNT0;
659                 devpriv->CntrlReg |= Control_SW;
660                 /* reset any operations */
661                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
662                 pci171x_start_pacer(dev, false);
663                 outb(0, dev->iobase + PCI171x_CLRFIFO);
664                 outb(0, dev->iobase + PCI171x_CLRINT);
665                 break;
666         }
667
668         return 0;
669 }
670
671 static void pci1710_handle_every_sample(struct comedi_device *dev,
672                                         struct comedi_subdevice *s)
673 {
674         struct comedi_cmd *cmd = &s->async->cmd;
675         unsigned int status;
676         unsigned int val;
677         int ret;
678
679         status = inw(dev->iobase + PCI171x_STATUS);
680         if (status & Status_FE) {
681                 dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status);
682                 s->async->events |= COMEDI_CB_ERROR;
683                 comedi_handle_events(dev, s);
684                 return;
685         }
686         if (status & Status_FF) {
687                 dev_dbg(dev->class_dev,
688                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", status);
689                 s->async->events |= COMEDI_CB_ERROR;
690                 comedi_handle_events(dev, s);
691                 return;
692         }
693
694         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
695
696         for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
697                 val = inw(dev->iobase + PCI171x_AD_DATA);
698                 ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
699                 if (ret) {
700                         s->async->events |= COMEDI_CB_ERROR;
701                         break;
702                 }
703
704                 val &= s->maxdata;
705                 comedi_buf_write_samples(s, &val, 1);
706
707                 if (cmd->stop_src == TRIG_COUNT &&
708                     s->async->scans_done >= cmd->stop_arg) {
709                         s->async->events |= COMEDI_CB_EOA;
710                         break;
711                 }
712         }
713
714         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
715
716         comedi_handle_events(dev, s);
717 }
718
719 /*
720 ==============================================================================
721 */
722 static int move_block_from_fifo(struct comedi_device *dev,
723                                 struct comedi_subdevice *s, int n, int turn)
724 {
725         unsigned int val;
726         int ret;
727         int i;
728
729         for (i = 0; i < n; i++) {
730                 val = inw(dev->iobase + PCI171x_AD_DATA);
731
732                 ret = pci171x_ai_dropout(dev, s, s->async->cur_chan, val);
733                 if (ret) {
734                         s->async->events |= COMEDI_CB_ERROR;
735                         return ret;
736                 }
737
738                 val &= s->maxdata;
739                 comedi_buf_write_samples(s, &val, 1);
740         }
741         return 0;
742 }
743
744 static void pci1710_handle_fifo(struct comedi_device *dev,
745                                 struct comedi_subdevice *s)
746 {
747         struct pci1710_private *devpriv = dev->private;
748         struct comedi_cmd *cmd = &s->async->cmd;
749         unsigned int nsamples;
750         unsigned int m;
751
752         m = inw(dev->iobase + PCI171x_STATUS);
753         if (!(m & Status_FH)) {
754                 dev_dbg(dev->class_dev, "A/D FIFO not half full! (%4x)\n", m);
755                 s->async->events |= COMEDI_CB_ERROR;
756                 comedi_handle_events(dev, s);
757                 return;
758         }
759         if (m & Status_FF) {
760                 dev_dbg(dev->class_dev,
761                         "A/D FIFO Full status (Fatal Error!) (%4x)\n", m);
762                 s->async->events |= COMEDI_CB_ERROR;
763                 comedi_handle_events(dev, s);
764                 return;
765         }
766
767         nsamples = devpriv->max_samples;
768         if (comedi_samples_to_bytes(s, nsamples) >= s->async->prealloc_bufsz) {
769                 m = comedi_bytes_to_samples(s, s->async->prealloc_bufsz);
770                 if (move_block_from_fifo(dev, s, m, 0))
771                         return;
772                 nsamples -= m;
773         }
774
775         if (nsamples) {
776                 if (move_block_from_fifo(dev, s, nsamples, 1))
777                         return;
778         }
779
780         if (cmd->stop_src == TRIG_COUNT &&
781             s->async->scans_done >= cmd->stop_arg) {
782                 s->async->events |= COMEDI_CB_EOA;
783                 comedi_handle_events(dev, s);
784                 return;
785         }
786         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear our INT request */
787
788         comedi_handle_events(dev, s);
789 }
790
791 /*
792 ==============================================================================
793 */
794 static irqreturn_t interrupt_service_pci1710(int irq, void *d)
795 {
796         struct comedi_device *dev = d;
797         struct pci1710_private *devpriv = dev->private;
798         struct comedi_subdevice *s;
799         struct comedi_cmd *cmd;
800
801         if (!dev->attached)     /*  is device attached? */
802                 return IRQ_NONE;        /*  no, exit */
803
804         s = dev->read_subdev;
805         cmd = &s->async->cmd;
806
807         /*  is this interrupt from our board? */
808         if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
809                 return IRQ_NONE;        /*  no, exit */
810
811         if (devpriv->ai_et) {   /*  Switch from initial TRIG_EXT to TRIG_xxx. */
812                 devpriv->ai_et = 0;
813                 devpriv->CntrlReg &= Control_CNT0;
814                 devpriv->CntrlReg |= Control_SW; /* set software trigger */
815                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
816                 devpriv->CntrlReg = devpriv->ai_et_CntrlReg;
817                 outb(0, dev->iobase + PCI171x_CLRFIFO);
818                 outb(0, dev->iobase + PCI171x_CLRINT);
819                 outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
820                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
821                 pci171x_start_pacer(dev, true);
822                 return IRQ_HANDLED;
823         }
824
825         if (cmd->flags & CMDF_WAKE_EOS)
826                 pci1710_handle_every_sample(dev, s);
827         else
828                 pci1710_handle_fifo(dev, s);
829
830         return IRQ_HANDLED;
831 }
832
833 static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
834 {
835         struct pci1710_private *devpriv = dev->private;
836         struct comedi_cmd *cmd = &s->async->cmd;
837
838         pci171x_start_pacer(dev, false);
839
840         setup_channel_list(dev, s, cmd->chanlist, cmd->chanlist_len,
841                            devpriv->saved_seglen);
842
843         outb(0, dev->iobase + PCI171x_CLRFIFO);
844         outb(0, dev->iobase + PCI171x_CLRINT);
845
846         devpriv->CntrlReg &= Control_CNT0;
847         if ((cmd->flags & CMDF_WAKE_EOS) == 0)
848                 devpriv->CntrlReg |= Control_ONEFH;
849
850         devpriv->divisor1 = devpriv->next_divisor1;
851         devpriv->divisor2 = devpriv->next_divisor2;
852
853         if (cmd->convert_src == TRIG_TIMER) {
854                 devpriv->CntrlReg |= Control_PACER | Control_IRQEN;
855                 if (cmd->start_src == TRIG_EXT) {
856                         devpriv->ai_et_CntrlReg = devpriv->CntrlReg;
857                         devpriv->CntrlReg &=
858                             ~(Control_PACER | Control_ONEFH | Control_GATE);
859                         devpriv->CntrlReg |= Control_EXT;
860                         devpriv->ai_et = 1;
861                 } else {        /* TRIG_NOW */
862                         devpriv->ai_et = 0;
863                 }
864                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
865
866                 if (cmd->start_src == TRIG_NOW)
867                         pci171x_start_pacer(dev, true);
868         } else {        /* TRIG_EXT */
869                 devpriv->CntrlReg |= Control_EXT | Control_IRQEN;
870                 outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
871         }
872
873         return 0;
874 }
875
876 /*
877 ==============================================================================
878 */
879 static int pci171x_ai_cmdtest(struct comedi_device *dev,
880                               struct comedi_subdevice *s,
881                               struct comedi_cmd *cmd)
882 {
883         struct pci1710_private *devpriv = dev->private;
884         int err = 0;
885         unsigned int arg;
886
887         /* Step 1 : check if triggers are trivially valid */
888
889         err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
890         err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
891         err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
892         err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
893         err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
894
895         if (err)
896                 return 1;
897
898         /* step 2a: make sure trigger sources are unique */
899
900         err |= cfc_check_trigger_is_unique(cmd->start_src);
901         err |= cfc_check_trigger_is_unique(cmd->convert_src);
902         err |= cfc_check_trigger_is_unique(cmd->stop_src);
903
904         /* step 2b: and mutually compatible */
905
906         if (err)
907                 return 2;
908
909         /* Step 3: check if arguments are trivially valid */
910
911         err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0);
912         err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
913
914         if (cmd->convert_src == TRIG_TIMER)
915                 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 10000);
916         else    /* TRIG_FOLLOW */
917                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, 0);
918
919         err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len);
920
921         if (cmd->stop_src == TRIG_COUNT)
922                 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1);
923         else    /* TRIG_NONE */
924                 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0);
925
926         if (err)
927                 return 3;
928
929         /* step 4: fix up any arguments */
930
931         if (cmd->convert_src == TRIG_TIMER) {
932                 arg = cmd->convert_arg;
933                 i8253_cascade_ns_to_timer(I8254_OSC_BASE_10MHZ,
934                                           &devpriv->next_divisor1,
935                                           &devpriv->next_divisor2,
936                                           &arg, cmd->flags);
937                 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg);
938         }
939
940         if (err)
941                 return 4;
942
943         /* Step 5: check channel list */
944
945         err |= pci171x_ai_check_chanlist(dev, s, cmd);
946
947         if (err)
948                 return 5;
949
950         return 0;
951 }
952
953 /*
954 ==============================================================================
955 */
956 static int pci171x_reset(struct comedi_device *dev)
957 {
958         const struct boardtype *this_board = dev->board_ptr;
959         struct pci1710_private *devpriv = dev->private;
960
961         outw(0x30, dev->iobase + PCI171x_CNTCTRL);
962         /* Software trigger, CNT0=external */
963         devpriv->CntrlReg = Control_SW | Control_CNT0;
964         /* reset any operations */
965         outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
966         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
967         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
968         pci171x_start_pacer(dev, false);
969         devpriv->da_ranges = 0;
970         if (this_board->has_ao) {
971                 /* set DACs to 0..5V */
972                 outb(devpriv->da_ranges, dev->iobase + PCI171x_DAREF);
973                 outw(0, dev->iobase + PCI171x_DA1); /* set DA outputs to 0V */
974                 outw(0, dev->iobase + PCI171x_DA2);
975         }
976         outw(0, dev->iobase + PCI171x_DO);      /*  digital outputs to 0 */
977         outb(0, dev->iobase + PCI171x_CLRFIFO); /*  clear FIFO */
978         outb(0, dev->iobase + PCI171x_CLRINT);  /*  clear INT request */
979
980         return 0;
981 }
982
983 /*
984 ==============================================================================
985 */
986 static int pci1720_reset(struct comedi_device *dev)
987 {
988         struct pci1710_private *devpriv = dev->private;
989         /* set synchronous output mode */
990         outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT);
991         devpriv->da_ranges = 0xAA;
992         /* set all ranges to +/-5V */
993         outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE);
994         outw(0x0800, dev->iobase + PCI1720_DA0);        /*  set outputs to 0V */
995         outw(0x0800, dev->iobase + PCI1720_DA1);
996         outw(0x0800, dev->iobase + PCI1720_DA2);
997         outw(0x0800, dev->iobase + PCI1720_DA3);
998         outb(0, dev->iobase + PCI1720_SYNCOUT); /*  update outputs */
999
1000         return 0;
1001 }
1002
1003 /*
1004 ==============================================================================
1005 */
1006 static int pci1710_reset(struct comedi_device *dev)
1007 {
1008         const struct boardtype *this_board = dev->board_ptr;
1009
1010         switch (this_board->cardtype) {
1011         case TYPE_PCI1720:
1012                 return pci1720_reset(dev);
1013         default:
1014                 return pci171x_reset(dev);
1015         }
1016 }
1017
1018 static int pci1710_auto_attach(struct comedi_device *dev,
1019                                unsigned long context)
1020 {
1021         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
1022         const struct boardtype *this_board = NULL;
1023         struct pci1710_private *devpriv;
1024         struct comedi_subdevice *s;
1025         int ret, subdev, n_subdevices;
1026
1027         if (context < ARRAY_SIZE(boardtypes))
1028                 this_board = &boardtypes[context];
1029         if (!this_board)
1030                 return -ENODEV;
1031         dev->board_ptr = this_board;
1032         dev->board_name = this_board->name;
1033
1034         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
1035         if (!devpriv)
1036                 return -ENOMEM;
1037
1038         ret = comedi_pci_enable(dev);
1039         if (ret)
1040                 return ret;
1041         dev->iobase = pci_resource_start(pcidev, 2);
1042
1043         n_subdevices = 0;
1044         if (this_board->n_aichan)
1045                 n_subdevices++;
1046         if (this_board->has_ao)
1047                 n_subdevices++;
1048         if (this_board->has_di_do)
1049                 n_subdevices += 2;
1050         if (this_board->has_counter)
1051                 n_subdevices++;
1052
1053         ret = comedi_alloc_subdevices(dev, n_subdevices);
1054         if (ret)
1055                 return ret;
1056
1057         pci1710_reset(dev);
1058
1059         if (this_board->has_irq && pcidev->irq) {
1060                 ret = request_irq(pcidev->irq, interrupt_service_pci1710,
1061                                   IRQF_SHARED, dev->board_name, dev);
1062                 if (ret == 0)
1063                         dev->irq = pcidev->irq;
1064         }
1065
1066         subdev = 0;
1067
1068         if (this_board->n_aichan) {
1069                 s = &dev->subdevices[subdev];
1070                 s->type = COMEDI_SUBD_AI;
1071                 s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
1072                 if (this_board->has_diff_ai)
1073                         s->subdev_flags |= SDF_DIFF;
1074                 s->n_chan = this_board->n_aichan;
1075                 s->maxdata = 0x0fff;
1076                 s->range_table = this_board->rangelist_ai;
1077                 s->insn_read = pci171x_insn_read_ai;
1078                 if (dev->irq) {
1079                         dev->read_subdev = s;
1080                         s->subdev_flags |= SDF_CMD_READ;
1081                         s->len_chanlist = s->n_chan;
1082                         s->do_cmdtest = pci171x_ai_cmdtest;
1083                         s->do_cmd = pci171x_ai_cmd;
1084                         s->cancel = pci171x_ai_cancel;
1085                 }
1086                 subdev++;
1087         }
1088
1089         if (this_board->has_ao) {
1090                 s = &dev->subdevices[subdev];
1091                 s->type = COMEDI_SUBD_AO;
1092                 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
1093                 s->maxdata = 0x0fff;
1094                 s->range_table = this_board->rangelist_ao;
1095                 switch (this_board->cardtype) {
1096                 case TYPE_PCI1720:
1097                         s->n_chan = 4;
1098                         s->insn_write = pci1720_ao_insn_write;
1099                         break;
1100                 default:
1101                         s->n_chan = 2;
1102                         s->insn_write = pci171x_ao_insn_write;
1103                         break;
1104                 }
1105
1106                 ret = comedi_alloc_subdev_readback(s);
1107                 if (ret)
1108                         return ret;
1109
1110                 /* initialize the readback values to match the board reset */
1111                 if (this_board->cardtype == TYPE_PCI1720) {
1112                         int i;
1113
1114                         for (i = 0; i < s->n_chan; i++)
1115                                 s->readback[i] = 0x0800;
1116                 }
1117
1118                 subdev++;
1119         }
1120
1121         if (this_board->has_di_do) {
1122                 s = &dev->subdevices[subdev];
1123                 s->type         = COMEDI_SUBD_DI;
1124                 s->subdev_flags = SDF_READABLE;
1125                 s->n_chan       = 16;
1126                 s->maxdata      = 1;
1127                 s->range_table  = &range_digital;
1128                 s->insn_bits    = pci171x_di_insn_bits;
1129                 subdev++;
1130
1131                 s = &dev->subdevices[subdev];
1132                 s->type         = COMEDI_SUBD_DO;
1133                 s->subdev_flags = SDF_WRITABLE;
1134                 s->n_chan       = 16;
1135                 s->maxdata      = 1;
1136                 s->range_table  = &range_digital;
1137                 s->insn_bits    = pci171x_do_insn_bits;
1138                 subdev++;
1139         }
1140
1141         if (this_board->has_counter) {
1142                 s = &dev->subdevices[subdev];
1143                 s->type         = COMEDI_SUBD_COUNTER;
1144                 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
1145                 s->n_chan       = 1;
1146                 s->maxdata      = 0xffff;
1147                 s->range_table  = &range_unknown;
1148                 s->insn_read    = pci171x_counter_insn_read;
1149                 s->insn_write   = pci171x_counter_insn_write;
1150                 s->insn_config  = pci171x_counter_insn_config;
1151                 subdev++;
1152         }
1153
1154         /* max_samples is half the FIFO size (2 bytes/sample) */
1155         devpriv->max_samples = (this_board->has_large_fifo) ? 2048 : 512;
1156
1157         return 0;
1158 }
1159
1160 static void pci1710_detach(struct comedi_device *dev)
1161 {
1162         if (dev->iobase)
1163                 pci1710_reset(dev);
1164         comedi_pci_detach(dev);
1165 }
1166
1167 static struct comedi_driver adv_pci1710_driver = {
1168         .driver_name    = "adv_pci1710",
1169         .module         = THIS_MODULE,
1170         .auto_attach    = pci1710_auto_attach,
1171         .detach         = pci1710_detach,
1172 };
1173
1174 static int adv_pci1710_pci_probe(struct pci_dev *dev,
1175                                  const struct pci_device_id *id)
1176 {
1177         return comedi_pci_auto_config(dev, &adv_pci1710_driver,
1178                                       id->driver_data);
1179 }
1180
1181 static const struct pci_device_id adv_pci1710_pci_table[] = {
1182         {
1183                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1184                                PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
1185                 .driver_data = BOARD_PCI1710,
1186         }, {
1187                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1188                                PCI_VENDOR_ID_ADVANTECH, 0x0000),
1189                 .driver_data = BOARD_PCI1710,
1190         }, {
1191                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1192                                PCI_VENDOR_ID_ADVANTECH, 0xb100),
1193                 .driver_data = BOARD_PCI1710,
1194         }, {
1195                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1196                                PCI_VENDOR_ID_ADVANTECH, 0xb200),
1197                 .driver_data = BOARD_PCI1710,
1198         }, {
1199                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1200                                PCI_VENDOR_ID_ADVANTECH, 0xc100),
1201                 .driver_data = BOARD_PCI1710,
1202         }, {
1203                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1204                                PCI_VENDOR_ID_ADVANTECH, 0xc200),
1205                 .driver_data = BOARD_PCI1710,
1206         }, {
1207                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100),
1208                 .driver_data = BOARD_PCI1710,
1209         }, {
1210                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1211                                PCI_VENDOR_ID_ADVANTECH, 0x0002),
1212                 .driver_data = BOARD_PCI1710HG,
1213         }, {
1214                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1215                                PCI_VENDOR_ID_ADVANTECH, 0xb102),
1216                 .driver_data = BOARD_PCI1710HG,
1217         }, {
1218                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1219                                PCI_VENDOR_ID_ADVANTECH, 0xb202),
1220                 .driver_data = BOARD_PCI1710HG,
1221         }, {
1222                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1223                                PCI_VENDOR_ID_ADVANTECH, 0xc102),
1224                 .driver_data = BOARD_PCI1710HG,
1225         }, {
1226                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710,
1227                                PCI_VENDOR_ID_ADVANTECH, 0xc202),
1228                 .driver_data = BOARD_PCI1710HG,
1229         }, {
1230                 PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102),
1231                 .driver_data = BOARD_PCI1710HG,
1232         },
1233         { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
1234         { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
1235         { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
1236         { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
1237         { 0 }
1238 };
1239 MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
1240
1241 static struct pci_driver adv_pci1710_pci_driver = {
1242         .name           = "adv_pci1710",
1243         .id_table       = adv_pci1710_pci_table,
1244         .probe          = adv_pci1710_pci_probe,
1245         .remove         = comedi_pci_auto_unconfig,
1246 };
1247 module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
1248
1249 MODULE_AUTHOR("Comedi http://www.comedi.org");
1250 MODULE_DESCRIPTION("Comedi low-level driver");
1251 MODULE_LICENSE("GPL");