| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <soc/dramc_pi_api.h> |
| #include <soc/dramc_register.h> |
| #include <soc/pll.h> |
| #include <soc/pll_common.h> |
| #include <soc/regulator.h> |
| |
| static void dramc_write_shift_mck_write_dbi(const struct ddr_cali *cali, s8 shift_value) |
| { |
| u8 div_shift; |
| s8 ui_move; |
| |
| div_shift = get_mck2ui_div_shift(cali); |
| ui_move = shift_value * (1 << div_shift); |
| shift_dq_ui(cali, cali->rank, ui_move); |
| } |
| |
| static void dramc_ac_timing_optimize(const struct ddr_cali *cali) |
| { |
| u8 rf_group, cab_id; |
| u8 trfc, trfc_05t, trfc_pb, trfrc_pb05t, tx_ref_cnt; |
| |
| enum { |
| TRFCAB_130, |
| TRFCAB_180, |
| TRFCAB_280, |
| TRFCAB_380, |
| TRFCAB_NUM, |
| }; |
| enum { |
| GRP_DDR800_DIV4_ACTIM, |
| GRP_DDR1200_ACTIM, |
| GRP_DDR1600_ACTIM, |
| GRP_DDR1866_ACTIM, |
| GRP_DDR2400_ACTIM, |
| GRP_DDR3200_ACTIM, |
| GRP_DDR4266_ACTIM, |
| GRP_ACTIM_NUM, |
| }; |
| struct optimize_ac_time { |
| u8 trfc; |
| u8 trfc_05t; |
| u8 trfc_pb; |
| u8 trfrc_pb05t; |
| u16 tx_ref_cnt; |
| }; |
| |
| const u8 density = cali->density; |
| const dram_freq_grp freq_group = get_freq_group(cali); |
| |
| /* tRFCab */ |
| struct optimize_ac_time *ptr_trfcab_opt; |
| struct optimize_ac_time trfcab_opt[GRP_ACTIM_NUM][TRFCAB_NUM] = { |
| [GRP_DDR800_DIV4_ACTIM] = { |
| {.trfc = 14, .trfc_05t = 0, .trfc_pb = 0, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 28}, |
| {.trfc = 24, .trfc_05t = 0, .trfc_pb = 6, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 38}, |
| {.trfc = 44, .trfc_05t = 0, .trfc_pb = 16, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 58}, |
| {.trfc = 64, .trfc_05t = 0, .trfc_pb = 26, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 78} |
| }, |
| [GRP_DDR1200_ACTIM] = { |
| {.trfc = 8, .trfc_05t = 0, .trfc_pb = 0, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 21}, |
| {.trfc = 15, .trfc_05t = 1, .trfc_pb = 2, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 29}, |
| {.trfc = 30, .trfc_05t = 1, .trfc_pb = 9, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 44}, |
| {.trfc = 45, .trfc_05t = 1, .trfc_pb = 17, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 59} |
| }, |
| [GRP_DDR1600_ACTIM] = { |
| {.trfc = 14, .trfc_05t = 0, .trfc_pb = 0, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 28}, |
| {.trfc = 24, .trfc_05t = 0, .trfc_pb = 6, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 38}, |
| {.trfc = 44, .trfc_05t = 0, .trfc_pb = 16, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 58}, |
| {.trfc = 64, .trfc_05t = 0, .trfc_pb = 26, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 78} |
| }, |
| [GRP_DDR1866_ACTIM] = { |
| {.trfc = 18, .trfc_05t = 1, .trfc_pb = 2, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 33}, |
| {.trfc = 30, .trfc_05t = 0, .trfc_pb = 9, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 44}, |
| {.trfc = 53, .trfc_05t = 1, .trfc_pb = 21, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 68}, |
| {.trfc = 77, .trfc_05t = 0, .trfc_pb = 32, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 91} |
| }, |
| [GRP_DDR2400_ACTIM] = { |
| {.trfc = 27, .trfc_05t = 1, .trfc_pb = 6, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 42}, |
| {.trfc = 42, .trfc_05t = 1, .trfc_pb = 15, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 57}, |
| {.trfc = 72, .trfc_05t = 1, .trfc_pb = 30, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 87}, |
| {.trfc = 102, .trfc_05t = 1, .trfc_pb = 45, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 117} |
| }, |
| [GRP_DDR3200_ACTIM] = { |
| {.trfc = 40, .trfc_05t = 0, .trfc_pb = 12, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 55}, |
| {.trfc = 60, .trfc_05t = 0, .trfc_pb = 24, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 75}, |
| {.trfc = 100, .trfc_05t = 0, .trfc_pb = 44, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 115}, |
| {.trfc = 140, .trfc_05t = 0, .trfc_pb = 64, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 155} |
| }, |
| [GRP_DDR4266_ACTIM] = { |
| {.trfc = 57, .trfc_05t = 1, .trfc_pb = 20, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 74}, |
| {.trfc = 84, .trfc_05t = 0, .trfc_pb = 36, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 100}, |
| {.trfc = 137, .trfc_05t = 1, .trfc_pb = 63, |
| .trfrc_pb05t = 0, .tx_ref_cnt = 154}, |
| {.trfc = 191, .trfc_05t = 0, .trfc_pb = 89, |
| .trfrc_pb05t = 1, .tx_ref_cnt = 207} |
| } |
| }; |
| |
| switch (density) { |
| case 0x0: |
| rf_group = TRFCAB_130; |
| break; |
| case 0x1: |
| case 0x2: |
| rf_group = TRFCAB_180; |
| break; |
| case 0x3: |
| case 0x4: |
| rf_group = TRFCAB_280; |
| break; |
| case 0x5: |
| case 0x6: |
| rf_group = TRFCAB_380; |
| break; |
| default: |
| die("Invalid DDR density %u\n", density); |
| return; |
| } |
| |
| switch (freq_group) { |
| case DDRFREQ_400: |
| cab_id = GRP_DDR800_DIV4_ACTIM; |
| break; |
| case DDRFREQ_600: |
| cab_id = GRP_DDR1200_ACTIM; |
| break; |
| case DDRFREQ_800: |
| cab_id = GRP_DDR1600_ACTIM; |
| break; |
| case DDRFREQ_933: |
| cab_id = GRP_DDR1866_ACTIM; |
| break; |
| case DDRFREQ_1200: |
| cab_id = GRP_DDR2400_ACTIM; |
| break; |
| case DDRFREQ_1600: |
| cab_id = GRP_DDR3200_ACTIM; |
| break; |
| case DDRFREQ_2133: |
| cab_id = GRP_DDR4266_ACTIM; |
| break; |
| default: |
| die("Invalid DDR frequency group %u\n", freq_group); |
| return; |
| } |
| |
| ptr_trfcab_opt = &trfcab_opt[cab_id][0]; |
| trfc = ptr_trfcab_opt[rf_group].trfc; |
| trfc_05t = ptr_trfcab_opt[rf_group].trfc_05t; |
| trfc_pb = ptr_trfcab_opt[rf_group].trfc_pb; |
| trfrc_pb05t = ptr_trfcab_opt[rf_group].trfrc_pb05t; |
| tx_ref_cnt = ptr_trfcab_opt[rf_group].tx_ref_cnt; |
| |
| for (u8 chn = 0; chn < CHANNEL_MAX; chn++) { |
| SET32_BITFIELDS(&ch[chn].ao.shu_actim3, |
| SHU_ACTIM3_TRFC, trfc); |
| SET32_BITFIELDS(&ch[chn].ao.shu_ac_time_05t, |
| SHU_AC_TIME_05T_TRFC_05T, trfc_05t); |
| SET32_BITFIELDS(&ch[chn].ao.shu_actim4, |
| SHU_ACTIM4_TXREFCNT, tx_ref_cnt); |
| SET32_BITFIELDS(&ch[chn].ao.shu_actim3, |
| SHU_ACTIM3_TRFCPB, trfc_pb); |
| SET32_BITFIELDS(&ch[chn].ao.shu_ac_time_05t, |
| SHU_AC_TIME_05T_TRFCPB_05T, trfrc_pb05t); |
| dramc_dbg("Density (MR8 OP[5:2]) %u, TRFC %u, TRFC_05T %u, TXREFCNT %u, " |
| "TRFCpb %u, TRFCpb_05T %u\n", |
| density, trfc, trfc_05t, tx_ref_cnt, trfc_pb, trfrc_pb05t); |
| } |
| } |
| |
| static void set_vcore_voltage(const struct ddr_cali *cali) |
| { |
| u32 vcore = get_vcore_value(cali); |
| |
| dramc_info("Set DRAM vcore voltage to %u\n", vcore); |
| mainboard_set_regulator_vol(MTK_REGULATOR_VCORE, vcore); |
| } |
| |
| static void get_dram_info_after_cal(struct ddr_cali *cali) |
| { |
| u8 vendor_id, density, max_density = 0; |
| u32 size_gb, max_size = 0; |
| |
| vendor_id = dramc_mode_reg_read_by_rank(CHANNEL_A, RANK_0, 5) & 0xff; |
| dramc_info("Vendor id is %#x\n", vendor_id); |
| |
| for (u8 rk = RANK_0; rk < cali->support_ranks; rk++) { |
| density = dramc_mode_reg_read_by_rank(CHANNEL_A, rk, 8) & 0xff; |
| dramc_dbg("MR8 %#x\n", density); |
| density = (density >> 2) & 0xf; |
| |
| switch (density) { |
| /* these case values are from JESD209-4C MR8 Density OP[5:2] */ |
| case 0x0: |
| size_gb = 4; |
| break; |
| case 0x1: |
| size_gb = 6; |
| break; |
| case 0x2: |
| size_gb = 8; |
| break; |
| case 0x3: |
| size_gb = 12; |
| break; |
| case 0x4: |
| size_gb = 16; |
| break; |
| case 0x5: |
| size_gb = 24; |
| break; |
| case 0x6: |
| size_gb = 32; |
| break; |
| case 0xC: |
| size_gb = 2; |
| break; |
| default: |
| dramc_err("Unexpected mode register density value: %#x\n", density); |
| size_gb = 0; |
| break; |
| } |
| if (size_gb > max_size) { |
| max_size = size_gb; |
| max_density = density; |
| } |
| dramc_dbg("RK%u size %uGb, density:%u\n", rk, size_gb, max_density); |
| } |
| |
| cali->density = max_density; |
| } |
| |
| static void dramc_calibration_single_channel(struct ddr_cali *cali, u8 chn) |
| { |
| cali->chn = chn; |
| SET32_BITFIELDS(&ch[chn].phy_ao.ca_cmd2, |
| CA_CMD2_RG_TX_ARCMD_OE_DIS_CA, 0, |
| CA_CMD2_RG_TX_ARCA_OE_TIE_SEL_CA, 1, |
| CA_CMD2_RG_TX_ARCA_OE_TIE_EN_CA, 0xff); |
| } |
| |
| static void dramc_calibration_all_channels(struct ddr_cali *cali) |
| { |
| u8 chn_bak, rank_bak; |
| const dbi_mode w_dbi = get_write_dbi(cali); |
| |
| for (u8 chn = 0; chn < CHANNEL_MAX; chn++) |
| SET32_BITFIELDS(&ch[chn].phy_ao.ca_cmd2, |
| CA_CMD2_RG_TX_ARCMD_OE_DIS_CA, 1, |
| CA_CMD2_RG_TX_ARCA_OE_TIE_SEL_CA, 0, |
| CA_CMD2_RG_TX_ARCA_OE_TIE_EN_CA, 0xff); |
| |
| for (u8 chn = 0; chn < CHANNEL_MAX; chn++) |
| dramc_calibration_single_channel(cali, chn); |
| |
| if (w_dbi == DBI_ON) { |
| chn_bak = cali->chn; |
| rank_bak = cali->rank; |
| for (u8 chn = 0; chn < CHANNEL_MAX; chn++) |
| for (u8 rank = RANK_0; rank < RANK_MAX; rank++) { |
| cali->chn = chn; |
| cali->rank = rank; |
| dramc_write_shift_mck_write_dbi(cali, -1); |
| } |
| cali->chn = chn_bak; |
| cali->rank = rank_bak; |
| apply_write_dbi_power_improve(true); |
| } |
| |
| dramc_write_dbi_onoff(w_dbi); |
| |
| tx_picg_setting(cali); |
| if (cali->support_ranks == DUAL_RANK_DDR) |
| xrtrtr_shu_setting(cali); |
| freq_jump_ratio_calculation(cali); |
| |
| dramc_hmr4_presetting(cali); |
| dramc_enable_perbank_refresh(true); |
| dramc_modified_refresh_mode(); |
| dramc_cke_debounce(cali); |
| |
| for (u8 chn = 0; chn < CHANNEL_MAX; chn++) |
| dramc_hw_dqsosc(cali, chn); |
| } |
| |
| static void mem_pll_init(void) |
| { |
| SET32_BITFIELDS(&mtk_apmixed->mpll_con3, PLL_POWER_ISO_ENABLE, 3); |
| |
| udelay(30); |
| SET32_BITFIELDS(&mtk_apmixed->mpll_con3, PLL_ISO_ENABLE, 0); |
| |
| udelay(1); |
| SET32_BITFIELDS(&mtk_apmixed->mpll_con1, PLL_CON1, MPLL_CON1_FREQ); |
| SET32_BITFIELDS(&mtk_apmixed->mpll_con0, PLL_ENABLE, 1); |
| |
| udelay(20); |
| SET32_BITFIELDS(&mtk_apmixed->pllon_con0, MPLL_IOS_SEL, 0); |
| SET32_BITFIELDS(&mtk_apmixed->pllon_con0, MPLL_EN_SEL, 0); |
| SET32_BITFIELDS(&mtk_apmixed->pllon_con1, MPLL_PWR_SEL, 0); |
| SET32_BITFIELDS(&mtk_apmixed->pllon_con2, MPLL_BY_ISO_DLY, 0); |
| SET32_BITFIELDS(&mtk_apmixed->pllon_con3, MPLL_BY_PWR_DLY, 0); |
| } |
| |
| void init_dram(const struct dramc_data *dparam) |
| { |
| u32 bc_bak; |
| u8 k_shuffle, k_shuffle_end; |
| u8 pll_mode = 0; |
| bool first_freq_k = true; |
| |
| struct ddr_cali cali = {0}; |
| struct mr_values mr_value; |
| const struct ddr_base_info *ddr_info = &dparam->ddr_info; |
| |
| cali.pll_mode = &pll_mode; |
| cali.mr_value = &mr_value; |
| cali.support_ranks = ddr_info->support_ranks; |
| cali.cbt_mode[RANK_0] = ddr_info->cbt_mode[RANK_0]; |
| cali.cbt_mode[RANK_1] = ddr_info->cbt_mode[RANK_1]; |
| cali.emi_config = &ddr_info->emi_config; |
| |
| dramc_set_broadcast(DRAMC_BROADCAST_ON); |
| mem_pll_init(); |
| |
| global_option_init(&cali); |
| bc_bak = dramc_get_broadcast(); |
| dramc_set_broadcast(DRAMC_BROADCAST_OFF); |
| emi_mdl_init(cali.emi_config); |
| dramc_set_broadcast(bc_bak); |
| |
| dramc_sw_impedance_cal(ODT_OFF, &cali.impedance); |
| dramc_sw_impedance_cal(ODT_ON, &cali.impedance); |
| |
| if (ddr_info->config_dvfs == DRAMC_ENABLE_DVFS) |
| k_shuffle_end = CALI_SEQ_MAX; |
| else |
| k_shuffle_end = CALI_SEQ1; |
| |
| for (k_shuffle = CALI_SEQ0; k_shuffle < k_shuffle_end; k_shuffle++) { |
| set_cali_datas(&cali, dparam, k_shuffle); |
| set_vcore_voltage(&cali); |
| dfs_init_for_calibration(&cali); |
| |
| if (first_freq_k) |
| emi_init2(); |
| |
| dramc_calibration_all_channels(&cali); |
| |
| /* only need to do once to get DDR's base information */ |
| if (first_freq_k) |
| get_dram_info_after_cal(&cali); |
| |
| dramc_ac_timing_optimize(&cali); |
| dramc_save_result_to_shuffle(DRAM_DFS_SHU0, cali.shu); |
| |
| /* for frequency switch in dramc_mode_reg_init phase */ |
| if (first_freq_k) |
| dramc_load_shuffle_to_dramc(cali.shu, DRAM_DFS_SHU1); |
| |
| first_freq_k = false; |
| dramc_info("Calibration of data rate %u finished\n", get_frequency(&cali) * 2); |
| } |
| } |