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