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