diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/cb_pcidas64.c')
-rw-r--r-- | drivers/staging/comedi/drivers/cb_pcidas64.c | 4119 |
1 files changed, 0 insertions, 4119 deletions
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c deleted file mode 100644 index 41a8fea7f48a..000000000000 --- a/drivers/staging/comedi/drivers/cb_pcidas64.c +++ /dev/null @@ -1,4119 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * comedi/drivers/cb_pcidas64.c - * This is a driver for the ComputerBoards/MeasurementComputing PCI-DAS - * 64xx, 60xx, and 4020 cards. - * - * Author: Frank Mori Hess <fmhess@users.sourceforge.net> - * Copyright (C) 2001, 2002 Frank Mori Hess - * - * Thanks also go to the following people: - * - * Steve Rosenbluth, for providing the source code for - * his pci-das6402 driver, and source code for working QNX pci-6402 - * drivers by Greg Laird and Mariusz Bogacz. None of the code was - * used directly here, but it was useful as an additional source of - * documentation on how to program the boards. - * - * John Sims, for much testing and feedback on pcidas-4020 support. - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-8 David A. Schleef <ds@schleef.org> - */ - -/* - * Driver: cb_pcidas64 - * Description: MeasurementComputing PCI-DAS64xx, 60XX, and 4020 series - * with the PLX 9080 PCI controller - * Author: Frank Mori Hess <fmhess@users.sourceforge.net> - * Status: works - * Updated: Fri, 02 Nov 2012 18:58:55 +0000 - * Devices: [Measurement Computing] PCI-DAS6402/16 (cb_pcidas64), - * PCI-DAS6402/12, PCI-DAS64/M1/16, PCI-DAS64/M2/16, - * PCI-DAS64/M3/16, PCI-DAS6402/16/JR, PCI-DAS64/M1/16/JR, - * PCI-DAS64/M2/16/JR, PCI-DAS64/M3/16/JR, PCI-DAS64/M1/14, - * PCI-DAS64/M2/14, PCI-DAS64/M3/14, PCI-DAS6013, PCI-DAS6014, - * PCI-DAS6023, PCI-DAS6025, PCI-DAS6030, - * PCI-DAS6031, PCI-DAS6032, PCI-DAS6033, PCI-DAS6034, - * PCI-DAS6035, PCI-DAS6036, PCI-DAS6040, PCI-DAS6052, - * PCI-DAS6070, PCI-DAS6071, PCI-DAS4020/12 - * - * Configuration options: - * None. - * - * Manual attachment of PCI cards with the comedi_config utility is not - * supported by this driver; they are attached automatically. - * - * These boards may be autocalibrated with the comedi_calibrate utility. - * - * To select the bnc trigger input on the 4020 (instead of the dio input), - * specify a nonzero channel in the chanspec. If you wish to use an external - * master clock on the 4020, you may do so by setting the scan_begin_src - * to TRIG_OTHER, and using an INSN_CONFIG_TIMER_1 configuration insn - * to configure the divisor to use for the external clock. - * - * Some devices are not identified because the PCI device IDs are not yet - * known. If you have such a board, please let the maintainers know. - */ - -/* - * TODO: - * make it return error if user attempts an ai command that uses the - * external queue, and an ao command simultaneously user counter subdevice - * there are a number of boards this driver will support when they are - * fully released, but does not yet since the pci device id numbers - * are not yet available. - * - * support prescaled 100khz clock for slow pacing (not available on 6000 - * series?) - * - * make ao fifo size adjustable like ai fifo - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/interrupt.h> - -#include "../comedi_pci.h" - -#include "8255.h" -#include "plx9080.h" - -#define TIMER_BASE 25 /* 40MHz master clock */ -/* - * 100kHz 'prescaled' clock for slow acquisition, - * maybe I'll support this someday - */ -#define PRESCALED_TIMER_BASE 10000 -#define DMA_BUFFER_SIZE 0x1000 -#define DAC_FIFO_SIZE 0x2000 - -/* maximum value that can be loaded into board's 24-bit counters */ -static const int max_counter_value = 0xffffff; - -/* PCI-DAS64xxx base addresses */ - -/* devpriv->main_iobase registers */ -enum write_only_registers { - INTR_ENABLE_REG = 0x0, /* interrupt enable register */ - HW_CONFIG_REG = 0x2, /* hardware config register */ - DAQ_SYNC_REG = 0xc, - DAQ_ATRIG_LOW_4020_REG = 0xc, - ADC_CONTROL0_REG = 0x10, /* adc control register 0 */ - ADC_CONTROL1_REG = 0x12, /* adc control register 1 */ - CALIBRATION_REG = 0x14, - /* lower 16 bits of adc sample interval counter */ - ADC_SAMPLE_INTERVAL_LOWER_REG = 0x16, - /* upper 8 bits of adc sample interval counter */ - ADC_SAMPLE_INTERVAL_UPPER_REG = 0x18, - /* lower 16 bits of delay interval counter */ - ADC_DELAY_INTERVAL_LOWER_REG = 0x1a, - /* upper 8 bits of delay interval counter */ - ADC_DELAY_INTERVAL_UPPER_REG = 0x1c, - /* lower 16 bits of hardware conversion/scan counter */ - ADC_COUNT_LOWER_REG = 0x1e, - /* upper 8 bits of hardware conversion/scan counter */ - ADC_COUNT_UPPER_REG = 0x20, - ADC_START_REG = 0x22, /* software trigger to start acquisition */ - ADC_CONVERT_REG = 0x24, /* initiates single conversion */ - ADC_QUEUE_CLEAR_REG = 0x26, /* clears adc queue */ - ADC_QUEUE_LOAD_REG = 0x28, /* loads adc queue */ - ADC_BUFFER_CLEAR_REG = 0x2a, - /* high channel for internal queue, use adc_chan_bits() inline above */ - ADC_QUEUE_HIGH_REG = 0x2c, - DAC_CONTROL0_REG = 0x50, /* dac control register 0 */ - DAC_CONTROL1_REG = 0x52, /* dac control register 0 */ - /* lower 16 bits of dac sample interval counter */ - DAC_SAMPLE_INTERVAL_LOWER_REG = 0x54, - /* upper 8 bits of dac sample interval counter */ - DAC_SAMPLE_INTERVAL_UPPER_REG = 0x56, - DAC_SELECT_REG = 0x60, - DAC_START_REG = 0x64, - DAC_BUFFER_CLEAR_REG = 0x66, /* clear dac buffer */ -}; - -static inline unsigned int dac_convert_reg(unsigned int channel) -{ - return 0x70 + (2 * (channel & 0x1)); -} - -static inline unsigned int dac_lsb_4020_reg(unsigned int channel) -{ - return 0x70 + (4 * (channel & 0x1)); -} - -static inline unsigned int dac_msb_4020_reg(unsigned int channel) -{ - return 0x72 + (4 * (channel & 0x1)); -} - -enum read_only_registers { - /* - * hardware status register, - * reading this apparently clears pending interrupts as well - */ - HW_STATUS_REG = 0x0, - PIPE1_READ_REG = 0x4, - ADC_READ_PNTR_REG = 0x8, - LOWER_XFER_REG = 0x10, - ADC_WRITE_PNTR_REG = 0xc, - PREPOST_REG = 0x14, -}; - -enum read_write_registers { - I8255_4020_REG = 0x48, /* 8255 offset, for 4020 only */ - /* external channel/gain queue, uses same bits as ADC_QUEUE_LOAD_REG */ - ADC_QUEUE_FIFO_REG = 0x100, - ADC_FIFO_REG = 0x200, /* adc data fifo */ - /* dac data fifo, has weird interactions with external channel queue */ - DAC_FIFO_REG = 0x300, -}; - -/* dev->mmio registers */ -enum dio_counter_registers { - DIO_8255_OFFSET = 0x0, - DO_REG = 0x20, - DI_REG = 0x28, - DIO_DIRECTION_60XX_REG = 0x40, - DIO_DATA_60XX_REG = 0x48, -}; - -/* bit definitions for write-only registers */ - -enum intr_enable_contents { - ADC_INTR_SRC_MASK = 0x3, /* adc interrupt source mask */ - ADC_INTR_QFULL_BITS = 0x0, /* interrupt fifo quarter full */ - ADC_INTR_EOC_BITS = 0x1, /* interrupt end of conversion */ - ADC_INTR_EOSCAN_BITS = 0x2, /* interrupt end of scan */ - ADC_INTR_EOSEQ_BITS = 0x3, /* interrupt end of sequence mask */ - EN_ADC_INTR_SRC_BIT = 0x4, /* enable adc interrupt source */ - EN_ADC_DONE_INTR_BIT = 0x8, /* enable adc acquisition done intr */ - DAC_INTR_SRC_MASK = 0x30, - DAC_INTR_QEMPTY_BITS = 0x0, - DAC_INTR_HIGH_CHAN_BITS = 0x10, - EN_DAC_INTR_SRC_BIT = 0x40, /* enable dac interrupt source */ - EN_DAC_DONE_INTR_BIT = 0x80, - EN_ADC_ACTIVE_INTR_BIT = 0x200, /* enable adc active interrupt */ - EN_ADC_STOP_INTR_BIT = 0x400, /* enable adc stop trigger interrupt */ - EN_DAC_ACTIVE_INTR_BIT = 0x800, /* enable dac active interrupt */ - EN_DAC_UNDERRUN_BIT = 0x4000, /* enable dac underrun status bit */ - EN_ADC_OVERRUN_BIT = 0x8000, /* enable adc overrun status bit */ -}; - -enum hw_config_contents { - MASTER_CLOCK_4020_MASK = 0x3, /* master clock source mask for 4020 */ - INTERNAL_CLOCK_4020_BITS = 0x1, /* use 40 MHz internal master clock */ - BNC_CLOCK_4020_BITS = 0x2, /* use BNC input for master clock */ - EXT_CLOCK_4020_BITS = 0x3, /* use dio input for master clock */ - EXT_QUEUE_BIT = 0x200, /* use external channel/gain queue */ - /* use 225 nanosec strobe when loading dac instead of 50 nanosec */ - SLOW_DAC_BIT = 0x400, - /* - * bit with unknown function yet given as default value in pci-das64 - * manual - */ - HW_CONFIG_DUMMY_BITS = 0x2000, - /* bit selects channels 1/0 for analog input/output, otherwise 0/1 */ - DMA_CH_SELECT_BIT = 0x8000, - FIFO_SIZE_REG = 0x4, /* allows adjustment of fifo sizes */ - DAC_FIFO_SIZE_MASK = 0xff00, /* bits that set dac fifo size */ - DAC_FIFO_BITS = 0xf800, /* 8k sample ao fifo */ -}; - -enum daq_atrig_low_4020_contents { - /* use trig/ext clk bnc input for analog gate signal */ - EXT_AGATE_BNC_BIT = 0x8000, - /* use trig/ext clk bnc input for external stop trigger signal */ - EXT_STOP_TRIG_BNC_BIT = 0x4000, - /* use trig/ext clk bnc input for external start trigger signal */ - EXT_START_TRIG_BNC_BIT = 0x2000, -}; - -enum adc_control0_contents { - ADC_GATE_SRC_MASK = 0x3, /* bits that select gate */ - ADC_SOFT_GATE_BITS = 0x1, /* software gate */ - ADC_EXT_GATE_BITS = 0x2, /* external digital gate */ - ADC_ANALOG_GATE_BITS = 0x3, /* analog level gate */ - /* level-sensitive gate (for digital) */ - ADC_GATE_LEVEL_BIT = 0x4, - ADC_GATE_POLARITY_BIT = 0x8, /* gate active low */ - ADC_START_TRIG_SOFT_BITS = 0x10, - ADC_START_TRIG_EXT_BITS = 0x20, - ADC_START_TRIG_ANALOG_BITS = 0x30, - ADC_START_TRIG_MASK = 0x30, - ADC_START_TRIG_FALLING_BIT = 0x40, /* trig 1 uses falling edge */ - /* external pacing uses falling edge */ - ADC_EXT_CONV_FALLING_BIT = 0x800, - /* enable hardware scan counter */ - ADC_SAMPLE_COUNTER_EN_BIT = 0x1000, - ADC_DMA_DISABLE_BIT = 0x4000, /* disables dma */ - ADC_ENABLE_BIT = 0x8000, /* master adc enable */ -}; - -enum adc_control1_contents { - /* should be set for boards with > 16 channels */ - ADC_QUEUE_CONFIG_BIT = 0x1, - CONVERT_POLARITY_BIT = 0x10, - EOC_POLARITY_BIT = 0x20, - ADC_SW_GATE_BIT = 0x40, /* software gate of adc */ - ADC_DITHER_BIT = 0x200, /* turn on extra noise for dithering */ - RETRIGGER_BIT = 0x800, - ADC_LO_CHANNEL_4020_MASK = 0x300, - ADC_HI_CHANNEL_4020_MASK = 0xc00, - TWO_CHANNEL_4020_BITS = 0x1000, /* two channel mode for 4020 */ - FOUR_CHANNEL_4020_BITS = 0x2000, /* four channel mode for 4020 */ - CHANNEL_MODE_4020_MASK = 0x3000, - ADC_MODE_MASK = 0xf000, -}; - -static inline u16 adc_lo_chan_4020_bits(unsigned int channel) -{ - return (channel & 0x3) << 8; -}; - -static inline u16 adc_hi_chan_4020_bits(unsigned int channel) -{ - return (channel & 0x3) << 10; -}; - -static inline u16 adc_mode_bits(unsigned int mode) -{ - return (mode & 0xf) << 12; -}; - -enum calibration_contents { - SELECT_8800_BIT = 0x1, - SELECT_8402_64XX_BIT = 0x2, - SELECT_1590_60XX_BIT = 0x2, - CAL_EN_64XX_BIT = 0x40, /* calibration enable for 64xx series */ - SERIAL_DATA_IN_BIT = 0x80, - SERIAL_CLOCK_BIT = 0x100, - CAL_EN_60XX_BIT = 0x200, /* calibration enable for 60xx series */ - CAL_GAIN_BIT = 0x800, -}; - -/* - * calibration sources for 6025 are: - * 0 : ground - * 1 : 10V - * 2 : 5V - * 3 : 0.5V - * 4 : 0.05V - * 5 : ground - * 6 : dac channel 0 - * 7 : dac channel 1 - */ - -static inline u16 adc_src_bits(unsigned int source) -{ - return (source & 0xf) << 3; -}; - -static inline u16 adc_convert_chan_4020_bits(unsigned int channel) -{ - return (channel & 0x3) << 8; -}; - -enum adc_queue_load_contents { - UNIP_BIT = 0x800, /* unipolar/bipolar bit */ - ADC_SE_DIFF_BIT = 0x1000, /* single-ended/ differential bit */ - /* non-referenced single-ended (common-mode input) */ - ADC_COMMON_BIT = 0x2000, - QUEUE_EOSEQ_BIT = 0x4000, /* queue end of sequence */ - QUEUE_EOSCAN_BIT = 0x8000, /* queue end of scan */ -}; - -static inline u16 adc_chan_bits(unsigned int channel) -{ - return channel & 0x3f; -}; - -enum dac_control0_contents { - DAC_ENABLE_BIT = 0x8000, /* dac controller enable bit */ - DAC_CYCLIC_STOP_BIT = 0x4000, - DAC_WAVEFORM_MODE_BIT = 0x100, - DAC_EXT_UPDATE_FALLING_BIT = 0x80, - DAC_EXT_UPDATE_ENABLE_BIT = 0x40, - WAVEFORM_TRIG_MASK = 0x30, - WAVEFORM_TRIG_DISABLED_BITS = 0x0, - WAVEFORM_TRIG_SOFT_BITS = 0x10, - WAVEFORM_TRIG_EXT_BITS = 0x20, - WAVEFORM_TRIG_ADC1_BITS = 0x30, - WAVEFORM_TRIG_FALLING_BIT = 0x8, - WAVEFORM_GATE_LEVEL_BIT = 0x4, - WAVEFORM_GATE_ENABLE_BIT = 0x2, - WAVEFORM_GATE_SELECT_BIT = 0x1, -}; - -enum dac_control1_contents { - DAC_WRITE_POLARITY_BIT = 0x800, /* board-dependent setting */ - DAC1_EXT_REF_BIT = 0x200, - DAC0_EXT_REF_BIT = 0x100, - DAC_OUTPUT_ENABLE_BIT = 0x80, /* dac output enable bit */ - DAC_UPDATE_POLARITY_BIT = 0x40, /* board-dependent setting */ - DAC_SW_GATE_BIT = 0x20, - DAC1_UNIPOLAR_BIT = 0x8, - DAC0_UNIPOLAR_BIT = 0x2, -}; - -/* bit definitions for read-only registers */ -enum hw_status_contents { - DAC_UNDERRUN_BIT = 0x1, - ADC_OVERRUN_BIT = 0x2, - DAC_ACTIVE_BIT = 0x4, - ADC_ACTIVE_BIT = 0x8, - DAC_INTR_PENDING_BIT = 0x10, - ADC_INTR_PENDING_BIT = 0x20, - DAC_DONE_BIT = 0x40, - ADC_DONE_BIT = 0x80, - EXT_INTR_PENDING_BIT = 0x100, - ADC_STOP_BIT = 0x200, -}; - -static inline u16 pipe_full_bits(u16 hw_status_bits) -{ - return (hw_status_bits >> 10) & 0x3; -}; - -static inline unsigned int dma_chain_flag_bits(u16 prepost_bits) -{ - return (prepost_bits >> 6) & 0x3; -} - -static inline unsigned int adc_upper_read_ptr_code(u16 prepost_bits) -{ - return (prepost_bits >> 12) & 0x3; -} - -static inline unsigned int adc_upper_write_ptr_code(u16 prepost_bits) -{ - return (prepost_bits >> 14) & 0x3; -} - -/* I2C addresses for 4020 */ -enum i2c_addresses { - RANGE_CAL_I2C_ADDR = 0x20, - CALDAC0_I2C_ADDR = 0xc, - CALDAC1_I2C_ADDR = 0xd, -}; - -enum range_cal_i2c_contents { - /* bits that set what source the adc converter measures */ - ADC_SRC_4020_MASK = 0x70, - /* make bnc trig/ext clock threshold 0V instead of 2.5V */ - BNC_TRIG_THRESHOLD_0V_BIT = 0x80, -}; - -static inline u8 adc_src_4020_bits(unsigned int source) -{ - return (source << 4) & ADC_SRC_4020_MASK; -}; - -static inline u8 attenuate_bit(unsigned int channel) -{ - /* attenuate channel (+-5V input range) */ - return 1 << (channel & 0x3); -}; - -/* analog input ranges for 64xx boards */ -static const struct comedi_lrange ai_ranges_64xx = { - 8, { - BIP_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(2.5), - BIP_RANGE(1.25), - UNI_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(2.5), - UNI_RANGE(1.25) - } -}; - -static const u8 ai_range_code_64xx[8] = { - 0x0, 0x1, 0x2, 0x3, /* bipolar 10, 5, 2,5, 1.25 */ - 0x8, 0x9, 0xa, 0xb /* unipolar 10, 5, 2.5, 1.25 */ -}; - -/* analog input ranges for 64-Mx boards */ -static const struct comedi_lrange ai_ranges_64_mx = { - 7, { - BIP_RANGE(5), - BIP_RANGE(2.5), - BIP_RANGE(1.25), - BIP_RANGE(0.625), - UNI_RANGE(5), - UNI_RANGE(2.5), - UNI_RANGE(1.25) - } -}; - -static const u8 ai_range_code_64_mx[7] = { - 0x0, 0x1, 0x2, 0x3, /* bipolar 5, 2.5, 1.25, 0.625 */ - 0x9, 0xa, 0xb /* unipolar 5, 2.5, 1.25 */ -}; - -/* analog input ranges for 60xx boards */ -static const struct comedi_lrange ai_ranges_60xx = { - 4, { - BIP_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(0.5), - BIP_RANGE(0.05) - } -}; - -static const u8 ai_range_code_60xx[4] = { - 0x0, 0x1, 0x4, 0x7 /* bipolar 10, 5, 0.5, 0.05 */ -}; - -/* analog input ranges for 6030, etc boards */ -static const struct comedi_lrange ai_ranges_6030 = { - 14, { - BIP_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(2), - BIP_RANGE(1), - BIP_RANGE(0.5), - BIP_RANGE(0.2), - BIP_RANGE(0.1), - UNI_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(2), - UNI_RANGE(1), - UNI_RANGE(0.5), - UNI_RANGE(0.2), - UNI_RANGE(0.1) - } -}; - -static const u8 ai_range_code_6030[14] = { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, /* bip 10, 5, 2, 1, 0.5, 0.2, 0.1 */ - 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* uni 10, 5, 2, 1, 0.5, 0.2, 0.1 */ -}; - -/* analog input ranges for 6052, etc boards */ -static const struct comedi_lrange ai_ranges_6052 = { - 15, { - BIP_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(2.5), - BIP_RANGE(1), - BIP_RANGE(0.5), - BIP_RANGE(0.25), - BIP_RANGE(0.1), - BIP_RANGE(0.05), - UNI_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(2), - UNI_RANGE(1), - UNI_RANGE(0.5), - UNI_RANGE(0.2), - UNI_RANGE(0.1) - } -}; - -static const u8 ai_range_code_6052[15] = { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, /* bipolar 10 ... 0.05 */ - 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf /* unipolar 10 ... 0.1 */ -}; - -/* analog input ranges for 4020 board */ -static const struct comedi_lrange ai_ranges_4020 = { - 2, { - BIP_RANGE(5), - BIP_RANGE(1) - } -}; - -/* analog output ranges */ -static const struct comedi_lrange ao_ranges_64xx = { - 4, { - BIP_RANGE(5), - BIP_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(10) - } -}; - -static const int ao_range_code_64xx[] = { - 0x0, - 0x1, - 0x2, - 0x3, -}; - -static const int ao_range_code_60xx[] = { - 0x0, -}; - -static const struct comedi_lrange ao_ranges_6030 = { - 2, { - BIP_RANGE(10), - UNI_RANGE(10) - } -}; - -static const int ao_range_code_6030[] = { - 0x0, - 0x2, -}; - -static const struct comedi_lrange ao_ranges_4020 = { - 2, { - BIP_RANGE(5), - BIP_RANGE(10) - } -}; - -static const int ao_range_code_4020[] = { - 0x1, - 0x0, -}; - -enum register_layout { - LAYOUT_60XX, - LAYOUT_64XX, - LAYOUT_4020, -}; - -struct hw_fifo_info { - unsigned int num_segments; - unsigned int max_segment_length; - unsigned int sample_packing_ratio; - u16 fifo_size_reg_mask; -}; - -enum pcidas64_boardid { - BOARD_PCIDAS6402_16, - BOARD_PCIDAS6402_12, - BOARD_PCIDAS64_M1_16, - BOARD_PCIDAS64_M2_16, - BOARD_PCIDAS64_M3_16, - BOARD_PCIDAS6013, - BOARD_PCIDAS6014, - BOARD_PCIDAS6023, - BOARD_PCIDAS6025, - BOARD_PCIDAS6030, - BOARD_PCIDAS6031, - BOARD_PCIDAS6032, - BOARD_PCIDAS6033, - BOARD_PCIDAS6034, - BOARD_PCIDAS6035, - BOARD_PCIDAS6036, - BOARD_PCIDAS6040, - BOARD_PCIDAS6052, - BOARD_PCIDAS6070, - BOARD_PCIDAS6071, - BOARD_PCIDAS4020_12, - BOARD_PCIDAS6402_16_JR, - BOARD_PCIDAS64_M1_16_JR, - BOARD_PCIDAS64_M2_16_JR, - BOARD_PCIDAS64_M3_16_JR, - BOARD_PCIDAS64_M1_14, - BOARD_PCIDAS64_M2_14, - BOARD_PCIDAS64_M3_14, -}; - -struct pcidas64_board { - const char *name; - int ai_se_chans; /* number of ai inputs in single-ended mode */ - int ai_bits; /* analog input resolution */ - int ai_speed; /* fastest conversion period in ns */ - const struct comedi_lrange *ai_range_table; - const u8 *ai_range_code; - int ao_nchan; /* number of analog out channels */ - int ao_bits; /* analog output resolution */ - int ao_scan_speed; /* analog output scan speed */ - const struct comedi_lrange *ao_range_table; - const int *ao_range_code; - const struct hw_fifo_info *const ai_fifo; - /* different board families have slightly different registers */ - enum register_layout layout; - unsigned has_8255:1; -}; - -static const struct hw_fifo_info ai_fifo_4020 = { - .num_segments = 2, - .max_segment_length = 0x8000, - .sample_packing_ratio = 2, - .fifo_size_reg_mask = 0x7f, -}; - -static const struct hw_fifo_info ai_fifo_64xx = { - .num_segments = 4, - .max_segment_length = 0x800, - .sample_packing_ratio = 1, - .fifo_size_reg_mask = 0x3f, -}; - -static const struct hw_fifo_info ai_fifo_60xx = { - .num_segments = 4, - .max_segment_length = 0x800, - .sample_packing_ratio = 1, - .fifo_size_reg_mask = 0x7f, -}; - -/* - * maximum number of dma transfers we will chain together into a ring - * (and the maximum number of dma buffers we maintain) - */ -#define MAX_AI_DMA_RING_COUNT (0x80000 / DMA_BUFFER_SIZE) -#define MIN_AI_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE) -#define AO_DMA_RING_COUNT (0x10000 / DMA_BUFFER_SIZE) -static inline unsigned int ai_dma_ring_count(const struct pcidas64_board *board) -{ - if (board->layout == LAYOUT_4020) - return MAX_AI_DMA_RING_COUNT; - - return MIN_AI_DMA_RING_COUNT; -} - -static const int bytes_in_sample = 2; - -static const struct pcidas64_board pcidas64_boards[] = { - [BOARD_PCIDAS6402_16] = { - .name = "pci-das6402/16", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, - .ai_range_code = ai_range_code_64xx, - .ao_range_table = &ao_ranges_64xx, - .ao_range_code = ao_range_code_64xx, - .ai_fifo = &ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS6402_12] = { - .name = "pci-das6402/12", /* XXX check */ - .ai_se_chans = 64, - .ai_bits = 12, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, - .ai_range_code = ai_range_code_64xx, - .ao_range_table = &ao_ranges_64xx, - .ao_range_code = ao_range_code_64xx, - .ai_fifo = &ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M1_16] = { - .name = "pci-das64/m1/16", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 1000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ao_range_table = &ao_ranges_64xx, - .ao_range_code = ao_range_code_64xx, - .ai_fifo = &ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M2_16] = { - .name = "pci-das64/m2/16", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 500, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ao_range_table = &ao_ranges_64xx, - .ao_range_code = ao_range_code_64xx, - .ai_fifo = &ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M3_16] = { - .name = "pci-das64/m3/16", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 333, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ao_range_table = &ao_ranges_64xx, - .ao_range_code = ao_range_code_64xx, - .ai_fifo = &ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS6013] = { - .name = "pci-das6013", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 0, - .ao_bits = 16, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6014] = { - .name = "pci-das6014", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 100000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6023] = { - .name = "pci-das6023", - .ai_se_chans = 16, - .ai_bits = 12, - .ai_speed = 5000, - .ao_nchan = 0, - .ao_scan_speed = 100000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS6025] = { - .name = "pci-das6025", - .ai_se_chans = 16, - .ai_bits = 12, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 100000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS6030] = { - .name = "pci-das6030", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 10000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6030, - .ai_range_code = ai_range_code_6030, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6031] = { - .name = "pci-das6031", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 10000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 10000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6030, - .ai_range_code = ai_range_code_6030, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6032] = { - .name = "pci-das6032", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 10000, - .ao_nchan = 0, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6030, - .ai_range_code = ai_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6033] = { - .name = "pci-das6033", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 10000, - .ao_nchan = 0, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6030, - .ai_range_code = ai_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6034] = { - .name = "pci-das6034", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 0, - .ao_scan_speed = 0, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6035] = { - .name = "pci-das6035", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 100000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6036] = { - .name = "pci-das6036", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 100000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_60xx, - .ai_range_code = ai_range_code_60xx, - .ao_range_table = &range_bipolar10, - .ao_range_code = ao_range_code_60xx, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6040] = { - .name = "pci-das6040", - .ai_se_chans = 16, - .ai_bits = 12, - .ai_speed = 2000, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 1000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6052, - .ai_range_code = ai_range_code_6052, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6052] = { - .name = "pci-das6052", - .ai_se_chans = 16, - .ai_bits = 16, - .ai_speed = 3333, - .ao_nchan = 2, - .ao_bits = 16, - .ao_scan_speed = 3333, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6052, - .ai_range_code = ai_range_code_6052, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6070] = { - .name = "pci-das6070", - .ai_se_chans = 16, - .ai_bits = 12, - .ai_speed = 800, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 1000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6052, - .ai_range_code = ai_range_code_6052, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS6071] = { - .name = "pci-das6071", - .ai_se_chans = 64, - .ai_bits = 12, - .ai_speed = 800, - .ao_nchan = 2, - .ao_bits = 12, - .ao_scan_speed = 1000, - .layout = LAYOUT_60XX, - .ai_range_table = &ai_ranges_6052, - .ai_range_code = ai_range_code_6052, - .ao_range_table = &ao_ranges_6030, - .ao_range_code = ao_range_code_6030, - .ai_fifo = &ai_fifo_60xx, - .has_8255 = 0, - }, - [BOARD_PCIDAS4020_12] = { - .name = "pci-das4020/12", - .ai_se_chans = 4, - .ai_bits = 12, - .ai_speed = 50, - .ao_bits = 12, - .ao_nchan = 2, - .ao_scan_speed = 0, /* no hardware pacing on ao */ - .layout = LAYOUT_4020, - .ai_range_table = &ai_ranges_4020, - .ao_range_table = &ao_ranges_4020, - .ao_range_code = ao_range_code_4020, - .ai_fifo = &ai_fifo_4020, - .has_8255 = 1, - }, -#if 0 - /* The device id for these boards is unknown */ - - [BOARD_PCIDAS6402_16_JR] = { - .name = "pci-das6402/16/jr", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 5000, - .ao_nchan = 0, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64xx, - .ai_range_code = ai_range_code_64xx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M1_16_JR] = { - .name = "pci-das64/m1/16/jr", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 1000, - .ao_nchan = 0, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M2_16_JR] = { - .name = "pci-das64/m2/16/jr", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 500, - .ao_nchan = 0, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M3_16_JR] = { - .name = "pci-das64/m3/16/jr", - .ai_se_chans = 64, - .ai_bits = 16, - .ai_speed = 333, - .ao_nchan = 0, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M1_14] = { - .name = "pci-das64/m1/14", - .ai_se_chans = 64, - .ai_bits = 14, - .ai_speed = 1000, - .ao_nchan = 2, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M2_14] = { - .name = "pci-das64/m2/14", - .ai_se_chans = 64, - .ai_bits = 14, - .ai_speed = 500, - .ao_nchan = 2, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, - [BOARD_PCIDAS64_M3_14] = { - .name = "pci-das64/m3/14", - .ai_se_chans = 64, - .ai_bits = 14, - .ai_speed = 333, - .ao_nchan = 2, - .ao_scan_speed = 10000, - .layout = LAYOUT_64XX, - .ai_range_table = &ai_ranges_64_mx, - .ai_range_code = ai_range_code_64_mx, - .ai_fifo = ai_fifo_64xx, - .has_8255 = 1, - }, -#endif -}; - -static inline unsigned short se_diff_bit_6xxx(struct comedi_device *dev, - int use_differential) -{ - const struct pcidas64_board *board = dev->board_ptr; - - if ((board->layout == LAYOUT_64XX && !use_differential) || - (board->layout == LAYOUT_60XX && use_differential)) - return ADC_SE_DIFF_BIT; - - return 0; -} - -struct ext_clock_info { - /* master clock divisor to use for scans with external master clock */ - unsigned int divisor; - /* chanspec for master clock input when used as scan begin src */ - unsigned int chanspec; -}; - -/* this structure is for data unique to this hardware driver. */ -struct pcidas64_private { - /* base addresses (physical) */ - resource_size_t main_phys_iobase; - resource_size_t dio_counter_phys_iobase; - /* base addresses (ioremapped) */ - void __iomem *plx9080_iobase; - void __iomem *main_iobase; - /* local address (used by dma controller) */ - u32 local0_iobase; - u32 local1_iobase; - /* dma buffers for analog input */ - u16 *ai_buffer[MAX_AI_DMA_RING_COUNT]; - /* physical addresses of ai dma buffers */ - dma_addr_t ai_buffer_bus_addr[MAX_AI_DMA_RING_COUNT]; - /* - * array of ai dma descriptors read by plx9080, - * allocated to get proper alignment - */ - struct plx_dma_desc *ai_dma_desc; - /* physical address of ai dma descriptor array */ - dma_addr_t ai_dma_desc_bus_addr; - /* - * index of the ai dma descriptor/buffer - * that is currently being used - */ - unsigned int ai_dma_index; - /* dma buffers for analog output */ - u16 *ao_buffer[AO_DMA_RING_COUNT]; - /* physical addresses of ao dma buffers */ - dma_addr_t ao_buffer_bus_addr[AO_DMA_RING_COUNT]; - struct plx_dma_desc *ao_dma_desc; - dma_addr_t ao_dma_desc_bus_addr; - /* keeps track of buffer where the next ao sample should go */ - unsigned int ao_dma_index; - unsigned int hw_revision; /* stc chip hardware revision number */ - /* last bits sent to INTR_ENABLE_REG register */ - unsigned int intr_enable_bits; - /* last bits sent to ADC_CONTROL1_REG register */ - u16 adc_control1_bits; - /* last bits sent to FIFO_SIZE_REG register */ - u16 fifo_size_bits; - /* last bits sent to HW_CONFIG_REG register */ - u16 hw_config_bits; - u16 dac_control1_bits; - /* last bits written to plx9080 control register */ - u32 plx_control_bits; - /* last bits written to plx interrupt control and status register */ - u32 plx_intcsr_bits; - /* index of calibration source readable through ai ch0 */ - int calibration_source; - /* bits written to i2c calibration/range register */ - u8 i2c_cal_range_bits; - /* configure digital triggers to trigger on falling edge */ - unsigned int ext_trig_falling; - short ai_cmd_running; - unsigned int ai_fifo_segment_length; - struct ext_clock_info ext_clock; - unsigned short ao_bounce_buffer[DAC_FIFO_SIZE]; -}; - -static unsigned int ai_range_bits_6xxx(const struct comedi_device *dev, - unsigned int range_index) -{ - const struct pcidas64_board *board = dev->board_ptr; - - return board->ai_range_code[range_index] << 8; -} - -static unsigned int hw_revision(const struct comedi_device *dev, - u16 hw_status_bits) -{ - const struct pcidas64_board *board = dev->board_ptr; - - if (board->layout == LAYOUT_4020) - return (hw_status_bits >> 13) & 0x7; - - return (hw_status_bits >> 12) & 0xf; -} - -static void set_dac_range_bits(struct comedi_device *dev, - u16 *bits, unsigned int channel, - unsigned int range) -{ - const struct pcidas64_board *board = dev->board_ptr; - unsigned int code = board->ao_range_code[range]; - - if (channel > 1) - dev_err(dev->class_dev, "bug! bad channel?\n"); - if (code & ~0x3) - dev_err(dev->class_dev, "bug! bad range code?\n"); - - *bits &= ~(0x3 << (2 * channel)); - *bits |= code << (2 * channel); -}; - -static inline int ao_cmd_is_supported(const struct pcidas64_board *board) -{ - return board->ao_nchan && board->layout != LAYOUT_4020; -} - -static void abort_dma(struct comedi_device *dev, unsigned int channel) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned long flags; - - /* spinlock for plx dma control/status reg */ - spin_lock_irqsave(&dev->spinlock, flags); - - plx9080_abort_dma(devpriv->plx9080_iobase, channel); - - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -static void disable_plx_interrupts(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - - devpriv->plx_intcsr_bits = 0; - writel(devpriv->plx_intcsr_bits, - devpriv->plx9080_iobase + PLX_REG_INTCSR); -} - -static void disable_ai_interrupts(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned long flags; - - spin_lock_irqsave(&dev->spinlock, flags); - devpriv->intr_enable_bits &= - ~EN_ADC_INTR_SRC_BIT & ~EN_ADC_DONE_INTR_BIT & - ~EN_ADC_ACTIVE_INTR_BIT & ~EN_ADC_STOP_INTR_BIT & - ~EN_ADC_OVERRUN_BIT & ~ADC_INTR_SRC_MASK; - writew(devpriv->intr_enable_bits, - devpriv->main_iobase + INTR_ENABLE_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -static void enable_ai_interrupts(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - u32 bits; - unsigned long flags; - - bits = EN_ADC_OVERRUN_BIT | EN_ADC_DONE_INTR_BIT | - EN_ADC_ACTIVE_INTR_BIT | EN_ADC_STOP_INTR_BIT; - /* - * Use pio transfer and interrupt on end of conversion - * if CMDF_WAKE_EOS flag is set. - */ - if (cmd->flags & CMDF_WAKE_EOS) { - /* 4020 doesn't support pio transfers except for fifo dregs */ - if (board->layout != LAYOUT_4020) - bits |= ADC_INTR_EOSCAN_BITS | EN_ADC_INTR_SRC_BIT; - } - spin_lock_irqsave(&dev->spinlock, flags); - devpriv->intr_enable_bits |= bits; - writew(devpriv->intr_enable_bits, - devpriv->main_iobase + INTR_ENABLE_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -/* initialize plx9080 chip */ -static void init_plx9080(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - u32 bits; - void __iomem *plx_iobase = devpriv->plx9080_iobase; - - devpriv->plx_control_bits = - readl(devpriv->plx9080_iobase + PLX_REG_CNTRL); - -#ifdef __BIG_ENDIAN - bits = PLX_BIGEND_DMA0 | PLX_BIGEND_DMA1; -#else - bits = 0; -#endif - writel(bits, devpriv->plx9080_iobase + PLX_REG_BIGEND); - - disable_plx_interrupts(dev); - - abort_dma(dev, 0); - abort_dma(dev, 1); - - /* configure dma0 mode */ - bits = 0; - /* enable ready input, not sure if this is necessary */ - bits |= PLX_DMAMODE_READYIEN; - /* enable bterm, not sure if this is necessary */ - bits |= PLX_DMAMODE_BTERMIEN; - /* enable dma chaining */ - bits |= PLX_DMAMODE_CHAINEN; - /* - * enable interrupt on dma done - * (probably don't need this, since chain never finishes) - */ - bits |= PLX_DMAMODE_DONEIEN; - /* - * don't increment local address during transfers - * (we are transferring from a fixed fifo register) - */ - bits |= PLX_DMAMODE_LACONST; - /* route dma interrupt to pci bus */ - bits |= PLX_DMAMODE_INTRPCI; - /* enable demand mode */ - bits |= PLX_DMAMODE_DEMAND; - /* enable local burst mode */ - bits |= PLX_DMAMODE_BURSTEN; - /* 4020 uses 32 bit dma */ - if (board->layout == LAYOUT_4020) - bits |= PLX_DMAMODE_WIDTH_32; - else /* localspace0 bus is 16 bits wide */ - bits |= PLX_DMAMODE_WIDTH_16; - writel(bits, plx_iobase + PLX_REG_DMAMODE1); - if (ao_cmd_is_supported(board)) - writel(bits, plx_iobase + PLX_REG_DMAMODE0); - - /* enable interrupts on plx 9080 */ - devpriv->plx_intcsr_bits |= - PLX_INTCSR_LSEABORTEN | PLX_INTCSR_LSEPARITYEN | PLX_INTCSR_PIEN | - PLX_INTCSR_PLIEN | PLX_INTCSR_PABORTIEN | PLX_INTCSR_LIOEN | - PLX_INTCSR_DMA0IEN | PLX_INTCSR_DMA1IEN; - writel(devpriv->plx_intcsr_bits, - devpriv->plx9080_iobase + PLX_REG_INTCSR); -} - -static void disable_ai_pacing(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned long flags; - - disable_ai_interrupts(dev); - - spin_lock_irqsave(&dev->spinlock, flags); - devpriv->adc_control1_bits &= ~ADC_SW_GATE_BIT; - writew(devpriv->adc_control1_bits, - devpriv->main_iobase + ADC_CONTROL1_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* disable pacing, triggering, etc */ - writew(ADC_DMA_DISABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT, - devpriv->main_iobase + ADC_CONTROL0_REG); -} - -static int set_ai_fifo_segment_length(struct comedi_device *dev, - unsigned int num_entries) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - static const int increment_size = 0x100; - const struct hw_fifo_info *const fifo = board->ai_fifo; - unsigned int num_increments; - u16 bits; - - if (num_entries < increment_size) - num_entries = increment_size; - if (num_entries > fifo->max_segment_length) - num_entries = fifo->max_segment_length; - - /* 1 == 256 entries, 2 == 512 entries, etc */ - num_increments = DIV_ROUND_CLOSEST(num_entries, increment_size); - - bits = (~(num_increments - 1)) & fifo->fifo_size_reg_mask; - devpriv->fifo_size_bits &= ~fifo->fifo_size_reg_mask; - devpriv->fifo_size_bits |= bits; - writew(devpriv->fifo_size_bits, - devpriv->main_iobase + FIFO_SIZE_REG); - - devpriv->ai_fifo_segment_length = num_increments * increment_size; - - return devpriv->ai_fifo_segment_length; -} - -/* - * adjusts the size of hardware fifo (which determines block size for dma xfers) - */ -static int set_ai_fifo_size(struct comedi_device *dev, unsigned int num_samples) -{ - const struct pcidas64_board *board = dev->board_ptr; - unsigned int num_fifo_entries; - int retval; - const struct hw_fifo_info *const fifo = board->ai_fifo; - - num_fifo_entries = num_samples / fifo->sample_packing_ratio; - - retval = set_ai_fifo_segment_length(dev, - num_fifo_entries / - fifo->num_segments); - if (retval < 0) - return retval; - - return retval * fifo->num_segments * fifo->sample_packing_ratio; -} - -/* query length of fifo */ -static unsigned int ai_fifo_size(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - - return devpriv->ai_fifo_segment_length * - board->ai_fifo->num_segments * - board->ai_fifo->sample_packing_ratio; -} - -static void init_stc_registers(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - u16 bits; - unsigned long flags; - - spin_lock_irqsave(&dev->spinlock, flags); - - /* - * bit should be set for 6025, - * although docs say boards with <= 16 chans should be cleared XXX - */ - if (1) - devpriv->adc_control1_bits |= ADC_QUEUE_CONFIG_BIT; - writew(devpriv->adc_control1_bits, - devpriv->main_iobase + ADC_CONTROL1_REG); - - /* 6402/16 manual says this register must be initialized to 0xff? */ - writew(0xff, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG); - - bits = SLOW_DAC_BIT | DMA_CH_SELECT_BIT; - if (board->layout == LAYOUT_4020) - bits |= INTERNAL_CLOCK_4020_BITS; - devpriv->hw_config_bits |= bits; - writew(devpriv->hw_config_bits, - devpriv->main_iobase + HW_CONFIG_REG); - - writew(0, devpriv->main_iobase + DAQ_SYNC_REG); - writew(0, devpriv->main_iobase + CALIBRATION_REG); - - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* set fifos to maximum size */ - devpriv->fifo_size_bits |= DAC_FIFO_BITS; - set_ai_fifo_segment_length(dev, board->ai_fifo->max_segment_length); - - devpriv->dac_control1_bits = DAC_OUTPUT_ENABLE_BIT; - devpriv->intr_enable_bits = - /* EN_DAC_INTR_SRC_BIT | DAC_INTR_QEMPTY_BITS | */ - EN_DAC_DONE_INTR_BIT | EN_DAC_UNDERRUN_BIT; - writew(devpriv->intr_enable_bits, - devpriv->main_iobase + INTR_ENABLE_REG); - - disable_ai_pacing(dev); -}; - -static int alloc_and_init_dma_members(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pci_dev *pcidev = comedi_to_pci_dev(dev); - struct pcidas64_private *devpriv = dev->private; - int i; - - /* allocate pci dma buffers */ - for (i = 0; i < ai_dma_ring_count(board); i++) { - devpriv->ai_buffer[i] = - dma_alloc_coherent(&pcidev->dev, DMA_BUFFER_SIZE, - &devpriv->ai_buffer_bus_addr[i], - GFP_KERNEL); - if (!devpriv->ai_buffer[i]) - return -ENOMEM; - } - for (i = 0; i < AO_DMA_RING_COUNT; i++) { - if (ao_cmd_is_supported(board)) { - devpriv->ao_buffer[i] = - dma_alloc_coherent(&pcidev->dev, - DMA_BUFFER_SIZE, - &devpriv->ao_buffer_bus_addr[i], - GFP_KERNEL); - if (!devpriv->ao_buffer[i]) - return -ENOMEM; - } - } - /* allocate dma descriptors */ - devpriv->ai_dma_desc = - dma_alloc_coherent(&pcidev->dev, sizeof(struct plx_dma_desc) * - ai_dma_ring_count(board), - &devpriv->ai_dma_desc_bus_addr, GFP_KERNEL); - if (!devpriv->ai_dma_desc) - return -ENOMEM; - - if (ao_cmd_is_supported(board)) { - devpriv->ao_dma_desc = - dma_alloc_coherent(&pcidev->dev, - sizeof(struct plx_dma_desc) * - AO_DMA_RING_COUNT, - &devpriv->ao_dma_desc_bus_addr, - GFP_KERNEL); - if (!devpriv->ao_dma_desc) - return -ENOMEM; - } - /* initialize dma descriptors */ - for (i = 0; i < ai_dma_ring_count(board); i++) { - devpriv->ai_dma_desc[i].pci_start_addr = - cpu_to_le32(devpriv->ai_buffer_bus_addr[i]); - if (board->layout == LAYOUT_4020) - devpriv->ai_dma_desc[i].local_start_addr = - cpu_to_le32(devpriv->local1_iobase + - ADC_FIFO_REG); - else - devpriv->ai_dma_desc[i].local_start_addr = - cpu_to_le32(devpriv->local0_iobase + - ADC_FIFO_REG); - devpriv->ai_dma_desc[i].transfer_size = cpu_to_le32(0); - devpriv->ai_dma_desc[i].next = - cpu_to_le32((devpriv->ai_dma_desc_bus_addr + - ((i + 1) % ai_dma_ring_count(board)) * - sizeof(devpriv->ai_dma_desc[0])) | - PLX_DMADPR_DESCPCI | PLX_DMADPR_TCINTR | - PLX_DMADPR_XFERL2P); - } - if (ao_cmd_is_supported(board)) { - for (i = 0; i < AO_DMA_RING_COUNT; i++) { - devpriv->ao_dma_desc[i].pci_start_addr = - cpu_to_le32(devpriv->ao_buffer_bus_addr[i]); - devpriv->ao_dma_desc[i].local_start_addr = - cpu_to_le32(devpriv->local0_iobase + - DAC_FIFO_REG); - devpriv->ao_dma_desc[i].transfer_size = cpu_to_le32(0); - devpriv->ao_dma_desc[i].next = - cpu_to_le32((devpriv->ao_dma_desc_bus_addr + - ((i + 1) % (AO_DMA_RING_COUNT)) * - sizeof(devpriv->ao_dma_desc[0])) | - PLX_DMADPR_DESCPCI | - PLX_DMADPR_TCINTR); - } - } - return 0; -} - -static void cb_pcidas64_free_dma(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pci_dev *pcidev = comedi_to_pci_dev(dev); - struct pcidas64_private *devpriv = dev->private; - int i; - - if (!devpriv) - return; - - /* free pci dma buffers */ - for (i = 0; i < ai_dma_ring_count(board); i++) { - if (devpriv->ai_buffer[i]) - dma_free_coherent(&pcidev->dev, - DMA_BUFFER_SIZE, - devpriv->ai_buffer[i], - devpriv->ai_buffer_bus_addr[i]); - } - for (i = 0; i < AO_DMA_RING_COUNT; i++) { - if (devpriv->ao_buffer[i]) - dma_free_coherent(&pcidev->dev, - DMA_BUFFER_SIZE, - devpriv->ao_buffer[i], - devpriv->ao_buffer_bus_addr[i]); - } - /* free dma descriptors */ - if (devpriv->ai_dma_desc) - dma_free_coherent(&pcidev->dev, - sizeof(struct plx_dma_desc) * - ai_dma_ring_count(board), - devpriv->ai_dma_desc, - devpriv->ai_dma_desc_bus_addr); - if (devpriv->ao_dma_desc) - dma_free_coherent(&pcidev->dev, - sizeof(struct plx_dma_desc) * - AO_DMA_RING_COUNT, - devpriv->ao_dma_desc, - devpriv->ao_dma_desc_bus_addr); -} - -static inline void warn_external_queue(struct comedi_device *dev) -{ - dev_err(dev->class_dev, - "AO command and AI external channel queue cannot be used simultaneously\n"); - dev_err(dev->class_dev, - "Use internal AI channel queue (channels must be consecutive and use same range/aref)\n"); -} - -/* - * their i2c requires a huge delay on setting clock or data high for some reason - */ -static const int i2c_high_udelay = 1000; -static const int i2c_low_udelay = 10; - -/* set i2c data line high or low */ -static void i2c_set_sda(struct comedi_device *dev, int state) -{ - struct pcidas64_private *devpriv = dev->private; - static const int data_bit = PLX_CNTRL_EEWB; - void __iomem *plx_control_addr = devpriv->plx9080_iobase + - PLX_REG_CNTRL; - - if (state) { /* set data line high */ - devpriv->plx_control_bits &= ~data_bit; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(i2c_high_udelay); - } else { /* set data line low */ - devpriv->plx_control_bits |= data_bit; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(i2c_low_udelay); - } -} - -/* set i2c clock line high or low */ -static void i2c_set_scl(struct comedi_device *dev, int state) -{ - struct pcidas64_private *devpriv = dev->private; - static const int clock_bit = PLX_CNTRL_USERO; - void __iomem *plx_control_addr = devpriv->plx9080_iobase + - PLX_REG_CNTRL; - - if (state) { /* set clock line high */ - devpriv->plx_control_bits &= ~clock_bit; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(i2c_high_udelay); - } else { /* set clock line low */ - devpriv->plx_control_bits |= clock_bit; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(i2c_low_udelay); - } -} - -static void i2c_write_byte(struct comedi_device *dev, u8 byte) -{ - u8 bit; - unsigned int num_bits = 8; - - for (bit = 1 << (num_bits - 1); bit; bit >>= 1) { - i2c_set_scl(dev, 0); - if ((byte & bit)) - i2c_set_sda(dev, 1); - else - i2c_set_sda(dev, 0); - i2c_set_scl(dev, 1); - } -} - -/* we can't really read the lines, so fake it */ -static int i2c_read_ack(struct comedi_device *dev) -{ - i2c_set_scl(dev, 0); - i2c_set_sda(dev, 1); - i2c_set_scl(dev, 1); - - return 0; /* return fake acknowledge bit */ -} - -/* send start bit */ -static void i2c_start(struct comedi_device *dev) -{ - i2c_set_scl(dev, 1); - i2c_set_sda(dev, 1); - i2c_set_sda(dev, 0); -} - -/* send stop bit */ -static void i2c_stop(struct comedi_device *dev) -{ - i2c_set_scl(dev, 0); - i2c_set_sda(dev, 0); - i2c_set_scl(dev, 1); - i2c_set_sda(dev, 1); -} - -static void i2c_write(struct comedi_device *dev, unsigned int address, - const u8 *data, unsigned int length) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int i; - u8 bitstream; - static const int read_bit = 0x1; - - /* - * XXX need mutex to prevent simultaneous attempts to access - * eeprom and i2c bus - */ - - /* make sure we don't send anything to eeprom */ - devpriv->plx_control_bits &= ~PLX_CNTRL_EECS; - - i2c_stop(dev); - i2c_start(dev); - - /* send address and write bit */ - bitstream = (address << 1) & ~read_bit; - i2c_write_byte(dev, bitstream); - - /* get acknowledge */ - if (i2c_read_ack(dev) != 0) { - dev_err(dev->class_dev, "failed: no acknowledge\n"); - i2c_stop(dev); - return; - } - /* write data bytes */ - for (i = 0; i < length; i++) { - i2c_write_byte(dev, data[i]); - if (i2c_read_ack(dev) != 0) { - dev_err(dev->class_dev, "failed: no acknowledge\n"); - i2c_stop(dev); - return; - } - } - i2c_stop(dev); -} - -static int cb_pcidas64_ai_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned int status; - - status = readw(devpriv->main_iobase + HW_STATUS_REG); - if (board->layout == LAYOUT_4020) { - status = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG); - if (status) - return 0; - } else { - if (pipe_full_bits(status)) - return 0; - } - return -EBUSY; -} - -static int ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned int bits = 0, n; - unsigned int channel, range, aref; - unsigned long flags; - int ret; - - channel = CR_CHAN(insn->chanspec); - range = CR_RANGE(insn->chanspec); - aref = CR_AREF(insn->chanspec); - - /* disable card's analog input interrupt sources and pacing */ - /* 4020 generates dac done interrupts even though they are disabled */ - disable_ai_pacing(dev); - - spin_lock_irqsave(&dev->spinlock, flags); - if (insn->chanspec & CR_ALT_FILTER) - devpriv->adc_control1_bits |= ADC_DITHER_BIT; - else - devpriv->adc_control1_bits &= ~ADC_DITHER_BIT; - writew(devpriv->adc_control1_bits, - devpriv->main_iobase + ADC_CONTROL1_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); - - if (board->layout != LAYOUT_4020) { - /* use internal queue */ - devpriv->hw_config_bits &= ~EXT_QUEUE_BIT; - writew(devpriv->hw_config_bits, - devpriv->main_iobase + HW_CONFIG_REG); - - /* ALT_SOURCE is internal calibration reference */ - if (insn->chanspec & CR_ALT_SOURCE) { - unsigned int cal_en_bit; - - if (board->layout == LAYOUT_60XX) - cal_en_bit = CAL_EN_60XX_BIT; - else - cal_en_bit = CAL_EN_64XX_BIT; - /* - * select internal reference source to connect - * to channel 0 - */ - writew(cal_en_bit | - adc_src_bits(devpriv->calibration_source), - devpriv->main_iobase + CALIBRATION_REG); - } else { - /* - * make sure internal calibration source - * is turned off - */ - writew(0, devpriv->main_iobase + CALIBRATION_REG); - } - /* load internal queue */ - bits = 0; - /* set gain */ - bits |= ai_range_bits_6xxx(dev, CR_RANGE(insn->chanspec)); - /* set single-ended / differential */ - bits |= se_diff_bit_6xxx(dev, aref == AREF_DIFF); - if (aref == AREF_COMMON) - bits |= ADC_COMMON_BIT; - bits |= adc_chan_bits(channel); - /* set stop channel */ - writew(adc_chan_bits(channel), - devpriv->main_iobase + ADC_QUEUE_HIGH_REG); - /* set start channel, and rest of settings */ - writew(bits, devpriv->main_iobase + ADC_QUEUE_LOAD_REG); - } else { - u8 old_cal_range_bits = devpriv->i2c_cal_range_bits; - - devpriv->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK; - if (insn->chanspec & CR_ALT_SOURCE) { - devpriv->i2c_cal_range_bits |= - adc_src_4020_bits(devpriv->calibration_source); - } else { /* select BNC inputs */ - devpriv->i2c_cal_range_bits |= adc_src_4020_bits(4); - } - /* select range */ - if (range == 0) - devpriv->i2c_cal_range_bits |= attenuate_bit(channel); - else - devpriv->i2c_cal_range_bits &= ~attenuate_bit(channel); - /* - * update calibration/range i2c register only if necessary, - * as it is very slow - */ - if (old_cal_range_bits != devpriv->i2c_cal_range_bits) { - u8 i2c_data = devpriv->i2c_cal_range_bits; - - i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data, - sizeof(i2c_data)); - } - - /* - * 4020 manual asks that sample interval register to be set - * before writing to convert register. - * Using somewhat arbitrary setting of 4 master clock ticks - * = 0.1 usec - */ - writew(0, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG); - writew(2, devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG); - } - - for (n = 0; n < insn->n; n++) { - /* clear adc buffer (inside loop for 4020 sake) */ - writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG); - - /* trigger conversion, bits sent only matter for 4020 */ - writew(adc_convert_chan_4020_bits(CR_CHAN(insn->chanspec)), - devpriv->main_iobase + ADC_CONVERT_REG); - - /* wait for data */ - ret = comedi_timeout(dev, s, insn, cb_pcidas64_ai_eoc, 0); - if (ret) - return ret; - - if (board->layout == LAYOUT_4020) - data[n] = readl(dev->mmio + ADC_FIFO_REG) & 0xffff; - else - data[n] = readw(devpriv->main_iobase + PIPE1_READ_REG); - } - - return n; -} - -static int ai_config_calibration_source(struct comedi_device *dev, - unsigned int *data) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned int source = data[1]; - int num_calibration_sources; - - if (board->layout == LAYOUT_60XX) - num_calibration_sources = 16; - else - num_calibration_sources = 8; - if (source >= num_calibration_sources) { - dev_dbg(dev->class_dev, "invalid calibration source: %i\n", - source); - return -EINVAL; - } - - devpriv->calibration_source = source; - - return 2; -} - -static int ai_config_block_size(struct comedi_device *dev, unsigned int *data) -{ - const struct pcidas64_board *board = dev->board_ptr; - int fifo_size; - const struct hw_fifo_info *const fifo = board->ai_fifo; - unsigned int block_size, requested_block_size; - int retval; - - requested_block_size = data[1]; - - if (requested_block_size) { - fifo_size = requested_block_size * fifo->num_segments / - bytes_in_sample; - - retval = set_ai_fifo_size(dev, fifo_size); - if (retval < 0) - return retval; - } - - block_size = ai_fifo_size(dev) / fifo->num_segments * bytes_in_sample; - - data[1] = block_size; - - return 2; -} - -static int ai_config_master_clock_4020(struct comedi_device *dev, - unsigned int *data) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int divisor = data[4]; - int retval = 0; - - if (divisor < 2) { - divisor = 2; - retval = -EAGAIN; - } - - switch (data[1]) { - case COMEDI_EV_SCAN_BEGIN: - devpriv->ext_clock.divisor = divisor; - devpriv->ext_clock.chanspec = data[2]; - break; - default: - return -EINVAL; - } - - data[4] = divisor; - - return retval ? retval : 5; -} - -/* XXX could add support for 60xx series */ -static int ai_config_master_clock(struct comedi_device *dev, unsigned int *data) -{ - const struct pcidas64_board *board = dev->board_ptr; - - switch (board->layout) { - case LAYOUT_4020: - return ai_config_master_clock_4020(dev, data); - default: - return -EINVAL; - } - - return -EINVAL; -} - -static int ai_config_insn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - int id = data[0]; - - switch (id) { - case INSN_CONFIG_ALT_SOURCE: - return ai_config_calibration_source(dev, data); - case INSN_CONFIG_BLOCK_SIZE: - return ai_config_block_size(dev, data); - case INSN_CONFIG_TIMER_1: - return ai_config_master_clock(dev, data); - default: - return -EINVAL; - } - return -EINVAL; -} - -/* - * Gets nearest achievable timing given master clock speed, does not - * take into account possible minimum/maximum divisor values. Used - * by other timing checking functions. - */ -static unsigned int get_divisor(unsigned int ns, unsigned int flags) -{ - unsigned int divisor; - - switch (flags & CMDF_ROUND_MASK) { - case CMDF_ROUND_UP: - divisor = DIV_ROUND_UP(ns, TIMER_BASE); - break; - case CMDF_ROUND_DOWN: - divisor = ns / TIMER_BASE; - break; - case CMDF_ROUND_NEAREST: - default: - divisor = DIV_ROUND_CLOSEST(ns, TIMER_BASE); - break; - } - return divisor; -} - -/* - * utility function that rounds desired timing to an achievable time, and - * sets cmd members appropriately. - * adc paces conversions from master clock by dividing by (x + 3) where x is - * 24 bit number - */ -static void check_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - unsigned long long convert_divisor = 0; - unsigned int scan_divisor; - static const int min_convert_divisor = 3; - static const int max_convert_divisor = - max_counter_value + min_convert_divisor; - static const int min_scan_divisor_4020 = 2; - unsigned long long max_scan_divisor, min_scan_divisor; - - if (cmd->convert_src == TRIG_TIMER) { - if (board->layout == LAYOUT_4020) { - cmd->convert_arg = 0; - } else { - convert_divisor = get_divisor(cmd->convert_arg, - cmd->flags); - if (convert_divisor > max_convert_divisor) - convert_divisor = max_convert_divisor; - if (convert_divisor < min_convert_divisor) - convert_divisor = min_convert_divisor; - cmd->convert_arg = convert_divisor * TIMER_BASE; - } - } else if (cmd->convert_src == TRIG_NOW) { - cmd->convert_arg = 0; - } - - if (cmd->scan_begin_src == TRIG_TIMER) { - scan_divisor = get_divisor(cmd->scan_begin_arg, cmd->flags); - if (cmd->convert_src == TRIG_TIMER) { - min_scan_divisor = convert_divisor * cmd->chanlist_len; - max_scan_divisor = - (convert_divisor * cmd->chanlist_len - 1) + - max_counter_value; - } else { - min_scan_divisor = min_scan_divisor_4020; - max_scan_divisor = max_counter_value + min_scan_divisor; - } - if (scan_divisor > max_scan_divisor) - scan_divisor = max_scan_divisor; - if (scan_divisor < min_scan_divisor) - scan_divisor = min_scan_divisor; - cmd->scan_begin_arg = scan_divisor * TIMER_BASE; - } -} - -static int cb_pcidas64_ai_check_chanlist(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - unsigned int aref0 = CR_AREF(cmd->chanlist[0]); - int i; - - for (i = 1; i < cmd->chanlist_len; i++) { - unsigned int aref = CR_AREF(cmd->chanlist[i]); - - if (aref != aref0) { - dev_dbg(dev->class_dev, - "all elements in chanlist must use the same analog reference\n"); - return -EINVAL; - } - } - - if (board->layout == LAYOUT_4020) { - unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); - - for (i = 1; i < cmd->chanlist_len; i++) { - unsigned int chan = CR_CHAN(cmd->chanlist[i]); - - if (chan != (chan0 + i)) { - dev_dbg(dev->class_dev, - "chanlist must use consecutive channels\n"); - return -EINVAL; - } - } - if (cmd->chanlist_len == 3) { - dev_dbg(dev->class_dev, - "chanlist cannot be 3 channels long, use 1, 2, or 4 channels\n"); - return -EINVAL; - } - } - - return 0; -} - -static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - int err = 0; - unsigned int tmp_arg, tmp_arg2; - unsigned int triggers; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); - - triggers = TRIG_TIMER; - if (board->layout == LAYOUT_4020) - triggers |= TRIG_OTHER; - else - triggers |= TRIG_FOLLOW; - err |= comedi_check_trigger_src(&cmd->scan_begin_src, triggers); - - triggers = TRIG_TIMER; - if (board->layout == LAYOUT_4020) - triggers |= TRIG_NOW; - else - triggers |= TRIG_EXT; - err |= comedi_check_trigger_src(&cmd->convert_src, triggers); - err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); - err |= comedi_check_trigger_src(&cmd->stop_src, - TRIG_COUNT | TRIG_EXT | TRIG_NONE); - - if (err) - return 1; - - /* Step 2a : make sure trigger sources are unique */ - - err |= comedi_check_trigger_is_unique(cmd->start_src); - err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); - err |= comedi_check_trigger_is_unique(cmd->convert_src); - err |= comedi_check_trigger_is_unique(cmd->stop_src); - - /* Step 2b : and mutually compatible */ - - if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER) - err |= -EINVAL; - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - switch (cmd->start_src) { - case TRIG_NOW: - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - break; - case TRIG_EXT: - /* - * start_arg is the CR_CHAN | CR_INVERT of the - * external trigger. - */ - break; - } - - if (cmd->convert_src == TRIG_TIMER) { - if (board->layout == LAYOUT_4020) { - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, - 0); - } else { - err |= comedi_check_trigger_arg_min(&cmd->convert_arg, - board->ai_speed); - /* - * if scans are timed faster than conversion rate - * allows - */ - if (cmd->scan_begin_src == TRIG_TIMER) { - err |= comedi_check_trigger_arg_min( - &cmd->scan_begin_arg, - cmd->convert_arg * - cmd->chanlist_len); - } - } - } - - err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); - err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, - cmd->chanlist_len); - - switch (cmd->stop_src) { - case TRIG_EXT: - break; - case TRIG_COUNT: - err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); - break; - case TRIG_NONE: - err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); - break; - default: - break; - } - - if (err) - return 3; - - /* step 4: fix up any arguments */ - - if (cmd->convert_src == TRIG_TIMER) { - tmp_arg = cmd->convert_arg; - tmp_arg2 = cmd->scan_begin_arg; - check_adc_timing(dev, cmd); - if (tmp_arg != cmd->convert_arg) - err++; - if (tmp_arg2 != cmd->scan_begin_arg) - err++; - } - - if (err) - return 4; - - /* Step 5: check channel list if it exists */ - if (cmd->chanlist && cmd->chanlist_len > 0) - err |= cb_pcidas64_ai_check_chanlist(dev, s, cmd); - - if (err) - return 5; - - return 0; -} - -static int use_hw_sample_counter(struct comedi_cmd *cmd) -{ -/* disable for now until I work out a race */ - return 0; - - if (cmd->stop_src == TRIG_COUNT && cmd->stop_arg <= max_counter_value) - return 1; - - return 0; -} - -static void setup_sample_counters(struct comedi_device *dev, - struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - - /* load hardware conversion counter */ - if (use_hw_sample_counter(cmd)) { - writew(cmd->stop_arg & 0xffff, - devpriv->main_iobase + ADC_COUNT_LOWER_REG); - writew((cmd->stop_arg >> 16) & 0xff, - devpriv->main_iobase + ADC_COUNT_UPPER_REG); - } else { - writew(1, devpriv->main_iobase + ADC_COUNT_LOWER_REG); - } -} - -static inline unsigned int dma_transfer_size(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned int num_samples; - - num_samples = devpriv->ai_fifo_segment_length * - board->ai_fifo->sample_packing_ratio; - if (num_samples > DMA_BUFFER_SIZE / sizeof(u16)) - num_samples = DMA_BUFFER_SIZE / sizeof(u16); - - return num_samples; -} - -static u32 ai_convert_counter_6xxx(const struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - /* supposed to load counter with desired divisor minus 3 */ - return cmd->convert_arg / TIMER_BASE - 3; -} - -static u32 ai_scan_counter_6xxx(struct comedi_device *dev, - struct comedi_cmd *cmd) -{ - u32 count; - - /* figure out how long we need to delay at end of scan */ - switch (cmd->scan_begin_src) { - case TRIG_TIMER: - count = (cmd->scan_begin_arg - - (cmd->convert_arg * (cmd->chanlist_len - 1))) / - TIMER_BASE; - break; - case TRIG_FOLLOW: - count = cmd->convert_arg / TIMER_BASE; - break; - default: - return 0; - } - return count - 3; -} - -static u32 ai_convert_counter_4020(struct comedi_device *dev, - struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int divisor; - - switch (cmd->scan_begin_src) { - case TRIG_TIMER: - divisor = cmd->scan_begin_arg / TIMER_BASE; - break; - case TRIG_OTHER: - divisor = devpriv->ext_clock.divisor; - break; - default: /* should never happen */ - dev_err(dev->class_dev, "bug! failed to set ai pacing!\n"); - divisor = 1000; - break; - } - - /* supposed to load counter with desired divisor minus 2 for 4020 */ - return divisor - 2; -} - -static void select_master_clock_4020(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - - /* select internal/external master clock */ - devpriv->hw_config_bits &= ~MASTER_CLOCK_4020_MASK; - if (cmd->scan_begin_src == TRIG_OTHER) { - int chanspec = devpriv->ext_clock.chanspec; - - if (CR_CHAN(chanspec)) - devpriv->hw_config_bits |= BNC_CLOCK_4020_BITS; - else - devpriv->hw_config_bits |= EXT_CLOCK_4020_BITS; - } else { - devpriv->hw_config_bits |= INTERNAL_CLOCK_4020_BITS; - } - writew(devpriv->hw_config_bits, - devpriv->main_iobase + HW_CONFIG_REG); -} - -static void select_master_clock(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - - switch (board->layout) { - case LAYOUT_4020: - select_master_clock_4020(dev, cmd); - break; - default: - break; - } -} - -static inline void dma_start_sync(struct comedi_device *dev, - unsigned int channel) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned long flags; - - /* spinlock for plx dma control/status reg */ - spin_lock_irqsave(&dev->spinlock, flags); - writeb(PLX_DMACSR_ENABLE | PLX_DMACSR_START | PLX_DMACSR_CLEARINTR, - devpriv->plx9080_iobase + PLX_REG_DMACSR(channel)); - spin_unlock_irqrestore(&dev->spinlock, flags); -} - -static void set_ai_pacing(struct comedi_device *dev, struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - u32 convert_counter = 0, scan_counter = 0; - - check_adc_timing(dev, cmd); - - select_master_clock(dev, cmd); - - if (board->layout == LAYOUT_4020) { - convert_counter = ai_convert_counter_4020(dev, cmd); - } else { - convert_counter = ai_convert_counter_6xxx(dev, cmd); - scan_counter = ai_scan_counter_6xxx(dev, cmd); - } - - /* load lower 16 bits of convert interval */ - writew(convert_counter & 0xffff, - devpriv->main_iobase + ADC_SAMPLE_INTERVAL_LOWER_REG); - /* load upper 8 bits of convert interval */ - writew((convert_counter >> 16) & 0xff, - devpriv->main_iobase + ADC_SAMPLE_INTERVAL_UPPER_REG); - /* load lower 16 bits of scan delay */ - writew(scan_counter & 0xffff, - devpriv->main_iobase + ADC_DELAY_INTERVAL_LOWER_REG); - /* load upper 8 bits of scan delay */ - writew((scan_counter >> 16) & 0xff, - devpriv->main_iobase + ADC_DELAY_INTERVAL_UPPER_REG); -} - -static int use_internal_queue_6xxx(const struct comedi_cmd *cmd) -{ - int i; - - for (i = 0; i + 1 < cmd->chanlist_len; i++) { - if (CR_CHAN(cmd->chanlist[i + 1]) != - CR_CHAN(cmd->chanlist[i]) + 1) - return 0; - if (CR_RANGE(cmd->chanlist[i + 1]) != - CR_RANGE(cmd->chanlist[i])) - return 0; - if (CR_AREF(cmd->chanlist[i + 1]) != CR_AREF(cmd->chanlist[i])) - return 0; - } - return 1; -} - -static int setup_channel_queue(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned short bits; - int i; - - if (board->layout != LAYOUT_4020) { - if (use_internal_queue_6xxx(cmd)) { - devpriv->hw_config_bits &= ~EXT_QUEUE_BIT; - writew(devpriv->hw_config_bits, - devpriv->main_iobase + HW_CONFIG_REG); - bits = 0; - /* set channel */ - bits |= adc_chan_bits(CR_CHAN(cmd->chanlist[0])); - /* set gain */ - bits |= ai_range_bits_6xxx(dev, - CR_RANGE(cmd->chanlist[0])); - /* set single-ended / differential */ - bits |= se_diff_bit_6xxx(dev, - CR_AREF(cmd->chanlist[0]) == - AREF_DIFF); - if (CR_AREF(cmd->chanlist[0]) == AREF_COMMON) - bits |= ADC_COMMON_BIT; - /* set stop channel */ - writew(adc_chan_bits - (CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1])), - devpriv->main_iobase + ADC_QUEUE_HIGH_REG); - /* set start channel, and rest of settings */ - writew(bits, - devpriv->main_iobase + ADC_QUEUE_LOAD_REG); - } else { - /* use external queue */ - if (dev->write_subdev && dev->write_subdev->busy) { - warn_external_queue(dev); - return -EBUSY; - } - devpriv->hw_config_bits |= EXT_QUEUE_BIT; - writew(devpriv->hw_config_bits, - devpriv->main_iobase + HW_CONFIG_REG); - /* clear DAC buffer to prevent weird interactions */ - writew(0, - devpriv->main_iobase + DAC_BUFFER_CLEAR_REG); - /* clear queue pointer */ - writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG); - /* load external queue */ - for (i = 0; i < cmd->chanlist_len; i++) { - unsigned int chanspec = cmd->chanlist[i]; - int use_differential; - - bits = 0; - /* set channel */ - bits |= adc_chan_bits(CR_CHAN(chanspec)); - /* set gain */ - bits |= ai_range_bits_6xxx(dev, - CR_RANGE(chanspec)); - /* set single-ended / differential */ - use_differential = 0; - if (CR_AREF(chanspec) == AREF_DIFF) - use_differential = 1; - bits |= se_diff_bit_6xxx(dev, use_differential); - - if (CR_AREF(cmd->chanlist[i]) == AREF_COMMON) - bits |= ADC_COMMON_BIT; - /* mark end of queue */ - if (i == cmd->chanlist_len - 1) - bits |= QUEUE_EOSCAN_BIT | - QUEUE_EOSEQ_BIT; - writew(bits, - devpriv->main_iobase + - ADC_QUEUE_FIFO_REG); - } - /* - * doing a queue clear is not specified in board docs, - * but required for reliable operation - */ - writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG); - /* prime queue holding register */ - writew(0, devpriv->main_iobase + ADC_QUEUE_LOAD_REG); - } - } else { - unsigned short old_cal_range_bits = devpriv->i2c_cal_range_bits; - - devpriv->i2c_cal_range_bits &= ~ADC_SRC_4020_MASK; - /* select BNC inputs */ - devpriv->i2c_cal_range_bits |= adc_src_4020_bits(4); - /* select ranges */ - for (i = 0; i < cmd->chanlist_len; i++) { - unsigned int channel = CR_CHAN(cmd->chanlist[i]); - unsigned int range = CR_RANGE(cmd->chanlist[i]); - - if (range == 0) - devpriv->i2c_cal_range_bits |= - attenuate_bit(channel); - else - devpriv->i2c_cal_range_bits &= - ~attenuate_bit(channel); - } - /* - * update calibration/range i2c register only if necessary, - * as it is very slow - */ - if (old_cal_range_bits != devpriv->i2c_cal_range_bits) { - u8 i2c_data = devpriv->i2c_cal_range_bits; - - i2c_write(dev, RANGE_CAL_I2C_ADDR, &i2c_data, - sizeof(i2c_data)); - } - } - return 0; -} - -static inline void load_first_dma_descriptor(struct comedi_device *dev, - unsigned int dma_channel, - unsigned int descriptor_bits) -{ - struct pcidas64_private *devpriv = dev->private; - - /* - * The transfer size, pci address, and local address registers - * are supposedly unused during chained dma, - * but I have found that left over values from last operation - * occasionally cause problems with transfer of first dma - * block. Initializing them to zero seems to fix the problem. - */ - if (dma_channel) { - writel(0, devpriv->plx9080_iobase + PLX_REG_DMASIZ1); - writel(0, devpriv->plx9080_iobase + PLX_REG_DMAPADR1); - writel(0, devpriv->plx9080_iobase + PLX_REG_DMALADR1); - writel(descriptor_bits, - devpriv->plx9080_iobase + PLX_REG_DMADPR1); - } else { - writel(0, devpriv->plx9080_iobase + PLX_REG_DMASIZ0); - writel(0, devpriv->plx9080_iobase + PLX_REG_DMAPADR0); - writel(0, devpriv->plx9080_iobase + PLX_REG_DMALADR0); - writel(descriptor_bits, - devpriv->plx9080_iobase + PLX_REG_DMADPR0); - } -} - -static int ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - u32 bits; - unsigned int i; - unsigned long flags; - int retval; - - disable_ai_pacing(dev); - abort_dma(dev, 1); - - retval = setup_channel_queue(dev, cmd); - if (retval < 0) - return retval; - - /* make sure internal calibration source is turned off */ - writew(0, devpriv->main_iobase + CALIBRATION_REG); - - set_ai_pacing(dev, cmd); - - setup_sample_counters(dev, cmd); - - enable_ai_interrupts(dev, cmd); - - spin_lock_irqsave(&dev->spinlock, flags); - /* set mode, allow conversions through software gate */ - devpriv->adc_control1_bits |= ADC_SW_GATE_BIT; - devpriv->adc_control1_bits &= ~ADC_DITHER_BIT; - if (board->layout != LAYOUT_4020) { - devpriv->adc_control1_bits &= ~ADC_MODE_MASK; - if (cmd->convert_src == TRIG_EXT) - /* good old mode 13 */ - devpriv->adc_control1_bits |= adc_mode_bits(13); - else - /* mode 8. What else could you need? */ - devpriv->adc_control1_bits |= adc_mode_bits(8); - } else { - devpriv->adc_control1_bits &= ~CHANNEL_MODE_4020_MASK; - if (cmd->chanlist_len == 4) - devpriv->adc_control1_bits |= FOUR_CHANNEL_4020_BITS; - else if (cmd->chanlist_len == 2) - devpriv->adc_control1_bits |= TWO_CHANNEL_4020_BITS; - devpriv->adc_control1_bits &= ~ADC_LO_CHANNEL_4020_MASK; - devpriv->adc_control1_bits |= - adc_lo_chan_4020_bits(CR_CHAN(cmd->chanlist[0])); - devpriv->adc_control1_bits &= ~ADC_HI_CHANNEL_4020_MASK; - devpriv->adc_control1_bits |= - adc_hi_chan_4020_bits(CR_CHAN(cmd->chanlist - [cmd->chanlist_len - 1])); - } - writew(devpriv->adc_control1_bits, - devpriv->main_iobase + ADC_CONTROL1_REG); - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* clear adc buffer */ - writew(0, devpriv->main_iobase + ADC_BUFFER_CLEAR_REG); - - if ((cmd->flags & CMDF_WAKE_EOS) == 0 || - board->layout == LAYOUT_4020) { - devpriv->ai_dma_index = 0; - - /* set dma transfer size */ - for (i = 0; i < ai_dma_ring_count(board); i++) - devpriv->ai_dma_desc[i].transfer_size = - cpu_to_le32(dma_transfer_size(dev) * - sizeof(u16)); - - /* give location of first dma descriptor */ - load_first_dma_descriptor(dev, 1, - devpriv->ai_dma_desc_bus_addr | - PLX_DMADPR_DESCPCI | - PLX_DMADPR_TCINTR | - PLX_DMADPR_XFERL2P); - - dma_start_sync(dev, 1); - } - - if (board->layout == LAYOUT_4020) { - /* set source for external triggers */ - bits = 0; - if (cmd->start_src == TRIG_EXT && CR_CHAN(cmd->start_arg)) - bits |= EXT_START_TRIG_BNC_BIT; - if (cmd->stop_src == TRIG_EXT && CR_CHAN(cmd->stop_arg)) - bits |= EXT_STOP_TRIG_BNC_BIT; - writew(bits, devpriv->main_iobase + DAQ_ATRIG_LOW_4020_REG); - } - - spin_lock_irqsave(&dev->spinlock, flags); - - /* enable pacing, triggering, etc */ - bits = ADC_ENABLE_BIT | ADC_SOFT_GATE_BITS | ADC_GATE_LEVEL_BIT; - if (cmd->flags & CMDF_WAKE_EOS) - bits |= ADC_DMA_DISABLE_BIT; - /* set start trigger */ - if (cmd->start_src == TRIG_EXT) { - bits |= ADC_START_TRIG_EXT_BITS; - if (cmd->start_arg & CR_INVERT) - bits |= ADC_START_TRIG_FALLING_BIT; - } else if (cmd->start_src == TRIG_NOW) { - bits |= ADC_START_TRIG_SOFT_BITS; - } - if (use_hw_sample_counter(cmd)) - bits |= ADC_SAMPLE_COUNTER_EN_BIT; - writew(bits, devpriv->main_iobase + ADC_CONTROL0_REG); - - devpriv->ai_cmd_running = 1; - - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* start acquisition */ - if (cmd->start_src == TRIG_NOW) - writew(0, devpriv->main_iobase + ADC_START_REG); - - return 0; -} - -/* read num_samples from 16 bit wide ai fifo */ -static void pio_drain_ai_fifo_16(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->read_subdev; - unsigned int i; - u16 prepost_bits; - int read_segment, read_index, write_segment, write_index; - int num_samples; - - do { - /* get least significant 15 bits */ - read_index = readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & - 0x7fff; - write_index = readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & - 0x7fff; - /* - * Get most significant bits (grey code). - * Different boards use different code so use a scheme - * that doesn't depend on encoding. This read must - * occur after reading least significant 15 bits to avoid race - * with fifo switching to next segment. - */ - prepost_bits = readw(devpriv->main_iobase + PREPOST_REG); - - /* - * if read and write pointers are not on the same fifo segment, - * read to the end of the read segment - */ - read_segment = adc_upper_read_ptr_code(prepost_bits); - write_segment = adc_upper_write_ptr_code(prepost_bits); - - if (read_segment != write_segment) - num_samples = - devpriv->ai_fifo_segment_length - read_index; - else - num_samples = write_index - read_index; - if (num_samples < 0) { - dev_err(dev->class_dev, - "cb_pcidas64: bug! num_samples < 0\n"); - break; - } - - num_samples = comedi_nsamples_left(s, num_samples); - if (num_samples == 0) - break; - - for (i = 0; i < num_samples; i++) { - unsigned short val; - - val = readw(devpriv->main_iobase + ADC_FIFO_REG); - comedi_buf_write_samples(s, &val, 1); - } - - } while (read_segment != write_segment); -} - -/* - * Read from 32 bit wide ai fifo of 4020 - deal with insane grey coding of - * pointers. The pci-4020 hardware only supports dma transfers (it only - * supports the use of pio for draining the last remaining points from the - * fifo when a data acquisition operation has completed). - */ -static void pio_drain_ai_fifo_32(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->read_subdev; - unsigned int nsamples; - unsigned int i; - u32 fifo_data; - int write_code = - readw(devpriv->main_iobase + ADC_WRITE_PNTR_REG) & 0x7fff; - int read_code = - readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & 0x7fff; - - nsamples = comedi_nsamples_left(s, 100000); - for (i = 0; read_code != write_code && i < nsamples;) { - unsigned short val; - - fifo_data = readl(dev->mmio + ADC_FIFO_REG); - val = fifo_data & 0xffff; - comedi_buf_write_samples(s, &val, 1); - i++; - if (i < nsamples) { - val = (fifo_data >> 16) & 0xffff; - comedi_buf_write_samples(s, &val, 1); - i++; - } - read_code = readw(devpriv->main_iobase + ADC_READ_PNTR_REG) & - 0x7fff; - } -} - -/* empty fifo */ -static void pio_drain_ai_fifo(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - - if (board->layout == LAYOUT_4020) - pio_drain_ai_fifo_32(dev); - else - pio_drain_ai_fifo_16(dev); -} - -static void drain_dma_buffers(struct comedi_device *dev, unsigned int channel) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->read_subdev; - u32 next_transfer_addr; - int j; - int num_samples = 0; - void __iomem *pci_addr_reg; - - pci_addr_reg = devpriv->plx9080_iobase + PLX_REG_DMAPADR(channel); - - /* loop until we have read all the full buffers */ - for (j = 0, next_transfer_addr = readl(pci_addr_reg); - (next_transfer_addr < - devpriv->ai_buffer_bus_addr[devpriv->ai_dma_index] || - next_transfer_addr >= - devpriv->ai_buffer_bus_addr[devpriv->ai_dma_index] + - DMA_BUFFER_SIZE) && j < ai_dma_ring_count(board); j++) { - /* transfer data from dma buffer to comedi buffer */ - num_samples = comedi_nsamples_left(s, dma_transfer_size(dev)); - comedi_buf_write_samples(s, - devpriv->ai_buffer[devpriv->ai_dma_index], - num_samples); - devpriv->ai_dma_index = (devpriv->ai_dma_index + 1) % - ai_dma_ring_count(board); - } - /* - * XXX check for dma ring buffer overrun - * (use end-of-chain bit to mark last unused buffer) - */ -} - -static void handle_ai_interrupt(struct comedi_device *dev, - unsigned short status, - unsigned int plx_status) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - u8 dma1_status; - unsigned long flags; - - /* check for fifo overrun */ - if (status & ADC_OVERRUN_BIT) { - dev_err(dev->class_dev, "fifo overrun\n"); - async->events |= COMEDI_CB_ERROR; - } - /* spin lock makes sure no one else changes plx dma control reg */ - spin_lock_irqsave(&dev->spinlock, flags); - dma1_status = readb(devpriv->plx9080_iobase + PLX_REG_DMACSR1); - if (plx_status & PLX_INTCSR_DMA1IA) { /* dma chan 1 interrupt */ - writeb((dma1_status & PLX_DMACSR_ENABLE) | PLX_DMACSR_CLEARINTR, - devpriv->plx9080_iobase + PLX_REG_DMACSR1); - - if (dma1_status & PLX_DMACSR_ENABLE) - drain_dma_buffers(dev, 1); - } - spin_unlock_irqrestore(&dev->spinlock, flags); - - /* drain fifo with pio */ - if ((status & ADC_DONE_BIT) || - ((cmd->flags & CMDF_WAKE_EOS) && - (status & ADC_INTR_PENDING_BIT) && - (board->layout != LAYOUT_4020))) { - spin_lock_irqsave(&dev->spinlock, flags); - if (devpriv->ai_cmd_running) { - spin_unlock_irqrestore(&dev->spinlock, flags); - pio_drain_ai_fifo(dev); - } else { - spin_unlock_irqrestore(&dev->spinlock, flags); - } - } - /* if we are have all the data, then quit */ - if ((cmd->stop_src == TRIG_COUNT && - async->scans_done >= cmd->stop_arg) || - (cmd->stop_src == TRIG_EXT && (status & ADC_STOP_BIT))) - async->events |= COMEDI_CB_EOA; - - comedi_handle_events(dev, s); -} - -static inline unsigned int prev_ao_dma_index(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int buffer_index; - - if (devpriv->ao_dma_index == 0) - buffer_index = AO_DMA_RING_COUNT - 1; - else - buffer_index = devpriv->ao_dma_index - 1; - return buffer_index; -} - -static int last_ao_dma_load_completed(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int buffer_index; - unsigned int transfer_address; - unsigned short dma_status; - - buffer_index = prev_ao_dma_index(dev); - dma_status = readb(devpriv->plx9080_iobase + PLX_REG_DMACSR0); - if ((dma_status & PLX_DMACSR_DONE) == 0) - return 0; - - transfer_address = - readl(devpriv->plx9080_iobase + PLX_REG_DMAPADR0); - if (transfer_address != devpriv->ao_buffer_bus_addr[buffer_index]) - return 0; - - return 1; -} - -static inline int ao_dma_needs_restart(struct comedi_device *dev, - unsigned short dma_status) -{ - if ((dma_status & PLX_DMACSR_DONE) == 0 || - (dma_status & PLX_DMACSR_ENABLE) == 0) - return 0; - if (last_ao_dma_load_completed(dev)) - return 0; - - return 1; -} - -static void restart_ao_dma(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int dma_desc_bits; - - dma_desc_bits = readl(devpriv->plx9080_iobase + PLX_REG_DMADPR0); - dma_desc_bits &= ~PLX_DMADPR_CHAINEND; - load_first_dma_descriptor(dev, 0, dma_desc_bits); - - dma_start_sync(dev, 0); -} - -static unsigned int cb_pcidas64_ao_fill_buffer(struct comedi_device *dev, - struct comedi_subdevice *s, - unsigned short *dest, - unsigned int max_bytes) -{ - unsigned int nsamples = comedi_bytes_to_samples(s, max_bytes); - unsigned int actual_bytes; - - nsamples = comedi_nsamples_left(s, nsamples); - actual_bytes = comedi_buf_read_samples(s, dest, nsamples); - - return comedi_bytes_to_samples(s, actual_bytes); -} - -static unsigned int load_ao_dma_buffer(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->write_subdev; - unsigned int buffer_index = devpriv->ao_dma_index; - unsigned int prev_buffer_index = prev_ao_dma_index(dev); - unsigned int nsamples; - unsigned int nbytes; - unsigned int next_bits; - - nsamples = cb_pcidas64_ao_fill_buffer(dev, s, - devpriv->ao_buffer[buffer_index], - DMA_BUFFER_SIZE); - if (nsamples == 0) - return 0; - - nbytes = comedi_samples_to_bytes(s, nsamples); - devpriv->ao_dma_desc[buffer_index].transfer_size = cpu_to_le32(nbytes); - /* set end of chain bit so we catch underruns */ - next_bits = le32_to_cpu(devpriv->ao_dma_desc[buffer_index].next); - next_bits |= PLX_DMADPR_CHAINEND; - devpriv->ao_dma_desc[buffer_index].next = cpu_to_le32(next_bits); - /* - * clear end of chain bit on previous buffer now that we have set it - * for the last buffer - */ - next_bits = le32_to_cpu(devpriv->ao_dma_desc[prev_buffer_index].next); - next_bits &= ~PLX_DMADPR_CHAINEND; - devpriv->ao_dma_desc[prev_buffer_index].next = cpu_to_le32(next_bits); - - devpriv->ao_dma_index = (buffer_index + 1) % AO_DMA_RING_COUNT; - - return nbytes; -} - -static void load_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int num_bytes; - unsigned int next_transfer_addr; - void __iomem *pci_addr_reg = devpriv->plx9080_iobase + PLX_REG_DMAPADR0; - unsigned int buffer_index; - - do { - buffer_index = devpriv->ao_dma_index; - /* don't overwrite data that hasn't been transferred yet */ - next_transfer_addr = readl(pci_addr_reg); - if (next_transfer_addr >= - devpriv->ao_buffer_bus_addr[buffer_index] && - next_transfer_addr < - devpriv->ao_buffer_bus_addr[buffer_index] + - DMA_BUFFER_SIZE) - return; - num_bytes = load_ao_dma_buffer(dev, cmd); - } while (num_bytes >= DMA_BUFFER_SIZE); -} - -static void handle_ao_interrupt(struct comedi_device *dev, - unsigned short status, unsigned int plx_status) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->write_subdev; - struct comedi_async *async; - struct comedi_cmd *cmd; - u8 dma0_status; - unsigned long flags; - - /* board might not support ao, in which case write_subdev is NULL */ - if (!s) - return; - async = s->async; - cmd = &async->cmd; - - /* spin lock makes sure no one else changes plx dma control reg */ - spin_lock_irqsave(&dev->spinlock, flags); - dma0_status = readb(devpriv->plx9080_iobase + PLX_REG_DMACSR0); - if (plx_status & PLX_INTCSR_DMA0IA) { /* dma chan 0 interrupt */ - if ((dma0_status & PLX_DMACSR_ENABLE) && - !(dma0_status & PLX_DMACSR_DONE)) { - writeb(PLX_DMACSR_ENABLE | PLX_DMACSR_CLEARINTR, - devpriv->plx9080_iobase + PLX_REG_DMACSR0); - } else { - writeb(PLX_DMACSR_CLEARINTR, - devpriv->plx9080_iobase + PLX_REG_DMACSR0); - } - spin_unlock_irqrestore(&dev->spinlock, flags); - if (dma0_status & PLX_DMACSR_ENABLE) { - load_ao_dma(dev, cmd); - /* try to recover from dma end-of-chain event */ - if (ao_dma_needs_restart(dev, dma0_status)) - restart_ao_dma(dev); - } - } else { - spin_unlock_irqrestore(&dev->spinlock, flags); - } - - if ((status & DAC_DONE_BIT)) { - if ((cmd->stop_src == TRIG_COUNT && - async->scans_done >= cmd->stop_arg) || - last_ao_dma_load_completed(dev)) - async->events |= COMEDI_CB_EOA; - else - async->events |= COMEDI_CB_ERROR; - } - comedi_handle_events(dev, s); -} - -static irqreturn_t handle_interrupt(int irq, void *d) -{ - struct comedi_device *dev = d; - struct pcidas64_private *devpriv = dev->private; - unsigned short status; - u32 plx_status; - u32 plx_bits; - - plx_status = readl(devpriv->plx9080_iobase + PLX_REG_INTCSR); - status = readw(devpriv->main_iobase + HW_STATUS_REG); - - /* - * an interrupt before all the postconfig stuff gets done could - * cause a NULL dereference if we continue through the - * interrupt handler - */ - if (!dev->attached) - return IRQ_HANDLED; - - handle_ai_interrupt(dev, status, plx_status); - handle_ao_interrupt(dev, status, plx_status); - - /* clear possible plx9080 interrupt sources */ - if (plx_status & PLX_INTCSR_LDBIA) { - /* clear local doorbell interrupt */ - plx_bits = readl(devpriv->plx9080_iobase + PLX_REG_L2PDBELL); - writel(plx_bits, devpriv->plx9080_iobase + PLX_REG_L2PDBELL); - } - - return IRQ_HANDLED; -} - -static int ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned long flags; - - spin_lock_irqsave(&dev->spinlock, flags); - if (devpriv->ai_cmd_running == 0) { - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; - } - devpriv->ai_cmd_running = 0; - spin_unlock_irqrestore(&dev->spinlock, flags); - - disable_ai_pacing(dev); - - abort_dma(dev, 1); - - return 0; -} - -static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int range = CR_RANGE(insn->chanspec); - unsigned int val = s->readback[chan]; - unsigned int i; - - /* do some initializing */ - writew(0, devpriv->main_iobase + DAC_CONTROL0_REG); - - /* set range */ - set_dac_range_bits(dev, &devpriv->dac_control1_bits, chan, range); - writew(devpriv->dac_control1_bits, - devpriv->main_iobase + DAC_CONTROL1_REG); - - for (i = 0; i < insn->n; i++) { - /* write to channel */ - val = data[i]; - if (board->layout == LAYOUT_4020) { - writew(val & 0xff, - devpriv->main_iobase + dac_lsb_4020_reg(chan)); - writew((val >> 8) & 0xf, - devpriv->main_iobase + dac_msb_4020_reg(chan)); - } else { - writew(val, - devpriv->main_iobase + dac_convert_reg(chan)); - } - } - - /* remember last output value */ - s->readback[chan] = val; - - return insn->n; -} - -static void set_dac_control0_reg(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int bits = DAC_ENABLE_BIT | WAVEFORM_GATE_LEVEL_BIT | - WAVEFORM_GATE_ENABLE_BIT | WAVEFORM_GATE_SELECT_BIT; - - if (cmd->start_src == TRIG_EXT) { - bits |= WAVEFORM_TRIG_EXT_BITS; - if (cmd->start_arg & CR_INVERT) - bits |= WAVEFORM_TRIG_FALLING_BIT; - } else { - bits |= WAVEFORM_TRIG_SOFT_BITS; - } - if (cmd->scan_begin_src == TRIG_EXT) { - bits |= DAC_EXT_UPDATE_ENABLE_BIT; - if (cmd->scan_begin_arg & CR_INVERT) - bits |= DAC_EXT_UPDATE_FALLING_BIT; - } - writew(bits, devpriv->main_iobase + DAC_CONTROL0_REG); -} - -static void set_dac_control1_reg(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - int i; - - for (i = 0; i < cmd->chanlist_len; i++) { - int channel, range; - - channel = CR_CHAN(cmd->chanlist[i]); - range = CR_RANGE(cmd->chanlist[i]); - set_dac_range_bits(dev, &devpriv->dac_control1_bits, channel, - range); - } - devpriv->dac_control1_bits |= DAC_SW_GATE_BIT; - writew(devpriv->dac_control1_bits, - devpriv->main_iobase + DAC_CONTROL1_REG); -} - -static void set_dac_select_reg(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - u16 bits; - unsigned int first_channel, last_channel; - - first_channel = CR_CHAN(cmd->chanlist[0]); - last_channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]); - if (last_channel < first_channel) - dev_err(dev->class_dev, - "bug! last ao channel < first ao channel\n"); - - bits = (first_channel & 0x7) | (last_channel & 0x7) << 3; - - writew(bits, devpriv->main_iobase + DAC_SELECT_REG); -} - -static unsigned int get_ao_divisor(unsigned int ns, unsigned int flags) -{ - return get_divisor(ns, flags) - 2; -} - -static void set_dac_interval_regs(struct comedi_device *dev, - const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - unsigned int divisor; - - if (cmd->scan_begin_src != TRIG_TIMER) - return; - - divisor = get_ao_divisor(cmd->scan_begin_arg, cmd->flags); - if (divisor > max_counter_value) { - dev_err(dev->class_dev, "bug! ao divisor too big\n"); - divisor = max_counter_value; - } - writew(divisor & 0xffff, - devpriv->main_iobase + DAC_SAMPLE_INTERVAL_LOWER_REG); - writew((divisor >> 16) & 0xff, - devpriv->main_iobase + DAC_SAMPLE_INTERVAL_UPPER_REG); -} - -static int prep_ao_dma(struct comedi_device *dev, const struct comedi_cmd *cmd) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->write_subdev; - unsigned int nsamples; - unsigned int nbytes; - int i; - - /* - * clear queue pointer too, since external queue has - * weird interactions with ao fifo - */ - writew(0, devpriv->main_iobase + ADC_QUEUE_CLEAR_REG); - writew(0, devpriv->main_iobase + DAC_BUFFER_CLEAR_REG); - - nsamples = cb_pcidas64_ao_fill_buffer(dev, s, - devpriv->ao_bounce_buffer, - DAC_FIFO_SIZE); - if (nsamples == 0) - return -1; - - for (i = 0; i < nsamples; i++) { - writew(devpriv->ao_bounce_buffer[i], - devpriv->main_iobase + DAC_FIFO_REG); - } - - if (cmd->stop_src == TRIG_COUNT && - s->async->scans_done >= cmd->stop_arg) - return 0; - - nbytes = load_ao_dma_buffer(dev, cmd); - if (nbytes == 0) - return -1; - load_ao_dma(dev, cmd); - - dma_start_sync(dev, 0); - - return 0; -} - -static inline int external_ai_queue_in_use(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - - if (!dev->read_subdev->busy) - return 0; - if (board->layout == LAYOUT_4020) - return 0; - else if (use_internal_queue_6xxx(&dev->read_subdev->async->cmd)) - return 0; - return 1; -} - -static int ao_inttrig(struct comedi_device *dev, struct comedi_subdevice *s, - unsigned int trig_num) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - int retval; - - if (trig_num != cmd->start_arg) - return -EINVAL; - - retval = prep_ao_dma(dev, cmd); - if (retval < 0) - return -EPIPE; - - set_dac_control0_reg(dev, cmd); - - if (cmd->start_src == TRIG_INT) - writew(0, devpriv->main_iobase + DAC_START_REG); - - s->async->inttrig = NULL; - - return 0; -} - -static int ao_cmd(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct pcidas64_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - - if (external_ai_queue_in_use(dev)) { - warn_external_queue(dev); - return -EBUSY; - } - /* disable analog output system during setup */ - writew(0x0, devpriv->main_iobase + DAC_CONTROL0_REG); - - devpriv->ao_dma_index = 0; - - set_dac_select_reg(dev, cmd); - set_dac_interval_regs(dev, cmd); - load_first_dma_descriptor(dev, 0, devpriv->ao_dma_desc_bus_addr | - PLX_DMADPR_DESCPCI | PLX_DMADPR_TCINTR); - - set_dac_control1_reg(dev, cmd); - s->async->inttrig = ao_inttrig; - - return 0; -} - -static int cb_pcidas64_ao_check_chanlist(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - unsigned int chan0 = CR_CHAN(cmd->chanlist[0]); - int i; - - for (i = 1; i < cmd->chanlist_len; i++) { - unsigned int chan = CR_CHAN(cmd->chanlist[i]); - - if (chan != (chan0 + i)) { - dev_dbg(dev->class_dev, - "chanlist must use consecutive channels\n"); - return -EINVAL; - } - } - - return 0; -} - -static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - const struct pcidas64_board *board = dev->board_ptr; - int err = 0; - unsigned int tmp_arg; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, - TRIG_TIMER | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_NOW); - err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); - err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE); - - if (err) - return 1; - - /* Step 2a : make sure trigger sources are unique */ - - err |= comedi_check_trigger_is_unique(cmd->start_src); - err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); - - /* Step 2b : and mutually compatible */ - - if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER) - err |= -EINVAL; - if (cmd->stop_src != TRIG_COUNT && - cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT) - err |= -EINVAL; - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - - if (cmd->scan_begin_src == TRIG_TIMER) { - err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, - board->ao_scan_speed); - if (get_ao_divisor(cmd->scan_begin_arg, cmd->flags) > - max_counter_value) { - cmd->scan_begin_arg = (max_counter_value + 2) * - TIMER_BASE; - err |= -EINVAL; - } - } - - err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); - err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, - cmd->chanlist_len); - - if (err) - return 3; - - /* step 4: fix up any arguments */ - - if (cmd->scan_begin_src == TRIG_TIMER) { - tmp_arg = cmd->scan_begin_arg; - cmd->scan_begin_arg = get_divisor(cmd->scan_begin_arg, - cmd->flags) * TIMER_BASE; - if (tmp_arg != cmd->scan_begin_arg) - err++; - } - - if (err) - return 4; - - /* Step 5: check channel list if it exists */ - if (cmd->chanlist && cmd->chanlist_len > 0) - err |= cb_pcidas64_ao_check_chanlist(dev, s, cmd); - - if (err) - return 5; - - return 0; -} - -static int ao_cancel(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct pcidas64_private *devpriv = dev->private; - - writew(0x0, devpriv->main_iobase + DAC_CONTROL0_REG); - abort_dma(dev, 0); - return 0; -} - -static int dio_callback_4020(struct comedi_device *dev, - int dir, int port, int data, unsigned long iobase) -{ - struct pcidas64_private *devpriv = dev->private; - - if (dir) { - writew(data, devpriv->main_iobase + iobase + 2 * port); - return 0; - } - return readw(devpriv->main_iobase + iobase + 2 * port); -} - -static int di_rbits(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - unsigned int bits; - - bits = readb(dev->mmio + DI_REG); - bits &= 0xf; - data[1] = bits; - data[0] = 0; - - return insn->n; -} - -static int do_wbits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - if (comedi_dio_update_state(s, data)) - writeb(s->state, dev->mmio + DO_REG); - - data[1] = s->state; - - return insn->n; -} - -static int dio_60xx_config_insn(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int ret; - - ret = comedi_dio_insn_config(dev, s, insn, data, 0); - if (ret) - return ret; - - writeb(s->io_bits, dev->mmio + DIO_DIRECTION_60XX_REG); - - return insn->n; -} - -static int dio_60xx_wbits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - if (comedi_dio_update_state(s, data)) - writeb(s->state, dev->mmio + DIO_DATA_60XX_REG); - - data[1] = readb(dev->mmio + DIO_DATA_60XX_REG); - - return insn->n; -} - -/* - * pci-6025 8800 caldac: - * address 0 == dac channel 0 offset - * address 1 == dac channel 0 gain - * address 2 == dac channel 1 offset - * address 3 == dac channel 1 gain - * address 4 == fine adc offset - * address 5 == coarse adc offset - * address 6 == coarse adc gain - * address 7 == fine adc gain - */ -/* - * pci-6402/16 uses all 8 channels for dac: - * address 0 == dac channel 0 fine gain - * address 1 == dac channel 0 coarse gain - * address 2 == dac channel 0 coarse offset - * address 3 == dac channel 1 coarse offset - * address 4 == dac channel 1 fine gain - * address 5 == dac channel 1 coarse gain - * address 6 == dac channel 0 fine offset - * address 7 == dac channel 1 fine offset - */ - -static int caldac_8800_write(struct comedi_device *dev, unsigned int address, - u8 value) -{ - struct pcidas64_private *devpriv = dev->private; - static const int num_caldac_channels = 8; - static const int bitstream_length = 11; - unsigned int bitstream = ((address & 0x7) << 8) | value; - unsigned int bit, register_bits; - static const int caldac_8800_udelay = 1; - - if (address >= num_caldac_channels) { - dev_err(dev->class_dev, "illegal caldac channel\n"); - return -1; - } - for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) { - register_bits = 0; - if (bitstream & bit) - register_bits |= SERIAL_DATA_IN_BIT; - udelay(caldac_8800_udelay); - writew(register_bits, devpriv->main_iobase + CALIBRATION_REG); - register_bits |= SERIAL_CLOCK_BIT; - udelay(caldac_8800_udelay); - writew(register_bits, devpriv->main_iobase + CALIBRATION_REG); - } - udelay(caldac_8800_udelay); - writew(SELECT_8800_BIT, devpriv->main_iobase + CALIBRATION_REG); - udelay(caldac_8800_udelay); - writew(0, devpriv->main_iobase + CALIBRATION_REG); - udelay(caldac_8800_udelay); - return 0; -} - -/* 4020 caldacs */ -static int caldac_i2c_write(struct comedi_device *dev, - unsigned int caldac_channel, unsigned int value) -{ - u8 serial_bytes[3]; - u8 i2c_addr; - enum pointer_bits { - /* manual has gain and offset bits switched */ - OFFSET_0_2 = 0x1, - GAIN_0_2 = 0x2, - OFFSET_1_3 = 0x4, - GAIN_1_3 = 0x8, - }; - enum data_bits { - NOT_CLEAR_REGISTERS = 0x20, - }; - - switch (caldac_channel) { - case 0: /* chan 0 offset */ - i2c_addr = CALDAC0_I2C_ADDR; - serial_bytes[0] = OFFSET_0_2; - break; - case 1: /* chan 1 offset */ - i2c_addr = CALDAC0_I2C_ADDR; - serial_bytes[0] = OFFSET_1_3; - break; - case 2: /* chan 2 offset */ - i2c_addr = CALDAC1_I2C_ADDR; - serial_bytes[0] = OFFSET_0_2; - break; - case 3: /* chan 3 offset */ - i2c_addr = CALDAC1_I2C_ADDR; - serial_bytes[0] = OFFSET_1_3; - break; - case 4: /* chan 0 gain */ - i2c_addr = CALDAC0_I2C_ADDR; - serial_bytes[0] = GAIN_0_2; - break; - case 5: /* chan 1 gain */ - i2c_addr = CALDAC0_I2C_ADDR; - serial_bytes[0] = GAIN_1_3; - break; - case 6: /* chan 2 gain */ - i2c_addr = CALDAC1_I2C_ADDR; - serial_bytes[0] = GAIN_0_2; - break; - case 7: /* chan 3 gain */ - i2c_addr = CALDAC1_I2C_ADDR; - serial_bytes[0] = GAIN_1_3; - break; - default: - dev_err(dev->class_dev, "invalid caldac channel\n"); - return -1; - } - serial_bytes[1] = NOT_CLEAR_REGISTERS | ((value >> 8) & 0xf); - serial_bytes[2] = value & 0xff; - i2c_write(dev, i2c_addr, serial_bytes, 3); - return 0; -} - -static void caldac_write(struct comedi_device *dev, unsigned int channel, - unsigned int value) -{ - const struct pcidas64_board *board = dev->board_ptr; - - switch (board->layout) { - case LAYOUT_60XX: - case LAYOUT_64XX: - caldac_8800_write(dev, channel, value); - break; - case LAYOUT_4020: - caldac_i2c_write(dev, channel, value); - break; - default: - break; - } -} - -static int cb_pcidas64_calib_insn_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int chan = CR_CHAN(insn->chanspec); - - /* - * Programming the calib device is slow. Only write the - * last data value if the value has changed. - */ - if (insn->n) { - unsigned int val = data[insn->n - 1]; - - if (s->readback[chan] != val) { - caldac_write(dev, chan, val); - s->readback[chan] = val; - } - } - - return insn->n; -} - -static void ad8402_write(struct comedi_device *dev, unsigned int channel, - unsigned int value) -{ - struct pcidas64_private *devpriv = dev->private; - static const int bitstream_length = 10; - unsigned int bit, register_bits; - unsigned int bitstream = ((channel & 0x3) << 8) | (value & 0xff); - static const int ad8402_udelay = 1; - - register_bits = SELECT_8402_64XX_BIT; - udelay(ad8402_udelay); - writew(register_bits, devpriv->main_iobase + CALIBRATION_REG); - - for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) { - if (bitstream & bit) - register_bits |= SERIAL_DATA_IN_BIT; - else - register_bits &= ~SERIAL_DATA_IN_BIT; - udelay(ad8402_udelay); - writew(register_bits, devpriv->main_iobase + CALIBRATION_REG); - udelay(ad8402_udelay); - writew(register_bits | SERIAL_CLOCK_BIT, - devpriv->main_iobase + CALIBRATION_REG); - } - - udelay(ad8402_udelay); - writew(0, devpriv->main_iobase + CALIBRATION_REG); -} - -/* for pci-das6402/16, channel 0 is analog input gain and channel 1 is offset */ -static int cb_pcidas64_ad8402_insn_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int chan = CR_CHAN(insn->chanspec); - - /* - * Programming the calib device is slow. Only write the - * last data value if the value has changed. - */ - if (insn->n) { - unsigned int val = data[insn->n - 1]; - - if (s->readback[chan] != val) { - ad8402_write(dev, chan, val); - s->readback[chan] = val; - } - } - - return insn->n; -} - -static u16 read_eeprom(struct comedi_device *dev, u8 address) -{ - struct pcidas64_private *devpriv = dev->private; - static const int bitstream_length = 11; - static const int read_command = 0x6; - unsigned int bitstream = (read_command << 8) | address; - unsigned int bit; - void __iomem * const plx_control_addr = - devpriv->plx9080_iobase + PLX_REG_CNTRL; - u16 value; - static const int value_length = 16; - static const int eeprom_udelay = 1; - - udelay(eeprom_udelay); - devpriv->plx_control_bits &= ~PLX_CNTRL_EESK & ~PLX_CNTRL_EECS; - /* make sure we don't send anything to the i2c bus on 4020 */ - devpriv->plx_control_bits |= PLX_CNTRL_USERO; - writel(devpriv->plx_control_bits, plx_control_addr); - /* activate serial eeprom */ - udelay(eeprom_udelay); - devpriv->plx_control_bits |= PLX_CNTRL_EECS; - writel(devpriv->plx_control_bits, plx_control_addr); - - /* write read command and desired memory address */ - for (bit = 1 << (bitstream_length - 1); bit; bit >>= 1) { - /* set bit to be written */ - udelay(eeprom_udelay); - if (bitstream & bit) - devpriv->plx_control_bits |= PLX_CNTRL_EEWB; - else - devpriv->plx_control_bits &= ~PLX_CNTRL_EEWB; - writel(devpriv->plx_control_bits, plx_control_addr); - /* clock in bit */ - udelay(eeprom_udelay); - devpriv->plx_control_bits |= PLX_CNTRL_EESK; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(eeprom_udelay); - devpriv->plx_control_bits &= ~PLX_CNTRL_EESK; - writel(devpriv->plx_control_bits, plx_control_addr); - } - /* read back value from eeprom memory location */ - value = 0; - for (bit = 1 << (value_length - 1); bit; bit >>= 1) { - /* clock out bit */ - udelay(eeprom_udelay); - devpriv->plx_control_bits |= PLX_CNTRL_EESK; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(eeprom_udelay); - devpriv->plx_control_bits &= ~PLX_CNTRL_EESK; - writel(devpriv->plx_control_bits, plx_control_addr); - udelay(eeprom_udelay); - if (readl(plx_control_addr) & PLX_CNTRL_EERB) - value |= bit; - } - - /* deactivate eeprom serial input */ - udelay(eeprom_udelay); - devpriv->plx_control_bits &= ~PLX_CNTRL_EECS; - writel(devpriv->plx_control_bits, plx_control_addr); - - return value; -} - -static int eeprom_read_insn(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data) -{ - unsigned int val; - unsigned int i; - - if (insn->n) { - /* No point reading the same EEPROM location more than once. */ - val = read_eeprom(dev, CR_CHAN(insn->chanspec)); - for (i = 0; i < insn->n; i++) - data[i] = val; - } - - return insn->n; -} - -/* Allocate and initialize the subdevice structures. */ -static int setup_subdevices(struct comedi_device *dev) -{ - const struct pcidas64_board *board = dev->board_ptr; - struct pcidas64_private *devpriv = dev->private; - struct comedi_subdevice *s; - int i; - int ret; - - ret = comedi_alloc_subdevices(dev, 10); - if (ret) - return ret; - - s = &dev->subdevices[0]; - /* analog input subdevice */ - dev->read_subdev = s; - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DITHER | SDF_CMD_READ; - if (board->layout == LAYOUT_60XX) - s->subdev_flags |= SDF_COMMON | SDF_DIFF; - else if (board->layout == LAYOUT_64XX) - s->subdev_flags |= SDF_DIFF; - /* XXX Number of inputs in differential mode is ignored */ - s->n_chan = board->ai_se_chans; - s->len_chanlist = 0x2000; - s->maxdata = (1 << board->ai_bits) - 1; - s->range_table = board->ai_range_table; - s->insn_read = ai_rinsn; - s->insn_config = ai_config_insn; - s->do_cmd = ai_cmd; - s->do_cmdtest = ai_cmdtest; - s->cancel = ai_cancel; - if (board->layout == LAYOUT_4020) { - u8 data; - /* - * set adc to read from inputs - * (not internal calibration sources) - */ - devpriv->i2c_cal_range_bits = adc_src_4020_bits(4); - /* set channels to +-5 volt input ranges */ - for (i = 0; i < s->n_chan; i++) - devpriv->i2c_cal_range_bits |= attenuate_bit(i); - data = devpriv->i2c_cal_range_bits; - i2c_write(dev, RANGE_CAL_I2C_ADDR, &data, sizeof(data)); - } - - /* analog output subdevice */ - s = &dev->subdevices[1]; - if (board->ao_nchan) { - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | - SDF_GROUND | SDF_CMD_WRITE; - s->n_chan = board->ao_nchan; - s->maxdata = (1 << board->ao_bits) - 1; - s->range_table = board->ao_range_table; - s->insn_write = ao_winsn; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - - if (ao_cmd_is_supported(board)) { - dev->write_subdev = s; - s->do_cmdtest = ao_cmdtest; - s->do_cmd = ao_cmd; - s->len_chanlist = board->ao_nchan; - s->cancel = ao_cancel; - } - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* digital input */ - s = &dev->subdevices[2]; - if (board->layout == LAYOUT_64XX) { - s->type = COMEDI_SUBD_DI; - s->subdev_flags = SDF_READABLE; - s->n_chan = 4; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = di_rbits; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* digital output */ - if (board->layout == LAYOUT_64XX) { - s = &dev->subdevices[3]; - s->type = COMEDI_SUBD_DO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 4; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = do_wbits; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* 8255 */ - s = &dev->subdevices[4]; - if (board->has_8255) { - if (board->layout == LAYOUT_4020) { - ret = subdev_8255_init(dev, s, dio_callback_4020, - I8255_4020_REG); - } else { - ret = subdev_8255_mm_init(dev, s, NULL, - DIO_8255_OFFSET); - } - if (ret) - return ret; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* 8 channel dio for 60xx */ - s = &dev->subdevices[5]; - if (board->layout == LAYOUT_60XX) { - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_WRITABLE | SDF_READABLE; - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_config = dio_60xx_config_insn; - s->insn_bits = dio_60xx_wbits; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* caldac */ - s = &dev->subdevices[6]; - s->type = COMEDI_SUBD_CALIB; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; - s->n_chan = 8; - if (board->layout == LAYOUT_4020) - s->maxdata = 0xfff; - else - s->maxdata = 0xff; - s->insn_write = cb_pcidas64_calib_insn_write; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - - for (i = 0; i < s->n_chan; i++) { - caldac_write(dev, i, s->maxdata / 2); - s->readback[i] = s->maxdata / 2; - } - - /* 2 channel ad8402 potentiometer */ - s = &dev->subdevices[7]; - if (board->layout == LAYOUT_64XX) { - s->type = COMEDI_SUBD_CALIB; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL; - s->n_chan = 2; - s->maxdata = 0xff; - s->insn_write = cb_pcidas64_ad8402_insn_write; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - - for (i = 0; i < s->n_chan; i++) { - ad8402_write(dev, i, s->maxdata / 2); - s->readback[i] = s->maxdata / 2; - } - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* serial EEPROM, if present */ - s = &dev->subdevices[8]; - if (readl(devpriv->plx9080_iobase + PLX_REG_CNTRL) & - PLX_CNTRL_EEPRESENT) { - s->type = COMEDI_SUBD_MEMORY; - s->subdev_flags = SDF_READABLE | SDF_INTERNAL; - s->n_chan = 128; - s->maxdata = 0xffff; - s->insn_read = eeprom_read_insn; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* user counter subd XXX */ - s = &dev->subdevices[9]; - s->type = COMEDI_SUBD_UNUSED; - - return 0; -} - -static int auto_attach(struct comedi_device *dev, - unsigned long context) -{ - struct pci_dev *pcidev = comedi_to_pci_dev(dev); - const struct pcidas64_board *board = NULL; - struct pcidas64_private *devpriv; - u32 local_range, local_decode; - int retval; - - if (context < ARRAY_SIZE(pcidas64_boards)) - board = &pcidas64_boards[context]; - if (!board) - return -ENODEV; - dev->board_ptr = board; - - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - - retval = comedi_pci_enable(dev); - if (retval) - return retval; - pci_set_master(pcidev); - - /* Initialize dev->board_name */ - dev->board_name = board->name; - - devpriv->main_phys_iobase = pci_resource_start(pcidev, 2); - devpriv->dio_counter_phys_iobase = pci_resource_start(pcidev, 3); - - devpriv->plx9080_iobase = pci_ioremap_bar(pcidev, 0); - devpriv->main_iobase = pci_ioremap_bar(pcidev, 2); - dev->mmio = pci_ioremap_bar(pcidev, 3); - - if (!devpriv->plx9080_iobase || !devpriv->main_iobase || !dev->mmio) { - dev_warn(dev->class_dev, "failed to remap io memory\n"); - return -ENOMEM; - } - - /* figure out what local addresses are */ - local_range = readl(devpriv->plx9080_iobase + PLX_REG_LAS0RR) & - PLX_LASRR_MEM_MASK; - local_decode = readl(devpriv->plx9080_iobase + PLX_REG_LAS0BA) & - local_range & PLX_LASBA_MEM_MASK; - devpriv->local0_iobase = ((u32)devpriv->main_phys_iobase & - ~local_range) | local_decode; - local_range = readl(devpriv->plx9080_iobase + PLX_REG_LAS1RR) & - PLX_LASRR_MEM_MASK; - local_decode = readl(devpriv->plx9080_iobase + PLX_REG_LAS1BA) & - local_range & PLX_LASBA_MEM_MASK; - devpriv->local1_iobase = ((u32)devpriv->dio_counter_phys_iobase & - ~local_range) | local_decode; - - retval = alloc_and_init_dma_members(dev); - if (retval < 0) - return retval; - - devpriv->hw_revision = - hw_revision(dev, readw(devpriv->main_iobase + HW_STATUS_REG)); - dev_dbg(dev->class_dev, "stc hardware revision %i\n", - devpriv->hw_revision); - init_plx9080(dev); - init_stc_registers(dev); - - retval = request_irq(pcidev->irq, handle_interrupt, IRQF_SHARED, - "cb_pcidas64", dev); - if (retval) { - dev_dbg(dev->class_dev, "unable to allocate irq %u\n", - pcidev->irq); - return retval; - } - dev->irq = pcidev->irq; - dev_dbg(dev->class_dev, "irq %u\n", dev->irq); - - retval = setup_subdevices(dev); - if (retval < 0) - return retval; - - return 0; -} - -static void detach(struct comedi_device *dev) -{ - struct pcidas64_private *devpriv = dev->private; - - if (dev->irq) - free_irq(dev->irq, dev); - if (devpriv) { - if (devpriv->plx9080_iobase) { - disable_plx_interrupts(dev); - iounmap(devpriv->plx9080_iobase); - } - if (devpriv->main_iobase) - iounmap(devpriv->main_iobase); - if (dev->mmio) - iounmap(dev->mmio); - } - comedi_pci_disable(dev); - cb_pcidas64_free_dma(dev); -} - -static struct comedi_driver cb_pcidas64_driver = { - .driver_name = "cb_pcidas64", - .module = THIS_MODULE, - .auto_attach = auto_attach, - .detach = detach, -}; - -static int cb_pcidas64_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - return comedi_pci_auto_config(dev, &cb_pcidas64_driver, - id->driver_data); -} - -static const struct pci_device_id cb_pcidas64_pci_table[] = { - { PCI_VDEVICE(CB, 0x001d), BOARD_PCIDAS6402_16 }, - { PCI_VDEVICE(CB, 0x001e), BOARD_PCIDAS6402_12 }, - { PCI_VDEVICE(CB, 0x0035), BOARD_PCIDAS64_M1_16 }, - { PCI_VDEVICE(CB, 0x0036), BOARD_PCIDAS64_M2_16 }, - { PCI_VDEVICE(CB, 0x0037), BOARD_PCIDAS64_M3_16 }, - { PCI_VDEVICE(CB, 0x0052), BOARD_PCIDAS4020_12 }, - { PCI_VDEVICE(CB, 0x005d), BOARD_PCIDAS6023 }, - { PCI_VDEVICE(CB, 0x005e), BOARD_PCIDAS6025 }, - { PCI_VDEVICE(CB, 0x005f), BOARD_PCIDAS6030 }, - { PCI_VDEVICE(CB, 0x0060), BOARD_PCIDAS6031 }, - { PCI_VDEVICE(CB, 0x0061), BOARD_PCIDAS6032 }, - { PCI_VDEVICE(CB, 0x0062), BOARD_PCIDAS6033 }, - { PCI_VDEVICE(CB, 0x0063), BOARD_PCIDAS6034 }, - { PCI_VDEVICE(CB, 0x0064), BOARD_PCIDAS6035 }, - { PCI_VDEVICE(CB, 0x0065), BOARD_PCIDAS6040 }, - { PCI_VDEVICE(CB, 0x0066), BOARD_PCIDAS6052 }, - { PCI_VDEVICE(CB, 0x0067), BOARD_PCIDAS6070 }, - { PCI_VDEVICE(CB, 0x0068), BOARD_PCIDAS6071 }, - { PCI_VDEVICE(CB, 0x006f), BOARD_PCIDAS6036 }, - { PCI_VDEVICE(CB, 0x0078), BOARD_PCIDAS6013 }, - { PCI_VDEVICE(CB, 0x0079), BOARD_PCIDAS6014 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, cb_pcidas64_pci_table); - -static struct pci_driver cb_pcidas64_pci_driver = { - .name = "cb_pcidas64", - .id_table = cb_pcidas64_pci_table, - .probe = cb_pcidas64_pci_probe, - .remove = comedi_pci_auto_unconfig, -}; -module_comedi_pci_driver(cb_pcidas64_driver, cb_pcidas64_pci_driver); - -MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi low-level driver"); -MODULE_LICENSE("GPL"); |