new file mode 100644
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "rf_boot.h"
+#include "hw.h"
+#include "afe.h"
+#include "utils/file.h"
+#include "phy/phy.h"
+#include "reg/reg_access.h"
+#include "reg/reg_cmu.h"
+#include "reg/reg_modem_gcu.h"
+#include "reg/reg_ricu.h"
+#include "reg/reg_riu.h"
+#include "reg/reg_mac_hw.h"
+#include "reg/reg_lcu_phy.h"
+
+static void cl_clk_init(struct cl_chip *chip)
+{
+ cmu_clk_en_set(chip, CMU_MAC_ALL_CLK_EN);
+
+ cmu_phy_0_clk_en_set(chip, CMU_PHY_0_APB_CLK_EN_BIT | CMU_PHY_0_MAIN_CLK_EN_BIT);
+ cmu_phy_1_clk_en_set(chip, CMU_PHY_1_APB_CLK_EN_BIT | CMU_PHY_1_MAIN_CLK_EN_BIT);
+
+ cmu_phy_0_rst_ceva_0_global_rst_n_setf(chip, 0);
+ modem_gcu_ceva_ctrl_external_wait_setf(chip->cl_hw_tcv0, 1);
+ cmu_phy_0_rst_ceva_0_global_rst_n_setf(chip, 1);
+
+ cmu_phy_1_rst_ceva_1_global_rst_n_setf(chip, 0);
+ modem_gcu_ceva_ctrl_external_wait_setf(chip->cl_hw_tcv1, 1);
+ cmu_phy_1_rst_ceva_1_global_rst_n_setf(chip, 1);
+
+ cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 1);
+ cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 1);
+}
+
+static int cl_pll1_init(struct cl_chip *chip)
+{
+ int retry = 0;
+
+ /* Verify pll is locked */
+ while (!cmu_pll_1_stat_pll_lock_getf(chip) && (++retry < 10)) {
+ cl_dbg_chip_verbose(chip, "retry=%d\n", retry);
+ usleep_range(100, 200);
+ }
+
+ /* Pll is not locked - fatal error */
+ if (retry == 10) {
+ cl_dbg_chip_err(chip, "retry limit reached - pll is not locked !!!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int cl_cmu_init(struct cl_chip *chip)
+{
+ if (cl_pll1_init(chip))
+ return -1;
+
+ /* Set gl_mux_sel bit to work with pll1 instead of crystal */
+ cmu_control_gl_mux_sel_setf(chip, 1);
+
+ cmu_rst_n_ricurst_setf(chip, 1);
+
+ cmu_phy_0_rst_set(chip, CMU_PHY0_RST_EN);
+ cmu_phy_1_rst_set(chip, CMU_PHY1_RST_EN);
+ cmu_phy_0_rst_set(chip, 0x0);
+ cmu_phy_1_rst_set(chip, 0x0);
+ cmu_rst_n_ricurst_setf(chip, 1);
+ cmu_phy_0_rst_set(chip, CMU_PHY0_RST_EN);
+ cmu_phy_1_rst_set(chip, CMU_PHY1_RST_EN);
+
+ return 0;
+}
+
+static void cl_riu_clk_bw_init(struct cl_hw *cl_hw)
+{
+ u32 regval;
+
+ switch (cl_hw->conf->ce_channel_bandwidth) {
+ case CHNL_BW_20:
+ regval = 0x00000100;
+ break;
+ case CHNL_BW_40:
+ regval = 0x00000555;
+ break;
+ case CHNL_BW_160:
+ regval = 0x00000dff;
+ break;
+ case CHNL_BW_80:
+ default:
+ regval = 0x000009aa;
+ break;
+ }
+
+ /* Set RIU modules clock BW */
+ modem_gcu_riu_clk_bw_set(cl_hw, regval);
+}
+
+static int cl_load_riu_rsf_memory(struct cl_chip *chip, const char *filename)
+{
+ struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+ struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+ char *buf = NULL;
+ loff_t size, i = 0;
+ int ret = 0;
+
+ size = cl_file_open_and_read(chip, filename, &buf);
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (size > RIU_RSF_FILE_SIZE) {
+ ret = -EFBIG;
+ goto out;
+ }
+
+ /* Enable re-sampling filter init. */
+ riu_rsf_control_rsf_init_en_setf(cl_hw_tcv0, 0x1);
+ if (cl_hw_tcv1)
+ riu_rsf_control_rsf_init_en_setf(cl_hw_tcv1, 0x1);
+
+ while (i < size) {
+ riu_rsf_init_set(cl_hw_tcv0, *(u32 *)&buf[i]);
+ if (cl_hw_tcv1)
+ riu_rsf_init_set(cl_hw_tcv1, *(u32 *)&buf[i]);
+ i += 4;
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static int cl_load_riu_rsf_memory_recovery(struct cl_hw *cl_hw, const char *filename)
+{
+ struct cl_chip *chip = cl_hw->chip;
+ char *buf = NULL;
+ loff_t size, i = 0;
+ int ret = 0;
+
+ size = cl_file_open_and_read(chip, filename, &buf);
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (size > RIU_RSF_FILE_SIZE) {
+ ret = -EFBIG;
+ goto out;
+ }
+
+ /* Enable re-sampling filter init. */
+ riu_rsf_control_rsf_init_en_setf(cl_hw, 0x1);
+
+ while (i < size) {
+ riu_rsf_init_set(cl_hw, *(u32 *)&buf[i]);
+ i += 4;
+ }
+
+out:
+ kfree(buf);
+ return ret;
+}
+
+static int cl_load_agc_data(struct cl_hw *cl_hw, const char *filename)
+{
+ struct cl_chip *chip = cl_hw->chip;
+ char *buf = NULL;
+ loff_t size, i = 0;
+
+ size = cl_file_open_and_read(chip, filename, &buf);
+
+ if (!buf)
+ return -ENOMEM;
+
+ /* Enable AGC FSM ram init state */
+ riu_agcfsm_ram_init_1_agc_fsm_ram_init_en_setf(cl_hw, 0x1);
+ /* Start writing the firmware from WPTR=0 */
+ riu_agcfsm_ram_init_1_agc_fsm_ram_init_wptr_setf(cl_hw, 0x0);
+ /* Allow WPTR register to be writable */
+ riu_agcfsm_ram_init_1_agc_fsm_ram_init_wptr_set_setf(cl_hw, 0x1);
+ /* Enable auto increment WPTR by 1 upon any write */
+ riu_agcfsm_ram_init_1_agc_fsm_ram_init_ainc_1_setf(cl_hw, 0x1);
+
+ while (i < size) {
+ riu_agcfsm_ram_init_2_set(cl_hw, *(u32 *)&buf[i]);
+ i += 4;
+ }
+
+ /* Disable AGC FSM ram init state */
+ riu_agcfsm_ram_init_1_agc_fsm_ram_init_en_setf(cl_hw, 0x0);
+
+ kfree(buf);
+
+ return 0;
+}
+
+static int cl_load_agc_fw(struct cl_hw *cl_hw, const char *filename)
+{
+ int ret = 0;
+
+ /* Switch AGC to programming mode */
+
+ /* Disable RIU Modules clocks (RC,LB,ModemB,FE,ADC,Regs,AGC,Radar) */
+ modem_gcu_riu_clk_set(cl_hw, 0);
+
+ /* Switch AGC MEM clock to 480MHz */
+ modem_gcu_riu_clk_bw_agc_mem_clk_bw_setf(cl_hw, 3);
+
+ /* Enable RIU Modules clocks (RC,LB,ModemB,FE,ADC,Regs,AGC,Radar) */
+ modem_gcu_riu_clk_set(cl_hw, 0xFFFFFFFF);
+
+ /* Assert AGC FSM reset */
+ riu_rwnxagccntl_agcfsmreset_setf(cl_hw, 1);
+
+ /* Load AGC RAM data */
+ ret = cl_load_agc_data(cl_hw, filename);
+ if (ret)
+ goto out;
+
+ /* Switch AGC back to operational mode */
+
+ /* Disable RIU Modules clocks (RC, LB, ModemB, FE, ADC, Regs, AGC, Radar) */
+ modem_gcu_riu_clk_set(cl_hw, 0);
+ /* Switch AGC MEM clock back to 80M */
+ modem_gcu_riu_clk_bw_agc_mem_clk_bw_setf(cl_hw, 1);
+ /* Enable RIU Modules clocks (RC, LB, ModemB, FE, ADC, Regs, AGC, Radar) */
+ modem_gcu_riu_clk_set(cl_hw, 0xFFFFFFFF);
+
+ /* Release AGC FSM reset */
+ riu_rwnxagccntl_agcfsmreset_setf(cl_hw, 0);
+
+out:
+ return ret;
+}
+
+int cl_rf_boot(struct cl_chip *chip)
+{
+ int ret = 0;
+ struct cl_hw *cl_hw_tcv0 = chip->cl_hw_tcv0;
+ struct cl_hw *cl_hw_tcv1 = chip->cl_hw_tcv1;
+
+ /* Call only once per chip after ASIC reset - configure both phys */
+ ret = cl_cmu_init(chip);
+ if (ret != 0)
+ goto out;
+
+ cl_clk_init(chip);
+ cmu_phase_sel_set(chip, (CMU_GP_CLK_PHASE_SEL_BIT |
+ CMU_DAC_CDB_CLK_PHASE_SEL_BIT |
+ CMU_DAC_CLK_PHASE_SEL_BIT) &
+ ~(CMU_ADC_CDB_CLK_PHASE_SEL_BIT |
+ CMU_ADC_CLK_PHASE_SEL_BIT));
+
+ mac_hw_mac_cntrl_1_active_clk_gating_setf(cl_hw_tcv0, 1); /* Disable MPIF clock */
+ mac_hw_state_cntrl_next_state_setf(cl_hw_tcv0, 2); /* Move to doze */
+
+ mac_hw_mac_cntrl_1_active_clk_gating_setf(cl_hw_tcv1, 1); /* Disable MPIF clock */
+ mac_hw_state_cntrl_next_state_setf(cl_hw_tcv1, 2); /* Move to doze */
+
+ /* Enable all PHY modules */
+ cl_phy_enable(cl_hw_tcv0);
+ cl_phy_enable(cl_hw_tcv1);
+
+ mac_hw_doze_cntrl_2_wake_up_sw_setf(cl_hw_tcv0, 1); /* Exit from doze */
+ mac_hw_state_cntrl_next_state_setf(cl_hw_tcv0, 0); /* Move to idle */
+
+ mac_hw_doze_cntrl_2_wake_up_sw_setf(cl_hw_tcv1, 1); /* Exit from doze */
+ mac_hw_state_cntrl_next_state_setf(cl_hw_tcv1, 0); /* Move to idle */
+
+ cl_riu_clk_bw_init(cl_hw_tcv0);
+ cl_riu_clk_bw_init(cl_hw_tcv1);
+
+ /* Load RIU re-sampling filter memory with coefficients */
+ ret = cl_load_riu_rsf_memory(chip, "riu_rsf.bin");
+ if (ret != 0) {
+ pr_err("cl_load_riu_rsf_memory failed with error code %d.\n", ret);
+ goto out;
+ }
+
+ /* Load AGC FW */
+ ret = cl_load_agc_fw(cl_hw_tcv0, "agcram.bin");
+ if (ret) {
+ pr_err("cl_load_agc_fw failed for tcv0 with error code %d.\n", ret);
+ goto out;
+ }
+
+ ret = cl_load_agc_fw(cl_hw_tcv1, "agcram.bin");
+ if (ret) {
+ pr_err("cl_load_agc_fw failed for tcv1 with error code %d.\n", ret);
+ goto out;
+ }
+
+ /* AFE Registers configuration */
+ ret = cl_afe_cfg(chip);
+
+out:
+ return ret;
+}
+
+static void restore_ela_state(struct cl_hw *cl_hw)
+{
+ struct cl_recovery_db *recovery_db = &cl_hw->recovery_db;
+
+ /* Restore eLA state after MAC-HW reset */
+ if (recovery_db->ela_en) {
+ mac_hw_debug_port_sel_a_set(cl_hw, recovery_db->ela_sel_a);
+ mac_hw_debug_port_sel_b_set(cl_hw, recovery_db->ela_sel_b);
+ mac_hw_debug_port_sel_c_set(cl_hw, recovery_db->ela_sel_c);
+ }
+
+ mac_hw_debug_port_en_set(cl_hw, recovery_db->ela_en);
+}
+
+int cl_rf_boot_recovery(struct cl_hw *cl_hw)
+{
+ int ret = 0;
+
+ mac_hw_mac_cntrl_1_active_clk_gating_setf(cl_hw, 1); /* Disable MPIF clock */
+ mac_hw_state_cntrl_next_state_setf(cl_hw, 2); /* Move to doze */
+
+ /* Enable all PHY modules */
+ cl_phy_enable(cl_hw);
+
+ /* Restart LCU recording */
+ if (cl_hw_is_tcv0(cl_hw))
+ lcu_phy_lcu_ch_0_stop_set(cl_hw, 0);
+ else
+ lcu_phy_lcu_ch_1_stop_set(cl_hw, 0);
+
+ restore_ela_state(cl_hw);
+
+ mac_hw_doze_cntrl_2_wake_up_sw_setf(cl_hw, 1); /* Exit from doze */
+ mac_hw_state_cntrl_next_state_setf(cl_hw, 0); /* Move to idle */
+
+ cl_riu_clk_bw_init(cl_hw);
+
+ /* Load RIU re-sampling filter memory with coefficients */
+ ret = cl_load_riu_rsf_memory_recovery(cl_hw, "riu_rsf.bin");
+ if (ret != 0) {
+ pr_err("cl_load_riu_rsf_memory failed with error code %d.\n", ret);
+ goto out;
+ }
+
+ /* Load AGC FW */
+ ret = cl_load_agc_fw(cl_hw, "agcram.bin");
+ if (ret) {
+ pr_err("cl_load_agc_fw failed for with error code %d.\n", ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}