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