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 /*
24 Driver: daqboard2000
25 Description: IOTech DAQBoard/2000
26 Author: Anders Blomdell <anders.blomdell@control.lth.se>
27 Status: works
28 Updated: Mon, 14 Apr 2008 15:28:52 +0100
29 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
30 
31 Much of the functionality of this driver was determined from reading
32 the source code for the Windows driver.
33 
34 The FPGA on the board requires initialization code, which can
35 be loaded by comedi_config using the -i
36 option.  The initialization code is available from http://www.comedi.org
37 in the comedi_nonfree_firmware tarball.
38 
39 Configuration 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 */
146 static 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 
170 static const struct comedi_lrange range_daqboard2000_ao = { 1, {
171 								RANGE(-10, 10)
172 								}
173 };
174 
175 struct 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 
304 static int daqboard2000_attach(struct comedi_device *dev,
305 			       struct comedi_devconfig *it);
306 static int daqboard2000_detach(struct comedi_device *dev);
307 
308 static struct comedi_driver driver_daqboard2000 = {
309 	.driver_name = "daqboard2000",
310 	.module = THIS_MODULE,
311 	.attach = daqboard2000_attach,
312 	.detach = daqboard2000_detach,
313 };
314 
315 struct daq200_boardtype {
316 	const char *name;
317 	int id;
318 };
319 static 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 
327 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
328 	{ PCI_DEVICE(0x1616, 0x0409) },
329 	{0}
330 };
331 
332 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
333 
334 struct 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 
writeAcqScanListEntry(struct comedi_device * dev,u16 entry)347 static 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 
setup_sampling(struct comedi_device * dev,int chan,int gain)357 static 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 
daqboard2000_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)402 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
403 				     struct comedi_subdevice *s,
404 				     struct comedi_insn *insn,
405 				     unsigned int *data)
406 {
407 	int i;
408 	struct daqboard2000_hw *fpga = devpriv->daq;
409 	int gain, chan, timeout;
410 
411 	fpga->acqControl =
412 	    DAQBOARD2000_AcqResetScanListFifo |
413 	    DAQBOARD2000_AcqResetResultsFifo | DAQBOARD2000_AcqResetConfigPipe;
414 
415 	/* If pacer clock is not set to some high value (> 10 us), we
416 	   risk multiple samples to be put into the result FIFO. */
417 	fpga->acqPacerClockDivLow = 1000000;	/* 1 second, should be long enough */
418 	fpga->acqPacerClockDivHigh = 0;
419 
420 	gain = CR_RANGE(insn->chanspec);
421 	chan = CR_CHAN(insn->chanspec);
422 
423 	/* This doesn't look efficient.  I decided to take the conservative
424 	 * approach when I did the insn conversion.  Perhaps it would be
425 	 * better to have broken it completely, then someone would have been
426 	 * forced to fix it.  --ds */
427 	for (i = 0; i < insn->n; i++) {
428 		setup_sampling(dev, chan, gain);
429 		/* Enable reading from the scanlist FIFO */
430 		fpga->acqControl = DAQBOARD2000_SeqStartScanList;
431 		for (timeout = 0; timeout < 20; timeout++) {
432 			if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull)
433 				break;
434 			/* udelay(2); */
435 		}
436 		fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
437 		for (timeout = 0; timeout < 20; timeout++) {
438 			if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning)
439 				break;
440 			/* udelay(2); */
441 		}
442 		for (timeout = 0; timeout < 20; timeout++) {
443 			if (fpga->acqControl &
444 			    DAQBOARD2000_AcqResultsFIFOHasValidData) {
445 				break;
446 			}
447 			/* udelay(2); */
448 		}
449 		data[i] = fpga->acqResultsFIFO;
450 		fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
451 		fpga->acqControl = DAQBOARD2000_SeqStopScanList;
452 	}
453 
454 	return i;
455 }
456 
daqboard2000_ao_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)457 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
458 				     struct comedi_subdevice *s,
459 				     struct comedi_insn *insn,
460 				     unsigned int *data)
461 {
462 	int i;
463 	int chan = CR_CHAN(insn->chanspec);
464 
465 	for (i = 0; i < insn->n; i++)
466 		data[i] = devpriv->ao_readback[chan];
467 
468 	return i;
469 }
470 
daqboard2000_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)471 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
472 				      struct comedi_subdevice *s,
473 				      struct comedi_insn *insn,
474 				      unsigned int *data)
475 {
476 	int i;
477 	int chan = CR_CHAN(insn->chanspec);
478 	struct daqboard2000_hw *fpga = devpriv->daq;
479 	int timeout;
480 
481 	for (i = 0; i < insn->n; i++) {
482 		/*
483 		 * OK, since it works OK without enabling the DAC's, let's keep
484 		 * it as simple as possible...
485 		 */
486 		/* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
487 		fpga->dacSetting[chan] = data[i];
488 		for (timeout = 0; timeout < 20; timeout++) {
489 			if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0)
490 				break;
491 			/* udelay(2); */
492 		}
493 		devpriv->ao_readback[chan] = data[i];
494 		/*
495 		 * Since we never enabled the DAC's, we don't need to disable it...
496 		 * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
497 		 */
498 	}
499 
500 	return i;
501 }
502 
daqboard2000_resetLocalBus(struct comedi_device * dev)503 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
504 {
505 	dev_dbg(dev->hw_dev, "daqboard2000_resetLocalBus\n");
506 	writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
507 	udelay(10000);
508 	writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
509 	udelay(10000);
510 }
511 
daqboard2000_reloadPLX(struct comedi_device * dev)512 static void daqboard2000_reloadPLX(struct comedi_device *dev)
513 {
514 	dev_dbg(dev->hw_dev, "daqboard2000_reloadPLX\n");
515 	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
516 	udelay(10000);
517 	writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
518 	udelay(10000);
519 	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
520 	udelay(10000);
521 }
522 
daqboard2000_pulseProgPin(struct comedi_device * dev)523 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
524 {
525 	dev_dbg(dev->hw_dev, "daqboard2000_pulseProgPin 1\n");
526 	writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
527 	udelay(10000);
528 	writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
529 	udelay(10000);		/* Not in the original code, but I like symmetry... */
530 }
531 
daqboard2000_pollCPLD(struct comedi_device * dev,int mask)532 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
533 {
534 	int result = 0;
535 	int i;
536 	int cpld;
537 
538 	/* timeout after 50 tries -> 5ms */
539 	for (i = 0; i < 50; i++) {
540 		cpld = readw(devpriv->daq + 0x1000);
541 		if ((cpld & mask) == mask) {
542 			result = 1;
543 			break;
544 		}
545 		udelay(100);
546 	}
547 	udelay(5);
548 	return result;
549 }
550 
daqboard2000_writeCPLD(struct comedi_device * dev,int data)551 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
552 {
553 	int result = 0;
554 
555 	udelay(10);
556 	writew(data, devpriv->daq + 0x1000);
557 	if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
558 	    DAQBOARD2000_CPLD_INIT) {
559 		result = 1;
560 	}
561 	return result;
562 }
563 
initialize_daqboard2000(struct comedi_device * dev,unsigned char * cpld_array,int len)564 static int initialize_daqboard2000(struct comedi_device *dev,
565 				   unsigned char *cpld_array, int len)
566 {
567 	int result = -EIO;
568 	/* Read the serial EEPROM control register */
569 	int secr;
570 	int retry;
571 	int i;
572 
573 	/* Check to make sure the serial eeprom is present on the board */
574 	secr = readl(devpriv->plx + 0x6c);
575 	if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
576 #ifdef DEBUG_EEPROM
577 		dev_dbg(dev->hw_dev, "no serial eeprom\n");
578 #endif
579 		return -EIO;
580 	}
581 
582 	for (retry = 0; retry < 3; retry++) {
583 #ifdef DEBUG_EEPROM
584 		dev_dbg(dev->hw_dev, "Programming EEPROM try %x\n", retry);
585 #endif
586 
587 		daqboard2000_resetLocalBus(dev);
588 		daqboard2000_reloadPLX(dev);
589 		daqboard2000_pulseProgPin(dev);
590 		if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
591 			for (i = 0; i < len; i++) {
592 				if (cpld_array[i] == 0xff
593 				    && cpld_array[i + 1] == 0x20) {
594 #ifdef DEBUG_EEPROM
595 					dev_dbg(dev->hw_dev, "Preamble found at %d\n",
596 						i);
597 #endif
598 					break;
599 				}
600 			}
601 			for (; i < len; i += 2) {
602 				int data =
603 				    (cpld_array[i] << 8) + cpld_array[i + 1];
604 				if (!daqboard2000_writeCPLD(dev, data))
605 					break;
606 			}
607 			if (i >= len) {
608 #ifdef DEBUG_EEPROM
609 				dev_dbg(dev->hw_dev, "Programmed\n");
610 #endif
611 				daqboard2000_resetLocalBus(dev);
612 				daqboard2000_reloadPLX(dev);
613 				result = 0;
614 				break;
615 			}
616 		}
617 	}
618 	return result;
619 }
620 
daqboard2000_adcStopDmaTransfer(struct comedi_device * dev)621 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
622 {
623 /*  printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
624 }
625 
daqboard2000_adcDisarm(struct comedi_device * dev)626 static void daqboard2000_adcDisarm(struct comedi_device *dev)
627 {
628 	struct daqboard2000_hw *fpga = devpriv->daq;
629 
630 	/* Disable hardware triggers */
631 	udelay(2);
632 	fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
633 	udelay(2);
634 	fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
635 
636 	/* Stop the scan list FIFO from loading the configuration pipe */
637 	udelay(2);
638 	fpga->acqControl = DAQBOARD2000_SeqStopScanList;
639 
640 	/* Stop the pacer clock */
641 	udelay(2);
642 	fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
643 
644 	/* Stop the input dma (abort channel 1) */
645 	daqboard2000_adcStopDmaTransfer(dev);
646 }
647 
daqboard2000_activateReferenceDacs(struct comedi_device * dev)648 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
649 {
650 	struct daqboard2000_hw *fpga = devpriv->daq;
651 	int timeout;
652 
653 	/*  Set the + reference dac value in the FPGA */
654 	fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
655 	for (timeout = 0; timeout < 20; timeout++) {
656 		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
657 			break;
658 		udelay(2);
659 	}
660 /*  printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
661 
662 	/*  Set the - reference dac value in the FPGA */
663 	fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
664 	for (timeout = 0; timeout < 20; timeout++) {
665 		if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
666 			break;
667 		udelay(2);
668 	}
669 /*  printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
670 }
671 
daqboard2000_initializeCtrs(struct comedi_device * dev)672 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
673 {
674 /*  printk("Implement: daqboard2000_initializeCtrs\n");*/
675 }
676 
daqboard2000_initializeTmrs(struct comedi_device * dev)677 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
678 {
679 /*  printk("Implement: daqboard2000_initializeTmrs\n");*/
680 }
681 
daqboard2000_dacDisarm(struct comedi_device * dev)682 static void daqboard2000_dacDisarm(struct comedi_device *dev)
683 {
684 /*  printk("Implement: daqboard2000_dacDisarm\n");*/
685 }
686 
daqboard2000_initializeAdc(struct comedi_device * dev)687 static void daqboard2000_initializeAdc(struct comedi_device *dev)
688 {
689 	daqboard2000_adcDisarm(dev);
690 	daqboard2000_activateReferenceDacs(dev);
691 	daqboard2000_initializeCtrs(dev);
692 	daqboard2000_initializeTmrs(dev);
693 }
694 
daqboard2000_initializeDac(struct comedi_device * dev)695 static void daqboard2000_initializeDac(struct comedi_device *dev)
696 {
697 	daqboard2000_dacDisarm(dev);
698 }
699 
700 /*
701 The test command, REMOVE!!:
702 
703 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
704 */
705 
daqboard2000_8255_cb(int dir,int port,int data,unsigned long ioaddr)706 static int daqboard2000_8255_cb(int dir, int port, int data,
707 				unsigned long ioaddr)
708 {
709 	int result = 0;
710 	if (dir) {
711 		writew(data, ((void *)ioaddr) + port * 2);
712 		result = 0;
713 	} else {
714 		result = readw(((void *)ioaddr) + port * 2);
715 	}
716 /*
717   printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
718         arg, dir, port, data, result);
719 */
720 	return result;
721 }
722 
daqboard2000_attach(struct comedi_device * dev,struct comedi_devconfig * it)723 static int daqboard2000_attach(struct comedi_device *dev,
724 			       struct comedi_devconfig *it)
725 {
726 	int result = 0;
727 	struct comedi_subdevice *s;
728 	struct pci_dev *card = NULL;
729 	void *aux_data;
730 	unsigned int aux_len;
731 	int bus, slot;
732 
733 	bus = it->options[0];
734 	slot = it->options[1];
735 
736 	result = alloc_private(dev, sizeof(struct daqboard2000_private));
737 	if (result < 0)
738 		return -ENOMEM;
739 
740 	for (card = pci_get_device(0x1616, 0x0409, NULL);
741 	     card != NULL; 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 			dev_err(dev->hw_dev, "no daqboard2000 found at bus/slot: %d/%d\n",
754 				bus, slot);
755 		else
756 			dev_err(dev->hw_dev, "no daqboard2000 found\n");
757 		return -EIO;
758 	} else {
759 		u32 id;
760 		int i;
761 		devpriv->pci_dev = card;
762 		id = ((u32) card->
763 		      subsystem_device << 16) | card->subsystem_vendor;
764 		for (i = 0; i < n_boardtypes; i++) {
765 			if (boardtypes[i].id == id) {
766 				dev_dbg(dev->hw_dev, "%s\n",
767 					boardtypes[i].name);
768 				dev->board_ptr = boardtypes + i;
769 			}
770 		}
771 		if (!dev->board_ptr) {
772 			printk
773 			    (" unknown subsystem id %08x (pretend it is an ids2)",
774 			     id);
775 			dev->board_ptr = boardtypes;
776 		}
777 	}
778 
779 	result = comedi_pci_enable(card, "daqboard2000");
780 	if (result < 0) {
781 		dev_err(dev->hw_dev, "failed to enable PCI device and request regions\n");
782 		return -EIO;
783 	}
784 	devpriv->got_regions = 1;
785 	devpriv->plx =
786 	    ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
787 	devpriv->daq =
788 	    ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
789 	if (!devpriv->plx || !devpriv->daq)
790 		return -ENOMEM;
791 
792 	result = alloc_subdevices(dev, 3);
793 	if (result < 0)
794 		goto out;
795 
796 	readl(devpriv->plx + 0x6c);
797 
798 	/*
799 	   u8 interrupt;
800 	   Windows code does restore interrupts, but since we don't use them...
801 	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
802 	   printk("Interrupt before is: %x\n", interrupt);
803 	 */
804 
805 	aux_data = comedi_aux_data(it->options, 0);
806 	aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
807 
808 	if (aux_data && aux_len) {
809 		result = initialize_daqboard2000(dev, aux_data, aux_len);
810 	} else {
811 		dev_dbg(dev->hw_dev, "no FPGA initialization code, aborting\n");
812 		result = -EIO;
813 	}
814 	if (result < 0)
815 		goto out;
816 	daqboard2000_initializeAdc(dev);
817 	daqboard2000_initializeDac(dev);
818 	/*
819 	   Windows code does restore interrupts, but since we don't use them...
820 	   pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
821 	   printk("Interrupt after is: %x\n", interrupt);
822 	 */
823 
824 	dev->iobase = (unsigned long)devpriv->daq;
825 
826 	dev->board_name = this_board->name;
827 
828 	s = dev->subdevices + 0;
829 	/* ai subdevice */
830 	s->type = COMEDI_SUBD_AI;
831 	s->subdev_flags = SDF_READABLE | SDF_GROUND;
832 	s->n_chan = 24;
833 	s->maxdata = 0xffff;
834 	s->insn_read = daqboard2000_ai_insn_read;
835 	s->range_table = &range_daqboard2000_ai;
836 
837 	s = dev->subdevices + 1;
838 	/* ao subdevice */
839 	s->type = COMEDI_SUBD_AO;
840 	s->subdev_flags = SDF_WRITABLE;
841 	s->n_chan = 2;
842 	s->maxdata = 0xffff;
843 	s->insn_read = daqboard2000_ao_insn_read;
844 	s->insn_write = daqboard2000_ao_insn_write;
845 	s->range_table = &range_daqboard2000_ao;
846 
847 	s = dev->subdevices + 2;
848 	result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
849 				  (unsigned long)(dev->iobase + 0x40));
850 
851 out:
852 	return result;
853 }
854 
daqboard2000_detach(struct comedi_device * dev)855 static int daqboard2000_detach(struct comedi_device *dev)
856 {
857 	if (dev->subdevices)
858 		subdev_8255_cleanup(dev, dev->subdevices + 2);
859 
860 	if (dev->irq)
861 		free_irq(dev->irq, dev);
862 
863 	if (devpriv) {
864 		if (devpriv->daq)
865 			iounmap(devpriv->daq);
866 		if (devpriv->plx)
867 			iounmap(devpriv->plx);
868 		if (devpriv->pci_dev) {
869 			if (devpriv->got_regions)
870 				comedi_pci_disable(devpriv->pci_dev);
871 			pci_dev_put(devpriv->pci_dev);
872 		}
873 	}
874 	return 0;
875 }
876 
driver_daqboard2000_pci_probe(struct pci_dev * dev,const struct pci_device_id * ent)877 static int __devinit driver_daqboard2000_pci_probe(struct pci_dev *dev,
878 						   const struct pci_device_id
879 						   *ent)
880 {
881 	return comedi_pci_auto_config(dev, driver_daqboard2000.driver_name);
882 }
883 
driver_daqboard2000_pci_remove(struct pci_dev * dev)884 static void __devexit driver_daqboard2000_pci_remove(struct pci_dev *dev)
885 {
886 	comedi_pci_auto_unconfig(dev);
887 }
888 
889 static struct pci_driver driver_daqboard2000_pci_driver = {
890 	.id_table = daqboard2000_pci_table,
891 	.probe = &driver_daqboard2000_pci_probe,
892 	.remove = __devexit_p(&driver_daqboard2000_pci_remove)
893 };
894 
driver_daqboard2000_init_module(void)895 static int __init driver_daqboard2000_init_module(void)
896 {
897 	int retval;
898 
899 	retval = comedi_driver_register(&driver_daqboard2000);
900 	if (retval < 0)
901 		return retval;
902 
903 	driver_daqboard2000_pci_driver.name =
904 	    (char *)driver_daqboard2000.driver_name;
905 	return pci_register_driver(&driver_daqboard2000_pci_driver);
906 }
907 
driver_daqboard2000_cleanup_module(void)908 static void __exit driver_daqboard2000_cleanup_module(void)
909 {
910 	pci_unregister_driver(&driver_daqboard2000_pci_driver);
911 	comedi_driver_unregister(&driver_daqboard2000);
912 }
913 
914 module_init(driver_daqboard2000_init_module);
915 module_exit(driver_daqboard2000_cleanup_module);
916 
917 MODULE_AUTHOR("Comedi http://www.comedi.org");
918 MODULE_DESCRIPTION("Comedi low-level driver");
919 MODULE_LICENSE("GPL");
920