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