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