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