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