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