![]() |
RTXI 1.3
|
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);