From bff8bf117ea928a56dfe3a1dd947624627b9b3e7 Mon Sep 17 00:00:00 2001 From: George Mois Date: Thu, 22 May 2025 18:56:26 +0300 Subject: [PATCH 1/8] drivers: iio: frequency: adf4371: Add FSM ops Add JESD FSM ops. These modifications are required if the device is added to a JESD topology. Required by the AD9213 driver, so that the initialization sequence is synchronized with the AD9213 operation. Signed-off-by: George Mois --- drivers/iio/frequency/adf4371.c | 48 ++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index 32b058c9c8ee59..2b0768b89b0434 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -22,6 +22,8 @@ #include #include +#include + /* Registers address macro */ #define ADF4371_REG(x) (x) @@ -275,6 +277,7 @@ struct adf4371_state { * writes. */ struct mutex lock; + struct jesd204_dev *jdev; const struct adf4371_chip_info *chip_info; const char *adf4371_clk_names[4]; unsigned long clkin_freq; @@ -297,6 +300,10 @@ struct adf4371_state { u8 buf[10] __aligned(IIO_DMA_MINALIGN); }; +struct adf4371_jesd204_priv { + struct adf4371_state *adf_st; +}; + static unsigned long long adf4371_pll_fract_n_get_rate(struct adf4371_state *st, u32 channel) { @@ -1234,10 +1241,34 @@ static int adf4371_clks_register(struct iio_dev *indio_dev) adf4371_clk_del_provider, st); } +static int adf4371_jesd204_link_init(struct jesd204_dev *jdev, + enum jesd204_state_op_reason reason, + struct jesd204_link *lnk) +{ + struct device *dev = jesd204_dev_to_device(jdev); + + dev_dbg(dev, "%s:%d link_num %u reason %s\n", __func__, + __LINE__, lnk->link_id, jesd204_state_op_reason_str(reason)); + + return JESD204_STATE_CHANGE_DONE; +} + +static const struct jesd204_dev_data adf4371_jesd204_data = { + .state_ops = { + [JESD204_OP_LINK_INIT] = { + .per_link = adf4371_jesd204_link_init, + }, + }, + + .sizeof_priv = sizeof(struct adf4371_jesd204_priv), +}; + static int adf4371_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); + struct adf4371_jesd204_priv *priv; struct iio_dev *indio_dev; + struct jesd204_dev *jdev; struct adf4371_state *st; struct regmap *regmap; int ret; @@ -1294,6 +1325,11 @@ static int adf4371_probe(struct spi_device *spi) if (ret < 0) return ret; + jdev = devm_jesd204_dev_register(&spi->dev, &adf4371_jesd204_data); + if (IS_ERR(jdev)) + return dev_err_probe(&spi->dev, PTR_ERR(jdev), + "failed to register JESD204 device\n"); + ret = adf4371_setup(st); if (ret < 0) { dev_err(&spi->dev, "ADF4371 setup failed\n"); @@ -1304,7 +1340,17 @@ static int adf4371_probe(struct spi_device *spi) if (ret < 0) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + if (jdev) { + st->jdev = jdev; + priv = jesd204_dev_priv(jdev); + priv->adf_st = st; + } + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret < 0) + return ret; + + return devm_jesd204_fsm_start(&spi->dev, st->jdev, JESD204_LINKS_ALL); } static const struct spi_device_id adf4371_id_table[] = { From 6939e1fae414294d02bd51fb242be2f6e38bfb9d Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 09:42:33 +0300 Subject: [PATCH 2/8] drivers: iio: hmc7044: Add OSCOUT as channel Add OSCOUT1 as an output channel to the driver. The OSCOUT1 output is used a an input for the ADF4371 on the AD9213_EVB, and is used for synchronizing the initalization of the two devices in this setup. Signed-off-by: George Mois --- drivers/iio/frequency/hmc7044.c | 273 ++++++++++++++++++-------------- 1 file changed, 158 insertions(+), 115 deletions(-) diff --git a/drivers/iio/frequency/hmc7044.c b/drivers/iio/frequency/hmc7044.c index 9a94137d354dd3..e79e82d9e1be11 100644 --- a/drivers/iio/frequency/hmc7044.c +++ b/drivers/iio/frequency/hmc7044.c @@ -214,7 +214,7 @@ #define HMC7044_DYN_DRIVER_EN BIT(5) #define HMC7044_FORCE_MUTE_EN BIT(7) -#define HMC7044_NUM_CHAN 14 +#define HMC7044_NUM_CHAN 15 #define HMC7044_LOW_VCO_MIN 2150000 #define HMC7044_LOW_VCO_MAX 2880000 @@ -309,11 +309,6 @@ struct hmc7044 { bool oscout_path_en; bool oscout0_driver_en; bool oscout1_driver_en; - u32 oscout_divider_ratio; - u32 oscout0_driver_mode; - u32 oscout1_driver_mode; - u32 oscout0_driver_impedance; - u32 oscout1_driver_impedance; unsigned int sync_pin_mode; unsigned int pulse_gen_mode; unsigned int in_buf_mode[5]; @@ -449,6 +444,26 @@ static unsigned int hmc7044_calc_out_div(unsigned long parent_rate, return div; } +static unsigned int hmc7044_calc_oscout_div(unsigned long parent_rate, + unsigned long rate) +{ + unsigned int div; + + div = DIV_ROUND_CLOSEST(parent_rate, rate); + + /* Supported divide ratios are 1, 2, 4, and 8 */ + if (div == 1 || div == 2 || div == 4 || div == 8) + return div; + + if (div == 0) + return 1; + + if (div % 2) + div--; + + return (div == 6 || div > 8) ? 8 : div; +} + static int hmc7044_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -466,13 +481,20 @@ static int hmc7044_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_FREQUENCY: - *val = hmc->pll2_freq / ch->divider; + if (ch->num == 14) + *val = hmc->vcxo_freq / ch->divider; + else + *val = hmc->pll2_freq / ch->divider; return IIO_VAL_INT; case IIO_CHAN_INFO_PHASE: - hmc7044_read(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num), - &tmp); - tmp &= 0x1F; - code = DIV_ROUND_CLOSEST(tmp * 3141592, ch->divider); + if (ch->num == 14) { + code = 0; + } else { + hmc7044_read(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num), + &tmp); + tmp &= 0x1F; + code = DIV_ROUND_CLOSEST(tmp * 3141592, ch->divider); + } *val = code / 1000000; *val2 = code % 1000000; return IIO_VAL_INT_PLUS_MICRO; @@ -498,22 +520,33 @@ static int hmc7044_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_FREQUENCY: - ch->divider = hmc7044_calc_out_div(hmc->pll2_freq, val); - mutex_lock(&hmc->lock); - hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(ch->num), - HMC7044_DIV_LSB(ch->divider)); - hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(ch->num), - HMC7044_DIV_MSB(ch->divider)); - mutex_unlock(&hmc->lock); + if (ch->num == 14) { + ch->divider = hmc7044_calc_oscout_div(hmc->vcxo_freq, val); + mutex_lock(&hmc->lock); + hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH, + HMC7044_OSCOUT_DIVIDER(ch->divider <= 4 ? ch->divider / 2 : 3) | + hmc->oscout_path_en); + mutex_unlock(&hmc->lock); + } else { + ch->divider = hmc7044_calc_out_div(hmc->pll2_freq, val); + mutex_lock(&hmc->lock); + hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(ch->num), + HMC7044_DIV_LSB(ch->divider)); + hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(ch->num), + HMC7044_DIV_MSB(ch->divider)); + mutex_unlock(&hmc->lock); + } break; case IIO_CHAN_INFO_PHASE: - mutex_lock(&hmc->lock); - code = val * 1000000 + val2 % 1000000; - tmp = DIV_ROUND_CLOSEST(code * ch->divider, 3141592); - tmp = clamp_t(unsigned int, tmp, 0, 17); - hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num), - tmp); - mutex_unlock(&hmc->lock); + if (ch->num != 14) { + mutex_lock(&hmc->lock); + code = val * 1000000 + val2 % 1000000; + tmp = DIV_ROUND_CLOSEST(code * ch->divider, 3141592); + tmp = clamp_t(unsigned int, tmp, 0, 17); + hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(ch->num), + tmp); + mutex_unlock(&hmc->lock); + } break; default: return -EINVAL; @@ -811,11 +844,21 @@ static long hmc7044_clk_round_rate(struct clk_hw *hw, struct hmc7044_output *out = to_output(hw); struct iio_dev *indio_dev = out->indio_dev; struct hmc7044 *hmc = iio_priv(indio_dev); + struct hmc7044_chan_spec *ch; + long rounded_rate; unsigned int div; - div = hmc7044_calc_out_div(hmc->pll2_freq, rate); + ch = &hmc->channels[out->address]; + + if (ch->num == 14) { + div = hmc7044_calc_oscout_div(hmc->vcxo_freq, rate); + rounded_rate = DIV_ROUND_CLOSEST(hmc->vcxo_freq, div); + } else { + div = hmc7044_calc_out_div(hmc->vcxo_freq, rate); + rounded_rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div); + } - return DIV_ROUND_CLOSEST(hmc->pll2_freq, div); + return rounded_rate; } static int hmc7044_clk_determine_rate(struct clk_hw *hw, @@ -824,17 +867,25 @@ static int hmc7044_clk_determine_rate(struct clk_hw *hw, struct hmc7044_output *out = to_output(hw); struct iio_dev *indio_dev = out->indio_dev; struct hmc7044 *hmc = iio_priv(indio_dev); + struct hmc7044_chan_spec *ch; unsigned int div; - div = hmc7044_calc_out_div(hmc->pll2_freq, req->rate); + ch = &hmc->channels[out->address]; + + if (ch->num == 14) { + div = hmc7044_calc_oscout_div(hmc->vcxo_freq, req->rate); + + req->rate = DIV_ROUND_CLOSEST(hmc->vcxo_freq, div); + } else { + div = hmc7044_calc_out_div(hmc->pll2_freq, req->rate); - req->rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div); + req->rate = DIV_ROUND_CLOSEST(hmc->pll2_freq, div); + } return 0; } -static int hmc7044_clk_set_rate(struct clk_hw *hw, - unsigned long rate, +static int hmc7044_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { return hmc7044_set_clk_attr(hw, IIO_CHAN_INFO_FREQUENCY, rate); @@ -858,7 +909,7 @@ static int hmc7044_clk_register(struct iio_dev *indio_dev, init.name = hmc->clk_out_names[num]; init.ops = &hmc7044_clk_ops; - init.flags = 0; + init.flags = CLK_GET_RATE_NOCACHE; init.parent_names = (parent_name ? &parent_name : NULL); init.num_parents = (parent_name ? 1 : 0); @@ -1057,6 +1108,12 @@ static int hmc7044_setup(struct iio_dev *indio_dev) ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(i), 0); if (ret) return ret; + + if (i == 14) { + ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH, 0); + if (ret) + return ret; + } } /* Load the configuration updates (provided by Analog Devices) */ @@ -1264,43 +1321,75 @@ static int hmc7044_setup(struct iio_dev *indio_dev) if (chan->num >= HMC7044_NUM_CHAN || chan->disable) continue; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), - HMC7044_DIV_LSB(chan->divider)); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), - HMC7044_DIV_MSB(chan->divider)); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), - HMC7044_DRIVER_MODE(chan->driver_mode) | - HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | - (chan->dynamic_driver_enable ? - HMC7044_DYN_DRIVER_EN : 0) | - (chan->force_mute_enable ? - HMC7044_FORCE_MUTE_EN : 0)); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), - chan->fine_delay & 0x1F); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), - chan->coarse_delay & 0x1F); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), - chan->out_mux_mode & 0x3); - if (ret) - return ret; - ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), - (chan->start_up_mode_dynamic_enable ? - HMC7044_START_UP_MODE_DYN_EN : 0) | BIT(4) | - (chan->high_performance_mode_dis ? - 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | - HMC7044_CH_EN); - if (ret) - return ret; + if (chan->num < (HMC7044_NUM_CHAN - 1)) { + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_1(chan->num), + HMC7044_DIV_LSB(chan->divider)); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_2(chan->num), + HMC7044_DIV_MSB(chan->divider)); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_8(chan->num), + HMC7044_DRIVER_MODE(chan->driver_mode) | + HMC7044_DRIVER_Z_MODE(chan->driver_impedance) | + (chan->dynamic_driver_enable ? + HMC7044_DYN_DRIVER_EN : 0) | + (chan->force_mute_enable ? + HMC7044_FORCE_MUTE_EN : 0)); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_3(chan->num), + chan->fine_delay & 0x1F); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_4(chan->num), + chan->coarse_delay & 0x1F); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_7(chan->num), + chan->out_mux_mode & 0x3); + if (ret) + return ret; + ret = hmc7044_write(indio_dev, HMC7044_REG_CH_OUT_CRTL_0(chan->num), + (chan->start_up_mode_dynamic_enable ? + HMC7044_START_UP_MODE_DYN_EN : 0) | BIT(4) | + (chan->high_performance_mode_dis ? + 0 : HMC7044_HI_PERF_MODE) | HMC7044_SYNC_EN | + HMC7044_CH_EN); + if (ret) + return ret; + } else { + if (hmc->oscout_path_en) { + /* Make sure divider has acceptable value */ + if (chan->divider != 1 && chan->divider != 2 && + chan->divider != 4 && chan->divider != 8) + return -EINVAL; + ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH, + HMC7044_OSCOUT_DIVIDER(chan->divider <= 4 ? chan->divider / 2 : 3) | + HMC7044_CH_EN); + if (ret) + return ret; + } + + if (hmc->oscout0_driver_en) { + hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_0, + HMC7044_OSCOUT_DRIVER_MODE(chan->driver_mode) | + HMC7044_OSCOUT_IMPEDANCE(chan->driver_impedance) | + HMC7044_CH_EN); + if (ret) + return ret; + } + + if (hmc->oscout1_driver_en) { + hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_1, + HMC7044_OSCOUT_DRIVER_MODE(chan->driver_mode) | + HMC7044_OSCOUT_IMPEDANCE(chan->driver_impedance) | + HMC7044_CH_EN); + if (ret) + return ret; + } + } hmc->iio_channels[i].type = IIO_ALTVOLTAGE; hmc->iio_channels[i].output = 1; hmc->iio_channels[i].indexed = 1; @@ -1357,32 +1446,6 @@ static int hmc7044_setup(struct iio_dev *indio_dev) hmc->clk_data.clks = hmc->clks; hmc->clk_data.clk_num = HMC7044_NUM_CHAN; - if (hmc->oscout_path_en) { - ret = hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_PATH, - HMC7044_OSCOUT_DIVIDER(hmc->oscout_divider_ratio) | - hmc->oscout_path_en); - if (ret) - return ret; - } - - if (hmc->oscout0_driver_en) { - hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_0, - HMC7044_OSCOUT_DRIVER_MODE(hmc->oscout1_driver_mode) | - HMC7044_OSCOUT_IMPEDANCE(hmc->oscout1_driver_impedance) | - hmc->oscout1_driver_en); - if (ret) - return ret; - } - - if (hmc->oscout1_driver_en) { - hmc7044_write(indio_dev, HMC7044_REG_OSCOUT_DRIVER_1, - HMC7044_OSCOUT_DRIVER_MODE(hmc->oscout1_driver_mode) | - HMC7044_OSCOUT_IMPEDANCE(hmc->oscout1_driver_impedance) | - hmc->oscout1_driver_en); - if (ret) - return ret; - } - ret = hmc7044_info(indio_dev); if (ret) return ret; @@ -1707,30 +1770,10 @@ static int hmc7044_parse_dt(struct device *dev, hmc->oscout_path_en = of_property_read_bool(np, "adi,oscillator-output-path-enable"); - hmc->oscout_divider_ratio = HMC7044_DIVIDER_RATIO_1; - of_property_read_u32(np, "adi,oscillator-output-divider-ratio", - &hmc->oscout_divider_ratio); - hmc->oscout0_driver_en = of_property_read_bool(np, "adi,oscillator-output0-driver-enable"); - hmc->oscout0_driver_mode = HMC7044_DRIVER_MODE_CML; - of_property_read_u32(np, "adi,oscillator-output0-driver-mode", - &hmc->oscout0_driver_mode); - - hmc->oscout0_driver_impedance = HMC7044_DRIVER_IMPEDANCE_DISABLE; - of_property_read_u32(np, "adi,oscillator-output0-driver-impedance", - &hmc->oscout0_driver_impedance); - hmc->oscout1_driver_en = of_property_read_bool(np, "adi,oscillator-output1-driver-enable"); - hmc->oscout1_driver_mode = HMC7044_DRIVER_MODE_CML; - of_property_read_u32(np, "adi,oscillator-output1-driver-mode", - &hmc->oscout1_driver_mode); - - hmc->oscout1_driver_impedance = HMC7044_DRIVER_IMPEDANCE_DISABLE; - of_property_read_u32(np, "adi,oscillator-output1-driver-impedance", - &hmc->oscout1_driver_impedance); - hmc->sysref_timer_div = 256; of_property_read_u32(np, "adi,sysref-timer-divider", &hmc->sysref_timer_div); From 55e82166253f5ee4fcddd2e553cb3de2bc032195 Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 09:42:53 +0300 Subject: [PATCH 3/8] include: hmc7044: Remove unused defines Remove the adi,divider-ratio defines, as they are no longer used. Signed-off-by: George Mois --- include/dt-bindings/iio/frequency/hmc7044.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/dt-bindings/iio/frequency/hmc7044.h b/include/dt-bindings/iio/frequency/hmc7044.h index 3abacc8474bf9a..5739529e844457 100644 --- a/include/dt-bindings/iio/frequency/hmc7044.h +++ b/include/dt-bindings/iio/frequency/hmc7044.h @@ -19,14 +19,6 @@ #define HMC7044_PULSE_GEN_16_PULSE 5 #define HMC7044_PULSE_GEN_CONT_PULSE 7 -/* - * adi,divider-ratio - */ -#define HMC7044_DIVIDER_RATIO_1 0 -#define HMC7044_DIVIDER_RATIO_2 1 -#define HMC7044_DIVIDER_RATIO_4 2 -#define HMC7044_DIVIDER_RATIO_8 3 - /* * adi,driver-mode */ From 101b9508ea956e163b2d3f0036e313da22adebb6 Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 12:45:21 +0300 Subject: [PATCH 4/8] dt-bindings: iio: adc: AD9213 yaml documentation This adds device tree bindings for the AD9213 ADC. Signed-off-by: George Mois --- .../bindings/iio/adc/adi,ad9213.yaml | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/adi,ad9213.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad9213.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad9213.yaml new file mode 100644 index 00000000000000..4ea201c001977d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad9213.yaml @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2025 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad9213.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD9213 device driver + +maintainers: + - George Mois + +description: | + Bindings for the Analog Devices AD9213 ADC device. Datasheet can be found + here: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad9213.pdf + +properties: + compatible: + enum: + - adi,ad9213 + + reg: + maxItems: 1 + description: Chip select number on the SPI bus. + + spi-max-frequency: + const: 10000000 + description: SPI clock frequency in Hz. + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + jesd204-device: + description: Device is added to the jesd204-fsm framework + type: boolean + + '#jesd204-cells': + const: 2 + + jesd204-top-device: + const: 0 + $ref: /schemas/types.yaml#/definitions/uint32 + description: Indicates this is the top-level device in the JESD204 link. + + jesd204-link-ids: + const: 0 + $ref: /schemas/types.yaml#/definitions/uint32-array + + jesd204-inputs: + description: JESD204-fsm devices phandles and specifiers (used to build the link topology) + $ref: /schemas/types.yaml#/definitions/phandle + + adi,adc-frequency-hz: + description: ADC Sampling frequency of the device + minimum: 6000000000 + maximum: 10250000000 + + adi,octets-per-frame: + description: Number of octets per frame (F) + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,frames-per-multiframe: + description: Number of frames per multi-frame (K) + $ref: /schemas/types.yaml#/definitions/uint16 + + adi,converter-resolution: + description: Converter resolution (N) + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,bits-per-sample: + description: Number of bits per sample (N') + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,converters-per-device: + description: Number of converter per device (M) + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,control-bits-per-sample: + description: Number of control bits per conversion sample (CS) + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,lanes-per-device: + description: Number of lanes per link (L) + $ref: /schemas/types.yaml#/definitions/uint8 + + adi,subclass: + description: The JESD204B sublcass + $ref: /schemas/types.yaml#/definitions/uint8 + + io-backends: + description: + The AXI ADC IP block connected to the output lines of the + ADC. An example backend can be found at + http://analogdevicesinc.github.io/hdl/projects/pulsar_lvds/index.html. + maxItems: 1 + +required: + - compatible + - reg + - jesd204-device + - '#jesd-cells' + - jesd204-top-device + - jesd204-link-ids + - jesd204-inputs + - io-backends + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc_ad9213: ad9213@0 { + compatible = "adi,ad9213"; + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <10000000>; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-top-device = <0>; /* This is the TOP device */ + jesd204-link-ids = <0>; + jesd204-inputs = <&axi_ad9213_jesd_rx 0 0>; + + adi,adc-frequency-hz = /bits/ 64 <10000000000>; + adi,octets-per-frame = <2>; + adi,frames-per-multiframe = <32>; + adi,converter-resolution = <16>; + adi,bits-per-sample = <16>; + adi,converters-per-device = <1>; + adi,control-bits-per-sample = <0>; + adi,lanes-per-device = <16>; + adi,subclass = <1>; + + io-backends = <&iio_backend>; + }; + }; From 47a299dcfae55886b7e36b2a3d12e021c30ec114 Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 13:52:17 +0300 Subject: [PATCH 5/8] drivers: iio: adc: ad9213: Add support for AD9213 Add driver for AD9213. Signed-off-by: AndrDragomir Signed-off-by: George Mois --- drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 2 + drivers/iio/adc/ad9213.c | 762 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 776 insertions(+) create mode 100644 drivers/iio/adc/ad9213.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 69b96813d8c440..c1fc7d365a4da4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -561,6 +561,18 @@ config AD9208 AD9208, AD9689, AD9695, AD9697, AD9694, AD6688, AD6684 high speed ADCs. Provides direct access via sysfs. +config AD9213 + tristate "Analog Devices AD9213 ADC" + depends on SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices: + AD9213 JESD204B RF Analog-to-Digital Converter. + Provides direct access via sysfs. + + To compile this driver as a module, choose M here: the module will be + called ad9213. + config AD9361 bool "Analog Devices AD9361, AD9364 RF Agile Transceiver driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 293f74ec25c3da..1c97e411efb559 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -62,6 +62,8 @@ ad9208_drv-y := ad9208.o \ obj-$(CONFIG_AD9208) += ad9208_drv.o +obj-$(CONFIG_AD9213) += ad9213.o + ad9081_drv-y := ad9081.o \ ad9081/adi_ad9081_adc.o \ ad9081/adi_ad9081_device.o \ diff --git a/drivers/iio/adc/ad9213.c b/drivers/iio/adc/ad9213.c new file mode 100644 index 00000000000000..f84bc959546204 --- /dev/null +++ b/drivers/iio/adc/ad9213.c @@ -0,0 +1,762 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD9213 ADC driver + * + * Copyright 2021-2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JESD204_OF_PREFIX "adi," +#include +#include + +#define AD9213_REG_SPI_CONFIG_A 0x0000 +#define AD9213_SOFT_RESET_1 BIT(7) +#define AD9213_LSB_FIRST_1 BIT(6) +#define AD9213_ADDR_ASCENSION_1 BIT(5) +#define AD9213_ADDR_ASCENSION_0 BIT(2) +#define AD9213_LSB_FIRST_0 BIT(1) +#define AD9213_SOFT_RESET_0 BIT(0) + +#define AD9213_REG_GEN_CTRL 0x0026 +#define AD9213_CLK_SWITCH BIT(3) + +#define AD9213_REG_PLL_STATUS 0x0501 +#define AD9213_JTX_PLL_LOCKED BIT(7) + +#define AD9213_REG_JTX_LINK_CTRL2 0x0504 +#define AD9213_JTX_SYNC_PIN_MODE(x) ((x) & 0x3) +#define AD9213_JTX_SYNC_PIN_INV BIT(5) +#define AD9213_JTX_SYNC_PIN_TYPE BIT(4) +#define AD9213_JTX_8B10B_BYPASS BIT(2) +#define AD9213_JTX_10B_INV BIT(1) +#define AD9213_JTX_10B_MIRROR BIT(0) + +#define AD9213_REG_JTX_SYNC_CTRL 0x0508 +#define AD9213_SPI_CMOS_EN_RC BIT(5) + +#define AD9213_REG_JTX_LMFC_OFFSET 0x050A +#define AD9213_JTX_LMFC_OFFSET(x) ((x) & 0x1f) + +#define AD9213_REG_JTX_SCR_L_CFG 0x0520 +#define AD9213_JTX_SCR_CFG BIT(7) +#define AD9213_JTX_L_CFG(x) ((x) & 0x1f) + +#define AD9213_REG_JTX_F_CFG 0x0521 +#define AD9213_JTX_F_CFG(x) ((x) & 0xff) + +#define AD9213_REG_JTX_K_CFG 0x0522 +#define AD9213_JTX_K_CFG(x) ((x) & 0x1f) + +#define AD9213_REG_JTX_M_CFG 0x0523 +#define AD9213_JTX_M_CFG(x) ((x) & 0xff) + +#define AD9213_REG_JTX_CS_N_CFG 0x0524 +#define AD9213_JTX_CS_CFG(x) (((x) & 0x3) << 6) +#define AD9213_JTX_N_CFG(x) ((x) & 0x1f) + +#define AD9213_REG_JTX_SCV_NP_CFG 0x0525 +#define AD9213_JTX_SUBCLASSV_CFG(x) (((x) & 0x7) << 5) +#define AD9213_JTX_NP_CFG(x) ((x) & 0x1f) + +#define AD9213_REG_PLL_ENABLE_CTRL 0x0570 +#define AD9213_LOLSTICKYCLEAR_FORCE_LCPLL_RC BIT(4) +#define AD9213_LDSYNTH_FORCE_LCPLL_ADC BIT(2) +#define AD9213_PWRUP_LCPLL BIT(0) + +#define AD9213_REG_CHIP_DP_MODE 0x0606 +#define AD9213_CHIP_I_ONLY BIT(5) +#define AD9213_DDC_0_ONLY BIT(1) + +#define AD9213_REG_CHIP_DEC_RATIO 0x0607 +#define AD9213_CHIP_DEC_RATIO(x) ((x) & 0xf) + +#define AD9213_REG_OUT_RES 0x0626 +#define AD9213_DFORMAT_FBW_DITHER_EN BIT(4) +#define AD9213_DFORMAT_RES(x) ((x) & 0xf) + +#define AD9213_REG_DDC_CTRL 0x0630 +#define AD9213_DDC0_C2R_EN BIT(4) +#define AD9213_DDC0_IF_MODE(x) (((x) & 0x3) << 2) +#define AD9213_DDC0_GAIN BIT(1) + +#define AD9213_REG_DDC_DEC_CTRL 0x0631 +#define AD9213_DDC0_DEC_SEL(x) ((x) & 0xf) + +#define AD9213_REG_JTX_CLK 0x0681 +#define AD9213_JTX_CLK_EN BIT(4) + +struct ad9213 { + struct regmap *regmap; + struct spi_device *spi; + struct gpio_desc *reset_gpio; + struct clk *jesd_clk; + bool syncinb_cmos_en; + u32 lmfc_offset; + bool is_initialized; + int spi_device_id; + struct jesd204_dev *jdev; + struct jesd204_link jesd204_link; + /* protect against device accesses */ + struct mutex lock; + u64 adc_frequency_hz; + struct iio_backend *back; +}; + +static const struct regmap_config ad9213_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), + .max_register = 0x1636, +}; + +enum { + AD9213_JESD204_FSM_ERROR, + AD9213_JESD204_FSM_PAUSED, + AD9213_JESD204_FSM_STATE, + AD9213_JESD204_FSM_RESUME, + AD9213_JESD204_FSM_CTRL, +}; + +struct ad9213_jesd204_priv { + struct ad9213 *adc; +}; + +enum ad9213_device_id { + ID_AD9213, +}; + +static int ad9213_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int write_val, unsigned int *read_val) +{ + struct ad9213 *adc = iio_priv(indio_dev); + int ret; + + guard(mutex)(&adc->lock); + if (read_val) + ret = regmap_read(adc->regmap, reg, read_val); + else + ret = regmap_write(adc->regmap, reg, write_val); + + return ret; +} + +static ssize_t ad9213_phy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct ad9213 *adc = iio_priv(indio_dev); + bool enable; + int ret = 0; + + guard(mutex)(&adc->lock); + switch ((u32)this_attr->address & 0xFF) { + case AD9213_JESD204_FSM_RESUME: + if (!adc->jdev) { + ret = -EOPNOTSUPP; + break; + } + ret = jesd204_fsm_resume(adc->jdev, JESD204_LINKS_ALL); + break; + + case AD9213_JESD204_FSM_CTRL: + if (!adc->jdev) { + ret = -EOPNOTSUPP; + break; + } + ret = strtobool(buf, &enable); + if (ret) + break; + if (jesd204_dev_is_top(adc->jdev)) { + if (enable) { + jesd204_fsm_stop(adc->jdev, JESD204_LINKS_ALL); + jesd204_fsm_clear_errors(adc->jdev, + JESD204_LINKS_ALL); + ret = jesd204_fsm_start(adc->jdev, + JESD204_LINKS_ALL); + } else { + jesd204_fsm_stop(adc->jdev, JESD204_LINKS_ALL); + jesd204_fsm_clear_errors(adc->jdev, + JESD204_LINKS_ALL); + ret = 0; + } + } + break; + + default: + ret = -EINVAL; + break; + } + + return ret ? ret : len; +} + +static ssize_t ad9213_phy_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct ad9213 *adc = iio_priv(indio_dev); + int ret = 0; + int i, err, num_links; + bool paused; + struct jesd204_link *links[8]; + + guard(mutex)(&adc->lock); + + switch ((u32)this_attr->address & 0xFF) { + case AD9213_JESD204_FSM_ERROR: + if (!adc->jdev) { + ret = -EOPNOTSUPP; + break; + } + num_links = jesd204_get_active_links_num(adc->jdev); + if (num_links < 0) { + ret = num_links; + break; + } + + ret = jesd204_get_links_data(adc->jdev, links, num_links); + if (ret) + break; + err = 0; + for (i = 0; i < num_links; i++) { + if (links[i]->error) { + err = links[i]->error; + break; + } + } + ret = sprintf(buf, "%d\n", err); + break; + + case AD9213_JESD204_FSM_PAUSED: + if (!adc->jdev) { + ret = -EOPNOTSUPP; + break; + } + num_links = jesd204_get_active_links_num(adc->jdev); + if (num_links < 0) { + ret = num_links; + break; + } + ret = jesd204_get_links_data(adc->jdev, links, num_links); + if (ret) + break; + /* + * Take the slowest link; if there are N links and one is + * paused, all are paused. Not sure if this can happen yet, + * but best design it like this here. + */ + paused = false; + for (i = 0; i < num_links; i++) { + if (jesd204_link_get_paused(links[i])) { + paused = true; + break; + } + } + ret = sprintf(buf, "%d\n", paused); + break; + + case AD9213_JESD204_FSM_STATE: + if (!adc->jdev) + ret = -EOPNOTSUPP; + num_links = jesd204_get_active_links_num(adc->jdev); + if (num_links < 0) { + ret = num_links; + break; + } + ret = jesd204_get_links_data(adc->jdev, links, num_links); + if (ret) + break; + /* + * just get the first link state; we're assuming that all 3 + * are in sync and that AD9081_JESD204_FSM_PAUSED + * was called before + */ + ret = sprintf(buf, "%s\n", + jesd204_link_get_state_str(links[0])); + break; + + case AD9213_JESD204_FSM_CTRL: + if (!adc->jdev) { + ret = -EOPNOTSUPP; + break; + } + + ret = sprintf(buf, "%d\n", adc->is_initialized); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int ad9213_setup(struct ad9213 *adc) +{ + int ret; + + gpiod_set_value_cansleep(adc->reset_gpio, 0); + gpiod_set_value_cansleep(adc->reset_gpio, 1); + + ret = regmap_write(adc->regmap, AD9213_REG_SPI_CONFIG_A, + AD9213_ADDR_ASCENSION_1 | AD9213_ADDR_ASCENSION_0); + if (ret) + return ret; + mdelay(100); + + ret = regmap_write(adc->regmap, AD9213_REG_GEN_CTRL, AD9213_CLK_SWITCH); + if (ret) + return ret; + mdelay(100); + + if (adc->syncinb_cmos_en) { + ret = regmap_write(adc->regmap, AD9213_REG_JTX_LINK_CTRL2, 0x00); + if (ret) + return ret; + ret = regmap_write(adc->regmap, AD9213_REG_JTX_SYNC_CTRL, + AD9213_SPI_CMOS_EN_RC); + } else { + ret = regmap_write(adc->regmap, AD9213_REG_JTX_LINK_CTRL2, + AD9213_JTX_SYNC_PIN_TYPE); + } + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_SCR_L_CFG, + AD9213_JTX_SCR_CFG | + AD9213_JTX_L_CFG(adc->jesd204_link.num_lanes - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_F_CFG, + AD9213_JTX_F_CFG(adc->jesd204_link.octets_per_frame - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_K_CFG, + AD9213_JTX_K_CFG(adc->jesd204_link.frames_per_multiframe - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_M_CFG, + AD9213_JTX_M_CFG(adc->jesd204_link.num_converters - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_CS_N_CFG, + AD9213_JTX_CS_CFG(0x3) | + AD9213_JTX_N_CFG(adc->jesd204_link.converter_resolution - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_OUT_RES, + AD9213_DFORMAT_RES(16 - adc->jesd204_link.converter_resolution)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_SCV_NP_CFG, + AD9213_JTX_SUBCLASSV_CFG(adc->jesd204_link.subclass) | + AD9213_JTX_N_CFG(adc->jesd204_link.bits_per_sample - 1)); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_LMFC_OFFSET, + AD9213_JTX_LMFC_OFFSET(adc->lmfc_offset)); + if (ret) + return ret; + + /* full bandwidth mode */ + ret = regmap_write(adc->regmap, AD9213_REG_CHIP_DP_MODE, 0x00); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_CHIP_DEC_RATIO, 0x00); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_JTX_CLK, AD9213_JTX_CLK_EN); + if (ret) + return ret; + + ret = regmap_write(adc->regmap, AD9213_REG_PLL_ENABLE_CTRL, 0x00); + if (ret) + return ret; + + return regmap_write(adc->regmap, AD9213_REG_PLL_ENABLE_CTRL, + AD9213_PWRUP_LCPLL); +} + +static int ad9213_jesd204_link_init(struct jesd204_dev *jdev, + enum jesd204_state_op_reason reason, + struct jesd204_link *lnk) +{ + struct device *dev = jesd204_dev_to_device(jdev); + struct ad9213_jesd204_priv *priv = jesd204_dev_priv(jdev); + struct ad9213 *adc = priv->adc; + struct jesd204_link *link; + int ret; + + switch (reason) { + case JESD204_STATE_OP_REASON_INIT: + break; + default: + return JESD204_STATE_CHANGE_DONE; + } + + dev_dbg(dev, "%s:%d link_num %u reason %s\n", __func__, + __LINE__, lnk->link_id, jesd204_state_op_reason_str(reason)); + + ret = ad9213_setup(adc); + if (ret) + return ret; + + link = &adc->jesd204_link; + + jesd204_copy_link_params(lnk, link); + + lnk->sample_rate = adc->adc_frequency_hz; + lnk->jesd_encoder = JESD204_ENCODER_8B10B; + + return JESD204_STATE_CHANGE_DONE; +} + +static int ad9213_jesd204_clks_sync2(struct jesd204_dev *jdev, + enum jesd204_state_op_reason reason) +{ + struct device *dev = jesd204_dev_to_device(jdev); + + if (reason != JESD204_STATE_OP_REASON_INIT) + return JESD204_STATE_CHANGE_DONE; + + dev_dbg(dev, "%s:%d reason %s\n", __func__, __LINE__, + jesd204_state_op_reason_str(reason)); + + return JESD204_STATE_CHANGE_DONE; +} + +static int ad9213_jesd204_link_running(struct jesd204_dev *jdev, + enum jesd204_state_op_reason reason, + struct jesd204_link *lnk) +{ + struct device *dev = jesd204_dev_to_device(jdev); + struct ad9213_jesd204_priv *priv = jesd204_dev_priv(jdev); + struct ad9213 *adc = priv->adc; + unsigned int status; + int ret; + + dev_dbg(dev, "%s:%d link_num %u reason %s\n", __func__, __LINE__, + lnk->link_id, jesd204_state_op_reason_str(reason)); + + if (reason != JESD204_STATE_OP_REASON_INIT) { + adc->is_initialized = false; + return JESD204_STATE_CHANGE_DONE; + } + + ret = regmap_read(adc->regmap, AD9213_REG_PLL_STATUS, &status); + if (ret) + return ret; + dev_info(&adc->spi->dev, "AD9213 PLL %s\n", + status & AD9213_JTX_PLL_LOCKED ? "LOCKED" : "UNLOCKED"); + + adc->is_initialized = true; + + return JESD204_STATE_CHANGE_DONE; +} + +static const struct jesd204_dev_data jesd204_ad9213_init = { + .state_ops = { + [JESD204_OP_LINK_INIT] = { + .per_link = ad9213_jesd204_link_init, + }, + [JESD204_OP_CLK_SYNC_STAGE2] = { + .per_device = ad9213_jesd204_clks_sync2, + .mode = JESD204_STATE_OP_MODE_PER_DEVICE, + }, + [JESD204_OP_LINK_RUNNING] = { + .per_link = ad9213_jesd204_link_running, + }, + }, + + .max_num_links = 1, + .num_retries = 3, + .sizeof_priv = sizeof(struct ad9213_jesd204_priv), +}; + +static IIO_DEVICE_ATTR(jesd204_fsm_error, 0444, + ad9213_phy_show, + NULL, + AD9213_JESD204_FSM_ERROR); + +static IIO_DEVICE_ATTR(jesd204_fsm_paused, 0444, + ad9213_phy_show, + NULL, + AD9213_JESD204_FSM_PAUSED); + +static IIO_DEVICE_ATTR(jesd204_fsm_state, 0444, + ad9213_phy_show, + NULL, + AD9213_JESD204_FSM_STATE); + +static IIO_DEVICE_ATTR(jesd204_fsm_resume, 0200, + NULL, + ad9213_phy_store, + AD9213_JESD204_FSM_RESUME); + +static IIO_DEVICE_ATTR(jesd204_fsm_ctrl, 0644, + ad9213_phy_show, + ad9213_phy_store, + AD9213_JESD204_FSM_CTRL); + +static struct attribute *ad9213_phy_attributes[] = { + &iio_dev_attr_jesd204_fsm_error.dev_attr.attr, + &iio_dev_attr_jesd204_fsm_state.dev_attr.attr, + &iio_dev_attr_jesd204_fsm_paused.dev_attr.attr, + &iio_dev_attr_jesd204_fsm_resume.dev_attr.attr, + &iio_dev_attr_jesd204_fsm_ctrl.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad9213_phy_attribute_group = { + .attrs = ad9213_phy_attributes, +}; + +static int ad9213_phy_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad9213 *adc = iio_priv(indio_dev); + unsigned long long llval = adc->adc_frequency_hz; + + switch (m) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = lower_32_bits(llval); + *val2 = upper_32_bits(llval); + + return IIO_VAL_INT_64; + default: + return 0; + } + + return -EINVAL; +} + +static const struct iio_chan_spec ad9213_adc_chan[] = { + { + /* RX */ + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .shift = 4, + }, + }, +}; + +static int ad9213_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad9213 *adc = iio_priv(indio_dev); + int ret; + + if (test_bit(0, scan_mask)) + ret = iio_backend_chan_enable(adc->back, 0); + else + ret = iio_backend_chan_disable(adc->back, 0); + if (ret) + return ret; + + return 0; +} + +static const struct iio_info ad9213_iio_info = { + .read_raw = &ad9213_phy_read_raw, + .debugfs_reg_access = &ad9213_reg_access, + .attrs = &ad9213_phy_attribute_group, + .update_scan_mode = &ad9213_update_scan_mode, +}; + +static int ad9213_probe(struct spi_device *spi) +{ + struct device_node *np = spi->dev.of_node; + struct ad9213_jesd204_priv *priv; + struct iio_dev *indio_dev; + struct jesd204_dev *jdev; + unsigned int status; + struct ad9213 *adc; + int ret; + + int id = spi_get_device_id(spi)->driver_data; + + jdev = devm_jesd204_dev_register(&spi->dev, &jesd204_ad9213_init); + if (IS_ERR(jdev)) + return PTR_ERR(jdev); + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + + adc->regmap = devm_regmap_init_spi(spi, &ad9213_regmap_config); + if (IS_ERR(adc->regmap)) + return PTR_ERR(adc->regmap); + + adc->spi = spi; + adc->is_initialized = false; + adc->spi_device_id = id; + + adc->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(adc->reset_gpio)) + return PTR_ERR(adc->reset_gpio); + + if (jdev) { + adc->jdev = jdev; + priv = jesd204_dev_priv(jdev); + priv->adc = adc; + } + + adc->syncinb_cmos_en = + device_property_read_bool(&spi->dev, "adi,syncinb-cmos-enable"); + + adc->lmfc_offset = 0; + device_property_read_u32(&spi->dev, "adi,lmfc-offset", &adc->lmfc_offset); + + adc->adc_frequency_hz = 10000000000; + ret = device_property_read_u64(&spi->dev, "adi,adc-frequency-hz", + &adc->adc_frequency_hz); + if (ret) + return ret; + + JESD204_LNK_READ_NUM_LANES(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.num_lanes, 16); + + JESD204_LNK_READ_NUM_CONVERTERS(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.num_converters, 2); + + JESD204_LNK_READ_OCTETS_PER_FRAME(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.octets_per_frame, 2); + + JESD204_LNK_READ_SAMPLES_PER_CONVERTER_PER_FRAME(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.samples_per_conv_frame, + 16); + + JESD204_LNK_READ_HIGH_DENSITY(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.high_density, 0); + + JESD204_LNK_READ_CONVERTER_RESOLUTION(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.converter_resolution, + 16); + + JESD204_LNK_READ_BITS_PER_SAMPLE(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.bits_per_sample, + 16); + + JESD204_LNK_READ_CTRL_BITS_PER_SAMPLE(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.ctrl_bits_per_sample, 0); + + JESD204_LNK_READ_FRAMES_PER_MULTIFRAME(&spi->dev, np, + &adc->jesd204_link, + &adc->jesd204_link.frames_per_multiframe, 32); + + JESD204_LNK_READ_SUBCLASS(&spi->dev, np, + &adc->jesd204_link, &adc->jesd204_link.subclass, 0); + + ret = devm_mutex_init(&spi->dev, &adc->lock); + if (ret) + return ret; + + if (!adc->jdev) { + ret = ad9213_setup(adc); + if (ret) + return ret; + + regmap_read(adc->regmap, AD9213_REG_PLL_STATUS, &status); + dev_info(&adc->spi->dev, "AD9213 PLL %s\n", + status & AD9213_JTX_PLL_LOCKED ? "LOCKED" : "UNLOCKED"); + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi->dev.of_node->name; + indio_dev->info = &ad9213_iio_info; + + indio_dev->channels = ad9213_adc_chan; + indio_dev->num_channels = 1; + + adc->back = devm_iio_backend_get(&spi->dev, NULL); + if (IS_ERR(adc->back)) + return PTR_ERR(adc->back); + + ret = devm_iio_backend_request_buffer(&spi->dev, adc->back, indio_dev); + if (ret) + return ret; + + ret = devm_iio_backend_enable(&spi->dev, adc->back); + if (ret) + return ret; + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + + dev_info(&adc->spi->dev, "%s Probed\n", indio_dev->name); + + return jesd204_fsm_start(jdev, JESD204_LINKS_ALL); +} + +static const struct spi_device_id ad9213_id[] = { + { "ad9213", ID_AD9213 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad9213_id); + +static const struct of_device_id ad9213_of_match[] = { + { .compatible = "adi,ad9213" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad9213_of_match); + +static struct spi_driver ad9213_driver = { + .driver = { + .name = "ad9213", + .of_match_table = ad9213_of_match, + }, + .probe = ad9213_probe, + .id_table = ad9213_id, +}; +module_spi_driver(ad9213_driver); + +MODULE_AUTHOR("Dragos Bogdan "); +MODULE_DESCRIPTION("Analog Devices AD9213 ADC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BACKEND); From f255044950a9098ab66ed24a622393d6fa7bb2c3 Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 13:56:12 +0300 Subject: [PATCH 6/8] microblaze: adi_mb_defconfig: Add AD9213 Add AD9213 driver to adi_mb_defconfig. Signed-off-by: George Mois --- arch/microblaze/configs/adi_mb_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/microblaze/configs/adi_mb_defconfig b/arch/microblaze/configs/adi_mb_defconfig index a905aab44275e1..3989df8fa04fcd 100644 --- a/arch/microblaze/configs/adi_mb_defconfig +++ b/arch/microblaze/configs/adi_mb_defconfig @@ -170,6 +170,7 @@ CONFIG_ADM1177=y CONFIG_AD9081=y CONFIG_AD9083=y CONFIG_AD9208=y +CONFIG_AD9213=y CONFIG_AD9361=y CONFIG_AD9371=y CONFIG_ADRV9009=y From 2859ff8ee4b6445f321e787ea2d67e7c8027a7f8 Mon Sep 17 00:00:00 2001 From: George Mois Date: Tue, 27 May 2025 13:56:58 +0300 Subject: [PATCH 7/8] drivers: iio: Kconfig.adi: imply AD9213 Make sure the AD9213 ADC is built. Signed-off-by: George Mois --- drivers/iio/Kconfig.adi | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/Kconfig.adi b/drivers/iio/Kconfig.adi index a4730786c122e4..6a19ec8dde44dc 100644 --- a/drivers/iio/Kconfig.adi +++ b/drivers/iio/Kconfig.adi @@ -70,6 +70,7 @@ config IIO_ALL_ADI_DRIVERS imply AD9208 imply AD9081 imply AD9083 + imply AD9213 imply AD9361 imply AD9361_EXT_BAND_CONTROL imply AD9371 From 0161879c0e51fde2bd1e2e8d2fffb9463b6e853f Mon Sep 17 00:00:00 2001 From: AndrDragomir Date: Wed, 8 Feb 2023 14:36:04 +0200 Subject: [PATCH 8/8] arch: microblaze: boot: dts: Add devicetree for AD9213 Add devicetree for AD9213_EVB. Signed-off-by: AndrDragomir Signed-off-by: George Mois --- arch/microblaze/boot/dts/adi-ad9213-evb.dtsi | 171 ++++++++++++++++++ .../microblaze/boot/dts/vcu118_ad9213_evb.dts | 104 +++++++++++ 2 files changed, 275 insertions(+) create mode 100644 arch/microblaze/boot/dts/adi-ad9213-evb.dtsi create mode 100644 arch/microblaze/boot/dts/vcu118_ad9213_evb.dts diff --git a/arch/microblaze/boot/dts/adi-ad9213-evb.dtsi b/arch/microblaze/boot/dts/adi-ad9213-evb.dtsi new file mode 100644 index 00000000000000..e95c977ed42819 --- /dev/null +++ b/arch/microblaze/boot/dts/adi-ad9213-evb.dtsi @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +&amba_pl { + hmc7044_spi: spi@44a71000 { + #address-cells = <1>; + #size-cells = <0>; + bits-per-word = <8>; + compatible = "xlnx,axi-quad-spi-3.2", "xlnx,xps-spi-2.00.a"; + fifo-size = <16>; + interrupt-names = "ip2intc_irpt"; + interrupt-parent = <&axi_intc>; + interrupts = <7 0>; + num-cs = <0x2>; + reg = <0x44a71000 0x1000>; + xlnx,num-ss-bits = <0x2>; + xlnx,spi-mode = <0>; + + status = "okay"; + + hmc7044: hmc7044@0 { + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + compatible = "adi,hmc7044"; + spi-max-frequency = <10000000>; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-sysref-provider; + adi,jesd204-max-sysref-frequency-hz = <20000000>; + + adi,pll1-clkin-frequencies = <0 0 0 125000000>; + adi,vcxo-frequency = <125000000>; + + adi,pll1-loop-bandwidth-hz = <200>; + + adi,pll2-output-frequency = <2500000000>; + + adi,sysref-timer-divider = <1024>; + adi,pulse-generator-mode = <0>; + + adi,clkin3-buffer-mode = <0x15>; + adi,oscin-buffer-mode = <0x15>; + + adi,gpi-controls = <0x00 0x00 0x00 0x00>; + adi,gpo-controls = <0x1f 0x2b 0x33 0x37>; + + adi,oscillator-output-path-enable; + + adi,oscillator-output1-driver-enable; + + clock-output-names = + "hmc7044_out0", "hmc7044_out1", "hmc7044_out2", + "hmc7044_out3", "hmc7044_out4", "hmc7044_out5", + "hmc7044_out6", "hmc7044_out7", "hmc7044_out8", + "hmc7044_out9", "hmc7044_out10", "hmc7044_out11", + "hmc7044_out12", "hmc7044_out13", "hmc7044_oscout1"; + + hmc7044_c1: channel@1 { + reg = <1>; + adi,extended-name = "ADC_SYSREF"; + adi,divider = <128>; + adi,driver-mode = ; + adi,jesd204-sysref-chan; + }; + hmc7044_c3: channel@3 { + reg = <3>; + adi,extended-name = "FMC_SYSREF"; + adi,divider = <128>; + adi,driver-mode = ; + adi,jesd204-sysref-chan; + }; + hmc7044_c4: channel@4 { + reg = <4>; + adi,extended-name = "CLKOUT_MMCX"; + adi,divider = <8>; + adi,driver-mode = ; + }; + hmc7044_c5: channel@5 { + reg = <5>; + adi,extended-name = "FMC_CLK_COPY"; + adi,divider = <8>; + adi,driver-mode = ; + }; + hmc7044_c7: channel@7 { + reg = <7>; + adi,extended-name = "FMC_REFCLK"; + adi,divider = <4>; + adi,driver-mode = ; + }; + hmc7044_c9: channel@9 { + reg = <9>; + adi,extended-name = "FMC_REFCLK_COPY"; + adi,divider = <4>; + adi,driver-mode = ; + }; + hmc7044_c11: channel@11 { + reg = <11>; + adi,extended-name = "CORE_CLK"; + adi,divider = <8>; + adi,driver-mode = ; + }; + hmc7044_c14: channel@14 { + reg = <14>; + adi,extended-name = "ADF_CLK"; + adi,divider = <1>; + adi,driver-mode = ; + adi,driver-impedance-mode = ; + }; + }; + + adf4371: adf4371@1 { + compatible = "adi,adf4371"; + reg = <1>; + + #address-cells = <1>; + #clock-cells = <1>; + #size-cells = <0>; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-inputs = <&adc_ad9213 0 0>; + + spi-max-frequency = <10000000>; + adi,spi-3wire-enable; + adi,muxout-select = <1>; + clocks = <&hmc7044 14>; + clock-names = "clkin"; + clock-output-names = "pll0-clk-rf8", "pll0-clk-rfaux8", + "pll0-clk-rf16", "pll0-clk-rf32"; + + channel@2 { + reg = <2>; + adi,output-enable; + adi,power-up-frequency = /bits/ 64 <10000000000>; + }; + }; + }; + + axi_spi: spi@44a70000 { + status = "okay"; + + adc_ad9213: ad9213@0 { + compatible = "adi,ad9213"; + reg = <0>; + #address-cells = <1>; + #size-cells = <0>; + spi-max-frequency = <10000000>; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-top-device = <0>; /* This is the TOP device */ + jesd204-link-ids = <0>; + jesd204-inputs = <&axi_ad9213_jesd_rx 0 0>; + + adi,adc-frequency-hz = /bits/ 64 <10000000000>; + adi,octets-per-frame = <2>; + adi,frames-per-multiframe = <32>; + adi,converter-resolution = <16>; + adi,bits-per-sample = <16>; + adi,converters-per-device = <1>; + adi,control-bits-per-sample = <0>; + adi,lanes-per-device = <16>; + adi,subclass = <1>; + + io-backends = <&iio_backend>; + }; + }; +}; diff --git a/arch/microblaze/boot/dts/vcu118_ad9213_evb.dts b/arch/microblaze/boot/dts/vcu118_ad9213_evb.dts new file mode 100644 index 00000000000000..07c4ba5ddfcd9b --- /dev/null +++ b/arch/microblaze/boot/dts/vcu118_ad9213_evb.dts @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD9213 ANALOG-TO-DIGITAL CONVERTER + * + * hdl_project: + * board_revision: <> + * + * Copyright (C) 2023 Analog Devices Inc. + */ + +/dts-v1/; + +#include "vcu118.dtsi" +#include + +#define fmc_i2c fmcp_hspc_iic +#define fmc_spi axi_spi + +/ { + model = "Analog Devices AD9213-EVB @Xilinx/vcu118"; +}; + +&axi_intc { + xlnx,kind-of-intr = <0xffffc5f0>; +}; + +&amba_pl { + + rx_dma: rx-dmac@7c420000 { + #dma-cells = <1>; + compatible = "adi,axi-dmac-1.00.a"; + reg = <0x7c420000 0x1000>; + #clock-cells = <0>; + interrupt-parent = <&axi_intc>; + interrupts = <12 2>; + clocks = <&clk_bus_0>; + + adi,channels { + #size-cells = <0>; + #address-cells = <1>; + + dma-channel@0 { + reg = <0>; + adi,source-bus-width = <512>; + adi,source-bus-type = <1>; + adi,destination-bus-width = <512>; + adi,destination-bus-type = <0>; + }; + }; + }; + + axi_ad9213_jesd_rx: axi-jesd204-rx@44a90000 { + compatible = "adi,axi-jesd204-rx-1.0"; + interrupt-parent = <&axi_intc>; + interrupts = <13 2>; + reg = <0x44a90000 0x4000>; + clocks = <&clk_bus_0>, <&hmc7044 11>, <&axi_ad9213_adxcvr 1>, <&axi_ad9213_adxcvr 0>; + clock-names = "s_axi_aclk", "device_clk", "link_clk", "lane_clk"; + #clock-cells = <0>; + clock-output-names = "jesd_rx_lane_clk"; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-inputs = <&axi_ad9213_adxcvr 0 0>; + }; + + axi_ad9213_adxcvr: axi-adxcvr-rx@44a60000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,axi-adxcvr-1.0"; + reg = <0x44a60000 0x10000>; + clocks = <&hmc7044 7>; + clock-names = "conv"; + + adi,sys-clk-select = ; + adi,out-clk-select = ; /* not used */ + adi,use-lpm-enable; + + #clock-cells = <1>; + clock-output-names = "rx_gt_clk", "rx_out_clk"; + + jesd204-device; + #jesd204-cells = <2>; + jesd204-inputs = <&hmc7044 0 0>; + }; + + axi_clk: clock@0 { + compatible = "fixed-clock"; + clock-frequency = <100000000>; + clock-output-names = "axi-clk"; + #clock-cells = <0>; + }; + + iio_backend: backend@44a10000{ + reg = <0x44a10000 0x2000>; + compatible = "adi,axi-adc-10.0.a"; + dmas = <&rx_dma 0>; + dma-names = "rx"; + clocks = <&axi_clk>; + spibus-connected = <&adc_ad9213>; + }; +}; + +#include "adi-ad9213-evb.dtsi"