1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * addi_apci_3xxx.c 4 * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. 5 * Project manager: S. Weber 6 * 7 * ADDI-DATA GmbH 8 * Dieselstrasse 3 9 * D-77833 Ottersweier 10 * Tel: +19(0)7223/9493-0 11 * Fax: +49(0)7223/9493-92 12 * http://www.addi-data.com 13 * info@addi-data.com 14 */ 15 16 #include <linux/module.h> 17 #include <linux/interrupt.h> 18 #include <linux/comedi/comedi_pci.h> 19 20 #define CONV_UNIT_NS BIT(0) 21 #define CONV_UNIT_US BIT(1) 22 #define CONV_UNIT_MS BIT(2) 23 24 static const struct comedi_lrange apci3xxx_ai_range = { 25 8, { 26 BIP_RANGE(10), 27 BIP_RANGE(5), 28 BIP_RANGE(2), 29 BIP_RANGE(1), 30 UNI_RANGE(10), 31 UNI_RANGE(5), 32 UNI_RANGE(2), 33 UNI_RANGE(1) 34 } 35 }; 36 37 static const struct comedi_lrange apci3xxx_ao_range = { 38 2, { 39 BIP_RANGE(10), 40 UNI_RANGE(10) 41 } 42 }; 43 44 enum apci3xxx_boardid { 45 BOARD_APCI3000_16, 46 BOARD_APCI3000_8, 47 BOARD_APCI3000_4, 48 BOARD_APCI3006_16, 49 BOARD_APCI3006_8, 50 BOARD_APCI3006_4, 51 BOARD_APCI3010_16, 52 BOARD_APCI3010_8, 53 BOARD_APCI3010_4, 54 BOARD_APCI3016_16, 55 BOARD_APCI3016_8, 56 BOARD_APCI3016_4, 57 BOARD_APCI3100_16_4, 58 BOARD_APCI3100_8_4, 59 BOARD_APCI3106_16_4, 60 BOARD_APCI3106_8_4, 61 BOARD_APCI3110_16_4, 62 BOARD_APCI3110_8_4, 63 BOARD_APCI3116_16_4, 64 BOARD_APCI3116_8_4, 65 BOARD_APCI3003, 66 BOARD_APCI3002_16, 67 BOARD_APCI3002_8, 68 BOARD_APCI3002_4, 69 BOARD_APCI3500, 70 }; 71 72 struct apci3xxx_boardinfo { 73 const char *name; 74 int ai_subdev_flags; 75 int ai_n_chan; 76 unsigned int ai_maxdata; 77 unsigned char ai_conv_units; 78 unsigned int ai_min_acq_ns; 79 unsigned int has_ao:1; 80 unsigned int has_dig_in:1; 81 unsigned int has_dig_out:1; 82 unsigned int has_ttl_io:1; 83 }; 84 85 static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = { 86 [BOARD_APCI3000_16] = { 87 .name = "apci3000-16", 88 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 89 .ai_n_chan = 16, 90 .ai_maxdata = 0x0fff, 91 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 92 .ai_min_acq_ns = 10000, 93 .has_ttl_io = 1, 94 }, 95 [BOARD_APCI3000_8] = { 96 .name = "apci3000-8", 97 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 98 .ai_n_chan = 8, 99 .ai_maxdata = 0x0fff, 100 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 101 .ai_min_acq_ns = 10000, 102 .has_ttl_io = 1, 103 }, 104 [BOARD_APCI3000_4] = { 105 .name = "apci3000-4", 106 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 107 .ai_n_chan = 4, 108 .ai_maxdata = 0x0fff, 109 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 110 .ai_min_acq_ns = 10000, 111 .has_ttl_io = 1, 112 }, 113 [BOARD_APCI3006_16] = { 114 .name = "apci3006-16", 115 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 116 .ai_n_chan = 16, 117 .ai_maxdata = 0xffff, 118 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 119 .ai_min_acq_ns = 10000, 120 .has_ttl_io = 1, 121 }, 122 [BOARD_APCI3006_8] = { 123 .name = "apci3006-8", 124 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 125 .ai_n_chan = 8, 126 .ai_maxdata = 0xffff, 127 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 128 .ai_min_acq_ns = 10000, 129 .has_ttl_io = 1, 130 }, 131 [BOARD_APCI3006_4] = { 132 .name = "apci3006-4", 133 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 134 .ai_n_chan = 4, 135 .ai_maxdata = 0xffff, 136 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 137 .ai_min_acq_ns = 10000, 138 .has_ttl_io = 1, 139 }, 140 [BOARD_APCI3010_16] = { 141 .name = "apci3010-16", 142 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 143 .ai_n_chan = 16, 144 .ai_maxdata = 0x0fff, 145 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 146 .ai_min_acq_ns = 5000, 147 .has_dig_in = 1, 148 .has_dig_out = 1, 149 .has_ttl_io = 1, 150 }, 151 [BOARD_APCI3010_8] = { 152 .name = "apci3010-8", 153 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 154 .ai_n_chan = 8, 155 .ai_maxdata = 0x0fff, 156 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 157 .ai_min_acq_ns = 5000, 158 .has_dig_in = 1, 159 .has_dig_out = 1, 160 .has_ttl_io = 1, 161 }, 162 [BOARD_APCI3010_4] = { 163 .name = "apci3010-4", 164 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 165 .ai_n_chan = 4, 166 .ai_maxdata = 0x0fff, 167 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 168 .ai_min_acq_ns = 5000, 169 .has_dig_in = 1, 170 .has_dig_out = 1, 171 .has_ttl_io = 1, 172 }, 173 [BOARD_APCI3016_16] = { 174 .name = "apci3016-16", 175 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 176 .ai_n_chan = 16, 177 .ai_maxdata = 0xffff, 178 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 179 .ai_min_acq_ns = 5000, 180 .has_dig_in = 1, 181 .has_dig_out = 1, 182 .has_ttl_io = 1, 183 }, 184 [BOARD_APCI3016_8] = { 185 .name = "apci3016-8", 186 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 187 .ai_n_chan = 8, 188 .ai_maxdata = 0xffff, 189 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 190 .ai_min_acq_ns = 5000, 191 .has_dig_in = 1, 192 .has_dig_out = 1, 193 .has_ttl_io = 1, 194 }, 195 [BOARD_APCI3016_4] = { 196 .name = "apci3016-4", 197 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 198 .ai_n_chan = 4, 199 .ai_maxdata = 0xffff, 200 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 201 .ai_min_acq_ns = 5000, 202 .has_dig_in = 1, 203 .has_dig_out = 1, 204 .has_ttl_io = 1, 205 }, 206 [BOARD_APCI3100_16_4] = { 207 .name = "apci3100-16-4", 208 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 209 .ai_n_chan = 16, 210 .ai_maxdata = 0x0fff, 211 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 212 .ai_min_acq_ns = 10000, 213 .has_ao = 1, 214 .has_ttl_io = 1, 215 }, 216 [BOARD_APCI3100_8_4] = { 217 .name = "apci3100-8-4", 218 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 219 .ai_n_chan = 8, 220 .ai_maxdata = 0x0fff, 221 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 222 .ai_min_acq_ns = 10000, 223 .has_ao = 1, 224 .has_ttl_io = 1, 225 }, 226 [BOARD_APCI3106_16_4] = { 227 .name = "apci3106-16-4", 228 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 229 .ai_n_chan = 16, 230 .ai_maxdata = 0xffff, 231 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 232 .ai_min_acq_ns = 10000, 233 .has_ao = 1, 234 .has_ttl_io = 1, 235 }, 236 [BOARD_APCI3106_8_4] = { 237 .name = "apci3106-8-4", 238 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 239 .ai_n_chan = 8, 240 .ai_maxdata = 0xffff, 241 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 242 .ai_min_acq_ns = 10000, 243 .has_ao = 1, 244 .has_ttl_io = 1, 245 }, 246 [BOARD_APCI3110_16_4] = { 247 .name = "apci3110-16-4", 248 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 249 .ai_n_chan = 16, 250 .ai_maxdata = 0x0fff, 251 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 252 .ai_min_acq_ns = 5000, 253 .has_ao = 1, 254 .has_dig_in = 1, 255 .has_dig_out = 1, 256 .has_ttl_io = 1, 257 }, 258 [BOARD_APCI3110_8_4] = { 259 .name = "apci3110-8-4", 260 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 261 .ai_n_chan = 8, 262 .ai_maxdata = 0x0fff, 263 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 264 .ai_min_acq_ns = 5000, 265 .has_ao = 1, 266 .has_dig_in = 1, 267 .has_dig_out = 1, 268 .has_ttl_io = 1, 269 }, 270 [BOARD_APCI3116_16_4] = { 271 .name = "apci3116-16-4", 272 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 273 .ai_n_chan = 16, 274 .ai_maxdata = 0xffff, 275 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 276 .ai_min_acq_ns = 5000, 277 .has_ao = 1, 278 .has_dig_in = 1, 279 .has_dig_out = 1, 280 .has_ttl_io = 1, 281 }, 282 [BOARD_APCI3116_8_4] = { 283 .name = "apci3116-8-4", 284 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 285 .ai_n_chan = 8, 286 .ai_maxdata = 0xffff, 287 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 288 .ai_min_acq_ns = 5000, 289 .has_ao = 1, 290 .has_dig_in = 1, 291 .has_dig_out = 1, 292 .has_ttl_io = 1, 293 }, 294 [BOARD_APCI3003] = { 295 .name = "apci3003", 296 .ai_subdev_flags = SDF_DIFF, 297 .ai_n_chan = 4, 298 .ai_maxdata = 0xffff, 299 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US | 300 CONV_UNIT_NS, 301 .ai_min_acq_ns = 2500, 302 .has_dig_in = 1, 303 .has_dig_out = 1, 304 }, 305 [BOARD_APCI3002_16] = { 306 .name = "apci3002-16", 307 .ai_subdev_flags = SDF_DIFF, 308 .ai_n_chan = 16, 309 .ai_maxdata = 0xffff, 310 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 311 .ai_min_acq_ns = 5000, 312 .has_dig_in = 1, 313 .has_dig_out = 1, 314 }, 315 [BOARD_APCI3002_8] = { 316 .name = "apci3002-8", 317 .ai_subdev_flags = SDF_DIFF, 318 .ai_n_chan = 8, 319 .ai_maxdata = 0xffff, 320 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 321 .ai_min_acq_ns = 5000, 322 .has_dig_in = 1, 323 .has_dig_out = 1, 324 }, 325 [BOARD_APCI3002_4] = { 326 .name = "apci3002-4", 327 .ai_subdev_flags = SDF_DIFF, 328 .ai_n_chan = 4, 329 .ai_maxdata = 0xffff, 330 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 331 .ai_min_acq_ns = 5000, 332 .has_dig_in = 1, 333 .has_dig_out = 1, 334 }, 335 [BOARD_APCI3500] = { 336 .name = "apci3500", 337 .has_ao = 1, 338 .has_ttl_io = 1, 339 }, 340 }; 341 342 struct apci3xxx_private { 343 unsigned int ai_timer; 344 unsigned char ai_time_base; 345 }; 346 347 static irqreturn_t apci3xxx_irq_handler(int irq, void *d) 348 { 349 struct comedi_device *dev = d; 350 struct comedi_subdevice *s = dev->read_subdev; 351 unsigned int status; 352 unsigned int val; 353 354 /* Test if interrupt occur */ 355 status = readl(dev->mmio + 16); 356 if ((status & 0x2) == 0x2) { 357 /* Reset the interrupt */ 358 writel(status, dev->mmio + 16); 359 360 val = readl(dev->mmio + 28); 361 comedi_buf_write_samples(s, &val, 1); 362 363 s->async->events |= COMEDI_CB_EOA; 364 comedi_handle_events(dev, s); 365 366 return IRQ_HANDLED; 367 } 368 return IRQ_NONE; 369 } 370 371 static int apci3xxx_ai_started(struct comedi_device *dev) 372 { 373 if ((readl(dev->mmio + 8) & 0x80000) == 0x80000) 374 return 1; 375 376 return 0; 377 } 378 379 static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec) 380 { 381 unsigned int chan = CR_CHAN(chanspec); 382 unsigned int range = CR_RANGE(chanspec); 383 unsigned int aref = CR_AREF(chanspec); 384 unsigned int delay_mode; 385 unsigned int val; 386 387 if (apci3xxx_ai_started(dev)) 388 return -EBUSY; 389 390 /* Clear the FIFO */ 391 writel(0x10000, dev->mmio + 12); 392 393 /* Get and save the delay mode */ 394 delay_mode = readl(dev->mmio + 4); 395 delay_mode &= 0xfffffef0; 396 397 /* Channel configuration selection */ 398 writel(delay_mode, dev->mmio + 4); 399 400 /* Make the configuration */ 401 val = (range & 3) | ((range >> 2) << 6) | 402 ((aref == AREF_DIFF) << 7); 403 writel(val, dev->mmio + 0); 404 405 /* Channel selection */ 406 writel(delay_mode | 0x100, dev->mmio + 4); 407 writel(chan, dev->mmio + 0); 408 409 /* Restore delay mode */ 410 writel(delay_mode, dev->mmio + 4); 411 412 /* Set the number of sequence to 1 */ 413 writel(1, dev->mmio + 48); 414 415 return 0; 416 } 417 418 static int apci3xxx_ai_eoc(struct comedi_device *dev, 419 struct comedi_subdevice *s, 420 struct comedi_insn *insn, 421 unsigned long context) 422 { 423 unsigned int status; 424 425 status = readl(dev->mmio + 20); 426 if (status & 0x1) 427 return 0; 428 return -EBUSY; 429 } 430 431 static int apci3xxx_ai_insn_read(struct comedi_device *dev, 432 struct comedi_subdevice *s, 433 struct comedi_insn *insn, 434 unsigned int *data) 435 { 436 int ret; 437 int i; 438 439 ret = apci3xxx_ai_setup(dev, insn->chanspec); 440 if (ret) 441 return ret; 442 443 for (i = 0; i < insn->n; i++) { 444 /* Start the conversion */ 445 writel(0x80000, dev->mmio + 8); 446 447 /* Wait the EOS */ 448 ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0); 449 if (ret) 450 return ret; 451 452 /* Read the analog value */ 453 data[i] = readl(dev->mmio + 28); 454 } 455 456 return insn->n; 457 } 458 459 static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev, 460 unsigned int *ns, unsigned int flags) 461 { 462 const struct apci3xxx_boardinfo *board = dev->board_ptr; 463 struct apci3xxx_private *devpriv = dev->private; 464 unsigned int base; 465 unsigned int timer; 466 int time_base; 467 468 /* time_base: 0 = ns, 1 = us, 2 = ms */ 469 for (time_base = 0; time_base < 3; time_base++) { 470 /* skip unsupported time bases */ 471 if (!(board->ai_conv_units & (1 << time_base))) 472 continue; 473 474 switch (time_base) { 475 case 0: 476 base = 1; 477 break; 478 case 1: 479 base = 1000; 480 break; 481 case 2: 482 base = 1000000; 483 break; 484 } 485 486 switch (flags & CMDF_ROUND_MASK) { 487 case CMDF_ROUND_NEAREST: 488 default: 489 timer = DIV_ROUND_CLOSEST(*ns, base); 490 break; 491 case CMDF_ROUND_DOWN: 492 timer = *ns / base; 493 break; 494 case CMDF_ROUND_UP: 495 timer = DIV_ROUND_UP(*ns, base); 496 break; 497 } 498 499 if (timer < 0x10000) { 500 devpriv->ai_time_base = time_base; 501 devpriv->ai_timer = timer; 502 *ns = timer * time_base; 503 return 0; 504 } 505 } 506 return -EINVAL; 507 } 508 509 static int apci3xxx_ai_cmdtest(struct comedi_device *dev, 510 struct comedi_subdevice *s, 511 struct comedi_cmd *cmd) 512 { 513 const struct apci3xxx_boardinfo *board = dev->board_ptr; 514 int err = 0; 515 unsigned int arg; 516 517 /* Step 1 : check if triggers are trivially valid */ 518 519 err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); 520 err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 521 err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); 522 err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 523 err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 524 525 if (err) 526 return 1; 527 528 /* Step 2a : make sure trigger sources are unique */ 529 530 err |= comedi_check_trigger_is_unique(cmd->stop_src); 531 532 /* Step 2b : and mutually compatible */ 533 534 if (err) 535 return 2; 536 537 /* Step 3: check if arguments are trivially valid */ 538 539 err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); 540 err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 541 err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 542 board->ai_min_acq_ns); 543 err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, 544 cmd->chanlist_len); 545 546 if (cmd->stop_src == TRIG_COUNT) 547 err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); 548 else /* TRIG_NONE */ 549 err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); 550 551 if (err) 552 return 3; 553 554 /* step 4: fix up any arguments */ 555 556 arg = cmd->convert_arg; 557 err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags); 558 err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); 559 560 if (err) 561 return 4; 562 563 return 0; 564 } 565 566 static int apci3xxx_ai_cmd(struct comedi_device *dev, 567 struct comedi_subdevice *s) 568 { 569 struct apci3xxx_private *devpriv = dev->private; 570 struct comedi_cmd *cmd = &s->async->cmd; 571 int ret; 572 573 ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]); 574 if (ret) 575 return ret; 576 577 /* Set the convert timing unit */ 578 writel(devpriv->ai_time_base, dev->mmio + 36); 579 580 /* Set the convert timing */ 581 writel(devpriv->ai_timer, dev->mmio + 32); 582 583 /* Start the conversion */ 584 writel(0x180000, dev->mmio + 8); 585 586 return 0; 587 } 588 589 static int apci3xxx_ai_cancel(struct comedi_device *dev, 590 struct comedi_subdevice *s) 591 { 592 return 0; 593 } 594 595 static int apci3xxx_ao_eoc(struct comedi_device *dev, 596 struct comedi_subdevice *s, 597 struct comedi_insn *insn, 598 unsigned long context) 599 { 600 unsigned int status; 601 602 status = readl(dev->mmio + 96); 603 if (status & 0x100) 604 return 0; 605 return -EBUSY; 606 } 607 608 static int apci3xxx_ao_insn_write(struct comedi_device *dev, 609 struct comedi_subdevice *s, 610 struct comedi_insn *insn, 611 unsigned int *data) 612 { 613 unsigned int chan = CR_CHAN(insn->chanspec); 614 unsigned int range = CR_RANGE(insn->chanspec); 615 int ret; 616 int i; 617 618 for (i = 0; i < insn->n; i++) { 619 unsigned int val = data[i]; 620 621 /* Set the range selection */ 622 writel(range, dev->mmio + 96); 623 624 /* Write the analog value to the selected channel */ 625 writel((val << 8) | chan, dev->mmio + 100); 626 627 /* Wait the end of transfer */ 628 ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0); 629 if (ret) 630 return ret; 631 632 s->readback[chan] = val; 633 } 634 635 return insn->n; 636 } 637 638 static int apci3xxx_di_insn_bits(struct comedi_device *dev, 639 struct comedi_subdevice *s, 640 struct comedi_insn *insn, 641 unsigned int *data) 642 { 643 data[1] = inl(dev->iobase + 32) & 0xf; 644 645 return insn->n; 646 } 647 648 static int apci3xxx_do_insn_bits(struct comedi_device *dev, 649 struct comedi_subdevice *s, 650 struct comedi_insn *insn, 651 unsigned int *data) 652 { 653 s->state = inl(dev->iobase + 48) & 0xf; 654 655 if (comedi_dio_update_state(s, data)) 656 outl(s->state, dev->iobase + 48); 657 658 data[1] = s->state; 659 660 return insn->n; 661 } 662 663 static int apci3xxx_dio_insn_config(struct comedi_device *dev, 664 struct comedi_subdevice *s, 665 struct comedi_insn *insn, 666 unsigned int *data) 667 { 668 unsigned int chan = CR_CHAN(insn->chanspec); 669 unsigned int mask = 0; 670 int ret; 671 672 /* 673 * Port 0 (channels 0-7) are always inputs 674 * Port 1 (channels 8-15) are always outputs 675 * Port 2 (channels 16-23) are programmable i/o 676 */ 677 if (data[0] != INSN_CONFIG_DIO_QUERY) { 678 /* ignore all other instructions for ports 0 and 1 */ 679 if (chan < 16) 680 return -EINVAL; 681 682 /* changing any channel in port 2 changes the entire port */ 683 mask = 0xff0000; 684 } 685 686 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 687 if (ret) 688 return ret; 689 690 /* update port 2 configuration */ 691 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224); 692 693 return insn->n; 694 } 695 696 static int apci3xxx_dio_insn_bits(struct comedi_device *dev, 697 struct comedi_subdevice *s, 698 struct comedi_insn *insn, 699 unsigned int *data) 700 { 701 unsigned int mask; 702 unsigned int val; 703 704 mask = comedi_dio_update_state(s, data); 705 if (mask) { 706 if (mask & 0xff) 707 outl(s->state & 0xff, dev->iobase + 80); 708 if (mask & 0xff0000) 709 outl((s->state >> 16) & 0xff, dev->iobase + 112); 710 } 711 712 val = inl(dev->iobase + 80); 713 val |= (inl(dev->iobase + 64) << 8); 714 if (s->io_bits & 0xff0000) 715 val |= (inl(dev->iobase + 112) << 16); 716 else 717 val |= (inl(dev->iobase + 96) << 16); 718 719 data[1] = val; 720 721 return insn->n; 722 } 723 724 static int apci3xxx_reset(struct comedi_device *dev) 725 { 726 unsigned int val; 727 int i; 728 729 /* Disable the interrupt */ 730 disable_irq(dev->irq); 731 732 /* Clear the start command */ 733 writel(0, dev->mmio + 8); 734 735 /* Reset the interrupt flags */ 736 val = readl(dev->mmio + 16); 737 writel(val, dev->mmio + 16); 738 739 /* clear the EOS */ 740 readl(dev->mmio + 20); 741 742 /* Clear the FIFO */ 743 for (i = 0; i < 16; i++) 744 val = readl(dev->mmio + 28); 745 746 /* Enable the interrupt */ 747 enable_irq(dev->irq); 748 749 return 0; 750 } 751 752 static int apci3xxx_auto_attach(struct comedi_device *dev, 753 unsigned long context) 754 { 755 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 756 const struct apci3xxx_boardinfo *board = NULL; 757 struct apci3xxx_private *devpriv; 758 struct comedi_subdevice *s; 759 int n_subdevices; 760 int subdev; 761 int ret; 762 763 if (context < ARRAY_SIZE(apci3xxx_boardtypes)) 764 board = &apci3xxx_boardtypes[context]; 765 if (!board) 766 return -ENODEV; 767 dev->board_ptr = board; 768 dev->board_name = board->name; 769 770 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 771 if (!devpriv) 772 return -ENOMEM; 773 774 ret = comedi_pci_enable(dev); 775 if (ret) 776 return ret; 777 778 dev->iobase = pci_resource_start(pcidev, 2); 779 dev->mmio = pci_ioremap_bar(pcidev, 3); 780 if (!dev->mmio) 781 return -ENOMEM; 782 783 if (pcidev->irq > 0) { 784 ret = request_irq(pcidev->irq, apci3xxx_irq_handler, 785 IRQF_SHARED, dev->board_name, dev); 786 if (ret == 0) 787 dev->irq = pcidev->irq; 788 } 789 790 n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao + 791 board->has_dig_in + board->has_dig_out + 792 board->has_ttl_io; 793 ret = comedi_alloc_subdevices(dev, n_subdevices); 794 if (ret) 795 return ret; 796 797 subdev = 0; 798 799 /* Analog Input subdevice */ 800 if (board->ai_n_chan) { 801 s = &dev->subdevices[subdev]; 802 s->type = COMEDI_SUBD_AI; 803 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags; 804 s->n_chan = board->ai_n_chan; 805 s->maxdata = board->ai_maxdata; 806 s->range_table = &apci3xxx_ai_range; 807 s->insn_read = apci3xxx_ai_insn_read; 808 if (dev->irq) { 809 /* 810 * FIXME: The hardware supports multiple scan modes 811 * but the original addi-data driver only supported 812 * reading a single channel with interrupts. Need a 813 * proper datasheet to fix this. 814 * 815 * The following scan modes are supported by the 816 * hardware: 817 * 1) Single software scan 818 * 2) Single hardware triggered scan 819 * 3) Continuous software scan 820 * 4) Continuous software scan with timer delay 821 * 5) Continuous hardware triggered scan 822 * 6) Continuous hardware triggered scan with timer 823 * delay 824 * 825 * For now, limit the chanlist to a single channel. 826 */ 827 dev->read_subdev = s; 828 s->subdev_flags |= SDF_CMD_READ; 829 s->len_chanlist = 1; 830 s->do_cmdtest = apci3xxx_ai_cmdtest; 831 s->do_cmd = apci3xxx_ai_cmd; 832 s->cancel = apci3xxx_ai_cancel; 833 } 834 835 subdev++; 836 } 837 838 /* Analog Output subdevice */ 839 if (board->has_ao) { 840 s = &dev->subdevices[subdev]; 841 s->type = COMEDI_SUBD_AO; 842 s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; 843 s->n_chan = 4; 844 s->maxdata = 0x0fff; 845 s->range_table = &apci3xxx_ao_range; 846 s->insn_write = apci3xxx_ao_insn_write; 847 848 ret = comedi_alloc_subdev_readback(s); 849 if (ret) 850 return ret; 851 852 subdev++; 853 } 854 855 /* Digital Input subdevice */ 856 if (board->has_dig_in) { 857 s = &dev->subdevices[subdev]; 858 s->type = COMEDI_SUBD_DI; 859 s->subdev_flags = SDF_READABLE; 860 s->n_chan = 4; 861 s->maxdata = 1; 862 s->range_table = &range_digital; 863 s->insn_bits = apci3xxx_di_insn_bits; 864 865 subdev++; 866 } 867 868 /* Digital Output subdevice */ 869 if (board->has_dig_out) { 870 s = &dev->subdevices[subdev]; 871 s->type = COMEDI_SUBD_DO; 872 s->subdev_flags = SDF_WRITABLE; 873 s->n_chan = 4; 874 s->maxdata = 1; 875 s->range_table = &range_digital; 876 s->insn_bits = apci3xxx_do_insn_bits; 877 878 subdev++; 879 } 880 881 /* TTL Digital I/O subdevice */ 882 if (board->has_ttl_io) { 883 s = &dev->subdevices[subdev]; 884 s->type = COMEDI_SUBD_DIO; 885 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 886 s->n_chan = 24; 887 s->maxdata = 1; 888 s->io_bits = 0xff; /* channels 0-7 are always outputs */ 889 s->range_table = &range_digital; 890 s->insn_config = apci3xxx_dio_insn_config; 891 s->insn_bits = apci3xxx_dio_insn_bits; 892 893 subdev++; 894 } 895 896 apci3xxx_reset(dev); 897 return 0; 898 } 899 900 static void apci3xxx_detach(struct comedi_device *dev) 901 { 902 if (dev->iobase) 903 apci3xxx_reset(dev); 904 comedi_pci_detach(dev); 905 } 906 907 static struct comedi_driver apci3xxx_driver = { 908 .driver_name = "addi_apci_3xxx", 909 .module = THIS_MODULE, 910 .auto_attach = apci3xxx_auto_attach, 911 .detach = apci3xxx_detach, 912 }; 913 914 static int apci3xxx_pci_probe(struct pci_dev *dev, 915 const struct pci_device_id *id) 916 { 917 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data); 918 } 919 920 static const struct pci_device_id apci3xxx_pci_table[] = { 921 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 }, 922 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 }, 923 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 }, 924 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 }, 925 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 }, 926 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 }, 927 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 }, 928 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 }, 929 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 }, 930 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 }, 931 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 }, 932 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 }, 933 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 }, 934 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 }, 935 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 }, 936 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 }, 937 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 }, 938 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 }, 939 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 }, 940 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 }, 941 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 }, 942 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 }, 943 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 }, 944 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 }, 945 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 }, 946 { 0 } 947 }; 948 MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table); 949 950 static struct pci_driver apci3xxx_pci_driver = { 951 .name = "addi_apci_3xxx", 952 .id_table = apci3xxx_pci_table, 953 .probe = apci3xxx_pci_probe, 954 .remove = comedi_pci_auto_unconfig, 955 }; 956 module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver); 957 958 MODULE_AUTHOR("Comedi https://www.comedi.org"); 959 MODULE_DESCRIPTION("Comedi low-level driver"); 960 MODULE_LICENSE("GPL"); 961