/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include u8 get_freq_fsq(u8 freq) { if (freq == LP4X_DDR1600 || freq == LP4X_DDR2400) return FSP_0; else return FSP_1; } static void dramc_sw_imp_cal_vref_sel(u8 term_option, u8 impcal_stage) { u8 vref_sel = 0; if (term_option == ODT_ON) vref_sel = IMP_LP4X_TERM_VREF_SEL; else { switch (impcal_stage) { case IMPCAL_STAGE_DRVP: vref_sel = IMP_DRVP_LP4X_UNTERM_VREF_SEL; break; case IMPCAL_STAGE_DRVN: vref_sel = IMP_DRVN_LP4X_UNTERM_VREF_SEL; break; default: vref_sel = IMP_TRACK_LP4X_UNTERM_VREF_SEL; break; } } clrsetbits32(&ch[0].phy.shu[0].ca_cmd[11], 0x3f << 8, vref_sel << 8); } void dramc_sw_impedance_cal(const struct sdram_params *params, u8 term, struct dram_impedance *impedance) { u32 broadcast_bak, impcal_bak, imp_cal_result; u32 DRVP_result = 0xff, ODTN_result = 0xff, DRVN_result = 0x9; broadcast_bak = dramc_get_broadcast(); dramc_set_broadcast(DRAMC_BROADCAST_OFF); for (u8 chn = 0; chn < CHANNEL_MAX; chn++) { clrbits32(&ch[chn].phy.misc_spm_ctrl1, 0xf << 0); write32(&ch[chn].phy.misc_spm_ctrl2, 0x0); write32(&ch[chn].phy.misc_spm_ctrl0, 0x0); clrbits32(&ch[chn].ao.impcal, 0x1 << 31); } impcal_bak = read32(&ch[0].ao.impcal); dramc_sw_imp_cal_vref_sel(term, IMPCAL_STAGE_DRVP); clrbits32(&ch[0].phy.misc_imp_ctrl1, 0x1 << 6); clrsetbits32(&ch[0].ao.impcal, 0x1 << 21, 0x3 << 24); clrsetbits32(&ch[0].phy.misc_imp_ctrl0, 0x7 << 4, 0x3 << 4); udelay(1); dramc_dbg("impedance: K DRVP\n"); setbits32(&ch[0].ao.impcal, 0x1 << 23); setbits32(&ch[0].ao.impcal, 0x1 << 22); clrbits32(&ch[0].ao.impcal, 0x1 << 21); clrbits32(&ch[0].ao.shu[0].impcal1, 0x1f << 4 | 0x1f << 11); clrsetbits32(&ch[0].phy.shu[0].ca_cmd[11], 0xff << 0, 0x3); for (u8 impx_drv = 0; impx_drv < 32; impx_drv++) { impx_drv = (impx_drv == 16) ? 29 : impx_drv; clrsetbits32(&ch[0].ao.shu[0].impcal1, 0x1f << 4, impx_drv << 4); udelay(1); imp_cal_result = (read32(&ch[0].phy_nao.misc_phy_rgs_cmd) >> 24) & 0x1; dramc_dbg("1. OCD DRVP=%d CALOUT=%d\n", impx_drv, imp_cal_result); if (imp_cal_result == 1 && DRVP_result == 0xff) { DRVP_result = impx_drv; dramc_dbg("1. OCD DRVP calibration OK! DRVP=%d\n", DRVP_result); break; } } dramc_dbg("impedance: K ODTN\n"); dramc_sw_imp_cal_vref_sel(term, IMPCAL_STAGE_DRVN); clrbits32(&ch[0].ao.impcal, 0x1 << 22); if (term == ODT_ON) setbits32(&ch[0].ao.impcal, 0x1 << 21); clrsetbits32(&ch[0].ao.shu[0].impcal1, 0x1f << 4 | 0x1f << 11, DRVP_result << 4); clrsetbits32(&ch[0].phy.shu[0].ca_cmd[11], 0xff << 0, 0x3); for (u8 impx_drv = 0; impx_drv < 32; impx_drv++) { impx_drv = (impx_drv == 16) ? 29 : impx_drv; clrsetbits32(&ch[0].ao.shu[0].impcal1, 0x1f << 11, impx_drv << 11); udelay(1); imp_cal_result = (read32(&ch[0].phy_nao.misc_phy_rgs_cmd) >> 24) & 0x1; dramc_dbg("3. OCD ODTN=%d CALOUT=%d\n", impx_drv, imp_cal_result); if (imp_cal_result == 0 && ODTN_result == 0xff) { ODTN_result = impx_drv; dramc_dbg("3. OCD ODTN calibration OK! ODTN=%d\n", ODTN_result); break; } } write32(&ch[0].ao.impcal, impcal_bak); dramc_dbg("impedance: term=%d, DRVP=%d, DRVN=%d, ODTN=%d\n", term, DRVP_result, DRVN_result, ODTN_result); u32 *imp = impedance->data[term]; if (term == ODT_OFF) { imp[0] = DRVP_result; imp[1] = ODTN_result; imp[2] = 0; imp[3] = 15; } else { imp[0] = (DRVP_result <= 3) ? (DRVP_result * 3) : DRVP_result; imp[1] = (DRVN_result <= 3) ? (DRVN_result * 3) : DRVN_result; imp[2] = 0; imp[3] = (ODTN_result <= 3) ? (ODTN_result * 3) : ODTN_result; } dramc_sw_imp_cal_vref_sel(term, IMPCAL_STAGE_TRACKING); dramc_set_broadcast(broadcast_bak); } void dramc_sw_impedance_save_reg(u8 freq_group, const struct dram_impedance *impedance) { u8 ca_term = ODT_OFF, dq_term = ODT_ON; u32 sw_impedance[2][4] = {0}; if (get_freq_fsq(freq_group) == FSP_0) dq_term = ODT_OFF; for (u8 term = 0; term < 2; term++) for (u8 i = 0; i < 4; i++) sw_impedance[term][i] = impedance->data[term][i]; sw_impedance[ODT_OFF][2] = sw_impedance[ODT_ON][2]; sw_impedance[ODT_OFF][3] = sw_impedance[ODT_ON][3]; /* DQ */ clrsetbits32(&ch[0].ao.shu[0].drving[0], (0x1f << 5) | (0x1f << 0), (sw_impedance[dq_term][0] << 5) | (sw_impedance[dq_term][1] << 0)); clrsetbits32(&ch[0].ao.shu[0].drving[1], (0x1f << 25) | (0x1f << 20) | (1 << 31), (sw_impedance[dq_term][0] << 25) | (sw_impedance[dq_term][1] << 20) | (!dq_term << 31)); clrsetbits32(&ch[0].ao.shu[0].drving[2], (0x1f << 5) | (0x1f << 0), (sw_impedance[dq_term][2] << 5) | (sw_impedance[dq_term][3] << 0)); clrsetbits32(&ch[0].ao.shu[0].drving[3], (0x1f << 25) | (0x1f << 20), (sw_impedance[dq_term][2] << 25) | (sw_impedance[dq_term][3] << 20)); /* DQS */ for (u8 i = 0; i <= 2; i += 2) { clrsetbits32(&ch[0].ao.shu[0].drving[i], (0x1f << 25) | (0x1f << 20), (sw_impedance[dq_term][i] << 25) | (sw_impedance[dq_term][i + 1] << 20)); clrsetbits32(&ch[0].ao.shu[0].drving[i], (0x1f << 15) | (0x1f << 10), (sw_impedance[dq_term][i] << 15) | (sw_impedance[dq_term][i + 1] << 10)); } /* CMD & CLK */ for (u8 i = 1; i <= 3; i += 2) { clrsetbits32(&ch[0].ao.shu[0].drving[i], (0x1f << 15) | (0x1f << 10), (sw_impedance[ca_term][i - 1] << 15) | (sw_impedance[ca_term][i] << 10)); clrsetbits32(&ch[0].ao.shu[0].drving[i], (0x1f << 5) | (0x1f << 0), (sw_impedance[ca_term][i - 1] << 5) | (sw_impedance[ca_term][i] << 0)); } clrsetbits32(&ch[0].phy.shu[0].ca_cmd[11], 0x1f << 17, sw_impedance[ca_term][0] << 17); clrsetbits32(&ch[0].phy.shu[0].ca_cmd[11], 0x1f << 22, sw_impedance[ca_term][1] << 22); SET32_BITFIELDS(&ch[0].phy.shu[0].ca_cmd[3], SHU1_CA_CMD3_RG_TX_ARCMD_PU_PRE, 1); SET32_BITFIELDS(&ch[0].phy.shu[0].ca_cmd[0], SHU1_CA_CMD0_RG_TX_ARCLK_DRVN_PRE, 0); dramc_set_broadcast(DRAMC_BROADCAST_OFF); clrsetbits32(&ch[0].phy.shu[0].ca_dll[1], 0x1f << 16, 0x9 << 16); clrsetbits32(&ch[1].phy.shu[0].ca_dll[1], 0x1f << 16, 0x9 << 16); dramc_set_broadcast(DRAMC_BROADCAST_ON); } static void transfer_pll_to_spm_control(void) { u8 shu_lev = (read32(&ch[0].ao.shustatus) >> 1) & 0x3; clrsetbits32(&mtk_spm->poweron_config_set, (0xffff << 16) | (0x1 << 0), (0xb16 << 16) | (0x1 << 0)); /* Set SPM pinmux */ clrbits32(&mtk_spm->pcm_pwr_io_en, (0xff << 0) | (0xff << 16)); setbits32(&mtk_spm->dramc_dpy_clk_sw_con_sel, 0xffffffff); setbits32(&mtk_spm->dramc_dpy_clk_sw_con_sel2, 0xffffffff); setbits32(&mtk_spm->spm_power_on_val0, (0x1 << 8) | (0xf << 12)); setbits32(&mtk_spm->spm_s1_mode_ch, 0x3 << 0); shu_lev = (shu_lev == 1) ? 2 : 1; clrsetbits32(&mtk_spm->spm_power_on_val0, 0x3 << 28, shu_lev << 28); clrsetbits32(&mtk_spm->dramc_dpy_clk_sw_con2, 0x3 << 2, shu_lev << 2); udelay(1); for (size_t chn = CHANNEL_A; chn < CHANNEL_MAX; chn++) { clrbits32(&ch[chn].phy.pll1, 0x1 << 31); clrbits32(&ch[chn].phy.pll2, 0x1 << 31); } } static void dramc_rx_input_delay_tracking(u8 chn) { /* Enable RX_FIFO macro DIV4 clock CG */ write32(&ch[chn].phy.misc_cg_ctrl1, 0xffffffff); /* DVS mode to RG mode */ for (size_t r = 0; r < 2; r++) for (size_t b = 0; b < 2; b++) clrbits32(&ch[chn].phy.r[r].b[b].rxdvs[2], 3 << 30); clrsetbits32(&ch[chn].phy.b0_rxdvs[0], 0x1 << 19, 0x1 << 9); clrsetbits32(&ch[chn].phy.b1_rxdvs[0], 0x1 << 19, 0x1 << 9); for (size_t r = 0; r < 2; r++) for (size_t b = 0; b < 2; b++) { clrbits32(&ch[chn].phy.r[r].b[b].rxdvs[2], 1 << 29); clrsetbits32(&ch[chn].phy.r[r].b[b].rxdvs[7], (0x3f << 0) | (0x3f << 8) | (0x7f << 16) | (0x7f << 24), (0x0 << 0) | (0x3f << 8) | (0x0 << 16) | (0x7f << 24)); clrsetbits32(&ch[chn].phy.r[r].b[b].rxdvs[1], (0xffff << 16) | (0xffff << 0), (0x2 << 16) | (0x2 << 0)); /* DQ/DQS Rx DLY adjustment for tracking mode */ clrbits32(&ch[chn].phy.r[r].b[b].rxdvs[2], (0x3 << 26) | (0x3 << 24) | (0x3 << 18) | (0x3 << 16)); } /* Rx DLY tracking setting (Static) */ clrsetbits32(&ch[chn].phy.b0_rxdvs[0], (0x1 << 29) | (0xf << 4) | (0x1 << 0), (0x1 << 29) | (0x0 << 4) | (0x1 << 0)); clrsetbits32(&ch[chn].phy.b1_rxdvs[0], (0x1 << 29) | (0xf << 4) | (0x1 << 0), (0x1 << 29) | (0x0 << 4) | (0x1 << 0)); for (u8 b = 0; b < 2; b++) clrsetbits32(&ch[chn].phy.b[b].dq[9], (0x7 << 28) | (0x7 << 24), (0x1 << 28) | (0x0 << 24)); clrbits32(&ch[chn].phy.ca_cmd[10], (0x7 << 28) | (0x7 << 24)); for (u8 b = 0; b < 2; b++) setbits32(&ch[chn].phy.b[b].dq[5], 0x1 << 31); setbits32(&ch[chn].phy.b0_rxdvs[0], (0x1 << 28) | (0x1 << 31)); setbits32(&ch[chn].phy.b1_rxdvs[0], (0x1 << 28) | (0x1 << 31)); for (u8 rank = RANK_0; rank < RANK_MAX; rank++) for (u8 b = 0; b < 2; b++) clrsetbits32(&ch[chn].phy.r[rank].b[b].rxdvs[2], (0x3 << 30) | (0x1 << 28) | (0x1 << 23), (0x2 << 30) | (0x1 << 28) | (0x1 << 23)); } static void dramc_hw_dqs_gating_tracking(u8 chn) { clrsetbits32(&ch[chn].ao.stbcal, (0x1 << 21) | (0x3 << 15) | (0x1f << 8) | (0x1 << 4), (0x3 << 26) | (0x1 << 0)); clrsetbits32(&ch[chn].ao.stbcal1, (0xffff << 16) | (0x1 << 8) | (0x1 << 6), (0x1 << 16) | (0x1 << 8) | (0x1 << 6)); clrsetbits32(&ch[chn].phy.misc_ctrl0, (0x1 << 24) | (0x1f << 11) | (0xf << 0), (0x1 << 24) | (0x0 << 11) | (0x0 << 0)); clrbits32(&ch[chn].phy.b[0].dq[6], 0x1 << 31); clrbits32(&ch[chn].phy.b[1].dq[6], 0x1 << 31); clrbits32(&ch[chn].phy.ca_cmd[6], 0x1 << 31); } static void dramc_hw_gating_init(u8 chn) { clrbits32(&ch[chn].ao.stbcal, (0x7 << 22) | (0x3 << 14) | (0x1 << 19) | (0x1 << 21)); setbits32(&ch[chn].ao.stbcal, (0x1 << 20) | (0x3 << 28)); setbits32(&ch[chn].phy.misc_ctrl1, 0x1 << 24); dramc_hw_dqs_gating_tracking(chn); } static void dramc_impedance_tracking_enable(void) { setbits32(&ch[0].phy.misc_ctrl0, 0x1 << 10); for (size_t chn = 0; chn < CHANNEL_MAX; chn++) { setbits32(&ch[chn].ao.impcal, (0x1 << 31) | (0x1 << 29) | (0x1 << 26) | (0x1 << 17) | (0x7 << 11)); clrbits32(&ch[chn].ao.impcal, 0x1 << 30); setbits32(&ch[chn].phy.misc_ctrl0, 0x1 << 18); setbits32(&ch[chn].ao.impcal, 0x1 << 19); } setbits32(&ch[0].ao.impcal, 0x1 << 14); for (size_t chn = 0; chn < CHANNEL_MAX; chn++) setbits32(&ch[chn].ao.refctrl0, (0x1 << 2) | (0x1 << 3)); } static void dramc_phy_low_power_enable(u8 chn) { for (u8 b = 0; b < 2; b++) { clrbits32(&ch[chn].phy.b[b].dll_fine_tune[2], 0x3fffff << 10); write32(&ch[chn].phy.b[b].dll_fine_tune[3], 0x2e800); } clrsetbits32(&ch[chn].phy.ca_dll_fine_tune[2], 0x3fffff << 10, 0x2 << 10); write32(&ch[chn].phy.ca_dll_fine_tune[3], (chn == CHANNEL_A) ? 0xba000 : 0x3a000); } static void dramc_dummy_read_for_tracking_enable(u8 chn, u32 rk_num) { setbits32(&ch[chn].ao.dummy_rd, rk_num << 16); for (size_t r = 0; r < 2; r++) for (size_t i = 0; i < 4; i++) write32(&ch[chn].ao.rk[r].dummy_rd_wdata[i], 0xaaaa5555); clrsetbits32(&ch[chn].ao.test2_4, 0x7 << 28, 0x4 << 28); for (size_t r = 0; r < 2; r++) { clrsetbits32(&ch[chn].ao.rk[r].dummy_rd_adr, (0x1ffff << 0) | (0x7ff << 17) | (0xf << 28), (0xffff << 0) | (0x3f0 << 17)); clrbits32(&ch[chn].ao.rk[r].dummy_rd_bk, 0x7 << 0); } clrbits32(&ch[chn].ao.dummy_rd, 0x1 << 25 | 0x1 << 20); } static void dramc_set_CKE_2_rank_independent(u8 chn) { clrsetbits32(&ch[chn].ao.rkcfg, (0x1 << 15) | (0x1 << 12), 0x1 << 2); clrsetbits32(&ch[chn].ao.ckectrl, (0x1 << 1) | (0xf << 8) | (0x7 << 13), (0x4 << 8) | (0x2 << 13)); for (u8 shu = 0; shu < DRAM_DFS_SHUFFLE_MAX; shu++) setbits32(&ch[chn].ao.shu[shu].conf[2], (0x1 << 29) | (0x1 << 31)); clrbits32(&ch[chn].ao.dramctrl, 0x1 << 9); } static void dramc_pa_improve(u8 chn) { clrbits32(&ch[chn].ao.clkar, 0xffff); clrbits32(&ch[chn].ao.srefctrl, 0xf << 12); clrbits32(&ch[chn].ao.zqcs, 0x1 << 19); clrbits32(&ch[chn].ao.pre_tdqsck[0], 0x1 << 17); clrbits32(&ch[chn].ao.zqcs, 0x1 << 19); clrbits32(&ch[chn].ao.pre_tdqsck[0], 0x1 << 17); for (u8 shu = 0; shu < DRAM_DFS_SHUFFLE_MAX; shu++) clrbits32(&ch[chn].ao.shu[shu].odtctrl, 0x3 << 2); } static void dramc_enable_dramc_dcm(void) { for (size_t chn = 0; chn < CHANNEL_MAX; chn++) { clrsetbits32(&ch[chn].ao.dramc_pd_ctrl, (0x7 << 0) | (0x1 << 26) | (0x1 << 30) | (0x1 << 31), (0x7 << 0) | (0x1 << 30) | (0x1 << 31)); setbits32(&ch[chn].ao.clkar, 0x1 << 31); } } void dramc_runtime_config(u32 rk_num) { for (u8 chn = 0; chn < CHANNEL_MAX; chn++) clrbits32(&ch[chn].ao.refctrl0, 0x1 << 29); transfer_pll_to_spm_control(); setbits32(&mtk_spm->spm_power_on_val0, 0x1 << 25); for (u8 chn = 0; chn < CHANNEL_MAX; chn++) { dramc_hw_dqsosc(chn, rk_num); /* RX_TRACKING: ON */ dramc_rx_input_delay_tracking(chn); /* HW_GATING: ON */ dramc_hw_gating_init(chn); dramc_hw_gating_onoff(chn, true); /* HW_GATING DBG: OFF */ clrbits32(&ch[chn].ao.stbcal2, (0x3 << 4) | (0x3 << 8) | (0x1 << 28)); /* DUMMY_READ_FOR_TRACKING: ON */ dramc_dummy_read_for_tracking_enable(chn, rk_num); /* ZQCS_ENABLE_LP4: ON */ clrbits32(&ch[chn].ao.spcmdctrl, 0x1 << 30); /* LOWPOWER_GOLDEN_SETTINGS(DCM): ON */ dramc_phy_low_power_enable(chn); dramc_enable_phy_dcm(chn, true); /* DUMMY_READ_FOR_DQS_GATING_RETRY: OFF */ for (size_t shu = 0; shu < DRAM_DFS_SHUFFLE_MAX; shu++) clrbits32(&ch[chn].ao.shu[shu].dqsg_retry, (0x1 << 1) | (0x3 << 13)); } /* SPM_CONTROL_AFTERK: ON */ write32(&ch[0].phy.misc_spm_ctrl0, 0xfbffefff); write32(&ch[1].phy.misc_spm_ctrl0, 0xfbffefff); write32(&ch[0].phy.misc_spm_ctrl2, 0xffffffef); write32(&ch[1].phy.misc_spm_ctrl2, 0x7fffffef); /* IMPEDANCE_TRACKING: ON */ dramc_impedance_tracking_enable(); for (size_t chn = 0; chn < CHANNEL_MAX; chn++) { /* TEMP_SENSOR: ON */ clrbits32(&ch[chn].ao.spcmdctrl, 0x3 << 28); setbits32(&ch[chn].ao.hw_mrr_fun, (0x1 << 0) | (0x1 << 11)); /* PER_BANK_REFRESH: ON */ clrbits32(&ch[chn].ao.refctrl0, 0x1 << 18); /* HW_SAVE_FOR_SR: ON */ clrbits32(&ch[chn].ao.rstmask, (0x1 << 25) | (0x1 << 28)); setbits32(&ch[chn].ao.refctrl1, 0x1 << 0); clrsetbits32(&ch[chn].ao.srefctrl, 0x1 << 20, 0x1 << 22); /* SET_CKE_2_RANK_INDEPENDENT_RUN_TIME: ON */ dramc_set_CKE_2_rank_independent(chn); /* CLK_FREE_FUN_FOR_DRAMC_PSEL: ON */ clrbits32(&ch[chn].ao.refctrl1, (0x1 << 6) | (0x3 << 2)); clrbits32(&ch[chn].ao.clkar, 0x1 << 19); /* PA_IMPROVEMENT_FOR_DRAMC_ACTIVE_POWER: ON */ dramc_pa_improve(chn); /* DRAM DRS DISABLE */ clrsetbits32(&ch[chn].ao.drsctrl, (0x1 << 0) | (0x1 << 2) | (0x1 << 4) | (0x1 << 5) | (0x1 << 6) | (0xf << 8) | (0x7f << 12) | (0x1 << 19) | (0x1 << 21), (0x1 << 0) | (0x0 << 2) | (0x0 << 4) | (0x1 << 5) | (0x0 << 6) | (0x8 << 8) | (0x3 << 12) | (0x1 << 19) | (0x0 << 21)); setbits32(&ch[chn].ao.dummy_rd, 0x3 << 26); } dramc_dqs_precalculation_preset(); enable_emi_dcm(); dramc_enable_dramc_dcm(); }