2 comedi/drivers/daqboard2000.c
3 hardware driver for IOtech DAQboard/2000
5 COMEDI - Linux Control and Measurement Device Interface
6 Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
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.
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.
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.
25 Description: IOTech DAQBoard/2000
26 Author: Anders Blomdell <anders.blomdell@control.lth.se>
28 Updated: Mon, 14 Apr 2008 15:28:52 +0100
29 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
31 Much of the functionality of this driver was determined from reading
32 the source code for the Windows driver.
34 The FPGA on the board requires initialization code, which can
35 be loaded by comedi_config using the -i
36 option. The initialization code is available from http://www.comedi.org
37 in the comedi_nonfree_firmware tarball.
39 Configuration 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.
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).
50 With some help from our swedish distributor, we got the Windows sourcecode
51 for the card, and here are the findings so far.
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
56 2. The initialization done so far is:
57 a. program the FPGA (windows code sans a lot of error messages)
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.
66 Each channel in the scanlist seems to be controlled by four
70 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71 ! | | | ! | | | ! | | | ! | | | !
72 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 ! | | | ! | | | ! | | | ! | | | !
77 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
87 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 ! | | | ! | | | ! | | | ! | | | !
89 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91 +-----+ +--+--+ +++ +++ +--+--+
92 | | | | +----- Expansion channel
93 | | | +----------- Expansion gain
94 | | +--------------- Channel (low)
95 | +--------------------- Correction offset high
96 +----------------------------- Correction gain low
98 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99 ! | | | ! | | | ! | | | ! | | | !
100 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
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
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
116 #include "../comedidev.h"
118 #include <linux/delay.h>
119 #include <linux/interrupt.h>
123 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x00021616 /* Daqboard/2000 - 2 Dacs */
124 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x00041616 /* Daqboard/2000 - 4 Dacs */
126 #define DAQBOARD2000_DAQ_SIZE 0x1002
127 #define DAQBOARD2000_PLX_SIZE 0x100
129 /* Initialization bits for the Serial EEPROM Control Register */
130 #define DAQBOARD2000_SECRProgPinHi 0x8001767e
131 #define DAQBOARD2000_SECRProgPinLo 0x8000767e
132 #define DAQBOARD2000_SECRLocalBusHi 0xc000767e
133 #define DAQBOARD2000_SECRLocalBusLo 0x8000767e
134 #define DAQBOARD2000_SECRReloadHi 0xa000767e
135 #define DAQBOARD2000_SECRReloadLo 0x8000767e
137 /* SECR status bits */
138 #define DAQBOARD2000_EEPROM_PRESENT 0x10000000
140 /* CPLD status bits */
141 #define DAQBOARD2000_CPLD_INIT 0x0002
142 #define DAQBOARD2000_CPLD_DONE 0x0004
144 /* Available ranges */
145 static const struct comedi_lrange range_daqboard2000_ai = { 13, {
169 static const struct comedi_lrange range_daqboard2000_ao = { 1, {
174 struct daqboard2000_hw {
175 volatile u16 acqControl; /* 0x00 */
176 volatile u16 acqScanListFIFO; /* 0x02 */
177 volatile u32 acqPacerClockDivLow; /* 0x04 */
179 volatile u16 acqScanCounter; /* 0x08 */
180 volatile u16 acqPacerClockDivHigh; /* 0x0a */
181 volatile u16 acqTriggerCount; /* 0x0c */
182 volatile u16 fill2; /* 0x0e */
183 volatile u16 acqResultsFIFO; /* 0x10 */
184 volatile u16 fill3; /* 0x12 */
185 volatile u16 acqResultsShadow; /* 0x14 */
186 volatile u16 fill4; /* 0x16 */
187 volatile u16 acqAdcResult; /* 0x18 */
188 volatile u16 fill5; /* 0x1a */
189 volatile u16 dacScanCounter; /* 0x1c */
190 volatile u16 fill6; /* 0x1e */
192 volatile u16 dacControl; /* 0x20 */
193 volatile u16 fill7; /* 0x22 */
194 volatile s16 dacFIFO; /* 0x24 */
195 volatile u16 fill8[2]; /* 0x26 */
196 volatile u16 dacPacerClockDiv; /* 0x2a */
197 volatile u16 refDacs; /* 0x2c */
198 volatile u16 fill9; /* 0x2e */
200 volatile u16 dioControl; /* 0x30 */
201 volatile s16 dioP3hsioData; /* 0x32 */
202 volatile u16 dioP3Control; /* 0x34 */
203 volatile u16 calEepromControl; /* 0x36 */
204 volatile s16 dacSetting[4]; /* 0x38 */
205 volatile s16 dioP2ExpansionIO8Bit[32]; /* 0x40 */
207 volatile u16 ctrTmrControl; /* 0x80 */
208 volatile u16 fill10[3]; /* 0x82 */
209 volatile s16 ctrInput[4]; /* 0x88 */
210 volatile u16 fill11[8]; /* 0x90 */
211 volatile u16 timerDivisor[2]; /* 0xa0 */
212 volatile u16 fill12[6]; /* 0xa4 */
214 volatile u16 dmaControl; /* 0xb0 */
215 volatile u16 trigControl; /* 0xb2 */
216 volatile u16 fill13[2]; /* 0xb4 */
217 volatile u16 calEeprom; /* 0xb8 */
218 volatile u16 acqDigitalMark; /* 0xba */
219 volatile u16 trigDacs; /* 0xbc */
220 volatile u16 fill14; /* 0xbe */
221 volatile s16 dioP2ExpansionIO16Bit[32]; /* 0xc0 */
224 /* Scan Sequencer programming */
225 #define DAQBOARD2000_SeqStartScanList 0x0011
226 #define DAQBOARD2000_SeqStopScanList 0x0010
228 /* Prepare for acquisition */
229 #define DAQBOARD2000_AcqResetScanListFifo 0x0004
230 #define DAQBOARD2000_AcqResetResultsFifo 0x0002
231 #define DAQBOARD2000_AcqResetConfigPipe 0x0001
233 /* Acqusition status bits */
234 #define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
235 #define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
236 #define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
237 #define DAQBOARD2000_AcqLogicScanning 0x0008
238 #define DAQBOARD2000_AcqConfigPipeFull 0x0010
239 #define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
240 #define DAQBOARD2000_AcqAdcNotReady 0x0040
241 #define DAQBOARD2000_ArbitrationFailure 0x0080
242 #define DAQBOARD2000_AcqPacerOverrun 0x0100
243 #define DAQBOARD2000_DacPacerOverrun 0x0200
244 #define DAQBOARD2000_AcqHardwareError 0x01c0
246 /* Scan Sequencer programming */
247 #define DAQBOARD2000_SeqStartScanList 0x0011
248 #define DAQBOARD2000_SeqStopScanList 0x0010
250 /* Pacer Clock Control */
251 #define DAQBOARD2000_AdcPacerInternal 0x0030
252 #define DAQBOARD2000_AdcPacerExternal 0x0032
253 #define DAQBOARD2000_AdcPacerEnable 0x0031
254 #define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
255 #define DAQBOARD2000_AdcPacerDisable 0x0030
256 #define DAQBOARD2000_AdcPacerNormalMode 0x0060
257 #define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
258 #define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
259 #define DAQBOARD2000_AdcPacerExternalRising 0x0100
262 #define DAQBOARD2000_DacFull 0x0001
263 #define DAQBOARD2000_RefBusy 0x0002
264 #define DAQBOARD2000_TrgBusy 0x0004
265 #define DAQBOARD2000_CalBusy 0x0008
266 #define DAQBOARD2000_Dac0Busy 0x0010
267 #define DAQBOARD2000_Dac1Busy 0x0020
268 #define DAQBOARD2000_Dac2Busy 0x0040
269 #define DAQBOARD2000_Dac3Busy 0x0080
272 #define DAQBOARD2000_Dac0Enable 0x0021
273 #define DAQBOARD2000_Dac1Enable 0x0031
274 #define DAQBOARD2000_Dac2Enable 0x0041
275 #define DAQBOARD2000_Dac3Enable 0x0051
276 #define DAQBOARD2000_DacEnableBit 0x0001
277 #define DAQBOARD2000_Dac0Disable 0x0020
278 #define DAQBOARD2000_Dac1Disable 0x0030
279 #define DAQBOARD2000_Dac2Disable 0x0040
280 #define DAQBOARD2000_Dac3Disable 0x0050
281 #define DAQBOARD2000_DacResetFifo 0x0004
282 #define DAQBOARD2000_DacPatternDisable 0x0060
283 #define DAQBOARD2000_DacPatternEnable 0x0061
284 #define DAQBOARD2000_DacSelectSignedData 0x0002
285 #define DAQBOARD2000_DacSelectUnsignedData 0x0000
287 /* Trigger Control */
288 #define DAQBOARD2000_TrigAnalog 0x0000
289 #define DAQBOARD2000_TrigTTL 0x0010
290 #define DAQBOARD2000_TrigTransHiLo 0x0004
291 #define DAQBOARD2000_TrigTransLoHi 0x0000
292 #define DAQBOARD2000_TrigAbove 0x0000
293 #define DAQBOARD2000_TrigBelow 0x0004
294 #define DAQBOARD2000_TrigLevelSense 0x0002
295 #define DAQBOARD2000_TrigEdgeSense 0x0000
296 #define DAQBOARD2000_TrigEnable 0x0001
297 #define DAQBOARD2000_TrigDisable 0x0000
299 /* Reference Dac Selection */
300 #define DAQBOARD2000_PosRefDacSelect 0x0100
301 #define DAQBOARD2000_NegRefDacSelect 0x0000
303 struct daq200_boardtype {
307 static const struct daq200_boardtype boardtypes[] = {
308 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
309 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
312 #define this_board ((const struct daq200_boardtype *)dev->board_ptr)
314 struct daqboard2000_private {
318 struct pci_dev *pci_dev;
322 unsigned int ao_readback[2];
325 #define devpriv ((struct daqboard2000_private *)dev->private)
327 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
329 struct daqboard2000_hw *fpga = devpriv->daq;
332 fpga->acqScanListFIFO = entry & 0x00ff;
334 fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
337 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
339 u16 word0, word1, word2, word3;
341 /* Channel 0-7 diff, channel 8-23 single ended */
343 word1 = 0x0004; /* Last scan */
344 word2 = (chan << 6) & 0x00c0;
369 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
370 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
372 /* These should be read from EEPROM */
375 /* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
376 writeAcqScanListEntry(dev, word0);
377 writeAcqScanListEntry(dev, word1);
378 writeAcqScanListEntry(dev, word2);
379 writeAcqScanListEntry(dev, word3);
382 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
383 struct comedi_subdevice *s,
384 struct comedi_insn *insn,
388 struct daqboard2000_hw *fpga = devpriv->daq;
389 int gain, chan, timeout;
392 DAQBOARD2000_AcqResetScanListFifo |
393 DAQBOARD2000_AcqResetResultsFifo | DAQBOARD2000_AcqResetConfigPipe;
396 * If pacer clock is not set to some high value (> 10 us), we
397 * risk multiple samples to be put into the result FIFO.
399 /* 1 second, should be long enough */
400 fpga->acqPacerClockDivLow = 1000000;
401 fpga->acqPacerClockDivHigh = 0;
403 gain = CR_RANGE(insn->chanspec);
404 chan = CR_CHAN(insn->chanspec);
406 /* This doesn't look efficient. I decided to take the conservative
407 * approach when I did the insn conversion. Perhaps it would be
408 * better to have broken it completely, then someone would have been
409 * forced to fix it. --ds */
410 for (i = 0; i < insn->n; i++) {
411 setup_sampling(dev, chan, gain);
412 /* Enable reading from the scanlist FIFO */
413 fpga->acqControl = DAQBOARD2000_SeqStartScanList;
414 for (timeout = 0; timeout < 20; timeout++) {
415 if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull)
419 fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
420 for (timeout = 0; timeout < 20; timeout++) {
421 if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning)
425 for (timeout = 0; timeout < 20; timeout++) {
426 if (fpga->acqControl &
427 DAQBOARD2000_AcqResultsFIFOHasValidData) {
432 data[i] = fpga->acqResultsFIFO;
433 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
434 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
440 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
441 struct comedi_subdevice *s,
442 struct comedi_insn *insn,
446 int chan = CR_CHAN(insn->chanspec);
448 for (i = 0; i < insn->n; i++)
449 data[i] = devpriv->ao_readback[chan];
454 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
455 struct comedi_subdevice *s,
456 struct comedi_insn *insn,
460 int chan = CR_CHAN(insn->chanspec);
461 struct daqboard2000_hw *fpga = devpriv->daq;
464 for (i = 0; i < insn->n; i++) {
466 * OK, since it works OK without enabling the DAC's, let's keep
467 * it as simple as possible...
469 /* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
470 fpga->dacSetting[chan] = data[i];
471 for (timeout = 0; timeout < 20; timeout++) {
472 if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0)
476 devpriv->ao_readback[chan] = data[i];
478 * Since we never enabled the DAC's, we don't need to disable it...
479 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
486 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
488 dev_dbg(dev->class_dev, "daqboard2000_resetLocalBus\n");
489 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
491 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
495 static void daqboard2000_reloadPLX(struct comedi_device *dev)
497 dev_dbg(dev->class_dev, "daqboard2000_reloadPLX\n");
498 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
500 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
502 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
506 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
508 dev_dbg(dev->class_dev, "daqboard2000_pulseProgPin 1\n");
509 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
511 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
512 udelay(10000); /* Not in the original code, but I like symmetry... */
515 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
521 /* timeout after 50 tries -> 5ms */
522 for (i = 0; i < 50; i++) {
523 cpld = readw(devpriv->daq + 0x1000);
524 if ((cpld & mask) == mask) {
534 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
539 writew(data, devpriv->daq + 0x1000);
540 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
541 DAQBOARD2000_CPLD_INIT) {
547 static int initialize_daqboard2000(struct comedi_device *dev,
548 unsigned char *cpld_array, int len)
551 /* Read the serial EEPROM control register */
556 /* Check to make sure the serial eeprom is present on the board */
557 secr = readl(devpriv->plx + 0x6c);
558 if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
560 dev_dbg(dev->class_dev, "no serial eeprom\n");
565 for (retry = 0; retry < 3; retry++) {
567 dev_dbg(dev->class_dev, "Programming EEPROM try %x\n", retry);
570 daqboard2000_resetLocalBus(dev);
571 daqboard2000_reloadPLX(dev);
572 daqboard2000_pulseProgPin(dev);
573 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
574 for (i = 0; i < len; i++) {
575 if (cpld_array[i] == 0xff
576 && cpld_array[i + 1] == 0x20) {
578 dev_dbg(dev->class_dev,
579 "Preamble found at %d\n", i);
584 for (; i < len; i += 2) {
586 (cpld_array[i] << 8) + cpld_array[i + 1];
587 if (!daqboard2000_writeCPLD(dev, data))
592 dev_dbg(dev->class_dev, "Programmed\n");
594 daqboard2000_resetLocalBus(dev);
595 daqboard2000_reloadPLX(dev);
604 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
606 /* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
609 static void daqboard2000_adcDisarm(struct comedi_device *dev)
611 struct daqboard2000_hw *fpga = devpriv->daq;
613 /* Disable hardware triggers */
615 fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
617 fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
619 /* Stop the scan list FIFO from loading the configuration pipe */
621 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
623 /* Stop the pacer clock */
625 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
627 /* Stop the input dma (abort channel 1) */
628 daqboard2000_adcStopDmaTransfer(dev);
631 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
633 struct daqboard2000_hw *fpga = devpriv->daq;
636 /* Set the + reference dac value in the FPGA */
637 fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
638 for (timeout = 0; timeout < 20; timeout++) {
639 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
643 /* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
645 /* Set the - reference dac value in the FPGA */
646 fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
647 for (timeout = 0; timeout < 20; timeout++) {
648 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
652 /* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
655 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
657 /* printk("Implement: daqboard2000_initializeCtrs\n");*/
660 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
662 /* printk("Implement: daqboard2000_initializeTmrs\n");*/
665 static void daqboard2000_dacDisarm(struct comedi_device *dev)
667 /* printk("Implement: daqboard2000_dacDisarm\n");*/
670 static void daqboard2000_initializeAdc(struct comedi_device *dev)
672 daqboard2000_adcDisarm(dev);
673 daqboard2000_activateReferenceDacs(dev);
674 daqboard2000_initializeCtrs(dev);
675 daqboard2000_initializeTmrs(dev);
678 static void daqboard2000_initializeDac(struct comedi_device *dev)
680 daqboard2000_dacDisarm(dev);
684 The test command, REMOVE!!:
686 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
689 static int daqboard2000_8255_cb(int dir, int port, int data,
690 unsigned long ioaddr)
694 writew(data, ((void *)ioaddr) + port * 2);
697 result = readw(((void *)ioaddr) + port * 2);
700 printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
701 arg, dir, port, data, result);
706 static struct pci_dev *daqboard2000_find_pci_dev(struct comedi_device *dev,
707 struct comedi_devconfig *it)
709 struct pci_dev *pcidev = NULL;
710 int bus = it->options[0];
711 int slot = it->options[1];
713 for (pcidev = pci_get_device(0x1616, 0x0409, NULL);
714 pcidev != NULL; pcidev = pci_get_device(0x1616, 0x0409, pcidev)) {
716 /* requested particular bus/slot */
717 if (pcidev->bus->number != bus ||
718 PCI_SLOT(pcidev->devfn) != slot) {
722 break; /* found one */
726 dev_err(dev->class_dev,
727 "no daqboard2000 found at bus/slot: %d/%d\n",
730 dev_err(dev->class_dev, "no daqboard2000 found\n");
737 subsystem_device << 16) | pcidev->subsystem_vendor;
738 for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
739 if (boardtypes[i].id == id) {
740 dev_dbg(dev->class_dev, "%s\n",
742 dev->board_ptr = boardtypes + i;
745 if (!dev->board_ptr) {
747 (" unknown subsystem id %08x (pretend it is an ids2)",
749 dev->board_ptr = boardtypes;
755 static int daqboard2000_attach(struct comedi_device *dev,
756 struct comedi_devconfig *it)
758 struct pci_dev *pcidev;
759 struct comedi_subdevice *s;
761 unsigned int aux_len;
764 result = alloc_private(dev, sizeof(struct daqboard2000_private));
768 pcidev = daqboard2000_find_pci_dev(dev, it);
771 devpriv->pci_dev = pcidev;
773 result = comedi_pci_enable(pcidev, "daqboard2000");
775 dev_err(dev->class_dev,
776 "failed to enable PCI device and request regions\n");
779 devpriv->got_regions = 1;
781 ioremap(pci_resource_start(pcidev, 0), DAQBOARD2000_PLX_SIZE);
783 ioremap(pci_resource_start(pcidev, 2), DAQBOARD2000_DAQ_SIZE);
784 if (!devpriv->plx || !devpriv->daq)
787 result = comedi_alloc_subdevices(dev, 3);
791 readl(devpriv->plx + 0x6c);
795 Windows code does restore interrupts, but since we don't use them...
796 pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
797 printk("Interrupt before is: %x\n", interrupt);
800 aux_data = comedi_aux_data(it->options, 0);
801 aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
803 if (aux_data && aux_len) {
804 result = initialize_daqboard2000(dev, aux_data, aux_len);
806 dev_dbg(dev->class_dev,
807 "no FPGA initialization code, aborting\n");
812 daqboard2000_initializeAdc(dev);
813 daqboard2000_initializeDac(dev);
815 Windows code does restore interrupts, but since we don't use them...
816 pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
817 printk("Interrupt after is: %x\n", interrupt);
820 dev->iobase = (unsigned long)devpriv->daq;
822 dev->board_name = this_board->name;
824 s = dev->subdevices + 0;
826 s->type = COMEDI_SUBD_AI;
827 s->subdev_flags = SDF_READABLE | SDF_GROUND;
830 s->insn_read = daqboard2000_ai_insn_read;
831 s->range_table = &range_daqboard2000_ai;
833 s = dev->subdevices + 1;
835 s->type = COMEDI_SUBD_AO;
836 s->subdev_flags = SDF_WRITABLE;
839 s->insn_read = daqboard2000_ao_insn_read;
840 s->insn_write = daqboard2000_ao_insn_write;
841 s->range_table = &range_daqboard2000_ao;
843 s = dev->subdevices + 2;
844 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
845 (unsigned long)(dev->iobase + 0x40));
851 static void daqboard2000_detach(struct comedi_device *dev)
854 subdev_8255_cleanup(dev, dev->subdevices + 2);
856 free_irq(dev->irq, dev);
859 iounmap(devpriv->daq);
861 iounmap(devpriv->plx);
862 if (devpriv->pci_dev) {
863 if (devpriv->got_regions)
864 comedi_pci_disable(devpriv->pci_dev);
865 pci_dev_put(devpriv->pci_dev);
870 static struct comedi_driver daqboard2000_driver = {
871 .driver_name = "daqboard2000",
872 .module = THIS_MODULE,
873 .attach = daqboard2000_attach,
874 .detach = daqboard2000_detach,
877 static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
878 const struct pci_device_id *ent)
880 return comedi_pci_auto_config(dev, &daqboard2000_driver);
883 static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
885 comedi_pci_auto_unconfig(dev);
888 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
889 { PCI_DEVICE(0x1616, 0x0409) },
892 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
894 static struct pci_driver daqboard2000_pci_driver = {
895 .name = "daqboard2000",
896 .id_table = daqboard2000_pci_table,
897 .probe = daqboard2000_pci_probe,
898 .remove = __devexit_p(daqboard2000_pci_remove),
900 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
902 MODULE_AUTHOR("Comedi http://www.comedi.org");
903 MODULE_DESCRIPTION("Comedi low-level driver");
904 MODULE_LICENSE("GPL");