Skip to content

Commit

Permalink
iio: ad9361: Make sure to go to ALERT state when feedback clock changes
Browse files Browse the repository at this point in the history
When the feedback clock changes, either in phase or frequency, or any other
discontinuities appear in the feedback clock, the AD9361 must go to the
ALERT state.

Otherwise an incorrect internal state might be entered and data on the
transmit path might get corrupted. This issue is visible during interface
tuning where the TX tuning can fail under certain circumstances.

To avoid this issue make sure that the AD9361 is always in the ALERT state
when changing the feedback clock during interface tuning. This makes sure
that the interface tuning always succeeds if there is a valid delay setting
that results in a working interface.

Signed-off-by: Lars-Peter Clausen <[email protected]>
  • Loading branch information
larsclausen committed Jan 23, 2018
1 parent 78974c3 commit 8772a9a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 38 deletions.
27 changes: 18 additions & 9 deletions drivers/iio/adc/ad9361.c
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,12 @@ static int ad9361_get_rx_gain(struct ad9361_rf_phy *phy,
return rc;
}

u8 ad9361_ensm_get_state(struct ad9361_rf_phy *phy)
{
return ad9361_spi_readf(phy->spi, REG_STATE, ENSM_STATE(~0));
}
EXPORT_SYMBOL(ad9361_ensm_get_state);

void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, u8 ensm_state)
{
struct spi_device *spi = phy->spi;
Expand Down Expand Up @@ -1638,7 +1644,7 @@ void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, u8 ensm_state)
}
EXPORT_SYMBOL(ad9361_ensm_force_state);

void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
void ad9361_ensm_restore_state(struct ad9361_rf_phy *phy, u8 ensm_state)
{
struct spi_device *spi = phy->spi;
struct device *dev = &phy->spi->dev;
Expand All @@ -1653,8 +1659,7 @@ void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
val &= ~(FORCE_TX_ON | FORCE_RX_ON | FORCE_ALERT_STATE);
val |= TO_ALERT;

switch (phy->prev_ensm_state) {

switch (ensm_state) {
case ENSM_STATE_TX:
case ENSM_STATE_FDD:
val |= FORCE_TX_ON;
Expand All @@ -1667,19 +1672,19 @@ void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
break;
case ENSM_STATE_INVALID:
dev_dbg(dev, "No need to restore, ENSM state wasn't saved\n");
goto out;
return;
default:
dev_dbg(dev, "Could not restore to %d ENSM state\n",
phy->prev_ensm_state);
goto out;
ensm_state);
return;
}

ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE);

rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val);
if (rc) {
dev_err(dev, "Failed to write ENSM_CONFIG_1");
goto out;
return;
}

if (phy->ensm_pin_ctl_en) {
Expand All @@ -1688,8 +1693,12 @@ void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
if (rc)
dev_err(dev, "Failed to write ENSM_CONFIG_1");
}
out:
return;
}
EXPORT_SYMBOL(ad9361_ensm_restore_state);

void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy)
{
return ad9361_ensm_restore_state(phy, phy->prev_ensm_state);
}
EXPORT_SYMBOL(ad9361_ensm_restore_prev_state);

Expand Down
2 changes: 2 additions & 0 deletions drivers/iio/adc/ad9361.h
Original file line number Diff line number Diff line change
Expand Up @@ -3405,6 +3405,8 @@ int ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode);
int ad9361_find_opt(u8 *field, u32 size, u32 *ret_start);
int ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl);
void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, u8 ensm_state);
u8 ad9361_ensm_get_state(struct ad9361_rf_phy *phy);
void ad9361_ensm_restore_state(struct ad9361_rf_phy *phy, u8 ensm_state);
void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy);
int ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy,
unsigned long freq);
Expand Down
67 changes: 38 additions & 29 deletions drivers/iio/adc/ad9361_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,28 @@
#if IS_ENABLED(CONFIG_CF_AXI_ADC)
#include "cf_axi_adc.h"

static void ad9361_set_intf_delay(struct ad9361_rf_phy *phy, bool tx,
unsigned int clock_delay,
unsigned int data_delay, bool clock_changed)
{
if (clock_changed)
ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_spi_write(phy->spi,
REG_RX_CLOCK_DATA_DELAY + (tx ? 1 : 0),
RX_DATA_DELAY(data_delay) |
DATA_CLK_DELAY(clock_delay));
if (clock_changed)
ad9361_ensm_force_state(phy, ENSM_STATE_FDD);
}

ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
char *buf, unsigned buflen)
{
struct axiadc_converter *conv = spi_get_drvdata(phy->spi);
struct axiadc_state *st;
int ret, i, j, chan, num_chan, len = 0;
u8 field[16][16];
u8 ensm_state;
u8 rx;

if (!conv)
Expand All @@ -45,6 +60,7 @@ ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,

st = iio_priv(conv->indio_dev);

ensm_state = ad9361_ensm_get_state(phy);
rx = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);

num_chan = (conv->chip_info->num_channels > 4) ? 4 :
Expand All @@ -54,8 +70,7 @@ ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,

