Skip to content

Commit

Permalink
drivers: iio: ad9361: Introduce TX LO power-down managed mode
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
mhennerich authored and commodo committed Jul 16, 2018
1 parent f307784 commit be4d17e
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 109 deletions.
237 changes: 129 additions & 108 deletions drivers/iio/adc/ad9361.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 ||
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion drivers/iio/adc/ad9361.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/iio/adc/ad9361_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit be4d17e

Please sign in to comment.