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