for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
DATA_CLK_DELAY(j) | RX_DATA_DELAY(i));
ad9361_set_intf_delay(phy, false, i, j, j == 0);
for (chan = 0; chan < num_chan; chan++)
axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
ADI_PN_ERR | ADI_PN_OOS);
Expand All @@ -69,11 +84,13 @@ ssize_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy,
ret = 1;
}

field[i][j] = ret;
field[j][i] = ret;
}
}

ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, rx);
ad9361_ensm_restore_state(phy, ensm_state);

ad9361_bist_prbs(phy, BIST_DISABLE);

Expand Down Expand Up @@ -424,7 +441,7 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
struct axiadc_state *st;
int ret, i, j, k, chan, t, num_chan, err = 0;
u32 s0, s1, c0, c1, tmp, saved = 0;
u8 field[2][16], loopback, bist;
u8 field[2][16], loopback, bist, ensm_state;
u32 saved_dsel[4], saved_chan_ctrl6[4], saved_chan_ctrl0[4];
u32 rates[3] = {25000000U, 40000000U, 61440000U};
unsigned hdl_dac_version;
Expand All @@ -440,13 +457,14 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,

if ((phy->pdata->dig_interface_tune_skipmode == 2) ||
(flags & RESTORE_DEFAULT)) {
/* skip completely and use defaults */
/* skip completely and use defaults */
ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
phy->pdata->port_ctrl.rx_clk_data_delay);

ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY,
phy->pdata->port_ctrl.tx_clk_data_delay);

ad9361_ensm_restore_prev_state(phy);
return 0;
}

Expand All @@ -460,17 +478,13 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
ad9361_midscale_iodelay(phy, 1);


if (!phy->pdata->fdd) {
if (!phy->pdata->fdd)
ad9361_set_ensm_mode(phy, true, false);
ad9361_ensm_force_state(phy, ENSM_STATE_FDD);
} else {
ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_ensm_restore_prev_state(phy);
}

num_chan = (conv->chip_info->num_channels > 4) ? 4 :
conv->chip_info->num_channels;

ensm_state = ad9361_ensm_get_state(phy);
loopback = phy->bist_loopback_mode;
bist = phy->bist_config;

Expand All @@ -480,17 +494,17 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
for (t = 0; t < 2; t++) {
memset(field, 0, 32);
for (k = 0; k < (max_freq ? ARRAY_SIZE(rates) : 1); k++) {
if (max_freq)
if (max_freq) {
ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_set_trx_clock_chain_freq(phy,
((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) || !phy->pdata->rx2tx2) ?
rates[k] : rates[k] / 2);
}

for (i = 0; i < 2; i++) {
for (j = 0; j < 16; j++) {
ad9361_spi_write(phy->spi,
REG_RX_CLOCK_DATA_DELAY + t,
RX_DATA_DELAY(i == 0 ? j : 0) |
DATA_CLK_DELAY(i ? j : 0));
ad9361_set_intf_delay(phy, t == 1, i ? j : 0,
i ? 0 : j, i || j == 0);
for (chan = 0; chan < num_chan; chan++)
axiadc_write(st, ADI_REG_CHAN_STATUS(chan),
ADI_PN_ERR | ADI_PN_OOS);
Expand Down Expand Up @@ -526,13 +540,9 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
}

if (c1 > c0)
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t,
DATA_CLK_DELAY(s1 + c1 / 2) |
RX_DATA_DELAY(0));
ad9361_set_intf_delay(phy, (bool)t, s1 + c1 / 2, 0, true);
else
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t,
DATA_CLK_DELAY(0) |
RX_DATA_DELAY(s0 + c0 / 2));
ad9361_set_intf_delay(phy, (bool)t, 0, s0 + c0 / 2, true);

if (t == 0) {
if (flags & DO_IDELAY)
Expand All @@ -552,12 +562,11 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
phy->pdata->port_ctrl.rx_clk_data_delay =
ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY);

if (!phy->pdata->fdd) {
if (!phy->pdata->fdd)
ad9361_set_ensm_mode(phy, phy->pdata->fdd,
phy->pdata->ensm_pin_ctrl);
ad9361_ensm_restore_prev_state(phy);
}

ad9361_ensm_restore_state(phy, ensm_state);
ad9361_tx_mute(phy, 0);

return 0;
Expand Down Expand Up @@ -613,6 +622,7 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
}

if (err == -EIO) {
ad9361_ensm_force_state(phy, ENSM_STATE_ALERT);
ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY,
phy->pdata->port_ctrl.rx_clk_data_delay);

Expand All @@ -628,10 +638,9 @@ int ad9361_dig_tune(struct ad9361_rf_phy *phy, unsigned long max_freq,
}


if (!phy->pdata->fdd) {
if (!phy->pdata->fdd)
ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl);
ad9361_ensm_restore_prev_state(phy);
}
ad9361_ensm_restore_state(phy, ensm_state);

axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN);
axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN);
Expand Down

0 comments on commit 8772a9a

Please sign in to comment.