]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/staging/comedi/drivers/adv_pci1710.c
staging: comedi: adv_pci1710: Fix endian problem for AI command data
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / adv_pci1710.c
CommitLineData
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 75static 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 89static 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 106static 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 116static 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
124enum 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 132struct 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 140static 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 169struct 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 181static 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 247static 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 287static 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 300static 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 329static 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 374static 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
393static 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
435static 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 480static 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 522static 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 563static 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
635static 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
660static 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
670static 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 683static 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 720static 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 745static 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
880static 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 887static 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 894static 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 951MODULE_DEVICE_TABLE(pci, adv_pci1710_pci_table);
727b286b 952
958c5989
HS
953static 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};
959module_comedi_pci_driver(adv_pci1710_driver, adv_pci1710_pci_driver);
90f703d3 960
d761e90e 961MODULE_AUTHOR("Comedi https://www.comedi.org");
76201d05 962MODULE_DESCRIPTION("Comedi: Advantech PCI-1710 Series Multifunction DAS Cards");
90f703d3 963MODULE_LICENSE("GPL");