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