new file mode 100644
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: MIT
+/* Copyright(c) 2019-2021, Celeno Communications Ltd. */
+
+#include "phy/phy.h"
+#include "reg/reg_modem_gcu.h"
+#include "reg/reg_cmu.h"
+#include "reg/reg_mac_hw.h"
+#include "reg/reg_mac_hw_mu.h"
+#include "reg/reg_macsys_gcu.h"
+#include "reg/reg_lcu_phy.h"
+#include "rf_boot.h"
+#include "dsp.h"
+
+static void ceva_disable(struct cl_hw *cl_hw)
+{
+ struct cl_chip *chip = cl_hw->chip;
+
+ if (cl_hw_is_tcv0(cl_hw)) {
+ cmu_phy_0_rst_ceva_0_global_rst_n_setf(chip, 0);
+ cmu_phy_0_clk_en_ceva_0_clk_en_setf(chip, 0);
+ } else {
+ cmu_phy_1_rst_ceva_1_global_rst_n_setf(chip, 0);
+ cmu_phy_1_clk_en_ceva_1_clk_en_setf(chip, 0);
+ }
+}
+
+static void ceva_reset(struct cl_hw *cl_hw)
+{
+ if (cl_hw_is_tcv0(cl_hw))
+ cmu_phy_0_rst_set(cl_hw->chip, CMU_PHY0_RST_EN);
+ else
+ cmu_phy_1_rst_set(cl_hw->chip, CMU_PHY1_RST_EN);
+}
+
+static void phy_disable(struct cl_hw *cl_hw)
+{
+ /* Modem GCU modules - Reset */
+
+ /* Disable clocks (reset is not asserted) */
+ modem_gcu_mpu_set(cl_hw, MODEM_GCU_MPU_RST_N_BIT | MODEM_GCU_MPU_REG_RST_N_BIT);
+ modem_gcu_bpu_set(cl_hw, MODEM_GCU_BPU_RST_N_BIT | MODEM_GCU_BPU_RX_RST_N_BIT |
+ MODEM_GCU_BPU_TX_RST_N_BIT | MODEM_GCU_BPU_REG_RST_N_BIT);
+ modem_gcu_tfu_set(cl_hw, MODEM_GCU_TFU_RST_N_BIT | MODEM_GCU_TFU_REG_RST_N_BIT);
+ modem_gcu_smu_set(cl_hw, MODEM_GCU_SMU_RST_N_BIT | MODEM_GCU_SMU_REG_RST_N_BIT);
+ modem_gcu_spu_set(cl_hw, MODEM_GCU_SPU_RST_N_BIT | MODEM_GCU_SPU_REG_RST_N_BIT);
+ modem_gcu_bf_set(cl_hw, MODEM_GCU_BF_RST_N_BIT | MODEM_GCU_BF_REG_RST_N_BIT);
+ modem_gcu_epa_set(cl_hw, MODEM_GCU_EPA_RST_N_BIT | MODEM_GCU_EPA_REG_RST_N_BIT);
+ modem_gcu_lcu_set(cl_hw, MODEM_GCU_LCU_HLF_RST_N_BIT | MODEM_GCU_LCU_RST_N_BIT |
+ MODEM_GCU_LCU_REG_RST_N_BIT);
+ modem_gcu_mux_fic_set(cl_hw, MODEM_GCU_MUX_FIC_SOFT_RST_N_BIT |
+ MODEM_GCU_MUX_FIC_RST_N_BIT);
+ modem_gcu_riu_clk_set(cl_hw, 0);
+ modem_gcu_riu_clk_1_set(cl_hw, 0);
+
+ /* Assert reset (clocks already disabled) */
+ modem_gcu_mpu_set(cl_hw, 0);
+ modem_gcu_bpu_set(cl_hw, 0);
+ modem_gcu_tfu_set(cl_hw, 0);
+ modem_gcu_smu_set(cl_hw, 0);
+ modem_gcu_spu_set(cl_hw, 0);
+ modem_gcu_bf_set(cl_hw, 0);
+ modem_gcu_epa_set(cl_hw, 0);
+ modem_gcu_lcu_set(cl_hw, 0);
+ modem_gcu_mux_fic_set(cl_hw, MODEM_GCU_MUX_FIC_SOFT_RST_N_BIT);
+ modem_gcu_riu_rst_set(cl_hw, 0);
+}
+
+static void phy_reset(struct cl_hw *cl_hw)
+{
+ /* Isolate FIC bus in case CEVA reset is not required */
+ modem_gcu_mux_fic_config_fic_isolate_setf(cl_hw, 1);
+ while (modem_gcu_mux_fic_config_fic_isolated_getf(cl_hw) == 0)
+ ;
+ modem_gcu_mux_fic_config_fic_isolate_setf(cl_hw, 0);
+
+ /*
+ * Stop the LCU recording.
+ * Stop on one channel actually stops the recording on all channels.
+ * This stop is required because PHY LCU is going to be reset.
+ */
+ if (cl_hw_is_tcv0(cl_hw))
+ lcu_phy_lcu_ch_0_stop_set(cl_hw, 1);
+ else
+ lcu_phy_lcu_ch_1_stop_set(cl_hw, 1);
+
+ /* PHY reset sequence */
+ phy_disable(cl_hw);
+}
+
+static void phy0_off(struct cl_chip *chip)
+{
+ /* Disable APB0 clock but keep other clocks (main clock and DSP0 clock) active */
+ cmu_phy_0_clk_en_pack(chip, 1, 0, 1);
+ /* DSP0, MPIF0, APB0 reset */
+ cmu_phy_0_rst_set(chip, CMU_PHY_0_RST_N_BIT);
+ /* DSP0, MPIF0 are still in reset, but take APB0 out of reset to allow writing to GCU */
+ cmu_phy_0_rst_set(chip, CMU_PHY_0_RST_N_BIT | CMU_PHY_0_PRESET_N_BIT);
+ /* Enable APB0 clock (all other clocks are already active) */
+ cmu_phy_0_clk_en_phy_0_apb_clk_en_setf(chip, 1);
+}
+
+static void phy1_off(struct cl_chip *chip)
+{
+ /* Disable APB0 clock but keep other clocks (main clock and DSP0 clock) active */
+ cmu_phy_1_clk_en_pack(chip, 1, 0, 1);
+ /* DSP0, MPIF0, APB0 reset */
+ cmu_phy_1_rst_set(chip, CMU_PHY_0_RST_N_BIT);
+ /* DSP0, MPIF0 are still in reset, but take APB0 out of reset to allow writing to GCU */
+ cmu_phy_1_rst_set(chip, CMU_PHY_0_RST_N_BIT | CMU_PHY_0_PRESET_N_BIT);
+ /* Enable APB0 clock (all other clocks are already active) */
+ cmu_phy_1_clk_en_phy_1_apb_clk_en_setf(chip, 1);
+}
+
+static void phy_off(struct cl_hw *cl_hw)
+{
+ struct cl_chip *chip = cl_hw->chip;
+
+ if (cl_hw_is_tcv0(cl_hw))
+ phy0_off(chip);
+ else
+ phy1_off(chip);
+
+ phy_disable(cl_hw);
+}
+
+static void system_ctrl_reg_reset(struct cl_hw *cl_hw)
+{
+ struct cl_chip *chip = cl_hw->chip;
+ u32 regval = macsys_gcu_xt_control_get(chip);
+ u8 i;
+
+ /* Set XMAC_RUN_STALL */
+ regval |= cl_hw->controller_reg.run_stall;
+ /* Clear XMAC_OCD_HALT_ON_RESET and clear XMAC_BRESET */
+ regval &= ~(cl_hw->controller_reg.ocd_halt_on_reset | cl_hw->controller_reg.breset);
+
+ /* Reset MACHW */
+ macsys_gcu_xt_control_set(chip, regval);
+
+ for (i = 0; i < cl_hw->max_mu_cnt; i++)
+ mac_hw_mu_mac_cntrl_2_set(cl_hw, 1, i);
+}
+
+void cl_phy_off(struct cl_hw *cl_hw)
+{
+ system_ctrl_reg_reset(cl_hw);
+ phy_off(cl_hw);
+ ceva_disable(cl_hw);
+}
+
+static void save_ela_state(struct cl_hw *cl_hw)
+{
+ struct cl_recovery_db *recovery_db = &cl_hw->recovery_db;
+
+ /* Save eLA state before MAC-HW reset */
+ recovery_db->ela_en = mac_hw_debug_port_en_get(cl_hw);
+
+ if (recovery_db->ela_en) {
+ recovery_db->ela_sel_a = mac_hw_debug_port_sel_a_get(cl_hw);
+ recovery_db->ela_sel_b = mac_hw_debug_port_sel_b_get(cl_hw);
+ recovery_db->ela_sel_c = mac_hw_debug_port_sel_c_get(cl_hw);
+ }
+}
+
+void cl_phy_reset(struct cl_hw *cl_hw)
+{
+ save_ela_state(cl_hw);
+ system_ctrl_reg_reset(cl_hw);
+ phy_reset(cl_hw);
+ ceva_reset(cl_hw);
+}
+
+int cl_phy_load_recovery(struct cl_hw *cl_hw)
+{
+ int ret = cl_rf_boot_recovery(cl_hw);
+
+ if (ret) {
+ cl_dbg_err(cl_hw, "cl_rf_boot_recovery failed %d\n", ret);
+ return ret;
+ }
+
+ /* Load DSP */
+ ret = cl_dsp_load_recovery(cl_hw);
+ if (ret) {
+ cl_dbg_err(cl_hw, "cl_dsp_load_recovery failed %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * FIXME: It looks like there is a bug in the DSP. If we poll REG_CFG_SPACE
+ * (0x600018) at this point to verify that DSP has been initialized
+ * successfully, we read '1' and continue.
+ * However, the calibration fails.
+ *
+ * Please note that this is a WORKAROUND, not a final fix.
+ * The problem should be investigated
+ * further by the DSP team.
+ */
+ msleep(500);
+ return 0;
+}
+
+int cl_phy_data_alloc(struct cl_hw *cl_hw)
+{
+ struct cl_phy_data *buf = NULL;
+ u32 len = (u32)PAGE_SIZE;
+ dma_addr_t phys_dma_addr;
+
+ buf = dma_alloc_coherent(cl_hw->chip->dev, len, &phys_dma_addr, GFP_KERNEL);
+
+ if (!buf)
+ return -1;
+
+ cl_hw->phy_data_info.data = buf;
+ cl_hw->phy_data_info.dma_addr = cpu_to_le32(phys_dma_addr);
+
+ return 0;
+}
+
+void cl_phy_data_free(struct cl_hw *cl_hw)
+{
+ dma_addr_t phys_dma_addr = le32_to_cpu(cl_hw->phy_data_info.dma_addr);
+
+ if (!cl_hw->phy_data_info.data)
+ return;
+
+ dma_free_coherent(cl_hw->chip->dev, PAGE_SIZE,
+ (void *)cl_hw->phy_data_info.data, phys_dma_addr);
+ cl_hw->phy_data_info.data = NULL;
+}
+
+void cl_phy_enable(struct cl_hw *cl_hw)
+{
+ /* Modem GCU modules - De-assert Reset */
+
+ /* De-assert reset (clocks disabled) */
+ modem_gcu_mpu_set(cl_hw, MODEM_GCU_MPU_RST_N_BIT | MODEM_GCU_MPU_REG_RST_N_BIT);
+ modem_gcu_bpu_set(cl_hw, MODEM_GCU_BPU_RST_N_BIT | MODEM_GCU_BPU_RX_RST_N_BIT |
+ MODEM_GCU_BPU_TX_RST_N_BIT | MODEM_GCU_BPU_REG_RST_N_BIT);
+ modem_gcu_tfu_set(cl_hw, MODEM_GCU_TFU_RST_N_BIT | MODEM_GCU_TFU_REG_RST_N_BIT);
+ modem_gcu_smu_set(cl_hw, MODEM_GCU_SMU_RST_N_BIT | MODEM_GCU_SMU_REG_RST_N_BIT);
+ modem_gcu_spu_set(cl_hw, MODEM_GCU_SPU_RST_N_BIT | MODEM_GCU_SPU_REG_RST_N_BIT);
+ modem_gcu_bf_set(cl_hw, MODEM_GCU_BF_RST_N_BIT | MODEM_GCU_BF_REG_RST_N_BIT);
+ modem_gcu_epa_set(cl_hw, MODEM_GCU_EPA_RST_N_BIT | MODEM_GCU_EPA_REG_RST_N_BIT);
+ modem_gcu_lcu_set(cl_hw, MODEM_GCU_LCU_HLF_RST_N_BIT | MODEM_GCU_LCU_RST_N_BIT |
+ MODEM_GCU_LCU_REG_RST_N_BIT);
+ modem_gcu_mux_fic_set(cl_hw, MODEM_GCU_MUX_FIC_SOFT_RST_N_BIT |
+ MODEM_GCU_MUX_FIC_RST_N_BIT);
+ modem_gcu_riu_rst_set(cl_hw, MODEM_GCU_RIU_FE_RST_N_BIT | MODEM_GCU_RIU_AGC_RST_N_BIT |
+ MODEM_GCU_RIU_MDM_B_RST_N_BIT | MODEM_GCU_RIU_LB_RST_N_BIT |
+ MODEM_GCU_RIU_RC_RST_N_BIT | MODEM_GCU_RIU_RADAR_RST_N_BIT |
+ MODEM_GCU_RIU_RST_N_BIT | MODEM_GCU_RIU_REG_RST_N_BIT);
+
+ /* Enable clocks */
+ modem_gcu_mpu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_bpu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_tfu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_smu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_spu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_bf_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_epa_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_lcu_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_mux_fic_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_riu_clk_set(cl_hw, 0xFFFFFFFF);
+ modem_gcu_riu_clk_1_set(cl_hw, 0xFFFFFFFF);
+
+ /*
+ * Configure minimum latency between 2 masters connected
+ * to same FIC, Read/Write transaction
+ */
+ modem_gcu_mux_fic_config_set(cl_hw, 0x00000808);
+}