[PATCH 0/3] rockchip: Fix RAM training on RK3399 based platforms (Rock Pi 4)

This set fixes several issues found on the Rock Pi 4.
For full context, please see this initial bug report:
"There appear to be a number of issues with the Rockchip rk3399 DDR RAM initialisation sequence in Mainline. Specifically, I'm seeing consistent failures on the Rock Pi 4+ during early boot. A typical failure looks something like this:
U-Boot TPL 2022.07-rc3-00005-g1b04a961c6 (May 25 2022 - 11:09:19) Channel 0: LPDDR4, 50MHz BW=32 Col=10 Bk=8 CS0 Row=16/15 CS=1 Die BW=16 Size=2048MB Channel 1: col error Cap error! 256B stride lpddr4_set_rate: change freq to 400000000 mhz 0, 1 lpddr4_set_rate: change freq to 800000000 mhz 1, 0 Trying to boot from BOOTROM Returning to boot ROM...
Even when the system boots to a terminal, which happens very infrequently, the LPDDR4 RAM chip at Channel 1 can have conflicting discovery information printed during TPL. The following 3 lines were printed during successive reboots using the same SD card with no changes:
# Boot 1: BW=32 Col=9 Bk=4 CS0 Row=16/15 CS=1 Die BW=16 Size=384MB
# Boot 2: BW=32 Col=10 Bk=4 CS0 Row=16/15 CS=1 Die BW=16 Size=768MB
# Boot 3: BW=32 Col=10 Bk=4 CS0 Row=15 CS=1 Die BW=16 Size=512MB
The story changes when I build the idbloader.img image with Rockchip's TBL (?) binary blob [0]. With that built in, presumably in place of the upstream TBL, both RAM chips are successfully enumerated and boot succeeds with 100% success rate:
tools/mkimage -n rk3399 -T rksd -d \ rk3399_ddr_933MHz_v1.25.bin:spl/u-boot-spl.bin idbloader.img
Another thing that is very different between the 2 is the initial frequency the LPDDR4 chips are clocked at. Using the upstream TBL version, the default is 50Mhz, which seems very low. If using the Rockchip supplied binary blob file, this is increased to a respectable 416MHz:
# Mainline Channel 0: LPDDR4, 50MHz
# Rockchip TBL blob Channel 0: LPDDR4,416MHz
One thing I did try was to load in the 400Mhz configuration settings from drivers/ram/rockchip/sdram-rk3399-lpddr4-400.inc as the default initial values, instead of the 50MHz default taken from arch/arm/dts/rk3399-sdram-lpddr4-100.dtsi, but this failed in a number of ways:
Setting clock: Freq: 400MHz (400000000) Calling SDRAM init: 2 Channels Starting SDRAM initialization... mr5:0 mr12:0 mr14:0 Training failed for rank 2, ch 0 (ret: -22) mr5:0 mr12:0 mr14:0 Training failed for rank 1, ch 0 (ret: -22) mr5:0 mr12:0 mr14:0 Training failed for rank 2, ch 1 (ret: -22) mr5:0 mr12:0 mr14:0 Training failed for rank 1, ch 1 (ret: -22) Rank for Channel 1 is 0x0 Rank for Channel 0 is 0x0 Rank for Channel 1 is 0x0 sdram_init: LPDDR4 - 400MHz failed! rk3399_dmc_init DRAM init failed -22
So my question is; does Rockchip, or anyone else for that matter, have any plans on updating Mainline U-Boot with the upgraded/working LPDDR4 initialisation sequence?"
Lee Jones (3): ram: rk3399: Fix .set_rate_index() error handling ram: rk3399: Fix faulty frequency change reports ram: rk3399: Conduct memory training at 400MHz
drivers/ram/rockchip/sdram_rk3399.c | 38 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 15 deletions(-)
-- 2.37.1.559.g78731f0fdb-goog

