staging: comedi: daqboard2000: factor out the "find pci device" code
[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 "8255.h"
122
123 #define DAQBOARD2000_SUBSYSTEM_IDS2     0x00021616      /* Daqboard/2000 - 2 Dacs */
124 #define DAQBOARD2000_SUBSYSTEM_IDS4     0x00041616      /* Daqboard/2000 - 4 Dacs */
125
126 #define DAQBOARD2000_DAQ_SIZE           0x1002
127 #define DAQBOARD2000_PLX_SIZE           0x100
128
129 /* Initialization bits for the Serial EEPROM Control Register */
130 #define DAQBOARD2000_SECRProgPinHi      0x8001767e
131 #define DAQBOARD2000_SECRProgPinLo      0x8000767e
132 #define DAQBOARD2000_SECRLocalBusHi     0xc000767e
133 #define DAQBOARD2000_SECRLocalBusLo     0x8000767e
134 #define DAQBOARD2000_SECRReloadHi       0xa000767e
135 #define DAQBOARD2000_SECRReloadLo       0x8000767e
136
137 /* SECR status bits */
138 #define DAQBOARD2000_EEPROM_PRESENT     0x10000000
139
140 /* CPLD status bits */
141 #define DAQBOARD2000_CPLD_INIT          0x0002
142 #define DAQBOARD2000_CPLD_DONE          0x0004
143
144 /* Available ranges */
145 static const struct comedi_lrange range_daqboard2000_ai = { 13, {
146                                                                  RANGE(-10, 10),
147                                                                  RANGE(-5, 5),
148                                                                  RANGE(-2.5,
149                                                                        2.5),
150                                                                  RANGE(-1.25,
151                                                                        1.25),
152                                                                  RANGE(-0.625,
153                                                                        0.625),
154                                                                  RANGE(-0.3125,
155                                                                        0.3125),
156                                                                  RANGE(-0.156,
157                                                                        0.156),
158                                                                  RANGE(0, 10),
159                                                                  RANGE(0, 5),
160                                                                  RANGE(0, 2.5),
161                                                                  RANGE(0, 1.25),
162                                                                  RANGE(0,
163                                                                        0.625),
164                                                                  RANGE(0,
165                                                                        0.3125)
166                                                                  }
167 };
168
169 static const struct comedi_lrange range_daqboard2000_ao = { 1, {
170                                                                 RANGE(-10, 10)
171                                                                 }
172 };
173
174 struct daqboard2000_hw {
175         volatile u16 acqControl;        /*  0x00 */
176         volatile u16 acqScanListFIFO;   /*  0x02 */
177         volatile u32 acqPacerClockDivLow;       /*  0x04 */
178
179         volatile u16 acqScanCounter;    /*  0x08 */
180         volatile u16 acqPacerClockDivHigh;      /*  0x0a */
181         volatile u16 acqTriggerCount;   /*  0x0c */
182         volatile u16 fill2;     /*  0x0e */
183         volatile u16 acqResultsFIFO;    /*  0x10 */
184         volatile u16 fill3;     /*  0x12 */
185         volatile u16 acqResultsShadow;  /*  0x14 */
186         volatile u16 fill4;     /*  0x16 */
187         volatile u16 acqAdcResult;      /*  0x18 */
188         volatile u16 fill5;     /*  0x1a */
189         volatile u16 dacScanCounter;    /*  0x1c */
190         volatile u16 fill6;     /*  0x1e */
191
192         volatile u16 dacControl;        /*  0x20 */
193         volatile u16 fill7;     /*  0x22 */
194         volatile s16 dacFIFO;   /*  0x24 */
195         volatile u16 fill8[2];  /*  0x26 */
196         volatile u16 dacPacerClockDiv;  /*  0x2a */
197         volatile u16 refDacs;   /*  0x2c */
198         volatile u16 fill9;     /*  0x2e */
199
200         volatile u16 dioControl;        /*  0x30 */
201         volatile s16 dioP3hsioData;     /*  0x32 */
202         volatile u16 dioP3Control;      /*  0x34 */
203         volatile u16 calEepromControl;  /*  0x36 */
204         volatile s16 dacSetting[4];     /*  0x38 */
205         volatile s16 dioP2ExpansionIO8Bit[32];  /*  0x40 */
206
207         volatile u16 ctrTmrControl;     /*  0x80 */
208         volatile u16 fill10[3]; /*  0x82 */
209         volatile s16 ctrInput[4];       /*  0x88 */
210         volatile u16 fill11[8]; /*  0x90 */
211         volatile u16 timerDivisor[2];   /*  0xa0 */
212         volatile u16 fill12[6]; /*  0xa4 */
213
214         volatile u16 dmaControl;        /*  0xb0 */
215         volatile u16 trigControl;       /*  0xb2 */
216         volatile u16 fill13[2]; /*  0xb4 */
217         volatile u16 calEeprom; /*  0xb8 */
218         volatile u16 acqDigitalMark;    /*  0xba */
219         volatile u16 trigDacs;  /*  0xbc */
220         volatile u16 fill14;    /*  0xbe */
221         volatile s16 dioP2ExpansionIO16Bit[32]; /*  0xc0 */
222 };
223
224 /* Scan Sequencer programming */
225 #define DAQBOARD2000_SeqStartScanList            0x0011
226 #define DAQBOARD2000_SeqStopScanList             0x0010
227
228 /* Prepare for acquisition */
229 #define DAQBOARD2000_AcqResetScanListFifo        0x0004
230 #define DAQBOARD2000_AcqResetResultsFifo         0x0002
231 #define DAQBOARD2000_AcqResetConfigPipe          0x0001
232
233 /* Acqusition status bits */
234 #define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
235 #define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
236 #define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
237 #define DAQBOARD2000_AcqLogicScanning            0x0008
238 #define DAQBOARD2000_AcqConfigPipeFull           0x0010
239 #define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
240 #define DAQBOARD2000_AcqAdcNotReady              0x0040
241 #define DAQBOARD2000_ArbitrationFailure          0x0080
242 #define DAQBOARD2000_AcqPacerOverrun             0x0100
243 #define DAQBOARD2000_DacPacerOverrun             0x0200
244 #define DAQBOARD2000_AcqHardwareError            0x01c0
245
246 /* Scan Sequencer programming */
247 #define DAQBOARD2000_SeqStartScanList            0x0011
248 #define DAQBOARD2000_SeqStopScanList             0x0010
249
250 /* Pacer Clock Control */
251 #define DAQBOARD2000_AdcPacerInternal            0x0030
252 #define DAQBOARD2000_AdcPacerExternal            0x0032
253 #define DAQBOARD2000_AdcPacerEnable              0x0031
254 #define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
255 #define DAQBOARD2000_AdcPacerDisable             0x0030
256 #define DAQBOARD2000_AdcPacerNormalMode          0x0060
257 #define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
258 #define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
259 #define DAQBOARD2000_AdcPacerExternalRising      0x0100
260
261 /* DAC status */
262 #define DAQBOARD2000_DacFull                     0x0001
263 #define DAQBOARD2000_RefBusy                     0x0002
264 #define DAQBOARD2000_TrgBusy                     0x0004
265 #define DAQBOARD2000_CalBusy                     0x0008
266 #define DAQBOARD2000_Dac0Busy                    0x0010
267 #define DAQBOARD2000_Dac1Busy                    0x0020
268 #define DAQBOARD2000_Dac2Busy                    0x0040
269 #define DAQBOARD2000_Dac3Busy                    0x0080
270
271 /* DAC control */
272 #define DAQBOARD2000_Dac0Enable                  0x0021
273 #define DAQBOARD2000_Dac1Enable                  0x0031
274 #define DAQBOARD2000_Dac2Enable                  0x0041
275 #define DAQBOARD2000_Dac3Enable                  0x0051
276 #define DAQBOARD2000_DacEnableBit                0x0001
277 #define DAQBOARD2000_Dac0Disable                 0x0020
278 #define DAQBOARD2000_Dac1Disable                 0x0030
279 #define DAQBOARD2000_Dac2Disable                 0x0040
280 #define DAQBOARD2000_Dac3Disable                 0x0050
281 #define DAQBOARD2000_DacResetFifo                0x0004
282 #define DAQBOARD2000_DacPatternDisable           0x0060
283 #define DAQBOARD2000_DacPatternEnable            0x0061
284 #define DAQBOARD2000_DacSelectSignedData         0x0002
285 #define DAQBOARD2000_DacSelectUnsignedData       0x0000
286
287 /* Trigger Control */
288 #define DAQBOARD2000_TrigAnalog                  0x0000
289 #define DAQBOARD2000_TrigTTL                     0x0010
290 #define DAQBOARD2000_TrigTransHiLo               0x0004
291 #define DAQBOARD2000_TrigTransLoHi               0x0000
292 #define DAQBOARD2000_TrigAbove                   0x0000
293 #define DAQBOARD2000_TrigBelow                   0x0004
294 #define DAQBOARD2000_TrigLevelSense              0x0002
295 #define DAQBOARD2000_TrigEdgeSense               0x0000
296 #define DAQBOARD2000_TrigEnable                  0x0001
297 #define DAQBOARD2000_TrigDisable                 0x0000
298
299 /* Reference Dac Selection */
300 #define DAQBOARD2000_PosRefDacSelect             0x0100
301 #define DAQBOARD2000_NegRefDacSelect             0x0000
302
303 struct daq200_boardtype {
304         const char *name;
305         int id;
306 };
307 static const struct daq200_boardtype boardtypes[] = {
308         {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
309         {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
310 };
311
312 #define this_board ((const struct daq200_boardtype *)dev->board_ptr)
313
314 struct daqboard2000_private {
315         enum {
316                 card_daqboard_2000
317         } card;
318         struct pci_dev *pci_dev;
319         void *daq;
320         void *plx;
321         int got_regions;
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
713         for (pcidev = pci_get_device(0x1616, 0x0409, NULL);
714              pcidev != NULL; pcidev = pci_get_device(0x1616, 0x0409, pcidev)) {
715                 if (bus || slot) {
716                         /* requested particular bus/slot */
717                         if (pcidev->bus->number != bus ||
718                             PCI_SLOT(pcidev->devfn) != slot) {
719                                 continue;
720                         }
721                 }
722                 break;          /* found one */
723         }
724         if (!pcidev) {
725                 if (bus || slot)
726                         dev_err(dev->class_dev,
727                                 "no daqboard2000 found at bus/slot: %d/%d\n",
728                                 bus, slot);
729                 else
730                         dev_err(dev->class_dev, "no daqboard2000 found\n");
731                 return NULL;
732         } else {
733                 u32 id;
734                 int i;
735
736                 id = ((u32) pcidev->
737                       subsystem_device << 16) | pcidev->subsystem_vendor;
738                 for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
739                         if (boardtypes[i].id == id) {
740                                 dev_dbg(dev->class_dev, "%s\n",
741                                         boardtypes[i].name);
742                                 dev->board_ptr = boardtypes + i;
743                         }
744                 }
745                 if (!dev->board_ptr) {
746                         printk
747                             (" unknown subsystem id %08x (pretend it is an ids2)",
748                              id);
749                         dev->board_ptr = boardtypes;
750                 }
751                 return pcidev;
752         }
753 }
754
755 static int daqboard2000_attach(struct comedi_device *dev,
756                                struct comedi_devconfig *it)
757 {
758         struct pci_dev *pcidev;
759         struct comedi_subdevice *s;
760         void *aux_data;
761         unsigned int aux_len;
762         int result;
763
764         result = alloc_private(dev, sizeof(struct daqboard2000_private));
765         if (result < 0)
766                 return -ENOMEM;
767
768         pcidev = daqboard2000_find_pci_dev(dev, it);
769         if (!pcidev)
770                 return -EIO;
771         devpriv->pci_dev = pcidev;
772
773         result = comedi_pci_enable(pcidev, "daqboard2000");
774         if (result < 0) {
775                 dev_err(dev->class_dev,
776                         "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(pcidev, 0), DAQBOARD2000_PLX_SIZE);
782         devpriv->daq =
783             ioremap(pci_resource_start(pcidev, 2), DAQBOARD2000_DAQ_SIZE);
784         if (!devpriv->plx || !devpriv->daq)
785                 return -ENOMEM;
786
787         result = comedi_alloc_subdevices(dev, 3);
788         if (result)
789                 return result;
790
791         readl(devpriv->plx + 0x6c);
792
793         /*
794            u8 interrupt;
795            Windows code does restore interrupts, but since we don't use them...
796            pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
797            printk("Interrupt before is: %x\n", interrupt);
798          */
799
800         aux_data = comedi_aux_data(it->options, 0);
801         aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
802
803         if (aux_data && aux_len) {
804                 result = initialize_daqboard2000(dev, aux_data, aux_len);
805         } else {
806                 dev_dbg(dev->class_dev,
807                         "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(pcidev, 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 out:
848         return result;
849 }
850
851 static void daqboard2000_detach(struct comedi_device *dev)
852 {
853         if (dev->subdevices)
854                 subdev_8255_cleanup(dev, dev->subdevices + 2);
855         if (dev->irq)
856                 free_irq(dev->irq, dev);
857         if (devpriv) {
858                 if (devpriv->daq)
859                         iounmap(devpriv->daq);
860                 if (devpriv->plx)
861                         iounmap(devpriv->plx);
862                 if (devpriv->pci_dev) {
863                         if (devpriv->got_regions)
864                                 comedi_pci_disable(devpriv->pci_dev);
865                         pci_dev_put(devpriv->pci_dev);
866                 }
867         }
868 }
869
870 static struct comedi_driver daqboard2000_driver = {
871         .driver_name    = "daqboard2000",
872         .module         = THIS_MODULE,
873         .attach         = daqboard2000_attach,
874         .detach         = daqboard2000_detach,
875 };
876
877 static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
878                                             const struct pci_device_id *ent)
879 {
880         return comedi_pci_auto_config(dev, &daqboard2000_driver);
881 }
882
883 static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
884 {
885         comedi_pci_auto_unconfig(dev);
886 }
887
888 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
889         { PCI_DEVICE(0x1616, 0x0409) },
890         { 0 }
891 };
892 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
893
894 static struct pci_driver daqboard2000_pci_driver = {
895         .name           = "daqboard2000",
896         .id_table       = daqboard2000_pci_table,
897         .probe          = daqboard2000_pci_probe,
898         .remove         = __devexit_p(daqboard2000_pci_remove),
899 };
900 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
901
902 MODULE_AUTHOR("Comedi http://www.comedi.org");
903 MODULE_DESCRIPTION("Comedi low-level driver");
904 MODULE_LICENSE("GPL");