daqboard2000.c revision 5ec8df5e46660922810657d0dee06e408813c030
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			/* udelay(2); */
435		}
436		fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
437		for (timeout = 0; timeout < 20; timeout++) {
438			if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning)
439				break;
440			/* udelay(2); */
441		}
442		for (timeout = 0; timeout < 20; timeout++) {
443			if (fpga->acqControl &
444			    DAQBOARD2000_AcqResultsFIFOHasValidData) {
445				break;
446			}
447			/* udelay(2); */
448		}
449		data[i] = fpga->acqResultsFIFO;
450		fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
451		fpga->acqControl = DAQBOARD2000_SeqStopScanList;
452	}
453
454	return i;
455}
456
457static int daqboard2000_ao_insn_read(struct comedi_device *dev,
458				     struct comedi_subdevice *s,
459				     struct comedi_insn *insn,
460				     unsigned int *data)
461{
462	int i;
463	int chan = CR_CHAN(insn->chanspec);
464
465	for (i = 0; i < insn->n; i++)
466		data[i] = devpriv->ao_readback[chan];
467
468	return i;
469}
470
471static int daqboard2000_ao_insn_write(struct comedi_device *dev,
472				      struct comedi_subdevice *s,
473				      struct comedi_insn *insn,
474				      unsigned int *data)
475{
476	int i;
477	int chan = CR_CHAN(insn->chanspec);
478	struct daqboard2000_hw *fpga = devpriv->daq;
479	int timeout;
480
481	for (i = 0; i < insn->n; i++) {
482		/*
483		 * OK, since it works OK without enabling the DAC's, let's keep
484		 * it as simple as possible...
485		 */
486		/* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
487		fpga->dacSetting[chan] = data[i];
488		for (timeout = 0; timeout < 20; timeout++) {
489			if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0)
490				break;
491			/* udelay(2); */
492		}
493		devpriv->ao_readback[chan] = data[i];
494		/*
495		 * Since we never enabled the DAC's, we don't need to disable it...
496		 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
497		 */
498	}
499
500	return i;
501}
502
503static void daqboard2000_resetLocalBus(struct comedi_device *dev)
504{
505	dev_dbg(dev->hw_dev, "daqboard2000_resetLocalBus\n");
506	writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
507	udelay(10000);
508	writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
509	udelay(10000);
510}
511
512static void daqboard2000_reloadPLX(struct comedi_device *dev)
513{
514	dev_dbg(dev->hw_dev, "daqboard2000_reloadPLX\n");
515	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
516	udelay(10000);
517	writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
518	udelay(10000);
519	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
520	udelay(10000);
521}
522
523static void daqboard2000_pulseProgPin(struct comedi_device *dev)
524{
525	dev_dbg(dev->hw_dev, "daqboard2000_pulseProgPin 1\n");
526	writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
527	udelay(10000);
528	writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
529	udelay(10000);		/* Not in the original code, but I like symmetry... */
530}
531
532static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
533{
534	int result = 0;
535	int i;
536	int cpld;
537
538	/* timeout after 50 tries -> 5ms */
539	for (i = 0; i < 50; i++) {
540		cpld = readw(devpriv->daq + 0x1000);
541		if ((cpld & mask) == mask) {
542			result = 1;
543			break;
544		}
545		udelay(100);
546	}
547	udelay(5);
548	return result;
549}
550
551static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
552{
553	int result = 0;
554
555	udelay(10);
556	writew(data, devpriv->daq + 0x1000);
557	if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
558	    DAQBOARD2000_CPLD_INIT) {
559		result = 1;
560	}
561	return result;
562}
563
564static int initialize_daqboard2000(struct comedi_device *dev,
565				   unsigned char *cpld_array, int len)
566{
567	int result = -EIO;
568	/* Read the serial EEPROM control register */
569	int secr;
570	int retry;
571	int i;
572
573	/* Check to make sure the serial eeprom is present on the board */
574	secr = readl(devpriv->plx + 0x6c);
575	if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
576#ifdef DEBUG_EEPROM
577		printk("no serial eeprom\n");
578#endif
579		return -EIO;
580	}
581
582	for (retry = 0; retry < 3; retry++) {
583#ifdef DEBUG_EEPROM
584		printk("Programming EEPROM try %x\n", retry);
585#endif
586
587		daqboard2000_resetLocalBus(dev);
588		daqboard2000_reloadPLX(dev);
589		daqboard2000_pulseProgPin(dev);
590		if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
591			for (i = 0; i < len; i++) {
592				if (cpld_array[i] == 0xff
593				    && cpld_array[i + 1] == 0x20) {
594#ifdef DEBUG_EEPROM
595					printk("Preamble found at %d\n", i);
596#endif
597					break;
598				}
599			}
600			for (; i < len; i += 2) {
601				int data =
602				    (cpld_array[i] << 8) + cpld_array[i + 1];
603				if (!daqboard2000_writeCPLD(dev, data))
604					break;
605			}
606			if (i >= len) {
607#ifdef DEBUG_EEPROM
608				printk("Programmed\n");
609#endif
610				daqboard2000_resetLocalBus(dev);
611				daqboard2000_reloadPLX(dev);
612				result = 0;
613				break;
614			}
615		}
616	}
617	return result;
618}
619
620static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
621{
622/*  printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
623}
624
625static void daqboard2000_adcDisarm(struct comedi_device *dev)
626{
627	struct daqboard2000_hw *fpga = devpriv->daq;
628
629	/* Disable hardware triggers */
630	udelay(2);
631	fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
632	udelay(2);
633	fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
634
635	/* Stop the scan list FIFO from loading the configuration pipe */
636	udelay(2);
637	fpga->acqControl = DAQBOARD2000_SeqStopScanList;
638
639	/* Stop the pacer clock */
640	udelay(2);
641	fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
642
643	/* Stop the input dma (abort channel 1) */
644	daqboard2000_adcStopDmaTransfer(dev);
645}
646
647static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
648{
649	struct daqboard2000_hw *fpga = devpriv->daq;
650	int timeout;
651
652	/*  Set the + reference dac value in the FPGA */
653	fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
654	for (timeout = 0; timeout < 20; timeout++) {
655		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
656			break;
657		udelay(2);
658	}
659/*  printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
660
661	/*  Set the - reference dac value in the FPGA */
662	fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
663	for (timeout = 0; timeout < 20; timeout++) {
664		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
665			break;
666		udelay(2);
667	}
668/*  printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
669}
670
671static void daqboard2000_initializeCtrs(struct comedi_device *dev)
672{
673/*  printk("Implement: daqboard2000_initializeCtrs\n");*/
674}
675
676static void daqboard2000_initializeTmrs(struct comedi_device *dev)
677{
678/*  printk("Implement: daqboard2000_initializeTmrs\n");*/
679}
680
681static void daqboard2000_dacDisarm(struct comedi_device *dev)
682{
683/*  printk("Implement: daqboard2000_dacDisarm\n");*/
684}
685
686static void daqboard2000_initializeAdc(struct comedi_device *dev)
687{
688	daqboard2000_adcDisarm(dev);
689	daqboard2000_activateReferenceDacs(dev);
690	daqboard2000_initializeCtrs(dev);
691	daqboard2000_initializeTmrs(dev);
692}
693
694static void daqboard2000_initializeDac(struct comedi_device *dev)
695{
696	daqboard2000_dacDisarm(dev);
697}
698
699/*
700The test command, REMOVE!!:
701
702rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
703*/
704
705static int daqboard2000_8255_cb(int dir, int port, int data,
706				unsigned long ioaddr)
707{
708	int result = 0;
709	if (dir) {
710		writew(data, ((void *)ioaddr) + port * 2);
711		result = 0;
712	} else {
713		result = readw(((void *)ioaddr) + port * 2);
714	}
715/*
716  printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
717        arg, dir, port, data, result);
718*/
719	return result;
720}
721
722static int daqboard2000_attach(struct comedi_device *dev,
723			       struct comedi_devconfig *it)
724{
725	int result = 0;
726	struct comedi_subdevice *s;
727	struct pci_dev *card = NULL;
728	void *aux_data;
729	unsigned int aux_len;
730	int bus, slot;
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; 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			dev_err(dev->hw_dev, "no daqboard2000 found at bus/slot: %d/%d\n",
753				bus, slot);
754		else
755			dev_err(dev->hw_dev, "no daqboard2000 found\n");
756		return -EIO;
757	} else {
758		u32 id;
759		int i;
760		devpriv->pci_dev = card;
761		id = ((u32) card->
762		      subsystem_device << 16) | card->subsystem_vendor;
763		for (i = 0; i < n_boardtypes; i++) {
764			if (boardtypes[i].id == id) {
765				dev_dbg(dev->hw_dev, "%s\n",
766					boardtypes[i].name);
767				dev->board_ptr = boardtypes + i;
768			}
769		}
770		if (!dev->board_ptr) {
771			printk
772			    (" unknown subsystem id %08x (pretend it is an ids2)",
773			     id);
774			dev->board_ptr = boardtypes;
775		}
776	}
777
778	result = comedi_pci_enable(card, "daqboard2000");
779	if (result < 0) {
780		dev_err(dev->hw_dev, "failed to enable PCI device and request regions\n");
781		return -EIO;
782	}
783	devpriv->got_regions = 1;
784	devpriv->plx =
785	    ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
786	devpriv->daq =
787	    ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
788	if (!devpriv->plx || !devpriv->daq)
789		return -ENOMEM;
790
791	result = alloc_subdevices(dev, 3);
792	if (result < 0)
793		goto out;
794
795	readl(devpriv->plx + 0x6c);
796
797	/*
798	   u8 interrupt;
799	   Windows code does restore interrupts, but since we don't use them...
800	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
801	   printk("Interrupt before is: %x\n", interrupt);
802	 */
803
804	aux_data = comedi_aux_data(it->options, 0);
805	aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
806
807	if (aux_data && aux_len) {
808		result = initialize_daqboard2000(dev, aux_data, aux_len);
809	} else {
810		dev_dbg(dev->hw_dev, "no FPGA initialization code, aborting\n");
811		result = -EIO;
812	}
813	if (result < 0)
814		goto out;
815	daqboard2000_initializeAdc(dev);
816	daqboard2000_initializeDac(dev);
817	/*
818	   Windows code does restore interrupts, but since we don't use them...
819	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
820	   printk("Interrupt after is: %x\n", interrupt);
821	 */
822
823	dev->iobase = (unsigned long)devpriv->daq;
824
825	dev->board_name = this_board->name;
826
827	s = dev->subdevices + 0;
828	/* ai subdevice */
829	s->type = COMEDI_SUBD_AI;
830	s->subdev_flags = SDF_READABLE | SDF_GROUND;
831	s->n_chan = 24;
832	s->maxdata = 0xffff;
833	s->insn_read = daqboard2000_ai_insn_read;
834	s->range_table = &range_daqboard2000_ai;
835
836	s = dev->subdevices + 1;
837	/* ao subdevice */
838	s->type = COMEDI_SUBD_AO;
839	s->subdev_flags = SDF_WRITABLE;
840	s->n_chan = 2;
841	s->maxdata = 0xffff;
842	s->insn_read = daqboard2000_ao_insn_read;
843	s->insn_write = daqboard2000_ao_insn_write;
844	s->range_table = &range_daqboard2000_ao;
845
846	s = dev->subdevices + 2;
847	result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
848				  (unsigned long)(dev->iobase + 0x40));
849
850out:
851	return result;
852}
853
854static int daqboard2000_detach(struct comedi_device *dev)
855{
856	if (dev->subdevices)
857		subdev_8255_cleanup(dev, dev->subdevices + 2);
858
859	if (dev->irq)
860		free_irq(dev->irq, dev);
861
862	if (devpriv) {
863		if (devpriv->daq)
864			iounmap(devpriv->daq);
865		if (devpriv->plx)
866			iounmap(devpriv->plx);
867		if (devpriv->pci_dev) {
868			if (devpriv->got_regions)
869				comedi_pci_disable(devpriv->pci_dev);
870			pci_dev_put(devpriv->pci_dev);
871		}
872	}
873	return 0;
874}
875
876static int __devinit driver_daqboard2000_pci_probe(struct pci_dev *dev,
877						   const struct pci_device_id
878						   *ent)
879{
880	return comedi_pci_auto_config(dev, driver_daqboard2000.driver_name);
881}
882
883static void __devexit driver_daqboard2000_pci_remove(struct pci_dev *dev)
884{
885	comedi_pci_auto_unconfig(dev);
886}
887
888static struct pci_driver driver_daqboard2000_pci_driver = {
889	.id_table = daqboard2000_pci_table,
890	.probe = &driver_daqboard2000_pci_probe,
891	.remove = __devexit_p(&driver_daqboard2000_pci_remove)
892};
893
894static int __init driver_daqboard2000_init_module(void)
895{
896	int retval;
897
898	retval = comedi_driver_register(&driver_daqboard2000);
899	if (retval < 0)
900		return retval;
901
902	driver_daqboard2000_pci_driver.name =
903	    (char *)driver_daqboard2000.driver_name;
904	return pci_register_driver(&driver_daqboard2000_pci_driver);
905}
906
907static void __exit driver_daqboard2000_cleanup_module(void)
908{
909	pci_unregister_driver(&driver_daqboard2000_pci_driver);
910	comedi_driver_unregister(&driver_daqboard2000);
911}
912
913module_init(driver_daqboard2000_init_module);
914module_exit(driver_daqboard2000_cleanup_module);
915
916MODULE_AUTHOR("Comedi http://www.comedi.org");
917MODULE_DESCRIPTION("Comedi low-level driver");
918MODULE_LICENSE("GPL");
919