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