Version:  2.0.40 2.2.26 2.4.37 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18

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

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us