aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHuayang Duan <huayang.duan@mediatek.com>2018-09-26 17:39:29 +0800
committerPatrick Georgi <pgeorgi@google.com>2018-12-11 08:57:16 +0000
commit2b5067b2c70bf0e0a995b6e33485a8ac15268cbc (patch)
tree75ba50d1d2efe7767dd36cdd47d09c5de958f1f2 /src
parent0655761b67d515fbe44ec5b31c3e58167d5f2b4e (diff)
mediatek/mt8183: Add DDR driver of tx rx window perbit cal part
BUG=b:80501386 BRANCH=none TEST=Boots correctly on Kukui, and inits DRAM successfully with related patches. Change-Id: I4434897864993e254e1362416316470083351493 Signed-off-by: Huayang Duan <huayang.duan@mediatek.com> Reviewed-on: https://review.coreboot.org/c/28842 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: You-Cheng Syu <youcheng@google.com> Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Diffstat (limited to 'src')
-rw-r--r--src/soc/mediatek/mt8183/dramc_pi_calibration_api.c922
-rw-r--r--src/soc/mediatek/mt8183/emi.c2
-rw-r--r--src/soc/mediatek/mt8183/include/soc/dramc_pi_api.h24
-rw-r--r--src/soc/mediatek/mt8183/include/soc/dramc_register.h2
-rw-r--r--src/soc/mediatek/mt8183/include/soc/emi.h2
-rw-r--r--src/soc/mediatek/mt8183/include/soc/memlayout.ld2
6 files changed, 864 insertions, 90 deletions
diff --git a/src/soc/mediatek/mt8183/dramc_pi_calibration_api.c b/src/soc/mediatek/mt8183/dramc_pi_calibration_api.c
index 08264fd129..92117ef5ee 100644
--- a/src/soc/mediatek/mt8183/dramc_pi_calibration_api.c
+++ b/src/soc/mediatek/mt8183/dramc_pi_calibration_api.c
@@ -19,18 +19,79 @@
#include <soc/emi.h>
#include <soc/dramc_register.h>
#include <soc/dramc_pi_api.h>
+#include <timer.h>
+
+enum {
+ RX_VREF_BEGIN = 0,
+ RX_VREF_END = 12,
+ RX_VREF_STEP = 1,
+ TX_VREF_BEGIN = 8,
+ TX_VREF_END = 18,
+ TX_VREF_STEP = 2,
+};
+
+enum {
+ FIRST_DQ_DELAY = 0,
+ FIRST_DQS_DELAY = -10,
+ MAX_DQDLY_TAPS = 16,
+ MAX_RX_DQDLY_TAPS = 63,
+};
enum {
GATING_START = 26,
GATING_END = GATING_START + 24,
};
-static void auto_refresh_switch(u8 chn, u8 option)
+enum CAL_TYPE {
+ RX_WIN_RD_DQC = 0,
+ RX_WIN_TEST_ENG,
+ TX_WIN_DQ_ONLY,
+ TX_WIN_DQ_DQM,
+};
+
+enum RX_TYPE {
+ RX_DQ = 0,
+ RX_DQM,
+ RX_DQS,
+};
+
+struct dqdqs_perbit_dly {
+ struct perbit_dly {
+ s16 first;
+ s16 last;
+ s16 best_first;
+ s16 best_last;
+ s16 best;
+ } dqdly, dqsdly;
+};
+
+struct vref_perbit_dly {
+ u8 vref;
+ u16 max_win;
+ u16 min_win;
+ struct dqdqs_perbit_dly perbit_dly[DQ_DATA_WIDTH];
+};
+
+struct tx_dly_tune {
+ u8 fine_tune;
+ u8 coarse_tune_large;
+ u8 coarse_tune_small;
+ u8 coarse_tune_large_oen;
+ u8 coarse_tune_small_oen;
+};
+
+struct per_byte_dly {
+ u16 max_center;
+ u16 min_center;
+ u16 final_dly;
+};
+
+static void auto_refresh_switch(u8 chn, bool option)
{
clrsetbits_le32(&ch[chn].ao.refctrl0, 1 << REFCTRL0_REFDIS_SHIFT,
(option ? 0 : 1) << REFCTRL0_REFDIS_SHIFT);
- if (option == DISABLE) {
+ if (!option) {
/*
* Because HW will actually disable autorefresh after
* refresh_queue empty, we need to wait until queue empty.
@@ -41,25 +102,17 @@ static void auto_refresh_switch(u8 chn, u8 option)
}
}
-static void dramc_cke_fix_onoff(int option, u8 chn)
+static void dramc_cke_fix_onoff(u8 chn, bool fix_on, bool fix_off)
{
- u8 on = 0, off = 0;
-
- /* If CKE is dynamic, set both CKE fix On and Off as 0. */
- if (option != CKE_DYNAMIC) {
- on = option;
- off = (1 - option);
- }
-
- clrsetbits_le32(&ch[chn].ao.ckectrl,
- (0x1 << 6) | (0x1 << 7), (on << 6) | (off << 7));
+ clrsetbits_le32(&ch[chn].ao.ckectrl, (0x1 << 6) | (0x1 << 7),
+ ((fix_on ? 1 : 0) << 6) | ((fix_off ? 1 : 0) << 7));
}
static void dramc_mode_reg_write(u8 chn, u8 mr_idx, u8 value)
{
u32 ckectrl_bak = read32(&ch[chn].ao.ckectrl);
- dramc_cke_fix_onoff(CKE_FIXON, chn);
+ dramc_cke_fix_onoff(chn, true, false);
clrsetbits_le32(&ch[chn].ao.mrs,
MRS_MRSMA_MASK, mr_idx << MRS_MRSMA_SHIFT);
clrsetbits_le32(&ch[chn].ao.mrs,
@@ -120,21 +173,21 @@ static void cmd_bus_training(u8 chn, u8 rank,
dramc_mode_reg_write_by_rank(chn, rank, 12, mr12_value);
}
-static void dramc_read_dbi_onoff(u8 onoff)
+static void dramc_read_dbi_onoff(bool on)
{
for (size_t chn = 0; chn < CHANNEL_MAX; chn++)
for (size_t b = 0; b < 2; b++)
clrsetbits_le32(&ch[chn].phy.shu[0].b[b].dq[7],
- 0x1 << SHU1_BX_DQ7_R_DMDQMDBI_SHU_SHIFT,
- onoff << SHU1_BX_DQ7_R_DMDQMDBI_SHU_SHIFT);
+ 0x1 << SHU1_BX_DQ7_R_DMDQMDBI_SHIFT,
+ (on ? 1 : 0) << SHU1_BX_DQ7_R_DMDQMDBI_SHIFT);
}
-static void dramc_write_dbi_onoff(u8 onoff)
+static void dramc_write_dbi_onoff(bool on)
{
for (size_t chn = 0; chn < CHANNEL_MAX; chn++)
clrsetbits_le32(&ch[chn].ao.shu[0].wodt,
0x1 << SHU1_WODT_DBIWR_SHIFT,
- onoff << SHU1_WODT_DBIWR_SHIFT);
+ (on ? 1 : 0) << SHU1_WODT_DBIWR_SHIFT);
}
static void dramc_phy_dcm_disable(u8 chn)
@@ -152,7 +205,7 @@ static void dramc_phy_dcm_disable(u8 chn)
clrbits_le32(&ch[chn].phy.misc_cg_ctrl5, (0x7 << 16) | (0x7 << 20));
}
-static void dramc_enable_phy_dcm(u8 en)
+static void dramc_enable_phy_dcm(bool en)
{
u32 broadcast_bak = dramc_get_broadcast();
dramc_set_broadcast(DRAMC_BROADCAST_OFF);
@@ -178,10 +231,12 @@ static void dramc_enable_phy_dcm(u8 en)
((en ? 0x1 : 0) << 31));
/* DCM on: CHANNEL_EMI free run; DCM off: mem_dcm */
- assert(en == 0 || en == 1);
- write32(&ch[chn].phy.misc_cg_ctrl2, 0x8060033e | (0x40 << en));
- write32(&ch[chn].phy.misc_cg_ctrl2, 0x8060033f | (0x40 << en));
- write32(&ch[chn].phy.misc_cg_ctrl2, 0x8060033e | (0x40 << en));
+ write32(&ch[chn].phy.misc_cg_ctrl2,
+ 0x8060033e | (0x40 << (en ? 0x1 : 0)));
+ write32(&ch[chn].phy.misc_cg_ctrl2,
+ 0x8060033f | (0x40 << (en ? 0x1 : 0)));
+ write32(&ch[chn].phy.misc_cg_ctrl2,
+ 0x8060033e | (0x40 << (en ? 0x1 : 0)));
clrsetbits_le32(&ch[chn].phy.misc_ctrl3, 0x3 << 26,
(en ? 0 : 0x3) << 26);
@@ -215,13 +270,13 @@ static void reset_delay_chain_before_calibration(void)
}
}
-static void dramc_hw_gating_onoff(u8 chn, u8 onoff)
+static void dramc_hw_gating_onoff(u8 chn, bool on)
{
clrsetbits_le32(&ch[chn].ao.shuctrl2, 0x3 << 14,
- (onoff << 14) | (onoff << 15));
- clrsetbits_le32(&ch[chn].ao.stbcal2, 0x1 << 28, onoff << 28);
- clrsetbits_le32(&ch[chn].ao.stbcal, 0x1 << 24, onoff << 24);
- clrsetbits_le32(&ch[chn].ao.stbcal, 0x1 << 22, onoff << 22);
+ (on ? 0x3 : 0) << 14);
+ clrsetbits_le32(&ch[chn].ao.stbcal2, 0x1 << 28, (on ? 0x1 : 0) << 28);
+ clrsetbits_le32(&ch[chn].ao.stbcal, 0x1 << 24, (on ? 0x1 : 0) << 24);
+ clrsetbits_le32(&ch[chn].ao.stbcal, 0x1 << 22, (on ? 0x1 : 0) << 22);
}
static void dramc_rx_input_delay_tracking_init_by_freq(u8 chn)
@@ -236,7 +291,7 @@ static void dramc_rx_input_delay_tracking_init_by_freq(u8 chn)
void dramc_apply_pre_calibration_config(void)
{
- dramc_enable_phy_dcm(0);
+ dramc_enable_phy_dcm(false);
reset_delay_chain_before_calibration();
setbits_le32(&ch[0].ao.shu[0].conf[3], 0x1ff << 16);
@@ -252,8 +307,8 @@ void dramc_apply_pre_calibration_config(void)
clrbits_le32(&ch[0].ao.dqsoscr, 0x1 << 26);
clrbits_le32(&ch[0].ao.dqsoscr, 0x1 << 25);
- dramc_write_dbi_onoff(DBI_OFF);
- dramc_read_dbi_onoff(DBI_OFF);
+ dramc_write_dbi_onoff(false);
+ dramc_read_dbi_onoff(false);
for (size_t chn = 0; chn < CHANNEL_MAX; chn++) {
setbits_le32(&ch[chn].ao.spcmdctrl, 0x1 << 29);
@@ -262,7 +317,7 @@ void dramc_apply_pre_calibration_config(void)
setbits_le32(&ch[chn].ao.shu[shu].scintv, 0x1 << 30);
clrbits_le32(&ch[chn].ao.dummy_rd, (0x1 << 7) | (0x7 << 20));
- dramc_hw_gating_onoff(chn, GATING_OFF);
+ dramc_hw_gating_onoff(chn, false);
clrbits_le32(&ch[chn].ao.stbcal2, 0x1 << 28);
setbits_le32(&ch[chn].phy.misc_ctrl1,
@@ -303,7 +358,7 @@ static void dramc_set_rank_engine2(u8 chn, u8 rank)
rank << TEST2_4_TESTAGENTRK_SHIFT);
}
-static void dramc_engine2_init(u8 chn, u8 rank, u32 size, bool testaudpat)
+static void dramc_engine2_init(u8 chn, u8 rank, u32 size, bool test_pat)
{
const u32 pat0 = 0x55;
const u32 pat1 = 0xaa;
@@ -335,11 +390,11 @@ static void dramc_engine2_init(u8 chn, u8 rank, u32 size, bool testaudpat)
(0x1 << TEST2_4_TESTAUDMODE_SHIFT) |
(0x1 << TEST2_4_TESTAUDBITINV_SHIFT) |
(0x1 << TEST2_4_TESTXTALKPAT_SHIFT),
- ((!testaudpat ? 1 : 0) << TEST2_4_TESTXTALKPAT_SHIFT) |
- ((testaudpat ? 1 : 0) << TEST2_4_TESTAUDMODE_SHIFT) |
- ((testaudpat ? 1 : 0) << TEST2_4_TESTAUDBITINV_SHIFT));
+ ((!test_pat ? 1 : 0) << TEST2_4_TESTXTALKPAT_SHIFT) |
+ ((test_pat ? 1 : 0) << TEST2_4_TESTAUDMODE_SHIFT) |
+ ((test_pat ? 1 : 0) << TEST2_4_TESTAUDBITINV_SHIFT));
- if (!testaudpat) {
+ if (!test_pat) {
clrbits_le32(&ch[chn].ao.test2_4,
(0x1 << TEST2_4_TEST_REQ_LEN1_SHIFT) |
(0x1 << TEST2_4_TESTSSOPAT_SHIFT) |
@@ -354,28 +409,19 @@ static void dramc_engine2_init(u8 chn, u8 rank, u32 size, bool testaudpat)
}
clrsetbits_le32(&ch[chn].ao.test2_3,
TEST2_3_TESTCNT_MASK | (0x1 << TEST2_3_TESTAUDPAT_SHIFT),
- (testaudpat ? 1 : 0) << TEST2_3_TESTAUDPAT_SHIFT);
+ (test_pat ? 1 : 0) << TEST2_3_TESTAUDPAT_SHIFT);
}
static void dramc_engine2_check_complete(u8 chn)
{
- u32 u4loop_count = 0;
-
/* In some case test engine finished but the complete signal late come,
* system will wait very long time. Hence, we set a timeout here.
* After system receive complete signal or wait until time out
* it will return, the caller will check compare result to verify
* whether engine success.
*/
- while ((read32(&ch[chn].nao.testrpt) & 0x1) == 0) {
- udelay(1);
- u4loop_count++;
-
- if (u4loop_count > MAX_CMP_CPT_WAIT_LOOP) {
- dramc_dbg("MEASURE_A timeout\n");
- break;
- }
- }
+ if (!wait_us(10000, read32(&ch[chn].nao.testrpt) & 0x1))
+ dramc_dbg("MEASURE_A timeout\n");
}
static u32 dramc_engine2_run(u8 chn, enum dram_te_op wr)
@@ -530,10 +576,10 @@ static void dramc_set_gating_mode(u8 chn, bool mode)
static void dramc_rx_dqs_gating_cal_pre(u8 chn, u8 rank)
{
- rx_dqs_isi_pulse_cg_switch(chn, DISABLE);
+ rx_dqs_isi_pulse_cg_switch(chn, false);
clrbits_le32(&ch[chn].ao.refctrl0, 1 << REFCTRL0_PBREFEN_SHIFT);
- dramc_hw_gating_onoff(chn, GATING_OFF);
+ dramc_hw_gating_onoff(chn, false);
setbits_le32(&ch[chn].ao.stbcal1, 1 << STBCAL1_STBENCMPEN_SHIFT);
setbits_le32(&ch[chn].ao.stbcal1, 1 << STBCAL1_STBCNT_LATCH_EN_SHIFT);
@@ -559,7 +605,7 @@ static void dramc_write_dqs_gating_result(u8 chn, u8 rank,
u8 best_coarse_rodt_p1[DQS_NUMBER];
u8 best_coarse_0p5t_rodt_p1[DQS_NUMBER];
- rx_dqs_isi_pulse_cg_switch(chn, ENABLE);
+ rx_dqs_isi_pulse_cg_switch(chn, true);
write32(&ch[chn].ao.shu[0].rk[rank].selph_dqsg0,
((u32) best_coarse_tune2t[0] <<
@@ -604,7 +650,7 @@ static void dramc_write_dqs_gating_result(u8 chn, u8 rank,
best_coarse_0p5t_rodt[dqs] = 0;
best_coarse_rodt_p1[dqs] = 4;
best_coarse_0p5t_rodt_p1[dqs] = 4;
- dramc_dbg("RxdqsGatingCal error: best_coarse_tune2t:%d"
+ dramc_dbg("RxdqsGatingCal error: best_coarse_tune2t:%zd"
" is already 0. RODT cannot be -1 coarse\n",
dqs);
}
@@ -667,7 +713,7 @@ static void dramc_rx_dqs_gating_cal(u8 chn, u8 rank)
dramc_rx_dqs_gating_cal_pre(chn, rank);
u32 dummy_rd_backup = read32(&ch[chn].ao.dummy_rd);
- dramc_engine2_init(chn, rank, 0x23, 1);
+ dramc_engine2_init(chn, rank, 0x23, true);
dramc_dbg("[Gating]\n");
for (u32 coarse_tune = coarse_start; coarse_tune < coarse_end;
@@ -834,9 +880,753 @@ static void dramc_rx_dqs_gating_cal(u8 chn, u8 rank)
dram_phy_reset(chn);
}
+static void dramc_rd_dqc_init(u8 chn, u8 rank)
+{
+ const u8 *lpddr_phy_mapping = phy_mapping[chn];
+ u16 temp_value = 0;
+
+ for (size_t b = 0; b < 2; b++)
+ clrbits_le32(&ch[chn].phy.shu[0].b[b].dq[7],
+ 0x1 << SHU1_BX_DQ7_R_DMDQMDBI_SHIFT);
+
+ clrsetbits_le32(&ch[chn].ao.mrs,
+ MRS_MRSRK_MASK, rank << MRS_MRSRK_SHIFT);
+ setbits_le32(&ch[chn].ao.mpc_option,
+ 0x1 << MPC_OPTION_MPCRKEN_SHIFT);
+
+ for (size_t i = 0; i < 16; i++)
+ temp_value |= ((0x5555 >> i) & 0x1) << lpddr_phy_mapping[i];
+
+ u16 mr15_golden_value = temp_value & 0xff;
+ u16 mr20_golden_value = (temp_value >> 8) & 0xff;
+ clrsetbits_le32(&ch[chn].ao.mr_golden,
+ MR_GOLDEN_MR15_GOLDEN_MASK | MR_GOLDEN_MR20_GOLDEN_MASK,
+ (mr15_golden_value << 8) | mr20_golden_value);
+}
+
+static u32 dramc_rd_dqc_run(u8 chn)
+{
+ setbits_le32(&ch[chn].ao.spcmdctrl, 1 << SPCMDCTRL_RDDQCDIS_SHIFT);
+ setbits_le32(&ch[chn].ao.spcmd, 1 << SPCMD_RDDQCEN_SHIFT);
+
+ if (!wait_us(100, read32(&ch[chn].nao.spcmdresp) &
+ (0x1 << SPCMDRESP_RDDQC_RESPONSE_SHIFT)))
+ dramc_dbg("[RDDQC] resp fail (time out)\n");
+
+ u32 result = read32(&ch[chn].nao.rdqc_cmp);
+ clrbits_le32(&ch[chn].ao.spcmd, 1 << SPCMD_RDDQCEN_SHIFT);
+ clrbits_le32(&ch[chn].ao.spcmdctrl, 1 << SPCMDCTRL_RDDQCDIS_SHIFT);
+
+ return result;
+}
+
+static void dramc_rd_dqc_end(u8 chn)
+{
+ clrbits_le32(&ch[chn].ao.mrs, MRS_MRSRK_MASK);
+}
+
+static void dramc_rx_vref_enable(u8 chn)
+{
+ setbits_le32(&ch[chn].phy.b[0].dq[5],
+ 0x1 << B0_DQ5_RG_RX_ARDQ_VREF_EN_B0_SHIFT);
+ setbits_le32(&ch[chn].phy.b[1].dq[5],
+ 0x1 << B1_DQ5_RG_RX_ARDQ_VREF_EN_B1_SHIFT);
+}
+
+static void dramc_set_rx_vref(u8 chn, u8 value)
+{
+ for (size_t b = 0; b < 2; b++)
+ clrsetbits_le32(&ch[chn].phy.shu[0].b[b].dq[5],
+ SHU1_BX_DQ5_RG_RX_ARDQ_VREF_SEL_B0_MASK,
+ value << SHU1_BX_DQ5_RG_RX_ARDQ_VREF_SEL_B0_SHIFT);
+}
+
+static void dramc_set_tx_vref(u8 chn, u8 rank, u8 value)
+{
+ dramc_mode_reg_write_by_rank(chn, rank, 14, value);
+}
+
+static void dramc_set_vref(u8 chn, u8 rank, enum CAL_TYPE type, u8 vref)
+{
+ if (type == RX_WIN_TEST_ENG)
+ dramc_set_rx_vref(chn, vref);
+ else
+ dramc_set_tx_vref(chn, rank, vref);
+}
+
+static void dramc_transfer_dly_tune(
+ u8 chn, u32 dly, struct tx_dly_tune *dly_tune)
+{
+ u16 tmp_val;
+
+ dly_tune->fine_tune = dly & (TX_DQ_COARSE_TUNE_TO_FINE_TUNE_TAP - 1);
+
+ tmp_val = (dly / TX_DQ_COARSE_TUNE_TO_FINE_TUNE_TAP) << 1;
+ dly_tune->coarse_tune_small = tmp_val - ((tmp_val >> 3) << 3);
+ dly_tune->coarse_tune_large = tmp_val >> 3;
+
+ tmp_val -= 4;
+ dly_tune->coarse_tune_small_oen = tmp_val - ((tmp_val >> 3) << 3);
+ dly_tune->coarse_tune_large_oen = tmp_val >> 3;
+}
+
+static void set_rx_dly_factor(u8 chn, u8 rank, enum RX_TYPE type, u32 val)
+{
+ u32 tmp, mask;
+
+ switch (type) {
+ case RX_DQ:
+ tmp = (val << 24 | val << 16 | val << 8 | val);
+ for (size_t i = 2; i < 6; i++) {
+ write32(&ch[chn].phy.shu[0].rk[rank].b[0].dq[i], tmp);
+ write32(&ch[chn].phy.shu[0].rk[rank].b[1].dq[i], tmp);
+ }
+ break;
+
+ case RX_DQM:
+ tmp = (val << 8 | val);
+ mask = SHU1_B0_DQ6_RK_RX_ARDQM0_F_DLY_B0_MASK |
+ SHU1_B0_DQ6_RK_RX_ARDQM0_R_DLY_B0_MASK;
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[0].dq[6],
+ mask, tmp);
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[1].dq[6],
+ mask, tmp);
+ break;
+
+ case RX_DQS:
+ tmp = (val << 24 | val << 16);
+ mask = SHU1_B0_DQ6_RK_RX_ARDQS0_F_DLY_B0_MASK |
+ SHU1_B0_DQ6_RK_RX_ARDQS0_R_DLY_B0_MASK;
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[0].dq[6],
+ mask, tmp);
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[1].dq[6],
+ mask, tmp);
+ break;
+ }
+}
+
+static void set_tx_dly_factor(u8 chn, u8 rank, enum CAL_TYPE type, u32 val)
+{
+ struct tx_dly_tune dly_tune = {0};
+ u32 coarse_tune_large = 0, coarse_tune_large_oen = 0;
+ u32 coarse_tune_small = 0, coarse_tune_small_oen = 0;
+
+ dramc_transfer_dly_tune(chn, val, &dly_tune);
+
+ for (u8 i = 0; i < 4; i++) {
+ coarse_tune_large += dly_tune.coarse_tune_large << (i * 4);
+ coarse_tune_large_oen +=
+ dly_tune.coarse_tune_large_oen << (i * 4);
+ coarse_tune_small += dly_tune.coarse_tune_small << (i * 4);
+ coarse_tune_small_oen +=
+ dly_tune.coarse_tune_small_oen << (i * 4);
+ }
+ if (type == TX_WIN_DQ_DQM)
+ dramc_dbg("%3d |%d %d %2d | [0]",
+ val, dly_tune.coarse_tune_large,
+ dly_tune.coarse_tune_small, dly_tune.fine_tune);
+
+ if (type != TX_WIN_DQ_DQM && type != TX_WIN_DQ_ONLY)
+ return;
+
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[0],
+ (coarse_tune_large_oen << 16) | coarse_tune_large);
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[2],
+ (coarse_tune_small_oen << 16) | coarse_tune_small);
+ for (size_t b = 0; b < 2; b++)
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[b].dq[7],
+ FINE_TUNE_DQ_MASK, dly_tune.fine_tune << 8);
+
+ if (type == TX_WIN_DQ_DQM) {
+ /* Large coarse_tune setting */
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[1],
+ (coarse_tune_large_oen << 16) | coarse_tune_large);
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[3],
+ (coarse_tune_small_oen << 16) | coarse_tune_small);
+ /* Fine_tune delay setting */
+ for (size_t b = 0; b < 2; b++)
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[b].dq[7],
+ FINE_TUNE_DQM_MASK, dly_tune.fine_tune << 16);
+ }
+}
+
+static u32 dramc_get_smallest_dqs_dly(
+ u8 chn, u8 rank, const struct sdram_params *params)
+{
+ u32 min_dly = 0xffff;
+
+ for (size_t i = 0; i < DQS_NUMBER; i++)
+ min_dly = MIN(min_dly, params->wr_level[chn][rank][i]);
+
+ return DQS_DELAY + min_dly + 40;
+}
+
+static void dramc_get_dly_range(u8 chn, u8 rank, enum CAL_TYPE type,
+ u16 *pre_cal, s16 *begin, s16 *end,
+ const struct sdram_params *params)
+{
+ u16 pre_dq_dly;
+ switch (type) {
+ case RX_WIN_RD_DQC:
+ *begin = FIRST_DQS_DELAY;
+ *end = MAX_RX_DQDLY_TAPS;
+ break;
+
+ case RX_WIN_TEST_ENG:
+ *begin = FIRST_DQ_DELAY;
+ *end = MAX_RX_DQDLY_TAPS;
+ break;
+
+ case TX_WIN_DQ_DQM:
+ *begin = dramc_get_smallest_dqs_dly(chn, rank, params);
+ *end = *begin + 256;
+ break;
+
+ case TX_WIN_DQ_ONLY:
+ pre_dq_dly = MIN(pre_cal[0], pre_cal[1]);
+ pre_dq_dly = (pre_dq_dly > 24) ? (pre_dq_dly - 24) : 0;
+ *begin = pre_dq_dly;
+ *end = *begin + 64;
+ break;
+ }
+}
+static int dramc_check_dqdqs_win(
+ struct dqdqs_perbit_dly *p, s16 dly_pass, s16 last_step,
+ bool fail, bool is_dq)
+{
+ s16 best_pass_win;
+ struct perbit_dly *dly = is_dq ? &p->dqdly : &p->dqsdly;
+
+ if (!fail && dly->first == -1)
+ dly->first = dly_pass;
+
+ if (!fail && dly->last == -2 && dly_pass == last_step)
+ dly->last = dly_pass;
+ else if (fail && dly->first != -1 && dly->last == -2)
+ dly->last = dly_pass - 1;
+
+ if (dly->last == -2)
+ return 0;
+
+ int pass_win = dly->last - dly->first;
+ best_pass_win = dly->best_last - dly->best_first;
+ if (pass_win > best_pass_win) {
+ dly->best_last = dly->last;
+ dly->best_first = dly->first;
+ }
+ /* Clear to find the next pass range if it has */
+ dly->first = -1;
+ dly->last = -2;
+
+ return pass_win;
+}
+
+static void dramc_set_vref_dly(struct vref_perbit_dly *vref_dly,
+ u8 vref, u32 win_size_sum, struct dqdqs_perbit_dly delay[])
+{
+ struct dqdqs_perbit_dly *perbit_dly = vref_dly->perbit_dly;
+
+ vref_dly->max_win = win_size_sum;
+ vref_dly->vref = vref;
+ for (size_t bit = 0; bit < DQ_DATA_WIDTH; bit++) {
+ perbit_dly[bit].dqdly.best = delay[bit].dqdly.best;
+ perbit_dly[bit].dqdly.best_first = delay[bit].dqdly.best_first;
+ perbit_dly[bit].dqdly.best_last = delay[bit].dqdly.best_last;
+ perbit_dly[bit].dqsdly.best_first =
+ delay[bit].dqsdly.best_first;
+ perbit_dly[bit].dqsdly.best_last = delay[bit].dqsdly.best_last;
+ }
+}
+
+static bool dramk_calc_best_vref(enum CAL_TYPE type, u8 vref,
+ struct vref_perbit_dly *vref_dly,
+ struct dqdqs_perbit_dly delay[])
+{
+ u32 win_size;
+ u32 win_size_sum = 0;
+ static u32 min_win_size_vref;
+
+ switch (type) {
+ case RX_WIN_TEST_ENG:
+ for (size_t bit = 0; bit < DQ_DATA_WIDTH; bit++) {
+ win_size_sum += delay[bit].dqdly.best_last -
+ delay[bit].dqdly.best_first + 1;
+ win_size_sum += delay[bit].dqsdly.best_last -
+ delay[bit].dqsdly.best_first + 1;
+ }
+
+ if (win_size_sum > vref_dly->max_win)
+ dramc_set_vref_dly(vref_dly, vref, win_size_sum, delay);
+
+ if (win_size_sum < (vref_dly->max_win * 95 / 100))
+ return true;
+
+ break;
+ case TX_DQ_DQS_MOVE_DQ_ONLY:
+ for (size_t bit = 0; bit < DQ_DATA_WIDTH; bit++) {
+ win_size = delay[bit].dqdly.best_last -
+ delay[bit].dqdly.best_first + 1;
+ vref_dly->min_win = MIN(vref_dly->min_win, win_size);
+ win_size_sum += win_size;
+ }
+
+ if (win_size_sum > vref_dly->max_win
+ && vref_dly->min_win >= min_win_size_vref) {
+ min_win_size_vref = vref_dly->min_win;
+ dramc_set_vref_dly(vref_dly, vref, win_size_sum, delay);
+ }
+
+ break;
+ default:
+ dramc_set_vref_dly(vref_dly, vref, win_size_sum, delay);
+ break;
+ }
+
+ return false;
+}
+
+static void dramc_calc_tx_perbyte_dly(
+ struct dqdqs_perbit_dly *p, s16 *win,
+ struct per_byte_dly *byte_delay_prop)
+{
+ s16 win_center = (p->dqdly.best_first + p->dqdly.best_last) >> 1;
+ *win = win_center;
+
+ if (win_center < byte_delay_prop->min_center)
+ byte_delay_prop->min_center = win_center;
+ if (win_center > byte_delay_prop->max_center)
+ byte_delay_prop->max_center = win_center;
+}
+
+static void dramc_set_rx_dly(u8 chn, u8 rank, s32 dly)
+{
+ if (dly <= 0) {
+ /* Hold time calibration */
+ set_rx_dly_factor(chn, rank, RX_DQS, -dly);
+ dram_phy_reset(chn);
+ } else {
+ /* Setup time calibration */
+ set_rx_dly_factor(chn, rank, RX_DQS, 0);
+ set_rx_dly_factor(chn, rank, RX_DQM, dly);
+ dram_phy_reset(chn);
+ set_rx_dly_factor(chn, rank, RX_DQ, dly);
+ }
+
+}
+
+static void set_tx_best_dly_factor(u8 chn, u8 rank_start,
+ struct per_byte_dly *tx_perbyte_dly, u16 dq_precal_result[])
+{
+ u32 coarse_tune_large = 0;
+ u32 coarse_tune_large_oen = 0;
+ u32 coarse_tune_small = 0;
+ u32 coarse_tune_small_oen = 0;
+ u16 dq_oen[DQS_NUMBER] = {0}, dqm_oen[DQS_NUMBER] = {0};
+ struct tx_dly_tune dqdly_tune[DQS_NUMBER] = {0};
+ struct tx_dly_tune dqmdly_tune[DQS_NUMBER] = {0};
+
+ for (size_t i = 0; i < DQS_NUMBER; i++) {
+ dramc_transfer_dly_tune(chn, tx_perbyte_dly[i].final_dly,
+ &dqdly_tune[i]);
+ dramc_transfer_dly_tune(chn, dq_precal_result[i],
+ &dqmdly_tune[i]);
+
+ coarse_tune_large += dqdly_tune[i].coarse_tune_large << (i * 4);
+ coarse_tune_large_oen +=
+ dqdly_tune[i].coarse_tune_large_oen << (i * 4);
+ coarse_tune_small += dqdly_tune[i].coarse_tune_small << (i * 4);
+ coarse_tune_small_oen +=
+ dqdly_tune[i].coarse_tune_small_oen << (i * 4);
+
+ dq_oen[i] = (dqdly_tune[i].coarse_tune_large_oen << 3) +
+ (dqdly_tune[i].coarse_tune_small_oen << 5) +
+ dqdly_tune[i].fine_tune;
+ dqm_oen[i] = (dqmdly_tune[i].coarse_tune_large_oen << 3) +
+ (dqmdly_tune[i].coarse_tune_small_oen << 5) +
+ dqmdly_tune[i].fine_tune;
+ }
+
+ for (size_t rank = rank_start; rank < RANK_MAX; rank++) {
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[0],
+ (coarse_tune_large_oen << 16) | coarse_tune_large);
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[2],
+ (coarse_tune_small_oen << 16) | coarse_tune_small);
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[1],
+ (coarse_tune_large_oen << 16) | coarse_tune_large);
+ write32(&ch[chn].ao.shu[0].rk[rank].selph_dq[3],
+ (coarse_tune_small_oen << 16) | coarse_tune_small);
+ }
+
+ for (size_t rank = rank_start; rank < RANK_MAX; rank++)
+ for (size_t b = 0; b < 2; b++)
+ clrsetbits_le32(&ch[chn].phy.shu[0].rk[rank].b[b].dq[7],
+ FINE_TUNE_DQ_MASK | FINE_TUNE_DQM_MASK,
+ (dqdly_tune[b].fine_tune <<
+ FINE_TUNE_DQ_SHIFT) |
+ (dqmdly_tune[b].fine_tune <<
+ FINE_TUNE_DQM_SHIFT));
+}
+
+static void set_rx_best_dly_factor(u8 chn, u8 rank,
+ struct dqdqs_perbit_dly *dqdqs_perbit_dly,
+ u32 *max_dqsdly_byte, u32 *ave_dqm_dly)
+{
+ u32 value;
+
+ for (size_t i = 0; i < DQS_NUMBER; i++) {
+ value = (max_dqsdly_byte[i] << 24) |
+ (max_dqsdly_byte[i] << 16) |
+ (ave_dqm_dly[i] << 8) | (ave_dqm_dly[i] << 0);
+ write32(&ch[chn].phy.shu[0].rk[rank].b[i].dq[6], value);
+ }
+ dram_phy_reset(chn);
+
+ for (size_t i = 0; i < DQ_DATA_WIDTH; i += 2) {
+ u32 byte = i / DQS_BIT_NUMBER;
+ u32 index = 2 + ((i % 8) * 2) / 4;
+ value = dqdqs_perbit_dly[i + 1].dqdly.best << 24 |
+ dqdqs_perbit_dly[i + 1].dqdly.best << 16 |
+ dqdqs_perbit_dly[i].dqdly.best << 8 |
+ dqdqs_perbit_dly[i].dqdly.best;
+ write32(&ch[chn].phy.shu[0].rk[rank].b[byte].dq[index], value);
+ }
+}
+
+static bool dramc_calc_best_dly(u8 bit,
+ struct dqdqs_perbit_dly *p, u32 *p_max_byte)
+{
+ u8 fail = 0, hold, setup;
+
+ hold = p->dqsdly.best_last - p->dqsdly.best_first + 1;
+ setup = p->dqdly.best_last - p->dqdly.best_first + 1;
+
+ if (hold > setup) {
+ p->dqdly.best = 0;
+ p->dqsdly.best = (setup != 0) ? (hold - setup) / 2 :
+ (hold - setup) / 2 + p->dqsdly.best_first;
+
+ if (p->dqsdly.best > *p_max_byte)
+ *p_max_byte = p->dqsdly.best;
+
+ } else if (hold < setup) {
+ p->dqsdly.best = 0;
+ p->dqdly.best = (hold != 0) ? (setup - hold) / 2 :
+ (setup - hold) / 2 + p->dqdly.best_first;
+
+ } else { /* Hold time == setup time */
+ p->dqsdly.best = 0;
+ p->dqdly.best = 0;
+
+ if (hold == 0) {
+ dramc_dbg("Error bit %d, setup = hold = 0\n", bit);
+ fail = 1;
+ }
+ }
+
+ dramc_dbg("bit#%d : dq =%d dqs=%d win=%d (%d, %d)\n", bit, setup,
+ hold, setup + hold, p->dqdly.best, p->dqsdly.best);
+
+ return fail;
+}
+
+static void dramc_set_dqdqs_dly(u8 chn, u8 rank, enum CAL_TYPE type, s32 dly)
+{
+ if ((type == RX_WIN_RD_DQC) || (type == RX_WIN_TEST_ENG))
+ dramc_set_rx_dly(chn, rank, dly);
+ else
+ set_tx_dly_factor(chn, rank, type, dly);
+}
+
+static void dramc_set_tx_best_dly(u8 chn, u8 rank,
+ struct dqdqs_perbit_dly *tx_dly, u16 *tx_dq_precal_result,
+ const struct sdram_params *params)
+{
+ s16 dq_win_center[DQ_DATA_WIDTH];
+ u16 pi_diff;
+ u32 byte_dly_cell[DQS_NUMBER] = {0};
+ struct per_byte_dly tx_perbyte_dly[DQS_NUMBER];
+ u16 dly_cell_unit = params->delay_cell_unit;
+ int index, bit;
+ u16 dq_delay_cell[DQ_DATA_WIDTH];
+
+ for (size_t i = 0; i < DQS_NUMBER; i++) {
+ tx_perbyte_dly[i].min_center = 0xffff;
+ tx_perbyte_dly[i].max_center = 0;
+ }
+
+ for (size_t i = 0; i < DQ_DATA_WIDTH; i++) {
+ index = i / DQS_BIT_NUMBER;
+ dramc_calc_tx_perbyte_dly(&tx_dly[i],
+ &dq_win_center[i], &tx_perbyte_dly[index]);
+ }
+
+ for (size_t i = 0; i < DQS_NUMBER; i++) {
+ tx_perbyte_dly[i].final_dly = tx_perbyte_dly[i].min_center;
+ tx_dq_precal_result[i] = (tx_perbyte_dly[i].max_center
+ + tx_perbyte_dly[i].min_center) >> 1;
+
+ for (bit = 0; bit < DQS_BIT_NUMBER; bit++) {
+ pi_diff = dq_win_center[i * 8 + bit]
+ - tx_perbyte_dly[i].min_center;
+ dq_delay_cell[i * 8 + bit] =
+ ((pi_diff * 1000000) / (16 * 64))
+ / dly_cell_unit;
+ byte_dly_cell[i] |=
+ (dq_delay_cell[i * 8 + bit] << (bit * 4));
+ }
+
+ write32(&ch[chn].phy.shu[0].rk[rank].b[i].dq[0],
+ byte_dly_cell[i]);
+ }
+
+ set_tx_best_dly_factor(chn, rank, tx_perbyte_dly, tx_dq_precal_result);
+}
+
+static int dramc_set_rx_best_dly(u8 chn, u8 rank,
+ struct dqdqs_perbit_dly *rx_dly)
+{
+ s16 dly;
+ bool fail = false;
+ u8 index, max_limit;
+ static u32 max_dqsdly_byte[DQS_NUMBER];
+ static u32 ave_dqmdly_byte[DQS_NUMBER];
+
+ for (size_t i = 0; i < DQS_NUMBER; i++) {
+ max_dqsdly_byte[i] = 0;
+ ave_dqmdly_byte[i] = 0;
+ }
+
+ for (size_t i = 0; i < DQ_DATA_WIDTH; i++) {
+ index = i / DQS_BIT_NUMBER;
+ fail |= dramc_calc_best_dly(i, &rx_dly[i],
+ &max_dqsdly_byte[index]);
+ }
+
+ for (size_t i = 0; i < DQ_DATA_WIDTH; i++) {
+ index = i / DQS_BIT_NUMBER;
+ /* Set DQS to max for 8-bit */
+ if (rx_dly[i].dqsdly.best < max_dqsdly_byte[index]) {
+ /* Delay DQ to compensate extra DQS delay */
+ dly = max_dqsdly_byte[index] - rx_dly[i].dqsdly.best;
+ rx_dly[i].dqdly.best += dly;
+ max_limit = MAX_DQDLY_TAPS - 1;
+ if (rx_dly[i].dqdly.best > max_limit)
+ rx_dly[i].dqdly.best = max_limit;
+ }
+
+ ave_dqmdly_byte[index] += rx_dly[i].dqdly.best;
+ if ((i + 1) % DQS_BIT_NUMBER == 0)
+ ave_dqmdly_byte[index] /= DQS_BIT_NUMBER;
+ }
+
+ if (fail) {
+ dramc_dbg("Fail on perbit_window_cal()\n");
+ return -1;
+ }
+
+ set_rx_best_dly_factor(chn, rank, rx_dly, max_dqsdly_byte,
+ ave_dqmdly_byte);
+ return 0;
+}
+
+static void dramc_get_vref_prop(u8 rank, enum CAL_TYPE type,
+ u8 *vref_scan_en, u8 *vref_begin, u8 *vref_end)
+{
+ if (type == RX_WIN_TEST_ENG && rank == RANK_0) {
+ *vref_scan_en = 1;
+ *vref_begin = RX_VREF_BEGIN;
+ *vref_end = RX_VREF_END;
+ } else if (type == TX_WIN_DQ_ONLY) {
+ *vref_scan_en = 1;
+ *vref_begin = TX_VREF_BEGIN;
+ *vref_end = TX_VREF_END;
+ } else {
+ *vref_scan_en = 0;
+ }
+}
+
+static void dramc_engine2_setpat(u8 chn, bool test_pat)
+{
+ clrbits_le32(&ch[chn].ao.test2_4,
+ (0x1 << TEST2_4_TESTXTALKPAT_SHIFT) |
+ (0x1 << TEST2_4_TESTAUDMODE_SHIFT) |
+ (0x1 << TEST2_4_TESTAUDBITINV_SHIFT));
+
+ if (!test_pat) {
+ setbits_le32(&ch[chn].ao.perfctl0, 1 << PERFCTL0_RWOFOEN_SHIFT);
+
+ clrbits_le32(&ch[chn].ao.test2_4,
+ (0x1 << TEST2_4_TEST_REQ_LEN1_SHIFT) |
+ (0x1 << TEST2_4_TESTSSOPAT_SHIFT) |
+ (0x1 << TEST2_4_TESTSSOXTALKPAT_SHIFT) |
+ (0x1 << TEST2_4_TESTXTALKPAT_SHIFT));
+ } else {
+ clrsetbits_le32(&ch[chn].ao.test2_4,
+ TEST2_4_TESTAUDINIT_MASK | TEST2_4_TESTAUDINC_MASK,
+ (0x11 << 8) | (0xd << 0) | (0x1 << 14));
+ }
+ clrsetbits_le32(&ch[chn].ao.test2_3,
+ (0x1 << TEST2_3_TESTAUDPAT_SHIFT) | TEST2_3_TESTCNT_MASK,
+ (test_pat ? 1 : 0) << TEST2_3_TESTAUDPAT_SHIFT);
+}
+
+static u32 dram_k_perbit(u8 chn, enum CAL_TYPE type)
+{
+ u32 err_value;
+
+ if (type == RX_WIN_RD_DQC) {
+ err_value = dramc_rd_dqc_run(chn);
+ } else {
+ dramc_engine2_setpat(chn, true);
+ err_value = dramc_engine2_run(chn, TE_OP_WRITE_READ_CHECK);
+ dramc_engine2_setpat(chn, false);
+ err_value |= dramc_engine2_run(chn, TE_OP_WRITE_READ_CHECK);
+ }
+ return err_value;
+}
+
+static u8 dramc_window_perbit_cal(u8 chn, u8 rank,
+ enum CAL_TYPE type, const struct sdram_params *params)
+{
+ u8 vref = 0, vref_begin = 0, vref_end = 1, vref_step = 1;
+ u8 dly_step = 2, vref_scan_enable = 0;
+ s16 dly, dly_begin = 0, dly_end = 0, last_step;
+ s16 dly_pass;
+ u32 dummy_rd_backup = 0, err_value, finish_bit;
+ static u16 dq_precal_result[DQS_NUMBER];
+ static struct vref_perbit_dly vref_dly;
+ struct dqdqs_perbit_dly dq_perbit_dly[DQ_DATA_WIDTH];
+
+ dramc_get_vref_prop(rank, type,
+ &vref_scan_enable, &vref_begin, &vref_end);
+ if (vref_scan_enable && type == RX_WIN_RD_DQC)
+ dramc_rx_vref_enable(chn);
+
+ dramc_dbg("[channel %d] [rank %d] type:%d, vref_enable:%d\n",
+ chn, rank, type, vref_scan_enable);
+
+ if ((type == TX_WIN_DQ_ONLY) || (type == TX_WIN_DQ_DQM)) {
+ for (size_t i = 0; i < 2; i++) {
+ write32(&ch[chn].phy.shu[0].rk[rank].b[i].dq[0], 0);
+ clrbits_le32(&ch[chn].phy.shu[0].rk[rank].b[i].dq[1],
+ 0xf);
+ }
+ setbits_le32(&ch[chn].phy.misc_ctrl1,
+ 0x1 << MISC_CTRL1_R_DMAR_FINE_TUNE_DQ_SW_SHIFT);
+ setbits_le32(&ch[chn].ao.dqsoscr,
+ 0x1 << DQSOSCR_AR_COARSE_TUNE_DQ_SW_SHIFT);
+ vref_step = 2;
+ }
+
+ if (type == RX_WIN_RD_DQC) {
+ dramc_rd_dqc_init(chn, rank);
+ } else {
+ dummy_rd_backup = read32(&ch[chn].ao.dummy_rd);
+ dramc_engine2_init(chn, rank, 0x400, false);
+ }
+
+ vref_dly.max_win = 0;
+ vref_dly.min_win = 0xffff;
+ for (vref = vref_begin; vref < vref_end; vref += vref_step) {
+ vref_dly.vref = vref;
+ finish_bit = 0;
+ for (size_t i = 0; i < DQ_DATA_WIDTH; i++) {
+ dq_perbit_dly[i].dqdly.first = -1;
+ dq_perbit_dly[i].dqdly.last = -2;
+ dq_perbit_dly[i].dqsdly.first = -1;
+ dq_perbit_dly[i].dqsdly.last = -2;
+ dq_perbit_dly[i].dqdly.best_first = -1;
+ dq_perbit_dly[i].dqdly.best_last = -2;
+ dq_perbit_dly[i].dqsdly.best_first = -1;
+ dq_perbit_dly[i].dqsdly.best_last = -2;
+ }
+
+ if (vref_scan_enable)
+ dramc_set_vref(chn, rank, type, vref_dly.vref);
+
+ if ((type == RX_WIN_RD_DQC) || (type == RX_WIN_TEST_ENG)) {
+ set_rx_dly_factor(chn, rank, RX_DQM, FIRST_DQ_DELAY);
+ set_rx_dly_factor(chn, rank, RX_DQ, FIRST_DQ_DELAY);
+ }
+
+ dramc_get_dly_range(chn, rank, type, dq_precal_result,
+ &dly_begin, &dly_end, params);
+ for (dly = dly_begin; dly < dly_end; dly += dly_step) {
+ dramc_set_dqdqs_dly(chn, rank, type, dly);
+ err_value = dram_k_perbit(chn, type);
+ finish_bit = 0;
+ if (!vref_scan_enable)
+ dramc_dbg("%d ", dly);
+
+ for (size_t bit = 0; bit < DQ_DATA_WIDTH; bit++) {
+ bool flag;
+ bool fail = (err_value & ((u32) 1 << bit)) != 0;
+
+ if (dly < 0) {
+ dly_pass = -dly;
+ last_step = -FIRST_DQS_DELAY;
+ flag = false;
+ } else {
+ dly_pass = dly;
+ last_step = dly_end;
+ flag = true;
+ }
+
+ /* pass window bigger than 7,
+ consider as real pass window */
+ if (dramc_check_dqdqs_win(&(dq_perbit_dly[bit]),
+ dly_pass, last_step,
+ fail, flag) > 7)
+ finish_bit |= (1 << bit);
+
+ if (vref_scan_enable)
+ continue;
+ dramc_dbg("%s", !fail ? "o" : "x");
+ }
+
+ if (!vref_scan_enable)
+ dramc_dbg(" [MSB]\n");
+ if (finish_bit == ((1 << DQ_DATA_WIDTH) - 1)) {
+ dramc_dbg("all bits window found, break!\n");
+ break;
+ }
+ }
+
+ for (size_t bit = 0; bit < DQ_DATA_WIDTH; bit++)
+ dramc_dbg("Dq[%zd] win(%d ~ %d)\n", bit,
+ dq_perbit_dly[bit].dqdly.best_first,
+ dq_perbit_dly[bit].dqdly.best_last);
+
+ if (dramk_calc_best_vref(type, vref, &vref_dly, dq_perbit_dly))
+ break;
+
+ if (finish_bit == ((1 << DQ_DATA_WIDTH) - 1)) {
+ dramc_dbg("all bits window found, break!\n");
+ break;
+ }
+ }
+
+ if (type == RX_WIN_RD_DQC) {
+ dramc_rd_dqc_end(chn);
+ } else {
+ dramc_engine2_end(chn);
+ write32(&ch[chn].ao.dummy_rd, dummy_rd_backup);
+ }
+
+ if (vref_scan_enable)
+ dramc_set_vref(chn, rank, type, vref_dly.vref);
+
+ if ((type == RX_WIN_RD_DQC) || (type == RX_WIN_TEST_ENG))
+ dramc_set_rx_best_dly(chn, rank, vref_dly.perbit_dly);
+ else
+ dramc_set_tx_best_dly(chn, rank, vref_dly.perbit_dly,
+ dq_precal_result, params);
+ return 0;
+}
+
static void dramc_rx_dqs_gating_post_process(u8 chn)
{
- u8 dqs, rank_rx_dvs, dqsinctl;
+ u8 rank_rx_dvs, dqsinctl;
u32 read_dqsinctl, rankinctl_root, xrtr2r, reg_tx_dly_dqsgated_min = 3;
u8 txdly_cal_min = 0xff, txdly_cal_max = 0, tx_dly_dqs_gated = 0;
u32 best_coarse_tune2t[RANK_MAX][DQS_NUMBER];
@@ -851,7 +1641,7 @@ static void dramc_rx_dqs_gating_post_process(u8 chn)
for (size_t rank = 0; rank < RANK_MAX; rank++) {
u32 dqsg0 = read32(&ch[chn].ao.shu[0].rk[rank].selph_dqsg0);
- for (dqs = 0; dqs < DQS_NUMBER; dqs++) {
+ for (size_t dqs = 0; dqs < DQS_NUMBER; dqs++) {
best_coarse_tune2t[rank][dqs] =
(dqsg0 >> (dqs * 8)) &
SHURK_SELPH_DQSG0_TX_DLY_DQS0_GATED_MASK;
@@ -859,7 +1649,7 @@ static void dramc_rx_dqs_gating_post_process(u8 chn)
((dqsg0 >> (dqs * 8)) &
SHURK_SELPH_DQSG0_TX_DLY_DQS0_GATED_P1_MASK) >>
SHURK_SELPH_DQSG0_TX_DLY_DQS0_GATED_P1_SHIFT;
- dramc_dbg("Rank%d best DQS%d dly(2T,(P1)2T)=(%d, %d)\n",
+ dramc_dbg("Rank%zd best DQS%zd dly(2T,(P1)2T)=(%d, %d)\n",
rank, dqs, best_coarse_tune2t[rank][dqs],
best_coarse_tune2t_p1[rank][dqs]);
@@ -880,14 +1670,14 @@ static void dramc_rx_dqs_gating_post_process(u8 chn)
txdly_cal_max += dqsinctl;
for (size_t rank = 0; rank < RANK_MAX; rank++) {
- dramc_dbg("Rank: %d\n", rank);
- for (dqs = 0; dqs < DQS_NUMBER; dqs++) {
+ dramc_dbg("Rank: %zd\n", rank);
+ for (size_t dqs = 0; dqs < DQS_NUMBER; dqs++) {
best_coarse_tune2t[rank][dqs] += dqsinctl;
best_coarse_tune2t_p1[rank][dqs] += dqsinctl;
- dramc_dbg("Best DQS%d dly(2T) = (%d)\n",
+ dramc_dbg("Best DQS%zd dly(2T) = (%d)\n",
dqs, best_coarse_tune2t[rank][dqs]);
- dramc_dbg("Best DQS%d P1 dly(2T) = (%d)\n",
+ dramc_dbg("Best DQS%zd P1 dly(2T) = (%d)\n",
dqs,
best_coarse_tune2t_p1[rank][dqs]);
}
@@ -933,11 +1723,15 @@ void dramc_calibrate_all_channels(const struct sdram_params *pams)
for (u8 chn = 0; chn < CHANNEL_MAX; chn++) {
for (u8 rk = RANK_0; rk < RANK_MAX; rk++) {
dramc_show("Start K ch:%d, rank:%d\n", chn, rk);
- auto_refresh_switch(chn, 0);
+ auto_refresh_switch(chn, false);
cmd_bus_training(chn, rk, pams);
dramc_write_leveling(chn, rk, pams->wr_level);
- auto_refresh_switch(chn, 1);
+ auto_refresh_switch(chn, true);
dramc_rx_dqs_gating_cal(chn, rk);
+ dramc_window_perbit_cal(chn, rk, RX_WIN_RD_DQC, pams);
+ dramc_window_perbit_cal(chn, rk, TX_WIN_DQ_DQM, pams);
+ dramc_window_perbit_cal(chn, rk, TX_WIN_DQ_ONLY, pams);
+ dramc_window_perbit_cal(chn, rk, RX_WIN_TEST_ENG, pams);
}
dramc_rx_dqs_gating_post_process(chn);
diff --git a/src/soc/mediatek/mt8183/emi.c b/src/soc/mediatek/mt8183/emi.c
index a4ac4ab4d9..c1d9250f28 100644
--- a/src/soc/mediatek/mt8183/emi.c
+++ b/src/soc/mediatek/mt8183/emi.c
@@ -131,7 +131,7 @@ static void set_rank_info_to_conf(const struct sdram_params *params)
static void set_MRR_pinmux_mapping(void)
{
- for (u8 chn = 0; chn < CHANNEL_MAX; chn++) {
+ for (size_t chn = 0; chn < CHANNEL_MAX; chn++) {
const u8 *map = phy_mapping[chn];
write32(&ch[chn].ao.mrr_bit_mux1,
(map[0] << 0) | (map[1] << 8) |
diff --git a/src/soc/mediatek/mt8183/include/soc/dramc_pi_api.h b/src/soc/mediatek/mt8183/include/soc/dramc_pi_api.h
index 3fb8c25d47..7b3215840d 100644
--- a/src/soc/mediatek/mt8183/include/soc/dramc_pi_api.h
+++ b/src/soc/mediatek/mt8183/include/soc/dramc_pi_api.h
@@ -27,17 +27,11 @@
#define dramc_dbg(_x_...)
#endif
-#define ENABLE 1
-#define DISABLE 0
-
#define DATLAT_TAP_NUMBER 32
-#define MAX_CMP_CPT_WAIT_LOOP 10000
-#define TIME_OUT_CNT 100
-
#define DRAMC_BROADCAST_ON 0x1f
#define DRAMC_BROADCAST_OFF 0x0
-#define MAX_BACKUP_REG_CNT 32
+#define TX_DQ_COARSE_TUNE_TO_FINE_TUNE_TAP 64
#define IMP_LP4X_TERM_VREF_SEL 0x1b
#define IMP_DRVP_LP4X_UNTERM_VREF_SEL 0x1a
@@ -50,11 +44,6 @@ enum dram_te_op {
};
enum {
- DBI_OFF = 0,
- DBI_ON
-};
-
-enum {
FSP_0 = 0,
FSP_1,
FSP_MAX
@@ -75,17 +64,6 @@ enum {
};
enum {
- GATING_OFF = 0,
- GATING_ON = 1
-};
-
-enum {
- CKE_FIXOFF = 0,
- CKE_FIXON,
- CKE_DYNAMIC
-};
-
-enum {
GATING_PATTERN_NUM = 0x23,
GATING_GOLDEND_DQSCNT = 0x4646
};
diff --git a/src/soc/mediatek/mt8183/include/soc/dramc_register.h b/src/soc/mediatek/mt8183/include/soc/dramc_register.h
index 5d79b4949b..2487504a28 100644
--- a/src/soc/mediatek/mt8183/include/soc/dramc_register.h
+++ b/src/soc/mediatek/mt8183/include/soc/dramc_register.h
@@ -1145,7 +1145,7 @@ enum {
};
enum {
- SHU1_BX_DQ7_R_DMDQMDBI_SHU_SHIFT = 7,
+ SHU1_BX_DQ7_R_DMDQMDBI_SHIFT = 7,
SHU1_BX_DQ7_R_DMRANKRXDVS_SHIFT = 0,
SHU1_BX_DQ7_R_DMRANKRXDVS_MASK = 0x0000000f,
};
diff --git a/src/soc/mediatek/mt8183/include/soc/emi.h b/src/soc/mediatek/mt8183/include/soc/emi.h
index 22331ae42b..81e3d91daa 100644
--- a/src/soc/mediatek/mt8183/include/soc/emi.h
+++ b/src/soc/mediatek/mt8183/include/soc/emi.h
@@ -33,6 +33,8 @@ struct sdram_params {
u16 delay_cell_unit;
};
+extern const u8 phy_mapping[CHANNEL_MAX][16];
+
int complex_mem_test(u8 *start, unsigned int len);
size_t sdram_size(void);
const struct sdram_params *get_sdram_config(void);
diff --git a/src/soc/mediatek/mt8183/include/soc/memlayout.ld b/src/soc/mediatek/mt8183/include/soc/memlayout.ld
index f148eed519..2a6d42de63 100644
--- a/src/soc/mediatek/mt8183/include/soc/memlayout.ld
+++ b/src/soc/mediatek/mt8183/include/soc/memlayout.ld
@@ -39,7 +39,7 @@ SECTIONS
SRAM_END(0x00120000)
SRAM_L2C_START(0x00200000)
- OVERLAP_DECOMPRESSOR_ROMSTAGE(0x000201000, 92K)
+ OVERLAP_DECOMPRESSOR_ROMSTAGE(0x000201000, 110K)
BOOTBLOCK(0x00227000, 89K)
VERSTAGE(0x0023E000, 114K)
SRAM_L2C_END(0x00280000)