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