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 *plx;
 278};
 279
 280static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
 281{
 282        /* udelay(4); */
 283        writew(entry & 0x00ff, dev->mmio + acqScanListFIFO);
 284        /* udelay(4); */
 285        writew((entry >> 8) & 0x00ff, dev->mmio + acqScanListFIFO);
 286}
 287
 288static void setup_sampling(struct comedi_device *dev, int chan, int gain)
 289{
 290        u16 word0, word1, word2, word3;
 291
 292        /* Channel 0-7 diff, channel 8-23 single ended */
 293        word0 = 0;
 294        word1 = 0x0004;         /* Last scan */
 295        word2 = (chan << 6) & 0x00c0;
 296        switch (chan / 4) {
 297        case 0:
 298                word3 = 0x0001;
 299                break;
 300        case 1:
 301                word3 = 0x0002;
 302                break;
 303        case 2:
 304                word3 = 0x0005;
 305                break;
 306        case 3:
 307                word3 = 0x0006;
 308                break;
 309        case 4:
 310                word3 = 0x0041;
 311                break;
 312        case 5:
 313                word3 = 0x0042;
 314                break;
 315        default:
 316                word3 = 0;
 317                break;
 318        }
 319/*
 320  dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
 321  dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
 322*/
 323        /* These should be read from EEPROM */
 324        word2 |= 0x0800;
 325        word3 |= 0xc000;
 326        writeAcqScanListEntry(dev, word0);
 327        writeAcqScanListEntry(dev, word1);
 328        writeAcqScanListEntry(dev, word2);
 329        writeAcqScanListEntry(dev, word3);
 330}
 331
 332static int daqboard2000_ai_status(struct comedi_device *dev,
 333                                  struct comedi_subdevice *s,
 334                                  struct comedi_insn *insn,
 335                                  unsigned long context)
 336{
 337        unsigned int status;
 338
 339        status = readw(dev->mmio + acqControl);
 340        if (status & context)
 341                return 0;
 342        return -EBUSY;
 343}
 344
 345static int daqboard2000_ai_insn_read(struct comedi_device *dev,
 346                                     struct comedi_subdevice *s,
 347                                     struct comedi_insn *insn,
 348                                     unsigned int *data)
 349{
 350        int gain, chan;
 351        int ret;
 352        int i;
 353
 354        writew(DAQBOARD2000_AcqResetScanListFifo |
 355               DAQBOARD2000_AcqResetResultsFifo |
 356               DAQBOARD2000_AcqResetConfigPipe, dev->mmio + acqControl);
 357
 358        /*
 359         * If pacer clock is not set to some high value (> 10 us), we
 360         * risk multiple samples to be put into the result FIFO.
 361         */
 362        /* 1 second, should be long enough */
 363        writel(1000000, dev->mmio + acqPacerClockDivLow);
 364        writew(0, dev->mmio + acqPacerClockDivHigh);
 365
 366        gain = CR_RANGE(insn->chanspec);
 367        chan = CR_CHAN(insn->chanspec);
 368
 369        /* This doesn't look efficient.  I decided to take the conservative
 370         * approach when I did the insn conversion.  Perhaps it would be
 371         * better to have broken it completely, then someone would have been
 372         * forced to fix it.  --ds */
 373        for (i = 0; i < insn->n; i++) {
 374                setup_sampling(dev, chan, gain);
 375                /* Enable reading from the scanlist FIFO */
 376                writew(DAQBOARD2000_SeqStartScanList, dev->mmio + acqControl);
 377
 378                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 379                                     DAQBOARD2000_AcqConfigPipeFull);
 380                if (ret)
 381                        return ret;
 382
 383                writew(DAQBOARD2000_AdcPacerEnable, dev->mmio + acqControl);
 384
 385                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 386                                     DAQBOARD2000_AcqLogicScanning);
 387                if (ret)
 388                        return ret;
 389
 390                ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
 391                                     DAQBOARD2000_AcqResultsFIFOHasValidData);
 392                if (ret)
 393                        return ret;
 394
 395                data[i] = readw(dev->mmio + acqResultsFIFO);
 396                writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
 397                writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
 398        }
 399
 400        return i;
 401}
 402
 403static int daqboard2000_ao_eoc(struct comedi_device *dev,
 404                               struct comedi_subdevice *s,
 405                               struct comedi_insn *insn,
 406                               unsigned long context)
 407{
 408        unsigned int chan = CR_CHAN(insn->chanspec);
 409        unsigned int status;
 410
 411        status = readw(dev->mmio + dacControl);
 412        if ((status & ((chan + 1) * 0x0010)) == 0)
 413                return 0;
 414        return -EBUSY;
 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        unsigned int chan = CR_CHAN(insn->chanspec);
 423        int i;
 424
 425        for (i = 0; i < insn->n; i++) {
 426                unsigned int val = data[i];
 427                int ret;
 428
 429                writew(val, dev->mmio + dacSetting(chan));
 430
 431                ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0);
 432                if (ret)
 433                        return ret;
 434
 435                s->readback[chan] = val;
 436        }
 437
 438        return insn->n;
 439}
 440
 441static void daqboard2000_resetLocalBus(struct comedi_device *dev)
 442{
 443        struct daqboard2000_private *devpriv = dev->private;
 444
 445        writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
 446        mdelay(10);
 447        writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
 448        mdelay(10);
 449}
 450
 451static void daqboard2000_reloadPLX(struct comedi_device *dev)
 452{
 453        struct daqboard2000_private *devpriv = dev->private;
 454
 455        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 456        mdelay(10);
 457        writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
 458        mdelay(10);
 459        writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
 460        mdelay(10);
 461}
 462
 463static void daqboard2000_pulseProgPin(struct comedi_device *dev)
 464{
 465        struct daqboard2000_private *devpriv = dev->private;
 466
 467        writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
 468        mdelay(10);
 469        writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
 470        mdelay(10);     /* Not in the original code, but I like symmetry... */
 471}
 472
 473static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
 474{
 475        int result = 0;
 476        int i;
 477        int cpld;
 478
 479        /* timeout after 50 tries -> 5ms */
 480        for (i = 0; i < 50; i++) {
 481                cpld = readw(dev->mmio + 0x1000);
 482                if ((cpld & mask) == mask) {
 483                        result = 1;
 484                        break;
 485                }
 486                udelay(100);
 487        }
 488        udelay(5);
 489        return result;
 490}
 491
 492static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
 493{
 494        int result = 0;
 495
 496        udelay(10);
 497        writew(data, dev->mmio + 0x1000);
 498        if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
 499            DAQBOARD2000_CPLD_INIT) {
 500                result = 1;
 501        }
 502        return result;
 503}
 504
 505static int initialize_daqboard2000(struct comedi_device *dev,
 506                                   const u8 *cpld_array, size_t len,
 507                                   unsigned long context)
 508{
 509        struct daqboard2000_private *devpriv = dev->private;
 510        int result = -EIO;
 511        /* Read the serial EEPROM control register */
 512        int secr;
 513        int retry;
 514        size_t i;
 515
 516        /* Check to make sure the serial eeprom is present on the board */
 517        secr = readl(devpriv->plx + 0x6c);
 518        if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
 519                return -EIO;
 520
 521        for (retry = 0; retry < 3; retry++) {
 522                daqboard2000_resetLocalBus(dev);
 523                daqboard2000_reloadPLX(dev);
 524                daqboard2000_pulseProgPin(dev);
 525                if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
 526                        for (i = 0; i < len; i++) {
 527                                if (cpld_array[i] == 0xff &&
 528                                    cpld_array[i + 1] == 0x20)
 529                                        break;
 530                        }
 531                        for (; i < len; i += 2) {
 532                                int data =
 533                                    (cpld_array[i] << 8) + cpld_array[i + 1];
 534                                if (!daqboard2000_writeCPLD(dev, data))
 535                                        break;
 536                        }
 537                        if (i >= len) {
 538                                daqboard2000_resetLocalBus(dev);
 539                                daqboard2000_reloadPLX(dev);
 540                                result = 0;
 541                                break;
 542                        }
 543                }
 544        }
 545        return result;
 546}
 547
 548static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
 549{
 550}
 551
 552static void daqboard2000_adcDisarm(struct comedi_device *dev)
 553{
 554        /* Disable hardware triggers */
 555        udelay(2);
 556        writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
 557               dev->mmio + trigControl);
 558        udelay(2);
 559        writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
 560               dev->mmio + trigControl);
 561
 562        /* Stop the scan list FIFO from loading the configuration pipe */
 563        udelay(2);
 564        writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
 565
 566        /* Stop the pacer clock */
 567        udelay(2);
 568        writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
 569
 570        /* Stop the input dma (abort channel 1) */
 571        daqboard2000_adcStopDmaTransfer(dev);
 572}
 573
 574static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
 575{
 576        unsigned int val;
 577        int timeout;
 578
 579        /*  Set the + reference dac value in the FPGA */
 580        writew(0x80 | DAQBOARD2000_PosRefDacSelect, dev->mmio + refDacs);
 581        for (timeout = 0; timeout < 20; timeout++) {
 582                val = readw(dev->mmio + dacControl);
 583                if ((val & DAQBOARD2000_RefBusy) == 0)
 584                        break;
 585                udelay(2);
 586        }
 587
 588        /*  Set the - reference dac value in the FPGA */
 589        writew(0x80 | DAQBOARD2000_NegRefDacSelect, dev->mmio + refDacs);
 590        for (timeout = 0; timeout < 20; timeout++) {
 591                val = readw(dev->mmio + dacControl);
 592                if ((val & DAQBOARD2000_RefBusy) == 0)
 593                        break;
 594                udelay(2);
 595        }
 596}
 597
 598static void daqboard2000_initializeCtrs(struct comedi_device *dev)
 599{
 600}
 601
 602static void daqboard2000_initializeTmrs(struct comedi_device *dev)
 603{
 604}
 605
 606static void daqboard2000_dacDisarm(struct comedi_device *dev)
 607{
 608}
 609
 610static void daqboard2000_initializeAdc(struct comedi_device *dev)
 611{
 612        daqboard2000_adcDisarm(dev);
 613        daqboard2000_activateReferenceDacs(dev);
 614        daqboard2000_initializeCtrs(dev);
 615        daqboard2000_initializeTmrs(dev);
 616}
 617
 618static void daqboard2000_initializeDac(struct comedi_device *dev)
 619{
 620        daqboard2000_dacDisarm(dev);
 621}
 622
 623static int daqboard2000_8255_cb(struct comedi_device *dev,
 624                                int dir, int port, int data,
 625                                unsigned long iobase)
 626{
 627        if (dir) {
 628                writew(data, dev->mmio + iobase + port * 2);
 629                return 0;
 630        }
 631        return readw(dev->mmio + iobase + port * 2);
 632}
 633
 634static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
 635                                               struct pci_dev *pcidev)
 636{
 637        const struct daq200_boardtype *board;
 638        int i;
 639
 640        if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
 641                return NULL;
 642
 643        for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
 644                board = &boardtypes[i];
 645                if (pcidev->subsystem_device == board->id)
 646                        return board;
 647        }
 648        return NULL;
 649}
 650
 651static int daqboard2000_auto_attach(struct comedi_device *dev,
 652                                              unsigned long context_unused)
 653{
 654        struct pci_dev *pcidev = comedi_to_pci_dev(dev);
 655        const struct daq200_boardtype *board;
 656        struct daqboard2000_private *devpriv;
 657        struct comedi_subdevice *s;
 658        int result;
 659
 660        board = daqboard2000_find_boardinfo(dev, pcidev);
 661        if (!board)
 662                return -ENODEV;
 663        dev->board_ptr = board;
 664        dev->board_name = board->name;
 665
 666        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 667        if (!devpriv)
 668                return -ENOMEM;
 669
 670        result = comedi_pci_enable(dev);
 671        if (result)
 672                return result;
 673
 674        devpriv->plx = pci_ioremap_bar(pcidev, 0);
 675        dev->mmio = pci_ioremap_bar(pcidev, 2);
 676        if (!devpriv->plx || !dev->mmio)
 677                return -ENOMEM;
 678
 679        result = comedi_alloc_subdevices(dev, 3);
 680        if (result)
 681                return result;
 682
 683        readl(devpriv->plx + 0x6c);
 684
 685        result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
 686                                      DAQBOARD2000_FIRMWARE,
 687                                      initialize_daqboard2000, 0);
 688        if (result < 0)
 689                return result;
 690
 691        daqboard2000_initializeAdc(dev);
 692        daqboard2000_initializeDac(dev);
 693
 694        s = &dev->subdevices[0];
 695        /* ai subdevice */
 696        s->type = COMEDI_SUBD_AI;
 697        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 698        s->n_chan = 24;
 699        s->maxdata = 0xffff;
 700        s->insn_read = daqboard2000_ai_insn_read;
 701        s->range_table = &range_daqboard2000_ai;
 702
 703        s = &dev->subdevices[1];
 704        /* ao subdevice */
 705        s->type = COMEDI_SUBD_AO;
 706        s->subdev_flags = SDF_WRITABLE;
 707        s->n_chan = 2;
 708        s->maxdata = 0xffff;
 709        s->insn_write = daqboard2000_ao_insn_write;
 710        s->range_table = &range_bipolar10;
 711
 712        result = comedi_alloc_subdev_readback(s);
 713        if (result)
 714                return result;
 715
 716        s = &dev->subdevices[2];
 717        result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
 718                                  dioP2ExpansionIO8Bit);
 719        if (result)
 720                return result;
 721
 722        return 0;
 723}
 724
 725static void daqboard2000_detach(struct comedi_device *dev)
 726{
 727        struct daqboard2000_private *devpriv = dev->private;
 728
 729        if (devpriv && devpriv->plx)
 730                iounmap(devpriv->plx);
 731        comedi_pci_detach(dev);
 732}
 733
 734static struct comedi_driver daqboard2000_driver = {
 735        .driver_name    = "daqboard2000",
 736        .module         = THIS_MODULE,
 737        .auto_attach    = daqboard2000_auto_attach,
 738        .detach         = daqboard2000_detach,
 739};
 740
 741static int daqboard2000_pci_probe(struct pci_dev *dev,
 742                                  const struct pci_device_id *id)
 743{
 744        return comedi_pci_auto_config(dev, &daqboard2000_driver,
 745                                      id->driver_data);
 746}
 747
 748static const struct pci_device_id daqboard2000_pci_table[] = {
 749        { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
 750        { 0 }
 751};
 752MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
 753
 754static struct pci_driver daqboard2000_pci_driver = {
 755        .name           = "daqboard2000",
 756        .id_table       = daqboard2000_pci_table,
 757        .probe          = daqboard2000_pci_probe,
 758        .remove         = comedi_pci_auto_unconfig,
 759};
 760module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
 761
 762MODULE_AUTHOR("Comedi http://www.comedi.org");
 763MODULE_DESCRIPTION("Comedi low-level driver");
 764MODULE_LICENSE("GPL");
 765MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
 766
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.