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