From 7ddb8d999ac716d0acf5757ae27c12c167d69048 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 2 Mar 2025 21:59:11 +0100 Subject: [PATCH 01/11] arm64: renesas: Add TFA BL31 handoff support on Renesas R-Car Gen4 Implement custom U_BOOT_FIT_LOADABLE_HANDLER and armv8_switch_to_el2_prep() handling in case the TFA was loaded. The loadables handler sets up custom handoff structure used by Renesas TFA fork in fixed location in DRAM and indicates the TFA has been loaded. The custom armv8_switch_to_el2_prep() handling tests whether the TFA BL31 was previously loaded and the custom handoff structure was set up, and if so, jumps to TFA BL31 which switches from EL3 to EL2 and then returns to U-Boot just past bl in armv8_switch_to_el2() to finish starting the Linux kernel. The jump to Linux through TFA works in such a way that the custom armv8_switch_to_el2_prep() handler configures the custom handoff structure such that the target jump address of the TFA BL31 on exit is set to the armv8_switch_to_el2() + 4, which is just past the bl, and just before the U-Boot code which implements the Linux kernel boot from either EL. The registers passed through the TFA BL31 are all the registers passed into armv8_switch_to_el2_prep() to assure maximum compatibility with all the boot modes. The armv8_switch_to_el2_prep() handler jumps to the TFA BL31, which does its setup, drops EL from EL3 to EL2 and finally jumps to the armv8_switch_to_el2() + 4 entry point, which then allows U-Boot to boot the Linux kernel the usual way. In order to build suitable kernel fitImage, build TFA first, upstream is currently under review: https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/35799 Or if necessary, downstream repository: remote: https://github.com/renesas-rcar/arm-trusted-firmware.git branch: rcar_gen4_v2.7_v4x ``` $ git clean -fqdx $ MBEDTLS_DIR=/path/to/mbedtls/ make -j$(nproc) bl31 \ PLAT=rcar_gen4 ARCH=aarch64 LSI=V4H SPD=none \ CTX_INCLUDE_AARCH32_REGS=0 MBEDTLS_COMMON_MK=1 \ PTP_NONSECURE_ACCESS=1 LOG_LEVEL=20 DEBUG=0 \ ENABLE_ASSERTIONS=0 E=0 ``` Build Linux kernel Image and device tree from current mainline Linux kernel repository, obtain 'Image' and 'r8a779g0-white-hawk.dtb' . Bundle the files together using provided fit-image.its fitImage description: ``` $ mkimage -f fit-image.its fitImage ``` To start the kernel fiImage generated in previous step, load fitImage to DRAM and use the 'bootm' command to start it: => load 0x58000000 ... fitImage && bootm 0x58000000 Signed-off-by: Marek Vasut --- board/renesas/common/gen4-common.c | 126 +++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/board/renesas/common/gen4-common.c b/board/renesas/common/gen4-common.c index 52a0639073b..f7d129be4c8 100644 --- a/board/renesas/common/gen4-common.c +++ b/board/renesas/common/gen4-common.c @@ -7,11 +7,13 @@ #include #include +#include #include #include #include #include #include +#include #include #define RST_BASE 0xE6160000 /* Domain0 */ @@ -88,3 +90,127 @@ int ft_board_setup(void *blob, struct bd_info *bd) { return 0; } + +/* R-Car Gen4 TFA BL31 handoff structure and handling. */ +struct param_header { + u8 type; + u8 version; + u16 size; + u32 attr; +}; + +struct tfa_image_info { + struct param_header h; + uintptr_t image_base; + u32 image_size; + u32 image_max_size; +}; + +struct aapcs64_params { + u64 arg0; + u64 arg1; + u64 arg2; + u64 arg3; + u64 arg4; + u64 arg5; + u64 arg6; + u64 arg7; +}; + +struct entry_point_info { + struct param_header h; + uintptr_t pc; + u32 spsr; + struct aapcs64_params args; +}; + +struct bl2_to_bl31_params_mem { + struct tfa_image_info bl32_image_info; + struct tfa_image_info bl33_image_info; + struct entry_point_info bl33_ep_info; + struct entry_point_info bl32_ep_info; +}; + +/* Default jump address, return to U-Boot */ +#define BL33_BASE 0x44100000 +/* Custom parameters address passed to TFA by ICUMXA loader */ +#define PARAMS_BASE 0x46422200 + +/* Usually such a structure is produced by ICUMXA and passed in at 0x46422200 */ +static const struct bl2_to_bl31_params_mem blinfo_template = { + .bl33_ep_info.h.type = 1, /* PARAM_EP */ + .bl33_ep_info.h.version = 2, /* Version 2 */ + .bl33_ep_info.h.size = sizeof(struct entry_point_info), + .bl33_ep_info.h.attr = 0x81, /* Executable | Non-Secure */ + .bl33_ep_info.spsr = 0x2c9, /* Mode=EL2, SP=ELX, Exceptions=OFF */ + .bl33_ep_info.pc = BL33_BASE, + + .bl33_image_info.h.type = 1, /* PARAM_EP */ + .bl33_image_info.h.version = 2, /* Version 2 */ + .bl33_image_info.h.size = sizeof(struct image_info), + .bl33_image_info.h.attr = 0, + .bl33_image_info.image_base = BL33_BASE, +}; + +static bool tfa_bl31_image_loaded; +static ulong tfa_bl31_image_addr; + +static void tfa_bl31_image_process(ulong image, size_t size) +{ + /* Custom parameters address passed to TFA by ICUMXA loader */ + struct bl2_to_bl31_params_mem *blinfo = (struct bl2_to_bl31_params_mem *)PARAMS_BASE; + + /* Not in EL3, do nothing. */ + if (current_el() != 3) + return; + + /* Clear a page and copy template */ + memset((void *)PARAMS_BASE, 0, PAGE_SIZE); + memcpy(blinfo, &blinfo_template, sizeof(*blinfo)); + tfa_bl31_image_addr = image; + tfa_bl31_image_loaded = true; +} + +U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_TFA_BL31, tfa_bl31_image_process); + +void armv8_switch_to_el2_prep(u64 args, u64 mach_nr, u64 fdt_addr, + u64 arg4, u64 entry_point, u64 es_flag) +{ + typedef void __noreturn (*image_entry_noargs_t)(void); + image_entry_noargs_t image_entry = + (image_entry_noargs_t)(void *)tfa_bl31_image_addr; + struct bl2_to_bl31_params_mem *blinfo = + (struct bl2_to_bl31_params_mem *)PARAMS_BASE; + + /* Not in EL3, do nothing. */ + if (current_el() != 3) + return; + + /* + * Destination address in arch/arm/cpu/armv8/transition.S + * right past the first bl in armv8_switch_to_el2() to let + * the rest of U-Boot pre-Linux code run. The code does run + * without stack pointer! + */ + const u64 ep = ((u64)(uintptr_t)&armv8_switch_to_el2) + 4; + + /* If TFA BL31 was not part of the fitImage, do regular boot. */ + if (!tfa_bl31_image_loaded) + return; + + /* + * Set up kernel entry point and parameters: + * x0 is FDT address, x1..x3 must be 0 + */ + blinfo->bl33_ep_info.pc = ep; + blinfo->bl33_ep_info.args.arg0 = args; + blinfo->bl33_ep_info.args.arg1 = mach_nr; + blinfo->bl33_ep_info.args.arg2 = fdt_addr; + blinfo->bl33_ep_info.args.arg3 = arg4; + blinfo->bl33_ep_info.args.arg4 = entry_point; + blinfo->bl33_ep_info.args.arg5 = es_flag; + blinfo->bl33_image_info.image_base = ep; + + /* Jump to TFA BL31 */ + image_entry(); +} From 9292a3047181a62f00c8f97db2b46a146f803bff Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 2 Mar 2025 23:30:02 +0100 Subject: [PATCH 02/11] arm64: renesas: Enable fallback PSCI on Renesas R-Car R8A779G0 V4H White Hawk Enable fallback PSCI provider on Renesas R-Car R8A779G0 V4H White Hawk board. This fallback PSCI provider provides basic PSCI interface which can be used by the Linux kernel, but does not provide support for bringing up additional CPU cores or any other functionality, except for SoC level reset. This fallback PSCI provider is intended as a fallback in case a proper PSCI provider is not started before the Linux kernel is started. Linux kernel on ARMv8a will fail to boot in case a PSCI provider is not available, and this basic fallback PSCI provider assures such a boot failure cannot occur, even if that means the system will boot in degraded mode with only one CPU core available, that is still sufficient to perform recovery. In the common case, a proper PSCI provider should be started as part of the Linux kernel fitImage, as the BL31 loadable, and replace this basic fallback PSCI provider before the Linux kernel is started. Signed-off-by: Marek Vasut --- configs/r8a779g0_whitehawk_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configs/r8a779g0_whitehawk_defconfig b/configs/r8a779g0_whitehawk_defconfig index b49a81dcd49..8dcf8e35ee0 100644 --- a/configs/r8a779g0_whitehawk_defconfig +++ b/configs/r8a779g0_whitehawk_defconfig @@ -3,6 +3,8 @@ CONFIG_ARM=y CONFIG_ARCH_RENESAS=y CONFIG_RCAR_GEN4=y +CONFIG_ARM_SMCCC=y +CONFIG_ARMV8_PSCI=y CONFIG_ENV_SIZE=0x20000 CONFIG_ENV_OFFSET=0xFFFE0000 CONFIG_DEFAULT_DEVICE_TREE="renesas/r8a779g0-white-hawk" From 56490b1e0121112072392c41de419d91f8258dc2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:50:41 +0100 Subject: [PATCH 03/11] ARM: renesas: Drop stale common Makefile description Remove stale Makefile description, this used to be valid for the original Makefile from which the common Makefile was made generic, but is no longer applicable to the common Makefile. Fixes: c7d2d7f90a91 ("ARM: renesas: Simplify board Makefiles") Signed-off-by: Marek Vasut --- board/renesas/common/Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/board/renesas/common/Makefile b/board/renesas/common/Makefile index 347be5cc93c..5e51b691178 100644 --- a/board/renesas/common/Makefile +++ b/board/renesas/common/Makefile @@ -1,6 +1,4 @@ # -# board/renesas/whitehawk/Makefile -# # Copyright (C) 2024 Marek Vasut # # SPDX-License-Identifier: GPL-2.0+ From 92659829aa19024c8e6b880efdb562678b0b743f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:09 +0100 Subject: [PATCH 04/11] arm64: renesas: Drop stale R-Car V4H SPL implementation description The R-Car V4H SPL implementation was originally running on the Cortex-R52 core, but this is no longer the case. Majority of the SPL now runs on the Cortex-A76 core. Drop the stale description. Fixes: ec53fdee5bec ("arm64: renesas: Add Renesas R-Car V4H SPL implementation") Signed-off-by: Marek Vasut --- board/renesas/common/gen4-spl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/board/renesas/common/gen4-spl.c b/board/renesas/common/gen4-spl.c index e46ef0a3075..ebfefab7253 100644 --- a/board/renesas/common/gen4-spl.c +++ b/board/renesas/common/gen4-spl.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * R-Car Gen4 Cortex-R52 SPL + * R-Car Gen4 SPL * * Copyright (C) 2024 Marek Vasut */ From 15111aeb9c7ac7a8cbe6444090098a440d2bbb4f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:38 +0100 Subject: [PATCH 05/11] arm64: dts: renesas: Make OTP available in SPL on R8A779G0 V4H The DBSC5 DRAM controller driver needs access to OTP fuses to discern Renesas R-Car V4H-3, V4H-5 and V4H-7 SoC variants based on OTP fuse programming. Make OTP block DT node available in U-Boot SPL DT so the DBSC5 driver can determine its base address and read out the OTP fuses. Signed-off-by: Marek Vasut --- arch/arm/dts/r8a779g0-u-boot.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm/dts/r8a779g0-u-boot.dtsi b/arch/arm/dts/r8a779g0-u-boot.dtsi index 10051c9dbfe..cc9d99b0f34 100644 --- a/arch/arm/dts/r8a779g0-u-boot.dtsi +++ b/arch/arm/dts/r8a779g0-u-boot.dtsi @@ -147,6 +147,10 @@ bootph-all; }; +&otp { + bootph-all; +}; + &pfc { bootph-all; }; From 6c219e184f9efffed88dbd82dac94c24ebb00347 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:39 +0100 Subject: [PATCH 06/11] ram: renesas: dbsc5: Fix bitrate MD pin parsing Fix copy paste error in MD pin handling for 5500 Mbps and 4800 Mbps case, each should be handled by MD[19,17] == 2 and MD[19,17] == 3 respectively. Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dram.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 210a68f6496..939775d4d16 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -4450,10 +4450,10 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev) } else if (md == 1) { priv->ddr_mbps = 6000; priv->ddr_mbpsdiv = 1; - } else if (md == 1) { + } else if (md == 2) { priv->ddr_mbps = 5500; priv->ddr_mbpsdiv = 1; - } else if (md == 1) { + } else if (md == 3) { priv->ddr_mbps = 4800; priv->ddr_mbpsdiv = 1; } From 9a106f15fac20790c272023845ec10e46281bef2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:40 +0100 Subject: [PATCH 07/11] ram: renesas: dbsc5: Fix JS1 index calculation The JS1 index is calculated correctly, but the limiter cannot be the max() function because the index should be lower than JS1_USABLEC_SPEC_HI and the max() function would unconditionally override the JS1 index to JS1_USABLEC_SPEC_HI. Use clamp() to limit the JS1 index instead. Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 939775d4d16..289d1224673 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -2192,7 +2192,7 @@ static void dbsc5_ddrtbl_calc(struct renesas_dbsc5_dram_priv *priv) if (js1[i].fx3 * 2 * priv->ddr_mbpsdiv >= priv->ddr_mbps * 3) break; - priv->js1_ind = max(i, JS1_USABLEC_SPEC_HI); + priv->js1_ind = clamp(i, 0, JS1_USABLEC_SPEC_HI); priv->RL = js1[priv->js1_ind].RLset1; priv->WL = js1[priv->js1_ind].WLsetA; From da7662f6bb02806690dac6b25a6e582ed21254d0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:41 +0100 Subject: [PATCH 08/11] ram: renesas: dbsc5: Fix DBTR11 calculation Reinstate missing increment by two in DBTR11 calculation based on the original DBSC5 initialization code rev.0.80. The original code did ... ODTLon - (js2[JS2_tODTon_min] - 1) + 1 , which was incorrectly converted into ODTLon - js2[JS2_tODTon_min], but should have been converted to ODTLon - js2[JS2_tODTon_min] + 2. Add the missing +2 . Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 289d1224673..0616658e9f8 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -2635,7 +2635,7 @@ static void dbsc5_dbsc_regset(struct udevice *dev) */ dbsc5_reg_write(regs_dbsc_d + DBSC_DBTR(11), priv->RL + 4 + priv->js2[JS2_tWCK2DQO_HF] - - js1[priv->js1_ind].ODTLon - priv->js2[JS2_tODTon_min]); + js1[priv->js1_ind].ODTLon - priv->js2[JS2_tODTon_min] + 2); /* DBTR12.TWRRD_S : WL + BL/2 + tWTR_S, TWRRD_L : WL + BL + tWTR_L */ dbsc5_reg_write(regs_dbsc_d + DBSC_DBTR(12), From 9ae94d2aedbbd38dc681f89b82f8ce4f2c3ee387 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:42 +0100 Subject: [PATCH 09/11] ram: renesas: dbsc5: Synchronize initialization code to rev.1.10 Update the DRAM initialization code to match DBSC5 initialization code rev.1.10 , which is currently the latest version available. This makes DRAM initialization operational on Renesas R-Car V4H R8A779G0 rev.3.0. Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dram.c | 67 +++++++++++++++----------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 0616658e9f8..1b70faff7f7 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -220,6 +220,7 @@ static const u16 jedec_spec2_tRFC_ab[] = { #define PHY_RDLVL_RDDQS_DQ_TE_DLY_OBS DDR_REGDEF(0x00, 0x09, 0x103F) #define PHY_WDQLVL_STATUS_OBS DDR_REGDEF(0x00, 0x20, 0x1043) #define PHY_DATA_DC_CAL_START DDR_REGDEF(0x18, 0x01, 0x104D) +#define PHY_SLV_DLY_CTRL_GATE_DISABLE DDR_REGDEF(0x10, 0x01, 0x104E) #define PHY_REGULATOR_EN_CNT DDR_REGDEF(0x18, 0x06, 0x1050) #define PHY_VREF_INITIAL_START_POINT DDR_REGDEF(0x00, 0x09, 0x1055) #define PHY_VREF_INITIAL_STOP_POINT DDR_REGDEF(0x10, 0x09, 0x1055) @@ -469,7 +470,7 @@ static const u32 DDR_PHY_SLICE_REGSET_V4H[DDR_PHY_SLICE_REGSET_NUM_V4H] = { 0x00000000, 0x00500050, 0x00500050, 0x00500050, 0x00500050, 0x0D000050, 0x10100004, 0x06102010, 0x61619041, 0x07097000, 0x00644180, 0x00803280, - 0x00808001, 0x13010100, 0x02000016, 0x10001003, + 0x00808001, 0x13010101, 0x02000016, 0x10001003, 0x06093E42, 0x0F063D01, 0x011700C8, 0x04100140, 0x00000100, 0x000001D1, 0x05000068, 0x00030402, 0x01400000, 0x80800300, 0x00160010, 0x76543210, @@ -512,8 +513,8 @@ static const u32 DDR_PHY_ADR_G_REGSET_V4H[DDR_PHY_ADR_G_REGSET_NUM_V4H] = { 0x00040101, 0x00000000, 0x00000000, 0x00000064, 0x00000000, 0x00000000, 0x39421B42, 0x00010124, 0x00520052, 0x00000052, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x07030102, + 0x00010001, 0x00000000, 0x00000000, 0x00010001, + 0x00000000, 0x00000000, 0x00010001, 0x07030102, 0x01030307, 0x00000054, 0x00004096, 0x08200820, 0x08200820, 0x08200820, 0x08200820, 0x00000820, 0x004103B8, 0x0000003F, 0x000C0006, 0x00000000, @@ -1294,7 +1295,7 @@ static const struct dbsc5_table_patch dbsc5_table_patch_slice_mbpsdiv_572 = { }; static const struct dbsc5_table_patch dbsc5_table_patch_adr_g_mbpsdiv_572 = { - PHY_PAD_ACS_RX_PCLK_CLK_SEL, 0x03 + PHY_PAD_ACS_RX_PCLK_CLK_SEL, 0x02 }; static const struct dbsc5_table_patch dbsc5_table_patch_adr_g_mbpsdiv_400[] = { @@ -1713,14 +1714,17 @@ static void dbsc5_clk_wait_dbpdstat1(struct udevice *dev, u32 status) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; - u32 i, ch, reg; + u32 i, ch, chk, reg; for (i = 0; i < 2; i++) { do { reg = status; - r_foreach_vch(dev, ch) + chk = 0; + r_foreach_vch(dev, ch) { reg &= readl(regs_dbsc_d + DBSC_DBPDSTAT1(ch)); - } while (reg != status); + chk |= readl(regs_dbsc_d + DBSC_DBPDSTAT0(ch)); + } + } while (reg != status && !(chk & BIT(0))); } } @@ -3491,13 +3495,10 @@ static void dbsc5_manual_write_dca(struct udevice *dev) { struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); const u32 rank = priv->ch_have_this_cs[1] ? 0x2 : 0x1; - u32 slv_dly_center[DRAM_CH_CNT][CS_CNT][SLICE_CNT]; - u32 slv_dly_center_cyc; - u32 slv_dly_center_dly; + u32 phy_slv_dly[DRAM_CH_CNT][CS_CNT][SLICE_CNT]; + u32 phy_slv_dly_avg[DRAM_CH_CNT][SLICE_CNT]; u32 slv_dly_min[DRAM_CH_CNT][SLICE_CNT]; u32 slv_dly_max[DRAM_CH_CNT][SLICE_CNT]; - u32 slv_dly_min_tmp[DRAM_CH_CNT][CS_CNT][SLICE_CNT]; - u32 slv_dly_max_tmp[DRAM_CH_CNT][CS_CNT][SLICE_CNT]; u32 phy_dcc_code_min[DRAM_CH_CNT][SLICE_CNT]; u32 phy_dcc_code_max[DRAM_CH_CNT][SLICE_CNT]; u32 phy_dcc_code_mid; @@ -3521,18 +3522,9 @@ static void dbsc5_manual_write_dca(struct udevice *dev) dbsc5_ddr_setval_all_ch_all_slice(dev, PHY_PER_CS_TRAINING_INDEX, cs); r_foreach_vch(dev, ch) { for (slice = 0; slice < SLICE_CNT; slice++) { - slv_dly_center[ch][cs][slice] = - dbsc5_ddr_getval_slice(dev, ch, slice, PHY_CLK_WRDQS_SLAVE_DELAY); - slv_dly_center_cyc = slv_dly_center[ch][cs][slice] & 0x180; - slv_dly_center_dly = slv_dly_center[ch][cs][slice] & 0x7F; - slv_dly_min_tmp[ch][cs][slice] = - slv_dly_center_cyc | - (slv_dly_center_dly * ratio_min / ratio_min_div); - slv_dly_max_tmp[ch][cs][slice] = slv_dly_center_cyc; - if ((slv_dly_center_dly * ratio_max) > (0x7F * ratio_max_div)) - slv_dly_max_tmp[ch][cs][slice] |= 0x7F; - else - slv_dly_max_tmp[ch][cs][slice] |= slv_dly_center_dly * ratio_max / ratio_max_div; + phy_slv_dly[ch][cs][slice] = + dbsc5_ddr_getval_slice(dev, ch, slice, + PHY_CLK_WRDQS_SLAVE_DELAY); } } } @@ -3540,22 +3532,22 @@ static void dbsc5_manual_write_dca(struct udevice *dev) r_foreach_vch(dev, ch) { for (slice = 0; slice < SLICE_CNT; slice++) { if (rank == 0x2) { - if (slv_dly_max_tmp[ch][0][slice] < slv_dly_max_tmp[ch][1][slice]) - slv_dly_max[ch][slice] = slv_dly_max_tmp[ch][1][slice]; - else - slv_dly_max[ch][slice] = slv_dly_max_tmp[ch][0][slice]; - - if (slv_dly_min_tmp[ch][0][slice] < slv_dly_min_tmp[ch][1][slice]) - slv_dly_min[ch][slice] = slv_dly_min_tmp[ch][0][slice]; - else - slv_dly_min[ch][slice] = slv_dly_min_tmp[ch][1][slice]; + /* Calculate average between ranks */ + phy_slv_dly_avg[ch][slice] = (phy_slv_dly[ch][0][slice] + + phy_slv_dly[ch][1][slice]) / 2; } else { - slv_dly_max[ch][slice] = slv_dly_max_tmp[ch][0][slice]; - slv_dly_min[ch][slice] = slv_dly_min_tmp[ch][0][slice]; + phy_slv_dly_avg[ch][slice] = phy_slv_dly[ch][0][slice]; } + /* Determine the search range */ + slv_dly_min[ch][slice] = (phy_slv_dly_avg[ch][slice] & 0x07F) * ratio_min / ratio_min_div; + slv_dly_max[ch][slice] = (phy_slv_dly_avg[ch][slice] & 0x07F) * ratio_max / ratio_max_div; + if (slv_dly_max[ch][slice] > 0x7F) + slv_dly_max[ch][slice] = 0x7F; } } + dbsc5_ddr_setval_all_ch_all_slice(dev, PHY_SLV_DLY_CTRL_GATE_DISABLE, 0x1); + for (i = 0; i <= 0x7F; i++) { r_foreach_vch(dev, ch) { for (slice = 0; slice < SLICE_CNT; slice++) { @@ -3621,13 +3613,16 @@ static void dbsc5_manual_write_dca(struct udevice *dev) for (slice = 0; slice < SLICE_CNT; slice++) { dbsc5_ddr_setval_slice(dev, ch, slice, PHY_CLK_WRDQS_SLAVE_DELAY, - slv_dly_center[ch][cs][slice]); + phy_slv_dly[ch][cs][slice]); dbsc5_ddr_setval_slice(dev, ch, slice, SC_PHY_WCK_CALC, 0x1); dbsc5_ddr_setval(dev, ch, SC_PHY_MANUAL_UPDATE, 0x1); } } } + + dbsc5_ddr_setval_all_ch_all_slice(dev, PHY_SLV_DLY_CTRL_GATE_DISABLE, 0x0); + dbsc5_ddr_setval_all_ch_all_slice(dev, PHY_PER_CS_TRAINING_MULTICAST_EN, 0x1); r_foreach_vch(dev, ch) { From 74e28113618f362d2f8ee2edd874fd1789efceb7 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:43 +0100 Subject: [PATCH 10/11] ram: renesas: dbsc5: Add V4H-3/V4H-5/V4H-7 OTP based detection Add auto-detection and handling of Renesas R-Car V4H-3 and V4H-5 in addition to V4H-7 SoC variants based on OTP fuse programming. The V4H-3 and V4H-5 variants have reduced DRAM frequency options. Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dbsc5.c | 3 +- drivers/ram/renesas/dbsc5/dbsc5.h | 1 + drivers/ram/renesas/dbsc5/dram.c | 48 ++++++++++++++++++++----------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/drivers/ram/renesas/dbsc5/dbsc5.c b/drivers/ram/renesas/dbsc5/dbsc5.c index d24b7c5c30a..4cbc6aeda43 100644 --- a/drivers/ram/renesas/dbsc5/dbsc5.c +++ b/drivers/ram/renesas/dbsc5/dbsc5.c @@ -59,7 +59,8 @@ int renesas_dbsc5_bind(struct udevice *dev) struct renesas_dbsc5_data r8a779g0_dbsc5_data = { .clock_node = "renesas,r8a779g0-cpg-mssr", - .reset_node = "renesas,r8a779g0-rst" + .reset_node = "renesas,r8a779g0-rst", + .otp_node = "renesas,r8a779g0-otp", }; static const struct udevice_id renesas_dbsc5_ids[] = { diff --git a/drivers/ram/renesas/dbsc5/dbsc5.h b/drivers/ram/renesas/dbsc5/dbsc5.h index c410eb0c5ed..bf22fcb8c11 100644 --- a/drivers/ram/renesas/dbsc5/dbsc5.h +++ b/drivers/ram/renesas/dbsc5/dbsc5.h @@ -23,6 +23,7 @@ struct renesas_dbsc5_data { const char *clock_node; const char *reset_node; + const char *otp_node; }; #endif /* __DRIVERS_RAM_RENESAS_DBSC5_DBSC5_H__ */ diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index 1b70faff7f7..c5c57858aac 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -4364,16 +4364,20 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev) { #define RST_MODEMR0 0x0 #define RST_MODEMR1 0x4 +#define OTP_MONITOR17 0x1144 struct renesas_dbsc5_data *data = (struct renesas_dbsc5_data *)dev_get_driver_data(dev); ofnode cnode = ofnode_by_compatible(ofnode_null(), data->clock_node); ofnode rnode = ofnode_by_compatible(ofnode_null(), data->reset_node); + ofnode onode = ofnode_by_compatible(ofnode_null(), data->otp_node); struct renesas_dbsc5_dram_priv *priv = dev_get_priv(dev); void __iomem *regs_dbsc_a = priv->regs + DBSC5_DBSC_A_OFFSET; void __iomem *regs_dbsc_d = priv->regs + DBSC5_DBSC_D_OFFSET; phys_addr_t rregs = ofnode_get_addr(rnode); const u32 modemr0 = readl(rregs + RST_MODEMR0); const u32 modemr1 = readl(rregs + RST_MODEMR1); - u32 breg, reg, md, sscg; + phys_addr_t oregs = ofnode_get_addr(onode); + const u32 otpmon17 = readl(oregs + OTP_MONITOR17); + u32 breg, reg, md, sscg, product; u32 ch, cs; /* Get board data */ @@ -4428,29 +4432,41 @@ static int renesas_dbsc5_dram_probe(struct udevice *dev) /* Decode DDR operating frequency from MD[37:36,19,17] pins */ md = ((modemr0 & BIT(19)) >> 18) | ((modemr0 & BIT(17)) >> 17); + product = otpmon17 & 0xff; sscg = (modemr1 >> 4) & 0x03; if (sscg == 2) { printf("MD[37:36] setting 0x%x not supported!", sscg); hang(); } - if (md == 0) { - if (sscg == 0) { - priv->ddr_mbps = 6400; - priv->ddr_mbpsdiv = 1; - } else { - priv->ddr_mbps = 19000; - priv->ddr_mbpsdiv = 3; - } - } else if (md == 1) { - priv->ddr_mbps = 6000; - priv->ddr_mbpsdiv = 1; - } else if (md == 2) { - priv->ddr_mbps = 5500; - priv->ddr_mbpsdiv = 1; - } else if (md == 3) { + if (product == 0x2) { /* V4H-3 */ priv->ddr_mbps = 4800; priv->ddr_mbpsdiv = 1; + } else if (product == 0x1) { /* V4H-5 */ + if (md == 3) + priv->ddr_mbps = 4800; + else + priv->ddr_mbps = 5000; + priv->ddr_mbpsdiv = 1; + } else { /* V4H-7 */ + if (md == 0) { + if (sscg == 0) { + priv->ddr_mbps = 6400; + priv->ddr_mbpsdiv = 1; + } else { + priv->ddr_mbps = 19000; + priv->ddr_mbpsdiv = 3; + } + } else if (md == 1) { + priv->ddr_mbps = 6000; + priv->ddr_mbpsdiv = 1; + } else if (md == 2) { + priv->ddr_mbps = 5500; + priv->ddr_mbpsdiv = 1; + } else if (md == 3) { + priv->ddr_mbps = 4800; + priv->ddr_mbpsdiv = 1; + } } priv->ddr_mul = CLK_DIV(priv->ddr_mbps, priv->ddr_mbpsdiv * 2, From 885fd621a309a2c3f8e8d41bdc8ff893221dc478 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 16 Mar 2025 14:51:44 +0100 Subject: [PATCH 11/11] ram: renesas: dbsc5: Make struct renesas_dbsc5_board_config public Make struct renesas_dbsc5_board_config {} definition public via include/dbsc5.h, so this structure can be defined in board files and passed into the DBSC5 DRAM driver by overriding weak function dbsc5_get_board_data() on board level. Signed-off-by: Marek Vasut --- drivers/ram/renesas/dbsc5/dram.c | 48 +-------------------------- include/dbsc5.h | 56 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 47 deletions(-) create mode 100644 include/dbsc5.h diff --git a/drivers/ram/renesas/dbsc5/dram.c b/drivers/ram/renesas/dbsc5/dram.c index c5c57858aac..6f78afb0ab5 100644 --- a/drivers/ram/renesas/dbsc5/dram.c +++ b/drivers/ram/renesas/dbsc5/dram.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -12,13 +13,6 @@ #include #include "dbsc5.h" -/* The number of channels V4H has */ -#define DRAM_CH_CNT 4 -/* The number of slices V4H has */ -#define SLICE_CNT 2 -/* The number of chip select V4H has */ -#define CS_CNT 2 - /* Number of array elements in Data Slice */ #define DDR_PHY_SLICE_REGSET_SIZE_V4H 0x100 /* Number of array elements in Data Slice */ @@ -1375,46 +1369,6 @@ static const u32 PI_DARRAY3_1_CSx_Fx[CS_CNT][3] = { #define CLK_DIV(a, diva, b, divb) (((a) * (divb)) / ((b) * (diva))) -struct renesas_dbsc5_board_config { - /* Channels in use */ - u8 bdcfg_phyvalid; - /* Read vref (SoC) training range */ - u32 bdcfg_vref_r; - /* Write vref (MR14, MR15) training range */ - u16 bdcfg_vref_w; - /* CA vref (MR12) training range */ - u16 bdcfg_vref_ca; - /* RFM required check */ - bool bdcfg_rfm_chk; - - /* Board parameter about channels */ - struct { - /* - * 0x00: 4Gb dual channel die / 2Gb single channel die - * 0x01: 6Gb dual channel die / 3Gb single channel die - * 0x02: 8Gb dual channel die / 4Gb single channel die - * 0x03: 12Gb dual channel die / 6Gb single channel die - * 0x04: 16Gb dual channel die / 8Gb single channel die - * 0x05: 24Gb dual channel die / 12Gb single channel die - * 0x06: 32Gb dual channel die / 16Gb single channel die - * 0x07: 24Gb single channel die - * 0x08: 32Gb single channel die - * 0xFF: NO_MEMORY - */ - u8 bdcfg_ddr_density[CS_CNT]; - /* SoC caX([6][5][4][3][2][1][0]) -> MEM caY: */ - u32 bdcfg_ca_swap; - /* SoC dqsX([1][0]) -> MEM dqsY: */ - u8 bdcfg_dqs_swap; - /* SoC dq([7][6][5][4][3][2][1][0]) -> MEM dqY/dm: (8 means DM) */ - u32 bdcfg_dq_swap[SLICE_CNT]; - /* SoC dm -> MEM dqY/dm: (8 means DM) */ - u8 bdcfg_dm_swap[SLICE_CNT]; - /* SoC ckeX([1][0]) -> MEM csY */ - u8 bdcfg_cs_swap; - } ch[4]; -}; - struct renesas_dbsc5_dram_priv { void __iomem *regs; void __iomem *cpg_regs; diff --git a/include/dbsc5.h b/include/dbsc5.h new file mode 100644 index 00000000000..cec937d89e7 --- /dev/null +++ b/include/dbsc5.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024-2025 Renesas Electronics Corp. + */ + +#ifndef __INCLUDE_DBSC5_H__ +#define __INCLUDE_DBSC5_H__ + +/* The number of channels V4H has */ +#define DRAM_CH_CNT 4 +/* The number of slices V4H has */ +#define SLICE_CNT 2 +/* The number of chip select V4H has */ +#define CS_CNT 2 + +struct renesas_dbsc5_board_config { + /* Channels in use */ + u8 bdcfg_phyvalid; + /* Read vref (SoC) training range */ + u32 bdcfg_vref_r; + /* Write vref (MR14, MR15) training range */ + u16 bdcfg_vref_w; + /* CA vref (MR12) training range */ + u16 bdcfg_vref_ca; + /* RFM required check */ + bool bdcfg_rfm_chk; + + /* Board parameter about channels */ + struct { + /* + * 0x00: 4Gb dual channel die / 2Gb single channel die + * 0x01: 6Gb dual channel die / 3Gb single channel die + * 0x02: 8Gb dual channel die / 4Gb single channel die + * 0x03: 12Gb dual channel die / 6Gb single channel die + * 0x04: 16Gb dual channel die / 8Gb single channel die + * 0x05: 24Gb dual channel die / 12Gb single channel die + * 0x06: 32Gb dual channel die / 16Gb single channel die + * 0x07: 24Gb single channel die + * 0x08: 32Gb single channel die + * 0xFF: NO_MEMORY + */ + u8 bdcfg_ddr_density[CS_CNT]; + /* SoC caX([6][5][4][3][2][1][0]) -> MEM caY: */ + u32 bdcfg_ca_swap; + /* SoC dqsX([1][0]) -> MEM dqsY: */ + u8 bdcfg_dqs_swap; + /* SoC dq([7][6][5][4][3][2][1][0]) -> MEM dqY/dm: (8 means DM) */ + u32 bdcfg_dq_swap[SLICE_CNT]; + /* SoC dm -> MEM dqY/dm: (8 means DM) */ + u8 bdcfg_dm_swap[SLICE_CNT]; + /* SoC ckeX([1][0]) -> MEM csY */ + u8 bdcfg_cs_swap; + } ch[4]; +}; + +#endif /* __INCLUDE_DBSC5_H__ */