]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/daqboard2000.c
c5aa6b8d8f7b9f62dbe41f2b70969abc529c138e
[karo-tx-linux.git] / drivers / staging / comedi / drivers / daqboard2000.c
1 /*
2    comedi/drivers/daqboard2000.c
3    hardware driver for IOtech DAQboard/2000
4
5    COMEDI - Linux Control and Measurement Device Interface
6    Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22  */
23 /*
24 Driver: daqboard2000
25 Description: IOTech DAQBoard/2000
26 Author: Anders Blomdell <anders.blomdell@control.lth.se>
27 Status: works
28 Updated: Mon, 14 Apr 2008 15:28:52 +0100
29 Devices: [IOTech] DAQBoard/2000 (daqboard2000)
30
31 Much of the functionality of this driver was determined from reading
32 the source code for the Windows driver.
33
34 The FPGA on the board requires fimware, which is available from
35 http://www.comedi.org in the comedi_nonfree_firmware tarball.
36
37 Configuration options: not applicable, uses PCI auto config
38 */
39 /*
40    This card was obviously never intended to leave the Windows world,
41    since it lacked all kind of hardware documentation (except for cable
42    pinouts, plug and pray has something to catch up with yet).
43
44    With some help from our swedish distributor, we got the Windows sourcecode
45    for the card, and here are the findings so far.
46
47    1. A good document that describes the PCI interface chip is 9080db-106.pdf
48       available from http://www.plxtech.com/products/io/pci9080 
49
50    2. The initialization done so far is:
51         a. program the FPGA (windows code sans a lot of error messages)
52         b.
53
54    3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
55       you have to output values to all enabled DAC's until result appears, I
56       guess that it has something to do with pacer clocks, but the source
57       gives me no clues. I'll keep it simple so far.
58
59    4. Analog in.
60         Each channel in the scanlist seems to be controlled by four
61         control words:
62
63         Word0:
64           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65           ! | | | ! | | | ! | | | ! | | | !
66           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
67
68         Word1:
69           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70           ! | | | ! | | | ! | | | ! | | | !
71           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72            |             |       | | | | |
73            +------+------+       | | | | +-- Digital input (??)
74                   |              | | | +---- 10 us settling time
75                   |              | | +------ Suspend acquisition (last to scan)
76                   |              | +-------- Simultaneous sample and hold
77                   |              +---------- Signed data format
78                   +------------------------- Correction offset low
79
80         Word2:
81           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
82           ! | | | ! | | | ! | | | ! | | | !
83           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
84            |     | |     | | | | | |     |
85            +-----+ +--+--+ +++ +++ +--+--+
86               |       |     |   |     +----- Expansion channel
87               |       |     |   +----------- Expansion gain
88               |       |     +--------------- Channel (low)
89               |       +--------------------- Correction offset high
90               +----------------------------- Correction gain low
91         Word3:
92           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
93           ! | | | ! | | | ! | | | ! | | | !
94           +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
95            |             | | | |   | | | |
96            +------+------+ | | +-+-+ | | +-- Low bank enable
97                   |        | |   |   | +---- High bank enable
98                   |        | |   |   +------ Hi/low select
99                   |        | |   +---------- Gain (1,?,2,4,8,16,32,64)
100                   |        | +-------------- differential/single ended
101                   |        +---------------- Unipolar
102                   +------------------------- Correction gain high
103
104    999. The card seems to have an incredible amount of capabilities, but
105         trying to reverse engineer them from the Windows source is beyond my
106         patience.
107
108  */
109
110 #include "../comedidev.h"
111
112 #include <linux/delay.h>
113 #include <linux/interrupt.h>
114 #include <linux/firmware.h>
115
116 #include "8255.h"
117
118 #define DAQBOARD2000_FIRMWARE           "daqboard2000_firmware.bin"
119
120 #define DAQBOARD2000_SUBSYSTEM_IDS2     0x0002  /* Daqboard/2000 - 2 Dacs */
121 #define DAQBOARD2000_SUBSYSTEM_IDS4     0x0004  /* Daqboard/2000 - 4 Dacs */
122
123 /* Initialization bits for the Serial EEPROM Control Register */
124 #define DAQBOARD2000_SECRProgPinHi      0x8001767e
125 #define DAQBOARD2000_SECRProgPinLo      0x8000767e
126 #define DAQBOARD2000_SECRLocalBusHi     0xc000767e
127 #define DAQBOARD2000_SECRLocalBusLo     0x8000767e
128 #define DAQBOARD2000_SECRReloadHi       0xa000767e
129 #define DAQBOARD2000_SECRReloadLo       0x8000767e
130
131 /* SECR status bits */
132 #define DAQBOARD2000_EEPROM_PRESENT     0x10000000
133
134 /* CPLD status bits */
135 #define DAQBOARD2000_CPLD_INIT          0x0002
136 #define DAQBOARD2000_CPLD_DONE          0x0004
137
138 static const struct comedi_lrange range_daqboard2000_ai = {
139         13, {
140                 BIP_RANGE(10),
141                 BIP_RANGE(5),
142                 BIP_RANGE(2.5),
143                 BIP_RANGE(1.25),
144                 BIP_RANGE(0.625),
145                 BIP_RANGE(0.3125),
146                 BIP_RANGE(0.156),
147                 UNI_RANGE(10),
148                 UNI_RANGE(5),
149                 UNI_RANGE(2.5),
150                 UNI_RANGE(1.25),
151                 UNI_RANGE(0.625),
152                 UNI_RANGE(0.3125)
153         }
154 };
155
156 /*
157  * Register Memory Map
158  */
159 #define acqControl                      0x00            /* u16 */
160 #define acqScanListFIFO                 0x02            /* u16 */
161 #define acqPacerClockDivLow             0x04            /* u32 */
162 #define acqScanCounter                  0x08            /* u16 */
163 #define acqPacerClockDivHigh            0x0a            /* u16 */
164 #define acqTriggerCount                 0x0c            /* u16 */
165 #define acqResultsFIFO                  0x10            /* u16 */
166 #define acqResultsShadow                0x14            /* u16 */
167 #define acqAdcResult                    0x18            /* u16 */
168 #define dacScanCounter                  0x1c            /* u16 */
169 #define dacControl                      0x20            /* u16 */
170 #define dacFIFO                         0x24            /* s16 */
171 #define dacPacerClockDiv                0x2a            /* u16 */
172 #define refDacs                         0x2c            /* u16 */
173 #define dioControl                      0x30            /* u16 */
174 #define dioP3hsioData                   0x32            /* s16 */
175 #define dioP3Control                    0x34            /* u16 */
176 #define calEepromControl                0x36            /* u16 */
177 #define dacSetting(x)                   (0x38 + (x)*2)  /* s16 */
178 #define dioP2ExpansionIO8Bit            0x40            /* s16 */
179 #define ctrTmrControl                   0x80            /* u16 */
180 #define ctrInput(x)                     (0x88 + (x)*2)  /* s16 */
181 #define timerDivisor(x)                 (0xa0 + (x)*2)  /* u16 */
182 #define dmaControl                      0xb0            /* u16 */
183 #define trigControl                     0xb2            /* u16 */
184 #define calEeprom                       0xb8            /* u16 */
185 #define acqDigitalMark                  0xba            /* u16 */
186 #define trigDacs                        0xbc            /* u16 */
187 #define dioP2ExpansionIO16Bit(x)        (0xc0 + (x)*2)  /* s16 */
188
189 /* Scan Sequencer programming */
190 #define DAQBOARD2000_SeqStartScanList            0x0011
191 #define DAQBOARD2000_SeqStopScanList             0x0010
192
193 /* Prepare for acquisition */
194 #define DAQBOARD2000_AcqResetScanListFifo        0x0004
195 #define DAQBOARD2000_AcqResetResultsFifo         0x0002
196 #define DAQBOARD2000_AcqResetConfigPipe          0x0001
197
198 /* Acqusition status bits */
199 #define DAQBOARD2000_AcqResultsFIFOMore1Sample   0x0001
200 #define DAQBOARD2000_AcqResultsFIFOHasValidData  0x0002
201 #define DAQBOARD2000_AcqResultsFIFOOverrun       0x0004
202 #define DAQBOARD2000_AcqLogicScanning            0x0008
203 #define DAQBOARD2000_AcqConfigPipeFull           0x0010
204 #define DAQBOARD2000_AcqScanListFIFOEmpty        0x0020
205 #define DAQBOARD2000_AcqAdcNotReady              0x0040
206 #define DAQBOARD2000_ArbitrationFailure          0x0080
207 #define DAQBOARD2000_AcqPacerOverrun             0x0100
208 #define DAQBOARD2000_DacPacerOverrun             0x0200
209 #define DAQBOARD2000_AcqHardwareError            0x01c0
210
211 /* Scan Sequencer programming */
212 #define DAQBOARD2000_SeqStartScanList            0x0011
213 #define DAQBOARD2000_SeqStopScanList             0x0010
214
215 /* Pacer Clock Control */
216 #define DAQBOARD2000_AdcPacerInternal            0x0030
217 #define DAQBOARD2000_AdcPacerExternal            0x0032
218 #define DAQBOARD2000_AdcPacerEnable              0x0031
219 #define DAQBOARD2000_AdcPacerEnableDacPacer      0x0034
220 #define DAQBOARD2000_AdcPacerDisable             0x0030
221 #define DAQBOARD2000_AdcPacerNormalMode          0x0060
222 #define DAQBOARD2000_AdcPacerCompatibilityMode   0x0061
223 #define DAQBOARD2000_AdcPacerInternalOutEnable   0x0008
224 #define DAQBOARD2000_AdcPacerExternalRising      0x0100
225
226 /* DAC status */
227 #define DAQBOARD2000_DacFull                     0x0001
228 #define DAQBOARD2000_RefBusy                     0x0002
229 #define DAQBOARD2000_TrgBusy                     0x0004
230 #define DAQBOARD2000_CalBusy                     0x0008
231 #define DAQBOARD2000_Dac0Busy                    0x0010
232 #define DAQBOARD2000_Dac1Busy                    0x0020
233 #define DAQBOARD2000_Dac2Busy                    0x0040
234 #define DAQBOARD2000_Dac3Busy                    0x0080
235
236 /* DAC control */
237 #define DAQBOARD2000_Dac0Enable                  0x0021
238 #define DAQBOARD2000_Dac1Enable                  0x0031
239 #define DAQBOARD2000_Dac2Enable                  0x0041
240 #define DAQBOARD2000_Dac3Enable                  0x0051
241 #define DAQBOARD2000_DacEnableBit                0x0001
242 #define DAQBOARD2000_Dac0Disable                 0x0020
243 #define DAQBOARD2000_Dac1Disable                 0x0030
244 #define DAQBOARD2000_Dac2Disable                 0x0040
245 #define DAQBOARD2000_Dac3Disable                 0x0050
246 #define DAQBOARD2000_DacResetFifo                0x0004
247 #define DAQBOARD2000_DacPatternDisable           0x0060
248 #define DAQBOARD2000_DacPatternEnable            0x0061
249 #define DAQBOARD2000_DacSelectSignedData         0x0002
250 #define DAQBOARD2000_DacSelectUnsignedData       0x0000
251
252 /* Trigger Control */
253 #define DAQBOARD2000_TrigAnalog                  0x0000
254 #define DAQBOARD2000_TrigTTL                     0x0010
255 #define DAQBOARD2000_TrigTransHiLo               0x0004
256 #define DAQBOARD2000_TrigTransLoHi               0x0000
257 #define DAQBOARD2000_TrigAbove                   0x0000
258 #define DAQBOARD2000_TrigBelow                   0x0004
259 #define DAQBOARD2000_TrigLevelSense              0x0002
260 #define DAQBOARD2000_TrigEdgeSense               0x0000
261 #define DAQBOARD2000_TrigEnable                  0x0001
262 #define DAQBOARD2000_TrigDisable                 0x0000
263
264 /* Reference Dac Selection */
265 #define DAQBOARD2000_PosRefDacSelect             0x0100
266 #define DAQBOARD2000_NegRefDacSelect             0x0000
267
268 struct daq200_boardtype {
269         const char *name;
270         int id;
271 };
272 static const struct daq200_boardtype boardtypes[] = {
273         {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
274         {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
275 };
276
277 struct daqboard2000_private {
278         enum {
279                 card_daqboard_2000
280         } card;
281         void __iomem *daq;
282         void __iomem *plx;
283         unsigned int ao_readback[2];
284 };
285
286 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
287 {
288         struct daqboard2000_private *devpriv = dev->private;
289
290         /* udelay(4); */
291         writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
292         /* udelay(4); */
293         writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
294 }
295
296 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
297 {
298         u16 word0, word1, word2, word3;
299
300         /* Channel 0-7 diff, channel 8-23 single ended */
301         word0 = 0;
302         word1 = 0x0004;         /* Last scan */
303         word2 = (chan << 6) & 0x00c0;
304         switch (chan / 4) {
305         case 0:
306                 word3 = 0x0001;
307                 break;
308         case 1:
309                 word3 = 0x0002;
310                 break;
311         case 2:
312                 word3 = 0x0005;
313                 break;
314         case 3:
315                 word3 = 0x0006;
316                 break;
317         case 4:
318                 word3 = 0x0041;
319                 break;
320         case 5:
321                 word3 = 0x0042;
322                 break;
323         default:
324                 word3 = 0;
325                 break;
326         }
327 /*
328   dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
329   dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
330 */
331         /* These should be read from EEPROM */
332         word2 |= 0x0800;
333         word3 |= 0xc000;
334         writeAcqScanListEntry(dev, word0);
335         writeAcqScanListEntry(dev, word1);
336         writeAcqScanListEntry(dev, word2);
337         writeAcqScanListEntry(dev, word3);
338 }
339
340 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
341                                      struct comedi_subdevice *s,
342                                      struct comedi_insn *insn,
343                                      unsigned int *data)
344 {
345         struct daqboard2000_private *devpriv = dev->private;
346         unsigned int val;
347         int gain, chan, timeout;
348         int i;
349
350         writew(DAQBOARD2000_AcqResetScanListFifo |
351                DAQBOARD2000_AcqResetResultsFifo |
352                DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
353
354         /*
355          * If pacer clock is not set to some high value (> 10 us), we
356          * risk multiple samples to be put into the result FIFO.
357          */
358         /* 1 second, should be long enough */
359         writel(1000000, devpriv->daq + acqPacerClockDivLow);
360         writew(0, devpriv->daq + acqPacerClockDivHigh);
361
362         gain = CR_RANGE(insn->chanspec);
363         chan = CR_CHAN(insn->chanspec);
364
365         /* This doesn't look efficient.  I decided to take the conservative
366          * approach when I did the insn conversion.  Perhaps it would be
367          * better to have broken it completely, then someone would have been
368          * forced to fix it.  --ds */
369         for (i = 0; i < insn->n; i++) {
370                 setup_sampling(dev, chan, gain);
371                 /* Enable reading from the scanlist FIFO */
372                 writew(DAQBOARD2000_SeqStartScanList,
373                        devpriv->daq + acqControl);
374                 for (timeout = 0; timeout < 20; timeout++) {
375                         val = readw(devpriv->daq + acqControl);
376                         if (val & DAQBOARD2000_AcqConfigPipeFull)
377                                 break;
378                         /* udelay(2); */
379                 }
380                 writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
381                 for (timeout = 0; timeout < 20; timeout++) {
382                         val = readw(devpriv->daq + acqControl);
383                         if (val & DAQBOARD2000_AcqLogicScanning)
384                                 break;
385                         /* udelay(2); */
386                 }
387                 for (timeout = 0; timeout < 20; timeout++) {
388                         val = readw(devpriv->daq + acqControl);
389                         if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
390                                 break;
391                         /* udelay(2); */
392                 }
393                 data[i] = readw(devpriv->daq + acqResultsFIFO);
394                 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
395                 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
396         }
397
398         return i;
399 }
400
401 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
402                                      struct comedi_subdevice *s,
403                                      struct comedi_insn *insn,
404                                      unsigned int *data)
405 {
406         struct daqboard2000_private *devpriv = dev->private;
407         int chan = CR_CHAN(insn->chanspec);
408         int i;
409
410         for (i = 0; i < insn->n; i++)
411                 data[i] = devpriv->ao_readback[chan];
412
413         return i;
414 }
415
416 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
417                                       struct comedi_subdevice *s,
418                                       struct comedi_insn *insn,
419                                       unsigned int *data)
420 {
421         struct daqboard2000_private *devpriv = dev->private;
422         int chan = CR_CHAN(insn->chanspec);
423         unsigned int val;
424         int timeout;
425         int i;
426
427         for (i = 0; i < insn->n; i++) {
428 #if 0
429                 /*
430                  * OK, since it works OK without enabling the DAC's,
431                  * let's keep it as simple as possible...
432                  */
433                 writew((chan + 2) * 0x0010 | 0x0001,
434                        devpriv->daq + dacControl);
435                 udelay(1000);
436 #endif
437                 writew(data[i], devpriv->daq + dacSetting(chan));
438                 for (timeout = 0; timeout < 20; timeout++) {
439                         val = readw(devpriv->daq + dacControl);
440                         if ((val & ((chan + 1) * 0x0010)) == 0)
441                                 break;
442                         /* udelay(2); */
443                 }
444                 devpriv->ao_readback[chan] = data[i];
445 #if 0
446                 /*
447                  * Since we never enabled the DAC's, we don't need
448                  * to disable it...
449                  */
450                 writew((chan + 2) * 0x0010 | 0x0000,
451                        devpriv->daq + dacControl);
452                 udelay(1000);
453 #endif
454         }
455
456         return i;
457 }
458
459 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
460 {
461         struct daqboard2000_private *devpriv = dev->private;
462
463         writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
464         udelay(10000);
465         writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
466         udelay(10000);
467 }
468
469 static void daqboard2000_reloadPLX(struct comedi_device *dev)
470 {
471         struct daqboard2000_private *devpriv = dev->private;
472
473         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
474         udelay(10000);
475         writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
476         udelay(10000);
477         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
478         udelay(10000);
479 }
480
481 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
482 {
483         struct daqboard2000_private *devpriv = dev->private;
484
485         writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
486         udelay(10000);
487         writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
488         udelay(10000);          /* Not in the original code, but I like symmetry... */
489 }
490
491 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
492 {
493         struct daqboard2000_private *devpriv = dev->private;
494         int result = 0;
495         int i;
496         int cpld;
497
498         /* timeout after 50 tries -> 5ms */
499         for (i = 0; i < 50; i++) {
500                 cpld = readw(devpriv->daq + 0x1000);
501                 if ((cpld & mask) == mask) {
502                         result = 1;
503                         break;
504                 }
505                 udelay(100);
506         }
507         udelay(5);
508         return result;
509 }
510
511 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
512 {
513         struct daqboard2000_private *devpriv = dev->private;
514         int result = 0;
515
516         udelay(10);
517         writew(data, devpriv->daq + 0x1000);
518         if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
519             DAQBOARD2000_CPLD_INIT) {
520                 result = 1;
521         }
522         return result;
523 }
524
525 static int initialize_daqboard2000(struct comedi_device *dev,
526                                    const u8 *cpld_array, size_t len)
527 {
528         struct daqboard2000_private *devpriv = dev->private;
529         int result = -EIO;
530         /* Read the serial EEPROM control register */
531         int secr;
532         int retry;
533         size_t i;
534
535         /* Check to make sure the serial eeprom is present on the board */
536         secr = readl(devpriv->plx + 0x6c);
537         if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
538                 return -EIO;
539
540         for (retry = 0; retry < 3; retry++) {
541                 daqboard2000_resetLocalBus(dev);
542                 daqboard2000_reloadPLX(dev);
543                 daqboard2000_pulseProgPin(dev);
544                 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
545                         for (i = 0; i < len; i++) {
546                                 if (cpld_array[i] == 0xff &&
547                                     cpld_array[i + 1] == 0x20)
548                                         break;
549                         }
550                         for (; i < len; i += 2) {
551                                 int data =
552                                     (cpld_array[i] << 8) + cpld_array[i + 1];
553                                 if (!daqboard2000_writeCPLD(dev, data))
554                                         break;
555                         }
556                         if (i >= len) {
557                                 daqboard2000_resetLocalBus(dev);
558                                 daqboard2000_reloadPLX(dev);
559                                 result = 0;
560                                 break;
561                         }
562                 }
563         }
564         return result;
565 }
566
567 static int daqboard2000_upload_firmware(struct comedi_device *dev)
568 {
569         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
570         const struct firmware *fw;
571         int ret;
572
573         ret = request_firmware(&fw, DAQBOARD2000_FIRMWARE, &pcidev->dev);
574         if (ret)
575                 return ret;
576
577         ret = initialize_daqboard2000(dev, fw->data, fw->size);
578         release_firmware(fw);
579
580         return ret;
581 }
582
583 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
584 {
585 }
586
587 static void daqboard2000_adcDisarm(struct comedi_device *dev)
588 {
589         struct daqboard2000_private *devpriv = dev->private;
590
591         /* Disable hardware triggers */
592         udelay(2);
593         writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
594                devpriv->daq + trigControl);
595         udelay(2);
596         writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
597                devpriv->daq + trigControl);
598
599         /* Stop the scan list FIFO from loading the configuration pipe */
600         udelay(2);
601         writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
602
603         /* Stop the pacer clock */
604         udelay(2);
605         writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
606
607         /* Stop the input dma (abort channel 1) */
608         daqboard2000_adcStopDmaTransfer(dev);
609 }
610
611 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
612 {
613         struct daqboard2000_private *devpriv = dev->private;
614         unsigned int val;
615         int timeout;
616
617         /*  Set the + reference dac value in the FPGA */
618         writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
619         for (timeout = 0; timeout < 20; timeout++) {
620                 val = readw(devpriv->daq + dacControl);
621                 if ((val & DAQBOARD2000_RefBusy) == 0)
622                         break;
623                 udelay(2);
624         }
625
626         /*  Set the - reference dac value in the FPGA */
627         writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
628         for (timeout = 0; timeout < 20; timeout++) {
629                 val = readw(devpriv->daq + dacControl);
630                 if ((val & DAQBOARD2000_RefBusy) == 0)
631                         break;
632                 udelay(2);
633         }
634 }
635
636 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
637 {
638 }
639
640 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
641 {
642 }
643
644 static void daqboard2000_dacDisarm(struct comedi_device *dev)
645 {
646 }
647
648 static void daqboard2000_initializeAdc(struct comedi_device *dev)
649 {
650         daqboard2000_adcDisarm(dev);
651         daqboard2000_activateReferenceDacs(dev);
652         daqboard2000_initializeCtrs(dev);
653         daqboard2000_initializeTmrs(dev);
654 }
655
656 static void daqboard2000_initializeDac(struct comedi_device *dev)
657 {
658         daqboard2000_dacDisarm(dev);
659 }
660
661 static int daqboard2000_8255_cb(int dir, int port, int data,
662                                 unsigned long ioaddr)
663 {
664         void __iomem *mmio_base = (void __iomem *)ioaddr;
665
666         if (dir) {
667                 writew(data, mmio_base + port * 2);
668                 return 0;
669         } else {
670                 return readw(mmio_base + port * 2);
671         }
672 }
673
674 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
675                                                struct pci_dev *pcidev)
676 {
677         const struct daq200_boardtype *board;
678         int i;
679
680         if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
681                 return NULL;
682
683         for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
684                 board = &boardtypes[i];
685                 if (pcidev->subsystem_device == board->id)
686                         return board;
687         }
688         return NULL;
689 }
690
691 static int __devinit daqboard2000_auto_attach(struct comedi_device *dev,
692                                               unsigned long context_unused)
693 {
694         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
695         const struct daq200_boardtype *board;
696         struct daqboard2000_private *devpriv;
697         struct comedi_subdevice *s;
698         int result;
699
700         board = daqboard2000_find_boardinfo(dev, pcidev);
701         if (!board)
702                 return -ENODEV;
703         dev->board_ptr = board;
704         dev->board_name = board->name;
705
706         devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
707         if (!devpriv)
708                 return -ENOMEM;
709         dev->private = devpriv;
710
711         result = comedi_pci_enable(pcidev, dev->driver->driver_name);
712         if (result < 0)
713                 return result;
714         dev->iobase = 1;        /* the "detach" needs this */
715
716         devpriv->plx = ioremap(pci_resource_start(pcidev, 0),
717                                pci_resource_len(pcidev, 0));
718         devpriv->daq = ioremap(pci_resource_start(pcidev, 2),
719                                pci_resource_len(pcidev, 2));
720         if (!devpriv->plx || !devpriv->daq)
721                 return -ENOMEM;
722
723         result = comedi_alloc_subdevices(dev, 3);
724         if (result)
725                 return result;
726
727         readl(devpriv->plx + 0x6c);
728
729         result = daqboard2000_upload_firmware(dev);
730         if (result < 0)
731                 return result;
732
733         daqboard2000_initializeAdc(dev);
734         daqboard2000_initializeDac(dev);
735
736         s = &dev->subdevices[0];
737         /* ai subdevice */
738         s->type = COMEDI_SUBD_AI;
739         s->subdev_flags = SDF_READABLE | SDF_GROUND;
740         s->n_chan = 24;
741         s->maxdata = 0xffff;
742         s->insn_read = daqboard2000_ai_insn_read;
743         s->range_table = &range_daqboard2000_ai;
744
745         s = &dev->subdevices[1];
746         /* ao subdevice */
747         s->type = COMEDI_SUBD_AO;
748         s->subdev_flags = SDF_WRITABLE;
749         s->n_chan = 2;
750         s->maxdata = 0xffff;
751         s->insn_read = daqboard2000_ao_insn_read;
752         s->insn_write = daqboard2000_ao_insn_write;
753         s->range_table = &range_bipolar10;
754
755         s = &dev->subdevices[2];
756         result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
757                         (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
758         if (result)
759                 return result;
760
761         dev_info(dev->class_dev, "%s: %s attached\n",
762                 dev->driver->driver_name, dev->board_name);
763
764         return 0;
765 }
766
767 static void daqboard2000_detach(struct comedi_device *dev)
768 {
769         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
770         struct daqboard2000_private *devpriv = dev->private;
771
772         if (dev->subdevices)
773                 subdev_8255_cleanup(dev, &dev->subdevices[2]);
774         if (dev->irq)
775                 free_irq(dev->irq, dev);
776         if (devpriv) {
777                 if (devpriv->daq)
778                         iounmap(devpriv->daq);
779                 if (devpriv->plx)
780                         iounmap(devpriv->plx);
781         }
782         if (pcidev) {
783                 if (dev->iobase)
784                         comedi_pci_disable(pcidev);
785                 pci_dev_put(pcidev);
786         }
787 }
788
789 static struct comedi_driver daqboard2000_driver = {
790         .driver_name    = "daqboard2000",
791         .module         = THIS_MODULE,
792         .auto_attach    = daqboard2000_auto_attach,
793         .detach         = daqboard2000_detach,
794 };
795
796 static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
797                                             const struct pci_device_id *ent)
798 {
799         return comedi_pci_auto_config(dev, &daqboard2000_driver);
800 }
801
802 static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
803 {
804         comedi_pci_auto_unconfig(dev);
805 }
806
807 static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
808         { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
809         { 0 }
810 };
811 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
812
813 static struct pci_driver daqboard2000_pci_driver = {
814         .name           = "daqboard2000",
815         .id_table       = daqboard2000_pci_table,
816         .probe          = daqboard2000_pci_probe,
817         .remove         = __devexit_p(daqboard2000_pci_remove),
818 };
819 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
820
821 MODULE_AUTHOR("Comedi http://www.comedi.org");
822 MODULE_DESCRIPTION("Comedi low-level driver");
823 MODULE_LICENSE("GPL");
824 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);