Hello Duan huayang,
I'd like you to do a code review. Please visit
https://review.coreboot.org/c/coreboot/+/44720
to review the following change.
Change subject: soc/mediatek/mt8192: Do dramc tx window training ......................................................................
soc/mediatek/mt8192: Do dramc tx window training
Signed-off-by: Huayang Duan huayang.duan@mediatek.com Change-Id: Ica11573c1e0b657be0e8ddcbf81a9e3f2399e258 --- M src/soc/mediatek/mt8192/dramc_pi_calibration_api.c M src/soc/mediatek/mt8192/dramc_pi_main.c 2 files changed, 794 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/20/44720/1
diff --git a/src/soc/mediatek/mt8192/dramc_pi_calibration_api.c b/src/soc/mediatek/mt8192/dramc_pi_calibration_api.c index 363a3bd..602e61d 100644 --- a/src/soc/mediatek/mt8192/dramc_pi_calibration_api.c +++ b/src/soc/mediatek/mt8192/dramc_pi_calibration_api.c @@ -5,6 +5,11 @@ #include <soc/dramc_register.h> #include <timer.h>
+#define PASS_RANGE_NA 0x7fff +#define TX_VREF_RANGE_BEGIN 16 +#define TX_VREF_RANGE_END 50 +#define TX_VREF_RANGE_STEP 2 + static const u8 ca_pinmux_mapping[PINMUX_MAX][CHANNEL_MAX][CA_NUM_LP4] = { [PINMUX_DSC] = { [CHANNEL_A] = {1, 4, 5, 3, 2, 0}, @@ -35,6 +40,12 @@ u16 best_dqdly; } pass_win_data_t;
+typedef struct { + u16 vref_used; + u16 winsum_by_vref; + u8 worse_bit_winsize; + u8 worse_bit_idx; +} pass_win_data_vref_t;
static const u8 imp_vref_sel[ODT_MAX][IMP_DRV_MAX] = { /* DRVP DRVN ODTP ODTN */ @@ -1704,6 +1715,19 @@ read_dqs_inctl, rank_inctl_root, xrtr2r); }
+static void dramc_set_rw_ofoen(u8 chn, u8 onoff) +{ + u32 loop = 0, loop_cnt = 100000; + + while (READ32_BITFIELD(&ch[chn].nao.misc_statusa, MISC_STATUSA_REQQ_EMPTY) != 1) { + loop ++; + if (loop > loop_cnt) { + dramc_dbg("RWOFOEN timout! queue is not empty\n"); + break; + } + } + SET32_BITFIELDS(&ch[chn].ao.scheduler_com, SCHEDULER_COM_RWOFOEN, onoff); +}
static void dramc_set_rank_engin2(u8 chn, u8 rank) { @@ -1767,6 +1791,94 @@ dramc_engin2_set_pat(chn, rank, 0); }
+static void dramc_engine2_check_complete(u8 chn, u8 status) +{ + u32 loop = 0, loop_max = 100000; + u32 ta2_loop_cnt = 0; + + while ((read32(&ch[chn].nao.testrpt) & status) != status) { + udelay(1); + loop++; + if (loop > loop_max) { + dramc_dbg("testrpt time out, [22:20]=%#x\n", + READ32_BITFIELD(&ch[chn].nao.testrpt, TESTRPT_TESTSTAT)); + break; + } + } + + loop = 0; + if (READ32_BITFIELD(&ch[chn].ao.test2_a3, TEST2_A3_TEST2_PAT_SHIFT)) { + while ((ta2_loop_cnt = read32(&ch[chn].nao.test_loop_cnt)) != 8) { + loop++; + if (loop > loop_max) { + dramc_dbg("test_loop_cnt time out, TEST_LOOP_CNT[%d]\n", + ta2_loop_cnt); + break; + } + } + } +} + +static u32 dramc_engine2_compare(u8 chn) +{ + u8 status = 1; + u32 result = 0xffffffff; + u32 loop_cnt; + u32 shift_ui_flag = 0; + + loop_cnt = READ32_BITFIELD(&ch[chn].ao.test2_a3, TEST2_A3_TESTCNT); + if (loop_cnt == 1) + status = 3; + + shift_ui_flag = READ32_BITFIELD(&ch[chn].ao.test2_a3, TEST2_A3_TEST2_PAT_SHIFT); + + if (!shift_ui_flag) { + dramc_engine2_check_complete(chn, status); + + SET32_BITFIELDS(&ch[chn].ao.test2_a3, + TEST2_A3_TEST2W, 0, + TEST2_A3_TEST2R, 0, + TEST2_A3_TEST1, 0); + udelay(1); + SET32_BITFIELDS(&ch[chn].ao.test2_a3, + TEST2_A3_TEST2W, 0, + TEST2_A3_TEST2R, 1, + TEST2_A3_TEST1, 0); + } + + dramc_engine2_check_complete(chn, status); + result = (read32(&ch[chn].nao.testrpt) >> 4) & status; + + return result; +} + +static u32 dramc_engine2_run(u8 chn) +{ + u32 result = 0xffffffff; + + SET32_BITFIELDS(&ch[chn].ao.test2_a3, + TEST2_A3_TEST2W, 1, + TEST2_A3_TEST2R, 0, + TEST2_A3_TEST1, 0); + dramc_engine2_compare(chn); + udelay(1); + result = read32(&ch[chn].nao.cmp_err); + SET32_BITFIELDS(&ch[chn].ao.test2_a3, + TEST2_A3_TEST2W, 0, + TEST2_A3_TEST2R, 0, + TEST2_A3_TEST1, 0); + + return result; +} + +static void dramc_engine2_end(u8 chn, u32 dummy_rd) +{ + SET32_BITFIELDS(&ch[chn].ao.test2_a4, + TEST2_A4_TEST_REQ_LEN1, 0); + dramc_set_rw_ofoen(chn, 1); + write32(&ch[chn].ao.dummy_rd, dummy_rd); +} + static const u8 uiLPDDR4_RDDQC_Mapping_POP[PINMUX_MAX][CHANNEL_MAX][DQ_DATA_WIDTH] = { [PINMUX_DSC] = { @@ -1843,6 +1955,7 @@ SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].shu_b0_dq8, SHU_B1_DQ8_R_DMRXDLY_CG_IG_B1, 1); } + void dramc_rx_window_perbit_cal(const struct ddr_cali* cali, rx_cali_type cali_type) { u8 chn, rank, fsp; @@ -1956,3 +2069,647 @@ bit + 2, final_win_perbit[bit + 2].best_dqdly, bit + 3, final_win_perbit[bit + 3].best_dqdly); } + +static void tx_perbit_calibarion_init(u8 chn, u8 rank, u8 cali_type) +{ + if (cali_type != TX_DQ_DQS_MOVE_DQM_ONLY) { + write32(&ch[chn].phy_ao.byte[0].rk[rank].shu_r0_b0_txdly0, 0); + write32(&ch[chn].phy_ao.byte[0].rk[rank].shu_r0_b0_txdly1, 0); + write32(&ch[chn].phy_ao.byte[1].rk[rank].shu_r0_b0_txdly0, 0); + write32(&ch[chn].phy_ao.byte[1].rk[rank].shu_r0_b0_txdly1, 0); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[0].rk[rank].shu_r0_b0_txdly3, + SHU_R0_B0_TXDLY3_TX_ARDQM0_DLY_B0, 0x0); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].rk[rank].shu_r0_b0_txdly3, + SHU_R0_B1_TXDLY3_TX_ARDQM0_DLY_B1, 0x0); + } +} + +static void tx_win_transfer_delay_to_uipi(const struct ddr_cali* cali, u16 delay, + u8 pi_to_center, u8 *p_ui_large_dq, u8 *p_ui_small_dq, u8 *p_pi, + u8 *p_ui_large_dqoe, u8 *p_ui_small_dqoe) +{ + dram_freq_grp freq_group = cali->freq_group; + bool is_freq_400 = freq_group == DDRFREQ_400; + + u8 small_ui_to_large, pi = 0, pi_to_ui, tx_dq_oe_shift = 0; + u8 pi_tap = is_freq_400 ? TX_PHASE_DQ_UI_TO_PI_TAP : TX_DQ_UI_TO_PI_TAP; + u16 tmp_val, dqoe_shift; + u8 mr03 = cali->mr_value->mr03[get_fsp(cali)]; + small_ui_to_large = get_mck2ui_div_shift(cali); + tx_dq_oe_shift = 3; + + if (p_pi != NULL) { + pi = delay & (pi_tap-1); + *p_pi =pi; + } + + if (is_freq_400) + pi_to_ui = 0; + else + pi_to_ui = 1; + + tmp_val = (delay /pi_tap) << pi_to_ui; + + if (pi_to_center && (p_pi != NULL) && (!is_freq_400)) { + if (pi < 10) { + pi += (pi_tap) >> 1; + tmp_val --; + } else if (pi > pi_tap - 10) { + pi -= (pi_tap) >> 1; + tmp_val ++; + } + + *p_pi = pi; + } + + *p_ui_small_dq = tmp_val - ((tmp_val >> small_ui_to_large) << small_ui_to_large); + *p_ui_large_dq = (tmp_val >> small_ui_to_large); + + tmp_val -= tx_dq_oe_shift; + + if (((mr03 & 0x80) >> 7) == 1) { + if (get_div_mode(cali) == DIV4_MODE) + dqoe_shift = 4; + else + dqoe_shift = 8; + + tmp_val += dqoe_shift; + } + + *p_ui_small_dqoe = tmp_val - ((tmp_val >> small_ui_to_large) << small_ui_to_large); + *p_ui_large_dqoe = (tmp_val >> small_ui_to_large); +} + +static void tx_set_delay_reg_dq(u8 chn, u8 rank, u8 update_reg_ui, u8 dq_ui_large[], + u8 dq_oen_ui_large[], u8 dq_ui_small[], u8 dq_oen_ui_small[], u8 dql_pi[]) +{ + if (update_reg_ui) { + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_selph_dq0, + SHURK_SELPH_DQ0_TXDLY_DQ0, dq_ui_large[0], + SHURK_SELPH_DQ0_TXDLY_DQ1, dq_ui_large[1], + SHURK_SELPH_DQ0_TXDLY_DQ2, dq_ui_large[0], + SHURK_SELPH_DQ0_TXDLY_DQ3, dq_ui_large[1], + SHURK_SELPH_DQ0_TXDLY_OEN_DQ0, dq_oen_ui_large[0], + SHURK_SELPH_DQ0_TXDLY_OEN_DQ1, dq_oen_ui_large[1], + SHURK_SELPH_DQ0_TXDLY_OEN_DQ2, dq_oen_ui_large[0], + SHURK_SELPH_DQ0_TXDLY_OEN_DQ3, dq_oen_ui_large[1]); + + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_selph_dq2, + SHURK_SELPH_DQ2_DLY_DQ0, dq_ui_small[0], + SHURK_SELPH_DQ2_DLY_DQ1, dq_ui_small[1], + SHURK_SELPH_DQ2_DLY_DQ2, dq_ui_small[0], + SHURK_SELPH_DQ2_DLY_DQ3, dq_ui_small[1], + SHURK_SELPH_DQ2_DLY_OEN_DQ0, dq_oen_ui_small[0], + SHURK_SELPH_DQ2_DLY_OEN_DQ1, dq_oen_ui_small[1], + SHURK_SELPH_DQ2_DLY_OEN_DQ2, dq_oen_ui_small[0], + SHURK_SELPH_DQ2_DLY_OEN_DQ3, dq_oen_ui_small[1]); + } + + SET32_BITFIELDS(&ch[chn].phy_ao.byte[0].rk[rank].shu_r0_b0_dq0, + SHU_R0_B0_DQ0_SW_ARPI_DQ_B0, dql_pi[0]); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].rk[rank].shu_r0_b0_dq0, + SHU_R0_B1_DQ0_SW_ARPI_DQ_B1, dql_pi[1]); +} + +static void tx_set_delay_reg_dqm(u8 chn, u8 rank, u8 update_reg_ui, u8 dqm_ui_large[], + u8 dqm_oen_ui_large[], u8 dqm_ui_small[], u8 dqm_oen_ui_small[], u8 dqm_pi[]) +{ + if (update_reg_ui) { + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_selph_dq1, + SHURK_SELPH_DQ1_TXDLY_DQM0, dqm_ui_large[0], + SHURK_SELPH_DQ1_TXDLY_DQM1, dqm_ui_large[1], + SHURK_SELPH_DQ1_TXDLY_DQM2, dqm_ui_large[0], + SHURK_SELPH_DQ1_TXDLY_DQM3, dqm_ui_large[1], + SHURK_SELPH_DQ1_TXDLY_OEN_DQM0, dqm_oen_ui_large[0], + SHURK_SELPH_DQ1_TXDLY_OEN_DQM1, dqm_oen_ui_large[1], + SHURK_SELPH_DQ1_TXDLY_OEN_DQM2, dqm_oen_ui_large[0], + SHURK_SELPH_DQ1_TXDLY_OEN_DQM3, dqm_oen_ui_large[1]); + + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_selph_dq3, + SHURK_SELPH_DQ3_DLY_DQM0, dqm_ui_small[0], + SHURK_SELPH_DQ3_DLY_DQM1, dqm_ui_small[1], + SHURK_SELPH_DQ3_DLY_DQM2, dqm_ui_small[0], + SHURK_SELPH_DQ3_DLY_DQM3, dqm_ui_small[1], + SHURK_SELPH_DQ3_DLY_OEN_DQM0, dqm_oen_ui_small[0], + SHURK_SELPH_DQ3_DLY_OEN_DQM1, dqm_oen_ui_small[1], + SHURK_SELPH_DQ3_DLY_OEN_DQM2, dqm_oen_ui_small[0], + SHURK_SELPH_DQ3_DLY_OEN_DQM3, dqm_oen_ui_small[1]); + } + + SET32_BITFIELDS(&ch[chn].phy_ao.byte[0].rk[rank].shu_r0_b0_dq0, + SHU_R0_B0_DQ0_SW_ARPI_DQM_B0, dqm_pi[0]); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].rk[rank].shu_r0_b0_dq0, + SHU_R0_B1_DQ0_SW_ARPI_DQM_B1, dqm_pi[1]); +} + +static void update_tx_tracking(u8 chn, u8 rank, tx_cali_type cali_type, u8 dq_pi[], u8 dqm_pi[]) +{ + if (cali_type == TX_DQ_DQS_MOVE_DQ_ONLY || cali_type == TX_DQ_DQS_MOVE_DQM_ONLY) { + if (cali_type == TX_DQ_DQS_MOVE_DQ_ONLY) { + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_pi, + SHURK_PI_RK0_ARPI_DQ_B0, dq_pi[0], + SHURK_PI_RK0_ARPI_DQ_B1, dq_pi[1]); + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_dqs2dq_cal1, + SHURK_DQS2DQ_CAL1_BOOT_ORIG_UI_RK0_DQ1, dq_pi[1], + SHURK_DQS2DQ_CAL1_BOOT_ORIG_UI_RK0_DQ0, dq_pi[0]); + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_dqs2dq_cal2, + SHURK_DQS2DQ_CAL2_BOOT_TARG_UI_RK0_DQ1, dq_pi[1], + SHURK_DQS2DQ_CAL2_BOOT_TARG_UI_RK0_DQ0, dq_pi[0]); + } + + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_pi, + SHURK_PI_RK0_ARPI_DQM_B0, dqm_pi[0], + SHURK_PI_RK0_ARPI_DQM_B1, dqm_pi[1]); + + SET32_BITFIELDS(&ch[chn].ao.shu_rk[rank].shurk_dqs2dq_cal5, + SHURK_DQS2DQ_CAL5_BOOT_TARG_UI_RK0_DQM1, dqm_pi[1], + SHURK_DQS2DQ_CAL5_BOOT_TARG_UI_RK0_DQM0, dqm_pi[0]); + } +} + +static void dramc_tx_set_vref(const struct ddr_cali* cali, u8 vref_range, u8 vref) +{ + u8 mr_value = (vref & 0x3f) | (vref_range << 6); + + dramc_mode_reg_write_by_rank(cali, cali->chn, cali->rank, 14, mr_value); +} + +static void tx_scan_range_PI(const struct ddr_cali* cali, tx_cali_type cali_type, + u16 *begin, u16 *end, const u8 dqs_final_delay[2][2]) +{ + u8 mck_to_ui, ui_to_pi, byte; + u8 dq_ui_large_bak[DQS_NUMBER], dq_ui_small_bak[DQS_NUMBER]; + u16 temp_virtual_delay, smallest_virtual_delay = 0xffff; + u16 delay_begin = 0, delay_end = 0; + u32 tx_dly, delay; + + u8 chn = cali->chn; + u8 rank = cali->rank; + + tx_dly = read32(&ch[chn].ao.shu_selph_dqs0); + delay = read32(&ch[chn].ao.shu_selph_dqs1); + mck_to_ui = get_mck2ui_div_shift(cali); + ui_to_pi = 5; + + for (byte = 0; byte < BYTE_NUM; byte++) { + dq_ui_large_bak[byte] = (tx_dly >> (byte << 2)) & 0x7; + dq_ui_small_bak[byte] = (delay >> (byte << 2)) & 0x7; + temp_virtual_delay = (((dq_ui_large_bak[byte] << mck_to_ui) + + dq_ui_small_bak[byte]) << ui_to_pi) + dqs_final_delay[rank][byte]; + + if (temp_virtual_delay < smallest_virtual_delay) + smallest_virtual_delay = temp_virtual_delay; + } + + delay_begin = smallest_virtual_delay; + + if (cali_type == TX_DQ_DQS_MOVE_DQM_ONLY) + delay_begin -= (1 << (mck_to_ui + 5)); + delay_end = delay_begin + ((1 << mck_to_ui) << ui_to_pi); + + *begin = delay_begin; + *end = delay_end; +} + +static void tx_scan_range_vref(const struct ddr_cali* cali, bool enable_vref_scan, + u16 *range, u16 *begin, u16 *end, u16 *step) +{ + u16 vref_begin, vref_end; + u8 odt = get_odt_state(cali); + const struct sdram_params *params = cali->params; + + if (enable_vref_scan) { + vref_begin = params->tx_window_vref[cali->chn][cali->rank]; + vref_end = vref_begin + 1; + }else { + vref_begin = 0; + vref_end = 0; + } + + *range = (!odt); + *begin = vref_begin; + *end = vref_end; + *step = TX_VREF_RANGE_STEP; +} + +static u16 tx_choose_vref(pass_win_data_vref_t vref_info[], u8 vref_num) +{ + u8 vref, worse_bit = 0, win_size_worse_bit = 0; + u16 max_winsum = 0; + u16 final_vref = 0; + + for (vref = 0; vref < vref_num; vref++) + dramc_dbg("TX Vref=%d, minBit %d, minWin=%d, winSum=%d\n", + vref_info[vref].vref_used, + vref_info[vref].worse_bit_idx, + vref_info[vref].worse_bit_winsize, + vref_info[vref].winsum_by_vref); + + for (vref = 0; vref < vref_num; vref++) { + if ((vref_info[vref].worse_bit_winsize > win_size_worse_bit) || + ((vref_info[vref].worse_bit_winsize == win_size_worse_bit) && + (vref_info[vref].winsum_by_vref > max_winsum))) { + win_size_worse_bit = vref_info[vref].worse_bit_winsize; + worse_bit = vref_info[vref].worse_bit_idx; + max_winsum = vref_info[vref].winsum_by_vref; + final_vref = vref_info[vref].vref_used; + } + } + + dramc_dbg("Worse bit %d, Min win %d, Win sum %d, Final Vref %d\n", + worse_bit, win_size_worse_bit, max_winsum, final_vref); + + return final_vref; +} + +static void dramc_tx_window_perbit_cal_find_best_delay(const struct ddr_cali* cali, + tx_cali_type cali_type, bool enable_vref_scan, u16 delay_begin, u16 delay_end, + u16 delay_step, u32 *finish_count, u8 *ui_small_reg_value, + pass_win_data_t win_perbit[DQ_DATA_WIDTH], + pass_win_data_t vref_win_perbit[DQ_DATA_WIDTH]) +{ + u8 chn = cali->chn; + u8 rank = cali->rank; + u8 pi, ui_small, ui_large, oen_ui_small, oen_ui_large; + u8 update_reg_ui; + u8 dq_ui_large[DQS_NUMBER] = {0}, dq_ui_small[DQS_NUMBER] = {0}; + u8 dq_oen_ui_large[DQS_NUMBER] = {0}, dq_oen_ui_small[DQS_NUMBER] = {0}; + u8 dqm_ui_large[DQS_NUMBER] = {0}, dqm_ui_small[DQS_NUMBER] = {0}; + u8 dqm_oen_ui_large[DQS_NUMBER] = {0}, dqm_oen_ui_small[DQS_NUMBER] = {0}; + u8 dq_pi[DQS_NUMBER] = {0}, dqm_pi[DQS_NUMBER] = {0}; + u16 perbit_winsum, vref_winsum; + u32 err_value, fail_bit; + + for (u16 delay = delay_begin; delay < delay_end; delay += delay_step) { + tx_win_transfer_delay_to_uipi(cali, delay, 0, &ui_large, + &ui_small, &pi, &oen_ui_large, &oen_ui_small); + + if (*ui_small_reg_value != ui_small) { + update_reg_ui = 1; + *ui_small_reg_value = ui_small; + } else + update_reg_ui = 0; + + for (u8 byte = 0; byte < DQS_NUMBER; byte++) { + if (update_reg_ui) { + dq_ui_large[byte] = ui_large; + dq_ui_small[byte] = ui_small; + dq_oen_ui_large[byte] = oen_ui_large; + dq_oen_ui_small[byte] = oen_ui_small; + + dqm_ui_large[byte] = ui_large; + dqm_ui_small[byte] = ui_small; + dqm_oen_ui_large[byte] = oen_ui_large; + dqm_oen_ui_small[byte] = oen_ui_small; + } + + dq_pi[byte] = pi; + dqm_pi[byte] = pi; + } + + if (cali_type == TX_DQ_DQS_MOVE_DQ_ONLY || + cali_type == TX_DQ_DQS_MOVE_DQ_DQM) + tx_set_delay_reg_dq(chn, rank, update_reg_ui, dq_ui_large, + dq_oen_ui_large, dq_ui_small, dq_oen_ui_small, dq_pi); + + if (cali_type == TX_DQ_DQS_MOVE_DQM_ONLY || + cali_type == TX_DQ_DQS_MOVE_DQ_DQM) + tx_set_delay_reg_dqm(chn, rank, update_reg_ui, + dqm_ui_large, dqm_oen_ui_large, dqm_ui_small, + dqm_oen_ui_small, dqm_pi); + + err_value = dramc_engine2_run(chn); + + if (!enable_vref_scan && (cali_type != TX_DQ_DQS_MOVE_DQM_ONLY)) + if (err_value != 0) + dramc_dbg("%d |%d %d %d|[0]", delay, ui_large, ui_small, pi); + + for (u8 bit = 0; bit < DQ_DATA_WIDTH; bit++) { + fail_bit = err_value & BIT(bit); + + if (win_perbit[bit].first_pass == PASS_RANGE_NA) { + if (fail_bit == 0) + win_perbit[bit].first_pass = delay; + } else if (win_perbit[bit].last_pass == PASS_RANGE_NA) { + if (fail_bit != 0) + win_perbit[bit].last_pass = delay - delay_step; + else if (delay > (delay_end - delay_step)) + win_perbit[bit].last_pass = delay; + + if (win_perbit[bit].last_pass != PASS_RANGE_NA) { + perbit_winsum = win_perbit[bit].last_pass - + win_perbit[bit].first_pass; + vref_winsum = vref_win_perbit[bit].last_pass - + vref_win_perbit[bit].first_pass; + if (perbit_winsum >= vref_winsum) { + if ((vref_win_perbit[bit].last_pass != + PASS_RANGE_NA) && + vref_winsum > 0) + dramc_dbg("Bit[%d] Bigger window update %d > %d, window broken\n", + bit, perbit_winsum, + vref_winsum); + + if (perbit_winsum > TX_PASS_WIN_CRITERIA) + *finish_count |= BIT(bit); + + vref_win_perbit[bit].first_pass = + win_perbit[bit].first_pass; + vref_win_perbit[bit].last_pass = + win_perbit[bit].last_pass; + } + + win_perbit[bit].first_pass = PASS_RANGE_NA; + win_perbit[bit].last_pass = PASS_RANGE_NA; + } + } + + if (!enable_vref_scan && (cali_type != TX_DQ_DQS_MOVE_DQM_ONLY)) { + if (err_value != 0) { + if (bit % DQS_BIT_NUMBER == 0) + dramc_dbg(" "); + + if (fail_bit == 0) + dramc_dbg("o"); + else + dramc_dbg("x"); + } + } + } + + if (!enable_vref_scan && (cali_type != TX_DQ_DQS_MOVE_DQM_ONLY)) + if (err_value != 0) + dramc_dbg("[MSB]\n"); + + if (*finish_count == 0xffff) + break; + } +} + +static u16 dramc_tx_window_perbit_cal_find_best_vref(const struct ddr_cali* cali, + tx_cali_type cali_type, bool enable_vref_scan, u16 final_range, + u16 vref_begin, u16 vref_end, u16 vref_step, + u16 delay_begin, u16 delay_end, u16 delay_step, u8 *vref_tmp, + u16 center_min[DQS_NUMBER], u16 center_max[DQS_NUMBER], + pass_win_data_t final_win_perbit[DQ_DATA_WIDTH], + pass_win_data_vref_t vref_info[LP4_TX_VREF_DATA_NUM]) +{ + u8 bit, byte, min_bit, min_winsize = 0, bit_tmp; + u8 ui_small_reg_value; + u16 vref_level; + u16 winsum, temp_winsum, max_window_sum = 0; + u32 finish_count; + u32 dummy_rd_bak = 0; + pass_win_data_t win_perbit[DQ_DATA_WIDTH], vref_win_perbit[DQ_DATA_WIDTH]; + + u8 chn = cali->chn; + u8 rank = cali->rank; + + dummy_rd_bak = read32(&ch[chn].ao.dummy_rd); + dramc_engine2_init(chn, rank); + + for (vref_level = vref_begin; vref_level <= vref_end; vref_level += vref_step) { + if (enable_vref_scan) { + dramc_dbg("LP4 TX VrefRange %d, VrefLevel=%d\n", final_range, vref_level); + dramc_tx_set_vref(cali, final_range, vref_level); + } else { + dramc_dbg("TX Vref Scan disable\n"); + } + + finish_count = 0; + temp_winsum = 0; + ui_small_reg_value = 0xff; + + for (bit = 0; bit < DQ_DATA_WIDTH; bit++) { + win_perbit[bit].first_pass = (s16)PASS_RANGE_NA; + win_perbit[bit].last_pass = (s16)PASS_RANGE_NA; + vref_win_perbit[bit].first_pass = (s16)PASS_RANGE_NA; + vref_win_perbit[bit].last_pass = (s16)PASS_RANGE_NA; + } + + dramc_tx_window_perbit_cal_find_best_delay(cali, cali_type,enable_vref_scan, + delay_begin, delay_end, delay_step, &finish_count, &ui_small_reg_value, + win_perbit, vref_win_perbit); + + min_winsize = 0xff; + min_bit = 0xff; + for (bit = 0; bit < DQ_DATA_WIDTH; bit++) { + winsum = vref_win_perbit[bit].last_pass - + vref_win_perbit[bit].first_pass; + if (vref_win_perbit[bit].first_pass == PASS_RANGE_NA) + vref_win_perbit[bit].win_size = 0; + else + vref_win_perbit[bit].win_size = winsum + delay_step; + + if (vref_win_perbit[bit].win_size < min_winsize) { + min_bit = bit; + min_winsize = vref_win_perbit[bit].win_size; + } + + temp_winsum += vref_win_perbit[bit].win_size; + vref_win_perbit[bit].win_center = (vref_win_perbit[bit].first_pass + + vref_win_perbit[bit].last_pass) >> 1; + } + + if (enable_vref_scan) { + if (temp_winsum > max_window_sum) + max_window_sum = temp_winsum; + + vref_info[*vref_tmp].vref_used = vref_level; + vref_info[*vref_tmp].worse_bit_winsize = min_winsize; + vref_info[*vref_tmp].worse_bit_idx = min_bit; + vref_info[*vref_tmp].winsum_by_vref = temp_winsum; + *vref_tmp = *vref_tmp + 1; + } + + if (enable_vref_scan && + (temp_winsum < (max_window_sum * 95 / 100)) && + (min_winsize > TX_PASS_WIN_CRITERIA)) { + dramc_dbg("TX Vref early break, caculate TX vref\n"); + break; + } + } + + dramc_engine2_end(chn, dummy_rd_bak); + if (enable_vref_scan) + return vref_level; + + for (byte = 0; byte < (DQ_DATA_WIDTH / DQS_BIT_NUMBER); byte++) { + center_min[byte] = 0xffff; + center_max[byte] = 0; + + for (bit = 0; bit < DQS_BIT_NUMBER; bit++) { + bit_tmp = byte * DQS_BIT_NUMBER + bit; + memcpy(final_win_perbit, vref_win_perbit, sizeof(vref_win_perbit)); + + if (final_win_perbit[bit_tmp].win_center < center_min[byte]) + center_min[byte] = final_win_perbit[bit_tmp].win_center; + + if (final_win_perbit[bit_tmp].win_center > center_max[byte]) + center_max[byte] = final_win_perbit[bit_tmp].win_center; + } + } + + return vref_level; +} + +void dramc_tx_window_perbit_cal(const struct ddr_cali* cali, + tx_cali_type cali_type, const u8 dqs_final_delay[2][2], bool enable_vref_scan) +{ + u8 chn, rank, fsp, odt; + u8 pi_diff; + u8 bit_tmp, bit, byte, rk; + u8 enable_delaycell = 0; + u8 vref_tmp = 0; + u8 dq_ui_large[DQS_NUMBER], dq_ui_small[DQS_NUMBER], dq_pi[DQS_NUMBER]; + u8 dq_oen_ui_large[DQS_NUMBER], dq_oen_ui_small[DQS_NUMBER]; + u8 dqm_ui_large[DQS_NUMBER] = {0}, dqm_ui_small[DQS_NUMBER] = {0}; + u8 dqm_oen_ui_large[DQS_NUMBER] = {0}, dqm_oen_ui_small[DQS_NUMBER] = {0}; + u8 dqm_pi[DQS_NUMBER] = {0}; + u16 delay, delay_step, dqm_delay; + u16 center_min[DQS_NUMBER] = {0}, center_max[DQS_NUMBER] = {0}; + u16 delaycell_offset[DQ_DATA_WIDTH] = {0}; + u16 final_range = 0, final_vref = 0; + u16 vref_begin, vref_end, vref_step; + u16 delay_begin, delay_end; + u32 frequency; + dram_freq_grp freq_group; + pass_win_data_t final_win_perbit[DQ_DATA_WIDTH]; + pass_win_data_vref_t vref_info[LP4_TX_VREF_DATA_NUM]; + + fsp = get_fsp(cali); + odt = get_odt_state(cali); + bool bypass_cali = !fsp; + const struct sdram_params *params = cali->params; + + chn = cali->chn; + rank = cali->rank; + freq_group = cali->freq_group; + frequency = get_frequency(cali); + + tx_perbit_calibarion_init(chn, rank, cali_type); + + tx_scan_range_PI(cali, cali_type, &delay_begin, &delay_end, dqs_final_delay); + tx_scan_range_vref(cali, enable_vref_scan, &final_range, &vref_begin, &vref_end, &vref_step); + dramc_dbg("delay_begin:%d, delay_end:%d,final_range:%d,vref_begin:%d, vref_end:%d,vref_step:%d\n", + delay_begin, delay_end, final_range, vref_begin, vref_end, vref_step); + + if (freq_group == DDRFREQ_400) + delay_step = 8; + else if (cali_type == TX_DQ_DQS_MOVE_DQ_DQM) + delay_step = 2; + else + delay_step = 1; + + if (bypass_cali) { + for (byte = 0; byte < BYTE_NUM; byte++) { + center_min[byte] = params->tx_center_min[chn][rank][byte]; + center_max[byte] = params->tx_center_max[chn][rank][byte]; + + for (bit = 0; bit < DQS_BIT_NUMBER; bit++) { + bit_tmp = byte * DQS_BIT_NUMBER + bit; + final_win_perbit[bit_tmp].win_center = + params->tx_win_center[chn][rank][bit_tmp]; + } + } + } else { + final_vref = dramc_tx_window_perbit_cal_find_best_vref(cali, cali_type, enable_vref_scan, + final_range, vref_begin, vref_end, vref_step, + delay_begin, delay_end, delay_step, &vref_tmp, + center_min, center_max, final_win_perbit, vref_info); + } + + if (enable_vref_scan) { + if (bypass_cali) + final_vref = params->tx_window_vref[chn][rank]; + else + final_vref = tx_choose_vref(vref_info, vref_tmp); + + dramc_tx_set_vref(cali, final_range, final_vref); + return; + } + + if ((cali_type == TX_DQ_DQS_MOVE_DQ_ONLY) && (freq_group > DDRFREQ_1200)) { + enable_delaycell = 1; + dramc_dbg("[TX_PER_BIT_DELAY_CELL] DelayCellTimex100 =%d/100 ps\n", params->delay_cell_timex100); + } + + for (byte = 0; byte < BYTE_NUM; byte++) { + dramc_dbg(" == TX Byte %d ==\n", byte); + dqm_delay = ((center_min[byte] + center_max[byte]) >> 1); + + if (enable_delaycell == 0) { + delay = dqm_delay; + } else { + delay = center_min[byte]; + + for (bit = 0; bit < DQS_BIT_NUMBER; bit++) { + bit_tmp = byte * DQS_BIT_NUMBER + bit; + pi_diff = final_win_perbit[bit_tmp].win_center - + center_min[byte]; + if (params->delay_cell_timex100 != 0) { + delaycell_offset[bit_tmp] = (pi_diff * 100000000 / + (frequency << 6)) / + params->delay_cell_timex100; + dramc_dbg("delaycell_offset[%d]=%d cells (%d PI)\n", + bit_tmp, delaycell_offset[bit_tmp], pi_diff); + if (delaycell_offset[bit_tmp] > 255) { + dramc_dbg("[WARNING] TX DQ%d delay cell %d >255, adjust to 255 cell\n", + bit, delaycell_offset[bit_tmp]); + delaycell_offset[bit_tmp] = 255; + } + } + } + } + + tx_win_transfer_delay_to_uipi(cali, delay, 1, &dq_ui_large[byte], + &dq_ui_small[byte], &dq_pi[byte], + &dq_oen_ui_large[byte], &dq_oen_ui_small[byte]); + tx_win_transfer_delay_to_uipi(cali, dqm_delay, 1, &dqm_ui_large[byte], + &dqm_ui_small[byte], &dqm_pi[byte], + &dqm_oen_ui_large[byte], &dqm_oen_ui_small[byte]); + + if (cali_type == TX_DQ_DQS_MOVE_DQ_ONLY || cali_type == TX_DQ_DQS_MOVE_DQ_DQM) { + dramc_dbg("Update DQ dly =%d (%d ,%d, %d) DQ OEN =(%d ,%d)\n", + delay, dq_ui_large[byte], dq_ui_small[byte], dq_pi[byte], \ + dq_oen_ui_large[byte], dq_oen_ui_small[byte]); + } + + dramc_dbg("Update DQM dly =%d (%d ,%d, %d) DQM OEN =(%d ,%d)", + dqm_delay, dqm_ui_large[byte], dqm_ui_small[byte], dqm_pi[byte], + dqm_oen_ui_large[byte], dqm_oen_ui_small[byte]); + dramc_dbg("\n"); + } + + for (rk = rank; rk < RANK_MAX; rk++) { + if (cali_type == TX_DQ_DQS_MOVE_DQ_ONLY || cali_type == TX_DQ_DQS_MOVE_DQ_DQM) + tx_set_delay_reg_dq(chn, rk, true, dq_ui_large, dq_oen_ui_large, + dq_ui_small, dq_oen_ui_small, dq_pi); + tx_set_delay_reg_dqm(chn, rk, true, dqm_ui_large, dqm_oen_ui_large, + dqm_ui_small, dqm_oen_ui_small, dqm_pi); + + if (enable_delaycell) { + SET32_BITFIELDS(&ch[chn].phy_ao.byte[0].rk[rk].shu_r0_b0_txdly0, + SHU_R0_B0_TXDLY0_TX_ARDQ3_DLY_B0, delaycell_offset[3], + SHU_R0_B0_TXDLY0_TX_ARDQ2_DLY_B0, delaycell_offset[2], + SHU_R0_B0_TXDLY0_TX_ARDQ1_DLY_B0, delaycell_offset[1], + SHU_R0_B0_TXDLY0_TX_ARDQ0_DLY_B0, delaycell_offset[0]); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[0].rk[rk].shu_r0_b0_txdly1, + SHU_R0_B0_TXDLY1_TX_ARDQ7_DLY_B0, delaycell_offset[7], + SHU_R0_B0_TXDLY1_TX_ARDQ6_DLY_B0, delaycell_offset[6], + SHU_R0_B0_TXDLY1_TX_ARDQ5_DLY_B0, delaycell_offset[5], + SHU_R0_B0_TXDLY1_TX_ARDQ4_DLY_B0, delaycell_offset[4]); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].rk[rk].shu_r0_b0_txdly0, + SHU_R0_B1_TXDLY0_TX_ARDQ3_DLY_B1, delaycell_offset[11], + SHU_R0_B1_TXDLY0_TX_ARDQ2_DLY_B1, delaycell_offset[10], + SHU_R0_B1_TXDLY0_TX_ARDQ1_DLY_B1, delaycell_offset[9], + SHU_R0_B1_TXDLY0_TX_ARDQ0_DLY_B1, delaycell_offset[8]); + SET32_BITFIELDS(&ch[chn].phy_ao.byte[1].rk[rk].shu_r0_b0_txdly1, + SHU_R0_B1_TXDLY1_TX_ARDQ7_DLY_B1, delaycell_offset[15], + SHU_R0_B1_TXDLY1_TX_ARDQ6_DLY_B1, delaycell_offset[14], + SHU_R0_B1_TXDLY1_TX_ARDQ5_DLY_B1, delaycell_offset[13], + SHU_R0_B1_TXDLY1_TX_ARDQ4_DLY_B1, delaycell_offset[12]); + } + + update_tx_tracking(chn, rk, cali_type, dq_pi, dqm_pi); + } +} diff --git a/src/soc/mediatek/mt8192/dramc_pi_main.c b/src/soc/mediatek/mt8192/dramc_pi_main.c index 41d85a2..f724e54 100644 --- a/src/soc/mediatek/mt8192/dramc_pi_main.c +++ b/src/soc/mediatek/mt8192/dramc_pi_main.c @@ -16,6 +16,26 @@ shift_dq_ui(cali, cali->rank, ui_move); }
+static void switch_write_dbi_settings(const struct ddr_cali *cali, dbi_mode dbi_state) +{ + s8 tx_shift; + u8 chn, rank; + struct mr_values *mr_value = cali->mr_value; + u8 mr03 = mr_value->mr03[get_fsp(cali)]; + + chn = cali->chn; + rank = cali->rank; + + tx_shift = dbi_state ? -1 : 1; + dramc_write_shift_mck_write_DBI(cali, tx_shift); + + mr03 = (mr03 & ~(0x1 << 7)) | (dbi_state << 7); + dramc_mode_reg_write_by_rank(chn, rank, 3, mr03); + mr_value->mr03[get_fsp(cali)] = mr03; + + dramc_write_dbi_onoff(dbi_state); +} + static void dramc_ac_timing_optimize(const struct ddr_cali* cali) { u8 rf_group = 0, cab_id = 0; @@ -250,6 +270,23 @@ dramc_rx_dqs_gating_cal(cali, &txdly_min, &txdly_max);
dramc_rx_window_perbit_cal(cali, RX_WIN_RD_DQC); + dramc_tx_window_perbit_cal(cali, TX_DQ_DQS_MOVE_DQ_DQM, + dqs_final_delay, false); + if (get_vref_cali(cali) == VREF_CALI_ON) + dramc_tx_window_perbit_cal(cali, TX_DQ_DQS_MOVE_DQ_ONLY, + dqs_final_delay, true); + + dramc_tx_window_perbit_cal(cali, TX_DQ_DQS_MOVE_DQ_ONLY, + dqs_final_delay, false); + + if (get_write_dbi(cali) == DBI_ON) { + dramc_dbg("K DQM with DBI_ON\n"); + switch_write_dbi_settings(cali, DBI_ON); + dramc_tx_window_perbit_cal(cali, TX_DQ_DQS_MOVE_DQM_ONLY, + dqs_final_delay, false); + switch_write_dbi_settings(cali, DBI_OFF); + } + dramc_rx_window_perbit_cal(cali, RX_WIN_TEST_ENG); } dramc_rx_dqs_gating_post_process(cali, txdly_min, txdly_max);