]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/daqboard2000.c
staging: comedi: daqboard2000: add back subsystem_device check
[karo-tx-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 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         void *aux_data;
744         unsigned int aux_len;
745         int result;
746
747         result = alloc_private(dev, sizeof(struct daqboard2000_private));
748         if (result < 0)
749                 return -ENOMEM;
750
751         pcidev = daqboard2000_find_pci_dev(dev, it);
752         if (!pcidev)
753                 return -EIO;
754         comedi_set_hw_dev(dev, &pcidev->dev);
755
756         result = comedi_pci_enable(pcidev, "daqboard2000");
757         if (result < 0) {
758                 dev_err(dev->class_dev,
759                         "failed to enable PCI device and request regions\n");
760                 return -EIO;
761         }
762         dev->iobase = pci_resource_start(pcidev, 2);
763
764         devpriv->plx =
765             ioremap(pci_resource_start(pcidev, 0), DAQBOARD2000_PLX_SIZE);
766         devpriv->daq = ioremap(dev->iobase, DAQBOARD2000_DAQ_SIZE);
767         if (!devpriv->plx || !devpriv->daq)
768                 return -ENOMEM;
769
770         result = comedi_alloc_subdevices(dev, 3);
771         if (result)
772                 return result;
773
774         readl(devpriv->plx + 0x6c);
775
776         /*
777            u8 interrupt;
778            Windows code does restore interrupts, but since we don't use them...
779            pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
780            printk("Interrupt before is: %x\n", interrupt);
781          */
782
783         aux_data = comedi_aux_data(it->options, 0);
784         aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
785
786         if (aux_data && aux_len) {
787                 result = initialize_daqboard2000(dev, aux_data, aux_len);
788         } else {
789                 dev_dbg(dev->class_dev,
790                         "no FPGA initialization code, aborting\n");
791                 result = -EIO;
792         }
793         if (result < 0)
794                 goto out;
795         daqboard2000_initializeAdc(dev);
796         daqboard2000_initializeDac(dev);
797         /*
798            Windows code does restore interrupts, but since we don't use them...
799            pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
800            printk("Interrupt after is: %x\n", interrupt);
801          */
802
803         dev->iobase = (unsigned long)devpriv->daq;
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)(dev->iobase + 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");