addi_apci_3xxx.c revision 66e3015f6ec84f7143af0f01ce096b6cd9e1a4b9
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 void __iomem *mmio; 357 unsigned int ai_timer; 358 unsigned char ai_time_base; 359}; 360 361static irqreturn_t apci3xxx_irq_handler(int irq, void *d) 362{ 363 struct comedi_device *dev = d; 364 struct apci3xxx_private *devpriv = dev->private; 365 struct comedi_subdevice *s = dev->read_subdev; 366 unsigned int status; 367 unsigned int val; 368 369 /* Test if interrupt occur */ 370 status = readl(devpriv->mmio + 16); 371 if ((status & 0x2) == 0x2) { 372 /* Reset the interrupt */ 373 writel(status, devpriv->mmio + 16); 374 375 val = readl(devpriv->mmio + 28); 376 comedi_buf_put(s, val); 377 378 s->async->events |= COMEDI_CB_EOA; 379 comedi_event(dev, s); 380 381 return IRQ_HANDLED; 382 } 383 return IRQ_NONE; 384} 385 386static int apci3xxx_ai_started(struct comedi_device *dev) 387{ 388 struct apci3xxx_private *devpriv = dev->private; 389 390 if ((readl(devpriv->mmio + 8) & 0x80000) == 0x80000) 391 return 1; 392 else 393 return 0; 394 395} 396 397static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec) 398{ 399 struct apci3xxx_private *devpriv = dev->private; 400 unsigned int chan = CR_CHAN(chanspec); 401 unsigned int range = CR_RANGE(chanspec); 402 unsigned int aref = CR_AREF(chanspec); 403 unsigned int delay_mode; 404 unsigned int val; 405 406 if (apci3xxx_ai_started(dev)) 407 return -EBUSY; 408 409 /* Clear the FIFO */ 410 writel(0x10000, devpriv->mmio + 12); 411 412 /* Get and save the delay mode */ 413 delay_mode = readl(devpriv->mmio + 4); 414 delay_mode &= 0xfffffef0; 415 416 /* Channel configuration selection */ 417 writel(delay_mode, devpriv->mmio + 4); 418 419 /* Make the configuration */ 420 val = (range & 3) | ((range >> 2) << 6) | 421 ((aref == AREF_DIFF) << 7); 422 writel(val, devpriv->mmio + 0); 423 424 /* Channel selection */ 425 writel(delay_mode | 0x100, devpriv->mmio + 4); 426 writel(chan, devpriv->mmio + 0); 427 428 /* Restore delay mode */ 429 writel(delay_mode, devpriv->mmio + 4); 430 431 /* Set the number of sequence to 1 */ 432 writel(1, devpriv->mmio + 48); 433 434 return 0; 435} 436 437static int apci3xxx_ai_eoc(struct comedi_device *dev, 438 struct comedi_subdevice *s, 439 struct comedi_insn *insn, 440 unsigned long context) 441{ 442 struct apci3xxx_private *devpriv = dev->private; 443 unsigned int status; 444 445 status = readl(devpriv->mmio + 20); 446 if (status & 0x1) 447 return 0; 448 return -EBUSY; 449} 450 451static int apci3xxx_ai_insn_read(struct comedi_device *dev, 452 struct comedi_subdevice *s, 453 struct comedi_insn *insn, 454 unsigned int *data) 455{ 456 struct apci3xxx_private *devpriv = dev->private; 457 int ret; 458 int i; 459 460 ret = apci3xxx_ai_setup(dev, insn->chanspec); 461 if (ret) 462 return ret; 463 464 for (i = 0; i < insn->n; i++) { 465 /* Start the conversion */ 466 writel(0x80000, devpriv->mmio + 8); 467 468 /* Wait the EOS */ 469 ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0); 470 if (ret) 471 return ret; 472 473 /* Read the analog value */ 474 data[i] = readl(devpriv->mmio + 28); 475 } 476 477 return insn->n; 478} 479 480static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev, 481 unsigned int *ns, int round_mode) 482{ 483 const struct apci3xxx_boardinfo *board = comedi_board(dev); 484 struct apci3xxx_private *devpriv = dev->private; 485 unsigned int base; 486 unsigned int timer; 487 int time_base; 488 489 /* time_base: 0 = ns, 1 = us, 2 = ms */ 490 for (time_base = 0; time_base < 3; time_base++) { 491 /* skip unsupported time bases */ 492 if (!(board->ai_conv_units & (1 << time_base))) 493 continue; 494 495 switch (time_base) { 496 case 0: 497 base = 1; 498 break; 499 case 1: 500 base = 1000; 501 break; 502 case 2: 503 base = 1000000; 504 break; 505 } 506 507 switch (round_mode) { 508 case TRIG_ROUND_NEAREST: 509 default: 510 timer = (*ns + base / 2) / base; 511 break; 512 case TRIG_ROUND_DOWN: 513 timer = *ns / base; 514 break; 515 case TRIG_ROUND_UP: 516 timer = (*ns + base - 1) / base; 517 break; 518 } 519 520 if (timer < 0x10000) { 521 devpriv->ai_time_base = time_base; 522 devpriv->ai_timer = timer; 523 *ns = timer * time_base; 524 return 0; 525 } 526 } 527 return -EINVAL; 528} 529 530static int apci3xxx_ai_cmdtest(struct comedi_device *dev, 531 struct comedi_subdevice *s, 532 struct comedi_cmd *cmd) 533{ 534 const struct apci3xxx_boardinfo *board = comedi_board(dev); 535 int err = 0; 536 unsigned int arg; 537 538 /* Step 1 : check if triggers are trivially valid */ 539 540 err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW); 541 err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW); 542 err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER); 543 err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); 544 err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); 545 546 if (err) 547 return 1; 548 549 /* Step 2a : make sure trigger sources are unique */ 550 551 err |= cfc_check_trigger_is_unique(cmd->stop_src); 552 553 /* Step 2b : and mutually compatible */ 554 555 if (err) 556 return 2; 557 558 /* Step 3: check if arguments are trivially valid */ 559 560 err |= cfc_check_trigger_arg_is(&cmd->start_arg, 0); 561 err |= cfc_check_trigger_arg_is(&cmd->scan_begin_arg, 0); 562 err |= cfc_check_trigger_arg_min(&cmd->convert_arg, 563 board->ai_min_acq_ns); 564 err |= cfc_check_trigger_arg_is(&cmd->scan_end_arg, cmd->chanlist_len); 565 566 if (cmd->stop_src == TRIG_COUNT) 567 err |= cfc_check_trigger_arg_min(&cmd->stop_arg, 1); 568 else /* TRIG_NONE */ 569 err |= cfc_check_trigger_arg_is(&cmd->stop_arg, 0); 570 571 if (err) 572 return 3; 573 574 /* step 4: fix up any arguments */ 575 576 arg = cmd->convert_arg; 577 err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags & TRIG_ROUND_MASK); 578 err |= cfc_check_trigger_arg_is(&cmd->convert_arg, arg); 579 580 if (err) 581 return 4; 582 583 return 0; 584} 585 586static int apci3xxx_ai_cmd(struct comedi_device *dev, 587 struct comedi_subdevice *s) 588{ 589 struct apci3xxx_private *devpriv = dev->private; 590 struct comedi_cmd *cmd = &s->async->cmd; 591 int ret; 592 593 ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]); 594 if (ret) 595 return ret; 596 597 /* Set the convert timing unit */ 598 writel(devpriv->ai_time_base, devpriv->mmio + 36); 599 600 /* Set the convert timing */ 601 writel(devpriv->ai_timer, devpriv->mmio + 32); 602 603 /* Start the conversion */ 604 writel(0x180000, devpriv->mmio + 8); 605 606 return 0; 607} 608 609static int apci3xxx_ai_cancel(struct comedi_device *dev, 610 struct comedi_subdevice *s) 611{ 612 return 0; 613} 614 615static int apci3xxx_ao_eoc(struct comedi_device *dev, 616 struct comedi_subdevice *s, 617 struct comedi_insn *insn, 618 unsigned long context) 619{ 620 struct apci3xxx_private *devpriv = dev->private; 621 unsigned int status; 622 623 status = readl(devpriv->mmio + 96); 624 if (status & 0x100) 625 return 0; 626 return -EBUSY; 627} 628 629static int apci3xxx_ao_insn_write(struct comedi_device *dev, 630 struct comedi_subdevice *s, 631 struct comedi_insn *insn, 632 unsigned int *data) 633{ 634 struct apci3xxx_private *devpriv = dev->private; 635 unsigned int chan = CR_CHAN(insn->chanspec); 636 unsigned int range = CR_RANGE(insn->chanspec); 637 int ret; 638 int i; 639 640 for (i = 0; i < insn->n; i++) { 641 /* Set the range selection */ 642 writel(range, devpriv->mmio + 96); 643 644 /* Write the analog value to the selected channel */ 645 writel((data[i] << 8) | chan, devpriv->mmio + 100); 646 647 /* Wait the end of transfer */ 648 ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0); 649 if (ret) 650 return ret; 651 } 652 653 return insn->n; 654} 655 656static int apci3xxx_di_insn_bits(struct comedi_device *dev, 657 struct comedi_subdevice *s, 658 struct comedi_insn *insn, 659 unsigned int *data) 660{ 661 data[1] = inl(dev->iobase + 32) & 0xf; 662 663 return insn->n; 664} 665 666static int apci3xxx_do_insn_bits(struct comedi_device *dev, 667 struct comedi_subdevice *s, 668 struct comedi_insn *insn, 669 unsigned int *data) 670{ 671 s->state = inl(dev->iobase + 48) & 0xf; 672 673 if (comedi_dio_update_state(s, data)) 674 outl(s->state, dev->iobase + 48); 675 676 data[1] = s->state; 677 678 return insn->n; 679} 680 681static int apci3xxx_dio_insn_config(struct comedi_device *dev, 682 struct comedi_subdevice *s, 683 struct comedi_insn *insn, 684 unsigned int *data) 685{ 686 unsigned int chan = CR_CHAN(insn->chanspec); 687 unsigned int mask = 0; 688 int ret; 689 690 /* 691 * Port 0 (channels 0-7) are always inputs 692 * Port 1 (channels 8-15) are always outputs 693 * Port 2 (channels 16-23) are programmable i/o 694 */ 695 if (data[0] != INSN_CONFIG_DIO_QUERY) { 696 /* ignore all other instructions for ports 0 and 1 */ 697 if (chan < 16) 698 return -EINVAL; 699 else 700 /* changing any channel in port 2 */ 701 /* changes the entire port */ 702 mask = 0xff0000; 703 } 704 705 ret = comedi_dio_insn_config(dev, s, insn, data, mask); 706 if (ret) 707 return ret; 708 709 /* update port 2 configuration */ 710 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224); 711 712 return insn->n; 713} 714 715static int apci3xxx_dio_insn_bits(struct comedi_device *dev, 716 struct comedi_subdevice *s, 717 struct comedi_insn *insn, 718 unsigned int *data) 719{ 720 unsigned int mask; 721 unsigned int val; 722 723 mask = comedi_dio_update_state(s, data); 724 if (mask) { 725 if (mask & 0xff) 726 outl(s->state & 0xff, dev->iobase + 80); 727 if (mask & 0xff0000) 728 outl((s->state >> 16) & 0xff, dev->iobase + 112); 729 } 730 731 val = inl(dev->iobase + 80); 732 val |= (inl(dev->iobase + 64) << 8); 733 if (s->io_bits & 0xff0000) 734 val |= (inl(dev->iobase + 112) << 16); 735 else 736 val |= (inl(dev->iobase + 96) << 16); 737 738 data[1] = val; 739 740 return insn->n; 741} 742 743static int apci3xxx_reset(struct comedi_device *dev) 744{ 745 struct apci3xxx_private *devpriv = dev->private; 746 unsigned int val; 747 int i; 748 749 /* Disable the interrupt */ 750 disable_irq(dev->irq); 751 752 /* Clear the start command */ 753 writel(0, devpriv->mmio + 8); 754 755 /* Reset the interrupt flags */ 756 val = readl(devpriv->mmio + 16); 757 writel(val, devpriv->mmio + 16); 758 759 /* clear the EOS */ 760 readl(devpriv->mmio + 20); 761 762 /* Clear the FIFO */ 763 for (i = 0; i < 16; i++) 764 val = readl(devpriv->mmio + 28); 765 766 /* Enable the interrupt */ 767 enable_irq(dev->irq); 768 769 return 0; 770} 771 772static int apci3xxx_auto_attach(struct comedi_device *dev, 773 unsigned long context) 774{ 775 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 776 const struct apci3xxx_boardinfo *board = NULL; 777 struct apci3xxx_private *devpriv; 778 struct comedi_subdevice *s; 779 int n_subdevices; 780 int subdev; 781 int ret; 782 783 if (context < ARRAY_SIZE(apci3xxx_boardtypes)) 784 board = &apci3xxx_boardtypes[context]; 785 if (!board) 786 return -ENODEV; 787 dev->board_ptr = board; 788 dev->board_name = board->name; 789 790 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); 791 if (!devpriv) 792 return -ENOMEM; 793 794 ret = comedi_pci_enable(dev); 795 if (ret) 796 return ret; 797 798 dev->iobase = pci_resource_start(pcidev, 2); 799 devpriv->mmio = pci_ioremap_bar(pcidev, 3); 800 801 if (pcidev->irq > 0) { 802 ret = request_irq(pcidev->irq, apci3xxx_irq_handler, 803 IRQF_SHARED, dev->board_name, dev); 804 if (ret == 0) 805 dev->irq = pcidev->irq; 806 } 807 808 n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao + 809 board->has_dig_in + board->has_dig_out + 810 board->has_ttl_io; 811 ret = comedi_alloc_subdevices(dev, n_subdevices); 812 if (ret) 813 return ret; 814 815 subdev = 0; 816 817 /* Analog Input subdevice */ 818 if (board->ai_n_chan) { 819 s = &dev->subdevices[subdev]; 820 s->type = COMEDI_SUBD_AI; 821 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags; 822 s->n_chan = board->ai_n_chan; 823 s->maxdata = board->ai_maxdata; 824 s->range_table = &apci3xxx_ai_range; 825 s->insn_read = apci3xxx_ai_insn_read; 826 if (dev->irq) { 827 /* 828 * FIXME: The hardware supports multiple scan modes 829 * but the original addi-data driver only supported 830 * reading a single channel with interrupts. Need a 831 * proper datasheet to fix this. 832 * 833 * The following scan modes are supported by the 834 * hardware: 835 * 1) Single software scan 836 * 2) Single hardware triggered scan 837 * 3) Continuous software scan 838 * 4) Continuous software scan with timer delay 839 * 5) Continuous hardware triggered scan 840 * 6) Continuous hardware triggered scan with timer 841 * delay 842 * 843 * For now, limit the chanlist to a single channel. 844 */ 845 dev->read_subdev = s; 846 s->subdev_flags |= SDF_CMD_READ; 847 s->len_chanlist = 1; 848 s->do_cmdtest = apci3xxx_ai_cmdtest; 849 s->do_cmd = apci3xxx_ai_cmd; 850 s->cancel = apci3xxx_ai_cancel; 851 } 852 853 subdev++; 854 } 855 856 /* Analog Output subdevice */ 857 if (board->has_ao) { 858 s = &dev->subdevices[subdev]; 859 s->type = COMEDI_SUBD_AO; 860 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 861 s->n_chan = 4; 862 s->maxdata = 0x0fff; 863 s->range_table = &apci3xxx_ao_range; 864 s->insn_write = apci3xxx_ao_insn_write; 865 866 subdev++; 867 } 868 869 /* Digital Input subdevice */ 870 if (board->has_dig_in) { 871 s = &dev->subdevices[subdev]; 872 s->type = COMEDI_SUBD_DI; 873 s->subdev_flags = SDF_READABLE; 874 s->n_chan = 4; 875 s->maxdata = 1; 876 s->range_table = &range_digital; 877 s->insn_bits = apci3xxx_di_insn_bits; 878 879 subdev++; 880 } 881 882 /* Digital Output subdevice */ 883 if (board->has_dig_out) { 884 s = &dev->subdevices[subdev]; 885 s->type = COMEDI_SUBD_DO; 886 s->subdev_flags = SDF_WRITEABLE; 887 s->n_chan = 4; 888 s->maxdata = 1; 889 s->range_table = &range_digital; 890 s->insn_bits = apci3xxx_do_insn_bits; 891 892 subdev++; 893 } 894 895 /* TTL Digital I/O subdevice */ 896 if (board->has_ttl_io) { 897 s = &dev->subdevices[subdev]; 898 s->type = COMEDI_SUBD_DIO; 899 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE; 900 s->n_chan = 24; 901 s->maxdata = 1; 902 s->io_bits = 0xff; /* channels 0-7 are always outputs */ 903 s->range_table = &range_digital; 904 s->insn_config = apci3xxx_dio_insn_config; 905 s->insn_bits = apci3xxx_dio_insn_bits; 906 907 subdev++; 908 } 909 910 apci3xxx_reset(dev); 911 return 0; 912} 913 914static void apci3xxx_detach(struct comedi_device *dev) 915{ 916 struct apci3xxx_private *devpriv = dev->private; 917 918 if (devpriv) { 919 if (dev->iobase) 920 apci3xxx_reset(dev); 921 if (dev->irq) 922 free_irq(dev->irq, dev); 923 if (devpriv->mmio) 924 iounmap(devpriv->mmio); 925 } 926 comedi_pci_disable(dev); 927} 928 929static struct comedi_driver apci3xxx_driver = { 930 .driver_name = "addi_apci_3xxx", 931 .module = THIS_MODULE, 932 .auto_attach = apci3xxx_auto_attach, 933 .detach = apci3xxx_detach, 934}; 935 936static int apci3xxx_pci_probe(struct pci_dev *dev, 937 const struct pci_device_id *id) 938{ 939 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data); 940} 941 942static const struct pci_device_id apci3xxx_pci_table[] = { 943 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 }, 944 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 }, 945 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 }, 946 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 }, 947 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 }, 948 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 }, 949 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 }, 950 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 }, 951 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 }, 952 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 }, 953 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 }, 954 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 }, 955 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 }, 956 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 }, 957 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 }, 958 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 }, 959 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 }, 960 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 }, 961 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 }, 962 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 }, 963 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 }, 964 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 }, 965 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 }, 966 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 }, 967 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 }, 968 { 0 } 969}; 970MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table); 971 972static struct pci_driver apci3xxx_pci_driver = { 973 .name = "addi_apci_3xxx", 974 .id_table = apci3xxx_pci_table, 975 .probe = apci3xxx_pci_probe, 976 .remove = comedi_pci_auto_unconfig, 977}; 978module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver); 979 980MODULE_AUTHOR("Comedi http://www.comedi.org"); 981MODULE_DESCRIPTION("Comedi low-level driver"); 982MODULE_LICENSE("GPL"); 983