// SPDX-License-Identifier: GPL-2.0-only /* * AD9910 SPI DDS (Direct Digital Synthesizer) driver * * Copyright 2026 Analog Devices Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Register addresses */ #define AD9910_REG_CFR1 0x00 #define AD9910_REG_CFR2 0x01 #define AD9910_REG_CFR3 0x02 #define AD9910_REG_AUX_DAC 0x03 #define AD9910_REG_IO_UPDATE_RATE 0x04 #define AD9910_REG_FTW 0x07 #define AD9910_REG_POW 0x08 #define AD9910_REG_ASF 0x09 #define AD9910_REG_MULTICHIP_SYNC 0x0A #define AD9910_REG_DRG_LIMIT 0x0B #define AD9910_REG_DRG_STEP 0x0C #define AD9910_REG_DRG_RATE 0x0D #define AD9910_REG_PROFILE0 0x0E #define AD9910_REG_PROFILE1 0x0F #define AD9910_REG_PROFILE2 0x10 #define AD9910_REG_PROFILE3 0x11 #define AD9910_REG_PROFILE4 0x12 #define AD9910_REG_PROFILE5 0x13 #define AD9910_REG_PROFILE6 0x14 #define AD9910_REG_PROFILE7 0x15 #define AD9910_REG_RAM 0x16 #define AD9910_REG_NUM_CACHED 0x16 #define AD9910_REG_PROFILE(x) (AD9910_REG_PROFILE0 + (x)) /* CFR1 bit definitions */ #define AD9910_CFR1_RAM_ENABLE_MSK BIT(31) #define AD9910_CFR1_RAM_PLAYBACK_DEST_MSK GENMASK(30, 29) #define AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK BIT(23) #define AD9910_CFR1_INV_SINC_EN_MSK BIT(22) #define AD9910_CFR1_INT_PROFILE_CTL_MSK GENMASK(20, 17) #define AD9910_CFR1_SELECT_SINE_MSK BIT(16) #define AD9910_CFR1_LOAD_LRR_IO_UPDATE_MSK BIT(15) #define AD9910_CFR1_AUTOCLR_DIG_RAMP_ACCUM_MSK BIT(14) #define AD9910_CFR1_AUTOCLR_PHASE_ACCUM_MSK BIT(13) #define AD9910_CFR1_CLEAR_DIG_RAMP_ACCUM_MSK BIT(12) #define AD9910_CFR1_CLEAR_PHASE_ACCUM_MSK BIT(11) #define AD9910_CFR1_LOAD_ARR_IO_UPDATE_MSK BIT(10) #define AD9910_CFR1_OSK_ENABLE_MSK BIT(9) #define AD9910_CFR1_SELECT_AUTO_OSK_MSK BIT(8) #define AD9910_CFR1_DIGITAL_POWER_DOWN_MSK BIT(7) #define AD9910_CFR1_DAC_POWER_DOWN_MSK BIT(6) #define AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK BIT(5) #define AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK BIT(4) #define AD9910_CFR1_EXT_POWER_DOWN_CTL_MSK BIT(3) #define AD9910_CFR1_SDIO_INPUT_ONLY_MSK BIT(1) #define AD9910_CFR1_LSB_FIRST_MSK BIT(0) /* DIGITAL and REFCLK_INPUT powerdown bits are effective without I/O update */ #define AD9910_CFR1_SW0_POWER_DOWN_MSK (AD9910_CFR1_DIGITAL_POWER_DOWN_MSK | \ AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK) /* DAC and AUX_DAC powerdown require I/O update */ #define AD9910_CFR1_SW1_POWER_DOWN_MSK (AD9910_CFR1_DAC_POWER_DOWN_MSK | \ AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK) #define AD9910_CFR1_SW_POWER_DOWN_MSK (AD9910_CFR1_SW0_POWER_DOWN_MSK | \ AD9910_CFR1_SW1_POWER_DOWN_MSK) /* CFR2 bit definitions */ #define AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK BIT(24) #define AD9910_CFR2_INTERNAL_IO_UPDATE_MSK BIT(23) #define AD9910_CFR2_SYNC_CLK_EN_MSK BIT(22) #define AD9910_CFR2_DRG_DEST_MSK GENMASK(21, 20) #define AD9910_CFR2_DRG_ENABLE_MSK BIT(19) #define AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK BIT(18) #define AD9910_CFR2_DRG_NO_DWELL_LOW_MSK BIT(17) #define AD9910_CFR2_DRG_NO_DWELL_MSK GENMASK(18, 17) #define AD9910_CFR2_READ_EFFECTIVE_FTW_MSK BIT(16) #define AD9910_CFR2_IO_UPDATE_RATE_CTL_MSK GENMASK(15, 14) #define AD9910_CFR2_PDCLK_ENABLE_MSK BIT(11) #define AD9910_CFR2_PDCLK_INVERT_MSK BIT(10) #define AD9910_CFR2_TXENABLE_INVERT_MSK BIT(9) #define AD9910_CFR2_MATCHED_LATENCY_EN_MSK BIT(7) #define AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK BIT(6) #define AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK BIT(5) #define AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK BIT(4) #define AD9910_CFR2_FM_GAIN_MSK GENMASK(3, 0) /* CFR3 bit definitions */ #define AD9910_CFR3_OPEN_MSK (BIT(27) | GENMASK(18, 16)) #define AD9910_CFR3_DRV0_MSK GENMASK(29, 28) #define AD9910_CFR3_VCO_SEL_MSK GENMASK(26, 24) #define AD9910_CFR3_ICP_MSK GENMASK(21, 19) #define AD9910_CFR3_REFCLK_DIV_BYPASS_MSK BIT(15) #define AD9910_CFR3_REFCLK_DIV_RESETB_MSK BIT(14) #define AD9910_CFR3_PFD_RESET_MSK BIT(10) #define AD9910_CFR3_PLL_EN_MSK BIT(8) #define AD9910_CFR3_N_MSK GENMASK(7, 1) /* Auxiliary DAC Control Register Bits */ #define AD9910_AUX_DAC_FSC_MSK GENMASK(7, 0) /* POW Register Bits */ #define AD9910_POW_PP_LSB_MSK GENMASK(7, 0) /* ASF Register Bits */ #define AD9910_ASF_RAMP_RATE_MSK GENMASK(31, 16) #define AD9910_ASF_SCALE_FACTOR_MSK GENMASK(15, 2) #define AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK GENMASK(7, 2) #define AD9910_ASF_STEP_SIZE_MSK GENMASK(1, 0) /* Multichip Sync Register Bits */ #define AD9910_MC_SYNC_VALIDATION_DELAY_MSK GENMASK(31, 28) #define AD9910_MC_SYNC_RECEIVER_ENABLE_MSK BIT(27) #define AD9910_MC_SYNC_GENERATOR_ENABLE_MSK BIT(26) #define AD9910_MC_SYNC_GENERATOR_POLARITY_MSK BIT(25) #define AD9910_MC_SYNC_STATE_PRESET_MSK GENMASK(23, 18) #define AD9910_MC_SYNC_OUTPUT_DELAY_MSK GENMASK(15, 11) #define AD9910_MC_SYNC_INPUT_DELAY_MSK GENMASK(7, 3) /* Digital Ramp Limit Register */ #define AD9910_DRG_LIMIT_UPPER_MSK GENMASK_ULL(63, 32) #define AD9910_DRG_LIMIT_LOWER_MSK GENMASK_ULL(31, 0) /* Digital Ramp Step Register */ #define AD9910_DRG_STEP_DEC_MSK GENMASK_ULL(63, 32) #define AD9910_DRG_STEP_INC_MSK GENMASK_ULL(31, 0) /* Digital Ramp Rate Register */ #define AD9910_DRG_RATE_DEC_MSK GENMASK(31, 16) #define AD9910_DRG_RATE_INC_MSK GENMASK(15, 0) /* Profile Register Format (Single Tone Mode) */ #define AD9910_PROFILE_ST_ASF_MSK GENMASK_ULL(61, 48) #define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32) #define AD9910_PROFILE_ST_FTW_MSK GENMASK_ULL(31, 0) /* Profile Register Format (RAM Mode) */ #define AD9910_PROFILE_RAM_OPEN_MSK GENMASK_ULL(61, 56) #define AD9910_PROFILE_RAM_STEP_RATE_MSK GENMASK_ULL(55, 40) #define AD9910_PROFILE_RAM_END_ADDR_MSK GENMASK_ULL(39, 30) #define AD9910_PROFILE_RAM_START_ADDR_MSK GENMASK_ULL(23, 14) #define AD9910_PROFILE_RAM_NO_DWELL_HIGH_MSK BIT_ULL(5) #define AD9910_PROFILE_RAM_ZERO_CROSSING_MSK BIT_ULL(3) #define AD9910_PROFILE_RAM_MODE_CONTROL_MSK GENMASK_ULL(2, 0) /* Device constants */ #define AD9910_PI_NANORAD 3141592653UL #define AD9910_PI_PICORAD 3141592653590ULL #define AD9910_MAX_SYSCLK_HZ (1000 * HZ_PER_MHZ) #define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500) #define AD9910_ASF_MAX FIELD_MAX(AD9910_PROFILE_ST_ASF_MSK) #define AD9910_ASF_PP_LSB_MAX FIELD_MAX(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK) #define AD9910_POW_MAX FIELD_MAX(AD9910_PROFILE_ST_POW_MSK) #define AD9910_POW_PP_LSB_MAX FIELD_MAX(AD9910_POW_PP_LSB_MSK) #define AD9910_STEP_RATE_MAX FIELD_MAX(AD9910_DRG_RATE_DEC_MSK) #define AD9910_NUM_PROFILES 8 #define AD9910_RAM_FW_MAGIC 0x00AD9910 #define AD9910_RAM_FW_V1 0x0001 #define AD9910_RAM_SIZE_MAX_WORDS 1024 #define AD9910_RAM_WORD_SIZE sizeof(u32) #define AD9910_RAM_SIZE_MAX_BYTES (AD9910_RAM_SIZE_MAX_WORDS * AD9910_RAM_WORD_SIZE) #define AD9910_RAM_ADDR_MAX (AD9910_RAM_SIZE_MAX_WORDS - 1) #define AD9910_RAM_ENABLED(st) \ FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, (st)->reg[AD9910_REG_CFR1].val32) #define AD9910_DEST_FREQUENCY 0 #define AD9910_DEST_PHASE 1 #define AD9910_DEST_AMPLITUDE 2 #define AD9910_DEST_POLAR 3 /* PLL constants */ #define AD9910_PLL_MIN_N 12 #define AD9910_PLL_MAX_N 127 #define AD9910_PLL_IN_MIN_FREQ_HZ (3307 * HZ_PER_KHZ) #define AD9910_PLL_IN_MAX_FREQ_HZ (60 * HZ_PER_MHZ) #define AD9910_PLL_OUT_MIN_FREQ_HZ (420 * HZ_PER_MHZ) #define AD9910_PLL_OUT_MAX_FREQ_HZ AD9910_MAX_SYSCLK_HZ #define AD9910_VCO0_RANGE_AUTO_MAX_HZ (457 * HZ_PER_MHZ) #define AD9910_VCO1_RANGE_AUTO_MAX_HZ (530 * HZ_PER_MHZ) #define AD9910_VCO2_RANGE_AUTO_MAX_HZ (632 * HZ_PER_MHZ) #define AD9910_VCO3_RANGE_AUTO_MAX_HZ (775 * HZ_PER_MHZ) #define AD9910_VCO4_RANGE_AUTO_MAX_HZ (897 * HZ_PER_MHZ) #define AD9910_VCO_RANGE_NUM 6 #define AD9910_ICP_MIN_uA 212 #define AD9910_ICP_MAX_uA 387 #define AD9910_ICP_STEP_uA 25 #define AD9910_DAC_IOUT_MAX_uA 31590 #define AD9910_DAC_IOUT_DEFAULT_uA 20070 #define AD9910_DAC_IOUT_MIN_uA 8640 /* altcurrent ABI is in mA */ #define AD9910_NANO_MILLIAMP_PER_MICROAMP 1000000UL #define AD9910_PICO_MILLIAMP_PER_MICROAMP 1000000000UL #define AD9910_REFDIV2_MIN_FREQ_HZ (25 * HZ_PER_MHZ) #define AD9910_REFDIV2_MAX_FREQ_HZ (1900 * HZ_PER_MHZ) #define AD9910_WAKEUP_DELAY_us 1000 /* 1ms: datasheet Table 1 */ #define AD9910_RESET_DELAY_us 1 /* 5 sysclk cycles: < 1us */ #define AD9910_SPI_DATA_IDX 1 #define AD9910_SPI_DATA_LEN_MAX AD9910_RAM_SIZE_MAX_BYTES #define AD9910_SPI_MESSAGE_LEN_MAX (AD9910_SPI_DATA_IDX + AD9910_SPI_DATA_LEN_MAX) #define AD9910_SPI_READ_MSK BIT(7) #define AD9910_SPI_ADDR_MSK GENMASK(4, 0) /** * enum ad9910_channel - AD9910 channel identifiers in priority order * * @AD9910_CHANNEL_PHY: Physical output channel * @AD9910_CHANNEL_PROFILE_0: Profile 0 output channel * @AD9910_CHANNEL_PROFILE_1: Profile 1 output channel * @AD9910_CHANNEL_PROFILE_2: Profile 2 output channel * @AD9910_CHANNEL_PROFILE_3: Profile 3 output channel * @AD9910_CHANNEL_PROFILE_4: Profile 4 output channel * @AD9910_CHANNEL_PROFILE_5: Profile 5 output channel * @AD9910_CHANNEL_PROFILE_6: Profile 6 output channel * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel * @AD9910_CHANNEL_PARALLEL: Parallel Data output channel * @AD9910_CHANNEL_PARALLEL_POLAR: Parallel Polar Data output channel * @AD9910_CHANNEL_DRG: Digital Ramp Generator output channel * @AD9910_CHANNEL_DRG_RAMP_UP: DRG ramp up channel * @AD9910_CHANNEL_DRG_RAMP_DOWN: DRG ramp down channel * @AD9910_CHANNEL_RAM: RAM control output channel * @AD9910_CHANNEL_OSK: Output Shift Keying output channel */ enum ad9910_channel { AD9910_CHANNEL_PHY = 100, AD9910_CHANNEL_PROFILE_0 = 110, AD9910_CHANNEL_PROFILE_1 = 111, AD9910_CHANNEL_PROFILE_2 = 112, AD9910_CHANNEL_PROFILE_3 = 113, AD9910_CHANNEL_PROFILE_4 = 114, AD9910_CHANNEL_PROFILE_5 = 115, AD9910_CHANNEL_PROFILE_6 = 116, AD9910_CHANNEL_PROFILE_7 = 117, AD9910_CHANNEL_PARALLEL = 120, AD9910_CHANNEL_PARALLEL_POLAR = 121, AD9910_CHANNEL_DRG = 130, AD9910_CHANNEL_DRG_RAMP_UP = 131, AD9910_CHANNEL_DRG_RAMP_DOWN = 132, AD9910_CHANNEL_RAM = 140, AD9910_CHANNEL_OSK = 150, }; /** * struct ad9910_ram_fw - AD9910 RAM firmware format * @magic: Magic number for RAM firmware validation * @version: Firmware version number * @wcount: Number of RAM words to be written * @crc: CRC32 checksum of the RAM data for integrity verification * @cfr1: Value of CFR1 register to be configured (not all fields are * used, but this is included here for convenience) * @profiles: Array of RAM profile configurations * @words: Array of RAM words to be written. Data pattern should be set in * reverse order and wcount specifies the number of words in this * array */ struct ad9910_ram_fw { __be32 magic; __be16 version; __be16 wcount; __be32 crc; __be32 cfr1; __be64 profiles[AD9910_NUM_PROFILES]; __be32 words[] __counted_by_be(wcount); } __packed; enum { AD9910_CHAN_IDX_PHY, AD9910_CHAN_IDX_PROFILE_0, AD9910_CHAN_IDX_PROFILE_1, AD9910_CHAN_IDX_PROFILE_2, AD9910_CHAN_IDX_PROFILE_3, AD9910_CHAN_IDX_PROFILE_4, AD9910_CHAN_IDX_PROFILE_5, AD9910_CHAN_IDX_PROFILE_6, AD9910_CHAN_IDX_PROFILE_7, AD9910_CHAN_IDX_PARALLEL_AMP, AD9910_CHAN_IDX_PARALLEL_PHASE, AD9910_CHAN_IDX_PARALLEL_FREQ, AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, AD9910_CHAN_IDX_DRG_FREQ, AD9910_CHAN_IDX_DRG_PHASE, AD9910_CHAN_IDX_DRG_AMP, AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, AD9910_CHAN_IDX_RAM, AD9910_CHAN_IDX_OSK, }; enum { AD9910_POWERDOWN, AD9910_DWELL_EN, AD9910_ROC, AD9910_ROC_AVAIL, }; struct ad9910_data { u32 sysclk_freq_hz; u32 output_current_uA; u16 pll_charge_pump_current; u8 refclk_out_drv; bool pll_enabled; }; union ad9910_reg { u64 val64; u32 val32; u16 val16; }; struct ad9910_state { struct spi_device *spi; struct clk *refclk; struct fw_upload *ram_fwu; struct gpio_desc *gpio_pwdown; struct gpio_desc *gpio_update; struct gpio_descs *gpio_profile; /* cached registers */ union ad9910_reg reg[AD9910_REG_NUM_CACHED]; /* * alternate profile registers used to store RAM profile settings when * RAM mode is disabled and Single Tone profile settings when RAM mode * is enabled. */ u64 reg_profile[AD9910_NUM_PROFILES]; /* Lock for accessing device registers and state variables */ struct mutex lock; struct ad9910_data data; u8 profile; bool ram_fwu_cancel; char ram_fwu_name[20]; /* * RAM loading requires a reasonable amount of bytes, at the same time * DMA capable SPI drivers requires the transfer buffers to live in * their own cache lines. */ u8 tx_buf[AD9910_SPI_MESSAGE_LEN_MAX] __aligned(IIO_DMA_MINALIGN); }; /** * ad9910_rational_scale() - Perform scaling of input given a reference. * @input: The input value to be scaled. * @scale: The numerator of the scaling factor. * @reference: The denominator of the scaling factor. * * Closest rounding with mul_u64_add_u64_div_u64 * * Return: The scaled value. */ static u64 ad9910_rational_scale(u64 input, u64 scale, u64 reference) { return mul_u64_add_u64_div_u64(input, scale, reference >> 1, reference); } static inline u64 ad9910_ram_profile_val(struct ad9910_state *st) { if (AD9910_RAM_ENABLED(st)) return st->reg[AD9910_REG_PROFILE(st->profile)].val64; else return st->reg_profile[st->profile]; } static inline u64 ad9910_st_profile_val(struct ad9910_state *st, u8 profile) { if (AD9910_RAM_ENABLED(st)) return st->reg_profile[profile]; else return st->reg[AD9910_REG_PROFILE(profile)].val64; } static int ad9910_io_update(struct ad9910_state *st) { if (st->gpio_update) { gpiod_set_value_cansleep(st->gpio_update, 1); fsleep(1); gpiod_set_value_cansleep(st->gpio_update, 0); } return 0; } static int ad9910_spi_read(struct ad9910_state *st, u8 reg, void *data, size_t len) { u8 inst = AD9910_SPI_READ_MSK | FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); return spi_write_then_read(st->spi, &inst, sizeof(inst), data, len); } static int ad9910_spi_write(struct ad9910_state *st, u8 reg, size_t len, bool update) { int ret; st->tx_buf[0] = FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); ret = spi_write(st->spi, st->tx_buf, AD9910_SPI_DATA_IDX + len); if (ret) return ret; if (update) return ad9910_io_update(st); return 0; } #define AD9910_REG_READ_FN(nb) \ static int ad9910_reg##nb##_read(struct ad9910_state *st, u8 reg, \ u##nb * data) \ { \ __be##nb be_data; \ int ret; \ \ ret = ad9910_spi_read(st, reg, &be_data, sizeof(be_data)); \ if (ret) \ return ret; \ \ *data = be##nb##_to_cpu(be_data); \ return ret; \ } AD9910_REG_READ_FN(16) AD9910_REG_READ_FN(32) AD9910_REG_READ_FN(64) #define AD9910_REG_WRITE_FN(nb) \ static int ad9910_reg##nb##_write(struct ad9910_state *st, u8 reg, \ u##nb data, bool update) \ { \ int ret; \ \ put_unaligned_be##nb(data, &st->tx_buf[AD9910_SPI_DATA_IDX]); \ ret = ad9910_spi_write(st, reg, sizeof(data), update); \ if (ret) \ return ret; \ \ st->reg[reg].val##nb = data; \ return ret; \ } AD9910_REG_WRITE_FN(16) AD9910_REG_WRITE_FN(32) AD9910_REG_WRITE_FN(64) #define AD9910_REG_UPDATE_FN(nb) \ static int ad9910_reg##nb##_update(struct ad9910_state *st, \ u8 reg, u##nb mask, \ u##nb data, bool update) \ { \ u##nb reg_val = (st->reg[reg].val##nb & ~mask) | (data & mask); \ \ if (reg_val == st->reg[reg].val##nb && !update) \ return 0; \ \ return ad9910_reg##nb##_write(st, reg, reg_val, update); \ } AD9910_REG_UPDATE_FN(16) AD9910_REG_UPDATE_FN(32) AD9910_REG_UPDATE_FN(64) static int ad9910_set_dac_current(struct ad9910_state *st, u32 val_uA, bool update) { u32 code; /* FSC = (86.4 / Rset) * (1 + CODE/96) where Rset = 10k ohms */ val_uA = clamp(val_uA, AD9910_DAC_IOUT_MIN_uA, AD9910_DAC_IOUT_MAX_uA); code = DIV_ROUND_CLOSEST(val_uA - AD9910_DAC_IOUT_MIN_uA, 90); st->data.output_current_uA = AD9910_DAC_IOUT_MIN_uA + code * 90; return ad9910_reg32_write(st, AD9910_REG_AUX_DAC, code, update); } static int ad9910_set_sysclk_freq(struct ad9910_state *st, u32 freq_hz, bool update) { struct device *dev = &st->spi->dev; unsigned long refclk_freq_hz; u32 sysclk_freq_hz; u32 tmp32, vco_sel; int ret; if (!freq_hz || freq_hz > AD9910_MAX_SYSCLK_HZ) return -EINVAL; refclk_freq_hz = clk_get_rate(st->refclk); if (st->data.pll_enabled) { if (refclk_freq_hz < AD9910_PLL_IN_MIN_FREQ_HZ || refclk_freq_hz > AD9910_PLL_IN_MAX_FREQ_HZ) { dev_err(dev, "REF_CLK freq %lu Hz is out of PLL input range\n", refclk_freq_hz); return -ERANGE; } tmp32 = DIV_ROUND_CLOSEST(freq_hz, refclk_freq_hz); tmp32 = clamp(tmp32, DIV_ROUND_UP(AD9910_PLL_OUT_MIN_FREQ_HZ, refclk_freq_hz), AD9910_PLL_OUT_MAX_FREQ_HZ / refclk_freq_hz); tmp32 = clamp(tmp32, AD9910_PLL_MIN_N, AD9910_PLL_MAX_N); sysclk_freq_hz = refclk_freq_hz * tmp32; if (sysclk_freq_hz <= AD9910_VCO0_RANGE_AUTO_MAX_HZ) vco_sel = 0; else if (sysclk_freq_hz <= AD9910_VCO1_RANGE_AUTO_MAX_HZ) vco_sel = 1; else if (sysclk_freq_hz <= AD9910_VCO2_RANGE_AUTO_MAX_HZ) vco_sel = 2; else if (sysclk_freq_hz <= AD9910_VCO3_RANGE_AUTO_MAX_HZ) vco_sel = 3; else if (sysclk_freq_hz <= AD9910_VCO4_RANGE_AUTO_MAX_HZ) vco_sel = 4; else vco_sel = 5; ret = ad9910_reg32_update(st, AD9910_REG_CFR3, AD9910_CFR3_N_MSK | AD9910_CFR3_VCO_SEL_MSK, FIELD_PREP(AD9910_CFR3_N_MSK, tmp32) | FIELD_PREP(AD9910_CFR3_VCO_SEL_MSK, vco_sel), update); if (ret) return ret; } else { if (refclk_freq_hz < AD9910_REFDIV2_MIN_FREQ_HZ || refclk_freq_hz > AD9910_REFDIV2_MAX_FREQ_HZ) { dev_err(dev, "REF_CLK freq %lu Hz is out of divider range\n", refclk_freq_hz); return -ERANGE; } tmp32 = DIV_ROUND_CLOSEST(refclk_freq_hz, freq_hz); tmp32 = clamp(tmp32, 1U, 2U); sysclk_freq_hz = refclk_freq_hz / tmp32; tmp32 = AD9910_CFR3_VCO_SEL_MSK | FIELD_PREP(AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, tmp32 % 2); ret = ad9910_reg32_update(st, AD9910_REG_CFR3, AD9910_CFR3_VCO_SEL_MSK | AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, tmp32, update); if (ret) return ret; } st->data.sysclk_freq_hz = sysclk_freq_hz; return 0; } static int ad9910_profile_set(struct ad9910_state *st, u8 profile) { DECLARE_BITMAP(values, BITS_PER_TYPE(profile)); st->profile = profile; values[0] = profile; gpiod_multi_set_value_cansleep(st->gpio_profile, values); return 0; } static bool ad9910_sw_powerdown_get(struct ad9910_state *st) { return FIELD_GET(AD9910_CFR1_SW_POWER_DOWN_MSK, st->reg[AD9910_REG_CFR1].val32) ? true : false; } static int ad9910_sw_powerdown_set(struct ad9910_state *st, bool enable) { int ret; if (ad9910_sw_powerdown_get(st) == enable) return 0; /* * When powering down, the DAC and AUX_DAC (SW1) must be powered down * first, as they require an I/O update to take effect. The opposite is * true when powering up, the DAC and AUX_DAC must be powered up last, * i.e., after the DIGITAL and REFCLK_INPUT (SW0) power up. */ if (enable) { ret = ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_SW1_POWER_DOWN_MSK, AD9910_CFR1_SW1_POWER_DOWN_MSK, true); if (ret) return ret; return ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_SW0_POWER_DOWN_MSK, AD9910_CFR1_SW0_POWER_DOWN_MSK, false); } ret = ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_SW0_POWER_DOWN_MSK, 0, false); if (ret) return ret; return ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_SW1_POWER_DOWN_MSK, 0, true); } static ssize_t ad9910_ext_info_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9910_state *st = iio_priv(indio_dev); int val; guard(mutex)(&st->lock); switch (private) { case AD9910_POWERDOWN: val = ad9910_sw_powerdown_get(st); break; case AD9910_DWELL_EN: if (chan->channel == AD9910_CHANNEL_DRG_RAMP_UP) val = FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; else val = FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; break; default: return -EINVAL; } return iio_format_value(buf, IIO_VAL_INT, 1, &val); } static ssize_t ad9910_ext_info_write(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { struct ad9910_state *st = iio_priv(indio_dev); u32 val32; int ret; ret = kstrtou32(buf, 10, &val32); if (ret) return ret; guard(mutex)(&st->lock); switch (private) { case AD9910_POWERDOWN: ret = ad9910_sw_powerdown_set(st, val32 ? true : false); if (ret) return ret; break; case AD9910_DWELL_EN: if (chan->channel == AD9910_CHANNEL_DRG_RAMP_UP) { val32 = val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK; ret = ad9910_reg32_update(st, AD9910_REG_CFR2, AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, val32, true); if (ret) return ret; } else { val32 = val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_LOW_MSK; ret = ad9910_reg32_update(st, AD9910_REG_CFR2, AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, val32, true); if (ret) return ret; } break; default: return -EINVAL; } return len; } static ssize_t ad9910_drg_roc_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9910_state *st = iio_priv(indio_dev); u64 roc64; u32 rate; guard(mutex)(&st->lock); switch (chan->channel) { case AD9910_CHANNEL_DRG_RAMP_UP: roc64 = FIELD_GET(AD9910_DRG_STEP_INC_MSK, st->reg[AD9910_REG_DRG_STEP].val64); rate = FIELD_GET(AD9910_DRG_RATE_INC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); break; case AD9910_CHANNEL_DRG_RAMP_DOWN: roc64 = FIELD_GET(AD9910_DRG_STEP_DEC_MSK, st->reg[AD9910_REG_DRG_STEP].val64); rate = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); break; default: return -EINVAL; } if (!rate) return -ERANGE; roc64 *= st->data.sysclk_freq_hz; return sysfs_emit(buf, "%llu\n", div_u64(roc64, 4 * rate)); } static ssize_t ad9910_drg_roc_write(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { struct ad9910_state *st = iio_priv(indio_dev); u64 tmp64; u32 rate; int ret; ret = kstrtou64(buf, 10, &tmp64); if (ret) return ret; guard(mutex)(&st->lock); switch (chan->channel) { case AD9910_CHANNEL_DRG_RAMP_UP: rate = FIELD_GET(AD9910_DRG_RATE_INC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); if (!rate) return -ERANGE; tmp64 = ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz); tmp64 = min_t(u64, tmp64, U32_MAX); ret = ad9910_reg64_update(st, AD9910_REG_DRG_STEP, AD9910_DRG_STEP_INC_MSK, FIELD_PREP(AD9910_DRG_STEP_INC_MSK, tmp64), true); if (ret) return ret; break; case AD9910_CHANNEL_DRG_RAMP_DOWN: rate = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); if (!rate) return -ERANGE; tmp64 = ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz); tmp64 = min_t(u64, tmp64, U32_MAX); ret = ad9910_reg64_update(st, AD9910_REG_DRG_STEP, AD9910_DRG_STEP_DEC_MSK, FIELD_PREP(AD9910_DRG_STEP_DEC_MSK, tmp64), true); if (ret) return ret; break; default: return -EINVAL; } return len; } static const u32 ad9910_osk_raw_step[] = { 0, /* no step: manual mode (NOT pin controlled) */ 1, /* step size 1: automatic mode (pin controlled) */ 2, /* step size 2: automatic mode (pin controlled) */ 4, /* step size 4: automatic mode (pin controlled) */ 8, /* step size 8: automatic mode (pin controlled) */ GENMASK(13, 0), /* max step: manual mode (pin controlled) */ }; static ssize_t ad9910_osk_attrs_read(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad9910_state *st = iio_priv(indio_dev); bool auto_en, pinctrl_en; u32 rate, step; u64 roc64; guard(mutex)(&st->lock); rate = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val32); if (!rate) return -ERANGE; switch (private) { case AD9910_ROC: auto_en = FIELD_GET(AD9910_CFR1_SELECT_AUTO_OSK_MSK, st->reg[AD9910_REG_CFR1].val32); pinctrl_en = FIELD_GET(AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, st->reg[AD9910_REG_CFR1].val32); if (auto_en) { step = FIELD_GET(AD9910_ASF_STEP_SIZE_MSK, st->reg[AD9910_REG_ASF].val32); step = ad9910_osk_raw_step[step + 1]; } else if (pinctrl_en) { step = ad9910_osk_raw_step[ARRAY_SIZE(ad9910_osk_raw_step) - 1]; } else { step = ad9910_osk_raw_step[0]; } roc64 = div_u64((u64)step * st->data.sysclk_freq_hz, 4 * rate); return sysfs_emit(buf, "%llu\n", roc64); case AD9910_ROC_AVAIL: { ssize_t len = 0; for (unsigned int i = 0; i < ARRAY_SIZE(ad9910_osk_raw_step); i++) { roc64 = div_u64((u64)ad9910_osk_raw_step[i] * st->data.sysclk_freq_hz, 4 * rate); len += sysfs_emit_at(buf, len, "%llu ", roc64); } buf[len - 1] = '\n'; /* replace last space with a newline */ return len; } default: return -EINVAL; } } static ssize_t ad9910_osk_attrs_write(struct iio_dev *indio_dev, uintptr_t private, const struct iio_chan_spec *chan, const char *buf, size_t len) { struct ad9910_state *st = iio_priv(indio_dev); u32 idx, reg_val, rate; u64 step; int ret; ret = kstrtou64(buf, 10, &step); if (ret) return ret; guard(mutex)(&st->lock); rate = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val32); if (!rate) return -ERANGE; switch (private) { case AD9910_ROC: step = ad9910_rational_scale(step, 4 * rate, st->data.sysclk_freq_hz); step = min(step, AD9910_ASF_MAX); idx = find_closest(step, ad9910_osk_raw_step, ARRAY_SIZE(ad9910_osk_raw_step)); if (idx == ARRAY_SIZE(ad9910_osk_raw_step) - 1) { /* manual mode: pin-controlled */ reg_val = AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK; } else if (idx == 0) { /* manual mode, NOT pin-controlled */ reg_val = 0; } else { /* auto mode: pin-controlled */ reg_val = FIELD_PREP(AD9910_ASF_STEP_SIZE_MSK, idx - 1); ret = ad9910_reg32_update(st, AD9910_REG_ASF, AD9910_ASF_STEP_SIZE_MSK, reg_val, false); if (ret) return ret; reg_val = AD9910_CFR1_SELECT_AUTO_OSK_MSK; } ret = ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_SELECT_AUTO_OSK_MSK | AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, reg_val, true); if (ret) return ret; break; default: return -EINVAL; } return len; } static const struct iio_chan_spec_ext_info ad9910_phy_ext_info[] = { { .name = "powerdown", .read = ad9910_ext_info_read, .write = ad9910_ext_info_write, .private = AD9910_POWERDOWN, .shared = IIO_SEPARATE, }, { } }; static const struct iio_chan_spec_ext_info ad9910_drg_ramp_ext_info[] = { { .name = "dwell_en", .read = ad9910_ext_info_read, .write = ad9910_ext_info_write, .private = AD9910_DWELL_EN, .shared = IIO_SEPARATE, }, { .name = "raw_roc", .read = ad9910_drg_roc_read, .write = ad9910_drg_roc_write, .private = AD9910_ROC, .shared = IIO_SEPARATE, }, { } }; static const struct iio_chan_spec_ext_info ad9910_osk_ext_info[] = { { .name = "raw_roc", .read = ad9910_osk_attrs_read, .write = ad9910_osk_attrs_write, .private = AD9910_ROC, .shared = IIO_SEPARATE, }, { .name = "raw_roc_available", .read = ad9910_osk_attrs_read, .private = AD9910_ROC_AVAIL, .shared = IIO_SEPARATE, }, { } }; #define AD9910_PROFILE_CHAN(idx) { \ .type = IIO_ALTCURRENT, \ .indexed = 1, \ .output = 1, \ .channel = AD9910_CHANNEL_PROFILE_ ## idx, \ .address = AD9910_CHAN_IDX_PROFILE_ ## idx, \ .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | \ BIT(IIO_CHAN_INFO_FREQUENCY) | \ BIT(IIO_CHAN_INFO_PHASE) | \ BIT(IIO_CHAN_INFO_RAW), \ .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], \ } static const struct iio_chan_spec ad9910_channels[] = { [AD9910_CHAN_IDX_PHY] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PHY, .address = AD9910_CHAN_IDX_PHY, .info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), .ext_info = ad9910_phy_ext_info, }, [AD9910_CHAN_IDX_PROFILE_0] = AD9910_PROFILE_CHAN(0), [AD9910_CHAN_IDX_PROFILE_1] = AD9910_PROFILE_CHAN(1), [AD9910_CHAN_IDX_PROFILE_2] = AD9910_PROFILE_CHAN(2), [AD9910_CHAN_IDX_PROFILE_3] = AD9910_PROFILE_CHAN(3), [AD9910_CHAN_IDX_PROFILE_4] = AD9910_PROFILE_CHAN(4), [AD9910_CHAN_IDX_PROFILE_5] = AD9910_PROFILE_CHAN(5), [AD9910_CHAN_IDX_PROFILE_6] = AD9910_PROFILE_CHAN(6), [AD9910_CHAN_IDX_PROFILE_7] = AD9910_PROFILE_CHAN(7), [AD9910_CHAN_IDX_PARALLEL_AMP] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PARALLEL, .address = AD9910_CHAN_IDX_PARALLEL_AMP, .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_PARALLEL_PHASE] = { .type = IIO_PHASE, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PARALLEL, .address = AD9910_CHAN_IDX_PARALLEL_PHASE, .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_PARALLEL_FREQ] = { .type = IIO_FREQUENCY, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PARALLEL, .address = AD9910_CHAN_IDX_PARALLEL_FREQ, .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PARALLEL_POLAR, .address = AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] = { .type = IIO_PHASE, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_PARALLEL_POLAR, .address = AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_DRG_FREQ] = { .type = IIO_FREQUENCY, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG, .address = AD9910_CHAN_IDX_DRG_FREQ, .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_DRG_PHASE] = { .type = IIO_PHASE, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG, .address = AD9910_CHAN_IDX_DRG_PHASE, .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_DRG_AMP] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG, .address = AD9910_CHAN_IDX_DRG_AMP, .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] = { .type = IIO_FREQUENCY, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_UP, .address = AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], }, [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] = { .type = IIO_FREQUENCY, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, .address = AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], }, [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] = { .type = IIO_PHASE, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_UP, .address = AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], }, [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] = { .type = IIO_PHASE, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, .address = AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], }, [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_UP, .address = AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], }, [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_DRG_RAMP_DOWN, .address = AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_drg_ramp_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], }, [AD9910_CHAN_IDX_RAM] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_RAM, .address = AD9910_CHAN_IDX_RAM, .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_FREQUENCY) | BIT(IIO_CHAN_INFO_PHASE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, [AD9910_CHAN_IDX_OSK] = { .type = IIO_ALTCURRENT, .indexed = 1, .output = 1, .channel = AD9910_CHANNEL_OSK, .address = AD9910_CHAN_IDX_OSK, .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SAMP_FREQ), .ext_info = ad9910_osk_ext_info, .parent = &ad9910_channels[AD9910_CHAN_IDX_PHY], }, }; static int ad9910_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct ad9910_state *st = iio_priv(indio_dev); u64 tmp64; u32 tmp32; guard(mutex)(&st->lock); switch (info) { case IIO_CHAN_INFO_ENABLE: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: if (ad9910_sw_powerdown_get(st)) { *val = 0; } else { tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; *val = (tmp32 == st->profile); } break; case AD9910_CHANNEL_DRG: tmp32 = FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, st->reg[AD9910_REG_CFR2].val32); if (tmp32 == (chan->address - AD9910_CHAN_IDX_DRG_FREQ)) *val = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, st->reg[AD9910_REG_CFR2].val32); else *val = 0; break; case AD9910_CHANNEL_RAM: *val = FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, st->reg[AD9910_REG_CFR1].val32); break; case AD9910_CHANNEL_OSK: *val = FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, st->reg[AD9910_REG_CFR1].val32); break; default: return -EINVAL; } return IIO_VAL_INT; case IIO_CHAN_INFO_FREQUENCY: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; tmp64 = FIELD_GET(AD9910_PROFILE_ST_FTW_MSK, ad9910_st_profile_val(st, tmp32)); break; case AD9910_CHANNEL_RAM: tmp64 = st->reg[AD9910_REG_FTW].val32; break; default: return -EINVAL; } tmp64 *= st->data.sysclk_freq_hz; *val = tmp64 >> 32; *val2 = ((tmp64 & GENMASK_ULL(31, 0)) * MICRO) >> 32; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_PHASE: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; tmp64 = FIELD_GET(AD9910_PROFILE_ST_POW_MSK, ad9910_st_profile_val(st, tmp32)); break; case AD9910_CHANNEL_RAM: tmp64 = st->reg[AD9910_REG_POW].val16; break; default: return -EINVAL; } tmp32 = (tmp64 * AD9910_MAX_PHASE_MICRORAD) >> 16; *val = tmp32 / MICRO; *val2 = tmp32 % MICRO; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; *val = FIELD_GET(AD9910_PROFILE_ST_ASF_MSK, ad9910_st_profile_val(st, tmp32)); return IIO_VAL_INT; case AD9910_CHANNEL_DRG_RAMP_UP: tmp64 = FIELD_GET(AD9910_DRG_LIMIT_UPPER_MSK, st->reg[AD9910_REG_DRG_LIMIT].val64); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_INT_64; case AD9910_CHANNEL_DRG_RAMP_DOWN: tmp64 = FIELD_GET(AD9910_DRG_LIMIT_LOWER_MSK, st->reg[AD9910_REG_DRG_LIMIT].val64); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_INT_64; case AD9910_CHANNEL_OSK: *val = FIELD_GET(AD9910_ASF_SCALE_FACTOR_MSK, st->reg[AD9910_REG_ASF].val32); return IIO_VAL_INT; default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: switch (chan->channel) { case AD9910_CHANNEL_PHY: *val = st->data.sysclk_freq_hz; return IIO_VAL_INT; case AD9910_CHANNEL_DRG_RAMP_UP: tmp32 = FIELD_GET(AD9910_DRG_RATE_INC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); break; case AD9910_CHANNEL_DRG_RAMP_DOWN: tmp32 = FIELD_GET(AD9910_DRG_RATE_DEC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); break; case AD9910_CHANNEL_RAM: tmp32 = FIELD_GET(AD9910_PROFILE_RAM_STEP_RATE_MSK, ad9910_ram_profile_val(st)); break; case AD9910_CHANNEL_OSK: tmp32 = FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val32); break; default: return -EINVAL; } if (!tmp32) return -ERANGE; tmp32 *= 4; *val = st->data.sysclk_freq_hz / tmp32; *val2 = div_u64((u64)(st->data.sysclk_freq_hz % tmp32) * MICRO, tmp32); return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: switch (chan->address) { case AD9910_CHAN_IDX_PHY: tmp64 = (u64)st->data.output_current_uA * AD9910_NANO_MILLIAMP_PER_MICROAMP; *val = 0; *val2 = tmp64 >> 14; return IIO_VAL_INT_PLUS_NANO; case AD9910_CHAN_IDX_PARALLEL_PHASE: *val = 0; *val2 = AD9910_PI_NANORAD >> 15; return IIO_VAL_INT_PLUS_NANO; case AD9910_CHAN_IDX_PARALLEL_FREQ: tmp32 = FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, st->reg[AD9910_REG_CFR2].val32); tmp64 = (u64)st->data.sysclk_freq_hz << tmp32; tmp64 = ad9910_rational_scale(tmp64, NANO, BIT_ULL(32)); *val = div_s64_rem(tmp64, NANO, val2); return IIO_VAL_INT_PLUS_NANO; case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: tmp64 = (u64)st->data.output_current_uA * AD9910_NANO_MILLIAMP_PER_MICROAMP; *val = 0; *val2 = tmp64 >> 8; return IIO_VAL_INT_PLUS_NANO; case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: *val = 0; *val2 = AD9910_PI_NANORAD >> 7; return IIO_VAL_INT_PLUS_NANO; case AD9910_CHAN_IDX_DRG_FREQ: tmp64 = ad9910_rational_scale(st->data.sysclk_freq_hz, PICO, BIT_ULL(32)); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_DECIMAL64_PICO; case AD9910_CHAN_IDX_DRG_PHASE: tmp64 = DIV_U64_ROUND_CLOSEST(AD9910_PI_PICORAD, BIT(31)); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_DECIMAL64_PICO; case AD9910_CHAN_IDX_DRG_AMP: tmp64 = (u64)st->data.output_current_uA * AD9910_PICO_MILLIAMP_PER_MICROAMP; tmp64 = DIV_U64_ROUND_CLOSEST(tmp64 >> 1, BIT(31)); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_DECIMAL64_PICO; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: switch (chan->address) { case AD9910_CHAN_IDX_PARALLEL_FREQ: tmp64 = (u64)st->reg[AD9910_REG_FTW].val32 * MICRO; tmp64 >>= FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, st->reg[AD9910_REG_CFR2].val32); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_DECIMAL64_MICRO; case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: tmp32 = FIELD_GET(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, st->reg[AD9910_REG_ASF].val32); iio_val_s64_decompose(MICRO * tmp32 >> 6, val, val2); return IIO_VAL_DECIMAL64_MICRO; case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: tmp32 = FIELD_GET(AD9910_POW_PP_LSB_MSK, st->reg[AD9910_REG_POW].val16); iio_val_s64_decompose(MICRO * tmp32 >> 8, val, val2); return IIO_VAL_DECIMAL64_MICRO; default: return -EINVAL; } default: return -EINVAL; } } static int ad9910_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct ad9910_state *st = iio_priv(indio_dev); u64 tmp64; u32 tmp32; int ret, i; guard(mutex)(&st->lock); switch (info) { case IIO_CHAN_INFO_ENABLE: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; if (!val) { if (tmp32 != st->profile) return 0; /* nothing to do */ return ad9910_sw_powerdown_set(st, true); } ret = ad9910_sw_powerdown_set(st, false); if (ret) return ret; return ad9910_profile_set(st, tmp32); case AD9910_CHANNEL_DRG: tmp32 = chan->address - AD9910_CHAN_IDX_DRG_FREQ; if (val) { tmp32 = AD9910_CFR2_DRG_ENABLE_MSK | FIELD_PREP(AD9910_CFR2_DRG_DEST_MSK, tmp32); } else { if (tmp32 != FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, st->reg[AD9910_REG_CFR2].val32)) return 0; /* nothing to do */ tmp32 = 0; } return ad9910_reg32_update(st, AD9910_REG_CFR2, AD9910_CFR2_DRG_DEST_MSK | AD9910_CFR2_DRG_ENABLE_MSK, tmp32, true); case AD9910_CHANNEL_RAM: if (AD9910_RAM_ENABLED(st) == !!val) return 0; /* swap profile configs */ for (i = 0; i < AD9910_NUM_PROFILES; i++) { tmp64 = st->reg[AD9910_REG_PROFILE(i)].val64; ret = ad9910_reg64_write(st, AD9910_REG_PROFILE(i), st->reg_profile[i], false); if (ret) break; st->reg_profile[i] = tmp64; } if (ret) { /* * After the write failure, profiles 0..i-1 were * already swapped in SW, but Hw registers are * still pending an IO update, so swap them back * in SW to keep the state consistent. */ while (i--) { tmp64 = st->reg[AD9910_REG_PROFILE(i)].val64; st->reg[AD9910_REG_PROFILE(i)].val64 = st->reg_profile[i]; st->reg_profile[i] = tmp64; } return ret; } tmp32 = FIELD_PREP(AD9910_CFR1_RAM_ENABLE_MSK, !!val); return ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_RAM_ENABLE_MSK, tmp32, true); case AD9910_CHANNEL_OSK: tmp32 = FIELD_PREP(AD9910_CFR1_OSK_ENABLE_MSK, !!val); return ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_OSK_ENABLE_MSK, tmp32, true); default: return -EINVAL; } case IIO_CHAN_INFO_FREQUENCY: if (val < 0 || val2 < 0 || val >= st->data.sysclk_freq_hz / 2) return -EINVAL; tmp64 = ad9910_rational_scale((u64)val * MICRO + val2, BIT_ULL(32), (u64)MICRO * st->data.sysclk_freq_hz); tmp64 = min_t(u64, tmp64, U32_MAX); switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; if (AD9910_RAM_ENABLED(st)) { FIELD_MODIFY(AD9910_PROFILE_ST_FTW_MSK, &st->reg_profile[tmp32], tmp64); return 0; } tmp64 = FIELD_PREP(AD9910_PROFILE_ST_FTW_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_FTW_MSK, tmp64, true); case AD9910_CHANNEL_RAM: return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); default: return -EINVAL; } case IIO_CHAN_INFO_PHASE: if (val < 0 || val2 < 0) return -EINVAL; tmp64 = (u64)val * MICRO + val2; if (tmp64 >= AD9910_MAX_PHASE_MICRORAD) return -EINVAL; tmp64 = DIV_U64_ROUND_CLOSEST(tmp64 << 16, AD9910_MAX_PHASE_MICRORAD); tmp64 = min(tmp64, AD9910_POW_MAX); switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; if (AD9910_RAM_ENABLED(st)) { FIELD_MODIFY(AD9910_PROFILE_ST_POW_MSK, &st->reg_profile[tmp32], tmp64); return 0; } tmp64 = FIELD_PREP(AD9910_PROFILE_ST_POW_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_POW_MSK, tmp64, true); case AD9910_CHANNEL_RAM: return ad9910_reg16_write(st, AD9910_REG_POW, tmp64, true); default: return -EINVAL; } case IIO_CHAN_INFO_RAW: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: if (val < 0) return -EINVAL; tmp32 = chan->channel - AD9910_CHANNEL_PROFILE_0; tmp64 = min_t(u64, val, AD9910_ASF_MAX); if (AD9910_RAM_ENABLED(st)) { FIELD_MODIFY(AD9910_PROFILE_ST_ASF_MSK, &st->reg_profile[tmp32], tmp64); return 0; } tmp64 = FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_ASF_MSK, tmp64, true); case AD9910_CHANNEL_DRG_RAMP_UP: { s64 val64 = iio_val_s64_compose(val, val2); if (val64 < 0) return -EINVAL; tmp64 = min_t(u64, val64, U32_MAX); tmp64 = FIELD_PREP(AD9910_DRG_LIMIT_UPPER_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, AD9910_DRG_LIMIT_UPPER_MSK, tmp64, true); } case AD9910_CHANNEL_DRG_RAMP_DOWN: { s64 val64 = iio_val_s64_compose(val, val2); if (val64 < 0) return -EINVAL; tmp64 = min_t(u64, val64, U32_MAX); tmp64 = FIELD_PREP(AD9910_DRG_LIMIT_LOWER_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, AD9910_DRG_LIMIT_LOWER_MSK, tmp64, true); } case AD9910_CHANNEL_OSK: if (val < 0) return -EINVAL; tmp32 = min_t(u32, val, AD9910_ASF_MAX); tmp32 = FIELD_PREP(AD9910_ASF_SCALE_FACTOR_MSK, tmp32); return ad9910_reg32_update(st, AD9910_REG_ASF, AD9910_ASF_SCALE_FACTOR_MSK, tmp32, true); default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: if (chan->channel == AD9910_CHANNEL_PHY) return ad9910_set_sysclk_freq(st, val, true); if (val < 0 || val2 < 0 || val > st->data.sysclk_freq_hz / 4) return -EINVAL; tmp64 = ((u64)val * MICRO + val2) * 4; if (!tmp64) return -EINVAL; tmp64 = DIV64_U64_ROUND_CLOSEST((u64)st->data.sysclk_freq_hz * MICRO, tmp64); tmp32 = clamp(tmp64, 1U, AD9910_STEP_RATE_MAX); switch (chan->channel) { case AD9910_CHANNEL_DRG_RAMP_UP: tmp32 = FIELD_PREP(AD9910_DRG_RATE_INC_MSK, tmp32); return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, AD9910_DRG_RATE_INC_MSK, tmp32, true); case AD9910_CHANNEL_DRG_RAMP_DOWN: tmp32 = FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, tmp32); return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, AD9910_DRG_RATE_DEC_MSK, tmp32, true); case AD9910_CHANNEL_RAM: if (!AD9910_RAM_ENABLED(st)) { FIELD_MODIFY(AD9910_PROFILE_RAM_STEP_RATE_MSK, &st->reg_profile[st->profile], tmp32); return 0; } tmp64 = FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, tmp32); return ad9910_reg64_update(st, AD9910_REG_PROFILE(st->profile), AD9910_PROFILE_RAM_STEP_RATE_MSK, tmp64, true); case AD9910_CHANNEL_OSK: return ad9910_reg32_update(st, AD9910_REG_ASF, AD9910_ASF_RAMP_RATE_MSK, FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, tmp32), true); default: return -EINVAL; } case IIO_CHAN_INFO_SCALE: switch (chan->address) { case AD9910_CHAN_IDX_PHY: if (val != 0 || val2 < 0) return -EINVAL; tmp32 = DIV_U64_ROUND_CLOSEST((u64)val2 << 14, AD9910_NANO_MILLIAMP_PER_MICROAMP); return ad9910_set_dac_current(st, tmp32, true); case AD9910_CHAN_IDX_PARALLEL_FREQ: if (val < 0 || val2 < 0) return -EINVAL; tmp64 = ad9910_rational_scale((u64)val * NANO + val2, BIT_ULL(32), (u64)st->data.sysclk_freq_hz * NANO); tmp64 = roundup_pow_of_two(clamp(tmp64, 1ULL, BIT_ULL(15))); tmp32 = FIELD_PREP(AD9910_CFR2_FM_GAIN_MSK, ilog2(tmp64)); return ad9910_reg32_update(st, AD9910_REG_CFR2, AD9910_CFR2_FM_GAIN_MSK, tmp32, true); default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: { s64 val64 = iio_val_s64_compose(val, val2); if (val64 < 0) return -EINVAL; tmp64 = val64; switch (chan->address) { case AD9910_CHAN_IDX_PARALLEL_FREQ: tmp32 = BIT(FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, st->reg[AD9910_REG_CFR2].val32)); tmp64 = ad9910_rational_scale(tmp64, tmp32, MICRO); tmp64 = min_t(u64, tmp64, U32_MAX); return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: tmp64 = ad9910_rational_scale(tmp64, BIT(6), MICRO); tmp64 = min_t(u64, tmp64, AD9910_ASF_PP_LSB_MAX); tmp32 = FIELD_PREP(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, tmp64); return ad9910_reg32_update(st, AD9910_REG_ASF, AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, tmp32, true); case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: tmp64 = ad9910_rational_scale(tmp64, BIT(8), MICRO); tmp64 = min_t(u64, tmp64, AD9910_POW_PP_LSB_MAX); tmp32 = FIELD_PREP(AD9910_POW_PP_LSB_MSK, tmp64); return ad9910_reg16_update(st, AD9910_REG_POW, AD9910_POW_PP_LSB_MSK, tmp32, true); default: return -EINVAL; } } default: return -EINVAL; } } static int ad9910_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long mask) { switch (mask) { case IIO_CHAN_INFO_ENABLE: return IIO_VAL_INT; case IIO_CHAN_INFO_FREQUENCY: case IIO_CHAN_INFO_PHASE: return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_RAW: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: case AD9910_CHANNEL_OSK: return IIO_VAL_INT; case AD9910_CHANNEL_DRG_RAMP_UP: case AD9910_CHANNEL_DRG_RAMP_DOWN: return IIO_VAL_INT_64; default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: if (chan->channel == AD9910_CHANNEL_PHY) return IIO_VAL_INT; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_OFFSET: return IIO_VAL_DECIMAL64_MICRO; default: return -EINVAL; } } static int ad9910_debugfs_reg_access(struct iio_dev *indio_dev, unsigned int reg, u64 writeval, u64 *readval) { struct ad9910_state *st = iio_priv(indio_dev); union ad9910_reg tmp; int ret; if (reg >= AD9910_REG_RAM) return -EINVAL; guard(mutex)(&st->lock); switch (reg) { case AD9910_REG_DRG_LIMIT: case AD9910_REG_DRG_STEP: case AD9910_REG_PROFILE0 ... AD9910_REG_PROFILE7: if (!readval) return ad9910_reg64_write(st, reg, writeval, true); ret = ad9910_reg64_read(st, reg, &tmp.val64); if (ret) return ret; *readval = tmp.val64; return 0; case AD9910_REG_POW: if (!readval) return ad9910_reg16_write(st, reg, writeval, true); ret = ad9910_reg16_read(st, reg, &tmp.val16); if (ret) return ret; *readval = tmp.val16; return 0; default: if (!readval) return ad9910_reg32_write(st, reg, writeval, true); ret = ad9910_reg32_read(st, reg, &tmp.val32); if (ret) return ret; *readval = tmp.val32; return 0; } } static const char * const ad9910_channel_str[] = { [AD9910_CHAN_IDX_PHY] = "phy", [AD9910_CHAN_IDX_PROFILE_0] = "profile0", [AD9910_CHAN_IDX_PROFILE_1] = "profile1", [AD9910_CHAN_IDX_PROFILE_2] = "profile2", [AD9910_CHAN_IDX_PROFILE_3] = "profile3", [AD9910_CHAN_IDX_PROFILE_4] = "profile4", [AD9910_CHAN_IDX_PROFILE_5] = "profile5", [AD9910_CHAN_IDX_PROFILE_6] = "profile6", [AD9910_CHAN_IDX_PROFILE_7] = "profile7", [AD9910_CHAN_IDX_PARALLEL_AMP] = "parallel_amplitude", [AD9910_CHAN_IDX_PARALLEL_PHASE] = "parallel_phase", [AD9910_CHAN_IDX_PARALLEL_FREQ] = "parallel_frequency", [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] = "parallel_polar_amplitude", [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] = "parallel_polar_phase", [AD9910_CHAN_IDX_DRG_FREQ] = "drg_frequency", [AD9910_CHAN_IDX_DRG_PHASE] = "drg_phase", [AD9910_CHAN_IDX_DRG_AMP] = "drg_amplitude", [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] = "drg_rising_frequency", [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] = "drg_falling_frequency", [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] = "drg_rising_phase", [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] = "drg_falling_phase", [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] = "drg_rising_amplitude", [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] = "drg_falling_amplitude", [AD9910_CHAN_IDX_RAM] = "ram", [AD9910_CHAN_IDX_OSK] = "osk", }; static int ad9910_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) { return sysfs_emit(label, "%s\n", ad9910_channel_str[chan->address]); } static enum fw_upload_err ad9910_ram_fwu_prepare(struct fw_upload *fw_upload, const u8 *data, u32 size) { struct ad9910_state *st = fw_upload->dd_handle; const struct ad9910_ram_fw *fw_data = (const struct ad9910_ram_fw *)data; size_t wcount, bcount; scoped_guard(mutex, &st->lock) { /* cancel requests before prepare() are ignored */ st->ram_fwu_cancel = false; } if (size < sizeof(struct ad9910_ram_fw)) return FW_UPLOAD_ERR_INVALID_SIZE; if (get_unaligned_be32(&fw_data->magic) != AD9910_RAM_FW_MAGIC) return FW_UPLOAD_ERR_FW_INVALID; if (get_unaligned_be16(&fw_data->version) != AD9910_RAM_FW_V1) return FW_UPLOAD_ERR_FW_INVALID; wcount = get_unaligned_be16(&fw_data->wcount); bcount = size - sizeof(struct ad9910_ram_fw); if (wcount > AD9910_RAM_SIZE_MAX_WORDS || bcount != (wcount * AD9910_RAM_WORD_SIZE)) return FW_UPLOAD_ERR_INVALID_SIZE; bcount += sizeof(fw_data->cfr1) + sizeof(fw_data->profiles); if (crc32(0, &fw_data->cfr1, bcount) != get_unaligned_be32(&fw_data->crc)) return FW_UPLOAD_ERR_FW_INVALID; return FW_UPLOAD_ERR_NONE; } static enum fw_upload_err ad9910_ram_fwu_write(struct fw_upload *fw_upload, const u8 *data, u32 offset, u32 size, u32 *written) { const struct ad9910_ram_fw *fw_data = (const struct ad9910_ram_fw *)data; struct ad9910_state *st = fw_upload->dd_handle; int ret, ret2, idx, wcount; u64 tmp64, backup; if (offset != 0) return FW_UPLOAD_ERR_INVALID_SIZE; guard(mutex)(&st->lock); if (st->ram_fwu_cancel) return FW_UPLOAD_ERR_CANCELED; if (AD9910_RAM_ENABLED(st)) return FW_UPLOAD_ERR_HW_ERROR; for (idx = 0; idx < AD9910_NUM_PROFILES; idx++) st->reg_profile[idx] = get_unaligned_be64(&fw_data->profiles[idx]) | AD9910_PROFILE_RAM_OPEN_MSK; ret = ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_RAM_PLAYBACK_DEST_MSK | AD9910_CFR1_INT_PROFILE_CTL_MSK, get_unaligned_be32(&fw_data->cfr1), true); if (ret) return FW_UPLOAD_ERR_RW_ERROR; wcount = get_unaligned_be16(&fw_data->wcount); if (!wcount) { *written = size; return FW_UPLOAD_ERR_NONE; /* nothing else to write */ } ret = ad9910_profile_set(st, st->profile); if (ret) return FW_UPLOAD_ERR_HW_ERROR; /* backup profile register and update it with required address range */ backup = st->reg[AD9910_REG_PROFILE(st->profile)].val64; tmp64 = AD9910_PROFILE_RAM_STEP_RATE_MSK | FIELD_PREP(AD9910_PROFILE_RAM_START_ADDR_MSK, 0) | FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, wcount - 1); ret = ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), tmp64, true); if (ret) return FW_UPLOAD_ERR_RW_ERROR; /* copy data words into spi buffer and transfer */ memcpy(&st->tx_buf[1], fw_data->words, wcount * AD9910_RAM_WORD_SIZE); ret = ad9910_spi_write(st, AD9910_REG_RAM, wcount * AD9910_RAM_WORD_SIZE, false); /* restore active single tone profile regardless */ st->reg[AD9910_REG_PROFILE(st->profile)].val64 = backup; ret2 = ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), backup, true); if (ret || ret2) return FW_UPLOAD_ERR_RW_ERROR; *written = size; return FW_UPLOAD_ERR_NONE; } static enum fw_upload_err ad9910_ram_fwu_poll_complete(struct fw_upload *fw_upload) { return FW_UPLOAD_ERR_NONE; } static void ad9910_ram_fwu_cancel(struct fw_upload *fw_upload) { struct ad9910_state *st = fw_upload->dd_handle; guard(mutex)(&st->lock); st->ram_fwu_cancel = true; } static void ad9910_ram_fwu_unregister(void *data) { firmware_upload_unregister(data); } static const struct fw_upload_ops ad9910_ram_fwu_ops = { .prepare = ad9910_ram_fwu_prepare, .write = ad9910_ram_fwu_write, .poll_complete = ad9910_ram_fwu_poll_complete, .cancel = ad9910_ram_fwu_cancel, }; static const struct iio_info ad9910_info = { .read_raw = ad9910_read_raw, .write_raw = ad9910_write_raw, .write_raw_get_fmt = ad9910_write_raw_get_fmt, .read_label = ad9910_read_label, .debugfs_reg64_access = &ad9910_debugfs_reg_access, }; static int ad9910_cfg_sysclk(struct ad9910_state *st, bool update) { u32 cfr3 = AD9910_CFR3_OPEN_MSK; u32 tmp32; cfr3 |= FIELD_PREP(AD9910_CFR3_DRV0_MSK, st->data.refclk_out_drv); if (st->data.pll_enabled) { tmp32 = st->data.pll_charge_pump_current - AD9910_ICP_MIN_uA; tmp32 = DIV_ROUND_CLOSEST(tmp32, AD9910_ICP_STEP_uA); cfr3 |= FIELD_PREP(AD9910_CFR3_ICP_MSK, tmp32) | AD9910_CFR3_PLL_EN_MSK; } else { cfr3 |= AD9910_CFR3_ICP_MSK | AD9910_CFR3_REFCLK_DIV_RESETB_MSK | AD9910_CFR3_PFD_RESET_MSK; } st->reg[AD9910_REG_CFR3].val32 = cfr3; return ad9910_set_sysclk_freq(st, AD9910_MAX_SYSCLK_HZ, update); } static int ad9910_parse_fw(struct ad9910_state *st) { static const char * const refclk_out_drv0[] = { "disabled", "low", "medium", "high", }; struct device *dev = &st->spi->dev; const char *prop; u32 tmp; int ret; st->data.pll_enabled = device_property_read_bool(dev, "adi,pll-enable"); if (st->data.pll_enabled) { prop = "adi,charge-pump-current-microamp"; if (device_property_present(dev, prop)) { ret = device_property_read_u32(dev, prop, &tmp); if (ret) return dev_err_probe(dev, ret, "property read: %s\n", prop); if (tmp < AD9910_ICP_MIN_uA || tmp > AD9910_ICP_MAX_uA) return dev_err_probe(dev, -ERANGE, "invalid charge pump current %u\n", tmp); } else { tmp = AD9910_ICP_MIN_uA; } st->data.pll_charge_pump_current = tmp; prop = "adi,refclk-out-drive-strength"; if (device_property_present(dev, prop)) { ret = device_property_match_property_string(dev, prop, refclk_out_drv0, ARRAY_SIZE(refclk_out_drv0)); if (ret < 0) return dev_err_probe(dev, ret, "property read: %s\n", prop); st->data.refclk_out_drv = ret; } } return 0; } static void ad9910_sw_powerdown_action(void *data) { ad9910_sw_powerdown_set(data, true); } static void ad9910_hw_powerdown_action(void *data) { struct ad9910_state *st = data; gpiod_set_value_cansleep(st->gpio_pwdown, 1); } static int ad9910_setup(struct device *dev, struct ad9910_state *st, struct reset_control *dev_rst) { int ret; ret = reset_control_assert(dev_rst); if (ret) return ret; fsleep(AD9910_RESET_DELAY_us); ret = reset_control_deassert(dev_rst); if (ret) return ret; ret = ad9910_reg32_write(st, AD9910_REG_CFR1, (st->spi->mode & SPI_3WIRE ? 0 : AD9910_CFR1_SDIO_INPUT_ONLY_MSK), false); if (ret) return ret; ret = devm_add_action_or_reset(dev, ad9910_sw_powerdown_action, st); if (ret) return ret; ret = ad9910_reg32_write(st, AD9910_REG_CFR2, AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK | AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK | AD9910_CFR2_DRG_NO_DWELL_MSK | AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK | AD9910_CFR2_SYNC_CLK_EN_MSK | AD9910_CFR2_PDCLK_ENABLE_MSK, false); if (ret) return ret; ret = ad9910_cfg_sysclk(st, false); if (ret) return ret; ret = ad9910_set_dac_current(st, AD9910_DAC_IOUT_DEFAULT_uA, false); if (ret) return ret; /* configure step rate with default values */ ret = ad9910_reg32_write(st, AD9910_REG_ASF, FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, 1), false); if (ret) return ret; ret = ad9910_reg32_write(st, AD9910_REG_DRG_RATE, FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, 1) | FIELD_PREP(AD9910_DRG_RATE_INC_MSK, 1), false); if (ret) return ret; for (unsigned int i = 0; i < AD9910_NUM_PROFILES; i++) { st->reg_profile[i] = AD9910_PROFILE_RAM_OPEN_MSK; st->reg_profile[i] |= FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, 1); st->reg_profile[i] |= FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, AD9910_RAM_ADDR_MAX); } return ad9910_io_update(st); } static inline const char *ad9910_frequency_source_get(struct ad9910_state *st) { bool ram_en, mode_en; guard(mutex)(&st->lock); /* RAM enabled and data destination is frequency */ ram_en = AD9910_RAM_ENABLED(st); if (ram_en && AD9910_DEST_FREQUENCY == FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, st->reg[AD9910_REG_CFR1].val32)) return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; /* DRG enabled and data destination is frequency */ mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, st->reg[AD9910_REG_CFR2].val32); if (mode_en && AD9910_DEST_FREQUENCY == FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, st->reg[AD9910_REG_CFR2].val32)) return ad9910_channel_str[AD9910_CHAN_IDX_DRG_FREQ]; /* FTW: RAM enabled and data destination is phase, amplitude, or polar */ if (ram_en) return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; /* single tone profiles */ return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; } static int ad9910_frequency_source_show(struct seq_file *s, void *ignored) { seq_printf(s, "%s\n", ad9910_frequency_source_get(s->private)); return 0; } DEFINE_SHOW_ATTRIBUTE(ad9910_frequency_source); static inline const char *ad9910_phase_source_get(struct ad9910_state *st) { bool ram_en, mode_en; u32 destination; guard(mutex)(&st->lock); /* RAM enabled and data destination is phase or polar */ ram_en = AD9910_RAM_ENABLED(st); if (ram_en) { destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, st->reg[AD9910_REG_CFR1].val32); if (destination == AD9910_DEST_PHASE || destination == AD9910_DEST_POLAR) return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; } /* DRG enabled and data destination is phase */ mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, st->reg[AD9910_REG_CFR2].val32); if (mode_en && AD9910_DEST_PHASE == FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, st->reg[AD9910_REG_CFR2].val32)) return ad9910_channel_str[AD9910_CHAN_IDX_DRG_PHASE]; /* POW: RAM enabled and data destination is frequency or amplitude */ if (ram_en) return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; /* single tone profiles */ return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; } static int ad9910_phase_source_show(struct seq_file *s, void *ignored) { seq_printf(s, "%s\n", ad9910_phase_source_get(s->private)); return 0; } DEFINE_SHOW_ATTRIBUTE(ad9910_phase_source); static inline const char *ad9910_amplitude_source_get(struct ad9910_state *st) { bool ram_en, mode_en; u32 destination; guard(mutex)(&st->lock); /* OSK enabled */ mode_en = FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, st->reg[AD9910_REG_CFR1].val32); if (mode_en) return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; /* RAM enabled and data destination is amplitude or polar */ ram_en = AD9910_RAM_ENABLED(st); if (ram_en) { destination = FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, st->reg[AD9910_REG_CFR1].val32); if (destination == AD9910_DEST_AMPLITUDE || destination == AD9910_DEST_POLAR) return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; } /* DRG enabled and data destination is amplitude */ mode_en = FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, st->reg[AD9910_REG_CFR2].val32); if (mode_en && AD9910_DEST_AMPLITUDE == FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, st->reg[AD9910_REG_CFR2].val32)) return ad9910_channel_str[AD9910_CHAN_IDX_DRG_AMP]; /* only way to control amplitude at this point is through OSK */ if (ram_en) return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; /* single tone profiles */ return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; } static int ad9910_amplitude_source_show(struct seq_file *s, void *ignored) { seq_printf(s, "%s\n", ad9910_amplitude_source_get(s->private)); return 0; } DEFINE_SHOW_ATTRIBUTE(ad9910_amplitude_source); static inline void ad9910_debugfs_init(struct ad9910_state *st, struct iio_dev *indio_dev) { struct dentry *d = iio_get_debugfs_dentry(indio_dev); debugfs_create_file("frequency_source", 0400, d, st, &ad9910_frequency_source_fops); debugfs_create_file("phase_source", 0400, d, st, &ad9910_phase_source_fops); debugfs_create_file("amplitude_source", 0400, d, st, &ad9910_amplitude_source_fops); } static int ad9910_probe(struct spi_device *spi) { static const char * const supplies[] = { "dvdd-io33", "avdd33", "dvdd18", "avdd18", }; struct device *dev = &spi->dev; struct reset_control *dev_rst; struct gpio_desc *io_rst_gpio; struct iio_dev *indio_dev; struct ad9910_state *st; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); st->spi = spi; indio_dev->name = "ad9910"; indio_dev->info = &ad9910_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad9910_channels; indio_dev->num_channels = ARRAY_SIZE(ad9910_channels); ret = devm_mutex_init(dev, &st->lock); if (ret) return ret; ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies); if (ret) return dev_err_probe(dev, ret, "Failed to get regulators\n"); st->refclk = devm_clk_get_enabled(dev, "ref_clk"); if (IS_ERR(st->refclk)) return dev_err_probe(dev, PTR_ERR(st->refclk), "Failed to get reference clock\n"); dev_rst = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(dev_rst)) return dev_err_probe(dev, PTR_ERR(dev_rst), "failed to get device reset control\n"); /* * The IO RESET pin is not used in this driver, as we assume that all * SPI transfers are complete, but if it is wired up, we need to make * sure it is not floating. We can use either a reset controller or a * GPIO for this. */ io_rst_gpio = devm_gpiod_get_optional(dev, "io-reset", GPIOD_OUT_LOW); if (IS_ERR(io_rst_gpio)) return dev_err_probe(dev, PTR_ERR(io_rst_gpio), "failed to get io reset gpio\n"); st->gpio_update = devm_gpiod_get_optional(dev, "update", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_update)) return dev_err_probe(dev, PTR_ERR(st->gpio_update), "failed to get update gpio\n"); st->gpio_profile = devm_gpiod_get_array_optional(dev, "profile", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_profile)) return dev_err_probe(dev, PTR_ERR(st->gpio_profile), "failed to get profile gpios\n"); if (st->gpio_profile && st->gpio_profile->ndescs != 3) return dev_err_probe(dev, -EINVAL, "invalid number of profile gpios\n"); st->gpio_pwdown = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(st->gpio_pwdown)) return dev_err_probe(dev, PTR_ERR(st->gpio_pwdown), "failed to get powerdown gpio\n"); ret = devm_add_action_or_reset(dev, ad9910_hw_powerdown_action, st); if (ret) return dev_err_probe(dev, ret, "failed to add hw powerdown action\n"); fsleep(AD9910_WAKEUP_DELAY_us); ret = ad9910_parse_fw(st); if (ret) return ret; ret = ad9910_setup(dev, st, dev_rst); if (ret) return dev_err_probe(dev, ret, "device setup failed\n"); snprintf(st->ram_fwu_name, sizeof(st->ram_fwu_name), "%s:ram", dev_name(&indio_dev->dev)); st->ram_fwu = firmware_upload_register(THIS_MODULE, dev, st->ram_fwu_name, &ad9910_ram_fwu_ops, st); if (IS_ERR(st->ram_fwu)) return dev_err_probe(dev, PTR_ERR(st->ram_fwu), "failed to register ram upload ops\n"); ret = devm_add_action_or_reset(dev, ad9910_ram_fwu_unregister, st->ram_fwu); if (ret) return dev_err_probe(dev, ret, "failed to add ram upload unregister action\n"); ret = devm_iio_device_register(dev, indio_dev); if (ret) return ret; ad9910_debugfs_init(st, indio_dev); return 0; } static const struct spi_device_id ad9910_id[] = { { .name = "ad9910" }, { } }; MODULE_DEVICE_TABLE(spi, ad9910_id); static const struct of_device_id ad9910_of_match[] = { { .compatible = "adi,ad9910" }, { } }; MODULE_DEVICE_TABLE(of, ad9910_of_match); static struct spi_driver ad9910_driver = { .driver = { .name = "ad9910", .of_match_table = ad9910_of_match, }, .probe = ad9910_probe, .id_table = ad9910_id, }; module_spi_driver(ad9910_driver); MODULE_AUTHOR("Rodrigo Alencar "); MODULE_DESCRIPTION("Analog Devices AD9910 DDS driver"); MODULE_LICENSE("GPL");