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 /*
19 Driver: daqboard2000
20 Description: IOTech DAQBoard/2000
21 Author: Anders Blomdell <anders.blomdell@control.lth.se>
22 Status: works
23 Updated: Mon, 14 Apr 2008 15:28:52 +0100
24 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
25 
26 Much of the functionality of this driver was determined from reading
27 the source code for the Windows driver.
28 
29 The FPGA on the board requires fimware, which is available from
30 http://www.comedi.org in the comedi_nonfree_firmware tarball.
31 
32 Configuration options: not applicable, uses PCI auto config
33 */
34 /*
35    This card was obviously never intended to leave the Windows world,
36    since it lacked all kind of hardware documentation (except for cable
37    pinouts, plug and pray has something to catch up with yet).
38 
39    With some help from our swedish distributor, we got the Windows sourcecode
40    for the card, and here are the findings so far.
41 
42    1. A good document that describes the PCI interface chip is 9080db-106.pdf
43       available from http://www.plxtech.com/products/io/pci9080
44 
45    2. The initialization done so far is:
46         a. program the FPGA (windows code sans a lot of error messages)
47 	b.
48 
49    3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
50       you have to output values to all enabled DAC's until result appears, I
51       guess that it has something to do with pacer clocks, but the source
52       gives me no clues. I'll keep it simple so far.
53 
54    4. Analog in.
55         Each channel in the scanlist seems to be controlled by four
56 	control words:
57 
58         Word0:
59           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60           ! | | | ! | | | ! | | | ! | | | !
61           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 
63         Word1:
64           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65           ! | | | ! | | | ! | | | ! | | | !
66           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67 	   |             |       | | | | |
68            +------+------+       | | | | +-- Digital input (??)
69 		  |		 | | | +---- 10 us settling time
70 		  |		 | | +------ Suspend acquisition (last to scan)
71 		  |		 | +-------- Simultaneous sample and hold
72 		  |		 +---------- Signed data format
73 		  +------------------------- Correction offset low
74 
75         Word2:
76           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77           ! | | | ! | | | ! | | | ! | | | !
78           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79            |     | |     | | | | | |     |
80            +-----+ +--+--+ +++ +++ +--+--+
81               |       |     |   |     +----- Expansion channel
82 	      |       |     |   +----------- Expansion gain
83               |       |     +--------------- Channel (low)
84 	      |       +--------------------- Correction offset high
85 	      +----------------------------- Correction gain low
86         Word3:
87           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88           ! | | | ! | | | ! | | | ! | | | !
89           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90            |             | | | |   | | | |
91            +------+------+ | | +-+-+ | | +-- Low bank enable
92                   |        | |   |   | +---- High bank enable
93                   |        | |   |   +------ Hi/low select
94 		  |    	   | |   +---------- Gain (1,?,2,4,8,16,32,64)
95 		  |    	   | +-------------- differential/single ended
96 		  |    	   +---------------- Unipolar
97 		  +------------------------- Correction gain high
98 
99    999. The card seems to have an incredible amount of capabilities, but
100         trying to reverse engineer them from the Windows source is beyond my
101 	patience.
102 
103  */
104 
105 #include <linux/module.h>
106 #include <linux/delay.h>
107 #include <linux/interrupt.h>
108 
109 #include "../comedi_pci.h"
110 
111 #include "8255.h"
112 
113 #define DAQBOARD2000_FIRMWARE		"daqboard2000_firmware.bin"
114 
115 #define DAQBOARD2000_SUBSYSTEM_IDS2	0x0002	/* Daqboard/2000 - 2 Dacs */
116 #define DAQBOARD2000_SUBSYSTEM_IDS4	0x0004	/* Daqboard/2000 - 4 Dacs */
117 
118 /* Initialization bits for the Serial EEPROM Control Register */
119 #define DAQBOARD2000_SECRProgPinHi      0x8001767e
120 #define DAQBOARD2000_SECRProgPinLo      0x8000767e
121 #define DAQBOARD2000_SECRLocalBusHi     0xc000767e
122 #define DAQBOARD2000_SECRLocalBusLo     0x8000767e
123 #define DAQBOARD2000_SECRReloadHi       0xa000767e
124 #define DAQBOARD2000_SECRReloadLo       0x8000767e
125 
126 /* SECR status bits */
127 #define DAQBOARD2000_EEPROM_PRESENT     0x10000000
128 
129 /* CPLD status bits */
130 #define DAQBOARD2000_CPLD_INIT		0x0002
131 #define DAQBOARD2000_CPLD_DONE		0x0004
132 
133 static const struct comedi_lrange range_daqboard2000_ai = {
134 	13, {
135 		BIP_RANGE(10),
136 		BIP_RANGE(5),
137 		BIP_RANGE(2.5),
138 		BIP_RANGE(1.25),
139 		BIP_RANGE(0.625),
140 		BIP_RANGE(0.3125),
141 		BIP_RANGE(0.156),
142 		UNI_RANGE(10),
143 		UNI_RANGE(5),
144 		UNI_RANGE(2.5),
145 		UNI_RANGE(1.25),
146 		UNI_RANGE(0.625),
147 		UNI_RANGE(0.3125)
148 	}
149 };
150 
151 /*
152  * Register Memory Map
153  */
154 #define acqControl			0x00		/* u16 */
155 #define acqScanListFIFO			0x02		/* u16 */
156 #define acqPacerClockDivLow		0x04		/* u32 */
157 #define acqScanCounter			0x08		/* u16 */
158 #define acqPacerClockDivHigh		0x0a		/* u16 */
159 #define acqTriggerCount			0x0c		/* u16 */
160 #define acqResultsFIFO			0x10		/* u16 */
161 #define acqResultsShadow		0x14		/* u16 */
162 #define acqAdcResult			0x18		/* u16 */
163 #define dacScanCounter			0x1c		/* u16 */
164 #define dacControl			0x20		/* u16 */
165 #define dacFIFO				0x24		/* s16 */
166 #define dacPacerClockDiv		0x2a		/* u16 */
167 #define refDacs				0x2c		/* u16 */
168 #define dioControl			0x30		/* u16 */
169 #define dioP3hsioData			0x32		/* s16 */
170 #define dioP3Control			0x34		/* u16 */
171 #define calEepromControl		0x36		/* u16 */
172 #define dacSetting(x)			(0x38 + (x)*2)	/* s16 */
173 #define dioP2ExpansionIO8Bit		0x40		/* s16 */
174 #define ctrTmrControl			0x80		/* u16 */
175 #define ctrInput(x)			(0x88 + (x)*2)	/* s16 */
176 #define timerDivisor(x)			(0xa0 + (x)*2)	/* u16 */
177 #define dmaControl			0xb0		/* u16 */
178 #define trigControl			0xb2		/* u16 */
179 #define calEeprom			0xb8		/* u16 */
180 #define acqDigitalMark			0xba		/* u16 */
181 #define trigDacs			0xbc		/* u16 */
182 #define dioP2ExpansionIO16Bit(x)	(0xc0 + (x)*2)	/* s16 */
183 
184 /* Scan Sequencer programming */
185 #define DAQBOARD2000_SeqStartScanList            0x0011
186 #define DAQBOARD2000_SeqStopScanList             0x0010
187 
188 /* Prepare for acquisition */
189 #define DAQBOARD2000_AcqResetScanListFifo        0x0004
190 #define DAQBOARD2000_AcqResetResultsFifo         0x0002
191 #define DAQBOARD2000_AcqResetConfigPipe          0x0001
192 
193 /* Acqusition status bits */
194 #define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
195 #define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
196 #define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
197 #define DAQBOARD2000_AcqLogicScanning            0x0008
198 #define DAQBOARD2000_AcqConfigPipeFull           0x0010
199 #define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
200 #define DAQBOARD2000_AcqAdcNotReady              0x0040
201 #define DAQBOARD2000_ArbitrationFailure          0x0080
202 #define DAQBOARD2000_AcqPacerOverrun             0x0100
203 #define DAQBOARD2000_DacPacerOverrun             0x0200
204 #define DAQBOARD2000_AcqHardwareError            0x01c0
205 
206 /* Scan Sequencer programming */
207 #define DAQBOARD2000_SeqStartScanList            0x0011
208 #define DAQBOARD2000_SeqStopScanList             0x0010
209 
210 /* Pacer Clock Control */
211 #define DAQBOARD2000_AdcPacerInternal            0x0030
212 #define DAQBOARD2000_AdcPacerExternal            0x0032
213 #define DAQBOARD2000_AdcPacerEnable              0x0031
214 #define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
215 #define DAQBOARD2000_AdcPacerDisable             0x0030
216 #define DAQBOARD2000_AdcPacerNormalMode          0x0060
217 #define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
218 #define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
219 #define DAQBOARD2000_AdcPacerExternalRising      0x0100
220 
221 /* DAC status */
222 #define DAQBOARD2000_DacFull                     0x0001
223 #define DAQBOARD2000_RefBusy                     0x0002
224 #define DAQBOARD2000_TrgBusy                     0x0004
225 #define DAQBOARD2000_CalBusy                     0x0008
226 #define DAQBOARD2000_Dac0Busy                    0x0010
227 #define DAQBOARD2000_Dac1Busy                    0x0020
228 #define DAQBOARD2000_Dac2Busy                    0x0040
229 #define DAQBOARD2000_Dac3Busy                    0x0080
230 
231 /* DAC control */
232 #define DAQBOARD2000_Dac0Enable                  0x0021
233 #define DAQBOARD2000_Dac1Enable                  0x0031
234 #define DAQBOARD2000_Dac2Enable                  0x0041
235 #define DAQBOARD2000_Dac3Enable                  0x0051
236 #define DAQBOARD2000_DacEnableBit                0x0001
237 #define DAQBOARD2000_Dac0Disable                 0x0020
238 #define DAQBOARD2000_Dac1Disable                 0x0030
239 #define DAQBOARD2000_Dac2Disable                 0x0040
240 #define DAQBOARD2000_Dac3Disable                 0x0050
241 #define DAQBOARD2000_DacResetFifo                0x0004
242 #define DAQBOARD2000_DacPatternDisable           0x0060
243 #define DAQBOARD2000_DacPatternEnable            0x0061
244 #define DAQBOARD2000_DacSelectSignedData         0x0002
245 #define DAQBOARD2000_DacSelectUnsignedData       0x0000
246 
247 /* Trigger Control */
248 #define DAQBOARD2000_TrigAnalog                  0x0000
249 #define DAQBOARD2000_TrigTTL                     0x0010
250 #define DAQBOARD2000_TrigTransHiLo               0x0004
251 #define DAQBOARD2000_TrigTransLoHi               0x0000
252 #define DAQBOARD2000_TrigAbove                   0x0000
253 #define DAQBOARD2000_TrigBelow                   0x0004
254 #define DAQBOARD2000_TrigLevelSense              0x0002
255 #define DAQBOARD2000_TrigEdgeSense               0x0000
256 #define DAQBOARD2000_TrigEnable                  0x0001
257 #define DAQBOARD2000_TrigDisable                 0x0000
258 
259 /* Reference Dac Selection */
260 #define DAQBOARD2000_PosRefDacSelect             0x0100
261 #define DAQBOARD2000_NegRefDacSelect             0x0000
262 
263 struct daq200_boardtype {
264 	const char *name;
265 	int id;
266 };
267 static const struct daq200_boardtype boardtypes[] = {
268 	{"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
269 	{"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
270 };
271 
272 struct daqboard2000_private {
273 	enum {
274 		card_daqboard_2000
275 	} card;
276 	void __iomem *plx;
277 };
278 
writeAcqScanListEntry(struct comedi_device * dev,u16 entry)279 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
280 {
281 	/* udelay(4); */
282 	writew(entry & 0x00ff, dev->mmio + acqScanListFIFO);
283 	/* udelay(4); */
284 	writew((entry >> 8) & 0x00ff, dev->mmio + acqScanListFIFO);
285 }
286 
setup_sampling(struct comedi_device * dev,int chan,int gain)287 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
288 {
289 	u16 word0, word1, word2, word3;
290 
291 	/* Channel 0-7 diff, channel 8-23 single ended */
292 	word0 = 0;
293 	word1 = 0x0004;		/* Last scan */
294 	word2 = (chan << 6) & 0x00c0;
295 	switch (chan / 4) {
296 	case 0:
297 		word3 = 0x0001;
298 		break;
299 	case 1:
300 		word3 = 0x0002;
301 		break;
302 	case 2:
303 		word3 = 0x0005;
304 		break;
305 	case 3:
306 		word3 = 0x0006;
307 		break;
308 	case 4:
309 		word3 = 0x0041;
310 		break;
311 	case 5:
312 		word3 = 0x0042;
313 		break;
314 	default:
315 		word3 = 0;
316 		break;
317 	}
318 /*
319   dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
320   dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
321 */
322 	/* These should be read from EEPROM */
323 	word2 |= 0x0800;
324 	word3 |= 0xc000;
325 	writeAcqScanListEntry(dev, word0);
326 	writeAcqScanListEntry(dev, word1);
327 	writeAcqScanListEntry(dev, word2);
328 	writeAcqScanListEntry(dev, word3);
329 }
330 
daqboard2000_ai_status(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)331 static int daqboard2000_ai_status(struct comedi_device *dev,
332 				  struct comedi_subdevice *s,
333 				  struct comedi_insn *insn,
334 				  unsigned long context)
335 {
336 	unsigned int status;
337 
338 	status = readw(dev->mmio + acqControl);
339 	if (status & context)
340 		return 0;
341 	return -EBUSY;
342 }
343 
daqboard2000_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)344 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
345 				     struct comedi_subdevice *s,
346 				     struct comedi_insn *insn,
347 				     unsigned int *data)
348 {
349 	int gain, chan;
350 	int ret;
351 	int i;
352 
353 	writew(DAQBOARD2000_AcqResetScanListFifo |
354 	       DAQBOARD2000_AcqResetResultsFifo |
355 	       DAQBOARD2000_AcqResetConfigPipe, dev->mmio + acqControl);
356 
357 	/*
358 	 * If pacer clock is not set to some high value (> 10 us), we
359 	 * risk multiple samples to be put into the result FIFO.
360 	 */
361 	/* 1 second, should be long enough */
362 	writel(1000000, dev->mmio + acqPacerClockDivLow);
363 	writew(0, dev->mmio + acqPacerClockDivHigh);
364 
365 	gain = CR_RANGE(insn->chanspec);
366 	chan = CR_CHAN(insn->chanspec);
367 
368 	/* This doesn't look efficient.  I decided to take the conservative
369 	 * approach when I did the insn conversion.  Perhaps it would be
370 	 * better to have broken it completely, then someone would have been
371 	 * forced to fix it.  --ds */
372 	for (i = 0; i < insn->n; i++) {
373 		setup_sampling(dev, chan, gain);
374 		/* Enable reading from the scanlist FIFO */
375 		writew(DAQBOARD2000_SeqStartScanList, dev->mmio + acqControl);
376 
377 		ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
378 				     DAQBOARD2000_AcqConfigPipeFull);
379 		if (ret)
380 			return ret;
381 
382 		writew(DAQBOARD2000_AdcPacerEnable, dev->mmio + acqControl);
383 
384 		ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
385 				     DAQBOARD2000_AcqLogicScanning);
386 		if (ret)
387 			return ret;
388 
389 		ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
390 				     DAQBOARD2000_AcqResultsFIFOHasValidData);
391 		if (ret)
392 			return ret;
393 
394 		data[i] = readw(dev->mmio + acqResultsFIFO);
395 		writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
396 		writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
397 	}
398 
399 	return i;
400 }
401 
daqboard2000_ao_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)402 static int daqboard2000_ao_eoc(struct comedi_device *dev,
403 			       struct comedi_subdevice *s,
404 			       struct comedi_insn *insn,
405 			       unsigned long context)
406 {
407 	unsigned int chan = CR_CHAN(insn->chanspec);
408 	unsigned int status;
409 
410 	status = readw(dev->mmio + dacControl);
411 	if ((status & ((chan + 1) * 0x0010)) == 0)
412 		return 0;
413 	return -EBUSY;
414 }
415 
daqboard2000_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)416 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
417 				      struct comedi_subdevice *s,
418 				      struct comedi_insn *insn,
419 				      unsigned int *data)
420 {
421 	unsigned int chan = CR_CHAN(insn->chanspec);
422 	int i;
423 
424 	for (i = 0; i < insn->n; i++) {
425 		unsigned int val = data[i];
426 		int ret;
427 
428 		writew(val, dev->mmio + dacSetting(chan));
429 
430 		ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0);
431 		if (ret)
432 			return ret;
433 
434 		s->readback[chan] = val;
435 	}
436 
437 	return insn->n;
438 }
439 
daqboard2000_resetLocalBus(struct comedi_device * dev)440 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
441 {
442 	struct daqboard2000_private *devpriv = dev->private;
443 
444 	writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
445 	mdelay(10);
446 	writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
447 	mdelay(10);
448 }
449 
daqboard2000_reloadPLX(struct comedi_device * dev)450 static void daqboard2000_reloadPLX(struct comedi_device *dev)
451 {
452 	struct daqboard2000_private *devpriv = dev->private;
453 
454 	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
455 	mdelay(10);
456 	writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
457 	mdelay(10);
458 	writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
459 	mdelay(10);
460 }
461 
daqboard2000_pulseProgPin(struct comedi_device * dev)462 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
463 {
464 	struct daqboard2000_private *devpriv = dev->private;
465 
466 	writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
467 	mdelay(10);
468 	writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
469 	mdelay(10);	/* Not in the original code, but I like symmetry... */
470 }
471 
daqboard2000_pollCPLD(struct comedi_device * dev,int mask)472 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
473 {
474 	int result = 0;
475 	int i;
476 	int cpld;
477 
478 	/* timeout after 50 tries -> 5ms */
479 	for (i = 0; i < 50; i++) {
480 		cpld = readw(dev->mmio + 0x1000);
481 		if ((cpld & mask) == mask) {
482 			result = 1;
483 			break;
484 		}
485 		udelay(100);
486 	}
487 	udelay(5);
488 	return result;
489 }
490 
daqboard2000_writeCPLD(struct comedi_device * dev,int data)491 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
492 {
493 	int result = 0;
494 
495 	udelay(10);
496 	writew(data, dev->mmio + 0x1000);
497 	if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
498 	    DAQBOARD2000_CPLD_INIT) {
499 		result = 1;
500 	}
501 	return result;
502 }
503 
initialize_daqboard2000(struct comedi_device * dev,const u8 * cpld_array,size_t len,unsigned long context)504 static int initialize_daqboard2000(struct comedi_device *dev,
505 				   const u8 *cpld_array, size_t len,
506 				   unsigned long context)
507 {
508 	struct daqboard2000_private *devpriv = dev->private;
509 	int result = -EIO;
510 	/* Read the serial EEPROM control register */
511 	int secr;
512 	int retry;
513 	size_t i;
514 
515 	/* Check to make sure the serial eeprom is present on the board */
516 	secr = readl(devpriv->plx + 0x6c);
517 	if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
518 		return -EIO;
519 
520 	for (retry = 0; retry < 3; retry++) {
521 		daqboard2000_resetLocalBus(dev);
522 		daqboard2000_reloadPLX(dev);
523 		daqboard2000_pulseProgPin(dev);
524 		if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
525 			for (i = 0; i < len; i++) {
526 				if (cpld_array[i] == 0xff &&
527 				    cpld_array[i + 1] == 0x20)
528 					break;
529 			}
530 			for (; i < len; i += 2) {
531 				int data =
532 				    (cpld_array[i] << 8) + cpld_array[i + 1];
533 				if (!daqboard2000_writeCPLD(dev, data))
534 					break;
535 			}
536 			if (i >= len) {
537 				daqboard2000_resetLocalBus(dev);
538 				daqboard2000_reloadPLX(dev);
539 				result = 0;
540 				break;
541 			}
542 		}
543 	}
544 	return result;
545 }
546 
daqboard2000_adcStopDmaTransfer(struct comedi_device * dev)547 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
548 {
549 }
550 
daqboard2000_adcDisarm(struct comedi_device * dev)551 static void daqboard2000_adcDisarm(struct comedi_device *dev)
552 {
553 	/* Disable hardware triggers */
554 	udelay(2);
555 	writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
556 	       dev->mmio + trigControl);
557 	udelay(2);
558 	writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
559 	       dev->mmio + trigControl);
560 
561 	/* Stop the scan list FIFO from loading the configuration pipe */
562 	udelay(2);
563 	writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
564 
565 	/* Stop the pacer clock */
566 	udelay(2);
567 	writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
568 
569 	/* Stop the input dma (abort channel 1) */
570 	daqboard2000_adcStopDmaTransfer(dev);
571 }
572 
daqboard2000_activateReferenceDacs(struct comedi_device * dev)573 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
574 {
575 	unsigned int val;
576 	int timeout;
577 
578 	/*  Set the + reference dac value in the FPGA */
579 	writew(0x80 | DAQBOARD2000_PosRefDacSelect, dev->mmio + refDacs);
580 	for (timeout = 0; timeout < 20; timeout++) {
581 		val = readw(dev->mmio + dacControl);
582 		if ((val & DAQBOARD2000_RefBusy) == 0)
583 			break;
584 		udelay(2);
585 	}
586 
587 	/*  Set the - reference dac value in the FPGA */
588 	writew(0x80 | DAQBOARD2000_NegRefDacSelect, dev->mmio + refDacs);
589 	for (timeout = 0; timeout < 20; timeout++) {
590 		val = readw(dev->mmio + dacControl);
591 		if ((val & DAQBOARD2000_RefBusy) == 0)
592 			break;
593 		udelay(2);
594 	}
595 }
596 
daqboard2000_initializeCtrs(struct comedi_device * dev)597 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
598 {
599 }
600 
daqboard2000_initializeTmrs(struct comedi_device * dev)601 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
602 {
603 }
604 
daqboard2000_dacDisarm(struct comedi_device * dev)605 static void daqboard2000_dacDisarm(struct comedi_device *dev)
606 {
607 }
608 
daqboard2000_initializeAdc(struct comedi_device * dev)609 static void daqboard2000_initializeAdc(struct comedi_device *dev)
610 {
611 	daqboard2000_adcDisarm(dev);
612 	daqboard2000_activateReferenceDacs(dev);
613 	daqboard2000_initializeCtrs(dev);
614 	daqboard2000_initializeTmrs(dev);
615 }
616 
daqboard2000_initializeDac(struct comedi_device * dev)617 static void daqboard2000_initializeDac(struct comedi_device *dev)
618 {
619 	daqboard2000_dacDisarm(dev);
620 }
621 
daqboard2000_8255_cb(struct comedi_device * dev,int dir,int port,int data,unsigned long iobase)622 static int daqboard2000_8255_cb(struct comedi_device *dev,
623 				int dir, int port, int data,
624 				unsigned long iobase)
625 {
626 	if (dir) {
627 		writew(data, dev->mmio + iobase + port * 2);
628 		return 0;
629 	}
630 	return readw(dev->mmio + iobase + port * 2);
631 }
632 
daqboard2000_find_boardinfo(struct comedi_device * dev,struct pci_dev * pcidev)633 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
634 					       struct pci_dev *pcidev)
635 {
636 	const struct daq200_boardtype *board;
637 	int i;
638 
639 	if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
640 		return NULL;
641 
642 	for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
643 		board = &boardtypes[i];
644 		if (pcidev->subsystem_device == board->id)
645 			return board;
646 	}
647 	return NULL;
648 }
649 
daqboard2000_auto_attach(struct comedi_device * dev,unsigned long context_unused)650 static int daqboard2000_auto_attach(struct comedi_device *dev,
651 				    unsigned long context_unused)
652 {
653 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
654 	const struct daq200_boardtype *board;
655 	struct daqboard2000_private *devpriv;
656 	struct comedi_subdevice *s;
657 	int result;
658 
659 	board = daqboard2000_find_boardinfo(dev, pcidev);
660 	if (!board)
661 		return -ENODEV;
662 	dev->board_ptr = board;
663 	dev->board_name = board->name;
664 
665 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
666 	if (!devpriv)
667 		return -ENOMEM;
668 
669 	result = comedi_pci_enable(dev);
670 	if (result)
671 		return result;
672 
673 	devpriv->plx = pci_ioremap_bar(pcidev, 0);
674 	dev->mmio = pci_ioremap_bar(pcidev, 2);
675 	if (!devpriv->plx || !dev->mmio)
676 		return -ENOMEM;
677 
678 	result = comedi_alloc_subdevices(dev, 3);
679 	if (result)
680 		return result;
681 
682 	readl(devpriv->plx + 0x6c);
683 
684 	result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
685 				      DAQBOARD2000_FIRMWARE,
686 				      initialize_daqboard2000, 0);
687 	if (result < 0)
688 		return result;
689 
690 	daqboard2000_initializeAdc(dev);
691 	daqboard2000_initializeDac(dev);
692 
693 	s = &dev->subdevices[0];
694 	/* ai subdevice */
695 	s->type = COMEDI_SUBD_AI;
696 	s->subdev_flags = SDF_READABLE | SDF_GROUND;
697 	s->n_chan = 24;
698 	s->maxdata = 0xffff;
699 	s->insn_read = daqboard2000_ai_insn_read;
700 	s->range_table = &range_daqboard2000_ai;
701 
702 	s = &dev->subdevices[1];
703 	/* ao subdevice */
704 	s->type = COMEDI_SUBD_AO;
705 	s->subdev_flags = SDF_WRITABLE;
706 	s->n_chan = 2;
707 	s->maxdata = 0xffff;
708 	s->insn_write = daqboard2000_ao_insn_write;
709 	s->range_table = &range_bipolar10;
710 
711 	result = comedi_alloc_subdev_readback(s);
712 	if (result)
713 		return result;
714 
715 	s = &dev->subdevices[2];
716 	result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
717 				  dioP2ExpansionIO8Bit);
718 	if (result)
719 		return result;
720 
721 	return 0;
722 }
723 
daqboard2000_detach(struct comedi_device * dev)724 static void daqboard2000_detach(struct comedi_device *dev)
725 {
726 	struct daqboard2000_private *devpriv = dev->private;
727 
728 	if (devpriv && devpriv->plx)
729 		iounmap(devpriv->plx);
730 	comedi_pci_detach(dev);
731 }
732 
733 static struct comedi_driver daqboard2000_driver = {
734 	.driver_name	= "daqboard2000",
735 	.module		= THIS_MODULE,
736 	.auto_attach	= daqboard2000_auto_attach,
737 	.detach		= daqboard2000_detach,
738 };
739 
daqboard2000_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)740 static int daqboard2000_pci_probe(struct pci_dev *dev,
741 				  const struct pci_device_id *id)
742 {
743 	return comedi_pci_auto_config(dev, &daqboard2000_driver,
744 				      id->driver_data);
745 }
746 
747 static const struct pci_device_id daqboard2000_pci_table[] = {
748 	{ PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
749 	{ 0 }
750 };
751 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
752 
753 static struct pci_driver daqboard2000_pci_driver = {
754 	.name		= "daqboard2000",
755 	.id_table	= daqboard2000_pci_table,
756 	.probe		= daqboard2000_pci_probe,
757 	.remove		= comedi_pci_auto_unconfig,
758 };
759 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
760 
761 MODULE_AUTHOR("Comedi http://www.comedi.org");
762 MODULE_DESCRIPTION("Comedi low-level driver");
763 MODULE_LICENSE("GPL");
764 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
765