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