RTXI 1.3
comedi/comedi/drivers/daqboard2000.c
Go to the documentation of this file.
00001 /*
00002    comedi/drivers/daqboard2000.c
00003    hardware driver for IOtech DAQboard/2000
00004 
00005    COMEDI - Linux Control and Measurement Device Interface
00006    Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
00007 
00008    This program is free software; you can redistribute it and/or modify
00009    it under the terms of the GNU General Public License as published by
00010    the Free Software Foundation; either version 2 of the License, or
00011    (at your option) any later version.
00012 
00013    This program is distributed in the hope that it will be useful,
00014    but WITHOUT ANY WARRANTY; without even the implied warranty of
00015    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016    GNU General Public License for more details.
00017 
00018    You should have received a copy of the GNU General Public License
00019    along with this program; if not, write to the Free Software
00020    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022  */
00023 /*
00024 Driver: daqboard2000
00025 Description: IOTech DAQBoard/2000
00026 Author: Anders Blomdell <anders.blomdell@control.lth.se>
00027 Status: works
00028 Updated: Mon, 14 Apr 2008 15:28:52 +0100
00029 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
00030 
00031 Much of the functionality of this driver was determined from reading
00032 the source code for the Windows driver.
00033 
00034 The FPGA on the board requires initialization code, which can
00035 be loaded by comedi_config using the -i
00036 option.  The initialization code is available from http://www.comedi.org
00037 in the comedi_nonfree_firmware tarball.
00038 
00039 Configuration options:
00040   [0] - PCI bus of device (optional)
00041   [1] - PCI slot of device (optional)
00042   If bus/slot is not specified, the first supported
00043   PCI device found will be used.
00044 */
00045 /*
00046    This card was obviously never intended to leave the Windows world,
00047    since it lacked all kind of hardware documentation (except for cable
00048    pinouts, plug and pray has something to catch up with yet).
00049 
00050    With some help from our swedish distributor, we got the Windows sourcecode
00051    for the card, and here are the findings so far.
00052 
00053    1. A good document that describes the PCI interface chip is found at:
00054       http://plx.plxtech.com/download/9080/databook/9080db-106.pdf
00055 
00056    2. The initialization done so far is:
00057         a. program the FPGA (windows code sans a lot of error messages)
00058         b.
00059 
00060    3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
00061       you have to output values to all enabled DAC's until result appears, I
00062       guess that it has something to do with pacer clocks, but the source
00063       gives me no clues. I'll keep it simple so far.
00064 
00065    4. Analog in.
00066         Each channel in the scanlist seems to be controlled by four
00067         control words:
00068 
00069         Word0:
00070           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00071           ! | | | ! | | | ! | | | ! | | | !
00072           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00073 
00074         Word1:
00075           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00076           ! | | | ! | | | ! | | | ! | | | !
00077           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00078            |             |       | | | | |
00079            +------+------+       | | | | +-- Digital input (??)
00080                   |              | | | +---- 10 us settling time
00081                   |              | | +------ Suspend acquisition (last to scan)
00082                   |              | +-------- Simultaneous sample and hold
00083                   |              +---------- Signed data format
00084                   +------------------------- Correction offset low
00085 
00086         Word2:
00087           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00088           ! | | | ! | | | ! | | | ! | | | !
00089           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00090            |     | |     | | | | | |     |
00091            +-----+ +--+--+ +++ +++ +--+--+
00092               |       |     |   |     +----- Expansion channel
00093               |       |     |   +----------- Expansion gain
00094               |       |     +--------------- Channel (low)
00095               |       +--------------------- Correction offset high
00096               +----------------------------- Correction gain low
00097         Word3:
00098           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00099           ! | | | ! | | | ! | | | ! | | | !
00100           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
00101            |             | | | |   | | | |
00102            +------+------+ | | +-+-+ | | +-- Low bank enable
00103                   |        | |   |   | +---- High bank enable
00104                   |        | |   |   +------ Hi/low select
00105                   |        | |   +---------- Gain (1,?,2,4,8,16,32,64)
00106                   |        | +-------------- differential/single ended
00107                   |        +---------------- Unipolar
00108                   +------------------------- Correction gain high
00109 
00110 
00111 
00112    999. The card seems to have an incredible amount of capabilities, but
00113         trying to reverse engineer them from the Windows source is beyond my
00114         patience.
00115 
00116 
00117  */
00118 
00119 #include <linux/comedidev.h>
00120 
00121 #include <linux/delay.h>
00122 
00123 #include "comedi_pci.h"
00124 #include "8255.h"
00125 
00126 #define DAQBOARD2000_SUBSYSTEM_IDS2     0x00021616      /* Daqboard/2000 - 2 Dacs */
00127 #define DAQBOARD2000_SUBSYSTEM_IDS4     0x00041616      /* Daqboard/2000 - 4 Dacs */
00128 
00129 #define DAQBOARD2000_DAQ_SIZE           0x1002
00130 #define DAQBOARD2000_PLX_SIZE           0x100
00131 
00132 // Initialization bits for the Serial EEPROM Control Register
00133 #define DAQBOARD2000_SECRProgPinHi      0x8001767e
00134 #define DAQBOARD2000_SECRProgPinLo      0x8000767e
00135 #define DAQBOARD2000_SECRLocalBusHi     0xc000767e
00136 #define DAQBOARD2000_SECRLocalBusLo     0x8000767e
00137 #define DAQBOARD2000_SECRReloadHi       0xa000767e
00138 #define DAQBOARD2000_SECRReloadLo       0x8000767e
00139 
00140 // SECR status bits
00141 #define DAQBOARD2000_EEPROM_PRESENT     0x10000000
00142 
00143 // CPLD status bits
00144 #define DAQBOARD2000_CPLD_INIT          0x0002
00145 #define DAQBOARD2000_CPLD_DONE          0x0004
00146 
00147 // Available ranges
00148 static const comedi_lrange range_daqboard2000_ai = { 13, {
00149                         RANGE(-10, 10),
00150                         RANGE(-5, 5),
00151                         RANGE(-2.5, 2.5),
00152                         RANGE(-1.25, 1.25),
00153                         RANGE(-0.625, 0.625),
00154                         RANGE(-0.3125, 0.3125),
00155                         RANGE(-0.156, 0.156),
00156                         RANGE(0, 10),
00157                         RANGE(0, 5),
00158                         RANGE(0, 2.5),
00159                         RANGE(0, 1.25),
00160                         RANGE(0, 0.625),
00161                         RANGE(0, 0.3125)
00162         }
00163 };
00164 
00165 static const comedi_lrange range_daqboard2000_ao = { 1, {
00166                         RANGE(-10, 10)
00167         }
00168 };
00169 
00170 typedef struct daqboard2000_hw {
00171         volatile u16 acqControl;        // 0x00
00172         volatile u16 acqScanListFIFO;   // 0x02
00173         volatile u32 acqPacerClockDivLow;       // 0x04
00174 
00175         volatile u16 acqScanCounter;    // 0x08
00176         volatile u16 acqPacerClockDivHigh;      // 0x0a
00177         volatile u16 acqTriggerCount;   // 0x0c
00178         volatile u16 fill2;     // 0x0e
00179         volatile u16 acqResultsFIFO;    // 0x10
00180         volatile u16 fill3;     // 0x12
00181         volatile u16 acqResultsShadow;  // 0x14
00182         volatile u16 fill4;     // 0x16
00183         volatile u16 acqAdcResult;      // 0x18
00184         volatile u16 fill5;     // 0x1a
00185         volatile u16 dacScanCounter;    // 0x1c
00186         volatile u16 fill6;     // 0x1e
00187 
00188         volatile u16 dacControl;        // 0x20
00189         volatile u16 fill7;     // 0x22
00190         volatile s16 dacFIFO;   // 0x24
00191         volatile u16 fill8[2];  // 0x26
00192         volatile u16 dacPacerClockDiv;  // 0x2a
00193         volatile u16 refDacs;   // 0x2c
00194         volatile u16 fill9;     // 0x2e
00195 
00196         volatile u16 dioControl;        // 0x30
00197         volatile s16 dioP3hsioData;     // 0x32
00198         volatile u16 dioP3Control;      // 0x34
00199         volatile u16 calEepromControl;  // 0x36
00200         volatile s16 dacSetting[4];     // 0x38
00201         volatile s16 dioP2ExpansionIO8Bit[32];  // 0x40
00202 
00203         volatile u16 ctrTmrControl;     // 0x80
00204         volatile u16 fill10[3]; // 0x82
00205         volatile s16 ctrInput[4];       // 0x88
00206         volatile u16 fill11[8]; // 0x90
00207         volatile u16 timerDivisor[2];   // 0xa0
00208         volatile u16 fill12[6]; // 0xa4
00209 
00210         volatile u16 dmaControl;        // 0xb0
00211         volatile u16 trigControl;       // 0xb2
00212         volatile u16 fill13[2]; // 0xb4
00213         volatile u16 calEeprom; // 0xb8
00214         volatile u16 acqDigitalMark;    // 0xba
00215         volatile u16 trigDacs;  // 0xbc
00216         volatile u16 fill14;    // 0xbe
00217         volatile s16 dioP2ExpansionIO16Bit[32]; // 0xc0
00218 } daqboard2000_hw;
00219 
00220 /* Scan Sequencer programming */
00221 #define DAQBOARD2000_SeqStartScanList            0x0011
00222 #define DAQBOARD2000_SeqStopScanList             0x0010
00223 
00224 // Prepare for acquisition
00225 #define DAQBOARD2000_AcqResetScanListFifo        0x0004
00226 #define DAQBOARD2000_AcqResetResultsFifo         0x0002
00227 #define DAQBOARD2000_AcqResetConfigPipe          0x0001
00228 
00229 // Acqusition status bits
00230 #define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
00231 #define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
00232 #define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
00233 #define DAQBOARD2000_AcqLogicScanning            0x0008
00234 #define DAQBOARD2000_AcqConfigPipeFull           0x0010
00235 #define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
00236 #define DAQBOARD2000_AcqAdcNotReady              0x0040
00237 #define DAQBOARD2000_ArbitrationFailure          0x0080
00238 #define DAQBOARD2000_AcqPacerOverrun             0x0100
00239 #define DAQBOARD2000_DacPacerOverrun             0x0200
00240 #define DAQBOARD2000_AcqHardwareError            0x01c0
00241 
00242 // Scan Sequencer programming
00243 #define DAQBOARD2000_SeqStartScanList            0x0011
00244 #define DAQBOARD2000_SeqStopScanList             0x0010
00245 
00246 /* Pacer Clock Control */
00247 #define DAQBOARD2000_AdcPacerInternal            0x0030
00248 #define DAQBOARD2000_AdcPacerExternal            0x0032
00249 #define DAQBOARD2000_AdcPacerEnable              0x0031
00250 #define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
00251 #define DAQBOARD2000_AdcPacerDisable             0x0030
00252 #define DAQBOARD2000_AdcPacerNormalMode          0x0060
00253 #define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
00254 #define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
00255 #define DAQBOARD2000_AdcPacerExternalRising      0x0100
00256 
00257 // DAC status
00258 #define DAQBOARD2000_DacFull                     0x0001
00259 #define DAQBOARD2000_RefBusy                     0x0002
00260 #define DAQBOARD2000_TrgBusy                     0x0004
00261 #define DAQBOARD2000_CalBusy                     0x0008
00262 #define DAQBOARD2000_Dac0Busy                    0x0010
00263 #define DAQBOARD2000_Dac1Busy                    0x0020
00264 #define DAQBOARD2000_Dac2Busy                    0x0040
00265 #define DAQBOARD2000_Dac3Busy                    0x0080
00266 
00267 // DAC control
00268 #define DAQBOARD2000_Dac0Enable                  0x0021
00269 #define DAQBOARD2000_Dac1Enable                  0x0031
00270 #define DAQBOARD2000_Dac2Enable                  0x0041
00271 #define DAQBOARD2000_Dac3Enable                  0x0051
00272 #define DAQBOARD2000_DacEnableBit                0x0001
00273 #define DAQBOARD2000_Dac0Disable                 0x0020
00274 #define DAQBOARD2000_Dac1Disable                 0x0030
00275 #define DAQBOARD2000_Dac2Disable                 0x0040
00276 #define DAQBOARD2000_Dac3Disable                 0x0050
00277 #define DAQBOARD2000_DacResetFifo                0x0004
00278 #define DAQBOARD2000_DacPatternDisable           0x0060
00279 #define DAQBOARD2000_DacPatternEnable            0x0061
00280 #define DAQBOARD2000_DacSelectSignedData         0x0002
00281 #define DAQBOARD2000_DacSelectUnsignedData       0x0000
00282 
00283 /* Trigger Control */
00284 #define DAQBOARD2000_TrigAnalog                  0x0000
00285 #define DAQBOARD2000_TrigTTL                     0x0010
00286 #define DAQBOARD2000_TrigTransHiLo               0x0004
00287 #define DAQBOARD2000_TrigTransLoHi               0x0000
00288 #define DAQBOARD2000_TrigAbove                   0x0000
00289 #define DAQBOARD2000_TrigBelow                   0x0004
00290 #define DAQBOARD2000_TrigLevelSense              0x0002
00291 #define DAQBOARD2000_TrigEdgeSense               0x0000
00292 #define DAQBOARD2000_TrigEnable                  0x0001
00293 #define DAQBOARD2000_TrigDisable                 0x0000
00294 
00295 // Reference Dac Selection
00296 #define DAQBOARD2000_PosRefDacSelect             0x0100
00297 #define DAQBOARD2000_NegRefDacSelect             0x0000
00298 
00299 static int daqboard2000_attach(comedi_device * dev, comedi_devconfig * it);
00300 static int daqboard2000_detach(comedi_device * dev);
00301 
00302 static comedi_driver driver_daqboard2000 = {
00303       driver_name:"daqboard2000",
00304       module:THIS_MODULE,
00305       attach:daqboard2000_attach,
00306       detach:daqboard2000_detach,
00307 };
00308 
00309 typedef struct {
00310         const char *name;
00311         int id;
00312 } boardtype;
00313 static const boardtype boardtypes[] = {
00314         {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
00315         {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
00316 };
00317 
00318 #define n_boardtypes (sizeof(boardtypes)/sizeof(boardtype))
00319 #define this_board ((const boardtype *)dev->board_ptr)
00320 
00321 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
00322         {0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
00323         {0}
00324 };
00325 
00326 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
00327 
00328 typedef struct {
00329         enum {
00330                 card_daqboard_2000
00331         } card;
00332         struct pci_dev *pci_dev;
00333         void *daq;
00334         void *plx;
00335         int got_regions;
00336         lsampl_t ao_readback[2];
00337 } daqboard2000_private;
00338 
00339 #define devpriv ((daqboard2000_private*)dev->private)
00340 
00341 static void writeAcqScanListEntry(comedi_device * dev, u16 entry)
00342 {
00343         daqboard2000_hw *fpga = devpriv->daq;
00344 
00345 //  comedi_udelay(4);
00346         fpga->acqScanListFIFO = entry & 0x00ff;
00347 //  comedi_udelay(4);
00348         fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
00349 }
00350 
00351 static void setup_sampling(comedi_device * dev, int chan, int gain)
00352 {
00353         u16 word0, word1, word2, word3;
00354 
00355         /* Channel 0-7 diff, channel 8-23 single ended */
00356         word0 = 0;
00357         word1 = 0x0004;         /* Last scan */
00358         word2 = (chan << 6) & 0x00c0;
00359         switch (chan / 4) {
00360         case 0:
00361                 word3 = 0x0001;
00362                 break;
00363         case 1:
00364                 word3 = 0x0002;
00365                 break;
00366         case 2:
00367                 word3 = 0x0005;
00368                 break;
00369         case 3:
00370                 word3 = 0x0006;
00371                 break;
00372         case 4:
00373                 word3 = 0x0041;
00374                 break;
00375         case 5:
00376                 word3 = 0x0042;
00377                 break;
00378         default:
00379                 word3 = 0;
00380                 break;
00381         }
00382 /*
00383   dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
00384   dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
00385 */
00386         /* These should be read from EEPROM */
00387         word2 |= 0x0800;
00388         word3 |= 0xc000;
00389 /*  printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
00390         writeAcqScanListEntry(dev, word0);
00391         writeAcqScanListEntry(dev, word1);
00392         writeAcqScanListEntry(dev, word2);
00393         writeAcqScanListEntry(dev, word3);
00394 }
00395 
00396 static int daqboard2000_ai_insn_read(comedi_device * dev, comedi_subdevice * s,
00397         comedi_insn * insn, lsampl_t * data)
00398 {
00399         int i;
00400         daqboard2000_hw *fpga = devpriv->daq;
00401         int gain, chan, timeout;
00402 
00403         fpga->acqControl =
00404                 DAQBOARD2000_AcqResetScanListFifo |
00405                 DAQBOARD2000_AcqResetResultsFifo |
00406                 DAQBOARD2000_AcqResetConfigPipe;
00407 
00408         /* If pacer clock is not set to some high value (> 10 us), we
00409            risk multiple samples to be put into the result FIFO. */
00410         fpga->acqPacerClockDivLow = 1000000;    /* 1 second, should be long enough */
00411         fpga->acqPacerClockDivHigh = 0;
00412 
00413         gain = CR_RANGE(insn->chanspec);
00414         chan = CR_CHAN(insn->chanspec);
00415 
00416         /* This doesn't look efficient.  I decided to take the conservative
00417          * approach when I did the insn conversion.  Perhaps it would be
00418          * better to have broken it completely, then someone would have been
00419          * forced to fix it.  --ds */
00420         for (i = 0; i < insn->n; i++) {
00421                 setup_sampling(dev, chan, gain);
00422                 /* Enable reading from the scanlist FIFO */
00423                 fpga->acqControl = DAQBOARD2000_SeqStartScanList;
00424                 for (timeout = 0; timeout < 20; timeout++) {
00425                         if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
00426                                 break;
00427                         }
00428                         //comedi_udelay(2);
00429                 }
00430                 fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
00431                 for (timeout = 0; timeout < 20; timeout++) {
00432                         if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
00433                                 break;
00434                         }
00435                         //comedi_udelay(2);
00436                 }
00437                 for (timeout = 0; timeout < 20; timeout++) {
00438                         if (fpga->
00439                                 acqControl &
00440                                 DAQBOARD2000_AcqResultsFIFOHasValidData) {
00441                                 break;
00442                         }
00443                         //comedi_udelay(2);
00444                 }
00445                 data[i] = fpga->acqResultsFIFO;
00446                 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
00447                 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
00448         }
00449 
00450         return i;
00451 }
00452 
00453 static int daqboard2000_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
00454         comedi_insn * insn, lsampl_t * data)
00455 {
00456         int i;
00457         int chan = CR_CHAN(insn->chanspec);
00458 
00459         for (i = 0; i < insn->n; i++) {
00460                 data[i] = devpriv->ao_readback[chan];
00461         }
00462 
00463         return i;
00464 }
00465 
00466 static int daqboard2000_ao_insn_write(comedi_device * dev, comedi_subdevice * s,
00467         comedi_insn * insn, lsampl_t * data)
00468 {
00469         int i;
00470         int chan = CR_CHAN(insn->chanspec);
00471         daqboard2000_hw *fpga = devpriv->daq;
00472         int timeout;
00473 
00474         for (i = 0; i < insn->n; i++) {
00475                 /*
00476                  * OK, since it works OK without enabling the DAC's, let's keep
00477                  * it as simple as possible...
00478                  */
00479                 //fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; comedi_udelay(1000);
00480                 fpga->dacSetting[chan] = data[i];
00481                 for (timeout = 0; timeout < 20; timeout++) {
00482                         if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
00483                                 break;
00484                         }
00485                         //comedi_udelay(2);
00486                 }
00487                 devpriv->ao_readback[chan] = data[i];
00488                 /*
00489                  * Since we never enabled the DAC's, we don't need to disable it...
00490                  * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; comedi_udelay(1000);
00491                  */
00492         }
00493 
00494         return i;
00495 }
00496 
00497 static void daqboard2000_resetLocalBus(comedi_device * dev)
00498 {
00499         printk("daqboard2000_resetLocalBus\n");
00500         writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
00501         comedi_udelay(10000);
00502         writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
00503         comedi_udelay(10000);
00504 }
00505 
00506 static void daqboard2000_reloadPLX(comedi_device * dev)
00507 {
00508         printk("daqboard2000_reloadPLX\n");
00509         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
00510         comedi_udelay(10000);
00511         writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
00512         comedi_udelay(10000);
00513         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
00514         comedi_udelay(10000);
00515 }
00516 
00517 static void daqboard2000_pulseProgPin(comedi_device * dev)
00518 {
00519         printk("daqboard2000_pulseProgPin 1\n");
00520         writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
00521         comedi_udelay(10000);
00522         writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
00523         comedi_udelay(10000);   /* Not in the original code, but I like symmetry... */
00524 }
00525 
00526 static int daqboard2000_pollCPLD(comedi_device * dev, int mask)
00527 {
00528         int result = 0;
00529         int i;
00530         int cpld;
00531 
00532         /* timeout after 50 tries -> 5ms */
00533         for (i = 0; i < 50; i++) {
00534                 cpld = readw(devpriv->daq + 0x1000);
00535                 if ((cpld & mask) == mask) {
00536                         result = 1;
00537                         break;
00538                 }
00539                 comedi_udelay(100);
00540         }
00541         comedi_udelay(5);
00542         return result;
00543 }
00544 
00545 static int daqboard2000_writeCPLD(comedi_device * dev, int data)
00546 {
00547         int result = 0;
00548 
00549         comedi_udelay(10);
00550         writew(data, devpriv->daq + 0x1000);
00551         if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
00552                 DAQBOARD2000_CPLD_INIT) {
00553                 result = 1;
00554         }
00555         return result;
00556 }
00557 
00558 static int initialize_daqboard2000(comedi_device * dev,
00559         unsigned char *cpld_array, int len)
00560 {
00561         int result = -EIO;
00562         /* Read the serial EEPROM control register */
00563         int secr;
00564         int retry;
00565         int i;
00566 
00567         /* Check to make sure the serial eeprom is present on the board */
00568         secr = readl(devpriv->plx + 0x6c);
00569         if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
00570 #ifdef DEBUG_EEPROM
00571                 printk("no serial eeprom\n");
00572 #endif
00573                 return -EIO;
00574         }
00575 
00576         for (retry = 0; retry < 3; retry++) {
00577 #ifdef DEBUG_EEPROM
00578                 printk("Programming EEPROM try %x\n", retry);
00579 #endif
00580 
00581                 daqboard2000_resetLocalBus(dev);
00582                 daqboard2000_reloadPLX(dev);
00583                 daqboard2000_pulseProgPin(dev);
00584                 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
00585                         for (i = 0; i < len; i++) {
00586                                 if (cpld_array[i] == 0xff
00587                                         && cpld_array[i + 1] == 0x20) {
00588 #ifdef DEBUG_EEPROM
00589                                         printk("Preamble found at %d\n", i);
00590 #endif
00591                                         break;
00592                                 }
00593                         }
00594                         for (; i < len; i += 2) {
00595                                 int data =
00596                                         (cpld_array[i] << 8) + cpld_array[i +
00597                                         1];
00598                                 if (!daqboard2000_writeCPLD(dev, data)) {
00599                                         break;
00600                                 }
00601                         }
00602                         if (i >= len) {
00603 #ifdef DEBUG_EEPROM
00604                                 printk("Programmed\n");
00605 #endif
00606                                 daqboard2000_resetLocalBus(dev);
00607                                 daqboard2000_reloadPLX(dev);
00608                                 result = 0;
00609                                 break;
00610                         }
00611                 }
00612         }
00613         return result;
00614 }
00615 
00616 static void daqboard2000_adcStopDmaTransfer(comedi_device * dev)
00617 {
00618 /*  printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
00619 }
00620 
00621 static void daqboard2000_adcDisarm(comedi_device * dev)
00622 {
00623         daqboard2000_hw *fpga = devpriv->daq;
00624 
00625         /* Disable hardware triggers */
00626         comedi_udelay(2);
00627         fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
00628         comedi_udelay(2);
00629         fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
00630 
00631         /* Stop the scan list FIFO from loading the configuration pipe */
00632         comedi_udelay(2);
00633         fpga->acqControl = DAQBOARD2000_SeqStopScanList;
00634 
00635         /* Stop the pacer clock */
00636         comedi_udelay(2);
00637         fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
00638 
00639         /* Stop the input dma (abort channel 1) */
00640         daqboard2000_adcStopDmaTransfer(dev);
00641 }
00642 
00643 static void daqboard2000_activateReferenceDacs(comedi_device * dev)
00644 {
00645         daqboard2000_hw *fpga = devpriv->daq;
00646         int timeout;
00647 
00648         // Set the + reference dac value in the FPGA
00649         fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
00650         for (timeout = 0; timeout < 20; timeout++) {
00651                 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
00652                         break;
00653                 }
00654                 comedi_udelay(2);
00655         }
00656 /*  printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
00657 
00658         // Set the - reference dac value in the FPGA
00659         fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
00660         for (timeout = 0; timeout < 20; timeout++) {
00661                 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
00662                         break;
00663                 }
00664                 comedi_udelay(2);
00665         }
00666 /*  printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
00667 }
00668 
00669 static void daqboard2000_initializeCtrs(comedi_device * dev)
00670 {
00671 /*  printk("Implement: daqboard2000_initializeCtrs\n");*/
00672 }
00673 
00674 static void daqboard2000_initializeTmrs(comedi_device * dev)
00675 {
00676 /*  printk("Implement: daqboard2000_initializeTmrs\n");*/
00677 }
00678 
00679 static void daqboard2000_dacDisarm(comedi_device * dev)
00680 {
00681 /*  printk("Implement: daqboard2000_dacDisarm\n");*/
00682 }
00683 
00684 static void daqboard2000_initializeAdc(comedi_device * dev)
00685 {
00686         daqboard2000_adcDisarm(dev);
00687         daqboard2000_activateReferenceDacs(dev);
00688         daqboard2000_initializeCtrs(dev);
00689         daqboard2000_initializeTmrs(dev);
00690 }
00691 
00692 static void daqboard2000_initializeDac(comedi_device * dev)
00693 {
00694         daqboard2000_dacDisarm(dev);
00695 }
00696 
00697 /*
00698 The test command, REMOVE!!:
00699 
00700 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
00701 */
00702 
00703 static int daqboard2000_8255_cb(int dir, int port, int data,
00704         unsigned long ioaddr)
00705 {
00706         int result = 0;
00707         if (dir) {
00708                 writew(data, ((void *)ioaddr) + port * 2);
00709                 result = 0;
00710         } else {
00711                 result = readw(((void *)ioaddr) + port * 2);
00712         }
00713 /*
00714   printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
00715         arg, dir, port, data, result);
00716 */
00717         return result;
00718 }
00719 
00720 static int daqboard2000_attach(comedi_device * dev, comedi_devconfig * it)
00721 {
00722         int result = 0;
00723         comedi_subdevice *s;
00724         struct pci_dev *card = NULL;
00725         void *aux_data;
00726         unsigned int aux_len;
00727         int bus, slot;
00728 
00729         printk("comedi%d: daqboard2000:", dev->minor);
00730 
00731         bus = it->options[0];
00732         slot = it->options[1];
00733 
00734         result = alloc_private(dev, sizeof(daqboard2000_private));
00735         if (result < 0) {
00736                 return -ENOMEM;
00737         }
00738         for (card = pci_get_device(0x1616, 0x0409, NULL);
00739                 card != NULL;
00740                 card = pci_get_device(0x1616, 0x0409, card)) {
00741                 if (bus || slot) {
00742                         /* requested particular bus/slot */
00743                         if (card->bus->number != bus ||
00744                                 PCI_SLOT(card->devfn) != slot) {
00745                                 continue;
00746                         }
00747                 }
00748                 break;  /* found one */
00749         }
00750         if (!card) {
00751                 if (bus || slot)
00752                         printk(" no daqboard2000 found at bus/slot: %d/%d\n",
00753                                 bus, slot);
00754                 else
00755                         printk(" no daqboard2000 found\n");
00756                 return -EIO;
00757         } else {
00758                 u32 id;
00759                 int i;
00760                 devpriv->pci_dev = card;
00761                 id = ((u32) card->subsystem_device << 16) | card->
00762                         subsystem_vendor;
00763                 for (i = 0; i < n_boardtypes; i++) {
00764                         if (boardtypes[i].id == id) {
00765                                 printk(" %s", boardtypes[i].name);
00766                                 dev->board_ptr = boardtypes + i;
00767                         }
00768                 }
00769                 if (!dev->board_ptr) {
00770                         printk(" unknown subsystem id %08x (pretend it is an ids2)", id);
00771                         dev->board_ptr = boardtypes;
00772                 }
00773         }
00774 
00775         if ((result = comedi_pci_enable(card, "daqboard2000")) < 0) {
00776                 printk(" failed to enable PCI device and request regions\n");
00777                 return -EIO;
00778         }
00779         devpriv->got_regions = 1;
00780         devpriv->plx =
00781                 ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
00782         devpriv->daq =
00783                 ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
00784         if (!devpriv->plx || !devpriv->daq) {
00785                 return -ENOMEM;
00786         }
00787 
00788         result = alloc_subdevices(dev, 3);
00789         if (result < 0)
00790                 goto out;
00791 
00792         readl(devpriv->plx + 0x6c);
00793 
00794         /*
00795            u8 interrupt;
00796            Windows code does restore interrupts, but since we don't use them...
00797            pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
00798            printk("Interrupt before is: %x\n", interrupt);
00799          */
00800 
00801         aux_data = comedi_aux_data(it->options, 0);
00802         aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
00803 
00804         if (aux_data && aux_len) {
00805                 result = initialize_daqboard2000(dev, aux_data, aux_len);
00806         } else {
00807                 printk("no FPGA initialization code, aborting\n");
00808                 result = -EIO;
00809         }
00810         if (result < 0)
00811                 goto out;
00812         daqboard2000_initializeAdc(dev);
00813         daqboard2000_initializeDac(dev);
00814         /*
00815            Windows code does restore interrupts, but since we don't use them...
00816            pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
00817            printk("Interrupt after is: %x\n", interrupt);
00818          */
00819 
00820         dev->iobase = (unsigned long)devpriv->daq;
00821 
00822         dev->board_name = this_board->name;
00823 
00824         s = dev->subdevices + 0;
00825         /* ai subdevice */
00826         s->type = COMEDI_SUBD_AI;
00827         s->subdev_flags = SDF_READABLE | SDF_GROUND;
00828         s->n_chan = 24;
00829         s->maxdata = 0xffff;
00830         s->insn_read = daqboard2000_ai_insn_read;
00831         s->range_table = &range_daqboard2000_ai;
00832 
00833         s = dev->subdevices + 1;
00834         /* ao subdevice */
00835         s->type = COMEDI_SUBD_AO;
00836         s->subdev_flags = SDF_WRITABLE;
00837         s->n_chan = 2;
00838         s->maxdata = 0xffff;
00839         s->insn_read = daqboard2000_ao_insn_read;
00840         s->insn_write = daqboard2000_ao_insn_write;
00841         s->range_table = &range_daqboard2000_ao;
00842 
00843         s = dev->subdevices + 2;
00844         result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
00845                 (unsigned long)(dev->iobase + 0x40));
00846 
00847         printk("\n");
00848       out:
00849         return result;
00850 }
00851 
00852 static int daqboard2000_detach(comedi_device * dev)
00853 {
00854         printk("comedi%d: daqboard2000: remove\n", dev->minor);
00855 
00856         if (dev->subdevices)
00857                 subdev_8255_cleanup(dev, dev->subdevices + 2);
00858 
00859         if (dev->irq) {
00860                 free_irq(dev->irq, dev);
00861         }
00862         if (devpriv) {
00863                 if (devpriv->daq)
00864                         iounmap(devpriv->daq);
00865                 if (devpriv->plx)
00866                         iounmap(devpriv->plx);
00867                 if (devpriv->pci_dev) {
00868                         if (devpriv->got_regions) {
00869                                 comedi_pci_disable(devpriv->pci_dev);
00870                         }
00871                         pci_dev_put(devpriv->pci_dev);
00872                 }
00873         }
00874         return 0;
00875 }
00876 
00877 COMEDI_PCI_INITCLEANUP(driver_daqboard2000, daqboard2000_pci_table);
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines