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