daqboard2000.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
1/*
2   comedi/drivers/daqboard2000.c
3   hardware driver for IOtech DAQboard/2000
4
5   COMEDI - Linux Control and Measurement Device Interface
6   Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23/*
24Driver: daqboard2000
25Description: IOTech DAQBoard/2000
26Author: Anders Blomdell <anders.blomdell@control.lth.se>
27Status: works
28Updated: Mon, 14 Apr 2008 15:28:52 +0100
29Devices: [IOTech] DAQBoard/2000 (daqboard2000)
30
31Much of the functionality of this driver was determined from reading
32the source code for the Windows driver.
33
34The FPGA on the board requires initialization code, which can
35be loaded by comedi_config using the -i
36option.  The initialization code is available from http://www.comedi.org
37in the comedi_nonfree_firmware tarball.
38
39Configuration options:
40  [0] - PCI bus of device (optional)
41  [1] - PCI slot of device (optional)
42  If bus/slot is not specified, the first supported
43  PCI device found will be used.
44*/
45/*
46   This card was obviously never intended to leave the Windows world,
47   since it lacked all kind of hardware documentation (except for cable
48   pinouts, plug and pray has something to catch up with yet).
49
50   With some help from our swedish distributor, we got the Windows sourcecode
51   for the card, and here are the findings so far.
52
53   1. A good document that describes the PCI interface chip is found at:
54      http://plx.plxtech.com/download/9080/databook/9080db-106.pdf
55
56   2. The initialization done so far is:
57        a. program the FPGA (windows code sans a lot of error messages)
58	b.
59
60   3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
61      you have to output values to all enabled DAC's until result appears, I
62      guess that it has something to do with pacer clocks, but the source
63      gives me no clues. I'll keep it simple so far.
64
65   4. Analog in.
66        Each channel in the scanlist seems to be controlled by four
67	control words:
68
69        Word0:
70          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
71          ! | | | ! | | | ! | | | ! | | | !
72          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73
74        Word1:
75          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76          ! | | | ! | | | ! | | | ! | | | !
77          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78	   |             |       | | | | |
79           +------+------+       | | | | +-- Digital input (??)
80		  |		 | | | +---- 10 us settling time
81		  |		 | | +------ Suspend acquisition (last to scan)
82		  |		 | +-------- Simultaneous sample and hold
83		  |		 +---------- Signed data format
84		  +------------------------- Correction offset low
85
86        Word2:
87          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88          ! | | | ! | | | ! | | | ! | | | !
89          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90           |     | |     | | | | | |     |
91           +-----+ +--+--+ +++ +++ +--+--+
92              |       |     |   |     +----- Expansion channel
93	      |       |     |   +----------- Expansion gain
94              |       |     +--------------- Channel (low)
95	      |       +--------------------- Correction offset high
96	      +----------------------------- Correction gain low
97        Word3:
98          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
99          ! | | | ! | | | ! | | | ! | | | !
100          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
101           |             | | | |   | | | |
102           +------+------+ | | +-+-+ | | +-- Low bank enable
103                  |        | |   |   | +---- High bank enable
104                  |        | |   |   +------ Hi/low select
105		  |    	   | |   +---------- Gain (1,?,2,4,8,16,32,64)
106		  |    	   | +-------------- differential/single ended
107		  |    	   +---------------- Unipolar
108		  +------------------------- Correction gain high
109
110   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	{
329	0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
330	0}
331};
332
333MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
334
335struct daqboard2000_private {
336	enum {
337		card_daqboard_2000
338	} card;
339	struct pci_dev *pci_dev;
340	void *daq;
341	void *plx;
342	int got_regions;
343	unsigned int ao_readback[2];
344};
345
346#define devpriv ((struct daqboard2000_private *)dev->private)
347
348static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
349{
350	struct daqboard2000_hw *fpga = devpriv->daq;
351
352/* udelay(4); */
353	fpga->acqScanListFIFO = entry & 0x00ff;
354/* udelay(4); */
355	fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
356}
357
358static void setup_sampling(struct comedi_device *dev, int chan, int gain)
359{
360	u16 word0, word1, word2, word3;
361
362	/* Channel 0-7 diff, channel 8-23 single ended */
363	word0 = 0;
364	word1 = 0x0004;		/* Last scan */
365	word2 = (chan << 6) & 0x00c0;
366	switch (chan / 4) {
367	case 0:
368		word3 = 0x0001;
369		break;
370	case 1:
371		word3 = 0x0002;
372		break;
373	case 2:
374		word3 = 0x0005;
375		break;
376	case 3:
377		word3 = 0x0006;
378		break;
379	case 4:
380		word3 = 0x0041;
381		break;
382	case 5:
383		word3 = 0x0042;
384		break;
385	default:
386		word3 = 0;
387		break;
388	}
389/*
390  dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
391  dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
392*/
393	/* These should be read from EEPROM */
394	word2 |= 0x0800;
395	word3 |= 0xc000;
396/*  printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
397	writeAcqScanListEntry(dev, word0);
398	writeAcqScanListEntry(dev, word1);
399	writeAcqScanListEntry(dev, word2);
400	writeAcqScanListEntry(dev, word3);
401}
402
403static int daqboard2000_ai_insn_read(struct comedi_device *dev,
404				     struct comedi_subdevice *s,
405				     struct comedi_insn *insn,
406				     unsigned int *data)
407{
408	int i;
409	struct daqboard2000_hw *fpga = devpriv->daq;
410	int gain, chan, timeout;
411
412	fpga->acqControl =
413	    DAQBOARD2000_AcqResetScanListFifo |
414	    DAQBOARD2000_AcqResetResultsFifo | DAQBOARD2000_AcqResetConfigPipe;
415
416	/* If pacer clock is not set to some high value (> 10 us), we
417	   risk multiple samples to be put into the result FIFO. */
418	fpga->acqPacerClockDivLow = 1000000;	/* 1 second, should be long enough */
419	fpga->acqPacerClockDivHigh = 0;
420
421	gain = CR_RANGE(insn->chanspec);
422	chan = CR_CHAN(insn->chanspec);
423
424	/* This doesn't look efficient.  I decided to take the conservative
425	 * approach when I did the insn conversion.  Perhaps it would be
426	 * better to have broken it completely, then someone would have been
427	 * forced to fix it.  --ds */
428	for (i = 0; i < insn->n; i++) {
429		setup_sampling(dev, chan, gain);
430		/* Enable reading from the scanlist FIFO */
431		fpga->acqControl = DAQBOARD2000_SeqStartScanList;
432		for (timeout = 0; timeout < 20; timeout++) {
433			if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
434				break;
435			}
436			/* udelay(2); */
437		}
438		fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
439		for (timeout = 0; timeout < 20; timeout++) {
440			if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
441				break;
442			}
443			/* udelay(2); */
444		}
445		for (timeout = 0; timeout < 20; timeout++) {
446			if (fpga->acqControl &
447			    DAQBOARD2000_AcqResultsFIFOHasValidData) {
448				break;
449			}
450			/* udelay(2); */
451		}
452		data[i] = fpga->acqResultsFIFO;
453		fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
454		fpga->acqControl = DAQBOARD2000_SeqStopScanList;
455	}
456
457	return i;
458}
459
460static int daqboard2000_ao_insn_read(struct comedi_device *dev,
461				     struct comedi_subdevice *s,
462				     struct comedi_insn *insn,
463				     unsigned int *data)
464{
465	int i;
466	int chan = CR_CHAN(insn->chanspec);
467
468	for (i = 0; i < insn->n; i++) {
469		data[i] = devpriv->ao_readback[chan];
470	}
471
472	return i;
473}
474
475static int daqboard2000_ao_insn_write(struct comedi_device *dev,
476				      struct comedi_subdevice *s,
477				      struct comedi_insn *insn,
478				      unsigned int *data)
479{
480	int i;
481	int chan = CR_CHAN(insn->chanspec);
482	struct daqboard2000_hw *fpga = devpriv->daq;
483	int timeout;
484
485	for (i = 0; i < insn->n; i++) {
486		/*
487		 * OK, since it works OK without enabling the DAC's, let's keep
488		 * it as simple as possible...
489		 */
490		/* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
491		fpga->dacSetting[chan] = data[i];
492		for (timeout = 0; timeout < 20; timeout++) {
493			if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
494				break;
495			}
496			/* udelay(2); */
497		}
498		devpriv->ao_readback[chan] = data[i];
499		/*
500		 * Since we never enabled the DAC's, we don't need to disable it...
501		 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
502		 */
503	}
504
505	return i;
506}
507
508static void daqboard2000_resetLocalBus(struct comedi_device *dev)
509{
510	printk("daqboard2000_resetLocalBus\n");
511	writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
512	udelay(10000);
513	writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
514	udelay(10000);
515}
516
517static void daqboard2000_reloadPLX(struct comedi_device *dev)
518{
519	printk("daqboard2000_reloadPLX\n");
520	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
521	udelay(10000);
522	writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
523	udelay(10000);
524	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
525	udelay(10000);
526}
527
528static void daqboard2000_pulseProgPin(struct comedi_device *dev)
529{
530	printk("daqboard2000_pulseProgPin 1\n");
531	writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
532	udelay(10000);
533	writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
534	udelay(10000);		/* Not in the original code, but I like symmetry... */
535}
536
537static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
538{
539	int result = 0;
540	int i;
541	int cpld;
542
543	/* timeout after 50 tries -> 5ms */
544	for (i = 0; i < 50; i++) {
545		cpld = readw(devpriv->daq + 0x1000);
546		if ((cpld & mask) == mask) {
547			result = 1;
548			break;
549		}
550		udelay(100);
551	}
552	udelay(5);
553	return result;
554}
555
556static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
557{
558	int result = 0;
559
560	udelay(10);
561	writew(data, devpriv->daq + 0x1000);
562	if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
563	    DAQBOARD2000_CPLD_INIT) {
564		result = 1;
565	}
566	return result;
567}
568
569static int initialize_daqboard2000(struct comedi_device *dev,
570				   unsigned char *cpld_array, int len)
571{
572	int result = -EIO;
573	/* Read the serial EEPROM control register */
574	int secr;
575	int retry;
576	int i;
577
578	/* Check to make sure the serial eeprom is present on the board */
579	secr = readl(devpriv->plx + 0x6c);
580	if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
581#ifdef DEBUG_EEPROM
582		printk("no serial eeprom\n");
583#endif
584		return -EIO;
585	}
586
587	for (retry = 0; retry < 3; retry++) {
588#ifdef DEBUG_EEPROM
589		printk("Programming EEPROM try %x\n", retry);
590#endif
591
592		daqboard2000_resetLocalBus(dev);
593		daqboard2000_reloadPLX(dev);
594		daqboard2000_pulseProgPin(dev);
595		if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
596			for (i = 0; i < len; i++) {
597				if (cpld_array[i] == 0xff
598				    && cpld_array[i + 1] == 0x20) {
599#ifdef DEBUG_EEPROM
600					printk("Preamble found at %d\n", i);
601#endif
602					break;
603				}
604			}
605			for (; i < len; i += 2) {
606				int data =
607				    (cpld_array[i] << 8) + cpld_array[i + 1];
608				if (!daqboard2000_writeCPLD(dev, data)) {
609					break;
610				}
611			}
612			if (i >= len) {
613#ifdef DEBUG_EEPROM
614				printk("Programmed\n");
615#endif
616				daqboard2000_resetLocalBus(dev);
617				daqboard2000_reloadPLX(dev);
618				result = 0;
619				break;
620			}
621		}
622	}
623	return result;
624}
625
626static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
627{
628/*  printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
629}
630
631static void daqboard2000_adcDisarm(struct comedi_device *dev)
632{
633	struct daqboard2000_hw *fpga = devpriv->daq;
634
635	/* Disable hardware triggers */
636	udelay(2);
637	fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
638	udelay(2);
639	fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
640
641	/* Stop the scan list FIFO from loading the configuration pipe */
642	udelay(2);
643	fpga->acqControl = DAQBOARD2000_SeqStopScanList;
644
645	/* Stop the pacer clock */
646	udelay(2);
647	fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
648
649	/* Stop the input dma (abort channel 1) */
650	daqboard2000_adcStopDmaTransfer(dev);
651}
652
653static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
654{
655	struct daqboard2000_hw *fpga = devpriv->daq;
656	int timeout;
657
658	/*  Set the + reference dac value in the FPGA */
659	fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
660	for (timeout = 0; timeout < 20; timeout++) {
661		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
662			break;
663		}
664		udelay(2);
665	}
666/*  printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
667
668	/*  Set the - reference dac value in the FPGA */
669	fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
670	for (timeout = 0; timeout < 20; timeout++) {
671		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
672			break;
673		}
674		udelay(2);
675	}
676/*  printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
677}
678
679static void daqboard2000_initializeCtrs(struct comedi_device *dev)
680{
681/*  printk("Implement: daqboard2000_initializeCtrs\n");*/
682}
683
684static void daqboard2000_initializeTmrs(struct comedi_device *dev)
685{
686/*  printk("Implement: daqboard2000_initializeTmrs\n");*/
687}
688
689static void daqboard2000_dacDisarm(struct comedi_device *dev)
690{
691/*  printk("Implement: daqboard2000_dacDisarm\n");*/
692}
693
694static void daqboard2000_initializeAdc(struct comedi_device *dev)
695{
696	daqboard2000_adcDisarm(dev);
697	daqboard2000_activateReferenceDacs(dev);
698	daqboard2000_initializeCtrs(dev);
699	daqboard2000_initializeTmrs(dev);
700}
701
702static void daqboard2000_initializeDac(struct comedi_device *dev)
703{
704	daqboard2000_dacDisarm(dev);
705}
706
707/*
708The test command, REMOVE!!:
709
710rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
711*/
712
713static int daqboard2000_8255_cb(int dir, int port, int data,
714				unsigned long ioaddr)
715{
716	int result = 0;
717	if (dir) {
718		writew(data, ((void *)ioaddr) + port * 2);
719		result = 0;
720	} else {
721		result = readw(((void *)ioaddr) + port * 2);
722	}
723/*
724  printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
725        arg, dir, port, data, result);
726*/
727	return result;
728}
729
730static int daqboard2000_attach(struct comedi_device *dev,
731			       struct comedi_devconfig *it)
732{
733	int result = 0;
734	struct comedi_subdevice *s;
735	struct pci_dev *card = NULL;
736	void *aux_data;
737	unsigned int aux_len;
738	int bus, slot;
739
740	printk("comedi%d: daqboard2000:", dev->minor);
741
742	bus = it->options[0];
743	slot = it->options[1];
744
745	result = alloc_private(dev, sizeof(struct daqboard2000_private));
746	if (result < 0) {
747		return -ENOMEM;
748	}
749	for (card = pci_get_device(0x1616, 0x0409, NULL);
750	     card != NULL; card = pci_get_device(0x1616, 0x0409, card)) {
751		if (bus || slot) {
752			/* requested particular bus/slot */
753			if (card->bus->number != bus ||
754			    PCI_SLOT(card->devfn) != slot) {
755				continue;
756			}
757		}
758		break;		/* found one */
759	}
760	if (!card) {
761		if (bus || slot)
762			printk(" no daqboard2000 found at bus/slot: %d/%d\n",
763			       bus, slot);
764		else
765			printk(" no daqboard2000 found\n");
766		return -EIO;
767	} else {
768		u32 id;
769		int i;
770		devpriv->pci_dev = card;
771		id = ((u32) card->
772		      subsystem_device << 16) | card->subsystem_vendor;
773		for (i = 0; i < n_boardtypes; i++) {
774			if (boardtypes[i].id == id) {
775				printk(" %s", boardtypes[i].name);
776				dev->board_ptr = boardtypes + i;
777			}
778		}
779		if (!dev->board_ptr) {
780			printk
781			    (" unknown subsystem id %08x (pretend it is an ids2)",
782			     id);
783			dev->board_ptr = boardtypes;
784		}
785	}
786
787	result = comedi_pci_enable(card, "daqboard2000");
788	if (result < 0) {
789		printk(" failed to enable PCI device and request regions\n");
790		return -EIO;
791	}
792	devpriv->got_regions = 1;
793	devpriv->plx =
794	    ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
795	devpriv->daq =
796	    ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
797	if (!devpriv->plx || !devpriv->daq) {
798		return -ENOMEM;
799	}
800
801	result = alloc_subdevices(dev, 3);
802	if (result < 0)
803		goto out;
804
805	readl(devpriv->plx + 0x6c);
806
807	/*
808	   u8 interrupt;
809	   Windows code does restore interrupts, but since we don't use them...
810	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
811	   printk("Interrupt before is: %x\n", interrupt);
812	 */
813
814	aux_data = comedi_aux_data(it->options, 0);
815	aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
816
817	if (aux_data && aux_len) {
818		result = initialize_daqboard2000(dev, aux_data, aux_len);
819	} else {
820		printk("no FPGA initialization code, aborting\n");
821		result = -EIO;
822	}
823	if (result < 0)
824		goto out;
825	daqboard2000_initializeAdc(dev);
826	daqboard2000_initializeDac(dev);
827	/*
828	   Windows code does restore interrupts, but since we don't use them...
829	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
830	   printk("Interrupt after is: %x\n", interrupt);
831	 */
832
833	dev->iobase = (unsigned long)devpriv->daq;
834
835	dev->board_name = this_board->name;
836
837	s = dev->subdevices + 0;
838	/* ai subdevice */
839	s->type = COMEDI_SUBD_AI;
840	s->subdev_flags = SDF_READABLE | SDF_GROUND;
841	s->n_chan = 24;
842	s->maxdata = 0xffff;
843	s->insn_read = daqboard2000_ai_insn_read;
844	s->range_table = &range_daqboard2000_ai;
845
846	s = dev->subdevices + 1;
847	/* ao subdevice */
848	s->type = COMEDI_SUBD_AO;
849	s->subdev_flags = SDF_WRITABLE;
850	s->n_chan = 2;
851	s->maxdata = 0xffff;
852	s->insn_read = daqboard2000_ao_insn_read;
853	s->insn_write = daqboard2000_ao_insn_write;
854	s->range_table = &range_daqboard2000_ao;
855
856	s = dev->subdevices + 2;
857	result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
858				  (unsigned long)(dev->iobase + 0x40));
859
860	printk("\n");
861out:
862	return result;
863}
864
865static int daqboard2000_detach(struct comedi_device *dev)
866{
867	printk("comedi%d: daqboard2000: remove\n", dev->minor);
868
869	if (dev->subdevices)
870		subdev_8255_cleanup(dev, dev->subdevices + 2);
871
872	if (dev->irq) {
873		free_irq(dev->irq, dev);
874	}
875	if (devpriv) {
876		if (devpriv->daq)
877			iounmap(devpriv->daq);
878		if (devpriv->plx)
879			iounmap(devpriv->plx);
880		if (devpriv->pci_dev) {
881			if (devpriv->got_regions) {
882				comedi_pci_disable(devpriv->pci_dev);
883			}
884			pci_dev_put(devpriv->pci_dev);
885		}
886	}
887	return 0;
888}
889
890COMEDI_PCI_INITCLEANUP(driver_daqboard2000, daqboard2000_pci_table);
891