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