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