2 * comedi/drivers/daqboard2000.c
3 * hardware driver for IOtech DAQboard/2000
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 1999 Anders Blomdell <anders.blomdell@control.lth.se>
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.
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.
19 * Driver: daqboard2000
20 * Description: IOTech DAQBoard/2000
21 * Author: Anders Blomdell <anders.blomdell@control.lth.se>
23 * Updated: Mon, 14 Apr 2008 15:28:52 +0100
24 * Devices: [IOTech] DAQBoard/2000 (daqboard2000)
26 * Much of the functionality of this driver was determined from reading
27 * the source code for the Windows driver.
29 * The FPGA on the board requires firmware, which is available from
30 * http://www.comedi.org in the comedi_nonfree_firmware tarball.
32 * Configuration options: not applicable, uses PCI auto config
35 * This card was obviously never intended to leave the Windows world,
36 * since it lacked all kind of hardware documentation (except for cable
37 * pinouts, plug and pray has something to catch up with yet).
39 * With some help from our swedish distributor, we got the Windows sourcecode
40 * for the card, and here are the findings so far.
42 * 1. A good document that describes the PCI interface chip is 9080db-106.pdf
43 * available from http://www.plxtech.com/products/io/pci9080
45 * 2. The initialization done so far is:
46 * a. program the FPGA (windows code sans a lot of error messages)
49 * 3. Analog out seems to work OK with DAC's disabled, if DAC's are enabled,
50 * you have to output values to all enabled DAC's until result appears, I
51 * guess that it has something to do with pacer clocks, but the source
52 * gives me no clues. I'll keep it simple so far.
55 * Each channel in the scanlist seems to be controlled by four
59 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 * ! | | | ! | | | ! | | | ! | | | !
61 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
65 * ! | | | ! | | | ! | | | ! | | | !
66 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * +------+------+ | | | | +-- Digital input (??)
69 * | | | | +---- 10 us settling time
70 * | | | +------ Suspend acquisition (last to scan)
71 * | | +-------- Simultaneous sample and hold
72 * | +---------- Signed data format
73 * +------------------------- Correction offset low
76 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 * ! | | | ! | | | ! | | | ! | | | !
78 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
80 * +-----+ +--+--+ +++ +++ +--+--+
81 * | | | | +----- Expansion channel
82 * | | | +----------- Expansion gain
83 * | | +--------------- Channel (low)
84 * | +--------------------- Correction offset high
85 * +----------------------------- Correction gain low
87 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 * ! | | | ! | | | ! | | | ! | | | !
89 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91 * +------+------+ | | +-+-+ | | +-- Low bank enable
92 * | | | | | +---- High bank enable
93 * | | | | +------ Hi/low select
94 * | | | +---------- Gain (1,?,2,4,8,16,32,64)
95 * | | +-------------- differential/single ended
96 * | +---------------- Unipolar
97 * +------------------------- Correction gain high
99 * 999. The card seems to have an incredible amount of capabilities, but
100 * trying to reverse engineer them from the Windows source is beyond my
105 #include <linux/module.h>
106 #include <linux/delay.h>
107 #include <linux/interrupt.h>
109 #include "../comedi_pci.h"
114 #define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin"
116 #define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
117 #define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
119 static const struct comedi_lrange range_daqboard2000_ai
= {
138 * Register Memory Map
140 #define DB2K_REG_ACQ_CONTROL 0x00 /* u16 (w) */
141 #define DB2K_REG_ACQ_STATUS 0x00 /* u16 (r) */
142 #define DB2K_REG_ACQ_SCAN_LIST_FIFO 0x02 /* u16 */
143 #define DB2K_REG_ACQ_PACER_CLOCK_DIV_LOW 0x04 /* u32 */
144 #define DB2K_REG_ACQ_SCAN_COUNTER 0x08 /* u16 */
145 #define DB2K_REG_ACQ_PACER_CLOCK_DIV_HIGH 0x0a /* u16 */
146 #define DB2K_REG_ACQ_TRIGGER_COUNT 0x0c /* u16 */
147 #define DB2K_REG_ACQ_RESULTS_FIFO 0x10 /* u16 */
148 #define DB2K_REG_ACQ_RESULTS_SHADOW 0x14 /* u16 */
149 #define DB2K_REG_ACQ_ADC_RESULT 0x18 /* u16 */
150 #define DB2K_REG_DAC_SCAN_COUNTER 0x1c /* u16 */
151 #define DB2K_REG_DAC_CONTROL 0x20 /* u16 (w) */
152 #define DB2K_REG_DAC_STATUS 0x20 /* u16 (r) */
153 #define DB2K_REG_DAC_FIFO 0x24 /* s16 */
154 #define DB2K_REG_DAC_PACER_CLOCK_DIV 0x2a /* u16 */
155 #define DB2K_REG_REF_DACS 0x2c /* u16 */
156 #define DB2K_REG_DIO_CONTROL 0x30 /* u16 */
157 #define DB2K_REG_P3_HSIO_DATA 0x32 /* s16 */
158 #define DB2K_REG_P3_CONTROL 0x34 /* u16 */
159 #define DB2K_REG_CAL_EEPROM_CONTROL 0x36 /* u16 */
160 #define DB2K_REG_DAC_SETTING(x) (0x38 + (x) * 2) /* s16 */
161 #define DB2K_REG_DIO_P2_EXP_IO_8_BIT 0x40 /* s16 */
162 #define DB2K_REG_COUNTER_TIMER_CONTROL 0x80 /* u16 */
163 #define DB2K_REG_COUNTER_INPUT(x) (0x88 + (x) * 2) /* s16 */
164 #define DB2K_REG_TIMER_DIV(x) (0xa0 + (x) * 2) /* u16 */
165 #define DB2K_REG_DMA_CONTROL 0xb0 /* u16 */
166 #define DB2K_REG_TRIG_CONTROL 0xb2 /* u16 */
167 #define DB2K_REG_CAL_EEPROM 0xb8 /* u16 */
168 #define DB2K_REG_ACQ_DIGITAL_MARK 0xba /* u16 */
169 #define DB2K_REG_TRIG_DACS 0xbc /* u16 */
170 #define DB2K_REG_DIO_P2_EXP_IO_16_BIT(x) (0xc0 + (x) * 2) /* s16 */
173 #define DB2K_REG_CPLD_STATUS 0x1000 /* u16 (r) */
174 #define DB2K_REG_CPLD_WDATA 0x1000 /* u16 (w) */
176 /* Scan Sequencer programming */
177 #define DB2K_ACQ_CONTROL_SEQ_START_SCAN_LIST 0x0011
178 #define DB2K_ACQ_CONTROL_SEQ_STOP_SCAN_LIST 0x0010
180 /* Prepare for acquisition */
181 #define DB2K_ACQ_CONTROL_RESET_SCAN_LIST_FIFO 0x0004
182 #define DB2K_ACQ_CONTROL_RESET_RESULTS_FIFO 0x0002
183 #define DB2K_ACQ_CONTROL_RESET_CONFIG_PIPE 0x0001
185 /* Pacer Clock Control */
186 #define DB2K_ACQ_CONTROL_ADC_PACER_INTERNAL 0x0030
187 #define DB2K_ACQ_CONTROL_ADC_PACER_EXTERNAL 0x0032
188 #define DB2K_ACQ_CONTROL_ADC_PACER_ENABLE 0x0031
189 #define DB2K_ACQ_CONTROL_ADC_PACER_ENABLE_DAC_PACER 0x0034
190 #define DB2K_ACQ_CONTROL_ADC_PACER_DISABLE 0x0030
191 #define DB2K_ACQ_CONTROL_ADC_PACER_NORMAL_MODE 0x0060
192 #define DB2K_ACQ_CONTROL_ADC_PACER_COMPATIBILITY_MODE 0x0061
193 #define DB2K_ACQ_CONTROL_ADC_PACER_INTERNAL_OUT_ENABLE 0x0008
194 #define DB2K_ACQ_CONTROL_ADC_PACER_EXTERNAL_RISING 0x0100
196 /* Acquisition status bits */
197 #define DB2K_ACQ_STATUS_RESULTS_FIFO_MORE_1_SAMPLE 0x0001
198 #define DB2K_ACQ_STATUS_RESULTS_FIFO_HAS_DATA 0x0002
199 #define DB2K_ACQ_STATUS_RESULTS_FIFO_OVERRUN 0x0004
200 #define DB2K_ACQ_STATUS_LOGIC_SCANNING 0x0008
201 #define DB2K_ACQ_STATUS_CONFIG_PIPE_FULL 0x0010
202 #define DB2K_ACQ_STATUS_SCAN_LIST_FIFO_EMPTY 0x0020
203 #define DB2K_ACQ_STATUS_ADC_NOT_READY 0x0040
204 #define DB2K_ACQ_STATUS_ARBITRATION_FAILURE 0x0080
205 #define DB2K_ACQ_STATUS_ADC_PACER_OVERRUN 0x0100
206 #define DB2K_ACQ_STATUS_DAC_PACER_OVERRUN 0x0200
209 #define DB2K_DAC_STATUS_DAC_FULL 0x0001
210 #define DB2K_DAC_STATUS_REF_BUSY 0x0002
211 #define DB2K_DAC_STATUS_TRIG_BUSY 0x0004
212 #define DB2K_DAC_STATUS_CAL_BUSY 0x0008
213 #define DB2K_DAC_STATUS_DAC_BUSY(x) (0x0010 << (x))
216 #define DB2K_DAC_CONTROL_ENABLE_BIT 0x0001
217 #define DB2K_DAC_CONTROL_DATA_IS_SIGNED 0x0002
218 #define DB2K_DAC_CONTROL_RESET_FIFO 0x0004
219 #define DB2K_DAC_CONTROL_DAC_DISABLE(x) (0x0020 + ((x) << 4))
220 #define DB2K_DAC_CONTROL_DAC_ENABLE(x) (0x0021 + ((x) << 4))
221 #define DB2K_DAC_CONTROL_PATTERN_DISABLE 0x0060
222 #define DB2K_DAC_CONTROL_PATTERN_ENABLE 0x0061
224 /* Trigger Control */
225 #define DB2K_TRIG_CONTROL_TYPE_ANALOG 0x0000
226 #define DB2K_TRIG_CONTROL_TYPE_TTL 0x0010
227 #define DB2K_TRIG_CONTROL_EDGE_HI_LO 0x0004
228 #define DB2K_TRIG_CONTROL_EDGE_LO_HI 0x0000
229 #define DB2K_TRIG_CONTROL_LEVEL_ABOVE 0x0000
230 #define DB2K_TRIG_CONTROL_LEVEL_BELOW 0x0004
231 #define DB2K_TRIG_CONTROL_SENSE_LEVEL 0x0002
232 #define DB2K_TRIG_CONTROL_SENSE_EDGE 0x0000
233 #define DB2K_TRIG_CONTROL_ENABLE 0x0001
234 #define DB2K_TRIG_CONTROL_DISABLE 0x0000
236 /* Reference Dac Selection */
237 #define DB2K_REF_DACS_SET 0x0080
238 #define DB2K_REF_DACS_SELECT_POS_REF 0x0100
239 #define DB2K_REF_DACS_SELECT_NEG_REF 0x0000
241 /* CPLD status bits */
242 #define DB2K_CPLD_STATUS_INIT 0x0002
243 #define DB2K_CPLD_STATUS_TXDONE 0x0004
245 struct daq200_boardtype
{
250 static const struct daq200_boardtype boardtypes
[] = {
251 {"ids2", DAQBOARD2000_SUBSYSTEM_IDS2
},
252 {"ids4", DAQBOARD2000_SUBSYSTEM_IDS4
},
255 struct daqboard2000_private
{
262 static void daqboard2000_write_acq_scan_list_entry(struct comedi_device
*dev
,
265 writew(entry
& 0x00ff, dev
->mmio
+ DB2K_REG_ACQ_SCAN_LIST_FIFO
);
266 writew((entry
>> 8) & 0x00ff,
267 dev
->mmio
+ DB2K_REG_ACQ_SCAN_LIST_FIFO
);
270 static void daqboard2000_setup_sampling(struct comedi_device
*dev
, int chan
,
273 u16 word0
, word1
, word2
, word3
;
275 /* Channel 0-7 diff, channel 8-23 single ended */
277 word1
= 0x0004; /* Last scan */
278 word2
= (chan
<< 6) & 0x00c0;
302 /* These should be read from EEPROM */
303 word2
|= 0x0800; /* offset */
304 word3
|= 0xc000; /* gain */
305 daqboard2000_write_acq_scan_list_entry(dev
, word0
);
306 daqboard2000_write_acq_scan_list_entry(dev
, word1
);
307 daqboard2000_write_acq_scan_list_entry(dev
, word2
);
308 daqboard2000_write_acq_scan_list_entry(dev
, word3
);
311 static int daqboard2000_ai_status(struct comedi_device
*dev
,
312 struct comedi_subdevice
*s
,
313 struct comedi_insn
*insn
,
314 unsigned long context
)
318 status
= readw(dev
->mmio
+ DB2K_REG_ACQ_STATUS
);
319 if (status
& context
)
324 static int daqboard2000_ai_insn_read(struct comedi_device
*dev
,
325 struct comedi_subdevice
*s
,
326 struct comedi_insn
*insn
,
333 writew(DB2K_ACQ_CONTROL_RESET_SCAN_LIST_FIFO
|
334 DB2K_ACQ_CONTROL_RESET_RESULTS_FIFO
|
335 DB2K_ACQ_CONTROL_RESET_CONFIG_PIPE
,
336 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
339 * If pacer clock is not set to some high value (> 10 us), we
340 * risk multiple samples to be put into the result FIFO.
342 /* 1 second, should be long enough */
343 writel(1000000, dev
->mmio
+ DB2K_REG_ACQ_PACER_CLOCK_DIV_LOW
);
344 writew(0, dev
->mmio
+ DB2K_REG_ACQ_PACER_CLOCK_DIV_HIGH
);
346 gain
= CR_RANGE(insn
->chanspec
);
347 chan
= CR_CHAN(insn
->chanspec
);
350 * This doesn't look efficient. I decided to take the conservative
351 * approach when I did the insn conversion. Perhaps it would be
352 * better to have broken it completely, then someone would have been
353 * forced to fix it. --ds
355 for (i
= 0; i
< insn
->n
; i
++) {
356 daqboard2000_setup_sampling(dev
, chan
, gain
);
357 /* Enable reading from the scanlist FIFO */
358 writew(DB2K_ACQ_CONTROL_SEQ_START_SCAN_LIST
,
359 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
361 ret
= comedi_timeout(dev
, s
, insn
, daqboard2000_ai_status
,
362 DB2K_ACQ_STATUS_CONFIG_PIPE_FULL
);
366 writew(DB2K_ACQ_CONTROL_ADC_PACER_ENABLE
,
367 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
369 ret
= comedi_timeout(dev
, s
, insn
, daqboard2000_ai_status
,
370 DB2K_ACQ_STATUS_LOGIC_SCANNING
);
375 comedi_timeout(dev
, s
, insn
, daqboard2000_ai_status
,
376 DB2K_ACQ_STATUS_RESULTS_FIFO_HAS_DATA
);
380 data
[i
] = readw(dev
->mmio
+ DB2K_REG_ACQ_RESULTS_FIFO
);
381 writew(DB2K_ACQ_CONTROL_ADC_PACER_DISABLE
,
382 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
383 writew(DB2K_ACQ_CONTROL_SEQ_STOP_SCAN_LIST
,
384 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
390 static int daqboard2000_ao_eoc(struct comedi_device
*dev
,
391 struct comedi_subdevice
*s
,
392 struct comedi_insn
*insn
,
393 unsigned long context
)
395 unsigned int chan
= CR_CHAN(insn
->chanspec
);
398 status
= readw(dev
->mmio
+ DB2K_REG_DAC_STATUS
);
399 if ((status
& DB2K_DAC_STATUS_DAC_BUSY(chan
)) == 0)
404 static int daqboard2000_ao_insn_write(struct comedi_device
*dev
,
405 struct comedi_subdevice
*s
,
406 struct comedi_insn
*insn
,
409 unsigned int chan
= CR_CHAN(insn
->chanspec
);
412 for (i
= 0; i
< insn
->n
; i
++) {
413 unsigned int val
= data
[i
];
416 writew(val
, dev
->mmio
+ DB2K_REG_DAC_SETTING(chan
));
418 ret
= comedi_timeout(dev
, s
, insn
, daqboard2000_ao_eoc
, 0);
422 s
->readback
[chan
] = val
;
428 static void daqboard2000_reset_local_bus(struct comedi_device
*dev
)
430 struct daqboard2000_private
*devpriv
= dev
->private;
433 cntrl
= readl(devpriv
->plx
+ PLX_REG_CNTRL
);
434 cntrl
|= PLX_CNTRL_RESET
;
435 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
437 cntrl
&= ~PLX_CNTRL_RESET
;
438 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
442 static void daqboard2000_reload_plx(struct comedi_device
*dev
)
444 struct daqboard2000_private
*devpriv
= dev
->private;
447 cntrl
= readl(devpriv
->plx
+ PLX_REG_CNTRL
);
448 cntrl
&= ~PLX_CNTRL_EERELOAD
;
449 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
451 cntrl
|= PLX_CNTRL_EERELOAD
;
452 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
454 cntrl
&= ~PLX_CNTRL_EERELOAD
;
455 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
459 static void daqboard2000_pulse_prog_pin(struct comedi_device
*dev
)
461 struct daqboard2000_private
*devpriv
= dev
->private;
464 cntrl
= readl(devpriv
->plx
+ PLX_REG_CNTRL
);
465 cntrl
|= PLX_CNTRL_USERO
;
466 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
468 cntrl
&= ~PLX_CNTRL_USERO
;
469 writel(cntrl
, devpriv
->plx
+ PLX_REG_CNTRL
);
470 mdelay(10); /* Not in the original code, but I like symmetry... */
473 static int daqboard2000_poll_cpld(struct comedi_device
*dev
, int mask
)
479 /* timeout after 50 tries -> 5ms */
480 for (i
= 0; i
< 50; i
++) {
481 cpld
= readw(dev
->mmio
+ DB2K_REG_CPLD_STATUS
);
482 if ((cpld
& mask
) == mask
) {
486 usleep_range(100, 1000);
492 static int daqboard2000_write_cpld(struct comedi_device
*dev
, int data
)
496 usleep_range(10, 20);
497 writew(data
, dev
->mmio
+ DB2K_REG_CPLD_WDATA
);
498 if (readw(dev
->mmio
+ DB2K_REG_CPLD_STATUS
) & DB2K_CPLD_STATUS_INIT
)
504 static int daqboard2000_load_firmware(struct comedi_device
*dev
,
505 const u8
*cpld_array
, size_t len
,
506 unsigned long context
)
508 struct daqboard2000_private
*devpriv
= dev
->private;
514 /* Check to make sure the serial eeprom is present on the board */
515 cntrl
= readl(devpriv
->plx
+ PLX_REG_CNTRL
);
516 if (!(cntrl
& PLX_CNTRL_EEPRESENT
))
519 for (retry
= 0; retry
< 3; retry
++) {
520 daqboard2000_reset_local_bus(dev
);
521 daqboard2000_reload_plx(dev
);
522 daqboard2000_pulse_prog_pin(dev
);
523 if (daqboard2000_poll_cpld(dev
, DB2K_CPLD_STATUS_INIT
)) {
524 for (i
= 0; i
< len
; i
++) {
525 if (cpld_array
[i
] == 0xff &&
526 cpld_array
[i
+ 1] == 0x20)
529 for (; i
< len
; i
+= 2) {
531 (cpld_array
[i
] << 8) + cpld_array
[i
+ 1];
532 if (!daqboard2000_write_cpld(dev
, data
))
536 daqboard2000_reset_local_bus(dev
);
537 daqboard2000_reload_plx(dev
);
546 static void daqboard2000_adc_stop_dma_transfer(struct comedi_device
*dev
)
550 static void daqboard2000_adc_disarm(struct comedi_device
*dev
)
552 /* Disable hardware triggers */
554 writew(DB2K_TRIG_CONTROL_TYPE_ANALOG
| DB2K_TRIG_CONTROL_DISABLE
,
555 dev
->mmio
+ DB2K_REG_TRIG_CONTROL
);
557 writew(DB2K_TRIG_CONTROL_TYPE_TTL
| DB2K_TRIG_CONTROL_DISABLE
,
558 dev
->mmio
+ DB2K_REG_TRIG_CONTROL
);
560 /* Stop the scan list FIFO from loading the configuration pipe */
562 writew(DB2K_ACQ_CONTROL_SEQ_STOP_SCAN_LIST
,
563 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
565 /* Stop the pacer clock */
567 writew(DB2K_ACQ_CONTROL_ADC_PACER_DISABLE
,
568 dev
->mmio
+ DB2K_REG_ACQ_CONTROL
);
570 /* Stop the input dma (abort channel 1) */
571 daqboard2000_adc_stop_dma_transfer(dev
);
574 static void daqboard2000_activate_reference_dacs(struct comedi_device
*dev
)
579 /* Set the + reference dac value in the FPGA */
580 writew(DB2K_REF_DACS_SET
| DB2K_REF_DACS_SELECT_POS_REF
,
581 dev
->mmio
+ DB2K_REG_REF_DACS
);
582 for (timeout
= 0; timeout
< 20; timeout
++) {
583 val
= readw(dev
->mmio
+ DB2K_REG_DAC_STATUS
);
584 if ((val
& DB2K_DAC_STATUS_REF_BUSY
) == 0)
589 /* Set the - reference dac value in the FPGA */
590 writew(DB2K_REF_DACS_SET
| DB2K_REF_DACS_SELECT_NEG_REF
,
591 dev
->mmio
+ DB2K_REG_REF_DACS
);
592 for (timeout
= 0; timeout
< 20; timeout
++) {
593 val
= readw(dev
->mmio
+ DB2K_REG_DAC_STATUS
);
594 if ((val
& DB2K_DAC_STATUS_REF_BUSY
) == 0)
600 static void daqboard2000_initialize_ctrs(struct comedi_device
*dev
)
604 static void daqboard2000_initialize_tmrs(struct comedi_device
*dev
)
608 static void daqboard2000_dac_disarm(struct comedi_device
*dev
)
612 static void daqboard2000_initialize_adc(struct comedi_device
*dev
)
614 daqboard2000_adc_disarm(dev
);
615 daqboard2000_activate_reference_dacs(dev
);
616 daqboard2000_initialize_ctrs(dev
);
617 daqboard2000_initialize_tmrs(dev
);
620 static void daqboard2000_initialize_dac(struct comedi_device
*dev
)
622 daqboard2000_dac_disarm(dev
);
625 static int daqboard2000_8255_cb(struct comedi_device
*dev
,
626 int dir
, int port
, int data
,
627 unsigned long iobase
)
630 writew(data
, dev
->mmio
+ iobase
+ port
* 2);
633 return readw(dev
->mmio
+ iobase
+ port
* 2);
636 static const void *daqboard2000_find_boardinfo(struct comedi_device
*dev
,
637 struct pci_dev
*pcidev
)
639 const struct daq200_boardtype
*board
;
642 if (pcidev
->subsystem_vendor
!= PCI_VENDOR_ID_IOTECH
)
645 for (i
= 0; i
< ARRAY_SIZE(boardtypes
); i
++) {
646 board
= &boardtypes
[i
];
647 if (pcidev
->subsystem_device
== board
->id
)
653 static int daqboard2000_auto_attach(struct comedi_device
*dev
,
654 unsigned long context_unused
)
656 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
657 const struct daq200_boardtype
*board
;
658 struct daqboard2000_private
*devpriv
;
659 struct comedi_subdevice
*s
;
662 board
= daqboard2000_find_boardinfo(dev
, pcidev
);
665 dev
->board_ptr
= board
;
666 dev
->board_name
= board
->name
;
668 devpriv
= comedi_alloc_devpriv(dev
, sizeof(*devpriv
));
672 result
= comedi_pci_enable(dev
);
676 devpriv
->plx
= pci_ioremap_bar(pcidev
, 0);
677 dev
->mmio
= pci_ioremap_bar(pcidev
, 2);
678 if (!devpriv
->plx
|| !dev
->mmio
)
681 result
= comedi_alloc_subdevices(dev
, 3);
685 result
= comedi_load_firmware(dev
, &comedi_to_pci_dev(dev
)->dev
,
686 DAQBOARD2000_FIRMWARE
,
687 daqboard2000_load_firmware
, 0);
691 daqboard2000_initialize_adc(dev
);
692 daqboard2000_initialize_dac(dev
);
694 s
= &dev
->subdevices
[0];
696 s
->type
= COMEDI_SUBD_AI
;
697 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
700 s
->insn_read
= daqboard2000_ai_insn_read
;
701 s
->range_table
= &range_daqboard2000_ai
;
703 s
= &dev
->subdevices
[1];
705 s
->type
= COMEDI_SUBD_AO
;
706 s
->subdev_flags
= SDF_WRITABLE
;
709 s
->insn_write
= daqboard2000_ao_insn_write
;
710 s
->range_table
= &range_bipolar10
;
712 result
= comedi_alloc_subdev_readback(s
);
716 s
= &dev
->subdevices
[2];
717 return subdev_8255_init(dev
, s
, daqboard2000_8255_cb
,
718 DB2K_REG_DIO_P2_EXP_IO_8_BIT
);
721 static void daqboard2000_detach(struct comedi_device
*dev
)
723 struct daqboard2000_private
*devpriv
= dev
->private;
725 if (devpriv
&& devpriv
->plx
)
726 iounmap(devpriv
->plx
);
727 comedi_pci_detach(dev
);
730 static struct comedi_driver daqboard2000_driver
= {
731 .driver_name
= "daqboard2000",
732 .module
= THIS_MODULE
,
733 .auto_attach
= daqboard2000_auto_attach
,
734 .detach
= daqboard2000_detach
,
737 static int daqboard2000_pci_probe(struct pci_dev
*dev
,
738 const struct pci_device_id
*id
)
740 return comedi_pci_auto_config(dev
, &daqboard2000_driver
,
744 static const struct pci_device_id daqboard2000_pci_table
[] = {
745 { PCI_DEVICE(PCI_VENDOR_ID_IOTECH
, 0x0409) },
748 MODULE_DEVICE_TABLE(pci
, daqboard2000_pci_table
);
750 static struct pci_driver daqboard2000_pci_driver
= {
751 .name
= "daqboard2000",
752 .id_table
= daqboard2000_pci_table
,
753 .probe
= daqboard2000_pci_probe
,
754 .remove
= comedi_pci_auto_unconfig
,
756 module_comedi_pci_driver(daqboard2000_driver
, daqboard2000_pci_driver
);
758 MODULE_AUTHOR("Comedi http://www.comedi.org");
759 MODULE_DESCRIPTION("Comedi low-level driver");
760 MODULE_LICENSE("GPL");
761 MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE
);