Functions pointed to by this op pointer can return non-zero values indicating an error. Ensure any error value is propagated back up the call-chain.
Signed-off-by: Lee Jones lee@kernel.org Tested-by: Xavier Drudis Ferran xdrudis@tinet.cat Reviewed-by: Kever Yang kever.yang@rock-chips.com --- drivers/ram/rockchip/sdram_rk3399.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index c0a06dcaed..0af0fa9e7b 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -3005,7 +3005,9 @@ static int sdram_init(struct dram_info *dram, params->base.stride = calculate_stride(params); dram_all_config(dram, params);
- dram->ops->set_rate_index(dram, params); + ret = dram->ops->set_rate_index(dram, params); + if (ret) + return ret;
debug("Finish SDRAM initialization...\n"); return 0;

Frequency changes to 400MHz are presently reported as:
lpddr4_set_rate_0: change freq to 400000000 mhz 0, 1
This is obviously wrong by 6 orders of magnitude.
Ensure frequency changes are reported accurately.
Signed-off-by: Lee Jones lee@kernel.org Tested-by: Xavier Drudis Ferran xdrudis@tinet.cat Reviewed-by: Kever Yang kever.yang@rock-chips.com --- drivers/ram/rockchip/sdram_rk3399.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 0af0fa9e7b..34d6c93f95 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -2552,8 +2552,8 @@ static int lpddr4_set_rate(struct dram_info *dram, dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq);
if (IS_ENABLED(CONFIG_RAM_ROCKCHIP_DEBUG)) - printf("%s: change freq to %d mhz %d, %d\n", __func__, - dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq, + printf("%s: change freq to %dMHz %d, %d\n", __func__, + dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq / MHz, ctl_fn, phy_fn); }

Currently the default initialisation frequency is 50MHz. Although this does appear to be suitable for some LPDDR4 RAM chips, training at this low frequency has been seen to cause Column errors, leading to Capacity check errors on others.
Here we force RAM initialisation to happen at 400MHz before ramping up to the final value running value of 800MHz after everything has been successfully configured.
Link: https://lore.kernel.org/u-boot/Yo4v3jUeHXTovjOH@google.com/ Suggested-by: YouMin Chen cym@rock-chips.com Signed-off-by: Lee Jones lee@kernel.org Tested-by: Xavier Drudis Ferran xdrudis@tinet.cat Reviewed-by: Kever Yang kever.yang@rock-chips.com --- drivers/ram/rockchip/sdram_rk3399.c | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 34d6c93f95..b05c5925d5 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -85,7 +85,7 @@ struct sdram_rk3399_ops { int (*data_training_first)(struct dram_info *dram, u32 channel, u8 rank, struct rk3399_sdram_params *sdram); int (*set_rate_index)(struct dram_info *dram, - struct rk3399_sdram_params *params); + struct rk3399_sdram_params *params, u32 ctl_fn); void (*modify_param)(const struct chan_info *chan, struct rk3399_sdram_params *params); struct rk3399_sdram_params * @@ -1644,7 +1644,8 @@ static int data_training_first(struct dram_info *dram, u32 channel, u8 rank, }
static int switch_to_phy_index1(struct dram_info *dram, - struct rk3399_sdram_params *params) + struct rk3399_sdram_params *params, + u32 unused) { u32 channel; u32 *denali_phy; @@ -2539,26 +2540,25 @@ static int lpddr4_set_ctl(struct dram_info *dram, }
static int lpddr4_set_rate(struct dram_info *dram, - struct rk3399_sdram_params *params) + struct rk3399_sdram_params *params, + u32 ctl_fn) { - u32 ctl_fn; u32 phy_fn;
- for (ctl_fn = 0; ctl_fn < 2; ctl_fn++) { - phy_fn = lpddr4_get_phy_fn(params, ctl_fn); + phy_fn = lpddr4_get_phy_fn(params, ctl_fn);
- lpddr4_set_phy(dram, params, phy_fn, &dfs_cfgs_lpddr4[ctl_fn]); - lpddr4_set_ctl(dram, params, ctl_fn, - dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq); + lpddr4_set_phy(dram, params, phy_fn, &dfs_cfgs_lpddr4[ctl_fn]); + lpddr4_set_ctl(dram, params, ctl_fn, + dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq);
- if (IS_ENABLED(CONFIG_RAM_ROCKCHIP_DEBUG)) - printf("%s: change freq to %dMHz %d, %d\n", __func__, - dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq / MHz, - ctl_fn, phy_fn); - } + if (IS_ENABLED(CONFIG_RAM_ROCKCHIP_DEBUG)) + printf("%s: change freq to %dMHz %d, %d\n", __func__, + dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq / MHz, + ctl_fn, phy_fn);
return 0; } + #endif /* CONFIG_RAM_RK3399_LPDDR4 */
/* CS0,n=1 @@ -2955,6 +2955,12 @@ static int sdram_init(struct dram_info *dram, params->ch[ch].cap_info.rank = rank; }
+#if defined(CONFIG_RAM_RK3399_LPDDR4) + /* LPDDR4 needs to be trained at 400MHz */ + lpddr4_set_rate(dram, params, 0); + params->base.ddr_freq = dfs_cfgs_lpddr4[0].base.ddr_freq / MHz; +#endif + params->base.num_channels = 0; for (channel = 0; channel < 2; channel++) { const struct chan_info *chan = &dram->chan[channel]; @@ -3005,7 +3011,7 @@ static int sdram_init(struct dram_info *dram, params->base.stride = calculate_stride(params); dram_all_config(dram, params);
- ret = dram->ops->set_rate_index(dram, params); + ret = dram->ops->set_rate_index(dram, params, 1); if (ret) return ret;

