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