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