On Thu, Aug 11, 2022 at 08:58:48AM +0100, Lee Jones wrote:
Currently the default initialisation frequency is 50MHz. Although this does appear to be suitable for some LPDDR4 RAM chips, training at this low frequency has been seen to cause Column errors, leading to Capacity check errors on others.
Here we force RAM initialisation to happen at 400MHz before ramping up to the final value running value of 800MHz after everything has been successfully configured.
Link: https://lore.kernel.org/u-boot/Yo4v3jUeHXTovjOH@google.com/ Suggested-by: YouMin Chen cym@rock-chips.com Signed-off-by: Lee Jones lee@kernel.org Tested-by: Xavier Drudis Ferran xdrudis@tinet.cat
Also does not cause any regression on a Pinebook Pro
Tested-by: Michal Suchánek msuchanek@suse.de
Thanks
Michal
Reviewed-by: Kever Yang kever.yang@rock-chips.com
drivers/ram/rockchip/sdram_rk3399.c | 36 +++++++++++++++++------------ 1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/drivers/ram/rockchip/sdram_rk3399.c b/drivers/ram/rockchip/sdram_rk3399.c index 34d6c93f95..b05c5925d5 100644 --- a/drivers/ram/rockchip/sdram_rk3399.c +++ b/drivers/ram/rockchip/sdram_rk3399.c @@ -85,7 +85,7 @@ struct sdram_rk3399_ops { int (*data_training_first)(struct dram_info *dram, u32 channel, u8 rank, struct rk3399_sdram_params *sdram); int (*set_rate_index)(struct dram_info *dram,
struct rk3399_sdram_params *params);
void (*modify_param)(const struct chan_info *chan, struct rk3399_sdram_params *params); struct rk3399_sdram_params *struct rk3399_sdram_params *params, u32 ctl_fn);
@@ -1644,7 +1644,8 @@ static int data_training_first(struct dram_info *dram, u32 channel, u8 rank, }
static int switch_to_phy_index1(struct dram_info *dram,
struct rk3399_sdram_params *params)
struct rk3399_sdram_params *params,
u32 unused)
{ u32 channel; u32 *denali_phy; @@ -2539,26 +2540,25 @@ static int lpddr4_set_ctl(struct dram_info *dram, }
static int lpddr4_set_rate(struct dram_info *dram,
struct rk3399_sdram_params *params)
struct rk3399_sdram_params *params,
u32 ctl_fn)
{
u32 ctl_fn; u32 phy_fn;
for (ctl_fn = 0; ctl_fn < 2; ctl_fn++) {
phy_fn = lpddr4_get_phy_fn(params, ctl_fn);
- phy_fn = lpddr4_get_phy_fn(params, ctl_fn);
lpddr4_set_phy(dram, params, phy_fn, &dfs_cfgs_lpddr4[ctl_fn]);
lpddr4_set_ctl(dram, params, ctl_fn,
dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq);
- lpddr4_set_phy(dram, params, phy_fn, &dfs_cfgs_lpddr4[ctl_fn]);
- lpddr4_set_ctl(dram, params, ctl_fn,
dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq);
if (IS_ENABLED(CONFIG_RAM_ROCKCHIP_DEBUG))
printf("%s: change freq to %dMHz %d, %d\n", __func__,
dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq / MHz,
ctl_fn, phy_fn);
- }
if (IS_ENABLED(CONFIG_RAM_ROCKCHIP_DEBUG))
printf("%s: change freq to %dMHz %d, %d\n", __func__,
dfs_cfgs_lpddr4[ctl_fn].base.ddr_freq / MHz,
ctl_fn, phy_fn);
return 0;
}
#endif /* CONFIG_RAM_RK3399_LPDDR4 */
/* CS0,n=1 @@ -2955,6 +2955,12 @@ static int sdram_init(struct dram_info *dram, params->ch[ch].cap_info.rank = rank; }
+#if defined(CONFIG_RAM_RK3399_LPDDR4)
- /* LPDDR4 needs to be trained at 400MHz */
- lpddr4_set_rate(dram, params, 0);
- params->base.ddr_freq = dfs_cfgs_lpddr4[0].base.ddr_freq / MHz;
+#endif
- params->base.num_channels = 0; for (channel = 0; channel < 2; channel++) { const struct chan_info *chan = &dram->chan[channel];
@@ -3005,7 +3011,7 @@ static int sdram_init(struct dram_info *dram, params->base.stride = calculate_stride(params); dram_all_config(dram, params);
- ret = dram->ops->set_rate_index(dram, params);
- ret = dram->ops->set_rate_index(dram, params, 1); if (ret) return ret;
-- 2.37.1.559.g78731f0fdb-goog
participants (2)
-
Lee Jones
-
Michal Suchánek