summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/fpga_common
diff options
context:
space:
mode:
authorArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
committerArturs Artamonovs <arturs.artamonovs@protonmail.com>2024-11-03 15:56:55 +0000
commitcf4444e7390365df43ecbd3d130015c1e06ef88f (patch)
tree8a6eb114135a04d5efd5af213577b4fac47532ae /Radio/HW/BladeRF/fpga_common
parentca50c0f64f1b2fce46b4cb83ed111854bac13852 (diff)
downloadPrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.tar.gz
PrySDR-cf4444e7390365df43ecbd3d130015c1e06ef88f.zip
BladeRF library compiles
Diffstat (limited to 'Radio/HW/BladeRF/fpga_common')
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/ad936x.h858
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h127
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/band_select.h50
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h749
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/lms.h838
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h218
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h208
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h213
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h231
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h206
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h197
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h37
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h231
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h334
-rw-r--r--Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h273
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c196
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/ad936x_params.c1024
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/band_select.c59
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c194
-rw-r--r--Radio/HW/BladeRF/fpga_common/src/lms.c3637
20 files changed, 9880 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/fpga_common/include/ad936x.h b/Radio/HW/BladeRF/fpga_common/include/ad936x.h
new file mode 100644
index 0000000..5fd43e7
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/ad936x.h
@@ -0,0 +1,858 @@
+/**
+ * @file ad936x.h
+ *
+ * @brief Interface to the library for the AD936X RFIC family
+ *
+ * Copyright (c) 2018 Nuand LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef AD936X_H_
+#define AD936X_H_
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+/**
+ * The purpose of this header file is to allow the use of libad9361 without
+ * including all of the unnecessary defines, etc, used during compilation.
+ *
+ * This file is largely copied from the files named in each section. Only
+ * necessary declarations are present.
+ *
+ * In general, defines are prefixed with AD936X_ to avoid conflicts.
+ *
+ * Comments have been removed for brevity. Please see the original header
+ * files from the third-party ADI library for further details.
+ */
+
+/******************************************************************************
+ * From common.h
+ ******************************************************************************/
+
+struct clk_onecell_data {
+ struct clk **clks;
+ uint32_t clk_num;
+};
+
+/******************************************************************************
+ * From ad9361.h
+ ******************************************************************************/
+
+#define AD936X_REG_TX1_OUT_1_PHASE_CORR 0x08E
+#define AD936X_REG_TX1_OUT_1_GAIN_CORR 0x08F
+#define AD936X_REG_TX2_OUT_1_PHASE_CORR 0x090
+#define AD936X_REG_TX2_OUT_1_GAIN_CORR 0x091
+#define AD936X_REG_TX1_OUT_1_OFFSET_I 0x092
+#define AD936X_REG_TX1_OUT_1_OFFSET_Q 0x093
+#define AD936X_REG_TX2_OUT_1_OFFSET_I 0x094
+#define AD936X_REG_TX2_OUT_1_OFFSET_Q 0x095
+#define AD936X_REG_TX1_OUT_2_PHASE_CORR 0x096
+#define AD936X_REG_TX1_OUT_2_GAIN_CORR 0x097
+#define AD936X_REG_TX2_OUT_2_PHASE_CORR 0x098
+#define AD936X_REG_TX2_OUT_2_GAIN_CORR 0x099
+#define AD936X_REG_TX1_OUT_2_OFFSET_I 0x09A
+#define AD936X_REG_TX1_OUT_2_OFFSET_Q 0x09B
+#define AD936X_REG_TX2_OUT_2_OFFSET_I 0x09C
+#define AD936X_REG_TX2_OUT_2_OFFSET_Q 0x09D
+#define AD936X_REG_TX_FORCE_BITS 0x09F
+
+#define AD936X_REG_RX1_INPUT_A_PHASE_CORR 0x170
+#define AD936X_REG_RX1_INPUT_A_GAIN_CORR 0x171
+#define AD936X_REG_RX2_INPUT_A_PHASE_CORR 0x172
+#define AD936X_REG_RX2_INPUT_A_GAIN_CORR 0x173
+#define AD936X_REG_RX1_INPUT_A_Q_OFFSET 0x174
+#define AD936X_REG_RX1_INPUT_A_OFFSETS 0x175
+#define AD936X_REG_INPUT_A_OFFSETS_1 0x176
+#define AD936X_REG_RX2_INPUT_A_OFFSETS 0x177
+#define AD936X_REG_RX2_INPUT_A_I_OFFSET 0x178
+#define AD936X_REG_RX1_INPUT_BC_PHASE_CORR 0x179
+#define AD936X_REG_RX1_INPUT_BC_GAIN_CORR 0x17A
+#define AD936X_REG_RX2_INPUT_BC_PHASE_CORR 0x17B
+#define AD936X_REG_RX2_INPUT_BC_GAIN_CORR 0x17C
+#define AD936X_REG_RX1_INPUT_BC_Q_OFFSET 0x17D
+#define AD936X_REG_RX1_INPUT_BC_OFFSETS 0x17E
+#define AD936X_REG_INPUT_BC_OFFSETS_1 0x17F
+#define AD936X_REG_RX2_INPUT_BC_OFFSETS 0x180
+#define AD936X_REG_RX2_INPUT_BC_I_OFFSET 0x181
+#define AD936X_REG_FORCE_BITS 0x182
+
+#define AD936X_READ (0 << 15)
+#define AD936X_WRITE (1 << 15)
+#define AD936X_CNT(x) ((((x)-1) & 0x7) << 12)
+#define AD936X_ADDR(x) ((x)&0x3FF)
+
+enum dev_id { ID_AD9361, ID_AD9364, ID_AD9363A };
+
+enum ad9361_clocks {
+ BB_REFCLK,
+ RX_REFCLK,
+ TX_REFCLK,
+ BBPLL_CLK,
+ ADC_CLK,
+ R2_CLK,
+ R1_CLK,
+ CLKRF_CLK,
+ RX_SAMPL_CLK,
+ DAC_CLK,
+ T2_CLK,
+ T1_CLK,
+ CLKTF_CLK,
+ TX_SAMPL_CLK,
+ RX_RFPLL_INT,
+ TX_RFPLL_INT,
+ RX_RFPLL_DUMMY,
+ TX_RFPLL_DUMMY,
+ RX_RFPLL,
+ TX_RFPLL,
+ NUM_AD9361_CLKS,
+ EXT_REF_CLK,
+};
+
+enum rx_gain_table_name {
+ TBL_200_1300_MHZ,
+ TBL_1300_4000_MHZ,
+ TBL_4000_6000_MHZ,
+ RXGAIN_TBLS_END,
+};
+
+enum rx_gain_table_type {
+ RXGAIN_FULL_TBL,
+ RXGAIN_SPLIT_TBL,
+};
+
+struct rx_gain_info {
+ enum rx_gain_table_type tbl_type;
+ int32_t starting_gain_db;
+ int32_t max_gain_db;
+ int32_t gain_step_db;
+ int32_t max_idx;
+ int32_t idx_step_offset;
+};
+
+enum ad9361_pdata_rx_freq {
+ BBPLL_FREQ,
+ ADC_FREQ,
+ R2_FREQ,
+ R1_FREQ,
+ CLKRF_FREQ,
+ RX_SAMPL_FREQ,
+ NUM_RX_CLOCKS,
+};
+
+enum ad9361_pdata_tx_freq {
+ IGNORE_FREQ,
+ DAC_FREQ,
+ T2_FREQ,
+ T1_FREQ,
+ CLKTF_FREQ,
+ TX_SAMPL_FREQ,
+ NUM_TX_CLOCKS,
+};
+
+struct ad9361_fastlock_entry {
+ //#define FASTLOOK_INIT 1
+ uint8_t flags;
+ uint8_t alc_orig;
+ uint8_t alc_written;
+};
+
+struct ad9361_fastlock {
+ uint8_t save_profile;
+ uint8_t current_profile[2];
+ struct ad9361_fastlock_entry entry[2][8];
+};
+
+enum ad9361_bist_mode {
+ BIST_DISABLE,
+ BIST_INJ_TX,
+ BIST_INJ_RX,
+};
+
+enum ad9361_clkout {
+ CLKOUT_DISABLE,
+ BUFFERED_XTALN_DCXO,
+ ADC_CLK_DIV_2,
+ ADC_CLK_DIV_3,
+ ADC_CLK_DIV_4,
+ ADC_CLK_DIV_8,
+ ADC_CLK_DIV_16,
+};
+
+enum rf_gain_ctrl_mode {
+ RF_GAIN_MGC,
+ RF_GAIN_FASTATTACK_AGC,
+ RF_GAIN_SLOWATTACK_AGC,
+ RF_GAIN_HYBRID_AGC
+};
+
+enum f_agc_target_gain_index_type {
+ MAX_GAIN,
+ SET_GAIN,
+ OPTIMIZED_GAIN,
+ NO_GAIN_CHANGE,
+};
+
+enum rssi_restart_mode {
+ AGC_IN_FAST_ATTACK_MODE_LOCKS_THE_GAIN,
+ EN_AGC_PIN_IS_PULLED_HIGH,
+ ENTERS_RX_MODE,
+ GAIN_CHANGE_OCCURS,
+ SPI_WRITE_TO_REGISTER,
+ GAIN_CHANGE_OCCURS_OR_EN_AGC_PIN_PULLED_HIGH,
+};
+
+struct rssi_control {
+ enum rssi_restart_mode restart_mode;
+ bool rssi_unit_is_rx_samples;
+ uint32_t rssi_delay;
+ uint32_t rssi_wait;
+ uint32_t rssi_duration;
+};
+
+struct port_control {
+ uint8_t pp_conf[3];
+ uint8_t rx_clk_data_delay;
+ uint8_t tx_clk_data_delay;
+ uint8_t digital_io_ctrl;
+ uint8_t lvds_bias_ctrl;
+ uint8_t lvds_invert[2];
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+};
+
+struct ctrl_outs_control {
+ uint8_t index;
+ uint8_t en_mask;
+};
+
+struct elna_control {
+ uint16_t gain_mdB;
+ uint16_t bypass_loss_mdB;
+ uint32_t settling_delay_ns;
+ bool elna_1_control_en;
+ bool elna_2_control_en;
+ bool elna_in_gaintable_all_index_en;
+};
+
+struct auxadc_control {
+ int8_t offset;
+ uint32_t temp_time_inteval_ms;
+ uint32_t temp_sensor_decimation;
+ bool periodic_temp_measuremnt;
+ uint32_t auxadc_clock_rate;
+ uint32_t auxadc_decimation;
+};
+
+struct auxdac_control {
+ uint16_t dac1_default_value;
+ uint16_t dac2_default_value;
+ bool auxdac_manual_mode_en;
+ bool dac1_in_rx_en;
+ bool dac1_in_tx_en;
+ bool dac1_in_alert_en;
+ bool dac2_in_rx_en;
+ bool dac2_in_tx_en;
+ bool dac2_in_alert_en;
+ uint8_t dac1_rx_delay_us;
+ uint8_t dac1_tx_delay_us;
+ uint8_t dac2_rx_delay_us;
+ uint8_t dac2_tx_delay_us;
+};
+
+struct gpo_control {
+ bool gpo0_inactive_state_high_en;
+ bool gpo1_inactive_state_high_en;
+ bool gpo2_inactive_state_high_en;
+ bool gpo3_inactive_state_high_en;
+ bool gpo0_slave_rx_en;
+ bool gpo0_slave_tx_en;
+ bool gpo1_slave_rx_en;
+ bool gpo1_slave_tx_en;
+ bool gpo2_slave_rx_en;
+ bool gpo2_slave_tx_en;
+ bool gpo3_slave_rx_en;
+ bool gpo3_slave_tx_en;
+ uint8_t gpo0_rx_delay_us;
+ uint8_t gpo0_tx_delay_us;
+ uint8_t gpo1_rx_delay_us;
+ uint8_t gpo1_tx_delay_us;
+ uint8_t gpo2_rx_delay_us;
+ uint8_t gpo2_tx_delay_us;
+ uint8_t gpo3_rx_delay_us;
+ uint8_t gpo3_tx_delay_us;
+};
+
+struct tx_monitor_control {
+ bool tx_mon_track_en;
+ bool one_shot_mode_en;
+ uint32_t low_high_gain_threshold_mdB;
+ uint8_t low_gain_dB;
+ uint8_t high_gain_dB;
+ uint16_t tx_mon_delay;
+ uint16_t tx_mon_duration;
+ uint8_t tx1_mon_front_end_gain;
+ uint8_t tx2_mon_front_end_gain;
+ uint8_t tx1_mon_lo_cm;
+ uint8_t tx2_mon_lo_cm;
+};
+
+struct gain_control {
+ enum rf_gain_ctrl_mode rx1_mode;
+ enum rf_gain_ctrl_mode rx2_mode;
+ uint8_t adc_ovr_sample_size;
+ uint8_t adc_small_overload_thresh;
+ uint8_t adc_large_overload_thresh;
+ uint16_t lmt_overload_high_thresh;
+ uint16_t lmt_overload_low_thresh;
+ uint16_t dec_pow_measuremnt_duration;
+ uint8_t low_power_thresh;
+ bool dig_gain_en;
+ uint8_t max_dig_gain;
+ bool mgc_rx1_ctrl_inp_en;
+ bool mgc_rx2_ctrl_inp_en;
+ uint8_t mgc_inc_gain_step;
+ uint8_t mgc_dec_gain_step;
+ uint8_t mgc_split_table_ctrl_inp_gain_mode;
+ uint8_t agc_attack_delay_extra_margin_us;
+ uint8_t agc_outer_thresh_high;
+ uint8_t agc_outer_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_high;
+ uint8_t agc_inner_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_low;
+ uint8_t agc_inner_thresh_low_inc_steps;
+ uint8_t agc_outer_thresh_low;
+ uint8_t agc_outer_thresh_low_inc_steps;
+ uint8_t adc_small_overload_exceed_counter;
+ uint8_t adc_large_overload_exceed_counter;
+ uint8_t adc_large_overload_inc_steps;
+ bool adc_lmt_small_overload_prevent_gain_inc;
+ uint8_t lmt_overload_large_exceed_counter;
+ uint8_t lmt_overload_small_exceed_counter;
+ uint8_t lmt_overload_large_inc_steps;
+ uint8_t dig_saturation_exceed_counter;
+ uint8_t dig_gain_step_size;
+ bool sync_for_gain_counter_en;
+ uint32_t gain_update_interval_us;
+ bool immed_gain_change_if_large_adc_overload;
+ bool immed_gain_change_if_large_lmt_overload;
+ uint32_t f_agc_dec_pow_measuremnt_duration;
+ uint32_t f_agc_state_wait_time_ns;
+ bool f_agc_allow_agc_gain_increase;
+ uint8_t f_agc_lp_thresh_increment_time;
+ uint8_t f_agc_lp_thresh_increment_steps;
+ uint8_t f_agc_lock_level;
+ bool f_agc_lock_level_lmt_gain_increase_en;
+ uint8_t f_agc_lock_level_gain_increase_upper_limit;
+ uint8_t f_agc_lpf_final_settling_steps;
+ uint8_t f_agc_lmt_final_settling_steps;
+ uint8_t f_agc_final_overrange_count;
+ bool f_agc_gain_increase_after_gain_lock_en;
+ enum f_agc_target_gain_index_type f_agc_gain_index_type_after_exit_rx_mode;
+ bool f_agc_use_last_lock_level_for_set_gain_en;
+ uint8_t f_agc_optimized_gain_offset;
+ bool f_agc_rst_gla_stronger_sig_thresh_exceeded_en;
+ uint8_t f_agc_rst_gla_stronger_sig_thresh_above_ll;
+ bool f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ bool f_agc_rst_gla_engergy_lost_goto_optim_gain_en;
+ uint8_t f_agc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ uint8_t f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ bool f_agc_rst_gla_large_adc_overload_en;
+ bool f_agc_rst_gla_large_lmt_overload_en;
+ bool f_agc_rst_gla_en_agc_pulled_high_en;
+ enum f_agc_target_gain_index_type f_agc_rst_gla_if_en_agc_pulled_high_mode;
+ uint8_t f_agc_power_measurement_duration_in_state5;
+};
+
+struct ad9361_phy_platform_data {
+ bool rx2tx2;
+ bool fdd;
+ bool fdd_independent_mode;
+ bool split_gt;
+ bool use_extclk;
+ bool ensm_pin_pulse_mode;
+ bool ensm_pin_ctrl;
+ bool debug_mode;
+ bool tdd_use_dual_synth;
+ bool tdd_skip_vco_cal;
+ bool use_ext_rx_lo;
+ bool use_ext_tx_lo;
+ bool rx1rx2_phase_inversion_en;
+ bool qec_tracking_slow_mode_en;
+ uint8_t dc_offset_update_events;
+ uint8_t dc_offset_attenuation_high;
+ uint8_t dc_offset_attenuation_low;
+ uint8_t rf_dc_offset_count_high;
+ uint8_t rf_dc_offset_count_low;
+ uint8_t dig_interface_tune_skipmode;
+ uint8_t dig_interface_tune_fir_disable;
+ uint32_t dcxo_coarse;
+ uint32_t dcxo_fine;
+ uint32_t rf_rx_input_sel;
+ uint32_t rf_tx_output_sel;
+ uint32_t rx1tx1_mode_use_rx_num;
+ uint32_t rx1tx1_mode_use_tx_num;
+ uint32_t rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t trx_synth_max_fref;
+ uint64_t rx_synth_freq;
+ uint64_t tx_synth_freq;
+ uint32_t rf_rx_bandwidth_Hz;
+ uint32_t rf_tx_bandwidth_Hz;
+ int32_t tx_atten;
+ bool update_tx_gain_via_alert;
+ uint32_t rx_fastlock_delay_ns;
+ uint32_t tx_fastlock_delay_ns;
+ bool trx_fastlock_pinctrl_en[2];
+ enum ad9361_clkout ad9361_clkout_mode;
+ struct gain_control gain_ctrl;
+ struct rssi_control rssi_ctrl;
+ struct port_control port_ctrl;
+ struct ctrl_outs_control ctrl_outs_ctrl;
+ struct elna_control elna_ctrl;
+ struct auxadc_control auxadc_ctrl;
+ struct auxdac_control auxdac_ctrl;
+ struct gpo_control gpo_ctrl;
+ struct tx_monitor_control txmon_ctrl;
+ int32_t gpio_resetb;
+ int32_t gpio_sync;
+ int32_t gpio_cal_sw1;
+ int32_t gpio_cal_sw2;
+};
+
+struct ad9361_rf_phy {
+ enum dev_id dev_sel;
+ uint8_t id_no;
+ struct spi_device *spi;
+ struct gpio_device *gpio;
+ struct clk *clk_refin;
+ struct clk *clks[NUM_AD9361_CLKS];
+ struct refclk_scale *ref_clk_scale[NUM_AD9361_CLKS];
+ struct clk_onecell_data clk_data;
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ struct ad9361_phy_platform_data *pdata;
+ uint8_t prev_ensm_state;
+ uint8_t curr_ensm_state;
+ uint8_t cached_rx_rfpll_div;
+ uint8_t cached_tx_rfpll_div;
+ struct rx_gain_info rx_gain[RXGAIN_TBLS_END];
+ enum rx_gain_table_name current_table;
+ bool ensm_pin_ctl_en;
+ bool auto_cal_en;
+ uint64_t last_tx_quad_cal_freq;
+ uint32_t last_tx_quad_cal_phase;
+ uint64_t current_tx_lo_freq;
+ uint64_t current_rx_lo_freq;
+ bool current_tx_use_tdd_table;
+ bool current_rx_use_tdd_table;
+ uint32_t flags;
+ uint32_t cal_threshold_freq;
+ uint32_t current_rx_bw_Hz;
+ uint32_t current_tx_bw_Hz;
+ uint32_t rxbbf_div;
+ uint32_t rate_governor;
+ bool bypass_rx_fir;
+ bool bypass_tx_fir;
+ bool rx_eq_2tx;
+ bool filt_valid;
+ uint32_t filt_rx_path_clks[NUM_RX_CLOCKS];
+ uint32_t filt_tx_path_clks[NUM_TX_CLOCKS];
+ uint32_t filt_rx_bw_Hz;
+ uint32_t filt_tx_bw_Hz;
+ uint8_t tx_fir_int;
+ uint8_t tx_fir_ntaps;
+ uint8_t rx_fir_dec;
+ uint8_t rx_fir_ntaps;
+ uint8_t agc_mode[2];
+ bool rfdc_track_en;
+ bool bbdc_track_en;
+ bool quad_track_en;
+ bool txmon_tdd_en;
+ uint16_t auxdac1_value;
+ uint16_t auxdac2_value;
+ uint32_t tx1_atten_cached;
+ uint32_t tx2_atten_cached;
+ struct ad9361_fastlock fastlock;
+ struct axiadc_converter *adc_conv;
+ struct axiadc_state *adc_state;
+ int32_t bist_loopback_mode;
+ enum ad9361_bist_mode bist_prbs_mode;
+ enum ad9361_bist_mode bist_tone_mode;
+ uint32_t bist_tone_freq_Hz;
+ uint32_t bist_tone_level_dB;
+ uint32_t bist_tone_mask;
+ bool bbpll_initialized;
+};
+
+struct rf_rx_gain {
+ uint32_t ant;
+ int32_t gain_db;
+ uint32_t fgt_lmt_index;
+ uint32_t lmt_gain;
+ uint32_t lpf_gain;
+ uint32_t digital_gain;
+ uint32_t lna_index;
+ uint32_t tia_index;
+ uint32_t mixer_index;
+};
+
+struct rf_rssi {
+ uint32_t ant;
+ uint32_t symbol;
+ uint32_t preamble;
+ int32_t multiplier;
+ uint8_t duration;
+};
+
+int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy,
+ uint32_t rx_id,
+ struct rf_rx_gain *rx_gain);
+int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg);
+int32_t ad9361_spi_write(struct spi_device *spi, uint32_t reg, uint32_t val);
+void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode);
+int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode);
+
+/******************************************************************************
+ * From ad9361_api.h
+ ******************************************************************************/
+
+typedef struct {
+ enum dev_id dev_sel;
+ uint8_t id_no;
+ uint32_t reference_clk_rate;
+ uint8_t two_rx_two_tx_mode_enable;
+ uint8_t one_rx_one_tx_mode_use_rx_num;
+ uint8_t one_rx_one_tx_mode_use_tx_num;
+ uint8_t frequency_division_duplex_mode_enable;
+ uint8_t frequency_division_duplex_independent_mode_enable;
+ uint8_t tdd_use_dual_synth_mode_enable;
+ uint8_t tdd_skip_vco_cal_enable;
+ uint32_t tx_fastlock_delay_ns;
+ uint32_t rx_fastlock_delay_ns;
+ uint8_t rx_fastlock_pincontrol_enable;
+ uint8_t tx_fastlock_pincontrol_enable;
+ uint8_t external_rx_lo_enable;
+ uint8_t external_tx_lo_enable;
+ uint8_t dc_offset_tracking_update_event_mask;
+ uint8_t dc_offset_attenuation_high_range;
+ uint8_t dc_offset_attenuation_low_range;
+ uint8_t dc_offset_count_high_range;
+ uint8_t dc_offset_count_low_range;
+ uint8_t split_gain_table_mode_enable;
+ uint32_t trx_synthesizer_target_fref_overwrite_hz;
+ uint8_t qec_tracking_slow_mode_enable;
+ uint8_t ensm_enable_pin_pulse_mode_enable;
+ uint8_t ensm_enable_txnrx_control_enable;
+ uint64_t rx_synthesizer_frequency_hz;
+ uint64_t tx_synthesizer_frequency_hz;
+ uint32_t rx_path_clock_frequencies[6];
+ uint32_t tx_path_clock_frequencies[6];
+ uint32_t rf_rx_bandwidth_hz;
+ uint32_t rf_tx_bandwidth_hz;
+ uint32_t rx_rf_port_input_select;
+ uint32_t tx_rf_port_input_select;
+ int32_t tx_attenuation_mdB;
+ uint8_t update_tx_gain_in_alert_enable;
+ uint8_t xo_disable_use_ext_refclk_enable;
+ uint32_t dcxo_coarse_and_fine_tune[2];
+ uint32_t clk_output_mode_select;
+ uint8_t gc_rx1_mode;
+ uint8_t gc_rx2_mode;
+ uint8_t gc_adc_large_overload_thresh;
+ uint8_t gc_adc_ovr_sample_size;
+ uint8_t gc_adc_small_overload_thresh;
+ uint16_t gc_dec_pow_measurement_duration;
+ uint8_t gc_dig_gain_enable;
+ uint16_t gc_lmt_overload_high_thresh;
+ uint16_t gc_lmt_overload_low_thresh;
+ uint8_t gc_low_power_thresh;
+ uint8_t gc_max_dig_gain;
+ uint8_t mgc_dec_gain_step;
+ uint8_t mgc_inc_gain_step;
+ uint8_t mgc_rx1_ctrl_inp_enable;
+ uint8_t mgc_rx2_ctrl_inp_enable;
+ uint8_t mgc_split_table_ctrl_inp_gain_mode;
+ uint8_t agc_adc_large_overload_exceed_counter;
+ uint8_t agc_adc_large_overload_inc_steps;
+ uint8_t agc_adc_lmt_small_overload_prevent_gain_inc_enable;
+ uint8_t agc_adc_small_overload_exceed_counter;
+ uint8_t agc_dig_gain_step_size;
+ uint8_t agc_dig_saturation_exceed_counter;
+ uint32_t agc_gain_update_interval_us;
+ uint8_t agc_immed_gain_change_if_large_adc_overload_enable;
+ uint8_t agc_immed_gain_change_if_large_lmt_overload_enable;
+ uint8_t agc_inner_thresh_high;
+ uint8_t agc_inner_thresh_high_dec_steps;
+ uint8_t agc_inner_thresh_low;
+ uint8_t agc_inner_thresh_low_inc_steps;
+ uint8_t agc_lmt_overload_large_exceed_counter;
+ uint8_t agc_lmt_overload_large_inc_steps;
+ uint8_t agc_lmt_overload_small_exceed_counter;
+ uint8_t agc_outer_thresh_high;
+ uint8_t agc_outer_thresh_high_dec_steps;
+ uint8_t agc_outer_thresh_low;
+ uint8_t agc_outer_thresh_low_inc_steps;
+ uint32_t agc_attack_delay_extra_margin_us;
+ uint8_t agc_sync_for_gain_counter_enable;
+ uint32_t fagc_dec_pow_measuremnt_duration;
+ uint32_t fagc_state_wait_time_ns;
+ uint8_t fagc_allow_agc_gain_increase;
+ uint32_t fagc_lp_thresh_increment_time;
+ uint32_t fagc_lp_thresh_increment_steps;
+ uint8_t fagc_lock_level_lmt_gain_increase_en;
+ uint32_t fagc_lock_level_gain_increase_upper_limit;
+ uint32_t fagc_lpf_final_settling_steps;
+ uint32_t fagc_lmt_final_settling_steps;
+ uint32_t fagc_final_overrange_count;
+ uint8_t fagc_gain_increase_after_gain_lock_en;
+ uint32_t fagc_gain_index_type_after_exit_rx_mode;
+ uint8_t fagc_use_last_lock_level_for_set_gain_en;
+ uint8_t fagc_rst_gla_stronger_sig_thresh_exceeded_en;
+ uint32_t fagc_optimized_gain_offset;
+ uint32_t fagc_rst_gla_stronger_sig_thresh_above_ll;
+ uint8_t fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en;
+ uint8_t fagc_rst_gla_engergy_lost_goto_optim_gain_en;
+ uint32_t fagc_rst_gla_engergy_lost_sig_thresh_below_ll;
+ uint32_t fagc_energy_lost_stronger_sig_gain_lock_exit_cnt;
+ uint8_t fagc_rst_gla_large_adc_overload_en;
+ uint8_t fagc_rst_gla_large_lmt_overload_en;
+ uint8_t fagc_rst_gla_en_agc_pulled_high_en;
+ uint32_t fagc_rst_gla_if_en_agc_pulled_high_mode;
+ uint32_t fagc_power_measurement_duration_in_state5;
+ uint32_t rssi_delay;
+ uint32_t rssi_duration;
+ uint8_t rssi_restart_mode;
+ uint8_t rssi_unit_is_rx_samples_enable;
+ uint32_t rssi_wait;
+ uint32_t aux_adc_decimation;
+ uint32_t aux_adc_rate;
+ uint8_t aux_dac_manual_mode_enable;
+ uint32_t aux_dac1_default_value_mV;
+ uint8_t aux_dac1_active_in_rx_enable;
+ uint8_t aux_dac1_active_in_tx_enable;
+ uint8_t aux_dac1_active_in_alert_enable;
+ uint32_t aux_dac1_rx_delay_us;
+ uint32_t aux_dac1_tx_delay_us;
+ uint32_t aux_dac2_default_value_mV;
+ uint8_t aux_dac2_active_in_rx_enable;
+ uint8_t aux_dac2_active_in_tx_enable;
+ uint8_t aux_dac2_active_in_alert_enable;
+ uint32_t aux_dac2_rx_delay_us;
+ uint32_t aux_dac2_tx_delay_us;
+ uint32_t temp_sense_decimation;
+ uint16_t temp_sense_measurement_interval_ms;
+ int8_t temp_sense_offset_signed;
+ uint8_t temp_sense_periodic_measurement_enable;
+ uint8_t ctrl_outs_enable_mask;
+ uint8_t ctrl_outs_index;
+ uint32_t elna_settling_delay_ns;
+ uint32_t elna_gain_mdB;
+ uint32_t elna_bypass_loss_mdB;
+ uint8_t elna_rx1_gpo0_control_enable;
+ uint8_t elna_rx2_gpo1_control_enable;
+ uint8_t elna_gaintable_all_index_enable;
+ uint8_t digital_interface_tune_skip_mode;
+ uint8_t digital_interface_tune_fir_disable;
+ uint8_t pp_tx_swap_enable;
+ uint8_t pp_rx_swap_enable;
+ uint8_t tx_channel_swap_enable;
+ uint8_t rx_channel_swap_enable;
+ uint8_t rx_frame_pulse_mode_enable;
+ uint8_t two_t_two_r_timing_enable;
+ uint8_t invert_data_bus_enable;
+ uint8_t invert_data_clk_enable;
+ uint8_t fdd_alt_word_order_enable;
+ uint8_t invert_rx_frame_enable;
+ uint8_t fdd_rx_rate_2tx_enable;
+ uint8_t swap_ports_enable;
+ uint8_t single_data_rate_enable;
+ uint8_t lvds_mode_enable;
+ uint8_t half_duplex_mode_enable;
+ uint8_t single_port_mode_enable;
+ uint8_t full_port_enable;
+ uint8_t full_duplex_swap_bits_enable;
+ uint32_t delay_rx_data;
+ uint32_t rx_data_clock_delay;
+ uint32_t rx_data_delay;
+ uint32_t tx_fb_clock_delay;
+ uint32_t tx_data_delay;
+ uint32_t lvds_bias_mV;
+ uint8_t lvds_rx_onchip_termination_enable;
+ uint8_t rx1rx2_phase_inversion_en;
+ uint8_t lvds_invert1_control;
+ uint8_t lvds_invert2_control;
+ uint8_t clk_out_drive;
+ uint8_t dataclk_drive;
+ uint8_t data_port_drive;
+ uint8_t clk_out_slew;
+ uint8_t dataclk_slew;
+ uint8_t data_port_slew;
+ uint8_t gpo0_inactive_state_high_enable;
+ uint8_t gpo1_inactive_state_high_enable;
+ uint8_t gpo2_inactive_state_high_enable;
+ uint8_t gpo3_inactive_state_high_enable;
+ uint8_t gpo0_slave_rx_enable;
+ uint8_t gpo0_slave_tx_enable;
+ uint8_t gpo1_slave_rx_enable;
+ uint8_t gpo1_slave_tx_enable;
+ uint8_t gpo2_slave_rx_enable;
+ uint8_t gpo2_slave_tx_enable;
+ uint8_t gpo3_slave_rx_enable;
+ uint8_t gpo3_slave_tx_enable;
+ uint8_t gpo0_rx_delay_us;
+ uint8_t gpo0_tx_delay_us;
+ uint8_t gpo1_rx_delay_us;
+ uint8_t gpo1_tx_delay_us;
+ uint8_t gpo2_rx_delay_us;
+ uint8_t gpo2_tx_delay_us;
+ uint8_t gpo3_rx_delay_us;
+ uint8_t gpo3_tx_delay_us;
+ uint32_t low_high_gain_threshold_mdB;
+ uint32_t low_gain_dB;
+ uint32_t high_gain_dB;
+ uint8_t tx_mon_track_en;
+ uint8_t one_shot_mode_en;
+ uint32_t tx_mon_delay;
+ uint32_t tx_mon_duration;
+ uint32_t tx1_mon_front_end_gain;
+ uint32_t tx2_mon_front_end_gain;
+ uint32_t tx1_mon_lo_cm;
+ uint32_t tx2_mon_lo_cm;
+ int32_t gpio_resetb;
+ int32_t gpio_sync;
+ int32_t gpio_cal_sw1;
+ int32_t gpio_cal_sw2;
+ uint32_t (*ad9361_rfpll_ext_recalc_rate)(struct refclk_scale *clk_priv);
+ int32_t (*ad9361_rfpll_ext_round_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+ int32_t (*ad9361_rfpll_ext_set_rate)(struct refclk_scale *clk_priv,
+ uint32_t rate);
+} AD9361_InitParam;
+
+typedef struct {
+ uint32_t rx; /* 1, 2, 3(both) */
+ int32_t rx_gain; /* -12, -6, 0, 6 */
+ uint32_t rx_dec; /* 1, 2, 4 */
+ int16_t rx_coef[128];
+ uint8_t rx_coef_size;
+ uint32_t rx_path_clks[6];
+ uint32_t rx_bandwidth;
+} AD9361_RXFIRConfig;
+
+typedef struct {
+ uint32_t tx; /* 1, 2, 3(both) */
+ int32_t tx_gain; /* -6, 0 */
+ uint32_t tx_int; /* 1, 2, 4 */
+ int16_t tx_coef[128];
+ uint8_t tx_coef_size;
+ uint32_t tx_path_clks[6];
+ uint32_t tx_bandwidth;
+} AD9361_TXFIRConfig;
+
+#define AD936X_A_BALANCED 0
+#define AD936X_B_BALANCED 1
+#define AD936X_C_BALANCED 2
+#define AD936X_A_N 3
+#define AD936X_A_P 4
+#define AD936X_B_N 5
+#define AD936X_B_P 6
+#define AD936X_C_N 7
+#define AD936X_C_P 8
+#define AD936X_TX_MON1 9
+#define AD936X_TX_MON2 10
+#define AD936X_TX_MON1_2 11
+
+#define AD936X_TXA 0
+#define AD936X_TXB 1
+
+int32_t ad9361_init(struct ad9361_rf_phy **ad9361_phy,
+ AD9361_InitParam *init_param,
+ void *userdata);
+int32_t ad9361_deinit(struct ad9361_rf_phy *phy);
+int32_t ad9361_set_rx_rf_gain(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ int32_t gain_db);
+int32_t ad9361_set_rx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz);
+int32_t ad9361_get_rx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz);
+int32_t ad9361_set_rx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz);
+int32_t ad9361_get_rx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz);
+int32_t ad9361_set_rx_lo_freq(struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+int32_t ad9361_get_rx_lo_freq(struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+int32_t ad9361_get_rx_rssi(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ struct rf_rssi *rssi);
+int32_t ad9361_set_rx_gain_control_mode(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint8_t gc_mode);
+int32_t ad9361_get_rx_gain_control_mode(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint8_t *gc_mode);
+int32_t ad9361_set_rx_fir_config(struct ad9361_rf_phy *phy,
+ AD9361_RXFIRConfig fir_cfg);
+int32_t ad9361_set_rx_fir_en_dis(struct ad9361_rf_phy *phy, uint8_t en_dis);
+int32_t ad9361_set_rx_rf_port_input(struct ad9361_rf_phy *phy, uint32_t mode);
+int32_t ad9361_get_rx_rf_port_input(struct ad9361_rf_phy *phy, uint32_t *mode);
+int32_t ad9361_set_tx_attenuation(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t attenuation_mdb);
+int32_t ad9361_get_tx_attenuation(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t *attenuation_mdb);
+int32_t ad9361_set_tx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t bandwidth_hz);
+int32_t ad9361_get_tx_rf_bandwidth(struct ad9361_rf_phy *phy,
+ uint32_t *bandwidth_hz);
+int32_t ad9361_set_tx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t sampling_freq_hz);
+int32_t ad9361_get_tx_sampling_freq(struct ad9361_rf_phy *phy,
+ uint32_t *sampling_freq_hz);
+int32_t ad9361_set_tx_lo_freq(struct ad9361_rf_phy *phy, uint64_t lo_freq_hz);
+int32_t ad9361_get_tx_lo_freq(struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz);
+int32_t ad9361_set_tx_fir_config(struct ad9361_rf_phy *phy,
+ AD9361_TXFIRConfig fir_cfg);
+int32_t ad9361_set_tx_fir_en_dis(struct ad9361_rf_phy *phy, uint8_t en_dis);
+int32_t ad9361_get_tx_rssi(struct ad9361_rf_phy *phy,
+ uint8_t ch,
+ uint32_t *rssi_db_x_1000);
+int32_t ad9361_set_tx_rf_port_output(struct ad9361_rf_phy *phy, uint32_t mode);
+int32_t ad9361_get_tx_rf_port_output(struct ad9361_rf_phy *phy, uint32_t *mode);
+int32_t ad9361_get_temp(struct ad9361_rf_phy *phy);
+int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy,
+ uint32_t profile,
+ uint8_t *values);
+int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile);
+int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy,
+ uint32_t profile,
+ uint8_t *values);
+int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode);
+
+#endif // AD936X_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h b/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h
new file mode 100644
index 0000000..bd0037a
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/ad936x_helpers.h
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef FPGA_COMMON_AD936X_HELPERS_H_
+#define FPGA_COMMON_AD936X_HELPERS_H_
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include <libbladeRF.h>
+#else
+#include "libbladeRF_nios_compat.h"
+#endif
+
+#include "ad936x.h"
+
+/**
+ * @brief Retrieve current value in TX attenuation cache.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ *
+ * @return Cached attenuation value
+ */
+uint32_t txmute_get_cached(struct ad9361_rf_phy *phy, bladerf_channel ch);
+
+/**
+ * @brief Save a new value to the TX attenuation cache.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] atten Attenuation
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_set_cached(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ uint32_t atten);
+
+/**
+ * @brief Get the transmit mute state
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[out] state Mute state: true for muted, false for unmuted
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_get(struct ad9361_rf_phy *phy, bladerf_channel ch, bool *state);
+
+/**
+ * @brief Sets the transmit mute.
+ *
+ * If muted, the TX attenuation will be set to maximum to reduce leakage
+ * as much as possible.
+ *
+ * When unmuted, TX attenuation will be restored to its previous value.
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] state Mute state: true for muted, false for unmuted
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int txmute_set(struct ad9361_rf_phy *phy, bladerf_channel ch, bool state);
+
+/**
+ * @brief Set AD9361 RFIC RF port
+ *
+ * @param phy RFIC handle
+ * @param[in] ch Channel
+ * @param[in] enabled True if the channel is enabled, false otherwise
+ * @param[in] freq Frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int set_ad9361_port_by_freq(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq);
+
+/**
+ * @brief Translate bladerf_gain_mode to rf_gain_ctrl_mode
+ *
+ * @param[in] gainmode The libbladeRF gainmode
+ * @param[out] ok True if return value is valid, false otherwise
+ *
+ * @return rf_gain_ctrl_mode
+ */
+enum rf_gain_ctrl_mode gainmode_bladerf_to_ad9361(bladerf_gain_mode gainmode,
+ bool *ok);
+
+/**
+ * @brief Translate rf_gain_ctrl_mode to bladerf_gain_mode
+ *
+ * @param[in] gainmode The RFIC gainmode
+ * @param[out] ok True if return value is valid, false otherwise
+ *
+ * @return bladerf_gain_mode
+ */
+bladerf_gain_mode gainmode_ad9361_to_bladerf(enum rf_gain_ctrl_mode gainmode,
+ bool *ok);
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+#endif // FPGA_COMMON_AD936X_HELPERS_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/band_select.h b/Radio/HW/BladeRF/fpga_common/include/band_select.h
new file mode 100644
index 0000000..bae469d
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/band_select.h
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BAND_SELECT_H_
+#define BAND_SELECT_H_
+
+#include <stdbool.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+# include <libbladeRF.h>
+# include "board/board.h"
+# include "log.h"
+#else
+# include "libbladeRF_nios_compat.h"
+# include "devices.h"
+#endif
+
+/**
+ * Select the bladeRF's low or high band
+ *
+ * @param dev Device handle
+ * @param module Module to configure
+ * @param low_band Configure for low band (true) or high band (false)
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int band_select(struct bladerf *dev, bladerf_module module, bool low_band);
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h b/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h
new file mode 100644
index 0000000..f58e044
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/bladerf2_common.h
@@ -0,0 +1,749 @@
+/* This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef FPGA_COMMON_BLADERF2_COMMON_H_
+#define FPGA_COMMON_BLADERF2_COMMON_H_
+
+#include <errno.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include <libbladeRF.h>
+#else
+#include "libbladeRF_nios_compat.h"
+#endif // !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+
+#include "ad936x.h"
+#include "host_config.h"
+#include "nios_pkt_retune2.h"
+#include "range.h"
+
+/**
+ * Number of modules (directions) present. 1 RX, 1 TX = 2.
+ */
+#define NUM_MODULES 2
+
+/**
+ * Frequency of the system VCTCXO, in Hz.
+ */
+static bladerf_frequency const BLADERF_VCTCXO_FREQUENCY = 38400000;
+
+/**
+ * Default reference input frequency, in Hz.
+ */
+static bladerf_frequency const BLADERF_REFIN_DEFAULT = 10000000;
+
+/**
+ * RFIC reset frequency (arbitrary)
+ */
+static bladerf_frequency const RESET_FREQUENCY = 70000000;
+
+// clang-format off
+// Config GPIO
+#define CFG_GPIO_POWERSOURCE 0
+#define CFG_GPIO_PLL_EN 11
+#define CFG_GPIO_CLOCK_OUTPUT 17
+#define CFG_GPIO_CLOCK_SELECT 18
+
+// RFFE control
+#define RFFE_CONTROL_RESET_N 0
+#define RFFE_CONTROL_ENABLE 1
+#define RFFE_CONTROL_TXNRX 2
+#define RFFE_CONTROL_EN_AGC 3
+#define RFFE_CONTROL_SYNC_IN 4
+#define RFFE_CONTROL_RX_BIAS_EN 5
+#define RFFE_CONTROL_RX_SPDT_1 6 // 6 and 7
+#define RFFE_CONTROL_RX_SPDT_2 8 // 8 and 9
+#define RFFE_CONTROL_TX_BIAS_EN 10
+#define RFFE_CONTROL_TX_SPDT_1 11 // 11 and 12
+#define RFFE_CONTROL_TX_SPDT_2 13 // 13 and 14
+#define RFFE_CONTROL_MIMO_RX_EN_0 15
+#define RFFE_CONTROL_MIMO_TX_EN_0 16
+#define RFFE_CONTROL_MIMO_RX_EN_1 17
+#define RFFE_CONTROL_MIMO_TX_EN_1 18
+#define RFFE_CONTROL_ADF_MUXOUT 19 // input only
+#define RFFE_CONTROL_CTRL_OUT 24 // input only, 24 through 31
+#define RFFE_CONTROL_SPDT_MASK 0x3
+#define RFFE_CONTROL_SPDT_SHUTDOWN 0x0 // no connection
+#define RFFE_CONTROL_SPDT_LOWBAND 0x2 // RF1 <-> RF3
+#define RFFE_CONTROL_SPDT_HIGHBAND 0x1 // RF1 <-> RF2
+
+// Trim DAC control
+#define TRIMDAC_MASK 0x3FFC // 2 through 13
+#define TRIMDAC_EN 14 // 14 and 15
+#define TRIMDAC_EN_MASK 0x3
+#define TRIMDAC_EN_ACTIVE 0x0
+#define TRIMDAC_EN_HIGHZ 0x3
+
+/* Number of fast lock profiles that can be stored in the Nios
+ * Make sure this number matches that of the Nios' devices.h */
+#define NUM_BBP_FASTLOCK_PROFILES 256
+
+/* Number of fast lock profiles that can be stored in the RFFE
+ * Make sure this number matches that of the Nios' devices.h */
+#define NUM_RFFE_FASTLOCK_PROFILES 8
+// clang-format on
+
+/**
+ * RF front end bands
+ */
+enum bladerf2_band {
+ BAND_SHUTDOWN, /**< Inactive */
+ BAND_LOW, /**< Low-band */
+ BAND_HIGH, /**< High-band */
+};
+
+/**
+ * Mapping between libbladeRF gain modes and RFIC gain modes.
+ */
+struct bladerf_rfic_gain_mode_map {
+ bladerf_gain_mode brf_mode; /**< libbladeRF gain mode */
+ enum rf_gain_ctrl_mode rfic_mode; /**< RFIC gain mode */
+};
+
+/**
+ * Mapping between frequency ranges and gain ranges.
+ */
+struct bladerf_gain_range {
+ char const *name; /**< Gain stage name */
+ struct bladerf_range frequency; /**< Frequency range */
+ struct bladerf_range gain; /**< Applicable stage gain range */
+ float offset; /**< Offset in dB, for mapping dB gain to absolute dBm. */
+};
+
+/**
+ * Mapping between string names and RFIC port identifiers.
+ */
+struct bladerf_rfic_port_name_map {
+ char const *name; /**< Port name */
+ uint32_t id; /**< Port ID */
+};
+
+/**
+ * Mapping between RF front end bands, freqencies, and physical hardware
+ * configurations
+ */
+struct band_port_map {
+ struct bladerf_range const frequency; /**< Frequency range */
+ enum bladerf2_band band; /**< RF front end band */
+ uint32_t spdt; /**< RF switch configuration */
+ uint32_t rfic_port; /**< RFIC port configuration */
+};
+
+/**
+ * @brief Round a value into an int
+ *
+ * @param x Value to round
+ *
+ * @return int
+ */
+#define __round_int(x) (x >= 0 ? (int)(x + 0.5) : (int)(x - 0.5))
+
+/**
+ * @brief Round a value into an int64
+ *
+ * @param x Value to round
+ *
+ * @return int64
+ */
+#define __round_int64(x) (x >= 0 ? (int64_t)(x + 0.5) : (int64_t)(x - 0.5))
+
+/**
+ * Subcommands for the BLADERF_RFIC_COMMAND_INIT RFIC command.
+ */
+typedef enum {
+ BLADERF_RFIC_INIT_STATE_OFF = 0, /** Non-initialized state */
+ BLADERF_RFIC_INIT_STATE_ON, /** Initialized ("open") */
+ BLADERF_RFIC_INIT_STATE_STANDBY, /** Standby ("closed") */
+} bladerf_rfic_init_state;
+
+/**
+ * Commands available with the FPGA-based RFIC interface.
+ *
+ * There is an 8-bit address space (0x00 to 0xFF) available. Nuand will not
+ * assign values between 0x80 and 0xFF, so they may be used for custom
+ * applications.
+ */
+typedef enum {
+ /** Query the status register. (Read)
+ *
+ * Pass ::BLADERF_CHANNEL_INVALID as the `ch` parameter.
+ *
+ * Return structure:
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 63:16 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | 15:8 | count of items in |
+ * | | write queue |
+ * +----------------+------------------------+
+ * | 0 | 1 if initialized, 0 |
+ * | | otherwise |
+ * +----------------+------------------------+
+ */
+ BLADERF_RFIC_COMMAND_STATUS = 0x00,
+
+ /** Initialize the RFIC. (Read/Write)
+ *
+ * Pass ::BLADERF_CHANNEL_INVALID as the `ch` parameter.
+ *
+ * Pass/expect a ::bladerf_rfic_init_state value as the `data` parameter.
+ */
+ BLADERF_RFIC_COMMAND_INIT = 0x01,
+
+ /** Enable/disable a channel. (Read/Write)
+ *
+ * Set `data` to `true` to enable the channel, or `false` to disable it.
+ */
+ BLADERF_RFIC_COMMAND_ENABLE = 0x02,
+
+ /** Sample rate for a channel. (Read/Write)
+ *
+ * Value in samples per second.
+ */
+ BLADERF_RFIC_COMMAND_SAMPLERATE = 0x03,
+
+ /** Center frequency for a channel. (Read/Write)
+ *
+ * Value in Hz. Read or write.
+ */
+ BLADERF_RFIC_COMMAND_FREQUENCY = 0x04,
+
+ /** Bandwidth for a channel. (Read/Write)
+ *
+ * Value in Hz.
+ */
+ BLADERF_RFIC_COMMAND_BANDWIDTH = 0x05,
+
+ /** Gain mode for a channel. (Read/Write)
+ *
+ * Pass a ::bladerf_gain_mode value as the `data` parameter.
+ */
+ BLADERF_RFIC_COMMAND_GAINMODE = 0x06,
+
+ /** Overall gain for a channel. (Read/Write)
+ *
+ * Value in dB.
+ */
+ BLADERF_RFIC_COMMAND_GAIN = 0x07,
+
+ /** RSSI (received signal strength indication) for a channel. (Read)
+ *
+ * Value in dB.
+ */
+ BLADERF_RFIC_COMMAND_RSSI = 0x08,
+
+ /** FIR filter setting for a channel. (Read/Write)
+ *
+ * RX channels should pass a ::bladerf_rfic_rxfir value, TX channels should
+ * pass a ::bladerf_rfic_txfir value.
+ */
+ BLADERF_RFIC_COMMAND_FILTER = 0x09,
+
+ /** TX Mute setting for a channel. (Read/Write)
+ *
+ * 1 indicates TX mute is enabled, 0 indicates it is not.
+ */
+ BLADERF_RFIC_COMMAND_TXMUTE = 0x0A,
+
+ /** Store Fastlock profile. (Write)
+ *
+ * Stores the current tuning into a fastlock profile, for later recall
+ */
+ BLADERF_RFIC_COMMAND_FASTLOCK = 0x0B,
+
+ /** User-defined functionality (placeholder 1) */
+ BLADERF_RFIC_COMMAND_USER_001 = 0x80,
+
+ /** User-defined functionality (placeholder 128) */
+ BLADERF_RFIC_COMMAND_USER_128 = 0xFF,
+
+ /* Do not add additional commands beyond 0xFF */
+} bladerf_rfic_command;
+
+/** NIOS_PKT_16x64_RFIC_STATUS return structure
+ *
+ * +===============+===================================================+
+ * | Bit(s) | Value |
+ * +===============+===================================================+
+ * | 63:16 | Reserved. Set to 0. |
+ * +---------------+---------------------------------------------------+
+ * | 15:8 | count of items in write queue |
+ * +---------------+---------------------------------------------------+
+ * | 1 | 1 if the last job executed in the write queue was |
+ * | | successful, 0 otherwise |
+ * +---------------+---------------------------------------------------+
+ * | 0 | 1 if initialized, 0 otherwise |
+ * +---------------+---------------------------------------------------+
+ */
+// clang-format off
+#define BLADERF_RFIC_STATUS_INIT_SHIFT 0
+#define BLADERF_RFIC_STATUS_INIT_MASK 0x1
+#define BLADERF_RFIC_STATUS_WQSUCCESS_SHIFT 1
+#define BLADERF_RFIC_STATUS_WQSUCCESS_MASK 0x1
+#define BLADERF_RFIC_STATUS_WQLEN_SHIFT 8
+#define BLADERF_RFIC_STATUS_WQLEN_MASK 0xff
+
+#define BLADERF_RFIC_RSSI_MULT_SHIFT 32
+#define BLADERF_RFIC_RSSI_MULT_MASK 0xFFFF
+#define BLADERF_RFIC_RSSI_PRE_SHIFT 16
+#define BLADERF_RFIC_RSSI_PRE_MASK 0xFFFF
+#define BLADERF_RFIC_RSSI_SYM_SHIFT 0
+#define BLADERF_RFIC_RSSI_SYM_MASK 0xFFFF
+// clang-format on
+
+
+/******************************************************************************/
+/* Constants */
+/******************************************************************************/
+// clang-format off
+
+/* Gain mode mappings */
+static struct bladerf_rfic_gain_mode_map const bladerf2_rx_gain_mode_map[] = {
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_MGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_MGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_FASTATTACK_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_FASTATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_SLOWATTACK_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_SLOWATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.brf_mode, BLADERF_GAIN_HYBRID_AGC),
+ FIELD_INIT(.rfic_mode, RF_GAIN_HYBRID_AGC)
+ },
+};
+
+/* RX gain ranges */
+/* Reference: ad9361.c, ad9361_gt_tableindex and ad9361_init_gain_tables */
+static struct bladerf_gain_range const bladerf2_rx_gain_ranges[] = {
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 1300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, 1 - 17),
+ FIELD_INIT(.max, 77 - 17),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -17.0f),
+ },
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 1300000000UL),
+ FIELD_INIT(.max, 4000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -4 - 11),
+ FIELD_INIT(.max, 71 - 11),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -11.0f),
+ },
+ {
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 4000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -10 - 2),
+ FIELD_INIT(.max, 62 - 2),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, -2.0f),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 1300000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, 1),
+ FIELD_INIT(.max, 77),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 1300000000UL),
+ FIELD_INIT(.max, 4000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -4),
+ FIELD_INIT(.max, 71),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+ {
+ FIELD_INIT(.name, "full"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 4000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -10),
+ FIELD_INIT(.max, 62),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+};
+
+/* Overall TX gain range */
+static struct bladerf_gain_range const bladerf2_tx_gain_ranges[] = {
+ {
+ /* TX gain offset: 60 dB system gain ~= 0 dBm output */
+ FIELD_INIT(.name, NULL),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 47000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, __round_int64(1000*(-89.750 + 66.0))),
+ FIELD_INIT(.max, __round_int64(1000*(0 + 66.0))),
+ FIELD_INIT(.step, 250),
+ FIELD_INIT(.scale, 0.001F),
+ }),
+ FIELD_INIT(.offset, 66.0f),
+ },
+ {
+ FIELD_INIT(.name, "dsa"),
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 47000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.gain, {
+ FIELD_INIT(.min, -89750),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 250),
+ FIELD_INIT(.scale, 0.001F),
+ }),
+ FIELD_INIT(.offset, 0),
+ },
+};
+
+/* RX gain modes */
+static struct bladerf_gain_modes const bladerf2_rx_gain_modes[] = {
+ {
+ FIELD_INIT(.name, "automatic"),
+ FIELD_INIT(.mode, BLADERF_GAIN_DEFAULT)
+ },
+ {
+ FIELD_INIT(.name, "manual"),
+ FIELD_INIT(.mode, BLADERF_GAIN_MGC)
+ },
+ {
+ FIELD_INIT(.name, "fast"),
+ FIELD_INIT(.mode, BLADERF_GAIN_FASTATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.name, "slow"),
+ FIELD_INIT(.mode, BLADERF_GAIN_SLOWATTACK_AGC)
+ },
+ {
+ FIELD_INIT(.name, "hybrid"),
+ FIELD_INIT(.mode, BLADERF_GAIN_HYBRID_AGC)
+ }
+};
+
+/* Default RX gain control modes */
+static enum rf_gain_ctrl_mode const bladerf2_rx_gain_mode_default[2] = {
+ RF_GAIN_SLOWATTACK_AGC, RF_GAIN_SLOWATTACK_AGC
+};
+
+/* Sample Rate Range */
+static struct bladerf_range const bladerf2_sample_rate_range = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 122880000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample Rate Base Range */
+static struct bladerf_range const bladerf2_sample_rate_range_base = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 61440000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample Rate Oversample Range */
+static struct bladerf_range const bladerf2_sample_rate_range_oversample = {
+ FIELD_INIT(.min, 6250000),
+ FIELD_INIT(.max, 122880000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Sample rates requiring a 4x interpolation/decimation */
+static struct bladerf_range const bladerf2_sample_rate_range_4x = {
+ FIELD_INIT(.min, 520834),
+ FIELD_INIT(.max, 2083334),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Bandwidth Range */
+static struct bladerf_range const bladerf2_bandwidth_range = {
+ FIELD_INIT(.min, 200000),
+ FIELD_INIT(.max, 56000000),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+};
+
+/* Frequency Ranges */
+static struct bladerf_range const bladerf2_rx_frequency_range = {
+ FIELD_INIT(.min, 70000000),
+ FIELD_INIT(.max, 6000000000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+static struct bladerf_range const bladerf2_tx_frequency_range = {
+ FIELD_INIT(.min, 47000000),
+ FIELD_INIT(.max, 6000000000),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+};
+
+
+/* RF Ports */
+static struct bladerf_rfic_port_name_map const bladerf2_rx_port_map[] = {
+ { FIELD_INIT(.name, "A_BALANCED"), FIELD_INIT(.id, AD936X_A_BALANCED), },
+ { FIELD_INIT(.name, "B_BALANCED"), FIELD_INIT(.id, AD936X_B_BALANCED), },
+ { FIELD_INIT(.name, "C_BALANCED"), FIELD_INIT(.id, AD936X_C_BALANCED), },
+ { FIELD_INIT(.name, "A_N"), FIELD_INIT(.id, AD936X_A_N), },
+ { FIELD_INIT(.name, "A_P"), FIELD_INIT(.id, AD936X_A_P), },
+ { FIELD_INIT(.name, "B_N"), FIELD_INIT(.id, AD936X_B_N), },
+ { FIELD_INIT(.name, "B_P"), FIELD_INIT(.id, AD936X_B_P), },
+ { FIELD_INIT(.name, "C_N"), FIELD_INIT(.id, AD936X_C_N), },
+ { FIELD_INIT(.name, "C_P"), FIELD_INIT(.id, AD936X_C_P), },
+ { FIELD_INIT(.name, "TX_MON1"), FIELD_INIT(.id, AD936X_TX_MON1), },
+ { FIELD_INIT(.name, "TX_MON2"), FIELD_INIT(.id, AD936X_TX_MON2), },
+ { FIELD_INIT(.name, "TX_MON1_2"), FIELD_INIT(.id, AD936X_TX_MON1_2), },
+};
+
+static struct bladerf_rfic_port_name_map const bladerf2_tx_port_map[] = {
+ { FIELD_INIT(.name, "TXA"), FIELD_INIT(.id, AD936X_TXA), },
+ { FIELD_INIT(.name, "TXB"), FIELD_INIT(.id, AD936X_TXB), },
+};
+
+static struct band_port_map const bladerf2_rx_band_port_map[] = {
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_SHUTDOWN),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_SHUTDOWN),
+ FIELD_INIT(.rfic_port, 0),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 70000000UL),
+ FIELD_INIT(.max, 3000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_LOW),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_LOWBAND),
+ FIELD_INIT(.rfic_port, AD936X_B_BALANCED),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 3000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_HIGH),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_HIGHBAND),
+ FIELD_INIT(.rfic_port, AD936X_A_BALANCED),
+ },
+};
+
+static struct band_port_map const bladerf2_tx_band_port_map[] = {
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 0),
+ FIELD_INIT(.max, 0),
+ FIELD_INIT(.step, 1),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_SHUTDOWN),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_SHUTDOWN),
+ FIELD_INIT(.rfic_port, 0),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 46875000UL),
+ FIELD_INIT(.max, 3000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_LOW),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_LOWBAND),
+ FIELD_INIT(.rfic_port, AD936X_TXB),
+ },
+ {
+ FIELD_INIT(.frequency, {
+ FIELD_INIT(.min, 3000000000UL),
+ FIELD_INIT(.max, 6000000000UL),
+ FIELD_INIT(.step, 2),
+ FIELD_INIT(.scale, 1),
+ }),
+ FIELD_INIT(.band, BAND_HIGH),
+ FIELD_INIT(.spdt, RFFE_CONTROL_SPDT_HIGHBAND),
+ FIELD_INIT(.rfic_port, AD936X_TXA),
+ },
+};
+
+// clang-format on
+
+
+/******************************************************************************/
+/* Helpers */
+/******************************************************************************/
+
+/**
+ * @brief Translate libad936x error codes to libbladeRF error codes
+ *
+ * @param[in] err The error
+ *
+ * @return value from \ref RETCODES list, or 0 if err is >= 0
+ */
+int errno_ad9361_to_bladerf(int err);
+
+/**
+ * @brief Gets the band port map by frequency.
+ *
+ * @param[in] ch Channel
+ * @param[in] freq Frequency. Use 0 for the "disabled" state.
+ *
+ * @return pointer to band_port_map
+ */
+struct band_port_map const *_get_band_port_map_by_freq(bladerf_channel ch,
+ bladerf_frequency freq);
+
+/**
+ * @brief Modifies reg to configure the RF switch SPDT bits
+ *
+ * @param reg RFFE control register ptr
+ * @param[in] ch Channel
+ * @param[in] enabled True if the channel is enabled, False otherwise
+ * @param[in] freq Frequency
+ *
+ * @return 0 on success, value from \ref RETCODES list on failure
+ */
+int _modify_spdt_bits_by_freq(uint32_t *reg,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq);
+
+/**
+ * @brief Look up the RFFE control register bit for a bladerf_direction
+ *
+ * @param[in] dir Direction
+ *
+ * @return Bit index
+ */
+int _get_rffe_control_bit_for_dir(bladerf_direction dir);
+
+/**
+ * @brief Look up the RFFE control register bit for a bladerf_channel
+ *
+ * @param[in] ch Channel
+ *
+ * @return Bit index
+ */
+int _get_rffe_control_bit_for_ch(bladerf_channel ch);
+
+/**
+ * @brief Determine if a channel is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] ch Channel
+ *
+ * @return true if active, false otherwise
+ */
+bool _rffe_ch_enabled(uint32_t reg, bladerf_channel ch);
+
+/**
+ * @brief Determine if any channel in a direction is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] dir Direction
+ *
+ * @return true if any channel is active, false otherwise
+ */
+bool _rffe_dir_enabled(uint32_t reg, bladerf_direction dir);
+
+/**
+ * @brief Determine if any *other* channel in a direction is active
+ *
+ * @param[in] reg RFFE control register
+ * @param[in] ch Channel
+ *
+ * @return true if any channel in the same direction as ch is active, false
+ * otherwise
+ */
+bool _rffe_dir_otherwise_enabled(uint32_t reg, bladerf_channel ch);
+
+#endif // FPGA_COMMON_BLADERF2_COMMON_H_
diff --git a/Radio/HW/BladeRF/fpga_common/include/lms.h b/Radio/HW/BladeRF/fpga_common/include/lms.h
new file mode 100644
index 0000000..9f67536
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/lms.h
@@ -0,0 +1,838 @@
+/**
+ * @file lms.h
+ *
+ * @brief LMS6002D support
+ *
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef LMS_H_
+#define LMS_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+# include <libbladeRF.h>
+# include "board/board.h"
+# define LMS_WRITE(dev, addr, value) dev->backend->lms_write(dev, addr, value)
+# define LMS_READ(dev, addr, value) dev->backend->lms_read(dev, addr, value)
+#else
+# include "libbladeRF_nios_compat.h"
+# include "devices.h"
+#endif
+
+/*
+ * lms_freq.flags values
+ */
+
+/**
+ * If this bit is set, configure PLL output buffers for operation in the
+ * bladeRF's "low band." Otherwise, configure the device for operation in the
+ * "high band."
+ */
+#define LMS_FREQ_FLAGS_LOW_BAND (1 << 0)
+
+/**
+ * Use VCOCAP value as-is, rather as using it as a starting point hint
+ * to the tuning algorithm. This offers a faster retune, with a potential
+ * trade-off in phase noise.
+ */
+#define LMS_FREQ_FLAGS_FORCE_VCOCAP (1 << 1)
+
+/**
+ * This bit indicates whether the quicktune needs to set XB-200 parameters
+ */
+#define LMS_FREQ_XB_200_ENABLE (1 << 7)
+
+/*
+ * This bit indicates the quicktune is for the RX module, not setting this bit
+ * indicates the quicktune is for the TX module.
+ */
+#define LMS_FREQ_XB_200_MODULE_RX (1 << 6)
+
+/**
+ * This is the bit mask for the filter switch configuration for the XB-200.
+ */
+#define LMS_FREQ_XB_200_FILTER_SW (3 << 4)
+
+/**
+ * Macro that indicates the number of bitshifts necessary to get to the filter
+ * switch field
+ */
+#define LMS_FREQ_XB_200_FILTER_SW_SHIFT (4)
+
+/**
+ * This is the bit mask for the path configuration for the XB-200.
+ */
+#define LMS_FREQ_XB_200_PATH (3 << 2)
+
+/**
+ * Macro that indicates the number of bitshifts necessary to get to the path
+ * field
+ */
+#define LMS_FREQ_XB_200_PATH_SHIFT (2)
+
+/**
+ * Information about the frequency calculation for the LMS6002D PLL
+ * Calculation taken from the LMS6002D Programming and Calibration Guide
+ * version 1.1r1.
+ */
+struct lms_freq {
+ uint8_t freqsel; /**< Choice of VCO and dision ratio */
+ uint8_t vcocap; /**< VCOCAP hint */
+ uint16_t nint; /**< Integer portion of f_LO given f_REF */
+ uint32_t nfrac; /**< Fractional portion of f_LO given nint and f_REF */
+ uint8_t flags; /**< Additional parameters defining the tuning
+ configuration. See LMFS_FREQ_FLAGS_* values */
+ uint8_t xb_gpio; /**< Store XB-200 switch settings */
+
+#ifndef BLADERF_NIOS_BUILD
+ uint8_t x; /**< VCO division ratio */
+#endif
+
+ uint8_t vcocap_result; /**< Filled in by retune operation to denote
+ which VCOCAP value was used */
+};
+
+/* For >= 1.5 GHz uses the high band should be used. Otherwise, the low
+ * band should be selected */
+#define BLADERF1_BAND_HIGH 1500000000
+
+/**
+ * Internal low-pass filter bandwidth selection
+ */
+typedef enum {
+ BW_28MHz, /**< 28MHz bandwidth, 14MHz LPF */
+ BW_20MHz, /**< 20MHz bandwidth, 10MHz LPF */
+ BW_14MHz, /**< 14MHz bandwidth, 7MHz LPF */
+ BW_12MHz, /**< 12MHz bandwidth, 6MHz LPF */
+ BW_10MHz, /**< 10MHz bandwidth, 5MHz LPF */
+ BW_8p75MHz, /**< 8.75MHz bandwidth, 4.375MHz LPF */
+ BW_7MHz, /**< 7MHz bandwidth, 3.5MHz LPF */
+ BW_6MHz, /**< 6MHz bandwidth, 3MHz LPF */
+ BW_5p5MHz, /**< 5.5MHz bandwidth, 2.75MHz LPF */
+ BW_5MHz, /**< 5MHz bandwidth, 2.5MHz LPF */
+ BW_3p84MHz, /**< 3.84MHz bandwidth, 1.92MHz LPF */
+ BW_3MHz, /**< 3MHz bandwidth, 1.5MHz LPF */
+ BW_2p75MHz, /**< 2.75MHz bandwidth, 1.375MHz LPF */
+ BW_2p5MHz, /**< 2.5MHz bandwidth, 1.25MHz LPF */
+ BW_1p75MHz, /**< 1.75MHz bandwidth, 0.875MHz LPF */
+ BW_1p5MHz, /**< 1.5MHz bandwidth, 0.75MHz LPF */
+} lms_bw;
+
+
+/**
+ * LNA options
+ */
+typedef enum {
+ LNA_NONE, /**< Disable all LNAs */
+ LNA_1, /**< Enable LNA1 (300MHz - 2.8GHz) */
+ LNA_2, /**< Enable LNA2 (1.5GHz - 3.8GHz) */
+ LNA_3 /**< Enable LNA3 (Unused on the bladeRF) */
+} lms_lna;
+
+
+/**
+ * Loopback paths
+ */
+typedef enum {
+ LBP_BB, /**< Baseband loopback path */
+ LBP_RF /**< RF Loopback path */
+} lms_lbp;
+
+/**
+ * PA Selection
+ */
+typedef enum {
+ PA_AUX, /**< AUX PA Enable (for RF Loopback) */
+ PA_1, /**< PA1 Enable (300MHz - 2.8GHz) */
+ PA_2, /**< PA2 Enable (1.5GHz - 3.8GHz) */
+ PA_NONE, /**< All PAs disabled */
+} lms_pa;
+
+/**
+ * LMS6002D Transceiver configuration
+ */
+struct lms_xcvr_config {
+ uint32_t tx_freq_hz; /**< Transmit frequency in Hz */
+ uint32_t rx_freq_hz; /**< Receive frequency in Hz */
+ bladerf_loopback loopback_mode; /**< Loopback Mode */
+ lms_lna lna; /**< LNA Selection */
+ lms_pa pa; /**< PA Selection */
+ lms_bw tx_bw; /**< Transmit Bandwidth */
+ lms_bw rx_bw; /**< Receive Bandwidth */
+};
+
+/**
+ * Convert an integer to a bandwidth selection.
+ * If the actual bandwidth is not available, the closest
+ * bandwidth greater than the requested bandwidth is selected.
+ * If the provided value is greater than the maximum available bandwidth, the
+ * maximum available bandiwidth is returned.
+ *
+ * @param[in] req Requested bandwidth
+ *
+ * @return closest bandwidth
+ */
+lms_bw lms_uint2bw(unsigned int req);
+
+/**
+ * Convert a bandwidth seletion to an unsigned int.
+ *
+ * @param[in] bw Bandwidth enumeration
+ *
+ * @return bandwidth as an unsigned integer
+ */
+unsigned int lms_bw2uint(lms_bw bw);
+
+/**
+ * Wrapper for setting bits in an LMS6002 register via a RMW operation
+ *
+ * @param dev Device to operate on
+ * @param addr Register address
+ * @param mask Bits to set should be '1'
+ *
+ * @return BLADERF_ERR_* value
+ */
+static inline int lms_set(struct bladerf *dev, uint8_t addr, uint8_t mask)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval |= mask;
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+/*
+ * Wrapper for clearing bits in an LMS6002 register via a RMW operation
+ *
+ * @param dev Device to operate on
+ * @param addr Register address
+ * @param mask Bits to clear should be '1'
+ *
+ * @return BLADERF_ERR_* value
+ */
+static inline int lms_clear(struct bladerf *dev, uint8_t addr, uint8_t mask)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~mask;
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+/**
+ * Configure charge pump offset currents
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ */
+int lms_config_charge_pumps(struct bladerf *dev, bladerf_module module);
+
+/**
+ * Enable or disable the low-pass filter on the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_enable(struct bladerf *dev, bladerf_module mod, bool enable);
+
+/**
+ * Set the LPF mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] mode Mode to set to
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_set_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode mode);
+
+/**
+ * Get the LPF mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[out] mode Current LPF mode
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lpf_get_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode *mode);
+
+/**
+ * Set the bandwidth for the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to set bandwidth for
+ * @param[in] bw Desired bandwidth
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw bw);
+
+/**
+ * Get the bandwidth for the specified module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to read
+ * @param[out] bw Current bandwidth
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw *bw);
+
+/**
+ * Enable dithering on PLL in the module to help reduce any fractional spurs
+ * which might be occurring.
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] nbits Number of bits to dither (1 to 8). Ignored when
+ * disabling dithering.
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_dither_enable(struct bladerf *dev, bladerf_module mod,
+ uint8_t nbits, bool enable);
+
+/**
+ * Perform a soft reset of the LMS6002D device
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_soft_reset(struct bladerf *dev);
+
+/**
+ * Set the gain of the LNA
+ *
+ * The LNA gain can be one of three values: bypassed (0dB gain),
+ * mid (MAX-6dB) and max.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Bypass, mid or max gain
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lna_set_gain(struct bladerf *dev, bladerf_lna_gain gain);
+
+/**
+ * Get the gain of the LNA
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Bypass, mid or max gain
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_lna_get_gain(struct bladerf *dev, bladerf_lna_gain *gain);
+
+/**
+ * Select which LNA to enable
+ *
+ * LNA1 frequency range is from 300MHz to 2.8GHz
+ * LNA2 frequency range is from 1.5GHz to 3.8GHz
+ * LNA3 frequency range is from 300MHz to 3.0GHz
+ *
+ * @param[in] dev Device handle
+ * @param[in] lna LNA to enable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_select_lna(struct bladerf *dev, lms_lna lna);
+
+/**
+ * Get the currently selected LNA
+ *
+ * @param[in] dev Device handle
+ * @param[out] lna Currently selected LNA, according to device registers
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_lna(struct bladerf *dev, lms_lna *lna);
+
+/**
+ * Enable or disable RXVGA1
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Set the gain value of RXVGA1 (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 5 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the RXVGA1 gain value (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 5 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga1_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Enable or disable RXVGA2
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Set the gain value of RXVGA2 (in dB)
+ *
+ * The range of gain values is from 0dB to 60dB.
+ * Anything above 30dB is not recommended as a gain setting and will be clamped.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 0 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the RXVGA2 gain value (in dB)
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 0 to 30)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rxvga2_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Set the gain in dB of TXVGA2.
+ *
+ * The range of gain values is from 0dB to 25dB.
+ * Anything above 25 will be clamped at 25.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: 0 to 25). Out of range values will
+ * be clamped.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga2_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the gain in dB of TXVGA2.
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: 0 to 25)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga2_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Set the gain in dB of TXVGA1.
+ *
+ * The range of gain values is from -35dB to -4dB.
+ *
+ * @param[in] dev Device handle
+ * @param[in] gain Gain in dB (range: -4 to -35). Out of range values
+ * will be clamped.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga1_set_gain(struct bladerf *dev, int gain);
+
+/**
+ * Get the gain in dB of TXVGA1.
+ *
+ * The range of gain values is from -35dB to -4dB.
+ *
+ * @param[in] dev Device handle
+ * @param[out] gain Gain in dB (range: -4 to -35)
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_txvga1_get_gain(struct bladerf *dev, int *gain);
+
+/**
+ * Enable or disable a PA
+ *
+ * @note PA_ALL is NOT valid for enabling, only for disabling.
+ *
+ * @param[in] dev Device handle
+ * @param[in] pa PA to enable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_pa_enable(struct bladerf *dev, lms_pa pa, bool enable);
+
+/**
+ * Enable or disable the peak detectors.
+ *
+ * This is used as a baseband feedback to the system during transmit for
+ * calibration purposes.
+ *
+ * @note You cannot actively receive RF when the peak detectors are enabled.
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_peakdetect_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Enable or disable the RF front end.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to enable or disable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_enable_rffe(struct bladerf *dev, bladerf_module module, bool enable);
+
+/**
+ * Configure TX -> RX loopback mode
+ *
+ * @param[in] dev Device handle
+ * @param[in] mode Loopback mode. USE BLADERF_LB_NONE to disable
+ * loopback functionality.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_loopback_mode(struct bladerf *dev, bladerf_loopback mode);
+
+/**
+ * Figure out what loopback mode we're in.
+ *
+ * @param[in] dev Device handle
+ * @param[out] mode Current loopback mode, or BLADERF_LB_NONE if
+ * loopback is not enabled.
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_loopback_mode(struct bladerf *dev, bladerf_loopback *mode);
+
+/**
+ * Top level power down of the LMS6002D
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_power_down(struct bladerf *dev);
+
+/**
+ * Enable or disable the PLL of a module.
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module PLL to enable
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_pll_enable(struct bladerf *dev, bladerf_module mod, bool enable);
+
+/**
+ * Enable or disable the RX subsystem
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_rx_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Enable or disable the TX subsystem
+ *
+ * @param[in] dev Device handle
+ * @param[in] enable Set to `true` to enable, `false` to disable
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_tx_enable(struct bladerf *dev, bool enable);
+
+/**
+ * Converts a frequency structure into the final frequency in Hz
+ *
+ * @param[in] f Frequency structure to convert
+ * @returns The closest frequency in Hz that `f` can be converted to
+ */
+uint32_t lms_frequency_to_hz(struct lms_freq *f);
+
+/**
+ * Pretty print a frequency structure
+ *
+ * @note This is intended only for debug purposes. The log level must
+ * be set to DEBUG for this output to be made visible.
+ *
+ * @param[in] freq Frequency structure to print out
+ */
+void lms_print_frequency(struct lms_freq *freq);
+
+/**
+ * Get the frequency structure of the module
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[out] freq LMS frequency structure detailing VCO settings
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *freq);
+
+/**
+ * Fetch "Quick tune" parameters
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to query
+ * @param[out] quick_tune Quick retune parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_get_quick_tune(struct bladerf *dev,
+ bladerf_module module,
+ struct bladerf_quick_tune *quick_tune);
+
+/**
+ * Calculate the parameters to tune to a specified frequency
+ *
+ * @param[in] freq Desired frequency
+ * @param[out] f Computed tuning parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_calculate_tuning_params(unsigned int freq, struct lms_freq *f);
+
+/**
+ * Set the frequency of a module, given the lms_freq structure
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to tune
+ * @param[in] f lms_freq structure contaning desired tuning parameters
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_set_precalculated_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f);
+
+/**
+ * Set the frequency of a module in Hz
+ *
+ * @param[in] dev Device handle
+ * @param[in] mod Module to change
+ * @param[in] freq Frequency in Hz to tune
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+static inline int lms_set_frequency(struct bladerf *dev,
+ bladerf_module mod, uint32_t freq)
+{
+ struct lms_freq f;
+ int status;
+
+ status = lms_calculate_tuning_params(freq, &f);
+ if (status < 0) {
+ return status;
+ }
+
+ return lms_set_precalculated_frequency(dev, mod, &f);
+}
+
+/**
+ * Read back every register from the LMS6002D device.
+ *
+ * @note This is intended only for debug purposes.
+ *
+ * @param[in] dev Device handle
+ *
+ * @return 0 on success, BLADERF_ERR_* value on failure
+ */
+int lms_dump_registers(struct bladerf *dev);
+
+/**
+ * Calibrate the DC offset value for RX and TX modules for the
+ * direct conversion receiver.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to calibrate
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int lms_calibrate_dc(struct bladerf *dev, bladerf_cal_module module);
+
+/**
+ * Load DC calibration values directly via device registers instead of
+ * running autocalibration routines.
+ *
+ * @param[in] dev Device handle
+ * @param[in] dc_cals Calibration values to load
+ */
+int lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Retrieve the DC calibration values currently in use
+ *
+ * @param[in] dev Device handle
+ * @param[out] dc_cals Calibration values to load
+ */
+int lms_get_dc_cals(struct bladerf *dev, struct bladerf_lms_dc_cals *dc_cals);
+
+/**
+ * Initialize and configure the LMS6002D given the transceiver
+ * configuration passed in.
+ *
+ * @param[in] dev Device handle
+ * @param[in] config Transceiver configuration
+ *
+ * @return 0 on success, -1 on failure.
+ */
+int lms_config_init(struct bladerf *dev, struct lms_xcvr_config *config);
+
+/**
+ * Select the appropriate band fore the specified frequency
+ *
+ * @note This is band selection is specific to how the bladeRF is connected
+ * to the LNA and PA blocks.
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to configure
+ * @parma[in] low_band Select the low band
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_select_band(struct bladerf *dev, bladerf_module module, bool low_band);
+
+/**
+ * Select internal or external sampling
+ *
+ * @param[in] dev Device handle
+ * @param[in] sampling Desired sampling mode
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_select_sampling(struct bladerf *dev, bladerf_sampling sampling);
+
+/**
+ * Get the current sampling type
+ *
+ * @param[in] dev Device handle
+ * @param[out] sampling Desired sampling mode
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_sampling(struct bladerf *dev, bladerf_sampling *sampling);
+
+/**
+ * Set the DC offset value on the I channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[in] value DC offset adjustment value to write
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_set_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, uint16_t value);
+
+/**
+ * Get the DC offset value on the I channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[out] value On success, the DC offset value on the I channel
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, int16_t *value);
+
+/**
+ * Set the DC offset value on the Q channel.
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[in] value DC offset adjustment value to write
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_set_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t value);
+
+/**
+ * Get the DC offset value on the Q channel
+ *
+ * For consistency with other bladeRF correction values,
+ * this value is scaled to [-2048, 2048].
+ *
+ * @param[in] dev Device handle
+ * @param[in] module Module to adjust
+ * @param[out] value On success, the DC offset value on the I channel
+ *
+ * @return 0 on succes, BLADERF_ERR_* value on failure
+ */
+int lms_get_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t *value);
+
+#endif /* LMS_H_ */
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h
new file mode 100644
index 0000000..9e0b417
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_16x64.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_16x64_H_
+#define BLADERF_NIOS_PKT_16x64_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 16-bit addresses and 64-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 5:4 | 16-bit address, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 13:6 | 64-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:14 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_16x64_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_16x64_MAGIC ((uint8_t) 'E')
+
+/* Request packet indices */
+#define NIOS_PKT_16x64_IDX_MAGIC 0
+#define NIOS_PKT_16x64_IDX_TARGET_ID 1
+#define NIOS_PKT_16x64_IDX_FLAGS 2
+#define NIOS_PKT_16x64_IDX_RESV1 3
+#define NIOS_PKT_16x64_IDX_ADDR 4
+#define NIOS_PKT_16x64_IDX_DATA 6
+#define NIOS_PKT_16x64_IDX_RESV2 14
+
+/* Target IDs */
+#define NIOS_PKT_16x64_TARGET_AD9361 0x00
+#define NIOS_PKT_16x64_TARGET_RFIC 0x01 /* RFIC control */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_16x64_TARGET_USR1 0x80
+#define NIOS_PKT_16x64_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_16x64_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_16x64_FLAG_SUCCESS (1 << 1)
+
+/**
+ * Sub-addresses for rfic target.
+ *
+ * +================+============================================+
+ * | Bit(s) | Value |
+ * +================+============================================+
+ * | 15:12 | Reserved. Set to 0. |
+ * +----------------+--------------------------------------------+
+ * | 11:8 | bladerf_channel & 0xf |
+ * | | 1111 = system-wide |
+ * +----------------+--------------------------------------------+
+ * | 7:0 | Subaddress. See bladerf_rfic_command enum. |
+ * +----------------+--------------------------------------------+
+ */
+
+/* Pack the request buffer */
+static inline void nios_pkt_16x64_pack(uint8_t *buf, uint8_t target, bool write,
+ uint16_t addr, uint64_t data)
+{
+ buf[NIOS_PKT_16x64_IDX_MAGIC] = NIOS_PKT_16x64_MAGIC;
+ buf[NIOS_PKT_16x64_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] = NIOS_PKT_16x64_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_16x64_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_16x64_IDX_ADDR + 0] = (addr >> 0);
+ buf[NIOS_PKT_16x64_IDX_ADDR + 1] = (addr >> 8);
+
+ buf[NIOS_PKT_16x64_IDX_DATA + 0] = (data >> 0) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 1] = (data >> 8) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 2] = (data >> 16) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 3] = (data >> 24) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 4] = (data >> 32) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 5] = (data >> 40) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 6] = (data >> 48) & 0xff;
+ buf[NIOS_PKT_16x64_IDX_DATA + 7] = (data >> 56) & 0xff;
+
+ buf[NIOS_PKT_16x64_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_16x64_IDX_RESV2 + 1] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_16x64_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint16_t *addr,
+ uint64_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_16x64_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_16x64_IDX_FLAGS] & NIOS_PKT_16x64_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = (buf[NIOS_PKT_16x64_IDX_ADDR + 0] << 0) |
+ (buf[NIOS_PKT_16x64_IDX_ADDR + 1] << 8);
+ }
+
+ if (data != NULL) {
+ *data = ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 0] << 0) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 1] << 8) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 2] << 16) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 3] << 24) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 4] << 32) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 5] << 40) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 6] << 48) |
+ ((uint64_t) buf[NIOS_PKT_16x64_IDX_DATA + 7] << 56);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_16x64_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint16_t addr,
+ uint64_t data, bool success)
+{
+ nios_pkt_16x64_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_16x64_IDX_FLAGS] |= NIOS_PKT_16x64_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_16x64_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint16_t *addr, uint64_t *data,
+ bool *success)
+{
+ nios_pkt_16x64_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_16x64_IDX_FLAGS] & NIOS_PKT_16x64_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h
new file mode 100644
index 0000000..e77cdee
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_32x32.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_32x32_H_
+#define BLADERF_NIOS_PKT_32x32_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 32-bit addresses and 32-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 7:4 | 32-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 11:8 | 32-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:12 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_32x32_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_32x32_MAGIC ((uint8_t) 'K')
+
+/* Request packet indices */
+#define NIOS_PKT_32x32_IDX_MAGIC 0
+#define NIOS_PKT_32x32_IDX_TARGET_ID 1
+#define NIOS_PKT_32x32_IDX_FLAGS 2
+#define NIOS_PKT_32x32_IDX_RESV1 3
+#define NIOS_PKT_32x32_IDX_ADDR 4
+#define NIOS_PKT_32x32_IDX_DATA 8
+#define NIOS_PKT_32x32_IDX_RESV2 12
+
+/* Target IDs */
+
+/* For the EXP and EXP_DIR targets, the address is a bitmask of values
+ * to read/write */
+#define NIOS_PKT_32x32_TARGET_EXP 0x00 /* Expansion I/O */
+#define NIOS_PKT_32x32_TARGET_EXP_DIR 0x01 /* Expansion I/O Direction reg */
+#define NIOS_PKT_32x32_TARGET_ADI_AXI 0x02 /* ADI AXI Interface */
+#define NIOS_PKT_32x32_TARGET_WB_MSTR 0x03 /* Wishbone Master */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_32x32_TARGET_USR1 0x80
+#define NIOS_PKT_32x32_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_32x32_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_32x32_FLAG_SUCCESS (1 << 1)
+
+
+/* Pack the request buffer */
+static inline void nios_pkt_32x32_pack(uint8_t *buf, uint8_t target, bool write,
+ uint32_t addr, uint32_t data)
+{
+ buf[NIOS_PKT_32x32_IDX_MAGIC] = NIOS_PKT_32x32_MAGIC;
+ buf[NIOS_PKT_32x32_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] = NIOS_PKT_32x32_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_32x32_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_32x32_IDX_ADDR + 0] = (addr >> 0);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 1] = (addr >> 8);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 2] = (addr >> 16);
+ buf[NIOS_PKT_32x32_IDX_ADDR + 3] = (addr >> 24);
+
+ buf[NIOS_PKT_32x32_IDX_DATA + 0] = (data >> 0);
+ buf[NIOS_PKT_32x32_IDX_DATA + 1] = (data >> 8);
+ buf[NIOS_PKT_32x32_IDX_DATA + 2] = (data >> 16);
+ buf[NIOS_PKT_32x32_IDX_DATA + 3] = (data >> 24);
+
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_32x32_IDX_RESV2 + 3] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_32x32_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint32_t *addr,
+ uint32_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_32x32_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_32x32_IDX_FLAGS] & NIOS_PKT_32x32_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = (buf[NIOS_PKT_32x32_IDX_ADDR + 0] << 0) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 1] << 8) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 2] << 16) |
+ (buf[NIOS_PKT_32x32_IDX_ADDR + 3] << 24);
+ }
+
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_32x32_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 1] << 8) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 2] << 16) |
+ (buf[NIOS_PKT_32x32_IDX_DATA + 3] << 24);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_32x32_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint32_t addr,
+ uint32_t data, bool success)
+{
+ nios_pkt_32x32_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_32x32_IDX_FLAGS] |= NIOS_PKT_32x32_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_32x32_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint32_t *addr, uint32_t *data,
+ bool *success)
+{
+ nios_pkt_32x32_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_32x32_IDX_FLAGS] & NIOS_PKT_32x32_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h
new file mode 100644
index 0000000..9ed1d45
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x16.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x16_H_
+#define BLADERF_NIOS_PKT_8x16_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 16-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 5:6 | 16-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 7-15 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x16_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x16_MAGIC ((uint8_t) 'B')
+
+/* Request packet indices */
+#define NIOS_PKT_8x16_IDX_MAGIC 0
+#define NIOS_PKT_8x16_IDX_TARGET_ID 1
+#define NIOS_PKT_8x16_IDX_FLAGS 2
+#define NIOS_PKT_8x16_IDX_RESV1 3
+#define NIOS_PKT_8x16_IDX_ADDR 4
+#define NIOS_PKT_8x16_IDX_DATA 5
+#define NIOS_PKT_8x16_IDX_RESV2 7
+
+/* Target IDs */
+#define NIOS_PKT_8x16_TARGET_VCTCXO_DAC 0x00
+#define NIOS_PKT_8x16_TARGET_IQ_CORR 0x01
+#define NIOS_PKT_8x16_TARGET_AGC_CORR 0x02
+#define NIOS_PKT_8x16_TARGET_AD56X1_DAC 0x03
+#define NIOS_PKT_8x16_TARGET_INA219 0x04
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x16_TARGET_USR1 0x80
+#define NIOS_PKT_8x16_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x16_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x16_FLAG_SUCCESS (1 << 1)
+
+/* Sub-addresses for the IQ Correction target block */
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN 0x00
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE 0x01
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN 0x02
+#define NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE 0x03
+
+/* Sub-addresses for the AGC DC Correction target block */
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MAX 0x00
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MAX 0x01
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MID 0x02
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MID 0x03
+#define NIOS_PKT_8x16_ADDR_AGC_DC_Q_MIN 0x04
+#define NIOS_PKT_8x16_ADDR_AGC_DC_I_MIN 0x05
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x16_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint16_t data)
+{
+ buf[NIOS_PKT_8x16_IDX_MAGIC] = NIOS_PKT_8x16_MAGIC;
+ buf[NIOS_PKT_8x16_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] = NIOS_PKT_8x16_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x16_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x16_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x16_IDX_DATA] = data & 0xff;
+ buf[NIOS_PKT_8x16_IDX_DATA + 1] = (data >> 8);
+
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 6] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 7] = 0x00;
+ buf[NIOS_PKT_8x16_IDX_RESV2 + 8] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x16_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint16_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x16_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x16_IDX_FLAGS] & NIOS_PKT_8x16_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x16_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_8x16_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_8x16_IDX_DATA + 1] << 8);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x16_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint16_t data, bool success)
+{
+ nios_pkt_8x16_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x16_IDX_FLAGS] |= NIOS_PKT_8x16_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x16_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint16_t *data,
+ bool *success)
+{
+ nios_pkt_8x16_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x16_IDX_FLAGS] & NIOS_PKT_8x16_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h
new file mode 100644
index 0000000..27e6373
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x32.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x32_H_
+#define BLADERF_NIOS_PKT_8x32_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 32-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 8:5 | 32-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:9 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x32_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x32_MAGIC ((uint8_t) 'C')
+
+/* Request packet indices */
+#define NIOS_PKT_8x32_IDX_MAGIC 0
+#define NIOS_PKT_8x32_IDX_TARGET_ID 1
+#define NIOS_PKT_8x32_IDX_FLAGS 2
+#define NIOS_PKT_8x32_IDX_RESV1 3
+#define NIOS_PKT_8x32_IDX_ADDR 4
+#define NIOS_PKT_8x32_IDX_DATA 5
+#define NIOS_PKT_8x32_IDX_RESV2 9
+
+/* Target IDs */
+#define NIOS_PKT_8x32_TARGET_VERSION 0x00 /* FPGA version (read only) */
+#define NIOS_PKT_8x32_TARGET_CONTROL 0x01 /* FPGA control/config register */
+#define NIOS_PKT_8x32_TARGET_ADF4351 0x02 /* XB-200 ADF4351 register access
+ * (write-only) */
+#define NIOS_PKT_8x32_TARGET_RFFE_CSR 0x03 /* RFFE control & status GPIO */
+#define NIOS_PKT_8x32_TARGET_ADF400X 0x04 /* ADF400x config */
+#define NIOS_PKT_8x32_TARGET_FASTLOCK 0x05 /* Save AD9361 fast lock profile
+ * to Nios */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x32_TARGET_USR1 0x80
+#define NIOS_PKT_8x32_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x32_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x32_FLAG_SUCCESS (1 << 1)
+
+/* Function to convert target ID to string */
+static inline const char* target2str(uint8_t target_id) {
+ switch (target_id) {
+ case NIOS_PKT_8x32_TARGET_VERSION:
+ return "FPGA Version";
+ case NIOS_PKT_8x32_TARGET_CONTROL:
+ return "FPGA Control/Config Register";
+ case NIOS_PKT_8x32_TARGET_ADF4351:
+ return "XB-200 ADF4351 Register (Write-Only)";
+ case NIOS_PKT_8x32_TARGET_RFFE_CSR:
+ return "RFFE Control & Status GPIO";
+ case NIOS_PKT_8x32_TARGET_ADF400X:
+ return "ADF400x Config";
+ case NIOS_PKT_8x32_TARGET_FASTLOCK:
+ return "AD9361 Fast Lock Profile";
+
+ /* Reserved for user customizations */
+ case NIOS_PKT_8x32_TARGET_USR1:
+ return "User Defined 1";
+ case NIOS_PKT_8x32_TARGET_USR128:
+ return "User Defined 128";
+
+ default:
+ return "Unknown Target ID";
+ }
+}
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x32_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint32_t data)
+{
+ buf[NIOS_PKT_8x32_IDX_MAGIC] = NIOS_PKT_8x32_MAGIC;
+ buf[NIOS_PKT_8x32_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] = NIOS_PKT_8x32_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x32_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x32_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x32_IDX_DATA + 0] = data & 0xff;
+ buf[NIOS_PKT_8x32_IDX_DATA + 1] = (data >> 8);
+ buf[NIOS_PKT_8x32_IDX_DATA + 2] = (data >> 16);
+ buf[NIOS_PKT_8x32_IDX_DATA + 3] = (data >> 24);
+
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x32_IDX_RESV2 + 6] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x32_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint32_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x32_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x32_IDX_FLAGS] & NIOS_PKT_8x32_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x32_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = (buf[NIOS_PKT_8x32_IDX_DATA + 0] << 0) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 1] << 8) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 2] << 16) |
+ (buf[NIOS_PKT_8x32_IDX_DATA + 3] << 24);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x32_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint32_t data, bool success)
+{
+ nios_pkt_8x32_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x32_IDX_FLAGS] |= NIOS_PKT_8x32_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x32_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint32_t *data,
+ bool *success)
+{
+ nios_pkt_8x32_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x32_IDX_FLAGS] & NIOS_PKT_8x32_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h
new file mode 100644
index 0000000..420307c
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x64.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x64_H_
+#define BLADERF_NIOS_PKT_8x64_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 64-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 12:5 | 64-bit data, little-endian |
+ * +----------------+---------------------------------------------------------+
+ * | 15:13 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x64_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x64_MAGIC ((uint8_t) 'D')
+
+/* Request packet indices */
+#define NIOS_PKT_8x64_IDX_MAGIC 0
+#define NIOS_PKT_8x64_IDX_TARGET_ID 1
+#define NIOS_PKT_8x64_IDX_FLAGS 2
+#define NIOS_PKT_8x64_IDX_RESV1 3
+#define NIOS_PKT_8x64_IDX_ADDR 4
+#define NIOS_PKT_8x64_IDX_DATA 5
+#define NIOS_PKT_8x64_IDX_RESV2 13
+
+/* Target IDs */
+
+#define NIOS_PKT_8x64_TARGET_TIMESTAMP 0x00 /* Timestamp readback (read only) */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x64_TARGET_USR1 0x80
+#define NIOS_PKT_8x64_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x64_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x64_FLAG_SUCCESS (1 << 1)
+
+/* Sub-addresses for timestamp target */
+#define NIOS_PKT_8x64_TIMESTAMP_RX 0x00
+#define NIOS_PKT_8x64_TIMESTAMP_TX 0x01
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x64_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint64_t data)
+{
+ buf[NIOS_PKT_8x64_IDX_MAGIC] = NIOS_PKT_8x64_MAGIC;
+ buf[NIOS_PKT_8x64_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] = NIOS_PKT_8x64_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x64_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x64_IDX_ADDR] = addr;
+
+ buf[NIOS_PKT_8x64_IDX_DATA + 0] = (data >> 0) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 1] = (data >> 8) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 2] = (data >> 16) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 3] = (data >> 24) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 4] = (data >> 32) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 5] = (data >> 40) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 6] = (data >> 48) & 0xff;
+ buf[NIOS_PKT_8x64_IDX_DATA + 7] = (data >> 56) & 0xff;
+
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x64_IDX_RESV2 + 2] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x64_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint64_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x64_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x64_IDX_FLAGS] & NIOS_PKT_8x64_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x64_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 0] << 0) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 1] << 8) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 2] << 16) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 3] << 24) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 4] << 32) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 5] << 40) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 6] << 48) |
+ ((uint64_t) buf[NIOS_PKT_8x64_IDX_DATA + 7] << 56);
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x64_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint64_t data, bool success)
+{
+ nios_pkt_8x64_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x64_IDX_FLAGS] |= NIOS_PKT_8x64_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x64_resp_unpack(const uint8_t *buf,
+ uint8_t *target, bool *write,
+ uint8_t *addr, uint64_t *data,
+ bool *success)
+{
+ nios_pkt_8x64_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x64_IDX_FLAGS] & NIOS_PKT_8x64_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h
new file mode 100644
index 0000000..dd8072c
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_8x8.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_8x8_H_
+#define BLADERF_NIOS_PKT_8x8_H_
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+/*
+ * This file defines the Host <-> FPGA (NIOS II) packet formats for accesses
+ * to devices/blocks with 8-bit addresses and 8-bit data
+ *
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Target ID (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 | Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 3 | Reserved. Set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ * | 4 | 8-bit address |
+ * +----------------+---------------------------------------------------------+
+ * | 5 | 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ * | 15:6 | Reserved. Set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response packet contains the same information as the request.
+ * A status flag will be set if the operation completed successfully.
+ *
+ * In the case of a read request, the data field will contain the read data, if
+ * the read succeeded.
+ *
+ * (Note 1)
+ * The "Target ID" refers to the peripheral, device, or block to access.
+ * See the NIOS_PKT_8x8_TARGET_* values.
+ *
+ * (Note 2)
+ * The flags are defined as follows:
+ *
+ * +================+========================+
+ * | Bit(s) | Value |
+ * +================+========================+
+ * | 7:2 | Reserved. Set to 0. |
+ * +----------------+------------------------+
+ * | | Status. Only used in |
+ * | | response packet. |
+ * | | Ignored in request. |
+ * | 1 | |
+ * | | 1 = Success |
+ * | | 0 = Failure |
+ * +----------------+------------------------+
+ * | 0 | 0 = Read operation |
+ * | | 1 = Write operation |
+ * +----------------+------------------------+
+ *
+ */
+
+#define NIOS_PKT_8x8_MAGIC ((uint8_t) 'A')
+
+/* Request packet indices */
+#define NIOS_PKT_8x8_IDX_MAGIC 0
+#define NIOS_PKT_8x8_IDX_TARGET_ID 1
+#define NIOS_PKT_8x8_IDX_FLAGS 2
+#define NIOS_PKT_8x8_IDX_RESV1 3
+#define NIOS_PKT_8x8_IDX_ADDR 4
+#define NIOS_PKT_8x8_IDX_DATA 5
+#define NIOS_PKT_8x8_IDX_RESV2 6
+
+/* Target IDs */
+#define NIOS_PKT_8x8_TARGET_LMS6 0x00 /* LMS6002D register access */
+#define NIOS_PKT_8x8_TARGET_SI5338 0x01 /* Si5338 register access */
+#define NIOS_PKT_8x8_TARGET_VCTCXO_TAMER 0x02 /* VCTCXO Tamer control */
+#define NIOS_PKT_8x8_TX_TRIGGER_CTL 0x03 /* TX trigger control */
+#define NIOS_PKT_8x8_RX_TRIGGER_CTL 0x04 /* RX trigger control */
+
+/* IDs 0x80 through 0xff will not be assigned by Nuand. These are reserved
+ * for user customizations */
+#define NIOS_PKT_8x8_TARGET_USR1 0x80
+#define NIOS_PKT_8x8_TARGET_USR128 0xff
+
+/* Flag bits */
+#define NIOS_PKT_8x8_FLAG_WRITE (1 << 0)
+#define NIOS_PKT_8x8_FLAG_SUCCESS (1 << 1)
+
+
+/* Pack the request buffer */
+static inline void nios_pkt_8x8_pack(uint8_t *buf, uint8_t target, bool write,
+ uint8_t addr, uint8_t data)
+{
+ buf[NIOS_PKT_8x8_IDX_MAGIC] = NIOS_PKT_8x8_MAGIC;
+ buf[NIOS_PKT_8x8_IDX_TARGET_ID] = target;
+
+ if (write) {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] = NIOS_PKT_8x8_FLAG_WRITE;
+ } else {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] = 0x00;
+ }
+
+ buf[NIOS_PKT_8x8_IDX_RESV1] = 0x00;
+
+ buf[NIOS_PKT_8x8_IDX_ADDR] = addr;
+ buf[NIOS_PKT_8x8_IDX_DATA] = data;
+
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 0] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 1] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 2] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 3] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 4] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 5] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 6] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 7] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 8] = 0x00;
+ buf[NIOS_PKT_8x8_IDX_RESV2 + 9] = 0x00;
+}
+
+/* Unpack the request buffer */
+static inline void nios_pkt_8x8_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint8_t *data)
+{
+ if (target != NULL) {
+ *target = buf[NIOS_PKT_8x8_IDX_TARGET_ID];
+ }
+
+ if (write != NULL) {
+ *write = (buf[NIOS_PKT_8x8_IDX_FLAGS] & NIOS_PKT_8x8_FLAG_WRITE) != 0;
+ }
+
+ if (addr != NULL) {
+ *addr = buf[NIOS_PKT_8x8_IDX_ADDR];
+ }
+
+ if (data != NULL) {
+ *data = buf[NIOS_PKT_8x8_IDX_DATA];
+ }
+}
+
+/* Pack the response buffer */
+static inline void nios_pkt_8x8_resp_pack(uint8_t *buf, uint8_t target,
+ bool write, uint8_t addr,
+ uint8_t data, bool success)
+{
+ nios_pkt_8x8_pack(buf, target, write, addr, data);
+
+ if (success) {
+ buf[NIOS_PKT_8x8_IDX_FLAGS] |= NIOS_PKT_8x8_FLAG_SUCCESS;
+ }
+}
+
+/* Unpack the response buffer */
+static inline void nios_pkt_8x8_resp_unpack(const uint8_t *buf, uint8_t *target,
+ bool *write, uint8_t *addr,
+ uint8_t *data, bool *success)
+{
+ nios_pkt_8x8_unpack(buf, target, write, addr, data);
+
+ if ((buf[NIOS_PKT_8x8_IDX_FLAGS] & NIOS_PKT_8x8_FLAG_SUCCESS) != 0) {
+ *success = true;
+ } else {
+ *success = false;
+ }
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h
new file mode 100644
index 0000000..7ab5901
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_formats.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLADERF_NIOS_PKT_FORMATS_H_
+#define BLADERF_NIOS_PKT_FORMATS_H_
+
+#include "nios_pkt_legacy.h"
+#include "nios_pkt_retune.h"
+#include "nios_pkt_retune2.h"
+#include "nios_pkt_8x8.h"
+#include "nios_pkt_8x16.h"
+#include "nios_pkt_8x32.h"
+#include "nios_pkt_8x64.h"
+#include "nios_pkt_32x32.h"
+#include "nios_pkt_16x64.h"
+
+#define NIOS_PKT_LEN 16
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h
new file mode 100644
index 0000000..9912ce9
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_legacy.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_LEGACY_H_
+#define BLADERF_NIOS_PKT_LEGACY_H_
+
+/* This is the original packet format used to issue requests from the
+ * host to the FPGA via the FX3 UART.
+ *
+ * This format remains supported for backwards compatibility, but should no
+ * longer be added to.
+ *
+ * If you're looking to customize the FPGA, consider using
+ * one of the "pkt_AxB" packet formats and handlers, or implementing a new
+ * format and handler.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Configuration byte (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 2 - 15 | Pairs of 8-bit addr, 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ *
+ *
+ *
+ * Note 1: Configuration byte:
+ *
+ * +================+============================+
+ * | Bit(s) | Value |
+ * +================+============================+
+ * | 7 | 1 = Read operation |
+ * +----------------+----------------------------+
+ * | 6 | 1 = Write operation |
+ * +----------------+----------------------------+
+ * | 5:4 | Device: |
+ * | | 00 - Config PIO (Note 2) |
+ * | | 01 - LMS register |
+ * | | 10 - VCTCXO Trim DAC |
+ * | | 11 - SI5338 register |
+ * +----------------+----------------------------+
+ * | 3 | Unused |
+ * +----------------+----------------------------+
+ * | 2:0 | Addr/Data pair count |
+ * | | (Note 2) |
+ * +----------------+----------------------------+
+ *
+ * Note 2: Config PIO addresses
+ *
+ * The NIOS II core and modules in the FPGA's programmable fabric are connected
+ * via parallel IO (PIO). See the NIOS_PKT_LEGACY_PIO_ADDR_* definitions
+ * in this file contain a virtual "register map" for these modules.
+ *
+ * Note 3: "Count" field
+ *
+ * The original intent of this field was to allow multiple register
+ * accesses to be requested at once.
+ *
+ * However, this feature was not leveraged by the host code for the LMS and
+ * SI5338 accesses, so revised legacy packet handler only processes the
+ * first addr/data pair.
+ *
+ * Readback of the time tamer values is the only case where this field
+ * is set to a count greater than 1.
+ *
+ * Although config PIO values are larger than one byte, the host code
+ * accessed these byte by byte through multiple requests. For example,
+ * 4 accesses would be required to fully read/write the configuration PIO.
+ *
+ * The above inefficiency is the motivation behind adding packet handlers
+ * that can read/write 32 or 64 bits in a single request (e.g., pkt_8x32,
+ * pkt_8x64).
+ *
+ *
+ *
+ * Response
+ * ----------------------
+ *
+ * The response for the legacy packet is essentially just the device
+ * echoing the request.
+ *
+ * On a read request, the number of requested items will be populated
+ * in bytes 2:15.
+ *
+ * The remaining bytes, or all of bytes 2:15 on a write request, should
+ * be regarded as "undefined" values and not used.
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | Configuration byte |
+ * +----------------+---------------------------------------------------------+
+ * | 2 - 15 | Pairs of 8-bit addr, 8-bit data |
+ * +----------------+---------------------------------------------------------+
+ *
+ */
+
+#define NIOS_PKT_LEGACY_MAGIC 'N'
+
+#define NIOS_PKT_LEGACY_DEV_GPIO_ADDR 0
+#define NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR 4
+#define NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR 6
+#define NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR 8
+#define NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR 10
+#define NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID 12
+
+#define NIOS_PKT_LEGACY_MODE_CNT_MASK 0x7
+#define NIOS_PKT_LEGACY_MODE_CNT_SHIFT 0
+#define NIOS_PKT_LEGACY_MODE_DEV_MASK 0x30
+#define NIOS_PKT_LEGACY_MODE_DEV_SHIFT 4
+
+#define NIOS_PKT_LEGACY_DEV_CONFIG (0 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_LMS (1 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_VCTCXO (2 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+#define NIOS_PKT_LEGACY_DEV_SI5338 (3 << NIOS_PKT_LEGACY_MODE_DEV_SHIFT)
+
+#define NIOS_PKT_LEGACY_MODE_DIR_MASK 0xC0
+#define NIOS_PKT_LEGACY_MODE_DIR_SHIFT 6
+#define NIOS_PKT_LEGACY_MODE_DIR_READ (2 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
+#define NIOS_PKT_LEGACY_MODE_DIR_WRITE (1 << NIOS_PKT_LEGACY_MODE_DIR_SHIFT)
+
+
+/* PIO address space */
+
+/*
+ * 32-bit Device control register.
+ *
+ * This is register accessed via the libbladeRF functions,
+ * bladerf_config_gpio_write() and bladerf_config_gpio_read().
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_CONTROL 0
+#define NIOS_PKT_LEGACY_PIO_LEN_CONTROL 4
+
+/*
+ * IQ Correction: 16-bit RX Gain value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_RX_GAIN 4
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_RX_GAIN 2
+
+/*
+ * IQ Correction: 16-bit RX Phase value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_RX_PHASE 6
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_RX_PHASE 2
+
+/*
+ * IQ Correction: 16-bit TX Gain value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_TX_GAIN 8
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_TX_GAIN 2
+
+/*
+ * IQ Correction: 16-bit TX Phase value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_IQ_TX_PHASE 10
+#define NIOS_PKT_LEGACY_PIO_LEN_IQ_TX_PHASE 2
+
+/*
+ * 32-bit FPGA Version (read-only)
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_FPGA_VERSION 12
+#define NIOS_PKT_LEGACY_PIO_LEN_FPGA_VERSION 4
+
+/*
+ * 64-bit RX timestamp
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_RX_TIMESTAMP 16
+#define NIOS_PKT_LEGACY_PIO_LEN_RX_TIMESTAMP 8
+
+/*
+ * 64-bit TX timestamp
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_TX_TIMESTAMP 24
+#define NIOS_PKT_LEGACY_PIO_LEN_TX_TIMESTAMP 8
+
+/*
+ * VCTCXO Trim DAC value
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_VCTCXO 34
+#define NIOS_PKT_LEGACY_PIO_LEN_VCTCXO 2
+
+/*
+ * XB-200 ADF4351 Synthesizer
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_XB200_SYNTH 36
+#define NIOS_PKT_LEGACY_PIO_LEN_XB200_SYNTH 4
+
+/*
+ * Expansion IO
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_EXP 40
+#define NIOS_PKT_LEGACY_PIO_LEN_EXP 4
+
+/*
+ * Expansion IO Direction
+ */
+#define NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR 44
+#define NIOS_PKT_LEGACY_PIO_LEN_EXP_DIR 4
+
+struct uart_cmd {
+ unsigned char addr;
+ unsigned char data;
+};
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h
new file mode 100644
index 0000000..9bedcb0
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_RETUNE_H_
+#define BLADERF_NIOS_PKT_RETUNE_H_
+
+#ifndef BLADERF_NIOS_BUILD
+# include <libbladeRF.h>
+#else
+# include "libbladeRF_nios_compat.h"
+#endif
+
+#include <stdint.h>
+
+/* Specify this value instead of a timestamp to clear the retune queue */
+#define NIOS_PKT_RETUNE_CLEAR_QUEUE ((uint64_t) -1)
+
+/* This file defines the Host <-> FPGA (NIOS II) packet formats for
+ * retune messages. This packet is formatted, as follows. All values are
+ * little-endian.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit timestamp denoting when to retune. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | 32-bit LMS6002D n_int & n_frac register values (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 13 | RX/TX bit, FREQSEL LMS6002D reg value (Note 3) |
+ * +----------------+---------------------------------------------------------+
+ * | 14 | Bit 7: Band-selection (Note 4) |
+ * | | Bit 6: 1=Quick tune, 0=Normal tune |
+ * | | Bits [5:0] VCOCAP[5:0] Hint |
+ * +----------------+---------------------------------------------------------+
+ * | 15 | 8-bit reserved word. Should be set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) Special Timestamp Values:
+ *
+ * Tune "Now": 0x0000000000000000
+ * Clear Retune Queue: 0xffffffffffffffff
+ *
+ * When the "Clear Retune Queue" value is used, all of the other tuning
+ * parameters are ignored.
+ *
+ * (Note 2) Packed as follows:
+ *
+ * +================+=======================+
+ * | Byte offset | (MSB) Value (LSB)|
+ * +================+=======================+
+ * | 0 | NINT[8:1] |
+ * +----------------+-----------------------+
+ * | 1 | NINT[0], NFRAC[22:16] |
+ * +----------------+-----------------------+
+ * | 2 | NFRAC[15:8] |
+ * +----------------+-----------------------+
+ * | 3 | NFRAC[7:0] |
+ * +----------------+-----------------------+
+ *
+ * (Note 3) Packed as follows:
+ *
+ * +================+=======================+
+ * | Bit(s) | Value |
+ * +================+=======================+
+ * | 7 | TX |
+ * +----------------+-----------------------+
+ * | 6 | RX |
+ * +----------------+-----------------------+
+ * | [5:0] | FREQSEL |
+ * +----------------+-----------------------+
+ *
+ * (Notes 4) Band-selection bit = 1 implies "Low band". 0 = "High band"
+ */
+
+#define NIOS_PKT_RETUNE_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE_IDX_TIME 1
+#define NIOS_PKT_RETUNE_IDX_INTFRAC 9
+#define NIOS_PKT_RETUNE_IDX_FREQSEL 13
+#define NIOS_PKT_RETUNE_IDX_BANDSEL 14
+#define NIOS_PKT_RETUNE_IDX_RESV 15
+
+#define NIOS_PKT_RETUNE_MAGIC 'T'
+
+
+#define FLAG_QUICK_TUNE (1 << 6)
+#define FLAG_RX (1 << 6)
+#define FLAG_TX (1 << 7)
+#define FLAG_LOW_BAND (1 << 7)
+
+
+/* Denotes no tune word is supplied. */
+#define NIOS_PKT_RETUNE_NO_HINT 0xff
+
+/* Denotes that the retune should not be scheduled - it should occur "now" */
+#define NIOS_PKT_RETUNE_NOW ((uint64_t) 0x00)
+
+#define PACK_TXRX_FREQSEL(module_, freqsel_) \
+ (freqsel_ & 0x3f)
+
+/* Pack the retune request buffer with the provided parameters */
+static inline void nios_pkt_retune_pack(uint8_t *buf,
+ bladerf_module module,
+ uint64_t timestamp,
+ uint16_t nint,
+ uint32_t nfrac,
+ uint8_t freqsel,
+ uint8_t vcocap,
+ bool low_band,
+ uint8_t xb_gpio,
+ bool quick_tune)
+{
+ buf[NIOS_PKT_RETUNE_IDX_MAGIC] = NIOS_PKT_RETUNE_MAGIC;
+
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 0] = timestamp & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 1] = (timestamp >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 2] = (timestamp >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 3] = (timestamp >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 4] = (timestamp >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 5] = (timestamp >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 6] = (timestamp >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_TIME + 7] = (timestamp >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 0] = (nint >> 1) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] = (nint & 0x1) << 7;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] |= ((nfrac >> 16) & 0x7f);
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 2] = (nfrac >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 3] = nfrac & 0xff;
+
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] = freqsel & 0xff;
+
+ switch (module) {
+ case BLADERF_MODULE_TX:
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] |= FLAG_TX;
+ break;
+
+ case BLADERF_MODULE_RX:
+ buf[NIOS_PKT_RETUNE_IDX_FREQSEL] |= FLAG_RX;
+ break;
+
+ default:
+ /* Erroneous case - should not occur */
+ break;
+ }
+
+ if (low_band) {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] = FLAG_LOW_BAND;
+ } else {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] = 0x00;
+ }
+
+ if (quick_tune) {
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] |= FLAG_QUICK_TUNE;
+ }
+
+ buf[NIOS_PKT_RETUNE_IDX_BANDSEL] |= vcocap;
+
+ buf[NIOS_PKT_RETUNE_IDX_RESV] = xb_gpio;
+}
+
+/* Unpack a retune request */
+static inline void nios_pkt_retune_unpack(const uint8_t *buf,
+ bladerf_module *module,
+ uint64_t *timestamp,
+ uint16_t *nint,
+ uint32_t *nfrac,
+ uint8_t *freqsel,
+ uint8_t *vcocap,
+ bool *low_band,
+ uint8_t *xb_gpio,
+ bool *quick_tune)
+{
+ *timestamp = ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 0]) << 0);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 1]) << 8);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 2]) << 16);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 3]) << 24);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 4]) << 32);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 5]) << 40);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 6]) << 48);
+ *timestamp |= ( ((uint64_t) buf[NIOS_PKT_RETUNE_IDX_TIME + 7]) << 56);
+
+ *nint = buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 0] << 1;
+ *nint |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] >> 7;
+
+ *nfrac = (buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 1] & 0x7f) << 16;
+ *nfrac |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 2] << 8;
+ *nfrac |= buf[NIOS_PKT_RETUNE_IDX_INTFRAC + 3];
+
+ *freqsel = buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & 0x3f;
+
+ *module = -1;
+
+ if (buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & FLAG_TX) {
+ *module = BLADERF_MODULE_TX;
+ } else if (buf[NIOS_PKT_RETUNE_IDX_FREQSEL] & FLAG_RX) {
+ *module = BLADERF_MODULE_RX;
+ }
+
+ *low_band = (buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & FLAG_LOW_BAND) != 0;
+ *quick_tune = (buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & FLAG_QUICK_TUNE) != 0;
+ *vcocap = buf[NIOS_PKT_RETUNE_IDX_BANDSEL] & 0x3f;
+ *xb_gpio = buf[NIOS_PKT_RETUNE_IDX_RESV];
+}
+
+
+/*
+ * Response
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit duration denoting how long the operation took to |
+ * | | complete, in units of timestamp ticks. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | Bits [7:6] Reserved, set to 0. |
+ * | | Bits [5:0] VCOCAP value used. (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 10 | Status Flags (Note 3) |
+ * +----------------+---------------------------------------------------------+
+ * | 11-15 | Reserved. All bits set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) This value will be zero if timestamps are not running for the
+ * associated module.
+ *
+ * (Note 2) This field's value should be ignored when reading a response for
+ * a request to clear the retune queue.
+ *
+ * (Note 3) Description of Status Flags:
+ *
+ * flags[0]: 1 = Timestamp and VCOCAP are valid. This is only the case for
+ * "Tune NOW" requests. It is not possible to return this
+ * information for scheduled retunes, as the event generally
+ * does not occur before the response is set.
+ *
+ * 0 = This was a scheduled retune. Timestamp and VCOCAP Fields
+ * should be ignored.
+ *
+ *
+ * flags[1]: 1 = Operation completed successfully.
+ * 0 = Operation failed.
+ *
+ * For "Tune NOW" requests, a failure may occur as the result
+ * of the tuning algorithm failing to occur, and such other
+ * unexpected failurs.
+ *
+ * The scheduled tune request will failure if the retune queue
+ * is full.
+ *
+ * flags[7:2] Reserved. Set to 0.
+ */
+
+#define NIOS_PKT_RETUNERESP_IDX_MAGIC 0
+#define NIOS_PKT_RETUNERESP_IDX_TIME 1
+#define NIOS_PKT_RETUNERESP_IDX_VCOCAP 9
+#define NIOS_PKT_RETUNERESP_IDX_FLAGS 10
+#define NIOS_PKT_RETUNERESP_IDX_RESV 11
+
+#define NIOS_PKT_RETUNERESP_FLAG_TSVTUNE_VALID (1 << 0)
+#define NIOS_PKT_RETUNERESP_FLAG_SUCCESS (1 << 1)
+
+static inline void nios_pkt_retune_resp_pack(uint8_t *buf,
+ uint64_t duration,
+ uint8_t vcocap,
+ uint8_t flags)
+{
+ buf[NIOS_PKT_RETUNERESP_IDX_MAGIC] = NIOS_PKT_RETUNE_MAGIC;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 0] = duration & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 1] = (duration >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 2] = (duration >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 3] = (duration >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 4] = (duration >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 5] = (duration >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 6] = (duration >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNERESP_IDX_TIME + 7] = (duration >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_VCOCAP] = vcocap;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_FLAGS] = flags;
+
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 1] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 2] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 3] = 0x00;
+ buf[NIOS_PKT_RETUNERESP_IDX_RESV + 4] = 0x00;
+}
+
+static inline void nios_pkt_retune_resp_unpack(const uint8_t *buf,
+ uint64_t *duration,
+ uint8_t *vcocap,
+ uint8_t *flags)
+{
+ *duration = buf[NIOS_PKT_RETUNERESP_IDX_TIME + 0];
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 1]) << 8;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 2]) << 16;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 3]) << 24;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 4]) << 32;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 5]) << 40;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 6]) << 48;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNERESP_IDX_TIME + 7]) << 56;
+
+ *vcocap = buf[NIOS_PKT_RETUNERESP_IDX_VCOCAP];
+
+ *flags = buf[NIOS_PKT_RETUNERESP_IDX_FLAGS];
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h
new file mode 100644
index 0000000..e58b51b
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/include/nios_pkt_retune2.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef BLADERF_NIOS_PKT_RETUNE2_H_
+#define BLADERF_NIOS_PKT_RETUNE2_H_
+
+#ifndef BLADERF_NIOS_BUILD
+# include <libbladeRF.h>
+#else
+# include "libbladeRF_nios_compat.h"
+#endif
+
+#include <stdint.h>
+
+/* This file defines the Host <-> FPGA (NIOS II) packet formats for
+ * retune2 messages. This packet is formatted, as follows. All values are
+ * little-endian.
+ *
+ * Request
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit timestamp denoting when to retune. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | 16-bit Nios fast lock profile number to load (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 11 | 8-bit RFFE fast lock profile slot to use |
+ * +----------------+---------------------------------------------------------+
+ * | 12 | Bit 7: RX bit (set if this is an RX profile |
+ * | | Bits 6: TX output port selection |
+ * | | Bits [5:0]: RX input port selection |
+ * +----------------+---------------------------------------------------------+
+ * | 13 | Bits [7:6]: External TX2 SPDT switch setting |
+ * | | Bits [5:4]: External TX1 SPDT switch setting |
+ * | | Bits [3:2]: External RX2 SPDT switch setting |
+ * | | Bits [1:0]: External RX1 SPDT switch setting |
+ * +----------------+---------------------------------------------------------+
+ * | 14-15 | 8-bit reserved words. Should be set to 0x00. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) Special Timestamp Values:
+ *
+ * Tune "Now": 0x0000000000000000
+ * Clear Retune Queue: 0xffffffffffffffff
+ *
+ * When the "Clear Retune Queue" value is used, all of the other tuning
+ * parameters are ignored.
+ *
+ * (Note 2) Packed as follows:
+ *
+ * +================+=======================+
+ * | Byte offset | (MSB) Value (LSB)|
+ * +================+=======================+
+ * | 0 | NIOS_PROFILE[7:0] |
+ * +----------------+-----------------------+
+ * | 1 | NIOS_PROFILE[15:8] |
+ * +----------------+-----------------------+
+ *
+ */
+
+#define NIOS_PKT_RETUNE2_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE2_IDX_TIME 1
+#define NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE 9
+#define NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE 11
+#define NIOS_PKT_RETUNE2_IDX_RFFE_PORT 12
+#define NIOS_PKT_RETUNE2_IDX_SPDT 13
+#define NIOS_PKT_RETUNE2_IDX_RESV 14
+
+#define NIOS_PKT_RETUNE2_MAGIC 'U'
+
+/* Specify this value instead of a timestamp to clear the retune2 queue */
+#define NIOS_PKT_RETUNE2_CLEAR_QUEUE ((uint64_t) -1)
+
+/* Denotes that the retune2 should not be scheduled - it should occur "now" */
+#define NIOS_PKT_RETUNE2_NOW ((uint64_t) 0x00)
+
+/* The IS_RX bit embedded in the 'port' parameter of the retune2 packet */
+#define NIOS_PKT_RETUNE2_PORT_IS_RX_MASK (0x1 << 7)
+
+/* Pack the retune2 request buffer with the provided parameters */
+static inline void nios_pkt_retune2_pack(uint8_t *buf,
+ bladerf_module module,
+ uint64_t timestamp,
+ uint16_t nios_profile,
+ uint8_t rffe_profile,
+ uint8_t port,
+ uint8_t spdt)
+{
+ uint8_t pkt_port;
+
+ /* Clear the IS_RX bit of the port parameter */
+ pkt_port = (port & (~NIOS_PKT_RETUNE2_PORT_IS_RX_MASK));
+
+ /* Set the IS_RX bit (if needed) */
+ pkt_port = (pkt_port | (BLADERF_CHANNEL_IS_TX(module) ? 0x0 :
+ NIOS_PKT_RETUNE2_PORT_IS_RX_MASK)) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_MAGIC] = NIOS_PKT_RETUNE2_MAGIC;
+
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 0] = (timestamp >> 0) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 1] = (timestamp >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 2] = (timestamp >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 3] = (timestamp >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 4] = (timestamp >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 5] = (timestamp >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 6] = (timestamp >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_TIME + 7] = (timestamp >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 0] = (nios_profile >> 0) & 0xff;
+ buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 1] = (nios_profile >> 8) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE] = rffe_profile & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT] = pkt_port;
+
+ buf[NIOS_PKT_RETUNE2_IDX_SPDT] = spdt & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNE2_IDX_RESV + 1] = 0x00;
+}
+
+/* Unpack a retune request */
+static inline void nios_pkt_retune2_unpack(const uint8_t *buf,
+ bladerf_module *module,
+ uint64_t *timestamp,
+ uint16_t *nios_profile,
+ uint8_t *rffe_profile,
+ uint8_t *port,
+ uint8_t *spdt)
+{
+ *timestamp = ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 0]) << 0 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 1]) << 8 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 2]) << 16 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 3]) << 24 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 4]) << 32 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 5]) << 40 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 6]) << 48 );
+ *timestamp |= ( ((uint64_t)buf[NIOS_PKT_RETUNE2_IDX_TIME + 7]) << 56 );
+
+ *nios_profile = ( ((uint16_t)buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 0])
+ << 0 );
+ *nios_profile |= ( ((uint16_t)buf[NIOS_PKT_RETUNE2_IDX_NIOS_PROFILE + 1])
+ << 8 );
+
+ *rffe_profile = buf[NIOS_PKT_RETUNE2_IDX_RFFE_PROFILE];
+
+ *port = buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT];
+
+ *spdt = buf[NIOS_PKT_RETUNE2_IDX_SPDT];
+
+ *module = ( (buf[NIOS_PKT_RETUNE2_IDX_RFFE_PORT] &
+ NIOS_PKT_RETUNE2_PORT_IS_RX_MASK) ? BLADERF_MODULE_RX :
+ BLADERF_MODULE_TX );
+
+}
+
+
+/*
+ * Response
+ * ----------------------
+ *
+ * +================+=========================================================+
+ * | Byte offset | Description |
+ * +================+=========================================================+
+ * | 0 | Magic Value |
+ * +----------------+---------------------------------------------------------+
+ * | 1 | 64-bit duration denoting how long the operation took to |
+ * | | complete, in units of timestamp ticks. (Note 1) |
+ * +----------------+---------------------------------------------------------+
+ * | 9 | Status Flags (Note 2) |
+ * +----------------+---------------------------------------------------------+
+ * | 10-15 | Reserved. All bits set to 0. |
+ * +----------------+---------------------------------------------------------+
+ *
+ * (Note 1) This value will be zero if timestamps are not running for the
+ * associated module.
+ *
+ * (Note 2) Description of Status Flags:
+ *
+ * flags[0]: 1 = Timestamp is valid. This is only the case for "Tune NOW"
+ * requests. It is not possible to return this information
+ * for scheduled retunes, as the event generally does not
+ * occur before the response is set.
+ *
+ * 0 = This was a scheduled retune. Timestamp fields should be
+ * ignored.
+ *
+ * flags[1]: 1 = Operation completed successfully.
+ * 0 = Operation failed.
+ *
+ * For "Tune NOW" requests, a failure may occur as the result
+ * of the tuning algorithm failing to occur, and such other
+ * unexpected failurs.
+ *
+ * The scheduled tune request will failure if the retune queue
+ * is full.
+ *
+ * flags[7:2] Reserved. Set to 0.
+ */
+
+#define NIOS_PKT_RETUNE2_RESP_IDX_MAGIC 0
+#define NIOS_PKT_RETUNE2_RESP_IDX_TIME 1
+#define NIOS_PKT_RETUNE2_RESP_IDX_FLAGS 9
+#define NIOS_PKT_RETUNE2_RESP_IDX_RESV 10
+
+#define NIOS_PKT_RETUNE2_RESP_FLAG_TSVTUNE_VALID (1 << 0)
+#define NIOS_PKT_RETUNE2_RESP_FLAG_SUCCESS (1 << 1)
+
+static inline void nios_pkt_retune2_resp_pack(uint8_t *buf,
+ uint64_t duration,
+ uint8_t flags)
+{
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_MAGIC] = NIOS_PKT_RETUNE2_MAGIC;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 0] = duration & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 1] = (duration >> 8) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 2] = (duration >> 16) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 3] = (duration >> 24) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 4] = (duration >> 32) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 5] = (duration >> 40) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 6] = (duration >> 48) & 0xff;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 7] = (duration >> 56) & 0xff;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_FLAGS] = flags;
+
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 0] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 1] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 2] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 3] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 4] = 0x00;
+ buf[NIOS_PKT_RETUNE2_RESP_IDX_RESV + 5] = 0x00;
+}
+
+static inline void nios_pkt_retune2_resp_unpack(const uint8_t *buf,
+ uint64_t *duration,
+ uint8_t *flags)
+{
+ *duration = buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 0];
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 1]) << 8;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 2]) << 16;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 3]) << 24;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 4]) << 32;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 5]) << 40;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 6]) << 48;
+ *duration |= ((uint64_t) buf[NIOS_PKT_RETUNE2_RESP_IDX_TIME + 7]) << 56;
+
+ *flags = buf[NIOS_PKT_RETUNE2_RESP_IDX_FLAGS];
+}
+
+#endif
diff --git a/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c b/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c
new file mode 100644
index 0000000..15982fa
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/ad936x_helpers.c
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+#include "ad936x_helpers.h"
+#include "bladerf2_common.h"
+
+static bool tx_mute_state[2] = { false };
+
+uint32_t txmute_get_cached(struct ad9361_rf_phy *phy, bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ return phy->tx1_atten_cached;
+ case BLADERF_CHANNEL_TX(1):
+ return phy->tx2_atten_cached;
+ default:
+ return 0;
+ }
+}
+
+int txmute_set_cached(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ uint32_t atten)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_TX(0):
+ phy->tx1_atten_cached = atten;
+ return 0;
+ case BLADERF_CHANNEL_TX(1):
+ phy->tx2_atten_cached = atten;
+ return 0;
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+}
+
+int txmute_get(struct ad9361_rf_phy *phy, bladerf_channel ch, bool *state)
+{
+ int rfic_ch = (ch >> 1);
+
+ *state = tx_mute_state[rfic_ch];
+
+ return 0;
+}
+
+int txmute_set(struct ad9361_rf_phy *phy, bladerf_channel ch, bool state)
+{
+ int rfic_ch = (ch >> 1);
+ uint32_t const MUTED_ATTEN = 89750;
+ uint32_t atten, cached;
+ int status;
+
+ if (tx_mute_state[rfic_ch] == state) {
+ // short circuit if there's no change
+ return 0;
+ }
+
+ if (state) {
+ // mute: save the existing value before muting
+ uint32_t readval;
+
+ status = ad9361_get_tx_attenuation(phy, rfic_ch, &readval);
+ if (status < 0) {
+ return errno_ad9361_to_bladerf(status);
+ }
+
+ cached = readval;
+ atten = MUTED_ATTEN;
+ } else {
+ // unmute: restore the saved value
+ cached = txmute_get_cached(phy, ch);
+ atten = cached;
+ }
+
+ status = ad9361_set_tx_attenuation(phy, rfic_ch, atten);
+ if (status < 0) {
+ return errno_ad9361_to_bladerf(status);
+ }
+
+ status = txmute_set_cached(phy, ch, cached);
+ if (status < 0) {
+ return status;
+ }
+
+ tx_mute_state[rfic_ch] = state;
+
+ return 0;
+}
+
+int set_ad9361_port_by_freq(struct ad9361_rf_phy *phy,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map = NULL;
+ int status;
+
+ /* Look up the port configuration for this frequency */
+ port_map = _get_band_port_map_by_freq(ch, enabled ? freq : 0);
+
+ if (NULL == port_map) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Set the AD9361 port accordingly */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ status = ad9361_set_tx_rf_port_output(phy, port_map->rfic_port);
+ } else {
+ status = ad9361_set_rx_rf_port_input(phy, port_map->rfic_port);
+ }
+
+ return errno_ad9361_to_bladerf(status);
+}
+
+enum rf_gain_ctrl_mode gainmode_bladerf_to_ad9361(bladerf_gain_mode gainmode,
+ bool *ok)
+{
+ struct bladerf_rfic_gain_mode_map const *mode_map;
+ size_t mode_map_len;
+ size_t i;
+
+ mode_map = bladerf2_rx_gain_mode_map;
+ mode_map_len = ARRAY_SIZE(bladerf2_rx_gain_mode_map);
+
+ if (NULL != ok) {
+ *ok = false;
+ }
+
+ for (i = 0; i < mode_map_len; ++i) {
+ if (mode_map[i].brf_mode == gainmode) {
+ if (NULL != ok) {
+ *ok = true;
+ }
+ return mode_map[i].rfic_mode;
+ }
+ }
+
+ return 0;
+};
+
+bladerf_gain_mode gainmode_ad9361_to_bladerf(enum rf_gain_ctrl_mode gainmode,
+ bool *ok)
+{
+ struct bladerf_rfic_gain_mode_map const *mode_map;
+ size_t mode_map_len;
+ size_t i;
+
+ mode_map = bladerf2_rx_gain_mode_map;
+ mode_map_len = ARRAY_SIZE(bladerf2_rx_gain_mode_map);
+
+ if (NULL != ok) {
+ *ok = false;
+ }
+
+ for (i = 0; i < mode_map_len; ++i) {
+ if (mode_map[i].rfic_mode == gainmode) {
+ if (NULL != ok) {
+ *ok = true;
+ }
+ return mode_map[i].brf_mode;
+ }
+ }
+
+ return 0;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c b/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c
new file mode 100644
index 0000000..aa7bebe
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/ad936x_params.c
@@ -0,0 +1,1024 @@
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "ad9361_api.h"
+#include "platform.h"
+
+/**
+ * Reference:
+ * https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization
+ *
+ * N/A = not applicable due to other setting; changes may unmask these
+ * DEFAULT = changed during device initialization
+ */
+
+// clang-format off
+AD9361_InitParam bladerf2_rfic_init_params = {
+ /* Device selection */
+ ID_AD9361, // AD9361 RF Agile Transceiver // dev_sel
+
+ /* Identification number */
+ 0, // Chip ID 0 // id_no
+
+ /* Reference Clock */
+ 38400000UL, // RefClk = 38.4 MHz // reference_clk_rate
+
+ /* Base Configuration */
+ 1, // use 2Rx2Tx mode // two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num
+ 1, // use FDD mode // frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable
+ 1, // use independent FDD mode // frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable
+ 0, // TX fastlock delay = 0 ns // tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns
+ 0, // RX fastlock delay = 0 ns // rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns
+ 0, // RX fastlock pin control disabled // rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable
+ 0, // TX fastlock pin control disabled // tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable
+ 0, // use internal RX LO // external_rx_lo_enable *** adi,external-rx-lo-enable
+ 0, // use internal TX LO // external_tx_lo_enable *** adi,external-tx-lo-enable
+ 5, // apply new tracking word: on gain change, after exiting RX state // dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask
+ 6, // atten value for DC tracking, RX LO > 4 GHz // dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range
+ 5, // atten value for DC tracking, RX LO < 4 GHz // dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range
+ 0x28, // loop gain for DC tracking, RX LO > 4 GHz // dc_offset_count_high_range *** adi,dc-offset-count-high-range
+ 0x32, // loop gain for DC tracking, RX LO < 4 GHz // dc_offset_count_low_range *** adi,dc-offset-count-low-range
+ 0, // use full gain table // split_gain_table_mode_enable *** adi,split-gain-table-mode-enable
+ MAX_SYNTH_FREF, // f_ref window 80 MHz // trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz
+ 0, // don't use improved RX QEC tracking // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable
+
+ /* ENSM Control */
+ 0, // use level mode on ENABLE and TXNRX pins // ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable
+ 0, // use SPI writes for ENSM state, not ENABLE/TXNRX pins // ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable
+
+ /* LO Control */
+ 2400000000UL, // DEFAULT // rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz
+ 2400000000UL, // DEFAULT // tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz
+
+ /* Rate & BW Control */
+ { 983040000, 245760000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies
+ { 983040000, 122880000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies
+ 18000000, // DEFAULT // rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz
+ 18000000, // DEFAULT // rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz
+
+ /* RF Port Control */
+ 0, // DEFAULT // rx_rf_port_input_select *** adi,rx-rf-port-input-select
+ 0, // DEFAULT // tx_rf_port_input_select *** adi,tx-rf-port-input-select
+
+ /* TX Attenuation Control */
+ 10000, // DEFAULT // tx_attenuation_mdB *** adi,tx-attenuation-mdB
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable
+
+ /* Reference Clock Control */
+ 1, // Expect external clock into XTALN // xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable
+ {3, 5920}, // ~0 ppm DCXO trim (N/A if ext clk) // dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune
+ CLKOUT_DISABLE, // disable clkout pin (see enum ad9361_clkout) // clk_output_mode_select *** adi,clk-output-mode-select
+
+ /* Gain Control */
+ RF_GAIN_SLOWATTACK_AGC, // RX1 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx1_mode *** adi,gc-rx1-mode
+ RF_GAIN_SLOWATTACK_AGC, // RX2 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx2_mode *** adi,gc-rx2-mode
+ 58, // magic AGC setting, see AD9361 docs // gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh
+ 4, // magic AGC setting, see AD9361 docs // gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size
+ 47, // magic AGC setting, see AD9361 docs // gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh
+ 8192, // magic AGC setting, see AD9361 docs // gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration
+ 0, // magic AGC setting, see AD9361 docs // gc_dig_gain_enable *** adi,gc-dig-gain-enable
+ 800, // magic AGC setting, see AD9361 docs // gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh
+ 704, // magic AGC setting, see AD9361 docs // gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh
+ 24, // magic AGC setting, see AD9361 docs // gc_low_power_thresh *** adi,gc-low-power-thresh
+ 15, // magic AGC setting, see AD9361 docs // gc_max_dig_gain *** adi,gc-max-dig-gain
+
+ /* Gain MGC Control */
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_dec_gain_step *** adi,mgc-dec-gain-step
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_inc_gain_step *** adi,mgc-inc-gain-step
+ 0, // don't use CTRL_IN for RX1 MGC stepping // mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable
+ 0, // don't use CTRL_IN for RX2 MGC stepping // mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable
+ 0, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode
+
+ /* Gain AGC Control */
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps
+ 0, // magic AGC setting, see AD9361 docs // agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter
+ 4, // magic AGC setting, see AD9361 docs // agc_dig_gain_step_size *** adi,agc-dig-gain-step-size
+ 3, // magic AGC setting, see AD9361 docs // agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter
+ 1000, // magic AGC setting, see AD9361 docs // agc_gain_update_interval_us *** adi,agc-gain-update-interval-us
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high *** adi,agc-inner-thresh-high
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps
+ 12, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low *** adi,agc-inner-thresh-low
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter
+ 5, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high *** adi,agc-outer-thresh-high
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps
+ 18, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low *** adi,agc-outer-thresh-low
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps
+ 1, // magic AGC setting, see AD9361 docs // agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us
+ 0, // magic AGC setting, see AD9361 docs // agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable
+
+ /* Fast AGC */
+ 64, // magic AGC setting, see AD9361 docs // fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration
+ 260, // magic AGC setting, see AD9361 docs // fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns
+
+ /* Fast AGC - Low Power */
+ 0, // magic AGC setting, see AD9361 docs // fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time
+ 1, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps
+
+ /* Fast AGC - Lock Level */
+ 10, // magic AGC setting, see AD9361 docs // fagc_lock_level *** adi,fagc-lock-level
+ 1, // magic AGC setting, see AD9361 docs // fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit
+
+ /* Fast AGC - Peak Detectors and Final Settling */
+ 1, // magic AGC setting, see AD9361 docs // fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps
+ 1, // magic AGC setting, see AD9361 docs // fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps
+ 3, // magic AGC setting, see AD9361 docs // fagc_final_overrange_count *** adi,fagc-final-overrange-count
+
+ /* Fast AGC - Final Power Test */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable
+
+ /* Fast AGC - Unlocking the Gain */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode
+ 1, // magic AGC setting, see AD9361 docs // fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll
+ 8, // magic AGC setting, see AD9361 docs // fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode
+ 64, // magic AGC setting, see AD9361 docs // fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5
+
+ /* RSSI Control */
+ 1, // settling delay on RSSI algo restart = 1 μs // rssi_delay *** adi,rssi-delay
+ 1000, // total RSSI measurement duration = 1000 μs // rssi_duration *** adi,rssi-duration
+ GAIN_CHANGE_OCCURS, // reset RSSI accumulator on gain change event // rssi_restart_mode *** adi,rssi-restart-mode
+ 0, // RSSI control values are in microseconds // rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable
+ 1, // wait 1 μs between RSSI measurements // rssi_wait *** adi,rssi-wait
+
+ /* Aux ADC Control */
+ /* bladeRF Micro: N/A, pin tied to GND */
+ 256, // AuxADC decimate by 256 // aux_adc_decimation *** adi,aux-adc-decimation
+ 40000000UL, // AuxADC sample rate 40 MHz // aux_adc_rate *** adi,aux-adc-rate
+
+ /* AuxDAC Control */
+ /* bladeRF Micro: AuxDAC1 is TP7 and AUXDAC_TRIM, AuxDAC2 is TP8 */
+ 1, // AuxDAC does not slave the ENSM // aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable
+ 0, // AuxDAC1 default value = 0 mV // aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us
+ 0, // AuxDAC2 default value = 0 mV // aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us
+
+ /* Temperature Sensor Control */
+ 256, // Temperature sensor decimate by 256 // temp_sense_decimation *** adi,temp-sense-decimation
+ 1000, // Measure temperature every 1000 ms // temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms
+ 206, // Offset = +206 degrees C // temp_sense_offset_signed *** adi,temp-sense-offset-signed
+ 1, // Periodic temperature measurements enabled // temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable
+
+ /* Control Out Setup */
+ /* See https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization#control_output_setup */
+ 0xFF, // Enable all CTRL_OUT bits // ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask
+ 0, // CTRL_OUT index is 0 // ctrl_outs_index *** adi,ctrl-outs-index
+
+ /* External LNA Control */
+ /* bladeRF Micro: GPO_0 is TP3, GPO_1 is TP4 */
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_settling_delay_ns *** adi,elna-settling-delay-ns
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gain_mdB *** adi,elna-gain-mdB
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB
+ 0, // Ext LNA Ctrl bit in Rx1 gain table does NOT set GPO0 state // elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable
+ 0, // Ext LNA Ctrl bit in Rx2 gain table does NOT set GPO1 state // elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable
+
+ /* Digital Interface Control */
+#ifdef ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ /* Calibrate the digital interface delay (hardware validation) */
+ 0, // Don't skip digital interface tuning // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#else
+ /* Use hardcoded digital interface delay values (production) */
+ 2, // Skip RX and TX tuning; use hardcoded values below // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#endif // ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ 0, // ?? UNDOCUMENTED ?? // digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable
+ 1, // Swap I and Q (spectral inversion) // pp_tx_swap_enable *** adi,pp-tx-swap-enable
+ 1, // Swap I and Q (spectral inversion) // pp_rx_swap_enable *** adi,pp-rx-swap-enable
+ 0, // Don't swap TX1 and TX2 // tx_channel_swap_enable *** adi,tx-channel-swap-enable
+ 0, // Don't swap RX1 and RX2 // rx_channel_swap_enable *** adi,rx-channel-swap-enable
+ 1, // Toggle RX_FRAME with 50% duty cycle // rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable
+ 0, // Data port timing reflects # of enabled signal paths // two_t_two_r_timing_enable *** adi,2t2r-timing-enable
+ 0, // Don't invert data bus // invert_data_bus_enable *** adi,invert-data-bus-enable
+ 0, // Don't invert data clock // invert_data_clk_enable *** adi,invert-data-clk-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable
+ 0, // Don't invert RX_FRAME // invert_rx_frame_enable *** adi,invert-rx-frame-enable
+ 0, // Don't make RX sample rate 2x the TX sample rate // fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // swap_ports_enable *** adi,swap-ports-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_data_rate_enable *** adi,single-data-rate-enable
+ 1, // Use LVDS mode on data port // lvds_mode_enable *** adi,lvds-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // half_duplex_mode_enable *** adi,half-duplex-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_port_mode_enable *** adi,single-port-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_port_enable *** adi,full-port-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable
+ 0, // RX_DATA delay rel to RX_FRAME = 0 DATA_CLK/2 cycles // delay_rx_data *** adi,delay-rx-data
+ // Approx 0.3 ns/LSB on next 4 values
+ 5, // DATA_CLK delay = 1.5 ns // rx_data_clock_delay *** adi,rx-data-clock-delay
+ 0, // RX_DATA/RX_FRAME delay = 0 ns // rx_data_delay *** adi,rx-data-delay
+ 0, // FB_CLK delay = 0 ns // tx_fb_clock_delay *** adi,tx-fb-clock-delay
+ 5, // TX_DATA/TX_FRAME delay = 1.5 ns // tx_data_delay *** adi,tx-data-delay
+ 300, // LVDS driver bias 300 mV // lvds_bias_mV *** adi,lvds-bias-mV
+ 1, // Enable LVDS on-chip termination // lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable
+ 1, // RX1 and RX2 are not phase-aligned // rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable
+ 0xFF, // Default signal inversion mappings // lvds_invert1_control *** adi,lvds-invert1-control
+ 0x0F, // Default signal inversion mappings // lvds_invert2_control *** adi,lvds-invert2-control
+ 1, // CLK_OUT drive increased by ~20% // clk_out_drive
+ 1, // DATA_CLK drive increased by ~20% // dataclk_drive
+ 1, // Data port drive increased by ~20% // data_port_drive
+ 0, // CLK_OUT minimum slew (fastest rise/fall) // clk_out_slew
+ 0, // DATA_CLK minimum slew (fastest rise/fall) // dataclk_slew
+ 0, // Data port minimum slew (fastest rise/fall) // data_port_slew
+
+ /* GPO Control */
+ 0, // GPO0 is LOW in Sleep/Wait/Alert states // gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable
+ 0, // GPO1 is LOW in Sleep/Wait/Alert states // gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable
+ 0, // GPO2 is LOW in Sleep/Wait/Alert states // gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable
+ 0, // GPO3 is LOW in Sleep/Wait/Alert states // gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable
+ 0, // GPO0 does not change state when entering RX state // gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable
+ 0, // GPO0 does not change state when entering TX state // gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable
+ 0, // GPO1 does not change state when entering RX state // gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable
+ 0, // GPO1 does not change state when entering TX state // gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable
+ 0, // GPO2 does not change state when entering RX state // gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable
+ 0, // GPO2 does not change state when entering TX state // gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable
+ 0, // GPO3 does not change state when entering RX state // gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable
+ 0, // GPO3 does not change state when entering TX state // gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable
+ 0, // N/A when gpo0_slave_rx_enable = 0 // gpo0_rx_delay_us *** adi,gpo0-rx-delay-us
+ 0, // N/A when gpo0_slave_tx_enable = 0 // gpo0_tx_delay_us *** adi,gpo0-tx-delay-us
+ 0, // N/A when gpo1_slave_rx_enable = 0 // gpo1_rx_delay_us *** adi,gpo1-rx-delay-us
+ 0, // N/A when gpo1_slave_tx_enable = 0 // gpo1_tx_delay_us *** adi,gpo1-tx-delay-us
+ 0, // N/A when gpo2_slave_rx_enable = 0 // gpo2_rx_delay_us *** adi,gpo2-rx-delay-us
+ 0, // N/A when gpo2_slave_tx_enable = 0 // gpo2_tx_delay_us *** adi,gpo2-tx-delay-us
+ 0, // N/A when gpo3_slave_rx_enable = 0 // gpo3_rx_delay_us *** adi,gpo3-rx-delay-us
+ 0, // N/A when gpo3_slave_tx_enable = 0 // gpo3_tx_delay_us *** adi,gpo3-tx-delay-us
+
+ /* Tx Monitor Control */
+ /* bladeRF Micro: N/A, TX_MON1 and TX_MON2 tied to GND */
+ 37000, // N/A // low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh
+ 0, // N/A // low_gain_dB *** adi,txmon-low-gain
+ 24, // N/A // high_gain_dB *** adi,txmon-high-gain
+ 0, // N/A // tx_mon_track_en *** adi,txmon-dc-tracking-enable
+ 0, // N/A // one_shot_mode_en *** adi,txmon-one-shot-mode-enable
+ 511, // N/A // tx_mon_delay *** adi,txmon-delay
+ 8192, // N/A // tx_mon_duration *** adi,txmon-duration
+ 2, // N/A // tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain
+ 2, // N/A // tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain
+ 48, // N/A // tx1_mon_lo_cm *** adi,txmon-1-lo-cm
+ 48, // N/A // tx2_mon_lo_cm *** adi,txmon-2-lo-cm
+
+ /* GPIO definitions */
+ RFFE_CONTROL_RESET_N, // Reset using RFFE bit 0 // gpio_resetb *** reset-gpios
+
+ /* MCS Sync */
+ -1, // Future use (MCS Sync) // gpio_sync *** sync-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw1 *** cal-sw1-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw2 *** cal-sw2-gpios
+
+ /* External LO clocks */
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_recalc_rate)()
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_round_rate)()
+ NULL // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_set_rate)()
+};
+// clang-format on
+
+
+// clang-format off
+AD9361_InitParam bladerf2_rfic_init_params_fastagc_burst = {
+ /* Device selection */
+ ID_AD9361, // AD9361 RF Agile Transceiver // dev_sel
+
+ /* Identification number */
+ 0, // Chip ID 0 // id_no
+
+ /* Reference Clock */
+ 38400000UL, // RefClk = 38.4 MHz // reference_clk_rate
+
+ /* Base Configuration */
+ 1, // use 2Rx2Tx mode // two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num
+ 1, // N/A when two_rx_two_tx_mode_enable = 1 // one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num
+ 1, // use FDD mode // frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable
+ 1, // use independent FDD mode // frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable
+ 0, // TX fastlock delay = 0 ns // tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns
+ 0, // RX fastlock delay = 0 ns // rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns
+ 0, // RX fastlock pin control disabled // rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable
+ 0, // TX fastlock pin control disabled // tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable
+ 0, // use internal RX LO // external_rx_lo_enable *** adi,external-rx-lo-enable
+ 0, // use internal TX LO // external_tx_lo_enable *** adi,external-tx-lo-enable
+ 5, // apply new tracking word: on gain change, after exiting RX state // dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask
+ 6, // atten value for DC tracking, RX LO > 4 GHz // dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range
+ 5, // atten value for DC tracking, RX LO < 4 GHz // dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range
+ 0x28, // loop gain for DC tracking, RX LO > 4 GHz // dc_offset_count_high_range *** adi,dc-offset-count-high-range
+ 0x32, // loop gain for DC tracking, RX LO < 4 GHz // dc_offset_count_low_range *** adi,dc-offset-count-low-range
+ 0, // use full gain table // split_gain_table_mode_enable *** adi,split-gain-table-mode-enable
+ MAX_SYNTH_FREF, // f_ref window 80 MHz // trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz
+ 0, // don't use improved RX QEC tracking // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable
+
+ /* ENSM Control */
+ 0, // use level mode on ENABLE and TXNRX pins // ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable
+ 0, // use SPI writes for ENSM state, not ENABLE/TXNRX pins // ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable
+
+ /* LO Control */
+ 2400000000UL, // DEFAULT // rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz
+ 2400000000UL, // DEFAULT // tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz
+
+ /* Rate & BW Control */
+ { 983040000, 245760000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies
+ { 983040000, 122880000, 122880000, 61440000, 30720000, 30720000 }, // DEFAULT // uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies
+ 18000000, // DEFAULT // rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz
+ 18000000, // DEFAULT // rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz
+
+ /* RF Port Control */
+ 0, // DEFAULT // rx_rf_port_input_select *** adi,rx-rf-port-input-select
+ 0, // DEFAULT // tx_rf_port_input_select *** adi,tx-rf-port-input-select
+
+ /* TX Attenuation Control */
+ 10000, // DEFAULT // tx_attenuation_mdB *** adi,tx-attenuation-mdB
+ 0, // N/A when frequency_division_duplex_mode_enable = 1 // update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable
+
+ /* Reference Clock Control */
+ 1, // Expect external clock into XTALN // xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable
+ {3, 5920}, // ~0 ppm DCXO trim (N/A if ext clk) // dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune
+ CLKOUT_DISABLE, // disable clkout pin (see enum ad9361_clkout) // clk_output_mode_select *** adi,clk-output-mode-select
+
+ /* Gain Control */
+ RF_GAIN_FASTATTACK_AGC, // RX1 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx1_mode *** adi,gc-rx1-mode
+ RF_GAIN_FASTATTACK_AGC, // RX2 BLADERF_GAIN_DEFAULT = slow attack AGC // gc_rx2_mode *** adi,gc-rx2-mode
+ 58, // magic AGC setting, see AD9361 docs // gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh
+ 4, // magic AGC setting, see AD9361 docs // gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size
+ 47, // magic AGC setting, see AD9361 docs // gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh
+ 2, // magic AGC setting, see AD9361 docs // gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration
+ 0, // magic AGC setting, see AD9361 docs // gc_dig_gain_enable *** adi,gc-dig-gain-enable
+ 480, // magic AGC setting, see AD9361 docs // gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh
+ 400, // magic AGC setting, see AD9361 docs // gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh
+ 40, // magic AGC setting, see AD9361 docs // gc_low_power_thresh *** adi,gc-low-power-thresh
+ 15, // magic AGC setting, see AD9361 docs // gc_max_dig_gain *** adi,gc-max-dig-gain
+
+ /* Gain MGC Control */
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_dec_gain_step *** adi,mgc-dec-gain-step
+ 2, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_inc_gain_step *** adi,mgc-inc-gain-step
+ 0, // don't use CTRL_IN for RX1 MGC stepping // mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable
+ 0, // don't use CTRL_IN for RX2 MGC stepping // mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable
+ 0, // N/A when mgc_rx(1,2)_ctrl_inp_enable = 0 // mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode
+
+ /* Gain AGC Control */
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps
+ 0, // magic AGC setting, see AD9361 docs // agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter
+ 4, // magic AGC setting, see AD9361 docs // agc_dig_gain_step_size *** adi,agc-dig-gain-step-size
+ 3, // magic AGC setting, see AD9361 docs // agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter
+ 1, // magic AGC setting, see AD9361 docs // agc_gain_update_interval_us *** adi,agc-gain-update-interval-us
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable
+ 10, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high *** adi,agc-inner-thresh-high
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps
+ 12, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low *** adi,agc-inner-thresh-low
+ 1, // magic AGC setting, see AD9361 docs // agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter
+ 2, // magic AGC setting, see AD9361 docs // agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps
+ 10, // magic AGC setting, see AD9361 docs // agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter
+ 5, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high *** adi,agc-outer-thresh-high
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps
+ 18, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low *** adi,agc-outer-thresh-low
+ 2, // magic AGC setting, see AD9361 docs // agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps
+ 1, // magic AGC setting, see AD9361 docs // agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us
+ 0, // magic AGC setting, see AD9361 docs // agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable
+
+ /* Fast AGC */
+ 16, // magic AGC setting, see AD9361 docs // fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration
+ 260, // magic AGC setting, see AD9361 docs // fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns
+
+ /* Fast AGC - Low Power */
+ 0, // magic AGC setting, see AD9361 docs // fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time
+ 7, // magic AGC setting, see AD9361 docs // fagc.lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps
+
+ /* Fast AGC - Lock Level */
+ 10, // magic AGC setting, see AD9361 docs // fagc_lock_level *** adi,fagc-lock-level
+ 1, // magic AGC setting, see AD9361 docs // fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable
+ 63, // magic AGC setting, see AD9361 docs // fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit
+
+ /* Fast AGC - Peak Detectors and Final Settling */
+ 1, // magic AGC setting, see AD9361 docs // fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps
+ 1, // magic AGC setting, see AD9361 docs // fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps
+ 3, // magic AGC setting, see AD9361 docs // fagc_final_overrange_count *** adi,fagc-final-overrange-count
+
+ /* Fast AGC - Final Power Test */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable
+
+ /* Fast AGC - Unlocking the Gain */
+ 0, // magic AGC setting, see AD9361 docs // fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode
+ 1, // magic AGC setting, see AD9361 docs // fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable
+ 5, // magic AGC setting, see AD9361 docs // fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable
+ 10, // magic AGC setting, see AD9361 docs // fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll
+ 3, // magic AGC setting, see AD9361 docs // fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable
+ 1, // magic AGC setting, see AD9361 docs // fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable
+ 0, // magic AGC setting, see AD9361 docs // fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode
+ 64, // magic AGC setting, see AD9361 docs // fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5
+
+ /* RSSI Control */
+ 1, // settling delay on RSSI algo restart = 1 μs // rssi_delay *** adi,rssi-delay
+ 1000, // total RSSI measurement duration = 1000 μs // rssi_duration *** adi,rssi-duration
+ GAIN_CHANGE_OCCURS, // reset RSSI accumulator on gain change event // rssi_restart_mode *** adi,rssi-restart-mode
+ 0, // RSSI control values are in microseconds // rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable
+ 1, // wait 1 μs between RSSI measurements // rssi_wait *** adi,rssi-wait
+
+ /* Aux ADC Control */
+ /* bladeRF Micro: N/A, pin tied to GND */
+ 256, // AuxADC decimate by 256 // aux_adc_decimation *** adi,aux-adc-decimation
+ 40000000UL, // AuxADC sample rate 40 MHz // aux_adc_rate *** adi,aux-adc-rate
+
+ /* AuxDAC Control */
+ /* bladeRF Micro: AuxDAC1 is TP7 and AUXDAC_TRIM, AuxDAC2 is TP8 */
+ 1, // AuxDAC does not slave the ENSM // aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable
+ 0, // AuxDAC1 default value = 0 mV // aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us
+ 0, // AuxDAC2 default value = 0 mV // aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us
+ 0, // N/A when aux_dac_manual_mode_enable = 1 // aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us
+
+ /* Temperature Sensor Control */
+ 256, // Temperature sensor decimate by 256 // temp_sense_decimation *** adi,temp-sense-decimation
+ 1000, // Measure temperature every 1000 ms // temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms
+ 206, // Offset = +206 degrees C // temp_sense_offset_signed *** adi,temp-sense-offset-signed
+ 1, // Periodic temperature measurements enabled // temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable
+
+ /* Control Out Setup */
+ /* See https://wiki.analog.com/resources/tools-software/linux-drivers/iio-transceiver/ad9361-customization#control_output_setup */
+ 0xFF, // Enable all CTRL_OUT bits // ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask
+ 7, // CTRL_OUT index is 0 // ctrl_outs_index *** adi,ctrl-outs-index
+
+ /* External LNA Control */
+ /* bladeRF Micro: GPO_0 is TP3, GPO_1 is TP4 */
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_settling_delay_ns *** adi,elna-settling-delay-ns
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gain_mdB *** adi,elna-gain-mdB
+ 0, // MUST be 0 when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB
+ 0, // Ext LNA Ctrl bit in Rx1 gain table does NOT set GPO0 state // elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable
+ 0, // Ext LNA Ctrl bit in Rx2 gain table does NOT set GPO1 state // elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable
+ 0, // N/A when elna_rx(1,2)_gpo(0,1)_control_enable = 0 // elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable
+
+ /* Digital Interface Control */
+#ifdef ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ /* Calibrate the digital interface delay (hardware validation) */
+ 0, // Don't skip digital interface tuning // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#else
+ /* Use hardcoded digital interface delay values (production) */
+ 2, // Skip RX and TX tuning; use hardcoded values below // digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode
+#endif // ENABLE_AD9361_DIGITAL_INTERFACE_TIMING_VERIFICATION
+ 0, // ?? UNDOCUMENTED ?? // digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable
+ 1, // Swap I and Q (spectral inversion) // pp_tx_swap_enable *** adi,pp-tx-swap-enable
+ 1, // Swap I and Q (spectral inversion) // pp_rx_swap_enable *** adi,pp-rx-swap-enable
+ 0, // Don't swap TX1 and TX2 // tx_channel_swap_enable *** adi,tx-channel-swap-enable
+ 0, // Don't swap RX1 and RX2 // rx_channel_swap_enable *** adi,rx-channel-swap-enable
+ 1, // Toggle RX_FRAME with 50% duty cycle // rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable
+ 0, // Data port timing reflects # of enabled signal paths // two_t_two_r_timing_enable *** adi,2t2r-timing-enable
+ 0, // Don't invert data bus // invert_data_bus_enable *** adi,invert-data-bus-enable
+ 0, // Don't invert data clock // invert_data_clk_enable *** adi,invert-data-clk-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable
+ 0, // Don't invert RX_FRAME // invert_rx_frame_enable *** adi,invert-rx-frame-enable
+ 0, // Don't make RX sample rate 2x the TX sample rate // fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // swap_ports_enable *** adi,swap-ports-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_data_rate_enable *** adi,single-data-rate-enable
+ 1, // Use LVDS mode on data port // lvds_mode_enable *** adi,lvds-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // half_duplex_mode_enable *** adi,half-duplex-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // single_port_mode_enable *** adi,single-port-mode-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_port_enable *** adi,full-port-enable
+ 0, // MUST be 0 when lvds_mode_enable = 1 // full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable
+ 0, // RX_DATA delay rel to RX_FRAME = 0 DATA_CLK/2 cycles // delay_rx_data *** adi,delay-rx-data
+ // Approx 0.3 ns/LSB on next 4 values
+ 5, // DATA_CLK delay = 1.5 ns // rx_data_clock_delay *** adi,rx-data-clock-delay
+ 0, // RX_DATA/RX_FRAME delay = 0 ns // rx_data_delay *** adi,rx-data-delay
+ 0, // FB_CLK delay = 0 ns // tx_fb_clock_delay *** adi,tx-fb-clock-delay
+ 5, // TX_DATA/TX_FRAME delay = 1.5 ns // tx_data_delay *** adi,tx-data-delay
+ 300, // LVDS driver bias 300 mV // lvds_bias_mV *** adi,lvds-bias-mV
+ 1, // Enable LVDS on-chip termination // lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable
+ 1, // RX1 and RX2 are not phase-aligned // rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable
+ 0xFF, // Default signal inversion mappings // lvds_invert1_control *** adi,lvds-invert1-control
+ 0x0F, // Default signal inversion mappings // lvds_invert2_control *** adi,lvds-invert2-control
+ 1, // CLK_OUT drive increased by ~20% // clk_out_drive
+ 1, // DATA_CLK drive increased by ~20% // dataclk_drive
+ 1, // Data port drive increased by ~20% // data_port_drive
+ 0, // CLK_OUT minimum slew (fastest rise/fall) // clk_out_slew
+ 0, // DATA_CLK minimum slew (fastest rise/fall) // dataclk_slew
+ 0, // Data port minimum slew (fastest rise/fall) // data_port_slew
+
+ /* GPO Control */
+ 0, // GPO0 is LOW in Sleep/Wait/Alert states // gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable
+ 0, // GPO1 is LOW in Sleep/Wait/Alert states // gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable
+ 0, // GPO2 is LOW in Sleep/Wait/Alert states // gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable
+ 0, // GPO3 is LOW in Sleep/Wait/Alert states // gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable
+ 0, // GPO0 does not change state when entering RX state // gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable
+ 0, // GPO0 does not change state when entering TX state // gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable
+ 0, // GPO1 does not change state when entering RX state // gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable
+ 0, // GPO1 does not change state when entering TX state // gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable
+ 0, // GPO2 does not change state when entering RX state // gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable
+ 0, // GPO2 does not change state when entering TX state // gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable
+ 0, // GPO3 does not change state when entering RX state // gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable
+ 0, // GPO3 does not change state when entering TX state // gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable
+ 0, // N/A when gpo0_slave_rx_enable = 0 // gpo0_rx_delay_us *** adi,gpo0-rx-delay-us
+ 0, // N/A when gpo0_slave_tx_enable = 0 // gpo0_tx_delay_us *** adi,gpo0-tx-delay-us
+ 0, // N/A when gpo1_slave_rx_enable = 0 // gpo1_rx_delay_us *** adi,gpo1-rx-delay-us
+ 0, // N/A when gpo1_slave_tx_enable = 0 // gpo1_tx_delay_us *** adi,gpo1-tx-delay-us
+ 0, // N/A when gpo2_slave_rx_enable = 0 // gpo2_rx_delay_us *** adi,gpo2-rx-delay-us
+ 0, // N/A when gpo2_slave_tx_enable = 0 // gpo2_tx_delay_us *** adi,gpo2-tx-delay-us
+ 0, // N/A when gpo3_slave_rx_enable = 0 // gpo3_rx_delay_us *** adi,gpo3-rx-delay-us
+ 0, // N/A when gpo3_slave_tx_enable = 0 // gpo3_tx_delay_us *** adi,gpo3-tx-delay-us
+
+ /* Tx Monitor Control */
+ /* bladeRF Micro: N/A, TX_MON1 and TX_MON2 tied to GND */
+ 37000, // N/A // low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh
+ 0, // N/A // low_gain_dB *** adi,txmon-low-gain
+ 24, // N/A // high_gain_dB *** adi,txmon-high-gain
+ 0, // N/A // tx_mon_track_en *** adi,txmon-dc-tracking-enable
+ 0, // N/A // one_shot_mode_en *** adi,txmon-one-shot-mode-enable
+ 511, // N/A // tx_mon_delay *** adi,txmon-delay
+ 8192, // N/A // tx_mon_duration *** adi,txmon-duration
+ 2, // N/A // tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain
+ 2, // N/A // tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain
+ 48, // N/A // tx1_mon_lo_cm *** adi,txmon-1-lo-cm
+ 48, // N/A // tx2_mon_lo_cm *** adi,txmon-2-lo-cm
+
+ /* GPIO definitions */
+ RFFE_CONTROL_RESET_N, // Reset using RFFE bit 0 // gpio_resetb *** reset-gpios
+
+ /* MCS Sync */
+ -1, // Future use (MCS Sync) // gpio_sync *** sync-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw1 *** cal-sw1-gpios
+ -1, // Future use (MCS Sync) // gpio_cal_sw2 *** cal-sw2-gpios
+
+ /* External LO clocks */
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_recalc_rate)()
+ NULL, // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_round_rate)()
+ NULL // Future use (RX_EXT_LO, TX_EXT_LO control) // (*ad9361_rfpll_ext_set_rate)()
+};
+// clang-format on
+
+/**
+ * AD9361 FIR Filters
+ *
+ * The AD9361 RFIC provides programmable FIR filters on both the RX and TX
+ * paths.
+ *
+ * On TX, the signal path is:
+ *
+ * DIGITAL:
+ * [ Programmable TX FIR ] -> [ HB1 ] -> [ HB2 ] -> [ HB3/INT3 ] -> [ DAC ]
+ * ANALOG:
+ * [ DAC ] -> [ BB LPF ] -> [ Secondary LPF ] -> ...
+ *
+ * The Programmable TX FIR is a programmable polyphase FIR filter, which can
+ * interpolate by 1, 2, 4, or be bypassed. Taps are stored in 16-bit
+ * twos-complement. If interpolating by 1, there is a limit of 64 taps;
+ * otherwise, the limit is 128 taps.
+ *
+ * HB1 and HB2 are fixed-coefficient half-band interpolating filters, and can
+ * interpolate by 2 or be bypassed. HB3/INT3 is a fixed-coefficient
+ * interpolating filter, and can interpolate by 2 or 3, or be bypassed.
+ *
+ * BB LPF is a third-order Butterworth LPF, and the Secondary LPF is a
+ * single-pole low-pass filter. Both have programmable corner frequencies.
+ *
+ * On RX, the signal path is:
+ *
+ * ANALOG:
+ * ... -> [ TIA LPF ] -> [ BB LPF ] -> [ ADC ]
+ * DIGITAL:
+ * [ ADC ] -> [ HB3/DEC3 ] -> [ HB2 ] -> [ HB1 ] -> [ Programmable RX FIR ]
+ *
+ * The TIA LPF is a transimpedance amplifier which applies a single-pole
+ * low-pass filter, and the BB LPF is a third-order Butterworth low-pass filter.
+ * Both have programmable corner frequencies.
+ *
+ * HB3/DEC3 is a fixed-coefficient decimating filter, and can decimate by a
+ * factor of 2 or 3, or be bypassed. HB2 is a fixed-coefficient half-band
+ * decimating filter, and can decimate by a factor of 2 or be bypassed. HB1 is a
+ * fixed-coefficient half-band decimating filter, and can also decimate by a
+ * factor of 2 or be bypassed.
+ *
+ * The Programmable RX FIR filter is a programmable polyphase filter, which can
+ * decimate by a factor of 1, 2, or 4, or be bypassed. Similar to the TX FIR,
+ * taps are stored in 16-bit twos-complement. The maximum number of taps is
+ * limited to the ratio of the sample clock to the filter's output rate,
+ * multiplied by 16, up to a maximum of 128 taps. There is a fixed +6 dB gain,
+ * so the below RX filters are configured for a -6 dB gain to effect a net gain
+ * of 0 dB.
+ *
+ * In practice, the decimation/interpolation settings must match for both the RX
+ * and TX FIR filters. If they differ, TX quadrature calibration (and likely
+ * other calibrations) will fail.
+ *
+ *
+ * This file specifies four filters:
+ * bladerf2_rfic_rx_fir_config = decimate by 1 RX FIR
+ * bladerf2_rfic_tx_fir_config = interpolate by 1 TX FIR
+ * bladerf2_rfic_rx_fir_config_dec2 = decimate by 2 RX FIR
+ * bladerf2_rfic_tx_fir_config_int2 = interpolate by 2 TX FIR
+ *
+ * The first two (the 1x filters) are the default, and should provide reasonable
+ * performance under most circumstances. The other two filters are primarily
+ * intended for situations requiring a flatter TX spectrum, particularly when
+ * the ratio of sample rate to signal bandwidth is low.
+ */
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 1, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the RX spectrum. It is
+ * a 64-tap, decimate-by-1 filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 30720000;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 1;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.42;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = -1;
+ * fdp.wnom = 17920000;
+ * fdp.caldiv = 7;
+ * fdp.RFbw = 22132002;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 0, 0, 0, 1, -1, 3, -6, 11,
+ -19, 33, -53, 84, -129, 193, -282, 404,
+ -565, 777, -1052, 1401, -1841, 2390, -3071, 3911,
+ -4947, 6230, -7833, 9888, -12416, 15624, -21140, 32767,
+ 32767, -21140, 15624, -12416, 9888, -7833, 6230, -4947,
+ 3911, -3071, 2390, -1841, 1401, -1052, 777, -565,
+ 404, -282, 193, -129, 84, -53, 33, -19,
+ 11, -6, 3, -1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ }, // rx_coef[128]
+ // clang-format on
+ 64, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 1, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ *
+ * This filter literally does nothing, but it is here as a placeholder.
+ */
+ // clang-format off
+ {
+ 32767, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ 0, 0, 0, 0, 0, 0, 0, 0, // unused
+ }, // tx_coef[128]
+ // clang-format on
+ 64, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec2 = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 2, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the RX spectrum.
+ *
+ * It is a 128-tap, decimate-by-2 filter. Note that you MUST use a
+ * interpolate-by-2 filter on TX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 15360000;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 2;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.45;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.1250;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 8800000;
+ * fdp.caldiv = 19;
+ * fdp.RFbw = 8472407;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 22, 125, 207, 190, 15, -98, -45, 91,
+ 60, -76, -90, 69, 115, -47, -147, 22,
+ 173, 18, -198, -66, 211, 127, -214, -194,
+ 200, 269, -168, -345, 113, 419, -36, -484,
+ -66, 536, 193, -566, -343, 568, 513, -535,
+ -699, 458, 897, -329, -1099, 140, 1296, 120,
+ -1479, -464, 1636, 912, -1750, -1496, 1797, 2275,
+ -1734, -3378, 1464, 5120, -659, -8461, -2238, 18338,
+ 32689, 24727, 4100, -7107, -2663, 4128, 2513, -2578,
+ -2378, 1567, 2184, -861, -1953, 351, 1703, 22,
+ -1446, -290, 1190, 474, -942, -590, 710, 650,
+ -498, -664, 311, 642, -150, -593, 18, 524,
+ 86, -444, -162, 357, 211, -271, -238, 189,
+ 245, -116, -237, 53, 216, -2, -187, -37,
+ 154, 64, -119, -82, 87, 89, -56, -96,
+ 30, 99, 3, -120, -107, 0, 56, 45,
+ }, // rx_coef[128]
+ // clang-format on
+ 128, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int2 = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 2, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to improve the flatness of the TX spectrum.
+ *
+ * It is a 128-tap, interpolate-by-2 filter. Note that you MUST use a
+ * decimate-by-2 filter on RX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 15360000;
+ * fdp.RxTx = "Tx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 2;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 2;
+ * fdp.PLL_mult = 4;
+ * fdp.converter_rate = 245760000;
+ * fdp.PLL_rate = 983040000;
+ * fdp.Fpass = fdp.Rdata*0.45;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.1250;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 8800000;
+ * fdp.caldiv = 19;
+ * fdp.RFbw = 8472407;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ 20, 104, 183, 161, 0, -129, -82, 61,
+ 69, -65, -108, 31, 117, -15, -145, -23,
+ 155, 61, -167, -113, 163, 167, -149, -227,
+ 116, 286, -67, -342, -3, 388, 91, -421,
+ -197, 433, 321, -420, -457, 376, 602, -294,
+ -749, 171, 891, 1, -1019, -225, 1123, 507,
+ -1190, -855, 1205, 1279, -1148, -1800, 984, 2456,
+ -656, -3329, 31, 4619, 1275, -6897, -4889, 12679,
+ 29822, 27710, 9244, -5193, -4330, 2732, 3367, -1405,
+ -2793, 571, 2318, -25, -1901, -338, 1527, 574,
+ -1189, -716, 885, 787, -617, -802, 383, 774,
+ -187, -714, 25, 632, 101, -535, -193, 432,
+ 254, -330, -289, 232, 299, -143, -291, 66,
+ 267, -3, -234, -46, 192, 79, -153, -103,
+ 107, 109, -76, -117, 32, 103, -19, -115,
+ -35, 83, 34, -120, -204, -134, -42, 12,
+ }, // tx_coef[128]
+ // clang-format on
+ 128, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+AD9361_RXFIRConfig bladerf2_rfic_rx_fir_config_dec4 = {
+ 3, // rx (RX1 = 1, RX2 = 2, both = 3)
+ -6, // rx_gain (-12, -6, 0, or 6 dB)
+ 4, // rx_dec (decimate by 1, 2, or 4)
+
+ /**
+ * RX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to allow sample rates down to 520834 sps.
+ *
+ * It is a 128-tap, decimate-by-4 filter. Note that you MUST use a
+ * interpolate-by-4 filter on TX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 520834;
+ * fdp.RxTx = "Rx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 3;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 4;
+ * fdp.PLL_mult = 32;
+ * fdp.converter_rate = 25000000;
+ * fdp.PLL_rate = 800000000;
+ * fdp.Fpass = fdp.Rdata*0.375;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 347220;
+ * fdp.caldiv = 309;
+ * fdp.RFbw = 433256;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ -30, -24, -46, -54, -28, -6, 50, 82,
+ 108, 84, 28, -60, -136, -172, -132, -24,
+ 122, 244, 280, 192, -2, -238, -410, -428,
+ -250, 80, 436, 656, 614, 276, -252, -760,
+ -1006, -830, -234, 580, 1272, 1494, 1060, 48,
+ -1178, -2086, -2192, -1284, 420, 2296, 3504, 3324,
+ 1502, -1544, -4746, -6664, -5978, -1984, 5054, 13852,
+ 22416, 28606, 30790, 28370, 21974, 13264, 4422, -2522,
+ -6282, -6630, -4358, -892, 2236, 3914, 3762, 2144,
+ -80, -1948, -2774, -2376, -1078, 486, 1652, 2008,
+ 1510, 466, -640, -1352, -1434, -928, -110, 652,
+ 1060, 990, 532, -82, -586, -792, -654, -274,
+ 164, 480, 562, 410, 118, -178, -362, -378,
+ -244, -34, 154, 256, 240, 136, -4, -116,
+ -168, -144, -74, 14, 74, 102, 76, 38,
+ -28, -54, -96, -68, -82, -26, -34, -2,
+ }, // rx_coef[128]
+ // clang-format on
+ 128, // rx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // rx_path_clks[6]
+ 0 // rx_bandwidth
+};
+
+AD9361_TXFIRConfig bladerf2_rfic_tx_fir_config_int4 = {
+ 3, // tx (TX1 = 1, TX2 = 2, both = 3)
+ 0, // tx_gain (-6 or 0 dB)
+ 4, // tx_int (interpolate by 1, 2, or 4)
+
+ /**
+ * TX FIR Filter
+ * Built using https://github.com/analogdevicesinc/libad9361-iio
+ * Branch: filter_generation
+ * Commit: f749cef974f687f696226455dc7684277886cf3b
+ *
+ * This filter is intended to allow sample rates down to 520834 sps.
+ *
+ * It is a 128-tap, interpolate-by-4 filter. Note that you MUST use a
+ * decimate-by-4 filter on RX if you are using this filter.
+ *
+ * Design parameters:
+ *
+ * fdp.Rdata = 520834;
+ * fdp.RxTx = "Tx";
+ * fdp.Type = "Lowpass";
+ * fdp.DAC_div = 1;
+ * fdp.HB3 = 3;
+ * fdp.HB2 = 2;
+ * fdp.HB1 = 2;
+ * fdp.FIR = 4;
+ * fdp.PLL_mult = 32;
+ * fdp.converter_rate = 25000000;
+ * fdp.PLL_rate = 800000000;
+ * fdp.Fpass = fdp.Rdata*0.375;
+ * fdp.Fstop = fdp.Rdata*0.50;
+ * fdp.Fcenter = 0;
+ * fdp.Apass = 0.125;
+ * fdp.Astop = 85;
+ * fdp.phEQ = 217;
+ * fdp.wnom = 347220;
+ * fdp.caldiv = 309;
+ * fdp.RFbw = 1253611;
+ * fdp.FIRdBmin = 0;
+ * fdp.int_FIR = 1;
+ */
+ // clang-format off
+ {
+ -18, 2, -14, 16, 34, 76, 104, 124,
+ 108, 62, -12, -86, -136, -128, -58, 58,
+ 174, 242, 214, 84, -110, -294, -382, -310,
+ -84, 226, 494, 586, 426, 40, -434, -796,
+ -860, -538, 92, 792, 1258, 1226, 622, -386,
+ -1406, -1972, -1730, -628, 1002, 2522, 3200, 2526,
+ 466, -2400, -4986, -6012, -4444, 90, 7064, 15112,
+ 22370, 27018, 27836, 24590, 18120, 10072, 2400, -3220,
+ -5868, -5578, -3214, -106, 2446, 3584, 3126, 1508,
+ -464, -1970, -2484, -1940, -696, 666, 1590, 1764,
+ 1212, 244, -706, -1258, -1242, -732, 10, 656,
+ 964, 850, 412, -134, -560, -710, -560, -208,
+ 178, 444, 500, 352, 88, -172, -330, -336,
+ -212, -24, 144, 230, 218, 124, 2, -100,
+ -146, -126, -62, 18, 82, 110, 98, 60,
+ 12, -28, -52, -56, -50, -32, -18, -8,
+ }, // tx_coef[128]
+ // clang-format on
+ 128, // tx_coef_size
+ { 0, 0, 0, 0, 0, 0 }, // tx_path_clks[6]
+ 0 // tx_bandwidth
+};
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/band_select.c b/Radio/HW/BladeRF/fpga_common/src/band_select.c
new file mode 100644
index 0000000..3d20e8f
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/band_select.c
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdint.h>
+#include "band_select.h"
+#include "lms.h"
+
+int band_select(struct bladerf *dev, bladerf_module module, bool low_band)
+{
+ int status;
+ uint32_t gpio;
+ const uint32_t band = low_band ? 2 : 1;
+
+ log_debug("Selecting %s band.\n", low_band ? "low" : "high");
+
+ status = lms_select_band(dev, module, low_band);
+ if (status != 0) {
+ return status;
+ }
+
+#ifndef BLADERF_NIOS_BUILD
+ status = dev->backend->config_gpio_read(dev, &gpio);
+#else
+ status = CONFIG_GPIO_READ(dev, &gpio);
+#endif
+ if (status != 0) {
+ return status;
+ }
+
+ gpio &= ~(module == BLADERF_MODULE_TX ? (3 << 3) : (3 << 5));
+ gpio |= (module == BLADERF_MODULE_TX ? (band << 3) : (band << 5));
+
+#ifndef BLADERF_NIOS_BUILD
+ return dev->backend->config_gpio_write(dev, gpio);
+#else
+ return CONFIG_GPIO_WRITE(dev, gpio);
+#endif
+}
diff --git a/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c b/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c
new file mode 100644
index 0000000..6719ccd
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/bladerf2_common.c
@@ -0,0 +1,194 @@
+/* This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "bladerf2_common.h"
+
+/**
+ * Value of the current sense resistor (R132)
+ */
+float const ina219_r_shunt = 0.001F;
+
+int errno_ad9361_to_bladerf(int err)
+{
+ if (err >= 0) {
+ return 0;
+ }
+
+ switch (err) {
+ case EIO:
+ return BLADERF_ERR_IO;
+ case EAGAIN:
+ return BLADERF_ERR_WOULD_BLOCK;
+ case ENOMEM:
+ return BLADERF_ERR_MEM;
+ case EFAULT:
+ return BLADERF_ERR_UNEXPECTED;
+ case ENODEV:
+ return BLADERF_ERR_NODEV;
+ case EINVAL:
+ return BLADERF_ERR_INVAL;
+ case ETIMEDOUT:
+ return BLADERF_ERR_TIMEOUT;
+ }
+
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+struct band_port_map const *_get_band_port_map_by_freq(bladerf_channel ch,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map;
+ size_t port_map_len;
+ int64_t freqi = (int64_t)freq;
+ size_t i;
+
+ /* Select the band->port map for RX vs TX */
+ if (BLADERF_CHANNEL_IS_TX(ch)) {
+ port_map = bladerf2_tx_band_port_map;
+ port_map_len = ARRAY_SIZE(bladerf2_tx_band_port_map);
+ } else {
+ port_map = bladerf2_rx_band_port_map;
+ port_map_len = ARRAY_SIZE(bladerf2_rx_band_port_map);
+ }
+
+ if (NULL == port_map) {
+ return NULL;
+ }
+
+ /* Search through the band->port map for the desired band */
+ for (i = 0; i < port_map_len; i++) {
+ if (is_within_range(&port_map[i].frequency, freqi)) {
+ return &port_map[i];
+ }
+ }
+
+ /* Wasn't found, return a null ptr */
+ return NULL;
+}
+
+int _modify_spdt_bits_by_freq(uint32_t *reg,
+ bladerf_channel ch,
+ bool enabled,
+ bladerf_frequency freq)
+{
+ struct band_port_map const *port_map;
+ uint32_t shift;
+
+ if (NULL == reg) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Look up the port configuration for this frequency */
+ port_map = _get_band_port_map_by_freq(ch, enabled ? freq : 0);
+
+ if (NULL == port_map) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Modify the reg bits accordingly */
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ shift = RFFE_CONTROL_RX_SPDT_1;
+ break;
+ case BLADERF_CHANNEL_RX(1):
+ shift = RFFE_CONTROL_RX_SPDT_2;
+ break;
+ case BLADERF_CHANNEL_TX(0):
+ shift = RFFE_CONTROL_TX_SPDT_1;
+ break;
+ case BLADERF_CHANNEL_TX(1):
+ shift = RFFE_CONTROL_TX_SPDT_2;
+ break;
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ *reg &= ~(RFFE_CONTROL_SPDT_MASK << shift);
+ *reg |= port_map->spdt << shift;
+
+ return 0;
+}
+
+int _get_rffe_control_bit_for_dir(bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return RFFE_CONTROL_ENABLE;
+ case BLADERF_TX:
+ return RFFE_CONTROL_TXNRX;
+ default:
+ return UINT32_MAX;
+ }
+}
+
+int _get_rffe_control_bit_for_ch(bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return RFFE_CONTROL_MIMO_RX_EN_0;
+ case BLADERF_CHANNEL_RX(1):
+ return RFFE_CONTROL_MIMO_RX_EN_1;
+ case BLADERF_CHANNEL_TX(0):
+ return RFFE_CONTROL_MIMO_TX_EN_0;
+ case BLADERF_CHANNEL_TX(1):
+ return RFFE_CONTROL_MIMO_TX_EN_1;
+ default:
+ return UINT32_MAX;
+ }
+}
+
+bool _rffe_ch_enabled(uint32_t reg, bladerf_channel ch)
+{
+ return (reg >> _get_rffe_control_bit_for_ch(ch)) & 0x1;
+}
+
+bool _rffe_dir_enabled(uint32_t reg, bladerf_direction dir)
+{
+ return (reg >> _get_rffe_control_bit_for_dir(dir)) & 0x1;
+}
+
+bool _rffe_dir_otherwise_enabled(uint32_t reg, bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(1));
+ case BLADERF_CHANNEL_RX(1):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_RX(0));
+ case BLADERF_CHANNEL_TX(0):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(1));
+ case BLADERF_CHANNEL_TX(1):
+ return _rffe_ch_enabled(reg, BLADERF_CHANNEL_TX(0));
+ }
+
+ return false;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/fpga_common/src/lms.c b/Radio/HW/BladeRF/fpga_common/src/lms.c
new file mode 100644
index 0000000..4f2f930
--- /dev/null
+++ b/Radio/HW/BladeRF/fpga_common/src/lms.c
@@ -0,0 +1,3637 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2015 Nuand LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * If you're diving into this file, have the following documentation handy.
+ *
+ * As most registers don't have a clearly defined names, or are not grouped by
+ * a specific set of functionality, there's little value in providing named
+ * macro definitions, hence the hard-coded addresses and bitmasks.
+ *
+ * LMS6002D Project page:
+ * http://www.limemicro.com/products/LMS6002D.php?sector=default
+ *
+ * LMS6002D Datasheet:
+ * http://www.limemicro.com/download/LMS6002Dr2-DataSheet-1.2r0.pdf
+ *
+ * LMS6002D Programming and Calibration Guide:
+ * http://www.limemicro.com/download/LMS6002Dr2-Programming_and_Calibration_Guide-1.1r1.pdf
+ *
+ * LMS6002D FAQ:
+ * http://www.limemicro.com/download/FAQ_v1.0r10.pdf
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <libbladeRF.h>
+
+#include "lms.h"
+
+#ifndef BLADERF_NIOS_BUILD
+# include "log.h"
+# include "rel_assert.h"
+# include "board/board.h"
+# include "board/bladerf1/capabilities.h"
+
+// #define LMS_COUNT_BUSY_WAITS
+
+ /* Unneeded, due to USB transfer duration */
+# define VTUNE_BUSY_WAIT(us) \
+ do { \
+ INC_BUSY_WAIT_COUNT(us); \
+ log_verbose("VTUNE_BUSY_WAIT(%u)\n", us); \
+ } while(0)
+#else
+# include <unistd.h>
+# define VTUNE_BUSY_WAIT(us) { usleep(us); INC_BUSY_WAIT_COUNT(us); }
+#endif
+
+/* By counting the busy waits between a VCOCAP write and VTUNE read, we can
+ * get a sense of how good/bad our initial VCOCAP guess is and how
+ * (in)efficient our tuning routine is. */
+#ifdef LMS_COUNT_BUSY_WAITS
+ static volatile uint32_t busy_wait_count = 0;
+ static volatile uint32_t busy_wait_duration = 0;
+
+# define INC_BUSY_WAIT_COUNT(us) do { \
+ busy_wait_count++; \
+ busy_wait_duration += us; \
+ } while (0)
+
+# define RESET_BUSY_WAIT_COUNT() do { \
+ busy_wait_count = 0; \
+ busy_wait_duration = 0; \
+ } while (0)
+ static inline void PRINT_BUSY_WAIT_INFO()
+ {
+ if (busy_wait_count > 10) {
+ log_warning("Busy wait count: %u\n", busy_wait_count);
+ } else {
+ log_debug("Busy wait count: %u\n", busy_wait_count);
+ }
+
+ log_debug("Busy wait duration: %u us\n", busy_wait_duration);
+ }
+#else
+# define INC_BUSY_WAIT_COUNT(us) do {} while (0)
+# define RESET_BUSY_WAIT_COUNT() do {} while (0)
+# define PRINT_BUSY_WAIT_INFO()
+#endif
+
+#define LMS_REFERENCE_HZ (38400000u)
+
+#define kHz(x) (x * 1000)
+#define MHz(x) (x * 1000000)
+#define GHz(x) (x * 1000000000)
+
+struct dc_cal_state {
+ uint8_t clk_en; /* Backup of clock enables */
+
+ uint8_t reg0x72; /* Register backup */
+
+ bladerf_lna_gain lna_gain; /* Backup of gain values */
+ int rxvga1_gain;
+ int rxvga2_gain;
+
+ uint8_t base_addr; /* Base address of DC cal regs */
+ unsigned int num_submodules; /* # of DC cal submodules to operate on */
+
+ int rxvga1_curr_gain; /* Current gains used in retry loops */
+ int rxvga2_curr_gain;
+};
+
+#ifndef BLADERF_NIOS_BUILD
+/* LPF conversion table */
+static const unsigned int uint_bandwidths[] = {
+ MHz(28),
+ MHz(20),
+ MHz(14),
+ MHz(12),
+ MHz(10),
+ kHz(8750),
+ MHz(7),
+ MHz(6),
+ kHz(5500),
+ MHz(5),
+ kHz(3840),
+ MHz(3),
+ kHz(2750),
+ kHz(2500),
+ kHz(1750),
+ kHz(1500)
+};
+#endif
+
+#define FREQ_RANGE(low_, high_, value_) \
+{ \
+ FIELD_INIT(.low, low_), \
+ FIELD_INIT(.high, high_), \
+ FIELD_INIT(.value, value_), \
+}
+
+/* Here we define more conservative band ranges than those in the
+ * LMS FAQ (5.24), with the intent of avoiding the use of "edges" that might
+ * cause the PLLs to lose lock over temperature changes */
+#define VCO4_LOW 3800000000ull
+#define VCO4_HIGH 4535000000ull
+
+#define VCO3_LOW VCO4_HIGH
+#define VCO3_HIGH 5408000000ull
+
+#define VCO2_LOW VCO3_HIGH
+#define VCO2_HIGH 6480000000ull
+
+#define VCO1_LOW VCO2_HIGH
+#define VCO1_HIGH 7600000000ull
+
+#if VCO4_LOW/16 != BLADERF_FREQUENCY_MIN
+# error "BLADERF_FREQUENCY_MIN is not actual VCO4_LOW/16 minimum"
+#endif
+
+#if VCO1_HIGH/2 != BLADERF_FREQUENCY_MAX
+# error "BLADERF_FREQUENCY_MAX is not actual VCO1_HIGH/2 maximum"
+#endif
+
+/* SELVCO values */
+#define VCO4 (4 << 3)
+#define VCO3 (5 << 3)
+#define VCO2 (6 << 3)
+#define VCO1 (7 << 3)
+
+/* FRANGE values */
+#define DIV2 0x4
+#define DIV4 0x5
+#define DIV8 0x6
+#define DIV16 0x7
+
+#ifndef BLADERF_NIOS_BUILD
+/* Frequency Range table. Corresponds to the LMS FREQSEL table.
+ * Per feedback from the LMS google group, the last entry, listed as 3.72G
+ * in the programming manual, can be applied up to 3.8G */
+static const struct freq_range {
+ uint32_t low;
+ uint32_t high;
+ uint8_t value;
+} bands[] = {
+ FREQ_RANGE(BLADERF_FREQUENCY_MIN, VCO4_HIGH/16, VCO4 | DIV16),
+ FREQ_RANGE(VCO3_LOW/16, VCO3_HIGH/16, VCO3 | DIV16),
+ FREQ_RANGE(VCO2_LOW/16, VCO2_HIGH/16, VCO2 | DIV16),
+ FREQ_RANGE(VCO1_LOW/16, VCO1_HIGH/16, VCO1 | DIV16),
+ FREQ_RANGE(VCO4_LOW/8, VCO4_HIGH/8, VCO4 | DIV8),
+ FREQ_RANGE(VCO3_LOW/8, VCO3_HIGH/8, VCO3 | DIV8),
+ FREQ_RANGE(VCO2_LOW/8, VCO2_HIGH/8, VCO2 | DIV8),
+ FREQ_RANGE(VCO1_LOW/8, VCO1_HIGH/8, VCO1 | DIV8),
+ FREQ_RANGE(VCO4_LOW/4, VCO4_HIGH/4, VCO4 | DIV4),
+ FREQ_RANGE(VCO3_LOW/4, VCO3_HIGH/4, VCO3 | DIV4),
+ FREQ_RANGE(VCO2_LOW/4, VCO2_HIGH/4, VCO2 | DIV4),
+ FREQ_RANGE(VCO1_LOW/4, VCO1_HIGH/4, VCO1 | DIV4),
+ FREQ_RANGE(VCO4_LOW/2, VCO4_HIGH/2, VCO4 | DIV2),
+ FREQ_RANGE(VCO3_LOW/2, VCO3_HIGH/2, VCO3 | DIV2),
+ FREQ_RANGE(VCO2_LOW/2, VCO2_HIGH/2, VCO2 | DIV2),
+ FREQ_RANGE(VCO1_LOW/2, BLADERF_FREQUENCY_MAX, VCO1 | DIV2),
+};
+
+ /*
+ * The LMS FAQ (Rev 1.0r10, Section 5.20) states that the RXVGA1 codes may be
+ * converted to dB via:
+ * value_db = 20 * log10(127 / (127 - code))
+ *
+ * However, an offset of 5 appears to be required, yielding:
+ * value_db = 5 + 20 * log10(127 / (127 - code))
+ *
+ */
+static const uint8_t rxvga1_lut_code2val[] = {
+ 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
+ 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 17,
+ 17, 17, 18, 18, 18, 18, 19, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, 24,
+ 25, 25, 26, 27, 28, 29, 30
+};
+
+/* The closest values from the above forumla have been selected.
+ * indicides 0 - 4 are clamped to 5dB */
+static const uint8_t rxvga1_lut_val2code[] = {
+ 2, 2, 2, 2, 2, 2, 14, 26, 37, 47, 56, 63, 70, 76, 82, 87,
+ 91, 95, 99, 102, 104, 107, 109, 111, 113, 114, 116, 117, 118, 119, 120,
+};
+
+static const uint8_t lms_reg_dumpset[] = {
+ /* Top level configuration */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
+ 0x0E, 0x0F,
+
+ /* TX PLL Configuration */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
+ 0x1C, 0x1D, 0x1E, 0x1F,
+
+ /* RX PLL Configuration */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
+ 0x2C, 0x2D, 0x2E, 0x2F,
+
+ /* TX LPF Modules Configuration */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
+
+ /* TX RF Modules Configuration */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
+ 0x4C, 0x4D, 0x4E, 0x4F,
+
+ /* RX LPF, ADC, and DAC Modules Configuration */
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
+ 0x5C, 0x5D, 0x5E, 0x5F,
+
+ /* RX VGA2 Configuration */
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+
+ /* RX FE Modules Configuration */
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C
+};
+#endif
+
+/* Register 0x08: RF loopback config and additional BB config
+ *
+ * LBRFEN[3:0] @ [3:0]
+ * 0000 - RF loopback disabled
+ * 0001 - TXMIX output connected to LNA1 path
+ * 0010 - TXMIX output connected to LNA2 path
+ * 0011 - TXMIX output connected to LNA3 path
+ * else - Reserved
+ *
+ * LBEN_OPIN @ [4]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RX output pins
+ *
+ * LBEN_VGA2IN @ [5]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RXVGA2 input
+ *
+ * LBEN_LPFIN @ [6]
+ * 0 - Disabled
+ * 1 - TX BB loopback signal is connected to RXLPF input
+ *
+ */
+#define LBEN_OPIN (1 << 4)
+#define LBEN_VGA2IN (1 << 5)
+#define LBEN_LPFIN (1 << 6)
+#define LBEN_MASK (LBEN_OPIN | LBEN_VGA2IN | LBEN_LPFIN)
+
+#define LBRFEN_LNA1 1
+#define LBRFEN_LNA2 2
+#define LBRFEN_LNA3 3
+#define LBRFEN_MASK 0xf /* [3:2] are marked reserved */
+
+
+/* Register 0x46: Baseband loopback config
+ *
+ * LOOPBBEN[1:0] @ [3:2]
+ * 00 - All Baseband loops opened (default)
+ * 01 - TX loopback path connected from TXLPF output
+ * 10 - TX loopback path connected from TXVGA1 output
+ * 11 - TX loopback path connected from Env/peak detect output
+ */
+#define LOOPBBEN_TXLPF (1 << 2)
+#define LOOPBBEN_TXVGA (2 << 2)
+#define LOOPBBEN_ENVPK (3 << 2)
+#define LOOBBBEN_MASK (3 << 2)
+
+static inline int is_loopback_enabled(struct bladerf *dev)
+{
+ bladerf_loopback loopback;
+ int status;
+
+ status = lms_get_loopback_mode(dev, &loopback);
+ if (status != 0) {
+ return status;
+ }
+
+ return loopback != BLADERF_LB_NONE;
+}
+
+/* VCOCAP estimation. The MIN/MAX values were determined experimentally by
+ * sampling the VCOCAP values over frequency, for each of the VCOs and finding
+ * these to be in the "middle" of a linear regression. Although the curve
+ * isn't actually linear, the linear approximation yields satisfactory error. */
+#define VCOCAP_MAX_VALUE 0x3f
+#define VCOCAP_EST_MIN 15
+#define VCOCAP_EST_MAX 55
+#define VCOCAP_EST_RANGE (VCOCAP_EST_MAX - VCOCAP_EST_MIN)
+#define VCOCAP_EST_THRESH 7 /* Complain if we're +/- 7 on our guess */
+
+/* This is a linear interpolation of our experimentally identified
+ * mean VCOCAP min and VCOCAP max values:
+ */
+static inline uint8_t estimate_vcocap(unsigned int f_target,
+ unsigned int f_low, unsigned int f_high)
+{
+ unsigned int vcocap;
+ const float denom = (float) (f_high - f_low);
+ const float num = VCOCAP_EST_RANGE;
+ const float f_diff = (float) (f_target - f_low);
+
+ vcocap = (unsigned int) ((num / denom * f_diff) + 0.5 + VCOCAP_EST_MIN);
+
+ if (vcocap > VCOCAP_MAX_VALUE) {
+ log_warning("Clamping VCOCAP estimate from %u to %u\n",
+ vcocap, VCOCAP_MAX_VALUE);
+ vcocap = VCOCAP_MAX_VALUE;
+ } else {
+ log_verbose("VCOCAP estimate: %u\n", vcocap);
+ }
+ return (uint8_t) vcocap;
+}
+
+static int write_pll_config(struct bladerf *dev, bladerf_module module,
+ uint8_t freqsel, bool low_band)
+{
+ int status;
+ uint8_t regval;
+ uint8_t selout;
+ uint8_t addr;
+
+ if (module == BLADERF_MODULE_TX) {
+ addr = 0x15;
+ } else {
+ addr = 0x25;
+ }
+
+ status = LMS_READ(dev, addr, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = is_loopback_enabled(dev);
+ if (status < 0) {
+ return status;
+ }
+
+ if (status == 0) {
+ /* Loopback not enabled - update the PLL output buffer. */
+ selout = low_band ? 1 : 2;
+ regval = (freqsel << 2) | selout;
+ } else {
+ /* Loopback is enabled - don't touch PLL output buffer. */
+ regval = (regval & ~0xfc) | (freqsel << 2);
+ }
+
+ return LMS_WRITE(dev, addr, regval);
+}
+
+#ifndef BLADERF_NIOS_BUILD
+
+int lms_config_charge_pumps(struct bladerf *dev, bladerf_module module)
+{
+ int status;
+ uint8_t data;
+ const uint8_t base = (module == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+
+ /* Set the PLL Ichp, Iup and Idn currents */
+ status = LMS_READ(dev, base + 6, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 0x0c;
+
+ status = LMS_WRITE(dev, base + 6, data);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base + 7, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 3;
+
+ status = LMS_WRITE(dev, base + 7, data);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base + 8, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(0x1f);
+ data |= 3;
+ status = LMS_WRITE(dev, base + 8, data);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_enable(struct bladerf *dev, bladerf_module mod, bool enable)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+
+ status = LMS_WRITE(dev, reg, data);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Check to see if we are bypassed */
+ status = LMS_READ(dev, reg + 1, &data);
+ if (status != 0) {
+ return status;
+ } else if (data & (1 << 6)) {
+ /* Bypass is enabled; switch back to normal operation */
+ data &= ~(1 << 6);
+ status = LMS_WRITE(dev, reg + 1, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_get_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode *mode)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+ uint8_t data_h, data_l;
+ bool lpf_enabled, lpf_bypassed;
+
+ status = LMS_READ(dev, reg, &data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, reg + 1, &data_h);
+ if (status != 0) {
+ return status;
+ }
+
+ lpf_enabled = (data_l & (1 << 1)) != 0;
+ lpf_bypassed = (data_h & (1 << 6)) != 0;
+
+ if (lpf_enabled && !lpf_bypassed) {
+ *mode = BLADERF_LPF_NORMAL;
+ } else if (!lpf_enabled && lpf_bypassed) {
+ *mode = BLADERF_LPF_BYPASSED;
+ } else if (!lpf_enabled && !lpf_bypassed) {
+ *mode = BLADERF_LPF_DISABLED;
+ } else {
+ log_debug("Invalid LPF configuration: 0x%02x, 0x%02x\n",
+ data_l, data_h);
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lpf_set_mode(struct bladerf *dev, bladerf_module mod,
+ bladerf_lpf_mode mode)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+ uint8_t data_l, data_h;
+
+ status = LMS_READ(dev, reg, &data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, reg + 1, &data_h);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (mode) {
+ case BLADERF_LPF_NORMAL:
+ data_l |= (1 << 1); /* Enable LPF */
+ data_h &= ~(1 << 6); /* Disable LPF bypass */
+ break;
+
+ case BLADERF_LPF_BYPASSED:
+ data_l &= ~(1 << 1); /* Power down LPF */
+ data_h |= (1 << 6); /* Enable LPF bypass */
+ break;
+
+ case BLADERF_LPF_DISABLED:
+ data_l &= ~(1 << 1); /* Power down LPF */
+ data_h &= ~(1 << 6); /* Disable LPF bypass */
+ break;
+
+ default:
+ log_debug("Invalid LPF mode: %d\n", mode);
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, reg, data_l);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_WRITE(dev, reg + 1, data_h);
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw bw)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~0x3c; /* Clear out previous bandwidth setting */
+ data |= (bw << 2); /* Apply new bandwidth setting */
+
+ return LMS_WRITE(dev, reg, data);
+
+}
+#endif
+
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_bandwidth(struct bladerf *dev, bladerf_module mod, lms_bw *bw)
+{
+ int status;
+ uint8_t data;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x54 : 0x34;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Fetch bandwidth table index from reg[5:2] */
+ data >>= 2;
+ data &= 0xf;
+
+ assert(data < ARRAY_SIZE(uint_bandwidths));
+ *bw = (lms_bw)data;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+lms_bw lms_uint2bw(unsigned int req)
+{
+ lms_bw ret;
+
+ if ( req <= kHz(1500)) ret = BW_1p5MHz;
+ else if (req <= kHz(1750)) ret = BW_1p75MHz;
+ else if (req <= kHz(2500)) ret = BW_2p5MHz;
+ else if (req <= kHz(2750)) ret = BW_2p75MHz;
+ else if (req <= MHz(3) ) ret = BW_3MHz;
+ else if (req <= kHz(3840)) ret = BW_3p84MHz;
+ else if (req <= MHz(5) ) ret = BW_5MHz;
+ else if (req <= kHz(5500)) ret = BW_5p5MHz;
+ else if (req <= MHz(6) ) ret = BW_6MHz;
+ else if (req <= MHz(7) ) ret = BW_7MHz;
+ else if (req <= kHz(8750)) ret = BW_8p75MHz;
+ else if (req <= MHz(10) ) ret = BW_10MHz;
+ else if (req <= MHz(12) ) ret = BW_12MHz;
+ else if (req <= MHz(14) ) ret = BW_14MHz;
+ else if (req <= MHz(20) ) ret = BW_20MHz;
+ else ret = BW_28MHz;
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+/* Return the table entry */
+unsigned int lms_bw2uint(lms_bw bw)
+{
+ unsigned int idx = bw & 0xf;
+ assert(idx < ARRAY_SIZE(uint_bandwidths));
+ return uint_bandwidths[idx];
+}
+#endif
+
+/* Enable dithering on the module PLL */
+#ifndef BLADERF_NIOS_BUILD
+int lms_dither_enable(struct bladerf *dev, bladerf_module mod,
+ uint8_t nbits, bool enable)
+{
+ int status;
+
+ /* Select the base address based on which PLL we are configuring */
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x24 : 0x14;
+ uint8_t data;
+
+ /* Valid range is 1 - 8 bits (inclusive) */
+ if (nbits < 1 || nbits > 8) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Read what we currently have in there */
+ status = LMS_READ(dev, reg, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ /* Enable dithering */
+ data |= (1 << 7);
+
+ /* Clear out the previous setting of the number of bits to dither */
+ data &= ~(7 << 4);
+
+ /* Update with the desired number of bits to dither */
+ data |= (((nbits - 1) & 7) << 4);
+
+ } else {
+ /* Clear dithering enable bit */
+ data &= ~(1 << 7);
+ }
+
+ /* Write it out */
+ status = LMS_WRITE(dev, reg, data);
+ return status;
+}
+#endif
+
+/* Soft reset of the LMS */
+#ifndef BLADERF_NIOS_BUILD
+int lms_soft_reset(struct bladerf *dev)
+{
+
+ int status = LMS_WRITE(dev, 0x05, 0x12);
+
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x05, 0x32);
+ }
+
+ return status;
+}
+#endif
+
+/* Set the gain on the LNA */
+#ifndef BLADERF_NIOS_BUILD
+int lms_lna_set_gain(struct bladerf *dev, bladerf_lna_gain gain)
+{
+ int status;
+ uint8_t data;
+
+ if (gain == BLADERF_LNA_GAIN_BYPASS || gain == BLADERF_LNA_GAIN_MID ||
+ gain == BLADERF_LNA_GAIN_MAX) {
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status == 0) {
+ data &= ~(3 << 6); /* Clear out previous gain setting */
+ data |= ((gain & 3) << 6); /* Update gain value */
+ status = LMS_WRITE(dev, 0x75, data);
+ }
+
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_lna_get_gain(struct bladerf *dev, bladerf_lna_gain *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status == 0) {
+ data >>= 6;
+ data &= 3;
+ *gain = (bladerf_lna_gain)data;
+
+ if (*gain == BLADERF_LNA_GAIN_UNKNOWN) {
+ status = BLADERF_ERR_INVAL;
+ }
+ }
+
+ return status;
+}
+#endif
+
+/* Select which LNA to enable */
+int lms_select_lna(struct bladerf *dev, lms_lna lna)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ data &= ~(3 << 4);
+ data |= ((lna & 3) << 4);
+
+ return LMS_WRITE(dev, 0x75, data);
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_lna(struct bladerf *dev, lms_lna *lna)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x75, &data);
+ if (status != 0) {
+ *lna = LNA_NONE;
+ return status;
+ } else {
+ *lna = (lms_lna) ((data >> 4) & 0x3);
+ return 0;
+ }
+}
+#endif
+
+/* Enable bit is in reserved register documented in this thread:
+ * https://groups.google.com/forum/#!topic/limemicro-opensource/8iTannzlfzg
+ */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x7d, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data &= ~(1 << 3);
+ } else {
+ data |= (1 << 3);
+ }
+
+ return LMS_WRITE(dev, 0x7d, data);
+}
+#endif
+
+/* Set the RFB_TIA_RXFE mixer gain */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_set_gain(struct bladerf *dev, int gain)
+{
+ if (gain > BLADERF_RXVGA1_GAIN_MAX) {
+ gain = BLADERF_RXVGA1_GAIN_MAX;
+ log_info("Clamping RXVGA1 gain to %ddB\n", gain);
+ } else if (gain < BLADERF_RXVGA1_GAIN_MIN) {
+ gain = BLADERF_RXVGA1_GAIN_MIN;
+ log_info("Clamping RXVGA1 gain to %ddB\n", gain);
+ }
+
+ return LMS_WRITE(dev, 0x76, rxvga1_lut_val2code[gain]);
+}
+#endif
+
+/* Get the RFB_TIA_RXFE mixer gain */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga1_get_gain(struct bladerf *dev, int *gain)
+{
+ uint8_t data;
+ int status = LMS_READ(dev, 0x76, &data);
+
+ if (status == 0) {
+ data &= 0x7f;
+ if (data > 120) {
+ data = 120;
+ }
+
+ *gain = rxvga1_lut_code2val[data];
+ }
+
+ return status;
+}
+#endif
+
+/* Enable RXVGA2 */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x64, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+
+ return LMS_WRITE(dev, 0x64, data);
+}
+#endif
+
+
+/* Set the gain on RXVGA2 */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_set_gain(struct bladerf *dev, int gain)
+{
+ if (gain > BLADERF_RXVGA2_GAIN_MAX) {
+ gain = BLADERF_RXVGA2_GAIN_MAX;
+ log_info("Clamping RXVGA2 gain to %ddB\n", gain);
+ } else if (gain < BLADERF_RXVGA2_GAIN_MIN) {
+ gain = BLADERF_RXVGA2_GAIN_MIN;
+ log_info("Clamping RXVGA2 gain to %ddB\n", gain);
+ }
+
+ /* 3 dB per register code */
+ return LMS_WRITE(dev, 0x65, gain / 3);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_rxvga2_get_gain(struct bladerf *dev, int *gain)
+{
+
+ uint8_t data;
+ const int status = LMS_READ(dev, 0x65, &data);
+
+ if (status == 0) {
+ /* 3 dB per code */
+ data *= 3;
+ *gain = data;
+ }
+
+ return status;
+}
+#endif
+
+int lms_select_pa(struct bladerf *dev, lms_pa pa)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x44, &data);
+
+ /* Disable PA1, PA2, and AUX PA - we'll enable as requested below. */
+ data &= ~0x1C;
+
+ /* AUX PA powered down */
+ data |= (1 << 1);
+
+ switch (pa) {
+ case PA_AUX:
+ data &= ~(1 << 1); /* Power up the AUX PA */
+ break;
+
+ case PA_1:
+ data |= (2 << 2); /* PA_EN[2:0] = 010 - Enable PA1 */
+ break;
+
+ case PA_2:
+ data |= (4 << 2); /* PA_EN[2:0] = 100 - Enable PA2 */
+ break;
+
+ case PA_NONE:
+ break;
+
+ default:
+ assert(!"Invalid PA selection");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x44, data);
+ }
+
+ return status;
+
+};
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_peakdetect_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x44, &data);
+
+ if (status == 0) {
+ if (enable) {
+ data &= ~(1 << 0);
+ } else {
+ data |= (1 << 0);
+ }
+ status = LMS_WRITE(dev, 0x44, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_enable_rffe(struct bladerf *dev, bladerf_module module, bool enable)
+{
+ int status;
+ uint8_t data;
+ uint8_t addr = (module == BLADERF_MODULE_TX ? 0x40 : 0x70);
+
+ status = LMS_READ(dev, addr, &data);
+ if (status == 0) {
+
+ if (module == BLADERF_MODULE_TX) {
+ if (enable) {
+ data |= (1 << 1);
+ } else {
+ data &= ~(1 << 1);
+ }
+ } else {
+ if (enable) {
+ data |= (1 << 0);
+ } else {
+ data &= ~(1 << 0);
+ }
+ }
+
+ status = LMS_WRITE(dev, addr, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga2_set_gain(struct bladerf *dev, int gain_int)
+{
+ int status;
+ uint8_t data;
+ int8_t gain;
+
+ if (gain_int > BLADERF_TXVGA2_GAIN_MAX) {
+ gain = BLADERF_TXVGA2_GAIN_MAX;
+ log_info("Clamping TXVGA2 gain to %ddB\n", gain);
+ } else if (gain_int < BLADERF_TXVGA2_GAIN_MIN) {
+ gain = 0;
+ log_info("Clamping TXVGA2 gain to %ddB\n", gain);
+ } else {
+ gain = gain_int;
+ }
+
+ status = LMS_READ(dev, 0x45, &data);
+ if (status == 0) {
+ data &= ~(0x1f << 3);
+ data |= ((gain & 0x1f) << 3);
+ status = LMS_WRITE(dev, 0x45, data);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga2_get_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x45, &data);
+
+ if (status == 0) {
+ *gain = (data >> 3) & 0x1f;
+
+ /* Register values of 25-31 all correspond to 25 dB */
+ if (*gain > 25) {
+ *gain = 25;
+ }
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga1_set_gain(struct bladerf *dev, int gain_int)
+{
+ int8_t gain;
+
+ if (gain_int < BLADERF_TXVGA1_GAIN_MIN) {
+ gain = BLADERF_TXVGA1_GAIN_MIN;
+ log_info("Clamping TXVGA1 gain to %ddB\n", gain);
+ } else if (gain_int > BLADERF_TXVGA1_GAIN_MAX) {
+ gain = BLADERF_TXVGA1_GAIN_MAX;
+ log_info("Clamping TXVGA1 gain to %ddB\n", gain);
+ } else {
+ gain = gain_int;
+ }
+
+ /* Apply offset to convert gain to register table index */
+ gain = (gain + 35);
+
+ /* Since 0x41 is only VGA1GAIN, we don't need to RMW */
+ return LMS_WRITE(dev, 0x41, gain);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_txvga1_get_gain(struct bladerf *dev, int *gain)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x41, &data);
+ if (status == 0) {
+ /* Clamp to max value */
+ data = data & 0x1f;
+
+ /* Convert table index to value */
+ *gain = data - 35;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_lna_power(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t regval;
+
+ /* Magic test register to power down LNAs */
+ status = LMS_READ(dev, 0x7d, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval &= ~(1 << 0);
+ } else {
+ regval |= (1 << 0);
+ }
+
+ status = LMS_WRITE(dev, 0x7d, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Decode test registers */
+ status = LMS_READ(dev, 0x70, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval &= ~(1 << 1);
+ } else {
+ regval |= (1 << 1);
+ }
+
+ return LMS_WRITE(dev, 0x70, regval);
+}
+#endif
+
+/* Power up/down RF loopback switch */
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rf_loopback_switch(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t regval;
+
+ status = LMS_READ(dev, 0x0b, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (enable) {
+ regval |= (1 << 0);
+ } else {
+ regval &= ~(1 << 0);
+ }
+
+ return LMS_WRITE(dev, 0x0b, regval);
+}
+#endif
+
+
+/* Configure TX-side of loopback */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_tx(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status = 0;
+
+ switch(mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ status = lms_select_pa(dev, PA_AUX);
+ break;
+
+ case BLADERF_LB_NONE:
+ {
+ struct lms_freq f;
+
+ /* Restore proper settings (PA) for this frequency */
+ status = lms_get_frequency(dev, BLADERF_MODULE_TX, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_frequency(dev, BLADERF_MODULE_TX,
+ lms_frequency_to_hz(&f));
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_band(dev, BLADERF_MODULE_TX,
+ lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH);
+ break;
+ }
+
+ default:
+ assert(!"Invalid loopback mode encountered");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* Configure RX-side of loopback */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_rx(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+ bladerf_lpf_mode lpf_mode;
+ uint8_t lna;
+ uint8_t regval;
+
+ status = lms_lpf_get_mode(dev, BLADERF_MODULE_RX, &lpf_mode);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* RXLPF must be disabled */
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_DISABLED);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+
+ /* RXVGA1 must be disabled */
+ status = lms_rxvga1_enable(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable the RXLPF if needed */
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ lna = mode - BLADERF_LB_RF_LNA1 + 1;
+ assert(lna >= 1 && lna <= 3);
+
+ /* Power down LNAs */
+ status = enable_lna_power(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Ensure RXVGA1 is enabled */
+ status = lms_rxvga1_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable the RXLPF if needed */
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Ensure RXVGA2 is enabled */
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Select output buffer in RX PLL and select the desired LNA */
+ status = LMS_READ(dev, 0x25, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~0x03;
+ regval |= lna;
+
+ status = LMS_WRITE(dev, 0x25, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_lna(dev, (lms_lna) lna);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Enable RF loopback switch */
+ status = enable_rf_loopback_switch(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+ case BLADERF_LB_NONE:
+ {
+ struct lms_freq f;
+
+ /* Ensure all RX blocks are enabled */
+ status = lms_rxvga1_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (lpf_mode == BLADERF_LPF_DISABLED) {
+ status = lms_lpf_set_mode(dev, BLADERF_MODULE_RX,
+ BLADERF_LPF_NORMAL);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = lms_rxvga2_enable(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable RF loopback switch */
+ status = enable_rf_loopback_switch(dev, false);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power up LNAs */
+ status = enable_lna_power(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Restore proper settings (LNA, RX PLL) for this frequency */
+ status = lms_get_frequency(dev, BLADERF_MODULE_RX, &f);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_set_frequency(dev, BLADERF_MODULE_RX,
+ lms_frequency_to_hz(&f));
+ if (status != 0) {
+ return status;
+ }
+
+
+ status = lms_select_band(dev, BLADERF_MODULE_RX,
+ lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH);
+ break;
+ }
+
+ default:
+ assert(!"Invalid loopback mode encountered");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* Configure "switches" in loopback path */
+#ifndef BLADERF_NIOS_BUILD
+static int loopback_path(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+ uint8_t loopbben, lben_lbrf;
+
+ status = LMS_READ(dev, 0x46, &loopbben);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x08, &lben_lbrf);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Default to baseband loopback being disabled */
+ loopbben &= ~LOOBBBEN_MASK;
+
+ /* Default to RF and BB loopback options being disabled */
+ lben_lbrf &= ~(LBRFEN_MASK | LBEN_MASK);
+
+ switch(mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ loopbben |= LOOPBBEN_TXLPF;
+ lben_lbrf |= LBEN_VGA2IN;
+ break;
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ loopbben |= LOOPBBEN_TXLPF;
+ lben_lbrf |= LBEN_LPFIN;
+ break;
+
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ loopbben |= LOOPBBEN_TXVGA;
+ lben_lbrf |= LBEN_VGA2IN;
+ break;
+
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ loopbben |= LOOPBBEN_TXVGA;
+ lben_lbrf |= LBEN_LPFIN;
+ break;
+
+ case BLADERF_LB_RF_LNA1:
+ lben_lbrf |= LBRFEN_LNA1;
+ break;
+
+ case BLADERF_LB_RF_LNA2:
+ lben_lbrf |= LBRFEN_LNA2;
+ break;
+
+ case BLADERF_LB_RF_LNA3:
+ lben_lbrf |= LBRFEN_LNA3;
+ break;
+
+ case BLADERF_LB_NONE:
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, 0x46, loopbben);
+ if (status == 0) {
+ status = LMS_WRITE(dev, 0x08, lben_lbrf);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_loopback_mode(struct bladerf *dev, bladerf_loopback mode)
+{
+ int status;
+
+ /* Verify a valid mode is provided before shutting anything down */
+ switch (mode) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ case BLADERF_LB_RF_LNA1:
+ case BLADERF_LB_RF_LNA2:
+ case BLADERF_LB_RF_LNA3:
+ case BLADERF_LB_NONE:
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Disable all PA/LNAs while entering loopback mode or making changes */
+ status = lms_select_pa(dev, PA_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_select_lna(dev, LNA_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disconnect loopback paths while we re-configure blocks */
+ status = loopback_path(dev, BLADERF_LB_NONE);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the RX side of the loopback path */
+ status = loopback_rx(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the TX side of the path */
+ status = loopback_tx(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure "switches" along the loopback path */
+ status = loopback_path(dev, mode);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+int lms_get_loopback_mode(struct bladerf *dev, bladerf_loopback *loopback)
+{
+ int status;
+ uint8_t lben_lbrfen, loopbben;
+
+
+ status = LMS_READ(dev, 0x08, &lben_lbrfen);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x46, &loopbben);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (lben_lbrfen & 0x7) {
+ case LBRFEN_LNA1:
+ *loopback = BLADERF_LB_RF_LNA1;
+ return 0;
+
+ case LBRFEN_LNA2:
+ *loopback = BLADERF_LB_RF_LNA2;
+ return 0;
+
+ case LBRFEN_LNA3:
+ *loopback = BLADERF_LB_RF_LNA3;
+ return 0;
+
+ default:
+ break;
+ }
+
+ switch (lben_lbrfen & LBEN_MASK) {
+ case LBEN_VGA2IN:
+ if (loopbben & LOOPBBEN_TXLPF) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXVGA2;
+ return 0;
+ } else if (loopbben & LOOPBBEN_TXVGA) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXVGA2;
+ return 0;
+ }
+ break;
+
+ case LBEN_LPFIN:
+ if (loopbben & LOOPBBEN_TXLPF) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXLPF;
+ return 0;
+ } else if (loopbben & LOOPBBEN_TXVGA) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXLPF;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ *loopback = BLADERF_LB_NONE;
+ return 0;
+}
+
+/* Top level power down of the LMS */
+#ifndef BLADERF_NIOS_BUILD
+int lms_power_down(struct bladerf *dev)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+ if (status == 0) {
+ data &= ~(1 << 4);
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the PLL of a module */
+#ifndef BLADERF_NIOS_BUILD
+int lms_pll_enable(struct bladerf *dev, bladerf_module mod, bool enable)
+{
+ int status;
+ const uint8_t reg = (mod == BLADERF_MODULE_RX) ? 0x24 : 0x14;
+ uint8_t data;
+
+ status = LMS_READ(dev, reg, &data);
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 3);
+ } else {
+ data &= ~(1 << 3);
+ }
+ status = LMS_WRITE(dev, reg, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the RX subsystem */
+#ifndef BLADERF_NIOS_BUILD
+int lms_rx_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 2);
+ } else {
+ data &= ~(1 << 2);
+ }
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Enable the TX subsystem */
+#ifndef BLADERF_NIOS_BUILD
+int lms_tx_enable(struct bladerf *dev, bool enable)
+{
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, 0x05, &data);
+
+ if (status == 0) {
+ if (enable) {
+ data |= (1 << 3);
+ } else {
+ data &= ~(1 << 3);
+ }
+ status = LMS_WRITE(dev, 0x05, data);
+ }
+
+ return status;
+}
+#endif
+
+/* Converts frequency structure to Hz */
+#ifndef BLADERF_NIOS_BUILD
+uint32_t lms_frequency_to_hz(struct lms_freq *f)
+{
+ uint64_t pll_coeff;
+ uint32_t div;
+
+ pll_coeff = (((uint64_t)f->nint) << 23) + f->nfrac;
+ div = (f->x << 23);
+
+ return (uint32_t)(((LMS_REFERENCE_HZ * pll_coeff) + (div >> 1)) / div);
+}
+#endif
+
+/* Print a frequency structure */
+#ifndef BLADERF_NIOS_BUILD
+void lms_print_frequency(struct lms_freq *f)
+{
+ log_verbose("---- Frequency ----\n");
+ log_verbose(" x : %d\n", f->x);
+ log_verbose(" nint : %d\n", f->nint);
+ log_verbose(" nfrac : %u\n", f->nfrac);
+ log_verbose(" freqsel : 0x%02x\n", f->freqsel);
+ log_verbose(" reference: %u\n", LMS_REFERENCE_HZ);
+ log_verbose(" freq : %u\n", lms_frequency_to_hz(f));
+}
+#define PRINT_FREQUENCY lms_print_frequency
+#else
+#define PRINT_FREQUENCY(f)
+#endif
+
+/* Get the frequency structure */
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f)
+{
+ const uint8_t base = (mod == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+ int status;
+ uint8_t data;
+
+ status = LMS_READ(dev, base + 0, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nint = ((uint16_t)data) << 1;
+
+ status = LMS_READ(dev, base + 1, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nint |= (data & 0x80) >> 7;
+ f->nfrac = ((uint32_t)data & 0x7f) << 16;
+
+ status = LMS_READ(dev, base + 2, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nfrac |= ((uint32_t)data)<<8;
+
+ status = LMS_READ(dev, base + 3, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->nfrac |= data;
+
+ status = LMS_READ(dev, base + 5, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->freqsel = (data>>2);
+ f->x = 1 << ((f->freqsel & 7) - 3);
+
+ status = LMS_READ(dev, base + 9, &data);
+ if (status != 0) {
+ return status;
+ }
+
+ f->vcocap = data & 0x3f;
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_quick_tune(struct bladerf *dev,
+ bladerf_module mod,
+ struct bladerf_quick_tune *quick_tune)
+{
+ struct lms_freq f;
+ uint32_t val;
+ int status = lms_get_frequency(dev, mod, &f);
+ if (status == 0) {
+ quick_tune->freqsel = f.freqsel;
+ quick_tune->vcocap = f.vcocap;
+ quick_tune->nint = f.nint;
+ quick_tune->nfrac = f.nfrac;
+ quick_tune->xb_gpio = 0;
+
+ status = dev->backend->expansion_gpio_read(dev, &val);
+ if (status != 0)
+ goto out;
+
+ if (dev->xb == BLADERF_XB_200) {
+ quick_tune->xb_gpio |= LMS_FREQ_XB_200_ENABLE;
+ if (mod == BLADERF_CHANNEL_RX(0)) {
+ quick_tune->xb_gpio |= LMS_FREQ_XB_200_MODULE_RX;
+ /* BLADERF_XB_CONFIG_RX_BYPASS_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x30 ) >> 4)
+ << LMS_FREQ_XB_200_PATH_SHIFT;
+ /* BLADERF_XB_RX_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x30000000 ) >> 28)
+ << LMS_FREQ_XB_200_FILTER_SW_SHIFT;
+ } else {
+ /* BLADERF_XB_CONFIG_TX_BYPASS_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x0C ) >> 2)
+ << LMS_FREQ_XB_200_FILTER_SW_SHIFT;
+ /* BLADERF_XB_TX_MASK */
+ quick_tune->xb_gpio |= ( (val & 0x0C000000 ) >> 26)
+ << LMS_FREQ_XB_200_PATH_SHIFT;
+ }
+ }
+
+ quick_tune->flags = LMS_FREQ_FLAGS_FORCE_VCOCAP;
+
+ if (lms_frequency_to_hz(&f) < BLADERF1_BAND_HIGH) {
+ quick_tune->flags |= LMS_FREQ_FLAGS_LOW_BAND;
+ }
+ }
+
+out:
+ return status;
+}
+#endif
+
+static inline int get_vtune(struct bladerf *dev, uint8_t base, uint8_t delay,
+ uint8_t *vtune)
+{
+ int status;
+
+ if (delay != 0) {
+ VTUNE_BUSY_WAIT(delay);
+ }
+
+ status = LMS_READ(dev, base + 10, vtune);
+ *vtune >>= 6;
+
+ return status;
+}
+
+static inline int write_vcocap(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state)
+{
+ int status;
+
+ assert(vcocap <= VCOCAP_MAX_VALUE);
+ log_verbose("Writing VCOCAP=%u\n", vcocap);
+
+ status = LMS_WRITE(dev, base + 9, vcocap | vcocap_reg_state);
+
+ if (status != 0) {
+ log_debug("VCOCAP write failed: %d\n", status);
+ }
+
+ return status;
+}
+
+#define VTUNE_DELAY_LARGE 50
+#define VTUNE_DELAY_SMALL 25
+#define VTUNE_MAX_ITERATIONS 20
+
+#define VCO_HIGH 0x02
+#define VCO_NORM 0x00
+#define VCO_LOW 0x01
+
+#if defined(LOGGING_ENABLED) || defined(BLADERF_NIOS_DEBUG)
+static const char *vtune_str(uint8_t value) {
+ switch (value) {
+ case VCO_HIGH:
+ return "HIGH";
+
+ case VCO_NORM:
+ return "NORM";
+
+ case VCO_LOW:
+ return "LOW";
+
+ default:
+ return "INVALID";
+ }
+}
+#endif
+
+static int vtune_high_to_norm(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_high_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap >= VCOCAP_MAX_VALUE) {
+ *vtune_high_limit = VCOCAP_MAX_VALUE;
+ log_warning("%s: VCOCAP hit max value.\n", __FUNCTION__);
+ return 0;
+ }
+
+ vcocap++;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_NORM) {
+ *vtune_high_limit = vcocap - 1;
+ log_verbose("VTUNE NORM @ VCOCAP=%u\n", vcocap);
+ log_verbose("VTUNE HIGH @ VCOCAP=%u\n", *vtune_high_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE High->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+static int vtune_norm_to_high(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_high_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap == 0) {
+ *vtune_high_limit = 0;
+ log_warning("%s: VCOCAP hit min value.\n", __FUNCTION__);
+ return 0;
+ }
+
+ vcocap--;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_HIGH) {
+ *vtune_high_limit = vcocap;
+ log_verbose("VTUNE high @ VCOCAP=%u\n", *vtune_high_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE High->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+static int vtune_low_to_norm(struct bladerf *dev, uint8_t base,
+ uint8_t vcocap, uint8_t vcocap_reg_state,
+ uint8_t *vtune_low_limit)
+{
+ int status;
+ unsigned int i;
+ uint8_t vtune = 0xff;
+
+ for (i = 0; i < VTUNE_MAX_ITERATIONS; i++) {
+
+ if (vcocap == 0) {
+ *vtune_low_limit = 0;
+ log_warning("VCOCAP hit min value.\n");
+ return 0;
+ }
+
+ vcocap--;
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == VCO_NORM) {
+ *vtune_low_limit = vcocap + 1;
+ log_verbose("VTUNE NORM @ VCOCAP=%u\n", vcocap);
+ log_verbose("VTUNE LOW @ VCOCAP=%u\n", *vtune_low_limit);
+ return 0;
+ }
+ }
+
+ log_error("VTUNE Low->Norm loop failed to converge.\n");
+ return BLADERF_ERR_UNEXPECTED;
+}
+
+/* Wait for VTUNE to reach HIGH or LOW. NORM is not a valid option here */
+static int wait_for_vtune_value(struct bladerf *dev,
+ uint8_t base, uint8_t target_value,
+ uint8_t *vcocap, uint8_t vcocap_reg_state)
+{
+ uint8_t vtune;
+ unsigned int i;
+ int status = 0;
+ const unsigned int max_retries = 15;
+ const uint8_t limit = (target_value == VCO_HIGH) ? 0 : VCOCAP_MAX_VALUE;
+ int8_t inc = (target_value == VCO_HIGH) ? -1 : 1;
+
+ assert(target_value == VCO_HIGH || target_value == VCO_LOW);
+
+ for (i = 0; i < max_retries; i++) {
+ status = get_vtune(dev, base, 0, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ if (vtune == target_value) {
+ log_verbose("VTUNE reached %s at iteration %u\n",
+ vtune_str(target_value), i);
+ return 0;
+ } else {
+ log_verbose("VTUNE was %s. Waiting and retrying...\n",
+ vtune_str(vtune));
+
+ VTUNE_BUSY_WAIT(10);
+ }
+ }
+
+ log_debug("Timed out while waiting for VTUNE=%s. Walking VCOCAP...\n",
+ vtune_str(target_value));
+
+ while (*vcocap != limit) {
+ *vcocap += inc;
+
+ status = write_vcocap(dev, base, *vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ } else if (vtune == target_value) {
+ log_debug("VTUNE=%s reached with VCOCAP=%u\n",
+ vtune_str(vtune), *vcocap);
+ return 0;
+ }
+ }
+
+ log_warning("VTUNE did not reach %s. Tuning may not be nominal.\n",
+ vtune_str(target_value));
+
+# ifdef ERROR_ON_NO_VTUNE_LIMIT
+ return BLADERF_ERR_UNEXPECTED;
+# else
+ return 0;
+# endif
+}
+
+/* These values are the max counts we've seen (experimentally) between
+ * VCOCAP values that converged */
+#define VCOCAP_MAX_LOW_HIGH 12
+
+/* This function assumes an initial VCOCAP estimate has already been written.
+ *
+ * Remember, increasing VCOCAP works towards a lower voltage, and vice versa:
+ * From experimental observations, we don't expect to see the "normal" region
+ * extend beyond 16 counts.
+ *
+ * VCOCAP = 0 VCOCAP=63
+ * / \
+ * v v
+ * |----High-----[ Normal ]----Low----| VTUNE voltage comparison
+ *
+ * The VTUNE voltage can be found on R263 (RX) or R265 (Tx). (They're under the
+ * can shielding the LMS6002D.) By placing a scope probe on these and retuning,
+ * you should be able to see the relationship between VCOCAP changes and
+ * the voltage changes.
+ */
+static int tune_vcocap(struct bladerf *dev, uint8_t vcocap_est,
+ uint8_t base, uint8_t vcocap_reg_state,
+ uint8_t *vcocap_result)
+{
+ int status;
+ uint8_t vcocap = vcocap_est;
+ uint8_t vtune;
+ uint8_t vtune_high_limit; /* Where VCOCAP puts use into VTUNE HIGH region */
+ uint8_t vtune_low_limit; /* Where VCOCAP puts use into VTUNE HIGH region */
+
+ RESET_BUSY_WAIT_COUNT();
+
+ vtune_high_limit = VCOCAP_MAX_VALUE;
+ vtune_low_limit = 0;
+
+ status = get_vtune(dev, base, VTUNE_DELAY_LARGE, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (vtune) {
+ case VCO_HIGH:
+ log_verbose("Estimate HIGH: Walking down to NORM.\n");
+ status = vtune_high_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ break;
+
+ case VCO_NORM:
+ log_verbose("Estimate NORM: Walking up to HIGH.\n");
+ status = vtune_norm_to_high(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ break;
+
+ case VCO_LOW:
+ log_verbose("Estimate LOW: Walking down to NORM.\n");
+ status = vtune_low_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_low_limit);
+ break;
+ }
+
+ if (status != 0) {
+ return status;
+ } else if (vtune_high_limit != VCOCAP_MAX_VALUE) {
+
+ /* We determined our VTUNE HIGH limit. Try to force ourselves to the
+ * LOW limit and then walk back up to norm from there.
+ *
+ * Reminder - There's an inverse relationship between VTUNE and VCOCAP
+ */
+ switch (vtune) {
+ case VCO_HIGH:
+ case VCO_NORM:
+ if ( ((int) vtune_high_limit + VCOCAP_MAX_LOW_HIGH) < VCOCAP_MAX_VALUE) {
+ vcocap = vtune_high_limit + VCOCAP_MAX_LOW_HIGH;
+ } else {
+ vcocap = VCOCAP_MAX_VALUE;
+ log_verbose("Clamping VCOCAP to %u.\n", vcocap);
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Waiting for VTUNE LOW @ VCOCAP=%u,\n", vcocap);
+ status = wait_for_vtune_value(dev, base, VCO_LOW,
+ &vcocap, vcocap_reg_state);
+
+ if (status == 0) {
+ log_verbose("Walking VTUNE LOW to NORM from VCOCAP=%u,\n", vcocap);
+ status = vtune_low_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_low_limit);
+ }
+ } else {
+
+ /* We determined our VTUNE LOW limit. Try to force ourselves up to
+ * the HIGH limit and then walk down to NORM from there
+ *
+ * Reminder - There's an inverse relationship between VTUNE and VCOCAP
+ */
+ switch (vtune) {
+ case VCO_LOW:
+ case VCO_NORM:
+ if ( ((int) vtune_low_limit - VCOCAP_MAX_LOW_HIGH) > 0) {
+ vcocap = vtune_low_limit - VCOCAP_MAX_LOW_HIGH;
+ } else {
+ vcocap = 0;
+ log_verbose("Clamping VCOCAP to %u.\n", vcocap);
+ }
+ break;
+
+ default:
+ assert(!"Invalid state");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ log_verbose("Waiting for VTUNE HIGH @ VCOCAP=%u\n", vcocap);
+ status = wait_for_vtune_value(dev, base, VCO_HIGH,
+ &vcocap, vcocap_reg_state);
+
+ if (status == 0) {
+ log_verbose("Walking VTUNE HIGH to NORM from VCOCAP=%u,\n", vcocap);
+ status = vtune_high_to_norm(dev, base, vcocap, vcocap_reg_state,
+ &vtune_high_limit);
+ }
+ }
+
+ if (status == 0) {
+ vcocap = vtune_high_limit + (vtune_low_limit - vtune_high_limit) / 2;
+
+ log_verbose("VTUNE LOW: %u\n", vtune_low_limit);
+ log_verbose("VTUNE NORM: %u\n", vcocap);
+ log_verbose("VTUNE Est: %u (%d)\n",
+ vcocap_est, (int) vcocap_est - vcocap);
+ log_verbose("VTUNE HIGH: %u\n", vtune_high_limit);
+
+# if LMS_COUNT_BUSY_WAITS
+ log_verbose("Busy waits: %u\n", busy_wait_count);
+ log_verbose("Busy us: %u\n", busy_wait_duration);
+# endif
+
+ status = write_vcocap(dev, base, vcocap, vcocap_reg_state);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Inform the caller of what we converged to */
+ *vcocap_result = vcocap;
+
+ status = get_vtune(dev, base, VTUNE_DELAY_SMALL, &vtune);
+ if (status != 0) {
+ return status;
+ }
+
+ PRINT_BUSY_WAIT_INFO();
+
+ if (vtune != VCO_NORM) {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_error("Final VCOCAP=%u is not in VTUNE NORM region.\n", vcocap);
+ }
+ }
+
+ return status;
+}
+
+int lms_select_band(struct bladerf *dev, bladerf_module module, bool low_band)
+{
+ int status;
+
+ /* If loopback mode disabled, avoid changing the PA or LNA selection,
+ * as these need to remain are powered down or disabled */
+ status = is_loopback_enabled(dev);
+ if (status < 0) {
+ return status;
+ } else if (status > 0) {
+ return 0;
+ }
+
+ if (module == BLADERF_MODULE_TX) {
+ lms_pa pa = low_band ? PA_1 : PA_2;
+ status = lms_select_pa(dev, pa);
+ } else {
+ lms_lna lna = low_band ? LNA_1 : LNA_2;
+ status = lms_select_lna(dev, lna);
+ }
+
+ return status;
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_calculate_tuning_params(uint32_t freq, struct lms_freq *f)
+{
+ uint64_t vco_x;
+ uint64_t temp;
+ uint16_t nint;
+ uint32_t nfrac;
+ uint8_t freqsel = bands[0].value;
+ uint8_t i = 0;
+ const uint64_t ref_clock = LMS_REFERENCE_HZ;
+
+ /* Clamp out of range values */
+ if (freq < BLADERF_FREQUENCY_MIN) {
+ freq = BLADERF_FREQUENCY_MIN;
+ log_info("Clamping frequency to %uHz\n", freq);
+ } else if (freq > BLADERF_FREQUENCY_MAX) {
+ freq = BLADERF_FREQUENCY_MAX;
+ log_info("Clamping frequency to %uHz\n", freq);
+ }
+
+ /* Figure out freqsel */
+
+ while (i < ARRAY_SIZE(bands)) {
+ if ((freq >= bands[i].low) && (freq <= bands[i].high)) {
+ freqsel = bands[i].value;
+ break;
+ }
+ i++;
+ }
+
+ /* This condition should never occur. There's a bug if it does. */
+ if (i >= ARRAY_SIZE(bands)) {
+ log_critical("BUG: Failed to find frequency band information. "
+ "Setting frequency to %u Hz.\n", BLADERF_FREQUENCY_MIN);
+
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ /* Estimate our target VCOCAP value. */
+ f->vcocap = estimate_vcocap(freq, bands[i].low, bands[i].high);
+
+ /* Calculate integer portion of the frequency value */
+ vco_x = ((uint64_t)1) << ((freqsel & 7) - 3);
+ temp = (vco_x * freq) / ref_clock;
+ assert(temp <= UINT16_MAX);
+ nint = (uint16_t)temp;
+
+ temp = (1 << 23) * (vco_x * freq - nint * ref_clock);
+ temp = (temp + ref_clock / 2) / ref_clock;
+ assert(temp <= UINT32_MAX);
+ nfrac = (uint32_t)temp;
+
+ assert(vco_x <= UINT8_MAX);
+ f->x = (uint8_t)vco_x;
+ f->nint = nint;
+ f->nfrac = nfrac;
+ f->freqsel = freqsel;
+ f->xb_gpio = 0;
+ assert(ref_clock <= UINT32_MAX);
+
+ f->flags = 0;
+
+ if (freq < BLADERF1_BAND_HIGH) {
+ f->flags |= LMS_FREQ_FLAGS_LOW_BAND;
+ }
+
+ PRINT_FREQUENCY(f);
+
+ return 0;
+}
+#endif
+
+int lms_set_precalculated_frequency(struct bladerf *dev, bladerf_module mod,
+ struct lms_freq *f)
+{
+ /* Select the base address based on which PLL we are configuring */
+ const uint8_t base = (mod == BLADERF_MODULE_RX) ? 0x20 : 0x10;
+
+ uint8_t data;
+ uint8_t vcocap_reg_state;
+ int status, dsm_status;
+
+ /* Utilize atomic writes to the PLL registers, if possible. This
+ * "multiwrite" is indicated by the MSB being set. */
+# ifdef BLADERF_NIOS_BUILD
+ const uint8_t pll_base = base | 0x80;
+# else
+ const uint8_t pll_base =
+ have_cap(dev->board->get_capabilities(dev),
+ BLADERF_CAP_ATOMIC_NINT_NFRAC_WRITE) ?
+ (base | 0x80) : base;
+# endif
+
+ f->vcocap_result = 0xff;
+
+ /* Turn on the DSMs */
+ status = LMS_READ(dev, 0x09, &data);
+ if (status == 0) {
+ data |= 0x05;
+ status = LMS_WRITE(dev, 0x09, data);
+ }
+
+ if (status != 0) {
+ log_debug("Failed to turn on DSMs\n");
+ return status;
+ }
+
+ /* Write the initial vcocap estimate first to allow for adequate time for
+ * VTUNE to stabilize. We need to be sure to keep the upper bits of
+ * this register and perform a RMW, as bit 7 is VOVCOREG[0]. */
+ status = LMS_READ(dev, base + 9, &vcocap_reg_state);
+ if (status != 0) {
+ goto error;
+ }
+
+ vcocap_reg_state &= ~(0x3f);
+
+ status = write_vcocap(dev, base, f->vcocap, vcocap_reg_state);
+ if (status != 0) {
+ goto error;
+ }
+
+ status = write_pll_config(dev, mod, f->freqsel,
+ (f->flags & LMS_FREQ_FLAGS_LOW_BAND) != 0);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = f->nint >> 1;
+ status = LMS_WRITE(dev, pll_base + 0, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = ((f->nint & 1) << 7) | ((f->nfrac >> 16) & 0x7f);
+ status = LMS_WRITE(dev, pll_base + 1, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = ((f->nfrac >> 8) & 0xff);
+ status = LMS_WRITE(dev, pll_base + 2, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ data = (f->nfrac & 0xff);
+ status = LMS_WRITE(dev, pll_base + 3, data);
+ if (status != 0) {
+ goto error;
+ }
+
+ /* Perform tuning algorithm unless we've been instructed to just use
+ * the VCOCAP hint as-is. */
+ if (f->flags & LMS_FREQ_FLAGS_FORCE_VCOCAP) {
+ f->vcocap_result = f->vcocap;
+ } else {
+ /* Walk down VCOCAP values find an optimal values */
+ status = tune_vcocap(dev, f->vcocap, base, vcocap_reg_state,
+ &f->vcocap_result);
+ }
+
+error:
+ /* Turn off the DSMs */
+ dsm_status = LMS_READ(dev, 0x09, &data);
+ if (dsm_status == 0) {
+ data &= ~(0x05);
+ dsm_status = LMS_WRITE(dev, 0x09, data);
+ }
+
+ return (status == 0) ? dsm_status : status;
+}
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_dump_registers(struct bladerf *dev)
+{
+ int status = 0;
+ uint8_t data,i;
+ const uint16_t num_reg = sizeof(lms_reg_dumpset);
+
+ for (i = 0; i < num_reg; i++) {
+ status = LMS_READ(dev, lms_reg_dumpset[i], &data);
+ if (status != 0) {
+ log_debug("Failed to read LMS @ 0x%02x\n", lms_reg_dumpset[i]);
+ return status;
+ } else {
+ log_debug("LMS[0x%02x] = 0x%02x\n", lms_reg_dumpset[i], data);
+ }
+ }
+
+ return status;
+}
+#endif
+
+/* Reference LMS6002D calibration guide, section 4.1 flow chart */
+#ifndef BLADERF_NIOS_BUILD
+static int lms_dc_cal_loop(struct bladerf *dev, uint8_t base,
+ uint8_t cal_address, uint8_t dc_cntval,
+ uint8_t *dc_regval)
+{
+ int status;
+ uint8_t i, val;
+ bool done = false;
+ const unsigned int max_cal_count = 25;
+
+ log_debug("Calibrating module %2.2x:%2.2x\n", base, cal_address);
+
+ /* Set the calibration address for the block, and start it up */
+ status = LMS_READ(dev, base + 0x03, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(0x07);
+ val |= cal_address&0x07;
+
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Set and latch the DC_CNTVAL */
+ status = LMS_WRITE(dev, base + 0x02, dc_cntval);
+ if (status != 0) {
+ return status;
+ }
+
+ val |= (1 << 4);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1 << 4);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+
+ /* Start the calibration by toggling DC_START_CLBR */
+ val |= (1 << 5);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ val &= ~(1 << 5);
+ status = LMS_WRITE(dev, base + 0x03, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Main loop checking the calibration */
+ for (i = 0 ; i < max_cal_count && !done; i++) {
+ /* Read active low DC_CLBR_DONE */
+ status = LMS_READ(dev, base + 0x01, &val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Check if calibration is done */
+ if (((val >> 1) & 1) == 0) {
+ done = true;
+ /* Per LMS FAQ item 4.7, we should check DC_REG_VAL, as
+ * DC_LOCK is not a reliable indicator */
+ status = LMS_READ(dev, base, dc_regval);
+ if (status == 0) {
+ *dc_regval &= 0x3f;
+ }
+ }
+ }
+
+ if (done == false) {
+ log_warning("DC calibration loop did not converge.\n");
+ status = BLADERF_ERR_UNEXPECTED;
+ } else {
+ log_debug( "DC_REGVAL: %d\n", *dc_regval );
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_backup(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status;
+
+ memset(state, 0, sizeof(state[0]));
+
+ status = LMS_READ(dev, 0x09, &state->clk_en);
+ if (status != 0) {
+ return status;
+ }
+
+ if (module == BLADERF_DC_CAL_RX_LPF || module == BLADERF_DC_CAL_RXVGA2) {
+ status = LMS_READ(dev, 0x72, &state->reg0x72);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_lna_get_gain(dev, &state->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_rxvga1_get_gain(dev, &state->rxvga1_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = lms_rxvga2_get_gain(dev, &state->rxvga2_gain);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_module_init(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status;
+ uint8_t cal_clock;
+ uint8_t val;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ cal_clock = (1 << 5); /* CLK_EN[5] - LPF CAL Clock */
+ state->base_addr = 0x00;
+ state->num_submodules = 1;
+ break;
+
+ case BLADERF_DC_CAL_TX_LPF:
+ cal_clock = (1 << 1); /* CLK_EN[1] - TX LPF DCCAL Clock */
+ state->base_addr = 0x30;
+ state->num_submodules = 2;
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ cal_clock = (1 << 3); /* CLK_EN[3] - RX LPF DCCAL Clock */
+ state->base_addr = 0x50;
+ state->num_submodules = 2;
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ cal_clock = (1 << 4); /* CLK_EN[4] - RX VGA2 DCCAL Clock */
+ state->base_addr = 0x60;
+ state->num_submodules = 5;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ /* Enable the appropriate clock based on the module */
+ status = LMS_WRITE(dev, 0x09, state->clk_en | cal_clock);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (module) {
+
+ case BLADERF_DC_CAL_LPF_TUNING:
+ /* Nothing special to do */
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ case BLADERF_DC_CAL_RXVGA2:
+ /* FAQ 5.26 (rev 1.0r10) notes that the DC comparators should be
+ * powered up when performing DC calibration, and then powered down
+ * afterwards to improve receiver linearity */
+ if (module == BLADERF_DC_CAL_RXVGA2) {
+ status = lms_clear(dev, 0x6e, (3 << 6));
+ if (status != 0) {
+ return status;
+ }
+ } else {
+ /* Power up RX LPF DC calibration comparator */
+ status = lms_clear(dev, 0x5f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Disconnect LNA from the RXMIX input by opening up the
+ * INLOAD_LNA_RXFE switch. This should help reduce external
+ * interference while calibrating */
+ val = state->reg0x72 & ~(1 << 7);
+ status = LMS_WRITE(dev, 0x72, val);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Attempt to calibrate at max gain. */
+ status = lms_lna_set_gain(dev, BLADERF_LNA_GAIN_MAX);
+ if (status != 0) {
+ return status;
+ }
+
+ state->rxvga1_curr_gain = BLADERF_RXVGA1_GAIN_MAX;
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ state->rxvga2_curr_gain = BLADERF_RXVGA2_GAIN_MAX;
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_curr_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ break;
+
+
+ case BLADERF_DC_CAL_TX_LPF:
+ /* FAQ item 4.1 notes that the DAC should be turned off or set
+ * to generate minimum DC */
+ status = lms_set(dev, 0x36, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Ensure TX LPF DC calibration comparator is powered up */
+ status = lms_clear(dev, 0x3f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ default:
+ assert(!"Invalid module");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+/* The RXVGA2 items here are based upon Lime Microsystems' recommendations
+ * in their "Improving RxVGA2 DC Offset Calibration Stability" Document:
+ * https://groups.google.com/group/limemicro-opensource/attach/19b675d099a22b89/Improving%20RxVGA2%20DC%20Offset%20Calibration%20Stability_v1.pdf?part=0.1&authuser=0
+ *
+ * This function assumes that the submodules are preformed in a consecutive
+ * and increasing order, as outlined in the above document.
+ */
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_submodule(struct bladerf *dev,
+ bladerf_cal_module module,
+ unsigned int submodule,
+ struct dc_cal_state *state,
+ bool *converged)
+{
+ int status;
+ uint8_t val, dc_regval;
+
+ *converged = false;
+
+ if (module == BLADERF_DC_CAL_RXVGA2) {
+ switch (submodule) {
+ case 0:
+ /* Reset VGA2GAINA and VGA2GAINB to the default power-on values,
+ * in case we're retrying this calibration due to one of the
+ * later submodules failing. For the same reason, RXVGA2 decode
+ * is disabled; it is not used for the RC reference module (0)
+ */
+
+ /* Disable RXVGA2 DECODE */
+ status = lms_clear(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* VGA2GAINA = 0, VGA2GAINB = 0 */
+ status = LMS_WRITE(dev, 0x68, 0x01);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 1:
+ /* Setup for Stage 1 I and Q channels (submodules 1 and 2) */
+
+ /* Set to direct control signals: RXVGA2 Decode = 1 */
+ status = lms_set(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* VGA2GAINA = 0110, VGA2GAINB = 0 */
+ val = 0x06;
+ status = LMS_WRITE(dev, 0x68, val);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 2:
+ /* No additional changes needed - covered by previous execution
+ * of submodule == 1. */
+ break;
+
+ case 3:
+ /* Setup for Stage 2 I and Q channels (submodules 3 and 4) */
+
+ /* VGA2GAINA = 0, VGA2GAINB = 0110 */
+ val = 0x60;
+ status = LMS_WRITE(dev, 0x68, val);
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case 4:
+ /* No additional changes needed - covered by execution
+ * of submodule == 3 */
+ break;
+
+ default:
+ assert(!"Invalid submodule");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ status = lms_dc_cal_loop(dev, state->base_addr, submodule, 31, &dc_regval);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_regval == 31) {
+ log_debug("DC_REGVAL suboptimal value - retrying DC cal loop.\n");
+
+ /* FAQ item 4.7 indcates that can retry with DC_CNTVAL reset */
+ status = lms_dc_cal_loop(dev, state->base_addr, submodule, 0, &dc_regval);
+ if (status != 0) {
+ return status;
+ } else if (dc_regval == 0) {
+ log_debug("Bad DC_REGVAL detected. DC cal failed.\n");
+ return 0;
+ }
+ }
+
+ if (module == BLADERF_DC_CAL_LPF_TUNING) {
+ /* Special case for LPF tuning module where results are
+ * written to TX/RX LPF DCCAL */
+
+ /* Set the DC level to RX and TX DCCAL modules */
+ status = LMS_READ(dev, 0x35, &val);
+ if (status == 0) {
+ val &= ~(0x3f);
+ val |= dc_regval;
+ status = LMS_WRITE(dev, 0x35, val);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, 0x55, &val);
+ if (status == 0) {
+ val &= ~(0x3f);
+ val |= dc_regval;
+ status = LMS_WRITE(dev, 0x55, val);
+ }
+
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ *converged = true;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_retry_adjustment(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state,
+ bool *limit_reached)
+{
+ int status = 0;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ case BLADERF_DC_CAL_TX_LPF:
+ /* Nothing to adjust here */
+ *limit_reached = true;
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ if (state->rxvga1_curr_gain > BLADERF_RXVGA1_GAIN_MIN) {
+ state->rxvga1_curr_gain -= 1;
+ log_debug("Retrying DC cal with RXVGA1=%d\n",
+ state->rxvga1_curr_gain);
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ } else {
+ *limit_reached = true;
+ }
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ if (state->rxvga1_curr_gain > BLADERF_RXVGA1_GAIN_MIN) {
+ state->rxvga1_curr_gain -= 1;
+ log_debug("Retrying DC cal with RXVGA1=%d\n",
+ state->rxvga1_curr_gain);
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_curr_gain);
+ } else if (state->rxvga2_curr_gain > BLADERF_RXVGA2_GAIN_MIN) {
+ state->rxvga2_curr_gain -= 3;
+ log_debug("Retrying DC cal with RXVGA2=%d\n",
+ state->rxvga2_curr_gain);
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_curr_gain);
+ } else {
+ *limit_reached = true;
+ }
+ break;
+
+ default:
+ *limit_reached = true;
+ assert(!"Invalid module");
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ if (*limit_reached) {
+ log_debug("DC Cal retry limit reached\n");
+ }
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int dc_cal_module_deinit(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status = 0;
+
+ switch (module) {
+ case BLADERF_DC_CAL_LPF_TUNING:
+ /* Nothing special to do here */
+ break;
+
+ case BLADERF_DC_CAL_RX_LPF:
+ /* Power down RX LPF calibration comparator */
+ status = lms_set(dev, 0x5f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_DC_CAL_RXVGA2:
+ /* Restore defaults: VGA2GAINA = 1, VGA2GAINB = 0 */
+ status = LMS_WRITE(dev, 0x68, 0x01);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Disable decode control signals: RXVGA2 Decode = 0 */
+ status = lms_clear(dev, 0x64, (1 << 0));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Power DC comparitors down, per FAQ 5.26 (rev 1.0r10) */
+ status = lms_set(dev, 0x6e, (3 << 6));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ case BLADERF_DC_CAL_TX_LPF:
+ /* Power down TX LPF DC calibration comparator */
+ status = lms_set(dev, 0x3f, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Re-enable the DACs */
+ status = lms_clear(dev, 0x36, (1 << 7));
+ if (status != 0) {
+ return status;
+ }
+ break;
+
+ default:
+ assert(!"Invalid module");
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_restore(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state)
+{
+ int status, ret;
+ ret = 0;
+
+ status = LMS_WRITE(dev, 0x09, state->clk_en);
+ if (status != 0) {
+ ret = status;
+ }
+
+ if (module == BLADERF_DC_CAL_RX_LPF || module == BLADERF_DC_CAL_RXVGA2) {
+ status = LMS_WRITE(dev, 0x72, state->reg0x72);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_lna_set_gain(dev, state->lna_gain);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_rxvga1_set_gain(dev, state->rxvga1_gain);
+ if (status != 0 && ret == 0) {
+ ret = status;
+ }
+
+ status = lms_rxvga2_set_gain(dev, state->rxvga2_gain);
+ if (status != 0) {
+ ret = status;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int dc_cal_module(struct bladerf *dev,
+ bladerf_cal_module module,
+ struct dc_cal_state *state,
+ bool *converged)
+{
+ unsigned int i;
+ int status = 0;
+
+ *converged = true;
+
+ for (i = 0; i < state->num_submodules && *converged && status == 0; i++) {
+ status = dc_cal_submodule(dev, module, i, state, converged);
+ }
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_calibrate_dc(struct bladerf *dev, bladerf_cal_module module)
+{
+ int status, tmp_status;
+ struct dc_cal_state state;
+ bool converged, limit_reached;
+
+ status = dc_cal_backup(dev, module, &state);
+ if (status != 0) {
+ return status;
+ }
+
+ status = dc_cal_module_init(dev, module, &state);
+ if (status != 0) {
+ goto error;
+ }
+
+ converged = false;
+ limit_reached = false;
+
+ while (!converged && !limit_reached && status == 0) {
+ status = dc_cal_module(dev, module, &state, &converged);
+
+ if (status == 0 && !converged) {
+ status = dc_cal_retry_adjustment(dev, module, &state,
+ &limit_reached);
+ }
+ }
+
+ if (!converged && status == 0) {
+ log_warning("DC Calibration (module=%d) failed to converge.\n", module);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+error:
+ tmp_status = dc_cal_module_deinit(dev, module, &state);
+ status = (status != 0) ? status : tmp_status;
+
+ tmp_status = dc_cal_restore(dev, module, &state);
+ status = (status != 0) ? status : tmp_status;
+
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_lpf_cal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 5);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rxvga2_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 4);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_rxlpf_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 3);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline int enable_txlpf_dccal_clock(struct bladerf *dev, bool enable)
+{
+ const uint8_t mask = (1 << 1);
+
+ if (enable) {
+ return lms_set(dev, 0x09, mask);
+ } else {
+ return lms_clear(dev, 0x09, mask);
+ }
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int set_dc_cal_value(struct bladerf *dev, uint8_t base,
+ uint8_t dc_addr, int16_t value)
+{
+ int status;
+ const uint8_t new_value = (uint8_t)value;
+ uint8_t regval = (0x08 | dc_addr);
+
+ /* Keep reset inactive, cal disable, load addr */
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Update DC_CNTVAL */
+ status = LMS_WRITE(dev, base + 2, new_value);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Strobe DC_LOAD */
+ regval |= (1 << 4);
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ regval &= ~(1 << 4);
+ status = LMS_WRITE(dev, base + 3, regval);
+ if (status != 0) {
+ return status;
+ }
+
+ status = LMS_READ(dev, base, &regval);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int get_dc_cal_value(struct bladerf *dev, uint8_t base,
+ uint8_t dc_addr, int16_t *value)
+{
+ int status;
+ uint8_t regval;
+
+ /* Keep reset inactive, cal disable, load addr */
+ status = LMS_WRITE(dev, base + 3, (0x08 | dc_addr));
+ if (status != 0) {
+ return status;
+ }
+
+ /* Fetch value from DC_REGVAL */
+ status = LMS_READ(dev, base, &regval);
+ if (status != 0) {
+ *value = -1;
+ return status;
+ }
+
+ *value = regval;
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_cals(struct bladerf *dev,
+ const struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+ const bool cal_tx_lpf =
+ (dc_cals->tx_lpf_i >= 0) || (dc_cals->tx_lpf_q >= 0);
+
+ const bool cal_rx_lpf =
+ (dc_cals->rx_lpf_i >= 0) || (dc_cals->rx_lpf_q >= 0);
+
+ const bool cal_rxvga2 =
+ (dc_cals->dc_ref >= 0) ||
+ (dc_cals->rxvga2a_i >= 0) || (dc_cals->rxvga2a_q >= 0) ||
+ (dc_cals->rxvga2b_i >= 0) || (dc_cals->rxvga2b_q >= 0);
+
+ if (dc_cals->lpf_tuning >= 0) {
+ status = enable_lpf_cal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ status = set_dc_cal_value(dev, 0x00, 0, dc_cals->lpf_tuning);
+ if (status != 0) {
+ return status;
+ }
+
+ status = enable_lpf_cal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_tx_lpf) {
+ status = enable_txlpf_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->tx_lpf_i >= 0) {
+ status = set_dc_cal_value(dev, 0x30, 0, dc_cals->tx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->tx_lpf_q >= 0) {
+ status = set_dc_cal_value(dev, 0x30, 1, dc_cals->tx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_txlpf_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_rx_lpf) {
+ status = enable_rxlpf_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->rx_lpf_i >= 0) {
+ status = set_dc_cal_value(dev, 0x50, 0, dc_cals->rx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rx_lpf_q >= 0) {
+ status = set_dc_cal_value(dev, 0x50, 1, dc_cals->rx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_rxlpf_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (cal_rxvga2) {
+ status = enable_rxvga2_dccal_clock(dev, true);
+ if (status != 0) {
+ return status;
+ }
+
+ if (dc_cals->dc_ref >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 0, dc_cals->dc_ref);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2a_i >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 1, dc_cals->rxvga2a_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2a_q >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 2, dc_cals->rxvga2a_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2b_i >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 3, dc_cals->rxvga2b_i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ if (dc_cals->rxvga2b_q >= 0) {
+ status = set_dc_cal_value(dev, 0x60, 4, dc_cals->rxvga2b_q);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ status = enable_rxvga2_dccal_clock(dev, false);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_cals(struct bladerf *dev, struct bladerf_lms_dc_cals *dc_cals)
+{
+ int status;
+
+ status = get_dc_cal_value(dev, 0x00, 0, &dc_cals->lpf_tuning);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x30, 0, &dc_cals->tx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x30, 1, &dc_cals->tx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x50, 0, &dc_cals->rx_lpf_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x50, 1, &dc_cals->rx_lpf_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 0, &dc_cals->dc_ref);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 1, &dc_cals->rxvga2a_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 2, &dc_cals->rxvga2a_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 3, &dc_cals->rxvga2b_i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = get_dc_cal_value(dev, 0x60, 4, &dc_cals->rxvga2b_q);
+ if (status != 0) {
+ return status;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_select_sampling(struct bladerf *dev, bladerf_sampling sampling)
+{
+ uint8_t val;
+ int status = 0;
+
+ if (sampling == BLADERF_SAMPLING_INTERNAL) {
+ /* Disconnect the ADC input from the outside world */
+ status = LMS_READ( dev, 0x09, &val );
+ if (status) {
+ log_warning( "Could not read LMS to connect ADC to external pins\n" );
+ goto out;
+ }
+
+ val &= ~(1<<7);
+ status = LMS_WRITE( dev, 0x09, val );
+ if (status) {
+ log_warning( "Could not write LMS to connect ADC to external pins\n" );
+ goto out;
+ }
+
+ /* Turn on RXVGA2 */
+ status = LMS_READ( dev, 0x64, &val );
+ if (status) {
+ log_warning( "Could not read LMS to enable RXVGA2\n" );
+ goto out;
+ }
+
+ val |= (1<<1);
+ status = LMS_WRITE( dev, 0x64, val );
+ if (status) {
+ log_warning( "Could not write LMS to enable RXVGA2\n" );
+ goto out;
+ }
+ } else if (sampling == BLADERF_SAMPLING_EXTERNAL) {
+ /* Turn off RXVGA2 */
+ status = LMS_READ( dev, 0x64, &val );
+ if (status) {
+ log_warning( "Could not read the LMS to disable RXVGA2\n" );
+ goto out;
+ }
+
+ val &= ~(1<<1);
+ status = LMS_WRITE( dev, 0x64, val );
+ if (status) {
+ log_warning( "Could not write the LMS to disable RXVGA2\n" );
+ goto out;
+ }
+
+ /* Connect the external ADC pins to the internal ADC input */
+ status = LMS_READ( dev, 0x09, &val );
+ if (status) {
+ log_warning( "Could not read the LMS to connect ADC to internal pins\n" );
+ goto out;
+ }
+
+ val |= (1<<7);
+ status = LMS_WRITE( dev, 0x09, val );
+ if (status) {
+ log_warning( "Could not write the LMS to connect ADC to internal pins\n" );
+ }
+ } else {
+ status = BLADERF_ERR_INVAL;
+ }
+
+out:
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_sampling(struct bladerf *dev, bladerf_sampling *sampling)
+{
+ int status = 0, external = 0;
+ uint8_t val = 0;
+
+ status = LMS_READ(dev, 0x09, &val);
+ if (status != 0) {
+ log_warning("Could not read state of ADC pin connectivity\n");
+ goto out;
+ }
+ external = (val & (1 << 7)) ? 1 : 0;
+
+ status = LMS_READ(dev, 0x64, &val);
+ if (status != 0) {
+ log_warning( "Could not read RXVGA2 state\n" );
+ goto out;
+ }
+ external |= (val & (1 << 1)) ? 0 : 2;
+
+ switch (external) {
+ case 0:
+ *sampling = BLADERF_SAMPLING_INTERNAL;
+ break;
+
+ case 3:
+ *sampling = BLADERF_SAMPLING_EXTERNAL;
+ break;
+
+ default:
+ *sampling = BLADERF_SAMPLING_UNKNOWN;
+ break;
+ }
+
+out:
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static inline uint8_t scale_dc_offset(bladerf_module module, int16_t value)
+{
+ uint8_t ret;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ /* RX only has 6 bits of scale to work with, remove normalization */
+ value >>= 5;
+
+ if (value < 0) {
+ if (value <= -64) {
+ /* Clamp */
+ value = 0x3f;
+ } else {
+ value = (-value) & 0x3f;
+ }
+
+ /* This register uses bit 6 to denote a negative value */
+ value |= (1 << 6);
+ } else {
+ if (value >= 64) {
+ /* Clamp */
+ value = 0x3f;
+ } else {
+ value = value & 0x3f;
+ }
+ }
+
+ ret = (uint8_t) value;
+ break;
+
+ case BLADERF_MODULE_TX:
+ /* TX only has 7 bits of scale to work with, remove normalization */
+ value >>= 4;
+
+ /* LMS6002D 0x00 = -16, 0x80 = 0, 0xff = 15.9375 */
+ if (value >= 0) {
+ ret = (uint8_t) (value >= 128) ? 0x7f : (value & 0x7f);
+
+ /* Assert bit 7 for positive numbers */
+ ret = (1 << 7) | ret;
+ } else {
+ ret = (uint8_t) (value <= -128) ? 0x00 : (value & 0x7f);
+ }
+ break;
+
+ default:
+ assert(!"Invalid module provided");
+ ret = 0x00;
+ }
+
+ return ret;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+static int set_dc_offset_reg(struct bladerf *dev, bladerf_module module,
+ uint8_t addr, int16_t value)
+{
+ int status;
+ uint8_t regval, tmp;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ status = LMS_READ(dev, addr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Bit 7 is unrelated to lms dc correction, save its state */
+ tmp = tmp & (1 << 7);
+ regval = scale_dc_offset(module, value) | tmp;
+ break;
+
+ case BLADERF_MODULE_TX:
+ regval = scale_dc_offset(module, value);
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ status = LMS_WRITE(dev, addr, regval);
+ return status;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, uint16_t value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x42 : 0x71;
+ return set_dc_offset_reg(dev, module, addr, value);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_set_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x43 : 0x72;
+ return set_dc_offset_reg(dev, module, addr, value);
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int get_dc_offset(struct bladerf *dev, bladerf_module module,
+ uint8_t addr, int16_t *value)
+{
+ int status;
+ uint8_t tmp;
+
+ status = LMS_READ(dev, addr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+
+ /* Mask out an unrelated control bit */
+ tmp = tmp & 0x7f;
+
+ /* Determine sign */
+ if (tmp & (1 << 6)) {
+ *value = -(int16_t)(tmp & 0x3f);
+ } else {
+ *value = (int16_t)(tmp & 0x3f);
+ }
+
+ /* Renormalize to 2048 */
+ *value <<= 5;
+ break;
+
+ case BLADERF_MODULE_TX:
+ *value = (int16_t) tmp;
+
+ /* Renormalize to 2048 */
+ *value <<= 4;
+ break;
+
+ default:
+ return BLADERF_ERR_INVAL;
+ }
+
+ return 0;
+}
+#endif
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_offset_i(struct bladerf *dev,
+ bladerf_module module, int16_t *value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x42 : 0x71;
+ return get_dc_offset(dev, module, addr, value);
+}
+#endif
+
+
+#ifndef BLADERF_NIOS_BUILD
+int lms_get_dc_offset_q(struct bladerf *dev,
+ bladerf_module module, int16_t *value)
+{
+ const uint8_t addr = (module == BLADERF_MODULE_TX) ? 0x43 : 0x72;
+ return get_dc_offset(dev, module, addr, value);
+}
+#endif