Version:  2.0.40 2.2.26 2.4.37 3.1 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

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         unsigned int ao_readback[2];
279 };
280 
281 static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
282 {
283         /* udelay(4); */
284         writew(entry & 0x00ff, dev->mmio + acqScanListFIFO);
285         /* udelay(4); */
286         writew((entry >> 8) & 0x00ff, dev->mmio + acqScanListFIFO);
287 }
288 
289 static void setup_sampling(struct comedi_device *dev, int chan, int gain)
290 {
291         u16 word0, word1, word2, word3;
292 
293         /* Channel 0-7 diff, channel 8-23 single ended */
294         word0 = 0;
295         word1 = 0x0004;         /* Last scan */
296         word2 = (chan << 6) & 0x00c0;
297         switch (chan / 4) {
298         case 0:
299                 word3 = 0x0001;
300                 break;
301         case 1:
302                 word3 = 0x0002;
303                 break;
304         case 2:
305                 word3 = 0x0005;
306                 break;
307         case 3:
308                 word3 = 0x0006;
309                 break;
310         case 4:
311                 word3 = 0x0041;
312                 break;
313         case 5:
314                 word3 = 0x0042;
315                 break;
316         default:
317                 word3 = 0;
318                 break;
319         }
320 /*
321   dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
322   dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
323 */
324         /* These should be read from EEPROM */
325         word2 |= 0x0800;
326         word3 |= 0xc000;
327         writeAcqScanListEntry(dev, word0);
328         writeAcqScanListEntry(dev, word1);
329         writeAcqScanListEntry(dev, word2);
330         writeAcqScanListEntry(dev, word3);
331 }
332 
333 static int daqboard2000_ai_status(struct comedi_device *dev,
334                                   struct comedi_subdevice *s,
335                                   struct comedi_insn *insn,
336                                   unsigned long context)
337 {
338         unsigned int status;
339 
340         status = readw(dev->mmio + acqControl);
341         if (status & context)
342                 return 0;
343         return -EBUSY;
344 }
345 
346 static int daqboard2000_ai_insn_read(struct comedi_device *dev,
347                                      struct comedi_subdevice *s,
348                                      struct comedi_insn *insn,
349                                      unsigned int *data)
350 {
351         int gain, chan;
352         int ret;
353         int i;
354 
355         writew(DAQBOARD2000_AcqResetScanListFifo |
356                DAQBOARD2000_AcqResetResultsFifo |
357                DAQBOARD2000_AcqResetConfigPipe, dev->mmio + acqControl);
358 
359         /*
360          * If pacer clock is not set to some high value (> 10 us), we
361          * risk multiple samples to be put into the result FIFO.
362          */
363         /* 1 second, should be long enough */
364         writel(1000000, dev->mmio + acqPacerClockDivLow);
365         writew(0, dev->mmio + acqPacerClockDivHigh);
366 
367         gain = CR_RANGE(insn->chanspec);
368         chan = CR_CHAN(insn->chanspec);
369 
370         /* This doesn't look efficient.  I decided to take the conservative
371          * approach when I did the insn conversion.  Perhaps it would be
372          * better to have broken it completely, then someone would have been
373          * forced to fix it.  --ds */
374         for (i = 0; i < insn->n; i++) {
375                 setup_sampling(dev, chan, gain);
376                 /* Enable reading from the scanlist FIFO */
377                 writew(DAQBOARD2000_SeqStartScanList, dev->mmio + acqControl);
378 
379                 ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
380                                      DAQBOARD2000_AcqConfigPipeFull);
381                 if (ret)
382                         return ret;
383 
384                 writew(DAQBOARD2000_AdcPacerEnable, dev->mmio + acqControl);
385 
386                 ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
387                                      DAQBOARD2000_AcqLogicScanning);
388                 if (ret)
389                         return ret;
390 
391                 ret = comedi_timeout(dev, s, insn, daqboard2000_ai_status,
392                                      DAQBOARD2000_AcqResultsFIFOHasValidData);
393                 if (ret)
394                         return ret;
395 
396                 data[i] = readw(dev->mmio + acqResultsFIFO);
397                 writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
398                 writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
399         }
400 
401         return i;
402 }
403 
404 static int daqboard2000_ao_insn_read(struct comedi_device *dev,
405                                      struct comedi_subdevice *s,
406                                      struct comedi_insn *insn,
407                                      unsigned int *data)
408 {
409         struct daqboard2000_private *devpriv = dev->private;
410         int chan = CR_CHAN(insn->chanspec);
411         int i;
412 
413         for (i = 0; i < insn->n; i++)
414                 data[i] = devpriv->ao_readback[chan];
415 
416         return i;
417 }
418 
419 static int daqboard2000_ao_eoc(struct comedi_device *dev,
420                                struct comedi_subdevice *s,
421                                struct comedi_insn *insn,
422                                unsigned long context)
423 {
424         unsigned int chan = CR_CHAN(insn->chanspec);
425         unsigned int status;
426 
427         status = readw(dev->mmio + dacControl);
428         if ((status & ((chan + 1) * 0x0010)) == 0)
429                 return 0;
430         return -EBUSY;
431 }
432 
433 static int daqboard2000_ao_insn_write(struct comedi_device *dev,
434                                       struct comedi_subdevice *s,
435                                       struct comedi_insn *insn,
436                                       unsigned int *data)
437 {
438         struct daqboard2000_private *devpriv = dev->private;
439         int chan = CR_CHAN(insn->chanspec);
440         int ret;
441         int i;
442 
443         for (i = 0; i < insn->n; i++) {
444 #if 0
445                 /*
446                  * OK, since it works OK without enabling the DAC's,
447                  * let's keep it as simple as possible...
448                  */
449                 writew((chan + 2) * 0x0010 | 0x0001, dev->mmio + dacControl);
450                 udelay(1000);
451 #endif
452                 writew(data[i], dev->mmio + dacSetting(chan));
453 
454                 ret = comedi_timeout(dev, s, insn, daqboard2000_ao_eoc, 0);
455                 if (ret)
456                         return ret;
457 
458                 devpriv->ao_readback[chan] = data[i];
459 #if 0
460                 /*
461                  * Since we never enabled the DAC's, we don't need
462                  * to disable it...
463                  */
464                 writew((chan + 2) * 0x0010 | 0x0000, dev->mmio + dacControl);
465                 udelay(1000);
466 #endif
467         }
468 
469         return i;
470 }
471 
472 static void daqboard2000_resetLocalBus(struct comedi_device *dev)
473 {
474         struct daqboard2000_private *devpriv = dev->private;
475 
476         writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
477         mdelay(10);
478         writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
479         mdelay(10);
480 }
481 
482 static void daqboard2000_reloadPLX(struct comedi_device *dev)
483 {
484         struct daqboard2000_private *devpriv = dev->private;
485 
486         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
487         mdelay(10);
488         writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
489         mdelay(10);
490         writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
491         mdelay(10);
492 }
493 
494 static void daqboard2000_pulseProgPin(struct comedi_device *dev)
495 {
496         struct daqboard2000_private *devpriv = dev->private;
497 
498         writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
499         mdelay(10);
500         writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
501         mdelay(10);     /* Not in the original code, but I like symmetry... */
502 }
503 
504 static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
505 {
506         int result = 0;
507         int i;
508         int cpld;
509 
510         /* timeout after 50 tries -> 5ms */
511         for (i = 0; i < 50; i++) {
512                 cpld = readw(dev->mmio + 0x1000);
513                 if ((cpld & mask) == mask) {
514                         result = 1;
515                         break;
516                 }
517                 udelay(100);
518         }
519         udelay(5);
520         return result;
521 }
522 
523 static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
524 {
525         int result = 0;
526 
527         udelay(10);
528         writew(data, dev->mmio + 0x1000);
529         if ((readw(dev->mmio + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
530             DAQBOARD2000_CPLD_INIT) {
531                 result = 1;
532         }
533         return result;
534 }
535 
536 static int initialize_daqboard2000(struct comedi_device *dev,
537                                    const u8 *cpld_array, size_t len,
538                                    unsigned long context)
539 {
540         struct daqboard2000_private *devpriv = dev->private;
541         int result = -EIO;
542         /* Read the serial EEPROM control register */
543         int secr;
544         int retry;
545         size_t i;
546 
547         /* Check to make sure the serial eeprom is present on the board */
548         secr = readl(devpriv->plx + 0x6c);
549         if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
550                 return -EIO;
551 
552         for (retry = 0; retry < 3; retry++) {
553                 daqboard2000_resetLocalBus(dev);
554                 daqboard2000_reloadPLX(dev);
555                 daqboard2000_pulseProgPin(dev);
556                 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
557                         for (i = 0; i < len; i++) {
558                                 if (cpld_array[i] == 0xff &&
559                                     cpld_array[i + 1] == 0x20)
560                                         break;
561                         }
562                         for (; i < len; i += 2) {
563                                 int data =
564                                     (cpld_array[i] << 8) + cpld_array[i + 1];
565                                 if (!daqboard2000_writeCPLD(dev, data))
566                                         break;
567                         }
568                         if (i >= len) {
569                                 daqboard2000_resetLocalBus(dev);
570                                 daqboard2000_reloadPLX(dev);
571                                 result = 0;
572                                 break;
573                         }
574                 }
575         }
576         return result;
577 }
578 
579 static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
580 {
581 }
582 
583 static void daqboard2000_adcDisarm(struct comedi_device *dev)
584 {
585         /* Disable hardware triggers */
586         udelay(2);
587         writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
588                dev->mmio + trigControl);
589         udelay(2);
590         writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
591                dev->mmio + trigControl);
592 
593         /* Stop the scan list FIFO from loading the configuration pipe */
594         udelay(2);
595         writew(DAQBOARD2000_SeqStopScanList, dev->mmio + acqControl);
596 
597         /* Stop the pacer clock */
598         udelay(2);
599         writew(DAQBOARD2000_AdcPacerDisable, dev->mmio + acqControl);
600 
601         /* Stop the input dma (abort channel 1) */
602         daqboard2000_adcStopDmaTransfer(dev);
603 }
604 
605 static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
606 {
607         unsigned int val;
608         int timeout;
609 
610         /*  Set the + reference dac value in the FPGA */
611         writew(0x80 | DAQBOARD2000_PosRefDacSelect, dev->mmio + refDacs);
612         for (timeout = 0; timeout < 20; timeout++) {
613                 val = readw(dev->mmio + dacControl);
614                 if ((val & DAQBOARD2000_RefBusy) == 0)
615                         break;
616                 udelay(2);
617         }
618 
619         /*  Set the - reference dac value in the FPGA */
620         writew(0x80 | DAQBOARD2000_NegRefDacSelect, dev->mmio + refDacs);
621         for (timeout = 0; timeout < 20; timeout++) {
622                 val = readw(dev->mmio + dacControl);
623                 if ((val & DAQBOARD2000_RefBusy) == 0)
624                         break;
625                 udelay(2);
626         }
627 }
628 
629 static void daqboard2000_initializeCtrs(struct comedi_device *dev)
630 {
631 }
632 
633 static void daqboard2000_initializeTmrs(struct comedi_device *dev)
634 {
635 }
636 
637 static void daqboard2000_dacDisarm(struct comedi_device *dev)
638 {
639 }
640 
641 static void daqboard2000_initializeAdc(struct comedi_device *dev)
642 {
643         daqboard2000_adcDisarm(dev);
644         daqboard2000_activateReferenceDacs(dev);
645         daqboard2000_initializeCtrs(dev);
646         daqboard2000_initializeTmrs(dev);
647 }
648 
649 static void daqboard2000_initializeDac(struct comedi_device *dev)
650 {
651         daqboard2000_dacDisarm(dev);
652 }
653 
654 static int daqboard2000_8255_cb(int dir, int port, int data,
655                                 unsigned long ioaddr)
656 {
657         void __iomem *mmio_base = (void __iomem *)ioaddr;
658 
659         if (dir) {
660                 writew(data, mmio_base + port * 2);
661                 return 0;
662         }
663         return readw(mmio_base + port * 2);
664 }
665 
666 static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
667                                                struct pci_dev *pcidev)
668 {
669         const struct daq200_boardtype *board;
670         int i;
671 
672         if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
673                 return NULL;
674 
675         for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
676                 board = &boardtypes[i];
677                 if (pcidev->subsystem_device == board->id)
678                         return board;
679         }
680         return NULL;
681 }
682 
683 static int daqboard2000_auto_attach(struct comedi_device *dev,
684                                               unsigned long context_unused)
685 {
686         struct pci_dev *pcidev = comedi_to_pci_dev(dev);
687         const struct daq200_boardtype *board;
688         struct daqboard2000_private *devpriv;
689         struct comedi_subdevice *s;
690         int result;
691 
692         board = daqboard2000_find_boardinfo(dev, pcidev);
693         if (!board)
694                 return -ENODEV;
695         dev->board_ptr = board;
696         dev->board_name = board->name;
697 
698         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
699         if (!devpriv)
700                 return -ENOMEM;
701 
702         result = comedi_pci_enable(dev);
703         if (result)
704                 return result;
705 
706         devpriv->plx = pci_ioremap_bar(pcidev, 0);
707         dev->mmio = pci_ioremap_bar(pcidev, 2);
708         if (!devpriv->plx || !dev->mmio)
709                 return -ENOMEM;
710 
711         result = comedi_alloc_subdevices(dev, 3);
712         if (result)
713                 return result;
714 
715         readl(devpriv->plx + 0x6c);
716 
717         result = comedi_load_firmware(dev, &comedi_to_pci_dev(dev)->dev,
718                                       DAQBOARD2000_FIRMWARE,
719                                       initialize_daqboard2000, 0);
720         if (result < 0)
721                 return result;
722 
723         daqboard2000_initializeAdc(dev);
724         daqboard2000_initializeDac(dev);
725 
726         s = &dev->subdevices[0];
727         /* ai subdevice */
728         s->type = COMEDI_SUBD_AI;
729         s->subdev_flags = SDF_READABLE | SDF_GROUND;
730         s->n_chan = 24;
731         s->maxdata = 0xffff;
732         s->insn_read = daqboard2000_ai_insn_read;
733         s->range_table = &range_daqboard2000_ai;
734 
735         s = &dev->subdevices[1];
736         /* ao subdevice */
737         s->type = COMEDI_SUBD_AO;
738         s->subdev_flags = SDF_WRITABLE;
739         s->n_chan = 2;
740         s->maxdata = 0xffff;
741         s->insn_read = daqboard2000_ao_insn_read;
742         s->insn_write = daqboard2000_ao_insn_write;
743         s->range_table = &range_bipolar10;
744 
745         s = &dev->subdevices[2];
746         result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
747                         (unsigned long)(dev->mmio + dioP2ExpansionIO8Bit));
748         if (result)
749                 return result;
750 
751         return 0;
752 }
753 
754 static void daqboard2000_detach(struct comedi_device *dev)
755 {
756         struct daqboard2000_private *devpriv = dev->private;
757 
758         if (dev->irq)
759                 free_irq(dev->irq, dev);
760         if (devpriv) {
761                 if (dev->mmio)
762                         iounmap(dev->mmio);
763                 if (devpriv->plx)
764                         iounmap(devpriv->plx);
765         }
766         comedi_pci_disable(dev);
767 }
768 
769 static struct comedi_driver daqboard2000_driver = {
770         .driver_name    = "daqboard2000",
771         .module         = THIS_MODULE,
772         .auto_attach    = daqboard2000_auto_attach,
773         .detach         = daqboard2000_detach,
774 };
775 
776 static int daqboard2000_pci_probe(struct pci_dev *dev,
777                                   const struct pci_device_id *id)
778 {
779         return comedi_pci_auto_config(dev, &daqboard2000_driver,
780                                       id->driver_data);
781 }
782 
783 static const struct pci_device_id daqboard2000_pci_table[] = {
784         { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
785         { 0 }
786 };
787 MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
788 
789 static struct pci_driver daqboard2000_pci_driver = {
790         .name           = "daqboard2000",
791         .id_table       = daqboard2000_pci_table,
792         .probe          = daqboard2000_pci_probe,
793         .remove         = comedi_pci_auto_unconfig,
794 };
795 module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
796 
797 MODULE_AUTHOR("Comedi http://www.comedi.org");
798 MODULE_DESCRIPTION("Comedi low-level driver");
799 MODULE_LICENSE("GPL");
800 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
801 

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