staging: comedi: daqboard2000: remove struct daqboard2000_hw
[linux-2.6-block.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
f34ec800
HS
123#define PCI_VENDOR_ID_IOTECH 0x1616
124
125#define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
126#define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
48a4d521
AB
127
128#define DAQBOARD2000_DAQ_SIZE 0x1002
129#define DAQBOARD2000_PLX_SIZE 0x100
130
2696fb57 131/* Initialization bits for the Serial EEPROM Control Register */
48a4d521
AB
132#define DAQBOARD2000_SECRProgPinHi 0x8001767e
133#define DAQBOARD2000_SECRProgPinLo 0x8000767e
134#define DAQBOARD2000_SECRLocalBusHi 0xc000767e
135#define DAQBOARD2000_SECRLocalBusLo 0x8000767e
136#define DAQBOARD2000_SECRReloadHi 0xa000767e
137#define DAQBOARD2000_SECRReloadLo 0x8000767e
138
2696fb57 139/* SECR status bits */
48a4d521
AB
140#define DAQBOARD2000_EEPROM_PRESENT 0x10000000
141
2696fb57 142/* CPLD status bits */
48a4d521
AB
143#define DAQBOARD2000_CPLD_INIT 0x0002
144#define DAQBOARD2000_CPLD_DONE 0x0004
145
2696fb57 146/* Available ranges */
9ced1de6 147static const struct comedi_lrange range_daqboard2000_ai = { 13, {
0a85b6f0
MT
148 RANGE(-10, 10),
149 RANGE(-5, 5),
150 RANGE(-2.5,
151 2.5),
152 RANGE(-1.25,
153 1.25),
154 RANGE(-0.625,
155 0.625),
156 RANGE(-0.3125,
157 0.3125),
158 RANGE(-0.156,
159 0.156),
160 RANGE(0, 10),
161 RANGE(0, 5),
162 RANGE(0, 2.5),
163 RANGE(0, 1.25),
164 RANGE(0,
165 0.625),
166 RANGE(0,
167 0.3125)
168 }
48a4d521
AB
169};
170
9ced1de6 171static const struct comedi_lrange range_daqboard2000_ao = { 1, {
0a85b6f0
MT
172 RANGE(-10, 10)
173 }
48a4d521
AB
174};
175
f657b14a
HS
176/*
177 * Register Memory Map
178 */
179#define acqControl 0x00 /* u16 */
180#define acqScanListFIFO 0x02 /* u16 */
181#define acqPacerClockDivLow 0x04 /* u32 */
182#define acqScanCounter 0x08 /* u16 */
183#define acqPacerClockDivHigh 0x0a /* u16 */
184#define acqTriggerCount 0x0c /* u16 */
185#define acqResultsFIFO 0x10 /* u16 */
186#define acqResultsShadow 0x14 /* u16 */
187#define acqAdcResult 0x18 /* u16 */
188#define dacScanCounter 0x1c /* u16 */
189#define dacControl 0x20 /* u16 */
190#define dacFIFO 0x24 /* s16 */
191#define dacPacerClockDiv 0x2a /* u16 */
192#define refDacs 0x2c /* u16 */
193#define dioControl 0x30 /* u16 */
194#define dioP3hsioData 0x32 /* s16 */
195#define dioP3Control 0x34 /* u16 */
196#define calEepromControl 0x36 /* u16 */
197#define dacSetting(x) (0x38 + (x)*2) /* s16 */
198#define dioP2ExpansionIO8Bit 0x40 /* s16 */
199#define ctrTmrControl 0x80 /* u16 */
200#define ctrInput(x) (0x88 + (x)*2) /* s16 */
201#define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
202#define dmaControl 0xb0 /* u16 */
203#define trigControl 0xb2 /* u16 */
204#define calEeprom 0xb8 /* u16 */
205#define acqDigitalMark 0xba /* u16 */
206#define trigDacs 0xbc /* u16 */
207#define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
48a4d521
AB
208
209/* Scan Sequencer programming */
210#define DAQBOARD2000_SeqStartScanList 0x0011
211#define DAQBOARD2000_SeqStopScanList 0x0010
212
2696fb57 213/* Prepare for acquisition */
48a4d521
AB
214#define DAQBOARD2000_AcqResetScanListFifo 0x0004
215#define DAQBOARD2000_AcqResetResultsFifo 0x0002
216#define DAQBOARD2000_AcqResetConfigPipe 0x0001
217
2696fb57 218/* Acqusition status bits */
48a4d521
AB
219#define DAQBOARD2000_AcqResultsFIFOMore1Sample 0x0001
220#define DAQBOARD2000_AcqResultsFIFOHasValidData 0x0002
221#define DAQBOARD2000_AcqResultsFIFOOverrun 0x0004
222#define DAQBOARD2000_AcqLogicScanning 0x0008
223#define DAQBOARD2000_AcqConfigPipeFull 0x0010
224#define DAQBOARD2000_AcqScanListFIFOEmpty 0x0020
225#define DAQBOARD2000_AcqAdcNotReady 0x0040
226#define DAQBOARD2000_ArbitrationFailure 0x0080
227#define DAQBOARD2000_AcqPacerOverrun 0x0100
228#define DAQBOARD2000_DacPacerOverrun 0x0200
229#define DAQBOARD2000_AcqHardwareError 0x01c0
230
2696fb57 231/* Scan Sequencer programming */
48a4d521
AB
232#define DAQBOARD2000_SeqStartScanList 0x0011
233#define DAQBOARD2000_SeqStopScanList 0x0010
234
235/* Pacer Clock Control */
236#define DAQBOARD2000_AdcPacerInternal 0x0030
237#define DAQBOARD2000_AdcPacerExternal 0x0032
238#define DAQBOARD2000_AdcPacerEnable 0x0031
239#define DAQBOARD2000_AdcPacerEnableDacPacer 0x0034
240#define DAQBOARD2000_AdcPacerDisable 0x0030
241#define DAQBOARD2000_AdcPacerNormalMode 0x0060
242#define DAQBOARD2000_AdcPacerCompatibilityMode 0x0061
243#define DAQBOARD2000_AdcPacerInternalOutEnable 0x0008
244#define DAQBOARD2000_AdcPacerExternalRising 0x0100
245
2696fb57 246/* DAC status */
48a4d521
AB
247#define DAQBOARD2000_DacFull 0x0001
248#define DAQBOARD2000_RefBusy 0x0002
249#define DAQBOARD2000_TrgBusy 0x0004
250#define DAQBOARD2000_CalBusy 0x0008
251#define DAQBOARD2000_Dac0Busy 0x0010
252#define DAQBOARD2000_Dac1Busy 0x0020
253#define DAQBOARD2000_Dac2Busy 0x0040
254#define DAQBOARD2000_Dac3Busy 0x0080
255
2696fb57 256/* DAC control */
48a4d521
AB
257#define DAQBOARD2000_Dac0Enable 0x0021
258#define DAQBOARD2000_Dac1Enable 0x0031
259#define DAQBOARD2000_Dac2Enable 0x0041
260#define DAQBOARD2000_Dac3Enable 0x0051
261#define DAQBOARD2000_DacEnableBit 0x0001
262#define DAQBOARD2000_Dac0Disable 0x0020
263#define DAQBOARD2000_Dac1Disable 0x0030
264#define DAQBOARD2000_Dac2Disable 0x0040
265#define DAQBOARD2000_Dac3Disable 0x0050
266#define DAQBOARD2000_DacResetFifo 0x0004
267#define DAQBOARD2000_DacPatternDisable 0x0060
268#define DAQBOARD2000_DacPatternEnable 0x0061
269#define DAQBOARD2000_DacSelectSignedData 0x0002
270#define DAQBOARD2000_DacSelectUnsignedData 0x0000
271
272/* Trigger Control */
273#define DAQBOARD2000_TrigAnalog 0x0000
274#define DAQBOARD2000_TrigTTL 0x0010
275#define DAQBOARD2000_TrigTransHiLo 0x0004
276#define DAQBOARD2000_TrigTransLoHi 0x0000
277#define DAQBOARD2000_TrigAbove 0x0000
278#define DAQBOARD2000_TrigBelow 0x0004
279#define DAQBOARD2000_TrigLevelSense 0x0002
280#define DAQBOARD2000_TrigEdgeSense 0x0000
281#define DAQBOARD2000_TrigEnable 0x0001
282#define DAQBOARD2000_TrigDisable 0x0000
283
2696fb57 284/* Reference Dac Selection */
48a4d521
AB
285#define DAQBOARD2000_PosRefDacSelect 0x0100
286#define DAQBOARD2000_NegRefDacSelect 0x0000
287
9da18ff8 288struct daq200_boardtype {
48a4d521
AB
289 const char *name;
290 int id;
9da18ff8
BP
291};
292static const struct daq200_boardtype boardtypes[] = {
48a4d521
AB
293 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2},
294 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
295};
296
9da18ff8 297#define this_board ((const struct daq200_boardtype *)dev->board_ptr)
48a4d521 298
b3653681 299struct daqboard2000_private {
48a4d521
AB
300 enum {
301 card_daqboard_2000
302 } card;
f657b14a 303 void __iomem *daq;
d73805ce 304 void __iomem *plx;
790c5541 305 unsigned int ao_readback[2];
b3653681 306};
48a4d521 307
b3653681 308#define devpriv ((struct daqboard2000_private *)dev->private)
48a4d521 309
da91b269 310static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
48a4d521 311{
f657b14a
HS
312 /* udelay(4); */
313 writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
314 /* udelay(4); */
315 writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
48a4d521
AB
316}
317
da91b269 318static void setup_sampling(struct comedi_device *dev, int chan, int gain)
48a4d521
AB
319{
320 u16 word0, word1, word2, word3;
321
322 /* Channel 0-7 diff, channel 8-23 single ended */
323 word0 = 0;
324 word1 = 0x0004; /* Last scan */
325 word2 = (chan << 6) & 0x00c0;
326 switch (chan / 4) {
327 case 0:
328 word3 = 0x0001;
329 break;
330 case 1:
331 word3 = 0x0002;
332 break;
333 case 2:
334 word3 = 0x0005;
335 break;
336 case 3:
337 word3 = 0x0006;
338 break;
339 case 4:
340 word3 = 0x0041;
341 break;
342 case 5:
343 word3 = 0x0042;
344 break;
345 default:
346 word3 = 0;
347 break;
348 }
349/*
350 dev->eeprom.correctionDACSE[i][j][k].offset = 0x800;
351 dev->eeprom.correctionDACSE[i][j][k].gain = 0xc00;
352*/
353 /* These should be read from EEPROM */
354 word2 |= 0x0800;
355 word3 |= 0xc000;
356/* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
357 writeAcqScanListEntry(dev, word0);
358 writeAcqScanListEntry(dev, word1);
359 writeAcqScanListEntry(dev, word2);
360 writeAcqScanListEntry(dev, word3);
361}
362
0a85b6f0
MT
363static int daqboard2000_ai_insn_read(struct comedi_device *dev,
364 struct comedi_subdevice *s,
365 struct comedi_insn *insn,
366 unsigned int *data)
48a4d521 367{
f657b14a 368 unsigned int val;
48a4d521 369 int gain, chan, timeout;
f657b14a 370 int i;
48a4d521 371
f657b14a
HS
372 writew(DAQBOARD2000_AcqResetScanListFifo |
373 DAQBOARD2000_AcqResetResultsFifo |
374 DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
48a4d521 375
ac971c94
RKM
376 /*
377 * If pacer clock is not set to some high value (> 10 us), we
378 * risk multiple samples to be put into the result FIFO.
379 */
380 /* 1 second, should be long enough */
f657b14a
HS
381 writel(1000000, devpriv->daq + acqPacerClockDivLow);
382 writew(0, devpriv->daq + acqPacerClockDivHigh);
48a4d521
AB
383
384 gain = CR_RANGE(insn->chanspec);
385 chan = CR_CHAN(insn->chanspec);
386
387 /* This doesn't look efficient. I decided to take the conservative
388 * approach when I did the insn conversion. Perhaps it would be
389 * better to have broken it completely, then someone would have been
390 * forced to fix it. --ds */
391 for (i = 0; i < insn->n; i++) {
392 setup_sampling(dev, chan, gain);
393 /* Enable reading from the scanlist FIFO */
f657b14a
HS
394 writew(DAQBOARD2000_SeqStartScanList,
395 devpriv->daq + acqControl);
48a4d521 396 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
397 val = readw(devpriv->daq + acqControl);
398 if (val & DAQBOARD2000_AcqConfigPipeFull)
48a4d521 399 break;
5f74ea14 400 /* udelay(2); */
48a4d521 401 }
f657b14a 402 writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
48a4d521 403 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
404 val = readw(devpriv->daq + acqControl);
405 if (val & DAQBOARD2000_AcqLogicScanning)
48a4d521 406 break;
5f74ea14 407 /* udelay(2); */
48a4d521
AB
408 }
409 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
410 val = readw(devpriv->daq + acqControl);
411 if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
48a4d521 412 break;
5f74ea14 413 /* udelay(2); */
48a4d521 414 }
f657b14a
HS
415 data[i] = readw(devpriv->daq + acqResultsFIFO);
416 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
417 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
48a4d521
AB
418 }
419
420 return i;
421}
422
0a85b6f0
MT
423static int daqboard2000_ao_insn_read(struct comedi_device *dev,
424 struct comedi_subdevice *s,
425 struct comedi_insn *insn,
426 unsigned int *data)
48a4d521
AB
427{
428 int i;
429 int chan = CR_CHAN(insn->chanspec);
430
5f1d0f3f 431 for (i = 0; i < insn->n; i++)
48a4d521 432 data[i] = devpriv->ao_readback[chan];
48a4d521
AB
433
434 return i;
435}
436
0a85b6f0
MT
437static int daqboard2000_ao_insn_write(struct comedi_device *dev,
438 struct comedi_subdevice *s,
439 struct comedi_insn *insn,
440 unsigned int *data)
48a4d521 441{
48a4d521 442 int chan = CR_CHAN(insn->chanspec);
f657b14a 443 unsigned int val;
48a4d521 444 int timeout;
f657b14a 445 int i;
48a4d521
AB
446
447 for (i = 0; i < insn->n; i++) {
f657b14a 448#if 0
48a4d521 449 /*
f657b14a
HS
450 * OK, since it works OK without enabling the DAC's,
451 * let's keep it as simple as possible...
48a4d521 452 */
f657b14a
HS
453 writew((chan + 2) * 0x0010 | 0x0001,
454 devpriv->daq + dacControl);
455 udelay(1000);
456#endif
457 writew(data[i], devpriv->daq + dacSetting(chan));
48a4d521 458 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
459 val = readw(devpriv->daq + dacControl);
460 if ((val & ((chan + 1) * 0x0010)) == 0)
48a4d521 461 break;
5f74ea14 462 /* udelay(2); */
48a4d521
AB
463 }
464 devpriv->ao_readback[chan] = data[i];
f657b14a 465#if 0
48a4d521 466 /*
f657b14a
HS
467 * Since we never enabled the DAC's, we don't need
468 * to disable it...
48a4d521 469 */
f657b14a
HS
470 writew((chan + 2) * 0x0010 | 0x0000,
471 devpriv->daq + dacControl);
472 udelay(1000);
473#endif
48a4d521
AB
474 }
475
476 return i;
477}
478
da91b269 479static void daqboard2000_resetLocalBus(struct comedi_device *dev)
48a4d521 480{
f41ad667 481 dev_dbg(dev->class_dev, "daqboard2000_resetLocalBus\n");
48a4d521 482 writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
5f74ea14 483 udelay(10000);
48a4d521 484 writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
5f74ea14 485 udelay(10000);
48a4d521
AB
486}
487
da91b269 488static void daqboard2000_reloadPLX(struct comedi_device *dev)
48a4d521 489{
f41ad667 490 dev_dbg(dev->class_dev, "daqboard2000_reloadPLX\n");
48a4d521 491 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
5f74ea14 492 udelay(10000);
48a4d521 493 writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
5f74ea14 494 udelay(10000);
48a4d521 495 writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
5f74ea14 496 udelay(10000);
48a4d521
AB
497}
498
da91b269 499static void daqboard2000_pulseProgPin(struct comedi_device *dev)
48a4d521 500{
f41ad667 501 dev_dbg(dev->class_dev, "daqboard2000_pulseProgPin 1\n");
48a4d521 502 writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
5f74ea14 503 udelay(10000);
48a4d521 504 writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
0a85b6f0 505 udelay(10000); /* Not in the original code, but I like symmetry... */
48a4d521
AB
506}
507
da91b269 508static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
48a4d521
AB
509{
510 int result = 0;
511 int i;
512 int cpld;
513
514 /* timeout after 50 tries -> 5ms */
515 for (i = 0; i < 50; i++) {
516 cpld = readw(devpriv->daq + 0x1000);
517 if ((cpld & mask) == mask) {
518 result = 1;
519 break;
520 }
5f74ea14 521 udelay(100);
48a4d521 522 }
5f74ea14 523 udelay(5);
48a4d521
AB
524 return result;
525}
526
da91b269 527static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
48a4d521
AB
528{
529 int result = 0;
530
5f74ea14 531 udelay(10);
48a4d521
AB
532 writew(data, devpriv->daq + 0x1000);
533 if ((readw(devpriv->daq + 0x1000) & DAQBOARD2000_CPLD_INIT) ==
0a85b6f0 534 DAQBOARD2000_CPLD_INIT) {
48a4d521
AB
535 result = 1;
536 }
537 return result;
538}
539
da91b269 540static int initialize_daqboard2000(struct comedi_device *dev,
0a85b6f0 541 unsigned char *cpld_array, int len)
48a4d521
AB
542{
543 int result = -EIO;
544 /* Read the serial EEPROM control register */
545 int secr;
546 int retry;
547 int i;
548
549 /* Check to make sure the serial eeprom is present on the board */
550 secr = readl(devpriv->plx + 0x6c);
551 if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
552#ifdef DEBUG_EEPROM
f41ad667 553 dev_dbg(dev->class_dev, "no serial eeprom\n");
48a4d521
AB
554#endif
555 return -EIO;
556 }
557
558 for (retry = 0; retry < 3; retry++) {
559#ifdef DEBUG_EEPROM
f41ad667 560 dev_dbg(dev->class_dev, "Programming EEPROM try %x\n", retry);
48a4d521
AB
561#endif
562
563 daqboard2000_resetLocalBus(dev);
564 daqboard2000_reloadPLX(dev);
565 daqboard2000_pulseProgPin(dev);
566 if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
567 for (i = 0; i < len; i++) {
568 if (cpld_array[i] == 0xff
0a85b6f0 569 && cpld_array[i + 1] == 0x20) {
48a4d521 570#ifdef DEBUG_EEPROM
f41ad667
IA
571 dev_dbg(dev->class_dev,
572 "Preamble found at %d\n", i);
48a4d521
AB
573#endif
574 break;
575 }
576 }
577 for (; i < len; i += 2) {
578 int data =
0a85b6f0 579 (cpld_array[i] << 8) + cpld_array[i + 1];
5f1d0f3f 580 if (!daqboard2000_writeCPLD(dev, data))
48a4d521 581 break;
48a4d521
AB
582 }
583 if (i >= len) {
584#ifdef DEBUG_EEPROM
f41ad667 585 dev_dbg(dev->class_dev, "Programmed\n");
48a4d521
AB
586#endif
587 daqboard2000_resetLocalBus(dev);
588 daqboard2000_reloadPLX(dev);
589 result = 0;
590 break;
591 }
592 }
593 }
594 return result;
595}
596
da91b269 597static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
48a4d521
AB
598{
599/* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
600}
601
da91b269 602static void daqboard2000_adcDisarm(struct comedi_device *dev)
48a4d521 603{
48a4d521 604 /* Disable hardware triggers */
5f74ea14 605 udelay(2);
f657b14a
HS
606 writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
607 devpriv->daq + trigControl);
5f74ea14 608 udelay(2);
f657b14a
HS
609 writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
610 devpriv->daq + trigControl);
48a4d521
AB
611
612 /* Stop the scan list FIFO from loading the configuration pipe */
5f74ea14 613 udelay(2);
f657b14a 614 writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
48a4d521
AB
615
616 /* Stop the pacer clock */
5f74ea14 617 udelay(2);
f657b14a 618 writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
48a4d521
AB
619
620 /* Stop the input dma (abort channel 1) */
621 daqboard2000_adcStopDmaTransfer(dev);
622}
623
da91b269 624static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
48a4d521 625{
f657b14a 626 unsigned int val;
48a4d521
AB
627 int timeout;
628
2696fb57 629 /* Set the + reference dac value in the FPGA */
f657b14a 630 writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
48a4d521 631 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
632 val = readw(devpriv->daq + dacControl);
633 if ((val & DAQBOARD2000_RefBusy) == 0)
48a4d521 634 break;
5f74ea14 635 udelay(2);
48a4d521
AB
636 }
637/* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
638
2696fb57 639 /* Set the - reference dac value in the FPGA */
f657b14a 640 writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
48a4d521 641 for (timeout = 0; timeout < 20; timeout++) {
f657b14a
HS
642 val = readw(devpriv->daq + dacControl);
643 if ((val & DAQBOARD2000_RefBusy) == 0)
48a4d521 644 break;
5f74ea14 645 udelay(2);
48a4d521
AB
646 }
647/* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
648}
649
da91b269 650static void daqboard2000_initializeCtrs(struct comedi_device *dev)
48a4d521
AB
651{
652/* printk("Implement: daqboard2000_initializeCtrs\n");*/
653}
654
da91b269 655static void daqboard2000_initializeTmrs(struct comedi_device *dev)
48a4d521
AB
656{
657/* printk("Implement: daqboard2000_initializeTmrs\n");*/
658}
659
da91b269 660static void daqboard2000_dacDisarm(struct comedi_device *dev)
48a4d521
AB
661{
662/* printk("Implement: daqboard2000_dacDisarm\n");*/
663}
664
da91b269 665static void daqboard2000_initializeAdc(struct comedi_device *dev)
48a4d521
AB
666{
667 daqboard2000_adcDisarm(dev);
668 daqboard2000_activateReferenceDacs(dev);
669 daqboard2000_initializeCtrs(dev);
670 daqboard2000_initializeTmrs(dev);
671}
672
da91b269 673static void daqboard2000_initializeDac(struct comedi_device *dev)
48a4d521
AB
674{
675 daqboard2000_dacDisarm(dev);
676}
677
678/*
679The test command, REMOVE!!:
680
681rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
682*/
683
684static int daqboard2000_8255_cb(int dir, int port, int data,
0a85b6f0 685 unsigned long ioaddr)
48a4d521 686{
f657b14a
HS
687 void __iomem *mmio_base = (void __iomem *)ioaddr;
688
48a4d521 689 if (dir) {
f657b14a
HS
690 writew(data, mmio_base + port * 2);
691 return 0;
48a4d521 692 } else {
f657b14a 693 return readw(mmio_base + port * 2);
48a4d521 694 }
48a4d521
AB
695}
696
6896ddfa
HS
697static struct pci_dev *daqboard2000_find_pci_dev(struct comedi_device *dev,
698 struct comedi_devconfig *it)
48a4d521 699{
6896ddfa
HS
700 struct pci_dev *pcidev = NULL;
701 int bus = it->options[0];
702 int slot = it->options[1];
f34ec800 703 int i;
48a4d521 704
f34ec800 705 for_each_pci_dev(pcidev) {
48a4d521 706 if (bus || slot) {
f34ec800
HS
707 if (bus != pcidev->bus->number ||
708 slot != PCI_SLOT(pcidev->devfn))
48a4d521 709 continue;
48a4d521 710 }
f34ec800 711 if (pcidev->vendor != PCI_VENDOR_ID_IOTECH ||
7e8401b2
HS
712 pcidev->device != 0x0409 ||
713 pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
f34ec800 714 continue;
6896ddfa 715
542038f4 716 for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
f34ec800
HS
717 if (boardtypes[i].id != pcidev->subsystem_device)
718 continue;
719 dev->board_ptr = boardtypes + i;
720 return pcidev;
48a4d521
AB
721 }
722 }
f34ec800
HS
723 dev_err(dev->class_dev,
724 "No supported board found! (req. bus %d, slot %d)\n",
725 bus, slot);
726 return NULL;
6896ddfa
HS
727}
728
729static int daqboard2000_attach(struct comedi_device *dev,
730 struct comedi_devconfig *it)
731{
732 struct pci_dev *pcidev;
733 struct comedi_subdevice *s;
325a01f3 734 resource_size_t pci_base;
6896ddfa
HS
735 void *aux_data;
736 unsigned int aux_len;
737 int result;
738
739 result = alloc_private(dev, sizeof(struct daqboard2000_private));
740 if (result < 0)
741 return -ENOMEM;
742
743 pcidev = daqboard2000_find_pci_dev(dev, it);
744 if (!pcidev)
745 return -EIO;
ad196be7 746 comedi_set_hw_dev(dev, &pcidev->dev);
48a4d521 747
6896ddfa 748 result = comedi_pci_enable(pcidev, "daqboard2000");
c3744138 749 if (result < 0) {
f41ad667
IA
750 dev_err(dev->class_dev,
751 "failed to enable PCI device and request regions\n");
48a4d521
AB
752 return -EIO;
753 }
325a01f3 754 dev->iobase = 1; /* the "detach" needs this */
f55354b0 755
325a01f3
HS
756 pci_base = pci_resource_start(pcidev, 0);
757 devpriv->plx = ioremap(pci_base, DAQBOARD2000_PLX_SIZE);
758 pci_base = pci_resource_start(pcidev, 2);
759 devpriv->daq = ioremap(pci_base, DAQBOARD2000_DAQ_SIZE);
352f9174 760 if (!devpriv->plx || !devpriv->daq)
48a4d521 761 return -ENOMEM;
48a4d521 762
2f0b9d08 763 result = comedi_alloc_subdevices(dev, 3);
8b6c5694
HS
764 if (result)
765 return result;
48a4d521
AB
766
767 readl(devpriv->plx + 0x6c);
768
769 /*
770 u8 interrupt;
771 Windows code does restore interrupts, but since we don't use them...
6896ddfa 772 pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
48a4d521
AB
773 printk("Interrupt before is: %x\n", interrupt);
774 */
775
776 aux_data = comedi_aux_data(it->options, 0);
777 aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
778
779 if (aux_data && aux_len) {
780 result = initialize_daqboard2000(dev, aux_data, aux_len);
781 } else {
f41ad667
IA
782 dev_dbg(dev->class_dev,
783 "no FPGA initialization code, aborting\n");
48a4d521
AB
784 result = -EIO;
785 }
786 if (result < 0)
787 goto out;
788 daqboard2000_initializeAdc(dev);
789 daqboard2000_initializeDac(dev);
790 /*
791 Windows code does restore interrupts, but since we don't use them...
6896ddfa 792 pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
48a4d521
AB
793 printk("Interrupt after is: %x\n", interrupt);
794 */
795
48a4d521
AB
796 dev->board_name = this_board->name;
797
b8153577 798 s = &dev->subdevices[0];
48a4d521
AB
799 /* ai subdevice */
800 s->type = COMEDI_SUBD_AI;
801 s->subdev_flags = SDF_READABLE | SDF_GROUND;
802 s->n_chan = 24;
803 s->maxdata = 0xffff;
804 s->insn_read = daqboard2000_ai_insn_read;
805 s->range_table = &range_daqboard2000_ai;
806
b8153577 807 s = &dev->subdevices[1];
48a4d521
AB
808 /* ao subdevice */
809 s->type = COMEDI_SUBD_AO;
810 s->subdev_flags = SDF_WRITABLE;
811 s->n_chan = 2;
812 s->maxdata = 0xffff;
813 s->insn_read = daqboard2000_ao_insn_read;
814 s->insn_write = daqboard2000_ao_insn_write;
815 s->range_table = &range_daqboard2000_ao;
816
b8153577 817 s = &dev->subdevices[2];
48a4d521 818 result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
f657b14a 819 (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
48a4d521 820
0a85b6f0 821out:
48a4d521
AB
822 return result;
823}
824
484ecc95 825static void daqboard2000_detach(struct comedi_device *dev)
48a4d521 826{
ad196be7
HS
827 struct pci_dev *pcidev = comedi_to_pci_dev(dev);
828
48a4d521 829 if (dev->subdevices)
b8153577 830 subdev_8255_cleanup(dev, &dev->subdevices[2]);
352f9174 831 if (dev->irq)
48a4d521 832 free_irq(dev->irq, dev);
48a4d521
AB
833 if (devpriv) {
834 if (devpriv->daq)
835 iounmap(devpriv->daq);
836 if (devpriv->plx)
837 iounmap(devpriv->plx);
ad196be7
HS
838 }
839 if (pcidev) {
840 if (dev->iobase)
841 comedi_pci_disable(pcidev);
842 pci_dev_put(pcidev);
48a4d521 843 }
48a4d521
AB
844}
845
ace44dc8
HS
846static struct comedi_driver daqboard2000_driver = {
847 .driver_name = "daqboard2000",
848 .module = THIS_MODULE,
849 .attach = daqboard2000_attach,
850 .detach = daqboard2000_detach,
851};
852
853static int __devinit daqboard2000_pci_probe(struct pci_dev *dev,
854 const struct pci_device_id *ent)
727b286b 855{
ace44dc8 856 return comedi_pci_auto_config(dev, &daqboard2000_driver);
727b286b
AT
857}
858
ace44dc8 859static void __devexit daqboard2000_pci_remove(struct pci_dev *dev)
727b286b
AT
860{
861 comedi_pci_auto_unconfig(dev);
862}
863
ace44dc8 864static DEFINE_PCI_DEVICE_TABLE(daqboard2000_pci_table) = {
f34ec800 865 { PCI_DEVICE(PCI_VENDOR_ID_IOTECH, 0x0409) },
ace44dc8 866 { 0 }
727b286b 867};
ace44dc8 868MODULE_DEVICE_TABLE(pci, daqboard2000_pci_table);
727b286b 869
ace44dc8
HS
870static struct pci_driver daqboard2000_pci_driver = {
871 .name = "daqboard2000",
872 .id_table = daqboard2000_pci_table,
873 .probe = daqboard2000_pci_probe,
874 .remove = __devexit_p(daqboard2000_pci_remove),
875};
876module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
90f703d3
AT
877
878MODULE_AUTHOR("Comedi http://www.comedi.org");
879MODULE_DESCRIPTION("Comedi low-level driver");
880MODULE_LICENSE("GPL");