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