/***************************************************************************//** * @brief axiadc_set_pnsel *******************************************************************************/ int axiadc_set_pnsel(struct axiadc_state *st, int channel, enum adc_pn_sel sel) { unsigned reg; uint32_t version = axiadc_read(st, 0x4000); if (PCORE_VERSION_MAJOR(version) > 7) { reg = axiadc_read(st, ADI_REG_CHAN_CNTRL_3(channel)); reg &= ~ADI_ADC_PN_SEL(~0); reg |= ADI_ADC_PN_SEL(sel); axiadc_write(st, ADI_REG_CHAN_CNTRL_3(channel), reg); } else { reg = axiadc_read(st, ADI_REG_CHAN_CNTRL(channel)); if (sel == ADC_PN_CUSTOM) { reg |= ADI_PN_SEL; } else if (sel == ADC_PN9) { reg &= ~ADI_PN23_TYPE; reg &= ~ADI_PN_SEL; } else { reg |= ADI_PN23_TYPE; reg &= ~ADI_PN_SEL; } axiadc_write(st, ADI_REG_CHAN_CNTRL(channel), reg); } return 0; }
/***************************************************************************//** * @brief dds_set_phase *******************************************************************************/ void dds_set_scale(uint32_t chan, double scale) { uint32_t scale_reg; uint32_t sign_part; uint32_t int_part; uint32_t fract_part; if (PCORE_VERSION_MAJOR(dds_st.pcore_version) > 6) { if(scale >= 1.0) { sign_part = 0; int_part = 1; fract_part = 0; dds_st.cached_scale[chan] = 1.0; goto set_scale_reg; } if(scale <= -1.0) { sign_part = 1; int_part = 1; fract_part = 0; dds_st.cached_scale[chan] = -1.0; goto set_scale_reg; } if(scale < 0) { sign_part = 1; int_part = 0; dds_st.cached_scale[chan] = scale; scale *= -1; goto set_scale_reg; } sign_part = 0; int_part = 0; dds_st.cached_scale[chan] = scale; fract_part = (uint32_t)(scale * 0x4000); set_scale_reg: scale_reg = (sign_part << 15) | (int_part << 14) | fract_part; } else { if(scale >= 1.0) { scale_reg = 0; scale = 1.0; } if(scale <= 0.0) { scale_reg = 0; scale = 0.0; } dds_st.cached_scale[chan] = scale; fract_part = (uint32_t)(scale * 1000000); scale_reg = 500000 / fract_part; } dac_stop(); dac_write(ADI_REG_CHAN_CNTRL_1_IIOCHAN(chan), ADI_DDS_SCALE(scale_reg)); dac_start_sync(0); }
/***************************************************************************//** * @brief dac_datasel *******************************************************************************/ int dac_datasel(int32_t chan, enum dds_data_select sel) { if (PCORE_VERSION_MAJOR(dds_st.pcore_version) > 7) { if (chan < 0) { /* ALL */ int i; for (i = 0; i < dds_st.num_dds_channels; i++) { dac_write(ADI_REG_CHAN_CNTRL_7(i), sel); } } else { dac_write(ADI_REG_CHAN_CNTRL_7(chan), sel); } } else { uint32_t reg; switch(sel) { case DATA_SEL_DDS: case DATA_SEL_SED: case DATA_SEL_DMA: dac_read(ADI_REG_CNTRL_2, ®); reg &= ~ADI_DATA_SEL(~0); reg |= ADI_DATA_SEL(sel); dac_write(ADI_REG_CNTRL_2, reg); break; default: return -EINVAL; } } return 0; }
/***************************************************************************//** * @brief dac_stop *******************************************************************************/ void dac_stop(void) { if (PCORE_VERSION_MAJOR(dds_st.pcore_version) < 8) { dac_write(ADI_REG_CNTRL_1, 0); } }
/***************************************************************************//** * @brief dds_set_calib_scale_phase *******************************************************************************/ int32_t dds_set_calib_scale_phase(struct ad9361_rf_phy *phy, uint32_t phase, uint32_t chan, int32_t val, int32_t val2) { uint32_t reg; uint32_t i; if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { return -1; } i = dds_to_signed_mag_fmt(val, val2); dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), ®); if (!((chan + phase) % 2)) { reg &= ~DAC_IQCOR_COEFF_1(~0); reg |= DAC_IQCOR_COEFF_1(i); } else { reg &= ~DAC_IQCOR_COEFF_2(~0); reg |= DAC_IQCOR_COEFF_2(i); } dac_write(phy, DAC_REG_CHAN_CNTRL_8(chan), reg); dac_write(phy, DAC_REG_CHAN_CNTRL_6(chan), DAC_IQCOR_ENB); return 0; }
/***************************************************************************//** * @brief dds_get_calib_scale_phase *******************************************************************************/ int32_t dds_get_calib_scale_phase(struct ad9361_rf_phy *phy, uint32_t phase, uint32_t chan, int32_t *val, int32_t *val2) { uint32_t reg; if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { return -1; } dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), ®); /* format is 1.1.14 (sign, integer and fractional bits) */ if (!((phase + chan) % 2)) { reg = DAC_TO_IQCOR_COEFF_1(reg); } else { reg = DAC_TO_IQCOR_COEFF_2(reg); } dds_from_signed_mag_fmt(reg, val, val2); return 0; }
/***************************************************************************//** * @brief dac_stop *******************************************************************************/ void dac_stop(struct ad9361_rf_phy *phy) { if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { dac_write(phy, DAC_REG_CNTRL_1, 0); } }
/***************************************************************************//** * @brief dac_datasel *******************************************************************************/ int dac_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select sel) { if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) > 7) { if (chan < 0) { /* ALL */ uint32_t i; for (i = 0; i < dds_st[phy->id_no].num_dds_channels; i++) { dac_write(phy, DAC_REG_CHAN_CNTRL_7(i), sel); } } else { dac_write(phy, DAC_REG_CHAN_CNTRL_7(chan), sel); } } else { uint32_t reg; switch(sel) { case DATA_SEL_DDS: case DATA_SEL_SED: case DATA_SEL_DMA: dac_read(phy, DAC_REG_CNTRL_2, ®); reg &= ~DAC_DATA_SEL(~0); reg |= DAC_DATA_SEL(sel); dac_write(phy, DAC_REG_CNTRL_2, reg); break; default: return -EINVAL; } } return 0; }
enum adc_pn_sel axiadc_get_pnsel(struct axiadc_state *st, int channel, const char **name) { unsigned val; if (PCORE_VERSION_MAJOR(st->pcore_version) > 7) { const char *ident[] = {"PN9", "PN23A", "UNDEF", "UNDEF", "PN7", "PN15", "PN23", "PN31", "UNDEF", "PN_CUSTOM"}; val = ADI_TO_ADC_PN_SEL(axiadc_read(st, ADI_REG_CHAN_CNTRL_3(channel))); if (name) *name = ident[val]; return val; } else { val = axiadc_read(st, ADI_REG_CHAN_CNTRL(channel));; if (name) { if (val & ADI_PN_SEL) *name = "PN_CUSTOM"; else if (val & ADI_PN23_TYPE) *name = "PN23"; else *name = "PN9"; } return val & (ADI_PN23_TYPE | ADI_PN_SEL); } }
/***************************************************************************//** * @brief dac_start_sync *******************************************************************************/ void dac_start_sync(bool force_on) { if (PCORE_VERSION_MAJOR(dds_st.pcore_version) < 8) { dac_write(ADI_REG_CNTRL_1, (dds_st.enable || force_on) ? ADI_ENABLE : 0); } else { dac_write(ADI_REG_CNTRL_1, ADI_SYNC); } }
/***************************************************************************//** * @brief dac_start_sync *******************************************************************************/ void dac_start_sync(struct ad9361_rf_phy *phy, bool force_on) { if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { dac_write(phy, DAC_REG_CNTRL_1, (dds_st[phy->id_no].enable || force_on) ? DAC_ENABLE : 0); } else { dac_write(phy, DAC_REG_CNTRL_1, DAC_SYNC); } }
/***************************************************************************//** * @brief axiadc_idelay_set *******************************************************************************/ void axiadc_idelay_set(struct axiadc_state *st, unsigned lane, unsigned val) { if (PCORE_VERSION_MAJOR(st->pcore_version) > 8) { axiadc_write(st, ADI_REG_DELAY(lane), val); } else { axiadc_write(st, ADI_REG_DELAY_CNTRL, 0); axiadc_write(st, ADI_REG_DELAY_CNTRL, ADI_DELAY_ADDRESS(lane) | ADI_DELAY_WDATA(val) | ADI_DELAY_SEL); } }
/** * Set IO delay. * @param st The AXI ADC state structure. * @param lane Lane number. * @param val Value. * @param tx The Synthesizer TX = 1, RX = 0. * @return 0 in case of success, negative error code otherwise. */ static int32_t ad9361_iodelay_set(struct axiadc_state *st, unsigned lane, unsigned val, bool tx) { if (tx) { if (PCORE_VERSION_MAJOR(st->pcore_version) > 8) axiadc_write(st, 0x4000 + ADI_REG_DELAY(lane), val); else return -ENODEV; } else { axiadc_idelay_set(st, lane, val); } return 0; }
/** * HDL loopback enable/disable. * @param phy The AD9361 state structure. * @param enable Enable/disable option. * @return 0 in case of success, negative error code otherwise. */ int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable) { struct axiadc_converter *conv = phy->adc_conv; struct axiadc_state *st = phy->adc_state; int32_t reg, addr, chan; uint32_t version = axiadc_read(st, 0x4000); /* Still there but implemented a bit different */ if (PCORE_VERSION_MAJOR(version) > 7) addr = 0x4418; else addr = 0x4414; for (chan = 0; chan < conv->chip_info->num_channels; chan++) { reg = axiadc_read(st, addr + (chan) * 0x40); if (PCORE_VERSION_MAJOR(version) > 7) { if (enable && reg != 0x8) { conv->scratch_reg[chan] = reg; reg = 0x8; } else if (reg == 0x8) { reg = conv->scratch_reg[chan]; } } else { /* DAC_LB_ENB If set enables loopback of receive data */ if (enable) reg |= BIT(1); else reg &= ~BIT(1); } axiadc_write(st, addr + (chan) * 0x40, reg); } return 0; }
/***************************************************************************//** * @brief dds_set_phase *******************************************************************************/ void dds_set_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t scale_micro_units) { uint32_t scale_reg; uint32_t sign_part; uint32_t int_part; uint32_t fract_part; if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) > 6) { if(scale_micro_units >= 1000000) { sign_part = 0; int_part = 1; fract_part = 0; dds_st[phy->id_no].cached_scale[chan] = 1000000; goto set_scale_reg; } if(scale_micro_units <= -1000000) { sign_part = 1; int_part = 1; fract_part = 0; dds_st[phy->id_no].cached_scale[chan] = -1000000; goto set_scale_reg; } dds_st[phy->id_no].cached_scale[chan] = scale_micro_units; if(scale_micro_units < 0) { sign_part = 1; int_part = 0; scale_micro_units *= -1; } else { sign_part = 0; int_part = 0; } fract_part = (uint32_t)(((uint64_t)scale_micro_units * 0x4000) / 1000000); set_scale_reg: scale_reg = (sign_part << 15) | (int_part << 14) | fract_part; } else { if(scale_micro_units >= 1000000) { scale_reg = 0; scale_micro_units = 1000000; } if(scale_micro_units <= 0) { scale_reg = 0; scale_micro_units = 0; } dds_st[phy->id_no].cached_scale[chan] = scale_micro_units; fract_part = (uint32_t)(scale_micro_units); scale_reg = 500000 / fract_part; } dac_stop(phy); dac_write(phy, DAC_REG_CHAN_CNTRL_1_IIOCHAN(chan), DAC_DDS_SCALE(scale_reg)); dac_start_sync(phy, 0); }
/** * Digital tune. * @param phy The AD9361 state structure. * @param max_freq Maximum frequency. * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY. * @return 0 in case of success, negative error code otherwise. */ int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, enum dig_tune_flags flags) { struct axiadc_converter *conv = phy->adc_conv; struct axiadc_state *st = phy->adc_state; int32_t ret, i, j, k, chan, t, num_chan, err = 0; uint32_t s0, s1, c0, c1, tmp, saved = 0; uint8_t field[2][16]; uint32_t saved_dsel[4], saved_chan_ctrl6[4], saved_chan_ctrl0[4]; uint32_t rates[3] = {25000000U, 40000000U, 61440000U}; uint32_t hdl_dac_version; dev_dbg(&phy->spi->dev, "%s: freq %"PRIu32" flags 0x%X\n", __func__, max_freq, flags); hdl_dac_version = axiadc_read(st, 0x4000); if ((phy->pdata->dig_interface_tune_skipmode == 2) || (flags & RESTORE_DEFAULT)) { /* skip completely and use defaults */ 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); return 0; } if (flags & DO_IDELAY) ad9361_midscale_iodelay(phy, 0); if (flags & DO_ODELAY) ad9361_midscale_iodelay(phy, 1); 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; ad9361_bist_prbs(phy, BIST_INJ_RX); for (t = 0; t < 2; t++) { memset(field, 0, 32); for (k = 0; (uint32_t)k < (max_freq ? ARRAY_SIZE(rates) : 1); k++) { if (max_freq) 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)); for (chan = 0; chan < num_chan; chan++) axiadc_write(st, ADI_REG_CHAN_STATUS(chan), ADI_PN_ERR | ADI_PN_OOS); mdelay(4); if ((t == 1) || (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS)) { for (chan = 0, ret = 0; chan < num_chan; chan++) { ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan)); } } else { ret = 1; } field[i][j] |= ret; } } if ((flags & BE_MOREVERBOSE) && max_freq) { ad9361_dig_tune_verbose_print(phy, field, t); } } c0 = ad9361_find_opt(&field[0][0], 16, &s0); c1 = ad9361_find_opt(&field[1][0], 16, &s1); if (!c0 && !c1) { ad9361_dig_tune_verbose_print(phy, field, t); dev_err(&phy->spi->dev, "%s: Tuning %s FAILED!", __func__, t ? "TX" : "RX"); err |= -EIO; } else if (flags & BE_VERBOSE) { ad9361_dig_tune_verbose_print(phy, field, t); } if (c1 > c0) ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t, DATA_CLK_DELAY(s1 + c1 / 2) | RX_DATA_DELAY(0)); else ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t, DATA_CLK_DELAY(0) | RX_DATA_DELAY(s0 + c0 / 2)); if (t == 0) { if (flags & DO_IDELAY) ad9361_dig_tune_iodelay(phy, 0); /* Now do the loopback and tune the digital out */ ad9361_bist_prbs(phy, BIST_DISABLE); axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN); axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); if (phy->pdata->dig_interface_tune_skipmode == 1) { /* skip TX */ if (!(flags & SKIP_STORE_RESULT)) phy->pdata->port_ctrl.rx_clk_data_delay = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); if (!phy->pdata->fdd) { ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl); ad9361_ensm_restore_prev_state(phy); } return 0; } ad9361_bist_loopback(phy, 1); axiadc_write(st, 0x4000 + ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); for (chan = 0; chan < num_chan; chan++) { saved_chan_ctrl0[chan] = axiadc_read(st, ADI_REG_CHAN_CNTRL(chan)); axiadc_write(st, ADI_REG_CHAN_CNTRL(chan), ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE | ADI_ENABLE | ADI_IQCOR_ENB); axiadc_set_pnsel(st, chan, ADC_PN_CUSTOM); saved_chan_ctrl6[chan] = axiadc_read(st, 0x4414 + (chan) * 0x40); if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7) { saved_dsel[chan] = axiadc_read(st, 0x4418 + (chan) * 0x40); axiadc_write(st, 0x4418 + (chan) * 0x40, 9); axiadc_write(st, 0x4044, 0x1); } else axiadc_write(st, 0x4414 + (chan) * 0x40, 1); } if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8) { saved = tmp = axiadc_read(st, 0x4048); tmp &= ~0xF; tmp |= 1; axiadc_write(st, 0x4048, tmp); } } else { if (flags & DO_ODELAY) ad9361_dig_tune_iodelay(phy, 1); ad9361_bist_loopback(phy, 0); if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8) axiadc_write(st, 0x4048, saved); for (chan = 0; chan < num_chan; chan++) { axiadc_write(st, ADI_REG_CHAN_CNTRL(chan), saved_chan_ctrl0[chan]); axiadc_set_pnsel(st, chan, ADC_PN9); if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7) { axiadc_write(st, 0x4418 + (chan) * 0x40, saved_dsel[chan]); axiadc_write(st, 0x4044, 0x1); } axiadc_write(st, 0x4414 + (chan) * 0x40, saved_chan_ctrl6[chan]); } if (err == -EIO) { 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); if (!max_freq) err = 0; } else if (!(flags & SKIP_STORE_RESULT)) { phy->pdata->port_ctrl.rx_clk_data_delay = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); phy->pdata->port_ctrl.tx_clk_data_delay = ad9361_spi_read(phy->spi, REG_TX_CLOCK_DATA_DELAY); } if (!phy->pdata->fdd) { ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl); ad9361_ensm_restore_prev_state(phy); } axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN); axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); return err; } } return -EINVAL; }