addi_apci_3xxx.c revision 8d4729066cde58c730573d6aad2ea29f8048fcb3
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#define CONV_UNIT_NS (1 << 0) 31#define CONV_UNIT_US (1 << 1) 32#define CONV_UNIT_MS (1 << 2) 33 34static const struct comedi_lrange apci3xxx_ai_range = { 35 8, { 36 BIP_RANGE(10), 37 BIP_RANGE(5), 38 BIP_RANGE(2), 39 BIP_RANGE(1), 40 UNI_RANGE(10), 41 UNI_RANGE(5), 42 UNI_RANGE(2), 43 UNI_RANGE(1) 44 } 45}; 46 47static const struct comedi_lrange apci3xxx_ao_range = { 48 2, { 49 BIP_RANGE(10), 50 UNI_RANGE(10) 51 } 52}; 53 54enum apci3xxx_boardid { 55 BOARD_APCI3000_16, 56 BOARD_APCI3000_8, 57 BOARD_APCI3000_4, 58 BOARD_APCI3006_16, 59 BOARD_APCI3006_8, 60 BOARD_APCI3006_4, 61 BOARD_APCI3010_16, 62 BOARD_APCI3010_8, 63 BOARD_APCI3010_4, 64 BOARD_APCI3016_16, 65 BOARD_APCI3016_8, 66 BOARD_APCI3016_4, 67 BOARD_APCI3100_16_4, 68 BOARD_APCI3100_8_4, 69 BOARD_APCI3106_16_4, 70 BOARD_APCI3106_8_4, 71 BOARD_APCI3110_16_4, 72 BOARD_APCI3110_8_4, 73 BOARD_APCI3116_16_4, 74 BOARD_APCI3116_8_4, 75 BOARD_APCI3003, 76 BOARD_APCI3002_16, 77 BOARD_APCI3002_8, 78 BOARD_APCI3002_4, 79 BOARD_APCI3500, 80}; 81 82struct apci3xxx_boardinfo { 83 const char *name; 84 int ai_subdev_flags; 85 int ai_n_chan; 86 unsigned int ai_maxdata; 87 unsigned char ai_conv_units; 88 unsigned int ai_min_acq_ns; 89 unsigned int has_ao:1; 90 unsigned int has_dig_in:1; 91 unsigned int has_dig_out:1; 92 unsigned int has_ttl_io:1; 93}; 94 95static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = { 96 [BOARD_APCI3000_16] = { 97 .name = "apci3000-16", 98 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 99 .ai_n_chan = 16, 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_8] = { 106 .name = "apci3000-8", 107 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 108 .ai_n_chan = 8, 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_APCI3000_4] = { 115 .name = "apci3000-4", 116 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 117 .ai_n_chan = 4, 118 .ai_maxdata = 0x0fff, 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_16] = { 124 .name = "apci3006-16", 125 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 126 .ai_n_chan = 16, 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_8] = { 133 .name = "apci3006-8", 134 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 135 .ai_n_chan = 8, 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_APCI3006_4] = { 142 .name = "apci3006-4", 143 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 144 .ai_n_chan = 4, 145 .ai_maxdata = 0xffff, 146 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 147 .ai_min_acq_ns = 10000, 148 .has_ttl_io = 1, 149 }, 150 [BOARD_APCI3010_16] = { 151 .name = "apci3010-16", 152 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 153 .ai_n_chan = 16, 154 .ai_maxdata = 0x0fff, 155 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 156 .ai_min_acq_ns = 5000, 157 .has_dig_in = 1, 158 .has_dig_out = 1, 159 .has_ttl_io = 1, 160 }, 161 [BOARD_APCI3010_8] = { 162 .name = "apci3010-8", 163 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 164 .ai_n_chan = 8, 165 .ai_maxdata = 0x0fff, 166 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 167 .ai_min_acq_ns = 5000, 168 .has_dig_in = 1, 169 .has_dig_out = 1, 170 .has_ttl_io = 1, 171 }, 172 [BOARD_APCI3010_4] = { 173 .name = "apci3010-4", 174 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 175 .ai_n_chan = 4, 176 .ai_maxdata = 0x0fff, 177 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 178 .ai_min_acq_ns = 5000, 179 .has_dig_in = 1, 180 .has_dig_out = 1, 181 .has_ttl_io = 1, 182 }, 183 [BOARD_APCI3016_16] = { 184 .name = "apci3016-16", 185 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 186 .ai_n_chan = 16, 187 .ai_maxdata = 0xffff, 188 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 189 .ai_min_acq_ns = 5000, 190 .has_dig_in = 1, 191 .has_dig_out = 1, 192 .has_ttl_io = 1, 193 }, 194 [BOARD_APCI3016_8] = { 195 .name = "apci3016-8", 196 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 197 .ai_n_chan = 8, 198 .ai_maxdata = 0xffff, 199 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 200 .ai_min_acq_ns = 5000, 201 .has_dig_in = 1, 202 .has_dig_out = 1, 203 .has_ttl_io = 1, 204 }, 205 [BOARD_APCI3016_4] = { 206 .name = "apci3016-4", 207 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 208 .ai_n_chan = 4, 209 .ai_maxdata = 0xffff, 210 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 211 .ai_min_acq_ns = 5000, 212 .has_dig_in = 1, 213 .has_dig_out = 1, 214 .has_ttl_io = 1, 215 }, 216 [BOARD_APCI3100_16_4] = { 217 .name = "apci3100-16-4", 218 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 219 .ai_n_chan = 16, 220 .ai_maxdata = 0x0fff, 221 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 222 .ai_min_acq_ns = 10000, 223 .has_ao = 1, 224 .has_ttl_io = 1, 225 }, 226 [BOARD_APCI3100_8_4] = { 227 .name = "apci3100-8-4", 228 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 229 .ai_n_chan = 8, 230 .ai_maxdata = 0x0fff, 231 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 232 .ai_min_acq_ns = 10000, 233 .has_ao = 1, 234 .has_ttl_io = 1, 235 }, 236 [BOARD_APCI3106_16_4] = { 237 .name = "apci3106-16-4", 238 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 239 .ai_n_chan = 16, 240 .ai_maxdata = 0xffff, 241 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 242 .ai_min_acq_ns = 10000, 243 .has_ao = 1, 244 .has_ttl_io = 1, 245 }, 246 [BOARD_APCI3106_8_4] = { 247 .name = "apci3106-8-4", 248 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 249 .ai_n_chan = 8, 250 .ai_maxdata = 0xffff, 251 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 252 .ai_min_acq_ns = 10000, 253 .has_ao = 1, 254 .has_ttl_io = 1, 255 }, 256 [BOARD_APCI3110_16_4] = { 257 .name = "apci3110-16-4", 258 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 259 .ai_n_chan = 16, 260 .ai_maxdata = 0x0fff, 261 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 262 .ai_min_acq_ns = 5000, 263 .has_ao = 1, 264 .has_dig_in = 1, 265 .has_dig_out = 1, 266 .has_ttl_io = 1, 267 }, 268 [BOARD_APCI3110_8_4] = { 269 .name = "apci3110-8-4", 270 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 271 .ai_n_chan = 8, 272 .ai_maxdata = 0x0fff, 273 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 274 .ai_min_acq_ns = 5000, 275 .has_ao = 1, 276 .has_dig_in = 1, 277 .has_dig_out = 1, 278 .has_ttl_io = 1, 279 }, 280 [BOARD_APCI3116_16_4] = { 281 .name = "apci3116-16-4", 282 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 283 .ai_n_chan = 16, 284 .ai_maxdata = 0xffff, 285 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 286 .ai_min_acq_ns = 5000, 287 .has_ao = 1, 288 .has_dig_in = 1, 289 .has_dig_out = 1, 290 .has_ttl_io = 1, 291 }, 292 [BOARD_APCI3116_8_4] = { 293 .name = "apci3116-8-4", 294 .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF, 295 .ai_n_chan = 8, 296 .ai_maxdata = 0xffff, 297 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 298 .ai_min_acq_ns = 5000, 299 .has_ao = 1, 300 .has_dig_in = 1, 301 .has_dig_out = 1, 302 .has_ttl_io = 1, 303 }, 304 [BOARD_APCI3003] = { 305 .name = "apci3003", 306 .ai_subdev_flags = SDF_DIFF, 307 .ai_n_chan = 4, 308 .ai_maxdata = 0xffff, 309 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US | 310 CONV_UNIT_NS, 311 .ai_min_acq_ns = 2500, 312 .has_dig_in = 1, 313 .has_dig_out = 1, 314 }, 315 [BOARD_APCI3002_16] = { 316 .name = "apci3002-16", 317 .ai_subdev_flags = SDF_DIFF, 318 .ai_n_chan = 16, 319 .ai_maxdata = 0xffff, 320 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 321 .ai_min_acq_ns = 5000, 322 .has_dig_in = 1, 323 .has_dig_out = 1, 324 }, 325 [BOARD_APCI3002_8] = { 326 .name = "apci3002-8", 327 .ai_subdev_flags = SDF_DIFF, 328 .ai_n_chan = 8, 329 .ai_maxdata = 0xffff, 330 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 331 .ai_min_acq_ns = 5000, 332 .has_dig_in = 1, 333 .has_dig_out = 1, 334 }, 335 [BOARD_APCI3002_4] = { 336 .name = "apci3002-4", 337 .ai_subdev_flags = SDF_DIFF, 338 .ai_n_chan = 4, 339 .ai_maxdata = 0xffff, 340 .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US, 341 .ai_min_acq_ns = 5000, 342 .has_dig_in = 1, 343 .has_dig_out = 1, 344 }, 345 [BOARD_APCI3500] = { 346 .name = "apci3500", 347 .has_ao = 1, 348 .has_ttl_io = 1, 349 }, 350}; 351 352struct apci3xxx_private { 353 void __iomem *mmio; 354 unsigned int ui_AiNbrofChannels; /* how many channels is measured */ 355 unsigned int ui_AiReadData[32]; 356 unsigned char b_EocEosInterrupt; 357 unsigned int ui_EocEosConversionTime; 358 unsigned char b_EocEosConversionTimeBase; 359 unsigned char b_SingelDiff; 360}; 361 362#include "addi-data/hwdrv_apci3xxx.c" 363 364static irqreturn_t apci3xxx_irq_handler(int irq, void *d) 365{ 366 struct comedi_device *dev = d; 367 struct apci3xxx_private *devpriv = dev->private; 368 unsigned int status; 369 int i; 370 371 /* Test if interrupt occur */ 372 status = readl(devpriv->mmio + 16); 373 if ((status & 0x2) == 0x2) { 374 /* Reset the interrupt */ 375 writel(status, devpriv->mmio + 16); 376 377 /* Test if interrupt enabled */ 378 if (devpriv->b_EocEosInterrupt == 1) { 379 /* Read all analog inputs value */ 380 for (i = 0; i < devpriv->ui_AiNbrofChannels; i++) { 381 unsigned int val; 382 383 val = readl(devpriv->mmio + 28); 384 devpriv->ui_AiReadData[i] = val; 385 } 386 387 /* Set the interrupt flag */ 388 devpriv->b_EocEosInterrupt = 2; 389 390 /* FIXME: comedi_event() */ 391 } 392 } 393 return IRQ_RETVAL(1); 394} 395 396static int apci3xxx_ao_insn_write(struct comedi_device *dev, 397 struct comedi_subdevice *s, 398 struct comedi_insn *insn, 399 unsigned int *data) 400{ 401 struct apci3xxx_private *devpriv = dev->private; 402 unsigned int chan = CR_CHAN(insn->chanspec); 403 unsigned int range = CR_RANGE(insn->chanspec); 404 unsigned int status; 405 int i; 406 407 for (i = 0; i < insn->n; i++) { 408 /* Set the range selection */ 409 writel(range, devpriv->mmio + 96); 410 411 /* Write the analog value to the selected channel */ 412 writel((data[i] << 8) | chan, devpriv->mmio + 100); 413 414 /* Wait the end of transfer */ 415 do { 416 status = readl(devpriv->mmio + 96); 417 } while ((status & 0x100) != 0x100); 418 } 419 420 return insn->n; 421} 422 423static int apci3xxx_di_insn_bits(struct comedi_device *dev, 424 struct comedi_subdevice *s, 425 struct comedi_insn *insn, 426 unsigned int *data) 427{ 428 data[1] = inl(dev->iobase + 32) & 0xf; 429 430 return insn->n; 431} 432 433static int apci3xxx_do_insn_bits(struct comedi_device *dev, 434 struct comedi_subdevice *s, 435 struct comedi_insn *insn, 436 unsigned int *data) 437{ 438 unsigned int mask = data[0]; 439 unsigned int bits = data[1]; 440 441 s->state = inl(dev->iobase + 48) & 0xf; 442 if (mask) { 443 s->state &= ~mask; 444 s->state |= (bits & mask); 445 446 outl(s->state, dev->iobase + 48); 447 } 448 449 data[1] = s->state; 450 451 return insn->n; 452} 453 454static int apci3xxx_dio_insn_config(struct comedi_device *dev, 455 struct comedi_subdevice *s, 456 struct comedi_insn *insn, 457 unsigned int *data) 458{ 459 unsigned int chan = CR_CHAN(insn->chanspec); 460 unsigned int mask = 1 << chan; 461 unsigned int bits; 462 463 /* 464 * Port 0 (channels 0-7) are always inputs 465 * Port 1 (channels 8-15) are always outputs 466 * Port 2 (channels 16-23) are programmable i/o 467 * 468 * Changing any channel in port 2 changes the entire port. 469 */ 470 if (mask & 0xff0000) 471 bits = 0xff0000; 472 else 473 bits = 0; 474 475 switch (data[0]) { 476 case INSN_CONFIG_DIO_INPUT: 477 s->io_bits &= ~bits; 478 break; 479 case INSN_CONFIG_DIO_OUTPUT: 480 s->io_bits |= bits; 481 break; 482 case INSN_CONFIG_DIO_QUERY: 483 data[1] = (s->io_bits & bits) ? COMEDI_OUTPUT : COMEDI_INPUT; 484 return insn->n; 485 default: 486 return -EINVAL; 487 } 488 489 /* update port 2 configuration */ 490 if (bits) 491 outl((s->io_bits >> 24) & 0xff, dev->iobase + 224); 492 493 return insn->n; 494} 495 496static int apci3xxx_dio_insn_bits(struct comedi_device *dev, 497 struct comedi_subdevice *s, 498 struct comedi_insn *insn, 499 unsigned int *data) 500{ 501 unsigned int mask = data[0]; 502 unsigned int bits = data[1]; 503 unsigned int val; 504 505 /* only update output channels */ 506 mask &= s->io_bits; 507 if (mask) { 508 s->state &= ~mask; 509 s->state |= (bits & mask); 510 511 if (mask & 0xff) 512 outl(s->state & 0xff, dev->iobase + 80); 513 if (mask & 0xff0000) 514 outl((s->state >> 16) & 0xff, dev->iobase + 112); 515 } 516 517 val = inl(dev->iobase + 80); 518 val |= (inl(dev->iobase + 64) << 8); 519 if (s->io_bits & 0xff0000) 520 val |= (inl(dev->iobase + 112) << 16); 521 else 522 val |= (inl(dev->iobase + 96) << 16); 523 524 data[1] = val; 525 526 return insn->n; 527} 528 529static int apci3xxx_reset(struct comedi_device *dev) 530{ 531 struct apci3xxx_private *devpriv = dev->private; 532 unsigned int val; 533 int i; 534 535 /* Disable the interrupt */ 536 disable_irq(dev->irq); 537 538 /* Reset the interrupt flag */ 539 devpriv->b_EocEosInterrupt = 0; 540 541 /* Clear the start command */ 542 writel(0, devpriv->mmio + 8); 543 544 /* Reset the interrupt flags */ 545 val = readl(devpriv->mmio + 16); 546 writel(val, devpriv->mmio + 16); 547 548 /* clear the EOS */ 549 readl(devpriv->mmio + 20); 550 551 /* Clear the FIFO */ 552 for (i = 0; i < 16; i++) 553 val = readl(devpriv->mmio + 28); 554 555 /* Enable the interrupt */ 556 enable_irq(dev->irq); 557 558 return 0; 559} 560 561static int apci3xxx_auto_attach(struct comedi_device *dev, 562 unsigned long context) 563{ 564 struct pci_dev *pcidev = comedi_to_pci_dev(dev); 565 const struct apci3xxx_boardinfo *board = NULL; 566 struct apci3xxx_private *devpriv; 567 struct comedi_subdevice *s; 568 int ret, n_subdevices; 569 570 if (context < ARRAY_SIZE(apci3xxx_boardtypes)) 571 board = &apci3xxx_boardtypes[context]; 572 if (!board) 573 return -ENODEV; 574 dev->board_ptr = board; 575 dev->board_name = board->name; 576 577 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL); 578 if (!devpriv) 579 return -ENOMEM; 580 dev->private = devpriv; 581 582 ret = comedi_pci_enable(dev); 583 if (ret) 584 return ret; 585 586 dev->iobase = pci_resource_start(pcidev, 2); 587 devpriv->mmio = pci_ioremap_bar(pcidev, 3); 588 589 if (pcidev->irq > 0) { 590 ret = request_irq(pcidev->irq, apci3xxx_irq_handler, 591 IRQF_SHARED, dev->board_name, dev); 592 if (ret == 0) 593 dev->irq = pcidev->irq; 594 } 595 596 n_subdevices = 7; 597 ret = comedi_alloc_subdevices(dev, n_subdevices); 598 if (ret) 599 return ret; 600 601 /* Allocate and Initialise AI Subdevice Structures */ 602 s = &dev->subdevices[0]; 603 if (board->ai_n_chan) { 604 dev->read_subdev = s; 605 s->type = COMEDI_SUBD_AI; 606 s->subdev_flags = SDF_READABLE | board->ai_subdev_flags; 607 s->n_chan = board->ai_n_chan; 608 s->maxdata = board->ai_maxdata; 609 s->len_chanlist = s->n_chan; 610 s->range_table = &apci3xxx_ai_range; 611 s->insn_config = apci3xxx_ai_insn_config; 612 s->insn_read = apci3xxx_ai_insn_read; 613 614 if ((board->ai_subdev_flags & (SDF_COMMON | SDF_GROUND)) == 0) 615 devpriv->b_SingelDiff = 1; 616 617 } else { 618 s->type = COMEDI_SUBD_UNUSED; 619 } 620 621 /* Analog Output subdevice */ 622 s = &dev->subdevices[1]; 623 if (board->has_ao) { 624 s->type = COMEDI_SUBD_AO; 625 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON; 626 s->n_chan = 4; 627 s->maxdata = 0x0fff; 628 s->range_table = &apci3xxx_ao_range; 629 s->insn_write = apci3xxx_ao_insn_write; 630 } else { 631 s->type = COMEDI_SUBD_UNUSED; 632 } 633 634 /* Digital Input subdevice */ 635 s = &dev->subdevices[2]; 636 if (board->has_dig_in) { 637 s->type = COMEDI_SUBD_DI; 638 s->subdev_flags = SDF_READABLE; 639 s->n_chan = 4; 640 s->maxdata = 1; 641 s->range_table = &range_digital; 642 s->insn_bits = apci3xxx_di_insn_bits; 643 } else { 644 s->type = COMEDI_SUBD_UNUSED; 645 } 646 647 /* Digital Output subdevice */ 648 s = &dev->subdevices[3]; 649 if (board->has_dig_out) { 650 s->type = COMEDI_SUBD_DO; 651 s->subdev_flags = SDF_WRITEABLE; 652 s->n_chan = 4; 653 s->maxdata = 1; 654 s->range_table = &range_digital; 655 s->insn_bits = apci3xxx_do_insn_bits; 656 } else { 657 s->type = COMEDI_SUBD_UNUSED; 658 } 659 660 /* Allocate and Initialise Timer Subdevice Structures */ 661 s = &dev->subdevices[4]; 662 s->type = COMEDI_SUBD_UNUSED; 663 664 /* TTL Digital I/O subdevice */ 665 s = &dev->subdevices[5]; 666 if (board->has_ttl_io) { 667 s->type = COMEDI_SUBD_DIO; 668 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE; 669 s->n_chan = 24; 670 s->maxdata = 1; 671 s->io_bits = 0xff; /* channels 0-7 are always outputs */ 672 s->range_table = &range_digital; 673 s->insn_config = apci3xxx_dio_insn_config; 674 s->insn_bits = apci3xxx_dio_insn_bits; 675 } else { 676 s->type = COMEDI_SUBD_UNUSED; 677 } 678 679 /* EEPROM */ 680 s = &dev->subdevices[6]; 681 s->type = COMEDI_SUBD_UNUSED; 682 683 apci3xxx_reset(dev); 684 return 0; 685} 686 687static void apci3xxx_detach(struct comedi_device *dev) 688{ 689 struct apci3xxx_private *devpriv = dev->private; 690 691 if (devpriv) { 692 if (dev->iobase) 693 apci3xxx_reset(dev); 694 if (dev->irq) 695 free_irq(dev->irq, dev); 696 if (devpriv->mmio) 697 iounmap(devpriv->mmio); 698 } 699 comedi_pci_disable(dev); 700} 701 702static struct comedi_driver apci3xxx_driver = { 703 .driver_name = "addi_apci_3xxx", 704 .module = THIS_MODULE, 705 .auto_attach = apci3xxx_auto_attach, 706 .detach = apci3xxx_detach, 707}; 708 709static int apci3xxx_pci_probe(struct pci_dev *dev, 710 const struct pci_device_id *id) 711{ 712 return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data); 713} 714 715static DEFINE_PCI_DEVICE_TABLE(apci3xxx_pci_table) = { 716 { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 }, 717 { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 }, 718 { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 }, 719 { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 }, 720 { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 }, 721 { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 }, 722 { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 }, 723 { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 }, 724 { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 }, 725 { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 }, 726 { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 }, 727 { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 }, 728 { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 }, 729 { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 }, 730 { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 }, 731 { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 }, 732 { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 }, 733 { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 }, 734 { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 }, 735 { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 }, 736 { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 }, 737 { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 }, 738 { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 }, 739 { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 }, 740 { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 }, 741 { 0 } 742}; 743MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table); 744 745static struct pci_driver apci3xxx_pci_driver = { 746 .name = "addi_apci_3xxx", 747 .id_table = apci3xxx_pci_table, 748 .probe = apci3xxx_pci_probe, 749 .remove = comedi_pci_auto_unconfig, 750}; 751module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver); 752 753MODULE_AUTHOR("Comedi http://www.comedi.org"); 754MODULE_DESCRIPTION("Comedi low-level driver"); 755MODULE_LICENSE("GPL"); 756