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