* Re: [PATCH v4 01/15] misc: fs_loader: Add QSPI RAW offset loading support [not found] ` <20261016044047.21496-2-chen.huei.lok@altera.com> @ 2026-10-21 1:55 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 1:55 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Add support for loading data from a QSPI RAW flash offset through the > generic firmware loader. Consumers include the DDR calibration backup > restore path, and optionally FPGA bitstream loading from QSPI. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > doc/device-tree-bindings/misc/fs_loader.txt | 8 ++ > drivers/misc/fs_loader.c | 99 +++++++++++++++++---- > include/fs_loader.h | 33 ++++++- > 3 files changed, 120 insertions(+), 20 deletions(-) > > diff --git a/doc/device-tree-bindings/misc/fs_loader.txt b/doc/device-tree-bindings/misc/fs_loader.txt > index 542be4b25a0..8ee59db24d5 100644 > --- a/doc/device-tree-bindings/misc/fs_loader.txt > +++ b/doc/device-tree-bindings/misc/fs_loader.txt > @@ -12,6 +12,7 @@ Required properties: > required for ubi and mounting. > - ubivol: which volume of ubi the image loading from, this property is required > for ubi and mounting. > +- sfconfig: should only contain "bus number" and "chip selection". > > Example of storage device and partition search set for mmc, usb, sata and > ubi in device tree source as shown in below: > @@ -46,3 +47,10 @@ ubi in device tree source as shown in below: > mtdpart = "UBI", > ubivol = "ubi0"; > }; > + > + Example for qspi: > + fs_loader4: fs-loader@4 { > + bootph-all; > + compatible = "u-boot,fs-loader"; > + sfconfig = <0 0>; > + }; > diff --git a/drivers/misc/fs_loader.c b/drivers/misc/fs_loader.c > index 6af4c7f15e7..293db94dd29 100644 > --- a/drivers/misc/fs_loader.c > +++ b/drivers/misc/fs_loader.c > @@ -19,6 +19,7 @@ > #include <mapmem.h> > #include <malloc.h> > #include <spl.h> > +#include <spi_flash.h> > > #ifdef CONFIG_CMD_UBIFS > #include <ubi_uboot.h> > @@ -121,16 +122,31 @@ static int _request_firmware_prepare(struct udevice *dev, > const char *name, void *dbuf, > size_t size, u32 offset) > { > - if (!name || name[0] == '\0') > - return -EINVAL; > - > struct firmware *firmwarep = dev_get_priv(dev); > + struct device_plat *plat = dev_get_plat(dev); > + char *endptr; > + u32 fw_offset; > > if (!firmwarep) > return -ENOMEM; > > + if (plat->data_type == DATA_RAW) { > + if (!name) > + return -EINVAL; > + > + fw_offset = simple_strtoul(name, &endptr, 16); > + if (name == endptr || *endptr != '\0') > + return -EINVAL; > + > + firmwarep->offset = fw_offset + offset; > + } else { > + if (!name || name[0] == '\0') > + return -EINVAL; > + > + firmwarep->offset = offset; > + } > + > firmwarep->name = name; > - firmwarep->offset = offset; > firmwarep->data = dbuf; > firmwarep->size = size; > > @@ -146,13 +162,17 @@ static int _request_firmware_prepare(struct udevice *dev, > static int fw_get_filesystem_firmware(struct udevice *dev) > { > loff_t actread = 0; > - char *storage_interface, *dev_part, *ubi_mtdpart, *ubi_volume; > - int ret; > + char *storage_interface = NULL, *dev_part = NULL; > + char *ubi_mtdpart = NULL, *ubi_volume = NULL; > + int ret = 0; > + struct device_plat *plat = dev_get_plat(dev); > > - storage_interface = env_get("storage_interface"); > - dev_part = env_get("fw_dev_part"); > - ubi_mtdpart = env_get("fw_ubi_mtdpart"); > - ubi_volume = env_get("fw_ubi_volume"); > + if (CONFIG_IS_ENABLED(ENV_SUPPORT)) { > + storage_interface = env_get("storage_interface"); > + dev_part = env_get("fw_dev_part"); > + ubi_mtdpart = env_get("fw_ubi_mtdpart"); > + ubi_volume = env_get("fw_ubi_volume"); > + } > > if (storage_interface && dev_part) { > ret = fs_set_blk_dev(storage_interface, dev_part, FS_TYPE_ANY); > @@ -167,7 +187,8 @@ static int fw_get_filesystem_firmware(struct udevice *dev) > else > ret = -ENODEV; > } else { > - ret = select_fs_dev(dev_get_plat(dev)); > + if (plat->data_type == DATA_FS) > + ret = select_fs_dev(dev_get_plat(dev)); > } > > if (ret) > @@ -178,8 +199,20 @@ static int fw_get_filesystem_firmware(struct udevice *dev) > if (!firmwarep) > return -ENOMEM; > > - ret = fs_read(firmwarep->name, (ulong)map_to_sysmem(firmwarep->data), > - firmwarep->offset, firmwarep->size, &actread); > + if (plat->data_type == DATA_FS) > + ret = fs_read(firmwarep->name, > + (ulong)map_to_sysmem(firmwarep->data), > + firmwarep->offset, firmwarep->size, &actread); > + else if (plat->data_type == DATA_RAW) { > + if (IS_ENABLED(CONFIG_SPI_FLASH)) { > + ret = spi_flash_read_dm(plat->flash, firmwarep->offset, > + firmwarep->size, > + (void *)firmwarep->data); > + actread = firmwarep->size; > + } else { > + ret = -ENOSYS; > + } > + } > > if (ret) { > debug("Error: %d Failed to read %s from flash %lld != %zu.\n", > @@ -228,6 +261,7 @@ int request_firmware_into_buf(struct udevice *dev, > static int fs_loader_of_to_plat(struct udevice *dev) > { > u32 phandlepart[2]; > + u32 sfconfig[2]; > > ofnode fs_loader_node = dev_ofnode(dev); > > @@ -247,6 +281,15 @@ static int fs_loader_of_to_plat(struct udevice *dev) > > plat->ubivol = (char *)ofnode_read_string( > fs_loader_node, "ubivol"); > + > + if (!ofnode_read_u32_array(fs_loader_node, "sfconfig", sfconfig, > + 2)) { > + plat->data_type = DATA_RAW; > + plat->sfconfig.bus = sfconfig[0]; > + plat->sfconfig.cs = sfconfig[1]; > + } else { > + plat->data_type = DATA_FS; > + } > } > > return 0; > @@ -254,11 +297,30 @@ static int fs_loader_of_to_plat(struct udevice *dev) > > static int fs_loader_probe(struct udevice *dev) > { > -#if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK) > - int ret; > + int ret = 0; > struct device_plat *plat = dev_get_plat(dev); > > - if (plat->phandlepart.phandle) { > + if (IS_ENABLED(CONFIG_SPI_FLASH) && > + plat->data_type == DATA_RAW && !plat->flash) { > + debug("bus = %d\ncs = %d\n", > + plat->sfconfig.bus, plat->sfconfig.cs); > + > + ret = spi_flash_probe_bus_cs(plat->sfconfig.bus, > + plat->sfconfig.cs, > + &plat->flash); > + if (ret) { > + debug("fs_loader: Failed to initialize SPI flash at "); > + debug("%u:%u (error %d)\n", plat->sfconfig.bus, > + plat->sfconfig.cs, ret); > + return -ENODEV; > + } > + > + if (!plat->flash) > + return -EINVAL; > + } > + > + if (CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(BLK) && > + plat->phandlepart.phandle) { > ofnode node = ofnode_get_by_phandle(plat->phandlepart.phandle); > struct udevice *parent_dev = NULL; > > @@ -269,15 +331,14 @@ static int fs_loader_probe(struct udevice *dev) > ret = blk_get_from_parent(parent_dev, &dev); > if (ret) { > debug("fs_loader: No block device: %d\n", > - ret); > + ret); > > return ret; > } > } > } > -#endif > > - return 0; > + return ret; > }; > > static const struct udevice_id fs_loader_ids[] = { > diff --git a/include/fs_loader.h b/include/fs_loader.h > index 7e16e0f7030..f9c073fee6e 100644 > --- a/include/fs_loader.h > +++ b/include/fs_loader.h > @@ -23,18 +23,49 @@ struct phandle_part { > }; > > /** > - * struct phandle_part - A place for storing all supported storage devices > + * struct sf_config - A place for storing serial flash configuration > + * > + * This holds information about bus and chip-select of a serial flash > + * configuration. > + * > + * @bus: SPI bus number. > + * @cs: SPI chip selection. > + */ > +struct sf_config { > + u32 bus; > + u32 cs; > +}; > + > +/** > + * enum data_flags - Flag to indicate data as RAW or as filesystem > + * > + * DATA_RAW: Data stored as RAW. > + * DATA_FS: DATA stored as filesystem. > + */ > +enum data_flags { > + DATA_RAW, /* Stored in raw */ > + DATA_FS, /* Stored within a file system */ > +}; > + > +/** > + * struct device_plat - A place for storing all supported storage devices > * > * This holds information about all supported storage devices for driver use. > * > * @phandlepart: Attribute data for block device. > * @mtdpart: MTD partition for ubi partition. > * @ubivol: UBI volume-name for ubifsmount. > + * @data_type: Data type (RAW or filesystem). > + * @sfconfig: Serial flash configuration. > + * @flash: SPI flash device used for RAW offset reads. > */ > struct device_plat { > struct phandle_part phandlepart; > char *mtdpart; > char *ubivol; > + enum data_flags data_type; > + struct sf_config sfconfig; > + struct udevice *flash; > }; > > /** Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-3-chen.huei.lok@altera.com>]
* Re: [PATCH v4 02/15] arm: dts: socfpga: n5x: add DDR retention calibration backup nodes [not found] ` <20261016044047.21496-3-chen.huei.lok@altera.com> @ 2026-10-21 2:07 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:07 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > Add the device tree nodes that the N5X DDR retention restore path depends > on. Reintroduce the fs-loader node and the &sdr DDR calibration backup > properties (firmware-loader, intel,ddrcal-qspi-offset and > intel,ddrcal-ddr-offset) that were dropped during the switch to the > upstream Linux DT config. These describe where the backup calibration > data is stored and the firmware-loader instance used to read it back in > SPL. > > Document the intel,sdr-ctl-n5x retention properties in > doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt, including the QSPI > calibration-restore trust model (System Manager retention bit plus flash > integrity checks) and fail-closed behaviour, and add an F: entry under > ARM ALTERA SOCFPGA in MAINTAINERS for that binding path. > > Use bootph-all in place of the legacy u-boot,dm-pre-reloc so the nodes > are bound in the pre-relocation SPL phase on the migrated device tree. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > MAINTAINERS | 1 + > arch/arm/dts/socfpga_n5x_socdk-u-boot.dtsi | 13 ++++++ > .../ram/intel,sdr-ctl-n5x.txt | 44 +++++++++++++++++++ > 3 files changed, 58 insertions(+) > create mode 100644 doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt > > diff --git a/MAINTAINERS b/MAINTAINERS > index 420a3bbf730..3f83a3b11a1 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -181,6 +181,7 @@ M: Simon Goldschmidt <simon.k.r.goldschmidt@gmail.com> > M: Tien Fong Chee <tien.fong.chee@altera.com> > S: Maintained > T: git https://git.u-boot-project.org/u-boot/custodians/u-boot-socfpga.git > +F: doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt > N: altera > N: agilex > N: socfpga > diff --git a/arch/arm/dts/socfpga_n5x_socdk-u-boot.dtsi b/arch/arm/dts/socfpga_n5x_socdk-u-boot.dtsi > index 3de75f36cbb..ea95d04d37d 100644 > --- a/arch/arm/dts/socfpga_n5x_socdk-u-boot.dtsi > +++ b/arch/arm/dts/socfpga_n5x_socdk-u-boot.dtsi > @@ -8,6 +8,12 @@ > #include "socfpga_n5x-u-boot.dtsi" > > /{ > + fs_loader0: fs-loader { > + bootph-all; > + compatible = "u-boot,fs-loader"; > + sfconfig = <0 0>; > + }; > + > chosen { > u-boot,spl-boot-order = &mmc; > }; > @@ -36,6 +42,13 @@ > }; > }; > > +&sdr { > + intel,ddrcal-qspi-offset = "0x7000000"; > + intel,ddrcal-ddr-offset = "0x100000"; > + firmware-loader = <&fs_loader0>; > + bootph-all; > +}; > + > &flash0 { > bootph-all; > cdns,page-size = <256>; > diff --git a/doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt b/doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt > new file mode 100644 > index 00000000000..a86e7efdfb9 > --- /dev/null > +++ b/doc/device-tree-bindings/ram/intel,sdr-ctl-n5x.txt > @@ -0,0 +1,44 @@ > +* Intel N5X SDRAM controller > + > +Required properties: > +-------------------- > +- compatible: must be "intel,sdr-ctl-n5x" > + > +Optional properties (DDR retention): > +------------------------------------ > +- intel,ddrcal-qspi-offset: string, hex byte offset of the calibration > + backup in QSPI (e.g. "0x7000000"). Used by SPL to load the backup > + via the firmware loader. > +- intel,ddrcal-ddr-offset: string, hex address in DRAM where a valid > + backup is copied after a successful restore (e.g. "0x100000"). > +- firmware-loader: phandle to a "u-boot,fs-loader" node used to read > + the QSPI backup. If omitted, the loader under /chosen is used. > + > +Trust model (QSPI calibration restore): > +--------------------------------------- > +Skipping PHY recalibration after a configuration reset is gated by the > +System Manager DDR retention bit together with the reset type. > +The QSPI-sourced calibration backup is treated as untrusted until > +integrity checks pass. Restore is fail-closed: on any missing DT > +property, flash read failure, header magic mismatch, oversized > +data_len that would overflow the HPS handoff region, CRC32 mismatch > +over the calibration payload, or SHA384 mismatch over the DDR > +configuration image, U-Boot discards the backup and runs a full PHY > +calibration instead. > + > +Example: > +-------- > + > + fs_loader0: fs-loader { > + bootph-all; > + compatible = "u-boot,fs-loader"; > + sfconfig = <0 0>; > + }; > + > + sdr { > + compatible = "intel,sdr-ctl-n5x"; > + intel,ddrcal-qspi-offset = "0x7000000"; > + intel,ddrcal-ddr-offset = "0x100000"; > + firmware-loader = <&fs_loader0>; > + bootph-all; > + }; Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-4-chen.huei.lok@altera.com>]
* Re: [PATCH v4 03/15] arm: socfpga: n5x: enable QSPI mailbox before DDR init [not found] ` <20261016044047.21496-4-chen.huei.lok@altera.com> @ 2026-10-21 2:09 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:09 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > Move the mbox_qspi_open() call ahead of DRAM initialization so the QSPI > controller is handed over to the HPS and its reference clock is > configured before the DDR retention path reads the calibration backup > from QSPI during DDR init. > > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > arch/arm/mach-socfpga/spl_n5x.c | 7 +++---- > 1 file changed, 3 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/mach-socfpga/spl_n5x.c b/arch/arm/mach-socfpga/spl_n5x.c > index 381aca6d32f..bb2d6187ffc 100644 > --- a/arch/arm/mach-socfpga/spl_n5x.c > +++ b/arch/arm/mach-socfpga/spl_n5x.c > @@ -105,6 +105,9 @@ void board_init_f(ulong dummy) > hang(); > } > > + if (IS_ENABLED(CONFIG_CADENCE_QSPI)) > + mbox_qspi_open(); > + > #if CONFIG_IS_ENABLED(ALTERA_SDRAM) > ret = uclass_get_device(UCLASS_RAM, 0, &dev); > if (ret) { > @@ -112,8 +115,4 @@ void board_init_f(ulong dummy) > hang(); > } > #endif > - > -#ifdef CONFIG_CADENCE_QSPI > - mbox_qspi_open(); > -#endif > } Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-5-chen.huei.lok@altera.com>]
* Re: [PATCH v4 04/15] ddr: altera: n5x: Add self-refresh support in DDR4 [not found] ` <20261016044047.21496-5-chen.huei.lok@altera.com> @ 2026-10-21 2:14 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:14 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Enable self-refresh support in DDR4 for retaining DDR4 content with > minimal power when reset is triggered. On a configuration reset with > retention enabled, the trained PHY calibration data is restored from a > backup instead of running a full calibration, so the DDR contents are > preserved across the reset. > > The same INIT0.skip_dram_init / PWRCTL self-refresh entry settings that > LPDDR4 requires are also applied to DDR4, as some DDR4 chips need them to > enter and exit retention correctly. > > The backup calibration data is copied to DDR only when DDR retention is > enabled, and the backup header length ('cal->header.data_len') is > validated against the HPS handoff region so a corrupted or oversized > value cannot overflow into the handoff data before it is consumed. > > Look up the DDR controller node using the "intel,sdr-ctl-n5x" compatible > string so it matches the node defined in the device tree. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > .../include/mach/system_manager_soc64.h | 3 +- > drivers/ddr/altera/sdram_n5x.c | 869 ++++++++++++++++-- > 2 files changed, 790 insertions(+), 82 deletions(-) [...] Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-6-chen.huei.lok@altera.com>]
* Re: [PATCH v4 05/15] configs: socfpga: n5x: enable required configs for DDR retention [not found] ` <20261016044047.21496-6-chen.huei.lok@altera.com> @ 2026-10-21 2:16 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:16 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > DDR retention lets N5X skip a full DDR re-calibration across a > configuration reset by reusing previously calibrated PHY data, which > significantly shortens bring-up time. To do this the SPL must, early in > boot and before DRAM is usable, locate a known-good backup of the > calibration data, verify it, and replay it into the DDR PHY before > handoff. Enable the configs that this restore path depends on: > > - CONFIG_FS_LOADER / CONFIG_SPL_FS_LOADER: the generic firmware loader is > used by the DDR driver (request_firmware_into_buf()) to pull the backup > calibration header and data from the QSPI/NAND RAW partition into the > OCRAM scratch buffer (SOC64_OCRAM_PHY_BACKUP_BASE). The loader device is > resolved through the "firmware-loader" phandle on the DDR node, falling > back to /chosen. SPL_FS_LOADER is required because the restore runs in > SPL, before DRAM is available. The N5X RAW path binds SPI flash from DT > (sfconfig), so CONFIG_SPL_ENV_SUPPORT is not required. > > - CONFIG_SPL_CRC32: the retention path checksums the calibration payload > with crc32_wd_buf() when storing and validating the QSPI backup. SPL > must build lib/crc32.c (obj-$(CONFIG_SPL_CRC32)), which is not selected > by SHA384. > > - CONFIG_SHA384: the calibration backup is bound to the current DDR > configuration with a SHA-384 digest (sha384_csum_wd() over the DDR > handoff image and the mem-clk table). On store the digest is written > into the backup header; on restore it is recomputed and compared > (is_ddrconfig_hash_match()), so a backup that no longer matches the > running DDR / mem-clk configuration is rejected. Enabling SHA384 selects > the SHA-512 implementation it is built on, so CONFIG_SHA512_ALGO does > not need to be listed explicitly. > > Init order and failure modes: the restore is best-effort and fully > guarded. The buffer load is bounds-checked against the HPS handoff region > (SOC64_HANDOFF_BASE) to prevent overflow, the header magic must match, a > CRC32 over the data must match, and finally the SHA-384 config hash must > match. If any check fails (missing loader, read error, bad magic, CRC or > hash mismatch) the backup is discarded and DDR falls back to a normal > full calibration, so an invalid or stale backup never corrupts bring-up. > > These symbols are enabled only after the fs_loader RAW-partition support > and the N5X self-refresh / retention restore code that consume them, so > the series stays bisectable. The defconfig is kept in canonical > savedefconfig order. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > configs/socfpga_n5x_defconfig | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/configs/socfpga_n5x_defconfig b/configs/socfpga_n5x_defconfig > index 2d9456ef435..2119d8f4f4f 100644 > --- a/configs/socfpga_n5x_defconfig > +++ b/configs/socfpga_n5x_defconfig > @@ -72,6 +72,8 @@ CONFIG_SPL_DM_SEQ_ALIAS=y > CONFIG_DWAPB_GPIO=y > CONFIG_DM_I2C=y > CONFIG_SYS_I2C_DW=y > +CONFIG_FS_LOADER=y > +CONFIG_SPL_FS_LOADER=y > CONFIG_SYS_MMC_MAX_BLK_COUNT=256 > CONFIG_MMC_DW=y > CONFIG_SPI_FLASH_ISSI=y > @@ -93,4 +95,5 @@ CONFIG_WDT=y > # CONFIG_SPL_USE_TINY_PRINTF is not set > CONFIG_PANIC_HANG=y > CONFIG_SPL_CRC32=y > +CONFIG_SHA384=y > # CONFIG_TOOLS_MKEFICAPSULE is not set Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-7-chen.huei.lok@altera.com>]
* Re: [PATCH v4 06/15] ddr: altera: n5x: set DDR init progress bit for watchdog recovery [not found] ` <20261016044047.21496-7-chen.huei.lok@altera.com> @ 2026-10-21 2:18 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:18 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Bit[30] of the boot scratch cold 8 register tracks DDR init progress. SDM > inspects it when the watchdog fires due to a DDR init hang, so that it can > run a clean reset of the DDR subsystem before the next attempt. > > Set the bit when SDRAM init starts and clear it once the basic memory test > has passed. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 16 ++++++++++++++++ > 1 file changed, 16 insertions(+) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 4ac22c536fc..4da0617e37c 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -420,6 +420,18 @@ enum data_process { > LOADING > }; > > +void ddr_init_inprogress(bool start) > +{ > + if (start) > + setbits_le32(socfpga_get_sysmgr_addr() + > + SYSMGR_SOC64_BOOT_SCRATCH_COLD8, > + ALT_SYSMGR_SCRATCH_REG_8_DDR_PROGRESS_MASK); > + else > + clrbits_le32(socfpga_get_sysmgr_addr() + > + SYSMGR_SOC64_BOOT_SCRATCH_COLD8, > + ALT_SYSMGR_SCRATCH_REG_8_DDR_PROGRESS_MASK); > +} > + > bool is_ddr_retention_enabled(u32 reg) > { > if (reg & ALT_SYSMGR_SCRATCH_REG_0_DDR_RETENTION_MASK) > @@ -2820,6 +2832,7 @@ int sdram_mmr_init_full(struct udevice *dev) > > if (!is_ddr_init_skipped(reg)) { > printf("SDRAM init in progress ...\n"); > + ddr_init_inprogress(true); > > /* > * Polling reset complete, must be high to ensure DDR > @@ -2970,6 +2983,9 @@ int sdram_mmr_init_full(struct udevice *dev) > > sdram_size_check(&bd); > > + /* Marking end of ddr init with passing basic memory test */ > + ddr_init_inprogress(false); > + > sdram_set_firewall(&bd); > > if (is_ddr_retention_enabled(reg)) { Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-8-chen.huei.lok@altera.com>]
* Re: [PATCH v4 07/15] ddr: altera: n5x: re-init DDR when double-bit ECC error is detected [not found] ` <20261016044047.21496-8-chen.huei.lok@altera.com> @ 2026-10-21 2:20 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:20 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > A double-bit ECC error (DBE) means the retained DDR contents can no longer > be trusted. Detect the DBE status and force a full DDR init and > calibration in that case instead of taking the retention / warm-reset skip > path. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 33 ++++++++++++++++++++++++--------- > 1 file changed, 24 insertions(+), 9 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 4da0617e37c..1bbd5bb0082 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -420,6 +420,17 @@ enum data_process { > LOADING > }; > > +bool is_ddr_dbe_triggered(void) > +{ > + u32 reg = readl(socfpga_get_sysmgr_addr() + > + SYSMGR_SOC64_BOOT_SCRATCH_COLD8); > + > + if (reg & ALT_SYSMGR_SCRATCH_REG_8_DDR_DBE_MASK) > + return true; > + > + return false; > +} > + > void ddr_init_inprogress(bool start) > { > if (start) > @@ -478,17 +489,20 @@ bool is_ddr_init_skipped(u32 reg) > > reset_type_print(reset_t); > > - if (reset_t == WARM_RESET) { > - debug("%s: DDR init is skipped\n", __func__); > - return true; > - } > - > - if (reset_t == COLD_RESET) { > - if (is_ddr_retention_enabled(reg)) { > - debug("%s: DDR retention bit is set\n", __func__); > + if (!is_ddr_dbe_triggered()) { > + if (reset_t == WARM_RESET) { > debug("%s: DDR init is skipped\n", __func__); > return true; > } > + > + if (reset_t == COLD_RESET) { > + if (is_ddr_retention_enabled(reg)) { > + debug("%s: DDR retention bit is set\n", > + __func__); > + debug("%s: DDR init is skipped\n", __func__); > + return true; > + } > + } > } > > debug("%s: DDR init is required\n", __func__); > @@ -500,7 +514,8 @@ bool is_ddr_calibration_skipped(u32 reg) > enum reset_type reset_t = get_reset_type(reg); > > if ((reset_t == NCONFIG || reset_t == JTAG_CONFIG || > - reset_t == RSU_RECONFIG) && is_ddr_retention_enabled(reg)) { > + reset_t == RSU_RECONFIG) && is_ddr_retention_enabled(reg) && > + !is_ddr_dbe_triggered()) { > debug("%s: DDR retention bit is set\n", __func__); > return true; > } Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-9-chen.huei.lok@altera.com>]
* Re: [PATCH v4 08/15] ddr: altera: n5x: re-init DDR after watchdog reset from init hang [not found] ` <20261016044047.21496-9-chen.huei.lok@altera.com> @ 2026-10-21 2:21 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:21 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > If the previous DDR init hung and was recovered by a watchdog reset, the > DDR state is undefined and must not be reused. Detect the init-hang > indication and force a full DDR init and calibration instead of taking the > retention skip path. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 29 +++++++++++++++++++++-------- > 1 file changed, 21 insertions(+), 8 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 1bbd5bb0082..858f2e76aa9 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -420,6 +420,17 @@ enum data_process { > LOADING > }; > > +bool is_ddr_init_hang(void) > +{ > + u32 reg = readl(socfpga_get_sysmgr_addr() + > + SYSMGR_SOC64_BOOT_SCRATCH_COLD8); > + > + if (reg & ALT_SYSMGR_SCRATCH_REG_8_DDR_PROGRESS_MASK) > + return true; > + > + return false; > +} > + > bool is_ddr_dbe_triggered(void) > { > u32 reg = readl(socfpga_get_sysmgr_addr() + > @@ -483,13 +494,13 @@ void reset_type_print(enum reset_type reset_t) > } > } > > -bool is_ddr_init_skipped(u32 reg) > +bool is_ddr_init_skipped(u32 reg, bool is_ddr_hang_be4_rst) > { > enum reset_type reset_t = get_reset_type(reg); > > reset_type_print(reset_t); > > - if (!is_ddr_dbe_triggered()) { > + if (!is_ddr_dbe_triggered() && !is_ddr_hang_be4_rst) { > if (reset_t == WARM_RESET) { > debug("%s: DDR init is skipped\n", __func__); > return true; > @@ -509,13 +520,13 @@ bool is_ddr_init_skipped(u32 reg) > return false; > } > > -bool is_ddr_calibration_skipped(u32 reg) > +bool is_ddr_calibration_skipped(u32 reg, bool is_ddr_hang_be4_rst) > { > enum reset_type reset_t = get_reset_type(reg); > > if ((reset_t == NCONFIG || reset_t == JTAG_CONFIG || > reset_t == RSU_RECONFIG) && is_ddr_retention_enabled(reg) && > - !is_ddr_dbe_triggered()) { > + !is_ddr_dbe_triggered() && !is_ddr_hang_be4_rst) { > debug("%s: DDR retention bit is set\n", __func__); > return true; > } > @@ -1598,7 +1609,7 @@ static bool is_cal_bak_data_valid(void) > } > > static int init_phy(struct ddr_handoff *ddr_handoff_info, > - bool *need_calibrate) > + bool *need_calibrate, bool is_ddr_hang_be4_rst) > { > u32 reg = readl(socfpga_get_sysmgr_addr() + > SYSMGR_SOC64_BOOT_SCRATCH_COLD0); > @@ -1627,7 +1638,7 @@ static int init_phy(struct ddr_handoff *ddr_handoff_info, > ddr_handoff_info->phy_handoff_length, > ddr_handoff_info->phy_base); > > - if (is_ddr_calibration_skipped(reg)) { > + if (is_ddr_calibration_skipped(reg, is_ddr_hang_be4_rst)) { > if (is_cal_bak_data_valid()) > *need_calibrate = false; > else > @@ -2820,6 +2831,7 @@ int sdram_mmr_init_full(struct udevice *dev) > u32 reg = readl(socfpga_get_sysmgr_addr() + > SYSMGR_SOC64_BOOT_SCRATCH_COLD0); > int ret; > + bool is_ddr_hang_be4_rst = is_ddr_init_hang(); > bool need_calibrate = true; > ulong ddr_offset; > char *endptr; > @@ -2845,7 +2857,7 @@ int sdram_mmr_init_full(struct udevice *dev) > */ > writel(SOC64_CRAM_PHY_BACKUP_SKIP_MAGIC, SOC64_OCRAM_PHY_BACKUP_BASE); > > - if (!is_ddr_init_skipped(reg)) { > + if (!is_ddr_init_skipped(reg, is_ddr_hang_be4_rst)) { > printf("SDRAM init in progress ...\n"); > ddr_init_inprogress(true); > > @@ -2887,7 +2899,8 @@ int sdram_mmr_init_full(struct udevice *dev) > printf("DDR controller configuration is completed\n"); > > /* Initialize DDR PHY */ > - ret = init_phy(&ddr_handoff_info, &need_calibrate); > + ret = init_phy(&ddr_handoff_info, &need_calibrate, > + is_ddr_hang_be4_rst); > if (ret) { > debug("%s: Failed to inilialize DDR PHY\n", __func__); > return ret; Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-10-chen.huei.lok@altera.com>]
* Re: [PATCH v4 09/15] ddr: altera: n5x: return error on invalid DDR controller type [not found] ` <20261016044047.21496-10-chen.huei.lok@altera.com> @ 2026-10-21 2:22 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:22 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Return an error immediately when an invalid DDR controller type is passed > in, so that uninitialised local variables such as val and upd_val are > never used. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 858f2e76aa9..43d654341cc 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -2450,6 +2450,9 @@ static int set_cal_res_to_umctl2(struct ddr_handoff *handoff, > > upd_val = GET_LOWB_DATA(readw((uintptr_t)(handoff->phy_base + > DMEM_MB_CDD_CHB_WW_0_1_OFFSET))); > + } else { > + debug("%s: Invalid DDR controller type\n", __func__); > + return -ENXIO; > } > > upd_val = max(val, upd_val); Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-11-chen.huei.lok@altera.com>]
* Re: [PATCH v4 10/15] ddr: altera: n5x: use correct cast for reset status register read [not found] ` <20261016044047.21496-11-chen.huei.lok@altera.com> @ 2026-10-21 2:24 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:24 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Klocwork reported a potential value overflow from an incorrect typecast. > It cannot actually happen here because the result never exceeds 32 bits, > but correct the casting of the reset-status register read for the sake of > good coding practice. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 43d654341cc..d1f0a0d312e 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -2869,9 +2869,9 @@ int sdram_mmr_init_full(struct udevice *dev) > * subsystem in complete reset state before init DDR clock > * and DDR controller > */ > - ret = wait_for_bit_le32((const void *)((uintptr_t)(readl > + ret = wait_for_bit_le32((const void *)((uintptr_t)readl > (ddr_handoff_info.mem_reset_base) + > - MEM_RST_MGR_STATUS)), > + MEM_RST_MGR_STATUS), > MEM_RST_MGR_STATUS_RESET_COMPLETE, > true, TIMEOUT_200MS, false); > if (ret) { > @@ -2894,8 +2894,8 @@ int sdram_mmr_init_full(struct udevice *dev) > > /* Release the controller from reset */ > setbits_le32((uintptr_t) > - (readl(ddr_handoff_info.mem_reset_base) + > - MEM_RST_MGR_STATUS), MEM_RST_MGR_STATUS_AXI_RST | > + readl(ddr_handoff_info.mem_reset_base) + > + MEM_RST_MGR_STATUS, MEM_RST_MGR_STATUS_AXI_RST | > MEM_RST_MGR_STATUS_CONTROLLER_RST | > MEM_RST_MGR_STATUS_RESET_COMPLETE); > Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-12-chen.huei.lok@altera.com>]
* Re: [PATCH v4 11/15] ddr: altera: n5x: fix debug log typo [not found] ` <20261016044047.21496-12-chen.huei.lok@altera.com> @ 2026-10-21 2:25 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:25 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > From: Tien Fong Chee <tien.fong.chee@altera.com> > > Fix a typo in the calibration-result debug log messages. > > Signed-off-by: Tien Fong Chee <tien.fong.chee@altera.com> > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index d1f0a0d312e..244bf8ccb8c 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -2341,7 +2341,7 @@ static void set_cal_res_to_rankctrl(u32 reg_addr, u16 update_value, > > debug("max value divided by 2 is 0x%x\n", update_value); > debug("umclt2 register 0x%x value is 0%x before ", reg_addr, reg); > - debug("update with train result\n"); > + debug("updating with train result\n"); > > value = (reg & mask) >> shift; > > @@ -2362,7 +2362,7 @@ static void set_cal_res_to_rankctrl(u32 reg_addr, u16 update_value, > > reg = readl((uintptr_t)reg_addr); > debug("umclt2 register 0x%x value is 0%x before ", reg_addr, reg); > - debug("update with train result\n"); > + debug("updating with train result\n"); > } > > /* helper function for updating train result to register */ > @@ -2375,7 +2375,7 @@ static void set_cal_res_to_reg(u32 reg_addr, u16 update_value, u32 mask, > > debug("max value divided by 2 is 0x%x\n", update_value); > debug("umclt2 register 0x%x value is 0%x before ", reg_addr, reg); > - debug("update with train result\n"); > + debug("updating with train result\n"); > > value = (reg & mask) >> shift; > > @@ -2386,7 +2386,7 @@ static void set_cal_res_to_reg(u32 reg_addr, u16 update_value, u32 mask, > > reg = readl((uintptr_t)reg_addr); > debug("umclt2 register 0x%x value is 0%x before ", reg_addr, reg); > - debug("update with train result\n"); > + debug("updating with train result\n"); > } > > static u16 get_max_txdqsdlytg0_ux_p0(struct ddr_handoff *handoff, u32 reg, Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-13-chen.huei.lok@altera.com>]
* Re: [PATCH v4 12/15] ddr: altera: n5x: rename is_ddr_init_skipped() and quieten reset log [not found] ` <20261016044047.21496-13-chen.huei.lok@altera.com> @ 2026-10-21 2:27 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:27 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > Rename is_ddr_init_skipped() to is_ddr_init() and invert its return value > so the helper reads naturally at the call site (true means DDR must be > initialised). The decision is otherwise unchanged: init is required on > POR, skipped on a warm reset, and skipped on a cold reset only when the > retention bit is set. > > Switch the reset-type reporting from printf() to debug() so the reset > cause is not printed on every boot, and add the NCONFIG, JTAG_CONFIG and > RSU_RECONFIG cases. Define reset_type_debug_print() above its first caller > so the new call site does not trigger an implicit-declaration warning. > > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 56 +++++++++++++--------------------- > 1 file changed, 22 insertions(+), 34 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 244bf8ccb8c..be512cd33b1 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -468,17 +468,17 @@ static enum reset_type get_reset_type(u32 reg) > ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_SHIFT; > } > > -void reset_type_print(enum reset_type reset_t) > +void reset_type_debug_print(u32 boot_scratch_cold0_reg) > { > - switch (reset_t) { > + switch (get_reset_type(boot_scratch_cold0_reg)) { > case POR_RESET: > - printf("%s: POR is triggered\n", __func__); > + debug("%s: POR is triggered\n", __func__); > break; > case WARM_RESET: > - printf("%s: Warm reset is triggered\n", __func__); > + debug("%s: Warm reset is triggered\n", __func__); > break; > case COLD_RESET: > - printf("%s: Cold reset is triggered\n", __func__); > + debug("%s: Cold reset is triggered\n", __func__); > break; > case NCONFIG: > printf("%s: NCONFIG is triggered\n", __func__); > @@ -490,34 +490,39 @@ void reset_type_print(enum reset_type reset_t) > printf("%s: RSU_RECONFIG is triggered\n", __func__); > break; > default: > - printf("%s: Invalid reset type\n", __func__); > + debug("%s: Invalid reset type\n", __func__); > } > } > > -bool is_ddr_init_skipped(u32 reg, bool is_ddr_hang_be4_rst) > +bool is_ddr_init(bool is_ddr_hang_be4_rst) > { > - enum reset_type reset_t = get_reset_type(reg); > + u32 reg = readl(socfpga_get_sysmgr_addr() + > + SYSMGR_SOC64_BOOT_SCRATCH_COLD0); > > - reset_type_print(reset_t); > + reset_type_debug_print(reg); > > if (!is_ddr_dbe_triggered() && !is_ddr_hang_be4_rst) { > - if (reset_t == WARM_RESET) { > - debug("%s: DDR init is skipped\n", __func__); > + if (get_reset_type(reg) == POR_RESET) { > + debug("%s: DDR init is required\n", __func__); > return true; > } > > - if (reset_t == COLD_RESET) { > + if (get_reset_type(reg) == WARM_RESET) { > + debug("%s: DDR init is skipped\n", __func__); > + return false; > + } > + > + if (get_reset_type(reg) == COLD_RESET) { > if (is_ddr_retention_enabled(reg)) { > - debug("%s: DDR retention bit is set\n", > - __func__); > + debug("%s: DDR retention bit is set\n", __func__); > debug("%s: DDR init is skipped\n", __func__); > - return true; > + return false; > } > } > } > > debug("%s: DDR init is required\n", __func__); > - return false; > + return true; > } > > bool is_ddr_calibration_skipped(u32 reg, bool is_ddr_hang_be4_rst) > @@ -2811,23 +2816,6 @@ static int ddr_post_config(struct ddr_handoff *handoff, bool *need_calibrate) > return ret; > } > > -void reset_type_debug_print(u32 boot_scratch_cold0_reg) > -{ > - switch (get_reset_type(boot_scratch_cold0_reg)) { > - case POR_RESET: > - debug("%s: POR is triggered\n", __func__); > - break; > - case WARM_RESET: > - debug("%s: Warm reset is triggered\n", __func__); > - break; > - case COLD_RESET: > - debug("%s: Cold reset is triggered\n", __func__); > - break; > - default: > - debug("%s: Invalid reset type\n", __func__); > - } > -} > - > int sdram_mmr_init_full(struct udevice *dev) > { > u32 user_backup[2], user_backup_2nd[2]; > @@ -2860,7 +2848,7 @@ int sdram_mmr_init_full(struct udevice *dev) > */ > writel(SOC64_CRAM_PHY_BACKUP_SKIP_MAGIC, SOC64_OCRAM_PHY_BACKUP_BASE); > > - if (!is_ddr_init_skipped(reg, is_ddr_hang_be4_rst)) { > + if (is_ddr_init(is_ddr_hang_be4_rst)) { > printf("SDRAM init in progress ...\n"); > ddr_init_inprogress(true); > Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-14-chen.huei.lok@altera.com>]
* Re: [PATCH v4 13/15] ddr: altera: n5x: drop always-false unsigned less-than-zero checks [not found] ` <20261016044047.21496-14-chen.huei.lok@altera.com> @ 2026-10-21 2:29 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:29 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > The *_handoff_length fields are unsigned, so the "< 0" checks on them in > populate_ddr_handoff() can never be true. Remove these dead comparisons. > > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 32 ++++++-------------------------- > 1 file changed, 6 insertions(+), 26 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index be512cd33b1..558415d7931 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause > /* > - * Copyright (C) 2020-2022 Intel Corporation <www.intel.com> > + * Copyright (C) 2020-2023 Intel Corporation <www.intel.com> > * > */ > > @@ -1731,12 +1731,6 @@ int populate_ddr_handoff(struct ddr_handoff *handoff) > debug("%s: Umctl2 handoff length in word(32-bit) = 0x%x\n", __func__, > (u32)handoff->cntlr_handoff_length); > > - /* Wrong format on user setting data */ > - if (handoff->cntlr_handoff_length < 0) { > - debug("%s: Wrong format on user setting data\n", __func__); > - return -ENOEXEC; > - } > - > /* Get the next handoff section address */ > next_section_header = handoff->cntlr_handoff_base + > handoff->cntlr_total_length; > @@ -1789,13 +1783,6 @@ int populate_ddr_handoff(struct ddr_handoff *handoff) > __func__, > (u32)handoff->cntlr2_handoff_length); > > - /* Wrong format on user setting data */ > - if (handoff->cntlr2_handoff_length < 0) { > - debug("%s: Wrong format on umctl2 user setting data\n", > - __func__); > - return -ENOEXEC; > - } > - > /* Get the next handoff section address */ > next_section_header = handoff->cntlr2_handoff_base + > handoff->cntlr2_total_length; > @@ -1831,13 +1818,6 @@ int populate_ddr_handoff(struct ddr_handoff *handoff) > debug("%s: PHY handoff length in word(32-bit) = 0x%x\n", > __func__, (u32)handoff->phy_handoff_length); > > - /* Wrong format on PHY user setting data */ > - if (handoff->phy_handoff_length < 0) { > - debug("%s: Wrong format on PHY user setting data\n", > - __func__); > - return -ENOEXEC; > - } > - > /* Get the next handoff section address */ > next_section_header = handoff->phy_handoff_base + > handoff->phy_total_length; > @@ -1875,11 +1855,11 @@ int populate_ddr_handoff(struct ddr_handoff *handoff) > debug("%s: PHY engine handoff length in word(32-bit) = 0x%x\n", > __func__, (u32)handoff->phy_engine_handoff_length); > > - /* Wrong format on PHY init engine setting data */ > - if (handoff->phy_engine_handoff_length < 0) { > - debug("%s: Wrong format on PHY init engine ", > - __func__); > - debug("user setting data\n"); > + /* Checking DDR handoff is overflow? */ > + if (SOC64_OCRAM_PHY_BACKUP_BASE <= > + (handoff->phy_engine_handoff_base + > + handoff->phy_engine_total_length)) { > + printf("%s: DDR handoff is overflow\n ", __func__); > return -ENOEXEC; > } > Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-15-chen.huei.lok@altera.com>]
* Re: [PATCH v4 14/15] ddr: altera: n5x: reconfigure DDR when mem-clk handoff changes [not found] ` <20261016044047.21496-15-chen.huei.lok@altera.com> @ 2026-10-21 2:30 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:30 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > Trigger a DDR reconfiguration when the memory clock values in the handoff > data have changed, so that a retention restore is not attempted against a > DDR configuration that no longer matches the running mem-clk settings. > > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > .../mach-socfpga/include/mach/handoff_soc64.h | 1 + > arch/arm/mach-socfpga/wrap_handoff_soc64.c | 7 ++- > drivers/ddr/altera/sdram_n5x.c | 59 ++++++++++++++++++- > 3 files changed, 65 insertions(+), 2 deletions(-) > > diff --git a/arch/arm/mach-socfpga/include/mach/handoff_soc64.h b/arch/arm/mach-socfpga/include/mach/handoff_soc64.h > index ae5af1f0100..0fb43acc98b 100644 > --- a/arch/arm/mach-socfpga/include/mach/handoff_soc64.h > +++ b/arch/arm/mach-socfpga/include/mach/handoff_soc64.h > @@ -47,6 +47,7 @@ > #define SOC64_HANDOFF_BASE 0x0007F000 > #elif IS_ENABLED(CONFIG_ARCH_SOCFPGA_N5X) > #define SOC64_HANDOFF_BASE 0xFFE5F000 > +#define SOC64_HANDOFF_DDR_CLK_HANDOFF (SOC64_HANDOFF_BASE + 0x610) > #define SOC64_HANDOFF_MISC (SOC64_HANDOFF_BASE + 0x630) > > /* DDR handoff */ > diff --git a/arch/arm/mach-socfpga/wrap_handoff_soc64.c b/arch/arm/mach-socfpga/wrap_handoff_soc64.c > index ecde90f76f4..72e98806f8d 100644 > --- a/arch/arm/mach-socfpga/wrap_handoff_soc64.c > +++ b/arch/arm/mach-socfpga/wrap_handoff_soc64.c > @@ -27,7 +27,6 @@ static enum endianness check_endianness(u32 handoff) > case SOC64_HANDOFF_MAGIC_IOCTL: > case SOC64_HANDOFF_MAGIC_FPGA: > case SOC64_HANDOFF_MAGIC_DELAY: > - case SOC64_HANDOFF_MAGIC_CLOCK: > case SOC64_HANDOFF_MAGIC_SDRAM: > #if IS_ENABLED(CONFIG_ARCH_SOCFPGA_AGILEX5) > case SOC64_HANDOFF_MAGIC_PERI: > @@ -35,6 +34,12 @@ static enum endianness check_endianness(u32 handoff) > case SOC64_HANDOFF_MAGIC_MISC: > #endif > return BIG_ENDIAN; > + case SOC64_HANDOFF_MAGIC_CLOCK: > + if (IS_ENABLED(CONFIG_ARCH_SOCFPGA_N5X)) { > + debug("%s: mem clk handoff data\n", __func__); > + return LITTLE_ENDIAN; > + } > + return BIG_ENDIAN; > #if IS_ENABLED(CONFIG_ARCH_SOCFPGA_N5X) > case SOC64_HANDOFF_DDR_UMCTL2_MAGIC: > debug("%s: umctl2 handoff data\n", __func__); > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 558415d7931..11aa020d380 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -37,8 +37,9 @@ DECLARE_GLOBAL_DATA_PTR; > > /* DDR handoff SHA384 attribute */ > #define DDR_HANDOFF_IMG_ADDR 0xFFE44000 > -#define DDR_HANDOFF_IMG_LEN 0x1A000 > +#define DDR_HANDOFF_IMG_LEN 0x1A200 > #define CHUNKSZ_PER_WD_RESET (256 * 1024) > +#define MEMCLK_HANDOFF_SIZE 0x2C > > /* MPFE NOC registers */ > #define FPGA2SDRAM_MGR_MAIN_SIDEBANDMGR_FLAGOUTSET0 0xF8024050 > @@ -1105,6 +1106,28 @@ static void phy_ocram(phys_addr_t phy_base, phys_addr_t phy_offset, > } > } > > +static int populate_memclk_handoff(void *handoff_base, u32 *mclk_table, u32 size) > +{ > + u32 handoff_table[MEMCLK_HANDOFF_SIZE - 4]; > + u32 length = ARRAY_SIZE(handoff_table); > + u32 offset = length - 8; > + u32 i; > + int ret = 0; > + > + ret = socfpga_handoff_read((void *)handoff_base, handoff_table, length); > + if (ret) { > + debug("%s: handoff read failed. ret: %d\n", __func__, ret); > + return ret; > + } > + > + for (i = 0; i < size; i++) { > + mclk_table[i] = handoff_table[offset + i]; > + debug("mclk_table[%d]: 0x%x\n", i, mclk_table[i]); > + } > + > + return ret; > +} > + > static int cal_data_ocram(phys_addr_t phy_base, u32 addr, > enum data_process proc) > { > @@ -1390,6 +1413,11 @@ static int cal_data_ocram(phys_addr_t phy_base, u32 addr, > size_t phybak_num; > const u32 *phybak_p = phybak; > u16 *data; > + u32 memclk_table[4]; > + u32 size = ARRAY_SIZE(memclk_table); > + u8 *memclk_hash_offset = (u8 *)(SOC64_OCRAM_PHY_BACKUP_BASE - SZ_1K); > + int ret = 0; > + > struct bac_cal_data_t *cal = (struct bac_cal_data_t *) > ((uintptr_t)addr); > > @@ -1424,6 +1452,17 @@ static int cal_data_ocram(phys_addr_t phy_base, u32 addr, > }; > > if (proc == STORE) { > + ret = populate_memclk_handoff((void *)SOC64_HANDOFF_CLOCK, > + memclk_table, size); > + if (ret) { > + debug("%s: Memclks config Magic Number miss match. ret :%d\n", > + __func__, ret); > + return -EFAULT; > + } > + > + sha384_csum_wd((u8 *)memclk_table, 4 * size, > + memclk_hash_offset, CHUNKSZ_PER_WD_RESET); > + > /* Creating header */ > /* Generate HASH384 from the DDR config */ > sha384_csum_wd((u8 *)DDR_HANDOFF_IMG_ADDR, > @@ -1454,10 +1493,28 @@ static bool is_ddrconfig_hash_match(const void *buffer) > { > int ret; > u8 hash[SHA384_SUM_LEN]; > + u32 memclk_table[4]; > + u32 size = ARRAY_SIZE(memclk_table); > > /* Magic symbol in first 4 bytes of header */ > struct cal_header_t *header = (struct cal_header_t *)buffer; > > + /* > + * Read and generate the hash for the Mem clk values and store before > + * calcuting the HASH384 generation before Image. > + */ > + ret = populate_memclk_handoff((void *)SOC64_HANDOFF_CLOCK, > + memclk_table, size); > + if (ret) { > + debug("%s: Memclks config Magic Number miss match. ret : %d\n", > + __func__, ret); > + return false; > + } > + > + sha384_csum_wd((u8 *)memclk_table, (4 * sizeof(u32)), > + (u8 *)DDR_HANDOFF_IMG_ADDR + DDR_HANDOFF_IMG_LEN - SZ_512, > + CHUNKSZ_PER_WD_RESET); > + > /* Generate HASH384 from the image */ > sha384_csum_wd((u8 *)DDR_HANDOFF_IMG_ADDR, DDR_HANDOFF_IMG_LEN, > hash, CHUNKSZ_PER_WD_RESET); Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
[parent not found: <20261016044047.21496-16-chen.huei.lok@altera.com>]
* Re: [PATCH v4 15/15] ddr: altera: n5x: Replay PIE handoff on DDR retention restore [not found] ` <20261016044047.21496-16-chen.huei.lok@altera.com> @ 2026-10-21 2:31 ` Chee, Tien Fong 0 siblings, 0 replies; 15+ messages in thread From: Chee, Tien Fong @ 2026-10-21 2:31 UTC (permalink / raw) To: Chen Huei Lok, u-boot Cc: Tom Rini, Marek Vasut, Simon Goldschmidt, Alif Zakuan Yuslaimi, Boon Khai Ng, Kok Kiang Hea On 16/10/2026 12:40 pm, Chen Huei Lok wrote: > On the DDR retention restore path the trained PHY results are reloaded > from the calibration backup by cal_data_ocram() in init_phy(), which > leaves the PHY microcontroller stalled. Running update_training_result() > there reads back results from a PHY that was never freshly trained and > fails the DFI handshake, so DRAM init returns -ENODEV. > > Instead, bring the PHY microcontroller out of reset/stall and replay the > PHY Init Engine handoff to return the PHY to normal operating mode. > > The PIE handoff table shares memory with the memclk hash scratch area > (DDR_HANDOFF_IMG_ADDR + DDR_HANDOFF_IMG_LEN - SZ_512) used by > is_ddrconfig_hash_match(). Snapshot and restore those bytes around the > hash computation so the replayed PIE table is not corrupted. > > Signed-off-by: Chen Huei Lok <chen.huei.lok@altera.com> > --- > drivers/ddr/altera/sdram_n5x.c | 48 +++++++++++++++++++++++++++++----- > 1 file changed, 42 insertions(+), 6 deletions(-) > > diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c > index 11aa020d380..fd90a895a80 100644 > --- a/drivers/ddr/altera/sdram_n5x.c > +++ b/drivers/ddr/altera/sdram_n5x.c > @@ -1495,6 +1495,9 @@ static bool is_ddrconfig_hash_match(const void *buffer) > u8 hash[SHA384_SUM_LEN]; > u32 memclk_table[4]; > u32 size = ARRAY_SIZE(memclk_table); > + u8 *memclk_hash_dst = (u8 *)(DDR_HANDOFF_IMG_ADDR + > + DDR_HANDOFF_IMG_LEN - SZ_512); > + u8 saved_handoff[SHA384_SUM_LEN]; > > /* Magic symbol in first 4 bytes of header */ > struct cal_header_t *header = (struct cal_header_t *)buffer; > @@ -1511,14 +1514,26 @@ static bool is_ddrconfig_hash_match(const void *buffer) > return false; > } > > + /* > + * The memclk hash is written into the tail of the hashed handoff image > + * (DDR_HANDOFF_IMG_ADDR + DDR_HANDOFF_IMG_LEN - SZ_512) so that the > + * whole-image hash covers it. That location overlaps the PHY init > + * engine handoff table that phy_init_engine() replays on the retention > + * restore path, so snapshot the original handoff bytes and restore them > + * once the hash has been computed. > + */ > + memcpy(saved_handoff, memclk_hash_dst, SHA384_SUM_LEN); > + > sha384_csum_wd((u8 *)memclk_table, (4 * sizeof(u32)), > - (u8 *)DDR_HANDOFF_IMG_ADDR + DDR_HANDOFF_IMG_LEN - SZ_512, > - CHUNKSZ_PER_WD_RESET); > + memclk_hash_dst, CHUNKSZ_PER_WD_RESET); > > /* Generate HASH384 from the image */ > sha384_csum_wd((u8 *)DDR_HANDOFF_IMG_ADDR, DDR_HANDOFF_IMG_LEN, > hash, CHUNKSZ_PER_WD_RESET); > > + /* Restore the handoff bytes clobbered by the memclk hash above */ > + memcpy(memclk_hash_dst, saved_handoff, SHA384_SUM_LEN); > + > ret = memcmp((void *)hash, (const void *)header->ddrconfig_hash, > SHA384_SUM_LEN); > if (ret) { > @@ -2961,10 +2976,31 @@ int sdram_mmr_init_full(struct udevice *dev) > } > > } else { > - /* Updating training result to DDR controller */ > - ret = update_training_result(&ddr_handoff_info); > - if (ret) > - return ret; > + /* > + * Retention restore path: the trained results were > + * already reloaded into the PHY by cal_data_ocram() > + * (LOADING) in init_phy(), so update_training_result() > + * must not run here (it would read back results from a > + * PHY that was never freshly trained and fail the DFI > + * handshake). Bring the PHY microcontroller out of > + * reset/stall, then replay the PIE handoff to return the > + * PHY to normal operating mode. > + */ > + clrbits_le16(ddr_handoff_info.phy_base + > + DDR_PHY_APBONLY0_OFFSET, > + DDR_PHY_MICROCONTMUXSEL); > + setbits_le16(ddr_handoff_info.phy_base + > + DDR_PHY_MICRORESET_OFFSET, > + DDR_PHY_MICRORESET_RESET | > + DDR_PHY_MICRORESET_STALL); > + clrbits_le16(ddr_handoff_info.phy_base + > + DDR_PHY_MICRORESET_OFFSET, > + DDR_PHY_MICRORESET_RESET); > + setbits_le16(ddr_handoff_info.phy_base + > + DDR_PHY_APBONLY0_OFFSET, > + DDR_PHY_MICROCONTMUXSEL); > + > + phy_init_engine(&ddr_handoff_info); > } > > /* Reviewed-by: Tien Fong Chee <tien.fong.chee@altera.com> Best regards, Tien Fong ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-21 2:31 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20261016044047.21496-1-chen.huei.lok@altera.com>
[not found] ` <20261016044047.21496-2-chen.huei.lok@altera.com>
2026-10-21 1:55 ` [PATCH v4 01/15] misc: fs_loader: Add QSPI RAW offset loading support Chee, Tien Fong
[not found] ` <20261016044047.21496-3-chen.huei.lok@altera.com>
2026-10-21 2:07 ` [PATCH v4 02/15] arm: dts: socfpga: n5x: add DDR retention calibration backup nodes Chee, Tien Fong
[not found] ` <20261016044047.21496-4-chen.huei.lok@altera.com>
2026-10-21 2:09 ` [PATCH v4 03/15] arm: socfpga: n5x: enable QSPI mailbox before DDR init Chee, Tien Fong
[not found] ` <20261016044047.21496-5-chen.huei.lok@altera.com>
2026-10-21 2:14 ` [PATCH v4 04/15] ddr: altera: n5x: Add self-refresh support in DDR4 Chee, Tien Fong
[not found] ` <20261016044047.21496-6-chen.huei.lok@altera.com>
2026-10-21 2:16 ` [PATCH v4 05/15] configs: socfpga: n5x: enable required configs for DDR retention Chee, Tien Fong
[not found] ` <20261016044047.21496-7-chen.huei.lok@altera.com>
2026-10-21 2:18 ` [PATCH v4 06/15] ddr: altera: n5x: set DDR init progress bit for watchdog recovery Chee, Tien Fong
[not found] ` <20261016044047.21496-8-chen.huei.lok@altera.com>
2026-10-21 2:20 ` [PATCH v4 07/15] ddr: altera: n5x: re-init DDR when double-bit ECC error is detected Chee, Tien Fong
[not found] ` <20261016044047.21496-9-chen.huei.lok@altera.com>
2026-10-21 2:21 ` [PATCH v4 08/15] ddr: altera: n5x: re-init DDR after watchdog reset from init hang Chee, Tien Fong
[not found] ` <20261016044047.21496-10-chen.huei.lok@altera.com>
2026-10-21 2:22 ` [PATCH v4 09/15] ddr: altera: n5x: return error on invalid DDR controller type Chee, Tien Fong
[not found] ` <20261016044047.21496-11-chen.huei.lok@altera.com>
2026-10-21 2:24 ` [PATCH v4 10/15] ddr: altera: n5x: use correct cast for reset status register read Chee, Tien Fong
[not found] ` <20261016044047.21496-12-chen.huei.lok@altera.com>
2026-10-21 2:25 ` [PATCH v4 11/15] ddr: altera: n5x: fix debug log typo Chee, Tien Fong
[not found] ` <20261016044047.21496-13-chen.huei.lok@altera.com>
2026-10-21 2:27 ` [PATCH v4 12/15] ddr: altera: n5x: rename is_ddr_init_skipped() and quieten reset log Chee, Tien Fong
[not found] ` <20261016044047.21496-14-chen.huei.lok@altera.com>
2026-10-21 2:29 ` [PATCH v4 13/15] ddr: altera: n5x: drop always-false unsigned less-than-zero checks Chee, Tien Fong
[not found] ` <20261016044047.21496-15-chen.huei.lok@altera.com>
2026-10-21 2:30 ` [PATCH v4 14/15] ddr: altera: n5x: reconfigure DDR when mem-clk handoff changes Chee, Tien Fong
[not found] ` <20261016044047.21496-16-chen.huei.lok@altera.com>
2026-10-21 2:31 ` [PATCH v4 15/15] ddr: altera: n5x: Replay PIE handoff on DDR retention restore Chee, Tien Fong
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox