82be77daa7d7853571cacc8a3b38d81037186bbd
[cascardo/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 "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 static int daqboard2000_attach(struct comedi_device *dev,
305                                struct comedi_devconfig *it);
306 static int daqboard2000_detach(struct comedi_device *dev);
307
308 static struct comedi_driver driver_daqboard2000 = {
309         .driver_name = "daqboard2000",
310         .module = THIS_MODULE,
311         .attach = daqboard2000_attach,
312         .detach = daqboard2000_detach,
313 };
314
315 struct daq200_boardtype {
316         const char *name;
317         int id;
318 };
319 static const struct daq200_boardtype boardtypes[] = {
320         {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
321         {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
322 };
323
324 #define n_boardtypes (sizeof(boardtypes)/sizeof(struct daq200_boardtype))
325 #define this_board ((const struct daq200_boardtype *)dev->board_ptr)
326
327 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
328         {
329         0x1616, 0x0409, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
330         0}
331 };
332
333 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
334
335 struct daqboard2000_private {
336         enum {
337                 card_daqboard_2000
338         } card;
339         struct pci_dev *pci_dev;
340         void *daq;
341         void *plx;
342         int got_regions;
343         unsigned int ao_readback[2];
344 };
345
346 #define devpriv ((struct daqboard2000_private *)dev->private)
347
348 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
349 {
350         struct daqboard2000_hw *fpga = devpriv->daq;
351
352 /* udelay(4); */
353         fpga->acqScanListFIFO = entry & 0x00ff;
354 /* udelay(4); */
355         fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
356 }
357
358 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
359 {
360         u16 word0, word1, word2, word3;
361
362         /* Channel 0-7 diff, channel 8-23 single ended */
363         word0 = 0;
364         word1 = 0x0004;         /* Last scan */
365         word2 = (chan << 6) & 0x00c0;
366         switch (chan / 4) {
367         case 0:
368                 word3 = 0x0001;
369                 break;
370         case 1:
371                 word3 = 0x0002;
372                 break;
373         case 2:
374                 word3 = 0x0005;
375                 break;
376         case 3:
377                 word3 = 0x0006;
378                 break;
379         case 4:
380                 word3 = 0x0041;
381                 break;
382         case 5:
383                 word3 = 0x0042;
384                 break;
385         default:
386                 word3 = 0;
387                 break;
388         }
389 /*
390   dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
391   dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
392 */
393         /* These should be read from EEPROM */
394         word2 |= 0x0800;
395         word3 |= 0xc000;
396 /*  printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
397         writeAcqScanListEntry(dev, word0);
398         writeAcqScanListEntry(dev, word1);
399         writeAcqScanListEntry(dev, word2);
400         writeAcqScanListEntry(dev, word3);
401 }
402
403 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
404                                      struct comedi_subdevice *s,
405                                      struct comedi_insn *insn,
406                                      unsigned int *data)
407 {
408         int i;
409         struct daqboard2000_hw *fpga = devpriv->daq;
410         int gain, chan, timeout;
411
412         fpga->acqControl =
413             DAQBOARD2000_AcqResetScanListFifo |
414             DAQBOARD2000_AcqResetResultsFifo | DAQBOARD2000_AcqResetConfigPipe;
415
416         /* If pacer clock is not set to some high value (> 10 us), we
417            risk multiple samples to be put into the result FIFO. */
418         fpga->acqPacerClockDivLow = 1000000;    /* 1 second, should be long enough */
419         fpga->acqPacerClockDivHigh = 0;
420
421         gain = CR_RANGE(insn->chanspec);
422         chan = CR_CHAN(insn->chanspec);
423
424         /* This doesn't look efficient.  I decided to take the conservative
425          * approach when I did the insn conversion.  Perhaps it would be
426          * better to have broken it completely, then someone would have been
427          * forced to fix it.  --ds */
428         for (i = 0; i < insn->n; i++) {
429                 setup_sampling(dev, chan, gain);
430                 /* Enable reading from the scanlist FIFO */
431                 fpga->acqControl = DAQBOARD2000_SeqStartScanList;
432                 for (timeout = 0; timeout < 20; timeout++) {
433                         if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull) {
434                                 break;
435                         }
436                         /* udelay(2); */
437                 }
438                 fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
439                 for (timeout = 0; timeout < 20; timeout++) {
440                         if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning) {
441                                 break;
442                         }
443                         /* udelay(2); */
444                 }
445                 for (timeout = 0; timeout < 20; timeout++) {
446                         if (fpga->acqControl &
447                             DAQBOARD2000_AcqResultsFIFOHasValidData) {
448                                 break;
449                         }
450                         /* udelay(2); */
451                 }
452                 data[i] = fpga->acqResultsFIFO;
453                 fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
454                 fpga->acqControl = DAQBOARD2000_SeqStopScanList;
455         }
456
457         return i;
458 }
459
460 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
461                                      struct comedi_subdevice *s,
462                                      struct comedi_insn *insn,
463                                      unsigned int *data)
464 {
465         int i;
466         int chan = CR_CHAN(insn->chanspec);
467
468         for (i = 0; i < insn->n; i++) {
469                 data[i] = devpriv->ao_readback[chan];
470         }
471
472         return i;
473 }
474
475 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
476                                       struct comedi_subdevice *s,
477                                       struct comedi_insn *insn,
478                                       unsigned int *data)
479 {
480         int i;
481         int chan = CR_CHAN(insn->chanspec);
482         struct daqboard2000_hw *fpga = devpriv->daq;
483         int timeout;
484
485         for (i = 0; i < insn->n; i++) {
486                 /*
487                  * OK, since it works OK without enabling the DAC's, let's keep
488                  * it as simple as possible...
489                  */
490                 /* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
491                 fpga->dacSetting[chan] = data[i];
492                 for (timeout = 0; timeout < 20; timeout++) {
493                         if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0) {
494                                 break;
495                         }
496                         /* udelay(2); */
497                 }
498                 devpriv->ao_readback[chan] = data[i];
499                 /*
500                  * Since we never enabled the DAC's, we don't need to disable it...
501                  * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
502                  */
503         }
504
505         return i;
506 }
507
508 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
509 {
510         printk("daqboard2000_resetLocalBus\n");
511         writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
512         udelay(10000);
513         writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
514         udelay(10000);
515 }
516
517 static void daqboard2000_reloadPLX(struct comedi_device *dev)
518 {
519         printk("daqboard2000_reloadPLX\n");
520         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
521         udelay(10000);
522         writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
523         udelay(10000);
524         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
525         udelay(10000);
526 }
527
528 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
529 {
530         printk("daqboard2000_pulseProgPin 1\n");
531         writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
532         udelay(10000);
533         writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
534         udelay(10000);          /* Not in the original code, but I like symmetry... */
535 }
536
537 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
538 {
539         int result = 0;
540         int i;
541         int cpld;
542
543         /* timeout after 50 tries -> 5ms */
544         for (i = 0; i < 50; i++) {
545                 cpld = readw(devpriv->daq + 0x1000);
546                 if ((cpld & mask) == mask) {
547                         result = 1;
548                         break;
549                 }
550                 udelay(100);
551         }
552         udelay(5);
553         return result;
554 }
555
556 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
557 {
558         int result = 0;
559
560         udelay(10);
561         writew(data, devpriv->daq + 0x1000);
562         if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
563             DAQBOARD2000_CPLD_INIT) {
564                 result = 1;
565         }
566         return result;
567 }
568
569 static int initialize_daqboard2000(struct comedi_device *dev,
570                                    unsigned char *cpld_array, int len)
571 {
572         int result = -EIO;
573         /* Read the serial EEPROM control register */
574         int secr;
575         int retry;
576         int i;
577
578         /* Check to make sure the serial eeprom is present on the board */
579         secr = readl(devpriv->plx + 0x6c);
580         if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
581 #ifdef DEBUG_EEPROM
582                 printk("no serial eeprom\n");
583 #endif
584                 return -EIO;
585         }
586
587         for (retry = 0; retry < 3; retry++) {
588 #ifdef DEBUG_EEPROM
589                 printk("Programming EEPROM try %x\n", retry);
590 #endif
591
592                 daqboard2000_resetLocalBus(dev);
593                 daqboard2000_reloadPLX(dev);
594                 daqboard2000_pulseProgPin(dev);
595                 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
596                         for (i = 0; i < len; i++) {
597                                 if (cpld_array[i] == 0xff
598                                     && cpld_array[i + 1] == 0x20) {
599 #ifdef DEBUG_EEPROM
600                                         printk("Preamble found at %d\n", i);
601 #endif
602                                         break;
603                                 }
604                         }
605                         for (; i < len; i += 2) {
606                                 int data =
607                                     (cpld_array[i] << 8) + cpld_array[i + 1];
608                                 if (!daqboard2000_writeCPLD(dev, data)) {
609                                         break;
610                                 }
611                         }
612                         if (i >= len) {
613 #ifdef DEBUG_EEPROM
614                                 printk("Programmed\n");
615 #endif
616                                 daqboard2000_resetLocalBus(dev);
617                                 daqboard2000_reloadPLX(dev);
618                                 result = 0;
619                                 break;
620                         }
621                 }
622         }
623         return result;
624 }
625
626 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
627 {
628 /*  printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
629 }
630
631 static void daqboard2000_adcDisarm(struct comedi_device *dev)
632 {
633         struct daqboard2000_hw *fpga = devpriv->daq;
634
635         /* Disable hardware triggers */
636         udelay(2);
637         fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
638         udelay(2);
639         fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
640
641         /* Stop the scan list FIFO from loading the configuration pipe */
642         udelay(2);
643         fpga->acqControl = DAQBOARD2000_SeqStopScanList;
644
645         /* Stop the pacer clock */
646         udelay(2);
647         fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
648
649         /* Stop the input dma (abort channel 1) */
650         daqboard2000_adcStopDmaTransfer(dev);
651 }
652
653 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
654 {
655         struct daqboard2000_hw *fpga = devpriv->daq;
656         int timeout;
657
658         /*  Set the + reference dac value in the FPGA */
659         fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
660         for (timeout = 0; timeout < 20; timeout++) {
661                 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
662                         break;
663                 }
664                 udelay(2);
665         }
666 /*  printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
667
668         /*  Set the - reference dac value in the FPGA */
669         fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
670         for (timeout = 0; timeout < 20; timeout++) {
671                 if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0) {
672                         break;
673                 }
674                 udelay(2);
675         }
676 /*  printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
677 }
678
679 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
680 {
681 /*  printk("Implement: daqboard2000_initializeCtrs\n");*/
682 }
683
684 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
685 {
686 /*  printk("Implement: daqboard2000_initializeTmrs\n");*/
687 }
688
689 static void daqboard2000_dacDisarm(struct comedi_device *dev)
690 {
691 /*  printk("Implement: daqboard2000_dacDisarm\n");*/
692 }
693
694 static void daqboard2000_initializeAdc(struct comedi_device *dev)
695 {
696         daqboard2000_adcDisarm(dev);
697         daqboard2000_activateReferenceDacs(dev);
698         daqboard2000_initializeCtrs(dev);
699         daqboard2000_initializeTmrs(dev);
700 }
701
702 static void daqboard2000_initializeDac(struct comedi_device *dev)
703 {
704         daqboard2000_dacDisarm(dev);
705 }
706
707 /*
708 The test command, REMOVE!!:
709
710 rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
711 */
712
713 static int daqboard2000_8255_cb(int dir, int port, int data,
714                                 unsigned long ioaddr)
715 {
716         int result = 0;
717         if (dir) {
718                 writew(data, ((void *)ioaddr) + port * 2);
719                 result = 0;
720         } else {
721                 result = readw(((void *)ioaddr) + port * 2);
722         }
723 /*
724   printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
725         arg, dir, port, data, result);
726 */
727         return result;
728 }
729
730 static int daqboard2000_attach(struct comedi_device *dev,
731                                struct comedi_devconfig *it)
732 {
733         int result = 0;
734         struct comedi_subdevice *s;
735         struct pci_dev *card = NULL;
736         void *aux_data;
737         unsigned int aux_len;
738         int bus, slot;
739
740         printk("comedi%d: daqboard2000:", dev->minor);
741
742         bus = it->options[0];
743         slot = it->options[1];
744
745         result = alloc_private(dev, sizeof(struct daqboard2000_private));
746         if (result < 0) {
747                 return -ENOMEM;
748         }
749         for (card = pci_get_device(0x1616, 0x0409, NULL);
750              card != NULL; card = pci_get_device(0x1616, 0x0409, card)) {
751                 if (bus || slot) {
752                         /* requested particular bus/slot */
753                         if (card->bus->number != bus ||
754                             PCI_SLOT(card->devfn) != slot) {
755                                 continue;
756                         }
757                 }
758                 break;          /* found one */
759         }
760         if (!card) {
761                 if (bus || slot)
762                         printk(" no daqboard2000 found at bus/slot: %d/%d\n",
763                                bus, slot);
764                 else
765                         printk(" no daqboard2000 found\n");
766                 return -EIO;
767         } else {
768                 u32 id;
769                 int i;
770                 devpriv->pci_dev = card;
771                 id = ((u32) card->
772                       subsystem_device << 16) | card->subsystem_vendor;
773                 for (i = 0; i < n_boardtypes; i++) {
774                         if (boardtypes[i].id == id) {
775                                 printk(" %s", boardtypes[i].name);
776                                 dev->board_ptr = boardtypes + i;
777                         }
778                 }
779                 if (!dev->board_ptr) {
780                         printk
781                             (" unknown subsystem id %08x (pretend it is an ids2)",
782                              id);
783                         dev->board_ptr = boardtypes;
784                 }
785         }
786
787         result = comedi_pci_enable(card, "daqboard2000");
788         if (result < 0) {
789                 printk(" failed to enable PCI device and request regions\n");
790                 return -EIO;
791         }
792         devpriv->got_regions = 1;
793         devpriv->plx =
794             ioremap(pci_resource_start(card, 0), DAQBOARD2000_PLX_SIZE);
795         devpriv->daq =
796             ioremap(pci_resource_start(card, 2), DAQBOARD2000_DAQ_SIZE);
797         if (!devpriv->plx || !devpriv->daq) {
798                 return -ENOMEM;
799         }
800
801         result = alloc_subdevices(dev, 3);
802         if (result < 0)
803                 goto out;
804
805         readl(devpriv->plx + 0x6c);
806
807         /*
808            u8 interrupt;
809            Windows code does restore interrupts, but since we don't use them...
810            pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
811            printk("Interrupt before is: %x\n", interrupt);
812          */
813
814         aux_data = comedi_aux_data(it->options, 0);
815         aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
816
817         if (aux_data && aux_len) {
818                 result = initialize_daqboard2000(dev, aux_data, aux_len);
819         } else {
820                 printk("no FPGA initialization code, aborting\n");
821                 result = -EIO;
822         }
823         if (result < 0)
824                 goto out;
825         daqboard2000_initializeAdc(dev);
826         daqboard2000_initializeDac(dev);
827         /*
828            Windows code does restore interrupts, but since we don't use them...
829            pci_read_config_byte(card, PCI_INTERRUPT_LINE, &interrupt);
830            printk("Interrupt after is: %x\n", interrupt);
831          */
832
833         dev->iobase = (unsigned long)devpriv->daq;
834
835         dev->board_name = this_board->name;
836
837         s = dev->subdevices + 0;
838         /* ai subdevice */
839         s->type = COMEDI_SUBD_AI;
840         s->subdev_flags = SDF_READABLE | SDF_GROUND;
841         s->n_chan = 24;
842         s->maxdata = 0xffff;
843         s->insn_read = daqboard2000_ai_insn_read;
844         s->range_table = &range_daqboard2000_ai;
845
846         s = dev->subdevices + 1;
847         /* ao subdevice */
848         s->type = COMEDI_SUBD_AO;
849         s->subdev_flags = SDF_WRITABLE;
850         s->n_chan = 2;
851         s->maxdata = 0xffff;
852         s->insn_read = daqboard2000_ao_insn_read;
853         s->insn_write = daqboard2000_ao_insn_write;
854         s->range_table = &range_daqboard2000_ao;
855
856         s = dev->subdevices + 2;
857         result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
858                                   (unsigned long)(dev->iobase + 0x40));
859
860         printk("\n");
861 out:
862         return result;
863 }
864
865 static int daqboard2000_detach(struct comedi_device *dev)
866 {
867         printk("comedi%d: daqboard2000: remove\n", dev->minor);
868
869         if (dev->subdevices)
870                 subdev_8255_cleanup(dev, dev->subdevices + 2);
871
872         if (dev->irq) {
873                 free_irq(dev->irq, dev);
874         }
875         if (devpriv) {
876                 if (devpriv->daq)
877                         iounmap(devpriv->daq);
878                 if (devpriv->plx)
879                         iounmap(devpriv->plx);
880                 if (devpriv->pci_dev) {
881                         if (devpriv->got_regions) {
882                                 comedi_pci_disable(devpriv->pci_dev);
883                         }
884                         pci_dev_put(devpriv->pci_dev);
885                 }
886         }
887         return 0;
888 }
889
890 static int __devinit driver_daqboard2000_pci_probe(struct pci_dev *dev,
891                                                    const struct pci_device_id
892                                                    *ent)
893 {
894         return comedi_pci_auto_config(dev, driver_daqboard2000.driver_name);
895 }
896
897 static void __devexit driver_daqboard2000_pci_remove(struct pci_dev *dev)
898 {
899         comedi_pci_auto_unconfig(dev);
900 }
901
902 static struct pci_driver driver_daqboard2000_pci_driver = {
903         .id_table = daqboard2000_pci_table,
904         .probe = &driver_daqboard2000_pci_probe,
905         .remove = __devexit_p(&driver_daqboard2000_pci_remove)
906 };
907
908 static int __init driver_daqboard2000_init_module(void)
909 {
910         int retval;
911
912         retval = comedi_driver_register(&driver_daqboard2000);
913         if (retval < 0)
914                 return retval;
915
916         driver_daqboard2000_pci_driver.name =
917             (char *)driver_daqboard2000.driver_name;
918         return pci_register_driver(&driver_daqboard2000_pci_driver);
919 }
920
921 static void __exit driver_daqboard2000_cleanup_module(void)
922 {
923         pci_unregister_driver(&driver_daqboard2000_pci_driver);
924         comedi_driver_unregister(&driver_daqboard2000);
925 }
926
927 module_init(driver_daqboard2000_init_module);
928 module_exit(driver_daqboard2000_cleanup_module);
929
930 MODULE_AUTHOR("Comedi http://www.comedi.org");
931 MODULE_DESCRIPTION("Comedi low-level driver");
932 MODULE_LICENSE("GPL");