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