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