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