This source file includes following definitions.
- apci1564_reset
 
- apci1564_interrupt
 
- apci1564_di_insn_bits
 
- apci1564_do_insn_bits
 
- apci1564_diag_insn_bits
 
- apci1564_cos_insn_config
 
- apci1564_cos_insn_bits
 
- apci1564_cos_cmdtest
 
- apci1564_cos_cmd
 
- apci1564_cos_cancel
 
- apci1564_timer_insn_config
 
- apci1564_timer_insn_write
 
- apci1564_timer_insn_read
 
- apci1564_counter_insn_config
 
- apci1564_counter_insn_write
 
- apci1564_counter_insn_read
 
- apci1564_auto_attach
 
- apci1564_detach
 
- apci1564_pci_probe
 
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 
  30 
  31 
  32 
  33 
  34 
  35 
  36 
  37 
  38 
  39 
  40 
  41 
  42 
  43 
  44 
  45 
  46 
  47 
  48 
  49 
  50 
  51 
  52 
  53 
  54 
  55 
  56 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 
  67 
  68 
  69 #include <linux/module.h>
  70 #include <linux/interrupt.h>
  71 
  72 #include "../comedi_pci.h"
  73 #include "addi_tcw.h"
  74 #include "addi_watchdog.h"
  75 
  76 
  77 
  78 
  79 
  80 
  81 
  82 
  83 
  84 
  85 
  86 
  87 
  88 
  89 
  90 #define APCI1564_EEPROM_REG                     0x00
  91 #define APCI1564_EEPROM_VCC_STATUS              BIT(8)
  92 #define APCI1564_EEPROM_TO_REV(x)               (((x) >> 4) & 0xf)
  93 #define APCI1564_EEPROM_DI                      BIT(3)
  94 #define APCI1564_EEPROM_DO                      BIT(2)
  95 #define APCI1564_EEPROM_CS                      BIT(1)
  96 #define APCI1564_EEPROM_CLK                     BIT(0)
  97 #define APCI1564_REV1_TIMER_IOBASE              0x04
  98 #define APCI1564_REV2_MAIN_IOBASE               0x04
  99 #define APCI1564_REV2_TIMER_IOBASE              0x48
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 #define APCI1564_REV1_MAIN_IOBASE               0x00
 115 
 116 
 117 
 118 
 119 
 120 
 121 #define APCI1564_DI_REG                         0x00
 122 #define APCI1564_DI_INT_MODE1_REG               0x04
 123 #define APCI1564_DI_INT_MODE2_REG               0x08
 124 #define APCI1564_DI_INT_MODE_MASK               0x000ffff0 
 125 #define APCI1564_DI_INT_STATUS_REG              0x0c
 126 #define APCI1564_DI_IRQ_REG                     0x10
 127 #define APCI1564_DI_IRQ_ENA                     BIT(2)
 128 #define APCI1564_DI_IRQ_MODE                    BIT(1)  
 129 #define APCI1564_DO_REG                         0x14
 130 #define APCI1564_DO_INT_CTRL_REG                0x18
 131 #define APCI1564_DO_INT_CTRL_CC_INT_ENA         BIT(1)
 132 #define APCI1564_DO_INT_CTRL_VCC_INT_ENA        BIT(0)
 133 #define APCI1564_DO_INT_STATUS_REG              0x1c
 134 #define APCI1564_DO_INT_STATUS_CC               BIT(1)
 135 #define APCI1564_DO_INT_STATUS_VCC              BIT(0)
 136 #define APCI1564_DO_IRQ_REG                     0x20
 137 #define APCI1564_DO_IRQ_INTR                    BIT(0)
 138 #define APCI1564_WDOG_IOBASE                    0x24
 139 
 140 
 141 
 142 
 143 
 144 
 145 
 146 
 147 
 148 
 149 
 150 #define APCI1564_COUNTER(x)                     ((x) * 0x20)
 151 
 152 
 153 
 154 
 155 
 156 #define APCI1564_EVENT_COS                      BIT(31)
 157 #define APCI1564_EVENT_TIMER                    BIT(30)
 158 #define APCI1564_EVENT_COUNTER(x)               BIT(27 + (x)) 
 159 #define APCI1564_EVENT_MASK                     0xfff0000f 
 160 
 161 struct apci1564_private {
 162         unsigned long eeprom;   
 163         unsigned long timer;    
 164         unsigned long counters; 
 165         unsigned int mode1;     
 166         unsigned int mode2;     
 167         unsigned int ctrl;      
 168 };
 169 
 170 static int apci1564_reset(struct comedi_device *dev)
 171 {
 172         struct apci1564_private *devpriv = dev->private;
 173 
 174         
 175         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
 176         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
 177         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
 178         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
 179 
 180         
 181         outl(0x0, dev->iobase + APCI1564_DO_REG);
 182         outl(0x0, dev->iobase + APCI1564_DO_INT_CTRL_REG);
 183 
 184         
 185         addi_watchdog_reset(dev->iobase + APCI1564_WDOG_IOBASE);
 186 
 187         
 188         outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
 189         outl(0x0, devpriv->timer + ADDI_TCW_RELOAD_REG);
 190 
 191         if (devpriv->counters) {
 192                 unsigned long iobase = devpriv->counters + ADDI_TCW_CTRL_REG;
 193 
 194                 
 195                 outl(0x0, iobase + APCI1564_COUNTER(0));
 196                 outl(0x0, iobase + APCI1564_COUNTER(1));
 197                 outl(0x0, iobase + APCI1564_COUNTER(2));
 198         }
 199 
 200         return 0;
 201 }
 202 
 203 static irqreturn_t apci1564_interrupt(int irq, void *d)
 204 {
 205         struct comedi_device *dev = d;
 206         struct apci1564_private *devpriv = dev->private;
 207         struct comedi_subdevice *s = dev->read_subdev;
 208         unsigned int status;
 209         unsigned int ctrl;
 210         unsigned int chan;
 211 
 212         s->state &= ~APCI1564_EVENT_MASK;
 213 
 214         status = inl(dev->iobase + APCI1564_DI_IRQ_REG);
 215         if (status & APCI1564_DI_IRQ_ENA) {
 216                 
 217                 s->state = inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
 218                 s->state &= APCI1564_DI_INT_MODE_MASK;
 219                 s->state |= APCI1564_EVENT_COS;
 220 
 221                 
 222                 outl(status & ~APCI1564_DI_IRQ_ENA,
 223                      dev->iobase + APCI1564_DI_IRQ_REG);
 224                 outl(status, dev->iobase + APCI1564_DI_IRQ_REG);
 225         }
 226 
 227         status = inl(devpriv->timer + ADDI_TCW_IRQ_REG);
 228         if (status & ADDI_TCW_IRQ) {
 229                 s->state |= APCI1564_EVENT_TIMER;
 230 
 231                 
 232                 ctrl = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
 233                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
 234                 outl(ctrl, devpriv->timer + ADDI_TCW_CTRL_REG);
 235         }
 236 
 237         if (devpriv->counters) {
 238                 for (chan = 0; chan < 3; chan++) {
 239                         unsigned long iobase;
 240 
 241                         iobase = devpriv->counters + APCI1564_COUNTER(chan);
 242 
 243                         status = inl(iobase + ADDI_TCW_IRQ_REG);
 244                         if (status & ADDI_TCW_IRQ) {
 245                                 s->state |= APCI1564_EVENT_COUNTER(chan);
 246 
 247                                 
 248                                 ctrl = inl(iobase + ADDI_TCW_CTRL_REG);
 249                                 outl(0x0, iobase + ADDI_TCW_CTRL_REG);
 250                                 outl(ctrl, iobase + ADDI_TCW_CTRL_REG);
 251                         }
 252                 }
 253         }
 254 
 255         if (s->state & APCI1564_EVENT_MASK) {
 256                 comedi_buf_write_samples(s, &s->state, 1);
 257                 comedi_handle_events(dev, s);
 258         }
 259 
 260         return IRQ_HANDLED;
 261 }
 262 
 263 static int apci1564_di_insn_bits(struct comedi_device *dev,
 264                                  struct comedi_subdevice *s,
 265                                  struct comedi_insn *insn,
 266                                  unsigned int *data)
 267 {
 268         data[1] = inl(dev->iobase + APCI1564_DI_REG);
 269 
 270         return insn->n;
 271 }
 272 
 273 static int apci1564_do_insn_bits(struct comedi_device *dev,
 274                                  struct comedi_subdevice *s,
 275                                  struct comedi_insn *insn,
 276                                  unsigned int *data)
 277 {
 278         s->state = inl(dev->iobase + APCI1564_DO_REG);
 279 
 280         if (comedi_dio_update_state(s, data))
 281                 outl(s->state, dev->iobase + APCI1564_DO_REG);
 282 
 283         data[1] = s->state;
 284 
 285         return insn->n;
 286 }
 287 
 288 static int apci1564_diag_insn_bits(struct comedi_device *dev,
 289                                    struct comedi_subdevice *s,
 290                                    struct comedi_insn *insn,
 291                                    unsigned int *data)
 292 {
 293         data[1] = inl(dev->iobase + APCI1564_DO_INT_STATUS_REG) & 3;
 294 
 295         return insn->n;
 296 }
 297 
 298 
 299 
 300 
 301 
 302 
 303 
 304 
 305 
 306 
 307 
 308 
 309 
 310 
 311 
 312 
 313 
 314 
 315 
 316 
 317 
 318 
 319 
 320 
 321 
 322 
 323 
 324 
 325 
 326 
 327 
 328 static int apci1564_cos_insn_config(struct comedi_device *dev,
 329                                     struct comedi_subdevice *s,
 330                                     struct comedi_insn *insn,
 331                                     unsigned int *data)
 332 {
 333         struct apci1564_private *devpriv = dev->private;
 334         unsigned int shift, oldmask;
 335 
 336         switch (data[0]) {
 337         case INSN_CONFIG_DIGITAL_TRIG:
 338                 if (data[1] != 0)
 339                         return -EINVAL;
 340                 shift = data[3];
 341                 oldmask = (1U << shift) - 1;
 342                 switch (data[2]) {
 343                 case COMEDI_DIGITAL_TRIG_DISABLE:
 344                         devpriv->ctrl = 0;
 345                         devpriv->mode1 = 0;
 346                         devpriv->mode2 = 0;
 347                         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
 348                         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
 349                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
 350                         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
 351                         break;
 352                 case COMEDI_DIGITAL_TRIG_ENABLE_EDGES:
 353                         if (devpriv->ctrl != APCI1564_DI_IRQ_ENA) {
 354                                 
 355                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA;
 356                                 
 357                                 devpriv->mode1 = 0;
 358                                 devpriv->mode2 = 0;
 359                         } else {
 360                                 
 361                                 devpriv->mode1 &= oldmask;
 362                                 devpriv->mode2 &= oldmask;
 363                         }
 364                         
 365                         devpriv->mode1 |= data[4] << shift;
 366                         devpriv->mode2 |= data[5] << shift;
 367                         break;
 368                 case COMEDI_DIGITAL_TRIG_ENABLE_LEVELS:
 369                         if (devpriv->ctrl != (APCI1564_DI_IRQ_ENA |
 370                                               APCI1564_DI_IRQ_MODE)) {
 371                                 
 372                                 devpriv->ctrl = APCI1564_DI_IRQ_ENA |
 373                                                 APCI1564_DI_IRQ_MODE;
 374                                 
 375                                 devpriv->mode1 = 0;
 376                                 devpriv->mode2 = 0;
 377                         } else {
 378                                 
 379                                 devpriv->mode1 &= oldmask;
 380                                 devpriv->mode2 &= oldmask;
 381                         }
 382                         
 383                         devpriv->mode1 |= data[4] << shift;
 384                         devpriv->mode2 |= data[5] << shift;
 385                         break;
 386                 default:
 387                         return -EINVAL;
 388                 }
 389 
 390                 
 391                 devpriv->mode1 &= APCI1564_DI_INT_MODE_MASK;
 392                 devpriv->mode2 &= APCI1564_DI_INT_MODE_MASK;
 393                 break;
 394         default:
 395                 return -EINVAL;
 396         }
 397         return insn->n;
 398 }
 399 
 400 static int apci1564_cos_insn_bits(struct comedi_device *dev,
 401                                   struct comedi_subdevice *s,
 402                                   struct comedi_insn *insn,
 403                                   unsigned int *data)
 404 {
 405         data[1] = s->state;
 406 
 407         return 0;
 408 }
 409 
 410 static int apci1564_cos_cmdtest(struct comedi_device *dev,
 411                                 struct comedi_subdevice *s,
 412                                 struct comedi_cmd *cmd)
 413 {
 414         int err = 0;
 415 
 416         
 417 
 418         err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
 419         err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
 420         err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
 421         err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
 422         err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
 423 
 424         if (err)
 425                 return 1;
 426 
 427         
 428         
 429 
 430         
 431 
 432         err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
 433         err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
 434         err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
 435         err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
 436                                            cmd->chanlist_len);
 437         err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
 438 
 439         if (err)
 440                 return 3;
 441 
 442         
 443 
 444         
 445 
 446         return 0;
 447 }
 448 
 449 
 450 
 451 
 452 
 453 
 454 static int apci1564_cos_cmd(struct comedi_device *dev,
 455                             struct comedi_subdevice *s)
 456 {
 457         struct apci1564_private *devpriv = dev->private;
 458 
 459         if (!devpriv->ctrl && !(devpriv->mode1 || devpriv->mode2)) {
 460                 dev_warn(dev->class_dev,
 461                          "Interrupts disabled due to mode configuration!\n");
 462                 return -EINVAL;
 463         }
 464 
 465         outl(devpriv->mode1, dev->iobase + APCI1564_DI_INT_MODE1_REG);
 466         outl(devpriv->mode2, dev->iobase + APCI1564_DI_INT_MODE2_REG);
 467         outl(devpriv->ctrl, dev->iobase + APCI1564_DI_IRQ_REG);
 468 
 469         return 0;
 470 }
 471 
 472 static int apci1564_cos_cancel(struct comedi_device *dev,
 473                                struct comedi_subdevice *s)
 474 {
 475         outl(0x0, dev->iobase + APCI1564_DI_IRQ_REG);
 476         inl(dev->iobase + APCI1564_DI_INT_STATUS_REG);
 477         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE1_REG);
 478         outl(0x0, dev->iobase + APCI1564_DI_INT_MODE2_REG);
 479 
 480         return 0;
 481 }
 482 
 483 static int apci1564_timer_insn_config(struct comedi_device *dev,
 484                                       struct comedi_subdevice *s,
 485                                       struct comedi_insn *insn,
 486                                       unsigned int *data)
 487 {
 488         struct apci1564_private *devpriv = dev->private;
 489         unsigned int val;
 490 
 491         switch (data[0]) {
 492         case INSN_CONFIG_ARM:
 493                 if (data[1] > s->maxdata)
 494                         return -EINVAL;
 495                 outl(data[1], devpriv->timer + ADDI_TCW_RELOAD_REG);
 496                 outl(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_TIMER_ENA,
 497                      devpriv->timer + ADDI_TCW_CTRL_REG);
 498                 break;
 499         case INSN_CONFIG_DISARM:
 500                 outl(0x0, devpriv->timer + ADDI_TCW_CTRL_REG);
 501                 break;
 502         case INSN_CONFIG_GET_COUNTER_STATUS:
 503                 data[1] = 0;
 504                 val = inl(devpriv->timer + ADDI_TCW_CTRL_REG);
 505                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
 506                         data[1] |= COMEDI_COUNTER_ARMED;
 507                 if (val & ADDI_TCW_CTRL_TIMER_ENA)
 508                         data[1] |= COMEDI_COUNTER_COUNTING;
 509                 val = inl(devpriv->timer + ADDI_TCW_STATUS_REG);
 510                 if (val & ADDI_TCW_STATUS_OVERFLOW)
 511                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
 512                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
 513                           COMEDI_COUNTER_TERMINAL_COUNT;
 514                 break;
 515         case INSN_CONFIG_SET_CLOCK_SRC:
 516                 if (data[2] > s->maxdata)
 517                         return -EINVAL;
 518                 outl(data[1], devpriv->timer + ADDI_TCW_TIMEBASE_REG);
 519                 outl(data[2], devpriv->timer + ADDI_TCW_RELOAD_REG);
 520                 break;
 521         case INSN_CONFIG_GET_CLOCK_SRC:
 522                 data[1] = inl(devpriv->timer + ADDI_TCW_TIMEBASE_REG);
 523                 data[2] = inl(devpriv->timer + ADDI_TCW_RELOAD_REG);
 524                 break;
 525         default:
 526                 return -EINVAL;
 527         }
 528 
 529         return insn->n;
 530 }
 531 
 532 static int apci1564_timer_insn_write(struct comedi_device *dev,
 533                                      struct comedi_subdevice *s,
 534                                      struct comedi_insn *insn,
 535                                      unsigned int *data)
 536 {
 537         struct apci1564_private *devpriv = dev->private;
 538 
 539         
 540         if (insn->n) {
 541                 unsigned int val = data[insn->n - 1];
 542 
 543                 outl(val, devpriv->timer + ADDI_TCW_RELOAD_REG);
 544         }
 545 
 546         return insn->n;
 547 }
 548 
 549 static int apci1564_timer_insn_read(struct comedi_device *dev,
 550                                     struct comedi_subdevice *s,
 551                                     struct comedi_insn *insn,
 552                                     unsigned int *data)
 553 {
 554         struct apci1564_private *devpriv = dev->private;
 555         int i;
 556 
 557         
 558         for (i = 0; i < insn->n; i++)
 559                 data[i] = inl(devpriv->timer + ADDI_TCW_VAL_REG);
 560 
 561         return insn->n;
 562 }
 563 
 564 static int apci1564_counter_insn_config(struct comedi_device *dev,
 565                                         struct comedi_subdevice *s,
 566                                         struct comedi_insn *insn,
 567                                         unsigned int *data)
 568 {
 569         struct apci1564_private *devpriv = dev->private;
 570         unsigned int chan = CR_CHAN(insn->chanspec);
 571         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
 572         unsigned int val;
 573 
 574         switch (data[0]) {
 575         case INSN_CONFIG_ARM:
 576                 val = inl(iobase + ADDI_TCW_CTRL_REG);
 577                 val |= ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA;
 578                 outl(data[1], iobase + ADDI_TCW_RELOAD_REG);
 579                 outl(val, iobase + ADDI_TCW_CTRL_REG);
 580                 break;
 581         case INSN_CONFIG_DISARM:
 582                 val = inl(iobase + ADDI_TCW_CTRL_REG);
 583                 val &= ~(ADDI_TCW_CTRL_IRQ_ENA | ADDI_TCW_CTRL_CNTR_ENA);
 584                 outl(val, iobase + ADDI_TCW_CTRL_REG);
 585                 break;
 586         case INSN_CONFIG_SET_COUNTER_MODE:
 587                 
 588 
 589 
 590 
 591 
 592                 outl(data[1], iobase + ADDI_TCW_CTRL_REG);
 593                 break;
 594         case INSN_CONFIG_GET_COUNTER_STATUS:
 595                 data[1] = 0;
 596                 val = inl(iobase + ADDI_TCW_CTRL_REG);
 597                 if (val & ADDI_TCW_CTRL_IRQ_ENA)
 598                         data[1] |= COMEDI_COUNTER_ARMED;
 599                 if (val & ADDI_TCW_CTRL_CNTR_ENA)
 600                         data[1] |= COMEDI_COUNTER_COUNTING;
 601                 val = inl(iobase + ADDI_TCW_STATUS_REG);
 602                 if (val & ADDI_TCW_STATUS_OVERFLOW)
 603                         data[1] |= COMEDI_COUNTER_TERMINAL_COUNT;
 604                 data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING |
 605                           COMEDI_COUNTER_TERMINAL_COUNT;
 606                 break;
 607         default:
 608                 return -EINVAL;
 609         }
 610 
 611         return insn->n;
 612 }
 613 
 614 static int apci1564_counter_insn_write(struct comedi_device *dev,
 615                                        struct comedi_subdevice *s,
 616                                        struct comedi_insn *insn,
 617                                        unsigned int *data)
 618 {
 619         struct apci1564_private *devpriv = dev->private;
 620         unsigned int chan = CR_CHAN(insn->chanspec);
 621         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
 622 
 623         
 624         if (insn->n) {
 625                 unsigned int val = data[insn->n - 1];
 626 
 627                 outl(val, iobase + ADDI_TCW_RELOAD_REG);
 628         }
 629 
 630         return insn->n;
 631 }
 632 
 633 static int apci1564_counter_insn_read(struct comedi_device *dev,
 634                                       struct comedi_subdevice *s,
 635                                       struct comedi_insn *insn,
 636                                       unsigned int *data)
 637 {
 638         struct apci1564_private *devpriv = dev->private;
 639         unsigned int chan = CR_CHAN(insn->chanspec);
 640         unsigned long iobase = devpriv->counters + APCI1564_COUNTER(chan);
 641         int i;
 642 
 643         
 644         for (i = 0; i < insn->n; i++)
 645                 data[i] = inl(iobase + ADDI_TCW_VAL_REG);
 646 
 647         return insn->n;
 648 }
 649 
 650 static int apci1564_auto_attach(struct comedi_device *dev,
 651                                 unsigned long context_unused)
 652 {
 653         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 654         struct apci1564_private *devpriv;
 655         struct comedi_subdevice *s;
 656         unsigned int val;
 657         int ret;
 658 
 659         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 660         if (!devpriv)
 661                 return -ENOMEM;
 662 
 663         ret = comedi_pci_enable(dev);
 664         if (ret)
 665                 return ret;
 666 
 667         
 668         devpriv->eeprom = pci_resource_start(pcidev, 0);
 669         val = inl(devpriv->eeprom + APCI1564_EEPROM_REG);
 670         if (APCI1564_EEPROM_TO_REV(val) == 0) {
 671                 
 672                 dev->iobase = pci_resource_start(pcidev, 1) +
 673                               APCI1564_REV1_MAIN_IOBASE;
 674                 devpriv->timer = devpriv->eeprom + APCI1564_REV1_TIMER_IOBASE;
 675         } else {
 676                 
 677                 dev->iobase = devpriv->eeprom + APCI1564_REV2_MAIN_IOBASE;
 678                 devpriv->timer = devpriv->eeprom + APCI1564_REV2_TIMER_IOBASE;
 679                 devpriv->counters = pci_resource_start(pcidev, 1);
 680         }
 681 
 682         apci1564_reset(dev);
 683 
 684         if (pcidev->irq > 0) {
 685                 ret = request_irq(pcidev->irq, apci1564_interrupt, IRQF_SHARED,
 686                                   dev->board_name, dev);
 687                 if (ret == 0)
 688                         dev->irq = pcidev->irq;
 689         }
 690 
 691         ret = comedi_alloc_subdevices(dev, 7);
 692         if (ret)
 693                 return ret;
 694 
 695         
 696         s = &dev->subdevices[0];
 697         s->type         = COMEDI_SUBD_DI;
 698         s->subdev_flags = SDF_READABLE;
 699         s->n_chan       = 32;
 700         s->maxdata      = 1;
 701         s->range_table  = &range_digital;
 702         s->insn_bits    = apci1564_di_insn_bits;
 703 
 704         
 705         s = &dev->subdevices[1];
 706         s->type         = COMEDI_SUBD_DO;
 707         s->subdev_flags = SDF_WRITABLE;
 708         s->n_chan       = 32;
 709         s->maxdata      = 1;
 710         s->range_table  = &range_digital;
 711         s->insn_bits    = apci1564_do_insn_bits;
 712 
 713         
 714         s = &dev->subdevices[2];
 715         if (dev->irq) {
 716                 dev->read_subdev = s;
 717                 s->type         = COMEDI_SUBD_DI;
 718                 s->subdev_flags = SDF_READABLE | SDF_CMD_READ | SDF_LSAMPL;
 719                 s->n_chan       = 1;
 720                 s->maxdata      = 1;
 721                 s->range_table  = &range_digital;
 722                 s->len_chanlist = 1;
 723                 s->insn_config  = apci1564_cos_insn_config;
 724                 s->insn_bits    = apci1564_cos_insn_bits;
 725                 s->do_cmdtest   = apci1564_cos_cmdtest;
 726                 s->do_cmd       = apci1564_cos_cmd;
 727                 s->cancel       = apci1564_cos_cancel;
 728         } else {
 729                 s->type         = COMEDI_SUBD_UNUSED;
 730         }
 731 
 732         
 733         s = &dev->subdevices[3];
 734         s->type         = COMEDI_SUBD_TIMER;
 735         s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
 736         s->n_chan       = 1;
 737         s->maxdata      = 0x0fff;
 738         s->range_table  = &range_digital;
 739         s->insn_config  = apci1564_timer_insn_config;
 740         s->insn_write   = apci1564_timer_insn_write;
 741         s->insn_read    = apci1564_timer_insn_read;
 742 
 743         
 744         s = &dev->subdevices[4];
 745         if (devpriv->counters) {
 746                 s->type         = COMEDI_SUBD_COUNTER;
 747                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
 748                 s->n_chan       = 3;
 749                 s->maxdata      = 0xffffffff;
 750                 s->range_table  = &range_digital;
 751                 s->insn_config  = apci1564_counter_insn_config;
 752                 s->insn_write   = apci1564_counter_insn_write;
 753                 s->insn_read    = apci1564_counter_insn_read;
 754         } else {
 755                 s->type         = COMEDI_SUBD_UNUSED;
 756         }
 757 
 758         
 759         s = &dev->subdevices[5];
 760         ret = addi_watchdog_init(s, dev->iobase + APCI1564_WDOG_IOBASE);
 761         if (ret)
 762                 return ret;
 763 
 764         
 765         s = &dev->subdevices[6];
 766         s->type         = COMEDI_SUBD_DI;
 767         s->subdev_flags = SDF_READABLE;
 768         s->n_chan       = 2;
 769         s->maxdata      = 1;
 770         s->range_table  = &range_digital;
 771         s->insn_bits    = apci1564_diag_insn_bits;
 772 
 773         return 0;
 774 }
 775 
 776 static void apci1564_detach(struct comedi_device *dev)
 777 {
 778         if (dev->iobase)
 779                 apci1564_reset(dev);
 780         comedi_pci_detach(dev);
 781 }
 782 
 783 static struct comedi_driver apci1564_driver = {
 784         .driver_name    = "addi_apci_1564",
 785         .module         = THIS_MODULE,
 786         .auto_attach    = apci1564_auto_attach,
 787         .detach         = apci1564_detach,
 788 };
 789 
 790 static int apci1564_pci_probe(struct pci_dev *dev,
 791                               const struct pci_device_id *id)
 792 {
 793         return comedi_pci_auto_config(dev, &apci1564_driver, id->driver_data);
 794 }
 795 
 796 static const struct pci_device_id apci1564_pci_table[] = {
 797         { PCI_DEVICE(PCI_VENDOR_ID_ADDIDATA, 0x1006) },
 798         { 0 }
 799 };
 800 MODULE_DEVICE_TABLE(pci, apci1564_pci_table);
 801 
 802 static struct pci_driver apci1564_pci_driver = {
 803         .name           = "addi_apci_1564",
 804         .id_table       = apci1564_pci_table,
 805         .probe          = apci1564_pci_probe,
 806         .remove         = comedi_pci_auto_unconfig,
 807 };
 808 module_comedi_pci_driver(apci1564_driver, apci1564_pci_driver);
 809 
 810 MODULE_AUTHOR("Comedi http://www.comedi.org");
 811 MODULE_DESCRIPTION("ADDI-DATA APCI-1564, 32 channel DI / 32 channel DO boards");
 812 MODULE_LICENSE("GPL");