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/*
  19Driver: daqboard2000
  20Description: IOTech DAQBoard/2000
  21Author: Anders Blomdell <anders.blomdell@control.lth.se>
  22Status: works
  23Updated: Mon, 14 Apr 2008 15:28:52 +0100
  24Devices: [IOTech] DAQBoard/2000 (daqboard2000)
  25
  26Much of the functionality of this driver was determined from reading
  27the source code for the Windows driver.
  28
  29The FPGA on the board requires fimware, which is available from
  30http://www.comedi.org in the comedi_nonfree_firmware tarball.
  31
  32Configuration 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/pci.h>
 107#include <linux/delay.h>
 108#include <linux/interrupt.h>
 109
 110#include "../comedidev.h"
 111
 112#include "8255.h"
 113
 114#define DAQBOARD2000_FIRMWARE           "daqboard2000_firmware.bin"
 115
 116#define DAQBOARD2000_SUBSYSTEM_IDS2     0x0002  /* Daqboard/2000 - 2 Dacs */
 117#define DAQBOARD2000_SUBSYSTEM_IDS4     0x0004  /* Daqboard/2000 - 4 Dacs */
 118
 119/* Initialization bits for the Serial EEPROM Control Register */
 120#define DAQBOARD2000_SECRProgPinHi      0x8001767e
 121#define DAQBOARD2000_SECRProgPinLo      0x8000767e
 122#define DAQBOARD2000_SECRLocalBusHi     0xc000767e
 123#define DAQBOARD2000_SECRLocalBusLo     0x8000767e
 124#define DAQBOARD2000_SECRReloadHi       0xa000767e
 125#define DAQBOARD2000_SECRReloadLo       0x8000767e
 126
 127/* SECR status bits */
 128#define DAQBOARD2000_EEPROM_PRESENT     0x10000000
 129
 130/* CPLD status bits */
 131#define DAQBOARD2000_CPLD_INIT          0x0002
 132#define DAQBOARD2000_CPLD_DONE          0x0004
 133
 134static const struct comedi_lrange range_daqboard2000_ai = {
 135        13, {
 136                BIP_RANGE(10),
 137                BIP_RANGE(5),
 138                BIP_RANGE(2.5),
 139                BIP_RANGE(1.25),
 140                BIP_RANGE(0.625),
 141                BIP_RANGE(0.3125),
 142                BIP_RANGE(0.156),
 143                UNI_RANGE(10),
 144                UNI_RANGE(5),
 145                UNI_RANGE(2.5),
 146                UNI_RANGE(1.25),
 147                UNI_RANGE(0.625),
 148                UNI_RANGE(0.3125)
 149        }
 150};
 151
 152/*
 153 * Register Memory Map
 154 */
 155#define acqControl                      0x00            /* u16 */
 156#define acqScanListFIFO                 0x02            /* u16 */
 157#define acqPacerClockDivLow             0x04            /* u32 */
 158#define acqScanCounter                  0x08            /* u16 */
 159#define acqPacerClockDivHigh            0x0a            /* u16 */
 160#define acqTriggerCount                 0x0c            /* u16 */
 161#define acqResultsFIFO                  0x10            /* u16 */
 162#define acqResultsShadow                0x14            /* u16 */
 163#define acqAdcResult                    0x18            /* u16 */
 164#define dacScanCounter                  0x1c            /* u16 */
 165#define dacControl                      0x20            /* u16 */
 166#define dacFIFO                         0x24            /* s16 */
 167#define dacPacerClockDiv                0x2a            /* u16 */
 168#define refDacs                         0x2c            /* u16 */
 169#define dioControl                      0x30            /* u16 */
 170#define dioP3hsioData                   0x32            /* s16 */
 171#define dioP3Control                    0x34            /* u16 */
 172#define calEepromControl                0x36            /* u16 */
 173#define dacSetting(x)                   (0x38 + (x)*2)  /* s16 */
 174#define dioP2ExpansionIO8Bit            0x40            /* s16 */
 175#define ctrTmrControl                   0x80            /* u16 */
 176#define ctrInput(x)                     (0x88 + (x)*2)  /* s16 */
 177#define timerDivisor(x)                 (0xa0 + (x)*2)  /* u16 */
 178#define dmaControl                      0xb0            /* u16 */
 179#define trigControl                     0xb2            /* u16 */
 180#define calEeprom                       0xb8            /* u16 */
 181#define acqDigitalMark                  0xba            /* u16 */
 182#define trigDacs                        0xbc            /* u16 */
 183#define dioP2ExpansionIO16Bit(x)        (0xc0 + (x)*2)  /* s16 */
 184
 185/* Scan Sequencer programming */
 186#define DAQBOARD2000_SeqStartScanList            0x0011
 187#define DAQBOARD2000_SeqStopScanList             0x0010
 188
 189/* Prepare for acquisition */
 190#define DAQBOARD2000_AcqResetScanListFifo        0x0004
 191#define DAQBOARD2000_AcqResetResultsFifo         0x0002
 192#define DAQBOARD2000_AcqResetConfigPipe          0x0001
 193
 194/* Acqusition status bits */
 195#define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
 196#define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
 197#define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
 198#define DAQBOARD2000_AcqLogicScanning            0x0008
 199#define DAQBOARD2000_AcqConfigPipeFull           0x0010
 200#define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
 201#define DAQBOARD2000_AcqAdcNotReady              0x0040
 202#define DAQBOARD2000_ArbitrationFailure          0x0080
 203#define DAQBOARD2000_AcqPacerOverrun             0x0100
 204#define DAQBOARD2000_DacPacerOverrun             0x0200
 205#define DAQBOARD2000_AcqHardwareError            0x01c0
 206
 207/* Scan Sequencer programming */
 208#define DAQBOARD2000_SeqStartScanList            0x0011
 209#define DAQBOARD2000_SeqStopScanList             0x0010
 210
 211/* Pacer Clock Control */
 212#define DAQBOARD2000_AdcPacerInternal            0x0030
 213#define DAQBOARD2000_AdcPacerExternal            0x0032
 214#define DAQBOARD2000_AdcPacerEnable              0x0031
 215#define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
 216#define DAQBOARD2000_AdcPacerDisable             0x0030
 217#define DAQBOARD2000_AdcPacerNormalMode          0x0060
 218#define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
 219#define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
 220#define DAQBOARD2000_AdcPacerExternalRising      0x0100
 221
 222/* DAC status */
 223#define DAQBOARD2000_DacFull                     0x0001
 224#define DAQBOARD2000_RefBusy                     0x0002
 225#define DAQBOARD2000_TrgBusy                     0x0004
 226#define DAQBOARD2000_CalBusy                     0x0008
 227#define DAQBOARD2000_Dac0Busy                    0x0010
 228#define DAQBOARD2000_Dac1Busy                    0x0020
 229#define DAQBOARD2000_Dac2Busy                    0x0040
 230#define DAQBOARD2000_Dac3Busy                    0x0080
 231
 232/* DAC control */
 233#define DAQBOARD2000_Dac0Enable                  0x0021
 234#define DAQBOARD2000_Dac1Enable                  0x0031
 235#define DAQBOARD2000_Dac2Enable                  0x0041
 236#define DAQBOARD2000_Dac3Enable                  0x0051
 237#define DAQBOARD2000_DacEnableBit                0x0001
 238#define DAQBOARD2000_Dac0Disable                 0x0020
 239#define DAQBOARD2000_Dac1Disable                 0x0030
 240#define DAQBOARD2000_Dac2Disable                 0x0040
 241#define DAQBOARD2000_Dac3Disable                 0x0050
 242#define DAQBOARD2000_DacResetFifo                0x0004
 243#define DAQBOARD2000_DacPatternDisable           0x0060
 244#define DAQBOARD2000_DacPatternEnable            0x0061
 245#define DAQBOARD2000_DacSelectSignedData         0x0002
 246#define DAQBOARD2000_DacSelectUnsignedData       0x0000
 247
 248/* Trigger Control */
 249#define DAQBOARD2000_TrigAnalog                  0x0000
 250#define DAQBOARD2000_TrigTTL                     0x0010
 251#define DAQBOARD2000_TrigTransHiLo               0x0004
 252#define DAQBOARD2000_TrigTransLoHi               0x0000
 253#define DAQBOARD2000_TrigAbove                   0x0000
 254#define DAQBOARD2000_TrigBelow                   0x0004
 255#define DAQBOARD2000_TrigLevelSense              0x0002
 256#define DAQBOARD2000_TrigEdgeSense               0x0000
 257#define DAQBOARD2000_TrigEnable                  0x0001
 258#define DAQBOARD2000_TrigDisable                 0x0000
 259
 260/* Reference Dac Selection */
 261#define DAQBOARD2000_PosRefDacSelect             0x0100
 262#define DAQBOARD2000_NegRefDacSelect             0x0000
 263
 264struct daq200_boardtype {
 265        const char *name;
 266        int id;
 267};
 268static const struct daq200_boardtype boardtypes[] = {
 269        {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
 270        {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
 271};
 272
 273struct daqboard2000_private {
 274        enum {
 275                card_daqboard_2000
 276        } card;
 277        void __iomem *daq;
 278        void __iomem *plx;
 279        unsigned int ao_readback[2];
 280};
 281
 282static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
 283{
 284        struct daqboard2000_private *devpriv = dev->private;
 285
 286        /* udelay(4); */
 287        writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
 288        /* udelay(4); */
 289        writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
 290}
 291
 292static void setup_sampling(struct comedi_device *dev, int chan, int gain)
 293{
 294        u16 word0, word1, word2, word3;
 295
 296        /* Channel 0-7 diff, channel 8-23 single ended */
 297        word0 = 0;
 298        word1 = 0x0004;         /* Last scan */
 299        word2 = (chan << 6) & 0x00c0;
 300        switch (chan / 4) {
 301        case 0:
 302                word3 = 0x0001;
 303                break;
 304        case 1:
 305                word3 = 0x0002;
 306                break;
 307        case 2:
 308                word3 = 0x0005;
 309                break;
 310        case 3:
 311                word3 = 0x0006;
 312                break;
 313        case 4:
 314                word3 = 0x0041;
 315                break;
 316        case 5:
 317                word3 = 0x0042;
 318                break;
 319        default:
 320                word3 = 0;
 321                break;
 322        }
 323/*
 324  dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
 325  dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
 326*/
 327        /* These should be read from EEPROM */
 328        word2 |= 0x0800;
 329        word3 |= 0xc000;
 330        writeAcqScanListEntry(dev, word0);
 331        writeAcqScanListEntry(dev, word1);
 332        writeAcqScanListEntry(dev, word2);
 333        writeAcqScanListEntry(dev, word3);
 334}
 335
 336static int daqboard2000_ai_insn_read(struct comedi_device *dev,
 337                                     struct comedi_subdevice *s,
 338                                     struct comedi_insn *insn,
 339                                     unsigned int *data)
 340{
 341        struct daqboard2000_private *devpriv = dev->private;
 342        unsigned int val;
 343        int gain, chan, timeout;
 344        int i;
 345
 346        writew(DAQBOARD2000_AcqResetScanListFifo |
 347               DAQBOARD2000_AcqResetResultsFifo |
 348               DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
 349
 350        /*
 351         * If pacer clock is not set to some high value (> 10 us), we
 352         * risk multiple samples to be put into the result FIFO.
 353         */
 354        /* 1 second, should be long enough */
 355        writel(1000000, devpriv->daq + acqPacerClockDivLow);
 356        writew(0, devpriv->daq + acqPacerClockDivHigh);
 357
 358        gain = CR_RANGE(insn->chanspec);
 359        chan = CR_CHAN(insn->chanspec);
 360
 361        /* This doesn't look efficient.  I decided to take the conservative
 362         * approach when I did the insn conversion.  Perhaps it would be
 363         * better to have broken it completely, then someone would have been
 364         * forced to fix it.  --ds */
 365        for (i = 0; i < insn->n; i++) {
 366                setup_sampling(dev, chan, gain);
 367                /* Enable reading from the scanlist FIFO */
 368                writew(DAQBOARD2000_SeqStartScanList,
 369                       devpriv->daq + acqControl);
 370                for (timeout = 0; timeout < 20; timeout++) {
 371                        val = readw(devpriv->daq + acqControl);
 372                        if (val & DAQBOARD2000_AcqConfigPipeFull)
 373                                break;
 374                        /* udelay(2); */
 375                }
 376                writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
 377                for (timeout = 0; timeout < 20; timeout++) {
 378                        val = readw(devpriv->daq + acqControl);
 379                        if (val & DAQBOARD2000_AcqLogicScanning)
 380                                break;
 381                        /* udelay(2); */
 382                }
 383                for (timeout = 0; timeout < 20; timeout++) {
 384                        val = readw(devpriv->daq + acqControl);
 385                        if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
 386                                break;
 387                        /* udelay(2); */
 388                }
 389                data[i] = readw(devpriv->daq + acqResultsFIFO);
 390                writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
 391                writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
 392        }
 393
 394        return i;
 395}
 396
 397static int daqboard2000_ao_insn_read(struct comedi_device *dev,
 398                                     struct comedi_subdevice *s,
 399                                     struct comedi_insn *insn,
 400                                     unsigned int *data)
 401{
 402        struct daqboard2000_private *devpriv = dev->private;
 403        int chan = CR_CHAN(insn->chanspec);
 404        int i;
 405
 406        for (i = 0; i < insn->n; i++)
 407                data[i] = devpriv->ao_readback[chan];
 408
 409        return i;
 410}
 411
 412static int daqboard2000_ao_insn_write(struct comedi_device *dev,
 413                                      struct comedi_subdevice *s,
 414                                      struct comedi_insn *insn,
 415                                      unsigned int *data)
 416{
 417        struct daqboard2000_private *devpriv = dev->private;
 418        int chan = CR_CHAN(insn->chanspec);
 419        unsigned int val;
 420        int timeout;
 421        int i;
 422
 423        for (i = 0; i < insn->n; i++) {
 424#if 0
 425                /*
 426                 * OK, since it works OK without enabling the DAC's,
 427                 * let's keep it as simple as possible...
 428                 */
 429                writew((chan + 2) * 0x0010 | 0x0001,
 430                       devpriv->daq + dacControl);
 431                udelay(1000);
 432#endif
 433                writew(data[i], devpriv->daq + dacSetting(chan));
 434                for (timeout = 0; timeout < 20; timeout++) {
 435                        val = readw(devpriv->daq + dacControl);
 436                        if ((val & ((chan + 1) * 0x0010)) == 0)
 437                                break;
 438                        /* udelay(2); */
 439                }
 440                devpriv->ao_readback[chan] = data[i];
 441#if 0
 442                /*
 443                 * Since we never enabled the DAC's, we don't need
 444                 * to disable it...
 445                 */
 446                writew((chan + 2) * 0x0010 | 0x0000,
 447                       devpriv->daq + dacControl);
 448                udelay(1000);
 449#endif
 450        }
 451
 452        return i;
 453}
 454
 455static void daqboard2000_resetLocalBus(struct comedi_device *dev)
 456{
 457        struct daqboard2000_private *devpriv = dev->private;
 458
 459        writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
 460        mdelay(10);
 461        writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
 462        mdelay(10);
 463}
 464
 465static void daqboard2000_reloadPLX(struct comedi_device *dev)
 466{
 467        struct daqboard2000_private *devpriv = dev->private;
 468
 469        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 470        mdelay(10);
 471        writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
 472        mdelay(10);
 473        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 474        mdelay(10);
 475}
 476
 477static void daqboard2000_pulseProgPin(struct comedi_device *dev)
 478{
 479        struct daqboard2000_private *devpriv = dev->private;
 480
 481        writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
 482        mdelay(10);
 483        writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
 484        mdelay(10);     /* Not in the original code, but I like symmetry... */
 485}
 486
 487static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
 488{
 489        struct daqboard2000_private *devpriv = dev->private;
 490        int result = 0;
 491        int i;
 492        int cpld;
 493
 494        /* timeout after 50 tries -> 5ms */
 495        for (i = 0; i < 50; i++) {
 496                cpld = readw(devpriv->daq + 0x1000);
 497                if ((cpld & mask) == mask) {
 498                        result = 1;
 499                        break;
 500                }
 501                udelay(100);
 502        }
 503        udelay(5);
 504        return result;
 505}
 506
 507static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
 508{
 509        struct daqboard2000_private *devpriv = dev->private;
 510        int result = 0;
 511
 512        udelay(10);
 513        writew(data, devpriv->daq + 0x1000);
 514        if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
 515            DAQBOARD2000_CPLD_INIT) {
 516                result = 1;
 517        }
 518        return result;
 519}
 520
 521static int initialize_daqboard2000(struct comedi_device *dev,
 522                                   const u8 *cpld_array, size_t len,
 523                                   unsigned long context)
 524{
 525        struct daqboard2000_private *devpriv = dev->private;
 526        int result = -EIO;
 527        /* Read the serial EEPROM control register */
 528        int secr;
 529        int retry;
 530        size_t i;
 531
 532        /* Check to make sure the serial eeprom is present on the board */
 533        secr = readl(devpriv->plx + 0x6c);
 534        if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
 535                return -EIO;
 536
 537        for (retry = 0; retry < 3; retry++) {
 538                daqboard2000_resetLocalBus(dev);
 539                daqboard2000_reloadPLX(dev);
 540                daqboard2000_pulseProgPin(dev);
 541                if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
 542                        for (i = 0; i < len; i++) {
 543                                if (cpld_array[i] == 0xff &&
 544                                    cpld_array[i + 1] == 0x20)
 545                                        break;
 546                        }
 547                        for (; i < len; i += 2) {
 548                                int data =
 549                                    (cpld_array[i] << 8) + cpld_array[i + 1];
 550                                if (!daqboard2000_writeCPLD(dev, data))
 551                                        break;
 552                        }
 553                        if (i >= len) {
 554                                daqboard2000_resetLocalBus(dev);
 555                                daqboard2000_reloadPLX(dev);
 556                                result = 0;
 557                                break;
 558                        }
 559                }
 560        }
 561        return result;
 562}
 563
 564static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
 565{
 566}
 567
 568static void daqboard2000_adcDisarm(struct comedi_device *dev)
 569{
 570        struct daqboard2000_private *devpriv = dev->private;
 571
 572        /* Disable hardware triggers */
 573        udelay(2);
 574        writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
 575               devpriv->daq + trigControl);
 576        udelay(2);
 577        writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
 578               devpriv->daq + trigControl);
 579
 580        /* Stop the scan list FIFO from loading the configuration pipe */
 581        udelay(2);
 582        writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
 583
 584        /* Stop the pacer clock */
 585        udelay(2);
 586        writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
 587
 588        /* Stop the input dma (abort channel 1) */
 589        daqboard2000_adcStopDmaTransfer(dev);
 590}
 591
 592static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
 593{
 594        struct daqboard2000_private *devpriv = dev->private;
 595        unsigned int val;
 596        int timeout;
 597
 598        /*  Set the + reference dac value in the FPGA */
 599        writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
 600        for (timeout = 0; timeout < 20; timeout++) {
 601                val = readw(devpriv->daq + dacControl);
 602                if ((val & DAQBOARD2000_RefBusy) == 0)
 603                        break;
 604                udelay(2);
 605        }
 606
 607        /*  Set the - reference dac value in the FPGA */
 608        writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
 609        for (timeout = 0; timeout < 20; timeout++) {
 610                val = readw(devpriv->daq + dacControl);
 611                if ((val & DAQBOARD2000_RefBusy) == 0)
 612                        break;
 613                udelay(2);
 614        }
 615}
 616
 617static void daqboard2000_initializeCtrs(struct comedi_device *dev)
 618{
 619}
 620
 621static void daqboard2000_initializeTmrs(struct comedi_device *dev)
 622{
 623}
 624
 625static void daqboard2000_dacDisarm(struct comedi_device *dev)
 626{
 627}
 628
 629static void daqboard2000_initializeAdc(struct comedi_device *dev)
 630{
 631        daqboard2000_adcDisarm(dev);
 632        daqboard2000_activateReferenceDacs(dev);
 633        daqboard2000_initializeCtrs(dev);
 634        daqboard2000_initializeTmrs(dev);
 635}
 636
 637static void daqboard2000_initializeDac(struct comedi_device *dev)
 638{
 639        daqboard2000_dacDisarm(dev);
 640}
 641
 642static int daqboard2000_8255_cb(int dir, int port, int data,
 643                                unsigned long ioaddr)
 644{
 645        void __iomem *mmio_base = (void __iomem *)ioaddr;
 646
 647        if (dir) {
 648                writew(data, mmio_base + port * 2);
 649                return 0;
 650        } else {
 651                return readw(mmio_base + port * 2);
 652        }
 653}
 654
 655static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
 656                                               struct pci_dev *pcidev)
 657{
 658        const struct daq200_boardtype *board;
 659        int i;
 660
 661        if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
 662                return NULL;
 663
 664        for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
 665                board = &boardtypes[i];
 666                if (pcidev->subsystem_device == board->id)
 667                        return board;
 668        }
 669        return NULL;
 670}
 671
 672static int daqboard2000_auto_attach(struct comedi_device *dev,
 673                                              unsigned long context_unused)
 674{
 675        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 676        const struct daq200_boardtype *board;
 677        struct daqboard2000_private *devpriv;
 678        struct comedi_subdevice *s;
 679        int result;
 680
 681        board = daqboard2000_find_boardinfo(dev, pcidev);
 682        if (!board)
 683                return -ENODEV;
 684        dev->board_ptr = board;
 685        dev->board_name = board->name;
 686
 687        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 688        if (!devpriv)
 689                return -ENOMEM;
 690
 691        result = comedi_pci_enable(dev);
 692        if (result)
 693                return result;
 694
 695        devpriv->plx = pci_ioremap_bar(pcidev, 0);
 696        devpriv->daq = pci_ioremap_bar(pcidev, 2);
 697        if (!devpriv->plx || !devpriv->daq)
 698                return -ENOMEM;
 699
 700        result = comedi_alloc_subdevices(dev, 3);
 701        if (result)
 702                return result;
 703
 704        readl(devpriv->plx + 0x6c);
 705
 706        result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
 707                                      DAQBOARD2000_FIRMWARE,
 708                                      initialize_daqboard2000, 0);
 709        if (result < 0)
 710                return result;
 711
 712        daqboard2000_initializeAdc(dev);
 713        daqboard2000_initializeDac(dev);
 714
 715        s = &dev->subdevices[0];
 716        /* ai subdevice */
 717        s->type = COMEDI_SUBD_AI;
 718        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 719        s->n_chan = 24;
 720        s->maxdata = 0xffff;
 721        s->insn_read = daqboard2000_ai_insn_read;
 722        s->range_table = &range_daqboard2000_ai;
 723
 724        s = &dev->subdevices[1];
 725        /* ao subdevice */
 726        s->type = COMEDI_SUBD_AO;
 727        s->subdev_flags = SDF_WRITABLE;
 728        s->n_chan = 2;
 729        s->maxdata = 0xffff;
 730        s->insn_read = daqboard2000_ao_insn_read;
 731        s->insn_write = daqboard2000_ao_insn_write;
 732        s->range_table = &range_bipolar10;
 733
 734        s = &dev->subdevices[2];
 735        result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
 736                        (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
 737        if (result)
 738                return result;
 739
 740        dev_info(dev->class_dev, "%s: %s attached\n",
 741                dev->driver->driver_name, dev->board_name);
 742
 743        return 0;
 744}
 745
 746static void daqboard2000_detach(struct comedi_device *dev)
 747{
 748        struct daqboard2000_private *devpriv = dev->private;
 749
 750        if (dev->irq)
 751                free_irq(dev->irq, dev);
 752        if (devpriv) {
 753                if (devpriv->daq)
 754                        iounmap(devpriv->daq);
 755                if (devpriv->plx)
 756                        iounmap(devpriv->plx);
 757        }
 758        comedi_pci_disable(dev);
 759}
 760
 761static struct comedi_driver daqboard2000_driver = {
 762        .driver_name    = "daqboard2000",
 763        .module         = THIS_MODULE,
 764        .auto_attach    = daqboard2000_auto_attach,
 765        .detach         = daqboard2000_detach,
 766};
 767
 768static int daqboard2000_pci_probe(struct pci_dev *dev,
 769                                  const struct pci_device_id *id)
 770{
 771        return comedi_pci_auto_config(dev, &daqboard2000_driver,
 772                                      id->driver_data);
 773}
 774
 775static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
 776        { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
 777        { 0 }
 778};
 779MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
 780
 781static struct pci_driver daqboard2000_pci_driver = {
 782        .name           = "daqboard2000",
 783        .id_table       = daqboard2000_pci_table,
 784        .probe          = daqboard2000_pci_probe,
 785        .remove         = comedi_pci_auto_unconfig,
 786};
 787module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
 788
 789MODULE_AUTHOR("Comedi http://www.comedi.org");
 790MODULE_DESCRIPTION("Comedi low-level driver");
 791MODULE_LICENSE("GPL");
 792MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
 793