]>
Commit | Line | Data |
---|---|---|
e184e2be | 1 | // SPDX-License-Identifier: GPL-2.0 |
0e8db97a | 2 | /* |
a8285330 HS |
3 | * adv_pci1710.c |
4 | * Comedi driver for Advantech PCI-1710 series boards | |
0e8db97a MD |
5 | * Author: Michal Dobes <dobes@tesnet.cz> |
6 | * | |
7 | * Thanks to ZhenGang Shang <ZhenGang.Shang@Advantech.com.cn> | |
9949595c | 8 | * for testing and information. |
a8285330 HS |
9 | */ |
10 | ||
11 | /* | |
12 | * Driver: adv_pci1710 | |
13 | * Description: Comedi driver for Advantech PCI-1710 series boards | |
14 | * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711, | |
b2cb0686 | 15 | * PCI-1713, PCI-1731 |
a8285330 | 16 | * Author: Michal Dobes <dobes@tesnet.cz> |
b2cb0686 | 17 | * Updated: Fri, 29 Oct 2015 17:19:35 -0700 |
a8285330 | 18 | * Status: works |
0e8db97a | 19 | * |
a8285330 | 20 | * Configuration options: not applicable, uses PCI auto config |
0e8db97a | 21 | * |
a8285330 HS |
22 | * This driver supports AI, AO, DI and DO subdevices. |
23 | * AI subdevice supports cmd and insn interface, | |
24 | * other subdevices support only insn interface. | |
0e8db97a | 25 | * |
a8285330 HS |
26 | * The PCI-1710 and PCI-1710HG have the same PCI device ID, so the |
27 | * driver cannot distinguish between them, as would be normal for a | |
28 | * PCI driver. | |
29 | */ | |
0e8db97a | 30 | |
ce157f80 | 31 | #include <linux/module.h> |
70265d24 JS |
32 | #include <linux/interrupt.h> |
33 | ||
2a12d968 | 34 | #include "../comedi_pci.h" |
0e8db97a | 35 | |
48a4f222 | 36 | #include "comedi_8254.h" |
0e8db97a MD |
37 | #include "amcc_s5933.h" |
38 | ||
39363d06 HS |
39 | /* |
40 | * PCI BAR2 Register map (dev->iobase) | |
41 | */ | |
42 | #define PCI171X_AD_DATA_REG 0x00 /* R: A/D data */ | |
43 | #define PCI171X_SOFTTRG_REG 0x00 /* W: soft trigger for A/D */ | |
44 | #define PCI171X_RANGE_REG 0x02 /* W: A/D gain/range register */ | |
7603900f HS |
45 | #define PCI171X_RANGE_DIFF BIT(5) |
46 | #define PCI171X_RANGE_UNI BIT(4) | |
47 | #define PCI171X_RANGE_GAIN(x) (((x) & 0x7) << 0) | |
39363d06 | 48 | #define PCI171X_MUX_REG 0x04 /* W: A/D multiplexor control */ |
a9d3a9ce IA |
49 | #define PCI171X_MUX_CHANH(x) (((x) & 0xff) << 8) |
50 | #define PCI171X_MUX_CHANL(x) (((x) & 0xff) << 0) | |
92c65e55 | 51 | #define PCI171X_MUX_CHAN(x) (PCI171X_MUX_CHANH(x) | PCI171X_MUX_CHANL(x)) |
d67e50fe HS |
52 | #define PCI171X_STATUS_REG 0x06 /* R: status register */ |
53 | #define PCI171X_STATUS_IRQ BIT(11) /* 1=IRQ occurred */ | |
54 | #define PCI171X_STATUS_FF BIT(10) /* 1=FIFO is full, fatal error */ | |
55 | #define PCI171X_STATUS_FH BIT(9) /* 1=FIFO is half full */ | |
56 | #define PCI171X_STATUS_FE BIT(8) /* 1=FIFO is empty */ | |
9fcce6e2 HS |
57 | #define PCI171X_CTRL_REG 0x06 /* W: control register */ |
58 | #define PCI171X_CTRL_CNT0 BIT(6) /* 1=ext. clk, 0=int. 100kHz clk */ | |
59 | #define PCI171X_CTRL_ONEFH BIT(5) /* 1=on FIFO half full, 0=on sample */ | |
60 | #define PCI171X_CTRL_IRQEN BIT(4) /* 1=enable IRQ */ | |
61 | #define PCI171X_CTRL_GATE BIT(3) /* 1=enable ext. trigger GATE (8254?) */ | |
62 | #define PCI171X_CTRL_EXT BIT(2) /* 1=enable ext. trigger source */ | |
63 | #define PCI171X_CTRL_PACER BIT(1) /* 1=enable int. 8254 trigger source */ | |
64 | #define PCI171X_CTRL_SW BIT(0) /* 1=enable software trigger source */ | |
39363d06 HS |
65 | #define PCI171X_CLRINT_REG 0x08 /* W: clear interrupts request */ |
66 | #define PCI171X_CLRFIFO_REG 0x09 /* W: clear FIFO */ | |
67 | #define PCI171X_DA_REG(x) (0x0a + ((x) * 2)) /* W: D/A register */ | |
68 | #define PCI171X_DAREF_REG 0x0e /* W: D/A reference control */ | |
976e893b HS |
69 | #define PCI171X_DAREF(c, r) (((r) & 0x3) << ((c) * 2)) |
70 | #define PCI171X_DAREF_MASK(c) PCI171X_DAREF((c), 0x3) | |
39363d06 HS |
71 | #define PCI171X_DI_REG 0x10 /* R: digital inputs */ |
72 | #define PCI171X_DO_REG 0x10 /* W: digital outputs */ | |
73 | #define PCI171X_TIMER_BASE 0x18 /* R/W: 8254 timer */ | |
925ddefc | 74 | |
7603900f | 75 | static const struct comedi_lrange pci1710_ai_range = { |
bdaa6140 | 76 | 9, { |
7603900f HS |
77 | BIP_RANGE(5), /* gain 1 (0x00) */ |
78 | BIP_RANGE(2.5), /* gain 2 (0x01) */ | |
79 | BIP_RANGE(1.25), /* gain 4 (0x02) */ | |
80 | BIP_RANGE(0.625), /* gain 8 (0x03) */ | |
81 | BIP_RANGE(10), /* gain 0.5 (0x04) */ | |
82 | UNI_RANGE(10), /* gain 1 (0x00 | UNI) */ | |
83 | UNI_RANGE(5), /* gain 2 (0x01 | UNI) */ | |
84 | UNI_RANGE(2.5), /* gain 4 (0x02 | UNI) */ | |
85 | UNI_RANGE(1.25) /* gain 8 (0x03 | UNI) */ | |
bdaa6140 | 86 | } |
0e8db97a MD |
87 | }; |
88 | ||
7603900f | 89 | static const struct comedi_lrange pci1710hg_ai_range = { |
bdaa6140 | 90 | 12, { |
7603900f HS |
91 | BIP_RANGE(5), /* gain 1 (0x00) */ |
92 | BIP_RANGE(0.5), /* gain 10 (0x01) */ | |
93 | BIP_RANGE(0.05), /* gain 100 (0x02) */ | |
94 | BIP_RANGE(0.005), /* gain 1000 (0x03) */ | |
95 | BIP_RANGE(10), /* gain 0.5 (0x04) */ | |
96 | BIP_RANGE(1), /* gain 5 (0x05) */ | |
97 | BIP_RANGE(0.1), /* gain 50 (0x06) */ | |
98 | BIP_RANGE(0.01), /* gain 500 (0x07) */ | |
99 | UNI_RANGE(10), /* gain 1 (0x00 | UNI) */ | |
100 | UNI_RANGE(1), /* gain 10 (0x01 | UNI) */ | |
101 | UNI_RANGE(0.1), /* gain 100 (0x02 | UNI) */ | |
102 | UNI_RANGE(0.01) /* gain 1000 (0x03 | UNI) */ | |
bdaa6140 | 103 | } |
0e8db97a MD |
104 | }; |
105 | ||
7603900f | 106 | static const struct comedi_lrange pci1711_ai_range = { |
bdaa6140 | 107 | 5, { |
7603900f HS |
108 | BIP_RANGE(10), /* gain 1 (0x00) */ |
109 | BIP_RANGE(5), /* gain 2 (0x01) */ | |
110 | BIP_RANGE(2.5), /* gain 4 (0x02) */ | |
111 | BIP_RANGE(1.25), /* gain 8 (0x03) */ | |
112 | BIP_RANGE(0.625) /* gain 16 (0x04) */ | |
bdaa6140 | 113 | } |
0e8db97a MD |
114 | }; |
115 | ||
7a772eea | 116 | static const struct comedi_lrange pci171x_ao_range = { |
976e893b HS |
117 | 3, { |
118 | UNI_RANGE(5), /* internal -5V ref */ | |
119 | UNI_RANGE(10), /* internal -10V ref */ | |
120 | RANGE_ext(0, 1) /* external -Vref (+/-10V max) */ | |
bdaa6140 | 121 | } |
0e8db97a MD |
122 | }; |
123 | ||
0005fbed HS |
124 | enum pci1710_boardid { |
125 | BOARD_PCI1710, | |
94cc409b | 126 | BOARD_PCI1710HG, |
0005fbed HS |
127 | BOARD_PCI1711, |
128 | BOARD_PCI1713, | |
0005fbed HS |
129 | BOARD_PCI1731, |
130 | }; | |
131 | ||
7875a00b | 132 | struct boardtype { |
b5b147dc HS |
133 | const char *name; |
134 | const struct comedi_lrange *ai_range; | |
dbdb6248 | 135 | unsigned int is_pci1711:1; |
e4451eeb | 136 | unsigned int is_pci1713:1; |
87abf660 | 137 | unsigned int has_ao:1; |
7875a00b | 138 | }; |
0e8db97a | 139 | |
7875a00b | 140 | static const struct boardtype boardtypes[] = { |
0005fbed | 141 | [BOARD_PCI1710] = { |
e199ec95 | 142 | .name = "pci1710", |
b5b147dc | 143 | .ai_range = &pci1710_ai_range, |
87abf660 | 144 | .has_ao = 1, |
94cc409b IA |
145 | }, |
146 | [BOARD_PCI1710HG] = { | |
147 | .name = "pci1710hg", | |
b5b147dc | 148 | .ai_range = &pci1710hg_ai_range, |
87abf660 | 149 | .has_ao = 1, |
0005fbed HS |
150 | }, |
151 | [BOARD_PCI1711] = { | |
e199ec95 | 152 | .name = "pci1711", |
b5b147dc | 153 | .ai_range = &pci1711_ai_range, |
dbdb6248 | 154 | .is_pci1711 = 1, |
87abf660 | 155 | .has_ao = 1, |
0005fbed HS |
156 | }, |
157 | [BOARD_PCI1713] = { | |
e199ec95 | 158 | .name = "pci1713", |
b5b147dc | 159 | .ai_range = &pci1710_ai_range, |
e4451eeb | 160 | .is_pci1713 = 1, |
0005fbed | 161 | }, |
0005fbed | 162 | [BOARD_PCI1731] = { |
e199ec95 | 163 | .name = "pci1731", |
b5b147dc | 164 | .ai_range = &pci1711_ai_range, |
dbdb6248 | 165 | .is_pci1711 = 1, |
e199ec95 | 166 | }, |
0e8db97a MD |
167 | }; |
168 | ||
6e8131a8 | 169 | struct pci1710_private { |
7bd428c4 | 170 | unsigned int max_samples; |
9fcce6e2 HS |
171 | unsigned int ctrl; /* control register value */ |
172 | unsigned int ctrl_ext; /* used to switch from TRIG_EXT to TRIG_xxx */ | |
92c65e55 | 173 | unsigned int mux_scan; /* used to set the channel interval to scan */ |
0e8db97a | 174 | unsigned char ai_et; |
20ce161d | 175 | unsigned int act_chanlist[32]; /* list of scanned channel */ |
56556577 | 176 | unsigned char saved_seglen; /* len of the non-repeating chanlist */ |
2696fb57 | 177 | unsigned char da_ranges; /* copy of D/A outpit range register */ |
7603900f | 178 | unsigned char unipolar_gain; /* adjust for unipolar gain codes */ |
6e8131a8 | 179 | }; |
0e8db97a | 180 | |
e209f7cc | 181 | static int pci1710_ai_check_chanlist(struct comedi_device *dev, |
7cc054d0 HS |
182 | struct comedi_subdevice *s, |
183 | struct comedi_cmd *cmd) | |
4fa7bbec | 184 | { |
56556577 | 185 | struct pci1710_private *devpriv = dev->private; |
b5a7a466 HS |
186 | unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); |
187 | unsigned int last_aref = CR_AREF(cmd->chanlist[0]); | |
188 | unsigned int next_chan = (chan0 + 1) % s->n_chan; | |
4fa7bbec | 189 | unsigned int chansegment[32]; |
b5a7a466 HS |
190 | unsigned int seglen; |
191 | int i; | |
4fa7bbec | 192 | |
56556577 HS |
193 | if (cmd->chanlist_len == 1) { |
194 | devpriv->saved_seglen = cmd->chanlist_len; | |
195 | return 0; | |
196 | } | |
4fa7bbec | 197 | |
b5a7a466 HS |
198 | /* first channel is always ok */ |
199 | chansegment[0] = cmd->chanlist[0]; | |
200 | ||
201 | for (i = 1; i < cmd->chanlist_len; i++) { | |
202 | unsigned int chan = CR_CHAN(cmd->chanlist[i]); | |
203 | unsigned int aref = CR_AREF(cmd->chanlist[i]); | |
204 | ||
205 | if (cmd->chanlist[0] == cmd->chanlist[i]) | |
4fa7bbec | 206 | break; /* we detected a loop, stop */ |
b5a7a466 HS |
207 | |
208 | if (aref == AREF_DIFF && (chan & 1)) { | |
209 | dev_err(dev->class_dev, | |
210 | "Odd channel cannot be differential input!\n"); | |
56556577 | 211 | return -EINVAL; |
4fa7bbec | 212 | } |
b5a7a466 HS |
213 | |
214 | if (last_aref == AREF_DIFF) | |
215 | next_chan = (next_chan + 1) % s->n_chan; | |
216 | if (chan != next_chan) { | |
217 | dev_err(dev->class_dev, | |
218 | "channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n", | |
219 | i, chan, next_chan, chan0); | |
56556577 | 220 | return -EINVAL; |
4fa7bbec | 221 | } |
4fa7bbec | 222 | |
b5a7a466 HS |
223 | /* next correct channel in list */ |
224 | chansegment[i] = cmd->chanlist[i]; | |
225 | last_aref = aref; | |
226 | } | |
227 | seglen = i; | |
228 | ||
229 | for (i = 0; i < cmd->chanlist_len; i++) { | |
230 | if (cmd->chanlist[i] != chansegment[i % seglen]) { | |
231 | dev_err(dev->class_dev, | |
232 | "bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n", | |
233 | i, CR_CHAN(chansegment[i]), | |
234 | CR_RANGE(chansegment[i]), | |
235 | CR_AREF(chansegment[i]), | |
236 | CR_CHAN(cmd->chanlist[i % seglen]), | |
237 | CR_RANGE(cmd->chanlist[i % seglen]), | |
238 | CR_AREF(chansegment[i % seglen])); | |
56556577 | 239 | return -EINVAL; |
4fa7bbec HS |
240 | } |
241 | } | |
56556577 HS |
242 | devpriv->saved_seglen = seglen; |
243 | ||
244 | return 0; | |
4fa7bbec HS |
245 | } |
246 | ||
e209f7cc | 247 | static void pci1710_ai_setup_chanlist(struct comedi_device *dev, |
a65e1419 HS |
248 | struct comedi_subdevice *s, |
249 | unsigned int *chanlist, | |
250 | unsigned int n_chan, | |
251 | unsigned int seglen) | |
4fa7bbec | 252 | { |
4fa7bbec | 253 | struct pci1710_private *devpriv = dev->private; |
a65e1419 HS |
254 | unsigned int first_chan = CR_CHAN(chanlist[0]); |
255 | unsigned int last_chan = CR_CHAN(chanlist[seglen - 1]); | |
256 | unsigned int i; | |
0e8db97a | 257 | |
4fa7bbec | 258 | for (i = 0; i < seglen; i++) { /* store range list to card */ |
a65e1419 HS |
259 | unsigned int chan = CR_CHAN(chanlist[i]); |
260 | unsigned int range = CR_RANGE(chanlist[i]); | |
261 | unsigned int aref = CR_AREF(chanlist[i]); | |
7603900f | 262 | unsigned int rangeval = 0; |
a65e1419 | 263 | |
a65e1419 | 264 | if (aref == AREF_DIFF) |
7603900f HS |
265 | rangeval |= PCI171X_RANGE_DIFF; |
266 | if (comedi_range_is_unipolar(s, range)) { | |
267 | rangeval |= PCI171X_RANGE_UNI; | |
268 | range -= devpriv->unipolar_gain; | |
269 | } | |
270 | rangeval |= PCI171X_RANGE_GAIN(range); | |
a65e1419 HS |
271 | |
272 | /* select channel and set range */ | |
92c65e55 | 273 | outw(PCI171X_MUX_CHAN(chan), dev->iobase + PCI171X_MUX_REG); |
39363d06 | 274 | outw(rangeval, dev->iobase + PCI171X_RANGE_REG); |
a65e1419 HS |
275 | |
276 | devpriv->act_chanlist[i] = chan; | |
4fa7bbec | 277 | } |
133dfbfb HS |
278 | for ( ; i < n_chan; i++) /* store remainder of channel list */ |
279 | devpriv->act_chanlist[i] = CR_CHAN(chanlist[i]); | |
4fa7bbec | 280 | |
4fa7bbec | 281 | /* select channel interval to scan */ |
92c65e55 HS |
282 | devpriv->mux_scan = PCI171X_MUX_CHANL(first_chan) | |
283 | PCI171X_MUX_CHANH(last_chan); | |
284 | outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG); | |
4fa7bbec | 285 | } |
0e8db97a | 286 | |
e209f7cc | 287 | static int pci1710_ai_eoc(struct comedi_device *dev, |
fa3fa1de HS |
288 | struct comedi_subdevice *s, |
289 | struct comedi_insn *insn, | |
290 | unsigned long context) | |
291 | { | |
292 | unsigned int status; | |
293 | ||
d67e50fe HS |
294 | status = inw(dev->iobase + PCI171X_STATUS_REG); |
295 | if ((status & PCI171X_STATUS_FE) == 0) | |
fa3fa1de HS |
296 | return 0; |
297 | return -EBUSY; | |
298 | } | |
299 | ||
e209f7cc | 300 | static int pci1710_ai_read_sample(struct comedi_device *dev, |
7fd2dae2 HS |
301 | struct comedi_subdevice *s, |
302 | unsigned int cur_chan, | |
35c73410 | 303 | unsigned short *val) |
7fd2dae2 HS |
304 | { |
305 | const struct boardtype *board = dev->board_ptr; | |
306 | struct pci1710_private *devpriv = dev->private; | |
35c73410 | 307 | unsigned short sample; |
7fd2dae2 HS |
308 | unsigned int chan; |
309 | ||
39363d06 | 310 | sample = inw(dev->iobase + PCI171X_AD_DATA_REG); |
7fd2dae2 HS |
311 | if (!board->is_pci1713) { |
312 | /* | |
313 | * The upper 4 bits of the 16-bit sample are the channel number | |
314 | * that the sample was acquired from. Verify that this channel | |
315 | * number matches the expected channel number. | |
316 | */ | |
317 | chan = sample >> 12; | |
318 | if (chan != devpriv->act_chanlist[cur_chan]) { | |
319 | dev_err(dev->class_dev, | |
b368fa11 | 320 | "A/D data dropout: received from channel %d, expected %d\n", |
7fd2dae2 HS |
321 | chan, devpriv->act_chanlist[cur_chan]); |
322 | return -ENODATA; | |
323 | } | |
324 | } | |
325 | *val = sample & s->maxdata; | |
326 | return 0; | |
327 | } | |
328 | ||
5ce43852 | 329 | static int pci1710_ai_insn_read(struct comedi_device *dev, |
0a85b6f0 | 330 | struct comedi_subdevice *s, |
fc8e8689 HS |
331 | struct comedi_insn *insn, |
332 | unsigned int *data) | |
0e8db97a | 333 | { |
6bd65164 | 334 | struct pci1710_private *devpriv = dev->private; |
16c7eb60 HS |
335 | int ret = 0; |
336 | int i; | |
0e8db97a | 337 | |
5ce43852 HS |
338 | /* enable software trigger */ |
339 | devpriv->ctrl |= PCI171X_CTRL_SW; | |
9fcce6e2 | 340 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); |
5ce43852 | 341 | |
39363d06 HS |
342 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
343 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
0e8db97a | 344 | |
e209f7cc | 345 | pci1710_ai_setup_chanlist(dev, s, &insn->chanspec, 1, 1); |
0e8db97a | 346 | |
16c7eb60 | 347 | for (i = 0; i < insn->n; i++) { |
35c73410 | 348 | unsigned short val; |
16c7eb60 | 349 | |
39363d06 HS |
350 | /* start conversion */ |
351 | outw(0, dev->iobase + PCI171X_SOFTTRG_REG); | |
fa3fa1de | 352 | |
e209f7cc | 353 | ret = comedi_timeout(dev, s, insn, pci1710_ai_eoc, 0); |
16c7eb60 HS |
354 | if (ret) |
355 | break; | |
0e8db97a | 356 | |
e209f7cc | 357 | ret = pci1710_ai_read_sample(dev, s, 0, &val); |
16c7eb60 HS |
358 | if (ret) |
359 | break; | |
0e8db97a | 360 | |
7fd2dae2 | 361 | data[i] = val; |
0e8db97a MD |
362 | } |
363 | ||
5ce43852 HS |
364 | /* disable software trigger */ |
365 | devpriv->ctrl &= ~PCI171X_CTRL_SW; | |
366 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); | |
367 | ||
39363d06 HS |
368 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
369 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
0e8db97a | 370 | |
16c7eb60 | 371 | return ret ? ret : insn->n; |
0e8db97a MD |
372 | } |
373 | ||
adbc9ec7 | 374 | static int pci1710_ai_cancel(struct comedi_device *dev, |
4fa7bbec HS |
375 | struct comedi_subdevice *s) |
376 | { | |
4fa7bbec HS |
377 | struct pci1710_private *devpriv = dev->private; |
378 | ||
adbc9ec7 HS |
379 | /* disable A/D triggers and interrupt sources */ |
380 | devpriv->ctrl &= PCI171X_CTRL_CNT0; /* preserve counter 0 clk src */ | |
9fcce6e2 | 381 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); |
adbc9ec7 HS |
382 | |
383 | /* disable pacer */ | |
48a4f222 | 384 | comedi_8254_pacer_enable(dev->pacer, 1, 2, false); |
adbc9ec7 HS |
385 | |
386 | /* clear A/D FIFO and any pending interrutps */ | |
39363d06 HS |
387 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
388 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
4fa7bbec | 389 | |
4fa7bbec HS |
390 | return 0; |
391 | } | |
392 | ||
e2d8c43b HS |
393 | static void pci1710_handle_every_sample(struct comedi_device *dev, |
394 | struct comedi_subdevice *s) | |
0e8db97a | 395 | { |
aaf483b1 | 396 | struct comedi_cmd *cmd = &s->async->cmd; |
16c7eb60 | 397 | unsigned int status; |
35c73410 | 398 | unsigned short val; |
16c7eb60 | 399 | int ret; |
0e8db97a | 400 | |
d67e50fe HS |
401 | status = inw(dev->iobase + PCI171X_STATUS_REG); |
402 | if (status & PCI171X_STATUS_FE) { | |
16c7eb60 | 403 | dev_dbg(dev->class_dev, "A/D FIFO empty (%4x)\n", status); |
3e6cb74f | 404 | s->async->events |= COMEDI_CB_ERROR; |
0e8db97a MD |
405 | return; |
406 | } | |
d67e50fe | 407 | if (status & PCI171X_STATUS_FF) { |
96a1f91a | 408 | dev_dbg(dev->class_dev, |
16c7eb60 | 409 | "A/D FIFO Full status (Fatal Error!) (%4x)\n", status); |
3e6cb74f | 410 | s->async->events |= COMEDI_CB_ERROR; |
0e8db97a MD |
411 | return; |
412 | } | |
413 | ||
39363d06 | 414 | outb(0, dev->iobase + PCI171X_CLRINT_REG); |
0e8db97a | 415 | |
d67e50fe | 416 | for (; !(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_FE);) { |
e209f7cc | 417 | ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val); |
16c7eb60 | 418 | if (ret) { |
3e6cb74f | 419 | s->async->events |= COMEDI_CB_ERROR; |
16c7eb60 HS |
420 | break; |
421 | } | |
0e8db97a | 422 | |
a9c3a015 | 423 | comedi_buf_write_samples(s, &val, 1); |
16c7eb60 | 424 | |
f831de10 HS |
425 | if (cmd->stop_src == TRIG_COUNT && |
426 | s->async->scans_done >= cmd->stop_arg) { | |
427 | s->async->events |= COMEDI_CB_EOA; | |
428 | break; | |
0e8db97a MD |
429 | } |
430 | } | |
431 | ||
39363d06 | 432 | outb(0, dev->iobase + PCI171X_CLRINT_REG); |
0e8db97a MD |
433 | } |
434 | ||
e2d8c43b HS |
435 | static void pci1710_handle_fifo(struct comedi_device *dev, |
436 | struct comedi_subdevice *s) | |
0e8db97a | 437 | { |
7bd428c4 | 438 | struct pci1710_private *devpriv = dev->private; |
c0e4a8de HS |
439 | struct comedi_async *async = s->async; |
440 | struct comedi_cmd *cmd = &async->cmd; | |
441 | unsigned int status; | |
442 | int i; | |
0e8db97a | 443 | |
d67e50fe HS |
444 | status = inw(dev->iobase + PCI171X_STATUS_REG); |
445 | if (!(status & PCI171X_STATUS_FH)) { | |
c0e4a8de HS |
446 | dev_dbg(dev->class_dev, "A/D FIFO not half full!\n"); |
447 | async->events |= COMEDI_CB_ERROR; | |
0e8db97a MD |
448 | return; |
449 | } | |
d67e50fe | 450 | if (status & PCI171X_STATUS_FF) { |
96a1f91a | 451 | dev_dbg(dev->class_dev, |
c0e4a8de HS |
452 | "A/D FIFO Full status (Fatal Error!)\n"); |
453 | async->events |= COMEDI_CB_ERROR; | |
0e8db97a MD |
454 | return; |
455 | } | |
456 | ||
c0e4a8de | 457 | for (i = 0; i < devpriv->max_samples; i++) { |
35c73410 | 458 | unsigned short val; |
c0e4a8de | 459 | int ret; |
0e8db97a | 460 | |
e209f7cc | 461 | ret = pci1710_ai_read_sample(dev, s, s->async->cur_chan, &val); |
c0e4a8de HS |
462 | if (ret) { |
463 | s->async->events |= COMEDI_CB_ERROR; | |
464 | break; | |
465 | } | |
466 | ||
467 | if (!comedi_buf_write_samples(s, &val, 1)) | |
468 | break; | |
469 | ||
470 | if (cmd->stop_src == TRIG_COUNT && | |
471 | async->scans_done >= cmd->stop_arg) { | |
472 | async->events |= COMEDI_CB_EOA; | |
473 | break; | |
474 | } | |
b4720286 | 475 | } |
0e8db97a | 476 | |
39363d06 | 477 | outb(0, dev->iobase + PCI171X_CLRINT_REG); |
0e8db97a MD |
478 | } |
479 | ||
96d57c15 | 480 | static irqreturn_t pci1710_irq_handler(int irq, void *d) |
0e8db97a | 481 | { |
71b5f4f1 | 482 | struct comedi_device *dev = d; |
6bd65164 | 483 | struct pci1710_private *devpriv = dev->private; |
5297a6ba HS |
484 | struct comedi_subdevice *s; |
485 | struct comedi_cmd *cmd; | |
0e8db97a | 486 | |
2696fb57 BP |
487 | if (!dev->attached) /* is device attached? */ |
488 | return IRQ_NONE; /* no, exit */ | |
5297a6ba HS |
489 | |
490 | s = dev->read_subdev; | |
491 | cmd = &s->async->cmd; | |
492 | ||
ed7dcb47 | 493 | /* is this interrupt from our board? */ |
d67e50fe | 494 | if (!(inw(dev->iobase + PCI171X_STATUS_REG) & PCI171X_STATUS_IRQ)) |
2696fb57 | 495 | return IRQ_NONE; /* no, exit */ |
0e8db97a | 496 | |
2696fb57 | 497 | if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */ |
0e8db97a | 498 | devpriv->ai_et = 0; |
9fcce6e2 HS |
499 | devpriv->ctrl &= PCI171X_CTRL_CNT0; |
500 | devpriv->ctrl |= PCI171X_CTRL_SW; /* set software trigger */ | |
501 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); | |
502 | devpriv->ctrl = devpriv->ctrl_ext; | |
39363d06 HS |
503 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
504 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
fff5b04f | 505 | /* no sample on this interrupt; reset the channel interval */ |
92c65e55 | 506 | outw(devpriv->mux_scan, dev->iobase + PCI171X_MUX_REG); |
9fcce6e2 | 507 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); |
48a4f222 | 508 | comedi_8254_pacer_enable(dev->pacer, 1, 2, true); |
0e8db97a MD |
509 | return IRQ_HANDLED; |
510 | } | |
5297a6ba | 511 | |
3e609aff | 512 | if (cmd->flags & CMDF_WAKE_EOS) |
e2d8c43b | 513 | pci1710_handle_every_sample(dev, s); |
5297a6ba | 514 | else |
e2d8c43b | 515 | pci1710_handle_fifo(dev, s); |
5297a6ba | 516 | |
d61b786a HS |
517 | comedi_handle_events(dev, s); |
518 | ||
0e8db97a MD |
519 | return IRQ_HANDLED; |
520 | } | |
521 | ||
6f05ce9c | 522 | static int pci1710_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) |
0e8db97a | 523 | { |
6bd65164 | 524 | struct pci1710_private *devpriv = dev->private; |
cd0164e6 | 525 | struct comedi_cmd *cmd = &s->async->cmd; |
0e8db97a | 526 | |
e209f7cc | 527 | pci1710_ai_setup_chanlist(dev, s, cmd->chanlist, cmd->chanlist_len, |
a65e1419 | 528 | devpriv->saved_seglen); |
0e8db97a | 529 | |
39363d06 HS |
530 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
531 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
0e8db97a | 532 | |
9fcce6e2 | 533 | devpriv->ctrl &= PCI171X_CTRL_CNT0; |
3e609aff | 534 | if ((cmd->flags & CMDF_WAKE_EOS) == 0) |
9fcce6e2 | 535 | devpriv->ctrl |= PCI171X_CTRL_ONEFH; |
0e8db97a | 536 | |
33573343 | 537 | if (cmd->convert_src == TRIG_TIMER) { |
48a4f222 HS |
538 | comedi_8254_update_divisors(dev->pacer); |
539 | ||
9fcce6e2 | 540 | devpriv->ctrl |= PCI171X_CTRL_PACER | PCI171X_CTRL_IRQEN; |
33573343 | 541 | if (cmd->start_src == TRIG_EXT) { |
9fcce6e2 HS |
542 | devpriv->ctrl_ext = devpriv->ctrl; |
543 | devpriv->ctrl &= ~(PCI171X_CTRL_PACER | | |
544 | PCI171X_CTRL_ONEFH | | |
545 | PCI171X_CTRL_GATE); | |
546 | devpriv->ctrl |= PCI171X_CTRL_EXT; | |
0e8db97a | 547 | devpriv->ai_et = 1; |
33573343 | 548 | } else { /* TRIG_NOW */ |
0e8db97a MD |
549 | devpriv->ai_et = 0; |
550 | } | |
9fcce6e2 | 551 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); |
33573343 | 552 | |
925ddefc | 553 | if (cmd->start_src == TRIG_NOW) |
48a4f222 | 554 | comedi_8254_pacer_enable(dev->pacer, 1, 2, true); |
33573343 | 555 | } else { /* TRIG_EXT */ |
9fcce6e2 HS |
556 | devpriv->ctrl |= PCI171X_CTRL_EXT | PCI171X_CTRL_IRQEN; |
557 | outw(devpriv->ctrl, dev->iobase + PCI171X_CTRL_REG); | |
0e8db97a MD |
558 | } |
559 | ||
0e8db97a MD |
560 | return 0; |
561 | } | |
562 | ||
6f05ce9c | 563 | static int pci1710_ai_cmdtest(struct comedi_device *dev, |
0a85b6f0 MT |
564 | struct comedi_subdevice *s, |
565 | struct comedi_cmd *cmd) | |
0e8db97a MD |
566 | { |
567 | int err = 0; | |
0e8db97a | 568 | |
27020ffe | 569 | /* Step 1 : check if triggers are trivially valid */ |
0e8db97a | 570 | |
3087f440 IA |
571 | err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); |
572 | err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); | |
573 | err |= comedi_check_trigger_src(&cmd->convert_src, | |
574 | TRIG_TIMER | TRIG_EXT); | |
575 | err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); | |
576 | err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); | |
0e8db97a | 577 | |
f5f9a3ff | 578 | if (err) |
0e8db97a | 579 | return 1; |
0e8db97a | 580 | |
b7f16de6 | 581 | /* step 2a: make sure trigger sources are unique */ |
0e8db97a | 582 | |
3087f440 IA |
583 | err |= comedi_check_trigger_is_unique(cmd->start_src); |
584 | err |= comedi_check_trigger_is_unique(cmd->convert_src); | |
585 | err |= comedi_check_trigger_is_unique(cmd->stop_src); | |
0e8db97a | 586 | |
b7f16de6 | 587 | /* step 2b: and mutually compatible */ |
0e8db97a | 588 | |
f5f9a3ff | 589 | if (err) |
0e8db97a | 590 | return 2; |
0e8db97a | 591 | |
ad23feaa | 592 | /* Step 3: check if arguments are trivially valid */ |
0e8db97a | 593 | |
3087f440 IA |
594 | err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); |
595 | err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); | |
0e8db97a | 596 | |
ad23feaa | 597 | if (cmd->convert_src == TRIG_TIMER) |
3087f440 | 598 | err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); |
ad23feaa | 599 | else /* TRIG_FOLLOW */ |
3087f440 | 600 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0); |
0e8db97a | 601 | |
3087f440 IA |
602 | err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, |
603 | cmd->chanlist_len); | |
0e8db97a | 604 | |
ad23feaa | 605 | if (cmd->stop_src == TRIG_COUNT) |
3087f440 | 606 | err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); |
ad23feaa | 607 | else /* TRIG_NONE */ |
3087f440 | 608 | err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); |
0e8db97a | 609 | |
f5f9a3ff | 610 | if (err) |
0e8db97a | 611 | return 3; |
0e8db97a MD |
612 | |
613 | /* step 4: fix up any arguments */ | |
614 | ||
615 | if (cmd->convert_src == TRIG_TIMER) { | |
48a4f222 HS |
616 | unsigned int arg = cmd->convert_arg; |
617 | ||
618 | comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags); | |
3087f440 | 619 | err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); |
0e8db97a MD |
620 | } |
621 | ||
f5f9a3ff | 622 | if (err) |
0e8db97a | 623 | return 4; |
0e8db97a | 624 | |
7cc054d0 | 625 | /* Step 5: check channel list */ |
0e8db97a | 626 | |
e209f7cc | 627 | err |= pci1710_ai_check_chanlist(dev, s, cmd); |
56556577 HS |
628 | |
629 | if (err) | |
7cc054d0 | 630 | return 5; |
0e8db97a | 631 | |
0e8db97a MD |
632 | return 0; |
633 | } | |
634 | ||
baacf6ca HS |
635 | static int pci1710_ao_insn_write(struct comedi_device *dev, |
636 | struct comedi_subdevice *s, | |
637 | struct comedi_insn *insn, | |
638 | unsigned int *data) | |
639 | { | |
640 | struct pci1710_private *devpriv = dev->private; | |
641 | unsigned int chan = CR_CHAN(insn->chanspec); | |
642 | unsigned int range = CR_RANGE(insn->chanspec); | |
643 | unsigned int val = s->readback[chan]; | |
644 | int i; | |
645 | ||
976e893b HS |
646 | devpriv->da_ranges &= ~PCI171X_DAREF_MASK(chan); |
647 | devpriv->da_ranges |= PCI171X_DAREF(chan, range); | |
baacf6ca HS |
648 | outw(devpriv->da_ranges, dev->iobase + PCI171X_DAREF_REG); |
649 | ||
650 | for (i = 0; i < insn->n; i++) { | |
651 | val = data[i]; | |
652 | outw(val, dev->iobase + PCI171X_DA_REG(chan)); | |
653 | } | |
654 | ||
655 | s->readback[chan] = val; | |
656 | ||
657 | return insn->n; | |
658 | } | |
659 | ||
60392786 HS |
660 | static int pci1710_di_insn_bits(struct comedi_device *dev, |
661 | struct comedi_subdevice *s, | |
662 | struct comedi_insn *insn, | |
663 | unsigned int *data) | |
664 | { | |
665 | data[1] = inw(dev->iobase + PCI171X_DI_REG); | |
666 | ||
667 | return insn->n; | |
668 | } | |
669 | ||
670 | static int pci1710_do_insn_bits(struct comedi_device *dev, | |
671 | struct comedi_subdevice *s, | |
672 | struct comedi_insn *insn, | |
673 | unsigned int *data) | |
674 | { | |
675 | if (comedi_dio_update_state(s, data)) | |
676 | outw(s->state, dev->iobase + PCI171X_DO_REG); | |
677 | ||
678 | data[1] = s->state; | |
679 | ||
680 | return insn->n; | |
681 | } | |
682 | ||
f1f4ce64 | 683 | static int pci1710_counter_insn_config(struct comedi_device *dev, |
48a4f222 HS |
684 | struct comedi_subdevice *s, |
685 | struct comedi_insn *insn, | |
686 | unsigned int *data) | |
687 | { | |
688 | struct pci1710_private *devpriv = dev->private; | |
689 | ||
690 | switch (data[0]) { | |
691 | case INSN_CONFIG_SET_CLOCK_SRC: | |
692 | switch (data[1]) { | |
693 | case 0: /* internal */ | |
9fcce6e2 | 694 | devpriv->ctrl_ext &= ~PCI171X_CTRL_CNT0; |
48a4f222 HS |
695 | break; |
696 | case 1: /* external */ | |
9fcce6e2 | 697 | devpriv->ctrl_ext |= PCI171X_CTRL_CNT0; |
48a4f222 HS |
698 | break; |
699 | default: | |
700 | return -EINVAL; | |
701 | } | |
9fcce6e2 | 702 | outw(devpriv->ctrl_ext, dev->iobase + PCI171X_CTRL_REG); |
48a4f222 HS |
703 | break; |
704 | case INSN_CONFIG_GET_CLOCK_SRC: | |
9fcce6e2 | 705 | if (devpriv->ctrl_ext & PCI171X_CTRL_CNT0) { |
48a4f222 HS |
706 | data[1] = 1; |
707 | data[2] = 0; | |
708 | } else { | |
709 | data[1] = 0; | |
d0445303 | 710 | data[2] = I8254_OSC_BASE_1MHZ; |
48a4f222 HS |
711 | } |
712 | break; | |
713 | default: | |
714 | return -EINVAL; | |
715 | } | |
716 | ||
717 | return insn->n; | |
718 | } | |
719 | ||
0c917a93 | 720 | static void pci1710_reset(struct comedi_device *dev) |
0e8db97a | 721 | { |
383fab40 | 722 | const struct boardtype *board = dev->board_ptr; |
6bd65164 | 723 | |
0c917a93 HS |
724 | /* |
725 | * Disable A/D triggers and interrupt sources, set counter 0 | |
726 | * to use internal 1 MHz clock. | |
727 | */ | |
728 | outw(0, dev->iobase + PCI171X_CTRL_REG); | |
729 | ||
730 | /* clear A/D FIFO and any pending interrutps */ | |
39363d06 HS |
731 | outb(0, dev->iobase + PCI171X_CLRFIFO_REG); |
732 | outb(0, dev->iobase + PCI171X_CLRINT_REG); | |
0c917a93 | 733 | |
383fab40 | 734 | if (board->has_ao) { |
39363d06 | 735 | /* set DACs to 0..5V and outputs to 0V */ |
0c917a93 | 736 | outb(0, dev->iobase + PCI171X_DAREF_REG); |
39363d06 HS |
737 | outw(0, dev->iobase + PCI171X_DA_REG(0)); |
738 | outw(0, dev->iobase + PCI171X_DA_REG(1)); | |
0e8db97a | 739 | } |
0e8db97a | 740 | |
0c917a93 HS |
741 | /* set digital outputs to 0 */ |
742 | outw(0, dev->iobase + PCI171X_DO_REG); | |
0e8db97a MD |
743 | } |
744 | ||
a690b7e5 | 745 | static int pci1710_auto_attach(struct comedi_device *dev, |
0005fbed | 746 | unsigned long context) |
96554c81 | 747 | { |
750af5e5 | 748 | struct pci_dev *pcidev = comedi_to_pci_dev(dev); |
383fab40 | 749 | const struct boardtype *board = NULL; |
6bd65164 | 750 | struct pci1710_private *devpriv; |
96554c81 HS |
751 | struct comedi_subdevice *s; |
752 | int ret, subdev, n_subdevices; | |
7603900f | 753 | int i; |
f62608e3 | 754 | |
0005fbed | 755 | if (context < ARRAY_SIZE(boardtypes)) |
383fab40 HS |
756 | board = &boardtypes[context]; |
757 | if (!board) | |
f62608e3 | 758 | return -ENODEV; |
383fab40 HS |
759 | dev->board_ptr = board; |
760 | dev->board_name = board->name; | |
96554c81 | 761 | |
0bdab509 | 762 | devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); |
c34fa261 HS |
763 | if (!devpriv) |
764 | return -ENOMEM; | |
96554c81 | 765 | |
818f569f | 766 | ret = comedi_pci_enable(dev); |
5f5d1b9a HS |
767 | if (ret) |
768 | return ret; | |
ad37c85c | 769 | dev->iobase = pci_resource_start(pcidev, 2); |
0e8db97a | 770 | |
48a4f222 HS |
771 | dev->pacer = comedi_8254_init(dev->iobase + PCI171X_TIMER_BASE, |
772 | I8254_OSC_BASE_10MHZ, I8254_IO16, 0); | |
773 | if (!dev->pacer) | |
774 | return -ENOMEM; | |
775 | ||
0b458e73 | 776 | n_subdevices = 1; /* all boards have analog inputs */ |
383fab40 | 777 | if (board->has_ao) |
0e8db97a | 778 | n_subdevices++; |
88601533 HS |
779 | if (!board->is_pci1713) { |
780 | /* | |
781 | * All other boards have digital inputs and outputs as | |
782 | * well as a user counter. | |
783 | */ | |
784 | n_subdevices += 3; | |
785 | } | |
0e8db97a | 786 | |
2f0b9d08 | 787 | ret = comedi_alloc_subdevices(dev, n_subdevices); |
8b6c5694 | 788 | if (ret) |
0e8db97a | 789 | return ret; |
0e8db97a MD |
790 | |
791 | pci1710_reset(dev); | |
792 | ||
6fa60dd4 | 793 | if (pcidev->irq) { |
96d57c15 | 794 | ret = request_irq(pcidev->irq, pci1710_irq_handler, |
f62608e3 HS |
795 | IRQF_SHARED, dev->board_name, dev); |
796 | if (ret == 0) | |
797 | dev->irq = pcidev->irq; | |
0e8db97a MD |
798 | } |
799 | ||
0e8db97a MD |
800 | subdev = 0; |
801 | ||
0b458e73 | 802 | /* Analog Input subdevice */ |
d3e8ab48 | 803 | s = &dev->subdevices[subdev++]; |
0b458e73 | 804 | s->type = COMEDI_SUBD_AI; |
73873325 | 805 | s->subdev_flags = SDF_READABLE | SDF_GROUND; |
dbdb6248 | 806 | if (!board->is_pci1711) |
0b458e73 HS |
807 | s->subdev_flags |= SDF_DIFF; |
808 | s->n_chan = board->is_pci1713 ? 32 : 16; | |
809 | s->maxdata = 0x0fff; | |
b5b147dc | 810 | s->range_table = board->ai_range; |
5ce43852 | 811 | s->insn_read = pci1710_ai_insn_read; |
0b458e73 HS |
812 | if (dev->irq) { |
813 | dev->read_subdev = s; | |
814 | s->subdev_flags |= SDF_CMD_READ; | |
815 | s->len_chanlist = s->n_chan; | |
6f05ce9c HS |
816 | s->do_cmdtest = pci1710_ai_cmdtest; |
817 | s->do_cmd = pci1710_ai_cmd; | |
adbc9ec7 | 818 | s->cancel = pci1710_ai_cancel; |
0b458e73 | 819 | } |
7603900f | 820 | |
0b458e73 HS |
821 | /* find the value needed to adjust for unipolar gain codes */ |
822 | for (i = 0; i < s->range_table->length; i++) { | |
823 | if (comedi_range_is_unipolar(s, i)) { | |
824 | devpriv->unipolar_gain = i; | |
825 | break; | |
7603900f | 826 | } |
0e8db97a MD |
827 | } |
828 | ||
383fab40 | 829 | if (board->has_ao) { |
1ef6e0a4 | 830 | /* Analog Output subdevice */ |
d3e8ab48 | 831 | s = &dev->subdevices[subdev++]; |
b420eeaf | 832 | s->type = COMEDI_SUBD_AO; |
1ef6e0a4 | 833 | s->subdev_flags = SDF_WRITABLE | SDF_GROUND; |
b2cb0686 | 834 | s->n_chan = 2; |
b420eeaf | 835 | s->maxdata = 0x0fff; |
b2cb0686 | 836 | s->range_table = &pci171x_ao_range; |
baacf6ca | 837 | s->insn_write = pci1710_ao_insn_write; |
e4623cee HS |
838 | |
839 | ret = comedi_alloc_subdev_readback(s); | |
840 | if (ret) | |
841 | return ret; | |
0e8db97a MD |
842 | } |
843 | ||
88601533 HS |
844 | if (!board->is_pci1713) { |
845 | /* Digital Input subdevice */ | |
d3e8ab48 | 846 | s = &dev->subdevices[subdev++]; |
1fbe6e91 HS |
847 | s->type = COMEDI_SUBD_DI; |
848 | s->subdev_flags = SDF_READABLE; | |
849 | s->n_chan = 16; | |
850 | s->maxdata = 1; | |
851 | s->range_table = &range_digital; | |
60392786 | 852 | s->insn_bits = pci1710_di_insn_bits; |
0e8db97a | 853 | |
88601533 | 854 | /* Digital Output subdevice */ |
d3e8ab48 | 855 | s = &dev->subdevices[subdev++]; |
1fbe6e91 HS |
856 | s->type = COMEDI_SUBD_DO; |
857 | s->subdev_flags = SDF_WRITABLE; | |
858 | s->n_chan = 16; | |
859 | s->maxdata = 1; | |
860 | s->range_table = &range_digital; | |
60392786 | 861 | s->insn_bits = pci1710_do_insn_bits; |
0e8db97a | 862 | |
8a8d0875 | 863 | /* Counter subdevice (8254) */ |
d3e8ab48 | 864 | s = &dev->subdevices[subdev++]; |
48a4f222 HS |
865 | comedi_8254_subdevice_init(s, dev->pacer); |
866 | ||
f1f4ce64 | 867 | dev->pacer->insn_config = pci1710_counter_insn_config; |
48a4f222 HS |
868 | |
869 | /* counters 1 and 2 are used internally for the pacer */ | |
870 | comedi_8254_set_busy(dev->pacer, 1, true); | |
871 | comedi_8254_set_busy(dev->pacer, 2, true); | |
0e8db97a MD |
872 | } |
873 | ||
7bd428c4 | 874 | /* max_samples is half the FIFO size (2 bytes/sample) */ |
dbdb6248 | 875 | devpriv->max_samples = (board->is_pci1711) ? 512 : 2048; |
7bd428c4 | 876 | |
0e8db97a MD |
877 | return 0; |
878 | } | |
879 | ||
958c5989 HS |
880 | static struct comedi_driver adv_pci1710_driver = { |
881 | .driver_name = "adv_pci1710", | |
882 | .module = THIS_MODULE, | |
750af5e5 | 883 | .auto_attach = pci1710_auto_attach, |
19cbb8fb | 884 | .detach = comedi_pci_detach, |
958c5989 HS |
885 | }; |
886 | ||
a690b7e5 | 887 | static int adv_pci1710_pci_probe(struct pci_dev *dev, |
b8f4ac23 | 888 | const struct pci_device_id *id) |
727b286b | 889 | { |
b8f4ac23 HS |
890 | return comedi_pci_auto_config(dev, &adv_pci1710_driver, |
891 | id->driver_data); | |
727b286b AT |
892 | } |
893 | ||
41e043fc | 894 | static const struct pci_device_id adv_pci1710_pci_table[] = { |
94cc409b IA |
895 | { |
896 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
897 | PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), | |
898 | .driver_data = BOARD_PCI1710, | |
899 | }, { | |
900 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
901 | PCI_VENDOR_ID_ADVANTECH, 0x0000), | |
902 | .driver_data = BOARD_PCI1710, | |
903 | }, { | |
904 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
905 | PCI_VENDOR_ID_ADVANTECH, 0xb100), | |
906 | .driver_data = BOARD_PCI1710, | |
907 | }, { | |
908 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
909 | PCI_VENDOR_ID_ADVANTECH, 0xb200), | |
910 | .driver_data = BOARD_PCI1710, | |
911 | }, { | |
912 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
913 | PCI_VENDOR_ID_ADVANTECH, 0xc100), | |
914 | .driver_data = BOARD_PCI1710, | |
915 | }, { | |
916 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
917 | PCI_VENDOR_ID_ADVANTECH, 0xc200), | |
918 | .driver_data = BOARD_PCI1710, | |
919 | }, { | |
920 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd100), | |
921 | .driver_data = BOARD_PCI1710, | |
922 | }, { | |
923 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
924 | PCI_VENDOR_ID_ADVANTECH, 0x0002), | |
925 | .driver_data = BOARD_PCI1710HG, | |
926 | }, { | |
927 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
928 | PCI_VENDOR_ID_ADVANTECH, 0xb102), | |
929 | .driver_data = BOARD_PCI1710HG, | |
930 | }, { | |
931 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
932 | PCI_VENDOR_ID_ADVANTECH, 0xb202), | |
933 | .driver_data = BOARD_PCI1710HG, | |
934 | }, { | |
935 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
936 | PCI_VENDOR_ID_ADVANTECH, 0xc102), | |
937 | .driver_data = BOARD_PCI1710HG, | |
938 | }, { | |
939 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, | |
940 | PCI_VENDOR_ID_ADVANTECH, 0xc202), | |
941 | .driver_data = BOARD_PCI1710HG, | |
942 | }, { | |
943 | PCI_DEVICE_SUB(PCI_VENDOR_ID_ADVANTECH, 0x1710, 0x1000, 0xd102), | |
944 | .driver_data = BOARD_PCI1710HG, | |
945 | }, | |
0005fbed HS |
946 | { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 }, |
947 | { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 }, | |
0005fbed | 948 | { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 }, |
958c5989 | 949 | { 0 } |
727b286b | 950 | }; |
958c5989 | 951 | MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table); |
727b286b | 952 | |
958c5989 HS |
953 | static struct pci_driver adv_pci1710_pci_driver = { |
954 | .name = "adv_pci1710", | |
955 | .id_table = adv_pci1710_pci_table, | |
956 | .probe = adv_pci1710_pci_probe, | |
9901a4d7 | 957 | .remove = comedi_pci_auto_unconfig, |
958c5989 HS |
958 | }; |
959 | module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver); | |
90f703d3 | 960 | |
d761e90e | 961 | MODULE_AUTHOR("Comedi https://www.comedi.org"); |
76201d05 | 962 | MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards"); |
90f703d3 | 963 | MODULE_LICENSE("GPL"); |