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