[PATCH v3 3/3] ddr: socfpga: Implement ECC DRAM scrubbing support for both Gen5/Arria10
Yuslaimi, Alif Zakuan
alif.zakuan.yuslaimi at altera.com
Mon Jun 8 08:07:27 CEST 2026
Hi Tien Fong,
On 5/6/2026 12:56 pm, Chee, Tien Fong wrote:
> Hi Alif,
>
>
> On 12/5/2026 2:49 pm, alif.zakuan.yuslaimi at altera.com wrote:
>> From: Alif Zakuan Yuslaimi <alif.zakuan.yuslaimi at altera.com>
>>
>> Enable ECC scrubbing support for Gen5 by moving sdram_init_ecc_bits()
>> from
>> sdram_arria10.c to a new common file, sdram_soc32.c which is shared by
>> both
>> Arria10 and Gen5 devices. This makes ECC scrubbing support no longer
>> exclusive only to Arria10.
>>
>> New Kconfig is introduced to enable this implementation only on the
>> default
>> Arria10 and CycloneV boards as this will increase the SPL size which
>> will exceed some Gen5 devices' SPL size limit.
>>
>> Signed-off-by: Tien Fong Chee <tien.fong.chee at altera.com>
>> Signed-off-by: Alif Zakuan Yuslaimi <alif.zakuan.yuslaimi at altera.com>
>> ---
>>
>> Changes in v3:
>> - Updated commit message
>> - Removed legacy HW watchdog check in favor of current WDT DM for
>> Gen5/Arria10
>> - Dead code cleanups, indentation fixes
>>
>> Changes in v2:
>> - ECC scrubbing is set as optional via Kconfig and defaulted on only for
>> the reference Arria10/CycloneV boards to avoid SPL overflows on
>> size-limited Gen5 defconfigs.
>>
>> arch/arm/mach-socfpga/Kconfig | 12 +++++
>> arch/arm/mach-socfpga/spl_a10.c | 11 ++--
>> arch/arm/mach-socfpga/spl_gen5.c | 10 +++-
>> drivers/ddr/altera/Makefile | 4 +-
>> drivers/ddr/altera/sdram_arria10.c | 31 ++++-------
>> drivers/ddr/altera/sdram_gen5.c | 39 ++++++++++++--
>> drivers/ddr/altera/sdram_soc32.c | 85 ++++++++++++++++++++++++++++++
>> drivers/ddr/altera/sdram_soc32.h | 15 ++++++
>> 8 files changed, 172 insertions(+), 35 deletions(-)
>> create mode 100644 drivers/ddr/altera/sdram_soc32.c
>> create mode 100644 drivers/ddr/altera/sdram_soc32.h
>>
>> diff --git a/arch/arm/mach-socfpga/Kconfig b/arch/arm/mach-socfpga/
>> Kconfig
>> index b814e034ecd..dd71691b724 100644
>> --- a/arch/arm/mach-socfpga/Kconfig
>> +++ b/arch/arm/mach-socfpga/Kconfig
>> @@ -6,6 +6,13 @@ config ERR_PTR_OFFSET
>> config NR_DRAM_BANKS
>> default 1
>> +config SOCFPGA_ECC_SUPPORT
>> + bool "Enable ECC support for DRAM"
>> + help
>> + Adds CPU-based ECC support for DRAM at boot. This will initialize
>> + all DRAM ECC metadata to zero, preventing false ECC errors and
>> + improving reliability.
>> +
>> config SOCFPGA_DRAM_SIZE_CHECK
>> bool "Enable DRAM size checking for safety"
>> help
>> @@ -105,6 +112,7 @@ config ARCH_SOCFPGA_ARRIA10
>> select ETH_DESIGNWARE_SOCFPGA
>> imply FPGA_SOCFPGA
>> imply SPL_USE_TINY_PRINTF
>> + select SOCFPGA_ECC_SUPPORT
>> config SOCFPGA_ARRIA10_ALWAYS_REPROGRAM
>> bool "Always reprogram Arria 10 FPGA"
>> @@ -117,6 +125,9 @@ config SOCFPGA_ARRIA10_ALWAYS_REPROGRAM
>> config ARCH_SOCFPGA_CYCLONE5
>> bool
>> select ARCH_SOCFPGA_GEN5
>> + select SOCFPGA_ECC_SUPPORT if \
>> + !TARGET_SOCFPGA_TERASIC_SOCKIT && !TARGET_SOCFPGA_EBV_SOCRATES \
>> + && !TARGET_SOCFPGA_SOFTING_VINING_FPGA
>> select SOCFPGA_DRAM_SIZE_CHECK if !TARGET_SOCFPGA_TERASIC_SOCKIT \
>> && !TARGET_SOCFPGA_EBV_SOCRATES && \
>> !TARGET_SOCFPGA_SOFTING_VINING_FPGA
>> @@ -124,6 +135,7 @@ config ARCH_SOCFPGA_CYCLONE5
>> config ARCH_SOCFPGA_GEN5
>> bool
>> select SPL_ALTERA_SDRAM
>> + select SPL_CACHE if SPL && SOCFPGA_ECC_SUPPORT
>> imply FPGA_SOCFPGA
>> imply SPL_SIZE_LIMIT_SUBTRACT_GD
>> imply SPL_SIZE_LIMIT_SUBTRACT_MALLOC
>> diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/
>> spl_a10.c
>> index c20376f7f8e..0d0412d3507 100644
>> --- a/arch/arm/mach-socfpga/spl_a10.c
>> +++ b/arch/arm/mach-socfpga/spl_a10.c
>> @@ -25,6 +25,7 @@
>> #include <asm/sections.h>
>> #include <fdtdec.h>
>> #include <watchdog.h>
>> +#include <wdt.h>
>> #include <asm/arch/pinmux.h>
>> #include <asm/arch/fpga_manager.h>
>> #include <mmc.h>
>> @@ -265,14 +266,8 @@ void board_init_f(ulong dummy)
>> /* Configure the clock based on handoff */
>> cm_basic_init(gd->fdt_blob);
>> -#ifdef CONFIG_HW_WATCHDOG
>> - /* release osc1 watchdog timer 0 from reset */
>> - socfpga_reset_deassert_osc1wd0();
>> -
>> - /* reconfigure and enable the watchdog */
>> - hw_watchdog_init();
>> - schedule();
>> -#endif /* CONFIG_HW_WATCHDOG */
>> + if (CONFIG_IS_ENABLED(WDT))
>> + initr_watchdog();
>> config_dedicated_pins(gd->fdt_blob);
>> schedule();
>> diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/
>> spl_gen5.c
>> index a9825173189..530863b1564 100644
>> --- a/arch/arm/mach-socfpga/spl_gen5.c
>> +++ b/arch/arm/mach-socfpga/spl_gen5.c
>> @@ -22,12 +22,14 @@
>> #include <debug_uart.h>
>> #include <fdtdec.h>
>> #include <watchdog.h>
>> +#include <wdt.h>
>> #include <dm/uclass.h>
>> #include <linux/bitops.h>
>> DECLARE_GLOBAL_DATA_PTR;
>> -#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT) || \
>> + IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> static struct bd_info bdata __attribute__ ((section(".data")));
>> #endif
>> @@ -113,6 +115,9 @@ void board_init_f(ulong dummy)
>> if (cm_basic_init(cm_default_cfg))
>> hang();
>> + if (CONFIG_IS_ENABLED(WDT))
>> + initr_watchdog();
>> +
>> /* Enable bootrom to configure IOs. */
>> sysmgr_config_warmrstcfgio(1);
>> @@ -150,7 +155,8 @@ void board_init_f(ulong dummy)
>> /* enable console uart printing */
>> preloader_console_init();
>> -#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT) || \
>> + IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> gd->bd = &bdata;
>> #endif
>> diff --git a/drivers/ddr/altera/Makefile b/drivers/ddr/altera/Makefile
>> index 8259ab04a7e..ece6a131897 100644
>> --- a/drivers/ddr/altera/Makefile
>> +++ b/drivers/ddr/altera/Makefile
>> @@ -7,8 +7,8 @@
>> # Copyright (C) 2014-2025 Altera Corporation <www.altera.com>
>> ifdef CONFIG_$(PHASE_)ALTERA_SDRAM
>> -obj-$(CONFIG_ARCH_SOCFPGA_GEN5) += sdram_gen5.o sequencer.o
>> -obj-$(CONFIG_ARCH_SOCFPGA_ARRIA10) += sdram_arria10.o
>> +obj-$(CONFIG_ARCH_SOCFPGA_GEN5) += sdram_soc32.o sdram_gen5.o
>> sequencer.o
>> +obj-$(CONFIG_ARCH_SOCFPGA_ARRIA10) += sdram_soc32.o sdram_arria10.o
>> obj-$(CONFIG_ARCH_SOCFPGA_STRATIX10) += sdram_soc64.o sdram_s10.o
>> obj-$(CONFIG_ARCH_SOCFPGA_AGILEX) += sdram_soc64.o sdram_agilex.o
>> obj-$(CONFIG_ARCH_SOCFPGA_N5X) += sdram_soc64.o sdram_n5x.o
>> diff --git a/drivers/ddr/altera/sdram_arria10.c b/drivers/ddr/altera/
>> sdram_arria10.c
>> index c281f711fdf..ac5d3f03f05 100644
>> --- a/drivers/ddr/altera/sdram_arria10.c
>> +++ b/drivers/ddr/altera/sdram_arria10.c
>> @@ -22,6 +22,8 @@
>> #include <linux/bitops.h>
>> #include <linux/delay.h>
>> #include <linux/kernel.h>
>> +#include <linux/sizes.h>
>> +#include "sdram_soc32.h"
>> DECLARE_GLOBAL_DATA_PTR;
>> @@ -193,24 +195,6 @@ static int sdram_is_ecc_enabled(void)
>> ALT_ECC_HMC_OCP_ECCCTL_ECC_EN_SET_MSK);
>> }
>> -/* Initialize SDRAM ECC bits to avoid false DBE */
>> -static void sdram_init_ecc_bits(u32 size)
>> -{
>> - icache_enable();
>> -
>> - memset(0, 0, 0x8000);
>> - gd->arch.tlb_addr = 0x4000;
>> - gd->arch.tlb_size = PGTABLE_SIZE;
>> -
>> - dcache_enable();
>> -
>> - printf("DDRCAL: Scrubbing ECC RAM (%i MiB).\n", size >> 20);
>> - memset((void *)0x8000, 0, size - 0x8000);
>> - flush_dcache_all();
>> - printf("DDRCAL: Scrubbing ECC RAM done.\n");
>> - dcache_disable();
>> -}
>> -
>> /* Function to startup the SDRAM*/
>> static int sdram_startup(void)
>> {
>> @@ -735,8 +719,15 @@ int ddr_calibration_sequence(void)
>> if (of_sdram_firewall_setup(gd->fdt_blob))
>> puts("FW: Error Configuring Firewall\n");
>> - if (sdram_is_ecc_enabled())
>> - sdram_init_ecc_bits(gd->ram_size);
>> + if (sdram_is_ecc_enabled()) {
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> + sdram_init_ecc_bits();
>> +#else
>> + puts("DDR: Enable CONFIG_SOCFPGA_ECC_SUPPORT when SDRAM ");
>> + puts("ECC is enabled.\n");
>> + hang();
>> +#endif
>> + }
>> sdram_size_check();
>> diff --git a/drivers/ddr/altera/sdram_gen5.c b/drivers/ddr/altera/
>> sdram_gen5.c
>> index 1c3c70ea8ae..9c58e4355ae 100644
>> --- a/drivers/ddr/altera/sdram_gen5.c
>> +++ b/drivers/ddr/altera/sdram_gen5.c
>> @@ -2,6 +2,7 @@
>> /*
>> * Copyright Altera Corporation (C) 2014-2015
>> */
>> +#include <cpu_func.h>
>> #include <dm.h>
>> #include <errno.h>
>> #include <div64.h>
>> @@ -19,8 +20,9 @@
>> #include <asm/global_data.h>
>> #include <asm/io.h>
>> #include <dm/device_compat.h>
>> -
>> +#include <linux/sizes.h>
>> #include "sequencer.h"
>> +#include "sdram_soc32.h"
>> #ifdef CONFIG_XPL_BUILD
>> @@ -566,6 +568,19 @@ static unsigned long sdram_calculate_size(struct
>> socfpga_sdr_ctrl *sdr_ctrl)
>> return temp;
>> }
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> +static int sdram_is_ecc_enabled(struct socfpga_sdr_ctrl *sdr_ctrl)
>> +{
>> + return !!(readl(&sdr_ctrl->ctrl_cfg) &
>> + SDR_CTRLGRP_CTRLCFG_ECCEN_MASK);
>> +}
>> +#else
>> +static int sdram_is_ecc_enabled(struct socfpga_sdr_ctrl *sdr_ctrl)
>> +{
>> + return 0;
>> +}
>> +#endif
>> +
>> static int altera_gen5_sdram_of_to_plat(struct udevice *dev)
>> {
>> struct altera_gen5_sdram_plat *plat = dev_get_plat(dev);
>> @@ -608,10 +623,13 @@ static int altera_gen5_sdram_probe(struct
>> udevice *dev)
>> sdram_size = sdram_calculate_size(sdr_ctrl);
>> debug("SDRAM: %ld MiB\n", sdram_size >> 20);
>> -#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> +#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK) || \
>> + IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> /* setup the dram info within bd */
>> dram_init_banksize();
>> +#endif
>> +#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> if (sdram_size != gd->bd->bi_dram[0].size) {
>> printf("DDR: Warning: DRAM size from device tree (%lu MiB)\n",
>> (ulong)(gd->bd->bi_dram[0].size >> 20));
>> @@ -626,8 +644,23 @@ static int altera_gen5_sdram_probe(struct udevice
>> *dev)
>> }
>> #endif
>> + if (sdram_is_ecc_enabled(sdr_ctrl)) {
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> + /* Must set USEECCASDATA to 0 if ECC is enabled */
>> + clrbits_le32(&sdr_ctrl->static_cfg,
>> + SDR_CTRLGRP_STATICCFG_USEECCASDATA_MASK);
>> + sdram_init_ecc_bits();
>> +#else
>> + puts("DDR: Enable CONFIG_SOCFPGA_ECC_SUPPORT when SDRAM ");
>> + puts("ECC is enabled.\n");
>> + puts("DDR: Without scrub, false ECC errors may occur.\n");
>> + hang();
>> +#endif
>> + }
>> +
>> /* Sanity check ensure correct SDRAM size specified */
>> -#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK)
>> +#if IS_ENABLED(CONFIG_SOCFPGA_DRAM_SIZE_CHECK) || \
>> + IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> if (get_ram_size(0, gd->bd->bi_dram[0].size) !=
>> gd->bd->bi_dram[0].size) {
>> #else
>> diff --git a/drivers/ddr/altera/sdram_soc32.c b/drivers/ddr/altera/
>> sdram_soc32.c
>> new file mode 100644
>> index 00000000000..2fc06fddd4c
>> --- /dev/null
>> +++ b/drivers/ddr/altera/sdram_soc32.c
>> @@ -0,0 +1,85 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2025 Altera Corporation <www.altera.com>
>> + */
>> +
>> +#include "sdram_soc32.h"
>> +#include <string.h>
>> +#include <hang.h>
>> +#include <linux/sizes.h>
>> +#include <cpu_func.h>
>> +#include <watchdog.h>
>> +#include <wait_bit.h>
>> +#include <asm/global_data.h>
>> +#include <asm/system.h>
>> +#if !IS_ENABLED(CONFIG_WATCHDOG) && !CONFIG_IS_ENABLED(WDT)
>> +#include <asm/arch/reset_manager.h>
>> +#endif
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +#define PGTABLE_OFF 0x4000
>> +#define PGTABLE_RESERVE (PGTABLE_OFF + PGTABLE_SIZE)
>> +
>> +#if IS_ENABLED(CONFIG_SOCFPGA_ECC_SUPPORT)
>> +static void socfpga_prepare_watchdog_for_long_op(void)
>> +{
>> +#if !IS_ENABLED(CONFIG_WATCHDOG) && !CONFIG_IS_ENABLED(WDT)
>
>
> note in commit that legacy HW_WATCHDOG path was removed in v3
>
>
>> + /*
>> + * No watchdog driver is active in U-Boot. The bootrom or a
>> + * previous boot stage may have left L4WD0 running. Assert and
>> + * deassert its reset to stop it before the long ECC scrub,
>> + * preventing a spurious watchdog reset during DDR init.
>> + */
>
>
> checkpatch BLOCK_COMMENT_STYLE on */
>
>
>> + socfpga_per_reset(SOCFPGA_RESET(L4WD0), 1);
>> + socfpga_per_reset(SOCFPGA_RESET(L4WD0), 0);
>> +#endif
>> +}
>> +
>> +/* Initialize SDRAM ECC bits to avoid false DBE */
>> +void sdram_init_ecc_bits(void)
>> +{
>> + u32 start;
>> + phys_addr_t start_addr;
>> + phys_size_t size, size_init;
>> +
>> + start = get_timer(0);
>> +
>> + start_addr = gd->bd->bi_dram[0].start;
>> + size = gd->bd->bi_dram[0].size;
>> +
>> + printf("DDRCAL: Scrubbing ECC RAM (%lu MiB).\n",
>> + (ulong)(size >> 20));
>> +
>> + if (size <= PGTABLE_RESERVE) {
>> + printf("DDRCAL: Error: DRAM size %#llx smaller than scrub
>> reserve %#x\n",
>> + (unsigned long long)size, PGTABLE_RESERVE);
>> + hang();
>> + }
>> +
>> + memset((void *)start_addr, 0, PGTABLE_RESERVE);
>> + gd->arch.tlb_addr = start_addr + PGTABLE_OFF;
>> + gd->arch.tlb_size = PGTABLE_SIZE;
>> + start_addr += PGTABLE_RESERVE;
>> + size -= PGTABLE_RESERVE;
>> +
>> + dcache_enable();
>> +
>> + socfpga_prepare_watchdog_for_long_op();
>> +
>> + while (size > 0) {
>> + size_init = min_t(phys_size_t, (phys_size_t)SZ_1G, size);
>> + memset((void *)start_addr, 0, size_init);
>> + size -= size_init;
>> + start_addr += size_init;
>> +
>> + /* Service DM watchdog cyclic callbacks */
>> + schedule();
>> + }
>> +
>> + dcache_disable();
>> +
>> + printf("DDRCAL: SDRAM-ECC initialized success with %u ms\n",
>
>
> “initialized success” → “initialized successfully”
>
> Best regards,
>
> Tien Fong
>
Thank you for your input. Further clean up will be done for v4
submission based on your comments
Alif
More information about the U-Boot
mailing list