/***************************************************************************//** * @brief Waits for the AD9122 to sync. * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ int32_t ad9122_sync() { int32_t ret, timeout; timeout = 255; do { delay_us(1000); ret = ad9122_read(AD9122_REG_FIFO_STATUS_1); if (ret < 0) return ret; } while (timeout-- && !(ret & AD9122_FIFO_STATUS_1_FIFO_SOFT_ALIGN_ACK)); ad9122_write(AD9122_REG_FIFO_STATUS_1, 0x0); ad9122_write(AD9122_REG_SYNC_CTRL_1, AD9122_SYNC_CTRL_1_SYNC_EN | AD9122_SYNC_CTRL_1_RISING_EDGE_SYNC); timeout = 255; do { delay_us(1000); ret = ad9122_read(AD9122_REG_SYNC_STATUS_1); if (ret < 0) return ret; } while (timeout-- && !(ret & AD9122_SYNC_STATUS_1_SYNC_LOCKED)); return 0; }
/***************************************************************************//** * @brief Writes interpolation data to the AD9122. * * @return Returns negative error code or the written data in case of success. *******************************************************************************/ uint32_t ad9122_interpolation_store(uint32_t address, int32_t readin) { struct cf_axi_converter *conv = &dds_conv; unsigned pwr; int32_t ret; pwr = ad9122_read(AD9122_REG_POWER_CTRL); ad9122_write(AD9122_REG_POWER_CTRL, pwr | AD9122_POWER_CTRL_PD_I_DAC | AD9122_POWER_CTRL_PD_Q_DAC); switch (address) { case 0: ret = ad9122_set_interpol_freq(conv, readin); break; case 1: ret = ad9122_set_interpol_fcent_freq(conv, readin); break; default: ret = -1; } if (conv->pcore_sync) conv->pcore_sync(); ad9122_write(AD9122_REG_POWER_CTRL, pwr); return ret ? ret : readin; }
/***************************************************************************//** * @brief Initializes the AD9122. * * @param pfnSetDataClock - Pointer to a function which sets the data clock * @param pfnSetDacClock - Pointer to a function which sets the DAC clock * @param pfnRoundRateDataClock - Pointer to a function which computes the * actual data clock for a desired clock value * @param pfnRoundRateDacClock - Pointer to a function which computes the * actual DAC clock for a desired clock value * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ int32_t ad9122_setup(void* pfnSetDataClock, void* pfnSetDacClock, void* pfnSetRefClock, void* pfnRoundRateDataClock, void* pfnRoundRateDacClock, void* pfnRoundRateRefClock) { int32_t ret; int32_t i; uint32_t datapath_ctrl, rate; struct cf_axi_converter *conv = &dds_conv; if(ad9122_reset() < 0) return -1; pfnSetDataClk = pfnSetDataClock; pfnSetDacClk = pfnSetDacClock; pfnSetRefClk = pfnSetRefClock; pfnRoundRateDataClk = pfnRoundRateDataClock; pfnRoundRateDacClk = pfnRoundRateDacClock; pfnRoundRateRefClk = pfnRoundRateRefClock; conv->write = ad9122_write; conv->read = ad9122_read; conv->setup = ad9122_tune_dci; conv->get_fifo_status = ad9122_get_fifo_status; conv->get_data_clk = ad9122_get_data_clk; conv->write_raw = ad9122_write_raw; conv->read_raw = ad9122_read_raw; for (i = 0; i < ARRAY_SIZE(ad9122_reg_defaults); i++) { ad9122_write(ad9122_reg_defaults[i][0], ad9122_reg_defaults[i][1]); } if(ad9122_sync() < 0) return -1; conv->interp_factor = 1; conv->interp_factor = ad9122_validate_interp_factor(conv->interp_factor); conv->fcenter_shift = 0; datapath_ctrl = AD9122_DATAPATH_CTRL_BYPASS_PREMOD | AD9122_DATAPATH_CTRL_BYPASS_NCO | AD9122_DATAPATH_CTRL_BYPASS_INV_SINC; ad9122_write(AD9122_REG_DATAPATH_CTRL, datapath_ctrl); rate = 491520000; ret = ad9122_set_interpol(conv, conv->interp_factor, conv->fcenter_shift, rate); cf_axi_dds_of_probe(); return ret; }
/***************************************************************************//** * @brief Resets the device. * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ int32_t ad9122_reset(void) { int32_t ret; ret = ad9122_write(AD9122_REG_COMM, AD9122_COMM_RESET); if(ret < 0) return ret; ret = ad9122_write(AD9122_REG_COMM, 0x00); return ret; }
/***************************************************************************//** * @brief Sets the full-scale current for I DAC. * * @param fs_adj - Full scale current value. If the value equals INT32_MAX then * the function returns the current set value. * * @return Returns the set full-scale current. *******************************************************************************/ int32_t ad9122_fs_adj_I_DAC(int32_t fs_adj) { uint8_t sleepBit = 0; uint8_t regData1 = 0; uint8_t regData2 = 0; if(fs_adj != INT32_MAX) { /* Read the current state of the sleep bit */ sleepBit = (ad9122_read(AD9122_REG_I_DAC_CTRL) & AD9122_I_DAC_CTRL_I_DAC_SLEEP); /* Set the full-scale value and keep the state of the sleep bit. */ regData1 = AD9122_I_DAC_CTRL_I_DAC_FS_ADJ_9_8((fs_adj & 0x0300) >> 8); ad9122_write(AD9122_REG_I_DAC_CTRL, (sleepBit | regData1)); regData2 = AD9122_I_DAC_FS_ADJ_I_DAC_FS_ADJ_7_0((fs_adj & 0x00FF) >> 0); ad9122_write(AD9122_REG_I_DAC_FS_ADJ, regData2); /* Compute the set full scale current */ fs_adj = ((regData1 & 0x3) << 8) + (regData2 << 0); }
/***************************************************************************//** * @brief Writes data to the AD9122. * * @return Returns negative error code or the written value in case of success. *******************************************************************************/ uint32_t ad9122_store(uint32_t address, int32_t readin) { int32_t ret = 0; switch (address) { case AD9122_REG_I_DAC_OFFSET_MSB: case AD9122_REG_Q_DAC_OFFSET_MSB: if (readin < 0 || readin > 0xFFFF) { ret = -1; goto out; } break; case AD9122_REG_I_PHA_ADJ_MSB: case AD9122_REG_Q_PHA_ADJ_MSB: if (readin < -512 || readin > 511) { ret = -1; goto out; } break; default: if (readin < 0 || readin > 0x3FF) { ret = -1; goto out; } break; } ret = ad9122_write((uint32_t)address, readin >> 8); if (ret < 0) goto out; ret = ad9122_write((uint32_t)address - 1, readin & 0xFF); if (ret < 0) goto out; out: return ret ? ret : readin; }
/***************************************************************************//** * @brief Sets the interpolation factor and the center shift frequency. * * @param conv - Pointer to a cf_axi_converter struct. * @param interp - Interpolation factor * @param fcent_shift - Center frequency shift as a multiplier of fData / 2. * The shift values should be in the range [0, 15] * @param data_rate - Data rate in Hz * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ static int32_t ad9122_set_interpol(struct cf_axi_converter *conv, uint32_t interp, uint32_t fcent_shift, uint32_t data_rate) { uint32_t hb1, hb2, hb3, tmp; int32_t ret, cached; hb1 = AD9122_HB1_CTRL_BYPASS_HB1; hb2 = AD9122_HB2_CTRL_BYPASS_HB2; hb3 = AD9122_HB3_CTRL_BYPASS_HB3; switch (interp) { case 1: break; case 2: if (fcent_shift > 3) return -1; hb1 = AD9122_HB1_INTERP(fcent_shift); break; case 4: if (fcent_shift > 7) return -1; hb1 = AD9122_HB1_INTERP(fcent_shift % 4); hb2 = AD9122_HB23_INTERP(fcent_shift); break; case 8: if (fcent_shift > 15) return -1; hb1 = AD9122_HB1_INTERP(fcent_shift % 4); hb2 = AD9122_HB23_INTERP(fcent_shift % 8); hb3 = AD9122_HB23_INTERP(fcent_shift / 2); break; default: return -1; } cached = conv->interp_factor; conv->interp_factor = interp; ret = ad9122_set_data_clk(conv, data_rate ? data_rate : ad9122_get_data_clk(conv)); if (ret < 0) { conv->interp_factor = cached; return ret; } tmp = ad9122_read(AD9122_REG_DATAPATH_CTRL); switch (hb1) { case AD9122_HB1_INTERP(1): case AD9122_HB1_INTERP(3): tmp &= ~AD9122_DATAPATH_CTRL_BYPASS_PREMOD; break; default: tmp |= AD9122_DATAPATH_CTRL_BYPASS_PREMOD; } ad9122_write(AD9122_REG_DATAPATH_CTRL, tmp); ad9122_write(AD9122_REG_HB1_CTRL, hb1); ad9122_write(AD9122_REG_HB2_CTRL, hb2); ad9122_write(AD9122_REG_HB3_CTRL, hb3); conv->fcenter_shift = fcent_shift; return 0; }
/***************************************************************************//** * @brief Calibrates the AD9122 DCI. * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ int32_t ad9122_tune_dci(struct cf_axi_converter *conv) { uint32_t reg; int32_t i = 0, dci; uint32_t err_bfield = 0; for (dci = 0; dci < 4; dci++) { ad9122_write(AD9122_REG_DCI_DELAY, dci); for (i = 0; i < ARRAY_SIZE(dac_sed_pattern); i++) { ad9122_write(AD9122_REG_SED_CTRL, 0); if(conv->pcore_set_sed_pattern) { conv->pcore_set_sed_pattern(0, dac_sed_pattern[i].i0, dac_sed_pattern[i].i1); conv->pcore_set_sed_pattern(1, dac_sed_pattern[i].q0, dac_sed_pattern[i].q1); } ad9122_write(AD9122_REG_COMPARE_I0_LSBS, dac_sed_pattern[i].i0 & 0xFF); ad9122_write(AD9122_REG_COMPARE_I0_MSBS, dac_sed_pattern[i].i0 >> 8); ad9122_write(AD9122_REG_COMPARE_Q0_LSBS, dac_sed_pattern[i].q0 & 0xFF); ad9122_write(AD9122_REG_COMPARE_Q0_MSBS, dac_sed_pattern[i].q0 >> 8); ad9122_write(AD9122_REG_COMPARE_I1_LSBS, dac_sed_pattern[i].i1 & 0xFF); ad9122_write(AD9122_REG_COMPARE_I1_MSBS, dac_sed_pattern[i].i1 >> 8); ad9122_write(AD9122_REG_COMPARE_Q1_LSBS, dac_sed_pattern[i].q1 & 0xFF); ad9122_write(AD9122_REG_COMPARE_Q1_MSBS, dac_sed_pattern[i].q1 >> 8); ad9122_write(AD9122_REG_SED_CTRL, AD9122_SED_CTRL_SED_COMPARE_EN); ad9122_write(AD9122_REG_EVENT_FLAG_2, AD9122_EVENT_FLAG_2_AED_COMPARE_PASS | AD9122_EVENT_FLAG_2_AED_COMPARE_FAIL | AD9122_EVENT_FLAG_2_SED_COMPARE_FAIL); ad9122_write(AD9122_REG_SED_CTRL, AD9122_SED_CTRL_SED_COMPARE_EN | AD9122_SED_CTRL_AUTOCLEAR_EN); msleep(100); reg = ad9122_read(AD9122_REG_SED_CTRL); if(!(reg & (AD9122_SED_CTRL_SAMPLE_ERR_DETECTED | AD9122_SED_CTRL_COMPARE_PASS))) { return -1; } if (reg & AD9122_SED_CTRL_SAMPLE_ERR_DETECTED) set_bit(dci, &err_bfield); } } dci = ad9122_find_dci(&err_bfield, 4); if(dci < 0) { return -1; } ad9122_write(AD9122_REG_DCI_DELAY, dci); ad9122_write(AD9122_REG_SED_CTRL, 0); return 0; }
/***************************************************************************//** * @brief Calibrates the AD9122 DCI. * * @return Returns negative error code or 0 in case of success. *******************************************************************************/ int32_t ad9122_tune_dci(struct cf_axi_converter *conv) { uint32_t reg, err_mask, pwr; int32_t i = 0, dci; uint32_t err_bfield = 0; pwr = ad9122_read(AD9122_REG_POWER_CTRL); ad9122_write(AD9122_REG_POWER_CTRL, pwr | AD9122_POWER_CTRL_PD_I_DAC | AD9122_POWER_CTRL_PD_Q_DAC); for (dci = 0; dci < 4; dci++) { ad9122_write(AD9122_REG_DCI_DELAY, dci); for (i = 0; i < ARRAY_SIZE(dac_sed_pattern); i++) { ad9122_write(AD9122_REG_SED_CTRL, 0); #ifdef CF_AXI_DDS if(conv->pcore_set_sed_pattern) conv->pcore_set_sed_pattern( (dac_sed_pattern[i].i1 << 16) | dac_sed_pattern[i].i0, (dac_sed_pattern[i].q1 << 16) | dac_sed_pattern[i].q0); #endif ad9122_write(AD9122_REG_COMPARE_I0_LSBS, dac_sed_pattern[i].i0 & 0xFF); ad9122_write(AD9122_REG_COMPARE_I0_MSBS, dac_sed_pattern[i].i0 >> 8); ad9122_write(AD9122_REG_COMPARE_Q0_LSBS, dac_sed_pattern[i].q0 & 0xFF); ad9122_write(AD9122_REG_COMPARE_Q0_MSBS, dac_sed_pattern[i].q0 >> 8); ad9122_write(AD9122_REG_COMPARE_I1_LSBS, dac_sed_pattern[i].i1 & 0xFF); ad9122_write(AD9122_REG_COMPARE_I1_MSBS, dac_sed_pattern[i].i1 >> 8); ad9122_write(AD9122_REG_COMPARE_Q1_LSBS, dac_sed_pattern[i].q1 & 0xFF); ad9122_write(AD9122_REG_COMPARE_Q1_MSBS, dac_sed_pattern[i].q1 >> 8); ad9122_write(AD9122_REG_SED_CTRL, AD9122_SED_CTRL_SED_COMPARE_EN); ad9122_write(AD9122_REG_EVENT_FLAG_2, AD9122_EVENT_FLAG_2_AED_COMPARE_PASS | AD9122_EVENT_FLAG_2_AED_COMPARE_FAIL | AD9122_EVENT_FLAG_2_SED_COMPARE_FAIL); ad9122_write(AD9122_REG_SED_CTRL, AD9122_SED_CTRL_SED_COMPARE_EN); msleep(100); reg = ad9122_read(AD9122_REG_SED_CTRL); err_mask = ad9122_read(AD9122_REG_SED_I_LSBS); err_mask |= ad9122_read(AD9122_REG_SED_I_MSBS); err_mask |= ad9122_read(AD9122_REG_SED_Q_LSBS); err_mask |= ad9122_read(AD9122_REG_SED_Q_MSBS); if (err_mask || (reg & AD9122_SED_CTRL_SAMPLE_ERR_DETECTED)) set_bit(dci, &err_bfield); } } ad9122_write(AD9122_REG_DCI_DELAY, ad9122_find_dci(&err_bfield, 4)); ad9122_write(AD9122_REG_SED_CTRL, 0); ad9122_write(AD9122_REG_POWER_CTRL, pwr); return 0; }