/** * Digital interface timing analysis. * @param phy The AD9361 state structure. * @param buf The buffer. * @param buflen The buffer length. * @return The size in case of success, negative error code otherwise. */ int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy, char *buf, int32_t buflen) { struct axiadc_state *st = phy->adc_state; int32_t ret, i, j, chan, len = 0; uint8_t field[16][16]; uint8_t rx; dev_dbg(&phy->spi->dev, "%s:\n", __func__); rx = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); ad9361_bist_prbs(phy, BIST_INJ_RX); 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)); for (chan = 0; chan < 4; chan++) axiadc_write(st, ADI_REG_CHAN_STATUS(chan), ADI_PN_ERR | ADI_PN_OOS); mdelay(1); if (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS) { for (chan = 0, ret = 0; chan < 4; chan++) ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan)); } else { ret = 1; } field[i][j] = ret; } } ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, rx); ad9361_bist_prbs(phy, BIST_DISABLE); len += snprintf(buf + len, buflen, "CLK: %"PRIu32" Hz 'o' = PASS\n", clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK])); len += snprintf(buf + len, buflen, "DC"); for (i = 0; i < 16; i++) len += snprintf(buf + len, buflen, "%"PRIx32":", i); len += snprintf(buf + len, buflen, "\n"); for (i = 0; i < 16; i++) { len += snprintf(buf + len, buflen, "%"PRIx32":", i); for (j = 0; j < 16; j++) { len += snprintf(buf + len, buflen, "%c ", (field[i][j] ? '.' : 'o')); } len += snprintf(buf + len, buflen, "\n"); } len += snprintf(buf + len, buflen, "\n"); return len; }
/** * Initialize the AD9361 part. * @param init_param The structure that contains the AD9361 initial parameters. * @return A structure that contains the AD9361 current state in case of * success, negative error code otherwise. */ struct ad9361_rf_phy *ad9361_init (AD9361_InitParam *init_param) { struct ad9361_rf_phy *phy; int32_t ret = 0; int32_t rev = 0; int32_t i = 0; phy = (struct ad9361_rf_phy *)zmalloc(sizeof(*phy)); if (!phy) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } phy->spi = (struct spi_device *)zmalloc(sizeof(*phy->spi)); if (!phy->spi) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } phy->clk_refin = (struct clk *)zmalloc(sizeof(*phy->clk_refin)); if (!phy->clk_refin) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } phy->pdata = (struct ad9361_phy_platform_data *)zmalloc(sizeof(*phy->pdata)); if (!phy->pdata) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } phy->adc_conv = (struct axiadc_converter *)zmalloc(sizeof(*phy->adc_conv)); if (!phy->adc_conv) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } phy->adc_state = (struct axiadc_state *)zmalloc(sizeof(*phy->adc_state)); if (!phy->adc_state) { return (struct ad9361_rf_phy *)ERR_PTR(-ENOMEM); } /* Reference Clock */ phy->clk_refin->rate = init_param->reference_clk_rate; /* Base Configuration */ phy->pdata->fdd = init_param->frequency_division_duplex_mode_enable; phy->pdata->rx2tx2 = init_param->two_rx_two_tx_mode_enable; phy->pdata->tdd_use_dual_synth = init_param->tdd_use_dual_synth_mode_enable; phy->pdata->tdd_skip_vco_cal = init_param->tdd_skip_vco_cal_enable; phy->pdata->rx_fastlock_delay_ns = init_param->rx_fastlock_delay_ns; phy->pdata->tx_fastlock_delay_ns = init_param->tx_fastlock_delay_ns; phy->pdata->trx_fastlock_pinctrl_en[0] = init_param->rx_fastlock_pincontrol_enable; phy->pdata->trx_fastlock_pinctrl_en[1] = init_param->tx_fastlock_pincontrol_enable; phy->pdata->use_ext_rx_lo = init_param->external_rx_lo_enable; phy->pdata->use_ext_tx_lo = init_param->external_tx_lo_enable; phy->pdata->dc_offset_update_events = init_param->dc_offset_tracking_update_event_mask; phy->pdata->dc_offset_attenuation_high = init_param->dc_offset_attenuation_high_range; phy->pdata->dc_offset_attenuation_low = init_param->dc_offset_attenuation_low_range; phy->pdata->rf_dc_offset_count_high = init_param->dc_offset_count_high_range; phy->pdata->rf_dc_offset_count_low = init_param->dc_offset_count_low_range; phy->pdata->tdd_use_fdd_tables = init_param->tdd_use_fdd_vco_tables_enable; phy->pdata->split_gt = init_param->split_gain_table_mode_enable; /* ENSM Control */ phy->pdata->ensm_pin_pulse_mode = init_param->ensm_enable_pin_pulse_mode_enable; phy->pdata->ensm_pin_ctrl = init_param->ensm_enable_txnrx_control_enable; /* LO Control */ phy->pdata->rx_synth_freq = init_param->rx_synthesizer_frequency_hz; phy->pdata->tx_synth_freq = init_param->tx_synthesizer_frequency_hz; /* Rate & BW Control */ for(i = 0; i < 6; i++) { phy->pdata->rx_path_clks[i] = init_param->rx_path_clock_frequencies[i]; } for(i = 0; i < 6; i++) { phy->pdata->tx_path_clks[i] = init_param->tx_path_clock_frequencies[i]; } phy->pdata->rf_rx_bandwidth_Hz = init_param->rf_rx_bandwidth_hz; phy->pdata->rf_tx_bandwidth_Hz = init_param->rf_tx_bandwidth_hz; /* RF Port Control */ phy->pdata->rf_rx_input_sel = init_param->rx_rf_port_input_select; phy->pdata->rf_tx_output_sel = init_param->tx_rf_port_input_select; /* TX Attenuation Control */ phy->pdata->tx_atten = init_param->tx_attenuation_mdB; phy->pdata->update_tx_gain_via_alert = init_param->update_tx_gain_in_alert_enable; /* Reference Clock Control */ phy->pdata->use_extclk = init_param->xo_disable_use_ext_refclk_enable; phy->pdata->dcxo_coarse = init_param->dcxo_coarse_and_fine_tune[0]; phy->pdata->dcxo_fine = init_param->dcxo_coarse_and_fine_tune[1]; phy->pdata->ad9361_clkout_mode = (enum ad9361_clkout)init_param->clk_output_mode_select; /* Gain Control */ phy->pdata->gain_ctrl.rx1_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx1_mode; phy->pdata->gain_ctrl.rx2_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx2_mode; phy->pdata->gain_ctrl.adc_large_overload_thresh = init_param->gc_adc_large_overload_thresh; phy->pdata->gain_ctrl.adc_ovr_sample_size = init_param->gc_adc_ovr_sample_size; phy->pdata->gain_ctrl.adc_small_overload_thresh = init_param->gc_adc_small_overload_thresh; phy->pdata->gain_ctrl.dec_pow_measuremnt_duration = init_param->gc_dec_pow_measurement_duration; phy->pdata->gain_ctrl.dig_gain_en = init_param->gc_dig_gain_enable; phy->pdata->gain_ctrl.lmt_overload_high_thresh = init_param->gc_lmt_overload_high_thresh; phy->pdata->gain_ctrl.lmt_overload_low_thresh = init_param->gc_lmt_overload_low_thresh; phy->pdata->gain_ctrl.low_power_thresh = init_param->gc_low_power_thresh; phy->pdata->gain_ctrl.max_dig_gain = init_param->gc_max_dig_gain; /* Gain MGC Control */ phy->pdata->gain_ctrl.mgc_dec_gain_step = init_param->mgc_dec_gain_step; phy->pdata->gain_ctrl.mgc_inc_gain_step = init_param->mgc_inc_gain_step; phy->pdata->gain_ctrl.mgc_rx1_ctrl_inp_en = init_param->mgc_rx1_ctrl_inp_enable; phy->pdata->gain_ctrl.mgc_rx2_ctrl_inp_en = init_param->mgc_rx2_ctrl_inp_enable; phy->pdata->gain_ctrl.mgc_split_table_ctrl_inp_gain_mode = init_param->mgc_split_table_ctrl_inp_gain_mode; /* Gain AGC Control */ phy->pdata->gain_ctrl.adc_large_overload_exceed_counter = init_param->agc_adc_large_overload_exceed_counter; phy->pdata->gain_ctrl.adc_large_overload_inc_steps = init_param->agc_adc_large_overload_inc_steps; phy->pdata->gain_ctrl.adc_lmt_small_overload_prevent_gain_inc = init_param->agc_adc_lmt_small_overload_prevent_gain_inc_enable; phy->pdata->gain_ctrl.adc_small_overload_exceed_counter = init_param->agc_adc_small_overload_exceed_counter; phy->pdata->gain_ctrl.dig_gain_step_size = init_param->agc_dig_gain_step_size; phy->pdata->gain_ctrl.dig_saturation_exceed_counter = init_param->agc_dig_saturation_exceed_counter; phy->pdata->gain_ctrl.gain_update_interval_us = init_param->agc_gain_update_interval_us; phy->pdata->gain_ctrl.immed_gain_change_if_large_adc_overload = init_param->agc_immed_gain_change_if_large_adc_overload_enable; phy->pdata->gain_ctrl.immed_gain_change_if_large_lmt_overload = init_param->agc_immed_gain_change_if_large_lmt_overload_enable; phy->pdata->gain_ctrl.agc_inner_thresh_high = init_param->agc_inner_thresh_high; phy->pdata->gain_ctrl.agc_inner_thresh_high_dec_steps = init_param->agc_inner_thresh_high_dec_steps; phy->pdata->gain_ctrl.agc_inner_thresh_low = init_param->agc_inner_thresh_low; phy->pdata->gain_ctrl.agc_inner_thresh_low_inc_steps = init_param->agc_inner_thresh_low_inc_steps; phy->pdata->gain_ctrl.lmt_overload_large_exceed_counter = init_param->agc_lmt_overload_large_exceed_counter; phy->pdata->gain_ctrl.lmt_overload_large_inc_steps = init_param->agc_lmt_overload_large_inc_steps; phy->pdata->gain_ctrl.lmt_overload_small_exceed_counter = init_param->agc_lmt_overload_small_exceed_counter; phy->pdata->gain_ctrl.agc_outer_thresh_high = init_param->agc_outer_thresh_high; phy->pdata->gain_ctrl.agc_outer_thresh_high_dec_steps = init_param->agc_outer_thresh_high_dec_steps; phy->pdata->gain_ctrl.agc_outer_thresh_low = init_param->agc_outer_thresh_low; phy->pdata->gain_ctrl.agc_outer_thresh_low_inc_steps = init_param->agc_outer_thresh_low_inc_steps; phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us = init_param->agc_attack_delay_extra_margin_us; phy->pdata->gain_ctrl.sync_for_gain_counter_en = init_param->agc_sync_for_gain_counter_enable; /* Fast AGC */ phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration = init_param->fagc_dec_pow_measuremnt_duration; phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration = init_param->fagc_state_wait_time_ns; /* Fast AGC - Low Power */ phy->pdata->gain_ctrl.f_agc_allow_agc_gain_increase = init_param->fagc_allow_agc_gain_increase; phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_time = init_param->fagc_lp_thresh_increment_time; phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_steps = init_param->fagc_lp_thresh_increment_steps; /* Fast AGC - Lock Level */ phy->pdata->gain_ctrl.f_agc_lock_level = init_param->fagc_lock_level; phy->pdata->gain_ctrl.f_agc_lock_level_lmt_gain_increase_en = init_param->fagc_lock_level_lmt_gain_increase_en; phy->pdata->gain_ctrl.f_agc_lock_level_gain_increase_upper_limit = init_param->fagc_lock_level_gain_increase_upper_limit; /* Fast AGC - Peak Detectors and Final Settling */ phy->pdata->gain_ctrl.f_agc_lpf_final_settling_steps = init_param->fagc_lpf_final_settling_steps; phy->pdata->gain_ctrl.f_agc_lmt_final_settling_steps = init_param->fagc_lmt_final_settling_steps; phy->pdata->gain_ctrl.f_agc_final_overrange_count = init_param->fagc_final_overrange_count; /* Fast AGC - Final Power Test */ phy->pdata->gain_ctrl.f_agc_gain_increase_after_gain_lock_en = init_param->fagc_gain_increase_after_gain_lock_en; /* Fast AGC - Unlocking the Gain */ phy->pdata->gain_ctrl.f_agc_gain_index_type_after_exit_rx_mode = (enum f_agc_target_gain_index_type)init_param->fagc_gain_index_type_after_exit_rx_mode; phy->pdata->gain_ctrl.f_agc_use_last_lock_level_for_set_gain_en = init_param->fagc_use_last_lock_level_for_set_gain_en; phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_exceeded_en = init_param->fagc_rst_gla_stronger_sig_thresh_exceeded_en; phy->pdata->gain_ctrl.f_agc_optimized_gain_offset = init_param->fagc_optimized_gain_offset; phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_above_ll = init_param->fagc_rst_gla_stronger_sig_thresh_above_ll; phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en = init_param->fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_goto_optim_gain_en = init_param->fagc_rst_gla_engergy_lost_goto_optim_gain_en; phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_below_ll = init_param->fagc_rst_gla_engergy_lost_sig_thresh_below_ll; phy->pdata->gain_ctrl.f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt = init_param->fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; phy->pdata->gain_ctrl.f_agc_rst_gla_large_adc_overload_en = init_param->fagc_rst_gla_large_adc_overload_en; phy->pdata->gain_ctrl.f_agc_rst_gla_large_lmt_overload_en = init_param->fagc_rst_gla_large_lmt_overload_en; phy->pdata->gain_ctrl.f_agc_rst_gla_en_agc_pulled_high_en = init_param->fagc_rst_gla_en_agc_pulled_high_en; phy->pdata->gain_ctrl.f_agc_rst_gla_if_en_agc_pulled_high_mode = (enum f_agc_target_gain_index_type)init_param->fagc_rst_gla_if_en_agc_pulled_high_mode; phy->pdata->gain_ctrl.f_agc_power_measurement_duration_in_state5 = init_param->fagc_power_measurement_duration_in_state5; /* RSSI Control */ phy->pdata->rssi_ctrl.rssi_delay = init_param->rssi_delay; phy->pdata->rssi_ctrl.rssi_duration = init_param->rssi_duration; phy->pdata->rssi_ctrl.restart_mode = (enum rssi_restart_mode)init_param->rssi_restart_mode; phy->pdata->rssi_ctrl.rssi_unit_is_rx_samples = init_param->rssi_unit_is_rx_samples_enable; phy->pdata->rssi_ctrl.rssi_wait = init_param->rssi_wait; /* Aux ADC Control */ phy->pdata->auxadc_ctrl.auxadc_decimation = init_param->aux_adc_decimation; phy->pdata->auxadc_ctrl.auxadc_clock_rate = init_param->aux_adc_rate; /* AuxDAC Control */ phy->pdata->auxdac_ctrl.auxdac_manual_mode_en = init_param->aux_dac_manual_mode_enable; phy->pdata->auxdac_ctrl.dac1_default_value = init_param->aux_dac1_default_value_mV; phy->pdata->auxdac_ctrl.dac1_in_rx_en = init_param->aux_dac1_active_in_rx_enable; phy->pdata->auxdac_ctrl.dac1_in_tx_en = init_param->aux_dac1_active_in_tx_enable; phy->pdata->auxdac_ctrl.dac1_in_alert_en = init_param->aux_dac1_active_in_alert_enable; phy->pdata->auxdac_ctrl.dac1_rx_delay_us = init_param->aux_dac1_rx_delay_us; phy->pdata->auxdac_ctrl.dac1_tx_delay_us = init_param->aux_dac1_tx_delay_us; phy->pdata->auxdac_ctrl.dac2_default_value = init_param->aux_dac2_default_value_mV; phy->pdata->auxdac_ctrl.dac2_in_rx_en = init_param->aux_dac2_active_in_rx_enable; phy->pdata->auxdac_ctrl.dac2_in_tx_en = init_param->aux_dac2_active_in_tx_enable; phy->pdata->auxdac_ctrl.dac2_in_alert_en = init_param->aux_dac2_active_in_alert_enable; phy->pdata->auxdac_ctrl.dac2_rx_delay_us = init_param->aux_dac2_rx_delay_us; phy->pdata->auxdac_ctrl.dac2_tx_delay_us = init_param->aux_dac2_tx_delay_us; /* Temperature Sensor Control */ phy->pdata->auxadc_ctrl.temp_sensor_decimation = init_param->temp_sense_decimation; phy->pdata->auxadc_ctrl.temp_time_inteval_ms = init_param->temp_sense_measurement_interval_ms; phy->pdata->auxadc_ctrl.offset = init_param->temp_sense_offset_signed; phy->pdata->auxadc_ctrl.periodic_temp_measuremnt = init_param->temp_sense_periodic_measurement_enable; /* Control Out Setup */ phy->pdata->ctrl_outs_ctrl.en_mask = init_param->ctrl_outs_enable_mask; phy->pdata->ctrl_outs_ctrl.index = init_param->ctrl_outs_index; /* External LNA Control */ phy->pdata->elna_ctrl.settling_delay_ns = init_param->elna_settling_delay_ns; phy->pdata->elna_ctrl.gain_mdB = init_param->elna_gain_mdB; phy->pdata->elna_ctrl.bypass_loss_mdB = init_param->elna_bypass_loss_mdB; phy->pdata->elna_ctrl.elna_1_control_en = init_param->elna_rx1_gpo0_control_enable; phy->pdata->elna_ctrl.elna_2_control_en = init_param->elna_rx2_gpo1_control_enable; /* Digital Interface Control */ phy->pdata->port_ctrl.pp_conf[0] = (init_param->pp_tx_swap_enable << 7); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->pp_rx_swap_enable << 6); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->tx_channel_swap_enable << 5); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_channel_swap_enable << 4); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_frame_pulse_mode_enable << 3); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->two_t_two_r_timing_enable << 2); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_bus_enable << 1); phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_clk_enable << 0); phy->pdata->port_ctrl.pp_conf[1] = (init_param->fdd_alt_word_order_enable << 7); phy->pdata->port_ctrl.pp_conf[1] |= (init_param->invert_rx_frame_enable << 2); phy->pdata->port_ctrl.pp_conf[2] = (init_param->fdd_rx_rate_2tx_enable << 7); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->swap_ports_enable << 6); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_data_rate_enable << 5); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->lvds_mode_enable << 4); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->half_duplex_mode_enable << 3); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_port_mode_enable << 2); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_port_enable << 1); phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_duplex_swap_bits_enable << 0); phy->pdata->port_ctrl.pp_conf[1] |= (init_param->delay_rx_data & 0x3); phy->pdata->port_ctrl.rx_clk_data_delay = DATA_CLK_DELAY(init_param->rx_data_clock_delay); phy->pdata->port_ctrl.rx_clk_data_delay |= RX_DATA_DELAY(init_param->rx_data_delay); phy->pdata->port_ctrl.tx_clk_data_delay = FB_CLK_DELAY(init_param->tx_fb_clock_delay); phy->pdata->port_ctrl.tx_clk_data_delay |= TX_DATA_DELAY(init_param->tx_data_delay); phy->pdata->port_ctrl.lvds_bias_ctrl = (init_param->lvds_bias_mV / 75) & 0x7; phy->pdata->port_ctrl.lvds_bias_ctrl |= (init_param->lvds_rx_onchip_termination_enable << 5); phy->pdata->rx1rx2_phase_inversion_en = init_param->rx1rx2_phase_inversion_en; /* Tx Monitor Control */ phy->pdata->txmon_ctrl.low_high_gain_threshold_mdB = init_param->low_high_gain_threshold_mdB; phy->pdata->txmon_ctrl.low_gain_dB = init_param->low_gain_dB; phy->pdata->txmon_ctrl.high_gain_dB = init_param->high_gain_dB; phy->pdata->txmon_ctrl.tx_mon_track_en = init_param->tx_mon_track_en; phy->pdata->txmon_ctrl.one_shot_mode_en = init_param->one_shot_mode_en; phy->pdata->txmon_ctrl.tx_mon_delay = init_param->tx_mon_delay; phy->pdata->txmon_ctrl.tx_mon_duration = init_param->tx_mon_duration; phy->pdata->txmon_ctrl.tx1_mon_front_end_gain = init_param->tx1_mon_front_end_gain; phy->pdata->txmon_ctrl.tx2_mon_front_end_gain = init_param->tx2_mon_front_end_gain; phy->pdata->txmon_ctrl.tx1_mon_lo_cm = init_param->tx1_mon_lo_cm; phy->pdata->txmon_ctrl.tx2_mon_lo_cm = init_param->tx2_mon_lo_cm; phy->pdata->debug_mode = true; phy->pdata->gpio_resetb = init_param->gpio_resetb; /* Optional: next three GPIOs are used for MCS synchronization */ phy->pdata->gpio_sync = init_param->gpio_sync; phy->pdata->gpio_cal_sw1 = init_param->gpio_cal_sw1; phy->pdata->gpio_cal_sw2 = init_param->gpio_cal_sw2; phy->pdata->port_ctrl.digital_io_ctrl = 0; phy->pdata->port_ctrl.lvds_invert[0] = 0xFF; phy->pdata->port_ctrl.lvds_invert[1] = 0x0F; phy->adc_conv->chip_info = &axiadc_chip_info_tbl[phy->pdata->rx2tx2 ? ID_AD9361 : ID_AD9364]; phy->rx_eq_2tx = false; phy->current_table = RXGAIN_TBLS_END; phy->bypass_tx_fir = true; phy->bypass_rx_fir = true; phy->rate_governor = 1; phy->rfdc_track_en = true; phy->bbdc_track_en = true; phy->quad_track_en = true; ad9361_reset(phy); ad9361_spi_write(NULL, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET); ad9361_spi_write(NULL, REG_SPI_CONF, 0x0); ret = ad9361_spi_read(NULL, REG_PRODUCT_ID); if ((ret & PRODUCT_ID_MASK) != PRODUCT_ID_9361) { printf("%s : Unsupported PRODUCT_ID 0x%X", "ad9361_init", (unsigned int)ret); ret = -ENODEV; goto out; } rev = ret & REV_MASK; ret = register_clocks(phy); if (ret < 0) goto out; axiadc_init(phy); ad9361_init_gain_tables(phy); ret = ad9361_setup(phy); if (ret < 0) goto out; ret = ad9361_post_setup(phy); if (ret < 0) goto out; printf("%s : AD9361 Rev %d successfully initialized\n", "ad9361_init", (int)rev); return phy; out: free(phy->spi); free(phy->adc_conv); free(phy->adc_state); free(phy->clk_refin); free(phy->pdata); free(phy); printf("%s : AD9361 initialization error\n", "ad9361_init"); return (struct ad9361_rf_phy *)ERR_PTR(ENODEV); }
/** * 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; }