From be4d17e4af58c8c5c02c8cdfad4c4ae86854d7f6 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Mon, 16 Jul 2018 10:40:35 +0200 Subject: [PATCH] drivers: iio: ad9361: Introduce TX LO power-down managed mode The TX Quadrature Calibration may fail in case the TX LO is in power-down mode. Error out if that's the case or specify a new device tree attribute adi,tx-lo-powerdown-managed-enable to automatically power-up the TX LO during this calibration. Once finished the previous mode is restored. Signed-off-by: Michael Hennerich --- drivers/iio/adc/ad9361.c | 237 +++++++++++++++++-------------- drivers/iio/adc/ad9361.h | 2 +- drivers/iio/adc/ad9361_private.h | 1 + 3 files changed, 131 insertions(+), 109 deletions(-) diff --git a/drivers/iio/adc/ad9361.c b/drivers/iio/adc/ad9361.c index b00cfa72b556d0..933deaa704ec37 100644 --- a/drivers/iio/adc/ad9361.c +++ b/drivers/iio/adc/ad9361.c @@ -1391,6 +1391,114 @@ int ad9361_tx_mute(struct ad9361_rf_phy *phy, u32 state) } EXPORT_SYMBOL(ad9361_tx_mute); +static int ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy, + bool tx, bool enable) +{ + struct ad9361_rf_phy_state *st = phy->state; + u32 val = enable ? ~0 : 0; + int ret; + + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + + bool mcs_rf_enable = ad9361_spi_readf(phy->spi, + REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + MCS_RF_ENABLE); + + dev_dbg(&phy->spi->dev, "%s : %s state %d", __func__, + tx ? "TX" : "RX", enable); + + if (tx) { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + TX_VCO_DIVIDER(~0), enable ? 7 : + st->cached_tx_rfpll_div); + + if (enable) + st->cached_synth_pd[0] |= TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN; + else + st->cached_synth_pd[0] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN); + + + ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + st->cached_synth_pd[0]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + TX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE, + TX_LO_GEN_POWER_MODE(val)); + } else { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + RX_VCO_DIVIDER(~0), enable ? 7 : + st->cached_rx_rfpll_div); + + if (enable) + st->cached_synth_pd[1] |= RX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN; + else + st->cached_synth_pd[1] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN); + + ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE, + st->cached_synth_pd[1]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + RX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE, + RX_LO_GEN_POWER_MODE(val)); + } + + return ret; +} + +static int ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, + enum synth_pd_ctrl rx, + enum synth_pd_ctrl tx) +{ + struct ad9361_rf_phy_state *st = phy->state; + + dev_dbg(&phy->spi->dev, "%s : RX(%d) TX(%d)", __func__, rx, tx); + + switch (rx) { + case LO_OFF: + st->cached_synth_pd[1] |= RX_LO_POWER_DOWN; + break; + case LO_ON: + st->cached_synth_pd[1] &= ~RX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + switch (tx) { + case LO_OFF: + st->cached_synth_pd[0] |= TX_LO_POWER_DOWN; + break; + case LO_ON: + st->cached_synth_pd[0] &= ~TX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + return ad9361_spi_writem(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + st->cached_synth_pd, 2); +} + static u32 ad9361_rfvco_tableindex(unsigned long freq) { if (freq < 50000000UL) @@ -2649,6 +2757,17 @@ static int ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, u8 __rx_phase = 0, reg_inv_bits, val, decim; bool phase_inversion_en; + if (st->cached_synth_pd[0] & TX_LO_POWER_DOWN) { + if (phy->pdata->lo_powerdown_managed_en) { + ad9361_spi_writef(spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + TX_LO_POWER_DOWN, 0); + } else { + dev_err(dev, + "%s : Tx QUAD Cal abort due to TX LO in powerdown\n", + __func__); + return -EFAULT; + } + } /* * Find NCO frequency that matches this equation: * BW / 4 = Rx NCO freq = Tx NCO freq: @@ -2722,7 +2841,7 @@ static int ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, /* Make sure the BW during calibration is wide enough */ ret = __ad9361_update_rf_bandwidth(phy, txnco_freq * 8, txnco_freq * 8); if (ret < 0) - return ret; + goto out_restore; } phase_inversion_en = phy->pdata->rx1rx2_phase_inversion_en || @@ -2787,6 +2906,12 @@ static int ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, st->current_tx_bw_Hz); } +out_restore: + /* Restore synthesizer powerdown configuration */ + if (phy->pdata->lo_powerdown_managed_en && + (st->cached_synth_pd[0] & TX_LO_POWER_DOWN)) + ad9361_synth_lo_powerdown(phy, LO_DONTCARE, LO_DONTCARE); + return ret; } @@ -2841,113 +2966,6 @@ static int ad9361_trx_vco_cal_control(struct ad9361_rf_phy *phy, BYPASS_LD_SYNTH, !enable); } -static int ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy, - bool tx, bool enable) -{ - struct ad9361_rf_phy_state *st = phy->state; - unsigned val = enable ? ~0 : 0; - int ret; - - /* REVIST: - * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict - */ - - bool mcs_rf_enable = ad9361_spi_readf(phy->spi, - REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, MCS_RF_ENABLE); - - dev_dbg(&phy->spi->dev, "%s : %s state %d",__func__, - tx ? "TX" : "RX", enable); - - if (tx) { - ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, - POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable); - - ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, - TX_VCO_DIVIDER(~0), enable ? 7 : - st->cached_tx_rfpll_div); - - if (enable) - st->cached_synth_pd[0] |= TX_SYNTH_VCO_ALC_POWER_DOWN | - TX_SYNTH_PTAT_POWER_DOWN | - TX_SYNTH_VCO_POWER_DOWN; - else - st->cached_synth_pd[0] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | - TX_SYNTH_PTAT_POWER_DOWN | - TX_SYNTH_VCO_POWER_DOWN); - - - ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, - st->cached_synth_pd[0]); - - ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, - TX_EXT_VCO_BUFFER_POWER_DOWN, !enable); - - ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE, - TX_LO_GEN_POWER_MODE(val)); - } else { - ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, - POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable); - - ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, - RX_VCO_DIVIDER(~0), enable ? 7 : - st->cached_rx_rfpll_div); - - if (enable) - st->cached_synth_pd[1] |= RX_SYNTH_VCO_ALC_POWER_DOWN | - RX_SYNTH_PTAT_POWER_DOWN | - RX_SYNTH_VCO_POWER_DOWN; - else - st->cached_synth_pd[1] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | - RX_SYNTH_PTAT_POWER_DOWN | - RX_SYNTH_VCO_POWER_DOWN); - - ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE, - st->cached_synth_pd[1]); - - ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, - RX_EXT_VCO_BUFFER_POWER_DOWN, !enable); - - ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE, - RX_LO_GEN_POWER_MODE(val)); - } - - return ret; -} - -static int ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, - enum synth_pd_ctrl rx, - enum synth_pd_ctrl tx) -{ - struct ad9361_rf_phy_state *st = phy->state; - - dev_dbg(&phy->spi->dev, "%s : RX(%d) TX(%d)",__func__, rx, tx); - - switch (rx) { - case LO_OFF: - st->cached_synth_pd[1] |= RX_LO_POWER_DOWN; - break; - case LO_ON: - st->cached_synth_pd[1] &= ~RX_LO_POWER_DOWN; - break; - case LO_DONTCARE: - break; - } - - switch (tx) { - case LO_OFF: - st->cached_synth_pd[0] |= TX_LO_POWER_DOWN; - break; - case LO_ON: - st->cached_synth_pd[0] &= ~TX_LO_POWER_DOWN; - break; - case LO_DONTCARE: - break; - } - - return ad9361_spi_writem(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, - st->cached_synth_pd, 2); -} - /* REFERENCE CLOCK DELAY UNIT COUNTER REGISTER */ static int ad9361_set_ref_clk_cycles(struct ad9361_rf_phy *phy, unsigned long ref_clk_hz) @@ -8476,6 +8494,9 @@ static struct ad9361_phy_platform_data ad9361_of_get_u32(iodev, np, "adi,trx-synthesizer-target-fref-overwrite-hz", MAX_SYNTH_FREF, &pdata->trx_synth_max_fref); + ad9361_of_get_bool(iodev, np, "adi,tx-lo-powerdown-managed-enable", + &pdata->lo_powerdown_managed_en); + tmpl = 2400000000ULL; of_property_read_u64(np, "adi,rx-synthesizer-frequency-hz", &tmpl); pdata->rx_synth_freq = tmpl; diff --git a/drivers/iio/adc/ad9361.h b/drivers/iio/adc/ad9361.h index d08a1b49e7e96a..7d5bffb093bd13 100644 --- a/drivers/iio/adc/ad9361.h +++ b/drivers/iio/adc/ad9361.h @@ -138,7 +138,7 @@ struct ad9361_rf_phy { struct refclk_scale clk_priv[NUM_AD9361_CLKS]; struct clk_onecell_data clk_data; struct ad9361_phy_platform_data *pdata; - struct ad9361_debugfs_entry debugfs_entry[180]; + struct ad9361_debugfs_entry debugfs_entry[181]; struct bin_attribute bin; struct bin_attribute bin_gt; struct iio_dev *indio_dev; diff --git a/drivers/iio/adc/ad9361_private.h b/drivers/iio/adc/ad9361_private.h index 5ba6bcb652c35b..2b74a55fcb7787 100644 --- a/drivers/iio/adc/ad9361_private.h +++ b/drivers/iio/adc/ad9361_private.h @@ -327,6 +327,7 @@ struct ad9361_phy_platform_data { bool rx1rx2_phase_inversion_en; bool qec_tracking_slow_mode_en; bool dig_interface_tune_fir_disable; + bool lo_powerdown_managed_en; u8 dc_offset_update_events; u8 dc_offset_attenuation_high; u8 dc_offset_attenuation_low;