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