From fefb7c452cc2055d236bc1bd24ab6817a96743fb Mon Sep 17 00:00:00 2001 From: Georgian Raul Date: Fri, 26 Jun 2026 13:55:22 +0300 Subject: [PATCH 1/4] iio: adc: adrv902x: Drop OFF from obs rx_port enum, return select index The dual-channel 4-pin ORx fix changes rf_port_select to a SELECT-only attribute with two items per pair (lower/upper ORx); the "OFF" entry no longer applies. Remove "OFF" from adrv9025_obs1_rx_port, adrv9025_obs2_rx_port and ad9371_obs_rx_port_lut, and have adrv9025_get_obs_rx_path parse the enable bits into the 2-item select index (upper bit => 1, else 0) so the readback always lands on a valid enum item. Signed-off-by: Georgian Raul --- drivers/iio/adc/adrv902x/adrv9025.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/adrv902x/adrv9025.c b/drivers/iio/adc/adrv902x/adrv9025.c index 1236bf4b7f4423..45dd14e32016df 100644 --- a/drivers/iio/adc/adrv902x/adrv9025.c +++ b/drivers/iio/adc/adrv902x/adrv9025.c @@ -1081,15 +1081,15 @@ static ssize_t adrv9025_phy_tx_write(struct iio_dev *indio_dev, } static const char * const adrv9025_obs1_rx_port[] = { - "OFF", "ORX1_ON_ORX2_OFF", "ORX1_OFF_ORX2_ON", + "ORX1_ON_ORX2_OFF", "ORX1_OFF_ORX2_ON", }; static const char * const adrv9025_obs2_rx_port[] = { - "OFF", "ORX3_ON_ORX4_OFF", "ORX3_OFF_ORX4_ON", + "ORX3_ON_ORX4_OFF", "ORX3_OFF_ORX4_ON", }; static const u8 ad9371_obs_rx_port_lut[] = { - 0x00, BIT(4), BIT(5) + BIT(4), BIT(5) }; static int adrv9025_set_obs_rx_path(struct iio_dev *indio_dev, @@ -1127,6 +1127,7 @@ static int adrv9025_get_obs_rx_path(struct iio_dev *indio_dev, struct adrv9025_rf_phy *phy = iio_priv(indio_dev); u32 rxchan = 0, txchan = 0; int shift_right = CHAN_OBS_RX1; + int pair; int ret; ret = adi_adrv9025_RxTxEnableGet(phy->madDevice, &rxchan, @@ -1137,7 +1138,16 @@ static int adrv9025_get_obs_rx_path(struct iio_dev *indio_dev, if (chan->channel > CHAN_OBS_RX1) shift_right = CHAN_OBS_RX3; - return rxchan >> shift_right & 0x3; + /* + * Each ORx pair occupies two adjacent enable bits (lower/upper). The + * rf_port_select enum is now SELECT-only with two items: index 0 = + * lower ORx, index 1 = upper ORx. Map the upper enable bit to index 1 + * and everything else (lower-on or disabled) to index 0 so the read + * always lands on a valid enum item. + */ + pair = rxchan >> shift_right & 0x3; + + return (pair & 0x2) ? 1 : 0; } static const struct iio_enum adrv9025_rf_obs1_rx_port_available = { From 710f93875b62c98f966d3814377b1f2e68e77137 Mon Sep 17 00:00:00 2001 From: Georgian Raul Date: Fri, 26 Jun 2026 13:55:22 +0300 Subject: [PATCH 2/4] iio: adc: adrv902x: Add dual-channel 4 pin mode crossbar and select fix Implement dual-channel 4-pin ORx mode (orxEnableMode=4). The ORx side enable and channel select are driven by the ORX_CTRL pins instead of the 0x106 SPI enable: A/C enable side-A/side-B, B/D select the ORx of each pair. Acquire the four optional orx-ctrl gpios at probe and add them to struct adrv9025_rf_phy. adrv9025_orx_dual_4pin() gates the new path on the gpios being wired and the cached init profile reporting orxEnableMode=4. In this mode rf_port_select becomes select-only (B/D pins) and _en drives the side enable (A/C pins) in read_raw/write_raw. The on-chip stream collapses the framer-1 ADC sample crossbar on every ORx enable edge, so adrv9025_orx_xbar_reassert() re-splits conv0/1 (side-A I/Q) and conv2/3 (side-B I/Q) after each enable/select change. Boards without the ORX_CTRL pins keep the legacy SPI select+enable path. Signed-off-by: Georgian Raul --- drivers/iio/adc/adrv902x/adrv9025.c | 130 +++++++++++++++++++++++++++- drivers/iio/adc/adrv902x/adrv9025.h | 9 ++ 2 files changed, 138 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/adrv902x/adrv9025.c b/drivers/iio/adc/adrv902x/adrv9025.c index 45dd14e32016df..4c6fa66c790e5f 100644 --- a/drivers/iio/adc/adrv902x/adrv9025.c +++ b/drivers/iio/adc/adrv902x/adrv9025.c @@ -1092,6 +1092,58 @@ static const u8 ad9371_obs_rx_port_lut[] = { BIT(4), BIT(5) }; +/* + * True when the device is configured for dual-channel 4-pin ORx mode + * (orxEnableMode=4) AND the ORX_CTRL pins are wired in the device tree. In + * this mode ORx enable/select is driven by the ORX_CTRL pins (A/C = side-A/ + * side-B enable, B/D = side-A/side-B channel select), not the 0x106 SPI + * enable. The enable mode comes from the init profile cached at probe. + */ +static bool adrv9025_orx_dual_4pin(struct adrv9025_rf_phy *phy) +{ + return phy->orx_ctrl_a_gpio && phy->orx_ctrl_c_gpio && + phy->adrv9025PostMcsInitInst.radioCtrlInit.radioCtrlModeCfg + .orxRadioCtrlModeCfg.orxEnableMode == + ADI_ADRV9025_ORX_EN_DUAL_CH_4PIN_MODE; +} + +/* + * Re-assert the framer-1 ADC sample crossbar to split routing. The on-chip + * stream processor collapses it on every ORx enable edge, so this must run + * AFTER the enable change. conv0/1 = side-A I/Q, conv2/3 = side-B I/Q. + */ +static int adrv9025_orx_xbar_reassert(struct adrv9025_rf_phy *phy) +{ + int ret; + + ret = adi_adrv9025_SpiFieldWrite(phy->madDevice, 0x7000, + ADI_ADRV9025_ADC_ORX1_I, 0x7F, 0); + if (ret) + return ret; + + ret = adi_adrv9025_SpiFieldWrite(phy->madDevice, 0x7001, + ADI_ADRV9025_ADC_ORX1_Q, 0x7F, 0); + if (ret) + return ret; + + ret = adi_adrv9025_SpiFieldWrite(phy->madDevice, 0x7002, + ADI_ADRV9025_ADC_ORX2_I, 0x7F, 0); + if (ret) + return ret; + + ret = adi_adrv9025_SpiFieldWrite(phy->madDevice, 0x7003, + ADI_ADRV9025_ADC_ORX2_Q, 0x7F, 0); + return ret; +} + +/* + * rf_port_select for the obs channels. In dual-channel 4-pin mode this is + * SELECT-ONLY: it drives the side's channel-select pin (B for side-A, D for + * side-B) to pick which ORx of the pair is observed. It does NOT enable the + * side - enable is owned by the _en attribute (write_raw). mode is the enum + * index: 0 = first ORx of the pair (sel low), 1 = second ORx (sel high). + * On boards without the ORX_CTRL pins it falls back to legacy SPI selection. + */ static int adrv9025_set_obs_rx_path(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, u32 mode) { @@ -1101,6 +1153,24 @@ static int adrv9025_set_obs_rx_path(struct iio_dev *indio_dev, u32 val = 0; int ret; + if (adrv9025_orx_dual_4pin(phy)) { + struct gpio_desc *sel_gpio = (chan->channel > CHAN_OBS_RX1) ? + phy->orx_ctrl_d_gpio : + phy->orx_ctrl_b_gpio; + + /* select line: mode 1 = second ORx of the pair => assert */ + if (sel_gpio) + gpiod_set_value_cansleep(sel_gpio, mode ? 1 : 0); + + /* selecting changes routing; re-assert the split crossbar */ + ret = adrv9025_orx_xbar_reassert(phy); + if (ret) + return adrv9025_dev_err(phy); + + return 0; + } + + /* Legacy SPI-mode select+enable (boards without ORX_CTRL pins). */ ret = adi_adrv9025_RxTxEnableGet(phy->madDevice, &rxchan, &txchan); if (ret) @@ -1130,6 +1200,19 @@ static int adrv9025_get_obs_rx_path(struct iio_dev *indio_dev, int pair; int ret; + /* + * In dual-4-pin mode selection is held by the channel-select pin, so + * read it back directly. The output gpio returns the last value set. + * 0 = first ORx of the pair, 1 = second ORx (matches the 2-item enum). + */ + if (adrv9025_orx_dual_4pin(phy)) { + struct gpio_desc *sel_gpio = (chan->channel > CHAN_OBS_RX1) ? + phy->orx_ctrl_d_gpio : + phy->orx_ctrl_b_gpio; + + return (sel_gpio && gpiod_get_value_cansleep(sel_gpio)) ? 1 : 0; + } + ret = adi_adrv9025_RxTxEnableGet(phy->madDevice, &rxchan, &txchan); if (ret) @@ -1277,7 +1360,17 @@ static int adrv9025_phy_read_raw(struct iio_dev *indio_dev, if (chan->output) *val = !!(txchan & (ADI_ADRV9025_TX1 << chan->channel)); else - if (chan->channel >= CHAN_OBS_RX1) { + if (chan->channel >= CHAN_OBS_RX1 && + adrv9025_orx_dual_4pin(phy)) { + /* enable is held by the side ENABLE pin (A/C) */ + struct gpio_desc *en_gpio = + (chan->channel > CHAN_OBS_RX1) ? + phy->orx_ctrl_c_gpio : + phy->orx_ctrl_a_gpio; + + *val = en_gpio ? + !!gpiod_get_value_cansleep(en_gpio) : 0; + } else if (chan->channel >= CHAN_OBS_RX1) { chan_no = chan->channel; if (chan_no == CHAN_OBS_RX2) chan_no += 1; @@ -1412,6 +1505,28 @@ static int adrv9025_phy_write_raw(struct iio_dev *indio_dev, txchan |= (ADI_ADRV9025_TX1 << chan->channel); else txchan &= ~(ADI_ADRV9025_TX1 << chan->channel); + } else if (chan->channel >= CHAN_OBS_RX1 && + adrv9025_orx_dual_4pin(phy)) { + /* + * Dual-channel 4-pin mode: the ORx side ENABLE is owned + * by the ORX_CTRL pin (A for side-A/obs1, C for side-B/ + * obs2), not the 0x106 SPI enable. _en drives the pin; + * which ORx of the pair is picked by rf_port_select (the + * select pin). Re-assert the split crossbar after the + * enable edge (the stream collapses it on that edge). + */ + struct gpio_desc *en_gpio = + (chan->channel > CHAN_OBS_RX1) ? + phy->orx_ctrl_c_gpio : phy->orx_ctrl_a_gpio; + + gpiod_set_value_cansleep(en_gpio, val ? 1 : 0); + + if (val) { + ret = adrv9025_orx_xbar_reassert(phy); + if (ret) + ret = adrv9025_dev_err(phy); + } + break; } else { chan_no = chan->channel; if (chan_no == CHAN_OBS_RX2) @@ -3654,6 +3769,19 @@ static int adrv9025_probe(struct spi_device *spi) phy->linux_hal.reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); + /* + * Optional ORx_CTRL pins for dual-channel 4-pin mode (orxEnableMode=4). + * Absent on boards/profiles using SPI or single-channel ORx modes. + */ + phy->orx_ctrl_a_gpio = + devm_gpiod_get_optional(&spi->dev, "orx-ctrl-a", GPIOD_OUT_LOW); + phy->orx_ctrl_b_gpio = + devm_gpiod_get_optional(&spi->dev, "orx-ctrl-b", GPIOD_OUT_LOW); + phy->orx_ctrl_c_gpio = + devm_gpiod_get_optional(&spi->dev, "orx-ctrl-c", GPIOD_OUT_LOW); + phy->orx_ctrl_d_gpio = + devm_gpiod_get_optional(&spi->dev, "orx-ctrl-d", GPIOD_OUT_LOW); + ret = clk_prepare_enable(phy->dev_clk); if (ret) return ret; diff --git a/drivers/iio/adc/adrv902x/adrv9025.h b/drivers/iio/adc/adrv902x/adrv9025.h index 7827afcdbd3997..f70420017f1915 100644 --- a/drivers/iio/adc/adrv902x/adrv9025.h +++ b/drivers/iio/adc/adrv902x/adrv9025.h @@ -166,6 +166,15 @@ struct adrv9025_rf_phy { struct gpio_desc *sysref_req_gpio; + /* + * ORx_CTRL pins for dual-channel 4-pin mode (orxEnableMode=4): + * A/C = side-A/side-B enable, B/D = side-A/side-B channel select. + */ + struct gpio_desc *orx_ctrl_a_gpio; + struct gpio_desc *orx_ctrl_b_gpio; + struct gpio_desc *orx_ctrl_c_gpio; + struct gpio_desc *orx_ctrl_d_gpio; + u8 device_id; u32 adrv9025_debugfs_entry_index; From 56d4947888b4c872444f09ab1fdd3990840b5444 Mon Sep 17 00:00:00 2001 From: Georgian Raul Date: Fri, 26 Jun 2026 13:58:45 +0300 Subject: [PATCH 3/4] arm64: dts: zynqmp-zcu102: Add GPIOs used by dual-channel 4 pin mode The dual -channel 4 pin mode uses 4 GPIO pins to control the enable and the selection of the ORX channels Signed-off-by: Georgian Raul --- .../dts/xilinx/zynqmp-zcu102-rev10-adrv9025-nls.dts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-rev10-adrv9025-nls.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-rev10-adrv9025-nls.dts index d378583afc6611..dffa29f27c37ae 100644 --- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-rev10-adrv9025-nls.dts +++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu102-rev10-adrv9025-nls.dts @@ -17,6 +17,16 @@ adi,device-profile-name = "ActiveUseCase_NLS.profile"; + /* + * ORx_CTRL pins for dual-channel 4-pin mode (orxEnableMode=4). + * HDL index + 78: A=54->132, B=53->131, C=52->130, D=51->129. + * A/C = side-A/side-B enable, B/D = side-A/side-B channel select. + */ + orx-ctrl-a-gpios = <&gpio 132 0>; + orx-ctrl-b-gpios = <&gpio 131 0>; + orx-ctrl-c-gpios = <&gpio 130 0>; + orx-ctrl-d-gpios = <&gpio 129 0>; + jesd204-device; #jesd204-cells = <2>; jesd204-top-device = <0>; /* This is the TOP device */ From 6eeb4c9a5d814d11c9e8d7d00433ac7f502deb61 Mon Sep 17 00:00:00 2001 From: Georgian Raul Date: Fri, 26 Jun 2026 14:00:37 +0300 Subject: [PATCH 4/4] firmware: Modify profile to support dual-channel 4 pin mode Nodify the profile to use dual-channel 4 pin mode, instead of SPI mode by modifying the number of pins used from 0 to 4. Signed-off-by: Georgian Raul --- firmware/ActiveUtilInit.profile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/ActiveUtilInit.profile b/firmware/ActiveUtilInit.profile index 88f52c639ecd5e..1036103955cbda 100644 --- a/firmware/ActiveUtilInit.profile +++ b/firmware/ActiveUtilInit.profile @@ -10,7 +10,7 @@ "rxChannelMask": 255 }, "orxRadioCtrlModeCfg": { - "orxEnableMode": 0, + "orxEnableMode": 4, "orxPinSelectSettlingDelay_armClkCycles": 0, "singleChannel1PinModeOrxSel": 0, "singleChannel2PinModeLowOrxSel": 0,