From 6e0a80814d06481021af19428d73e829ad3b5d46 Mon Sep 17 00:00:00 2001 From: abagu <352211526@qq.com> Date: Tue, 14 Apr 2020 10:43:08 +0800 Subject: [PATCH] initialize project with v003 Signed-off-by: abagu <352211526@qq.com> --- .ccsproject | 13 + .cproject | 217 + .gitignore | 6 + .project | 34 + evmc.cfg | 166 + src/ad9361.c | 670 +++ src/ad9361.h | 19 + src/ad9361_lib/ad9361_api.c | 2094 +++++++++ src/ad9361_lib/ad9361_api.h | 501 +++ src/ad9361_lib/ad9361_conv.c | 597 +++ src/ad9361_lib/ad9361_dev.c | 7120 +++++++++++++++++++++++++++++++ src/ad9361_lib/ad9361_dev.h | 3510 +++++++++++++++ src/ad9361_lib/adc_core.c | 417 ++ src/ad9361_lib/adc_core.h | 175 + src/ad9361_lib/common.h | 117 + src/ad9361_lib/config.h | 103 + src/ad9361_lib/dac_core.c | 807 ++++ src/ad9361_lib/dac_core.h | 203 + src/ad9361_lib/platform.c | 87 + src/ad9361_lib/platform.h | 64 + src/ad9361_lib/util.c | 346 ++ src/ad9361_lib/util.h | 142 + src/ad9680.c | 218 + src/ad9680.h | 18 + src/ad9779.c | 179 + src/ad9779.h | 20 + src/axi0.c | 89 + src/axi0.h | 24 + src/axi1.c | 86 + src/axi1.h | 25 + src/emif_test.c | 107 + src/emif_test.h | 20 + src/gspi.c | 117 + src/gspi.h | 39 + src/k7_download.c | 230 + src/k7_download.h | 20 + src/main.c | 271 ++ src/nandflash.c | 100 + src/nandflash.h | 15 + src/norflash.c | 169 + src/norflash.h | 15 + src/runtime.c | 192 + src/runtime.h | 15 + src/srio.c | 246 ++ src/srio.h | 20 + src/vsdef.h | 35 + targetConfigs/TMS320C6678.ccxml | 16 + targetConfigs/readme.txt | 9 + vslib/cmdline.cmd | 9 + vslib/vbios.c | 246 ++ vslib/vbios.h | 45 + vslib/vcmd.h | 24 + vslib/vconsole.c | 865 ++++ vslib/vconsole.h | 27 + vslib/vdebug.c | 101 + vslib/vdebug.h | 16 + vslib/vlock.c | 45 + vslib/vlock.h | 17 + vslib/vsem.c | 67 + vslib/vsem.h | 17 + vslib/vtask.c | 89 + vslib/vtask.h | 23 + vslib/vtelnetd.c | 217 + vslib/vtelnetd.h | 23 + vslib/vtftp.c | 55 + vslib/vtftp.h | 13 + vslib/vtimer.c | 75 + vslib/vtimer.h | 20 + 68 files changed, 21697 insertions(+) create mode 100644 .ccsproject create mode 100644 .cproject create mode 100644 .gitignore create mode 100644 .project create mode 100644 evmc.cfg create mode 100644 src/ad9361.c create mode 100644 src/ad9361.h create mode 100644 src/ad9361_lib/ad9361_api.c create mode 100644 src/ad9361_lib/ad9361_api.h create mode 100644 src/ad9361_lib/ad9361_conv.c create mode 100644 src/ad9361_lib/ad9361_dev.c create mode 100644 src/ad9361_lib/ad9361_dev.h create mode 100644 src/ad9361_lib/adc_core.c create mode 100644 src/ad9361_lib/adc_core.h create mode 100644 src/ad9361_lib/common.h create mode 100644 src/ad9361_lib/config.h create mode 100644 src/ad9361_lib/dac_core.c create mode 100644 src/ad9361_lib/dac_core.h create mode 100644 src/ad9361_lib/platform.c create mode 100644 src/ad9361_lib/platform.h create mode 100644 src/ad9361_lib/util.c create mode 100644 src/ad9361_lib/util.h create mode 100644 src/ad9680.c create mode 100644 src/ad9680.h create mode 100644 src/ad9779.c create mode 100644 src/ad9779.h create mode 100644 src/axi0.c create mode 100644 src/axi0.h create mode 100644 src/axi1.c create mode 100644 src/axi1.h create mode 100644 src/emif_test.c create mode 100644 src/emif_test.h create mode 100644 src/gspi.c create mode 100644 src/gspi.h create mode 100644 src/k7_download.c create mode 100644 src/k7_download.h create mode 100644 src/main.c create mode 100644 src/nandflash.c create mode 100644 src/nandflash.h create mode 100644 src/norflash.c create mode 100644 src/norflash.h create mode 100644 src/runtime.c create mode 100644 src/runtime.h create mode 100644 src/srio.c create mode 100644 src/srio.h create mode 100644 src/vsdef.h create mode 100644 targetConfigs/TMS320C6678.ccxml create mode 100644 targetConfigs/readme.txt create mode 100644 vslib/cmdline.cmd create mode 100644 vslib/vbios.c create mode 100644 vslib/vbios.h create mode 100644 vslib/vcmd.h create mode 100644 vslib/vconsole.c create mode 100644 vslib/vconsole.h create mode 100644 vslib/vdebug.c create mode 100644 vslib/vdebug.h create mode 100644 vslib/vlock.c create mode 100644 vslib/vlock.h create mode 100644 vslib/vsem.c create mode 100644 vslib/vsem.h create mode 100644 vslib/vtask.c create mode 100644 vslib/vtask.h create mode 100644 vslib/vtelnetd.c create mode 100644 vslib/vtelnetd.h create mode 100644 vslib/vtftp.c create mode 100644 vslib/vtftp.h create mode 100644 vslib/vtimer.c create mode 100644 vslib/vtimer.h diff --git a/.ccsproject b/.ccsproject new file mode 100644 index 0000000..9b3e4d7 --- /dev/null +++ b/.ccsproject @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..a4ea765 --- /dev/null +++ b/.cproject @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68f2629 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Debug +.settings +.settings +*.out +*.bin +.config diff --git a/.project b/.project new file mode 100644 index 0000000..33b76ae --- /dev/null +++ b/.project @@ -0,0 +1,34 @@ + + + vs_xc003 + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.rtsc.xdctools.buildDefinitions.XDC.xdcNature + com.ti.ccstudio.core.ccsNature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + VSKY_LIBDSP_ROOT + file:/E:/dspwork/libdsp + + + diff --git a/evmc.cfg b/evmc.cfg new file mode 100644 index 0000000..583fabc --- /dev/null +++ b/evmc.cfg @@ -0,0 +1,166 @@ +/* + * @file srio.cfg + * + * @brief + * Memory Map and Program intiializations for the HPDSP Utility. + * + */ + +/* + * Specify all needed RTSC MOudles and ocnfigure them. + */ +var Memory = xdc.useModule('xdc.runtime.Memory'); +var BIOS = xdc.useModule('ti.sysbios.BIOS'); +var Task = xdc.useModule('ti.sysbios.knl.Task'); +var Queue = xdc.useModule('ti.sysbios.knl.Queue'); +var HeapBuf = xdc.useModule('ti.sysbios.heaps.HeapBuf'); +var Log = xdc.useModule('xdc.runtime.Log'); +var Settings = xdc.useModule('vsky.libdsp.Settings'); +var HiSpeed = xdc.useModule('vsky.libdsp.HiSpeed'); + +/* + * Allow storing of task names. By default if you name a task with a friendly display name it will not be saved + * to conserve RAM. This must be set to true to allow it. We use friendly names on the Task List display. + */ +//Defaults.common$.namedInstance = true; +Task.common$.namedInstance = true; + +var Clock = xdc.useModule ('ti.sysbios.knl.Clock'); + +/* + * Interface with IPC. Depending on the version of BIOS you are using the + * module name may have changed. +*/ +/* Use this for pre BIOS 6.30 */ +/* var Sem = xdc.useModule ('ti.sysbios.ipc.Semaphore'); */ + +/* Use this for BIOS 6.30 plus to get the IPC module */ +var Sem = xdc.useModule ('ti.sysbios.knl.Semaphore'); + +var Hwi = xdc.useModule ('ti.sysbios.hal.Hwi'); +var CPINTC = xdc.useModule ('ti.sysbios.family.c66.tci66xx.CpIntc'); +var Ecm = xdc.useModule ('ti.sysbios.family.c64p.EventCombiner'); + +/* + * Configure this to turn on the CPU Load Module for BIOS. + * +*/ + +/* +var Load = xdc.useModule('ti.sysbios.utils.Load'); +Load.common$.diags_USER4 = Diags.ALWAYS_ON; +*/ + +var Diags = xdc.useModule('xdc.runtime.Diags'); + +/* + * Sets up the exception log so you can read it with ROV in CCS + */ +var LoggerBuf = xdc.useModule('xdc.runtime.LoggerBuf'); +var Exc = xdc.useModule('ti.sysbios.family.c64p.Exception'); +Exc.common$.logger = LoggerBuf.create(); +Exc.enablePrint = true; /* prints exception details to the CCS console */ + +/* + * Give the Load module it's own LoggerBuf to make sure the + * events are not overwritten. + */ +/* var loggerBufParams = new LoggerBuf.Params(); +loggerBufParams.exitFlush = true; +loggerBufParams.numEntries = 64; +Load.common$.logger = LoggerBuf.create(loggerBufParams); +*/ + +/* + * Use this load to configure NDK 2.2 and above using RTSC. In previous versions of + * the NDK RTSC configuration was not supported and you should comment this out. + */ +var Global = xdc.useModule('ti.ndk.config.Global'); + +/* + * This allows the heart beat (poll function) to be created but does not generate the stack threads + * + * Look in the cdoc (help files) to see what CfgAddEntry items can be configured. We tell it NOT + * to create any stack threads (services) as we configure those ourselves in our Main Task + * thread hpdspuaStart. + */ +Global.enableCodeGeneration = false; + + +/* Define a variable to set the MAR mode for MSMCSRAM as all cacheable */ +var Cache = xdc.useModule('ti.sysbios.family.c66.Cache'); +//Cache.MAR224_255 = 0x0000000f; + +var Startup = xdc.useModule('xdc.runtime.Startup'); + +var System = xdc.useModule('xdc.runtime.System'); + +/* + * Create a Heap. + */ +var HeapMem = xdc.useModule('ti.sysbios.heaps.HeapMem'); +var heapMemParams = new HeapMem.Params(); +heapMemParams.size = 0x8000000; +heapMemParams.sectionName = "systemHeap"; +Program.global.heap0 = HeapMem.create(heapMemParams); + +/* This is the default memory heap. */ +Memory.defaultHeapInstance = Program.global.heap0; +Program.sectMap["systemHeap"] = "DDR3"; + +/* Required if using System_printf to output on the console */ +SysStd = xdc.useModule('xdc.runtime.SysStd'); +System.SupportProxy = SysStd; + +/* + * Define hooks and static tasks that will always be running. + */ +/* + * Register an EVM Init handler with BIOS. This will initialize the hardware. BIOS calls before it starts. + * + * If yuo are debugging with CCS, then this function will execute as CCS loads it if the option in your + * Target Configuraiton file (.ccxml) has the option set to execute all code before Main. That is the + * default. + */ +//Startup.lastFxns.$add('&board_init'); + +/* + * Create the stack Thread Task for our application. + */ +//var tskNdkStackTest = Task.create("&main_task"); +//tskNdkStackTest.stackSize = 0x200000; +//tskNdkStackTest.priority = 0x2; + +/* + * Create a Periodic task to handle all NDK polling functions. + * If you are using RTSC configuration with NDK 2.2 and above, this is done by default and + * you do not need to do this. + */ +/* var prdNdkClkParams = new Clock.Params (); +prdNdkClkParams.period = 0x64; +prdNdkClkParams.startFlag = true; +Program.global.clockInst1 = Clock.create("&llTimerTick", 5, prdNdkClkParams); +*/ + +/* + * If you are using RTSC configuration with NDK 2.2 and above, this is done by default, else + * register hooks so that the stack can track all Task creation + */ +//Task.common$.namedInstance = true; +//Task.addHookSet ({ registerFxn: '&NDK_hookInit', createFxn: '&NDK_hookCreate', }); + +/* Enable BIOS Task Scheduler */ +BIOS.taskEnabled = true; + +/* + * Enable Event Groups here and registering of ISR for specific GEM INTC is done + * using EventCombiner_dispatchPlug() and Hwi_eventMap() APIs + */ +Ecm.eventGroupHwiNum[0] = 7; +Ecm.eventGroupHwiNum[1] = 8; +Ecm.eventGroupHwiNum[2] = 9; +Ecm.eventGroupHwiNum[3] = 10; +Settings.hyperlink_clock = 156250000; +Settings.emac_port = 1; +Settings.emac_address = "80:00:00:03:12:2C"; +Settings.sgmii_clock = 156250000; diff --git a/src/ad9361.c b/src/ad9361.c new file mode 100644 index 0000000..9e3b91a --- /dev/null +++ b/src/ad9361.c @@ -0,0 +1,670 @@ +/* + * ad9361.c + * + * Created on: 2019-7-16 + * Author: Administrator + */ +#include +#include +#include + +#include +#include +#include "gspi.h" +#include "ad9361_lib/platform.h" +#include "ad9361_lib/ad9361_api.h" +#include "ad9361_lib/dac_core.h" +#include "ad9361_lib/adc_core.h" +#include "ad9361.h" +#include "axi1.h" + +#if 0 +static gspi_t _ad9361 = NULL; +static int __gspi_ad9361_init(void ) +{ + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_0, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + _ad9361 = gspi_open(GSPI_CS_AD9361, &format); + return 0; +} + +uint8_t ad9361_read(uint16_t reg) +{ + int elms = 0; + unsigned char buf[3]; + + if (!_ad9361) + __gspi_ad9361_init(); + + buf[0] = 0x03 & (reg >> 8); + buf[1] = reg & 0xFF; + + elms = gspi_xfer(_ad9361, buf, 0, 2, &buf[2], 2, 1); + if (elms != sizeof buf) { + printf("error: ad9361 reg 0x%x read fail\r\n", reg); + return 0; + } + + printf("ad9361 read reg:0x%04x, val: 0x%x\r\n", reg, buf[2]); + return buf[2] & 0xff; +} + +int ad9361_write(uint16_t reg, uint8_t val) +{ + int elms = 0; + unsigned char buf[3]; + + if (!_ad9361) + __gspi_ad9361_init(); + + buf[0] = 0x80 | ((reg >> 8) & 0x03); + buf[1] = reg & 0xff; + buf[2] = val & 0xff; + elms = gspi_xfer(_ad9361, buf, 0, 3, NULL, 0, 0); + if (elms != sizeof buf) { + printf("error: ad9361 reg 0x%x write fail\r\n", reg); + return -1; + } + + printf("ad9361 write reg:0x%04x, val: 0x%x\r\n", reg, val); + return 0; +} +#endif // + +static int __ad9361_spi_xfer(void *dev, const unsigned char *txbuf, unsigned n_tx, unsigned char *rxbuf, unsigned n_rx) +{ + gspi_t gs = (gspi_t)dev; + gspi_xfer(gs, txbuf, 0, n_tx, rxbuf, n_tx, n_rx); + return 0; +} + +static void __ad9361_udelay(unsigned long us) +{ + tsc_delay(us); +} + +static void __ad9361_axi_write(uint32_t addr, uint32_t data) +{ + axi1_write(addr, data); +} + +static uint32_t __ad9361_axi_read(uint32_t addr) +{ + return axi1_read(addr); +} + +static void __ad9361_reset(void) +{ + axi1_write(0x1010, 0x0); + tsc_delay(100 * 1000); + axi1_write(0x1010, 0x1); + tsc_delay(100 * 1000); + printf("ad9361 reset success \r\n"); +} + +static AD9361_InitParam default_init_param = { + /* Device selection */ + ID_AD9361, // dev_sel + /* Identification number */ + 0, //id_no + /* Reference Clock */ + 40000000UL, //reference_clk_rate + /* Base Configuration */ + 1, //two_rx_two_tx_mode_enable *** adi,2rx-2tx-mode-enable + 1, //one_rx_one_tx_mode_use_rx_num *** adi,1rx-1tx-mode-use-rx-num + 1, //one_rx_one_tx_mode_use_tx_num *** adi,1rx-1tx-mode-use-tx-num + //0, //frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable + 1, //frequency_division_duplex_mode_enable *** adi,frequency-division-duplex-mode-enable + 0, //frequency_division_duplex_independent_mode_enable *** adi,frequency-division-duplex-independent-mode-enable + 0, //tdd_use_dual_synth_mode_enable *** adi,tdd-use-dual-synth-mode-enable + 0, //tdd_skip_vco_cal_enable *** adi,tdd-skip-vco-cal-enable + 0, //tx_fastlock_delay_ns *** adi,tx-fastlock-delay-ns + 0, //rx_fastlock_delay_ns *** adi,rx-fastlock-delay-ns + 0, //rx_fastlock_pincontrol_enable *** adi,rx-fastlock-pincontrol-enable + 0, //tx_fastlock_pincontrol_enable *** adi,tx-fastlock-pincontrol-enable + 0, //external_rx_lo_enable *** adi,external-rx-lo-enable + 0, //external_tx_lo_enable *** adi,external-tx-lo-enable + 5, //dc_offset_tracking_update_event_mask *** adi,dc-offset-tracking-update-event-mask + 6, //dc_offset_attenuation_high_range *** adi,dc-offset-attenuation-high-range + 5, //dc_offset_attenuation_low_range *** adi,dc-offset-attenuation-low-range + 0x28, //dc_offset_count_high_range *** adi,dc-offset-count-high-range + 0x32, //dc_offset_count_low_range *** adi,dc-offset-count-low-range + 0, //split_gain_table_mode_enable *** adi,split-gain-table-mode-enable + MAX_SYNTH_FREF, //trx_synthesizer_target_fref_overwrite_hz *** adi,trx-synthesizer-target-fref-overwrite-hz + 0, // qec_tracking_slow_mode_enable *** adi,qec-tracking-slow-mode-enable + /* ENSM Control */ + 0, //ensm_enable_pin_pulse_mode_enable *** adi,ensm-enable-pin-pulse-mode-enable + 0, //ensm_enable_txnrx_control_enable *** adi,ensm-enable-txnrx-control-enable + /* LO Control */ + //1200000000UL, //rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz + //1200000000UL, //tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz + 2250000000UL, //rx_synthesizer_frequency_hz *** adi,rx-synthesizer-frequency-hz + 2250000000UL, //tx_synthesizer_frequency_hz *** adi,tx-synthesizer-frequency-hz + /* Rate & BW Control */ + {983040000, 245760000, 122880000, 61440000, 30720000, 30720000},//uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies + {983040000, 122880000, 122880000, 61440000, 30720000, 30720000},//uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies + //{1280000000, 80000000, 40000000, 20000000, 10000000, 10000000},//uint32_t rx_path_clock_frequencies[6] *** adi,rx-path-clock-frequencies + //{1280000000, 80000000, 40000000, 20000000, 10000000, 10000000},//uint32_t tx_path_clock_frequencies[6] *** adi,tx-path-clock-frequencies + //18000000,//rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz + //18000000,//rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz + 56000000,//rf_rx_bandwidth_hz *** adi,rf-rx-bandwidth-hz + 56000000,//rf_tx_bandwidth_hz *** adi,rf-tx-bandwidth-hz + /* RF Port Control */ + 0, //rx_rf_port_input_select *** adi,rx-rf-port-input-select + 0, //tx_rf_port_input_select *** adi,tx-rf-port-input-select + /* TX Attenuation Control */ + 0, //tx_attenuation_mdB *** adi,tx-attenuation-mdB + 0, //update_tx_gain_in_alert_enable *** adi,update-tx-gain-in-alert-enable + /* Reference Clock Control */ + 0, //xo_disable_use_ext_refclk_enable *** adi,xo-disable-use-ext-refclk-enable + {8, 5920}, //dcxo_coarse_and_fine_tune[2] *** adi,dcxo-coarse-and-fine-tune + CLKOUT_DISABLE, //clk_output_mode_select *** adi,clk-output-mode-select + //ADC_CLK_DIV_4, //clk_output_mode_select *** adi,clk-output-mode-select + /* Gain Control */ + 2, //gc_rx1_mode *** adi,gc-rx1-mode + 2, //gc_rx2_mode *** adi,gc-rx2-mode + 58, //gc_adc_large_overload_thresh *** adi,gc-adc-large-overload-thresh + 4, //gc_adc_ovr_sample_size *** adi,gc-adc-ovr-sample-size + 47, //gc_adc_small_overload_thresh *** adi,gc-adc-small-overload-thresh + 8192, //gc_dec_pow_measurement_duration *** adi,gc-dec-pow-measurement-duration + 0, //gc_dig_gain_enable *** adi,gc-dig-gain-enable + 800, //gc_lmt_overload_high_thresh *** adi,gc-lmt-overload-high-thresh + 704, //gc_lmt_overload_low_thresh *** adi,gc-lmt-overload-low-thresh + 24, //gc_low_power_thresh *** adi,gc-low-power-thresh + 15, //gc_max_dig_gain *** adi,gc-max-dig-gain + /* Gain MGC Control */ + 2, //mgc_dec_gain_step *** adi,mgc-dec-gain-step + 2, //mgc_inc_gain_step *** adi,mgc-inc-gain-step + 0, //mgc_rx1_ctrl_inp_enable *** adi,mgc-rx1-ctrl-inp-enable + 0, //mgc_rx2_ctrl_inp_enable *** adi,mgc-rx2-ctrl-inp-enable + 0, //mgc_split_table_ctrl_inp_gain_mode *** adi,mgc-split-table-ctrl-inp-gain-mode + /* Gain AGC Control */ + 10, //agc_adc_large_overload_exceed_counter *** adi,agc-adc-large-overload-exceed-counter + 2, //agc_adc_large_overload_inc_steps *** adi,agc-adc-large-overload-inc-steps + 0, //agc_adc_lmt_small_overload_prevent_gain_inc_enable *** adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable + 10, //agc_adc_small_overload_exceed_counter *** adi,agc-adc-small-overload-exceed-counter + 4, //agc_dig_gain_step_size *** adi,agc-dig-gain-step-size + 3, //agc_dig_saturation_exceed_counter *** adi,agc-dig-saturation-exceed-counter + 1000, //agc_gain_update_interval_us *** adi,agc-gain-update-interval-us + 0, //agc_immed_gain_change_if_large_adc_overload_enable *** adi,agc-immed-gain-change-if-large-adc-overload-enable + 0, //agc_immed_gain_change_if_large_lmt_overload_enable *** adi,agc-immed-gain-change-if-large-lmt-overload-enable + 10, //agc_inner_thresh_high *** adi,agc-inner-thresh-high + 1, //agc_inner_thresh_high_dec_steps *** adi,agc-inner-thresh-high-dec-steps + 12, //agc_inner_thresh_low *** adi,agc-inner-thresh-low + 1, //agc_inner_thresh_low_inc_steps *** adi,agc-inner-thresh-low-inc-steps + 10, //agc_lmt_overload_large_exceed_counter *** adi,agc-lmt-overload-large-exceed-counter + 2, //agc_lmt_overload_large_inc_steps *** adi,agc-lmt-overload-large-inc-steps + 10, //agc_lmt_overload_small_exceed_counter *** adi,agc-lmt-overload-small-exceed-counter + 5, //agc_outer_thresh_high *** adi,agc-outer-thresh-high + 2, //agc_outer_thresh_high_dec_steps *** adi,agc-outer-thresh-high-dec-steps + 18, //agc_outer_thresh_low *** adi,agc-outer-thresh-low + 2, //agc_outer_thresh_low_inc_steps *** adi,agc-outer-thresh-low-inc-steps + 1, //agc_attack_delay_extra_margin_us; *** adi,agc-attack-delay-extra-margin-us + 0, //agc_sync_for_gain_counter_enable *** adi,agc-sync-for-gain-counter-enable + /* Fast AGC */ + 64, //fagc_dec_pow_measuremnt_duration *** adi,fagc-dec-pow-measurement-duration + 260, //fagc_state_wait_time_ns *** adi,fagc-state-wait-time-ns + /* Fast AGC - Low Power */ + 0, //fagc_allow_agc_gain_increase *** adi,fagc-allow-agc-gain-increase-enable + 5, //fagc_lp_thresh_increment_time *** adi,fagc-lp-thresh-increment-time + 1, //fagc_lp_thresh_increment_steps *** adi,fagc-lp-thresh-increment-steps + /* Fast AGC - Lock Level */ + 10, //fagc_lock_level *** adi,fagc-lock-level + 1, //fagc_lock_level_lmt_gain_increase_en *** adi,fagc-lock-level-lmt-gain-increase-enable + 5, //fagc_lock_level_gain_increase_upper_limit *** adi,fagc-lock-level-gain-increase-upper-limit + /* Fast AGC - Peak Detectors and Final Settling */ + 1, //fagc_lpf_final_settling_steps *** adi,fagc-lpf-final-settling-steps + 1, //fagc_lmt_final_settling_steps *** adi,fagc-lmt-final-settling-steps + 3, //fagc_final_overrange_count *** adi,fagc-final-overrange-count + /* Fast AGC - Final Power Test */ + 0, //fagc_gain_increase_after_gain_lock_en *** adi,fagc-gain-increase-after-gain-lock-enable + /* Fast AGC - Unlocking the Gain */ + 0, //fagc_gain_index_type_after_exit_rx_mode *** adi,fagc-gain-index-type-after-exit-rx-mode + 1, //fagc_use_last_lock_level_for_set_gain_en *** adi,fagc-use-last-lock-level-for-set-gain-enable + 1, //fagc_rst_gla_stronger_sig_thresh_exceeded_en *** adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable + 5, //fagc_optimized_gain_offset *** adi,fagc-optimized-gain-offset + 10, //fagc_rst_gla_stronger_sig_thresh_above_ll *** adi,fagc-rst-gla-stronger-sig-thresh-above-ll + 1, //fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en *** adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable + 1, //fagc_rst_gla_engergy_lost_goto_optim_gain_en *** adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable + 10, //fagc_rst_gla_engergy_lost_sig_thresh_below_ll *** adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll + 8, //fagc_energy_lost_stronger_sig_gain_lock_exit_cnt *** adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt + 1, //fagc_rst_gla_large_adc_overload_en *** adi,fagc-rst-gla-large-adc-overload-enable + 1, //fagc_rst_gla_large_lmt_overload_en *** adi,fagc-rst-gla-large-lmt-overload-enable + 0, //fagc_rst_gla_en_agc_pulled_high_en *** adi,fagc-rst-gla-en-agc-pulled-high-enable + 0, //fagc_rst_gla_if_en_agc_pulled_high_mode *** adi,fagc-rst-gla-if-en-agc-pulled-high-mode + 64, //fagc_power_measurement_duration_in_state5 *** adi,fagc-power-measurement-duration-in-state5 + /* RSSI Control */ + 1, //rssi_delay *** adi,rssi-delay + 1000, //rssi_duration *** adi,rssi-duration + 3, //rssi_restart_mode *** adi,rssi-restart-mode + 0, //rssi_unit_is_rx_samples_enable *** adi,rssi-unit-is-rx-samples-enable + 1, //rssi_wait *** adi,rssi-wait + /* Aux ADC Control */ + 256, //aux_adc_decimation *** adi,aux-adc-decimation + 40000000UL, //aux_adc_rate *** adi,aux-adc-rate + /* AuxDAC Control */ + 1, //aux_dac_manual_mode_enable *** adi,aux-dac-manual-mode-enable + 0, //aux_dac1_default_value_mV *** adi,aux-dac1-default-value-mV + 0, //aux_dac1_active_in_rx_enable *** adi,aux-dac1-active-in-rx-enable + 0, //aux_dac1_active_in_tx_enable *** adi,aux-dac1-active-in-tx-enable + 0, //aux_dac1_active_in_alert_enable *** adi,aux-dac1-active-in-alert-enable + 0, //aux_dac1_rx_delay_us *** adi,aux-dac1-rx-delay-us + 0, //aux_dac1_tx_delay_us *** adi,aux-dac1-tx-delay-us + 0, //aux_dac2_default_value_mV *** adi,aux-dac2-default-value-mV + 0, //aux_dac2_active_in_rx_enable *** adi,aux-dac2-active-in-rx-enable + 0, //aux_dac2_active_in_tx_enable *** adi,aux-dac2-active-in-tx-enable + 0, //aux_dac2_active_in_alert_enable *** adi,aux-dac2-active-in-alert-enable + 0, //aux_dac2_rx_delay_us *** adi,aux-dac2-rx-delay-us + 0, //aux_dac2_tx_delay_us *** adi,aux-dac2-tx-delay-us + /* Temperature Sensor Control */ + 256, //temp_sense_decimation *** adi,temp-sense-decimation + 1000, //temp_sense_measurement_interval_ms *** adi,temp-sense-measurement-interval-ms + 0xCE, //temp_sense_offset_signed *** adi,temp-sense-offset-signed + 1, //temp_sense_periodic_measurement_enable *** adi,temp-sense-periodic-measurement-enable + /* Control Out Setup */ + 0xFF, //ctrl_outs_enable_mask *** adi,ctrl-outs-enable-mask + 0, //ctrl_outs_index *** adi,ctrl-outs-index + /* External LNA Control */ + 0, //elna_settling_delay_ns *** adi,elna-settling-delay-ns + 0, //elna_gain_mdB *** adi,elna-gain-mdB + 0, //elna_bypass_loss_mdB *** adi,elna-bypass-loss-mdB + 0, //elna_rx1_gpo0_control_enable *** adi,elna-rx1-gpo0-control-enable + 0, //elna_rx2_gpo1_control_enable *** adi,elna-rx2-gpo1-control-enable + 0, //elna_gaintable_all_index_enable *** adi,elna-gaintable-all-index-enable + /* Digital Interface Control */ + 0, //digital_interface_tune_skip_mode *** adi,digital-interface-tune-skip-mode + 0, //digital_interface_tune_fir_disable *** adi,digital-interface-tune-fir-disable + 1, //pp_tx_swap_enable *** adi,pp-tx-swap-enable + 1, //pp_rx_swap_enable *** adi,pp-rx-swap-enable + 0, //tx_channel_swap_enable *** adi,tx-channel-swap-enable + 0, //rx_channel_swap_enable *** adi,rx-channel-swap-enable + 1, //rx_frame_pulse_mode_enable *** adi,rx-frame-pulse-mode-enable + 0, //two_t_two_r_timing_enable *** adi,2t2r-timing-enable + 0, //invert_data_bus_enable *** adi,invert-data-bus-enable + 0, //invert_data_clk_enable *** adi,invert-data-clk-enable + 0, //fdd_alt_word_order_enable *** adi,fdd-alt-word-order-enable + 0, //invert_rx_frame_enable *** adi,invert-rx-frame-enable + 0, //fdd_rx_rate_2tx_enable *** adi,fdd-rx-rate-2tx-enable + 0, //swap_ports_enable *** adi,swap-ports-enable + 0, //single_data_rate_enable *** adi,single-data-rate-enable + 1, //lvds_mode_enable *** adi,lvds-mode-enable + 0, //half_duplex_mode_enable *** adi,half-duplex-mode-enable + 0, //single_port_mode_enable *** adi,single-port-mode-enable + 0, //full_port_enable *** adi,full-port-enable + 0, //full_duplex_swap_bits_enable *** adi,full-duplex-swap-bits-enable + 0, //delay_rx_data *** adi,delay-rx-data + 0, //rx_data_clock_delay *** adi,rx-data-clock-delay + 4, //rx_data_delay *** adi,rx-data-delay + 7, //tx_fb_clock_delay *** adi,tx-fb-clock-delay + 0, //tx_data_delay *** adi,tx-data-delay + 150, //lvds_bias_mV *** adi,lvds-bias-mV + 1, //lvds_rx_onchip_termination_enable *** adi,lvds-rx-onchip-termination-enable + 0, //rx1rx2_phase_inversion_en *** adi,rx1-rx2-phase-inversion-enable + 0xFF, //lvds_invert1_control *** adi,lvds-invert1-control + 0x0F, //lvds_invert2_control *** adi,lvds-invert2-control + /* GPO Control */ + 0, //gpo0_inactive_state_high_enable *** adi,gpo0-inactive-state-high-enable + 0, //gpo1_inactive_state_high_enable *** adi,gpo1-inactive-state-high-enable + 0, //gpo2_inactive_state_high_enable *** adi,gpo2-inactive-state-high-enable + 0, //gpo3_inactive_state_high_enable *** adi,gpo3-inactive-state-high-enable + 0, //gpo0_slave_rx_enable *** adi,gpo0-slave-rx-enable + 0, //gpo0_slave_tx_enable *** adi,gpo0-slave-tx-enable + 0, //gpo1_slave_rx_enable *** adi,gpo1-slave-rx-enable + 0, //gpo1_slave_tx_enable *** adi,gpo1-slave-tx-enable + 0, //gpo2_slave_rx_enable *** adi,gpo2-slave-rx-enable + 0, //gpo2_slave_tx_enable *** adi,gpo2-slave-tx-enable + 0, //gpo3_slave_rx_enable *** adi,gpo3-slave-rx-enable + 0, //gpo3_slave_tx_enable *** adi,gpo3-slave-tx-enable + 0, //gpo0_rx_delay_us *** adi,gpo0-rx-delay-us + 0, //gpo0_tx_delay_us *** adi,gpo0-tx-delay-us + 0, //gpo1_rx_delay_us *** adi,gpo1-rx-delay-us + 0, //gpo1_tx_delay_us *** adi,gpo1-tx-delay-us + 0, //gpo2_rx_delay_us *** adi,gpo2-rx-delay-us + 0, //gpo2_tx_delay_us *** adi,gpo2-tx-delay-us + 0, //gpo3_rx_delay_us *** adi,gpo3-rx-delay-us + 0, //gpo3_tx_delay_us *** adi,gpo3-tx-delay-us + /* Tx Monitor Control */ + 37000, //low_high_gain_threshold_mdB *** adi,txmon-low-high-thresh + 0, //low_gain_dB *** adi,txmon-low-gain + 24, //high_gain_dB *** adi,txmon-high-gain + 0, //tx_mon_track_en *** adi,txmon-dc-tracking-enable + 0, //one_shot_mode_en *** adi,txmon-one-shot-mode-enable + 511, //tx_mon_delay *** adi,txmon-delay + 8192, //tx_mon_duration *** adi,txmon-duration + 2, //tx1_mon_front_end_gain *** adi,txmon-1-front-end-gain + 2, //tx2_mon_front_end_gain *** adi,txmon-2-front-end-gain + 48, //tx1_mon_lo_cm *** adi,txmon-1-lo-cm + 48, //tx2_mon_lo_cm *** adi,txmon-2-lo-cm + /* GPIO definitions */ + -1, //gpio_resetb *** reset-gpios + /* MCS Sync */ + -1, //gpio_sync *** sync-gpios + -1, //gpio_cal_sw1 *** cal-sw1-gpios + -1, //gpio_cal_sw2 *** cal-sw2-gpios + /* External LO clocks */ + NULL, //(*ad9361_rfpll_ext_recalc_rate)() + NULL, //(*ad9361_rfpll_ext_round_rate)() + NULL //(*ad9361_rfpll_ext_set_rate)() +}; + +static AD9361_RXFIRConfig rx_fir_config = { // BPF PASSBAND 3/20 fs to 1/4 fs + 3, // rx + 0, // rx_gain + 1, // rx_dec + {-4, -6, -37, 35, 186, 86, -284, -315, + 107, 219, -4, 271, 558, -307, -1182, -356, + 658, 157, 207, 1648, 790, -2525, -2553, 748, + 865, -476, 3737, 6560, -3583, -14731, -5278, 14819, + 14819, -5278, -14731, -3583, 6560, 3737, -476, 865, + 748, -2553, -2525, 790, 1648, 207, 157, 658, + -356, -1182, -307, 558, 271, -4, 219, 107, + -315, -284, 86, 186, 35, -37, -6, -4, + 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}, // rx_coef[128] + 64, // rx_coef_size + {0, 0, 0, 0, 0, 0}, //rx_path_clks[6] + 0 // rx_bandwidth +}; + +static AD9361_TXFIRConfig tx_fir_config = { // BPF PASSBAND 3/20 fs to 1/4 fs + 3, // tx + -6, // tx_gain + 1, // tx_int + {-4, -6, -37, 35, 186, 86, -284, -315, + 107, 219, -4, 271, 558, -307, -1182, -356, + 658, 157, 207, 1648, 790, -2525, -2553, 748, + 865, -476, 3737, 6560, -3583, -14731, -5278, 14819, + 14819, -5278, -14731, -3583, 6560, 3737, -476, 865, + 748, -2553, -2525, 790, 1648, 207, 157, 658, + -356, -1182, -307, 558, 271, -4, 219, 107, + -315, -284, 86, 186, 35, -37, -6, -4, + 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}, // tx_coef[128] + 64, // tx_coef_size + {0, 0, 0, 0, 0, 0}, // tx_path_clks[6] + 0 // tx_bandwidth +}; + + +static struct ad9361_rf_phy rx_phy; +int ad9361_phy_init(void ) +{ + gspi_t ad9361; + struct ad9361_platform_func opt = { + .ad9361_spi_xfer = __ad9361_spi_xfer, + .ad9361_udelay = __ad9361_udelay, + .ad9361_axi_write = __ad9361_axi_write, + .ad9361_axi_read = __ad9361_axi_read, + .ad9361_reset = __ad9361_reset, + }; + + do { + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_0, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + ad9361 = gspi_open(GSPI_CS_AD9361, &format); + } while(0); + + opt.dev = ad9361; + ad9361_platform_init(&opt); + + // + do { + uint32_t ensm_mode; + + default_init_param.dev_sel = ID_AD9361; + default_init_param.gpio_sync = -1; + default_init_param.gpio_cal_sw1 = -1; + default_init_param.gpio_cal_sw2 = -1; + + ad9361_init(&rx_phy, NULL, &default_init_param); + + ad9361_set_tx_fir_config(&rx_phy, tx_fir_config); + ad9361_set_rx_fir_config(&rx_phy, rx_fir_config); + + //ad9361_set_en_state_machine_mode(&rx_phy, ENSM_MODE_ALERT); + //ad9361_get_en_state_machine_mode(&rx_phy, &ensm_mode); + //printf("SPI control - Alert: %s\r\n", ensm_mode == ENSM_MODE_ALERT ? "OK" : "Error"); + ad9361_set_en_state_machine_mode(&rx_phy, ENSM_MODE_FDD); + ad9361_get_en_state_machine_mode(&rx_phy, &ensm_mode); + printf("SPI control - RX: %s\n",ensm_mode == ENSM_MODE_FDD ? "OK" : "Error"); + + tsc_delay(10 * 1000); + //dac_init(&rx_phy, DATA_SEL_LB, 0); + adc_init(&rx_phy); + + + adc_set_calib_scale(&rx_phy, 0, 1, 0); + adc_set_calib_phase(&rx_phy, 0, 0, 0); + adc_set_calib_scale(&rx_phy, 1, 1, 0); + adc_set_calib_phase(&rx_phy, 1, 0, 0); + adc_set_calib_scale(&rx_phy, 2, 1, 0); + adc_set_calib_phase(&rx_phy, 2, 0, 0); + adc_set_calib_scale(&rx_phy, 3, 1, 0); + adc_set_calib_phase(&rx_phy, 3, 0, 0); + + dac_init(&rx_phy, DATA_SEL_DMA, 0); + + } while(0); + + return 0; +} + +void ad9361_status(char ch) +{ + uint32_t bandwidth; + uint32_t sampling_freq; + uint64_t lo_freq; + uint32_t aa, bb; + uint8_t fir_en_dis; + uint8_t gc_mode; + uint32_t tx_att; + + struct ad9361_rf_phy *phy = &rx_phy; + + ad9361_get_rx_rf_bandwidth(phy, &bandwidth); + printf("ad9361_get_rx_rf_bandwidth :%d\r\n", bandwidth); + ad9361_get_rx_sampling_freq(phy, &sampling_freq); + printf("ad9361_get_rx_sampling_freq :%d\r\n", sampling_freq); + ad9361_get_rx_lo_freq(phy, &lo_freq); + aa = lo_freq/1000; + bb = lo_freq%1000; + //xil_printf("ad9361_get_rx_lo_freq :%d%03d\r\n", lo_freq/1000, lo_freq%1000); + printf("ad9361_get_rx_lo_freq :%d%03d\r\n", aa, bb); + ad9361_get_rx_fir_en_dis(phy, &fir_en_dis); + printf("ad9361_get_rx_fir_en_dis :%d\r\n", fir_en_dis); + + + ad9361_get_tx_rf_bandwidth(phy, &bandwidth); + printf("ad9361_get_tx_rf_bandwidth :%d\r\n", bandwidth); + ad9361_get_tx_sampling_freq(phy, &sampling_freq); + printf("ad9361_get_tx_sampling_freq :%d\r\n", sampling_freq); + ad9361_get_tx_lo_freq(phy, &lo_freq); + aa = lo_freq/1000; + bb = lo_freq%1000; + //xil_printf("ad9361_get_rx_lo_freq :%d%03d\r\n", lo_freq/1000, lo_freq%1000); + printf("ad9361_get_tx_lo_freq :%d%03d\r\n", aa, bb); + ad9361_get_tx_fir_en_dis(phy, &fir_en_dis); + printf("ad9361_get_tx_fir_en_dis :%d\r\n", fir_en_dis); + + ad9361_get_tx_attenuation(phy, 1, &tx_att); + printf("tx_attenuation = %d\r\n",tx_att); + + ad9361_get_rx_gain_control_mode(phy, 0, &gc_mode); + printf("ad9361_get_rx_gain_control_mode :%d\r\n", gc_mode); + printf("\r\n"); + printf("phy->clks[BB_REFCLK]->rate :%d\r\n", phy->clks[BB_REFCLK]->rate); + printf("phy->clks[BBPLL_CLK]->rate :%d\r\n", phy->clks[BBPLL_CLK]->rate); + printf("phy->clks[ADC_CLK]->rate :%d\r\n", phy->clks[ADC_CLK]->rate); + printf("phy->clks[R2_CLK]->rate :%d\r\n", phy->clks[R2_CLK]->rate); + printf("phy->clks[R1_CLK]->rate :%d\r\n", phy->clks[R1_CLK]->rate); + printf("phy->clks[CLKRF_CLK]->rate :%d\r\n", phy->clks[CLKRF_CLK]->rate); + printf("phy->clks[RX_SAMPL_CLK]->rate :%d\r\n", phy->clks[RX_SAMPL_CLK]->rate); +} + +static int do_ad9361(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint64_t lo_freq; + uint8_t gc_mode; + struct ad9361_rf_phy *phy = &rx_phy; + + if (memcmp(argv[1], "bw", 2) == 0){ + ad9361_set_tx_rf_bandwidth(phy, atol(argv[2])); + ad9361_set_rx_rf_bandwidth(phy, atol(argv[2])); + } + else if (memcmp(argv[1], "init", 4) == 0) { + ad9361_phy_init(); + } + else if (memcmp(argv[1], "bistrx", 6) == 0) { + ad9361_bist_prbs(phy, BIST_INJ_RX); + } + else if (memcmp(argv[1], "bisttx", 6) == 0) { + ad9361_bist_prbs(phy, BIST_INJ_TX); + } + else if (memcmp(argv[1], "loopback", 8) == 0) { + ad9361_bist_loopback(phy, atol(argv[2])); + } + else if (memcmp(argv[1], "sf", 2) == 0){ + ad9361_set_tx_sampling_freq(phy, atol(argv[2])); + ad9361_set_rx_sampling_freq(phy, atol(argv[2])); + } + else if (memcmp(argv[1], "txlo", 4) == 0){ + ad9361_set_tx_lo_freq(phy, atoll(argv[2])); + //ad9361_set_rx_lo_freq(phy, atoll(argv[2])); + } + else if (memcmp(argv[1], "rxlo", 4) == 0){ + //ad9361_set_tx_lo_freq(phy, atoll(argv[2])); + ad9361_set_rx_lo_freq(phy, atoll(argv[2])); + } + else if (memcmp(argv[1], "lo+", 3) == 0){ + ad9361_get_rx_lo_freq(phy, &lo_freq); + ad9361_set_rx_lo_freq(phy, lo_freq + 100000); + ad9361_get_tx_lo_freq(phy, &lo_freq); + ad9361_set_tx_lo_freq(phy, lo_freq + 100000); + } + else if (memcmp(argv[1], "lo-", 3) == 0){ + ad9361_get_rx_lo_freq(phy, &lo_freq); + ad9361_set_rx_lo_freq(phy, lo_freq - 100000); + ad9361_get_tx_lo_freq(phy, &lo_freq); + ad9361_set_tx_lo_freq(phy, lo_freq - 100000); + } + else if (memcmp(argv[1], "sta", 3) == 0){ + ad9361_status(atoi(argv[2])); + } + else if(memcmp(argv[1], "mgc1", 4) == 0){ + ad9361_set_rx_gain_control_mode (phy, 0, RF_GAIN_MGC); + ad9361_get_rx_gain_control_mode (phy, 0, &gc_mode); + printf("GAIN control - RX1-manual: %s\n", gc_mode == RF_GAIN_MGC ? "true" : "Error"); + ad9361_set_rx_rf_gain (phy, 0, atol(argv[2])); + } + else if(memcmp(argv[1], "mgc2", 4) == 0){ + ad9361_set_rx_gain_control_mode (phy, 1, RF_GAIN_MGC); + ad9361_get_rx_gain_control_mode (phy, 1, &gc_mode); + printf("GAIN control - RX2-manual: %s\n", gc_mode == RF_GAIN_MGC ? "true" : "Error"); + ad9361_set_rx_rf_gain (phy, 1, atol(argv[2])); + } + else if(memcmp(argv[1], "fast_attack",11) == 0){ + ad9361_set_rx_gain_control_mode (phy, 0, RF_GAIN_FASTATTACK_AGC); + ad9361_get_rx_gain_control_mode (phy, 0, &gc_mode); + printf("GAIN control - RX1-fast_attack: %s\n", gc_mode == RF_GAIN_FASTATTACK_AGC ? "true" : "Error"); + ad9361_set_rx_gain_control_mode (phy, 1, RF_GAIN_FASTATTACK_AGC); + ad9361_get_rx_gain_control_mode (phy, 1, &gc_mode); + printf("GAIN control - RX2-fast_attack: %s\n", gc_mode == RF_GAIN_FASTATTACK_AGC ? "true" : "Error"); + } + else if(memcmp(argv[1], "slow_attack",11) == 0){ + ad9361_set_rx_gain_control_mode (phy, 0, RF_GAIN_SLOWATTACK_AGC); + ad9361_get_rx_gain_control_mode (phy, 0, &gc_mode); + printf("GAIN control - RX1-slow_attack: %s\n", gc_mode == RF_GAIN_SLOWATTACK_AGC ? "true" : "Error"); + ad9361_set_rx_gain_control_mode (phy, 1, RF_GAIN_SLOWATTACK_AGC); + ad9361_get_rx_gain_control_mode (phy, 1, &gc_mode); + printf("GAIN control - RX2-slow_attack: %s\n", gc_mode == RF_GAIN_SLOWATTACK_AGC ? "true" : "Error"); + } + else if (memcmp(argv[1],"rxextlo",7) == 0){ + ad9361_set_rx_lo_int_ext(phy, EXT_LO); + } + else if (memcmp(argv[1],"rxintlo",7) == 0){ + ad9361_set_rx_lo_int_ext(phy, INT_LO); + } + else if (memcmp(argv[1],"txextlo",7) == 0){ + ad9361_set_tx_lo_int_ext(phy, EXT_LO); + } + else if (memcmp(argv[1],"txintlo",7) == 0){ + ad9361_set_tx_lo_int_ext(phy, INT_LO); + } + else if (memcmp(argv[1], "att", 3) == 0){ + ad9361_set_tx_attenuation(phy, 0, atol(argv[2])); + ad9361_set_tx_attenuation(phy, 1, atol(argv[2])); + } +// else if (memcmp(cmd, "dds", 3) == 0){ +// dac_init(phy, DATA_SEL_DDS, 0); +// } +// else if (memcmp(cmd, "lb", 2) == 0){ +// dac_init(phy, DATA_SEL_LB, 0); +// } + else if (memcmp(argv[1], "dma", 3) == 0){ + dac_init(phy, DATA_SEL_DMA, 0); + axi1_write(0x01028, atol(argv[2])); + } + else if (memcmp(argv[1], "zero", 4) == 0){ + dac_init(phy, DATA_SEL_ZERO, 0); + dac_datasel(phy, -1, DATA_SEL_ZERO); + } + else if(memcmp(argv[1], "temp", 4) == 0) + { + int32_t temp = ad9361_get_temp(phy); + printf("the temperature of 9361 is %d\r\n",temp); + } + + else if (memcmp(argv[1], "?", 1) == 0){ + printf("sta ad9361 status\r\n"); + printf("init ad9361 init\r\n"); + printf("bw [val] set_rf_bandwidth[Hz]\r\n"); + printf("sf [val] set_sampling_freq[Hz]\r\n"); + printf("txlo [val] set_txlo_freq[Hz]\r\n"); + printf("rxlo [val] set_rxlo_freq[Hz]\r\n"); + printf("lo+ set_lo_freq\r\n"); + printf("lo- set_lo_freq\r\n"); + printf("mgcx [val] set rx x[1:2] gain is manual x[mdB]\r\n"); + printf("fast_attack set rx1 and rx2 gain is fast_attack\r\n"); + printf("slow_attack set rx1 and rx2 gain is slow_attack\r\n"); + printf("rxextlo set rx external lo\r\n"); + printf("rxintlo set rx internal lo\r\n"); + printf("txextlo set tx external lo\r\n"); + printf("txintlo set tx internal lo\r\n"); + printf("att [val] set_tx_attenuation[mdB]\r\n"); + printf("dma [val] set 0:RX1->TX 1:RX2->TX x[0:1]\r\n"); + printf("zero clear TX\r\n"); + } + else { + printf("\"%s\" is invalid, Enter \"?\" show help\r\n", argv[1]); + } + + return 0; +} + + +CON_CMD(ad9361, "ad9361 reg opts", NULL, do_ad9361) + diff --git a/src/ad9361.h b/src/ad9361.h new file mode 100644 index 0000000..5f74f66 --- /dev/null +++ b/src/ad9361.h @@ -0,0 +1,19 @@ +/* + * ad9361.h + * + * Created on: 2019-7-16 + * Author: Administrator + */ + +#ifndef AD9361_H_ +#define AD9361_H_ + +/* + * 6678 | <--- spi cs1 ---> | K7A | <---> | K7B | <--- spi --> ad9361 + * + * */ + +extern int ad9361_phy_init(void ); +extern void ad9361_status(char ch); + +#endif /* AD9361_H_ */ diff --git a/src/ad9361_lib/ad9361_api.c b/src/ad9361_lib/ad9361_api.c new file mode 100644 index 0000000..64e14af --- /dev/null +++ b/src/ad9361_lib/ad9361_api.c @@ -0,0 +1,2094 @@ +/***************************************************************************//** + * @file ad9361_api.c + * @brief Implementation of AD9361 API Driver. + * @author DBogdan (dragos.bogdan@analog.com) + ******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9361_dev.h" +#include "ad9361_api.h" +#include "platform.h" +#include "util.h" +#include "config.h" +#include + +#ifndef AXI_ADC_NOT_PRESENT +/******************************************************************************/ +/************************ Constants Definitions *******************************/ +/******************************************************************************/ +static struct axiadc_chip_info axiadc_chip_info_tbl[] = +{ + { + "4_CH_DEV", + 4 + }, + { + "2_CH_DEV", + 2 + }, +}; +#endif + +/** + * Initialize the AD9361 part. + * @param init_param The structure that contains the AD9361 initial parameters. + * @return A structure that contains the AD9361 current state in case of + * success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_init (struct ad9361_rf_phy *phy, struct spi_device *spi, AD9361_InitParam *init_param) +{ + int32_t ret = 0; + int32_t rev = 0; + int32_t i = 0; + + + memset(phy, 0, sizeof *phy); + + phy->spi = spi; + + phy->clk_refin = (struct clk *)zmalloc(sizeof(*phy->clk_refin)); + if (!phy->clk_refin) { + return -ENOMEM; + } + + phy->pdata = (struct ad9361_phy_platform_data *)zmalloc(sizeof(*phy->pdata)); + if (!phy->pdata) { + return -ENOMEM; + } + +#ifndef AXI_ADC_NOT_PRESENT + phy->adc_conv = (struct axiadc_converter *)zmalloc(sizeof(*phy->adc_conv)); + if (!phy->adc_conv) { + return -ENOMEM; + } + + phy->adc_state = (struct axiadc_state *)zmalloc(sizeof(*phy->adc_state)); + if (!phy->adc_state) { + return -ENOMEM; + } + phy->adc_state->phy = phy; +#endif + + /* Device selection */ + phy->dev_sel = init_param->dev_sel; + + /* Identification number */ + //phy->spi->id_no = init_param->id_no; + phy->id_no = init_param->id_no; + + /* Reference Clock */ + phy->clk_refin->rate = init_param->reference_clk_rate; + + /* Base Configuration */ + phy->pdata->fdd = init_param->frequency_division_duplex_mode_enable; + phy->pdata->fdd_independent_mode = init_param->frequency_division_duplex_independent_mode_enable; + phy->pdata->rx2tx2 = init_param->two_rx_two_tx_mode_enable; + phy->pdata->rx1tx1_mode_use_rx_num = init_param->one_rx_one_tx_mode_use_rx_num; + phy->pdata->rx1tx1_mode_use_tx_num = init_param->one_rx_one_tx_mode_use_tx_num; + phy->pdata->tdd_use_dual_synth = init_param->tdd_use_dual_synth_mode_enable; + phy->pdata->tdd_skip_vco_cal = init_param->tdd_skip_vco_cal_enable; + phy->pdata->rx_fastlock_delay_ns = init_param->rx_fastlock_delay_ns; + phy->pdata->tx_fastlock_delay_ns = init_param->tx_fastlock_delay_ns; + phy->pdata->trx_fastlock_pinctrl_en[0] = init_param->rx_fastlock_pincontrol_enable; + phy->pdata->trx_fastlock_pinctrl_en[1] = init_param->tx_fastlock_pincontrol_enable; + if (phy->dev_sel == ID_AD9363A) { + phy->pdata->use_ext_rx_lo = false; + phy->pdata->use_ext_tx_lo = false; + } else { + phy->pdata->use_ext_rx_lo = init_param->external_rx_lo_enable; + phy->pdata->use_ext_tx_lo = init_param->external_tx_lo_enable; + } + phy->pdata->dc_offset_update_events = init_param->dc_offset_tracking_update_event_mask; + phy->pdata->dc_offset_attenuation_high = init_param->dc_offset_attenuation_high_range; + phy->pdata->dc_offset_attenuation_low = init_param->dc_offset_attenuation_low_range; + phy->pdata->rf_dc_offset_count_high = init_param->dc_offset_count_high_range; + phy->pdata->rf_dc_offset_count_low = init_param->dc_offset_count_low_range; + phy->pdata->split_gt = init_param->split_gain_table_mode_enable; + phy->pdata->trx_synth_max_fref = init_param->trx_synthesizer_target_fref_overwrite_hz; + phy->pdata->qec_tracking_slow_mode_en = init_param->qec_tracking_slow_mode_enable; + + /* ENSM Control */ + phy->pdata->ensm_pin_pulse_mode = init_param->ensm_enable_pin_pulse_mode_enable; + phy->pdata->ensm_pin_ctrl = init_param->ensm_enable_txnrx_control_enable; + + /* LO Control */ + phy->pdata->rx_synth_freq = init_param->rx_synthesizer_frequency_hz; + phy->pdata->tx_synth_freq = init_param->tx_synthesizer_frequency_hz; + + /* Rate & BW Control */ + for(i = 0; i < 6; i++) { + phy->pdata->rx_path_clks[i] = init_param->rx_path_clock_frequencies[i]; + } + for(i = 0; i < 6; i++) { + phy->pdata->tx_path_clks[i] = init_param->tx_path_clock_frequencies[i]; + } + phy->pdata->rf_rx_bandwidth_Hz = init_param->rf_rx_bandwidth_hz; + phy->pdata->rf_tx_bandwidth_Hz = init_param->rf_tx_bandwidth_hz; + + /* RF Port Control */ + phy->pdata->rf_rx_input_sel = init_param->rx_rf_port_input_select; + phy->pdata->rf_tx_output_sel = init_param->tx_rf_port_input_select; + + /* TX Attenuation Control */ + phy->pdata->tx_atten = init_param->tx_attenuation_mdB; + phy->pdata->update_tx_gain_via_alert = init_param->update_tx_gain_in_alert_enable; + + /* Reference Clock Control */ + switch (phy->dev_sel) { + case ID_AD9363A: + phy->pdata->use_extclk = true; + break; + default: + phy->pdata->use_extclk = init_param->xo_disable_use_ext_refclk_enable; + } + phy->pdata->dcxo_coarse = init_param->dcxo_coarse_and_fine_tune[0]; + phy->pdata->dcxo_fine = init_param->dcxo_coarse_and_fine_tune[1]; + phy->pdata->ad9361_clkout_mode = (enum ad9361_clkout)init_param->clk_output_mode_select; + + /* Gain Control */ + phy->pdata->gain_ctrl.rx1_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx1_mode; + phy->pdata->gain_ctrl.rx2_mode = (enum rf_gain_ctrl_mode)init_param->gc_rx2_mode; + phy->pdata->gain_ctrl.adc_large_overload_thresh = init_param->gc_adc_large_overload_thresh; + phy->pdata->gain_ctrl.adc_ovr_sample_size = init_param->gc_adc_ovr_sample_size; + phy->pdata->gain_ctrl.adc_small_overload_thresh = init_param->gc_adc_small_overload_thresh; + phy->pdata->gain_ctrl.dec_pow_measuremnt_duration = init_param->gc_dec_pow_measurement_duration; + phy->pdata->gain_ctrl.dig_gain_en = init_param->gc_dig_gain_enable; + phy->pdata->gain_ctrl.lmt_overload_high_thresh = init_param->gc_lmt_overload_high_thresh; + phy->pdata->gain_ctrl.lmt_overload_low_thresh = init_param->gc_lmt_overload_low_thresh; + phy->pdata->gain_ctrl.low_power_thresh = init_param->gc_low_power_thresh; + phy->pdata->gain_ctrl.max_dig_gain = init_param->gc_max_dig_gain; + + /* Gain MGC Control */ + phy->pdata->gain_ctrl.mgc_dec_gain_step = init_param->mgc_dec_gain_step; + phy->pdata->gain_ctrl.mgc_inc_gain_step = init_param->mgc_inc_gain_step; + phy->pdata->gain_ctrl.mgc_rx1_ctrl_inp_en = init_param->mgc_rx1_ctrl_inp_enable; + phy->pdata->gain_ctrl.mgc_rx2_ctrl_inp_en = init_param->mgc_rx2_ctrl_inp_enable; + phy->pdata->gain_ctrl.mgc_split_table_ctrl_inp_gain_mode = init_param->mgc_split_table_ctrl_inp_gain_mode; + + /* Gain AGC Control */ + phy->pdata->gain_ctrl.adc_large_overload_exceed_counter = init_param->agc_adc_large_overload_exceed_counter; + phy->pdata->gain_ctrl.adc_large_overload_inc_steps = init_param->agc_adc_large_overload_inc_steps; + phy->pdata->gain_ctrl.adc_lmt_small_overload_prevent_gain_inc = init_param->agc_adc_lmt_small_overload_prevent_gain_inc_enable; + phy->pdata->gain_ctrl.adc_small_overload_exceed_counter = init_param->agc_adc_small_overload_exceed_counter; + phy->pdata->gain_ctrl.dig_gain_step_size = init_param->agc_dig_gain_step_size; + phy->pdata->gain_ctrl.dig_saturation_exceed_counter = init_param->agc_dig_saturation_exceed_counter; + phy->pdata->gain_ctrl.gain_update_interval_us = init_param->agc_gain_update_interval_us; + phy->pdata->gain_ctrl.immed_gain_change_if_large_adc_overload = init_param->agc_immed_gain_change_if_large_adc_overload_enable; + phy->pdata->gain_ctrl.immed_gain_change_if_large_lmt_overload = init_param->agc_immed_gain_change_if_large_lmt_overload_enable; + phy->pdata->gain_ctrl.agc_inner_thresh_high = init_param->agc_inner_thresh_high; + phy->pdata->gain_ctrl.agc_inner_thresh_high_dec_steps = init_param->agc_inner_thresh_high_dec_steps; + phy->pdata->gain_ctrl.agc_inner_thresh_low = init_param->agc_inner_thresh_low; + phy->pdata->gain_ctrl.agc_inner_thresh_low_inc_steps = init_param->agc_inner_thresh_low_inc_steps; + phy->pdata->gain_ctrl.lmt_overload_large_exceed_counter = init_param->agc_lmt_overload_large_exceed_counter; + phy->pdata->gain_ctrl.lmt_overload_large_inc_steps = init_param->agc_lmt_overload_large_inc_steps; + phy->pdata->gain_ctrl.lmt_overload_small_exceed_counter = init_param->agc_lmt_overload_small_exceed_counter; + phy->pdata->gain_ctrl.agc_outer_thresh_high = init_param->agc_outer_thresh_high; + phy->pdata->gain_ctrl.agc_outer_thresh_high_dec_steps = init_param->agc_outer_thresh_high_dec_steps; + phy->pdata->gain_ctrl.agc_outer_thresh_low = init_param->agc_outer_thresh_low; + phy->pdata->gain_ctrl.agc_outer_thresh_low_inc_steps = init_param->agc_outer_thresh_low_inc_steps; + phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us = init_param->agc_attack_delay_extra_margin_us; + phy->pdata->gain_ctrl.sync_for_gain_counter_en = init_param->agc_sync_for_gain_counter_enable; + + /* Fast AGC */ + phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration = init_param->fagc_dec_pow_measuremnt_duration; + phy->pdata->gain_ctrl.f_agc_state_wait_time_ns = init_param->fagc_state_wait_time_ns; + /* Fast AGC - Low Power */ + phy->pdata->gain_ctrl.f_agc_allow_agc_gain_increase = init_param->fagc_allow_agc_gain_increase; + phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_time = init_param->fagc_lp_thresh_increment_time; + phy->pdata->gain_ctrl.f_agc_lp_thresh_increment_steps = init_param->fagc_lp_thresh_increment_steps; + /* Fast AGC - Lock Level */ + phy->pdata->gain_ctrl.f_agc_lock_level = init_param->fagc_lock_level; + phy->pdata->gain_ctrl.f_agc_lock_level_lmt_gain_increase_en = init_param->fagc_lock_level_lmt_gain_increase_en; + phy->pdata->gain_ctrl.f_agc_lock_level_gain_increase_upper_limit = init_param->fagc_lock_level_gain_increase_upper_limit; + /* Fast AGC - Peak Detectors and Final Settling */ + phy->pdata->gain_ctrl.f_agc_lpf_final_settling_steps = init_param->fagc_lpf_final_settling_steps; + phy->pdata->gain_ctrl.f_agc_lmt_final_settling_steps = init_param->fagc_lmt_final_settling_steps; + phy->pdata->gain_ctrl.f_agc_final_overrange_count = init_param->fagc_final_overrange_count; + /* Fast AGC - Final Power Test */ + phy->pdata->gain_ctrl.f_agc_gain_increase_after_gain_lock_en = init_param->fagc_gain_increase_after_gain_lock_en; + /* Fast AGC - Unlocking the Gain */ + phy->pdata->gain_ctrl.f_agc_gain_index_type_after_exit_rx_mode = (enum f_agc_target_gain_index_type)init_param->fagc_gain_index_type_after_exit_rx_mode; + phy->pdata->gain_ctrl.f_agc_use_last_lock_level_for_set_gain_en = init_param->fagc_use_last_lock_level_for_set_gain_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_exceeded_en = init_param->fagc_rst_gla_stronger_sig_thresh_exceeded_en; + phy->pdata->gain_ctrl.f_agc_optimized_gain_offset = init_param->fagc_optimized_gain_offset; + phy->pdata->gain_ctrl.f_agc_rst_gla_stronger_sig_thresh_above_ll = init_param->fagc_rst_gla_stronger_sig_thresh_above_ll; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en = init_param->fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_goto_optim_gain_en = init_param->fagc_rst_gla_engergy_lost_goto_optim_gain_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_engergy_lost_sig_thresh_below_ll = init_param->fagc_rst_gla_engergy_lost_sig_thresh_below_ll; + phy->pdata->gain_ctrl.f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt = init_param->fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; + phy->pdata->gain_ctrl.f_agc_rst_gla_large_adc_overload_en = init_param->fagc_rst_gla_large_adc_overload_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_large_lmt_overload_en = init_param->fagc_rst_gla_large_lmt_overload_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_en_agc_pulled_high_en = init_param->fagc_rst_gla_en_agc_pulled_high_en; + phy->pdata->gain_ctrl.f_agc_rst_gla_if_en_agc_pulled_high_mode = (enum f_agc_target_gain_index_type)init_param->fagc_rst_gla_if_en_agc_pulled_high_mode; + phy->pdata->gain_ctrl.f_agc_power_measurement_duration_in_state5 = init_param->fagc_power_measurement_duration_in_state5; + + /* RSSI Control */ + phy->pdata->rssi_ctrl.rssi_delay = init_param->rssi_delay; + phy->pdata->rssi_ctrl.rssi_duration = init_param->rssi_duration; + phy->pdata->rssi_ctrl.restart_mode = (enum rssi_restart_mode)init_param->rssi_restart_mode; + phy->pdata->rssi_ctrl.rssi_unit_is_rx_samples = init_param->rssi_unit_is_rx_samples_enable; + phy->pdata->rssi_ctrl.rssi_wait = init_param->rssi_wait; + + /* Aux ADC Control */ + phy->pdata->auxadc_ctrl.auxadc_decimation = init_param->aux_adc_decimation; + phy->pdata->auxadc_ctrl.auxadc_clock_rate = init_param->aux_adc_rate; + + /* AuxDAC Control */ + phy->pdata->auxdac_ctrl.auxdac_manual_mode_en = init_param->aux_dac_manual_mode_enable; + phy->pdata->auxdac_ctrl.dac1_default_value = init_param->aux_dac1_default_value_mV; + phy->pdata->auxdac_ctrl.dac1_in_rx_en = init_param->aux_dac1_active_in_rx_enable; + phy->pdata->auxdac_ctrl.dac1_in_tx_en = init_param->aux_dac1_active_in_tx_enable; + phy->pdata->auxdac_ctrl.dac1_in_alert_en = init_param->aux_dac1_active_in_alert_enable; + phy->pdata->auxdac_ctrl.dac1_rx_delay_us = init_param->aux_dac1_rx_delay_us; + phy->pdata->auxdac_ctrl.dac1_tx_delay_us = init_param->aux_dac1_tx_delay_us; + phy->pdata->auxdac_ctrl.dac2_default_value = init_param->aux_dac2_default_value_mV; + phy->pdata->auxdac_ctrl.dac2_in_rx_en = init_param->aux_dac2_active_in_rx_enable; + phy->pdata->auxdac_ctrl.dac2_in_tx_en = init_param->aux_dac2_active_in_tx_enable; + phy->pdata->auxdac_ctrl.dac2_in_alert_en = init_param->aux_dac2_active_in_alert_enable; + phy->pdata->auxdac_ctrl.dac2_rx_delay_us = init_param->aux_dac2_rx_delay_us; + phy->pdata->auxdac_ctrl.dac2_tx_delay_us = init_param->aux_dac2_tx_delay_us; + + /* Temperature Sensor Control */ + phy->pdata->auxadc_ctrl.temp_sensor_decimation = init_param->temp_sense_decimation; + phy->pdata->auxadc_ctrl.temp_time_inteval_ms = init_param->temp_sense_measurement_interval_ms; + phy->pdata->auxadc_ctrl.offset = init_param->temp_sense_offset_signed; + phy->pdata->auxadc_ctrl.periodic_temp_measuremnt = init_param->temp_sense_periodic_measurement_enable; + + /* Control Out Setup */ + phy->pdata->ctrl_outs_ctrl.en_mask = init_param->ctrl_outs_enable_mask; + phy->pdata->ctrl_outs_ctrl.index = init_param->ctrl_outs_index; + + /* External LNA Control */ + phy->pdata->elna_ctrl.settling_delay_ns = init_param->elna_settling_delay_ns; + phy->pdata->elna_ctrl.gain_mdB = init_param->elna_gain_mdB; + phy->pdata->elna_ctrl.bypass_loss_mdB = init_param->elna_bypass_loss_mdB; + phy->pdata->elna_ctrl.elna_1_control_en = init_param->elna_rx1_gpo0_control_enable; + phy->pdata->elna_ctrl.elna_2_control_en = init_param->elna_rx2_gpo1_control_enable; + phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en = init_param->elna_gaintable_all_index_enable; + + /* Digital Interface Control */ + phy->pdata->dig_interface_tune_skipmode = (init_param->digital_interface_tune_skip_mode); + phy->pdata->dig_interface_tune_fir_disable = (init_param->digital_interface_tune_fir_disable); + phy->pdata->port_ctrl.pp_conf[0] = (init_param->pp_tx_swap_enable << 7); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->pp_rx_swap_enable << 6); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->tx_channel_swap_enable << 5); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_channel_swap_enable << 4); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->rx_frame_pulse_mode_enable << 3); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->two_t_two_r_timing_enable << 2); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_bus_enable << 1); + phy->pdata->port_ctrl.pp_conf[0] |= (init_param->invert_data_clk_enable << 0); + phy->pdata->port_ctrl.pp_conf[1] = (init_param->fdd_alt_word_order_enable << 7); + phy->pdata->port_ctrl.pp_conf[1] |= (init_param->invert_rx_frame_enable << 2); + phy->pdata->port_ctrl.pp_conf[2] = (init_param->fdd_rx_rate_2tx_enable << 7); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->swap_ports_enable << 6); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_data_rate_enable << 5); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->lvds_mode_enable << 4); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->half_duplex_mode_enable << 3); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->single_port_mode_enable << 2); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_port_enable << 1); + phy->pdata->port_ctrl.pp_conf[2] |= (init_param->full_duplex_swap_bits_enable << 0); + phy->pdata->port_ctrl.pp_conf[1] |= (init_param->delay_rx_data & 0x3); + phy->pdata->port_ctrl.rx_clk_data_delay = DATA_CLK_DELAY(init_param->rx_data_clock_delay); + phy->pdata->port_ctrl.rx_clk_data_delay |= RX_DATA_DELAY(init_param->rx_data_delay); + phy->pdata->port_ctrl.tx_clk_data_delay = FB_CLK_DELAY(init_param->tx_fb_clock_delay); + phy->pdata->port_ctrl.tx_clk_data_delay |= TX_DATA_DELAY(init_param->tx_data_delay); + phy->pdata->port_ctrl.lvds_bias_ctrl = ((init_param->lvds_bias_mV - 75) / 75) & 0x7; + phy->pdata->port_ctrl.lvds_bias_ctrl |= (init_param->lvds_rx_onchip_termination_enable << 5); + phy->pdata->rx1rx2_phase_inversion_en = init_param->rx1rx2_phase_inversion_en; + + /* GPO Control */ + phy->pdata->gpo_ctrl.gpo0_inactive_state_high_en = init_param->gpo0_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo1_inactive_state_high_en = init_param->gpo1_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo2_inactive_state_high_en = init_param->gpo2_inactive_state_high_enable; + phy->pdata->gpo_ctrl.gpo3_inactive_state_high_en = init_param->gpo3_inactive_state_high_enable; + + phy->pdata->gpo_ctrl.gpo0_slave_rx_en = init_param->gpo0_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo0_slave_tx_en = init_param->gpo0_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo1_slave_rx_en = init_param->gpo1_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo1_slave_tx_en = init_param->gpo1_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo2_slave_rx_en = init_param->gpo2_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo2_slave_tx_en = init_param->gpo2_slave_tx_enable; + phy->pdata->gpo_ctrl.gpo3_slave_rx_en = init_param->gpo3_slave_rx_enable; + phy->pdata->gpo_ctrl.gpo3_slave_tx_en = init_param->gpo3_slave_tx_enable; + + phy->pdata->gpo_ctrl.gpo0_rx_delay_us = init_param->gpo0_rx_delay_us; + phy->pdata->gpo_ctrl.gpo0_tx_delay_us = init_param->gpo0_tx_delay_us; + phy->pdata->gpo_ctrl.gpo1_rx_delay_us = init_param->gpo1_rx_delay_us; + phy->pdata->gpo_ctrl.gpo1_tx_delay_us = init_param->gpo1_tx_delay_us; + phy->pdata->gpo_ctrl.gpo2_rx_delay_us = init_param->gpo2_rx_delay_us; + phy->pdata->gpo_ctrl.gpo2_tx_delay_us = init_param->gpo2_tx_delay_us; + phy->pdata->gpo_ctrl.gpo3_rx_delay_us = init_param->gpo3_rx_delay_us; + phy->pdata->gpo_ctrl.gpo3_tx_delay_us = init_param->gpo3_tx_delay_us; + + /* Tx Monitor Control */ + phy->pdata->txmon_ctrl.low_high_gain_threshold_mdB = init_param->low_high_gain_threshold_mdB; + phy->pdata->txmon_ctrl.low_gain_dB = init_param->low_gain_dB; + phy->pdata->txmon_ctrl.high_gain_dB = init_param->high_gain_dB; + phy->pdata->txmon_ctrl.tx_mon_track_en = init_param->tx_mon_track_en; + phy->pdata->txmon_ctrl.one_shot_mode_en = init_param->one_shot_mode_en; + phy->pdata->txmon_ctrl.tx_mon_delay = init_param->tx_mon_delay; + phy->pdata->txmon_ctrl.tx_mon_duration = init_param->tx_mon_duration; + phy->pdata->txmon_ctrl.tx1_mon_front_end_gain = init_param->tx1_mon_front_end_gain; + phy->pdata->txmon_ctrl.tx2_mon_front_end_gain = init_param->tx2_mon_front_end_gain; + phy->pdata->txmon_ctrl.tx1_mon_lo_cm = init_param->tx1_mon_lo_cm; + phy->pdata->txmon_ctrl.tx2_mon_lo_cm = init_param->tx2_mon_lo_cm; + + phy->pdata->debug_mode = true; + phy->pdata->gpio_resetb = init_param->gpio_resetb; + /* Optional: next three GPIOs are used for MCS synchronization */ + phy->pdata->gpio_sync = init_param->gpio_sync; + phy->pdata->gpio_cal_sw1 = init_param->gpio_cal_sw1; + phy->pdata->gpio_cal_sw2 = init_param->gpio_cal_sw2; + + phy->pdata->port_ctrl.digital_io_ctrl = 0; + phy->pdata->port_ctrl.lvds_invert[0] = init_param->lvds_invert1_control; + phy->pdata->port_ctrl.lvds_invert[1] = init_param->lvds_invert2_control; + +#ifndef AXI_ADC_NOT_PRESENT + phy->adc_conv->chip_info = &axiadc_chip_info_tbl[phy->pdata->rx2tx2 ? ID_AD9361 : ID_AD9364]; +#endif + + phy->rx_eq_2tx = false; + + phy->current_table = RXGAIN_TBLS_END; + phy->bypass_tx_fir = true; + phy->bypass_rx_fir = true; + phy->rate_governor = 1; + phy->rfdc_track_en = true; + phy->bbdc_track_en = true; + phy->quad_track_en = true; + + phy->bist_loopback_mode = 0; + phy->bist_prbs_mode = BIST_DISABLE; + phy->bist_tone_mode = BIST_DISABLE; + phy->bist_tone_freq_Hz = 0; + phy->bist_tone_level_dB = 0; + phy->bist_tone_mask = 0; + + //ad9361_reset(phy); + + ret = ad9361_spi_read(phy->spi, REG_PRODUCT_ID); + + if ((ret & PRODUCT_ID_MASK) != PRODUCT_ID_9361) { + printf("%s : Unsupported PRODUCT_ID 0x%X", __func__, (unsigned int)ret); + ret = -ENODEV; + goto out; + } + rev = ret & REV_MASK; + + /* if (AD9364_DEVICE) { + phy->pdata->rx2tx2 = false; + phy->pdata->rx1tx1_mode_use_rx_num = 1; + phy->pdata->rx1tx1_mode_use_tx_num = 1; + } + */ + switch (phy->dev_sel) { + case ID_AD9364: + phy->pdata->rx2tx2 = false; + phy->pdata->rx1tx1_mode_use_rx_num = 1; + phy->pdata->rx1tx1_mode_use_tx_num = 1; + break; + default: + phy->pdata->rx2tx2 = true; + } + + phy->ad9361_rfpll_ext_recalc_rate = init_param->ad9361_rfpll_ext_recalc_rate; + phy->ad9361_rfpll_ext_round_rate = init_param->ad9361_rfpll_ext_round_rate; + phy->ad9361_rfpll_ext_set_rate = init_param->ad9361_rfpll_ext_set_rate; + + ret = register_clocks(phy); + if (ret < 0) + goto out; + +#ifndef AXI_ADC_NOT_PRESENT + axiadc_init(phy); + phy->adc_state->pcore_version = axiadc_read(phy->adc_state, ADI_REG_VERSION); +#endif + + ad9361_init_gain_tables(phy); + + ad9361_spi_write(phy->spi, REG_REFERENCE_CLOCK_CYCLES,REFERENCE_CLOCK_CYCLES_PER_US(0x0f)); + + ret = ad9361_setup(phy); + if (ret < 0) + goto out; + +#ifndef AXI_ADC_NOT_PRESENT + /* platform specific wrapper to call ad9361_post_setup() */ + ret = axiadc_post_setup(phy); + if (ret < 0) + goto out; +#endif + + printf("%s : AD936x Rev %d successfully initialized\n", __func__, (int)rev); + + return 0; + +out: +#ifndef AXI_ADC_NOT_PRESENT + free(phy->adc_conv); + free(phy->adc_state); +#endif + free(phy->clk_refin); + free(phy->pdata); + printf("%s : AD936x initialization error\n", __func__); + + return -ENODEV; +} + +/** + * Set the Enable State Machine (ENSM) mode. + * @param phy The AD9361 current state structure. + * @param mode The ENSM mode. + * Accepted values: + * ENSM_MODE_TX + * ENSM_MODE_RX + * ENSM_MODE_ALERT + * ENSM_MODE_FDD + * ENSM_MODE_WAIT + * ENSM_MODE_SLEEP + * ENSM_MODE_PINCTRL + * ENSM_MODE_PINCTRL_FDD_INDEP + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + uint8_t ensm_state; + bool pinctrl = false; + + phy->pdata->fdd_independent_mode = false; + + switch (mode) { + case ENSM_MODE_TX: + ensm_state = ENSM_STATE_TX; + break; + case ENSM_MODE_RX: + ensm_state = ENSM_STATE_RX; + break; + case ENSM_MODE_ALERT: + ensm_state = ENSM_STATE_ALERT; + break; + case ENSM_MODE_FDD: + ensm_state = ENSM_STATE_FDD; + break; + case ENSM_MODE_WAIT: + ensm_state = ENSM_STATE_SLEEP_WAIT; + break; + case ENSM_MODE_SLEEP: + ensm_state = ENSM_STATE_SLEEP; + break; + case ENSM_MODE_PINCTRL: + ensm_state = ENSM_STATE_SLEEP_WAIT; + pinctrl = true; + break; + case ENSM_MODE_PINCTRL_FDD_INDEP: + ensm_state = ENSM_STATE_FDD; + phy->pdata->fdd_independent_mode = true; + break; + default: + return -EINVAL; + } + + ad9361_set_ensm_mode(phy, phy->pdata->fdd, pinctrl); + ret = ad9361_ensm_set_state(phy, ensm_state, pinctrl); + + return ret; +} + +/** + * Get the Enable State Machine (ENSM) mode. + * @param phy The AD9361 current state structure. + * @param mode A variable to store the selected ENSM mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + uint8_t ensm_state; + bool pinctrl = false; + int32_t ret; + + ensm_state = ad9361_spi_read(phy->spi, REG_STATE); + ensm_state &= ENSM_STATE(~0); + ret = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_1); + if ((ret & ENABLE_ENSM_PIN_CTRL) == ENABLE_ENSM_PIN_CTRL) + pinctrl = true; + + switch (ensm_state) { + case ENSM_STATE_TX: + *mode = ENSM_MODE_TX; + break; + case ENSM_STATE_RX: + *mode = ENSM_MODE_RX; + break; + case ENSM_STATE_ALERT: + *mode = ENSM_MODE_ALERT; + break; + case ENSM_STATE_FDD: + if (phy->pdata->fdd_independent_mode) + *mode = ENSM_MODE_PINCTRL_FDD_INDEP; + else + *mode = ENSM_MODE_FDD; + break; + case ENSM_STATE_SLEEP_WAIT: + if (pinctrl) + *mode = ENSM_MODE_PINCTRL; + else + *mode = ENSM_MODE_WAIT; + break; + case ENSM_STATE_SLEEP: + *mode = ENSM_MODE_SLEEP; + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Set the receive RF gain for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gain_db The RF gain (dB). + * Example: + * 10 (10 dB) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy, + uint8_t ch, int32_t gain_db) +{ + struct rf_rx_gain rx_gain = {0}; + int32_t ret = 0; + + if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) { + printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + rx_gain.gain_db = gain_db; + ret = ad9361_set_rx_gain(phy, + ad9361_1rx1tx_channel_map(phy, false, + ch + 1), &rx_gain); + + return ret; +} + +/** + * Get current receive RF gain for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gain_db A variable to store the RF gain (dB). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy, + uint8_t ch, int32_t *gain_db) +{ + struct rf_rx_gain rx_gain = {0}; + int32_t ret = 0; + + if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) { + printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + ret = ad9361_get_rx_gain(phy, ad9361_1rx1tx_channel_map(phy, + false, ch + 1), &rx_gain); + + *gain_db = rx_gain.gain_db; + + return ret; +} + +/** + * Set the RX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz The desired bandwidth (Hz). + * Example: + * 18000000 (18 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz) +{ + int32_t ret = 0; + + bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz); + + if (phy->current_rx_bw_Hz != bandwidth_hz) + ret = ad9361_update_rf_bandwidth(phy, bandwidth_hz, + phy->current_tx_bw_Hz); + else + ret = 0; + + return ret; +} + +/** + * Get the RX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz A variable to store the bandwidth value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz) +{ + *bandwidth_hz = phy->current_rx_bw_Hz; + + return 0; +} + +/** + * Set the RX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz The desired frequency (Hz). + * Example: + * 30720000 (30.72 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + + ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + + ad9361_set_trx_clock_chain(phy, rx, tx); + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get current RX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz) +{ + *sampling_freq_hz = (uint32_t)clk_get_rate(phy, + phy->ref_clk_scale[RX_SAMPL_CLK]); + + return 0; +} + +/** + * Set the RX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz The desired frequency (Hz). + * Example: + * 2400000000 (2.4 GHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t lo_freq_hz) +{ + int32_t ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL], + ad9361_to_clk(lo_freq_hz)); + + return ret; +} + +/** + * Get current RX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t *lo_freq_hz) +{ + *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[RX_RFPLL])); + + return 0; +} + +/** + * Switch between internal and external LO. + * @param phy The AD9361 state structure. + * @param int_ext The selected lo (INT_LO, EXT_LO). + * Accepted values: + * INT_LO + * EXT_LO + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext) +{ + if ((phy->dev_sel == ID_AD9363A) && (int_ext == EXT_LO)) { + printf("%s : EXT_LO is not supported by AD9363!\n", __func__); + return -1; + } + + phy->pdata->use_ext_rx_lo = int_ext; + + return ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL], int_ext); +} + + +/** + * Power Down RX LO/Synthesizers. + * @param phy The AD9361 state structure. + * @param pd The power down state. + * Accepted values: + * LO_DONTCARE + * LO_OFF + * LO_ON + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd) +{ + return ad9361_synth_lo_powerdown(phy, (enum synth_pd_ctrl) pd, LO_DONTCARE); +} + +/** + * Get RX LO/Synthesizers power down state. + * @param phy The AD9361 state structure. + * @param pd A variable to store the pd state. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t *pd) +{ + *pd = !!(phy->cached_synth_pd[1] & RX_LO_POWER_DOWN); + + return 0; +} + +/** + * Get the RSSI for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param rssi A variable to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy, + uint8_t ch, struct rf_rssi *rssi) +{ + int32_t ret; + + if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) { + printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + rssi->ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1); + rssi->duration = 1; + ret = ad9361_read_rssi(phy, rssi); + + return ret; +} + +/** + * Set the gain control mode for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values in 2x2 mode: + * RX1 (0) + * RX2 (1) + * Accepted values in 1x1 mode: + * RX1 (0) + * @param gc_mode The gain control mode (manual, fast_attack, slow_attack, + * hybrid). + * Accepted values: + * RF_GAIN_MGC (manual) + * RF_GAIN_FASTATTACK_AGC (fast_attack) + * RF_GAIN_SLOWATTACK_AGC (slow_attack) + * RF_GAIN_HYBRID_AGC (hybrid) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy, + uint8_t ch, uint8_t gc_mode) +{ + struct rf_gain_ctrl gc = {0}; + + if ((phy->pdata->rx2tx2 == 0) && (ch == RX2)) { + printf("%s : RX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + gc.ant = ad9361_1rx1tx_channel_map(phy, false, ch + 1); + gc.mode = phy->agc_mode[ch] = gc_mode; + + ad9361_set_gain_ctrl_mode(phy, &gc); + + return 0; +} + +/** + * Get the gain control mode for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel (RX1, RX2). + * Accepted values: + * RX1 (0) + * RX2 (1) + * @param gc_mode A variable to store the gain control mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy, + uint8_t ch, uint8_t *gc_mode) +{ + *gc_mode = phy->agc_mode[ch]; + + return 0; +} + +/** + * Set the RX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param fir_cfg FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig fir_cfg) +{ + int32_t ret; + + phy->rx_fir_dec = fir_cfg.rx_dec; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(fir_cfg.rx | FIR_IS_RX), + fir_cfg.rx_gain, fir_cfg.rx_coef_size, fir_cfg.rx_coef); + + return ret; +} + +/** + * Get the RX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param tx_ch The selected RX channel (RX1, RX2). + * Accepted values: + * RX1 (0) + * RX2 (1) + * @param fir_cfg FIR filter configuration output file. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, AD9361_RXFIRConfig *fir_cfg) +{ + int32_t ret; + uint32_t fir_conf; + uint8_t index; + + rx_ch += 1; + + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_CONFIG); + if(ret < 0) + return ret; + fir_conf = ret; + + fir_cfg->rx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16; + + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_GAIN); + if(ret < 0) + return ret; + fir_cfg->rx_gain = -6 * (ret & FILTER_GAIN(3)) + 6; + fir_cfg->rx = rx_ch; + + fir_conf &= ~FIR_SELECT(3); + fir_conf |= FIR_SELECT(rx_ch) | FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf); + + for(index = 0; index < 128; index++) + { + ad9361_spi_write(phy->spi, REG_RX_FILTER_COEF_ADDR, index); + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_1); + if(ret < 0) + return ret; + fir_cfg->rx_coef[index] = ret; + ret = ad9361_spi_read(phy->spi, REG_RX_FILTER_COEF_READ_DATA_2); + if(ret < 0) + return ret; + fir_cfg->rx_coef[index] |= (ret << 8); + } + + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_RX_FILTER_CONFIG, fir_conf); + + fir_cfg->rx_dec = phy->rx_fir_dec; + + return 0; +} + +/** + * Enable/disable the RX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bypass_rx_fir == !en_dis) + return ret; + + phy->bypass_rx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_rx_fir = true; + } + + return ret; +} + +/** + * Get the status of the RX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = !phy->bypass_rx_fir; + + return 0; +} + +/** + * Enable/disable the RX RFDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->rfdc_track_en == en_dis) + return ret; + + phy->rfdc_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX RFDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->rfdc_track_en; + + return 0; +} + +/** + * Enable/disable the RX BasebandDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bbdc_track_en == en_dis) + return ret; + + phy->bbdc_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX BasebandDC Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->bbdc_track_en; + + return 0; +} + +/** + * Enable/disable the RX Quadrature Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->quad_track_en == en_dis) + return ret; + + phy->quad_track_en = en_dis; + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + + return ret; +} + +/** + * Get the status of the RX Quadrature Tracking. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = phy->quad_track_en; + + return 0; +} + +/** + * Set the RX RF input port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * Accepted values: + * A_BALANCED (0 - (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced) + * B_BALANCED (1 - (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced) + * C_BALANCED (2 - (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced) + * A_N (3 - RX1A_N and RX2A_N enabled; unbalanced) + * A_P (4 - RX1A_P and RX2A_P enabled; unbalanced) + * B_N (5 - RX1B_N and RX2B_N enabled; unbalanced) + * B_P (6 - RX1B_P and RX2B_P enabled; unbalanced) + * C_N (7 - RX1C_N and RX2C_N enabled; unbalanced) + * C_P (8 - RX1C_P and RX2C_P enabled; unbalanced) + * TX_MON1 (9 - TX_MONITOR1) + * TX_MON2 (10 - TX_MONITOR2) + * TX_MON1_2 (11 - TX_MONITOR1 & TX_MONITOR2) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + + phy->pdata->rf_rx_input_sel = mode; + + ret = ad9361_rf_port_setup(phy, false, + phy->pdata->rf_rx_input_sel, + phy->pdata->rf_tx_output_sel); + + return ret; +} + +/** + * Get the selected RX RF input port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + *mode = phy->pdata->rf_rx_input_sel; + + return 0; +} + +/** + * Store RX fastlock profile. + * To create a profile tune the synthesizer (ad9361_set_rx_lo_freq()) and then + * call this function specifying the target profile number. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_store(phy, 0, profile); +} + +/** + * Recall specified RX fastlock profile. + * When in fastlock pin select mode (init_param->rx_fastlock_pincontrol_enable), + * the function needs to be called before then the pin-control can be used. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_recall(phy, 0, profile); +} + +/** + * Load RX fastlock profile. A previously saved profile can be loaded in any + * of the 8 available slots. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * Example: + * val0,val1,val2,Â…,val15 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values) +{ + return ad9361_fastlock_load(phy, 0, profile, values); +} + +/** + * Save RX fastlock profile. In order to use more than 8 Profiles, an existing + * profile can be read back and stored by the user application. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values) +{ + return ad9361_fastlock_save(phy, 0, profile, values); +} + +/** + * Set the transmit attenuation for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (TX1, TX2). + * Accepted values in 2x2 mode: + * TX1 (0) + * TX2 (1) + * Accepted values in 1x1 mode: + * TX1 (0) + * @param attenuation_mdb The attenuation (mdB). + * Example: + * 10000 (10 dB) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy, + uint8_t ch, uint32_t attenuation_mdb) +{ + int32_t ret; + int32_t channel; + + if ((phy->pdata->rx2tx2 == 0) && (ch == TX2)) { + printf("%s : TX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + channel = ad9361_1rx1tx_channel_map(phy, true, ch); + ret = ad9361_set_tx_atten(phy, attenuation_mdb, + channel == 0, channel == 1, + !phy->pdata->update_tx_gain_via_alert); + + return ret; +} + +/** + * Get current transmit attenuation for the selected channel. + * @param phy The AD9361 current state structure. + * @param ch The desired channel number (TX1, TX2). + * Accepted values in 2x2 mode: + * TX1 (0) + * TX2 (1) + * Accepted values in 1x1 mode: + * TX1 (0) + * @param attenuation_mdb A variable to store the attenuation value (mdB). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy, + uint8_t ch, uint32_t *attenuation_db) +{ + int32_t ret; + + if ((phy->pdata->rx2tx2 == 0) && (ch == TX2)) { + printf("%s : TX2 is an invalid option in 1x1 mode!\n", __func__); + return -1; + } + + ret = ad9361_get_tx_atten(phy, + ad9361_1rx1tx_channel_map(phy, true, + ch + 1)); + + if(ret < 0) + return ret; + *attenuation_db = ret; + + return 0; +} + +/** + * Set the TX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz The desired bandwidth (Hz). + * Example: + * 18000000 (18 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t bandwidth_hz) +{ + int32_t ret = 0; + + bandwidth_hz = ad9361_validate_rf_bw(phy, bandwidth_hz); + + if (phy->current_tx_bw_Hz != bandwidth_hz) + ret = ad9361_update_rf_bandwidth(phy, + phy->current_rx_bw_Hz, bandwidth_hz); + else + ret = 0; + + return ret; +} + +/** + * Get the TX RF bandwidth. + * @param phy The AD9361 current state structure. + * @param bandwidth_hz A variable to store the bandwidth value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy, + uint32_t *bandwidth_hz) +{ + *bandwidth_hz = phy->current_tx_bw_Hz; + + return 0; +} + +/** + * Set the TX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz The desired frequency (Hz). + * Example: + * 30720000 (30.72 MHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t sampling_freq_hz) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + + ret = ad9361_calculate_rf_clock_chain(phy, sampling_freq_hz, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + + ad9361_set_trx_clock_chain(phy, rx, tx); + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get current TX sampling frequency. + * @param phy The AD9361 current state structure. + * @param sampling_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy, + uint32_t *sampling_freq_hz) +{ + *sampling_freq_hz = (uint32_t)clk_get_rate(phy, + phy->ref_clk_scale[TX_SAMPL_CLK]); + + return 0; +} + +/** + * Set the TX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz The desired frequency (Hz). + * Example: + * 2400000000 (2.4 GHz) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t lo_freq_hz) +{ + int32_t ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL], + ad9361_to_clk(lo_freq_hz)); + + return ret; +} + +/** + * Get current TX LO frequency. + * @param phy The AD9361 current state structure. + * @param lo_freq_hz A variable to store the frequency value (Hz). + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy, + uint64_t *lo_freq_hz) +{ + *lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[TX_RFPLL])); + + return 0; +} + +/** + * Switch between internal and external LO. + * @param phy The AD9361 state structure. + * @param int_ext The selected lo (INT_LO, EXT_LO). + * Accepted values: + * INT_LO + * EXT_LO + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext) +{ + if ((phy->dev_sel == ID_AD9363A) && (int_ext == EXT_LO)) { + printf("%s : EXT_LO is not supported by AD9363!\n", __func__); + return -1; + } + + phy->pdata->use_ext_tx_lo = int_ext; + + return ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL], int_ext); +} + +/** + * Power Down TX LO/Synthesizers. + * @param phy The AD9361 state structure. + * @param pd The power down state. + * Accepted values: + * LO_DONTCARE + * LO_OFF + * LO_ON + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd) +{ + return ad9361_synth_lo_powerdown(phy, LO_DONTCARE, (enum synth_pd_ctrl) pd); +} + +/** + * Get TX LO/Synthesizers power down state. + * @param phy The AD9361 state structure. + * @param pd A variable to store the pd state. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t *pd) +{ + *pd = !!(phy->cached_synth_pd[0] & TX_LO_POWER_DOWN); + + return 0; +} + +/** + * Set the TX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param fir_cfg FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy, + AD9361_TXFIRConfig fir_cfg) +{ + int32_t ret; + + phy->tx_fir_int = fir_cfg.tx_int; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)fir_cfg.tx, + fir_cfg.tx_gain, fir_cfg.tx_coef_size, fir_cfg.tx_coef); + + return ret; +} + +/** + * Get the TX FIR filter configuration. + * @param phy The AD9361 current state structure. + * @param tx_ch The selected TX channel (TX1, TX2). + * Accepted values: + * TX1 (0) + * TX2 (1) + * @param fir_cfg FIR filter configuration output file. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, AD9361_TXFIRConfig *fir_cfg) +{ + int32_t ret; + uint32_t fir_conf; + uint8_t index; + + tx_ch += 1; + + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_CONF); + if(ret < 0) + return ret; + fir_conf = ret; + fir_cfg->tx_coef_size = (((fir_conf & FIR_NUM_TAPS(7)) >> 5) + 1) * 16; + fir_cfg->tx_gain = -6 * (int32_t)(fir_conf & TX_FIR_GAIN_6DB); + fir_cfg->tx = tx_ch; + + fir_conf &= ~FIR_SELECT(3); + fir_conf |= FIR_SELECT(tx_ch) | FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf); + + for(index = 0; index < 128; index++) + { + ad9361_spi_write(phy->spi, REG_TX_FILTER_COEF_ADDR, index); + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_1); + if(ret < 0) + return ret; + fir_cfg->tx_coef[index] = ret; + ret = ad9361_spi_read(phy->spi, REG_TX_FILTER_COEF_READ_DATA_2); + if(ret < 0) + return ret; + fir_cfg->tx_coef[index] |= (ret << 8); + } + + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(phy->spi, REG_TX_FILTER_CONF, fir_conf); + + fir_cfg->tx_int = phy->tx_fir_int; + + return 0; +} + +/** + * Enable/disable the TX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if(phy->bypass_tx_fir == !en_dis) + return ret; + + phy->bypass_tx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_tx_fir = true; + } + + return ret; +} + +/** + * Get the status of the TX FIR filter. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t *en_dis) +{ + *en_dis = !phy->bypass_tx_fir; + + return 0; +} + +/** + * Get the TX RSSI for the selected channel (TX_MON should be enabled). + * @param phy The AD9361 current state structure. + * @param ch The desired channel (TX1, TX2). + * Accepted values: + * TX1 (0) + * TX2 (1) + * @param rssi_db_x_1000 A variable to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy, + uint8_t ch, + uint32_t *rssi_db_x_1000) +{ + uint8_t reg_val_buf[3]; + uint32_t val; + int32_t ret; + + ret = ad9361_spi_readm(phy->spi, REG_TX_RSSI_LSB, + reg_val_buf, ARRAY_SIZE(reg_val_buf)); + if (ret < 0) { + return ret; + } + + switch (ch) { + case 0: + val = (reg_val_buf[2] << 1) | (reg_val_buf[0] & TX_RSSI_1); + break; + case 1: + val = (reg_val_buf[1] << 1) | ((reg_val_buf[0] & TX_RSSI_2) >> 1); + break; + default: + return -EINVAL; + } + + val *= RSSI_RESOLUTION; + + *rssi_db_x_1000 = ((val / RSSI_MULTIPLIER) * 1000) + + (val % RSSI_MULTIPLIER); + + return 0; +} + +/** + * Set the TX RF output port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * Accepted values: + * TXA (0) + * TXB (1) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy, + uint32_t mode) +{ + int32_t ret; + + phy->pdata->rf_tx_output_sel = mode; + + ret = ad9361_rf_port_setup(phy, true, + phy->pdata->rf_rx_input_sel, + phy->pdata->rf_tx_output_sel); + + return ret; +} + +/** + * Get the selected TX RF output port. + * @param phy The AD9361 current state structure. + * @param mode The RF port. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy, + uint32_t *mode) +{ + *mode = phy->pdata->rf_tx_output_sel; + + return 0; +} + +/** + * Enable/disable the auto calibration. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis) +{ + if (en_dis == 0) + phy->auto_cal_en = 0; + else + phy->auto_cal_en = 1; + + return 0; +} + +/** + * Get the status of the auto calibration flag. + * @param phy The AD9361 current state structure. + * @param en_dis The enable/disable status buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis) +{ + *en_dis = phy->auto_cal_en; + + return 0; +} + +/** + * Store TX fastlock profile. + * To create a profile tune the synthesizer (ad9361_set_tx_lo_freq()) and then + * call this function specifying the target profile number. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_store(phy, 1, profile); +} + +/** + * Recall specified TX fastlock profile. + * When in fastlock pin select mode (init_param->tx_fastlock_pincontrol_enable), + * the function needs to be called before then the pin-control can be used. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile) +{ + return ad9361_fastlock_recall(phy, 1, profile); +} + +/** + * Load TX fastlock profile. A previously saved profile can be loaded in any + * of the 8 available slots. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * Example: + * val0,val1,val2,Â…,val15 + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values) +{ + return ad9361_fastlock_load(phy, 1, profile, values); +} + +/** + * Save TX fastlock profile. In order to use more than 8 Profiles, an existing + * profile can be read back and stored by the user application. + * @param phy The AD9361 state structure. + * @param profile The profile number (0 - 7). + * Accepted values: + * 0 - 7 + * @param values Fastlock profile program data. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values) +{ + return ad9361_fastlock_save(phy, 1, profile, values); +} + +/** + * Set the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t ret; + + ret = ad9361_set_trx_clock_chain(phy, rx_path_clks, tx_path_clks); + if (ret < 0) + return ret; + + ret = ad9361_update_rf_bandwidth(phy, phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + + return ret; +} + +/** + * Get the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + return ad9361_get_trx_clock_chain(phy, rx_path_clks, tx_path_clks); +} + +/** + * Set the number of channels mode. + * @param phy The AD9361 state structure. + * @param ch_mode Number of channels mode (MODE_1x1, MODE_2x2). + * Accepted values: + * MODE_1x1 (1) + * MODE_2x2 (2) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode) +{ + switch (no_ch_mode) { + case 1: + phy->pdata->rx2tx2 = 0; + break; + case 2: + phy->pdata->rx2tx2 = 1; + break; + default: + return -EINVAL; + } + +#ifndef AXI_ADC_NOT_PRESENT + phy->adc_conv->chip_info = &axiadc_chip_info_tbl[phy->pdata->rx2tx2 ? ID_AD9361 : ID_AD9364]; +#endif + ad9361_reset(phy); + ad9361_spi_write(phy->spi, REG_SPI_CONF, SOFT_RESET | _SOFT_RESET); + ad9361_spi_write(phy->spi, REG_SPI_CONF, 0x0); + + phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate); + phy->clks[TX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_REFCLK], phy->clk_refin->rate); + phy->clks[RX_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[RX_REFCLK], phy->clk_refin->rate); + phy->clks[BB_REFCLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[BB_REFCLK], phy->clk_refin->rate); + phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate(phy->ref_clk_scale[BBPLL_CLK], phy->clks[BB_REFCLK]->rate); + phy->clks[ADC_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[ADC_CLK], phy->clks[BBPLL_CLK]->rate); + phy->clks[R2_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[R2_CLK], phy->clks[ADC_CLK]->rate); + phy->clks[R1_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[R1_CLK], phy->clks[R2_CLK]->rate); + phy->clks[CLKRF_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[CLKRF_CLK], phy->clks[R1_CLK]->rate); + phy->clks[RX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[RX_SAMPL_CLK], phy->clks[CLKRF_CLK]->rate); + phy->clks[DAC_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[DAC_CLK], phy->clks[ADC_CLK]->rate); + phy->clks[T2_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[T2_CLK], phy->clks[DAC_CLK]->rate); + phy->clks[T1_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[T1_CLK], phy->clks[T2_CLK]->rate); + phy->clks[CLKTF_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[CLKTF_CLK], phy->clks[T1_CLK]->rate); + phy->clks[TX_SAMPL_CLK]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[TX_SAMPL_CLK], phy->clks[CLKTF_CLK]->rate); + phy->clks[RX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[RX_RFPLL_INT], phy->clks[RX_REFCLK]->rate); + phy->clks[TX_RFPLL_INT]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[TX_RFPLL_INT], phy->clks[TX_REFCLK]->rate); + phy->clks[RX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY]); + phy->clks[TX_RFPLL_DUMMY]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY]); + phy->clks[RX_RFPLL]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[RX_RFPLL]); + phy->clks[TX_RFPLL]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[TX_RFPLL]); + +#ifndef AXI_ADC_NOT_PRESENT + axiadc_init(phy); +#endif + ad9361_setup(phy); +#ifndef AXI_ADC_NOT_PRESENT + /* platform specific wrapper to call ad9361_post_setup() */ + axiadc_post_setup(phy); +#endif + + return 0; +} + +/** + * Do multi chip synchronization. + * @param phy_master The AD9361 Master state structure. + * @param phy_slave The AD9361 Slave state structure. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +/* + int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, struct ad9361_rf_phy *phy_slave) + { + uint32_t ensm_mode; + int32_t step; + int32_t reg; + + if ((phy_master->dev_sel == ID_AD9363A) || + (phy_slave->dev_sel == ID_AD9363A)) { + printf("%s : MCS is not supported by AD9363!\n", __func__); + return -1; + } + + reg = ad9361_spi_read(phy_master->spi, REG_RX_CLOCK_DATA_DELAY); + ad9361_spi_write(phy_slave->spi, REG_RX_CLOCK_DATA_DELAY, reg); + reg = ad9361_spi_read(phy_master->spi, REG_TX_CLOCK_DATA_DELAY); + ad9361_spi_write(phy_slave->spi, REG_TX_CLOCK_DATA_DELAY, reg); + + ad9361_get_en_state_machine_mode(phy_master, &ensm_mode); + + ad9361_set_en_state_machine_mode(phy_master, ENSM_MODE_ALERT); + ad9361_set_en_state_machine_mode(phy_slave, ENSM_MODE_ALERT); + + for (step = 0; step <= 5; step++) + { + ad9361_mcs(phy_slave, step); + ad9361_mcs(phy_master, step); + mdelay(100); + } + + ad9361_set_en_state_machine_mode(phy_master, ensm_mode); + ad9361_set_en_state_machine_mode(phy_slave, ensm_mode); + + return 0; + } + */ +/** + * Power Down RX/TX LO/Synthesizers. + * @param phy The AD9361 state structure. + * @param pd_rx The RX LO power down state. + * Accepted values: + * LO_DONTCARE + * LO_OFF + * LO_ON + * @param pd_tx The TX LO power down state. + * Accepted values: + * LO_DONTCARE + * LO_OFF + * LO_ON * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd_rx, uint8_t pd_tx) +{ + return ad9361_synth_lo_powerdown(phy, (enum synth_pd_ctrl) pd_rx, + (enum synth_pd_ctrl) pd_tx); +} + +/** + * Enable/disable the TRX FIR filters. + * @param phy The AD9361 current state structure. + * @param en_dis The option (ENABLE, DISABLE). + * Accepted values: + * ENABLE (1) + * DISABLE (0) + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy, + uint8_t en_dis) +{ + int32_t ret = 0; + + if ((phy->bypass_rx_fir == phy->bypass_tx_fir) && + (phy->bypass_rx_fir == !en_dis)) + return ret; + + phy->bypass_rx_fir = !en_dis; + phy->bypass_tx_fir = !en_dis; + ret = ad9361_validate_enable_fir(phy); + if (ret < 0) { + phy->bypass_rx_fir = true; + phy->bypass_tx_fir = true; + } + + return ret; +} + +/** + * Set the OSR rate governor. + * @param phy The AD9361 current state structure. + * @param rate_gov OSR rate governor (highest, nominal). + * Accepted values: + * HIGHEST_OSR (0 - highest OSR) + * NOMINAL_OSR (1 - nominal) + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov) +{ + if (rate_gov == 0) + phy->rate_governor = 0; + else + phy->rate_governor = 1; + + return 0; +} + +/** + * Get the OSR rate governor. + * @param phy The AD9361 current state structure. + * @param rate_gov Option buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov) +{ + *rate_gov = phy->rate_governor; + + return 0; +} + +/** + * Perform the selected calibration. + * @param phy The AD9361 state structure. + * @param cal The selected calibration (TX_QUAD_CAL, RFDC_CAL). + * Accepted values: + * TX_QUAD_CAL + * RFDC_CAL + * @param arg For TX_QUAD_CAL - the optional RX phase value overwrite (set to zero). + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg) +{ + return ad9361_do_calib_run(phy, cal, arg); +} + +/** + * Load and enable TRX FIR filters configurations. + * @param phy The AD9361 current state structure. + * @param rx_fir_cfg RX FIR filter configuration. + * @param tx_fir_cfg TX FIR filter configuration. + * @return 0 in case of success, negative error code otherwise. + * + * Note: This function will/may affect the data path. + */ +int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig rx_fir_cfg, + AD9361_TXFIRConfig tx_fir_cfg) +{ + int32_t rtx = -1, rrx = -1; + + phy->filt_rx_bw_Hz = 0; + phy->filt_tx_bw_Hz = 0; + phy->filt_valid = false; + + if (tx_fir_cfg.tx_path_clks[TX_SAMPL_FREQ]) { + memcpy(phy->filt_tx_path_clks, tx_fir_cfg.tx_path_clks, + sizeof(phy->filt_tx_path_clks)); + rtx = 0; + } + + if (rx_fir_cfg.rx_path_clks[RX_SAMPL_FREQ]) { + memcpy(phy->filt_rx_path_clks, rx_fir_cfg.rx_path_clks, + sizeof(phy->filt_rx_path_clks)); + rrx = 0; + } + + if (tx_fir_cfg.tx_bandwidth) { + phy->filt_tx_bw_Hz = tx_fir_cfg.tx_bandwidth; + } + + if (rx_fir_cfg.rx_bandwidth) { + phy->filt_rx_bw_Hz = rx_fir_cfg.rx_bandwidth; + } + + ad9361_set_tx_fir_config(phy, tx_fir_cfg); + ad9361_set_rx_fir_config(phy, rx_fir_cfg); + + if (!(rrx | rtx)) + phy->filt_valid = true; + + ad9361_set_trx_fir_en_dis(phy, 1); + + return 0; +} + +/** + * Do DCXO coarse tuning. + * @param phy The AD9361 current state structure. + * @param coarse The DCXO coarse tuning value. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy, + uint32_t coarse) +{ + phy->pdata->dcxo_coarse = coarse; + + return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, + phy->pdata->dcxo_fine); +} + +/** + * Do DCXO fine tuning. + * @param phy The AD9361 current state structure. + * @param fine The DCXO fine tuning value. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy, + uint32_t fine) +{ + phy->pdata->dcxo_fine = fine; + + return ad9361_set_dcxo_tune(phy, phy->pdata->dcxo_coarse, + phy->pdata->dcxo_fine); +} diff --git a/src/ad9361_lib/ad9361_api.h b/src/ad9361_lib/ad9361_api.h new file mode 100644 index 0000000..4cd185b --- /dev/null +++ b/src/ad9361_lib/ad9361_api.h @@ -0,0 +1,501 @@ +/***************************************************************************//** + * @file ad9361_api.h + * @brief Header file of AD9361 API Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef AD9361_API_H_ +#define AD9361_API_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "util.h" + +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +typedef struct +{ + /* Device selection */ + enum dev_id dev_sel; + /* Identification number */ + uint8_t id_no; + /* Reference Clock */ + uint32_t reference_clk_rate; + /* Base Configuration */ + uint8_t two_rx_two_tx_mode_enable; /* adi,2rx-2tx-mode-enable */ + uint8_t one_rx_one_tx_mode_use_rx_num; /* adi,1rx-1tx-mode-use-rx-num */ + uint8_t one_rx_one_tx_mode_use_tx_num; /* adi,1rx-1tx-mode-use-tx-num */ + uint8_t frequency_division_duplex_mode_enable; /* adi,frequency-division-duplex-mode-enable */ + uint8_t frequency_division_duplex_independent_mode_enable; /* adi,frequency-division-duplex-independent-mode-enable */ + uint8_t tdd_use_dual_synth_mode_enable; /* adi,tdd-use-dual-synth-mode-enable */ + uint8_t tdd_skip_vco_cal_enable; /* adi,tdd-skip-vco-cal-enable */ + uint32_t tx_fastlock_delay_ns; /* adi,tx-fastlock-delay-ns */ + uint32_t rx_fastlock_delay_ns; /* adi,rx-fastlock-delay-ns */ + uint8_t rx_fastlock_pincontrol_enable; /* adi,rx-fastlock-pincontrol-enable */ + uint8_t tx_fastlock_pincontrol_enable; /* adi,tx-fastlock-pincontrol-enable */ + uint8_t external_rx_lo_enable; /* adi,external-rx-lo-enable */ + uint8_t external_tx_lo_enable; /* adi,external-tx-lo-enable */ + uint8_t dc_offset_tracking_update_event_mask; /* adi,dc-offset-tracking-update-event-mask */ + uint8_t dc_offset_attenuation_high_range; /* adi,dc-offset-attenuation-high-range */ + uint8_t dc_offset_attenuation_low_range; /* adi,dc-offset-attenuation-low-range */ + uint8_t dc_offset_count_high_range; /* adi,dc-offset-count-high-range */ + uint8_t dc_offset_count_low_range; /* adi,dc-offset-count-low-range */ + uint8_t split_gain_table_mode_enable; /* adi,split-gain-table-mode-enable */ + uint32_t trx_synthesizer_target_fref_overwrite_hz; /* adi,trx-synthesizer-target-fref-overwrite-hz */ + uint8_t qec_tracking_slow_mode_enable; /* adi,qec-tracking-slow-mode-enable */ + /* ENSM Control */ + uint8_t ensm_enable_pin_pulse_mode_enable; /* adi,ensm-enable-pin-pulse-mode-enable */ + uint8_t ensm_enable_txnrx_control_enable; /* adi,ensm-enable-txnrx-control-enable */ + /* LO Control */ + uint64_t rx_synthesizer_frequency_hz; /* adi,rx-synthesizer-frequency-hz */ + uint64_t tx_synthesizer_frequency_hz; /* adi,tx-synthesizer-frequency-hz */ + /* Rate & BW Control */ + uint32_t rx_path_clock_frequencies[6]; /* adi,rx-path-clock-frequencies */ + uint32_t tx_path_clock_frequencies[6]; /* adi,tx-path-clock-frequencies */ + uint32_t rf_rx_bandwidth_hz; /* adi,rf-rx-bandwidth-hz */ + uint32_t rf_tx_bandwidth_hz; /* adi,rf-tx-bandwidth-hz */ + /* RF Port Control */ + uint32_t rx_rf_port_input_select; /* adi,rx-rf-port-input-select */ + uint32_t tx_rf_port_input_select; /* adi,tx-rf-port-input-select */ + /* TX Attenuation Control */ + int32_t tx_attenuation_mdB; /* adi,tx-attenuation-mdB */ + uint8_t update_tx_gain_in_alert_enable; /* adi,update-tx-gain-in-alert-enable */ + /* Reference Clock Control */ + uint8_t xo_disable_use_ext_refclk_enable; /* adi,xo-disable-use-ext-refclk-enable */ + uint32_t dcxo_coarse_and_fine_tune[2]; /* adi,dcxo-coarse-and-fine-tune */ + uint32_t clk_output_mode_select; /* adi,clk-output-mode-select */ + /* Gain Control */ + uint8_t gc_rx1_mode; /* adi,gc-rx1-mode */ + uint8_t gc_rx2_mode; /* adi,gc-rx2-mode */ + uint8_t gc_adc_large_overload_thresh; /* adi,gc-adc-large-overload-thresh */ + uint8_t gc_adc_ovr_sample_size; /* adi,gc-adc-ovr-sample-size */ + uint8_t gc_adc_small_overload_thresh; /* adi,gc-adc-small-overload-thresh */ + uint16_t gc_dec_pow_measurement_duration; /* adi,gc-dec-pow-measurement-duration */ + uint8_t gc_dig_gain_enable; /* adi,gc-dig-gain-enable */ + uint16_t gc_lmt_overload_high_thresh; /* adi,gc-lmt-overload-high-thresh */ + uint16_t gc_lmt_overload_low_thresh; /* adi,gc-lmt-overload-low-thresh */ + uint8_t gc_low_power_thresh; /* adi,gc-low-power-thresh */ + uint8_t gc_max_dig_gain; /* adi,gc-max-dig-gain */ + /* Gain MGC Control */ + uint8_t mgc_dec_gain_step; /* adi,mgc-dec-gain-step */ + uint8_t mgc_inc_gain_step; /* adi,mgc-inc-gain-step */ + uint8_t mgc_rx1_ctrl_inp_enable; /* adi,mgc-rx1-ctrl-inp-enable */ + uint8_t mgc_rx2_ctrl_inp_enable; /* adi,mgc-rx2-ctrl-inp-enable */ + uint8_t mgc_split_table_ctrl_inp_gain_mode; /* adi,mgc-split-table-ctrl-inp-gain-mode */ + /* Gain AGC Control */ + uint8_t agc_adc_large_overload_exceed_counter; /* adi,agc-adc-large-overload-exceed-counter */ + uint8_t agc_adc_large_overload_inc_steps; /* adi,agc-adc-large-overload-inc-steps */ + uint8_t agc_adc_lmt_small_overload_prevent_gain_inc_enable; /* adi,agc-adc-lmt-small-overload-prevent-gain-inc-enable */ + uint8_t agc_adc_small_overload_exceed_counter; /* adi,agc-adc-small-overload-exceed-counter */ + uint8_t agc_dig_gain_step_size; /* adi,agc-dig-gain-step-size */ + uint8_t agc_dig_saturation_exceed_counter; /* adi,agc-dig-saturation-exceed-counter */ + uint32_t agc_gain_update_interval_us; /* adi,agc-gain-update-interval-us */ + uint8_t agc_immed_gain_change_if_large_adc_overload_enable; /* adi,agc-immed-gain-change-if-large-adc-overload-enable */ + uint8_t agc_immed_gain_change_if_large_lmt_overload_enable; /* adi,agc-immed-gain-change-if-large-lmt-overload-enable */ + uint8_t agc_inner_thresh_high; /* adi,agc-inner-thresh-high */ + uint8_t agc_inner_thresh_high_dec_steps; /* adi,agc-inner-thresh-high-dec-steps */ + uint8_t agc_inner_thresh_low; /* adi,agc-inner-thresh-low */ + uint8_t agc_inner_thresh_low_inc_steps; /* adi,agc-inner-thresh-low-inc-steps */ + uint8_t agc_lmt_overload_large_exceed_counter; /* adi,agc-lmt-overload-large-exceed-counter */ + uint8_t agc_lmt_overload_large_inc_steps; /* adi,agc-lmt-overload-large-inc-steps */ + uint8_t agc_lmt_overload_small_exceed_counter; /* adi,agc-lmt-overload-small-exceed-counter */ + uint8_t agc_outer_thresh_high; /* adi,agc-outer-thresh-high */ + uint8_t agc_outer_thresh_high_dec_steps; /* adi,agc-outer-thresh-high-dec-steps */ + uint8_t agc_outer_thresh_low; /* adi,agc-outer-thresh-low */ + uint8_t agc_outer_thresh_low_inc_steps; /* adi,agc-outer-thresh-low-inc-steps */ + uint32_t agc_attack_delay_extra_margin_us; /* adi,agc-attack-delay-extra-margin-us */ + uint8_t agc_sync_for_gain_counter_enable; /* adi,agc-sync-for-gain-counter-enable */ + /* Fast AGC */ + uint32_t fagc_dec_pow_measuremnt_duration; /* adi,fagc-dec-pow-measurement-duration */ + uint32_t fagc_state_wait_time_ns; /* adi,fagc-state-wait-time-ns */ + /* Fast AGC - Low Power */ + uint8_t fagc_allow_agc_gain_increase; /* adi,fagc-allow-agc-gain-increase-enable */ + uint32_t fagc_lp_thresh_increment_time; /* adi,fagc-lp-thresh-increment-time */ + uint32_t fagc_lp_thresh_increment_steps; /* adi,fagc-lp-thresh-increment-steps */ + /* Fast AGC - Lock Level */ + uint32_t fagc_lock_level; /* adi,fagc-lock-level */ + uint8_t fagc_lock_level_lmt_gain_increase_en; /* adi,fagc-lock-level-lmt-gain-increase-enable */ + uint32_t fagc_lock_level_gain_increase_upper_limit; /* adi,fagc-lock-level-gain-increase-upper-limit */ + /* Fast AGC - Peak Detectors and Final Settling */ + uint32_t fagc_lpf_final_settling_steps; /* adi,fagc-lpf-final-settling-steps */ + uint32_t fagc_lmt_final_settling_steps; /* adi,fagc-lmt-final-settling-steps */ + uint32_t fagc_final_overrange_count; /* adi,fagc-final-overrange-count */ + /* Fast AGC - Final Power Test */ + uint8_t fagc_gain_increase_after_gain_lock_en; /* adi,fagc-gain-increase-after-gain-lock-enable */ + /* Fast AGC - Unlocking the Gain */ + uint32_t fagc_gain_index_type_after_exit_rx_mode; /* adi,fagc-gain-index-type-after-exit-rx-mode */ + uint8_t fagc_use_last_lock_level_for_set_gain_en; /* adi,fagc-use-last-lock-level-for-set-gain-enable */ + uint8_t fagc_rst_gla_stronger_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-stronger-sig-thresh-exceeded-enable */ + uint32_t fagc_optimized_gain_offset; /* adi,fagc-optimized-gain-offset */ + uint32_t fagc_rst_gla_stronger_sig_thresh_above_ll; /* adi,fagc-rst-gla-stronger-sig-thresh-above-ll */ + uint8_t fagc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-exceeded-enable */ + uint8_t fagc_rst_gla_engergy_lost_goto_optim_gain_en; /* adi,fagc-rst-gla-engergy-lost-goto-optim-gain-enable */ + uint32_t fagc_rst_gla_engergy_lost_sig_thresh_below_ll; /* adi,fagc-rst-gla-engergy-lost-sig-thresh-below-ll */ + uint32_t fagc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* adi,fagc-energy-lost-stronger-sig-gain-lock-exit-cnt */ + uint8_t fagc_rst_gla_large_adc_overload_en; /* adi,fagc-rst-gla-large-adc-overload-enable */ + uint8_t fagc_rst_gla_large_lmt_overload_en; /* adi,fagc-rst-gla-large-lmt-overload-enable */ + uint8_t fagc_rst_gla_en_agc_pulled_high_en; /* adi,fagc-rst-gla-en-agc-pulled-high-enable */ + uint32_t fagc_rst_gla_if_en_agc_pulled_high_mode; /* adi,fagc-rst-gla-if-en-agc-pulled-high-mode */ + uint32_t fagc_power_measurement_duration_in_state5; /* adi,fagc-power-measurement-duration-in-state5 */ + /* RSSI Control */ + uint32_t rssi_delay; /* adi,rssi-delay */ + uint32_t rssi_duration; /* adi,rssi-duration */ + uint8_t rssi_restart_mode; /* adi,rssi-restart-mode */ + uint8_t rssi_unit_is_rx_samples_enable; /* adi,rssi-unit-is-rx-samples-enable */ + uint32_t rssi_wait; /* adi,rssi-wait */ + /* Aux ADC Control */ + uint32_t aux_adc_decimation; /* adi,aux-adc-decimation */ + uint32_t aux_adc_rate; /* adi,aux-adc-rate */ + /* AuxDAC Control */ + uint8_t aux_dac_manual_mode_enable; /* adi,aux-dac-manual-mode-enable */ + uint32_t aux_dac1_default_value_mV; /* adi,aux-dac1-default-value-mV */ + uint8_t aux_dac1_active_in_rx_enable; /* adi,aux-dac1-active-in-rx-enable */ + uint8_t aux_dac1_active_in_tx_enable; /* adi,aux-dac1-active-in-tx-enable */ + uint8_t aux_dac1_active_in_alert_enable; /* adi,aux-dac1-active-in-alert-enable */ + uint32_t aux_dac1_rx_delay_us; /* adi,aux-dac1-rx-delay-us */ + uint32_t aux_dac1_tx_delay_us; /* adi,aux-dac1-tx-delay-us */ + uint32_t aux_dac2_default_value_mV; /* adi,aux-dac2-default-value-mV */ + uint8_t aux_dac2_active_in_rx_enable; /* adi,aux-dac2-active-in-rx-enable */ + uint8_t aux_dac2_active_in_tx_enable; /* adi,aux-dac2-active-in-tx-enable */ + uint8_t aux_dac2_active_in_alert_enable; /* adi,aux-dac2-active-in-alert-enable */ + uint32_t aux_dac2_rx_delay_us; /* adi,aux-dac2-rx-delay-us */ + uint32_t aux_dac2_tx_delay_us; /* adi,aux-dac2-tx-delay-us */ + /* Temperature Sensor Control */ + uint32_t temp_sense_decimation; /* adi,temp-sense-decimation */ + uint16_t temp_sense_measurement_interval_ms; /* adi,temp-sense-measurement-interval-ms */ + int8_t temp_sense_offset_signed; /* adi,temp-sense-offset-signed */ + uint8_t temp_sense_periodic_measurement_enable; /* adi,temp-sense-periodic-measurement-enable */ + /* Control Out Setup */ + uint8_t ctrl_outs_enable_mask; /* adi,ctrl-outs-enable-mask */ + uint8_t ctrl_outs_index; /* adi,ctrl-outs-index */ + /* External LNA Control */ + uint32_t elna_settling_delay_ns; /* adi,elna-settling-delay-ns */ + uint32_t elna_gain_mdB; /* adi,elna-gain-mdB */ + uint32_t elna_bypass_loss_mdB; /* adi,elna-bypass-loss-mdB */ + uint8_t elna_rx1_gpo0_control_enable; /* adi,elna-rx1-gpo0-control-enable */ + uint8_t elna_rx2_gpo1_control_enable; /* adi,elna-rx2-gpo1-control-enable */ + uint8_t elna_gaintable_all_index_enable; /* adi,elna-gaintable-all-index-enable */ + /* Digital Interface Control */ + uint8_t digital_interface_tune_skip_mode; /* adi,digital-interface-tune-skip-mode */ + uint8_t digital_interface_tune_fir_disable; /* adi,digital-interface-tune-fir-disable */ + uint8_t pp_tx_swap_enable; /* adi,pp-tx-swap-enable */ + uint8_t pp_rx_swap_enable; /* adi,pp-rx-swap-enable */ + uint8_t tx_channel_swap_enable; /* adi,tx-channel-swap-enable */ + uint8_t rx_channel_swap_enable; /* adi,rx-channel-swap-enable */ + uint8_t rx_frame_pulse_mode_enable; /* adi,rx-frame-pulse-mode-enable */ + uint8_t two_t_two_r_timing_enable; /* adi,2t2r-timing-enable */ + uint8_t invert_data_bus_enable; /* adi,invert-data-bus-enable */ + uint8_t invert_data_clk_enable; /* adi,invert-data-clk-enable */ + uint8_t fdd_alt_word_order_enable; /* adi,fdd-alt-word-order-enable */ + uint8_t invert_rx_frame_enable; /* adi,invert-rx-frame-enable */ + uint8_t fdd_rx_rate_2tx_enable; /* adi,fdd-rx-rate-2tx-enable */ + uint8_t swap_ports_enable; /* adi,swap-ports-enable */ + uint8_t single_data_rate_enable; /* adi,single-data-rate-enable */ + uint8_t lvds_mode_enable; /* adi,lvds-mode-enable */ + uint8_t half_duplex_mode_enable; /* adi,half-duplex-mode-enable */ + uint8_t single_port_mode_enable; /* adi,single-port-mode-enable */ + uint8_t full_port_enable; /* adi,full-port-enable */ + uint8_t full_duplex_swap_bits_enable; /* adi,full-duplex-swap-bits-enable */ + uint32_t delay_rx_data; /* adi,delay-rx-data */ + uint32_t rx_data_clock_delay; /* adi,rx-data-clock-delay */ + uint32_t rx_data_delay; /* adi,rx-data-delay */ + uint32_t tx_fb_clock_delay; /* adi,tx-fb-clock-delay */ + uint32_t tx_data_delay; /* adi,tx-data-delay */ + uint32_t lvds_bias_mV; /* adi,lvds-bias-mV */ + uint8_t lvds_rx_onchip_termination_enable; /* adi,lvds-rx-onchip-termination-enable */ + uint8_t rx1rx2_phase_inversion_en; /* adi,rx1-rx2-phase-inversion-enable */ + uint8_t lvds_invert1_control; /* adi,lvds-invert1-control */ + uint8_t lvds_invert2_control; /* adi,lvds-invert2-control */ + /* GPO Control */ + uint8_t gpo0_inactive_state_high_enable; /* adi,gpo0-inactive-state-high-enable */ + uint8_t gpo1_inactive_state_high_enable; /* adi,gpo1-inactive-state-high-enable */ + uint8_t gpo2_inactive_state_high_enable; /* adi,gpo2-inactive-state-high-enable */ + uint8_t gpo3_inactive_state_high_enable; /* adi,gpo3-inactive-state-high-enable */ + uint8_t gpo0_slave_rx_enable; /* adi,gpo0-slave-rx-enable */ + uint8_t gpo0_slave_tx_enable; /* adi,gpo0-slave-tx-enable */ + uint8_t gpo1_slave_rx_enable; /* adi,gpo1-slave-rx-enable */ + uint8_t gpo1_slave_tx_enable; /* adi,gpo1-slave-tx-enable */ + uint8_t gpo2_slave_rx_enable; /* adi,gpo2-slave-rx-enable */ + uint8_t gpo2_slave_tx_enable; /* adi,gpo2-slave-tx-enable */ + uint8_t gpo3_slave_rx_enable; /* adi,gpo3-slave-rx-enable */ + uint8_t gpo3_slave_tx_enable; /* adi,gpo3-slave-tx-enable */ + uint8_t gpo0_rx_delay_us; /* adi,gpo0-rx-delay-us */ + uint8_t gpo0_tx_delay_us; /* adi,gpo0-tx-delay-us */ + uint8_t gpo1_rx_delay_us; /* adi,gpo1-rx-delay-us */ + uint8_t gpo1_tx_delay_us; /* adi,gpo1-tx-delay-us */ + uint8_t gpo2_rx_delay_us; /* adi,gpo2-rx-delay-us */ + uint8_t gpo2_tx_delay_us; /* adi,gpo2-tx-delay-us */ + uint8_t gpo3_rx_delay_us; /* adi,gpo3-rx-delay-us */ + uint8_t gpo3_tx_delay_us; /* adi,gpo3-tx-delay-us */ + /* Tx Monitor Control */ + uint32_t low_high_gain_threshold_mdB; /* adi,txmon-low-high-thresh */ + uint32_t low_gain_dB; /* adi,txmon-low-gain */ + uint32_t high_gain_dB; /* adi,txmon-high-gain */ + uint8_t tx_mon_track_en; /* adi,txmon-dc-tracking-enable */ + uint8_t one_shot_mode_en; /* adi,txmon-one-shot-mode-enable */ + uint32_t tx_mon_delay; /* adi,txmon-delay */ + uint32_t tx_mon_duration; /* adi,txmon-duration */ + uint32_t tx1_mon_front_end_gain; /* adi,txmon-1-front-end-gain */ + uint32_t tx2_mon_front_end_gain; /* adi,txmon-2-front-end-gain */ + uint32_t tx1_mon_lo_cm; /* adi,txmon-1-lo-cm */ + uint32_t tx2_mon_lo_cm; /* adi,txmon-2-lo-cm */ + /* GPIO definitions */ + int32_t gpio_resetb; /* reset-gpios */ + /* MCS Sync */ + int32_t gpio_sync; /* sync-gpios */ + int32_t gpio_cal_sw1; /* cal-sw1-gpios */ + int32_t gpio_cal_sw2; /* cal-sw2-gpios */ + /* External LO clocks */ + 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; + +enum ad9361_ensm_mode { + ENSM_MODE_TX, + ENSM_MODE_RX, + ENSM_MODE_ALERT, + ENSM_MODE_FDD, + ENSM_MODE_WAIT, + ENSM_MODE_SLEEP, + ENSM_MODE_PINCTRL, + ENSM_MODE_PINCTRL_FDD_INDEP, +}; + +#define ENABLE 1 +#define DISABLE 0 + +#define RX1 0 +#define RX2 1 + +#define TX1 0 +#define TX2 1 + +#define A_BALANCED 0 +#define B_BALANCED 1 +#define C_BALANCED 2 +#define A_N 3 +#define A_P 4 +#define B_N 5 +#define B_P 6 +#define C_N 7 +#define C_P 8 +#define TX_MON1 9 +#define TX_MON2 10 +#define TX_MON1_2 11 + +#define TXA 0 +#define TXB 1 + +#define MODE_1x1 1 +#define MODE_2x2 2 + +#define HIGHEST_OSR 0 +#define NOMINAL_OSR 1 + +#define INT_LO 0 +#define EXT_LO 1 + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +/* Initialize the AD9361 part. */ +struct spi_device; +int32_t ad9361_init (struct ad9361_rf_phy *ad9361_phy, struct spi_device *spi, AD9361_InitParam *init_param); +/* Set the Enable State Machine (ENSM) mode. */ +int32_t ad9361_set_en_state_machine_mode (struct ad9361_rf_phy *phy, uint32_t mode); +/* Get the Enable State Machine (ENSM) mode. */ +int32_t ad9361_get_en_state_machine_mode (struct ad9361_rf_phy *phy, uint32_t *mode); +/* Set the receive RF gain for the selected channel. */ +int32_t ad9361_set_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, int32_t gain_db); +/* Get current receive RF gain for the selected channel. */ +int32_t ad9361_get_rx_rf_gain (struct ad9361_rf_phy *phy, uint8_t ch, int32_t *gain_db); +/* Set the RX RF bandwidth. */ +int32_t ad9361_set_rx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t bandwidth_hz); +/* Get the RX RF bandwidth. */ +int32_t ad9361_get_rx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t *bandwidth_hz); +/* Set the RX sampling frequency. */ +int32_t ad9361_set_rx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t sampling_freq_hz); +/* Get current RX sampling frequency. */ +int32_t ad9361_get_rx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t *sampling_freq_hz); +/* Set the RX LO frequency. */ +int32_t ad9361_set_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz); +/* Get current RX LO frequency. */ +int32_t ad9361_get_rx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz); +/* Switch between internal and external LO. */ +int32_t ad9361_set_rx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext); +/* Get the RSSI for the selected channel. */ +int32_t ad9361_get_rx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, struct rf_rssi *rssi); +/* Set the gain control mode for the selected channel. */ +int32_t ad9361_set_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, uint8_t gc_mode); +/* Get the gain control mode for the selected channel. */ +int32_t ad9361_get_rx_gain_control_mode (struct ad9361_rf_phy *phy, uint8_t ch, uint8_t *gc_mode); +/* Set the RX FIR filter configuration. */ +int32_t ad9361_set_rx_fir_config (struct ad9361_rf_phy *phy, AD9361_RXFIRConfig fir_cfg); +/* Get the RX FIR filter configuration. */ +int32_t ad9361_get_rx_fir_config(struct ad9361_rf_phy *phy, uint8_t rx_ch, AD9361_RXFIRConfig *fir_cfg); +/* Enable/disable the RX FIR filter. */ +int32_t ad9361_set_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the RX FIR filter. */ +int32_t ad9361_get_rx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Enable/disable the RX RFDC Tracking. */ +int32_t ad9361_set_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the RX RFDC Tracking. */ +int32_t ad9361_get_rx_rfdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Enable/disable the RX BasebandDC Tracking. */ +int32_t ad9361_set_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the RX BasebandDC Tracking. */ +int32_t ad9361_get_rx_bbdc_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Enable/disable the RX Quadrature Tracking. */ +int32_t ad9361_set_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the RX Quadrature Tracking. */ +int32_t ad9361_get_rx_quad_track_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Set the RX RF input port. */ +int32_t ad9361_set_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t mode); +/* Get the selected RX RF input port. */ +int32_t ad9361_get_rx_rf_port_input (struct ad9361_rf_phy *phy, uint32_t *mode); +/* Store RX fastlock profile. */ +int32_t ad9361_rx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile); +/* Recall RX fastlock profile. */ +int32_t ad9361_rx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile); +/* Load RX fastlock profile. */ +int32_t ad9361_rx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values); +/* Save RX fastlock profile. */ +int32_t ad9361_rx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values); +/* Set power down TX LO/Synthesizers */ +int32_t ad9361_set_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd); +/* Get power down TX LO/Synthesizers */ +int32_t ad9361_get_rx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t *pd); +/* Set the transmit attenuation for the selected channel. */ +int32_t ad9361_set_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t attenuation_mdb); +/* Get current transmit attenuation for the selected channel. */ +int32_t ad9361_get_tx_attenuation (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t *attenuation_mdb); +/* Set the TX RF bandwidth. */ +int32_t ad9361_set_tx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t bandwidth_hz); +/* Get the TX RF bandwidth. */ +int32_t ad9361_get_tx_rf_bandwidth (struct ad9361_rf_phy *phy, uint32_t *bandwidth_hz); +/* Set the TX sampling frequency. */ +int32_t ad9361_set_tx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t sampling_freq_hz); +/* Get current TX sampling frequency. */ +int32_t ad9361_get_tx_sampling_freq (struct ad9361_rf_phy *phy, uint32_t *sampling_freq_hz); +/* Set the TX LO frequency. */ +int32_t ad9361_set_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t lo_freq_hz); +/* Get current TX LO frequency. */ +int32_t ad9361_get_tx_lo_freq (struct ad9361_rf_phy *phy, uint64_t *lo_freq_hz); +/* Switch between internal and external LO. */ +int32_t ad9361_set_tx_lo_int_ext(struct ad9361_rf_phy *phy, uint8_t int_ext); +/* Set the TX FIR filter configuration. */ +int32_t ad9361_set_tx_fir_config (struct ad9361_rf_phy *phy, AD9361_TXFIRConfig fir_cfg); +/* Get the TX FIR filter configuration. */ +int32_t ad9361_get_tx_fir_config(struct ad9361_rf_phy *phy, uint8_t tx_ch, AD9361_TXFIRConfig *fir_cfg); +/* Enable/disable the TX FIR filter. */ +int32_t ad9361_set_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the TX FIR filter. */ +int32_t ad9361_get_tx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Get the TX RSSI for the selected channel. */ +int32_t ad9361_get_tx_rssi (struct ad9361_rf_phy *phy, uint8_t ch, uint32_t *rssi_db_x_1000); +/* Set the TX RF output port. */ +int32_t ad9361_set_tx_rf_port_output (struct ad9361_rf_phy *phy, uint32_t mode); +/* Get the selected TX RF output port. */ +int32_t ad9361_get_tx_rf_port_output (struct ad9361_rf_phy *phy, uint32_t *mode); +/* Enable/disable the auto calibration. */ +int32_t ad9361_set_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Get the status of the auto calibration flag. */ +int32_t ad9361_get_tx_auto_cal_en_dis (struct ad9361_rf_phy *phy, uint8_t *en_dis); +/* Store TX fastlock profile. */ +int32_t ad9361_tx_fastlock_store(struct ad9361_rf_phy *phy, uint32_t profile); +/* Recall TX fastlock profile. */ +int32_t ad9361_tx_fastlock_recall(struct ad9361_rf_phy *phy, uint32_t profile); +/* Load TX fastlock profile. */ +int32_t ad9361_tx_fastlock_load(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values); +/* Save TX fastlock profile. */ +int32_t ad9361_tx_fastlock_save(struct ad9361_rf_phy *phy, uint32_t profile, uint8_t *values); +/* Set power down TX LO/Synthesizers */ +int32_t ad9361_set_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd); +/* Get power down TX LO/Synthesizers */ +int32_t ad9361_get_tx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t *pd); +/* Set the RX and TX path rates. */ +int32_t ad9361_set_trx_path_clks(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, uint32_t *tx_path_clks); +/* Get the RX and TX path rates. */ +int32_t ad9361_get_trx_path_clks(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, uint32_t *tx_path_clks); +/* Power Down RX/TX LO/Synthesizers */ +int32_t ad9361_set_trx_lo_powerdown(struct ad9361_rf_phy *phy, uint8_t pd_rx, uint8_t pd_tx); +/* Set the number of channels mode. */ +int32_t ad9361_set_no_ch_mode(struct ad9361_rf_phy *phy, uint8_t no_ch_mode); +/* Do multi chip synchronization. */ +//int32_t ad9361_do_mcs(struct ad9361_rf_phy *phy_master, struct ad9361_rf_phy *phy_slave); +/* Enable/disable the TRX FIR filters. */ +int32_t ad9361_set_trx_fir_en_dis (struct ad9361_rf_phy *phy, uint8_t en_dis); +/* Set the OSR rate governor. */ +int32_t ad9361_set_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t rate_gov); +/* Get the OSR rate governor. */ +int32_t ad9361_get_trx_rate_gov (struct ad9361_rf_phy *phy, uint32_t *rate_gov); +/* Perform the selected calibration. */ +int32_t ad9361_do_calib(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg); +/* Load and enable TRX FIR filters configurations. */ +int32_t ad9361_trx_load_enable_fir(struct ad9361_rf_phy *phy, + AD9361_RXFIRConfig rx_fir_cfg, + AD9361_TXFIRConfig tx_fir_cfg); +/* Do DCXO coarse tuning. */ +int32_t ad9361_do_dcxo_tune_coarse(struct ad9361_rf_phy *phy, + uint32_t coarse); +/* Do DCXO fine tuning. */ +int32_t ad9361_do_dcxo_tune_fine(struct ad9361_rf_phy *phy, + uint32_t fine); +#endif diff --git a/src/ad9361_lib/ad9361_conv.c b/src/ad9361_lib/ad9361_conv.c new file mode 100644 index 0000000..3239d23 --- /dev/null +++ b/src/ad9361_lib/ad9361_conv.c @@ -0,0 +1,597 @@ + +/***************************************************************************//** + * @file ad9361_conv.c + * @brief Implementation of AD9361 Conv Driver. +******************************************************************************** + * Copyright 2014-2015(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "ad9361_dev.h" +#include "platform.h" +#include "config.h" + +#ifndef AXI_ADC_NOT_PRESENT +/** + * HDL loopback enable/disable. + * @param phy The AD9361 state structure. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable) +{ + struct axiadc_converter *conv = phy->adc_conv; + struct axiadc_state *st = phy->adc_state; + int32_t reg, addr, chan; + + uint32_t version = axiadc_read(st, 0x4000); + + /* Still there but implemented a bit different */ + if (PCORE_VERSION_MAJOR(version) > 7) + addr = 0x4418; + else + addr = 0x4414; + + for (chan = 0; chan < conv->chip_info->num_channels; chan++) { + reg = axiadc_read(st, addr + (chan) * 0x40); + + if (PCORE_VERSION_MAJOR(version) > 7) { + if (enable && reg != 0x8) { + conv->scratch_reg[chan] = reg; + reg = 0x8; + } else if (reg == 0x8) { + reg = conv->scratch_reg[chan]; + } + } else { + /* DAC_LB_ENB If set enables loopback of receive data */ + if (enable) + reg |= BIT(1); + else + reg &= ~BIT(1); + } + axiadc_write(st, addr + (chan) * 0x40, reg); + } + + return 0; +} + +/** + * Set IO delay. + * @param st The AXI ADC state structure. + * @param lane Lane number. + * @param val Value. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_iodelay_set(struct axiadc_state *st, unsigned lane, + unsigned val, bool tx) +{ + if (tx) { + if (PCORE_VERSION_MAJOR(st->pcore_version) > 8) + axiadc_write(st, 0x4000 + ADI_REG_DELAY(lane), val); + else + return -ENODEV; + } else { + axiadc_idelay_set(st, lane, val); + } + + return 0; +} + +/** + * Set midscale IO delay. + * @param phy The AD9361 state structure. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_midscale_iodelay(struct ad9361_rf_phy *phy, bool tx) +{ + struct axiadc_state *st = phy->adc_state; + int32_t ret = 0, i; + + for (i = 0; i < 7; i++) + ret |= ad9361_iodelay_set(st, i, 15, tx); + + return 0; +} + +/** + * Digital tune IO delay. + * @param phy The AD9361 state structure. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_dig_tune_iodelay(struct ad9361_rf_phy *phy, bool tx) +{ + struct axiadc_converter *conv = phy->adc_conv; + struct axiadc_state *st = phy->adc_state; + int32_t ret, i, j, chan, num_chan; + uint32_t s0, c0; + uint8_t field[32]; + + num_chan = (conv->chip_info->num_channels > 4) ? 4 : conv->chip_info->num_channels; + + for (i = 0; i < 7; i++) { + for (j = 0; j < 32; j++) { + ad9361_iodelay_set(st, i, j, tx); + mdelay(1); + + for (chan = 0; chan < num_chan; chan++) + axiadc_write(st, ADI_REG_CHAN_STATUS(chan), + ADI_PN_ERR | ADI_PN_OOS); + mdelay(10); + + for (chan = 0, ret = 0; chan < num_chan; chan++) + ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan)); + + field[j] = ret; + } + + c0 = ad9361_find_opt(&field[0], 32, &s0); + ad9361_iodelay_set(st, i, s0 + c0 / 2, tx); + + dev_dbg(&phy->spi->dev, + "%s Lane %"PRId32", window cnt %"PRIu32" , start %"PRIu32", IODELAY set to %"PRIu32"\n", + tx ? "TX" :"RX", i , c0, s0, s0 + c0 / 2); + } + + return 0; +} + +/** + * Digital tune verbose print. + * @param phy The AD9361 state structure. + * @param field Field. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static void ad9361_dig_tune_verbose_print(struct ad9361_rf_phy *phy, + uint8_t field[][16], bool tx) +{ + int32_t i, j; + + printk("SAMPL CLK: %"PRIu32" tuning: %s\n", + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]), tx ? "TX" : "RX"); + printk(" "); + for (i = 0; i < 16; i++) + printk("%"PRIx32":", i); + printk("\n"); + + for (i = 0; i < 2; i++) { + printk("%"PRIx32":", i); + for (j = 0; j < 16; j++) { + printk("%c ", (field[i][j] ? '#' : 'o')); + } + printk("\n"); + } + printk("\n"); +} + +/** + * Digital interface timing analysis. + * @param phy The AD9361 state structure. + * @param buf The buffer. + * @param buflen The buffer length. + * @return The size in case of success, negative error code otherwise. + */ +int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy, + char *buf, int32_t buflen) +{ + struct axiadc_state *st = phy->adc_state; + int32_t ret, i, j, chan, len = 0; + uint8_t field[16][16]; + uint8_t rx; + + dev_dbg(&phy->spi->dev, "%s:\n", __func__); + + rx = ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); + + ad9361_bist_prbs(phy, BIST_INJ_RX); + + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, + DATA_CLK_DELAY(j) | RX_DATA_DELAY(i)); + for (chan = 0; chan < 4; chan++) + axiadc_write(st, ADI_REG_CHAN_STATUS(chan), + ADI_PN_ERR | ADI_PN_OOS); + + mdelay(1); + + if (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS) { + for (chan = 0, ret = 0; chan < 4; chan++) + ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan)); + } + else { + ret = 1; + } + + field[i][j] = ret; + } + } + + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, rx); + + ad9361_bist_prbs(phy, BIST_DISABLE); + + len += snprintf(buf + len, buflen, "CLK: %"PRIu32" Hz 'o' = PASS\n", + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK])); + len += snprintf(buf + len, buflen, "DC"); + for (i = 0; i < 16; i++) + len += snprintf(buf + len, buflen, "%"PRIx32":", i); + len += snprintf(buf + len, buflen, "\n"); + + for (i = 0; i < 16; i++) { + len += snprintf(buf + len, buflen, "%"PRIx32":", i); + for (j = 0; j < 16; j++) { + len += snprintf(buf + len, buflen, "%c ", + (field[i][j] ? '.' : 'o')); + } + len += snprintf(buf + len, buflen, "\n"); + } + len += snprintf(buf + len, buflen, "\n"); + + return len; +} + +/** + * Digital tune. + * @param phy The AD9361 state structure. + * @param max_freq Maximum frequency. + * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, + enum dig_tune_flags flags) +{ + struct axiadc_converter *conv = phy->adc_conv; + struct axiadc_state *st = phy->adc_state; + int32_t ret, i, j, k, chan, t, num_chan, err = 0; + uint32_t s0, s1, c0, c1, tmp, saved = 0; + uint8_t field[2][16]; + uint32_t saved_dsel[4], saved_chan_ctrl6[4], saved_chan_ctrl0[4]; + uint32_t rates[3] = {25000000U, 40000000U, 61440000U}; + uint32_t hdl_dac_version; + + dev_dbg(&phy->spi->dev, "%s: freq %"PRIu32" flags 0x%X\n", __func__, + max_freq, flags); + + hdl_dac_version = axiadc_read(st, 0x4000); + + if ((phy->pdata->dig_interface_tune_skipmode == 2) || + (flags & RESTORE_DEFAULT)) { + /* skip completely and use defaults */ + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, + phy->pdata->port_ctrl.rx_clk_data_delay); + + ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY, + phy->pdata->port_ctrl.tx_clk_data_delay); + + return 0; + } + + /* Mute TX, we don't want to transmit the PRBS */ + ad9361_tx_mute(phy, 1); + + if (flags & DO_IDELAY) + ad9361_midscale_iodelay(phy, 0); + + if (flags & DO_ODELAY) + ad9361_midscale_iodelay(phy, 1); + + if (!phy->pdata->fdd) { + ad9361_set_ensm_mode(phy, true, false); + ad9361_ensm_force_state(phy, ENSM_STATE_FDD); + } else { + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + ad9361_ensm_restore_prev_state(phy); + } + + num_chan = (conv->chip_info->num_channels > 4) ? 4 : + conv->chip_info->num_channels; + + ad9361_bist_prbs(phy, BIST_INJ_RX); + + for (t = 0; t < 2; t++) { + memset(field, 0, 32); + for (k = 0; (uint32_t)k < (max_freq ? ARRAY_SIZE(rates) : 1); k++) { + if (max_freq) + ad9361_set_trx_clock_chain_freq(phy, + ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) || !phy->pdata->rx2tx2) ? + rates[k] : rates[k] / 2); + for (i = 0; i < 2; i++) { + for (j = 0; j < 16; j++) { + ad9361_spi_write(phy->spi, + REG_RX_CLOCK_DATA_DELAY + t, + RX_DATA_DELAY(i == 0 ? j : 0) | + DATA_CLK_DELAY(i ? j : 0)); + for (chan = 0; chan < num_chan; chan++) + axiadc_write(st, ADI_REG_CHAN_STATUS(chan), + ADI_PN_ERR | ADI_PN_OOS); + mdelay(4); + + if ((t == 1) || (axiadc_read(st, ADI_REG_STATUS) & ADI_STATUS)) { + for (chan = 0, ret = 0; chan < num_chan; chan++) { + ret |= axiadc_read(st, ADI_REG_CHAN_STATUS(chan)); + } + } + else { + ret = 1; + } + + field[i][j] |= ret; + } + } + if ((flags & BE_MOREVERBOSE) && max_freq) { + ad9361_dig_tune_verbose_print(phy, field, t); + } + } + + c0 = ad9361_find_opt(&field[0][0], 16, &s0); + c1 = ad9361_find_opt(&field[1][0], 16, &s1); + + if (!c0 && !c1) { + ad9361_dig_tune_verbose_print(phy, field, t); + dev_err(&phy->spi->dev, "%s: Tuning %s FAILED!", __func__, + t ? "TX" : "RX"); + err |= -EIO; + } else if (flags & BE_VERBOSE) { + ad9361_dig_tune_verbose_print(phy, field, t); + } + + if (c1 > c0) + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t, + DATA_CLK_DELAY(s1 + c1 / 2) | + RX_DATA_DELAY(0)); + else + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY + t, + DATA_CLK_DELAY(0) | + RX_DATA_DELAY(s0 + c0 / 2)); + + if (t == 0) { + if (flags & DO_IDELAY) + ad9361_dig_tune_iodelay(phy, 0); + + /* Now do the loopback and tune the digital out */ + ad9361_bist_prbs(phy, BIST_DISABLE); + + axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN); + axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); + + if (phy->pdata->dig_interface_tune_skipmode == 1) { + /* skip TX */ + if (!(flags & SKIP_STORE_RESULT)) + phy->pdata->port_ctrl.rx_clk_data_delay = + ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); + + if (!phy->pdata->fdd) { + ad9361_set_ensm_mode(phy, phy->pdata->fdd, + phy->pdata->ensm_pin_ctrl); + ad9361_ensm_restore_prev_state(phy); + } + + ad9361_tx_mute(phy, 0); + + return 0; + } + + ad9361_bist_loopback(phy, 1); + axiadc_write(st, 0x4000 + ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); + + for (chan = 0; chan < num_chan; chan++) { + saved_chan_ctrl0[chan] = axiadc_read(st, ADI_REG_CHAN_CNTRL(chan)); + axiadc_write(st, ADI_REG_CHAN_CNTRL(chan), + ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE | + ADI_ENABLE | ADI_IQCOR_ENB); + axiadc_set_pnsel(st, chan, ADC_PN_CUSTOM); + saved_chan_ctrl6[chan] = axiadc_read(st, 0x4414 + (chan) * 0x40); + if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7) + { + saved_dsel[chan] = axiadc_read(st, 0x4418 + (chan) * 0x40); + axiadc_write(st, 0x4418 + (chan) * 0x40, 9); + axiadc_write(st, 0x4414 + (chan) * 0x40, 0); /* !IQCOR_ENB */ + axiadc_write(st, 0x4044, 0x1); + } + else + axiadc_write(st, 0x4414 + (chan) * 0x40, 1); /* DAC_PN_ENB */ + + } + if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8) { + saved = tmp = axiadc_read(st, 0x4048); + tmp &= ~0xF; + tmp |= 1; + axiadc_write(st, 0x4048, tmp); + + } + } else { + if (flags & DO_ODELAY) + ad9361_dig_tune_iodelay(phy, 1); + + ad9361_bist_loopback(phy, 0); + + if (PCORE_VERSION_MAJOR(hdl_dac_version) < 8) + axiadc_write(st, 0x4048, saved); + + for (chan = 0; chan < num_chan; chan++) { + axiadc_write(st, ADI_REG_CHAN_CNTRL(chan), + saved_chan_ctrl0[chan]); + axiadc_set_pnsel(st, chan, ADC_PN9); + if (PCORE_VERSION_MAJOR(hdl_dac_version) > 7) + { + axiadc_write(st, 0x4418 + (chan) * 0x40, saved_dsel[chan]); + axiadc_write(st, 0x4044, 0x1); + } + + axiadc_write(st, 0x4414 + (chan) * 0x40, saved_chan_ctrl6[chan]); + + } + + if (err == -EIO) { + ad9361_spi_write(phy->spi, REG_RX_CLOCK_DATA_DELAY, + phy->pdata->port_ctrl.rx_clk_data_delay); + + ad9361_spi_write(phy->spi, REG_TX_CLOCK_DATA_DELAY, + phy->pdata->port_ctrl.tx_clk_data_delay); + if (!max_freq) + err = 0; + } else if (!(flags & SKIP_STORE_RESULT)) { + phy->pdata->port_ctrl.rx_clk_data_delay = + ad9361_spi_read(phy->spi, REG_RX_CLOCK_DATA_DELAY); + phy->pdata->port_ctrl.tx_clk_data_delay = + ad9361_spi_read(phy->spi, REG_TX_CLOCK_DATA_DELAY); + } + + if (!phy->pdata->fdd) { + ad9361_set_ensm_mode(phy, phy->pdata->fdd, phy->pdata->ensm_pin_ctrl); + ad9361_ensm_restore_prev_state(phy); + } + + axiadc_write(st, ADI_REG_RSTN, ADI_MMCM_RSTN); + axiadc_write(st, ADI_REG_RSTN, ADI_RSTN | ADI_MMCM_RSTN); + + ad9361_tx_mute(phy, 0); + + return err; + } + } + + return -EINVAL; +} + +/** +* Setup the AD9361 device. +* @param phy The AD9361 state structure. +* @return 0 in case of success, negative error code otherwise. +*/ +int32_t ad9361_post_setup(struct ad9361_rf_phy *phy) +{ + struct axiadc_converter *conv = phy->adc_conv; + struct axiadc_state *st = phy->adc_state; + int32_t rx2tx2 = phy->pdata->rx2tx2; + int32_t tmp, num_chan, flags; + int32_t i, ret; + + num_chan = (conv->chip_info->num_channels > 4) ? 4 : conv->chip_info->num_channels; + + axiadc_write(st, ADI_REG_CNTRL, rx2tx2 ? 0 : ADI_R1_MODE); + tmp = axiadc_read(st, 0x4048); + + if (!rx2tx2) { + axiadc_write(st, 0x4048, tmp | BIT(5)); /* R1_MODE */ + axiadc_write(st, 0x404c, + (phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 0); /* RATE */ + } + else { + tmp &= ~BIT(5); + axiadc_write(st, 0x4048, tmp); + axiadc_write(st, 0x404c, + (phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 3 : 1); /* RATE */ + } + +#ifdef ALTERA_PLATFORM + axiadc_write(st, 0x404c, 1); +#endif + + for (i = 0; i < num_chan; i++) { + axiadc_write(st, ADI_REG_CHAN_CNTRL_1(i), + ADI_DCFILT_OFFSET(0)); + axiadc_write(st, ADI_REG_CHAN_CNTRL_2(i), + (i & 1) ? 0x00004000 : 0x40000000); + axiadc_write(st, ADI_REG_CHAN_CNTRL(i), + ADI_FORMAT_SIGNEXT | ADI_FORMAT_ENABLE | + ADI_ENABLE | ADI_IQCOR_ENB); + } + + flags = 0x0; + + ret = ad9361_dig_tune(phy, ((conv->chip_info->num_channels > 4) || + axiadc_read(st, 0x0004)) ? 0 : 61440000, flags); + if (ret < 0) + return ret; + + if (flags & (DO_IDELAY | DO_ODELAY)) { + ret = ad9361_dig_tune(phy, (axiadc_read(st, ADI_REG_ID)) ? + 0 : 61440000, flags & BE_VERBOSE); + if (ret < 0) + return ret; + } + + ret = ad9361_set_trx_clock_chain(phy, + phy->pdata->rx_path_clks, + phy->pdata->tx_path_clks); + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + ad9361_ensm_restore_prev_state(phy); + + return ret; +} +#else +/** + * HDL loopback enable/disable. + * @param phy The AD9361 state structure. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable) +{ + return -ENODEV; +} + +/** + * Digital tune. + * @param phy The AD9361 state structure. + * @param max_freq Maximum frequency. + * @param flags Flags: BE_VERBOSE, BE_MOREVERBOSE, DO_IDELAY, DO_ODELAY. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, + enum dig_tune_flags flags) +{ + return 0; +} + +/** +* Setup the AD9361 device. +* @param phy The AD9361 state structure. +* @return 0 in case of success, negative error code otherwise. +*/ +int32_t ad9361_post_setup(struct ad9361_rf_phy *phy) +{ + return 0; +} +#endif diff --git a/src/ad9361_lib/ad9361_dev.c b/src/ad9361_lib/ad9361_dev.c new file mode 100644 index 0000000..fa40099 --- /dev/null +++ b/src/ad9361_lib/ad9361_dev.c @@ -0,0 +1,7120 @@ +/***************************************************************************//** + * @file ad9361.c + * @brief Implementation of AD9361 Driver. +******************************************************************************** + * Copyright 2014-2015(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "ad9361_dev.h" +#include "platform.h" +#include "util.h" +#include "config.h" + +/* Used for static code size optimization: please see config.h */ +const bool has_split_gt = HAVE_SPLIT_GAIN_TABLE; +const bool have_tdd_tables = HAVE_TDD_SYNTH_TABLE; + +#define SYNTH_LUT_SIZE 53 + +static const struct SynthLUT SynthLUT_FDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { + { + {12605, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, /* 40 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 5, 15}, + {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 14, 5, 15}, + {11588, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, + {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 14, 5, 15}, + {11007, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, + {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 14, 5, 15}, + {10492, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, + {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 14, 5, 15}, + {10036, 13, 1, 6, 2, 15, 12, 11, 15, 6, 14, 5, 15}, + {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 14, 5, 15}, + {9631, 13, 1, 6, 2, 13, 12, 12, 15, 6, 14, 5, 15}, + {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 14, 5, 15}, + {9269, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {8946, 13, 1, 6, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 14, 5, 15}, + {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 14, 5, 15}, + {8392, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, + {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 14, 5, 15}, + {8153, 12, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 14, 5, 15}, + {7934, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7733, 10, 1, 7, 3, 13, 12, 16, 15, 6, 14, 5, 15}, + {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 14, 5, 15}, + {7547, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, + {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 14, 5, 15}, + {7374, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 14, 5, 15}, + {7135, 10, 2, 7, 3, 14, 13, 15, 15, 7, 14, 5, 15}, + {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6988, 10, 1, 7, 3, 12, 14, 20, 15, 6, 14, 5, 15}, + {6918, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6720, 9, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, + {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 14, 5, 15}, + {6482, 8, 2, 7, 2, 13, 13, 16, 15, 6, 14, 5, 15}, + {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 14, 5, 15}, + {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6270, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 14, 5, 15}, + {6083, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 14, 5, 15}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, /* 60 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 13, 15, 12, 12, 5, 14}, + {11906, 13, 1, 4, 2, 15, 12, 13, 15, 13, 12, 5, 13}, + {11588, 13, 1, 4, 2, 15, 12, 14, 15, 13, 12, 5, 13}, + {11288, 13, 1, 5, 2, 15, 12, 15, 15, 13, 12, 5, 13}, + {11007, 13, 1, 5, 2, 15, 12, 16, 15, 13, 12, 5, 13}, + {10742, 13, 1, 5, 2, 15, 12, 16, 15, 12, 12, 5, 14}, + {10492, 13, 1, 6, 2, 15, 12, 17, 15, 12, 12, 5, 14}, + {10258, 13, 1, 6, 2, 15, 12, 18, 15, 13, 12, 5, 13}, + {10036, 13, 1, 6, 2, 15, 12, 19, 15, 13, 12, 5, 13}, + {9827, 13, 1, 6, 2, 14, 12, 20, 15, 13, 12, 5, 13}, + {9631, 13, 1, 6, 2, 13, 12, 21, 15, 13, 12, 5, 13}, + {9445, 13, 1, 6, 2, 12, 12, 22, 15, 13, 12, 5, 13}, + {9269, 13, 1, 6, 2, 12, 12, 22, 15, 12, 12, 5, 14}, + {9103, 13, 1, 6, 2, 12, 12, 23, 15, 13, 12, 5, 13}, + {8946, 13, 1, 6, 2, 12, 12, 24, 15, 13, 12, 5, 13}, + {8797, 12, 1, 7, 2, 12, 12, 24, 15, 13, 12, 5, 13}, + {8655, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, + {8520, 12, 1, 7, 2, 12, 12, 25, 15, 13, 12, 5, 13}, + {8392, 12, 1, 7, 2, 12, 12, 26, 15, 13, 12, 5, 13}, + {8269, 12, 1, 7, 2, 12, 12, 27, 15, 13, 12, 5, 13}, + {8153, 12, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, + {8041, 12, 1, 7, 2, 13, 12, 29, 15, 13, 12, 5, 13}, + {7934, 11, 1, 7, 2, 12, 12, 28, 15, 13, 12, 5, 13}, + {7831, 11, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7733, 10, 1, 7, 3, 13, 12, 28, 15, 13, 12, 5, 13}, + {7638, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7547, 10, 1, 7, 2, 12, 12, 29, 15, 13, 12, 5, 13}, + {7459, 10, 1, 7, 2, 12, 12, 30, 15, 13, 12, 5, 13}, + {7374, 10, 2, 7, 3, 14, 13, 24, 15, 13, 12, 5, 13}, + {7291, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, + {7212, 10, 2, 7, 3, 14, 13, 25, 15, 13, 12, 5, 13}, + {7135, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, + {7061, 10, 2, 7, 3, 14, 13, 26, 15, 13, 12, 5, 13}, + {6988, 10, 1, 7, 3, 12, 14, 35, 15, 13, 12, 5, 13}, + {6918, 9, 1, 7, 3, 12, 14, 33, 15, 13, 12, 5, 13}, + {6850, 9, 1, 7, 3, 12, 14, 34, 15, 13, 12, 5, 13}, + {6784, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, + {6720, 9, 1, 7, 2, 11, 14, 35, 15, 13, 12, 5, 13}, + {6658, 8, 2, 7, 3, 15, 13, 26, 15, 13, 12, 5, 13}, + {6597, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, + {6539, 8, 2, 7, 2, 15, 13, 27, 15, 13, 12, 5, 13}, + {6482, 8, 2, 7, 2, 15, 13, 28, 15, 13, 12, 5, 13}, + {6427, 7, 2, 7, 3, 14, 13, 27, 15, 13, 12, 5, 13}, + {6373, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6321, 7, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6270, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6222, 7, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6174, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6128, 6, 2, 7, 3, 15, 13, 27, 15, 13, 12, 5, 13}, + {6083, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {6040, 6, 2, 7, 3, 15, 13, 28, 15, 13, 12, 5, 13}, + {5997, 6, 2, 7, 3, 15, 13, 29, 15, 13, 12, 5, 13}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, /* 80 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, + {11906, 13, 1, 4, 2, 15, 12, 7, 15, 6, 13, 5, 14}, + {11588, 13, 1, 4, 2, 15, 12, 7, 14, 6, 14, 4, 14}, + {11288, 13, 1, 4, 2, 15, 12, 8, 15, 6, 13, 5, 14}, + {11007, 13, 1, 4, 2, 15, 12, 8, 14, 6, 13, 5, 14}, + {10742, 13, 1, 4, 2, 15, 12, 9, 15, 6, 13, 5, 14}, + {10492, 13, 1, 6, 2, 15, 12, 9, 14, 6, 13, 5, 14}, + {10258, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, + {10036, 13, 1, 6, 2, 15, 12, 10, 15, 6, 13, 5, 14}, + {9827, 13, 1, 6, 2, 14, 12, 11, 15, 6, 13, 5, 14}, + {9631, 13, 1, 6, 2, 13, 12, 11, 15, 6, 13, 5, 14}, + {9445, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, + {9269, 13, 1, 6, 2, 12, 12, 12, 15, 6, 13, 5, 14}, + {9103, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8946, 13, 1, 6, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8797, 12, 1, 7, 2, 12, 12, 13, 15, 6, 13, 5, 14}, + {8655, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, + {8520, 12, 1, 7, 2, 12, 12, 14, 15, 6, 13, 5, 14}, + {8392, 12, 1, 7, 2, 12, 12, 15, 15, 7, 13, 5, 14}, + {8269, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {8153, 12, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {8041, 12, 1, 7, 2, 13, 12, 16, 15, 6, 13, 5, 14}, + {7934, 11, 1, 7, 2, 12, 12, 15, 15, 6, 13, 5, 14}, + {7831, 11, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7733, 10, 1, 7, 3, 13, 12, 15, 15, 6, 13, 5, 14}, + {7638, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7547, 10, 1, 7, 2, 12, 12, 16, 15, 6, 13, 5, 14}, + {7459, 10, 1, 7, 2, 12, 12, 17, 15, 6, 13, 5, 14}, + {7374, 10, 2, 7, 3, 14, 13, 13, 15, 6, 13, 5, 14}, + {7291, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7212, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7135, 10, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {7061, 10, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6988, 10, 1, 7, 3, 12, 14, 19, 15, 6, 13, 5, 14}, + {6918, 9, 2, 7, 3, 14, 13, 14, 15, 6, 13, 5, 14}, + {6850, 9, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6784, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6720, 9, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6658, 8, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6597, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6539, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6482, 8, 2, 7, 2, 13, 13, 15, 15, 6, 13, 5, 14}, + {6427, 7, 2, 7, 3, 14, 13, 15, 15, 6, 13, 5, 14}, + {6373, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6321, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6270, 7, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6222, 7, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + {6174, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6128, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6083, 6, 2, 7, 3, 15, 13, 15, 15, 6, 13, 5, 14}, + {6040, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + {5997, 6, 2, 7, 3, 15, 13, 16, 15, 6, 13, 5, 14}, + } }; + +static const struct SynthLUT SynthLUT_TDD[LUT_FTDD_ENT][SYNTH_LUT_SIZE] = { + { + {12605, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, /* 40 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 27, 12, 15, 12, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 26, 11, 15, 12, 4, 13}, + {11588, 13, 1, 4, 2, 15, 12, 28, 12, 15, 12, 4, 13}, + {11288, 13, 1, 4, 2, 15, 12, 30, 12, 15, 12, 4, 13}, + {11007, 13, 1, 4, 2, 15, 12, 32, 12, 15, 12, 4, 13}, + {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 12, 4, 13}, + {10492, 13, 1, 6, 2, 15, 12, 35, 12, 15, 12, 4, 13}, + {10258, 13, 1, 6, 2, 15, 12, 37, 12, 15, 12, 4, 13}, + {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 12, 4, 13}, + {9827, 13, 1, 6, 2, 14, 12, 40, 12, 15, 12, 4, 13}, + {9631, 13, 1, 6, 2, 13, 12, 42, 12, 15, 12, 4, 13}, + {9445, 13, 1, 6, 2, 12, 12, 44, 12, 15, 12, 4, 13}, + {9269, 13, 1, 6, 2, 12, 12, 45, 12, 15, 12, 4, 13}, + {9103, 13, 1, 6, 2, 12, 12, 47, 12, 15, 12, 4, 13}, + {8946, 13, 1, 6, 2, 12, 12, 49, 12, 15, 12, 4, 13}, + {8797, 12, 1, 7, 2, 12, 12, 48, 12, 15, 12, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 50, 12, 15, 12, 4, 13}, + {8520, 12, 1, 7, 2, 12, 12, 51, 12, 15, 12, 4, 13}, + {8392, 12, 1, 7, 2, 12, 12, 53, 12, 15, 12, 4, 13}, + {8269, 12, 1, 7, 2, 12, 12, 55, 12, 15, 12, 4, 13}, + {8153, 12, 1, 7, 2, 12, 12, 56, 12, 15, 12, 4, 13}, + {8041, 12, 1, 7, 2, 13, 12, 58, 12, 15, 12, 4, 13}, + {7934, 11, 1, 7, 2, 12, 12, 57, 12, 15, 12, 4, 13}, + {7831, 11, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, + {7733, 10, 1, 7, 3, 13, 12, 56, 12, 15, 12, 4, 13}, + {7638, 10, 1, 7, 2, 12, 12, 58, 12, 15, 12, 4, 13}, + {7547, 10, 1, 7, 2, 12, 12, 59, 12, 15, 12, 4, 13}, + {7459, 10, 1, 7, 2, 12, 12, 61, 12, 15, 12, 4, 13}, + {7374, 10, 2, 7, 3, 14, 13, 49, 12, 15, 12, 4, 13}, + {7291, 10, 2, 7, 3, 14, 13, 50, 12, 15, 12, 4, 13}, + {7212, 10, 2, 7, 3, 14, 13, 51, 12, 15, 12, 4, 13}, + {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, + {7061, 10, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6988, 10, 1, 7, 3, 12, 14, 63, 11, 14, 12, 3, 13}, + {6918, 9, 2, 7, 3, 14, 13, 52, 12, 15, 12, 4, 13}, + {6850, 9, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6784, 9, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, + {6720, 9, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, + {6658, 8, 2, 7, 3, 14, 13, 53, 12, 15, 12, 4, 13}, + {6597, 8, 2, 7, 2, 13, 13, 54, 12, 15, 12, 4, 13}, + {6539, 8, 2, 7, 2, 13, 13, 55, 12, 15, 12, 4, 13}, + {6482, 8, 2, 7, 2, 13, 13, 56, 12, 15, 12, 4, 13}, + {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 12, 4, 13}, + {6373, 7, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, + {6321, 7, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, + {6270, 7, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, + {6222, 7, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, + {6174, 6, 2, 7, 3, 15, 13, 54, 12, 15, 12, 4, 13}, + {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 12, 4, 13}, + {6083, 6, 2, 7, 3, 15, 13, 56, 12, 15, 12, 4, 13}, + {6040, 6, 2, 7, 3, 15, 13, 57, 12, 15, 12, 4, 13}, + {5997, 6, 2, 7, 3, 15, 13, 58, 12, 15, 12, 4, 13}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, /* 60 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 26, 11, 15, 11, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 26, 12, 15, 11, 4, 12}, + {11588, 13, 1, 4, 2, 15, 12, 30, 12, 15, 11, 4, 12}, + {11288, 13, 1, 4, 2, 15, 12, 32, 12, 15, 10, 4, 12}, + {11007, 13, 1, 4, 2, 15, 12, 31, 12, 15, 11, 4, 12}, + {10742, 13, 1, 4, 2, 15, 12, 33, 12, 15, 10, 4, 12}, + {10492, 13, 1, 6, 2, 15, 12, 37, 12, 15, 10, 4, 12}, + {10258, 13, 1, 6, 2, 15, 12, 38, 12, 15, 11, 4, 13}, + {10036, 13, 1, 6, 2, 15, 12, 38, 12, 15, 10, 4, 12}, + {9827, 13, 1, 6, 2, 14, 12, 42, 12, 15, 11, 4, 12}, + {9631, 13, 1, 6, 2, 13, 12, 41, 12, 15, 11, 4, 12}, + {9445, 13, 1, 6, 2, 12, 12, 45, 12, 15, 11, 4, 12}, + {9269, 13, 1, 6, 2, 12, 12, 47, 12, 15, 11, 4, 12}, + {9103, 13, 1, 6, 2, 12, 12, 46, 12, 15, 11, 4, 12}, + {8946, 13, 1, 6, 2, 12, 12, 48, 12, 15, 10, 4, 12}, + {8797, 12, 1, 7, 2, 12, 12, 49, 12, 15, 11, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 51, 12, 15, 11, 4, 12}, + {8520, 12, 1, 7, 2, 12, 12, 50, 12, 15, 11, 4, 12}, + {8392, 12, 1, 7, 2, 12, 12, 52, 12, 15, 10, 4, 12}, + {8269, 12, 1, 7, 2, 12, 12, 56, 12, 15, 10, 4, 12}, + {8153, 12, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, + {8041, 12, 1, 7, 2, 13, 12, 57, 12, 15, 10, 4, 12}, + {7934, 11, 1, 7, 2, 12, 12, 55, 12, 15, 11, 4, 12}, + {7831, 11, 1, 7, 2, 12, 12, 57, 12, 15, 10, 4, 12}, + {7733, 10, 1, 7, 3, 13, 12, 55, 12, 15, 11, 4, 12}, + {7638, 10, 1, 7, 2, 12, 12, 59, 12, 15, 10, 4, 12}, + {7547, 10, 1, 7, 2, 12, 12, 60, 12, 15, 11, 4, 12}, + {7459, 10, 1, 7, 2, 12, 12, 48, 12, 15, 11, 4, 12}, + {7374, 10, 2, 7, 3, 14, 13, 47, 12, 15, 11, 4, 13}, + {7291, 10, 2, 7, 3, 14, 13, 49, 12, 15, 10, 4, 12}, + {7212, 10, 2, 7, 3, 14, 13, 50, 12, 15, 10, 4, 12}, + {7135, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 13}, + {7061, 10, 2, 7, 3, 14, 13, 52, 12, 15, 11, 4, 12}, + {6988, 10, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6918, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6850, 9, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6784, 9, 1, 7, 2, 11, 14, 63, 11, 15, 11, 4, 13}, + {6720, 9, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, + {6658, 8, 1, 7, 3, 12, 14, 63, 11, 15, 11, 4, 13}, + {6597, 8, 1, 7, 2, 11, 14, 63, 11, 14, 11, 3, 13}, + {6539, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, + {6482, 8, 1, 7, 2, 11, 14, 63, 10, 14, 11, 3, 13}, + {6427, 7, 2, 7, 3, 14, 13, 54, 12, 15, 10, 4, 12}, + {6373, 7, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, + {6321, 7, 2, 7, 3, 15, 13, 54, 12, 15, 11, 4, 12}, + {6270, 7, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, + {6222, 7, 2, 7, 3, 15, 13, 56, 12, 15, 11, 4, 12}, + {6174, 6, 2, 7, 3, 15, 13, 53, 12, 15, 11, 4, 12}, + {6128, 6, 2, 7, 3, 15, 13, 55, 12, 15, 11, 4, 12}, + {6083, 6, 2, 7, 3, 15, 13, 55, 12, 15, 10, 4, 12}, + {6040, 6, 2, 7, 3, 15, 13, 56, 12, 15, 10, 4, 12}, + {5997, 6, 2, 7, 3, 15, 13, 57, 12, 15, 10, 4, 12}, + }, { + {12605, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, /* 80 MHz */ + {12245, 13, 1, 4, 2, 15, 12, 21, 12, 15, 11, 4, 13}, + {11906, 13, 1, 4, 2, 15, 12, 20, 11, 15, 11, 4, 13}, + {11588, 13, 1, 4, 2, 15, 12, 22, 12, 15, 11, 4, 12}, + {11288, 13, 1, 5, 2, 15, 12, 23, 12, 15, 11, 4, 13}, + {11007, 13, 1, 5, 2, 15, 12, 25, 12, 15, 10, 4, 12}, + {10742, 13, 1, 5, 2, 15, 12, 26, 12, 15, 11, 4, 13}, + {10492, 13, 1, 6, 2, 15, 12, 27, 11, 15, 11, 4, 13}, + {10258, 13, 1, 6, 2, 15, 12, 29, 12, 15, 10, 4, 12}, + {10036, 13, 1, 6, 2, 15, 12, 30, 12, 15, 11, 4, 12}, + {9827, 13, 1, 6, 2, 14, 12, 31, 12, 15, 11, 4, 13}, + {9631, 13, 1, 6, 2, 13, 12, 33, 12, 15, 10, 4, 12}, + {9445, 13, 1, 6, 2, 12, 12, 34, 12, 15, 11, 4, 12}, + {9269, 13, 1, 6, 2, 12, 12, 35, 12, 15, 11, 4, 13}, + {9103, 13, 1, 6, 2, 12, 12, 37, 12, 15, 10, 4, 12}, + {8946, 13, 1, 6, 2, 12, 12, 38, 12, 15, 11, 4, 12}, + {8797, 12, 1, 7, 2, 12, 12, 37, 12, 15, 11, 4, 13}, + {8655, 12, 1, 7, 2, 12, 12, 39, 12, 15, 11, 4, 12}, + {8520, 12, 1, 7, 2, 12, 12, 40, 12, 15, 11, 4, 12}, + {8392, 12, 1, 7, 2, 12, 12, 41, 12, 15, 11, 4, 13}, + {8269, 12, 1, 7, 2, 12, 12, 43, 12, 15, 10, 4, 12}, + {8153, 12, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, + {8041, 12, 1, 7, 2, 13, 12, 45, 12, 15, 11, 4, 12}, + {7934, 11, 1, 7, 2, 12, 12, 44, 12, 15, 11, 4, 12}, + {7831, 11, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 13}, + {7733, 10, 1, 7, 3, 13, 12, 44, 12, 15, 11, 4, 12}, + {7638, 10, 1, 7, 2, 12, 12, 45, 12, 15, 11, 4, 12}, + {7547, 10, 1, 7, 2, 12, 12, 46, 12, 15, 11, 4, 12}, + {7459, 10, 1, 7, 2, 12, 12, 47, 12, 15, 11, 4, 13}, + {7374, 10, 2, 7, 3, 14, 13, 38, 12, 15, 11, 4, 12}, + {7291, 10, 2, 7, 3, 14, 13, 39, 12, 15, 10, 4, 12}, + {7212, 10, 2, 7, 3, 14, 13, 40, 12, 15, 10, 4, 12}, + {7135, 10, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, + {7061, 10, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, + {6988, 10, 1, 7, 3, 12, 14, 54, 12, 15, 11, 4, 12}, + {6918, 9, 2, 7, 3, 14, 13, 41, 12, 15, 10, 4, 12}, + {6850, 9, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, + {6784, 9, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 13}, + {6720, 9, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 13}, + {6658, 8, 2, 7, 3, 14, 13, 41, 12, 15, 11, 4, 13}, + {6597, 8, 2, 7, 2, 13, 13, 42, 12, 15, 11, 4, 12}, + {6539, 8, 2, 7, 2, 13, 13, 43, 12, 15, 11, 4, 12}, + {6482, 8, 2, 7, 2, 13, 13, 44, 12, 15, 11, 4, 12}, + {6427, 7, 2, 7, 3, 14, 13, 42, 12, 15, 10, 4, 12}, + {6373, 7, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, + {6321, 7, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, + {6270, 7, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 12}, + {6222, 7, 2, 7, 3, 15, 13, 45, 12, 15, 10, 4, 12}, + {6174, 6, 2, 7, 3, 15, 13, 42, 12, 15, 11, 4, 13}, + {6128, 6, 2, 7, 3, 15, 13, 43, 12, 15, 11, 4, 12}, + {6083, 6, 2, 7, 3, 15, 13, 44, 12, 15, 10, 4, 12}, + {6040, 6, 2, 7, 3, 15, 13, 44, 12, 15, 11, 4, 13}, + {5997, 6, 2, 7, 3, 15, 13, 45, 12, 15, 11, 4, 12}, + } }; + +/* Rx Gain Tables */ + +#define SIZE_FULL_TABLE 77 + +static const uint8_t full_gain_table[RXGAIN_TBLS_END][SIZE_FULL_TABLE][3] = +{ { /* 800 MHz */ + { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, + { 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, + { 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, + { 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, + { 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, + { 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, + { 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, + { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, + { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, + { 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, + { 0x04, 0x28, 0x20 }, { 0x04, 0x29, 0x00 }, { 0x04, 0x2A, 0x00 }, + { 0x04, 0x2B, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, + { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, { 0x44, 0x22, 0x00 }, + { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, { 0x44, 0x25, 0x00 }, + { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, { 0x44, 0x28, 0x00 }, + { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, { 0x44, 0x2B, 0x00 }, + { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, { 0x44, 0x2E, 0x00 }, + { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, { 0x44, 0x31, 0x00 }, + { 0x44, 0x32, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, + { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, + { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, + { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } +}, { /* 2300 MHz */ + { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, + { 0x00, 0x01, 0x00 }, { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, + { 0x00, 0x04, 0x00 }, { 0x00, 0x05, 0x00 }, { 0x01, 0x03, 0x20 }, + { 0x01, 0x04, 0x00 }, { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, + { 0x01, 0x07, 0x00 }, { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, + { 0x01, 0x0A, 0x00 }, { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, + { 0x01, 0x0D, 0x00 }, { 0x01, 0x0E, 0x00 }, { 0x02, 0x09, 0x20 }, + { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x00 }, { 0x02, 0x0C, 0x00 }, + { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, { 0x02, 0x0F, 0x00 }, + { 0x02, 0x10, 0x00 }, { 0x02, 0x2B, 0x20 }, { 0x02, 0x2C, 0x00 }, + { 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, + { 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x24, 0x21, 0x20 }, + { 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, + { 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, + { 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, + { 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, + { 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, + { 0x44, 0x2E, 0x00 }, { 0x44, 0x2F, 0x00 }, { 0x44, 0x30, 0x00 }, + { 0x44, 0x31, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, + { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, + { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, + { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }, +}, { /* 5500 MHz */ + { 0x00, 0x00, 0x20 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00 }, { 0x00, 0x01, 0x00 }, + { 0x00, 0x02, 0x00 }, { 0x00, 0x03, 0x00 }, { 0x01, 0x01, 0x20 }, + { 0x01, 0x02, 0x00 }, { 0x01, 0x03, 0x00 }, { 0x01, 0x04, 0x20 }, + { 0x01, 0x05, 0x00 }, { 0x01, 0x06, 0x00 }, { 0x01, 0x07, 0x00 }, + { 0x01, 0x08, 0x00 }, { 0x01, 0x09, 0x00 }, { 0x01, 0x0A, 0x00 }, + { 0x01, 0x0B, 0x00 }, { 0x01, 0x0C, 0x00 }, { 0x02, 0x08, 0x20 }, + { 0x02, 0x09, 0x00 }, { 0x02, 0x0A, 0x00 }, { 0x02, 0x0B, 0x20 }, + { 0x02, 0x0C, 0x00 }, { 0x02, 0x0D, 0x00 }, { 0x02, 0x0E, 0x00 }, + { 0x02, 0x0F, 0x00 }, { 0x02, 0x2A, 0x20 }, { 0x02, 0x2B, 0x00 }, + { 0x04, 0x27, 0x20 }, { 0x04, 0x28, 0x00 }, { 0x04, 0x29, 0x00 }, + { 0x04, 0x2A, 0x00 }, { 0x04, 0x2B, 0x00 }, { 0x04, 0x2C, 0x00 }, + { 0x04, 0x2D, 0x00 }, { 0x24, 0x20, 0x20 }, { 0x24, 0x21, 0x00 }, + { 0x24, 0x22, 0x00 }, { 0x44, 0x20, 0x20 }, { 0x44, 0x21, 0x00 }, + { 0x44, 0x22, 0x00 }, { 0x44, 0x23, 0x00 }, { 0x44, 0x24, 0x00 }, + { 0x44, 0x25, 0x00 }, { 0x44, 0x26, 0x00 }, { 0x44, 0x27, 0x00 }, + { 0x44, 0x28, 0x00 }, { 0x44, 0x29, 0x00 }, { 0x44, 0x2A, 0x00 }, + { 0x44, 0x2B, 0x00 }, { 0x44, 0x2C, 0x00 }, { 0x44, 0x2D, 0x00 }, + { 0x44, 0x2E, 0x00 }, { 0x64, 0x2E, 0x20 }, { 0x64, 0x2F, 0x00 }, + { 0x64, 0x30, 0x00 }, { 0x64, 0x31, 0x00 }, { 0x64, 0x32, 0x00 }, + { 0x64, 0x33, 0x00 }, { 0x64, 0x34, 0x00 }, { 0x64, 0x35, 0x00 }, + { 0x64, 0x36, 0x00 }, { 0x64, 0x37, 0x00 }, { 0x64, 0x38, 0x00 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 } +} }; + +#define SIZE_SPLIT_TABLE 41 + +static const uint8_t split_gain_table[RXGAIN_TBLS_END][SIZE_SPLIT_TABLE][3] = +{ { /* 800 MHz */ + { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x20 }, { 0x01, 0x18, 0x20 }, { 0x02, 0x18, 0x20 }, + { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 }, { 0x05, 0x38, 0x20 }, + { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 }, { 0x08, 0x38, 0x20 }, + { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 }, { 0x0B, 0x38, 0x20 }, + { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 }, { 0x0E, 0x38, 0x20 }, + { 0x0F, 0x38, 0x20 }, { 0x24, 0x38, 0x20 }, { 0x25, 0x38, 0x20 }, + { 0x44, 0x38, 0x20 }, { 0x45, 0x38, 0x20 }, { 0x46, 0x38, 0x20 }, + { 0x47, 0x38, 0x20 }, { 0x48, 0x38, 0x20 }, { 0x64, 0x38, 0x20 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }, +}, { /* 2300 MHz */ + { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x20 }, { 0x01, 0x18, 0x20 }, + { 0x02, 0x18, 0x20 }, { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 }, + { 0x05, 0x38, 0x20 }, { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 }, + { 0x08, 0x38, 0x20 }, { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 }, + { 0x0B, 0x38, 0x20 }, { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 }, + { 0x0E, 0x38, 0x20 }, { 0x0F, 0x38, 0x20 }, { 0x25, 0x38, 0x20 }, + { 0x26, 0x38, 0x20 }, { 0x44, 0x38, 0x20 }, { 0x45, 0x38, 0x20 }, + { 0x46, 0x38, 0x20 }, { 0x47, 0x38, 0x20 }, { 0x64, 0x38, 0x20 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }, +}, { /* 5500 MHz */ + { 0x00, 0x18, 0x20 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, { 0x00, 0x18, 0x00 }, + { 0x00, 0x18, 0x00 }, { 0x01, 0x18, 0x20 }, { 0x02, 0x18, 0x20 }, + { 0x04, 0x18, 0x20 }, { 0x04, 0x38, 0x20 }, { 0x05, 0x38, 0x20 }, + { 0x06, 0x38, 0x20 }, { 0x07, 0x38, 0x20 }, { 0x08, 0x38, 0x20 }, + { 0x09, 0x38, 0x20 }, { 0x0A, 0x38, 0x20 }, { 0x0B, 0x38, 0x20 }, + { 0x0C, 0x38, 0x20 }, { 0x0D, 0x38, 0x20 }, { 0x0E, 0x38, 0x20 }, + { 0x0F, 0x38, 0x20 }, { 0x62, 0x38, 0x20 }, { 0x25, 0x38, 0x20 }, + { 0x26, 0x38, 0x20 }, { 0x44, 0x38, 0x20 }, { 0x64, 0x38, 0x20 }, + { 0x65, 0x38, 0x20 }, { 0x66, 0x38, 0x20 }, { 0x67, 0x38, 0x20 }, + { 0x68, 0x38, 0x20 }, { 0x69, 0x38, 0x20 }, { 0x6A, 0x38, 0x20 }, + { 0x6B, 0x38, 0x20 }, { 0x6C, 0x38, 0x20 }, { 0x6D, 0x38, 0x20 }, + { 0x6E, 0x38, 0x20 }, { 0x6F, 0x38, 0x20 }, +} }; + +/* Mixer GM Sub-table */ + +static const uint8_t gm_st_gain[16] = { 0x78, 0x74, 0x70, 0x6C, 0x68, 0x64, 0x60, +0x5C, 0x58, 0x54, 0x50, 0x4C, 0x48, 0x30, 0x18, 0x0 }; +static const uint8_t gm_st_ctrl[16] = { 0x0, 0xD, 0x15, 0x1B, 0x21, 0x25, 0x29, +0x2C, 0x2F, 0x31, 0x33, 0x34, 0x35, 0x3A, 0x3D, 0x3E }; + +static const int8_t lna_table[RXGAIN_TBLS_END][4] = { + {5, 17, 19, 24}, {3, 14, 17, 21}, {-4, 10, 13, 14}}; +static const int8_t tia_table[] = { -6, 0 }; +static const int8_t mixer_table[RXGAIN_TBLS_END][16] = { + {0, 3, 9, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25}, + {0, 3, 9, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26}, + {0, 3, 8, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}}; + +static const uint32_t gain_step_calib_reg_val[4][5] = { + {0xC0, 0x2E, 0x10, 0x06, 0x00}, // LO Frequency Range: 600 to 1300 MHz + {0xC0, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 1300 to 3300 MHz + {0xB8, 0x2C, 0x10, 0x06, 0x00}, // LO Frequency Range: 2700 to 4100 MHz + {0xA0, 0x24, 0x10, 0x06, 0x00}, // LO Frequency Range: 4000 to 6000 MHz +}; + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +const char *ad9361_ensm_states[] = { + "sleep", "", "", "", "", "alert", "tx", "tx flush", + "rx", "rx_flush", "fdd", "fdd_flush" +}; + +/** + * SPI multiple bytes register read. + * @param spi + * @param reg The register address. + * @param rbuf The data buffer. + * @param num The number of bytes to read. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_spi_readm(struct spi_device *spi, uint32_t reg, + uint8_t *rbuf, uint32_t num) +{ + uint8_t buf[2]; + int32_t ret; + uint16_t cmd; + + if (num > MAX_MBYTE_SPI) + return -EINVAL; + + cmd = AD_READ | AD_CNT(num) | AD_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + + ret = spi_write_then_read(spi, &buf[0], 2, rbuf, num); + if (ret < 0) { + dev_err(&spi->dev, "Read Error %"PRId32, ret); + return ret; + } +#ifdef _DEBUG + { + int32_t i; + for (i = 0; i < num; i++) + dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", __func__, reg--, rbuf[i]); + } +#endif + + return 0; +} + +/** + * SPI register read. + * @param spi + * @param reg The register address. + * @return The register value or negative error code in case of failure. + */ +int32_t ad9361_spi_read(struct spi_device *spi, uint32_t reg) +{ + uint8_t buf; + int32_t ret; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + return buf; +} + +/** + * SPI register bits read. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param offset The mask offset. + * @return The bits value or negative error code in case of failure. + */ +static int32_t __ad9361_spi_readf(struct spi_device *spi, uint32_t reg, + uint32_t mask, uint32_t offset) +{ + uint8_t buf; + int32_t ret; + + if (!mask) + return -EINVAL; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + buf &= mask; + buf >>= offset; + + return buf; +} + +/** + * SPI register bits read. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @return The bits value or negative error code in case of failure. + */ +#define ad9361_spi_readf(spi, reg, mask) \ + __ad9361_spi_readf(spi, reg, mask, find_first_bit(mask)) + +/** + * SPI register write. + * @param spi + * @param reg The register address. + * @param val The value of the register. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_spi_write(struct spi_device *spi, + uint32_t reg, uint32_t val) +{ + uint8_t buf[3]; + int32_t ret; + uint16_t cmd; + + cmd = AD_WRITE | AD_CNT(1) | AD_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + buf[2] = val; + + ret = spi_write_then_read(spi, buf, 3, NULL, 0); + if (ret < 0) { + dev_err(&spi->dev, "Write Error %"PRId32, ret); + return ret; + } + +#ifdef _DEBUG + dev_dbg(&spi->dev, "%s: reg 0x%"PRIX32" val 0x%X", __func__, reg, buf[2]); +#endif + + return 0; +} + +/** + * SPI register bits write. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param offset The mask offset. + * @param val The bits value. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_spi_writef(struct spi_device *spi, uint32_t reg, + uint32_t mask, uint32_t offset, uint32_t val) +{ + uint8_t buf; + int32_t ret; + + if (!mask) + return -EINVAL; + + ret = ad9361_spi_readm(spi, reg, &buf, 1); + if (ret < 0) + return ret; + + buf &= ~mask; + buf |= ((val << offset) & mask); + + return ad9361_spi_write(spi, reg, buf); +} + +/** + * SPI register bits write. + * @param spi + * @param reg The register address. + * @param mask The bits mask. + * @param val The bits value. + * @return 0 in case of success, negative error code otherwise. + */ +#define ad9361_spi_writef(spi, reg, mask, val) \ + __ad9361_spi_writef(spi, reg, mask, find_first_bit(mask), val) + +/** + * SPI multiple bytes register write. + * @param spi + * @param reg The register address. + * @param tbuf The data buffer. + * @param num The number of bytes to read. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_spi_writem(struct spi_device *spi, + uint32_t reg, uint8_t *tbuf, uint32_t num) +{ + uint8_t buf[10]; + int32_t ret; + uint16_t cmd; + + if (num > MAX_MBYTE_SPI) + return -EINVAL; + + cmd = AD_WRITE | AD_CNT(num) | AD_ADDR(reg); + buf[0] = cmd >> 8; + buf[1] = cmd & 0xFF; + +#ifndef ALTERA_PLATFORM + memcpy(&buf[2], tbuf, num); +#else + int32_t i; + for (i = 0; i < num; i++) + buf[2 + i] = tbuf[i]; +#endif + ret = spi_write_then_read(spi, buf, num + 2, NULL, 0); + if (ret < 0) { + dev_err(&spi->dev, "Write Error %"PRId32, ret); + return ret; + } + +#ifdef _DEBUG + { + int32_t i; + for (i = 0; i < num; i++) + dev_dbg(&spi->dev, "Reg 0x%"PRIX32" val 0x%X", reg--, tbuf[i]); + } +#endif + + return 0; +} + +/** + * Validate RF BW frequency. + * @param phy The AD9361 state structure. + * @param bw The RF BW frequency. + * @return The validated RF BW frequency. + */ +uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw) +{ + switch (phy->dev_sel) { + case ID_AD9363A: + return clamp_t(uint32_t, bw, 0, 20000000UL); + default: + return clamp_t(uint32_t, bw, 0, 56000000UL); + } +} + +/** + * Validate RF PLL frequency. + * @param phy The AD9361 state structure. + * @param freq The RF PLL frequency. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_validate_rfpll(struct ad9361_rf_phy *phy, uint64_t freq) +{ + switch (phy->dev_sel) { + case ID_AD9363A: + if (freq > AD9363A_MAX_CARRIER_FREQ_HZ || + freq < AD9363A_MIN_CARRIER_FREQ_HZ) + return -EINVAL; + break; + default: + if (freq > MAX_CARRIER_FREQ_HZ || freq < MIN_CARRIER_FREQ_HZ) + return -EINVAL; + } + + return 0; +} + +/** + * Find optimal value. + * @param field + * @param ret_start + * @return The optimal delay in case of success, negative error code otherwise. + */ +int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start) +{ + int32_t i, cnt = 0, max_cnt = 0, start, max_start = 0; + + for(i = 0, start = -1; i < (int64_t)size; i++) { + if (field[i] == 0) { + if (start == -1) + start = i; + cnt++; + } else { + if (cnt > max_cnt) { + max_cnt = cnt; + max_start = start; + } + start = -1; + cnt = 0; + } + } + + if (cnt > max_cnt) { + max_cnt = cnt; + max_start = start; + } + + *ret_start = max_start; + + return max_cnt; +} + +/** + * Select the channel mapping in 1rx1tx mode. + * @param phy The AD9361 state structure. + * @param map Map + * @param channel Channel + * @return The channel number. + */ +int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, int32_t channel) +{ + uint32_t map; + + if (phy->pdata->rx2tx2) + return channel; + + if (tx) + map = phy->pdata->rx1tx1_mode_use_tx_num; + else + map = phy->pdata->rx1tx1_mode_use_rx_num; + + if (map == 2) + return channel + 1; + + return channel; +} + +/** + * AD9361 Device Reset + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_reset(struct ad9361_rf_phy *phy) +{ + apb_reset(); + return 0; +} + +/** + * Enable/disable the desired TX channel. + * @param phy The AD9361 state structure. + * @param tx_if The desired channel number [1, 2]. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, uint32_t enable) +{ + if ((tx_if & enable) > 1 && AD9364_DEVICE && enable) + return -EINVAL; + + return ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_CHANNEL_ENABLE(tx_if), enable); +} + +/** + * Enable/disable the desired RX channel. + * @param phy The AD9361 state structure. + * @param tx_if The desired channel number [1, 2]. + * @param enable Enable/disable option. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, uint32_t enable) +{ + if ((rx_if & enable) > 1 && AD9364_DEVICE && enable) + return -EINVAL; + + return ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_CHANNEL_ENABLE(rx_if), enable); +} + +/** + * Loopback works only TX1->RX1 or RX2->RX2. + * @param phy The AD9361 state structure. + * @param enable Enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_int_loopback_fix_ch_cross(struct ad9361_rf_phy *phy, bool enable) +{ + /* Loopback works only TX1->RX1 or RX2->RX2 */ + if (!phy->pdata->rx2tx2 && phy->pdata->rx1tx1_mode_use_rx_num != + phy->pdata->rx1tx1_mode_use_tx_num) + return ad9361_en_dis_tx(phy, TX_1 | TX_2, + enable ? phy->pdata->rx1tx1_mode_use_rx_num : + phy->pdata->rx1tx1_mode_use_tx_num); + + return 0; +} + +/** + * BIST loopback mode. + * @param phy The AD9361 state structure. + * @param mode BIST loopback mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode) +{ + uint32_t sp_hd, reg; + + dev_dbg(&phy->spi->dev, "%s: mode %"PRId32, __func__, mode); + + reg = ad9361_spi_read(phy->spi, REG_OBSERVE_CONFIG); + + phy->bist_loopback_mode = mode; + + switch (mode) { + case 0: + //ad9361_hdl_loopback(phy, false); + ad9361_int_loopback_fix_ch_cross(phy, false); + reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | + DATA_PORT_LOOP_TEST_ENABLE); + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + case 1: + /* loopback (AD9361 internal) TX->RX */ + //ad9361_hdl_loopback(phy, false); + ad9361_int_loopback_fix_ch_cross(phy, true); + sp_hd = ad9361_spi_read(phy->spi, REG_PARALLEL_PORT_CONF_3); + if ((sp_hd & SINGLE_PORT_MODE) && (sp_hd & HALF_DUPLEX_MODE)) + reg |= DATA_PORT_SP_HD_LOOP_TEST_OE; + else + reg &= ~DATA_PORT_SP_HD_LOOP_TEST_OE; + + reg |= DATA_PORT_LOOP_TEST_ENABLE; + + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + case 2: + /* loopback (FPGA internal) RX->TX */ + //ad9361_hdl_loopback(phy, true); + ad9361_int_loopback_fix_ch_cross(phy, false); + reg &= ~(DATA_PORT_SP_HD_LOOP_TEST_OE | + DATA_PORT_LOOP_TEST_ENABLE); + return ad9361_spi_write(phy->spi, REG_OBSERVE_CONFIG, reg); + default: + return -EINVAL; + } +} + +/** + * Get BIST loopback mode. + * @param phy The AD9361 state structure. + * @param mode BIST loopback mode. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode) +{ + *mode = phy->bist_loopback_mode; +} + +/** + * BIST mode. + * @param phy The AD9361 state structure. + * @param mode Bist mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode) +{ + uint32_t reg = 0; + + dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); + + phy->bist_prbs_mode = mode; + + switch (mode) { + case BIST_DISABLE: + reg = 0; + break; + case BIST_INJ_TX: + reg = BIST_CTRL_POINT(0) | BIST_ENABLE; + break; + case BIST_INJ_RX: + reg = BIST_CTRL_POINT(2) | BIST_ENABLE; + break; + }; + + return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg); +} + +/** + * Get BIST mode settings. + * @param phy The AD9361 state structure. + * @param mode Bist mode. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode *mode) +{ + *mode = phy->bist_prbs_mode; +} + +/** + * BIST tone. + * @param phy The AD9361 state structure. + * @param mode Bist tone mode. + * @param freq_Hz Bist tone frequency. + * @param level_dB Bist tone level. + * @param mask Bist reg mask. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode mode, uint32_t freq_Hz, + uint32_t level_dB, uint32_t mask) +{ + uint32_t clk = 0; + uint32_t reg = 0, reg1, reg_mask; + + dev_dbg(&phy->spi->dev, "%s: mode %d", __func__, mode); + + phy->bist_tone_mode = mode; + phy->bist_tone_freq_Hz = freq_Hz; + phy->bist_tone_level_dB = level_dB; + phy->bist_tone_mask = mask; + + switch (mode) { + case BIST_DISABLE: + reg = 0; + break; + case BIST_INJ_TX: + clk = clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]); + reg = BIST_CTRL_POINT(0) | BIST_ENABLE; + break; + case BIST_INJ_RX: + clk = clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]); + reg = BIST_CTRL_POINT(2) | BIST_ENABLE; + break; + }; + + reg |= TONE_PRBS; + reg |= TONE_LEVEL(level_dB / 6); + + if (freq_Hz < 4) { + reg |= TONE_FREQ(freq_Hz); + } + else { + if (clk) + reg |= TONE_FREQ(DIV_ROUND_CLOSEST(freq_Hz * 32, clk) - 1); + } + + reg_mask = BIST_MASK_CHANNEL_1_I_DATA | BIST_MASK_CHANNEL_1_Q_DATA | + BIST_MASK_CHANNEL_2_I_DATA | BIST_MASK_CHANNEL_2_Q_DATA; + + reg1 = ((mask << 2) & reg_mask); + ad9361_spi_write(phy->spi, REG_BIST_AND_DATA_PORT_TEST_CONFIG, reg1); + + return ad9361_spi_write(phy->spi, REG_BIST_CONFIG, reg); +} + +/** + * Get BIST tone settings. + * @param phy The AD9361 state structure. + * @param mode Bist tone mode. + * @param freq_Hz Bist tone frequency. + * @param level_dB Bist tone level. + * @param mask Bist reg mask. + * @return 0 in case of success, negative error code otherwise. + */ +void ad9361_get_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode, uint32_t *freq_Hz, + uint32_t *level_dB, uint32_t *mask) +{ + *mode = phy->bist_tone_mode; + *freq_Hz = phy->bist_tone_freq_Hz; + *level_dB = phy->bist_tone_level_dB; + *mask = phy->bist_tone_mask; +} + +/** + * Check the calibration done bit. + * @param phy The AD9361 state structure. + * @param reg The register address. + * @param mask The bit mask. + * @param done_state The done state [0,1]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_check_cal_done(struct ad9361_rf_phy *phy, uint32_t reg, + uint32_t mask, uint32_t done_state) +{ + uint32_t timeout = 5000; /* RFDC_CAL can take long */ + uint32_t state; + + do { + state = ad9361_spi_readf(phy->spi, reg, mask); + if (state == done_state) + return 0; + + if (reg == REG_CALIBRATION_CTRL) + udelay(1000); + else + udelay(100); + } while (timeout--); + + dev_err(&phy->spi->dev, "Calibration TIMEOUT (0x%"PRIX32", 0x%"PRIX32")", reg, mask); + + return -ETIMEDOUT; +} + +/** + * Run an AD9361 calibration and check the calibration done bit. + * @param phy The AD9361 state structure. + * @param mask The calibration bit mask[RX_BB_TUNE_CAL, TX_BB_TUNE_CAL, + * RX_QUAD_CAL, TX_QUAD_CAL, RX_GAIN_STEP_CAL, TXMON_CAL, + * RFDC_CAL, BBDC_CAL]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_run_calibration(struct ad9361_rf_phy *phy, uint32_t mask) +{ + int32_t ret = ad9361_spi_write(phy->spi, REG_CALIBRATION_CTRL, mask); + if (ret < 0) + return ret; + + dev_dbg(&phy->spi->dev, "%s: CAL Mask 0x%"PRIx32, __func__, mask); + + return ad9361_check_cal_done(phy, REG_CALIBRATION_CTRL, mask, 0); +} + +/** + * Choose the right RX gain table index for the selected frequency. + * @param freq The frequency value [Hz]. + * @return The index to the RX gain table. + */ +static enum rx_gain_table_name ad9361_gt_tableindex(uint64_t freq) +{ + if (freq <= 1300000000ULL) + return TBL_200_1300_MHZ; + + if (freq <= 4000000000ULL) + return TBL_1300_4000_MHZ; + + return TBL_4000_6000_MHZ; +} + +/** + * Shift the real frequency value, so it fits type unsigned long + * Note: PLL operates between 47 .. 6000 MHz which is > 2^32. + * @param freq The frequency value [Hz]. + * @return The shifted frequency value. + */ +uint32_t ad9361_to_clk(uint64_t freq) +{ + return (uint32_t)(freq >> 1); +} + +/** + * Shift back the frequency value, so it reflects the real value. + * Note: PLL operates between 47 .. 6000 MHz which is > 2^32. + * @param freq The frequency value [Hz]. + * @return The shifted frequency value. + */ +uint64_t ad9361_from_clk(uint32_t freq) +{ + return ((uint64_t)freq << 1); +} + +/** + * Load the gain table for the selected frequency range and receiver. + * @param phy The AD9361 state structure. + * @param freq The frequency value [Hz]. + * @param dest The destination [GT_RX1, GT_RX2]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_load_gt(struct ad9361_rf_phy *phy, uint64_t freq, uint32_t dest) +{ + struct spi_device *spi = phy->spi; + const uint8_t(*tab)[3]; + enum rx_gain_table_name band; + uint32_t index_max, i, lna; + + dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64, __func__, freq); + + band = ad9361_gt_tableindex(freq); + + dev_dbg(&phy->spi->dev, "%s: frequency %"PRIu64" (band %d)", + __func__, freq, band); + + /* check if table is present */ + if (phy->current_table == band) + return 0; + + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, + AGC_USE_FULL_GAIN_TABLE, !phy->pdata->split_gt); + + if (has_split_gt && phy->pdata->split_gt) { + tab = &split_gain_table[band][0]; + index_max = SIZE_SPLIT_TABLE; + } + else { + tab = &full_gain_table[band][0]; + index_max = SIZE_FULL_TABLE; + } + + lna = phy->pdata->elna_ctrl.elna_in_gaintable_all_index_en ? + EXT_LNA_CTRL : 0; + + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | + RECEIVER_SELECT(dest)); /* Start Gain Table Clock */ + + for (i = 0; i < index_max; i++) { + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, i); /* Gain Table Index */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA1, tab[i][0] | lna); /* Ext LNA, Int LNA, & Mixer Gain Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA2, tab[i][1]); /* TIA & LPF Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_WRITE_DATA3, tab[i][2]); /* DC Cal bit & Dig Gain Word */ + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, + START_GAIN_TABLE_CLOCK | + WRITE_GAIN_TABLE | + RECEIVER_SELECT(dest)); /* Gain Table Index */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay 3 ADCCLK/16 cycles */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ + } + + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, START_GAIN_TABLE_CLOCK | + RECEIVER_SELECT(dest)); /* Clear Write Bit */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ + ad9361_spi_write(spi, REG_GAIN_TABLE_READ_DATA1, 0); /* Dummy Write to delay ~1u */ + ad9361_spi_write(spi, REG_GAIN_TABLE_CONFIG, 0); /* Stop Gain Table Clock */ + + phy->current_table = band; + + return 0; +} + +/** + * Setup the external low-noise amplifier (LNA). + * @param phy The AD9361 state structure. + * @param ctrl Pointer to eLNA control structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_setup_ext_lna(struct ad9361_rf_phy *phy, +struct elna_control *ctrl) +{ + ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA1_CTRL, + ctrl->elna_1_control_en); + + ad9361_spi_writef(phy->spi, REG_EXTERNAL_LNA_CTRL, EXTERNAL_LNA2_CTRL, + ctrl->elna_2_control_en); + + ad9361_spi_write(phy->spi, REG_EXT_LNA_HIGH_GAIN, + EXT_LNA_HIGH_GAIN(ctrl->gain_mdB / 500)); + + return ad9361_spi_write(phy->spi, REG_EXT_LNA_LOW_GAIN, + EXT_LNA_LOW_GAIN(ctrl->bypass_loss_mdB / 500)); +} + +/** + * Set the clock output mode. + * @param phy The AD9361 state structure. + * @param mode The clock output mode []. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_clkout_control(struct ad9361_rf_phy *phy, +enum ad9361_clkout mode) +{ + if (mode == CLKOUT_DISABLE) + return ad9361_spi_writef(phy->spi, REG_BBPLL, CLKOUT_ENABLE, 0); + + return ad9361_spi_writef(phy->spi, REG_BBPLL, + CLKOUT_ENABLE | CLKOUT_SELECT(~0), + ((mode - 1) << 1) | 0x1); +} + +/** + * Load the Gm Sub Table. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_load_mixer_gm_subtable(struct ad9361_rf_phy *phy) +{ + int32_t i, addr; + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, + START_GM_SUB_TABLE_CLOCK); /* Start Clock */ + + for (i = 0, addr = ARRAY_SIZE(gm_st_ctrl); i < (int64_t)ARRAY_SIZE(gm_st_ctrl); i++) { + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_ADDRESS, --addr); /* Gain Table Index */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_BIAS_WRITE, 0); /* Bias */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_WRITE, gm_st_gain[i]); /* Gain */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CTRL_WRITE, gm_st_ctrl[i]); /* Control */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, + WRITE_GM_SUB_TABLE | START_GM_SUB_TABLE_CLOCK); /* Write Words */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + } + + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, START_GM_SUB_TABLE_CLOCK); /* Clear Write */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_GAIN_READ, 0); /* Dummy Delay */ + ad9361_spi_write(phy->spi, REG_GM_SUB_TABLE_CONFIG, 0); /* Stop Clock */ + + return 0; +} + +/** + * Set the attenuation for the selected TX channels. + * @param phy The AD9361 state structure. + * @param atten_mdb Attenuation value [mdB]. + * @param tx1 Set true, the attenuation of the TX1 will be affected. + * @param tx2 Set true, the attenuation of the TX2 will be affected. + * @param immed Set true, an immediate update will take place. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb, + bool tx1, bool tx2, bool immed) +{ + uint8_t buf[2]; + int32_t ret = 0; + + dev_dbg(&phy->spi->dev, "%s : attenuation %"PRIu32" mdB tx1=%d tx2=%d", + __func__, atten_mdb, tx1, tx2); + + if (atten_mdb > 89750) /* 89.75 dB */ + return -EINVAL; + + atten_mdb /= 250; /* Scale to 0.25dB / LSB */ + + buf[0] = atten_mdb >> 8; + buf[1] = atten_mdb & 0xFF; + + ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, + IMMEDIATELY_UPDATE_TPC_ATTEN, 0); + + if (tx1) + ret = ad9361_spi_writem(phy->spi, REG_TX1_ATTEN_1, buf, 2); + + if (tx2) + ret = ad9361_spi_writem(phy->spi, REG_TX2_ATTEN_1, buf, 2); + + if (immed) + ad9361_spi_writef(phy->spi, REG_TX2_DIG_ATTEN, + IMMEDIATELY_UPDATE_TPC_ATTEN, 1); + + return ret; +} + +/** + * Get the attenuation for the selected TX channel. + * @param phy The AD9361 state structure. + * @param tx_num The selected channel [1, 2]. + * @return The attenuation value [mdB] or negative error code in case of failure. + */ +int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num) +{ + uint8_t buf[2]; + int32_t ret = 0; + uint32_t code; + + ret = ad9361_spi_readm(phy->spi, (tx_num == 1) ? + REG_TX1_ATTEN_1 : REG_TX2_ATTEN_1, buf, 2); + + if (ret < 0) + return ret; + + code = (buf[0] << 8) | buf[1]; + + code *= 250; + + return code; +} + +/** + * Mute TX. + * @param phy The AD9361 state structure. + * @param state The state - 0 mute; 1 - unmute. + * @return 0 in case of success + */ +int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state) +{ + int32_t ret; + + if (state) { + phy->tx1_atten_cached = ad9361_get_tx_atten(phy, 1); + phy->tx2_atten_cached = ad9361_get_tx_atten(phy, 2); + + return ad9361_set_tx_atten(phy, 89750, true, true, true); + } else { + if (phy->tx1_atten_cached == phy->tx2_atten_cached) + return ad9361_set_tx_atten(phy, phy->tx1_atten_cached, + true, true, true); + + ret = ad9361_set_tx_atten(phy, phy->tx1_atten_cached, + true, false, true); + ret |= ad9361_set_tx_atten(phy, phy->tx2_atten_cached, + false, true, true); + + return ret; + } +} + +/** + * Choose the right RF VCO table index for the selected frequency. + * @param freq The frequency value [Hz]. + * @return The index from the RF VCO table. + */ +static uint32_t ad9361_rfvco_tableindex(uint32_t freq) +{ + if (freq < 50000000UL) + return LUT_FTDD_40; + + if (freq <= 70000000UL) + return LUT_FTDD_60; + + return LUT_FTDD_80; +} + +/** + * Initialize the RFPLL VCO. + * @param phy The AD9361 state structure. + * @param tx Set true for TX_RFPLL. + * @param vco_freq The VCO frequency [Hz]. + * @param ref_clk The reference clock frequency [Hz]. + * @return 0 in case of success + */ +static int32_t ad9361_rfpll_vco_init(struct ad9361_rf_phy *phy, + bool tx, uint64_t vco_freq, + uint32_t ref_clk) +{ + struct spi_device *spi = phy->spi; + const struct SynthLUT(*tab); + int32_t i = 0; + uint32_t range, offs = 0; + + range = ad9361_rfvco_tableindex(ref_clk); + + dev_dbg(&phy->spi->dev, "%s : vco_freq %"PRIu64" : ref_clk %"PRIu32" : range %"PRIu32, + __func__, vco_freq, ref_clk, range); + + do_div(&vco_freq, 1000000UL); /* vco_freq in MHz */ + + if ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) + && (phy->current_tx_lo_freq != phy->current_rx_lo_freq)) { + tab = &SynthLUT_FDD[range][0]; + if (tx) + phy->current_tx_use_tdd_table = false; + else + phy->current_rx_use_tdd_table = false; + } else { + tab = &SynthLUT_TDD[range][0]; + if (tx) + phy->current_tx_use_tdd_table = true; + else + phy->current_rx_use_tdd_table = true; + } + + if (tx) + offs = REG_TX_VCO_OUTPUT - REG_RX_VCO_OUTPUT; + + while (i < SYNTH_LUT_SIZE && tab[i].VCO_MHz > vco_freq) + i++; + + dev_dbg(&phy->spi->dev, "%s : freq %d MHz : index %"PRId32, + __func__, tab[i].VCO_MHz, i); + + ad9361_spi_write(spi, REG_RX_VCO_OUTPUT + offs, + VCO_OUTPUT_LEVEL(tab[i].VCO_Output_Level) | + PORB_VCO_LOGIC); + ad9361_spi_writef(spi, REG_RX_ALC_VARACTOR + offs, + VCO_VARACTOR(~0), tab[i].VCO_Varactor); + ad9361_spi_write(spi, REG_RX_VCO_BIAS_1 + offs, + VCO_BIAS_REF(tab[i].VCO_Bias_Ref) | + VCO_BIAS_TCF(tab[i].VCO_Bias_Tcf)); + + ad9361_spi_write(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, + VCO_CAL_OFFSET(tab[i].VCO_Cal_Offset)); + ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, + VCO_VARACTOR_REFERENCE( + tab[i].VCO_Varactor_Reference)); + + ad9361_spi_write(spi, REG_RX_VCO_CAL_REF + offs, VCO_CAL_REF_TCF(0)); + + ad9361_spi_write(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, + VCO_VARACTOR_OFFSET(0) | + VCO_VARACTOR_REFERENCE_TCF(7)); + + ad9361_spi_writef(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0), + tab[i].Charge_Pump_Current); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_1 + offs, + LOOP_FILTER_C2(tab[i].LF_C2) | + LOOP_FILTER_C1(tab[i].LF_C1)); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_2 + offs, + LOOP_FILTER_R1(tab[i].LF_R1) | + LOOP_FILTER_C3(tab[i].LF_C3)); + ad9361_spi_write(spi, REG_RX_LOOP_FILTER_3 + offs, + LOOP_FILTER_R3(tab[i].LF_R3)); + + return 0; +} + +/** + * Get the current gain in Split Gain Table Mode + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success, + */ +static int32_t ad9361_get_split_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, +struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + uint32_t val, tbl_addr; + int32_t rc = 0; + + rx_gain->fgt_lmt_index = ad9361_spi_readf(spi, idx_reg, + FULL_TABLE_GAIN_INDEX(~0)); + tbl_addr = ad9361_spi_read(spi, REG_GAIN_TABLE_ADDRESS); + + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, rx_gain->fgt_lmt_index); + + val = ad9361_spi_read(spi, REG_GAIN_TABLE_READ_DATA1); + rx_gain->lna_index = TO_LNA_GAIN(val); + rx_gain->mixer_index = TO_MIXER_GM_GAIN(val); + + rx_gain->tia_index = ad9361_spi_readf(spi, REG_GAIN_TABLE_READ_DATA2, TIA_GAIN); + + rx_gain->lmt_gain = lna_table[phy->current_table][rx_gain->lna_index] + + mixer_table[phy->current_table][rx_gain->mixer_index] + + tia_table[rx_gain->tia_index]; + + ad9361_spi_write(spi, REG_GAIN_TABLE_ADDRESS, tbl_addr); + + /* Read LPF Index */ + rx_gain->lpf_gain = ad9361_spi_readf(spi, idx_reg + 1, LPF_GAIN_RX(~0)); + + /* Read Digital Gain */ + rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, + DIGITAL_GAIN_RX(~0)); + + rx_gain->gain_db = rx_gain->lmt_gain + rx_gain->lpf_gain + + rx_gain->digital_gain; + return rc; +} + +/** + * Get the current gain in Full Gain Table Mode + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success + */ +static int32_t ad9361_get_full_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, +struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + int32_t val; + enum rx_gain_table_name tbl; + struct rx_gain_info *gain_info; + int32_t rc = 0, rx_gain_db; + + tbl = ad9361_gt_tableindex( + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + + rx_gain->fgt_lmt_index = val = ad9361_spi_readf(spi, idx_reg, + FULL_TABLE_GAIN_INDEX(~0)); + gain_info = &phy->rx_gain[tbl]; + if (val > gain_info->idx_step_offset) { + val = val - gain_info->idx_step_offset; + rx_gain_db = gain_info->starting_gain_db + + ((val)* gain_info->gain_step_db); + } + else { + rx_gain_db = gain_info->starting_gain_db; + } + + /* Read Digital Gain */ + rx_gain->digital_gain = ad9361_spi_readf(spi, idx_reg + 2, + DIGITAL_GAIN_RX(~0)); + + rx_gain->gain_db = rx_gain_db; + + return rc; +} + +/** + * Get current RX gain for the selected channel. + * @param phy The AD9361 state structure. + * @param rx_id The desired channel number (0, 1). + * @param rx_gain A rf_rx_gain struct to store the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + uint32_t val, idx_reg; + uint8_t gain_ctl_shift, rx_enable_mask; + uint8_t fast_atk_shift; + int32_t rc = 0; + + if (rx_id == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + idx_reg = REG_GAIN_RX1; + rx_enable_mask = RX_CHANNEL_ENABLE(RX_1); + fast_atk_shift = RX1_FAST_ATK_SHIFT; + + } + else if (rx_id == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + idx_reg = REG_GAIN_RX2; + rx_enable_mask = RX_CHANNEL_ENABLE(RX_2); + fast_atk_shift = RX2_FAST_ATK_SHIFT; + } + else { + dev_err(dev, "Unknown Rx path %"PRIu32, rx_id); + rc = -EINVAL; + goto out; + } + + val = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, rx_enable_mask); + + if (!val) { + dev_dbg(dev, "Rx%"PRIu32" is not enabled", rx_gain->ant); + rc = -EAGAIN; + goto out; + } + + val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); + + val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; + + if (val == RX_GAIN_CTL_AGC_FAST_ATK) { + /* In fast attack mode check whether Fast attack state machine + * has locked gain, if not then we can not read gain. + */ + val = ad9361_spi_read(spi, REG_FAST_ATTACK_STATE); + val = (val >> fast_atk_shift) & FAST_ATK_MASK; + if (val != FAST_ATK_GAIN_LOCKED) { + dev_warn(dev, "Failed to read gain, state m/c at %"PRIx32, + val); + rc = -EAGAIN; + goto out; + } + } + + if (has_split_gt && phy->pdata->split_gt) + rc = ad9361_get_split_table_gain(phy, idx_reg, rx_gain); + else + rc = ad9361_get_full_table_gain(phy, idx_reg, rx_gain); + +out: + return rc; +} + +/** + * Force Enable State Machine (ENSM) to the desired state (internally used only). + * @param phy The AD9361 state structure. + * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT, + * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX, + * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH]. + * @return None. + */ +void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state) +{ + struct spi_device *spi = phy->spi; + uint8_t dev_ensm_state; + int32_t rc; + uint32_t val; + + dev_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); + + phy->prev_ensm_state = dev_ensm_state; + + if (dev_ensm_state == ensm_state) { + dev_dbg(dev, "Nothing to do, device is already in %d state", + ensm_state); + goto out; + } + + dev_dbg(dev, "Device is in %x state, forcing to %x", dev_ensm_state, + ensm_state); + + val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); + + /* Enable control through SPI writes, and take out from + * Alert + */ + if (val & ENABLE_ENSM_PIN_CTRL) { + val &= ~ENABLE_ENSM_PIN_CTRL; + phy->ensm_pin_ctl_en = true; + } + else { + phy->ensm_pin_ctl_en = false; + } + + if (dev_ensm_state) + val &= ~(TO_ALERT); + + switch (ensm_state) { + + case ENSM_STATE_TX: + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + break; + case ENSM_STATE_ALERT: + val &= ~(FORCE_TX_ON | FORCE_RX_ON); + val |= TO_ALERT | FORCE_ALERT_STATE; + break; + default: + dev_err(dev, "No handling for forcing %d ensm state", + ensm_state); + goto out; + } + + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) + dev_err(dev, "Failed to restore state"); + +out: + return; + +} + +/** + * Restore the previous Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @return None. + */ +void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy) +{ + struct spi_device *spi = phy->spi; + int32_t rc; + uint32_t val; + + val = ad9361_spi_read(spi, REG_ENSM_CONFIG_1); + + /* We are restoring state only, so clear State bits first + * which might have set while forcing a particular state + */ + val &= ~(FORCE_TX_ON | FORCE_RX_ON | + TO_ALERT | FORCE_ALERT_STATE); + + switch (phy->prev_ensm_state) { + + case ENSM_STATE_TX: + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + break; + case ENSM_STATE_ALERT: + val |= TO_ALERT; + break; + case ENSM_STATE_INVALID: + dev_dbg(dev, "No need to restore, ENSM state wasn't saved"); + goto out; + default: + dev_dbg(dev, "Could not restore to %d ENSM state", + phy->prev_ensm_state); + goto out; + } + + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) { + dev_err(dev, "Failed to write ENSM_CONFIG_1"); + goto out; + } + + if (phy->ensm_pin_ctl_en) { + val |= ENABLE_ENSM_PIN_CTRL; + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) + dev_err(dev, "Failed to write ENSM_CONFIG_1"); + } + +out: + return; +} + +/** + * Set gain in Split Gain Table Mode (used only in Manual Gain Control Mode). + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t set_split_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, +struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + int32_t rc = 0; + + if ((rx_gain->fgt_lmt_index > MAX_LMT_INDEX) || + (rx_gain->lpf_gain > MAX_LPF_GAIN) || + (rx_gain->digital_gain > MAX_DIG_GAIN)) { + dev_err(dev, "LMT_INDEX missing or greater than max value %d", + MAX_LMT_INDEX); + dev_err(dev, "LPF_GAIN missing or greater than max value %d", + MAX_LPF_GAIN); + dev_err(dev, "DIGITAL_GAIN cannot be more than %d", + MAX_DIG_GAIN); + rc = -EINVAL; + goto out; + } + if (rx_gain->gain_db > 0) + dev_dbg(dev, "Ignoring rx_gain value in split table mode."); + if (rx_gain->fgt_lmt_index == 0 && rx_gain->lpf_gain == 0 && + rx_gain->digital_gain == 0) { + dev_err(dev, + "In split table mode, All LMT/LPF/digital gains cannot be 0"); + rc = -EINVAL; + goto out; + } + + ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, rx_gain->fgt_lmt_index); + ad9361_spi_writef(spi, idx_reg + 1, RX_LPF_IDX_MASK, rx_gain->lpf_gain); + + if (phy->pdata->gain_ctrl.dig_gain_en) { + ad9361_spi_writef(spi, idx_reg + 2, RX_DIGITAL_IDX_MASK, rx_gain->digital_gain); + + } + else if (rx_gain->digital_gain > 0) { + dev_err(dev, "Digital gain is disabled and cannot be set"); + } +out: + return rc; +} + +/** + * Set gain in Full Gain Table Mode (used only in Manual Gain Control Mode). + * @param phy The AD9361 state structure. + * @param idx_reg Register base address for the selected receiver + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t set_full_table_gain(struct ad9361_rf_phy *phy, uint32_t idx_reg, +struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + enum rx_gain_table_name tbl; + struct rx_gain_info *gain_info; + uint32_t val; + int32_t rc = 0; + + if (rx_gain->fgt_lmt_index != (uint32_t)~0 || (int64_t)rx_gain->lpf_gain != (uint32_t)~0 || + rx_gain->digital_gain > 0) + dev_dbg(dev, + "Ignoring lmt/lpf/digital gains in Single Table mode"); + + tbl = ad9361_gt_tableindex( + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + + gain_info = &phy->rx_gain[tbl]; + if ((rx_gain->gain_db < gain_info->starting_gain_db) || + (rx_gain->gain_db > gain_info->max_gain_db)) { + + dev_err(dev, "Invalid gain %"PRId32", supported range [%"PRId32" - %"PRId32"]", + rx_gain->gain_db, gain_info->starting_gain_db, + gain_info->max_gain_db); + rc = -EINVAL; + goto out; + + } + + val = ((rx_gain->gain_db - gain_info->starting_gain_db) / + gain_info->gain_step_db) + gain_info->idx_step_offset; + ad9361_spi_writef(spi, idx_reg, RX_FULL_TBL_IDX_MASK, val); + +out: + return rc; +} + +/** + * Set the RX gain for the selected channel. + * @param phy The AD9361 state structure. + * @param rx_id The desired channel number (0, 1). + * @param rx_gain The rf_rx_gain struct containing the RF gain. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy, + uint32_t rx_id, struct rf_rx_gain *rx_gain) +{ + struct spi_device *spi = phy->spi; + uint32_t val, idx_reg; + uint8_t gain_ctl_shift; + int32_t rc = 0; + + if (rx_id == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + idx_reg = REG_RX1_MANUAL_LMT_FULL_GAIN; + + } + else if (rx_id == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + idx_reg = REG_RX2_MANUAL_LMT_FULL_GAIN; + } + else { + dev_err(dev, "Unknown Rx path %"PRIu32, rx_id); + rc = -EINVAL; + goto out; + + } + + val = ad9361_spi_read(spi, REG_AGC_CONFIG_1); + val = (val >> gain_ctl_shift) & RX_GAIN_CTL_MASK; + + if (val != RX_GAIN_CTL_MGC) { + dev_dbg(dev, "Rx gain can be set in MGC mode only"); + goto out; + } + + if (has_split_gt && phy->pdata->split_gt) + rc = set_split_table_gain(phy, idx_reg, rx_gain); + else + rc = set_full_table_gain(phy, idx_reg, rx_gain); + + if (rc) { + dev_err(dev, "Unable to write gain tbl idx reg: %"PRIu32, idx_reg); + goto out; + } + +out: + return rc; + +} + +/** + * Initialize the rx_gain_info structure. + * @param rx_gain The rx_gain_info structure pointer. + * @param type Either Full or Split Table + * @param starting_gain The starting gain value. + * @param max_gain The maximum gain value. + * @param gain_step The gain step. + * @param max_idx The max table size. + * @param idx_offset Offset in the table where linear progression starts + * @return None + */ +static void ad9361_init_gain_info(struct rx_gain_info *rx_gain, +enum rx_gain_table_type type, int32_t starting_gain, + int32_t max_gain, int32_t gain_step, int32_t max_idx, int32_t idx_offset) +{ + rx_gain->tbl_type = type; + rx_gain->starting_gain_db = starting_gain; + rx_gain->max_gain_db = max_gain; + rx_gain->gain_step_db = gain_step; + rx_gain->max_idx = max_idx; + rx_gain->idx_step_offset = idx_offset; +} + +/** + * Initialize the gain table information. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_init_gain_tables(struct ad9361_rf_phy *phy) +{ + struct rx_gain_info *rx_gain; + + /* Intialize Meta data according to default gain tables + * of AD9631. Changing/Writing of gain tables is not + * supported yet. + */ + rx_gain = &phy->rx_gain[TBL_200_1300_MHZ]; + ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, 1, 77, 1, + SIZE_FULL_TABLE, 0); + + rx_gain = &phy->rx_gain[TBL_1300_4000_MHZ]; + ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, -4, 71, 1, + SIZE_FULL_TABLE, 1); + + rx_gain = &phy->rx_gain[TBL_4000_6000_MHZ]; + ad9361_init_gain_info(rx_gain, RXGAIN_FULL_TBL, -10, 62, 1, + SIZE_FULL_TABLE, 4); + + return 0; +} + +/** + * Update the Gain Control. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gc_update(struct ad9361_rf_phy *phy) +{ + struct spi_device *spi = phy->spi; + uint32_t clkrf; + uint32_t reg, delay_lna, settling_delay, dec_pow_meas_dur; + int32_t ret; + uint32_t fir_div; + + clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]); + delay_lna = phy->pdata->elna_ctrl.settling_delay_ns; + + /* + * AGC Attack Delay (us)=ceiling((((0.2+Delay_LNA)*ClkRF+14))/(2*ClkRF))+1 + * ClkRF in MHz, delay in us + */ + + reg = (200 * delay_lna) / 2 + (14000000UL / (clkrf / 500U)); + reg = DIV_ROUND_UP(reg, 1000UL) + + phy->pdata->gain_ctrl.agc_attack_delay_extra_margin_us; + reg = clamp_t(uint8_t, reg, 0U, 31U); + ret = ad9361_spi_writef(spi, REG_AGC_ATTACK_DELAY, + AGC_ATTACK_DELAY(~0), reg); + + /* + * Peak Overload Wait Time (ClkRF cycles)=ceiling((0.1+Delay_LNA) *clkRF+1) + */ + + reg = (delay_lna + 100UL) * (clkrf / 1000UL); + reg = DIV_ROUND_UP(reg, 1000000UL) + 1; + reg = clamp_t(uint8_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_PEAK_WAIT_TIME, + PEAK_OVERLOAD_WAIT_TIME(~0), reg); + + /* + * Settling Delay in 0x111. Applies to all gain control modes: + * 0x111[D4:D0]= ceiling(((0.2+Delay_LNA)*clkRF + dodebug = false;+14)/2) + */ + + reg = (delay_lna + 200UL) * (clkrf / 2000UL); + reg = DIV_ROUND_UP(reg, 1000000UL) + 7; + reg = settling_delay = clamp_t(uint8_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + SETTLING_DELAY(~0), reg); + + /* + * Gain Update Counter [15:0]= round((((time*ClkRF-0x111[D4:D0]*2)-2))/2) + */ + reg = phy->pdata->gain_ctrl.gain_update_interval_us * (clkrf / 1000UL) - + settling_delay * 2000UL - 2000UL; + + reg = DIV_ROUND_CLOSEST(reg, 2000UL); + reg = clamp_t(uint32_t, reg, 0U, 131071UL); + + if (phy->agc_mode[0] == RF_GAIN_FASTATTACK_AGC || + phy->agc_mode[1] == RF_GAIN_FASTATTACK_AGC) { + dec_pow_meas_dur = + phy->pdata->gain_ctrl.f_agc_dec_pow_measuremnt_duration; + } else { + fir_div = DIV_ROUND_CLOSEST(clkrf, + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK])); + dec_pow_meas_dur = phy->pdata->gain_ctrl.dec_pow_measuremnt_duration; + + if (((reg * 2 / fir_div) / dec_pow_meas_dur) < 2) { + dec_pow_meas_dur = reg / fir_div; + } + } + + /* Power Measurement Duration */ + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + DEC_POWER_MEASUREMENT_DURATION(~0), + ilog2(dec_pow_meas_dur / 16)); + + + ret |= ad9361_spi_writef(spi, REG_DIGITAL_SAT_COUNTER, + DOUBLE_GAIN_COUNTER, reg > 65535); + + if (reg > 65535) + reg /= 2; + + ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER1, reg & 0xFF); + ret |= ad9361_spi_write(spi, REG_GAIN_UPDATE_COUNTER2, reg >> 8); + + /* + * Fast AGC State Wait Time - Energy Detect Count + */ + + reg = DIV_ROUND_CLOSEST(phy->pdata->gain_ctrl.f_agc_state_wait_time_ns * + (clkrf / 1000UL), 1000000UL); + reg = clamp_t(uint32_t, reg, 0U, 31U); + ret |= ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, + ENERGY_DETECT_COUNT(~0), reg); + + return ret; +} + +/** + * Set the gain control mode. + * @param phy The AD9361 state structure. + * @param rf_gain_ctrl A rf_gain_ctrl struct that contains the the desired + * channel information and the gain control mode. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy, + struct rf_gain_ctrl *gain_ctrl) +{ + struct spi_device *spi = phy->spi; + int32_t rc = 0; + uint32_t gain_ctl_shift, mode; + uint8_t val; + + rc = ad9361_spi_readm(spi, REG_AGC_CONFIG_1, &val, 1); + if (rc) { + dev_err(dev, "Unable to read AGC config1 register: %x", + REG_AGC_CONFIG_1); + goto out; + } + + switch (gain_ctrl->mode) { + case RF_GAIN_MGC: + mode = RX_GAIN_CTL_MGC; + break; + case RF_GAIN_FASTATTACK_AGC: + mode = RX_GAIN_CTL_AGC_FAST_ATK; + break; + case RF_GAIN_SLOWATTACK_AGC: + mode = RX_GAIN_CTL_AGC_SLOW_ATK; + break; + case RF_GAIN_HYBRID_AGC: + mode = RX_GAIN_CTL_AGC_SLOW_ATK_HYBD; + break; + default: + rc = -EINVAL; + goto out; + } + + if (gain_ctrl->ant == 1) { + gain_ctl_shift = RX1_GAIN_CTRL_SHIFT; + } + else if (gain_ctrl->ant == 2) { + gain_ctl_shift = RX2_GAIN_CTRL_SHIFT; + } + else { + dev_err(dev, "Unknown Rx path %"PRIu32, gain_ctrl->ant); + rc = -EINVAL; + goto out; + } + + rc = ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_DISABLE); + if (rc) { + dev_err(dev, "Unable to disable rx%"PRIu32, gain_ctrl->ant); + goto out; + } + + val &= ~(RX_GAIN_CTL_MASK << gain_ctl_shift); + val |= mode << gain_ctl_shift; + if (mode == RX_GAIN_CTL_AGC_SLOW_ATK_HYBD) + val |= SLOW_ATTACK_HYBRID_MODE; + else + val &= ~SLOW_ATTACK_HYBRID_MODE; + + rc = ad9361_spi_write(spi, REG_AGC_CONFIG_1, val); + if (rc) { + dev_err(dev, "Unable to write AGC config1 register: %x", + REG_AGC_CONFIG_1); + goto out; + } + + ad9361_en_dis_rx(phy, gain_ctrl->ant, RX_ENABLE); + rc = ad9361_gc_update(phy); +out: + return rc; +} + +/** + * Get the RSSI. + * @param phy The AD9361 state structure. + * @param rssi A rf_rssi struct to store the RSSI. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi) +{ + struct spi_device *spi = phy->spi; + uint8_t reg_val_buf[6]; + int32_t rc; + + rc = ad9361_spi_readm(spi, REG_PREAMBLE_LSB, + reg_val_buf, ARRAY_SIZE(reg_val_buf)); + if (rssi->ant == 1) { + rssi->symbol = RSSI_RESOLUTION * + ((reg_val_buf[5] << RSSI_LSB_SHIFT) + + (reg_val_buf[1] & RSSI_LSB_MASK1)); + rssi->preamble = RSSI_RESOLUTION * + ((reg_val_buf[4] << RSSI_LSB_SHIFT) + + (reg_val_buf[0] & RSSI_LSB_MASK1)); + } + else if (rssi->ant == 2) { + rssi->symbol = RSSI_RESOLUTION * + ((reg_val_buf[3] << RSSI_LSB_SHIFT) + + ((reg_val_buf[1] & RSSI_LSB_MASK2) >> 1)); + rssi->preamble = RSSI_RESOLUTION * + ((reg_val_buf[2] << RSSI_LSB_SHIFT) + + ((reg_val_buf[0] & RSSI_LSB_MASK2) >> 1)); + } + else + rc = -EFAULT; + + rssi->multiplier = RSSI_MULTIPLIER; + + return rc; +} + +/** + * Setup the RX ADC. + * @param phy The AD9361 state structure. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @param adc_sampl_freq_Hz The ADC sampling frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_adc_setup(struct ad9361_rf_phy *phy, uint32_t bbpll_freq, + uint32_t adc_sampl_freq_Hz) +{ + + uint32_t scale_snr_1e3, maxsnr, sqrt_inv_rc_tconst_1e3, tmp_1e3, + scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3, sqrt_term_1e3, + min_sqrt_term_1e3, bb_bw_Hz; + uint64_t tmp, invrc_tconst_1e6; + uint8_t data[40]; + uint32_t i; + int32_t ret; + + uint8_t c3_msb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); + uint8_t c3_lsb = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); + uint8_t r2346 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); + + /* + * BBBW = (BBPLL / RxTuneDiv) * ln(2) / (1.4 * 2PI ) + * We assume ad9361_rx_bb_analog_filter_calib() is always run prior + */ + + tmp = bbpll_freq * 10000ULL; + do_div(&tmp, 126906UL * phy->rxbbf_div); + bb_bw_Hz = tmp; + + dev_dbg(&phy->spi->dev, "%s : BBBW %"PRIu32" : ADCfreq %"PRIu32, + __func__, bb_bw_Hz, adc_sampl_freq_Hz); + + dev_dbg(&phy->spi->dev, "c3_msb 0x%X : c3_lsb 0x%X : r2346 0x%X : ", + c3_msb, c3_lsb, r2346); + + bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 28000000UL); + + if (adc_sampl_freq_Hz < 80000000) + scale_snr_1e3 = 1000; + else + scale_snr_1e3 = 1585; /* pow(10, scale_snr_dB/10); */ + + if (bb_bw_Hz >= 18000000) { + invrc_tconst_1e6 = (160975ULL * r2346 * + (160 * c3_msb + 10 * c3_lsb + 140) * + (bb_bw_Hz)* (1000 + (10 * (bb_bw_Hz - 18000000) / 1000000))); + + do_div(&invrc_tconst_1e6, 1000UL); + + } + else { + invrc_tconst_1e6 = (160975ULL * r2346 * + (160 * c3_msb + 10 * c3_lsb + 140) * + (bb_bw_Hz)); + } + + do_div(&invrc_tconst_1e6, 1000000000UL); + + if (invrc_tconst_1e6 > ULONG_MAX) + dev_err(&phy->spi->dev, "invrc_tconst_1e6 > ULONG_MAX"); + + sqrt_inv_rc_tconst_1e3 = int_sqrt((uint32_t)invrc_tconst_1e6); + maxsnr = 640 / 160; + scaled_adc_clk_1e6 = DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 640); + inv_scaled_adc_clk_1e3 = DIV_ROUND_CLOSEST(640000000, + DIV_ROUND_CLOSEST(adc_sampl_freq_Hz, 1000)); + tmp_1e3 = DIV_ROUND_CLOSEST(980000 + 20 * max_t(uint32_t, 1000U, + DIV_ROUND_CLOSEST(inv_scaled_adc_clk_1e3, maxsnr)), 1000); + sqrt_term_1e3 = int_sqrt(scaled_adc_clk_1e6); + min_sqrt_term_1e3 = min_t(uint32_t, 1000U, + int_sqrt(maxsnr * scaled_adc_clk_1e6)); + + dev_dbg(&phy->spi->dev, "invrc_tconst_1e6 %"PRIu64", sqrt_inv_rc_tconst_1e3 %"PRIu32, + invrc_tconst_1e6, sqrt_inv_rc_tconst_1e3); + dev_dbg(&phy->spi->dev, "scaled_adc_clk_1e6 %"PRIu32", inv_scaled_adc_clk_1e3 %"PRIu32, + scaled_adc_clk_1e6, inv_scaled_adc_clk_1e3); + dev_dbg(&phy->spi->dev, "tmp_1e3 %"PRIu32", sqrt_term_1e3 %"PRIu32", min_sqrt_term_1e3 %"PRIu32, + tmp_1e3, sqrt_term_1e3, min_sqrt_term_1e3); + + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 0x24; + data[4] = 0x24; + data[5] = 0; + data[6] = 0; + + tmp = (uint64_t)(-50000000 + (int64_t)(8ULL * scale_snr_1e3 * sqrt_inv_rc_tconst_1e3 * + min_sqrt_term_1e3)); + do_div(&tmp, 100000000UL); + data[7] = min_t(uint64_t, 124U, tmp); + + tmp = (invrc_tconst_1e6 >> 1) + 20 * inv_scaled_adc_clk_1e3 * + data[7] / 80 * 1000ULL; + do_div(&tmp, invrc_tconst_1e6); + data[8] = min_t(uint64_t, 255U, tmp); + + tmp = (uint64_t)(-500000 + (int64_t)(77ULL * sqrt_inv_rc_tconst_1e3 * min_sqrt_term_1e3)); + do_div(&tmp, 1000000UL); + data[10] = min_t(uint64_t, 127U, tmp); + + data[9] = min_t(uint32_t, 127U, ((800 * data[10]) / 1000)); + tmp = ((invrc_tconst_1e6 >> 1) + (20 * inv_scaled_adc_clk_1e3 * + data[10] * 1000ULL)); + do_div(&tmp, invrc_tconst_1e6 * 77); + data[11] = min_t(uint64_t, 255U, tmp); + data[12] = min_t(uint32_t, 127U, (uint64_t)((-500000 + (int64_t)(80 * sqrt_inv_rc_tconst_1e3 * + min_sqrt_term_1e3))) / 1000000UL); + + tmp = -3 * (long)(invrc_tconst_1e6 >> 1) + inv_scaled_adc_clk_1e3 * + data[12] * (1000ULL * 20 / 80); + do_div(&tmp, invrc_tconst_1e6); + data[13] = min_t(uint64_t, 255, tmp); + + data[14] = 21 * (inv_scaled_adc_clk_1e3 / 10000); + data[15] = min_t(uint32_t, 127U, (500 + 1025 * data[7]) / 1000); + data[16] = min_t(uint32_t, 127U, (data[15] * tmp_1e3) / 1000); + data[17] = data[15]; + data[18] = min_t(uint32_t, 127U, (500 + 975 * data[10]) / 1000); + data[19] = min_t(uint32_t, 127U, (data[18] * tmp_1e3) / 1000); + data[20] = data[18]; + data[21] = min_t(uint32_t, 127U, (500 + 975 * data[12]) / 1000); + data[22] = min_t(uint32_t, 127, (data[21] * tmp_1e3) / 1000); + data[23] = data[21]; + data[24] = 0x2E; + data[25] = (128 + min_t(uint32_t, 63000U, DIV_ROUND_CLOSEST(63 * + scaled_adc_clk_1e6, 1000)) / 1000); + data[26] = min_t(uint32_t, 63U, 63 * scaled_adc_clk_1e6 / 1000000 * + (920 + 80 * inv_scaled_adc_clk_1e3 / 1000) / 1000); + data[27] = min_t(uint32_t, 63, (32 * sqrt_term_1e3) / 1000); + data[28] = data[25]; + data[29] = data[26]; + data[30] = data[27]; + data[31] = data[25]; + data[32] = data[26]; + data[33] = min_t(uint32_t, 63U, 63 * sqrt_term_1e3 / 1000); + data[34] = min_t(uint32_t, 127U, 64 * sqrt_term_1e3 / 1000); + data[35] = 0x40; + data[36] = 0x40; + data[37] = 0x2C; + data[38] = 0x00; + data[39] = 0x00; + + for (i = 0; i < 40; i++) { + ret = ad9361_spi_write(phy->spi, 0x200 + i, data[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +/** + * Perform a RX TIA calibration. + * @param phy The AD9361 state structure. + * @param bb_bw_Hz The baseband bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_tia_calib(struct ad9361_rf_phy *phy, uint32_t bb_bw_Hz) +{ + uint32_t Cbbf, R2346; + uint64_t CTIA_fF; + + uint8_t reg1EB = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_MSB); + uint8_t reg1EC = ad9361_spi_read(phy->spi, REG_RX_BBF_C3_LSB); + uint8_t reg1E6 = ad9361_spi_read(phy->spi, REG_RX_BBF_R2346); + uint8_t reg1DB, reg1DF, reg1DD, reg1DC, reg1DE, temp; + + dev_dbg(&phy->spi->dev, "%s : bb_bw_Hz %"PRIu32, + __func__, bb_bw_Hz); + + bb_bw_Hz = clamp(bb_bw_Hz, 200000UL, 20000000UL); + + Cbbf = (reg1EB * 160) + (reg1EC * 10) + 140; /* fF */ + R2346 = 18300 * RX_BBF_R2346(reg1E6); + + CTIA_fF = Cbbf * R2346 * 560ULL; + do_div(&CTIA_fF, 3500000UL); + + if (bb_bw_Hz <= 3000000UL) + reg1DB = 0xE0; + else if (bb_bw_Hz <= 10000000UL) + reg1DB = 0x60; + else + reg1DB = 0x20; + + if (CTIA_fF > 2920ULL) { + reg1DC = 0x40; + reg1DE = 0x40; + temp = min(127U, DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 320U)); + reg1DD = temp; + reg1DF = temp; + } + else { + temp = DIV_ROUND_CLOSEST((uint32_t)CTIA_fF - 400, 40U) + 0x40; + reg1DC = temp; + reg1DE = temp; + reg1DD = 0; + reg1DF = 0; + } + + ad9361_spi_write(phy->spi, REG_RX_TIA_CONFIG, reg1DB); + ad9361_spi_write(phy->spi, REG_TIA1_C_LSB, reg1DC); + ad9361_spi_write(phy->spi, REG_TIA1_C_MSB, reg1DD); + ad9361_spi_write(phy->spi, REG_TIA2_C_LSB, reg1DE); + ad9361_spi_write(phy->spi, REG_TIA2_C_MSB, reg1DF); + + return 0; +} + +/** + * Perform a baseband RX analog filter calibration. + * @param phy The AD9361 state structure. + * @param rx_bb_bw The baseband bandwidth [Hz]. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, + uint32_t rx_bb_bw, + uint32_t bbpll_freq) +{ + uint32_t target; + uint8_t tmp; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s : rx_bb_bw %"PRIu32" bbpll_freq %"PRIu32, + __func__, rx_bb_bw, bbpll_freq); + + rx_bb_bw = clamp(rx_bb_bw, 200000UL, 28000000UL); + + /* 1.4 * BBBW * 2PI / ln(2) */ + target = 126906UL * (rx_bb_bw / 10000UL); + phy->rxbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target)); + + /* Set RX baseband filter divide value */ + ad9361_spi_write(phy->spi, REG_RX_BBF_TUNE_DIVIDE, phy->rxbbf_div); + ad9361_spi_writef(phy->spi, REG_RX_BBF_TUNE_CONFIG, BIT(0), phy->rxbbf_div >> 8); + + /* Write the BBBW into registers 0x1FB and 0x1FC */ + ad9361_spi_write(phy->spi, REG_RX_BBBW_MHZ, rx_bb_bw / 1000000UL); + + tmp = DIV_ROUND_CLOSEST((rx_bb_bw % 1000000UL) * 128, 1000000UL); + ad9361_spi_write(phy->spi, REG_RX_BBBW_KHZ, min_t(uint8_t, 127, tmp)); + + ad9361_spi_write(phy->spi, REG_RX_MIX_LO_CM, RX_MIX_LO_CM(0x3F)); /* Set Rx Mix LO CM */ + ad9361_spi_write(phy->spi, REG_RX_MIX_GM_CONFIG, RX_MIX_GM_PLOAD(3)); /* Set GM common mode */ + + /* Enable the RX BBF tune circuit by writing 0x1E2=0x02 and 0x1E3=0x02 */ + ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, RX1_TUNE_RESAMPLE); + ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, RX2_TUNE_RESAMPLE); + + /* Start the RX Baseband Filter calibration in register 0x016[7] */ + /* Calibration is complete when register 0x016[7] self clears */ + ret = ad9361_run_calibration(phy, RX_BB_TUNE_CAL); + + /* Disable the RX baseband filter tune circuit, write 0x1E2=3, 0x1E3=3 */ + ad9361_spi_write(phy->spi, REG_RX1_TUNE_CTRL, + RX1_TUNE_RESAMPLE | RX1_PD_TUNE); + ad9361_spi_write(phy->spi, REG_RX2_TUNE_CTRL, + RX2_TUNE_RESAMPLE | RX2_PD_TUNE); + + return ret; +} + +/** + * Perform a baseband TX analog filter calibration. + * @param phy The AD9361 state structure. + * @param tx_bb_bw The baseband bandwidth [Hz]. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_bb_analog_filter_calib(struct ad9361_rf_phy *phy, + uint32_t tx_bb_bw, + uint32_t bbpll_freq) +{ + uint32_t target, txbbf_div; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32" bbpll_freq %"PRIu32, + __func__, tx_bb_bw, bbpll_freq); + + tx_bb_bw = clamp(tx_bb_bw, 625000UL, 20000000UL); + + /* 1.6 * BBBW * 2PI / ln(2) */ + target = 145036 * (tx_bb_bw / 10000UL); + txbbf_div = min_t(uint32_t, 511UL, DIV_ROUND_UP(bbpll_freq, target)); + + /* Set TX baseband filter divide value */ + ad9361_spi_write(phy->spi, REG_TX_BBF_TUNE_DIVIDER, txbbf_div); + ad9361_spi_writef(phy->spi, REG_TX_BBF_TUNE_MODE, + TX_BBF_TUNE_DIVIDER, txbbf_div >> 8); + + /* Enable the TX baseband filter tune circuit by setting 0x0CA=0x22. */ + ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, TUNER_RESAMPLE | TUNE_CTRL(1)); + + /* Start the TX Baseband Filter calibration in register 0x016[6] */ + /* Calibration is complete when register 0x016[] self clears */ + ret = ad9361_run_calibration(phy, TX_BB_TUNE_CAL); + + /* Disable the TX baseband filter tune circuit by writing 0x0CA=0x26. */ + ad9361_spi_write(phy->spi, REG_TX_TUNE_CTRL, + TUNER_RESAMPLE | TUNE_CTRL(1) | PD_TUNE); + + return ret; +} + +/** + * Perform a baseband TX secondary filter calibration. + * @param phy The AD9361 state structure. + * @param tx_rf_bw The RF bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_bb_second_filter_calib(struct ad9361_rf_phy *phy, + uint32_t tx_bb_bw) +{ + uint64_t cap; + uint32_t corner, res, div; + uint32_t reg_conf, reg_res; + int32_t ret, i; + + dev_dbg(&phy->spi->dev, "%s : tx_bb_bw %"PRIu32, + __func__, tx_bb_bw); + + tx_bb_bw = clamp(tx_bb_bw, 530000UL, 20000000UL); + + /* BBBW * 5PI */ + corner = 15708 * (tx_bb_bw / 10000UL); + + for (i = 0, res = 1; i < 4; i++) { + div = corner * res; + cap = (500000000ULL) + (div >> 1); + do_div(&cap, div); + cap -= 12ULL; + if (cap < 64ULL) + break; + + res <<= 1; + } + + if (cap > 63ULL) + cap = 63ULL; + + if (tx_bb_bw <= 4500000UL) + reg_conf = 0x59; + else if (tx_bb_bw <= 12000000UL) + reg_conf = 0x56; + else + reg_conf = 0x57; + + switch (res) { + case 1: + reg_res = 0x0C; + break; + case 2: + reg_res = 0x04; + break; + case 4: + reg_res = 0x03; + break; + case 8: + reg_res = 0x01; + break; + default: + reg_res = 0x01; + } + + ret = ad9361_spi_write(phy->spi, REG_CONFIG0, reg_conf); + ret |= ad9361_spi_write(phy->spi, REG_RESISTOR, reg_res); + ret |= ad9361_spi_write(phy->spi, REG_CAPACITOR, (uint8_t)cap); + + return ret; +} + +/** + * Perform a RF synthesizer charge pump calibration. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The reference clock rate [Hz]. + * @param tx The Synthesizer TX = 1, RX = 0. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txrx_synth_cp_calib(struct ad9361_rf_phy *phy, + uint32_t ref_clk_hz, bool tx) +{ + uint32_t offs = tx ? 0x40 : 0; + uint32_t vco_cal_cnt; + dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32" : is_tx %d", + __func__, ref_clk_hz, tx); + + /* REVIST: */ + ad9361_spi_write(phy->spi, REG_RX_CP_LEVEL_DETECT + offs, 0x17); + + ad9361_spi_write(phy->spi, REG_RX_DSM_SETUP_1 + offs, 0x0); + + ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE + offs, 0x00); + ad9361_spi_write(phy->spi, REG_RX_VCO_LDO + offs, 0x0B); + ad9361_spi_write(phy->spi, REG_RX_VCO_PD_OVERRIDES + offs, 0x02); + ad9361_spi_write(phy->spi, REG_RX_CP_CURRENT + offs, 0x80); + ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, 0x00); + + /* see Table 70 Example Calibration Times for RF VCO Cal */ + if (phy->pdata->fdd) { + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(3) | FB_CLOCK_ADV(2); + } + else { + if (ref_clk_hz > 40000000UL) + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(1) | + FB_CLOCK_ADV(2); + else + vco_cal_cnt = VCO_CAL_EN | VCO_CAL_COUNT(0) | + FB_CLOCK_ADV(2); + } + + ad9361_spi_write(phy->spi, REG_RX_VCO_CAL + offs, vco_cal_cnt); + + /* Enable FDD mode during calibrations */ + + if (!phy->pdata->fdd) { + ad9361_spi_writef(phy->spi, REG_PARALLEL_PORT_CONF_3, + HALF_DUPLEX_MODE, 0); + } + + ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, DUAL_SYNTH_MODE); + ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_1, + FORCE_ALERT_STATE | + TO_ALERT); + ad9361_spi_write(phy->spi, REG_ENSM_MODE, FDD_MODE); + + ad9361_spi_write(phy->spi, REG_RX_CP_CONFIG + offs, CP_CAL_ENABLE); + + return ad9361_check_cal_done(phy, REG_RX_CAL_STATUS + offs, + CP_CAL_VALID, 1); +} + +/** + * Perform a baseband DC offset calibration. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_bb_dc_offset_calib(struct ad9361_rf_phy *phy) +{ + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_COUNT, 0x3F); + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_SHIFT, BB_DC_M_SHIFT(0xF)); + ad9361_spi_write(phy->spi, REG_BB_DC_OFFSET_ATTEN, BB_DC_OFFSET_ATTEN(1)); + + return ad9361_run_calibration(phy, BBDC_CAL); +} + +/** + * Perform a RF DC offset calibration. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The RX LO frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rf_dc_offset_calib(struct ad9361_rf_phy *phy, + uint64_t rx_freq) +{ + struct spi_device *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s : rx_freq %"PRIu64, + __func__, rx_freq); + + ad9361_spi_write(spi, REG_WAIT_COUNT, 0x20); + + if (rx_freq <= 4000000000ULL) { + ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, + phy->pdata->rf_dc_offset_count_low); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, + RF_DC_CALIBRATION_COUNT(4) | DAC_FS(2)); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, + RF_DC_OFFSET_ATTEN( + phy->pdata->dc_offset_attenuation_low)); + } + else { + ad9361_spi_write(spi, REG_RF_DC_OFFSET_COUNT, + phy->pdata->rf_dc_offset_count_high); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_CONFIG_1, + RF_DC_CALIBRATION_COUNT(4) | DAC_FS(3)); + ad9361_spi_write(spi, REG_RF_DC_OFFSET_ATTEN, + RF_DC_OFFSET_ATTEN( + phy->pdata->dc_offset_attenuation_high)); + } + + ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, + USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | + DC_OFFSET_UPDATE(3)); + + if (phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD); + } else { + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD | + INVERT_RX2_RF_DC_CGOUT_WORD); + } + + return ad9361_run_calibration(phy, RFDC_CAL); +} + +/** + * Update RF bandwidth. + * @param phy The AD9361 state structure. + * @param rf_rx_bw RF RX bandwidth [Hz]. + * @param rf_tx_bw RF TX bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, + uint32_t rf_rx_bw, uint32_t rf_tx_bw) +{ + uint32_t real_rx_bandwidth = rf_rx_bw / 2; + uint32_t real_tx_bandwidth = rf_tx_bw / 2; + uint32_t bbpll_freq; + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32, + __func__, rf_rx_bw, rf_tx_bw); + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + + ret = ad9361_rx_bb_analog_filter_calib(phy, + real_rx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_analog_filter_calib(phy, + real_tx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_rx_adc_setup(phy, + bbpll_freq, + clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + if (ret < 0) + return ret; + + return 0; +} + +/** + * TX Quad Calib. + * @param phy The AD9361 state structure. + * @param phase phase + * @param rxnco_word Rx NCO word. + * @param decim decim + * @param res res + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t __ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, uint32_t phase, + uint32_t rxnco_word, uint32_t decim, uint8_t *res) +{ + int32_t ret; + + ad9361_spi_write(phy->spi, REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET, + RX_NCO_FREQ(rxnco_word) | RX_NCO_PHASE_OFFSET(phase)); + ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, + SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | QUAD_CAL_SOFT_RESET | + GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); + ad9361_spi_write(phy->spi, REG_QUAD_CAL_CTRL, + SETTLE_MAIN_ENABLE | DC_OFFSET_ENABLE | + GAIN_ENABLE | PHASE_ENABLE | M_DECIM(decim)); + + ret = ad9361_run_calibration(phy, TX_QUAD_CAL); + if (ret < 0) + return ret; + + if (res) + *res = ad9361_spi_read(phy->spi, + (phy->pdata->rx1tx1_mode_use_tx_num == 2) ? + REG_QUAD_CAL_STATUS_TX2 : REG_QUAD_CAL_STATUS_TX1) & + (TX1_LO_CONV | TX1_SSB_CONV); + + return 0; +} + +/** + * Loop through all possible phase offsets in case the QUAD CAL doesn't converge. + * @param phy The AD9361 state structure. + * @param rxnco_word Rx NCO word. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_quad_phase_search(struct ad9361_rf_phy *phy, uint32_t rxnco_word, uint8_t decim) +{ + int32_t i, ret; + uint8_t field[64], val; + uint32_t start; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + for (i = 0; i < (int64_t)(ARRAY_SIZE(field) / 2); i++) { + ret = __ad9361_tx_quad_calib(phy, i, rxnco_word, decim, &val); + if (ret < 0) + return ret; + + /* Handle 360/0 wrap around */ + field[i] = field[i + 32] = !((val & TX1_LO_CONV) && (val & TX1_SSB_CONV)); + } + + ret = ad9361_find_opt(field, ARRAY_SIZE(field), &start); + + phy->last_tx_quad_cal_phase = (start + ret / 2) & 0x1F; + +#ifdef _DEBUG + for (i = 0; i < 64; i++) { + printk("%c", (field[i] ? '#' : 'o')); + } +#ifdef WIN32 + printk(" RX_NCO_PHASE_OFFSET(%d, 0x%X) \n", phy->last_tx_quad_cal_phase, + phy->last_tx_quad_cal_phase); +#else + printk(" RX_NCO_PHASE_OFFSET(%"PRIu32", 0x%"PRIX32") \n", phy->last_tx_quad_cal_phase, + phy->last_tx_quad_cal_phase); +#endif +#endif + + ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase, rxnco_word, decim, NULL); + if (ret < 0) + return ret; + + return 0; +} + +/** + * Perform a TX quadrature calibration. + * @param phy The AD9361 state structure. + * @param bw The bandwidth [Hz]. + * @param rx_phase The optional RX phase value overwrite (set to zero). + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_tx_quad_calib(struct ad9361_rf_phy *phy, + uint32_t bw_rx, uint32_t bw_tx, + int32_t rx_phase) +{ + struct spi_device *spi = phy->spi; + uint32_t clktf, clkrf; + int32_t txnco_word, rxnco_word, txnco_freq, ret; + uint8_t __rx_phase = 0, reg_inv_bits = 0, val, decim; + const uint8_t(*tab)[3]; + uint32_t index_max, i, lpf_tia_mask; + + /* + * Find NCO frequency that matches this equation: + * BW / 4 = Rx NCO freq = Tx NCO freq: + * Rx NCO = ClkRF * (rxNCO <1:0> + 1) / 32 + * Tx NCO = ClkTF * (txNCO <1:0> + 1) / 32 + */ + + clkrf = clk_get_rate(phy, phy->ref_clk_scale[CLKRF_CLK]); + clktf = clk_get_rate(phy, phy->ref_clk_scale[CLKTF_CLK]); + + dev_dbg(&phy->spi->dev, "%s : bw_tx %"PRIu32" clkrf %"PRIu32" clktf %"PRIu32, + __func__, bw_tx, clkrf, clktf); + + txnco_word = DIV_ROUND_CLOSEST(bw_tx * 8, clktf) - 1; + txnco_word = clamp_t(int, txnco_word, 0, 3); + rxnco_word = txnco_word; + + dev_dbg(dev, "Tx NCO frequency: %"PRIu32" (BW/4: %"PRIu32") txnco_word %"PRId32, + clktf * (txnco_word + 1) / 32, bw_tx / 4, txnco_word); + + if (clktf <= 4000000UL) + decim = 2; + else + decim = 3; + + if (clkrf == (2 * clktf)) { + __rx_phase = 0x0E; + switch (txnco_word) { + case 0: + txnco_word++; + break; + case 1: + rxnco_word--; + break; + case 2: + rxnco_word -= 2; + txnco_word--; + break; + case 3: + rxnco_word -= 2; /* REVISIT */ + __rx_phase = 0x08; + break; + } + } + else if (clkrf == clktf) { + switch (txnco_word) { + case 0: + case 3: + __rx_phase = 0x15; + break; + case 2: + __rx_phase = 0x1F; + break; + case 1: + if (ad9361_spi_readf(spi, + REG_TX_ENABLE_FILTER_CTRL, 0x3F) == 0x22) + __rx_phase = 0x15; /* REVISIT */ + else + __rx_phase = 0x1A; + break; + } + } + else + dev_err(dev, "Unhandled case in %s line %d clkrf %"PRIu32" clktf %"PRIu32, + __func__, __LINE__, clkrf, clktf); + + + if (rx_phase >= 0) + __rx_phase = rx_phase; + + txnco_freq = clktf * (txnco_word + 1) / 32; + + if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) { + /* Make sure the BW during calibration is wide enough */ + ret = __ad9361_update_rf_bandwidth(phy, txnco_freq * 8, txnco_freq * 8); + if (ret < 0) + return ret; + } + + if (phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { + + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 0); + + reg_inv_bits = ad9361_spi_read(spi, REG_INVERT_BITS); + + ad9361_spi_write(spi, REG_INVERT_BITS, + INVERT_RX1_RF_DC_CGOUT_WORD | + INVERT_RX2_RF_DC_CGOUT_WORD); + } + + ad9361_spi_writef(spi, REG_KEXP_2, TX_NCO_FREQ(~0), txnco_word); + ad9361_spi_write(spi, REG_QUAD_CAL_COUNT, 0xFF); + ad9361_spi_write(spi, REG_KEXP_1, KEXP_TX(1) | KEXP_TX_COMP(3) | + KEXP_DC_I(3) | KEXP_DC_Q(3)); + ad9361_spi_write(spi, REG_MAG_FTEST_THRESH, 0x01); + ad9361_spi_write(spi, REG_MAG_FTEST_THRESH_2, 0x01); + + if (has_split_gt && phy->pdata->split_gt) { + tab = &split_gain_table[phy->current_table][0]; + index_max = SIZE_SPLIT_TABLE; + lpf_tia_mask = 0x20; + } + else { + tab = &full_gain_table[phy->current_table][0]; + index_max = SIZE_FULL_TABLE; + lpf_tia_mask = 0x3F; + } + + for (i = 0; i < index_max; i++) + if ((tab[i][1] & lpf_tia_mask) == 0x20) { + ad9361_spi_write(spi, REG_TX_QUAD_FULL_LMT_GAIN, i); + break; + } + + if (i >= index_max) + dev_err(dev, "failed to find suitable LPF TIA value in gain table"); + + ad9361_spi_write(spi, REG_QUAD_SETTLE_COUNT, 0xF0); + ad9361_spi_write(spi, REG_TX_QUAD_LPF_GAIN, 0x00); + + ret = __ad9361_tx_quad_calib(phy, __rx_phase, rxnco_word, decim, &val); + + dev_dbg(dev, "LO leakage: %d Quadrature Calibration: %d : rx_phase %d", + !!(val & TX1_LO_CONV), !!(val & TX1_SSB_CONV), __rx_phase); + + /* Calibration failed -> try last phase offset */ + if (val != (TX1_LO_CONV | TX1_SSB_CONV)) { + if (phy->last_tx_quad_cal_phase < 31) + ret = __ad9361_tx_quad_calib(phy, phy->last_tx_quad_cal_phase, + rxnco_word, decim, &val); + } else { + phy->last_tx_quad_cal_phase = __rx_phase; + } + + /* Calibration failed -> loop through all 32 phase offsets */ + if (val != (TX1_LO_CONV | TX1_SSB_CONV)) + ret = ad9361_tx_quad_phase_search(phy, rxnco_word, decim); + + if (phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); + ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits); + } + + if (phy->pdata->rx1rx2_phase_inversion_en || + (phy->pdata->port_ctrl.pp_conf[1] & INVERT_RX2)) { + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); + ad9361_spi_write(spi, REG_INVERT_BITS, reg_inv_bits); + } + + if (txnco_freq > (int64_t)(bw_rx / 4) || txnco_freq > (int64_t)(bw_tx / 4)) { + __ad9361_update_rf_bandwidth(phy, + phy->current_rx_bw_Hz, + phy->current_tx_bw_Hz); + } + + return ret; +} + +/** + * Setup RX tracking calibrations. + * @param phy The AD9361 state structure. + * @param bbdc_track Set true, will enable the BBDC tracking. + * @param rfdc_track Set true, will enable the RFDC tracking. + * @param rxquad_track Set true, will enable the RXQUAD tracking. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track, + bool rfdc_track, bool rxquad_track) +{ + struct spi_device *spi = phy->spi; + uint32_t qtrack = 0; + + dev_dbg(&spi->dev, "%s : bbdc_track=%d, rfdc_track=%d, rxquad_track=%d", + __func__, bbdc_track, rfdc_track, rxquad_track); + + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_2, + CALIBRATION_CONFIG2_DFLT | K_EXP_PHASE(0x15)); + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_3, + PREVENT_POS_LOOP_GAIN | K_EXP_AMPLITUDE(0x15)); + + ad9361_spi_write(spi, REG_DC_OFFSET_CONFIG2, + USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL | + DC_OFFSET_UPDATE(phy->pdata->dc_offset_update_events) | + (bbdc_track ? ENABLE_BB_DC_OFFSET_TRACKING : 0) | + (rfdc_track ? ENABLE_RF_OFFSET_TRACKING : 0)); + + ad9361_spi_writef(spi, REG_RX_QUAD_GAIN2, + CORRECTION_WORD_DECIMATION_M(~0), + phy->pdata->qec_tracking_slow_mode_en ? 4 : 0); + + if (rxquad_track) { + if (phy->pdata->rx2tx2) + qtrack = ENABLE_TRACKING_MODE_CH1 | ENABLE_TRACKING_MODE_CH2; + else + qtrack = (phy->pdata->rx1tx1_mode_use_rx_num == 1) ? + ENABLE_TRACKING_MODE_CH1 : ENABLE_TRACKING_MODE_CH2; + } + + ad9361_spi_write(spi, REG_CALIBRATION_CONFIG_1, + ENABLE_PHASE_CORR | ENABLE_GAIN_CORR | + FREE_RUN_MODE | ENABLE_CORR_WORD_DECIMATION | + qtrack); + + return 0; +} + +/** + * Enable/disable the VCO cal. + * @param phy The AD9361 state structure. + * @param rx Set true for rx. + * @param enable Set true to enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_trx_vco_cal_control(struct ad9361_rf_phy *phy, + bool tx, bool enable) +{ + dev_dbg(&phy->spi->dev, "%s : state %d", + __func__, enable); + + return ad9361_spi_writef(phy->spi, + tx ? REG_TX_PFD_CONFIG : REG_RX_PFD_CONFIG, + BYPASS_LD_SYNTH, !enable); +} + +/** + * Enable/disable the ext. LO. + * @param phy The AD9361 state structure. + * @param rx Set true for rx. + * @param enable Set true to enable. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_trx_ext_lo_control(struct ad9361_rf_phy *phy, + bool tx, bool enable) +{ + int32_t val = enable ? ~0 : 0; + int32_t ret; + + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + + bool mcs_rf_enable = ad9361_spi_readf(phy->spi, + REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, MCS_RF_ENABLE); + + dev_dbg(&phy->spi->dev, "%s : %s state %d", + __func__, tx ? "TX" : "RX", enable); + + if (tx) { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + TX_VCO_DIVIDER(~0), enable ? 7 : + phy->cached_tx_rfpll_div); + + if (enable) + phy->cached_synth_pd[0] |= TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN; + else + phy->cached_synth_pd[0] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + TX_SYNTH_PTAT_POWER_DOWN | + TX_SYNTH_VCO_POWER_DOWN); + + + ret |= ad9361_spi_write(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd[0]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + TX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_TX_LO_GEN_POWER_MODE, + TX_LO_GEN_POWER_MODE(val)); + } else { + ret = ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_RX_SYNTH, mcs_rf_enable ? 0 : enable); + + ret |= ad9361_spi_writef(phy->spi, REG_RFPLL_DIVIDERS, + RX_VCO_DIVIDER(~0), enable ? 7 : + phy->cached_rx_rfpll_div); + + if (enable) + phy->cached_synth_pd[1] |= RX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN; + else + phy->cached_synth_pd[1] &= ~(TX_SYNTH_VCO_ALC_POWER_DOWN | + RX_SYNTH_PTAT_POWER_DOWN | + RX_SYNTH_VCO_POWER_DOWN); + + ret |= ad9361_spi_write(phy->spi, REG_RX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd[1]); + + ret |= ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + RX_EXT_VCO_BUFFER_POWER_DOWN, !enable); + + ret |= ad9361_spi_write(phy->spi, REG_RX_LO_GEN_POWER_MODE, + RX_LO_GEN_POWER_MODE(val)); + } + + return ret; +} + +/** + * Selectively Power Down RX/TX LO/Synthesizers. + * @param phy The AD9361 state structure. + * @param rx Synthesizer PD enum + * @param tx Synthesizer PD enum + * @return 0 in case of success, negative error code otherwise. + */ + +int32_t ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, + enum synth_pd_ctrl rx, + enum synth_pd_ctrl tx) +{ + + dev_dbg(&phy->spi->dev, "%s : RX(%d) TX(%d)",__func__, rx, tx); + + switch (rx) { + case LO_OFF: + phy->cached_synth_pd[1] |= RX_LO_POWER_DOWN; + break; + case LO_ON: + phy->cached_synth_pd[1] &= ~RX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + switch (tx) { + case LO_OFF: + phy->cached_synth_pd[0] |= TX_LO_POWER_DOWN; + break; + case LO_ON: + phy->cached_synth_pd[0] &= ~TX_LO_POWER_DOWN; + break; + case LO_DONTCARE: + break; + } + + return ad9361_spi_writem(phy->spi, REG_TX_SYNTH_POWER_DOWN_OVERRIDE, + phy->cached_synth_pd, 2); +} + +/** + * Setup the reference clock delay unit counter register. + * @param phy The AD9361 state structure. + * @param ref_clk_hz The reference clock frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_set_ref_clk_cycles(struct ad9361_rf_phy *phy, + uint32_t ref_clk_hz) +{ + dev_dbg(&phy->spi->dev, "%s : ref_clk_hz %"PRIu32, + __func__, ref_clk_hz); + + return ad9361_spi_write(phy->spi, REG_REFERENCE_CLOCK_CYCLES, + REFERENCE_CLOCK_CYCLES_PER_US((ref_clk_hz / 1000000UL) - 1)); +} + +/** + * Setup the DCXO tune. + * @param phy The AD9361 state structure. + * @param coarse The DCXO tune coarse. + * @param fine The DCXO tune fine. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy, + uint32_t coarse, uint32_t fine) +{ + dev_dbg(&phy->spi->dev, "%s : coarse %"PRIu32" fine %"PRIu32, __func__, coarse, fine); + + ad9361_spi_write(phy->spi, REG_DCXO_COARSE_TUNE, DCXO_TUNE_COARSE(coarse)); + ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_LOW, DCXO_TUNE_FINE_LOW(fine)); + return ad9361_spi_write(phy->spi, REG_DCXO_FINE_TUNE_HIGH, DCXO_TUNE_FINE_HIGH(fine)); +} + +/** + * Setup TXMON. + * @param phy The AD9361 state structure. + * @param ctrl The TXMON settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txmon_setup(struct ad9361_rf_phy *phy, + struct tx_monitor_control *ctrl) +{ + struct spi_device *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_TPM_MODE_ENABLE, + (ctrl->one_shot_mode_en ? ONE_SHOT_MODE : 0) | + TX_MON_DURATION(ilog2(ctrl->tx_mon_duration / 16))); + + ad9361_spi_write(spi, REG_TX_MON_DELAY, ctrl->tx_mon_delay); + + ad9361_spi_write(spi, REG_TX_MON_1_CONFIG, + TX_MON_1_LO_CM(ctrl->tx1_mon_lo_cm) | + TX_MON_1_GAIN(ctrl->tx1_mon_front_end_gain)); + ad9361_spi_write(spi, REG_TX_MON_2_CONFIG, + TX_MON_2_LO_CM(ctrl->tx2_mon_lo_cm) | + TX_MON_2_GAIN(ctrl->tx2_mon_front_end_gain)); + + ad9361_spi_write(spi, REG_TX_ATTEN_THRESH, + ctrl->low_high_gain_threshold_mdB / 250); + + ad9361_spi_write(spi, REG_TX_MON_HIGH_GAIN, + TX_MON_HIGH_GAIN(ctrl->high_gain_dB)); + + ad9361_spi_write(spi, REG_TX_MON_LOW_GAIN, + (ctrl->tx_mon_track_en ? TX_MON_TRACK : 0) | + TX_MON_LOW_GAIN(ctrl->low_gain_dB)); + + return 0; +} + +/** + * Enable TXMON. + * @param phy The AD9361 state structure. + * @param en_mask The enable mask. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_txmon_control(struct ad9361_rf_phy *phy, + int32_t en_mask) +{ + dev_dbg(&phy->spi->dev, "%s: mask 0x%"PRIx32, __func__, en_mask); + +#if 0 + if (!phy->pdata->fdd && en_mask) { + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, + ENABLE_RX_DATA_PORT_FOR_CAL, 1); + phy->txmon_tdd_en = true; + } else { + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_1, + ENABLE_RX_DATA_PORT_FOR_CAL, 0); + phy->txmon_tdd_en = false; + } +#endif + + ad9361_spi_writef(phy->spi, REG_ANALOG_POWER_DOWN_OVERRIDE, + TX_MONITOR_POWER_DOWN(~0), ~en_mask); + + ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, + TX1_MON_ENABLE, !!(en_mask & TX_1)); + + return ad9361_spi_writef(phy->spi, REG_TPM_MODE_ENABLE, + TX2_MON_ENABLE, !!(en_mask & TX_2)); +} + +/** +* Setup the RF port. +* Note: +* val +* 0 (RX1A_N & RX1A_P) and (RX2A_N & RX2A_P) enabled; balanced +* 1 (RX1B_N & RX1B_P) and (RX2B_N & RX2B_P) enabled; balanced +* 2 (RX1C_N & RX1C_P) and (RX2C_N & RX2C_P) enabled; balanced +* +* 3 RX1A_N and RX2A_N enabled; unbalanced +* 4 RX1A_P and RX2A_P enabled; unbalanced +* 5 RX1B_N and RX2B_N enabled; unbalanced +* 6 RX1B_P and RX2B_P enabled; unbalanced +* 7 RX1C_N and RX2C_N enabled; unbalanced +* 8 RX1C_P and RX2C_P enabled; unbalanced +* 9 TX_MON1 +* 10 TX_MON2 +* 11 TX_MON1 & TX_MON2 +* @param phy The AD9361 state structure. +* @param rx_inputs RX input option identifier +* @param txb TX output option identifier +* @return 0 in case of success, negative error code otherwise. +*/ +int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out, + uint32_t rx_inputs, uint32_t txb) +{ + uint32_t val; + + if (rx_inputs > 11) + return -EINVAL; + + if (!is_out) { + if (rx_inputs > 8) + return ad9361_txmon_control(phy, rx_inputs & (TX_1 | TX_2)); + else + ad9361_txmon_control(phy, 0); + } + + if (rx_inputs < 3) + val = 3 << (rx_inputs * 2); + else + val = 1 << (rx_inputs - 3); + + if (txb) + val |= TX_OUTPUT; /* Select TX1B, TX2B */ + + dev_dbg(&phy->spi->dev, "%s : INPUT_SELECT 0x%"PRIx32, + __func__, val); + + return ad9361_spi_write(phy->spi, REG_INPUT_SELECT, val); +} + +/** + * Setup the Parallel Port (Digital Data Interface). + * @param phy The AD9361 state structure. + * @param restore_c3 Set true, will restore the Parallel Port Configuration 3 + * register. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_pp_port_setup(struct ad9361_rf_phy *phy, bool restore_c3) +{ + struct spi_device *spi = phy->spi; + struct ad9361_phy_platform_data *pd = phy->pdata; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (restore_c3) { + return ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, + pd->port_ctrl.pp_conf[2]); + } + + /* Sanity check */ + if (pd->port_ctrl.pp_conf[2] & LVDS_MODE) + pd->port_ctrl.pp_conf[2] &= + ~(HALF_DUPLEX_MODE | SINGLE_DATA_RATE | SINGLE_PORT_MODE); + + if (pd->port_ctrl.pp_conf[2] & FULL_PORT) + pd->port_ctrl.pp_conf[2] &= ~(HALF_DUPLEX_MODE | SINGLE_PORT_MODE); + + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_1, pd->port_ctrl.pp_conf[0]); + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_2, pd->port_ctrl.pp_conf[1]); + ad9361_spi_write(spi, REG_PARALLEL_PORT_CONF_3, pd->port_ctrl.pp_conf[2]); + ad9361_spi_write(spi, REG_RX_CLOCK_DATA_DELAY, pd->port_ctrl.rx_clk_data_delay); + ad9361_spi_write(spi, REG_TX_CLOCK_DATA_DELAY, pd->port_ctrl.tx_clk_data_delay); + + ad9361_spi_write(spi, REG_LVDS_BIAS_CTRL, pd->port_ctrl.lvds_bias_ctrl); + // ad9361_spi_write(spi, REG_DIGITAL_IO_CTRL, pd->port_ctrl.digital_io_ctrl); + ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL1, pd->port_ctrl.lvds_invert[0]); + ad9361_spi_write(spi, REG_LVDS_INVERT_CTRL2, pd->port_ctrl.lvds_invert[1]); + + if (pd->rx1rx2_phase_inversion_en || + (pd->port_ctrl.pp_conf[1] & INVERT_RX2)) { + + ad9361_spi_writef(spi, REG_PARALLEL_PORT_CONF_2, INVERT_RX2, 1); + ad9361_spi_writef(spi, REG_INVERT_BITS, + INVERT_RX2_RF_DC_CGOUT_WORD, 0); + } + + return 0; +} + +/** + * Setup the Gain Control Blocks (common function for MGC, AGC modes) + * @param phy The AD9361 state structure. + * @param ctrl The gain control settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gc_setup(struct ad9361_rf_phy *phy, struct gain_control *ctrl) +{ + struct spi_device *spi = phy->spi; + uint32_t reg, tmp1, tmp2; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + reg = DEC_PWR_FOR_GAIN_LOCK_EXIT | DEC_PWR_FOR_LOCK_LEVEL | + DEC_PWR_FOR_LOW_PWR; + + if (ctrl->rx1_mode == RF_GAIN_HYBRID_AGC || + ctrl->rx2_mode == RF_GAIN_HYBRID_AGC) + reg |= SLOW_ATTACK_HYBRID_MODE; + + reg |= RX1_GAIN_CTRL_SETUP(ctrl->rx1_mode) | + RX2_GAIN_CTRL_SETUP(ctrl->rx2_mode); + + phy->agc_mode[0] = ctrl->rx1_mode; + phy->agc_mode[1] = ctrl->rx2_mode; + + ad9361_spi_write(spi, REG_AGC_CONFIG_1, reg); // Gain Control Mode Select + + /* AGC_USE_FULL_GAIN_TABLE handled in ad9361_load_gt() */ + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX1, + ctrl->mgc_rx1_ctrl_inp_en); + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, MAN_GAIN_CTRL_RX2, + ctrl->mgc_rx2_ctrl_inp_en); + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, DIG_GAIN_EN, + ctrl->dig_gain_en); + + ctrl->adc_ovr_sample_size = clamp_t(uint8_t, ctrl->adc_ovr_sample_size, 1U, 8U); + reg = ADC_OVERRANGE_SAMPLE_SIZE(ctrl->adc_ovr_sample_size - 1); + + if (has_split_gt && phy->pdata->split_gt && + (ctrl->mgc_rx1_ctrl_inp_en || ctrl->mgc_rx2_ctrl_inp_en)) { + switch (ctrl->mgc_split_table_ctrl_inp_gain_mode) { + case 1: + reg &= ~INCDEC_LMT_GAIN; + break; + case 2: + reg |= INCDEC_LMT_GAIN; + break; + default: + case 0: + reg |= USE_AGC_FOR_LMTLPF_GAIN; + break; + } + } + + ctrl->mgc_inc_gain_step = clamp_t(uint8_t, ctrl->mgc_inc_gain_step, 1U, 8U); + reg |= MANUAL_INCR_STEP_SIZE(ctrl->mgc_inc_gain_step - 1); + ad9361_spi_write(spi, REG_AGC_CONFIG_3, reg); // Incr Step Size, ADC Overrange Size + + if (has_split_gt && phy->pdata->split_gt) { + reg = SIZE_SPLIT_TABLE - 1; + } + else { + reg = SIZE_FULL_TABLE - 1; + } + ad9361_spi_write(spi, REG_MAX_LMT_FULL_GAIN, reg); // Max Full/LMT Gain Table Index + ad9361_spi_write(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, reg); // Rx1 Full/LMT Gain Index + ad9361_spi_write(spi, REG_RX2_MANUAL_LMT_FULL_GAIN, reg); // Rx2 Full/LMT Gain Index + + ctrl->mgc_dec_gain_step = clamp_t(uint8_t, ctrl->mgc_dec_gain_step, 1U, 8U); + reg = MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(ctrl->mgc_dec_gain_step); + ad9361_spi_write(spi, REG_PEAK_WAIT_TIME, reg); // Decr Step Size, Peak Overload Time + + if (ctrl->dig_gain_en) + ad9361_spi_write(spi, REG_DIGITAL_GAIN, + MAXIMUM_DIGITAL_GAIN(ctrl->max_dig_gain) | + DIG_GAIN_STP_SIZE(ctrl->dig_gain_step_size)); + + if (ctrl->adc_large_overload_thresh >= ctrl->adc_small_overload_thresh) { + ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, + ctrl->adc_small_overload_thresh); // ADC Small Overload Threshold + ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, + ctrl->adc_large_overload_thresh); // ADC Large Overload Threshold + } + else { + ad9361_spi_write(spi, REG_ADC_SMALL_OVERLOAD_THRESH, + ctrl->adc_large_overload_thresh); // ADC Small Overload Threshold + ad9361_spi_write(spi, REG_ADC_LARGE_OVERLOAD_THRESH, + ctrl->adc_small_overload_thresh); // ADC Large Overload Threshold + } + + reg = (ctrl->lmt_overload_high_thresh / 16) - 1; + reg = clamp(reg, 0U, 63U); + ad9361_spi_write(spi, REG_LARGE_LMT_OVERLOAD_THRESH, reg); + reg = (ctrl->lmt_overload_low_thresh / 16) - 1; + reg = clamp(reg, 0U, 63U); + ad9361_spi_writef(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + SMALL_LMT_OVERLOAD_THRESH(~0), reg); + + if (has_split_gt && phy->pdata->split_gt) { + /* REVIST */ + ad9361_spi_write(spi, REG_RX1_MANUAL_LPF_GAIN, 0x58); // Rx1 LPF Gain Index + ad9361_spi_write(spi, REG_RX2_MANUAL_LPF_GAIN, 0x18); // Rx2 LPF Gain Index + ad9361_spi_write(spi, REG_FAST_INITIAL_LMT_GAIN_LIMIT, 0x27); // Initial LMT Gain Limit + } + + ad9361_spi_write(spi, REG_RX1_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx1 Digital Gain Index + ad9361_spi_write(spi, REG_RX2_MANUAL_DIGITALFORCED_GAIN, 0x00); // Rx2 Digital Gain Index + + reg = clamp_t(uint8_t, ctrl->low_power_thresh, 0U, 64U) * 2; + ad9361_spi_write(spi, REG_FAST_LOW_POWER_THRESH, reg); // Low Power Threshold + ad9361_spi_write(spi, REG_TX_SYMBOL_ATTEN_CONFIG, 0x00); // Tx Symbol Gain Control + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + USE_HB1_OUT_FOR_DEC_PWR_MEAS, 1); // Power Measurement Duration + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + ENABLE_DEC_PWR_MEAS, 1); // Power Measurement Duration + + if (ctrl->rx1_mode == RF_GAIN_FASTATTACK_AGC || + ctrl->rx2_mode == RF_GAIN_FASTATTACK_AGC) + reg = ilog2(ctrl->f_agc_dec_pow_measuremnt_duration / 16); + else + reg = ilog2(ctrl->dec_pow_measuremnt_duration / 16); + + ad9361_spi_writef(spi, REG_DEC_POWER_MEASURE_DURATION_0, + DEC_POWER_MEASUREMENT_DURATION(~0), reg); // Power Measurement Duration + + /* AGC */ + + tmp1 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_high, 0U, 127U); + ad9361_spi_writef(spi, REG_AGC_LOCK_LEVEL, + AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(~0), + reg); + + tmp2 = reg = clamp_t(uint8_t, ctrl->agc_inner_thresh_low, 0U, 127U); + reg |= (ctrl->adc_lmt_small_overload_prevent_gain_inc ? + PREVENT_GAIN_INC : 0); + ad9361_spi_write(spi, REG_AGC_INNER_LOW_THRESH, reg); + + reg = AGC_OUTER_HIGH_THRESH(tmp1 - ctrl->agc_outer_thresh_high) | + AGC_OUTER_LOW_THRESH(ctrl->agc_outer_thresh_low - tmp2); + ad9361_spi_write(spi, REG_OUTER_POWER_THRESHS, reg); + + reg = AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_high_dec_steps) | + AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_outer_thresh_low_inc_steps); + ad9361_spi_write(spi, REG_GAIN_STP_2, reg); + + reg = ((ctrl->immed_gain_change_if_large_adc_overload) ? + IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD : 0) | + ((ctrl->immed_gain_change_if_large_lmt_overload) ? + IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD : 0) | + AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_high_dec_steps) | + AGC_INNER_LOW_THRESH_EXED_STP_SIZE(ctrl->agc_inner_thresh_low_inc_steps); + ad9361_spi_write(spi, REG_GAIN_STP1, reg); + + reg = LARGE_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_large_overload_exceed_counter) | + SMALL_ADC_OVERLOAD_EXED_COUNTER(ctrl->adc_small_overload_exceed_counter); + ad9361_spi_write(spi, REG_ADC_OVERLOAD_COUNTERS, reg); + + ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG_2, LARGE_LPF_GAIN_STEP(~0), + LARGE_LPF_GAIN_STEP(ctrl->adc_large_overload_inc_steps)); + + reg = LARGE_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_large_exceed_counter) | + SMALL_LMT_OVERLOAD_EXED_COUNTER(ctrl->lmt_overload_small_exceed_counter); + ad9361_spi_write(spi, REG_LMT_OVERLOAD_COUNTERS, reg); + + ad9361_spi_writef(spi, REG_GAIN_STP_CONFIG1, + DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(~0), + ctrl->lmt_overload_large_inc_steps); + + reg = DIG_SATURATION_EXED_COUNTER(ctrl->dig_saturation_exceed_counter) | + (ctrl->sync_for_gain_counter_en ? + ENABLE_SYNC_FOR_GAIN_COUNTER : 0); + ad9361_spi_write(spi, REG_DIGITAL_SAT_COUNTER, reg); + + /* + * Fast AGC + */ + + /* Fast AGC - Low Power */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + ENABLE_INCR_GAIN, + ctrl->f_agc_allow_agc_gain_increase); + + ad9361_spi_write(spi, REG_FAST_INCREMENT_TIME, + ctrl->f_agc_lp_thresh_increment_time); + + reg = ctrl->f_agc_lp_thresh_increment_steps - 1; + reg = clamp_t(uint32_t, reg, 0U, 7U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_DETECT_COUNT, + INCREMENT_GAIN_STP_LPFLMT(~0), reg); + + /* Fast AGC - Lock Level */ + /* Dual use see also agc_inner_thresh_high */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL, + ctrl->f_agc_lock_level_lmt_gain_increase_en); + + reg = ctrl->f_agc_lock_level_gain_increase_upper_limit; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_AGCLL_UPPER_LIMIT, + AGCLL_MAX_INCREASE(~0), reg); + + /* Fast AGC - Peak Detectors and Final Settling */ + reg = ctrl->f_agc_lpf_final_settling_steps; + reg = clamp_t(uint32_t, reg, 0U, 3U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, + POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(~0), + reg); + + reg = ctrl->f_agc_lmt_final_settling_steps; + reg = clamp_t(uint32_t, reg, 0U, 3U); + ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, + POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(~0), reg); + + reg = ctrl->f_agc_final_overrange_count; + reg = clamp_t(uint32_t, reg, 0U, 7U); + ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, + FINAL_OVER_RANGE_COUNT(~0), reg); + + /* Fast AGC - Final Power Test */ + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + ENABLE_GAIN_INC_AFTER_GAIN_LOCK, + ctrl->f_agc_gain_increase_after_gain_lock_en); + + /* Fast AGC - Unlocking the Gain */ + /* 0 = MAX Gain, 1 = Optimized Gain, 2 = Set Gain */ + + reg = ctrl->f_agc_gain_index_type_after_exit_rx_mode; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EXIT_RX_STATE, reg == SET_GAIN); + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE, + reg == OPTIMIZED_GAIN); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + USE_LAST_LOCK_LEVEL_FOR_SET_GAIN, + ctrl->f_agc_use_last_lock_level_for_set_gain_en); + + reg = ctrl->f_agc_optimized_gain_offset; + reg = clamp_t(uint32_t, reg, 0U, 15U); + ad9361_spi_writef(spi, REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN, + OPTIMIZE_GAIN_OFFSET(~0), reg); + + tmp1 = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en || + !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en || + !ctrl->f_agc_rst_gla_large_adc_overload_en || + !ctrl->f_agc_rst_gla_large_lmt_overload_en || + ctrl->f_agc_rst_gla_en_agc_pulled_high_en; + + ad9361_spi_writef(spi, REG_AGC_CONFIG_2, + AGC_GAIN_UNLOCK_CTRL, tmp1); + + reg = !ctrl->f_agc_rst_gla_stronger_sig_thresh_exceeded_en; + ad9361_spi_writef(spi, REG_FAST_STRONG_SIGNAL_FREEZE, + DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL, reg); + + reg = ctrl->f_agc_rst_gla_stronger_sig_thresh_above_ll; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_STRONGER_SIGNAL_THRESH, + STRONGER_SIGNAL_THRESH(~0), reg); + + reg = ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_ENERGY_LOST_THRESH, + ENERGY_LOST_THRESH(~0), reg); + + reg = ctrl->f_agc_rst_gla_engergy_lost_goto_optim_gain_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, reg); + + reg = !ctrl->f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + DONT_UNLOCK_GAIN_IF_ENERGY_LOST, reg); + + reg = ctrl->f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; + reg = clamp_t(uint32_t, reg, 0U, 63U); + ad9361_spi_writef(spi, REG_FAST_GAIN_LOCK_EXIT_COUNT, + GAIN_LOCK_EXIT_COUNT(~0), reg); + + reg = !ctrl->f_agc_rst_gla_large_adc_overload_en || + !ctrl->f_agc_rst_gla_large_lmt_overload_en; + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG, reg); + + reg = !ctrl->f_agc_rst_gla_large_adc_overload_en; + ad9361_spi_writef(spi, REG_FAST_LOW_POWER_THRESH, + DONT_UNLOCK_GAIN_IF_ADC_OVRG, reg); + + /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */ + + if (ctrl->f_agc_rst_gla_en_agc_pulled_high_en) { + switch (ctrl->f_agc_rst_gla_if_en_agc_pulled_high_mode) { + case MAX_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 0); + break; + case SET_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 1); + break; + case OPTIMIZED_GAIN: + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 1); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH, 1); + break; + case NO_GAIN_CHANGE: + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + break; + } + } + else { + ad9361_spi_writef(spi, REG_FAST_CONFIG_1, + GOTO_SET_GAIN_IF_EN_AGC_HIGH, 0); + ad9361_spi_writef(spi, REG_FAST_CONFIG_2_SETTLING_DELAY, + GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH, 0); + } + + reg = ilog2(ctrl->f_agc_power_measurement_duration_in_state5 / 16); + reg = clamp_t(uint32_t, reg, 0U, 15U); + ad9361_spi_writef(spi, REG_RX1_MANUAL_LPF_GAIN, + POWER_MEAS_IN_STATE_5(~0), reg); + ad9361_spi_writef(spi, REG_RX1_MANUAL_LMT_FULL_GAIN, + POWER_MEAS_IN_STATE_5_MSB, reg >> 3); + + return ad9361_gc_update(phy); +} + +/** + * Set the Aux DAC. + * @param phy The AD9361 state structure. + * @param dac The DAC. + * @param val_mV The value. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxdac_set(struct ad9361_rf_phy *phy, int32_t dac, + int32_t val_mV) +{ + struct spi_device *spi = phy->spi; + uint32_t val, tmp; + + dev_dbg(&phy->spi->dev, "%s DAC%"PRId32" = %"PRId32" mV", __func__, dac, val_mV); + + /* Disable DAC if val == 0, Ignored in ENSM Auto Mode */ + ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, + AUXDAC_MANUAL_BAR(dac), val_mV ? 0 : 1); + + if (val_mV < 306) + val_mV = 306; + + if (val_mV < 1888) { + val = ((val_mV - 306) * 1000) / 1404; /* Vref = 1V, Step = 2 */ + tmp = AUXDAC_1_VREF(0); + } + else { + val = ((val_mV - 1761) * 1000) / 1836; /* Vref = 2.5V, Step = 2 */ + tmp = AUXDAC_1_VREF(3); + } + + val = clamp_t(uint32_t, val, 0, 1023); + + switch (dac) { + case 1: + ad9361_spi_write(spi, REG_AUXDAC_1_WORD, val >> 2); + ad9361_spi_write(spi, REG_AUXDAC_1_CONFIG, AUXDAC_1_WORD_LSB(val) | tmp); + phy->auxdac1_value = val_mV; + break; + case 2: + ad9361_spi_write(spi, REG_AUXDAC_2_WORD, val >> 2); + ad9361_spi_write(spi, REG_AUXDAC_2_CONFIG, AUXDAC_2_WORD_LSB(val) | tmp); + phy->auxdac2_value = val_mV; + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Get the Aux DAC value. + * @param phy The AD9361 state structure. + * @param dac The DAC. + * @return The value in case of success, negative error code otherwise. + */ +int32_t ad9361_auxdac_get(struct ad9361_rf_phy *phy, int32_t dac) +{ + + switch (dac) { + case 1: + return phy->auxdac1_value; + case 2: + return phy->auxdac2_value; + default: + return -EINVAL; + } +} + +/** + * Setup the AuxDAC. + * @param phy The AD9361 state structure. + * @param ctrl Pointer to auxdac_control structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxdac_setup(struct ad9361_rf_phy *phy, +struct auxdac_control *ctrl) +{ + struct spi_device *spi = phy->spi; + uint8_t tmp; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_auxdac_set(phy, 1, ctrl->dac1_default_value); + ad9361_auxdac_set(phy, 2, ctrl->dac2_default_value); + + tmp = ~(AUXDAC_AUTO_TX_BAR(ctrl->dac2_in_tx_en << 1 | ctrl->dac1_in_tx_en) | + AUXDAC_AUTO_RX_BAR(ctrl->dac2_in_rx_en << 1 | ctrl->dac1_in_rx_en) | + AUXDAC_INIT_BAR(ctrl->dac2_in_alert_en << 1 | ctrl->dac1_in_alert_en)); + + ad9361_spi_writef(spi, REG_AUXDAC_ENABLE_CTRL, + AUXDAC_AUTO_TX_BAR(~0) | + AUXDAC_AUTO_RX_BAR(~0) | + AUXDAC_INIT_BAR(~0), + tmp); /* Auto Control */ + + ad9361_spi_writef(spi, REG_EXTERNAL_LNA_CTRL, + AUXDAC_MANUAL_SELECT, ctrl->auxdac_manual_mode_en); + ad9361_spi_write(spi, REG_AUXDAC1_RX_DELAY, ctrl->dac1_rx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC1_TX_DELAY, ctrl->dac1_tx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC2_RX_DELAY, ctrl->dac2_rx_delay_us); + ad9361_spi_write(spi, REG_AUXDAC2_TX_DELAY, ctrl->dac2_tx_delay_us); + + return 0; +} + +/** + * Setup the AuxADC. + * @param phy The AD9361 state structure. + * @param ctrl The AuxADC settings. + * @param bbpll_freq The BBPLL frequency [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_auxadc_setup(struct ad9361_rf_phy *phy, +struct auxadc_control *ctrl, + uint32_t bbpll_freq) +{ + struct spi_device *spi = phy->spi; + uint32_t val; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + val = DIV_ROUND_CLOSEST(ctrl->temp_time_inteval_ms * + (bbpll_freq / 1000UL), (1 << 29)); + + ad9361_spi_write(spi, REG_TEMP_OFFSET, ctrl->offset); + ad9361_spi_write(spi, REG_START_TEMP_READING, 0x00); + ad9361_spi_write(spi, REG_TEMP_SENSE2, + MEASUREMENT_TIME_INTERVAL(val) | + (ctrl->periodic_temp_measuremnt ? + TEMP_SENSE_PERIODIC_ENABLE : 0)); + ad9361_spi_write(spi, REG_TEMP_SENSOR_CONFIG, + TEMP_SENSOR_DECIMATION( + ilog2(ctrl->temp_sensor_decimation) - 8)); + ad9361_spi_write(spi, REG_AUXADC_CLOCK_DIVIDER, + bbpll_freq / ctrl->auxadc_clock_rate); + ad9361_spi_write(spi, REG_AUXADC_CONFIG, + AUX_ADC_DECIMATION( + ilog2(ctrl->auxadc_decimation) - 8)); + + return 0; +} + +/** + * Get the measured temperature of the device. + * @param phy The AD9361 state structure. + * @return The measured temperature of the device. + */ +int32_t ad9361_get_temp(struct ad9361_rf_phy *phy) +{ + uint32_t val; + + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); + val = ad9361_spi_read(phy->spi, REG_TEMPERATURE); + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); + + return DIV_ROUND_CLOSEST(val * 1000000, 1140); +} + +/** + * Get the Aux ADC value. + * @param phy The AD9361 state structure. + * @return The value in case of success, negative error code otherwise. + */ +int32_t ad9361_get_auxadc(struct ad9361_rf_phy *phy) +{ + uint8_t buf[2]; + + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 1); + ad9361_spi_readm(phy->spi, REG_AUXADC_LSB, buf, 2); + ad9361_spi_writef(phy->spi, REG_AUXADC_CONFIG, AUXADC_POWER_DOWN, 0); + + return (buf[1] << 4) | AUXADC_WORD_LSB(buf[0]); +} + +/** + * Setup the Control Output pins. + * @param phy The AD9361 state structure. + * @param ctrl The Control Output pins settings. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_ctrl_outs_setup(struct ad9361_rf_phy *phy, +struct ctrl_outs_control *ctrl) +{ + struct spi_device *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_CTRL_OUTPUT_POINTER, ctrl->index); // Ctrl Out index + return ad9361_spi_write(spi, REG_CTRL_OUTPUT_ENABLE, ctrl->en_mask); // Ctrl Out [7:0] output enable +} + +/** + * Setup the GPO pins. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_gpo_setup(struct ad9361_rf_phy *phy, struct gpo_control *ctrl) +{ + struct spi_device *spi = phy->spi; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + ad9361_spi_write(spi, REG_AUTO_GPO, + GPO_ENABLE_AUTO_RX(ctrl->gpo0_slave_rx_en | + (ctrl->gpo1_slave_rx_en << 1) | + (ctrl->gpo2_slave_rx_en << 2) | + (ctrl->gpo3_slave_rx_en << 3)) | + GPO_ENABLE_AUTO_TX(ctrl->gpo0_slave_tx_en | + (ctrl->gpo1_slave_tx_en << 1) | + (ctrl->gpo2_slave_tx_en << 2) | + (ctrl->gpo3_slave_tx_en << 3))); + + ad9361_spi_write(spi, REG_GPO_FORCE_AND_INIT, + GPO_INIT_STATE(ctrl->gpo0_inactive_state_high_en | + (ctrl->gpo1_inactive_state_high_en << 1) | + (ctrl->gpo2_inactive_state_high_en << 2) | + (ctrl->gpo3_inactive_state_high_en << 3))); + + ad9361_spi_write(spi, REG_GPO0_RX_DELAY, ctrl->gpo0_rx_delay_us); + ad9361_spi_write(spi, REG_GPO0_TX_DELAY, ctrl->gpo0_tx_delay_us); + ad9361_spi_write(spi, REG_GPO1_RX_DELAY, ctrl->gpo1_rx_delay_us); + ad9361_spi_write(spi, REG_GPO1_TX_DELAY, ctrl->gpo1_tx_delay_us); + ad9361_spi_write(spi, REG_GPO2_RX_DELAY, ctrl->gpo2_rx_delay_us); + ad9361_spi_write(spi, REG_GPO2_TX_DELAY, ctrl->gpo2_tx_delay_us); + ad9361_spi_write(spi, REG_GPO3_RX_DELAY, ctrl->gpo3_rx_delay_us); + ad9361_spi_write(spi, REG_GPO3_TX_DELAY, ctrl->gpo3_tx_delay_us); + + return 0; +} + +/** + * Setup the RSSI. + * @param phy The AD9361 state structure. + * @param ctrl The RSSI settings. + * @param is_update True if update + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_rssi_setup(struct ad9361_rf_phy *phy, +struct rssi_control *ctrl, + bool is_update) +{ + struct spi_device *spi = phy->spi; + uint32_t total_weight, weight[4], total_dur = 0, temp; + uint8_t dur_buf[4] = { 0 }; + int32_t val, ret, i, j = 0; + uint32_t rssi_delay; + uint32_t rssi_wait; + uint32_t rssi_duration; + uint32_t rate; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (ctrl->rssi_unit_is_rx_samples) { + if (is_update) + return 0; /* no update required */ + + rssi_delay = ctrl->rssi_delay; + rssi_wait = ctrl->rssi_wait; + rssi_duration = ctrl->rssi_duration; + } + else { + /* update sample based on RX rate */ + rate = DIV_ROUND_CLOSEST( + clk_get_rate(phy, phy->ref_clk_scale[RX_SAMPL_CLK]), 1000); + /* units are in us */ + rssi_delay = DIV_ROUND_CLOSEST(ctrl->rssi_delay * rate, 1000); + rssi_wait = DIV_ROUND_CLOSEST(ctrl->rssi_wait * rate, 1000); + rssi_duration = DIV_ROUND_CLOSEST( + ctrl->rssi_duration * rate, 1000); + } + + if (ctrl->restart_mode == EN_AGC_PIN_IS_PULLED_HIGH) + rssi_delay = 0; + + rssi_delay = clamp(rssi_delay / 8, 0U, 255U); + rssi_wait = clamp(rssi_wait / 4, 0U, 255U); + + do { + for (i = 14; rssi_duration > 0 && i >= 0; i--) { + val = 1 << i; + if ((int64_t)rssi_duration >= val) { + dur_buf[j++] = i; + total_dur += val; + rssi_duration -= val; + break; + } + } + + } while (j < 4 && rssi_duration > 0); + + for (i = 0, total_weight = 0; i < 4; i++) + total_weight += weight[i] = + DIV_ROUND_CLOSEST(RSSI_MAX_WEIGHT * + (1 << dur_buf[i]), total_dur); + + /* total of all weights must be 0xFF */ + val = total_weight - 0xFF; + weight[j - 1] -= val; + + ad9361_spi_write(spi, REG_MEASURE_DURATION_01, + (dur_buf[1] << 4) | dur_buf[0]); // RSSI Measurement Duration 0, 1 + ad9361_spi_write(spi, REG_MEASURE_DURATION_23, + (dur_buf[3] << 4) | dur_buf[2]); // RSSI Measurement Duration 2, 3 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_0, weight[0]); // RSSI Weighted Multiplier 0 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_1, weight[1]); // RSSI Weighted Multiplier 1 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_2, weight[2]); // RSSI Weighted Multiplier 2 + ad9361_spi_write(spi, REG_RSSI_WEIGHT_3, weight[3]); // RSSI Weighted Multiplier 3 + ad9361_spi_write(spi, REG_RSSI_DELAY, rssi_delay); // RSSI Delay + ad9361_spi_write(spi, REG_RSSI_WAIT_TIME, rssi_wait); // RSSI Wait + + temp = RSSI_MODE_SELECT(ctrl->restart_mode); + if (ctrl->restart_mode == SPI_WRITE_TO_REGISTER) + temp |= START_RSSI_MEAS; + + ret = ad9361_spi_write(spi, REG_RSSI_CONFIG, temp); // RSSI Mode Select + + if (ret < 0) + dev_err(&phy->spi->dev, "Unable to write rssi config"); + + return 0; +} + +/** + * This function needs to be called whenever BBPLL changes. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_bb_clk_change_handler(struct ad9361_rf_phy *phy) +{ + int32_t ret; + + ret = ad9361_gc_update(phy); + ret |= ad9361_rssi_setup(phy, &phy->pdata->rssi_ctrl, true); + ret |= ad9361_auxadc_setup(phy, &phy->pdata->auxadc_ctrl, + clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK])); + + return ret; +} + +/** + * Set the desired Enable State Machine (ENSM) state. + * @param phy The AD9361 state structure. + * @param ensm_state The ENSM state [ENSM_STATE_SLEEP_WAIT, ENSM_STATE_ALERT, + * ENSM_STATE_TX, ENSM_STATE_TX_FLUSH, ENSM_STATE_RX, + * ENSM_STATE_RX_FLUSH, ENSM_STATE_FDD, ENSM_STATE_FDD_FLUSH]. + * @param pinctrl Set true, will enable the ENSM pin control. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state, + bool pinctrl) +{ + struct spi_device *spi = phy->spi; + int32_t rc = 0; + uint32_t val; + uint32_t tmp; + + dev_dbg(dev, "Device is in %x state, moving to %x", phy->curr_ensm_state, + ensm_state); + + + if (phy->curr_ensm_state == ENSM_STATE_SLEEP) { + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | + (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ + udelay(20); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, TO_ALERT | FORCE_ALERT_STATE); + ad9361_trx_vco_cal_control(phy, false, true); /* Enable VCO Cal */ + ad9361_trx_vco_cal_control(phy, true, true); + } + + val = (phy->pdata->ensm_pin_pulse_mode ? 0 : LEVEL_MODE) | + (pinctrl ? ENABLE_ENSM_PIN_CTRL : 0) | + (phy->txmon_tdd_en ? ENABLE_RX_DATA_PORT_FOR_CAL : 0) | + TO_ALERT; + + switch (ensm_state) { + case ENSM_STATE_TX: + val |= FORCE_TX_ON; + if (phy->pdata->fdd) + rc = -EINVAL; + else if (phy->curr_ensm_state != ENSM_STATE_ALERT) + rc = -EINVAL; + break; + case ENSM_STATE_RX: + val |= FORCE_RX_ON; + if (phy->pdata->fdd) + rc = -EINVAL; + else if (phy->curr_ensm_state != ENSM_STATE_ALERT) + rc = -EINVAL; + break; + case ENSM_STATE_FDD: + val |= FORCE_TX_ON; + if (!phy->pdata->fdd) + rc = -EINVAL; + break; + case ENSM_STATE_ALERT: + val &= ~(FORCE_TX_ON | FORCE_RX_ON); + val |= TO_ALERT | FORCE_ALERT_STATE; + break; + case ENSM_STATE_SLEEP_WAIT: + break; + case ENSM_STATE_SLEEP: + ad9361_trx_vco_cal_control(phy, false, false); /* Disable VCO Cal */ + ad9361_trx_vco_cal_control(phy, true, false); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Clear To Alert */ + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, + phy->pdata->fdd ? FORCE_TX_ON : FORCE_RX_ON); + /* Delay Flush Time 384 ADC clock cycles */ + udelay(384000000UL / clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, 0); /* Move to Wait*/ + udelay(1); /* Wait for ENSM settle */ + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + (phy->pdata->use_extclk ? XO_BYPASS : 0)); /* Turn off all clocks */ + phy->curr_ensm_state = ensm_state; + return 0; + + default: + dev_err(dev, "No handling for forcing %d ensm state", + ensm_state); + goto out; + } + + if (rc) { + if ((phy->curr_ensm_state != ENSM_STATE_ALERT) && (val & (FORCE_RX_ON | FORCE_TX_ON))) { + uint32_t val2 = val; + + val2 &= ~(FORCE_TX_ON | FORCE_RX_ON); + val2 |= TO_ALERT | FORCE_ALERT_STATE; + ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val2); + + ad9361_check_cal_done(phy, REG_STATE, ENSM_STATE(~0), ENSM_STATE_ALERT); + } else { + dev_err(dev, "Invalid ENSM state transition in %s mode", + phy->pdata->fdd ? "FDD" : "TDD"); + goto out; + } + } + + if (!phy->pdata->fdd && !pinctrl && !phy->pdata->tdd_use_dual_synth && + (ensm_state == ENSM_STATE_TX || ensm_state == ENSM_STATE_RX)) { + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + TXNRX_SPI_CTRL, ensm_state == ENSM_STATE_TX); + + ad9361_check_cal_done(phy, (ensm_state == ENSM_STATE_TX) ? + REG_TX_CP_OVERRANGE_VCO_LOCK : + REG_RX_CP_OVERRANGE_VCO_LOCK, + VCO_LOCK, 1); + } + + rc = ad9361_spi_write(spi, REG_ENSM_CONFIG_1, val); + if (rc) + dev_err(dev, "Failed to restore state"); + + if ((val & FORCE_RX_ON) && + (phy->agc_mode[0] == RF_GAIN_MGC || + phy->agc_mode[1] == RF_GAIN_MGC)) { + tmp = ad9361_spi_read(spi, REG_SMALL_LMT_OVERLOAD_THRESH); + ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + (tmp & SMALL_LMT_OVERLOAD_THRESH(~0)) | + (phy->agc_mode[0] == RF_GAIN_MGC ? FORCE_PD_RESET_RX1 : 0) | + (phy->agc_mode[1] == RF_GAIN_MGC ? FORCE_PD_RESET_RX2 : 0)); + ad9361_spi_write(spi, REG_SMALL_LMT_OVERLOAD_THRESH, + tmp & SMALL_LMT_OVERLOAD_THRESH(~0)); + } + + phy->curr_ensm_state = ensm_state; + +out: + return rc; + +} + +/** + * Check if at least one of the clock rates is equal to the DATA_CLK (lvds) rate. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_validate_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks) +{ + int32_t i; + uint32_t data_clk; + + data_clk = (phy->pdata->rx2tx2 ? 4 : 2) / + ((phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) ? 1 : 2) * + rx_path_clks[RX_SAMPL_FREQ]; + + /* CMOS Mode */ + if (!(phy->pdata->port_ctrl.pp_conf[2] & LVDS_MODE) && + (data_clk > 61440000UL)) { + dev_err(&phy->spi->dev, + "%s: Failed CMOS MODE DATA_CLK > 61.44MSPS", __func__); + return -EINVAL; + } + + for (i = 1; i <= 3; i++) { + if (abs(rx_path_clks[ADC_FREQ] / i - data_clk) < 4) + return 0; + } + + for (i = 1; i <= 4; i++) { + if (abs((rx_path_clks[R2_FREQ] >> i) - data_clk) < 4) + return 0; + } + + dev_err(&phy->spi->dev, "%s: Failed - at least one of the clock rates" + " must be equal to the DATA_CLK (lvds) rate", __func__); + + return -EINVAL; +} + +/** + * Set the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t ret, i, j, n; + + dev_dbg(&phy->spi->dev, "%s", __func__); + + if (!rx_path_clks || !tx_path_clks) + return -EINVAL; + + dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, rx_path_clks[BBPLL_FREQ], rx_path_clks[ADC_FREQ], + rx_path_clks[R2_FREQ], rx_path_clks[R1_FREQ], + rx_path_clks[CLKRF_FREQ], rx_path_clks[RX_SAMPL_FREQ]); + + dev_dbg(&phy->spi->dev, "%s: %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, tx_path_clks[BBPLL_FREQ], tx_path_clks[ADC_FREQ], + tx_path_clks[R2_FREQ], tx_path_clks[R1_FREQ], + tx_path_clks[CLKRF_FREQ], tx_path_clks[RX_SAMPL_FREQ]); + + ret = ad9361_validate_trx_clock_chain(phy, rx_path_clks); + if (ret < 0) + return ret; + + ret = clk_set_rate(phy, phy->ref_clk_scale[BBPLL_CLK], rx_path_clks[BBPLL_FREQ]); + if (ret < 0) + return ret; + + for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; + i <= RX_SAMPL_CLK; i++, j++, n++) { + ret = clk_set_rate(phy, phy->ref_clk_scale[i], rx_path_clks[n]); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", + ret); + return ret; + } + ret = clk_set_rate(phy, phy->ref_clk_scale[j], tx_path_clks[n]); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", + ret); + return ret; + } + } + + /* + * Workaround for clock framework since clocks don't change we + * manually need to enable the filter + */ + + if (phy->rx_fir_dec == 1 || phy->bypass_rx_fir) { + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), !phy->bypass_rx_fir); + } + + if (phy->tx_fir_int == 1 || phy->bypass_tx_fir) { + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), !phy->bypass_tx_fir); + } + + /* The FIR filter once enabled causes the interface timing to change. + * It's typically not a problem if the timing margin is big enough. + * However at 61.44 MSPS it causes problems on some systems. + * So we always run the digital tune in case the filter is enabled. + * If it is disabled we restore the values from the initial calibration. + */ + + //if (!phy->pdata->dig_interface_tune_fir_disable && + // !(phy->bypass_tx_fir && phy->bypass_rx_fir)) + // ret = ad9361_dig_tune(phy, 0, SKIP_STORE_RESULT); + + return ad9361_bb_clk_change_handler(phy); +} + +/** + * Get the RX and TX path rates. + * @param phy The AD9361 state structure. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + int32_t i, j, n; + uint32_t bbpll_freq; + + if (!rx_path_clks && !tx_path_clks) + return -EINVAL; + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + + if (rx_path_clks) + rx_path_clks[BBPLL_FREQ] = bbpll_freq; + + if (tx_path_clks) + tx_path_clks[BBPLL_FREQ] = bbpll_freq; + + for (i = ADC_CLK, j = DAC_CLK, n = ADC_FREQ; + i <= RX_SAMPL_CLK; i++, j++, n++) { + if (rx_path_clks) + rx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[i]); + if (tx_path_clks) + tx_path_clks[n] = clk_get_rate(phy, phy->ref_clk_scale[j]); + } + + return 0; +} + +/** + * Calculate the RX and TX path rates to obtain the desired sample rate. + * @param phy The AD9361 state structure. + * @param tx_sample_rate The desired sample rate. + * @param rate_gov The rate governor option. + * @param rx_path_clks RX path rates buffer. + * @param tx_path_clks TX path rates buffer. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy, + uint32_t tx_sample_rate, + uint32_t rate_gov, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks) +{ + uint32_t clktf, clkrf, adc_rate = 0, dac_rate = 0; + uint64_t bbpll_rate; + int32_t i, index_rx = -1, index_tx = -1, tmp; + uint32_t div, tx_intdec, rx_intdec, recursion = 1; + const int8_t clk_dividers[][4] = { + { 12, 3, 2, 2 }, + { 8, 2, 2, 2 }, + { 6, 3, 1, 2 }, + { 4, 2, 2, 1 }, + { 3, 3, 1, 1 }, + { 2, 2, 1, 1 }, + { 1, 1, 1, 1 }, + }; + + if (phy->bypass_rx_fir) + rx_intdec = 1; + else + rx_intdec = phy->rx_fir_dec; + + if (phy->bypass_tx_fir) + tx_intdec = 1; + else + tx_intdec = phy->tx_fir_int; + + if ((rate_gov == 1) && ((rx_intdec * tx_sample_rate * 8) < MIN_ADC_CLK)) { + recursion = 0; + rate_gov = 0; + } + + dev_dbg(&phy->spi->dev, "%s: requested rate %"PRIu32" TXFIR int %"PRIu32" RXFIR dec %"PRIu32" mode %s", + __func__, tx_sample_rate, tx_intdec, rx_intdec, + rate_gov ? "Nominal" : "Highest OSR"); + + if (tx_sample_rate > 61440000UL) + return -EINVAL; + + clktf = tx_sample_rate * tx_intdec; + clkrf = tx_sample_rate * rx_intdec * (phy->rx_eq_2tx ? 2 : 1); + + for (i = rate_gov; i < 7; i++) { + adc_rate = clkrf * clk_dividers[i][0]; + dac_rate = clktf * clk_dividers[i][0]; + + if ((adc_rate <= MAX_ADC_CLK) && (adc_rate >= MIN_ADC_CLK)) { + + + if (dac_rate > adc_rate) + tmp = ((int32_t)(dac_rate / adc_rate)) * -1; + else + tmp = adc_rate / dac_rate; + + if (adc_rate <= MAX_DAC_CLK) { + index_rx = i; + index_tx = i - ((tmp == 1) ? 0 : tmp); + dac_rate = adc_rate; /* ADC_CLK */ + break; + } + else { + dac_rate = adc_rate / 2; /* ADC_CLK/2 */ + index_rx = i; + + if (i == 4 && tmp >= 0) + index_tx = 7; /* STOP: 3/2 != 1 */ + else + index_tx = i + ((i == 5 && tmp >= 0) ? 1 : 2) - + ((tmp == 1) ? 0 : tmp); + + break; + } + } + } + + if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6) && rate_gov < 7 && recursion) { + return ad9361_calculate_rf_clock_chain(phy, tx_sample_rate, + ++rate_gov, rx_path_clks, tx_path_clks); + } + else if ((index_tx < 0 || index_tx > 6 || index_rx < 0 || index_rx > 6)) { + dev_err(&phy->spi->dev, "%s: Failed to find suitable dividers: %s", + __func__, (adc_rate < MIN_ADC_CLK) ? "ADC clock below limit" : "BBPLL rate above limit"); + + return -EINVAL; + } + + /* Calculate target BBPLL rate */ + div = MAX_BBPLL_DIV; + + do { + bbpll_rate = (uint64_t)adc_rate * div; + div >>= 1; + + } while ((bbpll_rate > MAX_BBPLL_FREQ) && (div >= MIN_BBPLL_DIV)); + + rx_path_clks[BBPLL_FREQ] = bbpll_rate; + rx_path_clks[ADC_FREQ] = adc_rate; + rx_path_clks[R2_FREQ] = rx_path_clks[ADC_FREQ] / clk_dividers[index_rx][1]; + rx_path_clks[R1_FREQ] = rx_path_clks[R2_FREQ] / clk_dividers[index_rx][2]; + rx_path_clks[CLKRF_FREQ] = rx_path_clks[R1_FREQ] / clk_dividers[index_rx][3]; + rx_path_clks[RX_SAMPL_FREQ] = rx_path_clks[CLKRF_FREQ] / rx_intdec; + + tx_path_clks[BBPLL_FREQ] = bbpll_rate; + tx_path_clks[DAC_FREQ] = dac_rate; + tx_path_clks[T2_FREQ] = tx_path_clks[DAC_FREQ] / clk_dividers[index_tx][1]; + tx_path_clks[T1_FREQ] = tx_path_clks[T2_FREQ] / clk_dividers[index_tx][2]; + tx_path_clks[CLKTF_FREQ] = tx_path_clks[T1_FREQ] / clk_dividers[index_tx][3]; + tx_path_clks[TX_SAMPL_FREQ] = tx_path_clks[CLKTF_FREQ] / tx_intdec; + + return 0; +} + +/** + * Set the desired sample rate. + * @param phy The AD9361 state structure. + * @param freq The desired sample rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy, + uint32_t freq) +{ + uint32_t rx[6], tx[6]; + int32_t ret; + + ret = ad9361_calculate_rf_clock_chain(phy, freq, + phy->rate_governor, rx, tx); + if (ret < 0) + return ret; + return ad9361_set_trx_clock_chain(phy, rx, tx); +} + +/** + * Internal ENSM mode options helper function. + * @param phy The AD9361 state structure. + * @param fdd + * @param pinctrl + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl) +{ + struct ad9361_phy_platform_data *pd = phy->pdata; + int32_t ret; + uint32_t val = 0; + + ad9361_spi_write(phy->spi, REG_ENSM_MODE, fdd ? FDD_MODE : 0); + + val = ad9361_spi_read(phy->spi, REG_ENSM_CONFIG_2); + val &= POWER_DOWN_RX_SYNTH | POWER_DOWN_TX_SYNTH; + + if (fdd) + ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, + val | DUAL_SYNTH_MODE | + (pd->fdd_independent_mode ? FDD_EXTERNAL_CTRL_ENABLE : 0)); + else + ret = ad9361_spi_write(phy->spi, REG_ENSM_CONFIG_2, val | + (pd->tdd_use_dual_synth ? DUAL_SYNTH_MODE : 0) | + (pd->tdd_use_dual_synth ? 0 : + (pinctrl ? SYNTH_ENABLE_PIN_CTRL_MODE : 0))); + + return ret; +} + +/** + * Fastlock read value. + * @param spi + * @param tx + * @param profile + * @param word + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_readval(struct spi_device *spi, bool tx, + uint32_t profile, uint32_t word) +{ + uint32_t offs = 0; + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, + RX_FAST_LOCK_PROFILE_ADDR(profile) | + RX_FAST_LOCK_PROFILE_WORD(word)); + + return ad9361_spi_read(spi, REG_RX_FAST_LOCK_PROGRAM_READ + offs); +} + +/** + * Fastlock write value. + * @param spi + * @param tx + * @param profile + * @param word + * @param val + * @param last + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_writeval(struct spi_device *spi, bool tx, + uint32_t profile, uint32_t word, uint8_t val, bool last) +{ + uint32_t offs = 0; + int32_t ret; + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + ret = ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_ADDR + offs, + RX_FAST_LOCK_PROFILE_ADDR(profile) | + RX_FAST_LOCK_PROFILE_WORD(word)); + ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, val); + ret |= ad9361_spi_write(spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + RX_FAST_LOCK_PROGRAM_WRITE | + RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); + + if (last) /* Stop Clocks */ + ret |= ad9361_spi_write(spi, + REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); + + return ret; +} + +/** + * Fastlock load values. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param values + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values) +{ + uint32_t offs = 0; + int32_t i, ret = 0; + uint8_t buf[4]; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + buf[0] = values[0]; + buf[1] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(0); + ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_DATA + offs, buf, 2); + + for (i = 1; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) { + buf[0] = RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE; + buf[1] = 0; + buf[2] = values[i]; + buf[3] = RX_FAST_LOCK_PROFILE_ADDR(profile) | RX_FAST_LOCK_PROFILE_WORD(i); + ad9361_spi_writem(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, buf, 4); + } + + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + RX_FAST_LOCK_PROGRAM_WRITE | RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, 0); + + phy->fastlock.entry[tx][profile].flags = FASTLOOK_INIT; + phy->fastlock.entry[tx][profile].alc_orig = values[15]; + phy->fastlock.entry[tx][profile].alc_written = values[15]; + + return ret; +} + +/** + * Fastlock store. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, uint32_t profile) +{ + struct spi_device *spi = phy->spi; + uint8_t val[16]; + uint32_t offs = 0, x, y; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + val[0] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_0 + offs); + val[1] = ad9361_spi_read(spi, REG_RX_INTEGER_BYTE_1 + offs); + val[2] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_0 + offs); + val[3] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_1 + offs); + val[4] = ad9361_spi_read(spi, REG_RX_FRACT_BYTE_2 + offs); + + x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_REF(~0)); + y = ad9361_spi_readf(spi, REG_RX_ALC_VARACTOR + offs, VCO_VARACTOR(~0)); + val[5] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_VCO_BIAS_1 + offs, VCO_BIAS_TCF(~0)); + y = ad9361_spi_readf(spi, REG_RX_CP_CURRENT + offs, CHARGE_PUMP_CURRENT(~0)); + /* Wide BW option: N = 1 + * Set init and steady state values to the same - let user space handle it + */ + val[6] = (x << 3) | y; + val[7] = y; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_3 + offs, LOOP_FILTER_R3(~0)); + val[8] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_C3(~0)); + val[9] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C1(~0)); + y = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_1 + offs, LOOP_FILTER_C2(~0)); + val[10] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_LOOP_FILTER_2 + offs, LOOP_FILTER_R1(~0)); + val[11] = (x << 4) | x; + + x = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_0 + offs, + VCO_VARACTOR_REFERENCE_TCF(~0)); + y = ad9361_spi_readf(spi, REG_RFPLL_DIVIDERS, + tx ? TX_VCO_DIVIDER(~0) : RX_VCO_DIVIDER(~0)); + val[12] = (x << 4) | y; + + x = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, VCO_CAL_OFFSET(~0)); + y = ad9361_spi_readf(spi, REG_RX_VCO_VARACTOR_CTRL_1 + offs, VCO_VARACTOR_REFERENCE(~0)); + val[13] = (x << 4) | y; + + val[14] = ad9361_spi_read(spi, REG_RX_FORCE_VCO_TUNE_0 + offs); + + x = ad9361_spi_readf(spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_WORD(~0)); + y = ad9361_spi_readf(spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE); + val[15] = (x << 1) | y; + + return ad9361_fastlock_load(phy, tx, profile, val); +} + +/** + * Fastlock prepare. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param prepare + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_fastlock_prepare(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, bool prepare) +{ + uint32_t offs, ready_mask; + bool is_prepared; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32": %s", + __func__, tx ? "TX" : "RX", profile, + prepare ? "Prepare" : "Un-Prepare"); + + if (tx) { + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + ready_mask = TX_SYNTH_READY_MASK; + } + else { + offs = 0; + ready_mask = RX_SYNTH_READY_MASK; + } + + is_prepared = !!phy->fastlock.current_profile[tx]; + + if (prepare && !is_prepared) { + ad9361_spi_write(phy->spi, + REG_RX_FAST_LOCK_SETUP_INIT_DELAY + offs, + (tx ? phy->pdata->tx_fastlock_delay_ns : + phy->pdata->rx_fastlock_delay_ns) / 250); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, + RX_FAST_LOCK_PROFILE(profile) | + RX_FAST_LOCK_MODE_ENABLE); + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_PROGRAM_CTRL + offs, + 0); + + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 1); + ad9361_trx_vco_cal_control(phy, tx, false); + } + else if (!prepare && is_prepared) { + ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, 0); + + /* Workaround: Exiting Fastlock Mode */ + ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 1); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 1); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_ALC + offs, FORCE_ALC_ENABLE, 0); + ad9361_spi_writef(phy->spi, REG_RX_FORCE_VCO_TUNE_1 + offs, FORCE_VCO_TUNE, 0); + + ad9361_trx_vco_cal_control(phy, tx, true); + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, ready_mask, 0); + + phy->fastlock.current_profile[tx] = 0; + } + + return 0; +} + +/** + * Fastlock recall. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, uint32_t profile) +{ + uint32_t offs = 0; + uint8_t curr, _new, orig, current_profile; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + if (tx) + offs = REG_TX_FAST_LOCK_SETUP - REG_RX_FAST_LOCK_SETUP; + + if (phy->fastlock.entry[tx][profile].flags != FASTLOOK_INIT) + return -EINVAL; + + /* Workaround: Lock problem with same ALC word */ + + current_profile = phy->fastlock.current_profile[tx]; + _new = phy->fastlock.entry[tx][profile].alc_written; + + if (current_profile == 0) + curr = ad9361_spi_readf(phy->spi, REG_RX_FORCE_ALC + offs, + FORCE_ALC_WORD(~0)) << 1; + else + curr = phy->fastlock.entry[tx][current_profile - 1].alc_written; + + if ((curr >> 1) == (_new >> 1)) { + orig = phy->fastlock.entry[tx][profile].alc_orig; + + if ((orig >> 1) == (_new >> 1)) + phy->fastlock.entry[tx][profile].alc_written += 2; + else + phy->fastlock.entry[tx][profile].alc_written = orig; + + ad9361_fastlock_writeval(phy->spi, tx, profile, 0xF, + phy->fastlock.entry[tx][profile].alc_written, true); + } + + ad9361_fastlock_prepare(phy, tx, profile, true); + phy->fastlock.current_profile[tx] = profile + 1; + + return ad9361_spi_write(phy->spi, REG_RX_FAST_LOCK_SETUP + offs, + RX_FAST_LOCK_PROFILE(profile) | + (phy->pdata->trx_fastlock_pinctrl_en[tx] ? + RX_FAST_LOCK_PROFILE_PIN_SELECT : 0) | + RX_FAST_LOCK_MODE_ENABLE); +} + +/** + * Fastlock save. + * @param phy The AD9361 state structure. + * @param tx + * @param profile + * @param values + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values) +{ + int32_t i; + + dev_dbg(&phy->spi->dev, "%s: %s Profile %"PRIu32":", + __func__, tx ? "TX" : "RX", profile); + + for (i = 0; i < RX_FAST_LOCK_CONFIG_WORD_NUM; i++) + values[i] = ad9361_fastlock_readval(phy->spi, tx, profile, i); + + return 0; +} + +/** + * Multi Chip Sync (MCS) config. + * @param phy The AD9361 state structure. + * @param step MCS step. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step) +{ + int32_t mcs_mask = MCS_RF_ENABLE | MCS_BBPLL_ENABLE | + MCS_DIGITAL_CLK_ENABLE | MCS_BB_ENABLE; + + dev_dbg(&phy->spi->dev, "%s: MCS step %"PRId32, __func__, step); + + switch (step) { + case 0: + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0); + case 1: + /* REVIST: + * POWER_DOWN_TRX_SYNTH and MCS_RF_ENABLE somehow conflict + */ + ad9361_spi_writef(phy->spi, REG_ENSM_CONFIG_2, + POWER_DOWN_TX_SYNTH | POWER_DOWN_RX_SYNTH, 0); + + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_BB_ENABLE | MCS_BBPLL_ENABLE | MCS_RF_ENABLE); + ad9361_spi_writef(phy->spi, REG_CP_BLEED_CURRENT, + MCS_REFCLK_SCALE_EN, 1); + break; + case 2: + /* + * NOTE: This is not a regular GPIO - + * HDL ensures Multi-chip Synchronization SYNC_IN Pulse Timing + * relative to rising and falling edge of REF_CLK + */ + break; + case 3: + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_BB_ENABLE | MCS_DIGITAL_CLK_ENABLE | MCS_RF_ENABLE); + break; + case 4: + break; + case 5: + ad9361_spi_writef(phy->spi, REG_MULTICHIP_SYNC_AND_TX_MON_CTRL, + mcs_mask, MCS_RF_ENABLE); + break; + } + + return 0; +} + +/** + * Clear state. + * @param phy The AD9361 state structure. + * @return None. + */ +void ad9361_clear_state(struct ad9361_rf_phy *phy) +{ + phy->current_table = RXGAIN_TBLS_END; + phy->bypass_tx_fir = true; + phy->bypass_rx_fir = true; + phy->rate_governor = 1; + phy->rfdc_track_en = true; + phy->bbdc_track_en = true; + phy->quad_track_en = true; + phy->prev_ensm_state = 0; + phy->curr_ensm_state = 0; + phy->auto_cal_en = false; + phy->last_tx_quad_cal_freq = 0; + phy->flags = 0; + phy->current_rx_bw_Hz = 0; + phy->current_tx_bw_Hz = 0; + phy->rxbbf_div = 0; + phy->tx_fir_int = 0; + phy->tx_fir_ntaps = 0; + phy->rx_fir_dec = 0; + phy->rx_fir_ntaps = 0; + phy->ensm_pin_ctl_en = false; + phy->txmon_tdd_en = 0; + phy->current_tx_lo_freq = 0; + phy->current_rx_lo_freq = 0; + phy->current_tx_use_tdd_table = false; + phy->current_rx_use_tdd_table = false; + phy->cached_synth_pd[0] = 0; + phy->cached_synth_pd[1] = 0; + + memset(&phy->fastlock, 0, sizeof(phy->fastlock)); +} + +/** + * Determine the reference frequency value. + * @param refin_Hz Maximum allowed frequency. + * @param max Reference in frequency value. + * @return Reference frequency value. + */ +static uint32_t ad9361_ref_div_sel(uint32_t refin_Hz, uint32_t max) +{ + if (refin_Hz <= (max / 2)) + return 2 * refin_Hz; + else if (refin_Hz <= max) + return refin_Hz; + else if (refin_Hz <= (max * 2)) + return refin_Hz / 2; + else if (refin_Hz <= (max * 4)) + return refin_Hz / 4; + else + return 0; +} + +/** + * Setup the AD9361 device. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_setup(struct ad9361_rf_phy *phy) +{ + uint32_t refin_Hz, ref_freq, bbpll_freq; + struct spi_device *spi = phy->spi; + struct ad9361_phy_platform_data *pd = phy->pdata; + int32_t ret; + uint32_t real_rx_bandwidth, real_tx_bandwidth; + bool tmp_use_ext_rx_lo = pd->use_ext_rx_lo; + bool tmp_use_ext_tx_lo = pd->use_ext_tx_lo; + + dev_dbg(dev, "%s", __func__); + + pd->rf_rx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_rx_bandwidth_Hz); + pd->rf_tx_bandwidth_Hz = ad9361_validate_rf_bw(phy, pd->rf_tx_bandwidth_Hz); + + real_rx_bandwidth = pd->rf_rx_bandwidth_Hz / 2; + real_tx_bandwidth = pd->rf_tx_bandwidth_Hz / 2; + + if (pd->fdd) { + pd->tdd_skip_vco_cal = false; + if (pd->ensm_pin_ctrl && pd->fdd_independent_mode) { + dev_warn(dev, "%s: Either set ENSM PINCTRL or FDD Independent Mode", __func__); + pd->ensm_pin_ctrl = false; + } + } + + if (pd->port_ctrl.pp_conf[2] & FDD_RX_RATE_2TX_RATE) + phy->rx_eq_2tx = true; + + ad9361_spi_write(spi, REG_CTRL, CTRL_ENABLE); + ad9361_spi_write(spi, REG_BANDGAP_CONFIG0, MASTER_BIAS_TRIM(0x0E)); /* Enable Master Bias */ + ad9361_spi_write(spi, REG_BANDGAP_CONFIG1, BANDGAP_TEMP_TRIM(0x0E)); /* Set Bandgap Trim */ + + ad9361_set_dcxo_tune(phy, pd->dcxo_coarse, pd->dcxo_fine); + + refin_Hz = phy->clk_refin->rate; + + ref_freq = ad9361_ref_div_sel(refin_Hz, MAX_BBPLL_FREF); + if (!ref_freq) + return -EINVAL; + + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, RX_REF_RESET_BAR, 1); + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, TX_REF_RESET_BAR, 1); + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ + ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DOUBLER_FB_DELAY(~0), 3); /* FB DELAY */ + + ad9361_spi_write(spi, REG_CLOCK_ENABLE, + DIGITAL_POWER_UP | CLOCK_ENABLE_DFLT | BBPLL_ENABLE | + (pd->use_extclk ? XO_BYPASS : 0)); /* Enable Clocks */ + + ret = clk_set_rate(phy, phy->ref_clk_scale[BB_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set BB ref clock rate (%"PRId32")", ret); + return ret; + } + + ret = ad9361_set_trx_clock_chain(phy, pd->rx_path_clks,pd->tx_path_clks); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(phy->clks[BB_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable BB ref clock rate (%"PRId32")", + ret); + return ret; + } + + if (!pd->rx2tx2) { + pd->rx1tx1_mode_use_tx_num = clamp_t(uint32_t, pd->rx1tx1_mode_use_tx_num, TX_1, TX_2); + pd->rx1tx1_mode_use_rx_num = clamp_t(uint32_t, pd->rx1tx1_mode_use_rx_num, RX_1, RX_2); + + ad9361_en_dis_tx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_tx_num); + ad9361_en_dis_rx(phy, TX_1 | TX_2, pd->rx1tx1_mode_use_rx_num); + } else { + ad9361_en_dis_tx(phy, TX_1 | TX_2, TX_1 | TX_2); + ad9361_en_dis_rx(phy, RX_1 | RX_2, RX_1 | RX_2); + } + + ret = ad9361_rf_port_setup(phy, true, pd->rf_rx_input_sel, + pd->rf_tx_output_sel); + if (ret < 0) + return ret; + + ret = ad9361_pp_port_setup(phy, false); + if (ret < 0) + return ret; + + ret = ad9361_auxdac_setup(phy, &pd->auxdac_ctrl); + if (ret < 0) + return ret; + + bbpll_freq = clk_get_rate(phy, phy->ref_clk_scale[BBPLL_CLK]); + ret = ad9361_auxadc_setup(phy, &pd->auxadc_ctrl, bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_ctrl_outs_setup(phy, &pd->ctrl_outs_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_gpo_setup(phy, &pd->gpo_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_set_ref_clk_cycles(phy, refin_Hz); + if (ret < 0) + return ret; + + ret = ad9361_setup_ext_lna(phy, &pd->elna_ctrl); + if (ret < 0) + return ret; + + /* + * This allows forcing a lower F_REF window + * (worse phase noise, better fractional spurs) + */ + pd->trx_synth_max_fref = clamp_t(uint32_t, pd->trx_synth_max_fref, + MIN_SYNTH_FREF, MAX_SYNTH_FREF); + + ref_freq = ad9361_ref_div_sel(refin_Hz, pd->trx_synth_max_fref); + if (!ref_freq) + return -EINVAL; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set RX Synth ref clock rate (%"PRId32")", ret); + return ret; + } + + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_REFCLK], ref_freq); + if (ret < 0) { + dev_err(dev, "Failed to set TX Synth ref clock rate (%"PRId32")", ret); + return ret; + } + + ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, false); /* RXCP */ + if (ret < 0) + return ret; + + ret = ad9361_txrx_synth_cp_calib(phy, ref_freq, true); /* TXCP */ + if (ret < 0) + return ret; + + phy->pdata->use_ext_rx_lo = 0; + phy->pdata->use_ext_tx_lo = 0; + + ret = clk_set_rate(phy, phy->ref_clk_scale[RX_RFPLL], ad9361_to_clk(pd->rx_synth_freq)); + if (ret < 0) { + dev_err(dev, "Failed to set RX Synth rate (%"PRId32")", + ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[RX_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable RX Synth ref clock (%"PRId32")", ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[RX_RFPLL]); + if (ret < 0) + return ret; + + /* Skip quad cal here we do it later again */ + phy->last_tx_quad_cal_freq = pd->tx_synth_freq; + ret = clk_set_rate(phy, phy->ref_clk_scale[TX_RFPLL], ad9361_to_clk(pd->tx_synth_freq)); + if (ret < 0) { + dev_err(dev, "Failed to set TX Synth rate (%"PRId32")", + ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[TX_REFCLK]); + if (ret < 0) { + dev_err(dev, "Failed to enable TX Synth ref clock (%"PRId32")", ret); + return ret; + } + + ret = clk_prepare_enable(phy->clks[TX_RFPLL]); + if (ret < 0) + return ret; + + phy->pdata->use_ext_rx_lo = tmp_use_ext_rx_lo; + phy->pdata->use_ext_tx_lo = tmp_use_ext_tx_lo; + + ad9361_clk_mux_set_parent(phy->ref_clk_scale[RX_RFPLL], + pd->use_ext_rx_lo); + + ad9361_clk_mux_set_parent(phy->ref_clk_scale[TX_RFPLL], + pd->use_ext_tx_lo); + + ret = ad9361_load_mixer_gm_subtable(phy); + if (ret < 0) + return ret; + + ret = ad9361_gc_setup(phy, &pd->gain_ctrl); + if (ret < 0) + return ret; + + ret = ad9361_rx_bb_analog_filter_calib(phy, + real_rx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_analog_filter_calib(phy, + real_tx_bandwidth, + bbpll_freq); + if (ret < 0) + return ret; + + ret = ad9361_rx_tia_calib(phy, real_rx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_tx_bb_second_filter_calib(phy, real_tx_bandwidth); + if (ret < 0) + return ret; + + ret = ad9361_rx_adc_setup(phy, + bbpll_freq, + clk_get_rate(phy, phy->ref_clk_scale[ADC_CLK])); + if (ret < 0) + return ret; + + ret = ad9361_bb_dc_offset_calib(phy); + if (ret < 0) + return ret; + + ret = ad9361_rf_dc_offset_calib(phy, + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + if (ret < 0) + return ret; + + phy->current_rx_bw_Hz = pd->rf_rx_bandwidth_Hz; + phy->current_tx_bw_Hz = pd->rf_tx_bandwidth_Hz; + phy->last_tx_quad_cal_phase = ~0; + ret = ad9361_tx_quad_calib(phy, real_rx_bandwidth, real_tx_bandwidth, -1); + if (ret < 0) + return ret; + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + if (ret < 0) + return ret; + + if (!pd->fdd) + ad9361_run_calibration(phy, TXMON_CAL); + + ad9361_pp_port_setup(phy, true); + + ret = ad9361_set_ensm_mode(phy, pd->fdd, pd->ensm_pin_ctrl); + if (ret < 0) + return ret; + + ad9361_spi_writef(phy->spi, REG_TX_ATTEN_OFFSET, + MASK_CLR_ATTEN_UPDATE, 0); + + ret = ad9361_set_tx_atten(phy, pd->tx_atten, + pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 1, + pd->rx2tx2 ? true : pd->rx1tx1_mode_use_tx_num == 2, true); + if (ret < 0) + return ret; + + if (!pd->rx2tx2) { + ret = ad9361_set_tx_atten(phy, 89750, + pd->rx1tx1_mode_use_tx_num == 2, + pd->rx1tx1_mode_use_tx_num == 1, true); + if (ret < 0) + return ret; + } + + ret = ad9361_rssi_setup(phy, &pd->rssi_ctrl, false); + if (ret < 0) + return ret; + + ret = ad9361_clkout_control(phy, pd->ad9361_clkout_mode); + if (ret < 0) + return ret; + + + ret = ad9361_txmon_setup(phy, &pd->txmon_ctrl); + if (ret < 0) + return ret; + + phy->curr_ensm_state = ad9361_spi_readf(spi, REG_STATE, ENSM_STATE(~0)); + ad9361_ensm_set_state(phy, pd->fdd ? ENSM_STATE_FDD : ENSM_STATE_RX, + pd->ensm_pin_ctrl); + + phy->auto_cal_en = true; + phy->cal_threshold_freq = 100000000ULL; /* 100 MHz */ + + return 0; + +} + +/** + * Perform the selected calibration + * @param phy The AD9361 state structure. + * @param cal The selected calibration. + * @param arg The argument of the calibration. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg) +{ + int32_t ret; + + dev_dbg(&phy->spi->dev, "%s: CAL %"PRIu32" ARG %"PRId32, __func__, cal, arg); + + ret = ad9361_tracking_control(phy, false, false, false); + if (ret < 0) + return ret; + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + switch (cal) { + case TX_QUAD_CAL: + ret = ad9361_tx_quad_calib(phy, phy->current_rx_bw_Hz / 2, + phy->current_tx_bw_Hz / 2, arg); + break; + case RFDC_CAL: + ret = ad9361_rf_dc_offset_calib(phy, + ad9361_from_clk(clk_get_rate(phy, phy->ref_clk_scale[RX_RFPLL]))); + break; + default: + ret = -EINVAL; + break; + } + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + ad9361_ensm_restore_prev_state(phy); + + return ret; +} + +/** + * Set the RF bandwidth. + * @param phy The AD9361 state structure. + * @param rf_rx_bw The desired RX bandwidth [Hz]. + * @param rf_tx_bw The desired TX bandwidth [Hz]. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy, + uint32_t rf_rx_bw, uint32_t rf_tx_bw) +{ + int32_t ret; + + ret = ad9361_tracking_control(phy, false, false, false); + if (ret < 0) + return ret; + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + ret = __ad9361_update_rf_bandwidth(phy, rf_rx_bw, rf_tx_bw); + if (ret < 0) + return ret; + + phy->current_rx_bw_Hz = rf_rx_bw; + phy->current_tx_bw_Hz = rf_tx_bw; + + ret = ad9361_tx_quad_calib(phy, rf_rx_bw / 2, rf_tx_bw / 2, -1); + if (ret < 0) + return ret; + + ret = ad9361_tracking_control(phy, phy->bbdc_track_en, + phy->rfdc_track_en, phy->quad_track_en); + if (ret < 0) + return ret; + + ad9361_ensm_restore_prev_state(phy); + + return 0; +} + +/** + * Verify the FIR filter coefficients. + * @param phy The AD9361 state structure. + * @param dest Destination identifier (RX1,2 / TX1,2). + * @param ntaps Number of filter Taps. + * @param coef Pointer to filter coefficients. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_verify_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, + uint32_t ntaps, short *coef) +{ + +#ifndef DEBUG + return 0; +#else + struct spi_device *spi = phy->spi; + uint32_t val, offs = 0, gain = 0, conf, sel, cnt; + int32_t ret = 0; + + dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", dest %d", + __func__, ntaps, dest); + + if (dest & FIR_IS_RX) { + gain = ad9361_spi_read(spi, REG_RX_FILTER_GAIN); + offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, 0); + } + + conf = ad9361_spi_read(spi, REG_TX_FILTER_CONF + offs); + + if ((dest & 3) == 3) { + sel = 1; + cnt = 2; + } else { + sel = (dest & 3); + cnt = 1; + } + + for (; cnt > 0; cnt--, sel++) { + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, + FIR_NUM_TAPS(ntaps / 16 - 1) | + FIR_SELECT(sel) | FIR_START_CLK); + for (val = 0; val < ntaps; val++) { + short tmp; + ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); + + tmp = (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_1 + offs) & 0xFF) | + (ad9361_spi_read(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs) << 8); + + if (tmp != coef[val]) { + dev_err(&phy->spi->dev,"%s%"PRIu32" read verify failed TAP%"PRIu32" %d =! %d", + (dest & FIR_IS_RX) ? "RX" : "TX", sel, + val, tmp, coef[val]); + ret = -EIO; + } + } + } + + if (dest & FIR_IS_RX) { + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, gain); + } + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, conf); + + return ret; + +#endif +} + +/** + * Load the FIR filter coefficients. + * @param phy The AD9361 state structure. + * @param dest Destination identifier (RX1,2 / TX1,2). + * @param gain_dB Gain option. + * @param ntaps Number of filter Taps. + * @param coef Pointer to filter coefficients. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, int32_t gain_dB, + uint32_t ntaps, int16_t *coef) +{ + struct spi_device *spi = phy->spi; + uint32_t val, offs = 0, fir_conf = 0, fir_enable = 0; + + dev_dbg(&phy->spi->dev, "%s: TAPS %"PRIu32", gain %"PRId32", dest %d", + __func__, ntaps, gain_dB, dest); + + if (coef == NULL || !ntaps || ntaps > 128 || ntaps % 16) { + dev_err(&phy->spi->dev, + "%s: Invalid parameters: TAPS %"PRIu32", gain %"PRId32", dest 0x%X", + __func__, ntaps, gain_dB, dest); + + return -EINVAL; + } + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + if (dest & FIR_IS_RX) { + val = 3 - (gain_dB + 12) / 6; + ad9361_spi_write(spi, REG_RX_FILTER_GAIN, val & 0x3); + offs = REG_RX_FILTER_COEF_ADDR - REG_TX_FILTER_COEF_ADDR; + phy->rx_fir_ntaps = ntaps; + fir_enable = ad9361_spi_readf(phy->spi, + REG_RX_ENABLE_FILTER_CTRL, RX_FIR_ENABLE_DECIMATION(~0)); + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), + (phy->rx_fir_dec == 4) ? 3 : phy->rx_fir_dec); + } + else { + if (gain_dB == -6) + fir_conf = TX_FIR_GAIN_6DB; + phy->tx_fir_ntaps = ntaps; + fir_enable = ad9361_spi_readf(phy->spi, + REG_TX_ENABLE_FILTER_CTRL, TX_FIR_ENABLE_INTERPOLATION(~0)); + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), + (phy->tx_fir_int == 4) ? 3 : phy->tx_fir_int); + } + + val = ntaps / 16 - 1; + + fir_conf |= FIR_NUM_TAPS(val) | FIR_SELECT(dest) | FIR_START_CLK; + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + + for (val = 0; val < ntaps; val++) { + ad9361_spi_write(spi, REG_TX_FILTER_COEF_ADDR + offs, val); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_1 + offs, coef[val] & 0xFF); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_WRITE_DATA_2 + offs, coef[val] >> 8); + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf | FIR_WRITE); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + ad9361_spi_write(spi, REG_TX_FILTER_COEF_READ_DATA_2 + offs, 0); + } + + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + fir_conf &= ~FIR_START_CLK; + ad9361_spi_write(spi, REG_TX_FILTER_CONF + offs, fir_conf); + + if (dest & FIR_IS_RX) + ad9361_spi_writef(phy->spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), fir_enable); + else + ad9361_spi_writef(phy->spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), fir_enable); + + ad9361_ensm_restore_prev_state(phy); + + return ad9361_verify_fir_filter_coef(phy, dest, ntaps, coef); +} + +/** + * Parse the FIR filter file/buffer. + * @param phy The AD9361 state structure. + * @param data Pointer to buffer. + * @param size Buffer size. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_parse_fir(struct ad9361_rf_phy *phy, + char *data, uint32_t size) +{ + char *line; + int32_t i = 0, ret, txc, rxc; + int32_t tx = -1, tx_gain, tx_int; + int32_t rx = -1, rx_gain, rx_dec; + int32_t rtx = -1, rrx = -1; + int16_t coef_tx[128]; + int16_t coef_rx[128]; + char *ptr = data; + + phy->filt_rx_bw_Hz = 0; + phy->filt_tx_bw_Hz = 0; + phy->filt_valid = false; + + while ((line = strsep(&ptr, "\n"))) { + if (line >= data + size) { + break; + } + + if (line[0] == '#') + continue; + + if (tx < 0) { +#ifdef WIN32 + ret = sscanf_s(line, "TX %d GAIN %d INT %d", + &tx, &tx_gain, &tx_int); +#else + ret = sscanf(line, "TX %"PRId32" GAIN %"PRId32" INT %"PRId32, + &tx, &tx_gain, &tx_int); +#endif + if (ret == 3) + continue; + else + tx = -1; + } + if (rx < 0) { +#ifdef WIN32 + ret = sscanf_s(line, "RX %d GAIN %d DEC %d", + &rx, &rx_gain, &rx_dec); +#else + ret = sscanf(line, "RX %"PRId32" GAIN %"PRId32" DEC %"PRId32, + &rx, &rx_gain, &rx_dec); +#endif + if (ret == 3) + continue; + else + tx = -1; + } + + if (rtx < 0) { +#ifdef WIN32 + ret = sscanf(line, "RTX %lu %lu %lu %lu %lu %lu", +#else + ret = sscanf(line, "RTX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, +#endif + &phy->filt_tx_path_clks[0], + &phy->filt_tx_path_clks[1], + &phy->filt_tx_path_clks[2], + &phy->filt_tx_path_clks[3], + &phy->filt_tx_path_clks[4], + &phy->filt_tx_path_clks[5]); + if (ret == 6) { + rtx = 0; + continue; + } else { + rtx = -1; + } + } + + if (rrx < 0) { +#ifdef WIN32 + ret = sscanf(line, "RRX %lu %lu %lu %lu %lu %lu", +#else + ret = sscanf(line, "RRX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, +#endif + &phy->filt_rx_path_clks[0], + &phy->filt_rx_path_clks[1], + &phy->filt_rx_path_clks[2], + &phy->filt_rx_path_clks[3], + &phy->filt_rx_path_clks[4], + &phy->filt_rx_path_clks[5]); + if (ret == 6) { + rrx = 0; + continue; + } else { + rrx = -1; + } + } + + if (!phy->filt_rx_bw_Hz) { +#ifdef WIN32 + ret = sscanf(line, "BWRX %d", &phy->filt_rx_bw_Hz); +#else + ret = sscanf(line, "BWRX %"PRId32, &phy->filt_rx_bw_Hz); +#endif + if (ret == 1) + continue; + else + phy->filt_rx_bw_Hz = 0; + } + + if (!phy->filt_tx_bw_Hz) { +#ifdef WIN32 + ret = sscanf(line, "BWTX %d", &phy->filt_tx_bw_Hz); +#else + ret = sscanf(line, "BWTX %"PRId32, &phy->filt_tx_bw_Hz); +#endif + if (ret == 1) + continue; + else + phy->filt_tx_bw_Hz = 0; + } + +#ifdef WIN32 + ret = sscanf_s(line, "%d,%d", &txc, &rxc); +#else + ret = sscanf(line, "%"PRId32",%"PRId32, &txc, &rxc); +#endif + if (ret == 1) { + coef_tx[i] = coef_rx[i] = (int16_t)txc; + i++; + continue; + } + else if (ret == 2) { + coef_tx[i] = (int16_t)txc; + coef_rx[i] = (int16_t)rxc; + i++; + continue; + } + } + + switch (tx) { + case FIR_TX1: + case FIR_TX2: + case FIR_TX1_TX2: + phy->tx_fir_int = tx_int; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)tx, tx_gain, i, coef_tx); + break; + default: + ret = -EINVAL; + } + + switch (rx | FIR_IS_RX) { + case FIR_RX1: + case FIR_RX2: + case FIR_RX1_RX2: + phy->rx_fir_dec = rx_dec; + ret = ad9361_load_fir_filter_coef(phy, (enum fir_dest)(rx | FIR_IS_RX), + rx_gain, i, coef_rx); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + if (!(rrx | rtx)) + phy->filt_valid = true; + + return size; +} + +/** + * Validate FIR filter configuration - on pass enable. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy) +{ + int32_t ret; + uint32_t rx[6], tx[6]; + uint32_t max, min, valid; + + dev_dbg(dev, "%s: TX FIR EN=%d/TAPS%d/INT%d, RX FIR EN=%d/TAPS%d/DEC%d", + __func__, !phy->bypass_tx_fir, phy->tx_fir_ntaps, phy->tx_fir_int, + !phy->bypass_rx_fir, phy->rx_fir_ntaps, phy->rx_fir_dec); + + if (!phy->bypass_tx_fir) { + if (!(phy->tx_fir_int == 1 || phy->tx_fir_int == 2 || + phy->tx_fir_int == 4)) { + dev_err(dev, + "%s: Invalid: Interpolation %d in filter config", + __func__, phy->tx_fir_int); + return -EINVAL; + } + + + if (phy->tx_fir_int == 1 && phy->tx_fir_ntaps > 64) { + dev_err(dev, + "%s: Invalid: TAPS > 64 and Interpolation = 1", + __func__); + return -EINVAL; + } + } + + if (!phy->bypass_rx_fir) { + if (!(phy->rx_fir_dec == 1 || phy->rx_fir_dec == 2 || + phy->rx_fir_dec == 4)) { + dev_err(dev, + "%s: Invalid: Decimation %d in filter config", + __func__, phy->rx_fir_dec); + + return -EINVAL; + } + } + + if (!phy->filt_valid || phy->bypass_rx_fir || phy->bypass_tx_fir) { + ret = ad9361_calculate_rf_clock_chain(phy, + clk_get_rate(phy, phy->ref_clk_scale[TX_SAMPL_CLK]), + phy->rate_governor, rx, tx); + if (ret < 0) { + min = phy->rate_governor ? 1500000U : 1000000U; + dev_err(dev, + "%s: Calculating filter rates failed %"PRId32 + " using min frequency",__func__, ret); + ret = ad9361_calculate_rf_clock_chain(phy, min, + phy->rate_governor, rx, tx); + if (ret < 0) { + return ret; + } + } + valid = false; + } else { +#ifndef ALTERA_PLATFORM + memcpy(rx, phy->filt_rx_path_clks, sizeof(rx)); + memcpy(tx, phy->filt_tx_path_clks, sizeof(tx)); +#else + int32_t i; + uint32_t num; + num = sizeof(rx); + for (i = 0; i < num; i++) + rx[i] = phy->filt_rx_path_clks[i]; + num = sizeof(tx); + for (i = 0; i < num; i++) + tx[i] = phy->filt_tx_path_clks[i]; +#endif + valid = true; + + } + +#ifdef _DEBUG + dev_dbg(&phy->spi->dev, "%s:RX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, rx[BBPLL_FREQ], rx[ADC_FREQ], + rx[R2_FREQ], rx[R1_FREQ], + rx[CLKRF_FREQ], rx[RX_SAMPL_FREQ]); + + dev_dbg(&phy->spi->dev, "%s:TX %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32, + __func__, tx[BBPLL_FREQ], tx[ADC_FREQ], + tx[R2_FREQ], tx[R1_FREQ], + tx[CLKRF_FREQ], tx[RX_SAMPL_FREQ]); +#endif + + if (!phy->bypass_tx_fir) { + max = (tx[DAC_FREQ] / tx[TX_SAMPL_FREQ]) * 16; + if (phy->tx_fir_ntaps > max) { + dev_err(dev, + "%s: Invalid: ratio ADC/2 / TX_SAMPL * 16 > TAPS" + "(max %"PRIu32", adc %"PRIu32", tx %"PRIu32")", + __func__, max, rx[ADC_FREQ], tx[TX_SAMPL_FREQ]); + return -EINVAL; + } + } + + if (!phy->bypass_rx_fir) { + max = ((rx[ADC_FREQ] / ((rx[ADC_FREQ] == rx[R2_FREQ]) ? 1 : 2)) / + rx[RX_SAMPL_FREQ]) * 16; + if (phy->rx_fir_ntaps > max) { + dev_err(dev, + "%s: Invalid: ratio ADC/2 / RX_SAMPL * 16 > TAPS (max %"PRIu32")", + __func__, max); + return -EINVAL; + } + } + + ret = ad9361_set_trx_clock_chain(phy, rx, tx); + if (ret < 0) + return ret; + + /* See also: ad9361_set_trx_clock_chain() */ + //if (!phy->pdata->dig_interface_tune_fir_disable && + // phy->bypass_tx_fir && phy->bypass_rx_fir) + // ad9361_dig_tune(phy, 0, RESTORE_DEFAULT); + + return ad9361_update_rf_bandwidth(phy, + valid ? phy->filt_rx_bw_Hz : phy->current_rx_bw_Hz, + valid ? phy->filt_tx_bw_Hz : phy->current_tx_bw_Hz); +} + +/* +* AD9361 Clocks +*/ + +/** +* Set the multiplier and the divider for the selected refclk_scale structure. +* @param priv The selected refclk_scale structure. +* @param mul The multiplier value. +* @param div The divider value. +* @return 0 in case of success, negative error code otherwise. +*/ +static inline int32_t ad9361_set_muldiv(struct refclk_scale *priv, uint32_t mul, uint32_t div) +{ + priv->mult = mul; + priv->div = div; + return 0; +} + +/** + * Get the clk scaler for the selected refclk_scale structure. + * @param priv The selected refclk_scale structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_get_clk_scaler(struct refclk_scale *clk_priv) +{ + struct spi_device *spi = clk_priv->spi; + uint32_t tmp, tmp1; + + switch (clk_priv->source) { + case BB_REFCLK: + tmp = ad9361_spi_read(spi, REG_CLOCK_CTRL); + tmp &= 0x3; + break; + case RX_REFCLK: + tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_1, + RX_REF_DIVIDER_MSB); + tmp1 = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DIVIDER_LSB); + tmp = (tmp << 1) | tmp1; + break; + case TX_REFCLK: + tmp = ad9361_spi_readf(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DIVIDER(~0)); + break; + case ADC_CLK: + tmp = ad9361_spi_read(spi, REG_BBPLL); + return ad9361_set_muldiv(clk_priv, 1, 1 << (tmp & 0x7)); + case R2_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, + DEC3_ENABLE_DECIMATION(~0)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case R1_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB2_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case CLKRF_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, RHB1_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case RX_SAMPL_CLK: + tmp = ad9361_spi_readf(spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0)); + + if (!tmp) + tmp = 1; /* bypass filter */ + else + tmp = (1 << (tmp - 1)); + + return ad9361_set_muldiv(clk_priv, 1, tmp); + case DAC_CLK: + tmp = ad9361_spi_readf(spi, REG_BBPLL, BIT(3)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case T2_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, + THB3_ENABLE_INTERP(~0)); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case T1_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB2_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case CLKTF_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, THB1_EN); + return ad9361_set_muldiv(clk_priv, 1, tmp + 1); + case TX_SAMPL_CLK: + tmp = ad9361_spi_readf(spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0)); + + if (!tmp) + tmp = 1; /* bypass filter */ + else + tmp = (1 << (tmp - 1)); + + return ad9361_set_muldiv(clk_priv, 1, tmp); + default: + return -EINVAL; + } + + /* REFCLK Scaler */ + switch (tmp) { + case 0: + ad9361_set_muldiv(clk_priv, 1, 1); + break; + case 1: + ad9361_set_muldiv(clk_priv, 1, 2); + break; + case 2: + ad9361_set_muldiv(clk_priv, 1, 4); + break; + case 3: + ad9361_set_muldiv(clk_priv, 2, 1); + break; + default: + return -EINVAL; + + } + + return 0; +} + +/** + * Calculate the REFCLK Scaler for the selected refclk_scale structure. + * Note: REFCLK Scaler values - 00: x1; 01: x½; 10: x¼; 11: x2. + * @param clk_priv The selected refclk_scale structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_to_refclk_scaler(struct refclk_scale *clk_priv) +{ + /* REFCLK Scaler */ + switch (((clk_priv->mult & 0xF) << 4) | (clk_priv->div & 0xF)) { + case 0x11: + return 0; + case 0x12: + return 1; + case 0x14: + return 2; + case 0x21: + return 3; + default: + return -EINVAL; + } +}; + +/** + * Set clk scaler for the selected refclk_scale structure. + * @param clk_priv The selected refclk_scale structure. + * @param set Set true, the reference clock frequency will be scaled before + * it enters the BBPLL. + * @return 0 in case of success, negative error code otherwise. + */ +static int32_t ad9361_set_clk_scaler(struct refclk_scale *clk_priv, bool set) +{ + struct spi_device *spi = clk_priv->spi; + uint32_t tmp; + int32_t ret; + + switch (clk_priv->source) { + case BB_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) + return ad9361_spi_writef(spi, REG_CLOCK_CTRL, + REF_FREQ_SCALER(~0), ret); + break; + + case RX_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) { + tmp = ret; + ret = ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_1, + RX_REF_DIVIDER_MSB, tmp >> 1); + ret |= ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + RX_REF_DIVIDER_LSB, tmp & 1); + return ret; + } + break; + case TX_REFCLK: + ret = ad9361_to_refclk_scaler(clk_priv); + if (ret < 0) + return ret; + if (set) + return ad9361_spi_writef(spi, REG_REF_DIVIDE_CONFIG_2, + TX_REF_DIVIDER(~0), ret); + break; + case ADC_CLK: + tmp = ilog2((uint8_t)clk_priv->div); + if (clk_priv->mult != 1 || tmp > 6 || tmp < 1) + return -EINVAL; + + if (set) + return ad9361_spi_writef(spi, REG_BBPLL, 0x7, tmp); + break; + case R2_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + DEC3_ENABLE_DECIMATION(~0), + clk_priv->div - 1); + break; + case R1_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RHB2_EN, clk_priv->div - 1); + break; + case CLKRF_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RHB1_EN, clk_priv->div - 1); + break; + case RX_SAMPL_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 4 || + clk_priv->div < 1 || clk_priv->div == 3) + return -EINVAL; + + if (clk_priv->phy->bypass_rx_fir) + tmp = 0; + else + tmp = ilog2(clk_priv->div) + 1; + + if (set) + return ad9361_spi_writef(spi, REG_RX_ENABLE_FILTER_CTRL, + RX_FIR_ENABLE_DECIMATION(~0), tmp); + break; + case DAC_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_BBPLL, + BIT(3), clk_priv->div - 1); + break; + case T2_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 3 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB3_ENABLE_INTERP(~0), + clk_priv->div - 1); + break; + case T1_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB2_EN, clk_priv->div - 1); + break; + case CLKTF_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 2 || clk_priv->div < 1) + return -EINVAL; + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + THB1_EN, clk_priv->div - 1); + break; + case TX_SAMPL_CLK: + if (clk_priv->mult != 1 || clk_priv->div > 4 || + clk_priv->div < 1 || clk_priv->div == 3) + return -EINVAL; + + if (clk_priv->phy->bypass_tx_fir) + tmp = 0; + else + tmp = ilog2(clk_priv->div) + 1; + + if (set) + return ad9361_spi_writef(spi, REG_TX_ENABLE_FILTER_CTRL, + TX_FIR_ENABLE_INTERPOLATION(~0), tmp); + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * Recalculate the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + uint64_t rate; + + ad9361_get_clk_scaler(clk_priv); + rate = (parent_rate * clk_priv->mult) / clk_priv->div; + + return (uint32_t)rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate) +{ + int32_t ret; + + if (rate >= *prate) { + clk_priv->mult = DIV_ROUND_CLOSEST(rate, *prate); + clk_priv->div = 1; + + } + else { + clk_priv->div = DIV_ROUND_CLOSEST(*prate, rate); + clk_priv->mult = 1; + if (!clk_priv->div) { + dev_err(&clk_priv->spi->dev, "%s: divide by zero", + __func__); + clk_priv->div = 1; + } + } + + ret = ad9361_set_clk_scaler(clk_priv, false); + if (ret < 0) + return ret; + + return (*prate / clk_priv->div) * clk_priv->mult; +} + +/** + * Set the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, rate, parent_rate); + + if (rate >= parent_rate) { + clk_priv->mult = DIV_ROUND_CLOSEST(rate, parent_rate); + clk_priv->div = 1; + } + else { + clk_priv->div = DIV_ROUND_CLOSEST(parent_rate, rate); + clk_priv->mult = 1; + if (!clk_priv->div) { + dev_err(&clk_priv->spi->dev, "%s: divide by zero", + __func__); + clk_priv->div = 1; + } + } + + return ad9361_set_clk_scaler(clk_priv, true); +} + +/* + * BBPLL + */ +/** + * Recalculate the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + uint64_t rate; + uint32_t fract, integer; + uint8_t buf[4]; + + ad9361_spi_readm(clk_priv->spi, REG_INTEGER_BB_FREQ_WORD, &buf[0], + REG_INTEGER_BB_FREQ_WORD - REG_FRACT_BB_FREQ_WORD_1 + 1); + + fract = (buf[3] << 16) | (buf[2] << 8) | buf[1]; + integer = buf[0]; + + rate = ((uint64_t)parent_rate * fract); + do_div(&rate, BBPLL_MODULUS); + rate += (uint64_t)parent_rate * integer; + + return (uint32_t)rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate) +{ + uint64_t tmp; + uint32_t fract, integer; + uint64_t temp; + + if (clk_priv) { + // Unused variable - fix compiler warning + } + + if (rate > MAX_BBPLL_FREQ) + return MAX_BBPLL_FREQ; + + if (rate < MIN_BBPLL_FREQ) + return MIN_BBPLL_FREQ; + + temp = rate; + tmp = do_div(&temp, *prate); + rate = temp; + tmp = tmp * BBPLL_MODULUS + (*prate >> 1); + do_div(&tmp, *prate); + + integer = rate; + fract = tmp; + + tmp = *prate * (uint64_t)fract; + do_div(&tmp, BBPLL_MODULUS); + tmp += *prate * integer; + + return tmp; +} + +/** + * Set the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + struct spi_device *spi = clk_priv->spi; + uint64_t tmp; + uint32_t fract, integer; + int32_t icp_val; + uint8_t lf_defaults[3] = { 0x35, 0x5B, 0xE8 }; + uint64_t temp; + + dev_dbg(&spi->dev, "%s: Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, rate, parent_rate); + + /* + * Setup Loop Filter and CP Current + * Scale is 150uA @ (1280MHz BBPLL, 40MHz REFCLK) + */ + tmp = (rate >> 7) * 150ULL; + do_div(&tmp, (parent_rate >> 7) * 32UL); + + /* 25uA/LSB, Offset 25uA */ + icp_val = DIV_ROUND_CLOSEST((uint32_t)tmp, 25U) - 1; + + icp_val = clamp(icp_val, 1, 64); + + ad9361_spi_write(spi, REG_CP_CURRENT, icp_val); + ad9361_spi_writem(spi, REG_LOOP_FILTER_3, lf_defaults, + ARRAY_SIZE(lf_defaults)); + + /* Allow calibration to occur and set cal count to 1024 for max accuracy */ + ad9361_spi_write(spi, REG_VCO_CTRL, + FREQ_CAL_ENABLE | FREQ_CAL_COUNT_LENGTH(3)); + /* Set calibration clock to REFCLK/4 for more accuracy */ + ad9361_spi_write(spi, REG_SDM_CTRL, 0x10); + + /* Calculate and set BBPLL frequency word */ + temp = rate; + tmp = do_div(&temp, parent_rate); + rate = temp; + tmp = tmp *(uint64_t)BBPLL_MODULUS + (parent_rate >> 1); + do_div(&tmp, parent_rate); + + integer = rate; + fract = tmp; + + ad9361_spi_write(spi, REG_INTEGER_BB_FREQ_WORD, integer); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_3, fract); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_2, fract >> 8); + ad9361_spi_write(spi, REG_FRACT_BB_FREQ_WORD_1, fract >> 16); + + ad9361_spi_write(spi, REG_SDM_CTRL_1, INIT_BB_FO_CAL | BBPLL_RESET_BAR); /* Start BBPLL Calibration */ + ad9361_spi_write(spi, REG_SDM_CTRL_1, BBPLL_RESET_BAR); /* Clear BBPLL start calibration bit */ + + ad9361_spi_write(spi, REG_VCO_PROGRAM_1, 0x86); /* Increase BBPLL KV and phase margin */ + ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x01); /* Increase BBPLL KV and phase margin */ + ad9361_spi_write(spi, REG_VCO_PROGRAM_2, 0x05); /* Increase BBPLL KV and phase margin */ + + return ad9361_check_cal_done(clk_priv->phy, REG_CH_1_OVERFLOW, + BBPLL_LOCK, 1); +} + +/* + * RFPLL + */ + +/** + * Calculate the RFPLL frequency. + * @param parent_rate The parent clock rate. + * @param integer The integer value. + * @param fract The fractional value. + * @param vco_div The VCO divider. + * @return The RFPLL frequency. + */ +static uint64_t ad9361_calc_rfpll_int_freq(uint64_t parent_rate, + uint64_t integer, + uint64_t fract, uint32_t vco_div) +{ + uint64_t rate; + + rate = parent_rate * fract; + do_div(&rate, RFPLL_MODULUS); + rate += parent_rate * integer; + + return rate >> (vco_div + 1); +} + +/** + * Calculate the RFPLL dividers. + * @param freq The RFPLL frequency. + * @param parent_rate The parent clock rate. + * @param integer The integer value. + * @param fract The fractional value. + * @param vco_div The VCO divider. + * @param vco_freq The VCO frequency. + * @return The RFPLL frequency. + */ +static int32_t ad9361_calc_rfpll_int_divder(struct ad9361_rf_phy *phy, + uint64_t freq, uint64_t parent_rate, uint32_t *integer, + uint32_t *fract, int32_t *vco_div, uint64_t *vco_freq) +{ + uint64_t tmp; + int32_t div, ret; + + ret = ad9361_validate_rfpll(phy, freq); + if (ret) + return ret; + + div = -1; + + while (freq <= MIN_VCO_FREQ_HZ) { + freq <<= 1; + div++; + } + + *vco_div = div; + *vco_freq = freq; + tmp = do_div(&freq, parent_rate); + tmp = tmp * RFPLL_MODULUS + (parent_rate >> 1); + do_div(&tmp, parent_rate); + *integer = freq; + *fract = tmp; + + return 0; +} + +/** + * Recalculate the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint32_t fract, integer; + uint8_t buf[5]; + uint32_t reg, div_mask, vco_div, profile; + + dev_dbg(&clk_priv->spi->dev, "%s: Parent Rate %"PRIu32" Hz", __func__, parent_rate); + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + div_mask = RX_VCO_DIVIDER(~0); + profile = phy->fastlock.current_profile[0]; + break; + case TX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + div_mask = TX_VCO_DIVIDER(~0); + profile = phy->fastlock.current_profile[1]; + break; + default: + return (uint32_t)(-EINVAL); + } + + if (profile) { + bool tx = clk_priv->source == TX_RFPLL_INT; + profile = profile - 1; + + buf[0] = ad9361_fastlock_readval(phy->spi, tx, profile, 4); + buf[1] = ad9361_fastlock_readval(phy->spi, tx, profile, 3); + buf[2] = ad9361_fastlock_readval(phy->spi, tx, profile, 2); + buf[3] = ad9361_fastlock_readval(phy->spi, tx, profile, 1); + buf[4] = ad9361_fastlock_readval(phy->spi, tx, profile, 0); + vco_div = ad9361_fastlock_readval(phy->spi, tx, profile, 12) & 0xF; + + } + else { + ad9361_spi_readm(clk_priv->spi, reg, &buf[0], ARRAY_SIZE(buf)); + vco_div = ad9361_spi_readf(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask); + } + + fract = (SYNTH_FRACT_WORD(buf[0]) << 16) | (buf[1] << 8) | buf[2]; + integer = (SYNTH_INTEGER_WORD(buf[3]) << 8) | buf[4]; + + return ad9361_to_clk(ad9361_calc_rfpll_int_freq(parent_rate, integer, + fract, vco_div)); +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate) +{ + dev_dbg(&clk_priv->spi->dev, "%s: Rate %"PRIu32" Hz", __func__, rate); + + if (prate) { + // Unused variable - fix compiler warning + } + + if (clk_priv) { + // Unused variable - fix compiler warning + } + + if (ad9361_from_clk(rate) > MAX_CARRIER_FREQ_HZ || + ad9361_from_clk(rate) < MIN_CARRIER_FREQ_HZ) + return -EINVAL; + + return rate; +} + +/** + * Set the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint64_t vco = 0; + uint8_t buf[5]; + uint32_t reg, div_mask, lock_reg, fract = 0, integer = 0; + int32_t vco_div, ret, fixup_other; + + dev_dbg(&clk_priv->spi->dev, "%s: %s Rate %"PRIu32" Hz Parent Rate %"PRIu32" Hz", + __func__, clk_priv->source == TX_RFPLL_INT ? "TX" : "RX", + rate, parent_rate); + + ad9361_fastlock_prepare(phy, clk_priv->source == TX_RFPLL_INT, 0, false); + + ret = ad9361_calc_rfpll_int_divder(phy, ad9361_from_clk(rate), parent_rate, + &integer, &fract, &vco_div, &vco); + if (ret < 0) + return ret; + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; + div_mask = RX_VCO_DIVIDER(~0); + phy->cached_rx_rfpll_div = vco_div; + phy->current_rx_lo_freq = rate; + break; + case TX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; + div_mask = TX_VCO_DIVIDER(~0); + phy->cached_tx_rfpll_div = vco_div; + phy->current_tx_lo_freq = rate; + break; + default: + return -EINVAL; + + } + + /* Option to skip VCO cal in TDD mode when moving from TX/RX to Alert */ + if (phy->pdata->tdd_skip_vco_cal) + ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, + true); + + do { + fixup_other = 0; + ad9361_rfpll_vco_init(phy, div_mask == TX_VCO_DIVIDER(~0), + vco, parent_rate); + + buf[0] = SYNTH_FRACT_WORD(fract >> 16); + buf[1] = fract >> 8; + buf[2] = fract & 0xFF; + buf[3] = SYNTH_INTEGER_WORD(integer >> 8) | + (~SYNTH_INTEGER_WORD(~0) & + ad9361_spi_read(clk_priv->spi, reg - 3)); + buf[4] = integer & 0xFF; + + ad9361_spi_writem(clk_priv->spi, reg, buf, 5); + ad9361_spi_writef(clk_priv->spi, REG_RFPLL_DIVIDERS, div_mask, vco_div); + + ret = ad9361_check_cal_done(phy, lock_reg, VCO_LOCK, 1); + + /* In FDD mode with RX LO == TX LO frequency we use TDD tables to + * reduce VCO pulling + */ + + if (((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && + (phy->current_tx_lo_freq == phy->current_rx_lo_freq) && + (phy->current_tx_use_tdd_table != phy->current_rx_use_tdd_table)) || + ((phy->pdata->fdd && !phy->pdata->fdd_independent_mode) && + (phy->current_tx_lo_freq != phy->current_rx_lo_freq) && + (phy->current_tx_use_tdd_table || phy->current_rx_use_tdd_table))) { + unsigned long _rate; + + switch (clk_priv->source) { + case RX_RFPLL_INT: + reg = REG_TX_FRACT_BYTE_2; + lock_reg = REG_TX_CP_OVERRANGE_VCO_LOCK; + div_mask = TX_VCO_DIVIDER(~0); + _rate = phy->current_tx_lo_freq; + break; + case TX_RFPLL_INT: + reg = REG_RX_FRACT_BYTE_2; + lock_reg = REG_RX_CP_OVERRANGE_VCO_LOCK; + div_mask = RX_VCO_DIVIDER(~0); + _rate = phy->current_rx_lo_freq; + break; + default: + return -EINVAL; + + } + + if (phy->current_tx_lo_freq != phy->current_rx_lo_freq) { + ad9361_calc_rfpll_int_divder(phy, ad9361_from_clk(_rate), + parent_rate, &integer, &fract, &vco_div, &vco); + + ad9361_fastlock_prepare(phy, clk_priv->source == RX_RFPLL_INT, 0, false); + } + + fixup_other = 1; + } + + } while (fixup_other); + + if (phy->pdata->tdd_skip_vco_cal) + ad9361_trx_vco_cal_control(phy, clk_priv->source == TX_RFPLL_INT, + false); + + return ret; +} + +/** + * Recalculate the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + + return phy->clks[clk_priv->source]->rate; +} + +/** + * Set the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + + phy->clks[clk_priv->source]->rate = rate; + + return 0; +} + +/** + * Recalculate the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param parent_rate The parent clock rate. + * @return The clock rate. + */ +uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + uint32_t rate; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_recalc_rate) + rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv); + else + rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY]); + } else { + rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[RX_RFPLL_INT], + phy->clks[RX_REFCLK]->rate); + } + break; + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_recalc_rate) + rate = phy->ad9361_rfpll_ext_recalc_rate(clk_priv); + else + rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY]); + } else { + rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[TX_RFPLL_INT], + phy->clks[TX_REFCLK]->rate); + } + break; + default: + rate = 0; + break; + } + + return rate; +} + +/** + * Calculate the closest possible clock rate that can be set. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return The closest possible clock rate that can be set. + */ +int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t round_rate; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_round_rate) + round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate); + else + round_rate = rate; + } else { + round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate, + &phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate); + } + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_round_rate) + round_rate = phy->ad9361_rfpll_ext_round_rate(clk_priv, rate); + else + round_rate = rate; + } else { + round_rate = ad9361_rfpll_int_round_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate, + &phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate); + } + break; + default: + round_rate = 0; + break; + } + + return round_rate; +} + +/** + * Set the clock rate. + * @param refclk_scale The refclk_scale structure. + * @param rate The clock rate. + * @param parent_rate The parent clock rate. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t ret; + + switch (clk_priv->source) { + case RX_RFPLL: + if (phy->pdata->use_ext_rx_lo) { + if (phy->ad9361_rfpll_ext_set_rate) + phy->ad9361_rfpll_ext_set_rate(clk_priv, rate); + else + ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[RX_RFPLL_DUMMY], rate); + } else { + ad9361_rfpll_int_set_rate(phy->ref_clk_scale[RX_RFPLL_INT], rate, + phy->clks[phy->ref_clk_scale[RX_RFPLL_INT]->parent_source]->rate); + } + /* Load Gain Table */ + ret = ad9361_load_gt(phy, ad9361_from_clk(rate), GT_RX1 + GT_RX2); + if (ret < 0) + return ret; + break; + case TX_RFPLL: + if (phy->pdata->use_ext_tx_lo) { + if (phy->ad9361_rfpll_ext_set_rate) + phy->ad9361_rfpll_ext_set_rate(clk_priv, rate); + else + ad9361_rfpll_dummy_set_rate(phy->ref_clk_scale[TX_RFPLL_DUMMY], rate); + } else { + ad9361_rfpll_int_set_rate(phy->ref_clk_scale[TX_RFPLL_INT], rate, + phy->clks[phy->ref_clk_scale[TX_RFPLL_INT]->parent_source]->rate); + } + /* For RX LO we typically have the tracking option enabled + * so for now do nothing here. + */ + if (phy->auto_cal_en && (clk_priv->source == TX_RFPLL_INT)) + if (abs((int64_t)(phy->last_tx_quad_cal_freq - ad9361_from_clk(rate))) > + (int64_t)phy->cal_threshold_freq) { + ret = ad9361_do_calib_run(phy, TX_QUAD_CAL, -1); + if (ret < 0) + dev_err(&phy->spi->dev, + "%s: TX QUAD cal failed", __func__); + phy->last_tx_quad_cal_freq = ad9361_from_clk(rate); + } + break; + default: + break; + } + + return 0; +} + +/** + * Set clock mux parent. + * @param refclk_scale The refclk_scale structure. + * @param index Index - Enable (1), disable (0) ext lo. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index) +{ + struct ad9361_rf_phy *phy = clk_priv->phy; + int32_t ret; + + dev_dbg(&clk_priv->spi->dev, "%s: index %d", __func__, index); + + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + ret = ad9361_trx_ext_lo_control(phy, clk_priv->source == TX_RFPLL, index == 1); + if (ret >= 0) + clk_priv->mult = index; + + ad9361_ensm_restore_prev_state(phy); + + return ret; +} + +/** + * Register and initialize a new clock. + * @param phy The AD9361 state structure. + * @param name The name of the new clock. + * @param parent_name The name of the parent clock. + * @param flags The flags. + * @param source The source of the new clock. + * @param parent_source The source of the parent clock. + * @return A struct clk for the new clock or a negative error code. + */ +static struct clk *ad9361_clk_register(struct ad9361_rf_phy *phy, const char *name, + const char *parent_name, uint32_t flags, + uint32_t source, uint32_t parent_source) +{ + struct refclk_scale *clk_priv; + struct clk *clk; + + if (name) { + // Unused variable - fix compiler warning + } + if (parent_name) { + // Unused variable - fix compiler warning + } + if (flags) { + // Unused variable - fix compiler warning + } + + clk_priv = (struct refclk_scale *)malloc(sizeof(*clk_priv)); + if (!clk_priv) { + dev_err(&phy->spi->dev, "ad9361_clk_register: could not allocate fixed factor clk"); + return (struct clk *)ERR_PTR(-ENOMEM); + } + + /* struct refclk_scale assignments */ + clk_priv->source = (enum ad9361_clocks)source; + clk_priv->parent_source = (enum ad9361_clocks)parent_source; + clk_priv->spi = phy->spi; + clk_priv->phy = phy; + + phy->ref_clk_scale[source] = clk_priv; + + clk = (struct clk *)malloc(sizeof(*clk)); + if (!clk) { + free(clk_priv); + return (struct clk *)ERR_PTR(-ENOMEM); + } + + switch (source) { + case TX_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case RX_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case BB_REFCLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clk_refin->rate); + break; + case BBPLL_CLK: + clk->rate = ad9361_bbpll_recalc_rate(clk_priv, phy->clks[BB_REFCLK]->rate); + break; + case ADC_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[BBPLL_CLK]->rate); + break; + case R2_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate); + break; + case R1_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R2_CLK]->rate); + break; + case CLKRF_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[R1_CLK]->rate); + break; + case RX_SAMPL_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKRF_CLK]->rate); + break; + case DAC_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[ADC_CLK]->rate); + break; + case T2_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[DAC_CLK]->rate); + break; + case T1_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T2_CLK]->rate); + break; + case CLKTF_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[T1_CLK]->rate); + break; + case TX_SAMPL_CLK: + clk->rate = ad9361_clk_factor_recalc_rate(clk_priv, phy->clks[CLKTF_CLK]->rate); + break; + case RX_RFPLL_INT: + clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[RX_REFCLK]->rate); + break; + case TX_RFPLL_INT: + clk->rate = ad9361_rfpll_int_recalc_rate(clk_priv, phy->clks[TX_REFCLK]->rate); + break; + case RX_RFPLL_DUMMY: + clk->rate = phy->pdata->rx_synth_freq; + break; + case TX_RFPLL_DUMMY: + clk->rate = phy->pdata->tx_synth_freq; + break; + case RX_RFPLL: + clk->rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case TX_RFPLL: + clk->rate = ad9361_rfpll_recalc_rate(clk_priv); + } + + return clk; +} + +/** + * Register and initialize all the system clocks. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t register_clocks(struct ad9361_rf_phy *phy) +{ + uint32_t flags = CLK_GET_RATE_NOCACHE; + + phy->clk_data.clks = (struct clk **)malloc(sizeof(*phy->clk_data.clks) * + NUM_AD9361_CLKS); + if (!phy->clk_data.clks) { + dev_err(&phy->spi->dev, "could not allocate memory"); + return -ENOMEM; + } + + phy->clk_data.clk_num = NUM_AD9361_CLKS; + + /* Scaled Reference Clocks */ + phy->clks[TX_REFCLK] = ad9361_clk_register(phy, + "tx_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + TX_REFCLK, EXT_REF_CLK); + + phy->clks[RX_REFCLK] = ad9361_clk_register(phy, + "rx_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + RX_REFCLK, EXT_REF_CLK); + + phy->clks[BB_REFCLK] = ad9361_clk_register(phy, + "bb_refclk", "ad9361_ext_refclk", + flags | CLK_IGNORE_UNUSED, + BB_REFCLK, EXT_REF_CLK); + + /* Base Band PLL Clock */ + phy->clks[BBPLL_CLK] = ad9361_clk_register(phy, + "bbpll_clk", "bb_refclk", + flags | CLK_IGNORE_UNUSED, + BBPLL_CLK, BB_REFCLK); + + phy->clks[ADC_CLK] = ad9361_clk_register(phy, + "adc_clk", "bbpll_clk", + flags | CLK_IGNORE_UNUSED, + ADC_CLK, BBPLL_CLK); + + phy->clks[R2_CLK] = ad9361_clk_register(phy, + "r2_clk", "adc_clk", + flags | CLK_IGNORE_UNUSED, + R2_CLK, ADC_CLK); + + phy->clks[R1_CLK] = ad9361_clk_register(phy, + "r1_clk", "r2_clk", + flags | CLK_IGNORE_UNUSED, + R1_CLK, R2_CLK); + + phy->clks[CLKRF_CLK] = ad9361_clk_register(phy, + "clkrf_clk", "r1_clk", + flags | CLK_IGNORE_UNUSED, + CLKRF_CLK, R1_CLK); + + phy->clks[RX_SAMPL_CLK] = ad9361_clk_register(phy, + "rx_sampl_clk", "clkrf_clk", + flags | CLK_IGNORE_UNUSED, + RX_SAMPL_CLK, CLKRF_CLK); + + + phy->clks[DAC_CLK] = ad9361_clk_register(phy, + "dac_clk", "adc_clk", + flags | CLK_IGNORE_UNUSED, + DAC_CLK, ADC_CLK); + + phy->clks[T2_CLK] = ad9361_clk_register(phy, + "t2_clk", "dac_clk", + flags | CLK_IGNORE_UNUSED, + T2_CLK, DAC_CLK); + + phy->clks[T1_CLK] = ad9361_clk_register(phy, + "t1_clk", "t2_clk", + flags | CLK_IGNORE_UNUSED, + T1_CLK, T2_CLK); + + phy->clks[CLKTF_CLK] = ad9361_clk_register(phy, + "clktf_clk", "t1_clk", + flags | CLK_IGNORE_UNUSED, + CLKTF_CLK, T1_CLK); + + phy->clks[TX_SAMPL_CLK] = ad9361_clk_register(phy, + "tx_sampl_clk", "clktf_clk", + flags | CLK_IGNORE_UNUSED, + TX_SAMPL_CLK, CLKTF_CLK); + + phy->clks[RX_RFPLL_INT] = ad9361_clk_register(phy, + "rx_rfpll", "rx_refclk", + flags | CLK_IGNORE_UNUSED, + RX_RFPLL_INT, RX_REFCLK); + + phy->clks[TX_RFPLL_INT] = ad9361_clk_register(phy, + "tx_rfpll", "tx_refclk", + flags | CLK_IGNORE_UNUSED, + TX_RFPLL_INT, TX_REFCLK); + + phy->clks[RX_RFPLL_DUMMY] = ad9361_clk_register(phy, + "rx_rfpll_dummy", NULL, + flags | CLK_IGNORE_UNUSED, + RX_RFPLL_DUMMY, 0); + + phy->clks[TX_RFPLL_DUMMY] = ad9361_clk_register(phy, + "tx_rfpll_dummy", NULL, + flags | CLK_IGNORE_UNUSED, + TX_RFPLL_DUMMY, 0); + + phy->clks[RX_RFPLL] = ad9361_clk_register(phy, + "rx_rfpll", NULL, + flags | CLK_IGNORE_UNUSED, + RX_RFPLL, 0); + + phy->clks[TX_RFPLL] = ad9361_clk_register(phy, + "tx_rfpll", NULL, + flags | CLK_IGNORE_UNUSED, + TX_RFPLL, 0); + + return 0; +} + +/** + * Perform an RSSI gain step calibration. + * Note: Before running the function, provide a single tone within the channel + * bandwidth and monitor the received data. Adjust the tone amplitude until + * the received data is within a few dB of full scale but not overloading. + * @param phy The AD9361 state structure. + * @return 0 in case of success, negative error code otherwise. + */ +int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy) +{ + uint32_t lna_error[4]; + uint32_t mixer_error[15]; + uint64_t lo_freq_hz; + uint8_t lo_index; + uint8_t i; + + lo_freq_hz = ad9361_from_clk(clk_get_rate(phy, + phy->ref_clk_scale[RX_RFPLL])); + if (lo_freq_hz < 1300000000ULL) + lo_index = 0; + else + if (lo_freq_hz < 3300000000ULL) + lo_index = 1; + else + if (lo_freq_hz < 4100000000ULL) + lo_index = 2; + else + lo_index = 3; + + /* Put the AD9361 into the Alert state. */ + ad9361_ensm_force_state(phy, ENSM_STATE_ALERT); + + /* Program the directly-addressable register values. */ + ad9361_spi_write(phy->spi, REG_MAX_MIXER_CALIBRATION_GAIN_INDEX, + MAX_MIXER_CALIBRATION_GAIN_INDEX(0x0F)); + ad9361_spi_write(phy->spi, REG_MEASURE_DURATION, + GAIN_CAL_MEAS_DURATION(0x0E)); + ad9361_spi_write(phy->spi, REG_SETTLE_TIME, + SETTLE_TIME(0x3F)); + ad9361_spi_write(phy->spi, REG_RSSI_CONFIG, + RSSI_MODE_SELECT(0x3) | DEFAULT_RSSI_MEAS_MODE); + ad9361_spi_write(phy->spi, REG_MEASURE_DURATION_01, + MEASUREMENT_DURATION_0(0x0E)); + ad9361_spi_write(phy->spi, REG_LNA_GAIN, + gain_step_calib_reg_val[lo_index][0]); + + /* Program the LNA gain step words into the internal table. */ + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, + gain_step_calib_reg_val[lo_index][i+1]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_LNA_GAIN_DIFF | START_CALIB_TABLE_CLOCK); + udelay(3); //Wait for data to fully write to internal table + } + + ad9361_spi_write(phy->spi, REG_CONFIG, START_CALIB_TABLE_CLOCK); + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + /* Run and wait until the calibration completes. */ + ad9361_run_calibration(phy, RX_GAIN_STEP_CAL); + + /* Read the LNA and Mixer error terms into nonvolatile memory. */ + ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1) | READ_SELECT); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + lna_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); + } + ad9361_spi_write(phy->spi, REG_CONFIG, CALIB_TABLE_SELECT(0x1)); + for(i = 0; i < 15; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + mixer_error[i] = ad9361_spi_read(phy->spi, REG_GAIN_ERROR_READ); + } + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + /* Programming gain step errors into the AD9361 in the field */ + ad9361_spi_write(phy->spi, + REG_CONFIG, CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 4; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, lna_error[i]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_LNA_ERROR_TABLE | START_CALIB_TABLE_CLOCK); + } + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | START_CALIB_TABLE_CLOCK); + for(i = 0; i < 15; i++) { + ad9361_spi_write(phy->spi, REG_WORD_ADDRESS, i); + ad9361_spi_write(phy->spi, REG_GAIN_DIFF_WORDERROR_WRITE, mixer_error[i]); + ad9361_spi_write(phy->spi, REG_CONFIG, + CALIB_TABLE_SELECT(0x3) | WRITE_MIXER_ERROR_TABLE | START_CALIB_TABLE_CLOCK); + } + ad9361_spi_write(phy->spi, REG_CONFIG, 0x00); + + ad9361_ensm_restore_prev_state(phy); + + return 0; +} diff --git a/src/ad9361_lib/ad9361_dev.h b/src/ad9361_lib/ad9361_dev.h new file mode 100644 index 0000000..8192c07 --- /dev/null +++ b/src/ad9361_lib/ad9361_dev.h @@ -0,0 +1,3510 @@ +/***************************************************************************//** + * @file ad9361.h + * @brief Header file of AD9361 Driver. +******************************************************************************** + * Copyright 2014(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef IIO_FREQUENCY_AD9361_H_ +#define IIO_FREQUENCY_AD9361_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "util.h" +#include "common.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define REG_SPI_CONF 0x000 /* SPI Configuration */ +#define REG_MULTICHIP_SYNC_AND_TX_MON_CTRL 0x001 /* Multi-Chip Sync and Tx Mon Control */ +#define REG_TX_ENABLE_FILTER_CTRL 0x002 /* Tx Enable & Filter Control */ +#define REG_RX_ENABLE_FILTER_CTRL 0x003 /* Rx Enable & Filter Control */ +#define REG_INPUT_SELECT 0x004 /* Input Select */ +#define REG_RFPLL_DIVIDERS 0x005 /* RFPLL Dividers */ +#define REG_RX_CLOCK_DATA_DELAY 0x006 /* Rx Clock & Data Delay */ +#define REG_TX_CLOCK_DATA_DELAY 0x007 /* Tx Clock & Data Delay */ +#define REG_CLOCK_ENABLE 0x009 /* Clock Enable */ +#define REG_BBPLL 0x00A /* BBPLL */ +#define REG_TEMP_OFFSET 0x00B /* Offset */ +#define REG_START_TEMP_READING 0x00C /* Start Temp Reading */ +#define REG_TEMP_SENSE2 0x00D /* Temp Sense2 */ +#define REG_TEMPERATURE 0x00E /* Temperature */ +#define REG_TEMP_SENSOR_CONFIG 0x00F /* Temp Sensor Config */ +#define REG_PARALLEL_PORT_CONF_1 0x010 /* Parallel Port Configuration 1 */ +#define REG_PARALLEL_PORT_CONF_2 0x011 /* Parallel Port Configuration 2 */ +#define REG_PARALLEL_PORT_CONF_3 0x012 /* Parallel Port Configuration 3 */ +#define REG_ENSM_MODE 0x013 /* ENSM Mode */ +#define REG_ENSM_CONFIG_1 0x014 /* ENSM Config 1 */ +#define REG_ENSM_CONFIG_2 0x015 /* ENSM Config 2 */ +#define REG_CALIBRATION_CTRL 0x016 /* Calibration Control */ +#define REG_STATE 0x017 /* State */ +#define REG_AUXDAC_1_WORD 0x018 /* AuxDAC 1 Word */ +#define REG_AUXDAC_2_WORD 0x019 /* AuxDAC 2 Word */ +#define REG_AUXDAC_1_CONFIG 0x01A /* AuxDAC 1 Config */ +#define REG_AUXDAC_2_CONFIG 0x01B /* AuxDAC 2 Config */ +#define REG_AUXADC_CLOCK_DIVIDER 0x01C /* AuxADC Clock Divider */ +#define REG_AUXADC_CONFIG 0x01D /* Aux ADC Config */ +#define REG_AUXADC_WORD_MSB 0x01E /* AuxADC Word MSB */ +#define REG_AUXADC_LSB 0x01F /* AuxADC LSB */ +#define REG_AUTO_GPO 0x020 /* Auto GPO */ +#define REG_AGC_GAIN_LOCK_DELAY 0x021 /* AGC Gain Lock Delay */ +#define REG_AGC_ATTACK_DELAY 0x022 /* AGC Attack Delay */ +#define REG_AUXDAC_ENABLE_CTRL 0x023 /* AuxDAC Enable Control */ +#define REG_RX_LOAD_SYNTH_DELAY 0x024 /* RX Load Synth Delay */ +#define REG_TX_LOAD_SYNTH_DELAY 0x025 /* TX Load Synth Delay */ +#define REG_EXTERNAL_LNA_CTRL 0x026 /* External LNA control */ +#define REG_GPO_FORCE_AND_INIT 0x027 /* GPO Force and Init */ +#define REG_GPO0_RX_DELAY 0x028 /* GPO0 Rx delay */ +#define REG_GPO1_RX_DELAY 0x029 /* GPO1 Rx delay */ +#define REG_GPO2_RX_DELAY 0x02A /* GPO2 Rx delay */ +#define REG_GPO3_RX_DELAY 0x02B /* GPO3 Rx delay */ +#define REG_GPO0_TX_DELAY 0x02C /* GPO0 Tx Delay */ +#define REG_GPO1_TX_DELAY 0x02D /* GPO1 Tx Delay */ +#define REG_GPO2_TX_DELAY 0x02E /* GPO2 Tx Delay */ +#define REG_GPO3_TX_DELAY 0x02F /* GPO3 Tx Delay */ +#define REG_AUXDAC1_RX_DELAY 0x030 /* AuxDAC1 Rx Delay */ +#define REG_AUXDAC1_TX_DELAY 0x031 /* AuxDAC1 Tx Delay */ +#define REG_AUXDAC2_RX_DELAY 0x032 /* AuxDAC2 Rx Delay */ +#define REG_AUXDAC2_TX_DELAY 0x033 /* AuxDAC2 Tx Delay */ +#define REG_CTRL_OUTPUT_POINTER 0x035 /* Control Output Pointer */ +#define REG_CTRL_OUTPUT_ENABLE 0x036 /* Control Output Enable */ +#define REG_PRODUCT_ID 0x037 /* Product ID */ +#define REG_REFERENCE_CLOCK_CYCLES 0x03A /* Reference Clock Cycles */ +#define REG_DIGITAL_IO_CTRL 0x03B /* Digital I/O Control */ +#define REG_LVDS_BIAS_CTRL 0x03C /* LVDS Bias control */ +#define REG_LVDS_INVERT_CTRL1 0x03D /* LVDS Invert control1 */ +#define REG_LVDS_INVERT_CTRL2 0x03E /* LVDS Invert control2 */ +#define REG_SDM_CTRL_1 0x03F /* SDM Control 1 */ +#define REG_FRACT_BB_FREQ_WORD_1 0x041 /* Fractional BB Freq Word 1 */ +#define REG_FRACT_BB_FREQ_WORD_2 0x042 /* Fractional BB Freq Word 2 */ +#define REG_FRACT_BB_FREQ_WORD_3 0x043 /* Fractional BB Freq Word 3 */ +#define REG_INTEGER_BB_FREQ_WORD 0x044 /* Integer BB Freq Word */ +#define REG_CLOCK_CTRL 0x045 /* Clock Control */ +#define REG_CP_CURRENT 0x046 /* CP Current */ +#define REG_CP_BLEED_CURRENT 0x047 /* CP Bleed Current */ +#define REG_LOOP_FILTER_1 0x048 /* Loop Filter 1 */ +#define REG_LOOP_FILTER_2 0x049 /* Loop Filter 2 */ +#define REG_LOOP_FILTER_3 0x04A /* Loop Filter 3 */ +#define REG_VCO_CTRL 0x04B /* VCO Control */ +#define REG_VCO_PROGRAM_1 0x04C +#define REG_VCO_PROGRAM_2 0x04D +#define REG_SDM_CTRL 0x04E /* SDM Control */ +#define REG_RX_SYNTH_POWER_DOWN_OVERRIDE 0x050 /* Rx Synth Power Down Override */ +#define REG_TX_SYNTH_POWER_DOWN_OVERRIDE 0x051 /* TX Synth Power Down Override */ +#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1 0x052 /* Rx Analog Power Down Override 1 */ +#define REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2 0x053 /* Rx Analog Power Down Override 2 */ +#define REG_RX1_ADC_POWER_DOWN_OVERRIDE 0x054 /* Rx1 ADC Power Down Override */ +#define REG_RX2_ADC_POWER_DOWN_OVERRIDE 0x055 /* Rx2 ADC Power Down Override */ +#define REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1 0x056 /* Tx Analog Power Down Override 1 */ +#define REG_ANALOG_POWER_DOWN_OVERRIDE 0x057 /* Analog Power Down Override */ +#define REG_MISC_POWER_DOWN_OVERRIDE 0x058 /* Misc Power Down Override */ +#define REG_CH_1_OVERFLOW 0x05E /* CH 1 Overflow */ +#define REG_CH_2_OVERFLOW 0x05F /* CH 2 Overflow */ +#define REG_TX_FILTER_COEF_ADDR 0x060 /* TX Filter Coefficient Address */ +#define REG_TX_FILTER_COEF_WRITE_DATA_1 0x061 /* TX Filter Coefficient Write Data 1 */ +#define REG_TX_FILTER_COEF_WRITE_DATA_2 0x062 /* TX Filter Coefficient Write Data 2 */ +#define REG_TX_FILTER_COEF_READ_DATA_1 0x063 /* TX Filter Coefficient Read Data 1 */ +#define REG_TX_FILTER_COEF_READ_DATA_2 0x064 /* TX Filter Coefficient Read Data 2 */ +#define REG_TX_FILTER_CONF 0x065 /* TX Filter Configuration */ +#define REG_TX_MON_LOW_GAIN 0x067 /* Tx Mon Low Gain */ +#define REG_TX_MON_HIGH_GAIN 0x068 /* Tx Mon High Gain */ +#define REG_TX_MON_DELAY 0x069 /* Tx Mon Delay */ +#define REG_TX_LEVEL_THRESH 0x06A /* Tx Level Threshold */ +#define REG_TX_RSSI1 0x06B /* TX RSSI1 */ +#define REG_TX_RSSI2 0x06C /* TX RSSI2 */ +#define REG_TX_RSSI_LSB 0x06D /* TX RSSI LSB */ +#define REG_TPM_MODE_ENABLE 0x06E /* TPM Mode Enable */ +#define REG_TX_MON_TEMP_GAIN_COEF 0x06F /* Temp Gain Coefficient */ +#define REG_TX_MON_1_CONFIG 0x070 /* Tx Mon 1 Config */ +#define REG_TX_MON_2_CONFIG 0x071 /* Tx Mon 2 Config */ +#define REG_TX1_ATTEN_0 0x073 /* Tx1 Atten 0 */ +#define REG_TX1_ATTEN_1 0x074 /* Tx1 Atten 1 */ +#define REG_TX2_ATTEN_0 0x075 /* Tx2 Atten 0 */ +#define REG_TX2_ATTEN_1 0x076 /* Tx2 Atten 1 */ +#define REG_TX_ATTEN_OFFSET 0x077 /* Tx Atten Offset */ +#define REG_TX_ATTEN_THRESH 0x078 /* Tx Atten Threshold */ +#define REG_TX1_DIG_ATTEN 0x079 /* Tx1 Dig Attenuation */ +#define REG_TX2_DIG_ATTEN 0x07C /* Tx2 Dig Attenuation */ +#define REG_TX1_SYMBOL_ATTEN 0x07F /* TX1 Symbol Attenuation */ +#define REG_TX2_SYMBOL_ATTEN 0x080 /* TX2 Symbol Attenuation */ +#define REG_TX_SYMBOL_ATTEN_CONFIG 0x081 /* TX Symbol Atten Config */ +#define REG_TX1_OUT_1_PHASE_CORR 0x08E /* Tx1 Out 1 Phase Corr */ +#define REG_TX1_OUT_1_GAIN_CORR 0x08F /* Tx1 Out 1 Gain Corr */ +#define REG_TX2_OUT_1_PHASE_CORR 0x090 /* Tx2 Out 1 Phase Corr */ +#define REG_TX2_OUT_1_GAIN_CORR 0x091 /* Tx2 Out 1 Gain Corr */ +#define REG_TX1_OUT_1_OFFSET_I 0x092 /* Tx1 Out 1 Offset I */ +#define REG_TX1_OUT_1_OFFSET_Q 0x093 /* Tx1 Out 1 Offset Q */ +#define REG_TX2_OUT_1_OFFSET_I 0x094 /* Tx2 Out 1 Offset I */ +#define REG_TX2_OUT_1_OFFSET_Q 0x095 /* Tx2 Out 1 Offset Q */ +#define REG_TX1_OUT_2_PHASE_CORR 0x096 /* Tx1 Out 2 Phase Corr */ +#define REG_TX1_OUT_2_GAIN_CORR 0x097 /* Tx1 Out 2 Gain Corr */ +#define REG_TX2_OUT_2_PHASE_CORR 0x098 /* Tx2 Out 2 Phase Corr */ +#define REG_TX2_OUT_2_GAIN_CORR 0x099 /* Tx2 Out 2 Gain Corr */ +#define REG_TX1_OUT_2_OFFSET_I 0x09A /* Tx1 Out 2 Offset I */ +#define REG_TX1_OUT_2_OFFSET_Q 0x09B /* Tx1 Out 2 Offset Q */ +#define REG_TX2_OUT_2_OFFSET_I 0x09C /* Tx2 Out 2 Offset I */ +#define REG_TX2_OUT_2_OFFSET_Q 0x09D /* Tx2 Out 2 Offset Q */ +#define REG_TX_FORCE_BITS 0x09F /* Force Bits */ +#define REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET 0x0A0 /* Quad Cal NCO Freq & Phase Offset */ +#define REG_QUAD_CAL_CTRL 0x0A1 /* Quad Cal Control */ +#define REG_KEXP_1 0x0A2 /* Kexp 1 */ +#define REG_KEXP_2 0x0A3 /* Kexp 2 */ +#define REG_QUAD_SETTLE_COUNT 0x0A4 /* QUAD Settle count */ +#define REG_MAG_FTEST_THRESH 0x0A5 /* Mag. Ftest Thresh */ +#define REG_MAG_FTEST_THRESH_2 0x0A6 /* Mag. Ftest Thresh 2 */ +#define REG_QUAD_CAL_STATUS_TX1 0x0A7 /* Quad cal status Tx1 */ +#define REG_QUAD_CAL_STATUS_TX2 0x0A8 /* Quad cal status Tx2 */ +#define REG_QUAD_CAL_COUNT 0x0A9 /* Quad cal Count */ +#define REG_TX_QUAD_FULL_LMT_GAIN 0x0AA /* Tx Quad Full/LMT Gain */ +#define REG_SQUARER_CONFIG 0x0AB /* Squarer Config */ +#define REG_TX_QUAD_CAL_ATTEN 0x0AC /* TX Quad Cal Atten */ +#define REG_THRESH_ACCUM 0x0AD /* Thresh Accum */ +#define REG_TX_QUAD_LPF_GAIN 0x0AE /* Tx Quad LPF Gain */ +#define REG_TXDAC_VDS_I 0x0B0 /* TxDAC Vds I */ +#define REG_TXDAC_VDS_Q 0x0B1 /* TxDAC Vds Q */ +#define REG_TXDAC_GN_I 0x0B2 /* TxDAC gn I */ +#define REG_TXDAC_GN_Q 0x0B3 /* TxDAC gn Q */ +#define REG_TXBBF_OPAMP_A 0x0C0 /* TxBBF OpAmp A */ +#define REG_TXBBF_OPAMP_B 0x0C1 /* TxBBF OpAmp B */ +#define REG_TX_BBF_R1 0x0C2 /* Tx BBF R1 */ +#define REG_TX_BBF_R2 0x0C3 /* Tx BBF R2 */ +#define REG_TX_BBF_R3 0x0C4 /* Tx BBF R3 */ +#define REG_TX_BBF_R4 0x0C5 /* Tx BBF R4 */ +#define REG_TX_BBF_RP 0x0C6 /* Tx BBF RP */ +#define REG_TX_BBF_C1 0x0C7 /* Tx BBF C1 */ +#define REG_TX_BBF_C2 0x0C8 /* Tx BBF C2 */ +#define REG_TX_BBF_CP 0x0C9 /* Tx BBF Cp */ +#define REG_TX_TUNE_CTRL 0x0CA /* Tx Tune Control */ +#define REG_TX_BBF_R2B 0x0CB /* Tx BBF R2b */ +#define REG_TX_BBF_TUNE 0x0CC /* Tx BBF Tune */ +#define REG_CONFIG0 0x0D0 /* Config0 */ +#define REG_RESISTOR 0x0D1 /* Resistor */ +#define REG_CAPACITOR 0x0D2 /* Capacitor */ +#define REG_LO_CM 0x0D3 /* LO CM */ +#define REG_TX_BBF_TUNE_DIVIDER 0x0D6 /* TX BBF Tune Divider */ +#define REG_TX_BBF_TUNE_MODE 0x0D7 /* TX BBF Tune Mode */ +#define REG_RX_FILTER_COEF_ADDR 0x0F0 /* Rx Filter Coeff Addr */ +#define REG_RX_FILTER_COEF_DATA_1 0x0F1 /* Rx Filter Coeff Data 1 */ +#define REG_RX_FILTER_COEF_DATA_2 0x0F2 /* Rx Filter Coeff Data 2 */ +#define REG_RX_FILTER_COEF_READ_DATA_1 0x0F3 /* Rx Filter Coeff Read Data 1 */ +#define REG_RX_FILTER_COEF_READ_DATA_2 0x0F4 /* Rx Filter Coeff Read Data 2 */ +#define REG_RX_FILTER_CONFIG 0x0F5 /* Rx Filter Config */ +#define REG_RX_FILTER_GAIN 0x0F6 /* Rx Filter Gain */ +#define REG_AGC_CONFIG_1 0x0FA /* AGC Config1 */ +#define REG_AGC_CONFIG_2 0x0FB /* AGC config2 */ +#define REG_AGC_CONFIG_3 0x0FC /* AGC Config3 */ +#define REG_MAX_LMT_FULL_GAIN 0x0FD /* Max LMT/Full Gain */ +#define REG_PEAK_WAIT_TIME 0x0FE /* Peak Wait Time */ +#define REG_DIGITAL_GAIN 0x100 /* Digital Gain */ +#define REG_AGC_LOCK_LEVEL 0x101 /* AGC Lock Level */ +#define REG_ADC_NOISE_CORRECTION_FACTOR 0x102 /* ADC noise Correction Factor */ +#define REG_GAIN_STP_CONFIG1 0x103 /* Gain Step Config1 */ +#define REG_ADC_SMALL_OVERLOAD_THRESH 0x104 /* ADC Small Overload Threshold */ +#define REG_ADC_LARGE_OVERLOAD_THRESH 0x105 /* ADC Large Overload Threshold */ +#define REG_GAIN_STP_CONFIG_2 0x106 /* Gain Step Config 2 */ +#define REG_SMALL_LMT_OVERLOAD_THRESH 0x107 /* Small LMT Overload Threshold */ +#define REG_LARGE_LMT_OVERLOAD_THRESH 0x108 /* Large LMT Overload Threshold */ +#define REG_RX1_MANUAL_LMT_FULL_GAIN 0x109 /* Rx1 Manual LMT/Full Gain */ +#define REG_RX1_MANUAL_LPF_GAIN 0x10A /* Rx1 Manual LPF gain */ +#define REG_RX1_MANUAL_DIGITALFORCED_GAIN 0x10B /* Rx1 Manual Digital/Forced Gain */ +#define REG_RX2_MANUAL_LMT_FULL_GAIN 0x10C /* Rx2 Manual LMT/Full Gain */ +#define REG_RX2_MANUAL_LPF_GAIN 0x10D /* Rx2 Manual LPF Gain */ +#define REG_RX2_MANUAL_DIGITALFORCED_GAIN 0x10E /* Rx2 Manual Digital/Forced Gain */ +#define REG_FAST_CONFIG_1 0x110 /* Config 1 */ +#define REG_FAST_CONFIG_2_SETTLING_DELAY 0x111 /* Config 2 & Settling Delay */ +#define REG_FAST_ENERGY_LOST_THRESH 0x112 /* Energy Lost Threshold */ +#define REG_FAST_STRONGER_SIGNAL_THRESH 0x113 /* Stronger Signal Threshold */ +#define REG_FAST_LOW_POWER_THRESH 0x114 /* Low Power Threshold */ +#define REG_FAST_STRONG_SIGNAL_FREEZE 0x115 /* Strong Signal Freeze */ +#define REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN 0x116 /* Final Over Range and Opt Gain */ +#define REG_FAST_ENERGY_DETECT_COUNT 0x117 /* Energy Detect Count */ +#define REG_FAST_AGCLL_UPPER_LIMIT 0x118 /* AGCLL Upper Limit */ +#define REG_FAST_GAIN_LOCK_EXIT_COUNT 0x119 /* Gain Lock Exit Count */ +#define REG_FAST_INITIAL_LMT_GAIN_LIMIT 0x11A /* Initial LMT Gain Limit */ +#define REG_FAST_INCREMENT_TIME 0x11B /* Increment Time */ +#define REG_AGC_INNER_LOW_THRESH 0x120 /* AGC Inner Low Threshold */ +#define REG_LMT_OVERLOAD_COUNTERS 0x121 /* LMT Overload Counters */ +#define REG_ADC_OVERLOAD_COUNTERS 0x122 /* ADC Overload Counters */ +#define REG_GAIN_STP1 0x123 /* Gain Step1 */ +#define REG_GAIN_UPDATE_COUNTER1 0x124 /* Gain Update Counter1 */ +#define REG_GAIN_UPDATE_COUNTER2 0x125 /* Gain Update Counter2 */ +#define REG_DIGITAL_SAT_COUNTER 0x128 /* Digital Sat Counter */ +#define REG_OUTER_POWER_THRESHS 0x129 /* Outer Power Thresholds */ +#define REG_GAIN_STP_2 0x12A /* Gain Step 2 */ +#define REG_EXT_LNA_HIGH_GAIN 0x12C /* Ext LNA High Gain */ +#define REG_EXT_LNA_LOW_GAIN 0x12D /* Ext LNA Low Gain */ +#define REG_GAIN_TABLE_ADDRESS 0x130 /* Gain Table Address */ +#define REG_GAIN_TABLE_WRITE_DATA1 0x131 /* Gain Table Write Data1 */ +#define REG_GAIN_TABLE_WRITE_DATA2 0x132 /* Gain Table Write Data2 */ +#define REG_GAIN_TABLE_WRITE_DATA3 0x133 /* Gain Table Write Data 3 */ +#define REG_GAIN_TABLE_READ_DATA1 0x134 /* Gain Table Read Data 1 */ +#define REG_GAIN_TABLE_READ_DATA2 0x135 /* Gain Table Read Data 2 */ +#define REG_GAIN_TABLE_READ_DATA3 0x136 /* Gain Table Read Data 3 */ +#define REG_GAIN_TABLE_CONFIG 0x137 /* Gain Table Config */ +#define REG_GM_SUB_TABLE_ADDRESS 0x138 /* Gm Sub Table Address */ +#define REG_GM_SUB_TABLE_GAIN_WRITE 0x139 /* Gm Sub Table Gain Word Write */ +#define REG_GM_SUB_TABLE_BIAS_WRITE 0x13A /* Gm Sub Table Bias Word Write */ +#define REG_GM_SUB_TABLE_CTRL_WRITE 0x13B /* Gm Sub Table Control Word Write */ +#define REG_GM_SUB_TABLE_GAIN_READ 0x13C /* Gm Sub Table Gain Word Read */ +#define REG_GM_SUB_TABLE_BIAS_READ 0x13D /* Gm Sub Table Bias Word Read */ +#define REG_GM_SUB_TABLE_CTRL_READ 0x13E /* Gm Sub Table Control Word Read */ +#define REG_GM_SUB_TABLE_CONFIG 0x13F /* Gm Sub Table Config */ +#define REG_WORD_ADDRESS 0x140 /* Word Address */ +#define REG_GAIN_DIFF_WORDERROR_WRITE 0x141 /* Gain Diff Word/Error Write */ +#define REG_GAIN_ERROR_READ 0x142 /* Gain Error Read */ +#define REG_CONFIG 0x143 /* Config */ +#define REG_LNA_GAIN_DIFF_READ_BACK 0x144 /* LNA Gain Diff Read Back */ +#define REG_MAX_MIXER_CALIBRATION_GAIN_INDEX 0x145 /* Max Mixer Calibration Gain Index */ +#define REG_TEMP_GAIN_COEF 0x146 /* Temp Gain Coefficient */ +#define REG_SETTLE_TIME 0x147 /* Settle Time */ +#define REG_MEASURE_DURATION 0x148 /* Measure Duration */ +#define REG_CAL_TEMP_SENSOR_WORD 0x149 /* Cal Temp sensor word */ +#define REG_MEASURE_DURATION_01 0x150 /* Measure Duration 0&1 */ +#define REG_MEASURE_DURATION_23 0x151 /* Measure Duration 2&3 */ +#define REG_RSSI_WEIGHT_0 0x152 /* RSSI Weight 0 */ +#define REG_RSSI_WEIGHT_1 0x153 /* RSSI Weight 1 */ +#define REG_RSSI_WEIGHT_2 0x154 /* RSSI Weight 2 */ +#define REG_RSSI_WEIGHT_3 0x155 /* RSSI Weight 3 */ +#define REG_RSSI_DELAY 0x156 /* RSSI delay */ +#define REG_RSSI_WAIT_TIME 0x157 /* RSSI wait time */ +#define REG_RSSI_CONFIG 0x158 /* RSSI Config */ +#define REG_ADC_MEASURE_DURATION_01 0x159 /* ADC Measure Duration 0&1 */ +#define REG_ADC_WEIGHT_0 0x15A /* ADC Weight 0 */ +#define REG_ADC_WEIGHT_1 0x15B /* ADC Weight 1 */ +#define REG_DEC_POWER_MEASURE_DURATION_0 0x15C /* Dec Power Measure Duration 0 */ +#define REG_LNA_GAIN 0x15D /* LNA Gain */ +#define REG_CH1_ADC_POWER 0x160 /* CH1 ADC Power */ +#define REG_CH1_RX_FILTER_POWER 0x161 /* CH1 Rx filter Power */ +#define REG_CH2_ADC_POWER 0x162 /* CH2 ADC Power */ +#define REG_CH2_RX_FILTER_POWER 0x163 /* CH2 Rx filter Power */ +#define REG_RX_QUAD_CAL_LEVEL 0x168 /* Rx Quad Cal Level */ +#define REG_CALIBRATION_CONFIG_1 0x169 /* Calibration Config 1 */ +#define REG_CALIBRATION_CONFIG_2 0x16A /* Calibration config2 */ +#define REG_CALIBRATION_CONFIG_3 0x16B /* Calibration config3 */ +#define REG_CALIB_COUNT 0x16C /* Calib count */ +#define REG_SETTLE_COUNT 0x16D /* Settle count */ +#define REG_RX_QUAD_GAIN1 0x16E /* Rx Quad gain1 */ +#define REG_RX_QUAD_GAIN2 0x16F /* Rx Quad gain2 */ +#define REG_RX1_INPUT_A_PHASE_CORR 0x170 /* Rx1 Input A Phase Corr */ +#define REG_RX1_INPUT_A_GAIN_CORR 0x171 /* Rx1 Input A Gain Corr */ +#define REG_RX2_INPUT_A_PHASE_CORR 0x172 /* Rx2 Input A Phase Corr */ +#define REG_RX2_INPUT_A_GAIN_CORR 0x173 /* Rx2 Input A Gain Corr */ +#define REG_RX1_INPUT_A_Q_OFFSET 0x174 /* Rx1 Input A Q" Offset */ +#define REG_RX1_INPUT_A_OFFSETS 0x175 /* Rx1 Input A Offsets */ +#define REG_INPUT_A_OFFSETS_1 0x176 /* Input A Offsets 1 */ +#define REG_RX2_INPUT_A_OFFSETS 0x177 /* Rx2 Input A Offsets */ +#define REG_RX2_INPUT_A_I_OFFSET 0x178 /* Rx2 Input A "I" Offset */ +#define REG_RX1_INPUT_BC_PHASE_CORR 0x179 /* Rx1 Input B&C Phase Corr */ +#define REG_RX1_INPUT_BC_GAIN_CORR 0x17A /* Rx1 Input B&C Gain Corr */ +#define REG_RX2_INPUT_BC_PHASE_CORR 0x17B /* Rx2 Input B&C Phase Corr */ +#define REG_RX2_INPUT_BC_GAIN_CORR 0x17C /* Rx2 Input B&C Gain Corr */ +#define REG_RX1_INPUT_BC_Q_OFFSET 0x17D /* Rx1 Input B&C "Q" Offset */ +#define REG_RX1_INPUT_BC_OFFSETS 0x17E /* Rx1 Input B&C Offsets */ +#define REG_INPUT_BC_OFFSETS_1 0x17F /* Input B&C Offsets 1 */ +#define REG_RX2_INPUT_BC_OFFSETS 0x180 /* Rx2 Input B&C Offsets */ +#define REG_RX2_INPUT_BC_I_OFFSET 0x181 /* Rx2 Input B&C "I" Offset */ +#define REG_FORCE_BITS 0x182 /* Force Bits */ +#define REG_WAIT_COUNT 0x185 /* Wait Count */ +#define REG_RF_DC_OFFSET_COUNT 0x186 /* RF DC Offset Count */ +#define REG_RF_DC_OFFSET_CONFIG_1 0x187 /* RF DC Offset Config1 */ +#define REG_RF_DC_OFFSET_ATTEN 0x188 /* RF DC Offset Attenuation */ +#define REG_INVERT_BITS 0x189 /* Invert Bits */ +#define REG_DC_OFFSET_CONFIG2 0x18B /* DC Offset Config2 */ +#define REG_RF_CAL_GAIN_INDEX 0x18C /* RF Cal Gain Index */ +#define REG_SOI_THRESH 0x18D /* SOI Threshold */ +#define REG_BB_DC_OFFSET_SHIFT 0x190 /* BB DC Offset Shift */ +#define REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT 0x191 /* BB DC Offset Fast Settle Shift */ +#define REG_BB_FAST_SETTLE_DUR 0x192 /* BB Fast Settle Dur */ +#define REG_BB_DC_OFFSET_COUNT 0x193 /* BB DC Offset Count */ +#define REG_BB_DC_OFFSET_ATTEN 0x194 /* BB DC Offset Attenuation */ +#define REG_RX1_BB_DC_WORD_I_MSB 0x19A /* RX1 BB DC word I MSB */ +#define REG_RX1_BB_DC_WORD_I_LSB 0x19B /* RX1 BB DC word I LSB */ +#define REG_RX1_BB_DC_WORD_Q_MSB 0x19C /* RX1 BB DC word Q MSB */ +#define REG_RX1_BB_DC_WORD_Q_LSB 0x19D /* RX1 BB DC word Q LSB */ +#define REG_RX2_BB_DC_WORD_I_MSB 0x19E /* RX2 BB DC word I MSB */ +#define REG_RX2_BB_DC_WORD_I_LSB 0x19F /* RX2 BB DC word I LSB */ +#define REG_RX2_BB_DC_WORD_Q_MSB 0x1A0 /* RX2 BB DC word Q MSB */ +#define REG_RX2_BB_DC_WORD_Q_LSB 0x1A1 /* RX2 BB DC word Q LSB */ +#define REG_BB_TRACK_CORR_WORD_I_MSB 0x1A2 /* BB Track corr word I MSB */ +#define REG_BB_TRACK_CORR_WORD_I_LSB 0x1A3 /* BB Track corr word I LSB */ +#define REG_BB_TRACK_CORR_WORD_Q_MSB 0x1A4 /* BB Track corr word Q MSB */ +#define REG_BB_TRACK_CORR_WORD_Q_LSB 0x1A5 /* BB Track corr word Q LSB */ +#define REG_RX1_RSSI_SYMBOL 0x1A7 /* Rx1 RSSI Symbol */ +#define REG_RX1_RSSI_PREAMBLE 0x1A8 /* Rx1 RSSI preamble */ +#define REG_RX2_RSSI_SYMBOL 0x1A9 /* Rx2 RSSI symbol */ +#define REG_RX2_RSSI_PREAMBLE 0x1AA /* Rx2 RSSI preamble */ +#define REG_SYMBOL_LSB 0x1AB /* Symbol LSB */ +#define REG_PREAMBLE_LSB 0x1AC /* Preamble LSB */ +#define REG_RX_PATH_GAIN_MSB 0x1AD /* Rx Path Gain */ +#define REG_RX_PATH_GAIN_LSB 0x1AE /* Rx Path Gain */ +#define REG_RX_DIFF_LNA_FORCE 0x1B0 /* Rx Diff LNA Force */ +#define REG_RX_LNA_BIAS_COARSE 0x1B1 /* Rx LNA Bias Coarse */ +#define REG_RX_LNA_BIAS_FINE_0 0x1B2 /* Rx LNA Bias Fine 0 */ +#define REG_RX_LNA_BIAS_FINE_1 0x1B3 /* Rx LNA Bias Fine 1 */ +#define REG_RX_MIX_GM_CONFIG 0x1C0 /* Rx Mix Gm Config */ +#define REG_RX1_MIX_GM_FORCE 0x1C1 /* Rx1 Mix Gm Force */ +#define REG_RX1_MIX_GM_BIAS_FORCE 0x1C2 /* Rx1 Mix Gm Bias (Force) */ +#define REG_RX2_MIX_GM_FORCE 0x1C3 /* Rx2 Mix Gm Force */ +#define REG_RX2_MIX_GM_BIAS_FORCE 0x1C4 /* Rx2 Mix Gm Bias (Force) */ +#define REG_INPUT_A_MSBS 0x1C8 /* Input A MSBs */ +#define REG_INPUT_A_RX1_I 0x1C9 /* Input A RX1 I */ +#define REG_INPUT_A_RX1_Q 0x1CA /* Input A RX1 Q */ +#define REG_INPUT_A_RX2_I 0x1CB /* Input A RX2 I */ +#define REG_INPUT_A_RX2_Q 0x1CC /* Input A RX2 Q */ +#define REG_INPUTS_BC_RX1_I 0x1CD /* Inputs B&C RX1 I */ +#define REG_BAND1_RX1_Q 0x1CE /* Band1 RX1 Q */ +#define REG_INPUTS_BC_RX2_I 0x1CF /* Inputs B&C RX2 I */ +#define REG_INPUTS_BC_RX2_Q 0x1D0 /* Inputs B&C RX2 Q */ +#define REG_INPUTS_BC_MSBS 0x1D1 /* Inputs B&C MSBs */ +#define REG_FORCE_OS_DAC 0x1D2 /* Force OS DAC */ +#define REG_RX_MIX_LO_CM 0x1D5 /* Rx Mix LO CM */ +#define REG_RX_CGB_SEG_ENABLE 0x1D6 /* Rx CGB Seg Enable */ +#define REG_RX_MIX_INPUTBIAS 0x1D7 /* Rx Mix Input/Bias */ +#define REG_RX_TIA_CONFIG 0x1DB /* Rx TIA Config */ +#define REG_TIA1_C_LSB 0x1DC /* TIA1 C LSB */ +#define REG_TIA1_C_MSB 0x1DD /* TIA1 C MSB */ +#define REG_TIA2_C_LSB 0x1DE /* TIA2 C LSB */ +#define REG_TIA2_C_MSB 0x1DF /* TIA2 C MSB */ +#define REG_RX1_BBF_R1A 0x1E0 /* Rx1 BBF R1A */ +#define REG_RX2_BBF_R1A 0x1E1 /* Rx2 BBF R1A */ +#define REG_RX1_TUNE_CTRL 0x1E2 /* Rx1 Tune Control */ +#define REG_RX2_TUNE_CTRL 0x1E3 /* Rx2 Tune Control */ +#define REG_RX1_BBF_R5 0x1E4 /* Rx1 BBF R5 */ +#define REG_RX2_BBF_R5 0x1E5 /* Rx2 BBF R5 */ +#define REG_RX_BBF_R2346 0x1E6 /* Rx BBF R2346 */ +#define REG_RX_BBF_C1_MSB 0x1E7 /* Rx BBF C1 MSB */ +#define REG_RX_BBF_C1_LSB 0x1E8 /* Rx BBF C1 LSB */ +#define REG_RX_BBF_C2_MSB 0x1E9 /* Rx BBF C2 MSB */ +#define REG_RX_BBF_C2_LSB 0x1EA /* Rx BBF C2 LSB */ +#define REG_RX_BBF_C3_MSB 0x1EB /* Rx BBF C3 MSB */ +#define REG_RX_BBF_C3_LSB 0x1EC /* Rx BBF C3 LSB */ +#define REG_RX_BBF_CC1_CTR 0x1ED /* Rx BBF CC1 Ctr */ +#define REG_RX_BBF_POW_RZ_BYTE0 0x1EE /* Rx BBF Pow Rz Byte0 */ +#define REG_RX_BBF_CC2_CTR 0x1EF /* Rx BBF CC2 Ctr */ +#define REG_RX_BBF_POW_RZ_BYTE1 0x1F0 /* Rx BBF Pow Rz Byte1 */ +#define REG_RX_BBF_CC3_CTR 0x1F1 /* Rx BBF CC3 Ctr */ +#define REG_RX_BBF_R5_TUNE 0x1F2 /* Rx BBF R5 Tune */ +#define REG_RX_BBF_TUNE 0x1F3 /* Rx BBF Tune */ +#define REG_RX1_BBF_MAN_GAIN 0x1F4 /* Rx1 BBF Man Gain */ +#define REG_RX2_BBF_MAN_GAIN 0x1F5 /* Rx2 BBF Man Gain */ +#define REG_RX_BBF_TUNE_DIVIDE 0x1F8 /* RX BBF Tune Divide */ +#define REG_RX_BBF_TUNE_CONFIG 0x1F9 /* RX BBF Tune Config */ +#define REG_POLE_GAIN 0x1FA /* Pole gain */ +#define REG_RX_BBBW_MHZ 0x1FB /* Rx BBBW MHz */ +#define REG_RX_BBBW_KHZ 0x1FC /* Rx BBBW kHz */ +#define REG_FB_DAC_CLK_DELAY1 0x201 /* FB DAC Clk Delay1 */ +#define REG_FB_DAC_CLK_DELAY2 0x202 /* FB DAC Clk Delay2 */ +#define REG_FLASH_SAMPLE_CLK_DELAY_3P 0x203 /* Flash Sample Clk Delay 3p */ +#define REG_FLASH_SAMPLE_CLK_DELAY_3N 0x204 /* Flash Sample Clk Delay 3n */ +#define REG_TEST_MUX_2I 0x205 /* Test MUX 2i */ +#define REG_TEST_MUX_2Q 0x206 /* Test MUX 2q */ +#define REG_INTEGRATOR_1_RESISTANCE 0x207 /* Integrator 1 Resistance */ +#define REG_INTEGRATOR_1_CAPACITANCE 0x208 /* Integrator 1 Capacitance */ +#define REG_INTEGRATOR_23_RESISTANCE 0x209 /* Integrator 23 Resistance */ +#define REG_INTEGRATOR_2_RESISTANCE 0x20A /* Integrator 2 Resistance */ +#define REG_INTEGRATOR_2_CAPACITANCE 0x20B /* Integrator 2 Capacitance */ +#define REG_INTEGRATOR_3_RESISTANCE 0x20C /* Integrator 3 Resistance */ +#define REG_INTEGRATOR_3_CAPACITANCE 0x20D /* Integrator 3 Capacitance */ +#define REG_INTEGRATOR_AMP_CC 0x20E /* Integrator Amp Cc */ +#define REG_INT_1_FB_DAC_NMOS_CURRENT_SOURCE 0x20F /* Int 1 FB DAC NMOS Current Source */ +#define REG_INT_1_FB_DAC_NMOS_CASOADE_BIAS_CURRENT 0x210 /* Int 1 FB DAC NMOS Casoade Bias Current */ +#define REG_INT_1_FB_DAC_PMOS_CURRENT_SOURCE 0x211 /* Int 1 FB DAC PMOS Current Source */ +#define REG_INT_2_FB_DAC_NMOS_CURRENT_SOURCE 0x212 /* Int 2 FB DAC NMOS Current Source */ +#define REG_INT_2_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x213 /* Int 2 FB DAC NMOS Cascode Bias Current */ +#define REG_INT_2_FB_DAC_PMOS_CURRENT_SOURCE 0x214 /* Int 2 FB DAC PMOS Current Source */ +#define REG_INT_3_FB_DAC_NMOS_CURRENT_SOURCE 0x215 /* Int 3 FB DAC NMOS Current Source */ +#define REG_INT_3_FB_DAC_NMOS_CASCODE_BIAS_CURRENT 0x216 /* Int 3 FB DAC NMOS Cascode Bias Current */ +#define REG_INT_3_FB_DAC_PMOS_CURRENT_SOURCE 0x217 /* Int 3 FB DAC PMOS Current Source */ +#define REG_FB_DAC_BIAS_CURRENT 0x218 /* FB DAC Bias Current */ +#define REG_INT_1_1ST_STAGE_CURRENT 0x219 /* Int 1 1st Stage Current */ +#define REG_INT_1_1ST_STAGE_CASCODE_CURRENT 0x21A /* Int 1 1st Stage Cascode Current */ +#define REG_INT_1_2ND_STAGE_CURRENT 0x21B /* Int 1 2nd Stage Current */ +#define REG_INTEGRATOR_2_1ST_STAGE_CURRENT 0x21C /* Integrator 2 1st Stage Current */ +#define REG_INT_2_1ST_STAGE_CASCODE_CURRENT 0x21D /* Int 2 1st Stage Cascode Current */ +#define REG_INT_2_2ND_STAGE_CURRENT 0x21E /* Int 2 2nd Stage Current */ +#define REG_INT_3_1ST_STAGE_CURRENT 0x21F /* Int 3 1st Stage Current */ +#define REG_INT_3_1ST_STAGE_CASCODE_CURRENT 0x220 /* Int 3 1st Stage Cascode Current */ +#define REG_INT_3_2ND_STAGE_CURRENT 0x221 /* Int 3 2nd Stage Current */ +#define REG_FLASH_BIAS_CURRENT 0x222 /* Flash Bias Current */ +#define REG_FLASH_LADDER_BIAS 0x223 /* Flash Ladder Bias */ +#define REG_FLASH_LADDER_CASCODE_CURRENT 0x224 /* Flash Ladder Cascode Current */ +#define REG_FLASH_LADDER_BIAS2 0x225 /* Flash Ladder Bias2 */ +#define REG_RESET 0x226 /* Reset */ +#define REG_RX_PFD_CONFIG 0x230 /* RX PFD Config */ +#define REG_RX_INTEGER_BYTE_0 0x231 /* RX Integer Byte 0 */ +#define REG_RX_INTEGER_BYTE_1 0x232 /* RX Integer Byte 1 */ +#define REG_RX_FRACT_BYTE_0 0x233 /* RX Fractional Byte 0 */ +#define REG_RX_FRACT_BYTE_1 0x234 /* RX Fractional Byte 1 */ +#define REG_RX_FRACT_BYTE_2 0x235 /* RX Fractional Byte 2 */ +#define REG_RX_FORCE_ALC 0x236 /* RX Force ALC */ +#define REG_RX_FORCE_VCO_TUNE_0 0x237 /* RX Force VCO Tune 0 */ +#define REG_RX_FORCE_VCO_TUNE_1 0x238 /* RX Force VCO Tune 1 */ +#define REG_RX_ALC_VARACTOR 0x239 /* RX ALC/Varactor */ +#define REG_RX_VCO_OUTPUT 0x23A /* RX VCO Output */ +#define REG_RX_CP_CURRENT 0x23B /* RX CP Current */ +#define REG_RX_CP_OFFSET 0x23C /* RX CP Offset */ +#define REG_RX_CP_CONFIG 0x23D /* RX CP Config */ +#define REG_RX_LOOP_FILTER_1 0x23E /* RX Loop Filter 1 */ +#define REG_RX_LOOP_FILTER_2 0x23F /* RX Loop Filter 2 */ +#define REG_RX_LOOP_FILTER_3 0x240 /* RX Loop Filter 3 */ +#define REG_RX_DITHERCP_CAL 0x241 /* RX Dither/CP Cal */ +#define REG_RX_VCO_BIAS_1 0x242 /* RX VCO Bias 1 */ +#define REG_RX_CAL_STATUS 0x244 /* RX Cal Status */ +#define REG_RX_VCO_CAL_REF 0x245 /* RX VCO Cal Ref */ +#define REG_RX_VCO_PD_OVERRIDES 0x246 /* RX VCO Pd Overrides */ +#define REG_RX_CP_OVERRANGE_VCO_LOCK 0x247 /* RX CP Over Range/VCO Lock */ +#define REG_RX_VCO_LDO 0x248 /* RX VCO LDO */ +#define REG_RX_VCO_CAL 0x249 /* RX VCO Cal */ +#define REG_RX_LOCK_DETECT_CONFIG 0x24A /* RX Lock Detect Config */ +#define REG_RX_CP_LEVEL_DETECT 0x24B /* RX CP Level Detect */ +#define REG_RX_DSM_SETUP_0 0x24C /* RX DSM Setup 0 */ +#define REG_RX_DSM_SETUP_1 0x24D /* RX DSM Setup 1 */ +#define REG_RX_CORRECTION_WORD0 0x24E /* RX Correction Word0 */ +#define REG_RX_CORRECTION_WORD1 0x24F /* RX Correction Word1 */ +#define REG_RX_VCO_VARACTOR_CTRL_0 0x250 /* RX VCO Varactor Control 0 */ +#define REG_RX_VCO_VARACTOR_CTRL_1 0x251 /* RX VCO Varactor Control 1 */ +#define REG_RX_FAST_LOCK_SETUP 0x25A /* Rx Fast Lock Setup */ +#define REG_RX_FAST_LOCK_SETUP_INIT_DELAY 0x25B /* Rx Fast Lock Setup Init Delay */ +#define REG_RX_FAST_LOCK_PROGRAM_ADDR 0x25C /* Rx Fast Lock Program Addr */ +#define REG_RX_FAST_LOCK_PROGRAM_DATA 0x25D /* Rx Fast Lock Program Data */ +#define REG_RX_FAST_LOCK_PROGRAM_READ 0x25E /* Rx Fast Lock Program Read */ +#define REG_RX_FAST_LOCK_PROGRAM_CTRL 0x25F /* Rx Fast Lock Program Control */ +#define REG_RX_LO_GEN_POWER_MODE 0x261 /* Rx LO Gen Power Mode */ +#define REG_TX_PFD_CONFIG 0x270 /* TX PFD Config */ +#define REG_TX_INTEGER_BYTE_0 0x271 /* TX Integer Byte 0 */ +#define REG_TX_INTEGER_BYTE_1 0x272 /* TX Integer Byte 1 */ +#define REG_TX_FRACT_BYTE_0 0x273 /* TX Fractional Byte 0 */ +#define REG_TX_FRACT_BYTE_1 0x274 /* TX Fractional Byte 1 */ +#define REG_TX_FRACT_BYTE_2 0x275 /* TX Fractional Byte 2 */ +#define REG_TX_FORCE_ALC 0x276 /* TX Force ALC */ +#define REG_TX_FORCE_VCO_TUNE_0 0x277 /* TX Force VCO Tune 0 */ +#define REG_TX_FORCE_VCO_TUNE_1 0x278 /* TX Force VCO Tune 1 */ +#define REG_TX_ALCVARACT_OR 0x279 /* TX ALC/Varact or */ +#define REG_TX_VCO_OUTPUT 0x27A /* TX VCO Output */ +#define REG_TX_CP_CURRENT 0x27B /* TX CP Current */ +#define REG_TX_CP_OFFSET 0x27C /* TX CP Offset */ +#define REG_TX_CP_CONFIG 0x27D /* TX CP Config */ +#define REG_TX_LOOP_FILTER_1 0x27E /* TX Loop Filter 1 */ +#define REG_TX_LOOP_FILTER_2 0x27F /* TX Loop Filter 2 */ +#define REG_TX_LOOP_FILTER_3 0x280 /* TX Loop Filter 3 */ +#define REG_TX_DITHERCP_CAL 0x281 /* TX Dither/CP Cal */ +#define REG_TX_VCO_BIAS_1 0x282 /* TX VCO Bias 1 */ +#define REG_TX_VCO_BIAS_2 0x283 /* TX VCO Bias 2 */ +#define REG_TX_CAL_STATUS 0x284 /* TX Cal Status */ +#define REG_TX_VCO_CAL_REF 0x285 /* TX VCO Cal Ref */ +#define REG_TX_VCO_PD_OVERRIDES 0x286 /* TX VCO Pd Overrides */ +#define REG_TX_CP_OVERRANGE_VCO_LOCK 0x287 /* TX CP Over Range/VCO Lock */ +#define REG_TX_VCO_LDO 0x288 /* TX VCO LDO */ +#define REG_TX_VCO_CAL 0x289 /* TX VCO Cal */ +#define REG_TX_LOCK_DETECT_CONFIG 0x28A /* TX Lock Detect Config */ +#define REG_TX_CP_LEVEL_DETECT 0x28B /* TX CP Level Detect */ +#define REG_TX_DSM_SETUP_0 0x28C /* TX DSM Setup 0 */ +#define REG_TX_DSM_SETUP_1 0x28D /* TX DSM Setup 1 */ +#define REG_TX_CORRECTION_WORD0 0x28E /* TX Correction Word0 */ +#define REG_TX_CORRECTION_WORD1 0x28F /* TX Correction Word1 */ +#define REG_TX_VCO_VARACTOR_CTRL_0 0x290 /* TX VCO Varactor Control 0 */ +#define REG_TX_VCO_VARACTOR_CTRL_1 0x291 /* TX VCO Varactor Control 1 */ +#define REG_DCXO_COARSE_TUNE 0x292 /* DCXO Coarse Tune */ +#define REG_DCXO_FINE_TUNE_HIGH 0x293 /* DCXO Fine Tune2 */ +#define REG_DCXO_FINE_TUNE_LOW 0x294 /* DCXO Fine Tune1 */ +#define REG_DCXO_CONFIG 0x295 /* DCXO Config */ +#define REG_DCXO_TEMPCO_WRITE 0x296 /* DCXO Tempco Write */ +#define REG_DCXO_TEMPCO_READ 0x297 /* DCXO Tempco Read */ +#define REG_DCXO_TEMPCO_ADDR 0x298 /* DCXO Tempco Addr */ +#define REG_DELTA_T_READ 0x299 /* Delta T Read */ +#define REG_TX_FAST_LOCK_SETUP 0x29A /* Tx Fast Lock Setup */ +#define REG_TX_FAST_LOCK_SETUP_INIT_DELAY 0x29B /* Tx Fast Lock Setup Init Delay */ +#define REG_TX_FAST_LOCK_PROGRAM_ADDR 0x29C /* Tx Fast Lock Program Addr */ +#define REG_TX_FAST_LOCK_PROGRAM_DATA 0x29D /* Tx Fast Lock Program Data */ +#define REG_TX_FAST_LOCK_PROGRAM_READ 0x29E /* Tx Fast Lock Program Read */ +#define REG_TX_FAST_LOCK_PROGRAM_CTRL 0x29F /* Tx Fast Lock Program Ctrl */ +#define REG_TX_LO_GEN_POWER_MODE 0x2A1 /* Tx LO Gen Power Mode */ +#define REG_BANDGAP_CONFIG0 0x2A6 /* Bandgap Config0 */ +#define REG_BANDGAP_CONFIG1 0x2A8 /* Bandgap Config1 */ +#define REG_REF_DIVIDE_CONFIG_1 0x2AB /* Ref Divide Config 1 */ +#define REG_REF_DIVIDE_CONFIG_2 0x2AC /* Ref Divide Config 2 */ +#define REG_GAIN_RX1 0x2B0 /* Gain Rx1 */ +#define REG_LPF_GAIN_RX1 0x2B1 /* LPF Gain Rx1 */ +#define REG_DIG_GAIN_RX1 0x2B2 /* Dig gain Rx1 */ +#define REG_FAST_ATTACK_STATE 0x2B3 /* Fast Attack State */ +#define REG_SLOW_LOOP_STATE 0x2B4 /* Slow Loop State */ +#define REG_GAIN_RX2 0x2B5 /* Gain Rx2 */ +#define REG_LPF_GAIN_RX2 0x2B6 /* LPF Gain Rx2 */ +#define REG_DIG_GAIN_RX2 0x2B7 /* Dig Gain Rx2 */ +#define REG_OVRG_SIGS_RX1 0x2B8 /* Ovrg Sigs Rx1 */ +#define REG_OVRG_SIGS_RX2 0x2B9 /* Ovrg Sigs Rx2 */ +#define REG_CTRL 0x3DF /* Control */ +#define REG_BIST_CONFIG 0x3F4 /* BIST Config */ +#define REG_OBSERVE_CONFIG 0x3F5 /* Observe Config */ +#define REG_BIST_AND_DATA_PORT_TEST_CONFIG 0x3F6 /* BIST and Data Port Test Config */ +#define REG_DAC_TEST_0 0x3FC /* DAC Test 0 */ +#define REG_DAC_TEST_1 0x3FD /* DAC Test 1 */ +#define REG_DAC_TEST_2 0x3FE /* DAC Test 2 */ + +/* +* REG_SPI_CONF +*/ +#define SOFT_RESET (1 << 7) /* Soft Reset */ +#define WIRE3_SPI (1 << 6) /* 3-Wire SPI */ +#define LSB_FIRST (1 << 5) /* LSB First */ +#define _LSB_FIRST (1 << 2) /* LSB First */ +#define _WIRE3_SPI (1 << 1) /* 3-Wire SPI */ +#define _SOFT_RESET (1 << 0) /* Soft reset */ + +/* +* REG_MULTICHIP_SYNC_AND_TX_MON_CTRL +*/ +#define TX2_MONITOR_ENABLE (1 << 6) /* Tx2 Monitor Enable */ +#define TX1_MONITOR_ENABLE (1 << 5) /* Tx1 Monitor Enable */ +#define MCS_RF_ENABLE (1 << 3) /* MCS RF Enable */ +#define MCS_BBPLL_ENABLE (1 << 2) /* MCS BBPLL enable */ +#define MCS_DIGITAL_CLK_ENABLE (1 << 1) /* MCS Digital CLK Enable */ +#define MCS_BB_ENABLE (1 << 0) /* MCS BB Enable */ + +/* +* REG_TX_ENABLE_FILTER_CTRL +*/ +#define THB2_EN (1 << 3) /* THB2 Enable */ +#define THB1_EN (1 << 2) /* THB1 Enable */ +#define TX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Tx channel Enable<1:0> */ +#define THB3_ENABLE_INTERP(x) (((x) & 0x3) << 4) /* THB3 Enable & Interp<1:0> */ +#define TX_FIR_ENABLE_INTERPOLATION(x) (((x) & 0x3) << 0) /* Tx FIR Enable & Interpolation<1:0> */ +#define TX_1 1 +#define TX_2 2 +#define TX_ENABLE 1 +#define TX_DISABLE 0 + +/* +* REG_RX_ENABLE_FILTER_CTRL +*/ +#define RHB2_EN (1 << 3) /* RHB2 Enable */ +#define RHB1_EN (1 << 2) /* RHB1 Enable */ +#define RX_CHANNEL_ENABLE(x) (((x) & 0x3) << 6) /* Rx channel Enable<1:0> */ +#define DEC3_ENABLE_DECIMATION(x) (((x) & 0x3) << 4) /* DEC3 Enable & Decimation<1:0> */ +#define RX_FIR_ENABLE_DECIMATION(x) (((x) & 0x3) << 0) /* Rx FIR Enable & Decimation<1:0> */ +#define RX_1 1 +#define RX_2 2 +#define RX_ENABLE 1 +#define RX_DISABLE 0 + +/* +* REG_INPUT_SELECT +*/ +#define TX_OUTPUT (1 << 6) /* TX Output */ +#define RX_INPUT(x) (((x) & 0x3F) << 0) /* RX Input <5:0> */ + +/* +* REG_RFPLL_DIVIDERS +*/ +#define TX_VCO_DIVIDER(x) (((x) & 0xF) << 4) /* TX VCO Divider<3:0> */ +#define RX_VCO_DIVIDER(x) (((x) & 0xF) << 0) /* RX VCO Divider<3:0> */ + +/* +* REG_RX_CLOCK_DATA_DELAY +*/ +#define DATA_CLK_DELAY(x) (((x) & 0xF) << 4) /* DATA_CLK Delay<3:0> */ +#define RX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Rx Data Delay <3:0> */ + +/* +* REG_TX_CLOCK_DATA_DELAY +*/ +#define FB_CLK_DELAY(x) (((x) & 0xF) << 4) /* FB_CLK Delay<3:0> */ +#define TX_DATA_DELAY(x) (((x) & 0xF) << 0) /* Tx Data Delay <3:0> */ + +/* +* REG_CLOCK_ENABLE +*/ +#define XO_BYPASS (1 << 4) /* XO Bypass */ +#define DIGITAL_POWER_UP (1 << 2) /* Digital Power Up */ +#define CLOCK_ENABLE_DFLT (1 << 1) /* Set to 1 */ +#define BBPLL_ENABLE (1 << 0) /* BBPLL Enable */ + +/* +* REG_BBPLL +*/ +#define CLKOUT_ENABLE (1 << 4) /* CLKOUT Enable */ +#define DAC_CLK_DIV2 (1 << 3) /* DAC Clk div2 */ +#define CLKOUT_SELECT(x) (((x) & 0x7) << 5) /* CLKOUT Select<2:0> */ +#define BBPLL_DIVIDER(x) (((x) & 0x7) << 0) /* BBPLL Divider <2:0> */ + +/* +* REG_START_TEMP_READING +*/ +#define START_TEMP_READING (1 << 0) /* Start Temp Reading */ + +/* +* REG_TEMP_SENSE2 +*/ +#define TEMP_SENSE_PERIODIC_ENABLE (1 << 0) /* Temp Sense Periodic Enable */ +#define MEASUREMENT_TIME_INTERVAL(x) (((x) & 0x7F) << 1) /* Measurement Time Interval<6:0> */ + +/* +* REG_TEMP_SENSOR_CONFIG +*/ +#define TEMP_SENSOR_DECIMATION(x) (((x) & 0x7) << 0) /* Temp Sensor Decimation<2:0> */ + +/* +* REG_PARALLEL_PORT_CONF_1 +*/ +#define PP_TX_SWAP_IQ (1 << 7) /* PP Tx Swap IQ */ +#define PP_RX_SWAP_IQ (1 << 6) /* PP Rx Swap IQ */ +#define TX_CHANNEL_SWAP (1 << 5) /* Tx Channel swap */ +#define RX_CHANNEL_SWAP (1 << 4) /* Rx Channel swap */ +#define RX_FRAME_PULSE_MODE (1 << 3) /* Rx Frame Pulse Mode */ +#define R2T2_TIMING (1 << 2) /* 2R2T Timing */ +#define INVERT_DATA_BUS (1 << 1) /* Invert data bus */ +#define INVERT_DATA_CLK (1 << 0) /* Invert DATA CLK */ + +/* +* REG_PARALLEL_PORT_CONF_2 +*/ +#define FDD_ALT_WORD_ORDER (1 << 7) /* FDD Alt Word Order */ +#define INVERT_RX1 (1 << 6) /* Invert Rx1 */ +#define INVERT_RX2 (1 << 5) /* Invert Rx2 */ +#define INVERT_TX1 (1 << 4) /* Invert Tx1 */ +#define INVERT_TX2 (1 << 3) /* Invert Tx2 */ +#define INVERT_RX_FRAME (1 << 2) /* Invert Rx Frame */ +#define DELAY_RX_DATA(x) (((x) & 0x3) << 0) /* Delay Rx Data<1:0> */ + +/* +* REG_PARALLEL_PORT_CONF_3 +*/ +#define FDD_RX_RATE_2TX_RATE (1 << 7) /* FDD Rx Rate = 2*Tx Rate */ +#define SWAP_PORTS (1 << 6) /* Swap Ports */ +#define SINGLE_DATA_RATE (1 << 5) /* Single Data Rate */ +#define LVDS_MODE (1 << 4) /* LVDS Mode */ +#define HALF_DUPLEX_MODE (1 << 3) /* Half Duplex Mode */ +#define SINGLE_PORT_MODE (1 << 2) /* Single Port Mode */ +#define FULL_PORT (1 << 1) /* Full Port */ +#define FULL_DUPLEX_SWAP_BITS (1 << 0) /* Full Duplex Swap Bits */ + +/* +* REG_ENSM_MODE +*/ +#define FDD_MODE (1 << 0) /* FDD Mode */ + +/* +* REG_ENSM_CONFIG_1 +*/ +#define ENABLE_RX_DATA_PORT_FOR_CAL (1 << 7) /* Enable Rx Data Port for Cal */ +#define FORCE_RX_ON (1 << 6) /* Force Rx On */ +#define FORCE_TX_ON (1 << 5) /* Force Tx On */ +#define ENABLE_ENSM_PIN_CTRL (1 << 4) /* Enable ENSM Pin Control */ +#define LEVEL_MODE (1 << 3) /* Level Mode */ +#define FORCE_ALERT_STATE (1 << 2) /* Force Alert State */ +#define AUTO_GAIN_LOCK (1 << 1) /* Auto Gain Lock */ +#define TO_ALERT (1 << 0) /* To Alert */ + +/* +* REG_ENSM_CONFIG_2 +*/ +#define FDD_EXTERNAL_CTRL_ENABLE (1 << 7) /* FDD External Control Enable */ +#define POWER_DOWN_RX_SYNTH (1 << 6) /* Power Down Rx Synth */ +#define POWER_DOWN_TX_SYNTH (1 << 5) /* Power Down Tx Synth */ +#define TXNRX_SPI_CTRL (1 << 4) /* TXNRX SPI Control */ +#define SYNTH_ENABLE_PIN_CTRL_MODE (1 << 3) /* Synth Enable Pin Control Mode */ +#define DUAL_SYNTH_MODE (1 << 2) /* Dual Synth Mode */ +#define RX_SYNTH_READY_MASK (1 << 1) /* Rx Synth Ready Mask */ +#define TX_SYNTH_READY_MASK (1 << 0) /* Tx Synth Ready Mask */ + +/* +* REG_CALIBRATION_CTRL +*/ +#define RX_BB_TUNE_CAL (1 << 7) /* Rx BB Tune */ +#define TX_BB_TUNE_CAL (1 << 6) /* Tx BB Tune */ +#define RX_QUAD_CAL (1 << 5) /* Rx Quad Cal */ +#define TX_QUAD_CAL (1 << 4) /* Tx Quad Cal */ +#define RX_GAIN_STEP_CAL (1 << 3) /* Rx Gain Step Cal */ +#define TXMON_CAL (1 << 2) +#define RFDC_CAL (1 << 1) /* DC Cal RF Start */ +#define BBDC_CAL (1 << 0) /* DC cal BB Start */ + + +/* +* REG_STATE +*/ +#define CALIBRATION_SEQUENCE_STATE(x) (((x) & 0xF) << 4) /* Calibration Sequence State<3:0> */ +#define ENSM_STATE(x) (((x) & 0xF) << 0) /* ENSM State<3:0> */ +#define ENSM_STATE_SLEEP_WAIT 0x0 +#define ENSM_STATE_ALERT 0x5 +#define ENSM_STATE_TX 0x6 +#define ENSM_STATE_TX_FLUSH 0x7 +#define ENSM_STATE_RX 0x8 +#define ENSM_STATE_RX_FLUSH 0x9 +#define ENSM_STATE_FDD 0xA +#define ENSM_STATE_FDD_FLUSH 0xB +#define ENSM_STATE_INVALID 0xFF +#define ENSM_STATE_SLEEP 0x80 + +/* +* REG_AUXDAC_2_WORD +*/ +#define AUXDAC_2_WORD_MSB(x) (((x) & 0x3F) << 2) /* AuxDAC 2 Word<9:2> */ +#define AUXDAC_1_WORD(x) (((x) & 0x3) << 0) /* AuxDAC 1 Word <1:0> */ + +/* +* REG_AUXDAC_1_CONFIG +*/ +#define COMP_CTRL_1 (1 << 5) /* Comp Ctrl 1 */ +#define AUXDAC1_STP_FACTOR (1 << 4) /* AuxDAC1 Step Factor */ +#define AUXDAC_1_VREF(x) (((x) & 0x3) << 2) /* AuxDAC 1 Vref<1:0> */ +#define AUXDAC_1_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */ + +/* +* REG_AUXDAC_2_CONFIG +*/ +#define COMP_CTRL_2 (1 << 5) /* Comp Ctrl 2 */ +#define AUXDAC2_STP_FACTOR (1 << 4) /* AuxDAC2 Step Factor */ +#define AUXDAC_2_VREF(x) (((x) & 0xF) << 2) /* AuxDAC 2 Vref<1:0> */ +#define AUXDAC_2_WORD_LSB(x) (((x) & 0x3) << 0) /* AuxDAC 2 Word <1:0> */ + +/* +* REG_AUXADC_CLOCK_DIVIDER +*/ +#define AUXADC_CLOCK_DIVIDER(x) (((x) & 0x3F) << 0) /* AuxADC Clock Divider<5:0> */ + +/* +* REG_AUXADC_CONFIG +*/ +#define AUXADC_POWER_DOWN (1 << 0) /* AuxADC Power Down */ +#define AUX_ADC_DECIMATION(x) (((x) & 0x7) << 1) /* Aux ADC Decimation<2:0> */ + +/* +* REG_AUXADC_LSB +*/ +#define AUXADC_WORD_LSB(x) (((x) & 0xF) << 0) /* AuxADC Word LSB<3:0> */ + +/* +* REG_AUTO_GPO +*/ +#define GPO_ENABLE_AUTO_RX(x) (((x) & 0xF) << 4) /* GPO Enable Auto Rx<3:0> */ +#define GPO_ENABLE_AUTO_TX(x) (((x) & 0xF) << 0) /* GPO Enable Auto Tx<3:0> */ + +/* +* REG_AGC_ATTACK_DELAY +*/ +#define INVERT_BYPASSED_LNA_POLARITY (1 << 6) /* Invert Bypassed LNA Polarity */ +#define AGC_ATTACK_DELAY(x) (((x) & 0x3F) << 0) /* AGC Attack Delay<5:0> */ + +/* +* REG_AUXDAC_ENABLE_CTRL +*/ +#define AUXDAC_MANUAL_BAR(x) (((x) & 0x3) << 6) /* AuxDac Manual Bar<1:0> */ +#define AUXDAC_AUTO_TX_BAR(x) (((x) & 0x3) << 4) /* AuxDAC Auto Tx Bar<1:0> */ +#define AUXDAC_AUTO_RX_BAR(x) (((x) & 0x3) << 2) /* AuxDAC Auto Rx Bar<1:0> */ +#define AUXDAC_INIT_BAR(x) (((x) & 0x3) << 0) /* AuxDAC Init Bar<1:0> */ + +/* +* REG_EXTERNAL_LNA_CTRL +*/ +#define AUXDAC_MANUAL_SELECT (1 << 7) /* AuxDAC Manual Select */ +#define EXTERNAL_LNA2_CTRL (1 << 6) /* External LNA2 control */ +#define EXTERNAL_LNA1_CTRL (1 << 5) /* External LNA1 control */ +#define GPO_MANUAL_SELECT (1 << 4) /* GPO manual select */ +#define OPEN(x) (((x) & 0xF) << 0) /* Open<3:0> */ + +/* +* REG_GPO_FORCE_AND_INIT +*/ +#define GPO_MANUAL_CTRL(x) (((x) & 0xF) << 4) /* GPO Manual Control<3:0> */ +#define GPO_INIT_STATE(x) (((x) & 0xF) << 0) /* GPO Init State<3:0> */ + +/* +* REG_CTRL_OUTPUT_ENABLE +*/ +#define EN_CTRL7 (1 << 7) /* En ctrl7 */ +#define EN_CTRL6 (1 << 6) /* En ctrl6 */ +#define EN_CTRL5 (1 << 5) /* En ctrl5 */ +#define EN_CTRL4 (1 << 4) /* En ctrl4 */ +#define EN_CTRL3 (1 << 3) /* En ctrl3 */ +#define EN_CTRL2 (1 << 2) /* En ctrl2 */ +#define EN_CTRL1 (1 << 1) /* En ctrl1 */ +#define EN_CTRL0 (1 << 0) /* En ctrl0 */ + +/* +* REG_PRODUCT_ID +*/ +#define PRODUCT_ID_MASK 0xF8 +#define PRODUCT_ID_9361 0x08 +#define REV_MASK 0x07 + +/* +* REG_REFERENCE_CLOCK_CYCLES +*/ +#define REFERENCE_CLOCK_CYCLES_PER_US(x) (((x) & 0x7F) << 0) /* Reference Clock Cycles per us<6:0> */ + +/* +* REG_DIGITAL_IO_CTRL +*/ +#define CLK_OUT_DRIVE (1 << 7) /* CLK Out Drive */ +#define DATACLK_DRIVE (1 << 6) /* DATACLK drive */ +#define DATA_PORT_DRIVE (1 << 2) /* Data Port Drive */ +#define DATACLK_SLEW(x) (((x) & 0x3) << 4) /* DATACLK slew <1:0> */ +#define DATA_PORT_SLEW(x) (((x) & 0x3) << 0) /* Data Port Slew<1:0> */ + +/* +* REG_LVDS_BIAS_CTRL +*/ +#define RX_ON_CHIP_TERM (1 << 5) /* Rx On Chip Term */ +#define LVDS_BYPASS_BIAS_R (1 << 4) /* Bypass Bias R */ +#define LVDS_TX_LO_VCM (1 << 3) /* LVDS Tx LO VCM */ +#define CLK_OUT_SLEW(x) (((x) & 0x3) << 6) /* CLK Out Slew<1:0> */ +#define LVDS_BIAS(x) (((x) & 0x7) << 0) /* LVDS Bias <2:0> */ + +/* +* REG_SDM_CTRL_1 +*/ +#define INIT_BB_FO_CAL (1 << 2) /* Init BB FO CAL */ +#define BBPLL_RESET_BAR (1 << 0) /* BBPLL Reset Bar */ + +/* +* REG_CLOCK_CTRL +*/ +#define REF_FREQ_SCALER(x) (((x) & 0x3) << 0) /* Ref Frequency Scaler */ + +/* +* REG_CP_CURRENT +*/ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_CP_BLEED_CURRENT +*/ +#define MCS_REFCLK_SCALE_EN (1 << 7) /* MCS refclk Scale En */ + +/* +* REG_LOOP_FILTER_1 +*/ +#define C1_WORD(x) (((x) & 0x7) << 5) /* C1 Word<2:0> */ +#define R1_WORD(x) (((x) & 0x1F) << 0) /* R1 Word<4:0> */ + +/* +* REG_LOOP_FILTER_2 +*/ +#define R2_WORD (1 << 7) /* R2 Word<0> */ +#define C2_WORD(x) (((x) & 0x1F) << 2) /* C2 Word<4:0> */ +#define C1_WORD_LSB(x) (((x) & 0x3) << 0) /* C1 Word<4:3> */ + +/* +* REG_LOOP_FILTER_3 +*/ +#define BYPASS_C3 (1 << 7) /* Bypass C3 */ +#define BYPASS_R2 (1 << 6) /* Bypass R2 */ +#define C3_WORD(x) (((x) & 0xF) << 2) /* C3 Word<3:0> */ +#define R2_WORD_LSB(x) (((x) & 0x3) << 0) /* R2 Word<2:1> */ + +/* +* REG_VCO_CTRL +*/ +#define FREQ_CAL_ENABLE (1 << 7) /* Freq Cal Enable */ +#define FREQ_CAL_RESET (1 << 4) /* Freq Cal Reset */ +#define FREQ_CAL_COUNT_LENGTH(x) (((x) & 0x3) << 5) /* Freq Cal Count Length<1:0> */ + +/* +* REG_SDM_CTRL +*/ +#define CAL_CLOCK_DIV_4 (1 << 4) /* Cal Clock div 4 */ + +/* +* REG_RX_SYNTH_POWER_DOWN_OVERRIDE +*/ +#define RX_LO_POWER_DOWN (1 << 4) /* Rx LO Power Down */ +#define RX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Rx Synth VCO ALC Power Down */ +#define RX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Rx Synth PTAT Power Down */ +#define RX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Rx Synth VCO Power Down */ +#define RX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Rx Synth VCO LDO Power Down */ + +/* +* REG_TX_SYNTH_POWER_DOWN_OVERRIDE +*/ +#define TX_LO_POWER_DOWN (1 << 4) /* Tx LO Power Down */ +#define TX_SYNTH_VCO_ALC_POWER_DOWN (1 << 3) /* Tx Synth VCO ALC Power Down */ +#define TX_SYNTH_PTAT_POWER_DOWN (1 << 2) /* Tx Synth PTAT Power Down */ +#define TX_SYNTH_VCO_POWER_DOWN (1 << 1) /* Tx Synth VCO Power Down */ +#define TX_SYNTH_VCO_LDO_POWER_DOWN (1 << 0) /* Tx Synth VCO LDO Power Down */ + +/* +* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_1 +*/ +#define RX_OFFSET_DAC_CGIN_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx Offset DAC CGin Power Down<1:0> */ +#define RX_LMT_OVERLOAD_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx LMT Overload Power Down<1:0> */ +#define RX_MIXER_GM_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Gm Power Down<1:0> */ +#define RX_CGB_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx CGB Power Down<1:0> */ + +/* +* REG_RX_ANALOG_POWER_DOWN_OVERRIDE_2 +*/ +#define RX_BBF_POWER_DOWN(x) (((x) & 0x3) << 6) /* Rx BBF Power Down<1:0> */ +#define RX_TIA_POWER_DOWN(x) (((x) & 0x3) << 4) /* Rx TIA Power Down<1:0> */ +#define RX_MIXER_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Mixer Power Down<1:0> */ +#define RX_OFFSET_DAC_CGOUT_POWER_DOWN(x) (((x) & 0x3) << 0) /* Rx Offset DAC CGOut Power Down<1:0> */ + +/* +* REG_TX_ANALOG_POWER_DOWN_OVERRIDE_1 +*/ +#define TX_SECONDARY_FILTER_POWER_DOWN(x) (((x) & 0x3) << 6) /* Tx Secondary Filter Power Down<1:0> */ +#define TX_BBF_POWER_DOWN(x) (((x) & 0x3) << 4) /* Tx BBF Power Down<1:0> */ +#define TX_DAC_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx DAC Power Down<1:0> */ +#define TX_DAC_BIAS_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx DAC Bias Power Down<1:0> */ + +/* +* REG_ANALOG_POWER_DOWN_OVERRIDE +*/ +#define RX_EXT_VCO_BUFFER_POWER_DOWN (1 << 5) /* Rx Ext VCO Buffer Power Down */ +#define TX_EXT_VCO_BUFFER_POWER_DOWN (1 << 4) /* Tx Ext VCO Buffer Power Down */ +#define TX_MONITOR_POWER_DOWN(x) (((x) & 0x3) << 2) /* Tx Monitor Power Down<1:0> */ +#define TX_UPCONVERTER_POWER_DOWN(x) (((x) & 0x3) << 0) /* Tx Upconverter Power Down<1:0> */ + +/* +* REG_MISC_POWER_DOWN_OVERRIDE +*/ +#define RX_LNA_POWER_DOWN (1 << 6) /* Rx LNA Power Down */ +#define DCXO_POWER_DOWN (1 << 1) /* DCXO Power Down */ +#define MASTER_BIAS_POWER_DOWN (1 << 0) /* Master Bias Power Down */ +#define RX_CALIBRATION_POWER_DOWN(x) (((x) & 0x3) << 2) /* Rx Calibration Power Down<1:0> */ + +/* +* REG_CH_1_OVERFLOW +*/ +#define BBPLL_LOCK (1 << 7) /* BBPLL Lock */ +#define CH_1_INT3 (1 << 6) /* CH 1 INT3 */ +#define CH1_HB3 (1 << 5) /* CH1 HB3 */ +#define CH1_HB2 (1 << 4) /* CH1 HB2 */ +#define CH1_QEC (1 << 3) /* CH1 QEC */ +#define CH1_HB1 (1 << 2) /* CH1 HB1 */ +#define CH1_TFIR (1 << 1) /* CH1 TFIR */ +#define CH1_RFIR (1 << 0) /* CH1 RFIR */ + +/* +* REG_CH_2_OVERFLOW +*/ +#define CH2_INT3 (1 << 6) /* CH2 INT3 */ +#define CH2_HB3 (1 << 5) /* CH2 HB3 */ +#define CH2_HB2 (1 << 4) /* CH2 HB2 */ +#define CH2_QEC (1 << 3) /* CH2 QEC */ +#define CH2_HB1 (1 << 2) /* CH2 HB1 */ +#define CH2_TFIR (1 << 1) /* CH2 TFIR */ +#define CH2_RFIR (1 << 0) /* CH2 RFIR */ + +/* +* REG_TX_FILTER_CONF +*/ +#define TX_FIR_GAIN_6DB (1 << 0) /* Filter Gain */ +#define FIR_START_CLK (1 << 1) /* Start Tx/Rx Clock */ +#define FIR_WRITE (1 << 2) /* Write Tx/Rx */ +#define FIR_SELECT(x) (((x) & 0x3) << 3) /* Select Tx/Rx CH<1:0> */ +#define FIR_NUM_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps<2:0> */ + +/* +* REG_TX_MON_LOW_GAIN +*/ +#define TX_MON_TRACK (1 << 5) /* Tx Mon Track */ +#define TX_MON_LOW_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon Low Gain<4:0> */ + +/* +* REG_TX_MON_HIGH_GAIN +*/ +#define TX_MON_HIGH_GAIN(x) (((x) & 0x1F) << 0) /* Tx Mon High Gain<4:0> */ + +/* +* REG_TX_LEVEL_THRESH +*/ +#define TX_LEVEL_THRESH(x) (((x) & 0x3F) << 2) /* Tx Level Threshold<5:0> */ +#define TX_MON_DELAY_COUNTER(x) (((x) & 0x3) << 0) /* Tx Mon Delay Counter<9:8> */ + +/* +* REG_TX_RSSI_LSB +*/ +#define TX_RSSI_2 (1 << 1) /* Tx RSSI 2<0> */ +#define TX_RSSI_1 (1 << 0) /* TX RSSI 1<0> */ + +/* +* REG_TPM_MODE_ENABLE +*/ +#define TX2_MON_ENABLE (1 << 7) /* Tx2 Monitor Enable */ +#define TX1_MON_ENABLE (1 << 5) /* Tx1 Monitor Enable */ +#define ONE_SHOT_MODE (1 << 6) /* One Shot Mode */ +#define TX_MON_DURATION(x) (((x) & 0xF) << 0) /* Tx Mon Duration<3:0> */ + +/* +* REG_TX_MON_1_CONFIG +*/ +#define TX_MON_1_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 1 LO CM<5:0> */ +#define TX_MON_1_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 1 Gain<1:0> */ + +/* +* REG_TX_MON_2_CONFIG +*/ +#define TX_MON_2_LO_CM(x) (((x) & 0x3F) << 2) /* Tx Mon 2 LO CM<5:0> */ +#define TX_MON_2_GAIN(x) (((x) & 0x3) << 0) /* Tx Mon 2 Gain<1:0> */ + +/* +* REG_TX1_ATTEN_1 +*/ +#define TX_1_ATTEN (1 << 0) /* Tx 1 Atten <8> */ + +/* +* REG_TX2_ATTEN_1 +*/ +#define TX_2_ATTEN (1 << 0) /* Tx 2 Atten <8> */ + +/* +* REG_TX_ATTEN_OFFSET +*/ +#define MASK_CLR_ATTEN_UPDATE (1 << 6) /* Mask Clr Atten Update */ +#define TX_ATTEN_OFFSET(x) (((x) & 0x3F) << 0) /* Tx Atten Offset<5:0> */ + +/* +* REG_TX1_DIG_ATTEN +*/ +#define SEL_TX1_TX2 (1 << 6) /* Sel Tx1 & Ttx2 */ + +/* +* REG_TX2_DIG_ATTEN +*/ +#define IMMEDIATELY_UPDATE_TPC_ATTEN (1 << 6) /* Immediately Update TPC Atten */ + +/* +* REG_TX1_SYMBOL_ATTEN +*/ +#define TX_1_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 1 Symbol Attenuation<6:0> */ + +/* +* REG_TX2_SYMBOL_ATTEN +*/ +#define TX_2_SYMBOL_ATTEN(x) (((x) & 0x7F) << 0) /* Tx 2 Symbol Attenuation<6:0> */ + +/* +* REG_TX_SYMBOL_ATTEN_CONFIG +*/ +#define USE_TX1_PIN_SYMBOL_ATTEN (1 << 3) /* Use Tx1 Pin & Symbol Atten */ +#define USE_CTRL_IN_FOR_SYMBOL_ATTEN (1 << 1) /* Use CTRL IN for symbol Atten */ +#define ENABLE_SYMBOL_ATTEN (1 << 0) /* Enable Symbol Atten */ + +/* +* REG_TX_FORCE_BITS +*/ +#define FORCE_OUT_2_TX2_OFFSET (1 << 7) /* Force Out 2 Tx2 Offset */ +#define FORCE_OUT_2_TX1_OFFSET (1 << 6) /* Force Out 2 Tx1 Offset */ +#define FORCE_OUT_2_TX2_PHASE_GAIN (1 << 5) /* Force Out 2 Tx2 Phase & Gain */ +#define FORCE_OUT_2_TX1_PHASE_GAIN (1 << 4) /* Force Out 2 Tx1 Phase & Gain */ +#define FORCE_OUT_1_TX2_OFFSET (1 << 3) /* Force Out 1 Tx2 Offset */ +#define FORCE_OUT_1_TX1_OFFSET (1 << 2) /* Force Out 1 Tx1 Offset */ +#define FORCE_OUT_1_TX2_PHASE_GAIN (1 << 1) /* Force Out 1 Tx2 Phase & Gain */ +#define FORCE_OUT_1_TX1_PHASE_GAIN (1 << 0) /* Force Out 1 Tx1 Phase & Gain */ + +/* +* REG_QUAD_CAL_NCO_FREQ_PHASE_OFFSET +*/ +#define RX_NCO_FREQ(x) (((x) & 0x3) << 5) /* Rx NCO Frequency<1:0> */ +#define RX_NCO_PHASE_OFFSET(x) (((x) & 0x1F) << 0) /* Rx NCO Phase Offset<4:0> */ + +/* +* REG_QUAD_CAL_CTRL +*/ +#define FREE_RUN_ENABLE (1 << 7) /* Free Run Enable */ +#define SETTLE_MAIN_ENABLE (1 << 6) /* Settle Main Enable */ +#define DC_OFFSET_ENABLE (1 << 5) /* DC Offset Enable */ +#define GAIN_ENABLE (1 << 4) /* Gain Enable */ +#define PHASE_ENABLE (1 << 3) /* Phase Enable */ +#define QUAD_CAL_SOFT_RESET (1 << 2) /* Quad Cal Soft Reset */ +#define M_DECIM(x) (((x) & 0x3) << 0) /* M<1:0> */ + +/* +* REG_KEXP_1 +*/ +#define KEXP_TX(x) (((x) & 0x3) << 6) /* Kexp Tx<1:0> */ +#define KEXP_TX_COMP(x) (((x) & 0x3) << 4) /* Kexp Tx_comp <1:0> */ +#define KEXP_DC_I(x) (((x) & 0x3) << 2) /* Kexp DC I <1:0> */ +#define KEXP_DC_Q(x) (((x) & 0x3) << 0) /* Kexp DC Q <1:0> */ + +/* +* REG_KEXP_2 +*/ +#define INVERT_I_DATA (1 << 5) /* Invert I data */ +#define INVERT_Q_DATA (1 << 4) /* Invert Q data */ +#define TX_NCO_FREQ(x) (((x) & 0x3) << 6) /* Tx NCO frequency<1:0> */ +#define KEXP_PHASE(x) (((x) & 0x3) << 2) /* Kexp Phase <1:0> */ +#define KEXP_AMP(x) (((x) & 0x3) << 0) /* Kexp Amp <1:0> */ + +/* +* REG_QUAD_CAL_STATUS_TX1 +*/ +#define TX1_LO_CONV (1 << 1) /* Tx1 LO Conv */ +#define TX1_SSB_CONV (1 << 0) /* Tx1 SSB Conv */ +#define TX1_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx1 Convergence Count<5:0> */ + +/* +* REG_QUAD_CAL_STATUS_TX2 +*/ +#define TX2_LO_CONV (1 << 1) /* Tx2 LO Conv */ +#define TX2_SSB_CONV (1 << 0) /* Tx2 SSB Conv */ +#define TX2_CONVERGENCE_COUNT(x) (((x) & 0x3F) << 2) /* Tx2 Convergence Count<5:0> */ + +/* +* REG_TX_QUAD_FULL_LMT_GAIN +*/ +#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* RX Full table/LMT table gain<6:0> */ + +/* +* REG_SQUARER_CONFIG +*/ +#define GM_STAGE_TIME_CON_OVERRIDE (1 << 5) /* Gm Stage Time Con Override */ +#define GM_STAGE_MV_HP_POLE (1 << 4) /* Gm Stage MV HP Pole */ +#define GM_STAGE_LOWER_CM (1 << 3) /* Gm Stage Lower CM */ +#define BYPASS_BIAS_R (1 << 0) /* Bypass Bias R */ +#define VBIAS_CTRL(x) (((x) & 0x3) << 1) /* Vbias Control<1:0> */ + +/* +* REG_THRESH_ACCUM +*/ +#define THRESH_ACCUMULATOR(x) (((x) & 0xF) << 0) /* Threshold Accumulator<3:0> */ + +/* +* REG_TX_QUAD_LPF_GAIN +*/ +#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* RX LPF gain<4:0> */ + +/* +* REG_TXDAC_VDS_I +*/ +#define TXDAC_VDS_I(x) (((x) & 0x3F) << 0) /* TxDAC Vds I<5:0> */ + +/* +* REG_TXDAC_VDS_Q +*/ +#define TXDAC_VDS_Q(x) (((x) & 0x3F) << 0) /* TxDAC Vds Q<5:0> */ + +/* +* REG_TXDAC_GN_I +*/ +#define TXDAC_GN_I(x) (((x) & 0x3F) << 0) /* txDAC_gn_I<5:0> */ + +/* +* REG_TXDAC_GN_Q +*/ +#define TXDAC_GN_Q(x) (((x) & 0x3F) << 0) /* txDAC_gn_Q<5:0> */ + +/* +* REG_TXBBF_OPAMP_A +*/ +#define OPAMPA_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpA Output Bias<1:0> */ +#define OPAMPA_RZ(x) (((x) & 0x3) << 3) /* OpAmpA RZ<1:0> */ +#define OPAMP_A_CC(x) (((x) & 0x7) << 0) /* OpAmp A CC<2:0> */ + +/* +* REG_TXBBF_OPAMP_B +*/ +#define OPAMPB_OUTPUT_BIAS(x) (((x) & 0x3) << 5) /* OpAmpB Output Bias<1:0> */ +#define OPAMPB_RZ(x) (((x) & 0x3) << 3) /* OpAmpB RZ<1:0> */ +#define OPAMP_B_CC(x) (((x) & 0x7) << 0) /* OpAmp B CC<2:0> */ + +/* +* REG_TX_BBF_R1 +*/ +#define OVERRIDE_ENABLE (1 << 7) /* Override enable */ +#define R1(x) (((x) & 0x1F) << 0) /* R1<4:0> */ + +/* +* REG_TX_BBF_R2 +*/ +#define R2(x) (((x) & 0x1F) << 0) /* R2<4:0> */ + +/* +* REG_TX_BBF_R3 +*/ +#define R3(x) (((x) & 0x1F) << 0) /* R3<4:0> */ + +/* +* REG_TX_BBF_R4 +*/ +#define R4(x) (((x) & 0x1F) << 0) /* R4<4:0> */ + +/* +* REG_TX_BBF_RP +*/ +#define RP(x) (((x) & 0x1F) << 0) /* Rp<4:0> */ + +/* +* REG_TX_BBF_C1 +*/ +#define C1(x) (((x) & 0x3F) << 0) /* C1<5:0> */ + +/* +* REG_TX_BBF_C2 +*/ +#define C2(x) (((x) & 0x3F) << 0) /* C2<5:0> */ + +/* +* REG_TX_BBF_CP +*/ +#define CP(x) (((x) & 0x3F) << 0) /* Cp<5:0> */ + +/* +* REG_TX_TUNE_CTRL +*/ +#define PD_TUNE (1 << 2) /* PD Tune */ +#define TUNER_RESAMPLE (1 << 1) /* Tuner Resample */ +#define TUNER_RESAMPLE_PHASE (1 << 0) /* Tuner Resample Phase */ +#define TUNE_CTRL(x) (((x) & 0x3) << 5) /* Tune Control<1:0> */ + +/* +* REG_TX_BBF_R2B +*/ +#define TX_BBF_BYPASS_BIAS_R (1 << 7) /* Bypass Bias R */ +#define R2B_OVR (1 << 5) /* R2b Ovr */ +#define R2B(x) (((x) & 0x1F) << 0) /* R2b<4:0> */ + +/* +* REG_TX_BBF_TUNE +*/ +#define BBF1_COMP_I (1 << 3) /* BBF1 Comp I */ +#define BBF1_COMP_Q (1 << 2) /* BBF1 Comp Q */ +#define BBF2_COMP_I (1 << 1) /* BBF2 Comp I */ +#define BBF2_COMP_Q (1 << 0) /* BBF2 Comp Q */ + +/* +* REG_CONFIG0 +*/ +#define BIAS(x) (((x) & 0x3) << 6) /* Bias<1:0> */ +#define RGM(x) (((x) & 0x3) << 4) /* Rgm<1:0> */ +#define CC(x) (((x) & 0x3) << 2) /* Cc<1:0> */ +#define AMPBIAS(x) (((x) & 0x3) << 0) /* AmpBias<1:0> */ + +/* +* REG_RESISTOR +*/ +#define RESISTOR(x) (((x) & 0xF) << 0) /* Resistor<3:0> */ + +/* +* REG_CAPACITOR +*/ +#define CAPACITOR(x) (((x) & 0x3F) << 0) /* Capacitor<5:0> */ + +/* +* REG_LO_CM +*/ +#define LO_COMMON_MODE(x) (((x) & 0x3) << 5) /* LO Common Mode<1:0> */ + +/* +* REG_TX_BBF_TUNE_MODE +*/ +#define EVALTIME (1 << 4) /* EvalTime */ +#define TX_BBF_TUNE_DIVIDER (1 << 0) /* TX BBF Tune Divider<8> */ +#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask<1:0> */ +#define TUNER_MODE(x) (((x) & 0x7) << 1) /* Tuner Mode<2:0> */ + +/* +* REG_RX_FILTER_CONFIG +*/ +#define WRITE_RX (1 << 2) /* Write Rx */ +#define START_RX_CLOCK (1 << 1) /* Start Rx Clock */ +#define NUMBER_OF_TAPS(x) (((x) & 0x7) << 5) /* Number of Taps */ +#define SELECT_RX_CH(x) (((x) & 0x3) << 3) /* Select Rx Ch<1:0> */ + +/* +* REG_RX_FILTER_GAIN +*/ +#define FILTER_GAIN(x) (((x) & 0x3) << 0) /* Filter gain<1:0> */ + +/* +* REG_AGC_CONFIG_1 +*/ +#define DEC_PWR_FOR_LOW_PWR (1 << 7) /* Dec Pwr for Low Pwr */ +#define DEC_PWR_FOR_LOCK_LEVEL (1 << 6) /* Dec Pwr for Lock Level */ +#define DEC_PWR_FOR_GAIN_LOCK_EXIT (1 << 5) /* Dec Pwr for Gain Lock Exit */ +#define SLOW_ATTACK_HYBRID_MODE (1 << 4) /* Slow Attack Hybrid Mode */ +#define RX2_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 2) /* Rx 2 Gain Control Setup<1:0> */ +#define RX1_GAIN_CTRL_SETUP(x) (((x) & 0x3) << 0) /* Rx 1 Gain Control Setup<1:0> */ +#define RX_GAIN_CTL_MASK 0x03 +#define RX2_GAIN_CTRL_SHIFT 2 +#define RX1_GAIN_CTRL_SHIFT 0 +#define RX_GAIN_CTL_MGC 0x00 +#define RX_GAIN_CTL_AGC_FAST_ATK 0x01 +#define RX_GAIN_CTL_AGC_SLOW_ATK 0x02 +#define RX_GAIN_CTL_AGC_SLOW_ATK_HYBD 0x03 + +/* +* REG_AGC_CONFIG_2 +*/ +#define AGC_SOFT_RESET (1 << 7) /* Soft Reset */ +#define AGC_GAIN_UNLOCK_CTRL (1 << 6) /* Gain Unlock Control */ +#define AGC_USE_FULL_GAIN_TABLE (1 << 3) /* Use Full Gain Table */ +#define DIG_GAIN_EN (1 << 2) /* Enable Digital Gain */ +#define MAN_GAIN_CTRL_RX2 (1 << 1) /* Manual Gain Control Rx 2 */ +#define MAN_GAIN_CTRL_RX1 (1 << 0) /* Manual Gain Control Rx 1 */ + +/* +* REG_AGC_CONFIG_3 +*/ +#define INCDEC_LMT_GAIN (1 << 4) /* Inc/Dec LMT Gain */ +#define USE_AGC_FOR_LMTLPF_GAIN (1 << 3) /* Use AGC for LMT/LPF Gain */ +#define MANUAL_INCR_STEP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Incr Gain Step Size<2:0> */ +#define ADC_OVERRANGE_SAMPLE_SIZE(x) (((x) & 0x7) << 0) /* ADC Overrange Sample Size<2:0> */ + +/* +* REG_MAX_LMT_FULL_GAIN +*/ +#define MAXIMUM_FULL_TABLELMT_TABLE_INDEX(x) (((x) & 0x7F) << 0) /* Maximum Full Table/LMT Table Index<6:0> */ + +/* +* REG_PEAK_WAIT_TIME +*/ +#define MANUAL_CTRL_IN_DECR_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Manual (CTRL_IN) Decr Gain Step Size<2:0> */ +#define PEAK_OVERLOAD_WAIT_TIME(x) (((x) & 0x1F) << 0) /* Peak Overload Wait Time<4:0> */ + +/* +* REG_DIGITAL_GAIN +*/ +#define DIG_GAIN_STP_SIZE(x) (((x) & 0x7) << 5) /* Dig Gain Step Size<2:0> */ +#define MAXIMUM_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Maximum Digital Gain<4:0> */ + +/* +* REG_AGC_LOCK_LEVEL +*/ +#define ENABLE_DIG_SAT_OVRG (1 << 7) /* Enable Dig Sat Ovrg */ +#define AGC_LOCK_LEVEL_FAST_AGC_INNER_HIGH_THRESH_SLOW(x) (((x) & 0x7F) << 0) /* AGC Lock Level (Fast)/ AGC Inner High Threshold (Slow) <6:0> */ + +/* +* REG_GAIN_STP_CONFIG1 +*/ +#define LMT_DETECTOR_SETTLING_TIME(x) (((x) & 0x7) << 5) /* LMT Detector Settling Time<2:0> */ +#define DEC_STP_SIZE_FOR_LARGE_LMT_OVERLOAD(x) (((x) & 0x7) << 2) /* Dec Step Size for: Large LMT Overload/ Full Table Case #3 <2:0> */ +#define ADC_NOISE_CORRECTION_FACTOR(x) (((x) & 0x3) << 0) /* ADC Noise Correction Factor<1:0> */ + +/* +* REG_GAIN_STP_CONFIG_2 +*/ +#define DECREMENT_STP_SIZE_FOR_SMALL_LPF_GAIN_CHANGE(x) (((x) & 0x7) << 4) /* Fast Attack Only. Decrement Step Size for: Small LPF Gain Change / Full Table Case #2 <2:0> */ +#define LARGE_LPF_GAIN_STEP(x) (((x) & 0xF) << 0) /* Decrement Step Size for: Large LPF Gain Change / Full Table Case #1<3:0> */ + +/* +* REG_SMALL_LMT_OVERLOAD_THRESH +*/ +#define FORCE_PD_RESET_RX2 (1 << 7) /* Force PD Reset Rx2 */ +#define FORCE_PD_RESET_RX1 (1 << 6) /* Force PD Reset Rx1 */ +#define SMALL_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Small LMT Overload Threshold<5:0> */ + +/* +* REG_LARGE_LMT_OVERLOAD_THRESH +*/ +#define LARGE_LMT_OVERLOAD_THRESH(x) (((x) & 0x3F) << 0) /* Large LMT Overload Threshold<5:0> */ + +/* +* REG_RX1_MANUAL_LMT_FULL_GAIN +*/ +#define POWER_MEAS_IN_STATE_5_MSB (1 << 7) /* Power Meas in State 5 <3> */ +#define RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx1 Manual Full table/LMT table Gain Index<6:0> */ +#define RX_FULL_TBL_IDX_MASK RX1_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(~0) + +/* +* REG_RX1_MANUAL_LPF_GAIN +*/ +#define POWER_MEAS_IN_STATE_5(x) (((x) & 0x7) << 5) /* Power Meas in State 5<2:0> */ +#define RX1_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual LPF Gain <4:0> */ +#define RX_LPF_IDX_MASK RX1_MANUAL_LPF_GAIN(~0) + +/* +* REG_RX1_MANUAL_DIGITALFORCED_GAIN +*/ +#define FORCE_RX1_DIGITAL_GAIN (1 << 5) /* Force Rx1 Digital Gain */ +#define RX1_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx1 Manual/Forced Digital Gain<4:0> */ +#define RX_DIGITAL_IDX_MASK RX1_MANUALFORCED_DIGITAL_GAIN(~0) +/* +* REG_RX2_MANUAL_LMT_FULL_GAIN +*/ +#define RX2_MANUAL_FULL_TABLE_LMT_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Rx2 Manual Full table/ LMT table Gain Index<6:0> */ + +/* +* REG_RX2_MANUAL_LPF_GAIN +*/ +#define RX2_MANUAL_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual LPF Gain<4:0> */ + +/* +* REG_RX2_MANUAL_DIGITALFORCED_GAIN +*/ +#define FORCE_RX2_DIGITAL_GAIN (1 << 5) /* Force Rx2 Digital Gain */ +#define RX2_MANUALFORCED_DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Rx2 Manual/Forced Digital Gain<4:0> */ + +/* +* REG_FAST_CONFIG_1 +*/ +#define ENABLE_GAIN_INC_AFTER_GAIN_LOCK (1 << 7) /* Enable Gain Inc after Gain Lock */ +#define GOTO_OPT_GAIN_IF_ENERGY_LOST_OR_EN_AGC_HIGH (1 << 6) /* Goto Opt Gain if Energy Lost or EN_AGC High */ +#define GOTO_SET_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Set Gain if EN_AGC High */ +#define GOTO_SET_GAIN_IF_EXIT_RX_STATE (1 << 4) /* Goto Set Gain if Exit Rx State */ +#define DONT_UNLOCK_GAIN_IF_ENERGY_LOST (1 << 3) /* Don't Unlock Gain if Energy Lost */ +#define GOTO_OPTIMIZED_GAIN_IF_EXIT_RX_STATE (1 << 2) /* Goto Optimized Gain if Exit Rx State */ +#define DONT_UNLOCK_GAIN_IF_LG_ADC_OR_LMT_OVRG (1 << 1) /* Don't Unlock Gain If Lg ADC or LMT Ovrg */ +#define ENABLE_INCR_GAIN (1 << 0) /* Enable Incr Gain */ + +/* +* REG_FAST_CONFIG_2_SETTLING_DELAY +*/ +#define USE_LAST_LOCK_LEVEL_FOR_SET_GAIN (1 << 7) /* Use Last Lock Level for Set Gain */ +#define ENABLE_LMT_GAIN_INC_FOR_LOCK_LEVEL (1 << 6) /* Enable LMT Gain Inc for Lock Level */ +#define GOTO_MAX_GAIN_OR_OPT_GAIN_IF_EN_AGC_HIGH (1 << 5) /* Goto Max Gain or Opt Gain if EN_AGC High */ +#define SETTLING_DELAY(x) (((x) & 0x1F) << 0) /* Settling Delay<4:0> */ + +/* +* REG_FAST_ENERGY_LOST_THRESH +*/ +#define POST_LOCK_LEVEL_STP_SIZE_FOR_LPF_TABLE_FULL_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step Size for: LPF Table/ Full Table <1:0> */ +#define ENERGY_LOST_THRESH(x) (((x) & 0x3F) << 0) /* Energy lost threshold<5:0> */ + +/* +* REG_FAST_STRONGER_SIGNAL_THRESH +*/ +#define POST_LOCK_LEVEL_STP_FOR_LMT_TABLE(x) (((x) & 0x3) << 6) /* Post Lock Level Step for LMT Table <1:0> */ +#define STRONGER_SIGNAL_THRESH(x) (((x) & 0x3F) << 0) /* Stronger Signal Threshold<5:0> */ + +/* +* REG_FAST_LOW_POWER_THRESH +*/ +#define DONT_UNLOCK_GAIN_IF_ADC_OVRG (1 << 7) /* Don't unlock gain if ADC Ovrg */ +#define LOW_POWER_THRESH(x) (((x) & 0x7F) << 0) /* Low Power Threshold<6:0> */ + +/* +* REG_FAST_STRONG_SIGNAL_FREEZE +*/ +#define DONT_UNLOCK_GAIN_IF_STRONGER_SIGNAL (1 << 7) /* Don't unlock gain if Stronger Signal */ + +/* +* REG_FAST_FINAL_OVER_RANGE_AND_OPT_GAIN +*/ +#define FINAL_OVER_RANGE_COUNT(x) (((x) & 0x7) << 5) /* Final Over Range Count<2:0> */ +#define OPTIMIZE_GAIN_OFFSET(x) (((x) & 0xF) << 0) /* Optimize Gain Offset<3:0> */ + +/* +* REG_FAST_ENERGY_DETECT_COUNT +*/ +#define INCREMENT_GAIN_STP_LPFLMT(x) (((x) & 0x7) << 5) /* Increment Gain Step (LPF/LMT)<2:0> */ +#define ENERGY_DETECT_COUNT(x) (((x) & 0x1F) << 0) /* Energy Detect count<4:0> */ + +/* +* REG_FAST_AGCLL_UPPER_LIMIT +*/ +#define AGCLL_MAX_INCREASE(x) (((x) & 0x3F) << 0) /* AGCLL Max Increase<5:0> */ + +/* +* REG_FAST_GAIN_LOCK_EXIT_COUNT +*/ +#define GAIN_LOCK_EXIT_COUNT(x) (((x) & 0x3F) << 0) /* Gain Lock Exit Count<5:0> */ + +/* +* REG_FAST_INITIAL_LMT_GAIN_LIMIT +*/ +#define INITIAL_LMT_GAIN_LIMIT(x) (((x) & 0x7F) << 0) /* Initial LMT Gain Limit<6:0> */ + +/* +* REG_AGC_INNER_LOW_THRESH +*/ +#define PREVENT_GAIN_INC (1 << 7) /* Prevent Gain Inc */ +#define AGC_INNER_LOW_THRESH(x) (((x) & 0x7F) << 0) /* AGC Inner Low Threshold<6:0> */ + +/* +* REG_LMT_OVERLOAD_COUNTERS +*/ +#define LARGE_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large LMT Overload Exceeded Counter<3:0> */ +#define SMALL_LMT_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small LMT Overload Exceeded Counter<3:0> */ + +/* +* REG_ADC_OVERLOAD_COUNTERS +*/ +#define LARGE_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 4) /* Large ADC Overload Exceeded Counter<3:0> */ +#define SMALL_ADC_OVERLOAD_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Small ADC Overload Exceeded Counter<3:0> */ + +/* +* REG_GAIN_STP1 +*/ +#define IMMED_GAIN_CHANGE_IF_LG_LMT_OVERLOAD (1 << 7) /* Immed. Gain Change if Lg LMT Overload */ +#define IMMED_GAIN_CHANGE_IF_LG_ADC_OVERLOAD (1 << 3) /* Immed. Gain Change if Lg ADC Overload */ +#define AGC_INNER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 4) /* AGC Inner High Threshold Exceeded Step Size<2:0> */ +#define AGC_INNER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0x7) << 0) /* AGC Inner Low Threshold Exceeded Step Size<2:0> */ + +/* +* REG_DIGITAL_SAT_COUNTER +*/ +#define DOUBLE_GAIN_COUNTER (1 << 5) /* Double Gain Counter */ +#define ENABLE_SYNC_FOR_GAIN_COUNTER (1 << 4) /* Enable Sync for Gain Counter */ +#define DIG_SATURATION_EXED_COUNTER(x) (((x) & 0xF) << 0) /* Dig Saturation Exceeded Counter<3:0> */ + +/* +* REG_OUTER_POWER_THRESHS +*/ +#define AGC_OUTER_HIGH_THRESH(x) (((x) & 0xF) << 4) /* AGC Outer High Threshold<3:0> */ +#define AGC_OUTER_LOW_THRESH(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold<3:0> */ + +/* +* REG_GAIN_STP_2 +*/ +#define AGC_OUTER_HIGH_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 4) /* AGC outer High Threshold Exceeded Step Size<3:0> */ +#define AGC_OUTER_LOW_THRESH_EXED_STP_SIZE(x) (((x) & 0xF) << 0) /* AGC Outer Low Threshold Exceeded Step Size<3:0> */ + +/* +* REG_EXT_LNA_HIGH_GAIN +*/ +#define EXT_LNA_HIGH_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA High Gain<5:0> */ + +/* +* REG_EXT_LNA_LOW_GAIN +*/ +#define EXT_LNA_LOW_GAIN(x) (((x) & 0x3F) << 0) /* Ext LNA Low Gain<5:0> */ + +/* +* REG_GAIN_TABLE_ADDRESS +*/ +#define GAIN_TABLE_ADDRESS(x) (((x) & 0x7F) << 0) /* Gain Table Address<6:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA1 +*/ +#define EXT_LNA_CTRL (1 << 7) /* Ext LNA Ctrl */ +#define LNA_GAIN(x) (((x) & 0x3) << 5) /* LNA Gain <1:0> */ +#define MIXER_GM_GAIN(x) (((x) & 0x1F) << 0) /* Mixer Gm Gain <4:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA2 +*/ +#define TIA_GAIN (1 << 5) /* TIA Gain */ +#define LPF_GAIN(x) (((x) & 0x1F) << 0) /* LPF Gain <4:0> */ + +/* +* REG_GAIN_TABLE_WRITE_DATA_3 +*/ +#define RF_DC_CAL (1 << 5) /* RF DC Cal */ +#define DIGITAL_GAIN(x) (((x) & 0x1F) << 0) /* Digital Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_1 +*/ +#define TO_LNA_GAIN(x) (((x) >> 5) & 0x3) /* LNA Gain <1:0> */ +#define TO_MIXER_GM_GAIN(x) (((x) >> 0) & 0x1F) /* Mixer Gm Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_2 +*/ +#define TO_LPF_GAIN(x) (((x) >> 0) & 0x1F) /* LPF Gain <4:0> */ + +/* +* REG_GAIN_TABLE_READ_DATA_3 +*/ +#define TO_DIGITAL_GAIN(x) (((x) >> 0) & 0x1F) /* Digital Gain <4:0> */ + +/* +* REG_GAIN_TABLE_CONFIG +*/ +#define WRITE_GAIN_TABLE (1 << 2) /* Write Gain Table */ +#define START_GAIN_TABLE_CLOCK (1 << 1) /* Start Gain Table Clock */ +#define RECEIVER_SELECT(x) (((x) & 0x3) << 3) /* Receiver Select<1:0> */ +#define GT_RX1 1 +#define GT_RX2 2 + + +/* +* REG_GM_SUB_TABLE_GAIN_WRITE +*/ +#define GM_SUB_TABLE_GAIN_WRITE(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Write<6:0> */ + +/* +* REG_GM_SUB_TABLE_BIAS_WRITE +*/ +#define GM_SUB_TABLE_BIAS_WRITE(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Write<4:0> */ + +/* +* REG_GM_SUB_TABLE_CTRL_WRITE +*/ +#define GM_SUB_TABLE_CTRL_WRITE(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Write<5:0> */ + +/* +* REG_GM_SUB_TABLE_GAIN_READ +*/ +#define GM_SUB_TABLE_GAIN_READ(x) (((x) & 0x7F) << 0) /* Gm Sub Table Gain Word Read<6:0> */ + +/* +* REG_GM_SUB_TABLE_BIAS_READ +*/ +#define GM_SUB_TABLE_BIAS_READ(x) (((x) & 0x1F) << 0) /* Gm Sub Table Bias Word Read<4:0> */ + +/* +* REG_GM_SUB_TABLE_CTRL_READ +*/ +#define GM_SUB_TABLE_CTRL_READ(x) (((x) & 0x3F) << 0) /* Gm Sub Table Control Word Read<5:0> */ + +/* +* REG_GM_SUB_TABLE_CONFIG +*/ +#define WRITE_GM_SUB_TABLE (1 << 2) /* Write Gm Sub Table */ +#define START_GM_SUB_TABLE_CLOCK (1 << 1) /* Start Gm Sub Table Clock */ + +/* +* REG_GAIN_DIFF_WORDERROR_WRITE +*/ +#define CALIB_TABLE_GAIN_DIFFERROR_WORD(x) (((x) & 0x3F) << 0) /* Calib Table Gain Diff/Error Word<5:0> */ + +/* +* REG_GAIN_ERROR_READ +*/ +#define CALIB_TABLE_GAIN_ERROR(x) (((x) & 0x1F) << 0) /* Calib Table Gain Error<4:0> */ + +/* +* REG_CONFIG +*/ +#define READ_SELECT (1 << 4) /* Read Select */ +#define WRITE_MIXER_ERROR_TABLE (1 << 3) /* Write Mixer Error Table */ +#define WRITE_LNA_ERROR_TABLE (1 << 2) /* Write LNA Error Table */ +#define WRITE_LNA_GAIN_DIFF (1 << 1) /* Write LNA Gain Diff */ +#define START_CALIB_TABLE_CLOCK (1 << 0) /* Start Calib Table Clock */ +#define CALIB_TABLE_SELECT(x) (((x) & 0x3) << 5) /* Calib Table Select<1:0> */ + +/* +* REG_LNA_GAIN_DIFF_READ_BACK +*/ +#define LNA_CALIB_TABLE_GAIN_DIFFERENCE_WORD(x) (((x) & 0x3F) << 0) /* LNA Calib Table Gain Difference Word<5:0> */ + +/* +* REG_MAX_MIXER_CALIBRATION_GAIN_INDEX +*/ +#define MAX_MIXER_CALIBRATION_GAIN_INDEX(x) (((x) & 0x1F) << 0) /* Max Mixer Calibration Gain Index<4:0> */ + +/* +* REG_SETTLE_TIME +*/ +#define ENABLE_DIG_GAIN_CORR (1 << 7) /* Enable Dig Gain Corr */ +#define FORCE_TEMP_SENSOR_FOR_CAL (1 << 6) /* Force Temp Sensor for Cal */ +#define SETTLE_TIME(x) (((x) & 0x3F) << 0) /* Settle Time<5:0> */ + +/* +* REG_MEASURE_DURATION +*/ +#define GAIN_CAL_MEAS_DURATION(x) (((x) & 0xF) << 0) /* Gain Cal Meas Duration<3:0> */ + +/* +* REG_MEASURE_DURATION_01 +*/ +#define MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* Measurement duration 1 <3:0> */ +#define MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* Measurement duration 0 <3:0> */ + +/* +* REG_MEASURE_DURATION_23 +*/ +#define MEASUREMENT_DURATION_3(x) (((x) & 0xF) << 4) /* Measurement duration 3 <3:0> */ +#define MEASUREMENT_DURATION_2(x) (((x) & 0xF) << 0) /* Measurement duration 2 <3:0> */ + +/* +* REG_RSSI_CONFIG +*/ +#define START_RSSI_MEAS (1 << 5) /* Start RSSI Meas (Mode 4) */ +#define ENABLE_ADC_POWER_MEAS (1 << 1) /* Enable ADC Power Meas. */ +#define DEFAULT_RSSI_MEAS_MODE (1 << 0) /* Default RSSI Meas Mode */ +#define RFIR_FOR_RSSI_MEASUREMENT(x) (((x) & 0x3) << 6) /* RFIR for RSSI measurement<1:0> */ +#define RSSI_MODE_SELECT(x) (((x) & 0x7) << 2) /* RSSI Mode Select<2:0> */ + +/* +* REG_ADC_MEASURE_DURATION_01 +*/ +#define ADC_POWER_MEASUREMENT_DURATION_1(x) (((x) & 0xF) << 4) /* ADC Power Measurement Duration 1<3:0> */ +#define ADC_POWER_MEASUREMENT_DURATION_0(x) (((x) & 0xF) << 0) /* ADC Power Measurement Duration 0 <3:0> */ + +/* +* REG_DEC_POWER_MEASURE_DURATION_0 +*/ +#define USE_HB3_OUT_FOR_ADC_PWR_MEAS (1 << 7) /* Use HB3 Out for ADC Pwr Meas */ +#define USE_HB1_OUT_FOR_DEC_PWR_MEAS (1 << 6) /* Use HB1 Out for Dec pwr Meas */ +#define ENABLE_DEC_PWR_MEAS (1 << 5) /* Enable Dec Pwr Meas */ +#define DEFAULT_MODE_ADC_POWER (1 << 4) /* Default Mode ADC Power */ +#define DEC_POWER_MEASUREMENT_DURATION(x) (((x) & 0xF) << 0) /* Dec Power Measurement Duration <3:0> */ + +/* +* REG_LNA_GAIN +*/ +#define DB_GAIN_READBACK_CHANNEL (1 << 0) /* dB Gain Read-back Channel */ +#define MAX_LNA_GAIN(x) (((x) & 0x7F) << 1) /* Max LNA Gain<6:0> */ + +/* +* REG_RX_QUAD_CAL_LEVEL +*/ +#define RX_QUAD_CAL_LEVEL(x) (((x) & 0xF) << 0) /* Rx Quad Cal Level <3 :0> */ + +/* +* REG_CALIBRATION_CONFIG_1 +*/ +#define ENABLE_PHASE_CORR (1 << 7) /* Enable Phase Corr */ +#define ENABLE_GAIN_CORR (1 << 6) /* Enable Gain Corr */ +#define USE_SETTLE_COUNT_FOR_DC_CAL_WAIT (1 << 5) /* Use Settle Count for DC Cal Wait */ +#define FIXED_DC_CAL_WAIT_TIME (1 << 4) /* Fixed DC Cal Wait Time */ +#define FREE_RUN_MODE (1 << 3) /* Free Run Mode */ +#define ENABLE_CORR_WORD_DECIMATION (1 << 2) /* Enable Corr Word Decimation */ +#define ENABLE_TRACKING_MODE_CH2 (1 << 1) /* Enable Tracking Mode CH2 */ +#define ENABLE_TRACKING_MODE_CH1 (1 << 0) /* Enable Tracking Mode CH1 */ + +/* +* REG_CALIBRATION_CONFIG_2 +*/ +#define SOFT_RESET (1 << 7) /* Soft Reset */ +#define CALIBRATION_CONFIG2_DFLT (0x3 << 5) /* Must be 2'b11 */ +#define K_EXP_PHASE(x) (((x) & 0x1F) << 0) /* K exp Phase<4:0> */ + +/* +* REG_CALIBRATION_CONFIG_3 +*/ +#define PREVENT_POS_LOOP_GAIN (1 << 7) /* Prevent Pos Loop Gain */ +#define K_EXP_AMPLITUDE(x) (((x) & 0x1F) << 0) /* K exp Amplitude<4:0> */ + +/* +* REG_RX_QUAD_GAIN1 +*/ +#define RX_FULL_TABLELMT_TABLE_GAIN(x) (((x) & 0x7F) << 0) /* Rx Full table/LMT table gain<6:0> */ + +/* +* REG_RX_QUAD_GAIN2 +*/ +#define CORRECTION_WORD_DECIMATION_M(x) (((x) & 0x7) << 5) /* Correction Word Decimation M<2:0> */ +#define RX_LPF_GAIN(x) (((x) & 0x1F) << 0) /* Rx LPF gain<4:0> */ + +/* +* REG_RX1_INPUT_A_OFFSETS +*/ +#define RX1_INPUT_A_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input A "I" DC Offset<5:0> */ +#define RX1_INPUT_A_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input A "Q" DC Offset<9:8> */ + +/* +* REG_INPUT_A_OFFSETS_1 +*/ +#define RX2_INPUT_A_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input A "Q" DC Offset<3:0> */ +#define RX1_INPUT_A_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input A "I" DC Offset<9:6> */ + +/* +* REG_RX2_INPUT_A_OFFSETS +*/ +#define RX2_INPUT_A_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input A "I" DC Offset<1:0> */ +#define RX2_INPUT_A_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input A "Q" DC Offset<9:4> */ + +/* +* REG_RX1_INPUT_BC_OFFSETS +*/ +#define RX1_INPUT_BC_I_DC_OFFSET_LSB(x) (((x) & 0x3F) << 2) /* Rx1 Input B&C "I" DC Offset<5:0> */ +#define RX1_INPUT_BC_Q_DC_OFFSET(x) (((x) & 0x3) << 0) /* Rx1 Input B&C "Q" DC Offset<9:8> */ + +/* +* REG_INPUT_BC_OFFSETS_1 +*/ +#define RX2_INPUT_BC_Q_DC_OFFSET_LSB(x) (((x) & 0xF) << 4) /* Rx2 Input B&C "Q" DC Offset<3:0> */ +#define RX1_INPUT_BC_I_DC_OFFSET_MSB(x) (((x) & 0xF) << 0) /* Rx1 Input B&C "I" DC Offset<9:6> */ + +/* +* REG_RX2_INPUT_BC_OFFSETS +*/ +#define RX2_INPUT_BC_I_DC_OFFSET(x) (((x) & 0x3) << 6) /* Rx2 Input B&C "I" DC Offset<1:0> */ +#define RX2_INPUT_BC_Q_DC_OFFSET_MSB(x) (((x) & 0x3F) << 0) /* Rx2 Input B&C "Q" DC Offset<9:4> */ + +/* +* REG_FORCE_BITS +*/ +#define RX2_INPUT_BC_FORCE_OFFSET (1 << 7) /* Rx2 Input B&C Force offset */ +#define RX1_INPUT_BC_FORCE_OFFSET (1 << 6) /* Rx1 Input B&C Force offset */ +#define RX2_INPUT_BC_FORCE_PHGAIN (1 << 5) /* Rx2 Input B&C Force Ph/Gain */ +#define RX1_INPUT_BC_FORCE_PHGAIN (1 << 4) /* Rx1 Input B&C Force Ph/Gain */ +#define RX2_INPUT_A_FORCE_OFFSET (1 << 3) /* Rx2 Input A Force offset */ +#define RX1_INPUT_A_FORCE_OFFSET (1 << 2) /* Rx1 Input A Force offset */ +#define RX2_INPUT_A_FORCE_PHGAIN (1 << 1) /* Rx2 Input A Force Ph/Gain */ +#define RX1_INPUT_A_FORCE_PHGAIN (1 << 0) /* Rx1 Input A Force Ph/Gain */ + +/* +* REG_RF_DC_OFFSET_CONFIG_1 +*/ +#define DAC_FS(x) (((x) & 0x3) << 4) /* DAC FS<1:0> */ +#define RF_DC_CALIBRATION_COUNT(x) (((x) & 0xF) << 0) /* RF DC Calibration Count<3:0> */ + +/* +* REG_RF_DC_OFFSET_ATTEN +*/ +#define RF_DC_OFFSET_TABLE_UPDATE_COUNT(x) (((x) & 0x7) << 5) /* RF DC Offset Table Update Count<2:0> */ +#define RF_DC_OFFSET_ATTEN(x) (((x) & 0x1F) << 0) /* RF DC Offset Attenuation<4:0> */ + +/* +* REG_INVERT_BITS +*/ +#define INVERT_RX2_RF_DC_CGIN_WORD (1 << 7) /* Invert Rx2 RF DC CGin Word */ +#define INVERT_RX1_RF_DC_CGIN_WORD (1 << 6) /* Invert Rx1 RF DC CGin Word */ +#define INVERT_RX2_RF_DC_CGOUT_WORD (1 << 5) /* Invert Rx2 RF DC CGout Word */ +#define INVERT_RX1_RF_DC_CGOUT_WORD (1 << 4) /* Invert Rx1 RF DC CGout Word */ + +/* +* REG_DC_OFFSET_CONFIG2 +*/ +#define USE_WAIT_COUNTER_FOR_RF_DC_INIT_CAL (1 << 7) /* Use Wait Counter for RF DC Init Cal */ +#define ENABLE_FAST_SETTLE_MODE (1 << 6) /* Enable Fast Settle Mode */ +#define ENABLE_BB_DC_OFFSET_TRACKING (1 << 5) /* Enable BB DC Offset Tracking */ +#define RESET_ACC_ON_GAIN_CHANGE (1 << 4) /* Reset Acc on Gain Change */ +#define ENABLE_RF_OFFSET_TRACKING (1 << 3) /* Enable RF Offset Tracking */ +#define DC_OFFSET_UPDATE(x) (((x) & 0x7) << 0) /* DC Offset Update<2:0> */ + +/* +* REG_RF_CAL_GAIN_INDEX +*/ +#define RF_MINIMUM_CALIBRATION_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* RF Minimum Calibration Gain Index<6:0> */ + +/* +* REG_SOI_THRESH +*/ +#define RF_SOI_THRESH(x) (((x) & 0x7F) << 0) /* RF SOI Threshold<6:0> */ + +/* +* REG_BB_DC_OFFSET_SHIFT +*/ +#define INCREASE_COUNT_DURATION (1 << 7) /* Increase Count Duration */ +#define BB_TRACKING_DECIMATE(x) (((x) & 0x3) << 5) /* BB Tracking Decimate<1:0> */ +#define BB_DC_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC M Shift<4:0> */ + +/* +* REG_BB_DC_OFFSET_FAST_SETTLE_SHIFT +*/ +#define READ_BACK_CH_SEL (1 << 7) /* Read Back CH Sel */ +#define UPDATE_TRACKING_WORD (1 << 6) /* Update Tracking Word */ +#define FORCE_RX_NULL (1 << 5) /* Force Rx Null */ +#define BB_DC_TRACKING_FAST_SETTLE_M_SHIFT(x) (((x) & 0x1F) << 0) /* BB DC Tracking Fast Settle M Shift<4:0> */ + +/* +* REG_BB_DC_OFFSET_ATTEN +*/ +#define BB_DC_OFFSET_ATTEN(x) (((x) & 0xF) << 0) /* BB DC Offset Atten<3:0> */ + +/* +* REG_RX1_BB_DC_WORD_I_MSB +*/ +#define RX1_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word I<14:8> */ + +/* +* REG_RX1_BB_DC_WORD_Q_MSB +*/ +#define RX1_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1 BB DC Offset Correction word Q<14:8> */ + +/* +* REG_RX2_BB_DC_WORD_I_MSB +*/ +#define RX2_BB_DC_OFFSET_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word I<14:8> */ + +/* +* REG_RX2_BB_DC_WORD_Q_MSB +*/ +#define RX2_BB_DC_OFFSET_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX2 BB DC Offset Correction word Q<14:8> */ + +/* +* REG_BB_TRACK_CORR_WORD_I_MSB +*/ +#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_I(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word I<14:8> */ + +/* +* REG_BB_TRACK_CORR_WORD_Q_MSB +*/ +#define RX1RX2_BB_DC_OFFSET_TRACKING_CORRECTION_WORD_Q(x) (((x) & 0x7F) << 0) /* RX1/RX2 BB DC Offset Tracking correction word Q<14:8> */ + +/* +* REG_SYMBOL_LSB +*/ +#define RX2_RSSI_SYMBOL (1 << 1) /* Rx2 RSSI symbol <0> */ +#define RX1_RSSI_SYMBOL (1 << 0) /* Rx1 RSSI symbol <0> */ + +/* +* REG_PREAMBLE_LSB +*/ +#define RX2_RSSI_PREAMBLE (1 << 1) /* Rx2 RSSI preamble <0> */ +#define RX1_RSSI_PREAMBLE (1 << 0) /* Rx1 RSSI preamble <0> */ + +/* +* REG_RX1_RSSI_SYMBOL, REG_RX1_RSSI_PREAMBLE, +* REG_RX2_RSSI_SYMBOL, REG_RX2_RSSI_PREAMBLE +*/ +#define RSSI_LSB_SHIFT 1 +#define RSSI_LSB_MASK1 0x01 +#define RSSI_LSB_MASK2 0x02 + +/* +* REG_RX_PATH_GAIN_LSB +*/ +#define RX_PATH_GAIN (1 << 0) /* Rx Path Gain<0> */ + +/* +* REG_RX_DIFF_LNA_FORCE +*/ +#define FORCE_RX2_LNA_GAIN (1 << 7) /* Force Rx2 LNA Gain */ +#define RX2_LNA_BYPASS (1 << 6) /* Rx2 LNA Bypass */ +#define FORCE_RX1_LNA_GAIN (1 << 3) /* Force Rx1 LNA Gain */ +#define RX1_LNA_BYPASS (1 << 2) /* Rx1 LNA Bypass */ +#define RX2_LNA_GAIN(x) (((x) & 0x3) << 4) /* Rx2 LNA Gain<1:0> */ +#define RX1_LNA_GAIN(x) (((x) & 0x3) << 0) /* Rx1 LNA Gain<1:0> */ + +/* +* REG_RX_LNA_BIAS_COARSE +*/ +#define RX_LNA_BIAS_COARSE(x) (((x) & 0xF) << 0) /* Rx LNA Bias Coarse<3:0> */ + +/* +* REG_RX_LNA_BIAS_FINE_0 +*/ +#define RX_LNA_PCASCODE_BIAS(x) (((x) & 0x7) << 5) /* Rx LNA p-Cascode Bias<2:0> */ +#define RX_LNA_BIAS(x) (((x) & 0x1F) << 0) /* Rx LNA Bias<4:0> */ + +/* +* REG_RX_LNA_BIAS_FINE_1 +*/ +#define RX_LNA_P_CASCODE_BIAS_FINE(x) (((x) & 0x3) << 0) /* Rx LNA p- Cascode Bias Fine<4:3> */ + +/* +* REG_RX_MIX_GM_CONFIG +*/ +#define RX_MIX_GM_CM_OUT(x) (((x) & 0x7) << 5) /* Rx Mix Gm CM Out<2:0> */ +#define RX_MIX_GM_PLOAD(x) (((x) & 0x3) << 0) /* Rx Mix Gm pload <1:0> */ + +/* +* REG_RX1_MIX_GM_FORCE +*/ +#define FORCE_RX1_MIX_GM (1 << 6) /* Force Rx1 Mix Gm */ +#define RX1_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx1 Mix Gm Gain<5:0> */ + +/* +* REG_RX1_MIX_GM_BIAS_FORCE +*/ +#define RX1_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx1 Mix Gm Bias<4:0> */ + +/* +* REG_RX2_MIX_GM_FORCE +*/ +#define FORCE_RX2_MIX_GM (1 << 6) /* Force Rx2 Mix Gm */ +#define RX2_MIX_GM_GAIN(x) (((x) & 0x3F) << 0) /* Rx2 Mix Gm Gain<5:0> */ + +/* +* REG_RX2_MIX_GM_BIAS_FORCE +*/ +#define RX2_MIX_GM_BIAS(x) (((x) & 0x1F) << 0) /* Rx2 Mix Gm Bias<4:0> */ + +/* +* REG_INPUT_A_MSBS +*/ +#define INPUT_A_RX1_Q(x) (((x) & 0x3) << 6) /* Input A RX1 Q<9:8> */ +#define INPUT_A_RX1_I(x) (((x) & 0x3) << 4) /* Input A RX1 I<9:8> */ +#define INPUT_A_RX2_I(x) (((x) & 0x3) << 2) /* Input A RX2 I<9:8> */ +#define INPUT_A_RX2_Q(x) (((x) & 0x3) << 0) /* Input A RX2 Q<9:8> */ + +/* +* REG_INPUTS_BC_MSBS +*/ +#define INPUTS_BC_RX1_Q(x) (((x) & 0x3) << 6) /* Inputs B&C RX1 Q<9:8> */ +#define INPUTS_BC_RX1_I(x) (((x) & 0x3) << 4) /* Inputs B&C RX1 I<9:8> */ +#define INPUTS_BC_RX2_I(x) (((x) & 0x3) << 2) /* Inputs B&C RX2 I<9:8> */ +#define INPUTS_BC_RX2_Q(x) (((x) & 0x3) << 0) /* Inputs B&C RX2 Q<9:8> */ + +/* +* REG_FORCE_OS_DAC +*/ +#define FORCE_CGIN_DAC (1 << 2) /* Force CGin DAC */ + +/* +* REG_RX_MIX_LO_CM +*/ +#define RX_MIX_LO_CM(x) (((x) & 0x3F) << 0) /* Rx Mix LO CM<5:0> */ + +/* +* REG_RX_CGB_SEG_ENABLE +*/ +#define RX_CGB_SEG_ENABLE(x) (((x) & 0x3F) << 0) /* Rx CGB Seg Enable<5:0> */ + +/* +* REG_RX_MIX_INPUTBIAS +*/ +#define RX_CGB_INPUT_CM_SEL(x) (((x) & 0x3) << 4) /* Rx CGB Input CM Sel<1:0> */ +#define RX_CGB_BIAS(x) (((x) & 0xF) << 0) /* Rx CGB Bias<3:0> */ + +/* +* REG_RX_TIA_CONFIG +*/ +#define TIA2_OVERRIDE_C (1 << 3) /* TIA2 Override C */ +#define TIA2_OVERRIDE_R (1 << 2) /* TIA2 Override R */ +#define TIA1_OVERRIDE_C (1 << 1) /* TIA1 Override C */ +#define TIA1_OVERRIDE_R (1 << 0) /* TIA1 Override R */ +#define TIA_SEL_CC(x) (((x) & 0x7) << 5) /* TIA Sel CC<2:0> */ + +/* +* REG_TIA1_C_LSB +*/ +#define TIA1_RF(x) (((x) & 0x3) << 6) /* TIA1 RF<1:0> */ +#define TIA1_C_LSB(x) (((x) & 0x3F) << 0) /* TIA1 C LSB<5:0> */ + +/* +* REG_TIA1_C_MSB +*/ +#define TIA1_C_MSB(x) (((x) & 0x7F) << 0) /* TIA1 C MSB<6:0> */ + +/* +* REG_TIA2_C_LSB +*/ +#define TIA2_RF(x) (((x) & 0x3) << 6) /* TIA2 RF<1:0> */ +#define TIA2_C_LSB(x) (((x) & 0x3F) << 0) /* TIA2 C LSB<5:0> */ + +/* +* REG_TIA2_C_MSB +*/ +#define TIA2_C_MSB(x) (((x) & 0x7F) << 0) /* TIA2 C MSB<6:0> */ + +/* +* REG_RX1_BBF_R1A +*/ +#define FORCE_RX1_RESISTORS (1 << 7) /* Force Rx1 Resistors */ +#define RX1_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx1 BBF R1A<5:0> */ + +/* +* REG_RX2_BBF_R1A +*/ +#define FORCE_RX2_RESISTORS (1 << 7) /* Force Rx2 Resistors */ +#define RX2_BBF_R1A(x) (((x) & 0x3F) << 0) /* Rx2 BBF R1A<5:0> */ + +/* +* REG_RX1_TUNE_CTRL +*/ +#define RX1_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx1 Tune Resample Phase */ +#define RX1_TUNE_RESAMPLE (1 << 1) /* Rx1 Tune Resample */ +#define RX1_PD_TUNE (1 << 0) /* Rx1 PD Tune */ + +/* +* REG_RX2_TUNE_CTRL +*/ +#define RX2_TUNE_RESAMPLE_PHASE (1 << 2) /* Rx2 Tune Resam ple Phase */ +#define RX2_TUNE_RESAMPLE (1 << 1) /* Rx2 Tune Resample */ +#define RX2_PD_TUNE (1 << 0) /* Rx2 PD Tune */ + +/* +* REG_RX_BBF_R2346 +*/ +#define TUNE_OVERRIDE (1 << 7) /* Tune Override */ +#define RX_BBF_R2346(x) (((x) & 0x7) << 0) /* Rx BBF R2346<2:0> */ + +/* +* REG_RX_BBF_C1_MSB +*/ +#define RX_BBF_C1_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C1 MSB<5:0> */ + +/* +* REG_RX_BBF_C1_LSB +*/ +#define RX_BBF_C1_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C1 LSB<6:0> */ + +/* +* REG_RX_BBF_C2_MSB +*/ +#define RX_BBF_C2_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C2 MSB<5:0> */ + +/* +* REG_RX_BBF_C2_LSB +*/ +#define RX_BBF_C2_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C2 LSB<6:0> */ + +/* +* REG_RX_BBF_C3_MSB +*/ +#define RX_BBF_C3_MSB(x) (((x) & 0x3F) << 0) /* Rx BBF C3 MSB<5:0> */ + +/* +* REG_RX_BBF_C3_LSB +*/ +#define RX_BBF_C3_LSB(x) (((x) & 0x7F) << 0) /* Rx BBF C3 LSB<6:0> */ + +/* +* REG_RX_BBF_CC1_CTR +*/ +#define RX_BBF_CC1_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC1 Ctr<6:0> */ + +/* +* REG_RX_BBF_POW_RZ_BYTE0 +*/ +#define MUST_BE_ZERO (1 << 7) /* Must be zero */ +#define RX1_BBF_POW_CTR(x) (((x) & 0x3) << 5) /* Rx1 BBF Pow Ctr<1:0> */ +#define RX_BBF_RZ1_CTR(x) (((x) & 0x3) << 3) /* Rx BBF Rz1 Ctr<1:0> */ + +/* +* REG_RX_BBF_CC2_CTR +*/ +#define RX_BBF_CC2_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC2 Ctr<6:0> */ + +/* +* REG_RX_BBF_POW_RZ_BYTE1 +*/ +#define RX_BBF_POW3_CTR(x) (((x) & 0x3) << 6) /* Rx BBF Pow3 Ctr<1:0> */ +#define RX_BBF_RZ3_CTR(x) (((x) & 0x3) << 4) /* Rx BBF RZ3 Ctr<1:0> */ +#define RX_BBF_POW2_CTR(x) (((x) & 0x3) << 2) /* Rx BBF Pow2 Ctr<1:0> */ +#define RX_BBF_RZ2_CTR(x) (((x) & 0x3) << 0) /* Rx BBF Rz2 Ctr<1:0> */ + +/* +* REG_RX_BBF_CC3_CTR +*/ +#define RX_BBF_CC3_CTR(x) (((x) & 0x7F) << 0) /* Rx BBF CC3 Ctr<6:0> */ + +/* +* REG_RX_BBF_TUNE +*/ +#define RXBBF_BYPASS_BIAS_R (1 << 7) /* RxBBF Bypass Bias R */ +#define RX_BBF_R5_TUNE (1 << 4) /* Rx BBF R5 Tune */ +#define RX1_BBF_TUNE_COMP_I (1 << 3) /* Rx1 BBF Tune Comp I */ +#define RX1_BBF_TUNE_COMP_Q (1 << 2) /* Rx1 BBF Tune Comp Q */ +#define RX2_BBF_TUNE_COMP_I (1 << 1) /* Rx2 BBF Tune Comp I */ +#define RX2_BBF_TUNE_COMP_Q (1 << 0) /* Rx2 BBF Tune Comp Q */ +#define RX_BBF_TUNE_CTR(x) (((x) & 0x3) << 5) /* Rx BBF Tune Ctr<1:0> */ + +/* +* REG_RX1_BBF_MAN_GAIN +*/ +#define RX1_BBF_FORCE_GAIN (1 << 5) /* Rx1 BBF Force Gain */ +#define RX1_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx1 BBF BQ Gain<1:0> */ +#define RX1_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx1 BBF Pole Gain<2:0> */ + +/* +* REG_RX2_BBF_MAN_GAIN +*/ +#define RX2_BBF_FORCE_GAIN (1 << 5) /* Rx2 BBF Force Gain */ +#define RX2_BBF_BQ_GAIN(x) (((x) & 0x3) << 3) /* Rx2 BBF BQ Gain<1:0> */ +#define RX2_BBF_POLE_GAIN(x) (((x) & 0x7) << 0) /* Rx2 BBF Pole Gain<2:0> */ + +/* +* REG_RX_BBF_TUNE_CONFIG +*/ +#define RX_TUNE_EVALTIME (1 << 4) /* Rx Tune Evaltime */ +#define RX_BBF_TUNE_DIVIDE (1 << 0) /* RX BBF Tune Divide<8> */ +#define TUNE_COMP_MASK(x) (((x) & 0x3) << 5) /* Tune Comp Mask <1:0> */ +#define RX_TUNE_MODE(x) (((x) & 0x7) << 1) /* Rx Tune Mode<2:0> */ + +/* +* REG_POLE_GAIN +*/ +#define POLE_GAIN_TUNE(x) (((x) & 0x3) << 0) /* Pole Gain Tune<1:0> */ + +/* +* REG_RX_BBBW_MHZ +*/ +#define RX_TUNE_BBBW_MHZ(x) (((x) & 0x1F) << 0) /* Rx Tune BBBW MHz<4::0> */ + +/* +* REG_RX_BBBW_KHZ +*/ +#define RX_TUNE_BBBW_KHZ(x) (((x) & 0x7F) << 0) /* Rx Tune BBBW kHz<6:0> */ + +/* +* REG_RX_PFD_CONFIG +*/ +#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */ + +/* +* REG_RX_INTEGER_BYTE_1 +*/ +#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */ + +/* +* REG_RX_FRACT_BYTE_2 +*/ +#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */ + +/* +* REG_RX_FORCE_VCO_TUNE_1 +*/ +#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */ + +/* +* REG_RX_ALC_VARACTOR +*/ +#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */ +#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */ + +/* +* REG_RX_VCO_OUTPUT +*/ +#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */ +#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */ + +/* +* REG_RX_CP_CURRENT +*/ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_RX_CP_OFFSET +*/ +#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */ + +/* +* REG_RX_CP_CONFIG +*/ +#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */ +#define F_CPCAL (1 << 3) /* F Cpcal */ +#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */ + +/* +* REG_RX_LOOP_FILTER_1 +*/ +#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */ +#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */ + +/* +* REG_RX_LOOP_FILTER_2 +*/ +#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */ +#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */ + +/* +* REG_RX_LOOP_FILTER_3 +*/ +#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */ +#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */ +#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */ +#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */ +#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */ + +/* +* REG_RX_DITHERCP_CAL +*/ +#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */ + +/* +* REG_RX_VCO_BIAS_1 +*/ +#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */ +#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */ + +/* +* REG_RX_CAL_STATUS +*/ +#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */ +#define CP_CAL_DONE (1 << 5) /* CP Cal Done */ +#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */ +#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */ + +/* +* REG_RX_VCO_CAL_REF +*/ +#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */ + +/* +* REG_RX_VCO_PD_OVERRIDES +*/ +#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */ +#define PWR_DOWN_VARACT_REF_TCF (1 << 2) /* Pwr Down Varact Ref Tcf */ +#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */ +#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */ + +/* +* REG_RX_CP_OVERRANGE_VCO_LOCK +*/ +#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */ +#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */ +#define VCO_LOCK (1 << 1) /* Lock */ + +/* +* REG_RX_VCO_LDO +*/ +#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */ +#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */ +#define VCO_LDO_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Sel<2:0> */ +#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */ + +/* +* REG_RX_VCO_CAL +*/ +#define VCO_CAL_EN (1 << 7) /* VCO Cal En */ +#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait <2:0> */ +#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count <1:0> */ + +/* +* REG_RX_LOCK_DETECT_CONFIG +*/ +#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */ +#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */ + +/* +* REG_RX_CP_LEVEL_DETECT +*/ +#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */ +#define CP_LEVEL_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Threshold Low<2:0> */ +#define CP_LEVEL_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Threshold High<2:0> */ + +/* +* REG_RX_DSM_SETUP_0 +*/ +#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */ + +/* +* REG_RX_DSM_SETUP_1 +*/ +#define SIF_CLOCK (1 << 6) /* SIF clock */ +#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */ +#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */ + +/* +* REG_RX_CORRECTION_WORD0 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */ +#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */ + +/* +* REG_RX_CORRECTION_WORD1 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */ + +/* +* REG_RX_VCO_VARACTOR_CTRL_0 +*/ +#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */ +#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */ + +/* +* REG_RX_VCO_VARACTOR_CTRL_1 +*/ +#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */ + +/* +* REG_RX_FAST_LOCK_SETUP +*/ +#define RX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Rx Fast Lock Load Synth */ +#define RX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Rx Fast Lock Profile Init */ +#define RX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Rx Fast Lock Profile Pin Select */ +#define RX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Rx Fast Lock Mode Enable */ +#define RX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Rx Fast Lock Profile<2:0> */ + +/* +* REG_RX_FAST_LOCK_PROGRAM_ADDR +*/ +#define RX_FAST_LOCK_PROFILE_ADDR(x) (((x) & 0x7) << 4) /* Rx Fast Lock Profile<2:0> */ +#define RX_FAST_LOCK_PROFILE_WORD(x) (((x) & 0xF) << 0) /* Configuration Word <3:0> */ + + +/* +* REG_RX_FAST_LOCK_PROGRAM_CTRL +*/ +#define RX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Rx Fast Lock Program Write */ +#define RX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Rx Fast Lock Program Clock Enable */ + +#define RX_FAST_LOCK_CONFIG_WORD_NUM 16 + +/* +* REG_RX_LO_GEN_POWER_MODE +*/ +#define RX_LO_GEN_POWER_MODE(x) (((x) & 0x3) << 4) /* Power Mode<3:0> */ + +/* +* REG_TX_PFD_CONFIG +*/ +#define DIV_TEST_EN (1 << 5) /* Div Test En */ +#define PFD_CLK_EDGE (1 << 1) /* PFD Clk Edge */ +#define BYPASS_LD_SYNTH (1 << 0) /* Bypass Ld Synth */ +#define PFD_WIDTH(x) (((x) & 0x3) << 2) /* PFD Width <1:0> */ + +/* +* REG_TX_INTEGER_BYTE_1 +*/ +#define SDM_BYPASS (1 << 7) /* SDM Bypass */ +#define SDM_POWER_DOWN (1 << 6) /* SDM Power Down */ +#define SYNTH_INTEGER_WORD(x) (((x) & 0x7) << 0) /* Synthesizer Integer Word<10:8> */ + +/* +* REG_TX_FRACT_BYTE_2 +*/ +#define SYNTH_FRACT_WORD(x) (((x) & 0x7F) << 0) /* Synthesizer Fractional Word <22:16> */ + +/* +* REG_TX_FORCE_ALC +*/ +#define FORCE_ALC_ENABLE (1 << 7) /* Force ALC Enable */ +#define FORCE_ALC_WORD(x) (((x) & 0x7F) << 0) /* Force ALC Word<6:0> */ + +/* +* REG_TX_FORCE_VCO_TUNE_1 +*/ +#define BYPASS_LOAD_DELAY (1 << 7) /* Bypass Load Delay */ +#define FORCE_VCO_TUNE_ENABLE (1 << 1) /* Force VCO Tune Enable */ +#define FORCE_VCO_TUNE (1 << 0) /* Force VCO Tune */ +#define VCO_CAL_OFFSET(x) (((x) & 0xF) << 3) /* VCO Cal Offset<3:0> */ + +/* +* REG_TX_ALCVARACT_OR +*/ +#define INIT_ALC_VALUE(x) (((x) & 0xF) << 4) /* Init ALC Value<3:0> */ +#define VCO_VARACTOR(x) (((x) & 0xF) << 0) /* VCO Varactor<3:0> */ + +/* +* REG_TX_VCO_OUTPUT +*/ +#define PORB_VCO_LOGIC (1 << 6) /* PORb VCO Logic */ +#define VCO_OUTPUT_LEVEL(x) (((x) & 0xF) << 0) /* VCO Output Level<3:0> */ + +/* +* REG_TX_CP_CURRENT +*/ +#define TX_CP_CURRENT_DFLT (1 << 7) /* Set to 1 */ +#define VTUNE_FORCE (1 << 6) /* Vtune Force */ +#define CHARGE_PUMP_CURRENT(x) (((x) & 0x3F) << 0) /* Charge Pump Current<5:0> */ + +/* +* REG_TX_CP_OFFSET +*/ +#define SYNTH_RECAL (1 << 7) /* Synth Re-Cal */ +#define CHARGE_PUMP_OFFSET(x) (((x) & 0x3F) << 0) /* Charge Pump Offset<5:0> */ + +/* +* REG_TX_CP_CONFIG +*/ +#define HALF_VCO_CAL_CLK (1 << 7) /* Half Vco Cal Clk */ +#define DITHER_MODE (1 << 6) /* Dither Mode */ +#define CP_OFFSET_OFF (1 << 4) /* Cp Offset Off */ +#define F_CPCAL (1 << 3) /* F Cpcal */ +#define CP_CAL_ENABLE (1 << 2) /* Cp Cal Enable */ +#define CP_TEST(x) (((x) & 0x3) << 0) /* Cp Test <1:0> */ + +/* +* REG_TX_LOOP_FILTER_1 +*/ +#define LOOP_FILTER_C2(x) (((x) & 0xF) << 4) /* Loop Filter C2<3:0> */ +#define LOOP_FILTER_C1(x) (((x) & 0xF) << 0) /* Loop Filter C1<3:0> */ + +/* +* REG_TX_LOOP_FILTER_2 +*/ +#define LOOP_FILTER_R1(x) (((x) & 0xF) << 4) /* Loop Filter R1<3:0> */ +#define LOOP_FILTER_C3(x) (((x) & 0xF) << 0) /* Loop Filter C3<3:0> */ + +/* +* REG_TX_LOOP_FILTER_3 +*/ +#define LOOP_FILTER_BYPASS_R3 (1 << 7) /* Loop Filter Bypass R3 */ +#define LOOP_FILTER_BYPASS_R1 (1 << 6) /* Loop Filter Bypass R1 */ +#define LOOP_FILTER_BYPASS_C2 (1 << 5) /* Loop Filter Bypass C2 */ +#define LOOP_FILTER_BYPASS_C1 (1 << 4) /* Loop Filter Bypass C1 */ +#define LOOP_FILTER_R3(x) (((x) & 0xF) << 0) /* Loop Filter R3<3:0> */ + +/* +* REG_TX_DITHERCP_CAL +*/ +#define NUMBER_SDM_DITHER_BITS(x) (((x) & 0xF) << 4) /* Number SDM Dither Bits<3:0> */ +#define FORCED_CP_CAL_WORD(x) (((x) & 0xF) << 0) /* Forced CP Cal Word<3:0> */ + +/* +* REG_TX_VCO_BIAS_1 +*/ +#define MUST_BE_ZEROS(x) (((x) & 0x3) << 5) /* Must be zeros */ +#define VCO_BIAS_TCF(x) (((x) & 0x3) << 3) /* VCO Bias Tcf<1:0> */ +#define VCO_BIAS_REF(x) (((x) & 0x7) << 0) /* VCO Bias Ref<2:0> */ + +/* +* REG_TX_VCO_BIAS_2 +*/ +#define VCO_BYPASS_BIAS_DAC_R (1 << 7) /* VCO Bypass Bias DAC R */ +#define VCO_COMP_BYPASS_BIAS_R (1 << 4) /* VCO Comp Bypass Bias R */ +#define BYPASS_PRESCALE_R (1 << 3) /* Bypass Prescale R */ +#define LAST_ALC_ENABLE (1 << 2) /* Last ALC Enable */ +#define PRESCALE_BIAS(x) (((x) & 0x3) << 0) /* Prescale Bias <1:0> */ + +/* +* REG_TX_CAL_STATUS +*/ +#define CP_CAL_VALID (1 << 7) /* CP Cal Valid */ +#define COMP_OUT (1 << 6) /* Comp Out */ +#define CP_CAL_DONE (1 << 5) /* CP Cal Done */ +#define VCO_CAL_BUSY (1 << 4) /* VCO Cal Busy */ +#define CP_CAL_WORD(x) (((x) & 0xF) << 0) /* CP Cal Word<3:0> */ + +/* +* REG_TX_VCO_CAL_REF +*/ +#define VCO_CAL_REF_MONITOR (1 << 3) /* VCO Cal Ref Monitor */ +#define VCO_CAL_REF_TCF(x) (((x) & 0x7) << 0) /* VCO Cal Ref Tcf<2:0> */ + +/* +* REG_TX_VCO_PD_OVERRIDES +*/ +#define POWER_DOWN_VARACTOR_REF (1 << 3) /* Power Down Varactor Ref */ +#define POWER_DOWN_VARACT_REF_TCF (1 << 2) /* Power Down Varact Ref Tcf */ +#define POWER_DOWN_CAL_TCF (1 << 1) /* Power Down Cal Tcf */ +#define POWER_DOWN_VCO_BUFFFER (1 << 0) /* Power Down VCO Bufffer */ + +/* +* REG_TX_CP_OVERRANGE_VCO_LOCK +*/ +#define CP_OVRG_HIGH (1 << 7) /* CP Ovrg High */ +#define CP_OVRG_LOW (1 << 6) /* CP Ovrg Low */ +#define VCO_LOCK (1 << 1) /* Lock */ + +/* +* REG_TX_VCO_LDO +*/ +#define VCO_LDO_BYPASS (1 << 7) /* VCO LDO Bypass */ +#define VCO_LDO_INRUSH(x) (((x) & 0x3) << 5) /* VCO LDO Inrush<1:0> */ +#define VCO_LDO_VOUT_SEL(x) (((x) & 0x7) << 2) /* VCO LDO Vout Sel<2:0> */ +#define VCO_LDO_VDROP_SEL(x) (((x) & 0x3) << 0) /* VCO LDO Vdrop Sel<1:0> */ + +/* +* REG_TX_VCO_CAL +*/ +#define VCO_CAL_EN (1 << 7) /* VCO Cal En */ +#define VCO_CAL_ALC_WAIT(x) (((x) & 0x7) << 4) /* VCO Cal ALC Wait<2:0) */ +#define VCO_CAL_COUNT(x) (((x) & 0x3) << 2) /* VCO Cal Count<1:0> */ +#define FB_CLOCK_ADV(x) (((x) & 0x3) << 0) /* FB Clock Adv<1:0> */ + +/* +* REG_TX_LOCK_DETECT_CONFIG +*/ +#define LOCK_DETECT_COUNT(x) (((x) & 0x3) << 2) /* Lock Detect Count<1:0> */ +#define LOCK_DETECT_MODE(x) (((x) & 0x3) << 0) /* Lock Detect Mode<1:0> */ + +/* +* REG_TX_CP_LEVEL_DETECT +*/ +#define CP_LEVEL_DETECT_POWER_DOWN (1 << 6) /* CP Level Detect Power Down */ +#define CP_LEVEL_DETECT_THRESH_LOW(x) (((x) & 0x7) << 3) /* CP Level Detect Threshold Low<2:0> */ +#define CP_LEVEL_DETECT_THRESH_HIGH(x) (((x) & 0x7) << 0) /* CP Level Detect Threshold High<2:0> */ + +/* +* REG_TX_DSM_SETUP_0 +*/ +#define DSM_PROG(x) (((x) & 0xF) << 0) /* DSM Prog<3:0> */ + +/* +* REG_TX_DSM_SETUP_1 +*/ +#define SIF_CLOCK (1 << 6) /* SIF clock */ +#define SIF_RESET_BAR (1 << 5) /* SIF Reset Bar */ +#define SIF_ADDR(x) (((x) & 0x1F) << 0) /* SIF Addr<4:0> */ + +/* +* REG_TX_CORRECTION_WORD0 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define READ_EFFECTIVE_TUNING_WORD (1 << 5) /* Read Effective Tuning Word */ +#define FREQ_CORRECTION_WORD_MSB(x) (((x) & 0x1F) << 0) /* Frequency Correction Word<11:7> */ + +/* +* REG_TX_CORRECTION_WORD1 +*/ +#define UPDATE_FREQ_WORD (1 << 7) /* Update Freq Word */ +#define FREQ_CORRECTION_WORD_LSB(x) (((x) & 0x7F) << 0) /* Frequency Correction Word<6:0> */ + +/* +* REG_TX_VCO_VARACTOR_CTRL_0 +*/ +#define VCO_VARACTOR_REFERENCE_TCF(x) (((x) & 0x7) << 4) /* VCO Varactor Reference Tcf<2:0> */ +#define VCO_VARACTOR_OFFSET(x) (((x) & 0xF) << 0) /* VCO Varactor Offset<3:0> */ + +/* +* REG_TX_VCO_VARACTOR_CTRL_1 +*/ +#define VCO_VARACTOR_REFERENCE(x) (((x) & 0xF) << 0) /* VCO Varactor Reference<3:0> */ + +/* +* REG_DCXO_COARSE_TUNE +*/ +#define DCXO_TUNE_COARSE(x) (((x) & 0x3F) << 0) /* DCXO Tune Coarse<5:0> */ + +/* +* REG_DCXO_FINE_TUNE_LOW +*/ +#define DCXO_TUNE_FINE_LOW(x) (((x) & 0x1F) << 3) /* DCXO Tune Fine<4:0> */ + +/* +* REG_DCXO_FINE_TUNE_HIGH +*/ +#define DCXO_TUNE_FINE_HIGH(x) ((x) >> 5) /* DCXO Tune Fine<12:5> */ + +/* +* REG_DCXO_CONFIG +*/ +#define MUST_BE_ZERO (1 << 7) /* Must be zero */ +#define DCXO_RTAIL(x) (((x) & 0x7) << 4) /* DCXO Rtail<2:0> */ +#define DCXO_RD(x) (((x) & 0x3) << 2) /* DCXO Rd<1:0> */ + +/* +* REG_DCXO_TEMPCO_ADDR +*/ +#define DCXO_TEMPCO_EN (1 << 7) /* DCXO Tempco En */ +#define DCXO_TEMPCO_CLK (1 << 6) /* DCXO Tempco Clk */ +#define DCXO_TEMPERATURE_COEF_ADDRESS(x) (((x) & 0x3F) << 0) /* DCXO Temperature Coefficient Address<5:0> */ + +/* +* REG_TX_FAST_LOCK_SETUP +*/ +#define TX_FAST_LOCK_LOAD_SYNTH (1 << 3) /* Tx Fast Lock Load Synth */ +#define TX_FAST_LOCK_PROFILE_INIT (1 << 2) /* Tx Fast Lock Profile Init */ +#define TX_FAST_LOCK_PROFILE_PIN_SELECT (1 << 1) /* Tx Fast Lock Profile Pin Select */ +#define TX_FAST_LOCK_MODE_ENABLE (1 << 0) /* Tx Fast Lock Mode Enable */ +#define TX_FAST_LOCK_PROFILE(x) (((x) & 0x7) << 5) /* Tx Fast Lock Profile<2:0> */ + +/* +* REG_TX_FAST_LOCK_PROGRAM_CTRL +*/ +#define TX_FAST_LOCK_PROGRAM_WRITE (1 << 1) /* Tx Fast Lock Program Write */ +#define TX_FAST_LOCK_PROGRAM_CLOCK_ENABLE (1 << 0) /* Tx Fast Lock Program Clock Enable */ + +/* +* REG_TX_LO_GEN_POWER_MODE +*/ +#define TX_LO_GEN_POWER_MODE(x) (((x) & 0xF) << 4) /* Power Mode<3:0> */ + +/* +* REG_BANDGAP_CONFIG0 +*/ +#define POWER_DOWN_BANDGAP_REF (1 << 7) /* Power Down Bandgap Ref */ +#define MASTER_BIAS_FILTER_BYPASS (1 << 6) /* Master Bias Filter Bypass */ +#define MASTER_BIAS_REF_SEL (1 << 5) /* Master Bias Ref Sel */ +#define MASTER_BIAS_TRIM(x) (((x) & 0x1F) << 0) /* Master Bias Trim<4:0> */ + +/* +* REG_BANDGAP_CONFIG1 +*/ +#define VCO_LDO_FILTER_BYPASS (1 << 7) /* VCO LDO Filter Bypass */ +#define VCO_LDO_REF_SEL (1 << 6) /* VCO LDO Ref Sel */ +#define BANDGAP_REF_RESET (1 << 5) /* Bandgap Ref Reset */ +#define BANDGAP_TEMP_TRIM(x) (((x) & 0x1F) << 0) /* Bandgap Temp Trim<4:0> */ + +/* +* REG_REF_DIVIDE_CONFIG_1 +*/ +#define REF_DIVIDE_CONFIG_1_DFLT (1 << 2) /* Set to 1 */ +#define RX_REF_RESET_BAR (1 << 1) /* Rx Ref Reset Bar */ +#define RX_REF_DIVIDER_MSB (1 << 0) /* Rx Ref Divider<1> */ + +/* +* REG_REF_DIVIDE_CONFIG_2 +*/ +#define RX_REF_DIVIDER_LSB (1 << 7) /* Rx Ref Divider< 0> */ +#define TX_REF_RESET_BAR (1 << 4) /* Tx Ref Reset Bar */ +#define RX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 5) /* Rx Ref Doubler FB Delay<1:0> */ +#define TX_REF_DIVIDER(x) (((x) & 0x3) << 2) /* Tx Ref Divider<1:0> */ +#define TX_REF_DOUBLER_FB_DELAY(x) (((x) & 0x3) << 0) /* Tx Ref Doubler FB Delay<1:0> */ + +/* +* REG_GAIN_RX1,2 +*/ +#define FULL_TABLE_GAIN_INDEX(x) (((x) & 0x7F) << 0) /* Full Table Gain Index Rx1/LMT Gain Rx1<6:0> */ + +/* +* REG_LPF_GAIN_RX1,2 +*/ +#define LPF_GAIN_RX(x) (((x) & 0x1F) << 0) /* LPF gain Rx1<4:0> */ + +/* +* REG_DIG_GAIN_RX1,2 +*/ +#define DIGITAL_GAIN_RX(x) (((x) & 0x1F) << 0) /* Digital gain Rx1<4:0> */ + +/* +* REG_FAST_ATTACK_STATE +*/ +#define FAST_ATTACK_STATE_RX2(x) (((x) & 0x7) << 4) /* Fast Attack State Rx2<2:0> */ +#define FAST_ATTACK_STATE_RX1(x) (((x) & 0x7) << 0) /* Fast Attack State Rx1<2:0> */ +#define FAST_ATK_MASK 0x7 +#define RX1_FAST_ATK_SHIFT 0 +#define RX2_FAST_ATK_SHIFT 4 +#define FAST_ATK_RESET 0 +#define FAST_ATK_PEAK_DETECT 1 +#define FAST_ATK_PWR_MEASURE 2 +#define FAST_ATK_FINAL_SETTELING 3 +#define FAST_ATK_FINAL_OVER 4 +#define FAST_ATK_GAIN_LOCKED 5 + +/* +* REG_SLOW_LOOP_STATE +*/ +#define SLOW_LOOP_STATE_RX2(x) (((x) & 0x7) << 4) /* Slow Loop State Rx2<2:0> */ +#define SLOW_LOOP_STATE_RX1(x) (((x) & 0x7) << 0) /* Slow Loop State Rx1<2:0> */ + + +/* +* REG_OVRG_SIGS_RX1,2 +*/ +#define GAIN_LOCK_1 (1 << 6) /* Gain Lock 1 */ +#define LOW_POWER_1 (1 << 5) /* Low Power 1 */ +#define LARGE_LMT_OL (1 << 4) /* Large LMT OL */ +#define SMALL_LMT_OL (1 << 3) /* Small LMT OL */ +#define LARGE_ADC_OL (1 << 2) /* Large ADC OL */ +#define SMALL_ADC_OL (1 << 1) /* Small ADC OL */ +#define DIG_SAT (1 << 0) /* Dig Sat */ +/* +* REG_CTRL +*/ +#define CTRL_ENABLE (1 << 0) /* Set to 1 */ + +/* +* REG_BIST_CONFIG +*/ +#define TONE_PRBS (1 << 1) /* Tone/ PRBS */ +#define BIST_ENABLE (1 << 0) /* BIST Enable */ +#define TONE_FREQ(x) (((x) & 0x3) << 6) /* Tone Frequency<1:0> */ +#define TONE_LEVEL(x) (((x) & 0x3) << 4) /* Tone Level<1:0> */ +#define BIST_CTRL_POINT(x) (((x) & 0x3) << 2) /* BIST Control Point <1:0> */ + +/* +* REG_OBSERVE_CONFIG +*/ +#define DATA_PORT_SP_HD_LOOP_TEST_OE (1 << 7) /* Data Port SP, HD Loop Test OE */ +#define RX_MASK (1 << 6) /* Rx Mask */ +#define CHANNEL (1 << 5) /* Channel */ +#define DATA_PORT_LOOP_TEST_ENABLE (1 << 0) /* Data Port Loop Test Enable */ +#define OBSERVATION_POINT(x) (((x) & 0xF) << 1) /* Observation Point<2:0> */ + +/* +* REG_BIST_AND_DATA_PORT_TEST_CONFIG +*/ +#define BIST_MASK_CHANNEL_2_Q_DATA (1 << 5) /* BIST Mask Channel 2 Q data */ +#define BIST_MASK_CHANNEL_2_I_DATA (1 << 4) /* BIST Mask Channel 2 I data */ +#define BIST_MASK_CHANNEL_1_Q_DATA (1 << 3) /* BIST Mask Channel 1 Q data */ +#define BIST_MASK_CHANNEL_1_I_DATA (1 << 2) /* BIST Mask Channel 1 I data */ +#define DATA_PORT_HILOW (1 << 1) /* Data Port Hi/Low */ +#define USE_DATA_PORT (1 << 0) /* Use Data Port */ +#define TEMP_SENSE_VBE_TEST(x) (((x) & 0x3) << 6) /* Temp Sense Vbe Test<1:0> */ + +/* +* REG_DAC_TEST_2 +*/ +#define DAC_TEST_ENABLE (1 << 7) /* DAC Test Enable */ +#define DAC_TEST_WORD(x) (((x) & 0x7F) << 0) /* DAC test Word <22:16> */ + +/* +* SPI Comm Helpers +*/ +#define AD_READ (0 << 15) +#define AD_WRITE (1 << 15) +#define AD_CNT(x) ((((x) - 1) & 0x7) << 12) +#define AD_ADDR(x) ((x) & 0x3FF) + + +/* +* AD9361 Limits +*/ + +#define RSSI_MULTIPLIER 100 +#define RSSI_RESOLUTION ((int) (0.25 * RSSI_MULTIPLIER)) +#define RSSI_MAX_WEIGHT 255 + +#define MAX_LMT_INDEX 40 +#define MAX_LPF_GAIN 24 +#define MAX_DIG_GAIN 31 + +#define MAX_BBPLL_FREF 70000000UL /* 70 MHz */ +#define MIN_BBPLL_FREQ 715000000UL /* 715 MHz */ +#define MAX_BBPLL_FREQ 1430000000UL /* 1430 MHz */ +#define MAX_BBPLL_DIV 64 +#define MIN_BBPLL_DIV 2 + +/* + * The ADC minimum and maximum operating output data rates + * are 25MHz and 640MHz respectively. + * For more information see here: https://ez.analog.com/docs/DOC-12763 + */ + +#define MIN_ADC_CLK 25000000UL /* 25 MHz */ +//#define MIN_ADC_CLK (MIN_BBPLL_FREQ / MAX_BBPLL_DIV) /* 11.17MHz */ +#define MAX_ADC_CLK 640000000UL /* 640 MHz */ +#define MAX_DAC_CLK (MAX_ADC_CLK / 2) + +#define MAX_MBYTE_SPI 8 + +#define RFPLL_MODULUS 8388593UL +#define BBPLL_MODULUS 2088960UL + +#define MAX_SYNTH_FREF 80000000UL /* 80 MHz */ +#define MIN_SYNTH_FREF 10000000UL /* 10 MHz */ +#define MIN_VCO_FREQ_HZ 6000000000ULL +#define MAX_CARRIER_FREQ_HZ 6000000000ULL +#define MIN_CARRIER_FREQ_HZ 70000000ULL + +#define AD9363A_MAX_CARRIER_FREQ_HZ 3800000000ULL +#define AD9363A_MIN_CARRIER_FREQ_HZ 325000000ULL + +/* +* Driver +*/ + +enum rx_gain_table_type { + RXGAIN_FULL_TBL, + RXGAIN_SPLIT_TBL, +}; + +enum rx_gain_table_name { + TBL_200_1300_MHZ, + TBL_1300_4000_MHZ, + TBL_4000_6000_MHZ, + RXGAIN_TBLS_END, +}; + +enum fir_dest { + FIR_TX1 = 0x01, + FIR_TX2 = 0x02, + FIR_TX1_TX2 = 0x03, + FIR_RX1 = 0x81, + FIR_RX2 = 0x82, + FIR_RX1_RX2 = 0x83, + FIR_IS_RX = 0x80, +}; + +struct rf_gain_ctrl { + uint32_t ant; + uint8_t mode; +}; + +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, +}; + +struct gain_control { + enum rf_gain_ctrl_mode rx1_mode; + enum rf_gain_ctrl_mode rx2_mode; + + /* Common */ + uint8_t adc_ovr_sample_size; /* 1..8 Sum x samples, AGC_CONFIG_3 */ + uint8_t adc_small_overload_thresh; /* 0..255, 0x105 */ + uint8_t adc_large_overload_thresh; /* 0..255, 0x104 */ + + uint16_t lmt_overload_high_thresh; /* 16..800 mV, 0x107 */ + uint16_t lmt_overload_low_thresh; /* 16..800 mV, 0x108 */ + uint16_t dec_pow_measuremnt_duration; /* Samples, 0x15C */ + uint8_t low_power_thresh; /* -64..0 dBFS, 0x114 */ + + bool dig_gain_en; /* should be turned off, since ADI GT doesn't use dig gain */ + uint8_t max_dig_gain; /* 0..31 */ + + /* MGC */ + bool mgc_rx1_ctrl_inp_en; /* Enables Pin control on RX1 default SPI ctrl */ + bool mgc_rx2_ctrl_inp_en; /* Enables Pin control on RX2 default SPI ctrl */ + + uint8_t mgc_inc_gain_step; /* 1..8 */ + uint8_t mgc_dec_gain_step; /* 1..8 */ + uint8_t mgc_split_table_ctrl_inp_gain_mode; /* 0=AGC determine this, 1=only in LPF, 2=only in LMT */ + + /* AGC */ + uint8_t agc_attack_delay_extra_margin_us; /* 0..31 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; /* 0..15, 0x122 */ + uint8_t adc_large_overload_exceed_counter; /* 0..15, 0x122 */ + uint8_t adc_large_overload_inc_steps; /* 0..15, 0x106 */ + + bool adc_lmt_small_overload_prevent_gain_inc; /* 0x120 */ + + uint8_t lmt_overload_large_exceed_counter; /* 0..15, 0x121 */ + uint8_t lmt_overload_small_exceed_counter; /* 0..15, 0x121 */ + uint8_t lmt_overload_large_inc_steps; /* 0..7, 0x121 */ + + uint8_t dig_saturation_exceed_counter; /* 0..15, 0x128 */ + uint8_t dig_gain_step_size; /* 1..8, 0x100 */ + bool sync_for_gain_counter_en; /* 0x128:4 !Hybrid */ + + uint32_t gain_update_interval_us; /* in us */ + bool immed_gain_change_if_large_adc_overload; /* 0x123:3 */ + bool immed_gain_change_if_large_lmt_overload; /* 0x123:7 */ + + /* + * Fast AGC + */ + uint32_t f_agc_dec_pow_measuremnt_duration; /* Samples, 0x15C */ + uint32_t f_agc_state_wait_time_ns; /* 0x117 0..31 RX samples -> time_ns */ + /* Fast AGC - Low Power */ + bool f_agc_allow_agc_gain_increase; /* 0x110:1 */ + uint8_t f_agc_lp_thresh_increment_time; /* 0x11B RX samples */ + uint8_t f_agc_lp_thresh_increment_steps; /* 0x117 1..8 */ + + /* Fast AGC - Lock Level */ + uint8_t f_agc_lock_level; /* 0x101 0..-127 dBFS */ + bool f_agc_lock_level_lmt_gain_increase_en; /* 0x111:6 */ + uint8_t f_agc_lock_level_gain_increase_upper_limit; /* 0x118 0..63 */ + /* Fast AGC - Peak Detectors and Final Settling */ + uint8_t f_agc_lpf_final_settling_steps; /* 0x112:6 0..3 (Post Lock Level Step)*/ + uint8_t f_agc_lmt_final_settling_steps; /* 0x113:6 0..3 (Post Lock Level Step)*/ + uint8_t f_agc_final_overrange_count; /* 0x116:5 0..7 */ + /* Fast AGC - Final Power Test */ + bool f_agc_gain_increase_after_gain_lock_en; /* 0x110:7 */ + /* Fast AGC - Unlocking the Gain */ + /* 0 = MAX Gain, 1 = Set Gain, 2 = Optimized Gain */ + enum f_agc_target_gain_index_type f_agc_gain_index_type_after_exit_rx_mode; /* 0x110:[4,2] */ + bool f_agc_use_last_lock_level_for_set_gain_en; /* 0x111:7 */ + uint8_t f_agc_optimized_gain_offset; /*0x116 0..15 steps */ + bool f_agc_rst_gla_stronger_sig_thresh_exceeded_en; /* 0x110:~6 */ + uint8_t f_agc_rst_gla_stronger_sig_thresh_above_ll; /*0x113 0..63 dbFS */ + bool f_agc_rst_gla_engergy_lost_sig_thresh_exceeded_en; /* 0x110:6 */ + bool f_agc_rst_gla_engergy_lost_goto_optim_gain_en; /* 0x110:6 */ + uint8_t f_agc_rst_gla_engergy_lost_sig_thresh_below_ll; /* 0x112:6 */ + uint8_t f_agc_energy_lost_stronger_sig_gain_lock_exit_cnt; /* 0x119 0..63 RX samples */ + bool f_agc_rst_gla_large_adc_overload_en; /*0x110:~1 and 0x114:~7 */ + bool f_agc_rst_gla_large_lmt_overload_en; /*0x110:~1 */ + bool f_agc_rst_gla_en_agc_pulled_high_en; + /* 0 = Max Gain, 1 = Set Gain, 2 = Optimized Gain, 3 = No Gain Change */ + + enum f_agc_target_gain_index_type f_agc_rst_gla_if_en_agc_pulled_high_mode; /* 0x0FB, 0x111 */ + uint8_t f_agc_power_measurement_duration_in_state5; /* 0x109, 0x10a RX samples 0..524288*/ + +}; + +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; +}; + +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; /* default unit is time */ + uint32_t rssi_delay; + uint32_t rssi_wait; + uint32_t rssi_duration; +}; + +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; +}; + +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]; +}; + +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; /* GPO0 */ + bool elna_2_control_en; /* GPO1 */ + 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 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; +}; + +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, + DAC_FREQ, + T2_FREQ, + T1_FREQ, + CLKTF_FREQ, + TX_SAMPL_FREQ, + NUM_TX_CLOCKS, +}; + +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, + ADC_CLK_DIV_32, + ADC_CLK_DIV_64, +}; + +enum synth_pd_ctrl { + LO_DONTCARE, + LO_OFF, + LO_ON, +}; + +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; + /* MCS SYNC */ + int32_t gpio_sync; + int32_t gpio_cal_sw1; + int32_t gpio_cal_sw2; +}; + +struct rf_rx_gain { + uint32_t ant; /* Antenna number to read gain */ + int32_t gain_db; /* gain value in dB */ + uint32_t fgt_lmt_index; /* Full Gain Table / LNA-MIXER-TIA gain index */ + uint32_t lmt_gain; /* LNA-MIXER-TIA gain in dB (Split GT mode only)*/ + uint32_t lpf_gain; /* Low pass filter gain in dB / index (Split GT mode only)*/ + uint32_t digital_gain; /* Digital gain in dB / index */ + /* Debug only */ + uint32_t lna_index; /* LNA Index (Split GT mode only) */ + uint32_t tia_index; /* TIA Index (Split GT mode only) */ + uint32_t mixer_index; /* MIXER Index (Split GT mode only) */ + +}; +struct rf_rssi { + uint32_t ant; /* Antenna number for which RSSI is reported */ + uint32_t symbol; /* Runtime RSSI */ + uint32_t preamble; /* Initial RSSI */ + int32_t multiplier; /* Multiplier to convert reported RSSI */ + uint8_t duration; /* Duration to be considered for measuring */ +}; + +struct SynthLUT { + uint16_t VCO_MHz; + uint8_t VCO_Output_Level; + uint8_t VCO_Varactor; + uint8_t VCO_Bias_Ref; + uint8_t VCO_Bias_Tcf; + uint8_t VCO_Cal_Offset; + uint8_t VCO_Varactor_Reference; + uint8_t Charge_Pump_Current; + uint8_t LF_C2; + uint8_t LF_C1; + uint8_t LF_R1; + uint8_t LF_C3; + uint8_t LF_R3; +}; + +enum { + LUT_FTDD_40, + LUT_FTDD_60, + LUT_FTDD_80, + LUT_FTDD_ENT, +}; + +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, +}; + +struct ad9361_debugfs_entry { + struct ad9361_rf_phy *phy; + const char *propname; + void *out_value; + uint32_t val; + uint8_t size; + uint8_t cmd; +}; + +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 dig_tune_flags { + BE_VERBOSE = 1, + BE_MOREVERBOSE = 2, + DO_IDELAY = 4, + DO_ODELAY = 8, + SKIP_STORE_RESULT = 16, + RESTORE_DEFAULT = 32, +}; + +enum ad9361_bist_mode { + BIST_DISABLE, + BIST_INJ_TX, + BIST_INJ_RX, +}; + +enum dev_id { + ID_AD9361, + ID_AD9364, + ID_AD9363A +}; + +struct refclk_scale; +struct ad9361_rf_phy { + enum dev_id dev_sel; + uint8_t id_no; + struct spi_device *spi; + 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; + uint8_t cached_synth_pd[2]; + 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 refclk_scale { + struct spi_device *spi; + struct ad9361_rf_phy *phy; + uint32_t mult; + uint32_t div; + enum ad9361_clocks source; + enum ad9361_clocks parent_source; +}; + +enum debugfs_cmd { + DBGFS_NONE, + DBGFS_INIT, + DBGFS_LOOPBACK, + DBGFS_BIST_PRBS, + DBGFS_BIST_TONE, + DBGFS_BIST_DT_ANALYSIS, + DBGFS_RXGAIN_1, + DBGFS_RXGAIN_2, +}; + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +int32_t ad9361_spi_readm(struct spi_device *spi, uint32_t reg,uint8_t *rbuf, uint32_t num); +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); +int32_t ad9361_reset(struct ad9361_rf_phy *phy); +int32_t register_clocks(struct ad9361_rf_phy *phy); +int32_t ad9361_init_gain_tables(struct ad9361_rf_phy *phy); +int32_t ad9361_setup(struct ad9361_rf_phy *phy); +int32_t ad9361_post_setup(struct ad9361_rf_phy *phy); +int32_t ad9361_set_ensm_mode(struct ad9361_rf_phy *phy, bool fdd, bool pinctrl); +int32_t ad9361_ensm_set_state(struct ad9361_rf_phy *phy, uint8_t ensm_state,bool pinctrl); +int32_t ad9361_set_rx_gain(struct ad9361_rf_phy *phy,uint32_t rx_id, struct rf_rx_gain *rx_gain); +int32_t ad9361_get_rx_gain(struct ad9361_rf_phy *phy,uint32_t rx_id, struct rf_rx_gain *rx_gain); +int32_t ad9361_update_rf_bandwidth(struct ad9361_rf_phy *phy,uint32_t rf_rx_bw, uint32_t rf_tx_bw); +int32_t ad9361_calculate_rf_clock_chain(struct ad9361_rf_phy *phy, + uint32_t tx_sample_rate, + uint32_t rate_gov, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +int32_t ad9361_set_trx_clock_chain(struct ad9361_rf_phy *phy, + uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +int32_t ad9361_get_trx_clock_chain(struct ad9361_rf_phy *phy, uint32_t *rx_path_clks, + uint32_t *tx_path_clks); +uint32_t ad9361_to_clk(uint64_t freq); +uint64_t ad9361_from_clk(uint32_t freq); +int32_t ad9361_read_rssi(struct ad9361_rf_phy *phy, struct rf_rssi *rssi); +int32_t ad9361_set_gain_ctrl_mode(struct ad9361_rf_phy *phy, + struct rf_gain_ctrl *gain_ctrl); +int32_t ad9361_load_fir_filter_coef(struct ad9361_rf_phy *phy, + enum fir_dest dest, int32_t gain_dB, + uint32_t ntaps, short *coef); +int32_t ad9361_validate_enable_fir(struct ad9361_rf_phy *phy); +int32_t ad9361_set_tx_atten(struct ad9361_rf_phy *phy, uint32_t atten_mdb, + bool tx1, bool tx2, bool immed); +int32_t ad9361_get_tx_atten(struct ad9361_rf_phy *phy, uint32_t tx_num); +uint32_t ad9361_clk_factor_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_clk_factor_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate); +int32_t ad9361_clk_factor_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_bbpll_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_bbpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate); +int32_t ad9361_bbpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_rfpll_int_recalc_rate(struct refclk_scale *clk_priv, + uint32_t parent_rate); +int32_t ad9361_rfpll_int_round_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t *prate); +int32_t ad9361_rfpll_int_set_rate(struct refclk_scale *clk_priv, uint32_t rate, + uint32_t parent_rate); +uint32_t ad9361_rfpll_dummy_recalc_rate(struct refclk_scale *clk_priv); +int32_t ad9361_rfpll_dummy_set_rate(struct refclk_scale *clk_priv, uint32_t rate); +uint32_t ad9361_rfpll_recalc_rate(struct refclk_scale *clk_priv); +int32_t ad9361_rfpll_round_rate(struct refclk_scale *clk_priv, uint32_t rate); +int32_t ad9361_rfpll_set_rate(struct refclk_scale *clk_priv, uint32_t rate); +int32_t ad9361_clk_mux_set_parent(struct refclk_scale *clk_priv, uint8_t index); +int32_t ad9361_tracking_control(struct ad9361_rf_phy *phy, bool bbdc_track, + bool rfdc_track, bool rxquad_track); +int32_t ad9361_bist_loopback(struct ad9361_rf_phy *phy, int32_t mode); +void ad9361_get_bist_loopback(struct ad9361_rf_phy *phy, int32_t *mode); +int32_t ad9361_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode mode); +void ad9361_get_bist_prbs(struct ad9361_rf_phy *phy, enum ad9361_bist_mode *mode); +int32_t ad9361_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode mode, uint32_t freq_Hz, + uint32_t level_dB, uint32_t mask); +void ad9361_get_bist_tone(struct ad9361_rf_phy *phy, + enum ad9361_bist_mode *mode, uint32_t *freq_Hz, + uint32_t *level_dB, uint32_t *mask); +int32_t ad9361_rf_port_setup(struct ad9361_rf_phy *phy, bool is_out, + uint32_t rx_inputs, uint32_t txb); +int32_t ad9361_mcs(struct ad9361_rf_phy *phy, int32_t step); +int32_t ad9361_do_calib_run(struct ad9361_rf_phy *phy, uint32_t cal, int32_t arg); +int32_t ad9361_fastlock_store(struct ad9361_rf_phy *phy, bool tx, uint32_t profile); +int32_t ad9361_fastlock_recall(struct ad9361_rf_phy *phy, bool tx, uint32_t profile); +int32_t ad9361_fastlock_load(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values); +int32_t ad9361_fastlock_save(struct ad9361_rf_phy *phy, bool tx, + uint32_t profile, uint8_t *values); +void ad9361_ensm_force_state(struct ad9361_rf_phy *phy, uint8_t ensm_state); +void ad9361_ensm_restore_prev_state(struct ad9361_rf_phy *phy); +int32_t ad9361_set_trx_clock_chain_freq(struct ad9361_rf_phy *phy, + uint32_t freq); +int32_t ad9361_find_opt(uint8_t *field, uint32_t size, uint32_t *ret_start); +int32_t ad9361_hdl_loopback(struct ad9361_rf_phy *phy, bool enable); +int32_t ad9361_dig_interface_timing_analysis(struct ad9361_rf_phy *phy, + char *buf, int32_t buflen); +int32_t ad9361_dig_tune(struct ad9361_rf_phy *phy, uint32_t max_freq, + enum dig_tune_flags flags); +int32_t ad9361_en_dis_tx(struct ad9361_rf_phy *phy, uint32_t tx_if, uint32_t enable); +int32_t ad9361_en_dis_rx(struct ad9361_rf_phy *phy, uint32_t rx_if, uint32_t enable); +int32_t ad9361_1rx1tx_channel_map(struct ad9361_rf_phy *phy, bool tx, int32_t channel); +int32_t ad9361_rssi_gain_step_calib(struct ad9361_rf_phy *phy); +int32_t ad9361_set_dcxo_tune(struct ad9361_rf_phy *phy, + uint32_t coarse, uint32_t fine); +int32_t ad9361_tx_mute(struct ad9361_rf_phy *phy, uint32_t state); +uint32_t ad9361_validate_rf_bw(struct ad9361_rf_phy *phy, uint32_t bw); +int32_t ad9361_synth_lo_powerdown(struct ad9361_rf_phy *phy, enum synth_pd_ctrl rx, enum synth_pd_ctrl tx); +int32_t ad9361_get_temp(struct ad9361_rf_phy *phy); +#endif diff --git a/src/ad9361_lib/adc_core.c b/src/ad9361_lib/adc_core.c new file mode 100644 index 0000000..e3b2eb3 --- /dev/null +++ b/src/ad9361_lib/adc_core.c @@ -0,0 +1,417 @@ +/***************************************************************************//** + * @file adc_core.c + * @brief Implementation of ADC Core Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include "platform.h" +#include "adc_core.h" +//#include "parameters.h" +#include "util.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +//#define FMCOMMS5 + +/******************************************************************************/ +/************************ Variables Definitions *******************************/ +/******************************************************************************/ +struct adc_state adc_st; + +/***************************************************************************//** + * @brief adc_read +*******************************************************************************/ +void adc_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data) +{ + switch (phy->id_no) + { + case 0: + *data = Xil_In32(AD9361_RX_0_BASEADDR + regAddr); + break; + case 1: + *data = Xil_In32(AD9361_RX_1_BASEADDR + regAddr); + break; + default: + break; + } +} + +/***************************************************************************//** + * @brief adc_write +*******************************************************************************/ +void adc_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data) +{ + switch (phy->id_no) + { + case 0: + Xil_Out32(AD9361_RX_0_BASEADDR + regAddr, data); + break; + case 1: + Xil_Out32(AD9361_RX_1_BASEADDR + regAddr, data); + break; + default: + break; + } +} + +/***************************************************************************//** + * @brief adc_dma_read +*******************************************************************************/ +#if 0 +void adc_dma_read(uint32_t regAddr, uint32_t *data) +{ + *data = Xil_In32(CF_AD9361_RX_DMA_BASEADDR + regAddr); +} + +/***************************************************************************//** + * @brief adc_dma_write +*******************************************************************************/ +void adc_dma_write(uint32_t regAddr, uint32_t data) +{ + Xil_Out32(CF_AD9361_RX_DMA_BASEADDR + regAddr, data); +} +#endif + +/***************************************************************************//** + * @brief adc_init +*******************************************************************************/ +void adc_init(struct ad9361_rf_phy *phy) +{ + adc_write(phy, ADC_REG_RSTN, 0); + adc_write(phy, ADC_REG_RSTN, ADC_RSTN); + + adc_write(phy, ADC_REG_CHAN_CNTRL(0), + ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE); + adc_write(phy, ADC_REG_CHAN_CNTRL(1), + ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE); + adc_st.rx2tx2 = phy->pdata->rx2tx2; + if(adc_st.rx2tx2) + { + adc_write(phy, ADC_REG_CHAN_CNTRL(2), + ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE); + adc_write(phy, ADC_REG_CHAN_CNTRL(3), + ADC_IQCOR_ENB | ADC_FORMAT_SIGNEXT | ADC_FORMAT_ENABLE | ADC_ENABLE); + } + else + { + adc_write(phy, ADC_REG_CHAN_CNTRL(2), 0); + adc_write(phy, ADC_REG_CHAN_CNTRL(3), 0); + } + printf("adc_init done! %d\r\n", adc_st.rx2tx2); +} +#if 0 +#include "xscugic.h" +#define ADC_DMAC_INTERRUPTS + +#ifdef ADC_DMAC_INTERRUPTS +uint8_t adc_sot_flag = 0; +uint8_t adc_eot_flag = 0; +uint32_t adc_dmac_start_address = 0; +/***************************************************************************//** + * @brief adc_dmac_isr +*******************************************************************************/ +void adc_dmac_isr(void *instance) +{ + uint32_t reg_val; + adc_dma_read(AXI_DMAC_REG_IRQ_PENDING, ®_val); + adc_dma_write(AXI_DMAC_REG_IRQ_PENDING, reg_val); + if(reg_val & AXI_DMAC_IRQ_SOT) + { + adc_sot_flag = 1; + adc_dmac_start_address += 32768; + adc_dma_write(AXI_DMAC_REG_DEST_ADDRESS, adc_dmac_start_address); + /* The current transfer was started and a new one is queued. */ + adc_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1); + xil_printf("AXI_DMAC_IRQ_SOT\r\n"); + } + if(reg_val & AXI_DMAC_IRQ_EOT) + { + adc_eot_flag = 1; + xil_printf("AXI_DMAC_IRQ_EOT\r\n"); + } +} +#endif +/***************************************************************************//** + * @brief adc_capture +*******************************************************************************/ +int32_t adc_capture(uint32_t size, uint32_t start_address) +{ + uint32_t reg_val; + uint32_t transfer_id; + uint32_t length; + + if(adc_st.rx2tx2) + { + length = (size * 8); + } + else + { + length = (size * 4); + } + +#ifdef FMCOMMS5 + length = (size * 16); +#endif + + adc_dma_write(AXI_DMAC_REG_CTRL, 0x0); + adc_dma_write(AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + + adc_dma_write(AXI_DMAC_REG_IRQ_MASK, 0x0); + + adc_dma_read(AXI_DMAC_REG_TRANSFER_ID, &transfer_id); + adc_dma_read(AXI_DMAC_REG_IRQ_PENDING, ®_val); + adc_dma_write(AXI_DMAC_REG_IRQ_PENDING, reg_val); +#ifndef ADC_DMAC_INTERRUPTS + adc_dma_write(AXI_DMAC_REG_DEST_ADDRESS, start_address); + adc_dma_write(AXI_DMAC_REG_DEST_STRIDE, 0x0); + adc_dma_write(AXI_DMAC_REG_X_LENGTH, length - 1); + adc_dma_write(AXI_DMAC_REG_Y_LENGTH, 0x0); + + adc_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1); + /* Wait until the new transfer is queued. */ + do { + adc_dma_read(AXI_DMAC_REG_START_TRANSFER, ®_val); + } + while(reg_val == 1); + + /* Wait until the current transfer is completed. */ + do { + adc_dma_read(AXI_DMAC_REG_IRQ_PENDING, ®_val); + } + while(reg_val != (AXI_DMAC_IRQ_SOT | AXI_DMAC_IRQ_EOT)); + adc_dma_write(AXI_DMAC_REG_IRQ_PENDING, reg_val); + + /* Wait until the transfer with the ID transfer_id is completed. */ + do { + adc_dma_read(AXI_DMAC_REG_TRANSFER_DONE, ®_val); + } + while((reg_val & (1 << transfer_id)) != (1 << transfer_id)); +#else + XScuGic_Config *gic_config; + XScuGic gic; + int32_t status; + + gic_config = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID); + if(gic_config == NULL) + xil_printf("%s %d Error\n", __FUNCTION__, __LINE__); + + status = XScuGic_CfgInitialize(&gic, gic_config, gic_config->CpuBaseAddress); + if(status) + xil_printf("%s %d Error\n", __FUNCTION__, __LINE__); +/* + XScuGic_SetPriorityTriggerType(&gic, XPAR_FABRIC_AXI_AD9361_ADC_DMA_IRQ_INTR, 0x0, 0x3); + + status = XScuGic_Connect(&gic, XPAR_FABRIC_AXI_AD9361_ADC_DMA_IRQ_INTR, (Xil_ExceptionHandler)adc_dmac_isr, NULL); + if(status) + xil_printf("%s %d Error\n", __FUNCTION__, __LINE__); + + XScuGic_Enable(&gic, XPAR_FABRIC_AXI_AD9361_ADC_DMA_IRQ_INTR); +*/ + Xil_ExceptionInit(); + XScuGic_SetPriorityTriggerType(&gic, 89, 0x0, 0x3); + Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, (void *)&gic); + status = XScuGic_Connect(&gic, 89, (Xil_ExceptionHandler)adc_dmac_isr, NULL); + if(status) + xil_printf("%s %d Error\n", __FUNCTION__, __LINE__); + + Xil_ExceptionEnable(); + + XScuGic_Enable(&gic, 89); + + adc_dmac_start_address = start_address; + + adc_dma_write(AXI_DMAC_REG_DEST_ADDRESS, adc_dmac_start_address); + adc_dma_write(AXI_DMAC_REG_DEST_STRIDE, 0x0); + adc_dma_write(AXI_DMAC_REG_X_LENGTH, length - 1); + adc_dma_write(AXI_DMAC_REG_Y_LENGTH, 0x0); + + adc_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1); + + xil_printf("AXI_DMAC_REG_START_TRANSFER\r\n"); + while(adc_dmac_start_address < (start_address + length + 32768)); + adc_dma_write(AXI_DMAC_REG_CTRL, 0x0); +#endif + xil_printf("adc_capture ok\r\n"); + return 0; +} +#endif +/***************************************************************************//** + * @brief adc_set_calib_scale_phase +*******************************************************************************/ +int32_t adc_set_calib_scale_phase(struct ad9361_rf_phy *phy, + uint32_t phase, + uint32_t chan, + int32_t val, + int32_t val2) +{ + uint32_t fract; + uint64_t llval; + uint32_t tmp; + + switch (val) { + case 1: + fract = 0x4000; + break; + case -1: + fract = 0xC000; + break; + case 0: + fract = 0; + if (val2 < 0) { + fract = 0x8000; + val2 *= -1; + } + break; + default: + return -1; + } + + llval = (uint64_t)val2 * 0x4000UL + (1000000UL / 2); + do_div(&llval, 1000000UL); + fract |= llval; + + adc_read(phy, ADC_REG_CHAN_CNTRL_2(chan), &tmp); + + if (!((chan + phase) % 2)) { + tmp &= ~ADC_IQCOR_COEFF_1(~0); + tmp |= ADC_IQCOR_COEFF_1(fract); + } else { + tmp &= ~ADC_IQCOR_COEFF_2(~0); + tmp |= ADC_IQCOR_COEFF_2(fract); + } + + adc_write(phy, ADC_REG_CHAN_CNTRL_2(chan), tmp); + + return 0; +} + +/***************************************************************************//** + * @brief adc_get_calib_scale_phase +*******************************************************************************/ +int32_t adc_get_calib_scale_phase(struct ad9361_rf_phy *phy, + uint32_t phase, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + uint32_t tmp; + int32_t sign; + uint64_t llval; + + adc_read(phy, ADC_REG_CHAN_CNTRL_2(chan), &tmp); + + /* format is 1.1.14 (sign, integer and fractional bits) */ + + if (!((phase + chan) % 2)) { + tmp = ADC_TO_IQCOR_COEFF_1(tmp); + } else { + tmp = ADC_TO_IQCOR_COEFF_2(tmp); + } + + if (tmp & 0x8000) + sign = -1; + else + sign = 1; + + if (tmp & 0x4000) + *val = 1 * sign; + else + *val = 0; + + tmp &= ~0xC000; + + llval = tmp * 1000000ULL + (0x4000 / 2); + do_div(&llval, 0x4000); + if (*val == 0) + *val2 = llval * sign; + else + *val2 = llval; + + return 0; +} + +/***************************************************************************//** + * @brief adc_set_calib_scale +*******************************************************************************/ +int32_t adc_set_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2) +{ + return adc_set_calib_scale_phase(phy, 0, chan, val, val2); +} + +/***************************************************************************//** + * @brief adc_get_calib_scale +*******************************************************************************/ +int32_t adc_get_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + return adc_get_calib_scale_phase(phy, 0, chan, val, val2); +} + +/***************************************************************************//** + * @brief adc_set_calib_phase +*******************************************************************************/ +int32_t adc_set_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2) +{ + return adc_set_calib_scale_phase(phy, 1, chan, val, val2); +} + +/***************************************************************************//** + * @brief adc_get_calib_phase +*******************************************************************************/ +int32_t adc_get_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + return adc_get_calib_scale_phase(phy, 1, chan, val, val2); +} + + + diff --git a/src/ad9361_lib/adc_core.h b/src/ad9361_lib/adc_core.h new file mode 100644 index 0000000..022c283 --- /dev/null +++ b/src/ad9361_lib/adc_core.h @@ -0,0 +1,175 @@ +/***************************************************************************//** + * @file adc_core.h + * @brief Header file of ADC Core Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef ADC_CORE_API_H_ +#define ADC_CORE_API_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9361_dev.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +/* ADC COMMON */ +#define ADC_REG_RSTN 0x0040 +#define ADC_RSTN (1 << 0) +#define ADC_MMCM_RSTN (1 << 1) + +#define ADC_REG_CNTRL 0x0044 +#define ADC_R1_MODE (1 << 2) +#define ADC_DDR_EDGESEL (1 << 1) +#define ADC_PIN_MODE (1 << 0) + +#define ADC_REG_STATUS 0x005C +#define ADC_MUX_PN_ERR (1 << 3) +#define ADC_MUX_PN_OOS (1 << 2) +#define ADC_MUX_OVER_RANGE (1 << 1) +#define ADC_STATUS (1 << 0) + +#define ADC_REG_DMA_CNTRL 0x0080 +#define ADC_DMA_STREAM (1 << 1) +#define ADC_DMA_START (1 << 0) + +#define ADC_REG_DMA_COUNT 0x0084 +#define ADC_DMA_COUNT(x) (((x) & 0xFFFFFFFF) << 0) +#define ADC_TO_DMA_COUNT(x) (((x) >> 0) & 0xFFFFFFFF) + +#define ADC_REG_DMA_STATUS 0x0088 +#define ADC_DMA_OVF (1 << 2) +#define ADC_DMA_UNF (1 << 1) +#define ADC_DMA_STATUS (1 << 0) + +#define ADC_REG_DMA_BUSWIDTH 0x008C +#define ADC_DMA_BUSWIDTH(x) (((x) & 0xFFFFFFFF) << 0) +#define ADC_TO_DMA_BUSWIDTH(x) (((x) >> 0) & 0xFFFFFFFF) + +/* ADC CHANNEL */ +#define ADC_REG_CHAN_CNTRL(c) (0x0400 + (c) * 0x40) +#define ADC_LB_EN (1 << 11) +#define ADC_PN_SEL (1 << 10) +#define ADC_IQCOR_ENB (1 << 9) +#define ADC_DCFILT_ENB (1 << 8) +#define ADC_FORMAT_SIGNEXT (1 << 6) +#define ADC_FORMAT_TYPE (1 << 5) +#define ADC_FORMAT_ENABLE (1 << 4) +#define ADC_PN23_TYPE (1 << 1) +#define ADC_ENABLE (1 << 0) + +#define ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40) +#define ADC_PN_ERR (1 << 2) +#define ADC_PN_OOS (1 << 1) +#define ADC_OVER_RANGE (1 << 0) + +#define ADC_REG_CHAN_CNTRL_1(c) (0x0410 + (c) * 0x40) +#define ADC_DCFILT_OFFSET(x) (((x) & 0xFFFF) << 16) +#define ADC_TO_DCFILT_OFFSET(x) (((x) >> 16) & 0xFFFF) +#define ADC_DCFILT_COEFF(x) (((x) & 0xFFFF) << 0) +#define ADC_TO_DCFILT_COEFF(x) (((x) >> 0) & 0xFFFF) + +#define ADC_REG_CHAN_CNTRL_2(c) (0x0414 + (c) * 0x40) +#define ADC_IQCOR_COEFF_1(x) (((x) & 0xFFFF) << 16) +#define ADC_TO_IQCOR_COEFF_1(x) (((x) >> 16) & 0xFFFF) +#define ADC_IQCOR_COEFF_2(x) (((x) & 0xFFFF) << 0) +#define ADC_TO_IQCOR_COEFF_2(x) (((x) >> 0) & 0xFFFF) + +#define ADC_REG_CHAN_CNTRL_3(c) (0x0418 + (c) * 0x40) /* v8.0 */ +#define ADC_ADC_PN_SEL(x) (((x) & 0xF) << 16) +#define ADC_TO_ADC_PN_SEL(x) (((x) >> 16) & 0xF) +#define ADC_ADC_DATA_SEL(x) (((x) & 0xF) << 0) +#define ADC_TO_ADC_DATA_SEL(x) (((x) >> 0) & 0xF) + +#define AXI_DMAC_REG_IRQ_MASK 0x80 +#define AXI_DMAC_REG_IRQ_PENDING 0x84 +#define AXI_DMAC_REG_IRQ_SOURCE 0x88 + +#define AXI_DMAC_REG_CTRL 0x400 +#define AXI_DMAC_REG_TRANSFER_ID 0x404 +#define AXI_DMAC_REG_START_TRANSFER 0x408 +#define AXI_DMAC_REG_FLAGS 0x40c +#define AXI_DMAC_REG_DEST_ADDRESS 0x410 +#define AXI_DMAC_REG_SRC_ADDRESS 0x414 +#define AXI_DMAC_REG_X_LENGTH 0x418 +#define AXI_DMAC_REG_Y_LENGTH 0x41c +#define AXI_DMAC_REG_DEST_STRIDE 0x420 +#define AXI_DMAC_REG_SRC_STRIDE 0x424 +#define AXI_DMAC_REG_TRANSFER_DONE 0x428 +#define AXI_DMAC_REG_ACTIVE_TRANSFER_ID 0x42c +#define AXI_DMAC_REG_STATUS 0x430 +#define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x434 +#define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x438 +#define AXI_DMAC_REG_DBG0 0x43c +#define AXI_DMAC_REG_DBG1 0x440 + +#define AXI_DMAC_CTRL_ENABLE (1 << 0) +#define AXI_DMAC_CTRL_PAUSE (1 << 1) + +#define AXI_DMAC_IRQ_SOT (1 << 0) +#define AXI_DMAC_IRQ_EOT (1 << 1) + +struct adc_state +{ + bool rx2tx2; +}; + +#define M_pi 3.14159265358979323846 +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +void adc_init(struct ad9361_rf_phy *phy); +//int32_t adc_capture(uint32_t size, uint32_t start_address); +void adc_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data); +void adc_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data); +int32_t adc_set_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2); +int32_t adc_get_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2); +int32_t adc_set_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2); +int32_t adc_get_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2); +#endif diff --git a/src/ad9361_lib/common.h b/src/ad9361_lib/common.h new file mode 100644 index 0000000..6e44444 --- /dev/null +++ b/src/ad9361_lib/common.h @@ -0,0 +1,117 @@ +/***************************************************************************//** + * @file common.h + * @brief Header file of Common Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef COMMON_H_ +#define COMMON_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define EIO 5 /* I/O error */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EFAULT 14 /* Bad address */ +#define ENODEV 19 /* No such device */ +#define EINVAL 22 /* Invalid argument */ +#ifndef ETIMEDOUT +#define ETIMEDOUT 110 /* Connection timed out */ +#endif +/******************************************************************************/ +/*************************** Types Declarations *******************************/ +/******************************************************************************/ +#if defined (__STDC__) && (__STDC_VERSION__ >= 199901L) +#include +#else +typedef enum { false, true } bool; +#endif + +/* copy from config.h */ +#define HAVE_VERBOSE_MESSAGES /* Recommended during development prints errors and warnings */ +//#define HAVE_DEBUG_MESSAGES /* For Debug purposes only */ + +/* + * In case memory footprint is a concern these options allow + * to disable unused functionality which may free up a few kb + */ + +#define HAVE_SPLIT_GAIN_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ +#define HAVE_TDD_SYNTH_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ + +#define AD9361_DEVICE 1 /* set it 1 if AD9361 device is used, 0 otherwise */ +#define AD9364_DEVICE 0 /* set it 1 if AD9364 device is used, 0 otherwise */ +#define AD9363A_DEVICE 0 /* set it 1 if AD9363A device is used, 0 otherwise */ + +//#define CONSOLE_COMMANDS +#define XILINX_PLATFORM +//#define ALTERA_PLATFORM +//#define FMCOMMS5 +//#define PICOZED_SDR +//#define PICOZED_SDR_CMOS +#define CAPTURE_SCRIPT +#define AXI_ADC_NOT_PRESENT +#define TDD_SWITCH_STATE_EXAMPLE + +struct clk { + const char *name; + uint32_t rate; +}; + +struct clk_hw { + struct clk *clk; +}; + +struct clk_init_data { + const char *name; + const struct clk_ops *ops; + const char **parent_names; + uint8_t num_parents; + uint32_t flags; +}; + +struct clk_onecell_data { + struct clk **clks; + uint32_t clk_num; +}; + +#endif + diff --git a/src/ad9361_lib/config.h b/src/ad9361_lib/config.h new file mode 100644 index 0000000..65faae7 --- /dev/null +++ b/src/ad9361_lib/config.h @@ -0,0 +1,103 @@ +/***************************************************************************//** + * @file config.h + * @brief Config file of AD9361/API Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2015(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef CONFIG_H_ +#define CONFIG_H_ + +#define HAVE_VERBOSE_MESSAGES /* Recommended during development prints errors and warnings */ +//#define HAVE_DEBUG_MESSAGES /* For Debug purposes only */ + +/* + * In case memory footprint is a concern these options allow + * to disable unused functionality which may free up a few kb + */ + +#define HAVE_SPLIT_GAIN_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ +#define HAVE_TDD_SYNTH_TABLE 1 /* only set to 0 in case split_gain_table_mode_enable = 0*/ + +#define AD9361_DEVICE 1 /* set it 1 if AD9361 device is used, 0 otherwise */ +#define AD9364_DEVICE 0 /* set it 1 if AD9364 device is used, 0 otherwise */ +#define AD9363A_DEVICE 0 /* set it 1 if AD9363A device is used, 0 otherwise */ + +//#define CONSOLE_COMMANDS +#define XILINX_PLATFORM +//#define ALTERA_PLATFORM +//#define FMCOMMS5 +//#define PICOZED_SDR +//#define PICOZED_SDR_CMOS +//#define CAPTURE_SCRIPT +#define AXI_ADC_NOT_PRESENT +#define TDD_SWITCH_STATE_EXAMPLE + +#define XPAR_AXI_AD9361_BASEADDR 0x10000 + +#ifdef XPAR_AXI_AD9361_0_BASEADDR +#define AD9361_RX_0_BASEADDR XPAR_AXI_AD9361_0_BASEADDR +#define AD9361_TX_0_BASEADDR XPAR_AXI_AD9361_0_BASEADDR + 0x4000 +#else +#define AD9361_RX_0_BASEADDR XPAR_AXI_AD9361_BASEADDR +#define AD9361_TX_0_BASEADDR XPAR_AXI_AD9361_BASEADDR + 0x4000 +#endif + +#ifdef XPAR_AXI_AD9361_1_BASEADDR +#define AD9361_RX_1_BASEADDR XPAR_AXI_AD9361_1_BASEADDR +#define AD9361_TX_1_BASEADDR XPAR_AXI_AD9361_1_BASEADDR + 0x4000 +#else +#ifdef XPAR_AXI_AD9361_0_BASEADDR +#define AD9361_RX_1_BASEADDR XPAR_AXI_AD9361_0_BASEADDR +#define AD9361_TX_1_BASEADDR XPAR_AXI_AD9361_0_BASEADDR + 0x4000 +#else +#define AD9361_RX_1_BASEADDR XPAR_AXI_AD9361_BASEADDR +#define AD9361_TX_1_BASEADDR XPAR_AXI_AD9361_BASEADDR + 0x4000 +#endif +#endif + +typedef struct{ + unsigned int JBP1_RESETB; + unsigned int JBP1_ENABLE; + unsigned int JBP1_EN_AGC; + unsigned int JBP1_TXNRX; + unsigned int JBP1_HMC472_ATT; + unsigned int JBP1_HMC472_ATTB; + unsigned int JBP1_RTPATH; +}APB_M0; + +extern APB_M0 *apb; + + +#endif diff --git a/src/ad9361_lib/dac_core.c b/src/ad9361_lib/dac_core.c new file mode 100644 index 0000000..5d82df5 --- /dev/null +++ b/src/ad9361_lib/dac_core.c @@ -0,0 +1,807 @@ +/***************************************************************************//** + * @file dac_core.c + * @brief Implementation of DAC Core Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include "platform.h" +#include "dac_core.h" +//#include "parameters.h" +#include "util.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +//#define FMCOMMS5 + +/******************************************************************************/ +/************************ Variables Definitions *******************************/ +/******************************************************************************/ +struct dds_state dds_st[2]; + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +const uint16_t sine_lut[128] = { + 0x000, 0x064, 0x0C8, 0x12C, 0x18F, 0x1F1, 0x252, 0x2B1, + 0x30F, 0x36B, 0x3C5, 0x41C, 0x471, 0x4C3, 0x512, 0x55F, + 0x5A7, 0x5ED, 0x62E, 0x66C, 0x6A6, 0x6DC, 0x70D, 0x73A, + 0x763, 0x787, 0x7A7, 0x7C2, 0x7D8, 0x7E9, 0x7F5, 0x7FD, + 0x7FF, 0x7FD, 0x7F5, 0x7E9, 0x7D8, 0x7C2, 0x7A7, 0x787, + 0x763, 0x73A, 0x70D, 0x6DC, 0x6A6, 0x66C, 0x62E, 0x5ED, + 0x5A7, 0x55F, 0x512, 0x4C3, 0x471, 0x41C, 0x3C5, 0x36B, + 0x30F, 0x2B1, 0x252, 0x1F1, 0x18F, 0x12C, 0xC8, 0x64, + 0x000, 0xF9B, 0xF37, 0xED3, 0xE70, 0xE0E, 0xDAD, 0xD4E, + 0xCF0, 0xC94, 0xC3A, 0xBE3, 0xB8E, 0xB3C, 0xAED, 0xAA0, + 0xA58, 0xA12, 0x9D1, 0x993, 0x959, 0x923, 0x8F2, 0x8C5, + 0x89C, 0x878, 0x858, 0x83D, 0x827, 0x816, 0x80A, 0x802, + 0x800, 0x802, 0x80A, 0x816, 0x827, 0x83D, 0x858, 0x878, + 0x89C, 0x8C5, 0x8F2, 0x923, 0x959, 0x993, 0x9D1, 0xA12, + 0xA58, 0xAA0, 0xAED, 0xB3C, 0xB8E, 0xBE3, 0xC3A, 0xC94, + 0xCF0, 0xD4E, 0xDAD, 0xE0E, 0xE70, 0xED3, 0xF37, 0xF9B +}; + +/***************************************************************************//** + * @brief dac_read +*******************************************************************************/ +void dac_read(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t *data) +{ + switch (phy->id_no) + { + case 0: + *data = Xil_In32(AD9361_TX_0_BASEADDR + regAddr); + break; + case 1: + *data = Xil_In32(AD9361_TX_1_BASEADDR + regAddr); + break; + default: + break; + } +} + +/***************************************************************************//** + * @brief dac_write +*******************************************************************************/ +void dac_write(struct ad9361_rf_phy *phy, uint32_t regAddr, uint32_t data) +{ + switch (phy->id_no) + { + case 0: + Xil_Out32(AD9361_TX_0_BASEADDR + regAddr, data); + break; + case 1: + Xil_Out32(AD9361_TX_1_BASEADDR + regAddr, data); + break; + default: + break; + } +} + +/***************************************************************************//** + * @brief dac_dma_read +*******************************************************************************/ +#if 0 +void dac_dma_read(uint32_t regAddr, uint32_t *data) +{ + *data = Xil_In32(CF_AD9361_TX_DMA_BASEADDR + regAddr); +} + +/***************************************************************************//** + * @brief dac_dma_write +*******************************************************************************/ +void dac_dma_write(uint32_t regAddr, uint32_t data) +{ + Xil_Out32(CF_AD9361_TX_DMA_BASEADDR + regAddr, data); +} +#endif +/***************************************************************************//** + * @brief dds_default_setup +*******************************************************************************/ +int32_t dds_default_setup(struct ad9361_rf_phy *phy, + uint32_t chan, uint32_t phase, + uint32_t freq, int32_t scale) +{ + dds_set_phase(phy, chan, phase); + dds_set_frequency(phy, chan, freq); + dds_set_scale(phy, chan, scale); + dds_st[phy->id_no].cached_freq[chan] = freq; + dds_st[phy->id_no].cached_phase[chan] = phase; + dds_st[phy->id_no].cached_scale[chan] = scale; + + return 0; +} + +/***************************************************************************//** + * @brief dac_stop +*******************************************************************************/ +void dac_stop(struct ad9361_rf_phy *phy) +{ + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) + { + dac_write(phy, DAC_REG_CNTRL_1, 0); + } +} + +/***************************************************************************//** + * @brief dac_start_sync +*******************************************************************************/ +void dac_start_sync(struct ad9361_rf_phy *phy, bool force_on) +{ + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) + { + dac_write(phy, DAC_REG_CNTRL_1, (dds_st[phy->id_no].enable || force_on) ? DAC_ENABLE : 0); + } + else + { + dac_write(phy, DAC_REG_CNTRL_1, DAC_SYNC); + } +} + +/***************************************************************************//** + * @brief dac_init +*******************************************************************************/ +void dac_init(struct ad9361_rf_phy *phy, uint8_t data_sel, uint8_t config_dma) +{ + /*uint32_t tx_count; + uint32_t index; + uint32_t index_i1; + uint32_t index_q1; + uint32_t index_i2; + uint32_t index_q2; + uint32_t index_mem; + uint32_t data_i1; + uint32_t data_q1; + uint32_t data_i2; + uint32_t data_q2; + uint32_t length;*/ + uint32_t reg_ctrl_2; + + dac_write(phy, DAC_REG_RSTN, 0x0); + dac_write(phy, DAC_REG_RSTN, DAC_RSTN | DAC_MMCM_RSTN); + + dds_st[phy->id_no].dac_clk = &phy->clks[TX_SAMPL_CLK]->rate; + dds_st[phy->id_no].rx2tx2 = phy->pdata->rx2tx2; + dac_read(phy, DAC_REG_CNTRL_2, ®_ctrl_2); + if(dds_st[phy->id_no].rx2tx2) + { + dds_st[phy->id_no].num_buf_channels = 4; + dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(3)); + reg_ctrl_2 &= ~DAC_R1_MODE; + } + else + { + dds_st[phy->id_no].num_buf_channels = 2; + dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(1)); + reg_ctrl_2 |= DAC_R1_MODE; + } + dac_write(phy, DAC_REG_CNTRL_2, reg_ctrl_2); + + dac_read(phy, DAC_REG_VERSION, &dds_st[phy->id_no].pcore_version); + + dac_stop(phy); + switch (data_sel) { + case DATA_SEL_DDS: + dds_default_setup(phy, DDS_CHAN_TX1_I_F1, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_I_F2, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_Q_F1, 0, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_Q_F2, 0, 1000000, 250000); + if(dds_st[phy->id_no].rx2tx2) + { + dds_default_setup(phy, DDS_CHAN_TX2_I_F1, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_I_F2, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_Q_F1, 0, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_Q_F2, 0, 1000000, 250000); + } + dac_datasel(phy, -1, DATA_SEL_DDS); + break; + case DATA_SEL_LB: + dac_datasel(phy, -1, DATA_SEL_LB); + break; + case DATA_SEL_DMA: + /* + if(config_dma) + { + tx_count = sizeof(sine_lut) / sizeof(uint16_t); + if(dds_st[phy->id_no].rx2tx2) + { +#ifdef FMCOMMS5 + for(index = 0, index_mem = 0; index < (tx_count * 2); index += 2, index_mem += 4) +#else + for(index = 0, index_mem = 0; index < (tx_count * 2); index += 2, index_mem += 2) +#endif + { + index_i1 = index; + index_q1 = index + (tx_count / 2); + if(index_q1 >= (tx_count * 2)) + index_q1 -= (tx_count * 2); + data_i1 = (sine_lut[index_i1 / 2] << 20); + data_q1 = (sine_lut[index_q1 / 2] << 4); + Xil_Out32(DAC_DDR_BASEADDR + index_mem * 4, data_i1 | data_q1); + + index_i2 = index_i1; + index_q2 = index_q1; + if(index_i2 >= (tx_count * 2)) + index_i2 -= (tx_count * 2); + if(index_q2 >= (tx_count * 2)) + index_q2 -= (tx_count * 2); + data_i2 = (sine_lut[index_i2 / 2] << 20); + data_q2 = (sine_lut[index_q2 / 2] << 4); + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 1) * 4, data_i2 | data_q2); +#ifdef FMCOMMS5 + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 2) * 4, data_i1 | data_q1); + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 3) * 4, data_i2 | data_q2); +#endif + } + } + else + { + for(index = 0; index < tx_count; index += 1) + { + index_i1 = index; + index_q1 = index + (tx_count / 4); + if(index_q1 >= tx_count) + index_q1 -= tx_count; + data_i1 = (sine_lut[index_i1] << 20); + data_q1 = (sine_lut[index_q1] << 4); + Xil_Out32(DAC_DDR_BASEADDR + index * 4, data_i1 | data_q1); + } + } + Xil_DCacheFlush(); + if(dds_st[phy->id_no].rx2tx2) + { + length = (tx_count * 8); + } + else + { + length = (tx_count * 4); + } +#ifdef FMCOMMS5 + length = (tx_count * 16); +#endif + dac_dma_write(AXI_DMAC_REG_CTRL, 0); + dac_dma_write(AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + dac_dma_write(AXI_DMAC_REG_SRC_ADDRESS, DAC_DDR_BASEADDR); + dac_dma_write(AXI_DMAC_REG_SRC_STRIDE, 0x0); + dac_dma_write(AXI_DMAC_REG_X_LENGTH, length - 1); + dac_dma_write(AXI_DMAC_REG_Y_LENGTH, 0x0); + dac_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1); + } + */ + dac_datasel(phy, -1, DATA_SEL_DMA); + break; + default: + break; + } + //dds_st[phy->id_no].enable = true; + //dac_start_sync(phy, 0); + printf("dac_init done!\r\n"); +#if 0 + uint32_t tx_count; + uint32_t index; + uint32_t index_i1; + uint32_t index_q1; + uint32_t index_i2; + uint32_t index_q2; + uint32_t index_mem; + uint32_t data_i1; + uint32_t data_q1; + uint32_t data_i2; + uint32_t data_q2; + uint32_t length; + uint32_t reg_ctrl_2; + + dac_write(phy, DAC_REG_RSTN, 0x0); + dac_write(phy, DAC_REG_RSTN, DAC_RSTN | DAC_MMCM_RSTN); + + dds_st[phy->id_no].dac_clk = &phy->clks[TX_SAMPL_CLK]->rate; + dds_st[phy->id_no].rx2tx2 = phy->pdata->rx2tx2; + dac_read(phy, DAC_REG_CNTRL_2, ®_ctrl_2); + if(dds_st[phy->id_no].rx2tx2) + { + dds_st[phy->id_no].num_buf_channels = 4; + dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(3)); + reg_ctrl_2 &= ~DAC_R1_MODE; + } + else + { + dds_st[phy->id_no].num_buf_channels = 2; + dac_write(phy, DAC_REG_RATECNTRL, DAC_RATE(1)); + reg_ctrl_2 |= DAC_R1_MODE; + } + dac_write(phy, DAC_REG_CNTRL_2, reg_ctrl_2); + + dac_read(phy, DAC_REG_VERSION, &dds_st[phy->id_no].pcore_version); + + dac_stop(phy); + switch (data_sel) { + case DATA_SEL_DDS: + dds_default_setup(phy, DDS_CHAN_TX1_I_F1, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_I_F2, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_Q_F1, 0, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX1_Q_F2, 0, 1000000, 250000); + if(dds_st[phy->id_no].rx2tx2) + { + dds_default_setup(phy, DDS_CHAN_TX2_I_F1, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_I_F2, 90000, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_Q_F1, 0, 1000000, 250000); + dds_default_setup(phy, DDS_CHAN_TX2_Q_F2, 0, 1000000, 250000); + } + dac_datasel(phy, -1, DATA_SEL_DDS); + break; + case DATA_SEL_DMA: + if(config_dma) + { + tx_count = sizeof(sine_lut) / sizeof(uint16_t); + if(dds_st[phy->id_no].rx2tx2) + { +#ifdef FMCOMMS5 + for(index = 0, index_mem = 0; index < (tx_count * 2); index += 2, index_mem += 4) +#else + for(index = 0, index_mem = 0; index < (tx_count * 2); index += 2, index_mem += 2) +#endif + { + index_i1 = index; + index_q1 = index + (tx_count / 2); + if(index_q1 >= (tx_count * 2)) + index_q1 -= (tx_count * 2); + data_i1 = (sine_lut[index_i1 / 2] << 20); + data_q1 = (sine_lut[index_q1 / 2] << 4); + Xil_Out32(DAC_DDR_BASEADDR + index_mem * 4, data_i1 | data_q1); + + index_i2 = index_i1; + index_q2 = index_q1; + if(index_i2 >= (tx_count * 2)) + index_i2 -= (tx_count * 2); + if(index_q2 >= (tx_count * 2)) + index_q2 -= (tx_count * 2); + data_i2 = (sine_lut[index_i2 / 2] << 20); + data_q2 = (sine_lut[index_q2 / 2] << 4); + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 1) * 4, data_i2 | data_q2); +#ifdef FMCOMMS5 + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 2) * 4, data_i1 | data_q1); + Xil_Out32(DAC_DDR_BASEADDR + (index_mem + 3) * 4, data_i2 | data_q2); +#endif + } + } + else + { + for(index = 0; index < tx_count; index += 1) + { + index_i1 = index; + index_q1 = index + (tx_count / 4); + if(index_q1 >= tx_count) + index_q1 -= tx_count; + data_i1 = (sine_lut[index_i1] << 20); + data_q1 = (sine_lut[index_q1] << 4); + Xil_Out32(DAC_DDR_BASEADDR + index * 4, data_i1 | data_q1); + } + } + Xil_DCacheFlush(); + if(dds_st[phy->id_no].rx2tx2) + { + length = (tx_count * 8); + } + else + { + length = (tx_count * 4); + } +#ifdef FMCOMMS5 + length = (tx_count * 16); +#endif + dac_dma_write(AXI_DMAC_REG_CTRL, 0); + dac_dma_write(AXI_DMAC_REG_CTRL, AXI_DMAC_CTRL_ENABLE); + dac_dma_write(AXI_DMAC_REG_SRC_ADDRESS, DAC_DDR_BASEADDR); + dac_dma_write(AXI_DMAC_REG_SRC_STRIDE, 0x0); + dac_dma_write(AXI_DMAC_REG_X_LENGTH, length - 1); + dac_dma_write(AXI_DMAC_REG_Y_LENGTH, 0x0); + dac_dma_write(AXI_DMAC_REG_START_TRANSFER, 0x1); + } + dac_datasel(phy, -1, DATA_SEL_DMA); + break; + default: + break; + } + dds_st[phy->id_no].enable = true; + dac_start_sync(phy, 0); +#endif +} + +/***************************************************************************//** + * @brief dds_set_frequency +*******************************************************************************/ +void dds_set_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t freq) +{ + uint64_t val64; + uint32_t reg; + + dds_st[phy->id_no].cached_freq[chan] = freq; + dac_stop(phy); + dac_read(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), ®); + reg &= ~DAC_DDS_INCR(~0); + val64 = (uint64_t) freq * 0xFFFFULL; + do_div(&val64, *dds_st[phy->id_no].dac_clk); + reg |= DAC_DDS_INCR(val64) | 1; + dac_write(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), reg); + dac_start_sync(phy, 0); +} + +/***************************************************************************//** + * @brief dds_get_frequency +*******************************************************************************/ +void dds_get_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t *freq) +{ + *freq = dds_st[phy->id_no].cached_freq[chan]; +} + +/***************************************************************************//** + * @brief dds_set_phase +*******************************************************************************/ +void dds_set_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t phase) +{ + uint64_t val64; + uint32_t reg; + + dds_st[phy->id_no].cached_phase[chan] = phase; + dac_stop(phy); + dac_read(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), ®); + reg &= ~DAC_DDS_INIT(~0); + val64 = (uint64_t) phase * 0x10000ULL + (360000 / 2); + do_div(&val64, 360000); + reg |= DAC_DDS_INIT(val64); + dac_write(phy, DAC_REG_CHAN_CNTRL_2_IIOCHAN(chan), reg); + dac_start_sync(phy, 0); +} + +/***************************************************************************//** + * @brief dds_get_phase +*******************************************************************************/ +void dds_get_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t *phase) +{ + *phase = dds_st[phy->id_no].cached_phase[chan]; +} + +/***************************************************************************//** + * @brief dds_set_phase +*******************************************************************************/ +void dds_set_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t scale_micro_units) +{ + uint32_t scale_reg; + uint32_t sign_part; + uint32_t int_part; + uint32_t fract_part; + + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) > 6) + { + if(scale_micro_units >= 1000000) + { + sign_part = 0; + int_part = 1; + fract_part = 0; + dds_st[phy->id_no].cached_scale[chan] = 1000000; + goto set_scale_reg; + } + if(scale_micro_units <= -1000000) + { + sign_part = 1; + int_part = 1; + fract_part = 0; + dds_st[phy->id_no].cached_scale[chan] = -1000000; + goto set_scale_reg; + } + dds_st[phy->id_no].cached_scale[chan] = scale_micro_units; + if(scale_micro_units < 0) + { + sign_part = 1; + int_part = 0; + scale_micro_units *= -1; + } + else + { + sign_part = 0; + int_part = 0; + } + fract_part = (uint32_t)(((uint64_t)scale_micro_units * 0x4000) / 1000000); + set_scale_reg: + scale_reg = (sign_part << 15) | (int_part << 14) | fract_part; + } + else + { + if(scale_micro_units >= 1000000) + { + scale_reg = 0; + scale_micro_units = 1000000; + } + if(scale_micro_units <= 0) + { + scale_reg = 0; + scale_micro_units = 0; + } + dds_st[phy->id_no].cached_scale[chan] = scale_micro_units; + fract_part = (uint32_t)(scale_micro_units); + scale_reg = 500000 / fract_part; + } + dac_stop(phy); + dac_write(phy, DAC_REG_CHAN_CNTRL_1_IIOCHAN(chan), DAC_DDS_SCALE(scale_reg)); + dac_start_sync(phy, 0); +} + +/***************************************************************************//** + * @brief dds_get_phase +*******************************************************************************/ +void dds_get_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t *scale_micro_units) +{ + *scale_micro_units = dds_st[phy->id_no].cached_scale[chan]; +} + +/***************************************************************************//** + * @brief dds_update +*******************************************************************************/ +void dds_update(struct ad9361_rf_phy *phy) +{ + uint32_t chan; + + for(chan = DDS_CHAN_TX1_I_F1; chan <= DDS_CHAN_TX2_Q_F2; chan++) + { + dds_set_frequency(phy, chan, dds_st[phy->id_no].cached_freq[chan]); + dds_set_phase(phy, chan, dds_st[phy->id_no].cached_phase[chan]); + dds_set_scale(phy, chan, dds_st[phy->id_no].cached_scale[chan]); + } +} + +/***************************************************************************//** + * @brief dac_datasel +*******************************************************************************/ +int32_t dac_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select sel) +{ + int32_t i; + + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) > 7) { + if (chan < 0) { /* ALL */ + for (i = 0; i < dds_st[phy->id_no].num_buf_channels; i++) { + dac_write(phy, DAC_REG_CHAN_CNTRL_7(i), sel); + dds_st[phy->id_no].cached_datasel[i] = sel; + } + } else { + dac_write(phy, DAC_REG_CHAN_CNTRL_7(chan), sel); + dds_st[phy->id_no].cached_datasel[chan] = sel; + } + } else { + uint32_t reg; + + switch(sel) { + case DATA_SEL_DDS: + case DATA_SEL_SED: + case DATA_SEL_DMA: + dac_read(phy, DAC_REG_CNTRL_2, ®); + reg &= ~DAC_DATA_SEL(~0); + reg |= DAC_DATA_SEL(sel); + dac_write(phy, DAC_REG_CNTRL_2, reg); + break; + default: + return -EINVAL; + } + for (i = 0; i < dds_st[phy->id_no].num_buf_channels; i++) { + dds_st[phy->id_no].cached_datasel[i] = sel; + } + } + + return 0; +} + +/***************************************************************************//** + * @brief dac_get_datasel +*******************************************************************************/ +void dac_get_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select *sel) +{ + *sel = dds_st[phy->id_no].cached_datasel[chan]; +} + +/***************************************************************************//** + * @brief dds_to_signed_mag_fmt +*******************************************************************************/ +uint32_t dds_to_signed_mag_fmt(int32_t val, int32_t val2) +{ + uint32_t i; + uint64_t val64; + + /* format is 1.1.14 (sign, integer and fractional bits) */ + + switch (val) { + case 1: + i = 0x4000; + break; + case -1: + i = 0xC000; + break; + case 0: + i = 0; + if (val2 < 0) { + i = 0x8000; + val2 *= -1; + } + break; + default: + /* Invalid Value */ + i = 0; + } + + val64 = (uint64_t)val2 * 0x4000UL + (1000000UL / 2); + do_div(&val64, 1000000UL); + + return i | val64; +} + +/***************************************************************************//** + * @brief dds_from_signed_mag_fmt +*******************************************************************************/ +void dds_from_signed_mag_fmt(uint32_t val, + int32_t *r_val, + int32_t *r_val2) +{ + uint64_t val64; + int32_t sign; + + if (val & 0x8000) + sign = -1; + else + sign = 1; + + if (val & 0x4000) + *r_val = 1 * sign; + else + *r_val = 0; + + val &= ~0xC000; + + val64 = val * 1000000ULL + (0x4000 / 2); + do_div(&val64, 0x4000); + + if (*r_val == 0) + *r_val2 = val64 * sign; + else + *r_val2 = val64; +} + +/***************************************************************************//** + * @brief dds_set_calib_scale_phase +*******************************************************************************/ +int32_t dds_set_calib_scale_phase(struct ad9361_rf_phy *phy, + uint32_t phase, + uint32_t chan, + int32_t val, + int32_t val2) +{ + uint32_t reg; + uint32_t i; + + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { + return -1; + } + + i = dds_to_signed_mag_fmt(val, val2); + + dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), ®); + + if (!((chan + phase) % 2)) { + reg &= ~DAC_IQCOR_COEFF_1(~0); + reg |= DAC_IQCOR_COEFF_1(i); + } else { + reg &= ~DAC_IQCOR_COEFF_2(~0); + reg |= DAC_IQCOR_COEFF_2(i); + } + dac_write(phy, DAC_REG_CHAN_CNTRL_8(chan), reg); + dac_write(phy, DAC_REG_CHAN_CNTRL_6(chan), DAC_IQCOR_ENB); + + return 0; +} + +/***************************************************************************//** + * @brief dds_get_calib_scale_phase +*******************************************************************************/ +int32_t dds_get_calib_scale_phase(struct ad9361_rf_phy *phy, + uint32_t phase, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + uint32_t reg; + + if (PCORE_VERSION_MAJOR(dds_st[phy->id_no].pcore_version) < 8) { + return -1; + } + + dac_read(phy, DAC_REG_CHAN_CNTRL_8(chan), ®); + + /* format is 1.1.14 (sign, integer and fractional bits) */ + + if (!((phase + chan) % 2)) { + reg = DAC_TO_IQCOR_COEFF_1(reg); + } else { + reg = DAC_TO_IQCOR_COEFF_2(reg); + } + + dds_from_signed_mag_fmt(reg, val, val2); + + return 0; +} + +/***************************************************************************//** + * @brief dds_set_calib_scale +*******************************************************************************/ +int32_t dds_set_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2) +{ + return dds_set_calib_scale_phase(phy, 0, chan, val, val2); +} + +/***************************************************************************//** + * @brief dds_get_calib_scale +*******************************************************************************/ +int32_t dds_get_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + return dds_get_calib_scale_phase(phy, 0, chan, val, val2); +} + +/***************************************************************************//** + * @brief dds_set_calib_phase +*******************************************************************************/ +int32_t dds_set_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2) +{ + return dds_set_calib_scale_phase(phy, 1, chan, val, val2); +} + +/***************************************************************************//** + * @brief dds_get_calib_phase +*******************************************************************************/ +int32_t dds_get_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2) +{ + return dds_get_calib_scale_phase(phy, 1, chan, val, val2); +} diff --git a/src/ad9361_lib/dac_core.h b/src/ad9361_lib/dac_core.h new file mode 100644 index 0000000..e586a4c --- /dev/null +++ b/src/ad9361_lib/dac_core.h @@ -0,0 +1,203 @@ +/***************************************************************************//** + * @file dac_core.h + * @brief Header file of DAC Core Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef DAC_CORE_API_H_ +#define DAC_CORE_API_H_ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "ad9361_dev.h" + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define DAC_REG_VERSION 0x0000 +#define DAC_VERSION(x) (((x) & 0xffffffff) << 0) +#define VERSION_IS(x,y,z) ((x) << 16 | (y) << 8 | (z)) +#define DAC_REG_ID 0x0004 +#define DAC_ID(x) (((x) & 0xffffffff) << 0) +#define DAC_REG_SCRATCH 0x0008 +#define DAC_SCRATCH(x) (((x) & 0xffffffff) << 0) + +#define PCORE_VERSION_MAJOR(version) (version >> 16) + +#define DAC_REG_RSTN 0x0040 +#define DAC_RSTN (1 << 0) +#define DAC_MMCM_RSTN (1 << 1) + +#define DAC_REG_RATECNTRL 0x004C +#define DAC_RATE(x) (((x) & 0xFF) << 0) +#define DAC_TO_RATE(x) (((x) >> 0) & 0xFF) + +#define DAC_REG_CNTRL_1 0x0048 +#define DAC_ENABLE (1 << 0) /* v7.0 */ +#define DAC_SYNC (1 << 0) /* v8.0 */ + +#define DAC_REG_CNTRL_2 0x0044 +#define DAC_PAR_TYPE (1 << 7) +#define DAC_PAR_ENB (1 << 6) +#define DAC_R1_MODE (1 << 5) +#define DAC_DATA_FORMAT (1 << 4) +#define DAC_DATA_SEL(x) (((x) & 0xF) << 0) /* v7.0 */ +#define DAC_TO_DATA_SEL(x) (((x) >> 0) & 0xF) /* v7.0 */ + +#define DAC_REG_VDMA_FRMCNT 0x0084 +#define DAC_VDMA_FRMCNT(x) (((x) & 0xFFFFFFFF) << 0) +#define DAC_TO_VDMA_FRMCNT(x) (((x) >> 0) & 0xFFFFFFFF) + +#define DAC_REG_VDMA_STATUS 0x0088 +#define DAC_VDMA_OVF (1 << 1) +#define DAC_VDMA_UNF (1 << 0) + +enum dds_data_select { + DATA_SEL_DDS, + DATA_SEL_SED, + DATA_SEL_DMA, + DATA_SEL_ZERO, /* OUTPUT 0 */ + DATA_SEL_PN7, + DATA_SEL_PN15, + DATA_SEL_PN23, + DATA_SEL_PN31, + DATA_SEL_LB, /* loopback data (ADC) */ + DATA_SEL_PNXX, /* (Device specific) */ +}; + +#define DAC_REG_CHAN_CNTRL_1_IIOCHAN(x) (0x0400 + ((x) >> 1) * 0x40 + ((x) & 1) * 0x8) +#define DAC_DDS_SCALE(x) (((x) & 0xFFFF) << 0) +#define DAC_TO_DDS_SCALE(x) (((x) >> 0) & 0xFFFF) + +#define DAC_REG_CHAN_CNTRL_2_IIOCHAN(x) (0x0404 + ((x) >> 1) * 0x40 + ((x) & 1) * 0x8) +#define DAC_DDS_INIT(x) (((x) & 0xFFFF) << 16) +#define DAC_TO_DDS_INIT(x) (((x) >> 16) & 0xFFFF) +#define DAC_DDS_INCR(x) (((x) & 0xFFFF) << 0) +#define DAC_TO_DDS_INCR(x) (((x) >> 0) & 0xFFFF) + +#define DDS_CHAN_TX1_I_F1 0 +#define DDS_CHAN_TX1_I_F2 1 +#define DDS_CHAN_TX1_Q_F1 2 +#define DDS_CHAN_TX1_Q_F2 3 +#define DDS_CHAN_TX2_I_F1 4 +#define DDS_CHAN_TX2_I_F2 5 +#define DDS_CHAN_TX2_Q_F1 6 +#define DDS_CHAN_TX2_Q_F2 7 + +#define AXI_DMAC_REG_IRQ_MASK 0x80 +#define AXI_DMAC_REG_IRQ_PENDING 0x84 +#define AXI_DMAC_REG_IRQ_SOURCE 0x88 + +#define AXI_DMAC_REG_CTRL 0x400 +#define AXI_DMAC_REG_TRANSFER_ID 0x404 +#define AXI_DMAC_REG_START_TRANSFER 0x408 +#define AXI_DMAC_REG_FLAGS 0x40c +#define AXI_DMAC_REG_DEST_ADDRESS 0x410 +#define AXI_DMAC_REG_SRC_ADDRESS 0x414 +#define AXI_DMAC_REG_X_LENGTH 0x418 +#define AXI_DMAC_REG_Y_LENGTH 0x41c +#define AXI_DMAC_REG_DEST_STRIDE 0x420 +#define AXI_DMAC_REG_SRC_STRIDE 0x424 +#define AXI_DMAC_REG_TRANSFER_DONE 0x428 +#define AXI_DMAC_REG_ACTIVE_TRANSFER_ID 0x42c +#define AXI_DMAC_REG_STATUS 0x430 +#define AXI_DMAC_REG_CURRENT_DEST_ADDR 0x434 +#define AXI_DMAC_REG_CURRENT_SRC_ADDR 0x438 +#define AXI_DMAC_REG_DBG0 0x43c +#define AXI_DMAC_REG_DBG1 0x440 + +#define AXI_DMAC_CTRL_ENABLE (1 << 0) +#define AXI_DMAC_CTRL_PAUSE (1 << 1) + +#define AXI_DMAC_IRQ_SOT (1 << 0) +#define AXI_DMAC_IRQ_EOT (1 << 1) + +struct dds_state +{ + uint32_t cached_freq[8]; + uint32_t cached_phase[8]; + int32_t cached_scale[8]; + enum dds_data_select cached_datasel[8]; + uint32_t *dac_clk; + uint32_t pcore_version; + uint32_t num_buf_channels; + bool enable; + bool rx2tx2; +}; + +#define DAC_REG_CHAN_CNTRL_6(c) (0x0414 + (c) * 0x40) +#define DAC_IQCOR_ENB (1 << 2) /* v8.0 */ + +#define DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40) /* v8.0 */ +#define DAC_DAC_DDS_SEL(x) (((x) & 0xF) << 0) +#define DAC_TO_DAC_DDS_SEL(x) (((x) >> 0) & 0xF) + +#define DAC_REG_CHAN_CNTRL_8(c) (0x041C + (c) * 0x40) /* v8.0 */ +#define DAC_IQCOR_COEFF_1(x) (((x) & 0xFFFF) << 16) +#define DAC_TO_IQCOR_COEFF_1(x) (((x) >> 16) & 0xFFFF) +#define DAC_IQCOR_COEFF_2(x) (((x) & 0xFFFF) << 0) +#define DAC_TO_IQCOR_COEFF_2(x) (((x) >> 0) & 0xFFFF) + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +void dac_init(struct ad9361_rf_phy *phy, uint8_t data_sel, uint8_t config_dma); +void dds_set_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t freq); +void dds_get_frequency(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t *freq); +void dds_set_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t phase); +void dds_get_phase(struct ad9361_rf_phy *phy, uint32_t chan, uint32_t *phase); +void dds_set_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t scale_micro_units); +void dds_get_scale(struct ad9361_rf_phy *phy, uint32_t chan, int32_t *scale_micro_units); +void dds_update(struct ad9361_rf_phy *phy); +int32_t dac_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select sel); +void dac_get_datasel(struct ad9361_rf_phy *phy, int32_t chan, enum dds_data_select *sel); +int32_t dds_set_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2); +int32_t dds_get_calib_scale(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2); +int32_t dds_set_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t val, + int32_t val2); +int32_t dds_get_calib_phase(struct ad9361_rf_phy *phy, + uint32_t chan, + int32_t *val, + int32_t *val2); +#endif diff --git a/src/ad9361_lib/platform.c b/src/ad9361_lib/platform.c new file mode 100644 index 0000000..4b11b54 --- /dev/null +++ b/src/ad9361_lib/platform.c @@ -0,0 +1,87 @@ +#include "util.h" +#include "platform.h" + +static struct ad9361_platform_func _opt = {0}; + +void udelay(unsigned long us) +{ + if (_opt.ad9361_udelay) + _opt.ad9361_udelay(us); +} + +void mdelay(unsigned long ms) +{ + if (_opt.ad9361_udelay) + _opt.ad9361_udelay(1000 * ms); +} + +void apb_reset() +{ + if (_opt.ad9361_reset) + _opt.ad9361_reset(); +} + +int spi_write_then_read(struct spi_device *spi, const unsigned char *txbuf, unsigned n_tx, + unsigned char *rxbuf, unsigned n_rx) +{ + if (_opt.ad9361_spi_xfer) + return _opt.ad9361_spi_xfer(_opt.dev, txbuf, n_tx, rxbuf, n_rx); + return SUCCESS; +} + +void Xil_Out32(uint32_t addr, uint32_t data) +{ + if (_opt.ad9361_axi_write) + _opt.ad9361_axi_write(addr, data); +} + +uint32_t Xil_In32(uint32_t addr) +{ + if (_opt.ad9361_axi_read) + return _opt.ad9361_axi_read(addr); + return 0; +} + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +extern int ad9361_platform_init(struct ad9361_platform_func *opt) +{ + memcpy(&_opt, opt, sizeof *opt); + return 0; +} + diff --git a/src/ad9361_lib/platform.h b/src/ad9361_lib/platform.h new file mode 100644 index 0000000..f90f3ad --- /dev/null +++ b/src/ad9361_lib/platform.h @@ -0,0 +1,64 @@ +/****************************************************************************** +* +* Copyright (C) 2008 - 2014 Xilinx, Inc. All rights reserved. +* +* 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. +* +* Use of the Software is limited solely to applications: +* (a) running on a Xilinx device, or +* (b) that interact with a Xilinx device through a bus or interconnect. +* +* 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 +* XILINX 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. +* +* Except as contained in this notice, the name of the Xilinx shall not be used +* in advertising or otherwise to promote the sale, use or other dealings in +* this Software without prior written authorization from Xilinx. +* +******************************************************************************/ +#ifndef __PLATFORM_H__ +#define __PLATFORM_H__ + +struct spi_device; +int spi_write_then_read(struct spi_device *, const unsigned char *txbuf, unsigned n_tx, + unsigned char *rxbuf, unsigned n_rx); + +void mdelay(unsigned long ms); +void udelay(unsigned long us); + +void Xil_Out32(uint32_t addr, uint32_t data); +uint32_t Xil_In32(uint32_t addr); + +/* replace apb struct */ +void apb_reset(void); + +char *strsep(char **, const char *); + +struct ad9361_platform_func { + void *dev; + int (*ad9361_spi_xfer)(void *dev, const unsigned char *txbuf, unsigned n_tx, unsigned char *rxbuf, unsigned n_rx); + + void (*ad9361_udelay)(unsigned long us); + + void (*ad9361_axi_write)(uint32_t addr, uint32_t data); + uint32_t (*ad9361_axi_read)(uint32_t addr); + + void (*ad9361_reset)(void); +}; + +extern int ad9361_platform_init(struct ad9361_platform_func *opt); + +#endif diff --git a/src/ad9361_lib/util.c b/src/ad9361_lib/util.c new file mode 100644 index 0000000..92685ce --- /dev/null +++ b/src/ad9361_lib/util.c @@ -0,0 +1,346 @@ +/***************************************************************************//** + * @file util.c + * @brief Implementation of Util Driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include "util.h" +#include "string.h" +#include "platform.h" + +/******************************************************************************/ +/*************************** Macros Definitions *******************************/ +/******************************************************************************/ +#define BITS_PER_LONG 32 + +/***************************************************************************//** + * @brief clk_prepare_enable +*******************************************************************************/ +int32_t clk_prepare_enable(struct clk *clk) +{ + if (clk) { + // Unused variable - fix compiler warning + } + + return 0; +} + +/***************************************************************************//** + * @brief clk_get_rate +*******************************************************************************/ +uint32_t clk_get_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv) +{ + uint32_t rate = 0; + uint32_t source; + + source = clk_priv->source; + + switch (source) { + case TX_REFCLK: + case RX_REFCLK: + case BB_REFCLK: + rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clk_refin->rate); + break; + case TX_RFPLL_INT: + case RX_RFPLL_INT: + rate = ad9361_rfpll_int_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + case RX_RFPLL_DUMMY: + case TX_RFPLL_DUMMY: + rate = ad9361_rfpll_dummy_recalc_rate(clk_priv); + break; + case TX_RFPLL: + case RX_RFPLL: + rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case BBPLL_CLK: + rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + case ADC_CLK: + case R2_CLK: + case R1_CLK: + case CLKRF_CLK: + case RX_SAMPL_CLK: + case DAC_CLK: + case T2_CLK: + case T1_CLK: + case CLKTF_CLK: + case TX_SAMPL_CLK: + rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + default: + break; + } + + return rate; +} + +/***************************************************************************//** + * @brief clk_set_rate +*******************************************************************************/ +int32_t clk_set_rate(struct ad9361_rf_phy *phy, + struct refclk_scale *clk_priv, + uint32_t rate) +{ + uint32_t source; + int32_t i; + uint32_t round_rate; + + source = clk_priv->source; + if(phy->clks[source]->rate != rate) + { + switch (source) { + case TX_REFCLK: + case RX_REFCLK: + case BB_REFCLK: + round_rate = ad9361_clk_factor_round_rate(clk_priv, rate, + &phy->clk_refin->rate); + ad9361_clk_factor_set_rate(clk_priv, round_rate, + phy->clk_refin->rate); + phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clk_refin->rate); + break; + case TX_RFPLL_INT: + case RX_RFPLL_INT: + round_rate = ad9361_rfpll_int_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_rfpll_int_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_rfpll_int_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + case RX_RFPLL_DUMMY: + case TX_RFPLL_DUMMY: + ad9361_rfpll_dummy_set_rate(clk_priv, rate); + case TX_RFPLL: + case RX_RFPLL: + round_rate = ad9361_rfpll_round_rate(clk_priv, rate); + ad9361_rfpll_set_rate(clk_priv, round_rate); + phy->clks[source]->rate = ad9361_rfpll_recalc_rate(clk_priv); + break; + case BBPLL_CLK: + round_rate = ad9361_bbpll_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_bbpll_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + phy->bbpll_initialized = true; + break; + case ADC_CLK: + case R2_CLK: + case R1_CLK: + case CLKRF_CLK: + case RX_SAMPL_CLK: + case DAC_CLK: + case T2_CLK: + case T1_CLK: + case CLKTF_CLK: + case TX_SAMPL_CLK: + round_rate = ad9361_clk_factor_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_clk_factor_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_clk_factor_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + break; + default: + break; + } + for(i = BB_REFCLK; i < BBPLL_CLK; i++) + { + phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i], + phy->clk_refin->rate); + } + phy->clks[BBPLL_CLK]->rate = ad9361_bbpll_recalc_rate(phy->ref_clk_scale[BBPLL_CLK], + phy->clks[phy->ref_clk_scale[BBPLL_CLK]->parent_source]->rate); + for(i = ADC_CLK; i < RX_RFPLL_INT; i++) + { + phy->clks[i]->rate = ad9361_clk_factor_recalc_rate(phy->ref_clk_scale[i], + phy->clks[phy->ref_clk_scale[i]->parent_source]->rate); + } + for(i = RX_RFPLL_INT; i < RX_RFPLL_DUMMY; i++) + { + phy->clks[i]->rate = ad9361_rfpll_int_recalc_rate(phy->ref_clk_scale[i], + phy->clks[phy->ref_clk_scale[i]->parent_source]->rate); + } + for(i = RX_RFPLL_DUMMY; i < RX_RFPLL; i++) + { + phy->clks[i]->rate = ad9361_rfpll_dummy_recalc_rate(phy->ref_clk_scale[i]); + } + for(i = RX_RFPLL; i < NUM_AD9361_CLKS; i++) + { + phy->clks[i]->rate = ad9361_rfpll_recalc_rate(phy->ref_clk_scale[i]); + } + } else { + if ((source == BBPLL_CLK) && !phy->bbpll_initialized) { + round_rate = ad9361_bbpll_round_rate(clk_priv, rate, + &phy->clks[clk_priv->parent_source]->rate); + ad9361_bbpll_set_rate(clk_priv, round_rate, + phy->clks[clk_priv->parent_source]->rate); + phy->clks[source]->rate = ad9361_bbpll_recalc_rate(clk_priv, + phy->clks[clk_priv->parent_source]->rate); + phy->bbpll_initialized = true; + } + } + + return 0; +} + +/***************************************************************************//** + * @brief int_sqrt +*******************************************************************************/ +uint32_t int_sqrt(uint32_t x) +{ + uint32_t b, m, y = 0; + + if (x <= 1) + return x; + + m = 1UL << (BITS_PER_LONG - 2); + while (m != 0) { + b = y + m; + y >>= 1; + + if (x >= b) { + x -= b; + y += m; + } + m >>= 2; + } + + return y; +} + +/***************************************************************************//** + * @brief ilog2 +*******************************************************************************/ +int32_t ilog2(int32_t x) +{ + int32_t A = !(!(x >> 16)); + int32_t count = 0; + int32_t x_copy = x; + + count = count + (A << 4); + + x_copy = (((~A + 1) & (x >> 16)) + (~(~A + 1) & x)); + + A = !(!(x_copy >> 8)); + count = count + (A << 3); + x_copy = (((~A + 1) & (x_copy >> 8)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 4)); + count = count + (A << 2); + x_copy = (((~A + 1) & (x_copy >> 4)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 2)); + count = count + (A << 1); + x_copy = (((~A + 1) & (x_copy >> 2)) + (~(~A + 1) & x_copy)); + + A = !(!(x_copy >> 1)); + count = count + A; + + return count; +} + +/***************************************************************************//** + * @brief do_div +*******************************************************************************/ +uint64_t do_div(uint64_t* n, uint64_t base) +{ + uint64_t mod = 0; + + mod = *n % base; + *n = *n / base; + + return mod; +} + +/***************************************************************************//** + * @brief find_first_bit +*******************************************************************************/ +uint32_t find_first_bit(uint32_t word) +{ + int32_t num = 0; + + if ((word & 0xffff) == 0) { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + num += 1; + return num; +} + +/***************************************************************************//** + * @brief ERR_PTR +*******************************************************************************/ +void * ERR_PTR(long error) +{ + return (void *) error; +} + +/***************************************************************************//** + * @brief zmalloc +*******************************************************************************/ +void *zmalloc(size_t size) +{ + void *ptr = malloc(size); + if (ptr) + memset(ptr, 0, size); + //mdelay(1); + + return ptr; +} diff --git a/src/ad9361_lib/util.h b/src/ad9361_lib/util.h new file mode 100644 index 0000000..c938896 --- /dev/null +++ b/src/ad9361_lib/util.h @@ -0,0 +1,142 @@ +/***************************************************************************//** + * @file util.h + * @brief Header file of Util driver. + * @author DBogdan (dragos.bogdan@analog.com) +******************************************************************************** + * Copyright 2013(c) Analog Devices, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * - Neither the name of Analog Devices, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * - The use of this software may or may not infringe the patent rights + * of one or more patent holders. This license does not release you + * from the requirement that you obtain separate licenses from these + * patent holders to use this software. + * - Use of the software either in source or binary form, must be run + * on or directly connected to an Analog Devices Inc. component. + * + * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, NON-INFRINGEMENT, + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL ANALOG DEVICES BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, INTELLECTUAL PROPERTY RIGHTS, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*******************************************************************************/ +#ifndef __NO_OS_PORT_H__ +#define __NO_OS_PORT_H__ + +/******************************************************************************/ +/***************************** Include Files **********************************/ +/******************************************************************************/ +#include +#include +#include +#include +#include "ad9361_dev.h" +#include "common.h" +#include "config.h" + +#ifndef bool +#define bool int +#endif + +/******************************************************************************/ +/********************** Macros and Constants Definitions **********************/ +/******************************************************************************/ +#define SUCCESS 0 +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif +#define min(x, y) (((x) < (y)) ? (x) : (y)) +#define min_t(type, x, y) (type)min((type)(x), (type)(y)) +#define max(x, y) (((x) > (y)) ? (x) : (y)) +#define max_t(type, x, y) (type)max((type)(x), (type)(y)) +#define clamp(val, min_val, max_val) (max(min((val), (max_val)), (min_val))) +#define clamp_t(type, val, min_val, max_val) (type)clamp((type)(val), (type)(min_val), (type)(max_val)) +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) +#define DIV_ROUND_CLOSEST(x, divisor) (((x) + (divisor) / 2) / (divisor)) +#define BIT(x) (1 << (x)) +#define CLK_IGNORE_UNUSED BIT(3) +#define CLK_GET_RATE_NOCACHE BIT(6) + +#if defined(HAVE_VERBOSE_MESSAGES) +#define dev_err(dev, format, ...) do {printf(format, ## __VA_ARGS__);printf("\r\n"); } while (0) +#define dev_warn(dev, format, ...) do {printf(format, ## __VA_ARGS__);printf("\r\n"); } while (0) +#if defined(HAVE_DEBUG_MESSAGES) +#define dev_dbg(dev, format, ...) do {printf(format, ## __VA_ARGS__);printf("\r\n"); } while (0) +#else +#define dev_dbg(dev, format, ...) do { if (0) printf(format, ## __VA_ARGS__); } while (0) +#endif +#define printk(format, ...) printf(format, ## __VA_ARGS__) +#else +#define dev_err(dev, format, ...) do{ if (0) xil_printf(format, ## __VA_ARGS__); }while(0) +#define dev_warn(dev, format, ...) do{ if (0) xil_printf(format, ## __VA_ARGS__); }while(0) +#define dev_dbg(dev, format, ...) do{ if (0) xil_printf(format, ## __VA_ARGS__); }while(0) +#define printk(format, ...) do{ if (0) xil_printf(format, ## __VA_ARGS__); }while(0) +#endif + + +/* +struct device { +}; + +struct spi_device { + struct device dev; + uint8_t id_no; +}; +*/ + + +struct axiadc_state { + struct ad9361_rf_phy *phy; + uint32_t pcore_version; +}; + +struct axiadc_chip_info { + char *name; + int32_t num_channels; +}; + +struct axiadc_converter { + struct axiadc_chip_info *chip_info; + uint32_t scratch_reg[16]; +}; + +#ifdef WIN32 +#include "basetsd.h" +typedef SSIZE_T ssize_t; +#define strsep(s, ct) 0 +#define snprintf(s, n, format, ...) 0 +#define __func__ __FUNCTION__ +#endif + +/******************************************************************************/ +/************************ Functions Declarations ******************************/ +/******************************************************************************/ +struct refclk_scale; +int32_t clk_prepare_enable(struct clk *clk); +uint32_t clk_get_rate(struct ad9361_rf_phy *phy, struct refclk_scale *clk_priv); +int32_t clk_set_rate(struct ad9361_rf_phy *phy, struct refclk_scale *clk_priv, uint32_t rate); +uint32_t int_sqrt(uint32_t x); +int32_t ilog2(int32_t x); +uint64_t do_div(uint64_t* n, + uint64_t base); +uint32_t find_first_bit(uint32_t word); +void * ERR_PTR(long error); +void *zmalloc(size_t size); + +#endif diff --git a/src/ad9680.c b/src/ad9680.c new file mode 100644 index 0000000..e997719 --- /dev/null +++ b/src/ad9680.c @@ -0,0 +1,218 @@ +/* + * ad9680.c + * + * Created on: 2019-6-13 + * Author: Administrator + */ +#include +#include +#include +#include +#include + +#include "gspi.h" +#include "axi0.h" +#include "ad9680.h" + +/******************************************************************************/ +#define AD9680_REG_INTERFACE_CONF_A 0x000 +#define AD9680_REG_INTERFACE_CONF_B 0x001 +#define AD9680_REG_CHIP_ID_LOW 0x004 +#define AD9680_REG_CHIP_ID_HIGH 0x005 +#define AD9680_REG_DEVICE_INDEX 0x008 +#define AD9680_REG_CHIP_DEC_RATIO 0x201 +#define AD9680_REG_ADC_TEST_MODE 0x550 +#define AD9680_REG_OUTPUT_MODE 0x561 +#define AD9680_REG_JESD204B_LANE_RATE_CTRL 0x56e +#define AD9680_REG_JESD204B_QUICK_CONFIG 0x570 +#define AD9680_REG_LINK_CONTROL 0x571 +#define AD9680_REG_JESD204B_SCR_LAN 0x58b +#define AD9680_REG_JESD204B_MF_CTRL 0x58d +#define AD9680_REG_JESD204B_CSN_CONFIG 0x58f +#define AD9680_REG_JESD204B_SUBCLASS_CONFIG 0x590 +#define AD9680_REG_JESD204B_LANE_SERD_OUT0_ASSIGN 0x5B2 +#define AD9680_REG_JESD204B_LANE_SERD_OUT1_ASSIGN 0x5b3 +#define AD9680_REG_JESD204B_LANE_SERD_OUT2_ASSIGN 0x5b5 +#define AD9680_REG_JESD204B_LANE_SERD_OUT3_ASSIGN 0x5b6 +#define AD9680_REG_JESD204B_PLL_LOCK_STATUS 0x56F + +#define AD9680_CHIP_ID 0x0C5 +#define AD9680_TEST_OFF 0x000 +#define AD9680_TEST_PN9 0x006 +#define AD9680_TEST_PN23 0x005 +#define AD9680_TEST_RAMP 0x00f +#define AD9680_FORMAT_2S_COMPLEMENT 0x001 +#define AD9680_FORMAT_OFFSET_BINARY 0x000 + +static gspi_t _ad9680 = NULL; +static int __gspi_ad9680_init(void ) +{ + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_1, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + _ad9680 = gspi_open(GSPI_CS_AD9680, &format); + return 0; +} + +uint8_t ad9680_read(uint16_t reg) +{ + int elms = 0; + unsigned char buf[3]; + + if (!_ad9680) + __gspi_ad9680_init(); + + buf[0] = 0x80 | (reg >> 8); + buf[1] = reg & 0xFF; + + elms = gspi_xfer(_ad9680, buf, 0, 2, &buf[2], 2, 1); + if (elms != sizeof buf) { + printf("error: ad9680 reg 0x%x read fail\r\n", reg); + return 0; + } + + printf("ad9680 read reg:0x%04x, val: 0x%x\r\n", reg, buf[2]); + return buf[2] & 0xff; + +} + +int ad9680_write(uint16_t reg, uint8_t val) +{ + int elms = 0; + unsigned char buf[3]; + + if (!_ad9680) + __gspi_ad9680_init(); + + buf[0] = 0x7F & (reg >> 8); + buf[1] = reg & 0xFF; + buf[2] = val & 0xFF; + elms = gspi_xfer(_ad9680, buf, 0, 3, NULL, 0, 0); + if (elms != sizeof buf) { + printf("error: ad9680 reg 0x%x write fail\r\n", reg); + return -1; + } + + printf("ad9680 write reg:0x%04x, val: 0x%x\r\n", reg, val); + return 0; +} + +int ad9680_setup(uint32_t speed) +{ + uint8_t chip_id; + uint8_t pll_stat; + uint8_t temp; + + /*enable axi scrambling*/ + axi0_write(0x00c, 0x1); + printf("enable fpga jesd204 scrambing... \r\n"); + + ad9680_write(AD9680_REG_INTERFACE_CONF_A, 0x81); // 0x000 RESET + tsc_delay(250000); + + chip_id = ad9680_read(AD9680_REG_CHIP_ID_LOW); //0x004 + printf("AD CHIP ID (0x%x).\r\n", chip_id); + + ad9680_write(AD9680_REG_LINK_CONTROL, 0x15); //0x571 disable link, ilas enable + temp = ad9680_read(AD9680_REG_LINK_CONTROL); + if(temp != 0x15){ + printf("AD9680_REG_LINK_CONTROL ERROR! %x\r\n", temp); + } + + ad9680_write(0x5b0, 0xaa); //JESD204B lane power-down; 0 = ¿ª; 1 = ¹Ø + ad9680_write(0x5b2, 0x0); //SERDOUT0¡ÀͨµÀ·ÖÅä; 000 = Âß¼­Í¨µÀ0; 001 = Âß¼­Í¨µÀ1; 010 = Âß¼­Í¨µÀ2; 011 = Âß¼­Í¨µÀ3 + ad9680_write(0x5b3, 0x1); //SERDOUT1¡ÀͨµÀ·ÖÅä; 000 = Âß¼­Í¨µÀ0; 001 = Âß¼­Í¨µÀ1; 010 = Âß¼­Í¨µÀ2; 011 = Âß¼­Í¨µÀ3 + ad9680_write(0x5b5, 0x2); //SERDOUT2¡ÀͨµÀ·ÖÅä; 000 = Âß¼­Í¨µÀ0; 001 = Âß¼­Í¨µÀ1; 010 = Âß¼­Í¨µÀ2; 011 = Âß¼­Í¨µÀ3 + ad9680_write(0x5b6, 0x3); //SERDOUT3¡ÀͨµÀ·ÖÅä; 000 = Âß¼­Í¨µÀ0; 001 = Âß¼­Í¨µÀ1; 010 = Âß¼­Í¨µÀ2; 011 = Âß¼­Í¨µÀ3 + + ad9680_write(AD9680_REG_JESD204B_SCR_LAN, 0x83); //0x58b JESD204B parameters SCR/L + temp = ad9680_read(AD9680_REG_JESD204B_SCR_LAN); + if(temp != 0x83){ + printf("AD9680_REG_JESD204B_SCR_LAN ERROR! %x\r\n", temp); + } + + ad9680_write(AD9680_REG_JESD204B_MF_CTRL, 0x1f); //0x58d mf-frame-count + temp = ad9680_read(AD9680_REG_JESD204B_MF_CTRL); + if(temp != 0x1f){ + printf("AD9680_REG_JESD204B_MF_CTRL ERROR! %x\r\n", temp); + } + + ad9680_write(AD9680_REG_JESD204B_CSN_CONFIG, 0x0f); //0x58f 14-bit + temp = ad9680_read(AD9680_REG_JESD204B_CSN_CONFIG); + if(temp != 0x0f){ + printf("AD9680_REG_JESD204B_CSN_CONFIG ERROR! %x\r\n", temp); + } + + ad9680_write(AD9680_REG_JESD204B_SUBCLASS_CONFIG, 0x2f); //0x590 subclass-1, N'=16 + temp = ad9680_read(AD9680_REG_JESD204B_SUBCLASS_CONFIG); + if(temp != 0x2f){ + printf("AD9680_REG_JESD204B_SUBCLASS_CONFIG ERROR! %x\r\n", temp); + } + + ad9680_write(AD9680_REG_JESD204B_QUICK_CONFIG, 0x89); //0x570 m=2, l=4, f= 1 + temp = ad9680_read(AD9680_REG_JESD204B_QUICK_CONFIG); + if(temp != 0x89){ + printf("AD9680_REG_JESD204B_QUICK_CONFIG ERROR! %x\r\n", temp); + } + + if (speed < 625) { + ad9680_write(AD9680_REG_JESD204B_LANE_RATE_CTRL, 0x10); // low line rate mode must be enabled + } else { + ad9680_write(AD9680_REG_JESD204B_LANE_RATE_CTRL, 0x00); //0x56e low line rate mode must be disabled + } + temp = ad9680_read(AD9680_REG_JESD204B_LANE_RATE_CTRL); + if(temp != ((speed < 625) ? 0x10 : 0x00)){ + printf("AD9680_REG_JESD204B_LANE_RATE_CTRL ERROR! %x\r\n", temp); + } + + ad9680_write(AD9680_REG_LINK_CONTROL, 0x14); //0x571 link enable + temp = ad9680_read(AD9680_REG_LINK_CONTROL); + if(temp != 0x14){ + printf("AD9680_REG_LINK_CONTROL ERROR! %x\r\n", temp); + } + + tsc_delay(250000); + + pll_stat = ad9680_read(AD9680_REG_JESD204B_PLL_LOCK_STATUS);//0x56f + if ((pll_stat & 0x80) != 0x80) + printf("AD9680: PLL is NOT locked!\r\n"); + else + printf("AD9680: PLL is Succeed!\r\n"); + + /*jesd204 reset*/ + tsc_delay(10000); + printf("jesd204 reset \r\n"); + axi0_write(0x1044, 0x1); + axi0_write(0x1044, 0x0); + printf("AD9680 setup complete...\r\n"); + return 0; +} + +static int do_ad9680(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t reg, val; + + if (argc == 3 && argv[1][0] == 'r') { + reg = strtoul(argv[2], NULL, 0); + ad9680_read(reg & 0xffff); + } else if (argc == 4 && argv[1][0] == 'w') { + reg = strtoul(argv[2], NULL, 0); + val = strtoul(argv[3], NULL, 0); + ad9680_write(reg & 0xffff, val & 0xff); + } else if (argc == 3 && argv[1][0] == 's') { + val = strtoul(argv[2], NULL, 0); + ad9680_setup(val); + } else { + printf("usge: %s [r/w] [reg] [val] \r\n", argv[0]); + } + + return 0; +} + +CON_CMD(ad9680, "ad9680 reg opts", NULL, do_ad9680) + diff --git a/src/ad9680.h b/src/ad9680.h new file mode 100644 index 0000000..2213795 --- /dev/null +++ b/src/ad9680.h @@ -0,0 +1,18 @@ +/* + * ad9680.h + * + * Created on: 2019-6-13 + * Author: Administrator + */ + +#ifndef AD9680_H_ +#define AD9680_H_ + +/* + * 6678 | <---- spi cs1 ---->| K7A | <--- spi --> ad9680 + * <- gpio12~15 cs0 ->| + * */ +extern uint8_t ad9680_read(uint16_t reg); +extern int ad9680_write(uint16_t reg, uint8_t val); +extern int ad9680_setup(uint32_t speed); +#endif /* AD9280_H_ */ diff --git a/src/ad9779.c b/src/ad9779.c new file mode 100644 index 0000000..99c6dc8 --- /dev/null +++ b/src/ad9779.c @@ -0,0 +1,179 @@ +/* + * ad9779.c + * + * Created on: 2019-7-16 + * Author: Administrator + */ +#include +#include +#include +#include +#include + +#include "gspi.h" +#include "axi1.h" +#include "ad9779.h" +static gspi_t _ad9779 = NULL; +static int __gspi_ad9779_init(void ) +{ + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_1, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + _ad9779 = gspi_open(GSPI_CS_AD9779, &format); + return 0; +} + +uint8_t ad9779_read(uint8_t reg) +{ + uint8_t buf[2]; + int elms; + + if (!_ad9779) + __gspi_ad9779_init(); + + buf[0] = 0x80 | reg; + elms = gspi_xfer(_ad9779, buf, 0, 1, &buf[1], 1, 1); + if (elms != sizeof buf) { + printf("error: ad9779 reg 0x%x read fail\r\n", reg); + return 0; + } + + printf("ad9779 read reg:0x%04x, val: 0x%x\r\n", reg, buf[1]); + return buf[1]; +} + +int ad9779_write(uint8_t reg, uint8_t val) +{ + uint8_t buf[2]; + int elms; + + if (!_ad9779) + __gspi_ad9779_init(); + + buf[0] = 0x7f & reg; + buf[1] = val; + elms = gspi_xfer(_ad9779, buf, 0, 2, NULL, 0, 0); + if (elms != sizeof buf) { + printf("error: ad9779 reg 0x%x write fail\r\n", reg); + return -1; + } + + printf("ad9779 write reg:0x%04x, val: 0x%x\r\n", reg, val); + return 0; +} + +int ad9779_setup(uint32_t fclk, uint32_t f0) +{ + uint32_t val; + uint8_t f1, div; + uint8_t log[16 + 1] = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4}; + + /* refclk = 125Mhz + * VCO = refclk * N1 * N2; - 2G + * DACCLK = VCO / N1; - 1G + * DATACLK = DACCLK / F1; - 125M + * fclk = DATACLK / DATACLKDIV + * + * N1 = 2, N2 = 8, F1 = 8 + * */ + + /* enable pll, set n1, n2; + * reg09: + * [7] enable pll + * [6~5] VCO = DACCLK * N1; 00-1, 01-2, 10-4, 11-8, + * [4~3] DACCLK = REFCLK * N2; 00-2, 01-4, 10-8, 11-16 + * [2~0] 011 - The best performance + * */ + val = (0x1 << 7) | (log[2] << 5) | ((log[8] - 1) << 3) | 0x3; + ad9779_write(0x09, val); + + + /* pll rate rang select (1956 ~ 2008) + * reg08: + * [7~2] 111101: 61 rate range + * [1~0] 11: The best performance + * */ + ad9779_write(0x08, 0xf7); + + /* wait pll lock + * reg00: + * [2] 0-unlock, 1-lock + * */ + tsc_delay(10); + if ((0x2 & ad9779_read(0x00))) + printf("AD9779 PLL is Locked \r\n"); + else + printf("AD9779: PLL is NOT locked!\r\n"); + + if (fclk < 125000000) { + f1 = 8; + div = 125000000 / fclk; + } else { + f1 = 1000000000 / fclk; + div = 1; + } + + /* set f1 + * reg01: + * [7:6] 00-1x, 01-2x, 10-4x, 11-8x + * */ + val = ad9779_read(0x01); + val &= 0x3F; + val |= (log[f1] & 0x3) << 6; + ad9779_write(0x01, val); + + /* set DATACLK + * reg03 + * [5:4]: 00-1div, 01-2div, 10-4div, 11-1div + * **/ + val = ad9779_read(0x03); + val &= 0xCF; + val |= (log[div] & 0x3) << 4; + ad9779_write(0x03, val); + + /* + * same cfg ad9779 by axi bus + * */ + //ad9779 sync + axi1_write(0x1004, 0x1); + axi1_write(0x1004, 0x0); + + //ad9779 tx enable + axi1_write(0x1008, 0x1); + + //FCW val + val = f0 / (fclk / 65536) ; + axi1_write(0x100C, val); + + return 0; +} + +static int do_ad9779(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t reg, val; + + if (argc == 3 && argv[1][0] == 'r') { + reg = strtoul(argv[2], NULL, 0); + ad9779_read(reg & 0xff); + } else if (argc == 4 && argv[1][0] == 'w') { + reg = strtoul(argv[2], NULL, 0); + val = strtoul(argv[3], NULL, 0); + ad9779_write(reg & 0xff, val & 0xff); + } else if (argc == 4 && argv[1][0] == 's') { + uint32_t fclk, f0; + fclk = atoi(argv[2]); + f0 = atoi(argv[3]); + ad9779_setup(fclk, f0); + } else { + printf("usge: %s [r/w/s] [reg/fclk] [val/f0] \r\n", argv[0]); + } + + return 0; +} + +CON_CMD(ad9779, "ad9779 reg opts", NULL, do_ad9779) diff --git a/src/ad9779.h b/src/ad9779.h new file mode 100644 index 0000000..6235b47 --- /dev/null +++ b/src/ad9779.h @@ -0,0 +1,20 @@ +/* + * ad9779.h + * + * Created on: 2019-7-16 + * Author: Administrator + */ + +#ifndef AD9779_H_ +#define AD9779_H_ + +/* + * 6678 | <--- spi cs1 ---> | K7A | <---> | K7B | <--- spi --> ad9779 + * <- gpio12~15 cs6 ->| + * + * */ +extern uint8_t ad9779_read(uint8_t reg); +extern int ad9779_write(uint8_t reg, uint8_t val); +extern int ad9779_setup(uint32_t fclk, uint32_t f0); + +#endif /* AD9779_H_ */ diff --git a/src/axi0.c b/src/axi0.c new file mode 100644 index 0000000..177cb0b --- /dev/null +++ b/src/axi0.c @@ -0,0 +1,89 @@ +/* + * axi0.c + * + * Created on: 2019-7-17 + * Author: Administrator + */ +#include +#include +#include + +#include +#include "gspi.h" +#include "axi0.h" + +static gspi_t _axi0 = NULL; +int axi0_init(void ) +{ + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_1, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + _axi0 = gspi_open(GSPI_CS_AXI0, &format); + return 0; +} + +uint32_t axi0_read(uint32_t reg) +{ + uint8_t w[3]; + uint8_t r[4]; //M byte first + uint32_t val; + + if (!_axi0) + axi0_init(); + + w[0] = 0x80 | ((reg >> 16) & 0xff); //bit[15]: 1-read, 0-write + w[1] = (reg >> 8)& 0xff; + w[2] = reg & 0xff; + + gspi_xfer(_axi0, w, 0, 3, r, 3, 4); + val = ((r[0] << 24) | (r[1] << 16) | (r[2] << 8) | r[3]); + + return val; +} + +int axi0_write(uint32_t reg, uint32_t val) +{ + uint8_t d[7]; + + if (!_axi0) + axi0_init(); + + d[0] = 0x7F & (reg >> 16); + d[1] = (reg >> 8) & 0xff; + d[2] = reg & 0xff; + d[3] = val >> 24 & 0xff; //M byte first + d[4] = val >> 16 & 0xff; + d[5] = val >> 8 & 0xff; + d[6] = val & 0xff; + + gspi_xfer(_axi0, d, 0, 7, NULL, 0, 0); + + return 0; +} + +static int do_axi0(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t reg, val; + + if (argc == 3 && argv[1][0] == 'r') { + reg = strtoul(argv[2], NULL, 0); + val = axi0_read(reg); + printf("axi0 read reg:0x%04x, val: 0x%x\r\n", reg, val); + } else if (argc == 4 && argv[1][0] == 'w') { + reg = strtoul(argv[2], NULL, 0); + val = strtoul(argv[3], NULL, 0); + axi0_write(reg, val); + printf("axi0 write reg:0x%04x, val: 0x%x\r\n", reg, val); + } else { + printf("usge: %s [r/w] [reg] [val] \r\n", argv[0]); + } + + return 0; +} + +CON_CMD(axi0, "axi0 reg opts", NULL, do_axi0) diff --git a/src/axi0.h b/src/axi0.h new file mode 100644 index 0000000..c662162 --- /dev/null +++ b/src/axi0.h @@ -0,0 +1,24 @@ +/* + * axi0.h + * + * Created on: 2019-7-17 + * Author: Administrator + */ + +#ifndef AXI0_H_ +#define AXI0_H_ + +/* + * 6678 | <---- spi cs1 ---->| K7A internal axi + * <- gpio12~15 cs1 ->| + * axi0 mem mmap + * 0x0000 ~ 0x0fff: ad9680 config space + * 0x1000 ~ 0x1fff: I/O space + * 0x2000 ~ 0x3fff: serv + * 0x4000 ~ 0x7fff: 16K mem space + * */ + +extern uint32_t axi0_read(uint32_t reg); +extern int axi0_write(uint32_t reg, uint32_t val); + +#endif /* AXI0_H_ */ diff --git a/src/axi1.c b/src/axi1.c new file mode 100644 index 0000000..20245fa --- /dev/null +++ b/src/axi1.c @@ -0,0 +1,86 @@ +/* + * axi1.c + * + * Created on: 2019-7-17 + * Author: Administrator + */ +#include +#include +#include + +#include +#include "gspi.h" +#include "axi1.h" + +static gspi_t _axi1 = NULL; +static int __gspi_axi1_init(void ) +{ + spi_dev_cfg_s format = { + .mode = SPI_CLK_MODE_1, + .speed = 5000000, + .bits = 8, + .endian = 0, + .t2delay = 0, + }; + + _axi1 = gspi_open(GSPI_CS_AXI1, &format); + return 0; +} + +uint32_t axi1_read(uint32_t reg) +{ + uint8_t w[3]; + uint8_t r[4]; //M byte first + uint32_t val; + + if (!_axi1) + __gspi_axi1_init(); + + w[0] = 0x80 | ((reg >> 16) & 0xff); //bit[15]: 1-read, 0-write + w[1] = (reg >> 8) & 0xff; + w[2] = reg & 0xff; + gspi_xfer(_axi1, w, 0, 3, r, 3, 4); + val = ((r[0] << 24) | (r[1] << 16) | (r[2] << 8) | r[3]); + return val; +} + +int axi1_write(uint32_t reg, uint32_t val) +{ + uint8_t d[7]; + + if (!_axi1) + __gspi_axi1_init(); + + d[0] = 0x7F & (reg >> 16); + d[1] = (reg >> 8) & 0xff; + d[2] = reg & 0xff; + d[3] = val >> 24 & 0xff; //M byte first + d[4] = val >> 16 & 0xff; + d[5] = val >> 8 & 0xff; + d[6] = val & 0xff; + + gspi_xfer(_axi1, d, 0, 7, NULL, 0, 0); + return 0; +} + +static int do_axi1(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t reg, val; + + if (argc == 3 && argv[1][0] == 'r') { + reg = strtoul(argv[2], NULL, 0); + val = axi1_read(reg & 0xffff); + printf("axi1 read reg: 0x%x, val: 0x%x \r\n", reg, val); + } else if (argc == 4 && argv[1][0] == 'w') { + reg = strtoul(argv[2], NULL, 0); + val = strtoul(argv[3], NULL, 0); + axi1_write(reg & 0xffff, val); + printf("axi1 write reg: 0x%x, val: 0x%x \r\n", reg, val); + } else { + printf("usge: %s [r/w] [reg] [val] \r\n", argv[0]); + } + + return 0; +} + +CON_CMD(axi1, "axi1 reg opts", NULL, do_axi1) diff --git a/src/axi1.h b/src/axi1.h new file mode 100644 index 0000000..389b1e7 --- /dev/null +++ b/src/axi1.h @@ -0,0 +1,25 @@ +/* + * axi1.h + * + * Created on: 2019-7-17 + * Author: Administrator + */ + +#ifndef AXI1_H_ +#define AXI1_H_ + +/* + * 6678 | <---- spi cs1 ---->| K7A| <--> |K7B internal axi + * <- gpio12~15 cs8 ->| + * axi0 mem mmap + * 0x0000 ~ 0x0fff: ad9361 config space + * 0x1000 [0] ad9779 reset pin + * 0x1004 [0] ad9779 sync pin + * 0x1008 [0] ad9779 tx enable pin + * 0x100C [0] ad9779 FCW val + * */ + +extern uint32_t axi1_read(uint32_t reg); +extern int axi1_write(uint32_t reg, uint32_t val); + +#endif /* AXI1_H_ */ diff --git a/src/emif_test.c b/src/emif_test.c new file mode 100644 index 0000000..86ccb5d --- /dev/null +++ b/src/emif_test.c @@ -0,0 +1,107 @@ +/* + * emif_cfg.c + * + * Created on: 2019-6-3 + * Author: Administrator + */ +#include +#include +#include +#include +#include + +#include "emif_test.h" + +#define NAND_CS0 (0) +#define NAND_CS1 (1) +#define SP3_CS (3) + +int emif_init(void ) +{ + unsigned int bitmode = 0; + + bitmode = (NAND_MODE << NAND_CS0) | (NAND_MODE << NAND_CS1) | (NOR_ASRAM_MODE << SP3_CS); + + emif16_init_ex(bitmode); + + return 0; +} + +static int do_sp3r(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t bass; + unsigned int reg; + uint32_t addr; + uint16_t val; + + if (argc >= 2) { + reg = strtoul(argv[1], NULL, 0); + bass = emif16_chip_base(SP3_CS); + addr = bass + (reg << 1); + val = *(volatile uint16_t *)(addr); + printf("r reg: 0x%x, val: 0x%x, [0x%08x] \r\n", reg, val, addr); + } + return (0); +} + +CON_CMD(sp3r, "read sp3 reg", NULL, do_sp3r) + +static int do_sp3w(cmd_tbl_t tbl, int argc, char *argv[]) +{ + uint32_t bass; + unsigned int reg, val; + uint32_t addr; + + if (argc >= 3) { + bass = emif16_chip_base(SP3_CS); + reg = strtoul(argv[1], NULL, 0); + val = strtoul(argv[2], NULL, 0); + addr = bass + (reg << 1); + + *(volatile uint16_t *)(addr) = val & 0xffff; + printf("w reg: 0x%x, val: 0x%x, [0x%08x] \r\n", reg, val, addr); + } + return 0; +} + +CON_CMD(sp3w, "write sp3 reg", NULL, do_sp3w) + +static int do_er(cmd_tbl_t tbl, int argc, char *argv[]) +{ + int cs; + unsigned int reg, regbase, val; + + if (argc >= 3) { + cs = atoi(argv[1]); + if (cs >= 0 && cs <= 3) { + reg = strtoul(argv[2], NULL, 0); + regbase = emif16_chip_base(cs); + val = *(volatile uint16_t *)(regbase + reg); + printf("r [%08x] %02x: %04x\r\n", (regbase + reg), reg, val); + } + } + return 0; +} + +CON_CMD(er, "read reg from cs x", NULL, do_er) + +static int do_ew(cmd_tbl_t tbl, int argc, char *argv[]) +{ + int cs; + unsigned int reg, regbase, val; + + if (argc >= 4) { + cs = atoi(argv[1]); + if (cs >= 0 && cs <= 3) { + reg = strtoul(argv[2], NULL, 0); + val = strtoul(argv[3], NULL, 0); + regbase = emif16_chip_base(cs); + *(volatile uint16_t *)(regbase + reg) = val; + printf("w [%08x] %02x: %04x\r\n", (regbase + reg), reg, val); + } + } + return 0; +} + +CON_CMD(ew, "write reg to cs x", NULL, do_ew) + diff --git a/src/emif_test.h b/src/emif_test.h new file mode 100644 index 0000000..239c2eb --- /dev/null +++ b/src/emif_test.h @@ -0,0 +1,20 @@ +/* + * emif_cfg.h + * + * Created on: 2019-6-3 + * Author: Administrator + */ + +#ifndef EMIF_CFG_H_ +#define EMIF_CFG_H_ + +/* + * EMIF + * CS0 --> NAND + * CS1 --> XC3S400AN + * CS2 --> XC3S400AN + * CS3 --> XC3S400AN + * */ +int emif_init(void ); + +#endif /* EMIF_CFG_H_ */ diff --git a/src/gspi.c b/src/gspi.c new file mode 100644 index 0000000..99de800 --- /dev/null +++ b/src/gspi.c @@ -0,0 +1,117 @@ +/* + * gspi.c + * + * Created on: 2019-7-11 + * Author: Administrator + */ +#include +#include + +#include +#include + +#include "vsdef.h" +#include "gspi.h" + +struct gspi_controller; +struct gspi { + int cs; + spi_dev_cfg_s format; + struct gspi_controller *gc; +}; + +struct gspi_controller { + spi_dev_t sd; + int spi_cs; + int gpio_num; + int gpio_id[GPIO_MAX_COUNT]; + int cs_count; + struct gspi gs[GSPI_MAX_CS_COUNT]; + gspi_t last_gs; +}; + +static struct gspi_controller _gc; + +int gspi_init(int spi_cs, int gpio_num, int gpio_id[]) +{ + int i, gs_count = 1; + struct gspi_controller *gc = &_gc; + + spi_dev_cfg_s _def = { + SPI_CLK_MODE_0, + 25000000, + 8, + 0, + 0 + }; + + memset(gc, 0, sizeof *gc); + gc->sd = spi_dev_open(0, spi_cs, &_def); + gc->spi_cs = spi_cs; + gc->gpio_num = gpio_num; + + //init gpio setout + for (i = 0; i < gpio_num; ++i) { + gpio_init(gpio_id[i], GPIOF_OUT); + gc->gpio_id[i] = gpio_id[i]; + gs_count *= 2; + } + + //init busy gpio + gpio_init(K7_SPI_BUSY_GPIO, GPIOF_IN); + + gc->cs_count = gs_count; + gc->last_gs = NULL; + return 0; +} + +gspi_t gspi_open(int cs, spi_dev_cfg_s *format) +{ + struct gspi_controller *gc = &_gc; + if (cs < gc->cs_count) { + gspi_t gs = &gc->gs[cs]; + gs->cs = cs; + memcpy(&gs->format, format, sizeof *format); + gs->gc = gc; + return gs; + } + + return NULL; +} + +int gspi_xfer(gspi_t gs, const void *tx, uint32_t first_tx_elms, uint32_t tx_elms, void *rx, uint32_t first_rx_elms, uint32_t rx_elms) +{ + int i, ret = 0, timeout = 0; + struct gspi_controller *gc = gs->gc; + + //gspi cs + for (i = 0; i < gc->gpio_num; ++i) + gpio_set_value(gc->gpio_id[i], (gs->cs & 0x1 << i) ? 1 : 0); + + //when opt ad9361 and axi1, must check gpio busy + if (gs->cs == GSPI_CS_AD9361 || gs->cs == GSPI_CS_AXI1) { + while((gpio_get_value(K7_SPI_BUSY_GPIO) & 0x1) == 0) { + if (timeout++ > 20) {//20ms timeout + printf("warning: gspi opt ad9361 or axi1, bus always busy \r\n"); + goto _exit; + } + tsc_delay(1000); + } + } + + /*advance.*/ + if (gc->last_gs != gs) { + //cfg formate + spi_dev_set_cfg(gc->sd, &gs->format); + + gc->last_gs = gs; + } + + ret = spi_dev_xfer(gc->sd, tx, first_tx_elms, tx_elms, rx, first_rx_elms, rx_elms); + +_exit: + //gspi cs + for (i = 0; i < gc->gpio_num; ++i) + gpio_set_value(gc->gpio_id[i], 0); + return ret; +} diff --git a/src/gspi.h b/src/gspi.h new file mode 100644 index 0000000..a01554f --- /dev/null +++ b/src/gspi.h @@ -0,0 +1,39 @@ +/* + * gspi.h + * + * Created on: 2019-7-11 + * Author: Administrator + */ + +#ifndef GSPI_H_ +#define GSPI_H_ + +#include + +/* + * spi cs and n gpio ext to more + * */ + +#define GPIO_MAX_COUNT 4 +#define GSPI_MAX_CS_COUNT 16 // 2 ^ 4; + +int gspi_init(int spi_cs, int gpio_num, int gpio_id[]); + +enum { + GSPI_CS_AD9680 = 1, + GSPI_CS_AXI0, + GSPI_CS_LVCOMS5, + GSPI_CS_LVCOMS6, + GSPI_CS_LVCOMS7, + GSPI_CS_LVCOMS8, + GSPI_CS_AD9779, + GSPI_CS_AD9361, + GSPI_CS_AXI1, +}; + +typedef struct gspi *gspi_t; +gspi_t gspi_open(int cs, spi_dev_cfg_s *format); +int gspi_xfer(gspi_t gs, const void *tx, uint32_t first_tx_elms, uint32_t tx_elms, + void *rx, uint32_t first_rx_elms, uint32_t rx_elms); + +#endif /* GSPI_H_ */ diff --git a/src/k7_download.c b/src/k7_download.c new file mode 100644 index 0000000..417b1a6 --- /dev/null +++ b/src/k7_download.c @@ -0,0 +1,230 @@ +/* + * k7_download.c + * + * Created on: 2019-6-10 + * Author: Administrator + */ +#include +#include + +#include +#include + +#include + +#include "vsdef.h" + +/* + * 6678 | -- emif cs3 -- | SP3 | -- SeletMAP cs0 -- | K7 A | + * cs1 -- | K7 B | + * + * */ + +#define EMIF_SP3_CS (3) + +#define K7_A_FPGA_M 0x5 +#define K7_B_FPGA_M 0x6 + +#define K7_A_PROG 0x7 +#define K7_B_PROG 0x8 + +#define K7_A_CS 0x9 +#define K7_B_CS 0xA + +#define K7_A_CLK 0xB +#define K7_B_CLK 0xC + +#define K7_WR 0xd +#define K7_DATA 0xe + +#define K7_A_DONE 0xF +#define K7_B_DONE 0x10 + +#define K7_A_INIT 0x11 +#define K7_B_INIT 0x12 + +#define SWAP_BYTES 1 + +struct k7_reg { + uint32_t m; + uint32_t prog; + uint32_t cs; + uint32_t clk; + uint32_t wr; + uint32_t data; + uint32_t done; + uint32_t init; +}; + +static struct k7_reg _k7[2] = { + {K7_A_FPGA_M, K7_A_PROG, K7_A_CS, K7_A_CLK, K7_WR, K7_DATA, K7_A_DONE, K7_A_INIT}, + {K7_B_FPGA_M, K7_B_PROG, K7_B_CS, K7_B_CLK, K7_WR, K7_DATA, K7_B_DONE, K7_B_INIT} +}; + +#define mdelay(x) tsc_delay((x) * 1000) +#define udelay(x) tsc_delay(x) + +static uint8_t swap_uint8(uint8_t data){ + uint8_t swapped; + + if (SWAP_BYTES == 1){ + swapped =( data << 4 )|( data >> 4 ); + swapped =((swapped << 2) & 0xcc) | ((swapped >> 2) & 0x33); + swapped =((swapped << 1) & 0xaa) | ((swapped >> 1) & 0x55); + } + else + swapped = data; + + return swapped; +} +static int sp3_reg_write(uint32_t reg, uint16_t val) +{ + uint32_t addr; + addr = emif16_chip_base(EMIF_CS_SP3) + (reg << 1); + *(volatile uint16_t *)addr = val & 0xffff; + //printf("w reg: 0x%x, val: 0x%x, [0x%08x] \r\n", reg, val, addr); + return 0; +} + +static uint16_t sp3_reg_read(uint32_t reg) +{ + uint32_t addr; + addr = emif16_chip_base(EMIF_CS_SP3) + (reg << 1); + return *(volatile uint16_t *)addr & 0xffff; +} + +static void shift_cclk(struct k7_reg *k7, uint32_t count) +{ + uint32_t i; + sp3_reg_write(k7->clk, 0); + for (i = 0; i < count; ++i) { + sp3_reg_write(k7->clk, 1); + sp3_reg_write(k7->clk, 0); + } +} + +static void shift_word_out(struct k7_reg *k7, uint8_t data8) +{ + sp3_reg_write(k7->data, data8 & 0xFF); + shift_cclk(k7, 1); +} + +int k7_flashboot(int k7_id) +{ + uint32_t i = 0; + + struct k7_reg *k7 = &_k7[k7_id]; //d36 + + /*M(2:0) set boot mode*/ + if (k7_id == 0) + sp3_reg_write(k7->m, 0x2); //Master BPI mode for d11 + else + sp3_reg_write(k7->m, 0x1); //Master SPI mode for d36 + mdelay(2); + + sp3_reg_write(k7->prog, 1); + sp3_reg_write(k7->cs, 1); + + /*Configuration Reset*/ + sp3_reg_write(k7->prog, 0); + mdelay(2); + sp3_reg_write(k7->prog, 1); + + + /* wait for DONE to assert */ + i = 0; + while(sp3_reg_read(k7->done) == 0) { + mdelay(100); + if (i++ > 200) { + printf("err: done has not gone high \r\n"); + return -3; + } + } + + printf("K7 %c BOOT Success \r\n", (k7_id == 0) ? 'A' : 'B'); + + return 0; +} + +int k7_download(int k7_id, uint32_t mem_addr, uint32_t len) +{ + uint32_t i = 0; + volatile uint8_t *data = (uint8_t *)mem_addr; + struct k7_reg *k7 = &_k7[k7_id]; + + /*M(2:0) set 110 for SelectMap mode*/ + sp3_reg_write(k7->m, 0x6); + mdelay(2); + + sp3_reg_write(k7->clk, 0); + + /*Bring cs, wr low and prog high*/ + sp3_reg_write(k7->prog, 1); + sp3_reg_write(k7->wr, 0); + sp3_reg_write(k7->cs, 0); + mdelay(2); + + /*Configuration Reset*/ + sp3_reg_write(k7->prog, 0); + mdelay(2); + sp3_reg_write(k7->prog, 1); + + /*wait for device initialization*/ + while(sp3_reg_read(k7->init) == 0) { + if (++i > 200) { + printf("err: init has not gone high \r\n"); + return -1; + } + mdelay(1); + } + + /*configuration load*/ + for (i = 0; i < len; ++i) { + shift_word_out(k7, swap_uint8(*(data + i))); + //udelay(2); + } + + /*check init must be high*/ + if (sp3_reg_read(k7->init) == 0) { + printf("err: init has be low \r\n"); + return -2; + } + + /* wait for DONE to assert */ + i = 0; + while(sp3_reg_read(k7->done) == 0) { + shift_cclk(k7, 1); + + if (i++ > 0x10000) { + printf("err: done has not gone high \r\n"); + return -3; + } + } + + /*compenstate for special startup conditions. */ + shift_cclk(k7, 8); + printf("K7 %c BIN Download Success \r\n", (k7_id == 0) ? 'A' : 'B'); + + /*M(2:0) set 5 JTAG mode */ + sp3_reg_write(k7->m, 5); + sp3_reg_write(k7->cs, 1); + + return 0; +} + +int do_k7(cmd_tbl_t tbl, int argc, char *argv[]) +{ + int k7_id = 0; + uint32_t mem_addr; + uint32_t len; + if (argc >= 4) { + k7_id = atoi(argv[1]); + mem_addr = strtoul(argv[2], NULL, 0); + len = strtoul(argv[3], NULL, 0); + printf("k7 %d mem: 0x%x, len: %d start upgrade\r\n", k7_id, mem_addr, len); + k7_download(k7_id, mem_addr, len); + } + return 0; +} + +CON_CMD(k7, "k7 bin upgrade", NULL, do_k7) diff --git a/src/k7_download.h b/src/k7_download.h new file mode 100644 index 0000000..a51fa32 --- /dev/null +++ b/src/k7_download.h @@ -0,0 +1,20 @@ +/* + * k7_down.h + * + * Created on: 2019-6-10 + * Author: Administrator + */ + +#ifndef K7_DOWNLOAD_H_ +#define K7_DOWNLOAD_H_ + +/* + * 1. dsp --> emif cs1 --> fpga --> k7 A/B + * */ + +int k7_download(int k7_id, uint32_t mem_addr, uint32_t len); + +/*cfg k7 from spi/pbi boot*/ +int k7_flashboot(int k7_id); + +#endif /* K7_DOWN_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7cd8955 --- /dev/null +++ b/src/main.c @@ -0,0 +1,271 @@ +/* + * main.c + * + * Created on: 2019-5-29 + * Author: Administrator + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vsdef.h" + +#include "norflash.h" +#include "nandflash.h" +#include "ad9680.h" + +#include "gspi.h" +#include "k7_download.h" +#include "srio.h" +#include "runtime.h" + +#define mdelay(x) tsc_delay((x) * 1000) + +static vsem_t _vsem = NULL; +static timer_t _tm = NULL; +static uart_t _u; + +extern int net_server(SOCKET s, UINT32 unused); + +static void __trigger_timer(void *param) +{ + k7_trigger(); //1-first: trigger k7 start sample. +} + +static void __k7_gpio_intr(void *arg) +{ + if (_vsem) + vsem_signal(_vsem); //2-second: k7 data already sample complete. +} + +static void __sample_task(void ) +{ + int ret; + printf("sample task running ... \r\n"); + while(1) { + ret = vsem_wait(_vsem, 100); + if (ret == 0) { + vtask_sleep(10); + continue; + } else if (ret < 0) { //err + printf("sample task wait sem fail \r\n"); + break; + } else { + k7_sample(); //3-second: dsp recv sample data by spi + } + } +} + +static int __uart_puts(const char *s, int len) +{ + static char last_ch = 0x00; + int i; + + if (len < 0) + len = strlen(s); + + for (i = 0; i < len; ++i) { + if(!(s[i] == ' ' && last_ch == '\r')) + uart_write(_u, s[i]); + last_ch = s[i]; + } + + return len; +} + +static int __uart_gets(char *buf, int len, int blocking /*in millis*/) +{ + static uint8_t _buf[1024]; + static uint32_t _n = 0; + + if (blocking <= 0) { + while(_n < sizeof _buf) { + if (uart_is_ready(_u)) + _buf[_n++] = uart_read(_u); + else + break; + } + } else { + while(blocking > 0 && _n < sizeof _buf) { + if (uart_is_ready(_u)) { + _buf[_n++] = uart_read(_u); + } else { + if (_n >= len) { + break; + } else { + mdelay(1); + blocking--; + } + } + } + } + + if (len > _n) + len = _n; + + memcpy(buf, _buf, len); + _n -=len; + + if (_n > 0) + memmove(_buf, &_buf[len], _n); + + return len; +} + +static void __usr_drv_init(void ) +{ + //systme tsc init. + tsc_init(); + + // uart init + do { + _u = uart_open(0); + uart_set_baudrate(_u, 115200); + uart_set_databits(_u, 8); + uart_set_stopbits(_u, 1); + uart_set_parity(_u, 0); + } while(0); + + //emif bus init. + do { + unsigned int bitmode = 0; + bitmode = (NAND_MODE << EMIF_CS_NAND0) | (NAND_MODE << EMIF_CS_NAND1) | (NOR_ASRAM_MODE << EMIF_CS_SP3); + emif16_init_ex(bitmode); + } while(0); + + //spi bus init + spi_bus_init(0, SPI_PIN_MODE_4); + + //gpio + spi + do { + int gpio_id[] = { + K7_SPI_CS_GPIO_0, + K7_SPI_CS_GPIO_1, + K7_SPI_CS_GPIO_2, + K7_SPI_CS_GPIO_3 + }; + gspi_init(SPI_CS_K7, sizeof gpio_id / sizeof gpio_id[0], gpio_id); + } while(0); + + //gpio intr by k7 use + do { + //cfg gpio input mode and enable rise edge intr. + gpio_init(K7_INTR_GPIO, GPIOF_IN); + gpio_bank_interrupt(0, 1); + gpio_set_rising_edge_interrupt(K7_INTR_GPIO, 1); + + //register intr handle func + intr_install_mode2(CSL_GEM_GPINT11, NULL, __k7_gpio_intr); + } while(0); + + //resource init + resource_mgr_init(); +} + +static void __usr_app_prev_func(void ) +{ + //nor + nor_init(); + + //nand + nand_init(); + + //runtime + runtime_init(); +} + +static void __app_run(void ) +{ + //startup k7 (D11) and (D36) code + if (1) { //from tftp + uint32_t size; + size = vtftp_load_file("192.168.0.18", "ppb_d11.bit", 0xC0000000); + k7_download(0, 0xC0000000, size); + mdelay(5); + + size = vtftp_load_file("192.168.0.18", "ppb_d36.bit", 0xC0000000); + k7_download(1, 0xC0000000, size); + } else { //from D11 BPI / D36 SPI + k7_flashboot(0); + mdelay(5); + k7_flashboot(1); + } + mdelay(20); + + //startup SRIO + srio_init(); + + //startup AD9680 + ad9680_setup(1000); + + //start a sample task from ad9680 + _vsem = vsem_new(); + vtask_create(__sample_task); + + //create a server task for pc read sample data + DaemonNew( SOCK_DGRAM, 0, DEFAULT_PORT, net_server, OS_TASKPRINORM, OS_TASKSTKNORM, 0, 1); + + //start a timer and pre 1s trigger once + _tm = timer_new(250, NULL, __trigger_timer); + timer_active(_tm); + + while(1) { + vbios_loop(); + vtask_sleep(5); + } +} + +void main (int argc, char *argv[]) +{ + struct vbios_param vp; + struct vbios_term_param term_param = {__uart_puts, __uart_gets}; + struct vbios_net_param net_param = {1, "192.168.0.123", "255.255.255.0", "192.168.0.1", "mydemo"}; + + vp.tp = &term_param; + vp.np = &net_param; + vp.usrDrvInitFunc = __usr_drv_init; + vp.usrAppPrevFunc = __usr_app_prev_func; + vp.usrAppFunc = __app_run; + + vbios_init(&vp); + vbios_start(); +} + +int enter_pragrom_mode(void ) +{ + if (_tm) + timer_deactive(_tm); + + intr_disable(); + return 0; +} + +int enter_run_mode(void ) +{ + if (_tm) + timer_active(_tm); + + intr_enable(); + return 0; +} diff --git a/src/nandflash.c b/src/nandflash.c new file mode 100644 index 0000000..12efdd1 --- /dev/null +++ b/src/nandflash.c @@ -0,0 +1,100 @@ +/* + * nandflash.c + * + * Created on: 2019-7-8 + * Author: Administrator + */ +#include +#include +#include +#include + +#include + +#include + +#define NAND_TARGET0_CS 0 //emif cs0 --> nand target 0 +#define NAND_TARGET1_CS 1 //emif cs1 --> nand target 1 + +static nand_flash_t g_nand; +int nand_init(void ) +{ + g_nand = nand_flash_open(NAND_TARGET0_CS, 0); + printf("nand manu id: 0x%x, dev id: %x \r\n", g_nand->manu_id, g_nand->dev_id); + printf("nand size: %d MB (%d block * %d pages * %d Byte) \r\n", \ + (g_nand->block_count * (g_nand->page_count_per_block * g_nand->data_bytes_per_page / 1024)) / 1024, + g_nand->block_count, g_nand->page_count_per_block, g_nand->data_bytes_per_page); + return 0; +} + +int nand_program(unsigned int mem_bass, unsigned int nand_addr, unsigned int size) +{ + return 0; +} + +int nand_test(unsigned int block, unsigned int num) +{ + int err = 0; + unsigned int i, page; + uint8_t w_page[8192 + 744], r_page[8192 + 744]; + + while(num-- > 0) { + nand_flash_earse_block(g_nand, block); + for (page = 0; page < g_nand->page_count_per_block; ++page) { + for (i = 0; i < g_nand->data_bytes_per_page; ++i) { + w_page[i] = (page + i); + r_page[i] = 0; + } + + nand_flash_write_page(g_nand, block, page, w_page); + + nand_flash_read_page(g_nand, block, page, r_page); + + for (i = 0; i < g_nand->data_bytes_per_page; ++i) { + if (r_page[i] != w_page[i]) { + err++; + printf("error: data invaild block: %d, page: %d, offset: %d \r\n", block, page, i); + break; + } + } + } + + printf("nand block%d test: %s \r\n", block++, (err == 0) ? "success" : "fail"); + } + + return 0; +} + +static int do_nand(cmd_tbl_t tbl, int argc, char *argv[]) +{ + if (argc >= 2 && (argv[1][0] == 't')) { + unsigned int block; + unsigned int num; + if (argc != 4) { + printf("usge: %s t [start_block] [count]\r\n", argv[0]); + return -1; + } + block = strtoul(argv[2], NULL, 0); + num = strtoul(argv[3], NULL, 0); + return nand_test(block, num); + } + + if (argc >= 2 && argv[1][0] == 'w') { + unsigned int mem_bass; + unsigned int nand_addr; + unsigned int size; + if (argc != 5) { + printf("usge: %s w [mem_addr] [nand_addr] [size]\r\n", argv[0]); + return -1; + } + mem_bass = strtoul(argv[2], NULL, 0); + nand_addr = strtoul(argv[3], NULL, 0); + size = strtoul(argv[4], NULL, 0); + return nand_program(mem_bass, nand_addr, size); + } + + printf("usge: %s [t/w] ... \r\n", argv[0]); + return 0; +} + +CON_CMD(nand, "nand opts", NULL, do_nand) diff --git a/src/nandflash.h b/src/nandflash.h new file mode 100644 index 0000000..a86015b --- /dev/null +++ b/src/nandflash.h @@ -0,0 +1,15 @@ +/* + * nandflash.h + * + * Created on: 2019-7-8 + * Author: Administrator + */ + +#ifndef NANDFLASH_H_ +#define NANDFLASH_H_ + +extern int nand_init(void ); + +extern int nand_program(unsigned int mem_bass, unsigned int nand_addr, unsigned int size); + +#endif /* NANDFLASH_H_ */ diff --git a/src/norflash.c b/src/norflash.c new file mode 100644 index 0000000..8ab32f3 --- /dev/null +++ b/src/norflash.c @@ -0,0 +1,169 @@ +/* + * nor_test.c + * + * Created on: 2019-6-3 + * Author: Administrator + */ +#include +#include +#include + +#include +#include + +#include +#include "vsdef.h" + +void nor_test(nor_flash_t nf, unsigned int start_offset, unsigned int leng) +{ + uint32_t test_addr; + uint32_t sector_size; + uint8_t src[4096]; + uint8_t dest[4096]; + int i, err = 0; + + if (!nf) { + printf("fail: nor flash don't init \r\n"); + return; + } + + sector_size = nor_flash_sector_size(nf); + nor_flash_erase(nf, start_offset / sector_size, (start_offset + leng)/ sector_size); + for (test_addr = start_offset; test_addr < (start_offset + leng); test_addr += sector_size) { + for (i = 0; i < sizeof src; ++i) { + src[i] = i; + dest[i] = 0; + } + + nor_flash_write(nf, src, test_addr, sizeof src); + + tsc_delay(1000); + + nor_flash_read(nf, dest, test_addr, sizeof dest); + + err = 0; + for (i = 0; i < sizeof dest; ++i) { + if (src[i] != dest[i]) + err++; + } + printf("nor test addr: 0x%x result err = %d \r\n", test_addr, err); + } + printf("nor test complete... \r\n"); +} + +static nor_flash_t _nf; +int nor_init(void ) +{ + uint32_t total_size, sectors; + + _nf = nor_flash_open(NOR_FLASH_MODE_SPI, SPI_CS_NOR, 0); + + total_size = nor_flash_size(_nf); + sectors = nor_flash_sectors(_nf); + printf("nor size: %dMB, sectors: %d \r\n", total_size / 1024 / 1024, sectors); + + return 0; +} + +int nor_program(unsigned int mem_bass, unsigned int nor_addr, unsigned int size) +{ + int ret = 0; + nor_flash_t nf = _nf; + unsigned int total_size, block_count, block_size; + unsigned int block; + unsigned char *data = (unsigned char *)mem_bass; + unsigned char *buf; + unsigned int i, len, leng = 0; + + if (!nf) { + printf("fail: nor flash don't init \r\n"); + return -1; + } + + total_size = nor_flash_size(nf); + block_count = nor_flash_sectors(nf); + block_size = nor_flash_sector_size(nf); + + if (!(buf = malloc(block_size))) { + printf("fail: malloc block buff error \r\n"); + return -1; + } + + if (nor_addr + size > total_size) { + printf("fail: data size more than flash size \r\n"); + return -2; + } + + for (block = nor_addr / block_size; block < block_count; ++block) { + //erase block. + nor_flash_erase(nf, block, block); + + //write data. + len = (size < block_size) ? size : block_size; + nor_flash_write(nf, data, nor_addr, len); + + //verfiy data. + nor_flash_read(nf, buf, nor_addr, len); + for (i = 0; i < len; ++i) { + if (buf[i] != data[i]) { + printf("data verfiy fail: block: %d, i = %d \r\n", block, i); + ret = -1; + goto _exit; + } + } + + data += len; + size -= len; + nor_addr += len; + + leng += len; + printf("write file data to nor: %d \r\n", leng); + if (size <= 0) + break; + } + +_exit: + + return ret; +} + +static int do_nor(cmd_tbl_t tbl, int argc, char *argv[]) +{ + if (argc >= 2) { + if (argv[1][0] == 't') { + unsigned int start_addr; + unsigned int leng; + if (argc != 4) { + printf("usge: %s t [addr] [leng]\r\n", argv[0]); + return -1; + } + start_addr = strtoul(argv[2], NULL, 0); + leng = strtoul(argv[3], NULL, 0); + enter_pragrom_mode(); + nor_test(_nf, start_addr, leng); + enter_run_mode(); + } else if (argv[1][0] == 'w') { + unsigned int mem_bass; + unsigned int nor_addr; + unsigned int size; + if (argc != 5) { + printf("usge: %s w [mem_addr] [nor_addr] [size]\r\n", argv[0]); + return -1; + } + mem_bass = strtoul(argv[2], NULL, 0); + nor_addr = strtoul(argv[3], NULL, 0); + size = strtoul(argv[4], NULL, 0); + enter_pragrom_mode(); + nor_program(mem_bass, nor_addr, size); + enter_run_mode(); + } else { + printf("usge: %s [t/w] ... \r\n", argv[0]); + return -1; + } + + } + + return 0; +} + +CON_CMD(nor, "nor opts", NULL, do_nor) diff --git a/src/norflash.h b/src/norflash.h new file mode 100644 index 0000000..0d2eb79 --- /dev/null +++ b/src/norflash.h @@ -0,0 +1,15 @@ +/* + * nor_test.h + * + * Created on: 2019-6-6 + * Author: Administrator + */ + +#ifndef NOR_TEST_H_ +#define NOR_TEST_H_ + +int nor_init(void ); + +extern int nor_program(unsigned int mem_bass, unsigned int nor_addr, unsigned int size); + +#endif /* NOR_TEST_H_ */ diff --git a/src/runtime.c b/src/runtime.c new file mode 100644 index 0000000..589ea25 --- /dev/null +++ b/src/runtime.c @@ -0,0 +1,192 @@ +/* + * runtime.c + * + * Created on: 2019-7-12 + * Author: Administrator + */ +#include +#include +#include + +#include +#include + +#include + +#include "axi0.h" +#include "runtime.h" + +#define SAMPLE_DATA_LENG (16 * 1024) + +struct runtime { + vlock_t lock; + uint8_t count; + uint8_t w_i; + uint8_t r_i; + uint8_t resv; + uint8_t data_pp[2][SAMPLE_DATA_LENG]; + uint8_t recv_buf[8192]; +}; + +static struct runtime _rt; + +static int dma_data(SOCKET sfd, struct sockaddr_in *from) +{ + uint8_t *data; + uint32_t timeout = 0; + uint32_t buf[256 + 2] = {0}; + int tot = 0, max; + const int hdr_size = 2 * sizeof buf[0]; + + //poll wait sample data recv complete. + while(_rt.count == 0) { + if (timeout++ >= 2000) { + printf("wait sample data time out \r\n"); + break; + } + Task_sleep(1); + } + + vlock_lock(_rt.lock); + _rt.count--; + data = _rt.data_pp[_rt.r_i]; + vlock_unlock(_rt.lock); + + max = sizeof buf - hdr_size; + buf[0] = 'd'; + + while(tot < SAMPLE_DATA_LENG) { + buf[1] = ((SAMPLE_DATA_LENG - tot) > max) ? max : (SAMPLE_DATA_LENG - tot); + memcpy(&buf[2], data + tot, buf[1]); + sendto(sfd, buf, sizeof buf, 0, (PSA)from, sizeof *from); + tot += buf[1]; + } + + buf[1] = 0; + sendto(sfd, buf, hdr_size, 0, (PSA)from, sizeof *from); + + return 0; +} + +static int do_process(SOCKET sfd, void *buf, int len, struct sockaddr_in *from, int *quit) +{ + unsigned int *cmdbuf = (uint32_t *)buf; + unsigned int cmd; + + if (len < sizeof cmdbuf[0]) + return -1; + +/* +* buf define +* word0 | word1 | word2 | word3 +* cmd cs reg val +* +*/ + cmd = cmdbuf[0]; + switch (cmd) { + case 'd': /* read sample data. */ + dma_data(sfd, from); + break; + case 't': /* send trigger to k7, start sample data. */ + k7_trigger(); + break; + default : + break; + } + return 0; +} + +int net_server( SOCKET s, UINT32 unused ) +{ + struct sockaddr_in sin1; + struct timeval to; + int len, tmp; + char *pBuf; + HANDLE hBuffer; + int quit = 0; + + (void)unused; + + // Configure our socket timeout to be 2 seconds + to.tv_sec = 2; + to.tv_usec = 0; + setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, &to, sizeof( to ) ); + setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof( to ) ); + + for(;;) + { + tmp = sizeof( sin1 ); + len = (int)recvncfrom( s, (void **)&pBuf, 0, (PSA)&sin1, &tmp, &hBuffer ); + + // Spit any data back out + if( len >= 0 ) + { + if (len <= sizeof _rt.recv_buf) + memcpy(_rt.recv_buf, pBuf, len); + else { + printf("error: udp packet more than 8k \n"); + break; + } + + do_process(s, _rt.recv_buf, len, &sin1, &quit); + recvncfree( hBuffer ); + } + else + break; + } + + // Since the socket is still open, return "1" + // (we need to leave UDP sockets open) + return (quit == 0) ? 1 : 0; +} + +int runtime_init() +{ + memset(&_rt, 0, sizeof _rt); + _rt.lock = vlock_new(); + return 0; +} + +void k7_trigger(void ) +{ + axi0_write(0x1048, 1); + axi0_write(0x1048, 0); +} + +int k7_sample(void ) +{ + int i; + uint32_t *val = (uint32_t *)_rt.data_pp[_rt.w_i]; + uint16_t addr_bass = 0x4000; + +// printf("AAA %d \r\n", time_millis()); + for (i = 0; i < SAMPLE_DATA_LENG / 4; ++i) { + val[i] = axi0_read(addr_bass); + addr_bass += 4; + } +// printf("BBB %d \r\n", time_millis()); + + vlock_lock(_rt.lock); + if (_rt.count++ == 1) + _rt.count = 1; + _rt.r_i = _rt.w_i; + _rt.w_i = (_rt.w_i == 0) ? 1 : 0; + vlock_unlock(_rt.lock); + + return 0; +} + +static int do_mm(cmd_tbl_t tbl, int argc, char *argv[]) +{ + unsigned int addr, val; + if (argc == 3 && argv[1][0] == 'r') { + addr = strtoul(argv[2], 0, NULL); + val = *(volatile unsigned int *)addr; + printf("0x%08x: 0x%08x \r\n", addr, val); + } else { + printf("usage. %s [r] [addr] \r\n", argv[0]); + } + return 0; +} + +CON_CMD(mm, "mm show opt", NULL, do_mm); diff --git a/src/runtime.h b/src/runtime.h new file mode 100644 index 0000000..88a7e8c --- /dev/null +++ b/src/runtime.h @@ -0,0 +1,15 @@ +/* + * runtime.h + * + * Created on: 2019-7-12 + * Author: Administrator + */ + +#ifndef RUNTIME_H_ +#define RUNTIME_H_ + +extern int runtime_init(); +extern void k7_trigger(void ); +extern int k7_sample(void ); + +#endif /* RUNTIME_H_ */ diff --git a/src/srio.c b/src/srio.c new file mode 100644 index 0000000..a4a1cef --- /dev/null +++ b/src/srio.c @@ -0,0 +1,246 @@ +/* + * srio.c + * + * Created on: 2019-7-25 + * Author: Administrator + */ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "srio.h" +#include "axi0.h" +#include "axi1.h" + +#define SRIO_LOCAL_MEM_SRC 0xC0000000 +#define SRIO_LOCAL_MEM_DEST 0xE0000000 + +#define SRIO_FPGA_MEM_DEST 0x11110000 + +typedef struct sd { + uint16_t node_id; + struct { + uint16_t k7_node; + int srio_port; + vsem_t sem; + }k7[2]; +}sd_s; + +static sd_s srio; +static void __data_init(uint32_t leng, uint32_t times) +{ + int i; + uint8_t bass = times & 0xff; + uint32_t *src = (uint32_t *)SRIO_LOCAL_MEM_SRC; + uint32_t *dest = (uint32_t *)SRIO_LOCAL_MEM_DEST; + for (i = 0; i < leng / 4; ++i) { + src[i] = (bass << 24) | i; + dest[i] = 0xffffffff; + } + Osal_CacheWriteBack(src, leng); + Osal_CacheWriteBack(dest, leng); +} + +void __data_verify(uint32_t leng, uint32_t times) +{ + int i, err = 0; + uint32_t *src = (uint32_t *)SRIO_LOCAL_MEM_SRC; + uint32_t *dest = (uint32_t *)SRIO_LOCAL_MEM_DEST; + + Osal_CacheInvalidate(src, leng); + Osal_CacheInvalidate(dest, leng); + + for (i = 0; i < leng / 4; ++i) { + if (src[i] != dest[i]) { + err++; + printf("0x%08x: 0x%08x (0x%08x)\r\n", (uint32_t)&dest[i], dest[i], src[i]); + } + } + + printf("srio swrite poll test time %d result: %s (err = %d) \r\n", times, (err == 0) ? "success" : "fail", err); +} + +static void __doorbell_cb(int port, void *param) +{ + sd_s *sr = (sd_s *)param; + + if (port == sr->k7[0].srio_port) + vsem_signal(sr->k7[0].sem); + + if (port == sr->k7[1].srio_port) + vsem_signal(sr->k7[1].sem); +} + +int srio_init(void) +{ + memset(&srio, 0, sizeof srio); + srio.node_id = SRIO_NODE_DSP; + srio.k7[0].k7_node = SRIO_NODE_K7A; + srio.k7[0].srio_port = 2; + srio.k7[0].sem = vsem_new(); + srio.k7[1].k7_node = SRIO_NODE_K7B; + srio.k7[1].srio_port = 0; + srio.k7[1].sem = vsem_new(); + + srio_func_init(srio.node_id, &srio, __doorbell_cb); + printf("srio link ok \r\n"); + + //init test data buff. + __data_init(0x100000, 0); + + //axi0 init fgpa srio cfg + axi0_write(0x2014, 0x1); + axi0_write(0x2010, 0xd11); + axi0_write(0x200C, 0xd31); + + axi1_write(0x2014, 0x1); + axi1_write(0x2010, 0xd36); + axi1_write(0x200C, 0xd31); + + //d11 + axi0_write(0x2018, SRIO_LOCAL_MEM_DEST); + axi0_write(0x201C, SRIO_FPGA_MEM_DEST); + axi0_write(0x2020, 256); + + //d36 + axi1_write(0x2018, SRIO_LOCAL_MEM_DEST); + axi1_write(0x201C, SRIO_FPGA_MEM_DEST); + axi1_write(0x2020, 256); + + return 0; +} + +int srio_swrite(int k7_id, uint32_t mem_addr, uint32_t rapid_addr, uint32_t len, int doorbell_enable) +{ + return srio_func_swrite(srio.k7[k7_id].srio_port, srio.k7[k7_id].k7_node, (uint8_t *)mem_addr, (uint8_t *)rapid_addr, len, doorbell_enable); +} + +int srio_nread(int k7_id, uint32_t mem_addr, uint32_t rapid_addr, uint32_t len) +{ + return srio_func_nread(srio.k7[k7_id].srio_port, srio.k7[k7_id].k7_node, (uint8_t *)rapid_addr, (uint8_t *)mem_addr, len); +} + +int srio_doorbell(int k7_id) +{ + return srio_func_doorbell(srio.k7[k7_id].srio_port, srio.k7[k7_id].k7_node); +} + +static int do_srio(cmd_tbl_t tbl, int argc, char *argv[]) +{ + int i, ret, k7_id = 0; + uint32_t leng, times; + + if (argc == 2) { + if (argv[1][0] == 'u') { + printf("cache invalid .. \r\n"); + Osal_CacheInvalidate((void *)SRIO_LOCAL_MEM_DEST, 0x100000); + Osal_Sync(); + } else { + goto _usage; + } + } else if (argc == 3) { + if (argv[1][0] == 'd') { + k7_id = atoi(argv[2]); + printf("srio k7_%c doorbell \r\n", (k7_id == 0 ? 'A' : 'B')); + srio_doorbell(k7_id); + } if (argv[1][0] == 's'){ +/* + k7_id = atoi(argv[2]); + leng = 0x100000; + unsigned int start; + unsigned int MB = 0; + start = time_millis(); + if (k7_id == 0) + axi0_write(0x2020, leng); + else + axi1_write(0x2020, leng); + do { + srio_swrite(k7_id, SRIO_LOCAL_MEM_SRC, SRIO_FPGA_MEM_DEST, leng, 1); + //wait fpga send doorbell + ret = vsem_wait(srio.k7[k7_id].sem, 10000); + if (ret <= 0) { + printf("wait sem fail or timeout!!!! \r\n"); + break; + } + MB += 2; + } while((time_millis() - start) < 1000); + + printf("srio with k7_%c speed test 1s tran %d MByte \r\n", (k7_id == 0) ? 'A' : 'B', MB); +*/ + unsigned int start, end; + unsigned int nlen = 0; + + k7_id = atoi(argv[2]); + leng = 0x10000000; //256M + + __data_init(leng, k7_id); + if (k7_id == 0) + axi0_write(0x2020, leng); + else + axi1_write(0x2020, leng); + + start = time_millis(); + printf("speed test start time %d ms \r\n", start); + while(nlen < leng) { + srio_swrite(k7_id, SRIO_LOCAL_MEM_SRC + nlen, SRIO_FPGA_MEM_DEST + nlen, 0x100000, 0); + nlen += 0x100000; + } + srio_doorbell(k7_id); + vsem_wait(srio.k7[k7_id].sem, 0); + end = time_millis(); + printf("speed test end time %d ms \r\n", end); + + __data_verify(leng, k7_id); + printf("srio with k7_%c tran %d MByte use %d ms\r\n", (k7_id == 0) ? 'A' : 'B', (leng * 2) / 0x100000, (end - start)); + }else { + goto _usage; + } + } else if (argc == 5) { + if (argv[1][0] == 't') { + k7_id = atoi(argv[2]); + leng = strtoul(argv[3], 0, NULL); + times = strtoul(argv[4], 0, NULL); + + if (leng & 0xff != 0) { + printf("warn: leng must algin 256B \r\n"); + leng &= 0xFFFFFF00; + } + printf("srio with k7_%c poll test from fpga leng: 0x%x, times: %d \r\n", (k7_id == 0) ? 'A' : 'B', leng, times); + if (k7_id == 0) + axi0_write(0x2020, leng); + else + axi1_write(0x2020, leng); + for (i = 0; i < times; ++i) { + __data_init(leng, i); + + srio_swrite(k7_id, SRIO_LOCAL_MEM_SRC, SRIO_FPGA_MEM_DEST, leng, 1); + + //wait fpga send doorbell + ret = vsem_wait(srio.k7[k7_id].sem, 10000); + if (ret == 0) { //timeout + printf("srio with k7%d poll test times %d ,wait doorbell timeout !!!!\r\n", k7_id, i); + return 0; + } else if (ret < 0) { //fail + printf("srio with k7%d poll test times %d ,wait sync signal fail !!!!\r\n", k7_id, i); + } else { //suceess + __data_verify(leng, i); + } + } + } + } + + return 0; + +_usage: + printf("usage: %s [k7_id: 0/1] [func: s/n/d] [fpga_addr] [data_len] \r\n", argv[0]); + return 0; +} + +CON_CMD(srio, "srio opts", NULL, do_srio) diff --git a/src/srio.h b/src/srio.h new file mode 100644 index 0000000..f9bd489 --- /dev/null +++ b/src/srio.h @@ -0,0 +1,20 @@ +/* + * srio.h + * + * Created on: 2019-7-25 + * Author: Administrator + */ + +#ifndef SRIO_H_ +#define SRIO_H_ + +#define SRIO_NODE_DSP 0xD31 +#define SRIO_NODE_K7A 0xD11 +#define SRIO_NODE_K7B 0xD36 + +extern int srio_init(void ); +extern int srio_swrite(int k7_id, uint32_t mem_addr, uint32_t rapid_addr, uint32_t len, int doorbell_enable); +extern int srio_nread(int k7_id, uint32_t mem_addr, uint32_t rapid_addr, uint32_t len); +extern int srio_doorbell(int k7_id); + +#endif /* SRIO_H_ */ diff --git a/src/vsdef.h b/src/vsdef.h new file mode 100644 index 0000000..48e4079 --- /dev/null +++ b/src/vsdef.h @@ -0,0 +1,35 @@ +/* + * vsdef.h + * + * Created on: 2019-7-5 + * Author: Administrator + */ + +#ifndef VSDEF_H_ +#define VSDEF_H_ + +/*emif cs define*/ +#define EMIF_CS_NAND0 (0) // emif cs0 --> nand target0 +#define EMIF_CS_NAND1 (1) // emif cs1 --> nand target1 +#define EMIF_CS_SP3 (3) // emif cs3 --> XC3S400AN (fpga) + +/*spi cs define*/ +#define SPI_CS_NOR (0) //spi cs0 --> nor flash +#define SPI_CS_K7 (1) //spi cs1 --> K7 A + +/* gpio as spi cs1 ext cs pin */ +#define K7_SPI_CS_GPIO_0 (12) +#define K7_SPI_CS_GPIO_1 (13) +#define K7_SPI_CS_GPIO_2 (14) +#define K7_SPI_CS_GPIO_3 (15) + +#define K7_SPI_BUSY_GPIO (9) + +#define K7_INTR_GPIO (11) + +extern int enter_pragrom_mode(void ); +extern int enter_run_mode(void ); + +#define DEFAULT_PORT 64000 + +#endif /* VSDEF_H_ */ diff --git a/targetConfigs/TMS320C6678.ccxml b/targetConfigs/TMS320C6678.ccxml new file mode 100644 index 0000000..a675955 --- /dev/null +++ b/targetConfigs/TMS320C6678.ccxml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/targetConfigs/readme.txt b/targetConfigs/readme.txt new file mode 100644 index 0000000..d783fef --- /dev/null +++ b/targetConfigs/readme.txt @@ -0,0 +1,9 @@ +The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based +on the device and connection settings specified in your project on the Properties > General page. + +Please note that in automatic target-configuration management, changes to the project's device and/or +connection settings will either modify an existing or generate a new target-configuration file. Thus, +if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively, +you may create your own target-configuration file for this project and manage it manually. You can +always switch back to automatic target-configuration management by checking the "Manage the project's +target-configuration automatically" checkbox on the project's Properties > General page. \ No newline at end of file diff --git a/vslib/cmdline.cmd b/vslib/cmdline.cmd new file mode 100644 index 0000000..2c79189 --- /dev/null +++ b/vslib/cmdline.cmd @@ -0,0 +1,9 @@ +--retain="*(.cmdline_cmd)" +SECTIONS +{ + .dsp_cmd_section: fill = 0x0 align =0x4 { + __cmdline_cmd_start = .; + *(.cmdline_cmd) + __cmdline_cmd_end = .; + } > DDR3 +} diff --git a/vslib/vbios.c b/vslib/vbios.c new file mode 100644 index 0000000..ecb3981 --- /dev/null +++ b/vslib/vbios.c @@ -0,0 +1,246 @@ +/* + * vbios.c + * + * Created on: 2019-8-13 + * Author: Administrator + */ +#include +#include +#include + +#include "vbios.h" + +struct vbios { + int net_enable; + struct vbios_net_param vnp; + struct vbios_term_param vtp; + struct vbios_param vp; + vsem_t sem; +}; + +static struct vbios _vs; +static HANDLE hCfg; +static char *TaskName[] = { "Telnet","HTTP","NAT","DHCPS","DHCPC","DNS" }; +static char *ReportStr[] = { "","Running","Updated","Complete","Fault" }; +static char *StatusStr[] = { "Disabled","Waiting","IPTerm","Failed","Enabled" }; + +static void NetworkOpen(void ) +{ + /* Create our local servers */ + TaskCreate( echosrv, "echosrv", OS_TASKPRINORM, 0x1400, 0, 0, 0 ); + + vsem_signal(_vs.sem); +} + +static void NetworkClose(void ) +{ + +} + +static void NetworkIPAddr( IPN IPAddr, uint32_t IfIdx, uint32_t fAdd ) +{ + IPN IPTmp; + + if( fAdd ) + printf("Network Added: "); + else + printf("Network Removed: "); + + /* Print a message */ + IPTmp = ntohl( IPAddr ); + printf("If-%d:%d.%d.%d.%d \r\n", IfIdx, + (uint8_t)(IPTmp>>24)&0xFF, (uint8_t)(IPTmp>>16)&0xFF, + (uint8_t)(IPTmp>>8)&0xFF, (uint8_t)IPTmp&0xFF ); + return; +} + +static void service_report( uint32_t item, uint32_t status, uint32_t report, HANDLE h ) +{ + printf( "Service Status: %-9s: %-9s: %-9s \r\n", TaskName[item-1], StatusStr[status],ReportStr[report/256]); +} + +static void __net_ndk_start(void ) +{ + int32_t rc; + struct vbios_net_param *vnp = &_vs.vnp; + + /* Start the NDK stack - The stack will start the Ethernet driver */ + rc = NC_SystemOpen( NC_PRIORITY_HIGH, NC_OPMODE_INTERRUPT ); + if( rc ) { + printf("NC_SystemOpen Failed (%d). Will die in an infinite loop so you need to reset...\n",rc); + for(;;); + } + + /* + * Create and build the system configuration from scratch. + */ + + /* Create a new configuration */ + if (!(hCfg = CfgNew())) { + printf("Unable to create a configuration for the IP stack.\n"); + goto _exit; + } + + do { + + CI_IPNET NA; + + /* Setup an IP address to this EVM */ + bzero( &NA, sizeof(NA) ); + NA.IPAddr = inet_addr(vnp->ip); + NA.IPMask = inet_addr(vnp->mask); + strcpy( NA.Domain, vnp->domain); + + /* Add the address to interface 1 */ + CfgAddEntry( hCfg, CFGTAG_IPNET, 1, 0, sizeof(CI_IPNET), (uint8_t *)&NA, 0 ); + }while(0); + + if (_vs.vnp.telnet_enable) { + CI_SERVICE_TELNET telnet; + + /*telnet init same param*/ + vtelnetd_init(); + + /* Specify TELNET service */ + bzero( &telnet, sizeof(telnet) ); + telnet.cisargs.IPAddr = INADDR_ANY; + telnet.cisargs.pCbSrv = &service_report; + telnet.param.MaxCon = TELNET_CLINET_MAX_COUNT; + telnet.param.Callback = &vtelnetd_client; + CfgAddEntry( hCfg, CFGTAG_SERVICE, CFGITEM_SERVICE_TELNET, 0, + sizeof(telnet), (uint8_t *)&telnet, &(telnet.cisargs.hService) ); + } + + /* + ** Configure IPStack/OS Options + */ + + /* Set debug message level */ + rc = DBG_WARN; + CfgAddEntry( hCfg, CFGTAG_OS, CFGITEM_OS_DBGPRINTLEVEL, + CFG_ADDMODE_UNIQUE, sizeof(uint), (uint8_t *)&rc, 0 ); + + /* + ** This code sets up the TCP and UDP buffer sizes + ** (Note 8192 is actually the default. This code is here to + ** illustrate how the buffer and limit sizes are configured.) + */ + + /* TCP Transmit buffer size */ + //rc = 8192; + rc = 64000; + CfgAddEntry( hCfg, CFGTAG_IP, CFGITEM_IP_SOCKTCPTXBUF, + CFG_ADDMODE_UNIQUE, sizeof(uint), (uint8_t *)&rc, 0 ); + + /* TCP Receive buffer size (copy mode) */ + //rc = 8192; + rc = 64000; + CfgAddEntry( hCfg, CFGTAG_IP, CFGITEM_IP_SOCKTCPRXBUF, + CFG_ADDMODE_UNIQUE, sizeof(uint), (uint8_t *)&rc, 0 ); + + /* TCP Receive limit (non-copy mode) */ + //rc = 8192; + rc = 64000; + CfgAddEntry( hCfg, CFGTAG_IP, CFGITEM_IP_SOCKTCPRXLIMIT, + CFG_ADDMODE_UNIQUE, sizeof(uint), (uint8_t *)&rc, 0 ); + + /* UDP Receive limit */ + rc = 8192; + CfgAddEntry( hCfg, CFGTAG_IP, CFGITEM_IP_SOCKUDPRXLIMIT, + CFG_ADDMODE_UNIQUE, sizeof(uint), (uint8_t *)&rc, 0 ); + + /* + ** Boot the system using this configuration + ** + ** We keep booting until the function returns 0. This allows + ** us to have a "reboot" command. + */ + + do + { + rc = NC_NetStart( hCfg, NetworkOpen, NetworkClose, NetworkIPAddr ); + } while( rc > 0 ); + + printf("Done with this utility. Shutting things down\n"); + + /* Delete Configuration */ + CfgFree( hCfg ); + + /* Close the OS */ +_exit: + printf("Exiting the system\n"); + NC_SystemClose(); + +} + +static void __main_task(void ) +{ + if (_vs.net_enable) { + vsem_wait(_vs.sem, 0); + fdOpenSession(TaskSelf()); + } + + if (_vs.vp.usrAppFunc) + _vs.vp.usrAppFunc(); + + if (_vs.net_enable) + fdCloseSession(TaskSelf()); +} + +static int __vbios_puts(void *param, const char *s, int len) +{ + if (_vs.vtp.puts) + return _vs.vtp.puts(s, len); + return 0; +} + +static int __vbios_gets(void *param, char *buf, int len, int blocking /*in millis*/) +{ + if (_vs.vtp.gets) + return _vs.vtp.gets(buf, len, blocking); + return 0; +} + +static vconsole_t _vb_tty = NULL; +int vbios_init(struct vbios_param *vp) +{ + memset(&_vs, 0, sizeof _vs); + memcpy(&_vs.vp, vp, sizeof *vp); + + if (vp->tp) { + memcpy(&_vs.vtp, vp->tp, sizeof *vp->tp); + struct vconsole_param vp; + vp.debug_console = 1; + vp.prompt = "vs> "; + vp.param = NULL; + vp.puts = __vbios_puts; + vp.gets = __vbios_gets; + _vb_tty = vconsole_new(&vp); + } + + if (vp->usrDrvInitFunc) + vp->usrDrvInitFunc(); + + if (vp->usrAppPrevFunc) + vp->usrAppPrevFunc(); + + if (vp->np) { + _vs.net_enable = 1; + memcpy(&_vs.vnp, vp->np, sizeof *vp->np); + _vs.sem = vsem_new(); + vtask_create(__net_ndk_start); + } + + return 0; +} + +void vbios_start(void ) +{ + vtask_create(__main_task); + BIOS_start(); +} + +void vbios_loop(void ) +{ + return vconsole_loop(_vb_tty); +} diff --git a/vslib/vbios.h b/vslib/vbios.h new file mode 100644 index 0000000..c2bba5b --- /dev/null +++ b/vslib/vbios.h @@ -0,0 +1,45 @@ +/* + * vbios.h + * + * Created on: 2019-8-13 + * Author: Administrator + */ + +#ifndef VBIOS_H_ +#define VBIOS_H_ + +#include "vtask.h" +#include "vsem.h" +#include "vlock.h" +#include "vtimer.h" +#include "vtelnetd.h" +#include "vtftp.h" +#include "vconsole.h" +#include "vcmd.h" + +//std io redirect +struct vbios_term_param { + int (*puts)(const char *s, int len); + int (*gets)(char *buf, int len, int blocking /*in millis*/); +}; + +struct vbios_net_param { + int telnet_enable; + char ip[32]; + char mask[32]; + char gate[32]; + char domain[32]; +}; + +struct vbios_param { + struct vbios_term_param *tp; //NULL no enable termial + struct vbios_net_param *np; //NULL no enable net + void (*usrDrvInitFunc)(void ); + void (*usrAppPrevFunc)(void ); + void (*usrAppFunc)(void ); +}; + +extern int vbios_init(struct vbios_param *vp); +extern void vbios_start(void ); +extern void vbios_loop(void ); +#endif /* VBIOS_H_ */ diff --git a/vslib/vcmd.h b/vslib/vcmd.h new file mode 100644 index 0000000..0959910 --- /dev/null +++ b/vslib/vcmd.h @@ -0,0 +1,24 @@ +#ifndef __CMD_H__ +#define __CMD_H__ +#ifdef __cplusplus +extern "C" { +#endif + +struct cmd_tbl_s { + const char *name; + const char *help; + void * user; + int (*cmd)(struct cmd_tbl_s *, int argc, char *argv[]); +}; + +typedef struct cmd_tbl_s *cmd_tbl_t; + +#define CON_CMD(name, usage, _user, handler) \ + struct cmd_tbl_s __con_cmd_##name __attribute__((unused, section(".cmdline_cmd"))) = \ + { #name, usage, _user, handler}; + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/vslib/vconsole.c b/vslib/vconsole.c new file mode 100644 index 0000000..a5bb50a --- /dev/null +++ b/vslib/vconsole.c @@ -0,0 +1,865 @@ +/* + * console.c + * + * Created on: 2019-7-4 + * Author: Administrator + */ + +#include +#include +#include +#include +#include "vcmd.h" +#include "vdebug.h" +#include "vconsole.h" + +#define TARGET_BUFFER_SIZE (256) +#define PROMPT_MAX_LEN (8) +#define TARGET_CMD_HISTORY 32 +#define TARGET_CMD_MAXARGS (8) + +enum KEY_ACTION { + KEY_NULL = 0, /* NULL */ + CTRL_A = 1, /* Ctrl+a */ + CTRL_B = 2, /* Ctrl-b */ + CTRL_C = 3, /* Ctrl-c */ + CTRL_D = 4, /* Ctrl-d */ + CTRL_E = 5, /* Ctrl-e */ + CTRL_F = 6, /* Ctrl-f */ + CTRL_H = 8, /* Ctrl-h */ + TAB = 9, /* Tab */ + CTRL_K = 11, /* Ctrl+k */ + CTRL_L = 12, /* Ctrl+l */ + ENTER = 13, /* Enter */ + CTRL_N = 14, /* Ctrl-n */ + CTRL_P = 16, /* Ctrl-p */ + CTRL_T = 20, /* Ctrl-t */ + CTRL_U = 21, /* Ctrl+u */ + CTRL_W = 23, /* Ctrl+w */ + ESC = 27, /* Escape */ + BACKSPACE = 127 /* Backspace */ +}; + +struct console { + char buf[TARGET_BUFFER_SIZE]; /* edited line buffer */ + int buflen; + int len; /* current edited line length */ + char prompt[PROMPT_MAX_LEN]; /* prompt to display */ + int plen; /* prompt length */ + int pos; /* current cursor poistion */ + int oldpos; /* previouse refresh cursor position */ + int cols; /* number of columns in terminal */ + int maxrows; /* maximum num of rows used so for (mulitline mode) */ + int hindex; /* the history index we are currently editing */ + + char *history[TARGET_CMD_HISTORY]; + int history_len; + + /*io interface func*/ + void *param; + int (*puts)(void *param, const char *s, int len); + int (*gets)(void *param, char *buf, int len, int blocking /*in millis*/); + + int debug_console; + int exit; + char last_ch; + int start; /*0-no prompt, 1- have prompt*/ +}; + +struct linebuf { + char *buf; + int buflen; + int len; +}; + +extern struct cmd_tbl_s __cmdline_cmd_start, __cmdline_cmd_end; + + +void *strdup(const char *str) +{ + void *m; + int len = strlen(str); + + m = calloc(1, len + 1); + memcpy(m, str, len); + + return m; +} + +static int tputs(vconsole_t cs, const char *buf, int len) +{ + if (cs->puts) + cs->puts(cs->param, buf, len); + return len; +} + +static int tgets(vconsole_t cs, char *buf, int len, int blocking) +{ + int ret; + if (cs->gets) + ret = cs->gets(cs->param, buf, len, blocking); + return ret; +} + +static void linebuf_init(struct linebuf *ln, char *buf, int len) +{ + ln->buf = buf; + ln->buflen = len; + ln->len = 0; +} + +static int linebuf_len(struct linebuf *ln) +{ + return ln->len; +} + +static int linebuf_append(struct linebuf *ln, const char *s, int len) +{ + if (len + ln->len > ln->buflen) { + len = ln->buflen - ln->len; + } + + if (len > 0) { + memcpy(ln->buf + ln->len, s, len); + ln->len += len; + } + + return (len); +} + +/* + * Use the ESC [6n escape sequence to query the horizontal cursor position + * and return it. On error -1 is returned, on success the position of the + * cursor. + */ +static int get_cursor_position(vconsole_t cs) +{ + char buf[32]; + int cols, rows; + unsigned int i = 0; + + /* Report cursor location */ + if (tputs(cs, "\x1b[6n", 4) != 4) + return -1; + + /* Read the response: ESC [ rows ; cols R */ + while (i < sizeof(buf)-1) { + if (tgets(cs, buf + i, 1, 1000) != 1) + break; + if (buf[i] == 'R') + break; + i++; + } + buf[i] = '\0'; + + /* Parse it. */ + if (buf[0] != ESC || buf[1] != '[') + return -1; + if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) + return -1; + return cols; +} + +/* + * Try to get the number of columns in the current terminal, or assume 80 + * if it fails. + */ +static int get_columns(vconsole_t cs) +{ + char seq[32]; + int start, cols; + + /* Get the initial position so we can restore it later. */ + start = get_cursor_position(cs); + if (start == -1) + goto failed; + + /* Go to right margin and get position. */ + if (tputs(cs, "\x1b[999C", 6) != 6) + goto failed; + cols = get_cursor_position(cs); + if (cols == -1) + goto failed; + + /* Restore position. */ + if (cols > start) { + snprintf(seq,32,"\x1b[%dD",cols-start); + if (tputs(cs, seq, strlen(seq)) == -1) { + /* Can't recover... */ + } + } + return cols; +failed: + return 80; +} + +/* + * Single line low level line refresh. + * + * Rewrite the currently edited line accordingly to the buffer content, + * cursor position, and number of columns of the terminal. + */ +static void refresh_single_line(vconsole_t cs) +{ + struct linebuf lb; + char line[512]; + char seq[64] = {0}; + char *buf = cs->buf; + size_t len = cs->len; + size_t pos = cs->pos; + + while((cs->plen + pos) >= cs->cols) { + buf++; + len--; + pos--; + } + + while (cs->plen+len > cs->cols) { + len--; + } + + linebuf_init(&lb, line, sizeof line); + + /* Cursor to left edge */ + linebuf_append(&lb, "\r", 1); + linebuf_append(&lb, cs->prompt, cs->plen); + linebuf_append(&lb, buf, len); + + /* Erase to right */ + linebuf_append(&lb, "\x1b[0K", 4); + + /* Move cursor to original position. */ + snprintf(seq, 64, "\r\x1b[%dC", (int)(pos + cs->plen)); + linebuf_append(&lb, seq, strlen(seq)); + tputs(cs, line, linebuf_len(&lb)); +} + +static void refresh_line(vconsole_t cs) +{ + refresh_single_line(cs); +} + +/* + * Delete the character at the right of the cursor without altering the cursor + * position. Basically this is what happens with the "Delete" keyboard key. + */ +static void edit_delete(vconsole_t cs) +{ + if (cs->len > 0 && cs->pos < cs->len) { + memmove(cs->buf + cs->pos, cs->buf + cs->pos + 1, cs->len - cs->pos - 1); + cs->len--; + cs->buf[cs->len] = '\0'; + refresh_line(cs); + } +} + +/* Backspace implementation. */ +static void edit_backspace(vconsole_t cs) +{ + if (cs->pos > 0 && cs->len > 0) { + memmove(cs->buf + cs->pos - 1, cs->buf + cs->pos, cs->len - cs->pos); + cs->pos--; + cs->len--; + cs->buf[cs->len] = '\0'; + refresh_line(cs); + } +} + +/* + * Delete the previosu word, maintaining the cursor at the start of the + * current word. + */ +static void edit_delete_prev(vconsole_t cs) +{ + size_t old_pos = cs->pos; + size_t diff; + + while (cs->pos > 0 && cs->buf[cs->pos - 1] == ' ') + cs->pos--; + while (cs->pos > 0 && cs->buf[cs->pos - 1] != ' ') + cs->pos--; + diff = old_pos - cs->pos; + memmove(cs->buf + cs->pos, cs->buf + old_pos, cs->len - old_pos + 1); + cs->len -= diff; + refresh_line(cs); +} + +/* + * Insert the character 'c' at cursor current position. + * + * On error writing to the terminal -1 is returned, otherwise 0. + */ +static void edit_insert(vconsole_t cs, char c) +{ + if (cs->len < sizeof cs->buf) { + if (cs->len == cs->pos) { + cs->buf[cs->pos] = c; + cs->pos++; + cs->len++; + cs->buf[cs->len] = '\0'; + if ((cs->plen + cs->len) < cs->cols) { + /* Avoid a full update of the line in the + * trivial case. */ + tputs(cs, &c, 1); + } else { + refresh_line(cs); + } + } else { + memmove(cs->buf + cs->pos + 1, cs->buf + cs->pos, cs->len - cs->pos); + cs->buf[cs->pos] = c; + cs->len++; + cs->pos++; + cs->buf[cs->len] = '\0'; + refresh_line(cs); + } + } +} + +static void move_right(vconsole_t cs) +{ + if (cs->pos != cs->len) { + ++cs->pos; + refresh_line(cs); + } +} + +static void move_left(vconsole_t cs) +{ + if (cs->pos > 0) { + cs->pos--; + refresh_line(cs); + } +} + +static void move_home(vconsole_t cs) +{ + if (cs->pos != 0) { + cs->pos = 0; + refresh_line(cs); + } +} + +static void move_end(vconsole_t cs) +{ + if (cs->pos != cs->len) { + cs->pos = cs->len; + refresh_line(cs); + } +} + +static int history_add(vconsole_t cs, const char *line) +{ + char *copy; + + if (cs->history_len && !strcmp(cs->history[cs->history_len - 1], line)) + return 0; + + copy = strdup(line); + if (!copy) + return 0; + if (cs->history_len == TARGET_CMD_HISTORY) { + free(cs->history[0]); + memmove(cs->history, cs->history + 1, sizeof (char *) * (cs->history_len - 1)); + --cs->history_len; + } + cs->history[cs->history_len++] = copy; + return 1; +} + +static int history_free(vconsole_t cs) +{ + while(cs->history_len > 0) { + --cs->history_len; + if (cs->history[cs->history_len]) { + free(cs->history[cs->history_len]); + cs->history[cs->history_len] = NULL; + } + } + return 0; +} + +/* + * Substitute the currently edited line with the next or previous history + * entry as specified by 'dir' + */ +static void edit_history(vconsole_t cs, int dir) +{ + if (cs->history_len > 0) { + cs->hindex += dir; + if (cs->hindex < 0) { + cs->hindex = cs->history_len - 1; + } else if (cs->hindex >= cs->history_len) { + cs->hindex = 0; + } + strncpy(cs->buf, cs->history[cs->hindex], cs->buflen); + cs->buf[cs->buflen - 1] = '\0'; + cs->len = cs->pos = strlen(cs->buf); + refresh_line(cs); + } +} + +static void history_prev(vconsole_t cs) +{ + edit_history(cs, -1); +} + +static void history_next(vconsole_t cs) +{ + edit_history(cs, 1); +} + +static void reset_linestate(vconsole_t cs) +{ + memset(cs->buf, 0, sizeof cs->buf); + + cs->oldpos = 0; + cs->pos = 0; + cs->len = 0; + cs->hindex = cs->history_len; + + tputs(cs, cs->prompt, cs->plen); +} + +static int get_mached(const char *buf, int len) +{ + cmd_tbl_t it = &__cmdline_cmd_start; + int n, mached = 0; + + while (it < &__cmdline_cmd_end) { + n = strlen(it->name); + if (n > len) + n = len; + if (memcmp(it->name, buf, n) == 0) + ++mached; + ++it; + } + + return (mached); +} + +static int fill_mached(int mached, const char *vec[], const char *buf, int len) +{ + cmd_tbl_t it = &__cmdline_cmd_start; + int i = 0, n; + + while (it < &__cmdline_cmd_end) { + n = strlen(it->name); + if (n > len) + n = len; + if (memcmp(it->name, buf, n) == 0) { + vec[i++] = it->name; + if (i >= mached) + break; + } + ++it; + } + + return (i); +} + +/* + * This is an helper function for cmd_loop() and is called when the + * user types the key in order to complete the string currently in the + * input. + */ +static int do_complete_line(vconsole_t cs, int mached) +{ + const char *cvec[TARGET_CMD_HISTORY]; + int nread, nwritten; + size_t stop = 0, i = 0; + char c = 0; + + if (fill_mached(mached, cvec, cs->buf, cs->len) != mached) + return 0; + + while(!stop) { + /* Show completion or original buffer */ + if (i < mached) { + struct console saved = *cs; + + cs->len = cs->pos = strlen(cvec[i]); + sprintf(cs->buf, "%s", (char *)cvec[i]); + refresh_line(cs); + + cs->len = saved.len; + cs->pos = saved.pos; + memcpy(cs->buf, saved.buf, saved.buflen); + } else { + refresh_line(cs); + } + + nread = tgets(cs, &c, 1, 1000); + if (nread <= 0) + return 0; + + switch(c) { + case TAB: /* tab */ + i = (i+1) % (mached + 1); + if (i == mached) { + tputs(cs, "\x7", 1); /* beep */ + } + break; + case ESC: /* escape */ + /* Re-show original buffer */ + if (i < mached) + refresh_line(cs); + stop = 1; + break; + default: + /* Update buffer and return */ + if (i < mached) { + nwritten = snprintf(cs->buf, cs->buflen, "%s", cvec[i]); + cs->len = cs->pos = nwritten; + } + stop = 1; + break; + } + } + + return c; /* Return last read character */ +} + +static int complete_line(vconsole_t cs) +{ + int mached = get_mached(cs->buf, cs->len); + + if (mached <= 0) { + tputs(cs, "\x7", 1); + return 0; + } + return do_complete_line(cs, mached); +} + +static int parse_line(char *cmd, int len, char *argv[], int _maxargs) +{ + int argc; + char *p; + int position; + + /* Init params */ + p = cmd; + position = 0; + argc = 0; + + while ((position < len) && (argc < _maxargs)) { + /* Skip all blanks */ + while (((*p) == ' ') && (position < len)) { + *p = '\0'; + p++; + position++; + } + /* Process begin of a string */ + if (*p == '"') { + p++; + position++; + argv[argc] = p; + argc++; + /* Skip this string */ + while ((*p != '"') && (position < len)) { + p++; + position++; + } + /* Skip '"' */ + *p = '\0'; + p++; + position++; + } else {/* Normal char */ + argv[argc] = p; + argc++; + while (((*p) != ' ') && ((*p) != '\t') && (position < len)) { + p++; + position++; + } + } + } + return argc; +} + +int cmd_process(vconsole_t cs, char *line) +{ + int i, argc, namelen; + int size = strlen(line); + char *argv[TARGET_CMD_MAXARGS + 1]; + + cmd_tbl_t it = &__cmdline_cmd_start; + + /* split line */ + for (i = 0; i < TARGET_CMD_MAXARGS + 1; ++i) { + argv[i] = NULL; + } + + argc = parse_line(line, size, argv, TARGET_CMD_MAXARGS + 1); + + if (argc <= 0) + goto recovery; + + debug_update_console(cs); + while (it < &__cmdline_cmd_end) { + namelen = strlen(it->name); + if (strncmp(argv[0], it->name, namelen) == 0) { + it->user = cs; + it->cmd(it, argc, argv); + return 1; + } + ++it; + } + + /* recovery line */ +recovery: + for (i = 0; i < size; ++i) { + if (line[i] == '\0') + line[i] = ' '; + } + + return 0; +} + +static void do_command(vconsole_t cs) +{ + tputs(cs, "\r\n", 2); + if (cs->len > 0) { + /* add to history */ + history_add(cs, cs->buf); + + cmd_process(cs, cs->buf); + } + + //start next cmd, print prompt... + if (!cs->exit) + reset_linestate(cs); +} + +vconsole_t vconsole_new(struct vconsole_param *cp) +{ + vconsole_t cs; + if (!(cs = calloc(1, sizeof *cs))) + return NULL; + + cs->buflen = sizeof cs->buf; + if (cp->prompt) { + cs->plen = strlen(cp->prompt); + memcpy(cs->prompt, cp->prompt, cs->plen); + } else { + strcpy(cs->prompt, "> "); + } + + cs->param = cp->param; + cs->puts = cp->puts; + cs->gets = cp->gets; + + if (cp->debug_console) { + cs->debug_console = 1; + cs->exit = 0; + debug_init(cs); + } + + cs->cols = get_columns(cs); + return cs; +} + +void vconsole_free(vconsole_t cs) +{ + if (cs) { + history_free(cs); + free(cs); + } +} + +int vconsole_puts(vconsole_t cs, const char *s, int len) +{ + return tputs(cs, s, len); +} + +int vconsole_status(vconsole_t cs) +{ + return cs->exit; +} + +void vconsole_loop(vconsole_t cs) +{ + int nread; + char c, seq[3]; + + //use firset loop puts "abc> " + if (cs->start == 0) { + reset_linestate(cs); + cs->start = 1; + } + + nread = tgets(cs, &c, 1, -1); + if (nread <= 0) + return; + + /* if c == TAB, do completion? */ + if (c == TAB) { + c = complete_line(cs); + if (c <= 0) { + return; + } + } + + switch (c) { + case ENTER: //'\r' + do_command(cs); + break; + case '\n': // '\n' + if (cs->last_ch != ENTER) //'\r\n' pre '\r' already handle + do_command(cs); + break; + case CTRL_C: /* ctrl-c */ + reset_linestate(cs); + break; + case BACKSPACE: /* backspace */ + case CTRL_H: + edit_backspace(cs); + break; + case CTRL_D: /* ctrl-d, remove the right of cursor. */ + if (cs->len > 0) { + edit_delete(cs); + } else { + reset_linestate(cs); + } + break; + case CTRL_T: /* ctrl-t, swaps current char with previous */ + if (cs->pos > 0 && cs->pos < cs->len) { + int aux = cs->buf[cs->pos - 1]; + cs->buf[cs->pos-1] = cs->buf[cs->pos]; + cs->buf[cs->pos] = aux; + if (cs->pos != cs->len - 1) cs->pos++; + refresh_line(cs); + } + break; + case CTRL_B: /* ctrl -b */ + move_left(cs); + break; + case CTRL_F: /* ctrl-f */ + move_right(cs); + break; + case CTRL_L: + tputs(cs, "\x1b[H\x1b[2J",7); + refresh_line(cs); + break; + case CTRL_U: /* ctrl-u, delete the whole line */ + cs->buf[0] = '\0'; + cs->pos = cs->len = 0; + refresh_line(cs); + break; + case CTRL_K: /* ctrl-k, delete from current to end of line */ + cs->buf[cs->pos] = '\0'; + cs->len = cs->pos; + refresh_line(cs); + break; + case CTRL_A: /* ctrl-a, goto start of the line */ + move_home(cs); + break; + case CTRL_E: /* ctrl-e, goto end of the line */ + move_end(cs); + break; + case CTRL_W: + edit_delete_prev(cs); + break; + case CTRL_P: + history_prev(cs); + break; + case CTRL_N: + history_next(cs); + break; + case ESC: /* escape sequence */ + /* Read the next two bytes representing the escape sequence. + * Use two calls to handle slow terminals returning the two + * chars at different times. */ + if (tgets(cs, seq, 1, 1000) <= 0) + break; + if (tgets(cs, seq + 1 ,1, 1000) <= 0) + break; + + /* ESC [ sequences. */ + if (seq[0] == '[') { + if (seq[1] >= '0' && seq[1] <= '9') { + /* Extended escape, read additional byte. */ + if (tgets(cs, seq + 2, 1, 1000) <= 0) + break; + if (seq[2] == '~') { + switch(seq[1]) { + case '3': /* Delete key. */ + edit_delete(cs); + break; + } + } + } else { + switch(seq[1]) { + case 'A': /* Up */ + history_prev(cs); + break; + case 'B': /* Down */ + history_next(cs); + break; + case 'C': /* Right */ + move_right(cs); + break; + case 'D': /* Left */ + move_left(cs); + break; + case 'H': /* Home */ + move_home(cs); + break; + case 'F': /* End*/ + move_end(cs); + break; + } + } + } else if (seq[0] == 'O') { /* ESC O sequences. */ + switch(seq[1]) { + case 'H': /* Home */ + move_home(cs); + break; + case 'F': /* End*/ + move_end(cs); + break; + } + } + break; + default: + edit_insert(cs, c); + break; + } + + cs->last_ch = c; +} + +static int do_help(cmd_tbl_t tbl, int argc, char *argv[]) +{ + cmd_tbl_t it = &__cmdline_cmd_start; + vconsole_t cs = (vconsole_t)tbl->user; + + while(it < &__cmdline_cmd_end) { + tputs(cs, it->name, -1); + tputs(cs, "\t", 1); + tputs(cs, it->help, -1); + tputs(cs, "\r\n", 2); + ++it; + } + return 0; +} + +CON_CMD(help, "mm show opt", NULL, do_help); + +static int do_clear(cmd_tbl_t tbl, int argc, char *argv[]) +{ + vconsole_t cs = (vconsole_t)tbl->user; + tputs(cs, "\x1b[H\x1b[2J",7); + return 0; +} + +CON_CMD(clear, "clear the screen", NULL, do_clear); + +static int do_exit(cmd_tbl_t tbl, int argc, char *argv[]) +{ + vconsole_t cs = (vconsole_t)tbl->user; + if (!cs->debug_console) + cs->exit = 1; + return 0; +} + +CON_CMD(exit, "exit the console", NULL, do_exit); + diff --git a/vslib/vconsole.h b/vslib/vconsole.h new file mode 100644 index 0000000..7e80475 --- /dev/null +++ b/vslib/vconsole.h @@ -0,0 +1,27 @@ +/* + * console.h + * + * Created on: 2019-7-4 + * Author: Administrator + */ + +#ifndef VCONSOLE_H_ +#define VCONSOLE_H_ + +typedef struct console *vconsole_t; + +struct vconsole_param { + int debug_console; /*debug default tty*/ + char *prompt; /*NULL use default prompt: '> '*/ + void *param; + int (*puts)(void *param, const char *s, int len); + int (*gets)(void *param, char *buf, int len, int blocking /*in millis*/); +}; + +vconsole_t vconsole_new(struct vconsole_param *cp); +void vconsole_free(vconsole_t cs); +void vconsole_loop(vconsole_t cs); +int vconsole_puts(vconsole_t cs, const char *s, int len); +int vconsole_status(vconsole_t cs); /*return: 0-running, 1-exit*/ + +#endif /* CONSOLE_H_ */ diff --git a/vslib/vdebug.c b/vslib/vdebug.c new file mode 100644 index 0000000..798ecb5 --- /dev/null +++ b/vslib/vdebug.c @@ -0,0 +1,101 @@ +/* + * debug.c + * + * Created on: 2019-5-29 + * Author: Administrator + */ +#include +#include +#include +#include +#include + +#include + +#include "vconsole.h" + +#define SERIAL_DEV_NAME "vtty" + +static vconsole_t _cs = NULL; + +static int dopen (const char *path, unsigned flags, int llv_fd) +{ + if (strcmp(path, "stdin") == 0) { + return 0; + } else if (strcmp(path, "stdout") == 0) { + return 1; + } else if (strcmp(path, "stderr")) { + return 2; + } + return -1; +} + +static int dclose ( int dev_fd) +{ + return 0; +} + +static int dread(int dev_fd, char *buf, unsigned count) +{ + return 0; +} + +static int dwrite(int dev_fd, const char *buf, unsigned count) +{ + int n; + + if (_cs) { + n = vconsole_puts(_cs, buf, count); + } + + return n; +} + +static off_t dlseek(int dev_fd, off_t ioffset, int origin) +{ + return (off_t)-1; +} + +int dunlink(const char * path) +{ + return -1; +} + +int drename(const char *old_name, const char *new_name) +{ + return -1; +} + +int debug_init(vconsole_t cs) +{ + char dev[64] = {0}; + _cs = cs; + + add_device(SERIAL_DEV_NAME, + _MSA, + dopen, + dclose, + dread, + dwrite, + dlseek, + dunlink, + drename); + + snprintf(dev, sizeof dev, "%s:stderr", SERIAL_DEV_NAME); + freopen(dev, "w", stderr); + snprintf(dev, sizeof dev, "%s:stdout", SERIAL_DEV_NAME); + freopen(dev, "w", stdout); + snprintf(dev, sizeof dev, "%s:stdin", SERIAL_DEV_NAME); + freopen(dev, "r", stdin); + + setvbuf(stderr, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdin, NULL, _IONBF, 0); + + return 0; +} + +void debug_update_console(vconsole_t cs) +{ + _cs = cs; +} diff --git a/vslib/vdebug.h b/vslib/vdebug.h new file mode 100644 index 0000000..12db0a1 --- /dev/null +++ b/vslib/vdebug.h @@ -0,0 +1,16 @@ +/* + * debug.h + * + * Created on: 2019-5-29 + * Author: Administrator + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ +#include "vconsole.h" + +void debug_init(vconsole_t cs); + +void debug_update_console(vconsole_t cs); + +#endif /* DEBUG_H_ */ diff --git a/vslib/vlock.c b/vslib/vlock.c new file mode 100644 index 0000000..078cc56 --- /dev/null +++ b/vslib/vlock.c @@ -0,0 +1,45 @@ +/* + * vlock.c + * + * Created on: 2019-4-2 + * Author: Administrator + */ +#include +#include +#include +#include +#include "vlock.h" + +struct vlock { + Semaphore_Handle sem; +}; + +vlock_t vlock_new(void ) +{ + Semaphore_Params semPrms; + vlock_t vk; + vk = malloc(sizeof *vk); + Semaphore_Params_init(&semPrms); + semPrms.mode = Semaphore_Mode_BINARY; + vk->sem = Semaphore_create(1, &semPrms, NULL); + return vk; +} + +void vlock_free(vlock_t vk) +{ + if (vk) { + Semaphore_delete(&vk->sem); + free(vk); + vk = NULL; + } +} + +void vlock_lock(vlock_t vk) +{ + Semaphore_pend(vk->sem, BIOS_WAIT_FOREVER); +} + +void vlock_unlock(vlock_t vk) +{ + Semaphore_post(vk->sem); +} diff --git a/vslib/vlock.h b/vslib/vlock.h new file mode 100644 index 0000000..a9f1016 --- /dev/null +++ b/vslib/vlock.h @@ -0,0 +1,17 @@ +/* + * vlock.h + * + * Created on: 2019-4-2 + * Author: Administrator + */ + +#ifndef VLOCK_H_ +#define VLOCK_H_ + +typedef struct vlock *vlock_t; +vlock_t vlock_new(void ); +void vlock_free(vlock_t vk); +void vlock_lock(vlock_t vk); +void vlock_unlock(vlock_t vk); + +#endif /* VLOCK_H_ */ diff --git a/vslib/vsem.c b/vslib/vsem.c new file mode 100644 index 0000000..cafb91f --- /dev/null +++ b/vslib/vsem.c @@ -0,0 +1,67 @@ +/* + * vsem.c + * + * Created on: 2019-3-29 + * Author: Administrator + */ +#include + +#include +#include +#include + +#include "vsem.h" + +struct vsem { + Semaphore_Handle sem; +}; + +vsem_t vsem_new(void ) +{ + Semaphore_Params semPrms; + vsem_t vs; + vs = malloc(sizeof *vs); + Semaphore_Params_init(&semPrms); + semPrms.mode = Semaphore_Mode_BINARY; + vs->sem = Semaphore_create(0, &semPrms, NULL); + return vs; +} + +void vsem_free(vsem_t vs) +{ + if (vs) { + Semaphore_delete(&vs->sem); + free(vs); + vs = NULL; + } +} + +int vsem_wait(vsem_t vs, unsigned int timeout) +{ + UInt bios6_timeout; + Int status; + if (timeout == 0) + bios6_timeout = BIOS_WAIT_FOREVER; + else { + bios6_timeout = timeout / Clock_tickPeriod; + /* + * Don't let nonzero timeout round to 0 - semantically very + * different + */ + if (timeout && (!bios6_timeout)) { + bios6_timeout = 1; + } + } + status = Semaphore_pend(vs->sem, bios6_timeout); + if (status == 1) //success + return 1; + else if (status == 0) //timeout + return 0; + else //error + return -1; +} + +void vsem_signal(vsem_t vs) +{ + Semaphore_post(vs->sem); +} diff --git a/vslib/vsem.h b/vslib/vsem.h new file mode 100644 index 0000000..e0ec07f --- /dev/null +++ b/vslib/vsem.h @@ -0,0 +1,17 @@ +/* + * vsem.h + * + * Created on: 2019-3-29 + * Author: Administrator + */ + +#ifndef VSEM_H_ +#define VSEM_H_ + +typedef struct vsem *vsem_t; +vsem_t vsem_new(void ); +void vsem_free(vsem_t vs); +int vsem_wait(vsem_t vs, unsigned int timeout /*0-forever*/); +void vsem_signal(vsem_t vs); + +#endif /* VSEM_H_ */ diff --git a/vslib/vtask.c b/vslib/vtask.c new file mode 100644 index 0000000..528412e --- /dev/null +++ b/vslib/vtask.c @@ -0,0 +1,89 @@ +/* + * vtask.c + * + * Created on: 2019-3-22 + * Author: Administrator + */ +#include +#include + +#include + +#include +#include +#include +#include + +#include "vtask.h" + + +struct vtask { + Task_Handle task; +}; + +vtask_t vtask_create(void (*vtk_pthread)(void )) +{ + Task_Params taskParams; + Error_Block eb; + vtask_t tk; + + if (!(tk = malloc(sizeof *tk))) + goto _err; + + /* Create 1 task with priority 15 */ + Error_init(&eb); + Task_Params_init(&taskParams); + taskParams.stackSize = VTASK_STACK_SIZE; + taskParams.priority = OS_TASKPRINORM; + if (!(tk->task = Task_create((Task_FuncPtr)vtk_pthread, &taskParams, &eb))) + goto _err; + + return tk; + +_err: + printf("vtask create failed\n"); + if (tk) + free(tk); + return NULL; +} + +vtask_t vtask_create_2(void (*vtk_pthread)(void *arg), void *arg) +{ + Task_Params taskParams; + Error_Block eb; + vtask_t tk; + + if (!(tk = malloc(sizeof *tk))) + goto _err; + + /* Create 1 task with priority 15 */ + Error_init(&eb); + Task_Params_init(&taskParams); + taskParams.arg0 = (UArg)arg; + taskParams.stackSize = VTASK_STACK_SIZE; + taskParams.priority = OS_TASKPRINORM; + if (!(tk->task = Task_create((Task_FuncPtr)vtk_pthread, &taskParams, &eb))) + goto _err; + + return tk; + +_err: + printf("vtask create failed\n"); + if (tk) + free(tk); + return NULL; +} + +void vtask_delete(vtask_t tk) +{ + if (tk) { + Task_delete(&tk->task); + free(tk); + } +} + +void vtask_sleep(int ms) +{ + Task_yield(); + Task_sleep(ms); +} diff --git a/vslib/vtask.h b/vslib/vtask.h new file mode 100644 index 0000000..bc4e185 --- /dev/null +++ b/vslib/vtask.h @@ -0,0 +1,23 @@ +/* + * vtask.h + * + * Created on: 2019-3-22 + * Author: abagu + */ + +#ifndef VTASK_H_ +#define VTASK_H_ + +#include + +//#define VTASK_PRI_LEVEL_DEFAULT OS_TASKPRINORM // priority low 0 --> high 31 +#define VTASK_STACK_SIZE (0x80000) //stack size 512K + +typedef struct vtask *vtask_t; +vtask_t vtask_create(void (*vtk_pthread)(void)); +vtask_t vtask_create_2(void (*vtk_pthread)(void *arg), void *arg); +void vtask_delete(vtask_t tk); + +void vtask_sleep(int ms); + +#endif /* VTASK_H_ */ diff --git a/vslib/vtelnetd.c b/vslib/vtelnetd.c new file mode 100644 index 0000000..af2754d --- /dev/null +++ b/vslib/vtelnetd.c @@ -0,0 +1,217 @@ +/* + * vtelnet.c + * + * Created on: 2019-5-30 + * Author: Administrator + */ +#include +#include +#include +#include +#include +#include +#include "vconsole.h" +#include "vtelnetd.h" + +#define MORE_THAN_CLIENTS "Error: more than max clients. \r\n" +#define THREAD_ERROR "Error: create thread failed.\r\n" + +struct vtelnetd; + +typedef struct telnet_client { + struct vtelnetd *vtld; + int login; + SOCKET sock; + SA addr; + int err; +}telnet_client_s; + +typedef struct vtelnetd { + int inited; + Semaphore_Handle mutex; + telnet_client_s client[TELNET_CLINET_MAX_COUNT]; +}vtelnetd_s; + +static vtelnetd_s _vtld = {0}; + +static void __report_error(HANDLE fd, const char *s) +{ + send(fd, (void *)s, strlen(s), 0); + fdClose(fd); +} + +static int do_puts(void *param, const char *s, int len) +{ + int i, sendlen = 0; + char line[260]; + telnet_client_s *tc = (telnet_client_s *)param; + + if (len < 0) + len = strlen(s); + + for (i = 0; i < len; ++i) { + line[i] = s[i]; + if (line[i] == ' ' && i > 0 && line[i-1] == '\r') { + len -= 1; + i--; + } + } + + if (tc) { + sendlen = send(tc->sock, (void *)line, len, 0); + if (sendlen != len) { + printf("sock send fail \r\n"); + tc->err = 1; + } + + //fd1 task can schedule by time + Task_sleep(1); + } + + return sendlen; +} + +//static int do_getch(void *priv) +//{ +// int ret = 0; +// char ch = 0; +// telnet_client_s *tc = (telnet_client_s *)priv; +// if (tc) { +// ret = recv(tc->sock, &ch, 1, 0); +// if (ret == 0) { +// ch = 0; +// } else if (ret < 0) { +// if (fdError() != EWOULDBLOCK) { +// printf("sock recv fail \r\n"); +// tc->err = 1; +// return 0; +// } +// } +// } +// +// return ch; +//} + +static int do_gets(void *param, char *buf, int len, int blocking) +{ + int n = 0, retlen, errno; + telnet_client_s *tc = (telnet_client_s *)param; + + if (tc) { + while(n < len) { + retlen = recv(tc->sock, buf + n, len - n, 0); + if (retlen < 0) { + errno = fdError(); + if (errno == EWOULDBLOCK) { + continue; + } else { + break; + } + } else if (retlen == 0) { + break; + } else { + n += retlen; + } + } + } + + return n; +} + +static void __console_client_task(UINT32 arg) +{ + telnet_client_s *tc = (telnet_client_s *)arg; + vtelnetd_s *vtld = tc->vtld; + struct vconsole_param cp; + vconsole_t cs; + + fdOpenSession(TaskSelf()); + do_puts(tc, "welcome vsky telnet \r\n", -1); + + cp.debug_console = 0; + cp.prompt = "telnet> "; + cp.param = tc; + cp.puts = do_puts; + cp.gets = do_gets; + cs = vconsole_new(&cp); + if (cs == NULL) { + printf("console new fail \r\n"); + return; + } + + while(tc->login) { + vconsole_loop(cs); + Task_sleep(5); + + if (tc->err) + break; + + if (vconsole_status(cs)) + break; + } + + printf("vsky telnet exit \r\n"); + vconsole_free(cs); + Semaphore_pend(vtld->mutex, BIOS_WAIT_FOREVER); + fdClose(tc->sock); + tc->login = 0; + Semaphore_post(vtld->mutex); + + Task_exit(); +} + +void vtelnetd_init(void ) +{ + _vtld.mutex = Semaphore_create(1, NULL, NULL); + _vtld.inited = 1; +} + +SOCKET vtelnetd_client(PSA client) +{ + int i; + HANDLE fd1, fd2; + vtelnetd_s *vtld = &_vtld; + telnet_client_s *tc = NULL; + + /* Create the local pipe - abort on error */ + if (pipe(&fd1, &fd2 ) != 0) + return(INVALID_SOCKET); + + /* find free telnet client instance. */ + Semaphore_pend(vtld->mutex, BIOS_WAIT_FOREVER); + for (i = 0; i < TELNET_CLINET_MAX_COUNT; ++i) { + if (!vtld->client[i].login) { + tc = &vtld->client[i]; + tc->vtld = vtld; + tc->login = 1; + tc->sock = fd2; + tc->addr = *client; + tc->err = 0; + if (NULL == TaskCreate(__console_client_task, NULL, OS_TASKPRINORM, 0x80000, (UINT32)tc, 0, 0)) { + __report_error(fd2, THREAD_ERROR); + tc->login = 0; + } + break; + } + } + Semaphore_post(vtld->mutex); + + /* no free telnet clinet instance. */ + if (!tc) + __report_error(fd2, MORE_THAN_CLIENTS); + + return (SOCKET) fd1; +} + +void nvt_print(char *fmt, ...) +{ + char buf[260] = {0}; + int len; + + va_list arg_ptr; + va_start( arg_ptr, fmt ); + len = vsprintf( (char *)buf, fmt, arg_ptr ); + va_end( arg_ptr ); + + (void) len; +} diff --git a/vslib/vtelnetd.h b/vslib/vtelnetd.h new file mode 100644 index 0000000..f12dc02 --- /dev/null +++ b/vslib/vtelnetd.h @@ -0,0 +1,23 @@ +/* + * vtelnet.h + * + * Created on: 2019-5-30 + * Author: Administrator + */ + +#ifndef VTELNET_H_ +#define VTELNET_H_ + +#include + +#define TELNET_CLINET_MAX_COUNT 2 +extern void vtelnetd_init(void ); +extern SOCKET vtelnetd_client(PSA client); + +/* +* NVT: Net Virtual Terminal +*/ +extern int nvt_puts(const char *str, int len); +extern void nvt_print(char *fmt, ...); + +#endif /* VTELNET_H_ */ diff --git a/vslib/vtftp.c b/vslib/vtftp.c new file mode 100644 index 0000000..90a4f96 --- /dev/null +++ b/vslib/vtftp.c @@ -0,0 +1,55 @@ +/* + * vtftp.c + * + * Created on: 2019-6-10 + * Author: Administrator + */ +#include +#include +#include +#include +#include +#include +#include + +#include "vcmd.h" + +uint32_t vtftp_load_file(char *ip, char *file, uint32_t mem) +{ + IPN ipInt; + uint32_t filesize = 0x2000000; // 32M; + + printf("tftp download file %s from %s to mem: 0x%x \r\n", file, ip, mem); + ConStrToIPN(ip, &ipInt); + if (NtTftpRecv(ipInt, file, (char *)mem, &filesize, NULL) < 0) { + printf("tftp download fail \r\n"); + return 0; + } + + printf("tftp file download success and filesize: %d \r\n", filesize); + + return filesize; +} + +int do_tftp(cmd_tbl_t tbl, int argc, char *argv[]) +{ + char *ip; + char *filename; + uint32_t mem_bass; + //IPN ipInt; + //uint32_t filesize = 0x2000000; // 32M + + if (argc != 4) { + printf("usage: %s [ip] [file] [mem_addr] \r\n", argv[0]); + return -1; + } + + ip = argv[1]; + filename = argv[2]; + mem_bass = strtoul(argv[3], NULL, 0); + vtftp_load_file(ip, filename, mem_bass); + + return 0; +} + +CON_CMD(tftp, "tftp down", NULL, do_tftp) diff --git a/vslib/vtftp.h b/vslib/vtftp.h new file mode 100644 index 0000000..7770485 --- /dev/null +++ b/vslib/vtftp.h @@ -0,0 +1,13 @@ +/* + * vtftp.h + * + * Created on: 2019-6-10 + * Author: Administrator + */ + +#ifndef VTFTP_H_ +#define VTFTP_H_ + +extern uint32_t vtftp_load_file(char *ip, char *file, uint32_t mem); + +#endif /* VTFTP_H_ */ diff --git a/vslib/vtimer.c b/vslib/vtimer.c new file mode 100644 index 0000000..7f83525 --- /dev/null +++ b/vslib/vtimer.c @@ -0,0 +1,75 @@ +/* + * vtimer.c + * + * Created on: 2019-7-10 + * Author: Administrator + */ + +#include +#include + +#include +#include + +#include +#include + +#include "vtimer.h" +struct timer { + Clock_Handle clk; + void *param; + void (*on_time)(void *param); +}; + +static Void _clkFxn(UArg arg0) +{ + timer_t tm = (timer_t)arg0; + if (tm && tm->on_time) + tm->on_time(tm->param); +} + +timer_t timer_new(int tick_ms, void *param, void (*on_time)(void *param)) +{ + timer_t tm; + Clock_Params clkParams; + + tm = calloc(1, sizeof *tm); + if (tm) { + Clock_Params_init(&clkParams); + clkParams.period = tick_ms; + clkParams.startFlag = 0; //FLASE + clkParams.arg = (UArg)tm; + tm->clk = Clock_create(_clkFxn, tick_ms, &clkParams, NULL); + tm->param = param; + tm->on_time = on_time; + } + + return tm; +} + +void timer_free(timer_t tm) +{ + if (tm) { + Clock_delete(&tm->clk); + free(tm); + } +} + +int timer_active(timer_t tm) +{ + if (tm) + Clock_start(tm->clk); + return 0; +} + +int timer_deactive(timer_t tm) +{ + if (tm) + Clock_stop(tm->clk); + return 0; +} + +unsigned int time_millis(void ) +{ + return (unsigned int)Clock_getTicks(); +} diff --git a/vslib/vtimer.h b/vslib/vtimer.h new file mode 100644 index 0000000..94ca12d --- /dev/null +++ b/vslib/vtimer.h @@ -0,0 +1,20 @@ +/* + * vtimer.h + * + * Created on: 2019-7-10 + * Author: Administrator + */ + +#ifndef VTIMER_H_ +#define VTIMER_H_ + +typedef struct timer *timer_t; + +timer_t timer_new(int tick_ms, void *param, void (*on_time)(void *param)); +void timer_free(timer_t tm); +int timer_active(timer_t tm); +int timer_deactive(timer_t tm); + +unsigned int time_millis(void ); + +#endif /* VTIMER_H_ */