linux/drivers/staging/comedi/drivers/daqboard2000.c
<<
>>
Prefs
   1/*
   2 * comedi/drivers/daqboard2000.c
   3 * hardware driver for IOtech DAQboard/2000
   4 *
   5 * COMEDI - Linux Control and Measurement Device Interface
   6 * Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18/*
  19 * Driver: daqboard2000
  20 * Description: IOTech DAQBoard/2000
  21 * Author: Anders Blomdell <anders.blomdell@control.lth.se>
  22 * Status: works
  23 * Updated: Mon, 14 Apr 2008 15:28:52 +0100
  24 * Devices: [IOTech] DAQBoard/2000 (daqboard2000)
  25 *
  26 * Much of the functionality of this driver was determined from reading
  27 * the source code for the Windows driver.
  28 *
  29 * The FPGA on the board requires fimware, which is available from
  30 * http://www.comedi.org in the comedi_nonfree_firmware tarball.
  31 *
  32 * Configuration options: not applicable, uses PCI auto config
  33 */
  34/*
  35 * This card was obviously never intended to leave the Windows world,
  36 * since it lacked all kind of hardware documentation (except for cable
  37 * pinouts, plug and pray has something to catch up with yet).
  38 *
  39 * With some help from our swedish distributor, we got the Windows sourcecode
  40 * for the card, and here are the findings so far.
  41 *
  42 * 1. A good document that describes the PCI interface chip is 9080db-106.pdf
  43 *    available from http://www.plxtech.com/products/io/pci9080
  44 *
  45 * 2. The initialization done so far is:
  46 *      a. program the FPGA (windows code sans a lot of error messages)
  47 *      b.
  48 *
  49 * 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
  50 *    you have to output values to all enabled DAC's until result appears, I
  51 *    guess that it has something to do with pacer clocks, but the source
  52 *    gives me no clues. I'll keep it simple so far.
  53 *
  54 * 4. Analog in.
  55 *    Each channel in the scanlist seems to be controlled by four
  56 *    control words:
  57 *
  58 *      Word0:
  59 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  60 *        ! | | | ! | | | ! | | | ! | | | !
  61 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  62 *
  63 *      Word1:
  64 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  65 *        ! | | | ! | | | ! | | | ! | | | !
  66 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  67 *         |             |       | | | | |
  68 *         +------+------+       | | | | +-- Digital input (??)
  69 *                |              | | | +---- 10 us settling time
  70 *                |              | | +------ Suspend acquisition (last to scan)
  71 *                |              | +-------- Simultaneous sample and hold
  72 *                |              +---------- Signed data format
  73 *                +------------------------- Correction offset low
  74 *
  75 *      Word2:
  76 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  77 *        ! | | | ! | | | ! | | | ! | | | !
  78 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  79 *         |     | |     | | | | | |     |
  80 *         +-----+ +--+--+ +++ +++ +--+--+
  81 *            |       |     |   |     +----- Expansion channel
  82 *            |       |     |   +----------- Expansion gain
  83 *            |       |     +--------------- Channel (low)
  84 *            |       +--------------------- Correction offset high
  85 *            +----------------------------- Correction gain low
  86 *      Word3:
  87 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  88 *        ! | | | ! | | | ! | | | ! | | | !
  89 *        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  90 *         |             | | | |   | | | |
  91 *         +------+------+ | | +-+-+ | | +-- Low bank enable
  92 *                |        | |   |   | +---- High bank enable
  93 *                |        | |   |   +------ Hi/low select
  94 *                |        | |   +---------- Gain (1,?,2,4,8,16,32,64)
  95 *                |        | +-------------- differential/single ended
  96 *                |        +---------------- Unipolar
  97 *                +------------------------- Correction gain high
  98 *
  99 * 999. The card seems to have an incredible amount of capabilities, but
 100 *      trying to reverse engineer them from the Windows source is beyond my
 101 *      patience.
 102 *
 103 */
 104
 105#include <linux/module.h>
 106#include <linux/delay.h>
 107#include <linux/interrupt.h>
 108
 109#include "../comedi_pci.h"
 110
 111#include "8255.h"
 112
 113#define DAQBOARD2000_FIRMWARE           "daqboard2000_firmware.bin"
 114
 115#define DAQBOARD2000_SUBSYSTEM_IDS2     0x0002  /* Daqboard/2000 - 2 Dacs */
 116#define DAQBOARD2000_SUBSYSTEM_IDS4     0x0004  /* Daqboard/2000 - 4 Dacs */
 117
 118/* Initialization bits for the Serial EEPROM Control Register */
 119#define DAQBOARD2000_SECRProgPinHi      0x8001767e
 120#define DAQBOARD2000_SECRProgPinLo      0x8000767e
 121#define DAQBOARD2000_SECRLocalBusHi     0xc000767e
 122#define DAQBOARD2000_SECRLocalBusLo     0x8000767e
 123#define DAQBOARD2000_SECRReloadHi       0xa000767e
 124#define DAQBOARD2000_SECRReloadLo       0x8000767e
 125
 126/* SECR status bits */
 127#define DAQBOARD2000_EEPROM_PRESENT     0x10000000
 128
 129/* CPLD status bits */
 130#define DAQBOARD2000_CPLD_INIT          0x0002
 131#define DAQBOARD2000_CPLD_DONE          0x0004
 132
 133static const struct comedi_lrange range_daqboard2000_ai = {
 134        13, {
 135                BIP_RANGE(10),
 136                BIP_RANGE(5),
 137                BIP_RANGE(2.5),
 138                BIP_RANGE(1.25),
 139                BIP_RANGE(0.625),
 140                BIP_RANGE(0.3125),
 141                BIP_RANGE(0.156),
 142                UNI_RANGE(10),
 143                UNI_RANGE(5),
 144                UNI_RANGE(2.5),
 145                UNI_RANGE(1.25),
 146                UNI_RANGE(0.625),
 147                UNI_RANGE(0.3125)
 148        }
 149};
 150
 151/*
 152 * Register Memory Map
 153 */
 154#define acqControl                      0x00            /* u16 */
 155#define acqScanListFIFO                 0x02            /* u16 */
 156#define acqPacerClockDivLow             0x04            /* u32 */
 157#define acqScanCounter                  0x08            /* u16 */
 158#define acqPacerClockDivHigh            0x0a            /* u16 */
 159#define acqTriggerCount                 0x0c            /* u16 */
 160#define acqResultsFIFO                  0x10            /* u16 */
 161#define acqResultsShadow                0x14            /* u16 */
 162#define acqAdcResult                    0x18            /* u16 */
 163#define dacScanCounter                  0x1c            /* u16 */
 164#define dacControl                      0x20            /* u16 */
 165#define dacFIFO                         0x24            /* s16 */
 166#define dacPacerClockDiv                0x2a            /* u16 */
 167#define refDacs                         0x2c            /* u16 */
 168#define dioControl                      0x30            /* u16 */
 169#define dioP3hsioData                   0x32            /* s16 */
 170#define dioP3Control                    0x34            /* u16 */
 171#define calEepromControl                0x36            /* u16 */
 172#define dacSetting(x)                   (0x38 + (x)*2)  /* s16 */
 173#define dioP2ExpansionIO8Bit            0x40            /* s16 */
 174#define ctrTmrControl                   0x80            /* u16 */
 175#define ctrInput(x)                     (0x88 + (x)*2)  /* s16 */
 176#define timerDivisor(x)                 (0xa0 + (x)*2)  /* u16 */
 177#define dmaControl                      0xb0            /* u16 */
 178#define trigControl                     0xb2            /* u16 */
 179#define calEeprom                       0xb8            /* u16 */
 180#define acqDigitalMark                  0xba            /* u16 */
 181#define trigDacs                        0xbc            /* u16 */
 182#define dioP2ExpansionIO16Bit(x)        (0xc0 + (x)*2)  /* s16 */
 183
 184/* Scan Sequencer programming */
 185#define DAQBOARD2000_SeqStartScanList            0x0011
 186#define DAQBOARD2000_SeqStopScanList             0x0010
 187
 188/* Prepare for acquisition */
 189#define DAQBOARD2000_AcqResetScanListFifo        0x0004
 190#define DAQBOARD2000_AcqResetResultsFifo         0x0002
 191#define DAQBOARD2000_AcqResetConfigPipe          0x0001
 192
 193/* Acqusition status bits */
 194#define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
 195#define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
 196#define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
 197#define DAQBOARD2000_AcqLogicScanning            0x0008
 198#define DAQBOARD2000_AcqConfigPipeFull           0x0010
 199#define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
 200#define DAQBOARD2000_AcqAdcNotReady              0x0040
 201#define DAQBOARD2000_ArbitrationFailure          0x0080
 202#define DAQBOARD2000_AcqPacerOverrun             0x0100
 203#define DAQBOARD2000_DacPacerOverrun             0x0200
 204#define DAQBOARD2000_AcqHardwareError            0x01c0
 205
 206/* Scan Sequencer programming */
 207#define DAQBOARD2000_SeqStartScanList            0x0011
 208#define DAQBOARD2000_SeqStopScanList             0x0010
 209
 210/* Pacer Clock Control */
 211#define DAQBOARD2000_AdcPacerInternal            0x0030
 212#define DAQBOARD2000_AdcPacerExternal            0x0032
 213#define DAQBOARD2000_AdcPacerEnable              0x0031
 214#define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
 215#define DAQBOARD2000_AdcPacerDisable             0x0030
 216#define DAQBOARD2000_AdcPacerNormalMode          0x0060
 217#define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
 218#define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
 219#define DAQBOARD2000_AdcPacerExternalRising      0x0100
 220
 221/* DAC status */
 222#define DAQBOARD2000_DacFull                     0x0001
 223#define DAQBOARD2000_RefBusy                     0x0002
 224#define DAQBOARD2000_TrgBusy                     0x0004
 225#define DAQBOARD2000_CalBusy                     0x0008
 226#define DAQBOARD2000_Dac0Busy                    0x0010
 227#define DAQBOARD2000_Dac1Busy                    0x0020
 228#define DAQBOARD2000_Dac2Busy                    0x0040
 229#define DAQBOARD2000_Dac3Busy                    0x0080
 230
 231/* DAC control */
 232#define DAQBOARD2000_Dac0Enable                  0x0021
 233#define DAQBOARD2000_Dac1Enable                  0x0031
 234#define DAQBOARD2000_Dac2Enable                  0x0041
 235#define DAQBOARD2000_Dac3Enable                  0x0051
 236#define DAQBOARD2000_DacEnableBit                0x0001
 237#define DAQBOARD2000_Dac0Disable                 0x0020
 238#define DAQBOARD2000_Dac1Disable                 0x0030
 239#define DAQBOARD2000_Dac2Disable                 0x0040
 240#define DAQBOARD2000_Dac3Disable                 0x0050
 241#define DAQBOARD2000_DacResetFifo                0x0004
 242#define DAQBOARD2000_DacPatternDisable           0x0060
 243#define DAQBOARD2000_DacPatternEnable            0x0061
 244#define DAQBOARD2000_DacSelectSignedData         0x0002
 245#define DAQBOARD2000_DacSelectUnsignedData       0x0000
 246
 247/* Trigger Control */
 248#define DAQBOARD2000_TrigAnalog                  0x0000
 249#define DAQBOARD2000_TrigTTL                     0x0010
 250#define DAQBOARD2000_TrigTransHiLo               0x0004
 251#define DAQBOARD2000_TrigTransLoHi               0x0000
 252#define DAQBOARD2000_TrigAbove                   0x0000
 253#define DAQBOARD2000_TrigBelow                   0x0004
 254#define DAQBOARD2000_TrigLevelSense              0x0002
 255#define DAQBOARD2000_TrigEdgeSense               0x0000
 256#define DAQBOARD2000_TrigEnable                  0x0001
 257#define DAQBOARD2000_TrigDisable                 0x0000
 258
 259/* Reference Dac Selection */
 260#define DAQBOARD2000_PosRefDacSelect             0x0100
 261#define DAQBOARD2000_NegRefDacSelect             0x0000
 262
 263struct daq200_boardtype {
 264        const char *name;
 265        int id;
 266};
 267static const struct daq200_boardtype boardtypes[] = {
 268        {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
 269        {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
 270};
 271
 272struct daqboard2000_private {
 273        enum {
 274                card_daqboard_2000
 275        } card;
 276        void __iomem *plx;
 277};
 278
 279static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
 280{
 281        /* udelay(4); */
 282        writew(entry & 0x00ff, dev->mmio + acqScanListFIFO);
 283        /* udelay(4); */
 284        writew((entry >> 8) & 0x00ff, dev->mmio + acqScanListFIFO);
 285}
 286
 287static void setup_sampling(struct comedi_device *dev, int chan, int gain)
 288{
 289        u16 word0, word1, word2, word3;
 290
 291        /* Channel 0-7 diff, channel 8-23 single ended */
 292        word0 = 0;
 293        word1 = 0x0004;         /* Last scan */
 294        word2 = (chan << 6) & 0x00c0;
 295        switch (chan / 4) {
 296        case 0:
 297                word3 = 0x0001;
 298                break;
 299        case 1:
 300                word3 = 0x0002;
 301                break;
 302        case 2:
 303                word3 = 0x0005;
 304                break;
 305        case 3:
 306                word3 = 0x0006;
 307                break;
 308        case 4:
 309                word3 = 0x0041;
 310                break;
 311        case 5:
 312                word3 = 0x0042;
 313                break;
 314        default:
 315                word3 = 0;
 316                break;
 317        }
 318/*
 319  dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
 320  dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
 321*/
 322        /* These should be read from EEPROM */
 323        word2 |= 0x0800;
 324        word3 |= 0xc000;
 325        writeAcqScanListEntry(dev, word0);
 326        writeAcqScanListEntry(dev, word1);
 327        writeAcqScanListEntry(dev, word2);
 328        writeAcqScanListEntry(dev, word3);
 329}
 330
 331static int daqboard2000_ai_status(struct comedi_device *dev,
 332                                  struct comedi_subdevice *s,
 333                                  struct comedi_insn *insn,
 334                                  unsigned long context)
 335{
 336        unsigned int status;
 337
 338        status = readw(dev->mmio + acqControl);
 339        if (status & context)
 340                return 0;
 341        return -EBUSY;
 342}
 343
 344static int daqboard2000_ai_insn_read(struct comedi_device *dev,
 345                                     struct comedi_subdevice *s,
 346                                     struct comedi_insn *insn,
 347                                     unsigned int *data)
 348{
 349        int gain, chan;
 350        int ret;
 351        int i;
 352
 353        writew(DAQBOARD2000_AcqResetScanListFifo |
 354               DAQBOARD2000_AcqResetResultsFifo |
 355               DAQBOARD2000_AcqResetConfigPipe, dev->mmio + acqControl);
 356
 357        /*
 358         * If pacer clock is not set to some high value (> 10 us), we
 359         * risk multiple samples to be put into the result FIFO.
 360         */
 361        /* 1 second, should be long enough */
 362        writel(1000000, dev->mmio + acqPacerClockDivLow);
 363        writew(0, dev->mmio + acqPacerClockDivHigh);
 364
 365        gain = CR_RANGE(insn->chanspec);
 366        chan = CR_CHAN(insn->chanspec);
 367
 368        /* This doesn't look efficient.  I decided to take the conservative
 369         * approach when I did the insn conversion.  Perhaps it would be
 370         * better to have broken it completely, then someone would have been
 371         * forced to fix it.  --ds */
 372        for (i = 0; i < insn->n; i++) {
 373                setup_sampling(dev, chan, gain);
 374                /* Enable reading from the scanlist FIFO */
 375                writew(DAQBOARD2000_SeqStartScanList, dev->mmio + acqControl);
 376
 377                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 378                                     DAQBOARD2000_AcqConfigPipeFull);
 379                if (ret)
 380                        return ret;
 381
 382                writew(DAQBOARD2000_AdcPacerEnable, dev->mmio + acqControl);
 383
 384                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 385                                     DAQBOARD2000_AcqLogicScanning);
 386                if (ret)
 387                        return ret;
 388
 389                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 390                                     DAQBOARD2000_AcqResultsFIFOHasValidData);
 391                if (ret)
 392                        return ret;
 393
 394                data[i] = readw(dev->mmio + acqResultsFIFO);
 395                writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
 396                writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
 397        }
 398
 399        return i;
 400}
 401
 402static int daqboard2000_ao_eoc(struct comedi_device *dev,
 403                               struct comedi_subdevice *s,
 404                               struct comedi_insn *insn,
 405                               unsigned long context)
 406{
 407        unsigned int chan = CR_CHAN(insn->chanspec);
 408        unsigned int status;
 409
 410        status = readw(dev->mmio + dacControl);
 411        if ((status & ((chan + 1) * 0x0010)) == 0)
 412                return 0;
 413        return -EBUSY;
 414}
 415
 416static int daqboard2000_ao_insn_write(struct comedi_device *dev,
 417                                      struct comedi_subdevice *s,
 418                                      struct comedi_insn *insn,
 419                                      unsigned int *data)
 420{
 421        unsigned int chan = CR_CHAN(insn->chanspec);
 422        int i;
 423
 424        for (i = 0; i < insn->n; i++) {
 425                unsigned int val = data[i];
 426                int ret;
 427
 428                writew(val, dev->mmio + dacSetting(chan));
 429
 430                ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0);
 431                if (ret)
 432                        return ret;
 433
 434                s->readback[chan] = val;
 435        }
 436
 437        return insn->n;
 438}
 439
 440static void daqboard2000_resetLocalBus(struct comedi_device *dev)
 441{
 442        struct daqboard2000_private *devpriv = dev->private;
 443
 444        writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
 445        mdelay(10);
 446        writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
 447        mdelay(10);
 448}
 449
 450static void daqboard2000_reloadPLX(struct comedi_device *dev)
 451{
 452        struct daqboard2000_private *devpriv = dev->private;
 453
 454        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 455        mdelay(10);
 456        writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
 457        mdelay(10);
 458        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 459        mdelay(10);
 460}
 461
 462static void daqboard2000_pulseProgPin(struct comedi_device *dev)
 463{
 464        struct daqboard2000_private *devpriv = dev->private;
 465
 466        writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
 467        mdelay(10);
 468        writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
 469        mdelay(10);     /* Not in the original code, but I like symmetry... */
 470}
 471
 472static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
 473{
 474        int result = 0;
 475        int i;
 476        int cpld;
 477
 478        /* timeout after 50 tries -> 5ms */
 479        for (i = 0; i < 50; i++) {
 480                cpld = readw(dev->mmio + 0x1000);
 481                if ((cpld & mask) == mask) {
 482                        result = 1;
 483                        break;
 484                }
 485                udelay(100);
 486        }
 487        udelay(5);
 488        return result;
 489}
 490
 491static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
 492{
 493        int result = 0;
 494
 495        udelay(10);
 496        writew(data, dev->mmio + 0x1000);
 497        if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
 498            DAQBOARD2000_CPLD_INIT) {
 499                result = 1;
 500        }
 501        return result;
 502}
 503
 504static int initialize_daqboard2000(struct comedi_device *dev,
 505                                   const u8 *cpld_array, size_t len,
 506                                   unsigned long context)
 507{
 508        struct daqboard2000_private *devpriv = dev->private;
 509        int result = -EIO;
 510        /* Read the serial EEPROM control register */
 511        int secr;
 512        int retry;
 513        size_t i;
 514
 515        /* Check to make sure the serial eeprom is present on the board */
 516        secr = readl(devpriv->plx + 0x6c);
 517        if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
 518                return -EIO;
 519
 520        for (retry = 0; retry < 3; retry++) {
 521                daqboard2000_resetLocalBus(dev);
 522                daqboard2000_reloadPLX(dev);
 523                daqboard2000_pulseProgPin(dev);
 524                if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
 525                        for (i = 0; i < len; i++) {
 526                                if (cpld_array[i] == 0xff &&
 527                                    cpld_array[i + 1] == 0x20)
 528                                        break;
 529                        }
 530                        for (; i < len; i += 2) {
 531                                int data =
 532                                    (cpld_array[i] << 8) + cpld_array[i + 1];
 533                                if (!daqboard2000_writeCPLD(dev, data))
 534                                        break;
 535                        }
 536                        if (i >= len) {
 537                                daqboard2000_resetLocalBus(dev);
 538                                daqboard2000_reloadPLX(dev);
 539                                result = 0;
 540                                break;
 541                        }
 542                }
 543        }
 544        return result;
 545}
 546
 547static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
 548{
 549}
 550
 551static void daqboard2000_adcDisarm(struct comedi_device *dev)
 552{
 553        /* Disable hardware triggers */
 554        udelay(2);
 555        writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
 556               dev->mmio + trigControl);
 557        udelay(2);
 558        writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
 559               dev->mmio + trigControl);
 560
 561        /* Stop the scan list FIFO from loading the configuration pipe */
 562        udelay(2);
 563        writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
 564
 565        /* Stop the pacer clock */
 566        udelay(2);
 567        writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
 568
 569        /* Stop the input dma (abort channel 1) */
 570        daqboard2000_adcStopDmaTransfer(dev);
 571}
 572
 573static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
 574{
 575        unsigned int val;
 576        int timeout;
 577
 578        /*  Set the + reference dac value in the FPGA */
 579        writew(0x80 | DAQBOARD2000_PosRefDacSelect, dev->mmio + refDacs);
 580        for (timeout = 0; timeout < 20; timeout++) {
 581                val = readw(dev->mmio + dacControl);
 582                if ((val & DAQBOARD2000_RefBusy) == 0)
 583                        break;
 584                udelay(2);
 585        }
 586
 587        /*  Set the - reference dac value in the FPGA */
 588        writew(0x80 | DAQBOARD2000_NegRefDacSelect, dev->mmio + refDacs);
 589        for (timeout = 0; timeout < 20; timeout++) {
 590                val = readw(dev->mmio + dacControl);
 591                if ((val & DAQBOARD2000_RefBusy) == 0)
 592                        break;
 593                udelay(2);
 594        }
 595}
 596
 597static void daqboard2000_initializeCtrs(struct comedi_device *dev)
 598{
 599}
 600
 601static void daqboard2000_initializeTmrs(struct comedi_device *dev)
 602{
 603}
 604
 605static void daqboard2000_dacDisarm(struct comedi_device *dev)
 606{
 607}
 608
 609static void daqboard2000_initializeAdc(struct comedi_device *dev)
 610{
 611        daqboard2000_adcDisarm(dev);
 612        daqboard2000_activateReferenceDacs(dev);
 613        daqboard2000_initializeCtrs(dev);
 614        daqboard2000_initializeTmrs(dev);
 615}
 616
 617static void daqboard2000_initializeDac(struct comedi_device *dev)
 618{
 619        daqboard2000_dacDisarm(dev);
 620}
 621
 622static int daqboard2000_8255_cb(struct comedi_device *dev,
 623                                int dir, int port, int data,
 624                                unsigned long iobase)
 625{
 626        if (dir) {
 627                writew(data, dev->mmio + iobase + port * 2);
 628                return 0;
 629        }
 630        return readw(dev->mmio + iobase + port * 2);
 631}
 632
 633static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
 634                                               struct pci_dev *pcidev)
 635{
 636        const struct daq200_boardtype *board;
 637        int i;
 638
 639        if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
 640                return NULL;
 641
 642        for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
 643                board = &boardtypes[i];
 644                if (pcidev->subsystem_device == board->id)
 645                        return board;
 646        }
 647        return NULL;
 648}
 649
 650static int daqboard2000_auto_attach(struct comedi_device *dev,
 651                                    unsigned long context_unused)
 652{
 653        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 654        const struct daq200_boardtype *board;
 655        struct daqboard2000_private *devpriv;
 656        struct comedi_subdevice *s;
 657        int result;
 658
 659        board = daqboard2000_find_boardinfo(dev, pcidev);
 660        if (!board)
 661                return -ENODEV;
 662        dev->board_ptr = board;
 663        dev->board_name = board->name;
 664
 665        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 666        if (!devpriv)
 667                return -ENOMEM;
 668
 669        result = comedi_pci_enable(dev);
 670        if (result)
 671                return result;
 672
 673        devpriv->plx = pci_ioremap_bar(pcidev, 0);
 674        dev->mmio = pci_ioremap_bar(pcidev, 2);
 675        if (!devpriv->plx || !dev->mmio)
 676                return -ENOMEM;
 677
 678        result = comedi_alloc_subdevices(dev, 3);
 679        if (result)
 680                return result;
 681
 682        readl(devpriv->plx + 0x6c);
 683
 684        result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
 685                                      DAQBOARD2000_FIRMWARE,
 686                                      initialize_daqboard2000, 0);
 687        if (result < 0)
 688                return result;
 689
 690        daqboard2000_initializeAdc(dev);
 691        daqboard2000_initializeDac(dev);
 692
 693        s = &dev->subdevices[0];
 694        /* ai subdevice */
 695        s->type = COMEDI_SUBD_AI;
 696        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 697        s->n_chan = 24;
 698        s->maxdata = 0xffff;
 699        s->insn_read = daqboard2000_ai_insn_read;
 700        s->range_table = &range_daqboard2000_ai;
 701
 702        s = &dev->subdevices[1];
 703        /* ao subdevice */
 704        s->type = COMEDI_SUBD_AO;
 705        s->subdev_flags = SDF_WRITABLE;
 706        s->n_chan = 2;
 707        s->maxdata = 0xffff;
 708        s->insn_write = daqboard2000_ao_insn_write;
 709        s->range_table = &range_bipolar10;
 710
 711        result = comedi_alloc_subdev_readback(s);
 712        if (result)
 713                return result;
 714
 715        s = &dev->subdevices[2];
 716        return subdev_8255_init(dev, s, daqboard2000_8255_cb,
 717                                dioP2ExpansionIO8Bit);
 718}
 719
 720static void daqboard2000_detach(struct comedi_device *dev)
 721{
 722        struct daqboard2000_private *devpriv = dev->private;
 723
 724        if (devpriv && devpriv->plx)
 725                iounmap(devpriv->plx);
 726        comedi_pci_detach(dev);
 727}
 728
 729static struct comedi_driver daqboard2000_driver = {
 730        .driver_name    = "daqboard2000",
 731        .module         = THIS_MODULE,
 732        .auto_attach    = daqboard2000_auto_attach,
 733        .detach         = daqboard2000_detach,
 734};
 735
 736static int daqboard2000_pci_probe(struct pci_dev *dev,
 737                                  const struct pci_device_id *id)
 738{
 739        return comedi_pci_auto_config(dev, &daqboard2000_driver,
 740                                      id->driver_data);
 741}
 742
 743static const struct pci_device_id daqboard2000_pci_table[] = {
 744        { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
 745        { 0 }
 746};
 747MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
 748
 749static struct pci_driver daqboard2000_pci_driver = {
 750        .name           = "daqboard2000",
 751        .id_table       = daqboard2000_pci_table,
 752        .probe          = daqboard2000_pci_probe,
 753        .remove         = comedi_pci_auto_unconfig,
 754};
 755module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
 756
 757MODULE_AUTHOR("Comedi http://www.comedi.org");
 758MODULE_DESCRIPTION("Comedi low-level driver");
 759MODULE_LICENSE("GPL");
 760MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
 761