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