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