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