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