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