diff mbox series

ddr: altera: n5x: Add self-refresh support in DDR4

Message ID 20221122153103.354-1-jit.loon.lim@intel.com
State Needs Review / ACK, archived
Delegated to: Marek Vasut
Headers show
Series ddr: altera: n5x: Add self-refresh support in DDR4 | expand

Commit Message

Jit Loon Lim Nov. 22, 2022, 3:31 p.m. UTC
From: Tien Fong Chee <tien.fong.chee@intel.com>

Enable self-refresh support in DDR4 for retaining DDR4 content with minimal
power when reset is triggered.

Signed-off-by: Tien Fong Chee <tien.fong.chee@intel.com>
Signed-off-by: Jit Loon Lim <jit.loon.lim@intel.com>
---
 .../include/mach/system_manager_soc64.h       |   3 +-
 drivers/ddr/altera/sdram_n5x.c                | 751 +++++++++++++++++-
 2 files changed, 730 insertions(+), 24 deletions(-)
diff mbox series

Patch

diff --git a/arch/arm/mach-socfpga/include/mach/system_manager_soc64.h b/arch/arm/mach-socfpga/include/mach/system_manager_soc64.h
index a8009664fe..c2d1f8e4b9 100644
--- a/arch/arm/mach-socfpga/include/mach/system_manager_soc64.h
+++ b/arch/arm/mach-socfpga/include/mach/system_manager_soc64.h
@@ -99,8 +99,7 @@  void populate_sysmgr_pinmux(void);
  */
 #define SYSMGR_SCRATCH_REG_0_QSPI_REFCLK_MASK		GENMASK(27, 0)
 #define ALT_SYSMGR_SCRATCH_REG_0_DDR_RETENTION_MASK	BIT(31)
-#define ALT_SYSMGR_SCRATCH_REG_0_DDR_SHA_MASK		BIT(30)
-#define ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_MASK	(BIT(29) | BIT(28))
+#define ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_MASK GENMASK(30, 28)
 #define ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_SHIFT	28
 
 #define SYSMGR_SDMMC				SYSMGR_SOC64_SDMMC
diff --git a/drivers/ddr/altera/sdram_n5x.c b/drivers/ddr/altera/sdram_n5x.c
index 5a20a8d78d..135bc8fd7d 100644
--- a/drivers/ddr/altera/sdram_n5x.c
+++ b/drivers/ddr/altera/sdram_n5x.c
@@ -9,6 +9,7 @@ 
 #include <div64.h>
 #include <dm.h>
 #include <errno.h>
+#include <fs_loader.h>
 #include <fdtdec.h>
 #include <hang.h>
 #include <ram.h>
@@ -21,11 +22,25 @@ 
 #include <asm/arch/reset_manager.h>
 #include <asm/arch/system_manager.h>
 #include <asm/io.h>
+#include <dm/ofnode.h>
 #include <linux/err.h>
 #include <linux/sizes.h>
+#include <u-boot/crc.h>
+#include <u-boot/sha512.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
+/* PHY calibration data backup attribute */
+#define SOC64_OCRAM_PHY_BACKUP_BASE		0xFFE5E400
+/* "SKIP" */
+#define SOC64_CRAM_PHY_BACKUP_SKIP_MAGIC	0x50494B53
+#define BAC_CAL_MMC_RAW_OFFSET (CONFIG_ENV_SIZE + CONFIG_ENV_OFFSET)
+
+/* DDR handoff SHA384 attribute */
+#define DDR_HANDOFF_IMG_ADDR	0xFFE44000
+#define DDR_HANDOFF_IMG_LEN	0x1A000
+#define CHUNKSZ_PER_WD_RESET	(256 * 1024)
+
 /* MPFE NOC registers */
 #define FPGA2SDRAM_MGR_MAIN_SIDEBANDMGR_FLAGOUTSET0	0xF8024050
 
@@ -379,6 +394,117 @@  enum message_mode {
 	STREAMING_MESSAGE
 };
 
+/* Reset type */
+enum reset_type {
+	POR_RESET,
+	WARM_RESET,
+	COLD_RESET,
+	NCONFIG,
+	JTAG_CONFIG,
+	RSU_RECONFIG
+};
+
+enum data_type {
+	HEADER_ONLY,
+	HEADER_DATA
+};
+
+/* Header for backup calibration data */
+struct cal_header_t {
+	u32 header_magic;
+	u32 data_len;
+	u32 caldata_crc32;
+	u8 ddrconfig_hash[SHA384_SUM_LEN];
+} __attribute__ ((__packed__));
+
+/* Header + backup calibration data */
+struct bac_cal_data_t {
+	struct cal_header_t header;
+	u16 data;
+} __attribute__ ((__packed__));
+
+enum data_process {
+	STORE,
+	LOADING
+};
+
+bool is_ddr_retention_enabled(u32 reg)
+{
+	if (reg & ALT_SYSMGR_SCRATCH_REG_0_DDR_RETENTION_MASK)
+		return true;
+
+	return false;
+}
+
+static enum reset_type get_reset_type(u32 reg)
+{
+	return (reg & ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_MASK) >>
+		ALT_SYSMGR_SCRATCH_REG_0_DDR_RESET_TYPE_SHIFT;
+}
+
+void reset_type_print(enum reset_type reset_t)
+{
+	switch (reset_t) {
+	case POR_RESET:
+		printf("%s: POR is triggered\n", __func__);
+		break;
+	case WARM_RESET:
+		printf("%s: Warm reset is triggered\n", __func__);
+		break;
+	case COLD_RESET:
+		printf("%s: Cold reset is triggered\n", __func__);
+		break;
+	case NCONFIG:
+		printf("%s: NCONFIG is triggered\n", __func__);
+		break;
+	case JTAG_CONFIG:
+		printf("%s: JTAG_CONFIG is triggered\n", __func__);
+		break;
+	case RSU_RECONFIG:
+		printf("%s: RSU_RECONFIG is triggered\n", __func__);
+		break;
+	default:
+		printf("%s: Invalid reset type\n", __func__);
+	}
+}
+
+bool is_ddr_init_skipped(u32 reg)
+{
+	enum reset_type reset_t = get_reset_type(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__);
+			debug("%s: DDR init is skipped\n", __func__);
+			return true;
+		}
+	}
+
+	debug("%s: DDR init is required\n", __func__);
+	return false;
+}
+
+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)) {
+		debug("%s: DDR retention bit is set\n", __func__);
+		return true;
+	}
+
+	debug("%s: DDR calibration is required\n", __func__);
+	return false;
+}
+
 static int clr_ca_parity_error_status(phys_addr_t umctl2_base)
 {
 	int ret;
@@ -711,6 +837,7 @@  static int scrubber_ddr_config(phys_addr_t umctl2_base,
 	writel(0, umctl2_base + DDR4_SBRRANGE0_OFFSET);
 	writel(0, umctl2_base + DDR4_SBRRANGE1_OFFSET);
 
+	printf("Enabling scrubber ...\n");
 	/* Enables scrubber */
 	setbits_le32(umctl2_base + DDR4_SBRCTL_OFFSET, DDR4_SBRCTL_SCRUB_EN);
 	/* Polling all scrub writes commands have been sent */
@@ -924,8 +1051,513 @@  static int phy_pre_handoff_config(phys_addr_t umctl2_base,
 	return ret;
 }
 
+static void phy_ocram(phys_addr_t phy_base, phys_addr_t phy_offset,
+		      u16 *data, enum data_process proc)
+{
+	u32 abs_addr;
+
+	abs_addr = phy_base + (phy_offset << 1);
+
+	if (proc == STORE) {
+		/* Storing PHY value */
+		writew(readw((uintptr_t)abs_addr), data);
+		debug("%s: PHY offset 0x%08x, data %x\n", __func__,
+		      (u32)phy_offset, readw((uintptr_t)data));
+	} else if (proc == LOADING) {
+		/* Loading bak cal data to PHY */
+		writew(readw(data), (uintptr_t)abs_addr);
+		debug("%s: PHY offset 0x%08x, data %x\n", __func__,
+		      (u32)phy_offset, readw((uintptr_t)abs_addr));
+	}
+}
+
+static void cal_data_ocram(phys_addr_t phy_base, u32 addr,
+			   enum data_process proc)
+{
+	/*
+	 * This array variable contains a list of PHY registers required for
+	 * backup before DDR entering self-refresh mode
+	 */
+	u32 phybak[] = {0x1005f, 0x1015f, 0x1105f, 0x1115f, 0x1205f,
+			0x1215f, 0x1305f, 0x1315f, 0x1405f, 0x1415f,
+			0x1505f, 0x1515f, 0x1605f, 0x1615f, 0x1705f,
+			0x1715f, 0x1805f, 0x1815f, 0x00055, 0x01055,
+			0x02055, 0x03055, 0x04055, 0x05055, 0x06055,
+			0x07055, 0x08055, 0x09055, 0x200c5, 0x2002e,
+			0x20024, 0x2003a, 0x2007d, 0x2007c, 0x20056,
+			0x1004d, 0x1014d, 0x1104d, 0x1114d, 0x1204d,
+			0x1214d, 0x1304d, 0x1314d, 0x1404d, 0x1414d,
+			0x1504d, 0x1514d, 0x1604d, 0x1614d, 0x1704d,
+			0x1714d, 0x1804d, 0x1814d, 0x10049, 0x10149,
+			0x11049, 0x11149, 0x12049, 0x12149, 0x13049,
+			0x13149, 0x14049, 0x14149, 0x15049, 0x15149,
+			0x16049, 0x16149, 0x17049, 0x17149, 0x18049,
+			0x18149, 0x00043, 0x01043, 0x02043, 0x03043,
+			0x04043, 0x05043, 0x06043, 0x07043, 0x08043,
+			0x09043, 0x20018, 0x20075, 0x20050, 0x2009b,
+			0x20008, 0x20088, 0x200b2, 0x10043, 0x10143,
+			0x11043, 0x11143, 0x12043, 0x12143, 0x13043,
+			0x13143, 0x14043, 0x14143, 0x15043, 0x15143,
+			0x16043, 0x16143, 0x17043, 0x17143, 0x18043,
+			0x18143, 0x2005b, 0x2005c, 0x200fa, 0x20019,
+			0x200f0, 0x200f1, 0x200f2, 0x200f3, 0x200f4,
+			0x200f5, 0x200f6, 0x200f7, 0x1004a, 0x1104a,
+			0x1204a, 0x1304a, 0x1404a, 0x1504a, 0x1604a,
+			0x1704a, 0x1804a, 0x20025, 0x2002d, 0x2002c,
+			0xd0000, 0x90000, 0x90001, 0x90002, 0x90003,
+			0x90004, 0x90005, 0x90029, 0x9002a, 0x9002b,
+			0x9002c, 0x9002d, 0x9002e, 0x9002f, 0x90030,
+			0x90031, 0x90032, 0x90033, 0x90034, 0x90035,
+			0x90036, 0x90037, 0x90038, 0x90039, 0x9003a,
+			0x9003b, 0x9003c, 0x9003d, 0x9003e, 0x9003f,
+			0x90040, 0x90041, 0x90042, 0x90043, 0x90044,
+			0x90045, 0x90046, 0x90047, 0x90048, 0x90049,
+			0x9004a, 0x9004b, 0x9004c, 0x9004d, 0x9004e,
+			0x9004f, 0x90050, 0x90051, 0x90052, 0x90053,
+			0x90054, 0x90055, 0x90056, 0x90057, 0x90058,
+			0x90059, 0x9005a, 0x9005b, 0x9005c, 0x9005d,
+			0x9005e, 0x9005f, 0x90060, 0x90061, 0x90062,
+			0x90063, 0x90064, 0x90065, 0x90066, 0x90067,
+			0x90068, 0x90069, 0x9006a, 0x9006b, 0x9006c,
+			0x9006d, 0x9006e, 0x9006f, 0x90070, 0x90071,
+			0x90072, 0x90073, 0x90074, 0x90075, 0x90076,
+			0x90077, 0x90078, 0x90079, 0x9007a, 0x9007b,
+			0x9007c, 0x9007d, 0x9007e, 0x9007f, 0x90080,
+			0x90081, 0x90082, 0x90083, 0x90084, 0x90085,
+			0x90086, 0x90087, 0x90088, 0x90089, 0x9008a,
+			0x9008b, 0x9008c, 0x9008d, 0x9008e, 0x9008f,
+			0x90090, 0x90091, 0x90092, 0x90093, 0x90094,
+			0x90095, 0x90096, 0x90097, 0x90098, 0x90099,
+			0x9009a, 0x9009b, 0x9009c, 0x9009d, 0x9009e,
+			0x9009f, 0x900a0, 0x900a1, 0x900a2, 0x900a3,
+			0x900a4, 0x900a5, 0x900a6, 0x900a7, 0x900a8,
+			0x900a9, 0x900aa, 0x900ab, 0x900ac, 0x900ad,
+			0x900ae, 0x900af, 0x900b0, 0x900b1, 0x900b2,
+			0x900b3, 0x900b4, 0x900b5, 0x900b6, 0x900b7,
+			0x900b8, 0x900b9, 0x900ba, 0x900bb, 0x900bc,
+			0x900bd, 0x900be, 0x900bf, 0x900c0, 0x900c1,
+			0x900c2, 0x900c3, 0x900c4, 0x900c5, 0x900c6,
+			0x900c7, 0x90006, 0x90007, 0x90008, 0x90009,
+			0x9000a, 0x9000b, 0xd00e7, 0x90017, 0x90026,
+			0x2000b, 0x2000c, 0x2000d, 0x2000e, 0x9000c,
+			0x9000d, 0x9000e, 0x9000f, 0x90010, 0x90011,
+			0x90012, 0x90013, 0x20089, 0xc0080, 0x200cb,
+			0x10068, 0x10069, 0x1006a, 0x1006b, 0x10168,
+			0x10169, 0x1016a, 0x1016b, 0x10268, 0x10269,
+			0x1026a, 0x1026b, 0x10368, 0x10369, 0x1036a,
+			0x1036b, 0x10468, 0x10469, 0x1046a, 0x1046b,
+			0x10568, 0x10569, 0x1056a, 0x1056b, 0x10668,
+			0x10669, 0x1066a, 0x1066b, 0x10768, 0x10769,
+			0x1076a, 0x1076b, 0x10868, 0x10869, 0x1086a,
+			0x1086b, 0x11068, 0x11069, 0x1106a, 0x1106b,
+			0x11168, 0x11169, 0x1116a, 0x1116b, 0x11268,
+			0x11269, 0x1126a, 0x1126b, 0x11368, 0x11369,
+			0x1136a, 0x1136b, 0x11468, 0x11469, 0x1146a,
+			0x1146b, 0x11568, 0x11569, 0x1156a, 0x1156b,
+			0x11668, 0x11669, 0x1166a, 0x1166b, 0x11768,
+			0x11769, 0x1176a, 0x1176b, 0x11868, 0x11869,
+			0x1186a, 0x1186b, 0x12068, 0x12069, 0x1206a,
+			0x1206b, 0x12168, 0x12169, 0x1216a, 0x1216b,
+			0x12268, 0x12269, 0x1226a, 0x1226b, 0x12368,
+			0x12369, 0x1236a, 0x1236b, 0x12468, 0x12469,
+			0x1246a, 0x1246b, 0x12568, 0x12569, 0x1256a,
+			0x1256b, 0x12668, 0x12669, 0x1266a, 0x1266b,
+			0x12768, 0x12769, 0x1276a, 0x1276b, 0x12868,
+			0x12869, 0x1286a, 0x1286b, 0x13068, 0x13069,
+			0x1306a, 0x1306b, 0x13168, 0x13169, 0x1316a,
+			0x1316b, 0x13268, 0x13269, 0x1326a, 0x1326b,
+			0x13368, 0x13369, 0x1336a, 0x1336b, 0x13468,
+			0x13469, 0x1346a, 0x1346b, 0x13568, 0x13569,
+			0x1356a, 0x1356b, 0x13668, 0x13669, 0x1366a,
+			0x1366b, 0x13768, 0x13769, 0x1376a, 0x1376b,
+			0x13868, 0x13869, 0x1386a, 0x1386b, 0x14068,
+			0x14069, 0x1406a, 0x1406b, 0x14168, 0x14169,
+			0x1416a, 0x1416b, 0x14268, 0x14269, 0x1426a,
+			0x1426b, 0x14368, 0x14369, 0x1436a, 0x1436b,
+			0x14468, 0x14469, 0x1446a, 0x1446b, 0x14568,
+			0x14569, 0x1456a, 0x1456b, 0x14668, 0x14669,
+			0x1466a, 0x1466b, 0x14768, 0x14769, 0x1476a,
+			0x1476b, 0x14868, 0x14869, 0x1486a, 0x1486b,
+			0x15068, 0x15069, 0x1506a, 0x1506b, 0x15168,
+			0x15169, 0x1516a, 0x1516b, 0x15268, 0x15269,
+			0x1526a, 0x1526b, 0x15368, 0x15369, 0x1536a,
+			0x1536b, 0x15468, 0x15469, 0x1546a, 0x1546b,
+			0x15568, 0x15569, 0x1556a, 0x1556b, 0x15668,
+			0x15669, 0x1566a, 0x1566b, 0x15768, 0x15769,
+			0x1576a, 0x1576b, 0x15868, 0x15869, 0x1586a,
+			0x1586b, 0x16068, 0x16069, 0x1606a, 0x1606b,
+			0x16168, 0x16169, 0x1616a, 0x1616b, 0x16268,
+			0x16269, 0x1626a, 0x1626b, 0x16368, 0x16369,
+			0x1636a, 0x1636b, 0x16468, 0x16469, 0x1646a,
+			0x1646b, 0x16568, 0x16569, 0x1656a, 0x1656b,
+			0x16668, 0x16669, 0x1666a, 0x1666b, 0x16768,
+			0x16769, 0x1676a, 0x1676b, 0x16868, 0x16869,
+			0x1686a, 0x1686b, 0x17068, 0x17069, 0x1706a,
+			0x1706b, 0x17168, 0x17169, 0x1716a, 0x1716b,
+			0x17268, 0x17269, 0x1726a, 0x1726b, 0x17368,
+			0x17369, 0x1736a, 0x1736b, 0x17468, 0x17469,
+			0x1746a, 0x1746b, 0x17568, 0x17569, 0x1756a,
+			0x1756b, 0x17668, 0x17669, 0x1766a, 0x1766b,
+			0x17768, 0x17769, 0x1776a, 0x1776b, 0x17868,
+			0x17869, 0x1786a, 0x1786b, 0x18068, 0x18069,
+			0x1806a, 0x1806b, 0x18168, 0x18169, 0x1816a,
+			0x1816b, 0x18268, 0x18269, 0x1826a, 0x1826b,
+			0x18368, 0x18369, 0x1836a, 0x1836b, 0x18468,
+			0x18469, 0x1846a, 0x1846b, 0x18568, 0x18569,
+			0x1856a, 0x1856b, 0x18668, 0x18669, 0x1866a,
+			0x1866b, 0x18768, 0x18769, 0x1876a, 0x1876b,
+			0x18868, 0x18869, 0x1886a, 0x1886b, 0x00080,
+			0x01080, 0x02080, 0x03080, 0x04080, 0x05080,
+			0x06080, 0x07080, 0x08080, 0x09080, 0x10020,
+			0x10080, 0x10081, 0x10082, 0x10083, 0x100d0,
+			0x100d1, 0x100d2, 0x100d3, 0x1008c, 0x1008d,
+			0x1008e, 0x1008f, 0x10180, 0x10181, 0x10182,
+			0x10183, 0x101d0, 0x101d1, 0x101d2, 0x101d3,
+			0x1018c, 0x1018d, 0x1018e, 0x1018f, 0x100c0,
+			0x100c1, 0x100c2, 0x100c3, 0x101c0, 0x101c1,
+			0x101c2, 0x101c3, 0x102c0, 0x102c1, 0x102c2,
+			0x102c3, 0x103c0, 0x103c1, 0x103c2, 0x103c3,
+			0x104c0, 0x104c1, 0x104c2, 0x104c3, 0x105c0,
+			0x105c1, 0x105c2, 0x105c3, 0x106c0, 0x106c1,
+			0x106c2, 0x106c3, 0x107c0, 0x107c1, 0x107c2,
+			0x107c3, 0x108c0, 0x108c1, 0x108c2, 0x108c3,
+			0x11020, 0x11080, 0x11081, 0x11082, 0x11083,
+			0x110d0, 0x110d1, 0x110d2, 0x110d3, 0x1108c,
+			0x1108d, 0x1108e, 0x1108f, 0x11180, 0x11181,
+			0x11182, 0x11183, 0x111d0, 0x111d1, 0x111d2,
+			0x111d3, 0x1118c, 0x1118d, 0x1118e, 0x1118f,
+			0x110c0, 0x110c1, 0x110c2, 0x110c3, 0x111c0,
+			0x111c1, 0x111c2, 0x111c3, 0x112c0, 0x112c1,
+			0x112c2, 0x112c3, 0x113c0, 0x113c1, 0x113c2,
+			0x113c3, 0x114c0, 0x114c1, 0x114c2, 0x114c3,
+			0x115c0, 0x115c1, 0x115c2, 0x115c3, 0x116c0,
+			0x116c1, 0x116c2, 0x116c3, 0x117c0, 0x117c1,
+			0x117c2, 0x117c3, 0x118c0, 0x118c1, 0x118c2,
+			0x118c3, 0x12020, 0x12080, 0x12081, 0x12082,
+			0x12083, 0x120d0, 0x120d1, 0x120d2, 0x120d3,
+			0x1208c, 0x1208d, 0x1208e, 0x1208f, 0x12180,
+			0x12181, 0x12182, 0x12183, 0x121d0, 0x121d1,
+			0x121d2, 0x121d3, 0x1218c, 0x1218d, 0x1218e,
+			0x1218f, 0x120c0, 0x120c1, 0x120c2, 0x120c3,
+			0x121c0, 0x121c1, 0x121c2, 0x121c3, 0x122c0,
+			0x122c1, 0x122c2, 0x122c3, 0x123c0, 0x123c1,
+			0x123c2, 0x123c3, 0x124c0, 0x124c1, 0x124c2,
+			0x124c3, 0x125c0, 0x125c1, 0x125c2, 0x125c3,
+			0x126c0, 0x126c1, 0x126c2, 0x126c3, 0x127c0,
+			0x127c1, 0x127c2, 0x127c3, 0x128c0, 0x128c1,
+			0x128c2, 0x128c3, 0x13020, 0x13080, 0x13081,
+			0x13082, 0x13083, 0x130d0, 0x130d1, 0x130d2,
+			0x130d3, 0x1308c, 0x1308d, 0x1308e, 0x1308f,
+			0x13180, 0x13181, 0x13182, 0x13183, 0x131d0,
+			0x131d1, 0x131d2, 0x131d3, 0x1318c, 0x1318d,
+			0x1318e, 0x1318f, 0x130c0, 0x130c1, 0x130c2,
+			0x130c3, 0x131c0, 0x131c1, 0x131c2, 0x131c3,
+			0x132c0, 0x132c1, 0x132c2, 0x132c3, 0x133c0,
+			0x133c1, 0x133c2, 0x133c3, 0x134c0, 0x134c1,
+			0x134c2, 0x134c3, 0x135c0, 0x135c1, 0x135c2,
+			0x135c3, 0x136c0, 0x136c1, 0x136c2, 0x136c3,
+			0x137c0, 0x137c1, 0x137c2, 0x137c3, 0x138c0,
+			0x138c1, 0x138c2, 0x138c3, 0x14020, 0x14080,
+			0x14081, 0x14082, 0x14083, 0x140d0, 0x140d1,
+			0x140d2, 0x140d3, 0x1408c, 0x1408d, 0x1408e,
+			0x1408f, 0x14180, 0x14181, 0x14182, 0x14183,
+			0x141d0, 0x141d1, 0x141d2, 0x141d3, 0x1418c,
+			0x1418d, 0x1418e, 0x1418f, 0x140c0, 0x140c1,
+			0x140c2, 0x140c3, 0x141c0, 0x141c1, 0x141c2,
+			0x141c3, 0x142c0, 0x142c1, 0x142c2, 0x142c3,
+			0x143c0, 0x143c1, 0x143c2, 0x143c3, 0x144c0,
+			0x144c1, 0x144c2, 0x144c3, 0x145c0, 0x145c1,
+			0x145c2, 0x145c3, 0x146c0, 0x146c1, 0x146c2,
+			0x146c3, 0x147c0, 0x147c1, 0x147c2, 0x147c3,
+			0x148c0, 0x148c1, 0x148c2, 0x148c3, 0x15020,
+			0x15080, 0x15081, 0x15082, 0x15083, 0x150d0,
+			0x150d1, 0x150d2, 0x150d3, 0x1508c, 0x1508d,
+			0x1508e, 0x1508f, 0x15180, 0x15181, 0x15182,
+			0x15183, 0x151d0, 0x151d1, 0x151d2, 0x151d3,
+			0x1518c, 0x1518d, 0x1518e, 0x1518f, 0x150c0,
+			0x150c1, 0x150c2, 0x150c3, 0x151c0, 0x151c1,
+			0x151c2, 0x151c3, 0x152c0, 0x152c1, 0x152c2,
+			0x152c3, 0x153c0, 0x153c1, 0x153c2, 0x153c3,
+			0x154c0, 0x154c1, 0x154c2, 0x154c3, 0x155c0,
+			0x155c1, 0x155c2, 0x155c3, 0x156c0, 0x156c1,
+			0x156c2, 0x156c3, 0x157c0, 0x157c1, 0x157c2,
+			0x157c3, 0x158c0, 0x158c1, 0x158c2, 0x158c3,
+			0x16020, 0x16080, 0x16081, 0x16082, 0x16083,
+			0x160d0, 0x160d1, 0x160d2, 0x160d3, 0x1608c,
+			0x1608d, 0x1608e, 0x1608f, 0x16180, 0x16181,
+			0x16182, 0x16183, 0x161d0, 0x161d1, 0x161d2,
+			0x161d3, 0x1618c, 0x1618d, 0x1618e, 0x1618f,
+			0x160c0, 0x160c1, 0x160c2, 0x160c3, 0x161c0,
+			0x161c1, 0x161c2, 0x161c3, 0x162c0, 0x162c1,
+			0x162c2, 0x162c3, 0x163c0, 0x163c1, 0x163c2,
+			0x163c3, 0x164c0, 0x164c1, 0x164c2, 0x164c3,
+			0x165c0, 0x165c1, 0x165c2, 0x165c3, 0x166c0,
+			0x166c1, 0x166c2, 0x166c3, 0x167c0, 0x167c1,
+			0x167c2, 0x167c3, 0x168c0, 0x168c1, 0x168c2,
+			0x168c3, 0x17020, 0x17080, 0x17081, 0x17082,
+			0x17083, 0x170d0, 0x170d1, 0x170d2, 0x170d3,
+			0x1708c, 0x1708d, 0x1708e, 0x1708f, 0x17180,
+			0x17181, 0x17182, 0x17183, 0x171d0, 0x171d1,
+			0x171d2, 0x171d3, 0x1718c, 0x1718d, 0x1718e,
+			0x1718f, 0x170c0, 0x170c1, 0x170c2, 0x170c3,
+			0x171c0, 0x171c1, 0x171c2, 0x171c3, 0x172c0,
+			0x172c1, 0x172c2, 0x172c3, 0x173c0, 0x173c1,
+			0x173c2, 0x173c3, 0x174c0, 0x174c1, 0x174c2,
+			0x174c3, 0x175c0, 0x175c1, 0x175c2, 0x175c3,
+			0x176c0, 0x176c1, 0x176c2, 0x176c3, 0x177c0,
+			0x177c1, 0x177c2, 0x177c3, 0x178c0, 0x178c1,
+			0x178c2, 0x178c3, 0x18020, 0x18080, 0x18081,
+			0x18082, 0x18083, 0x180d0, 0x180d1, 0x180d2,
+			0x180d3, 0x1808c, 0x1808d, 0x1808e, 0x1808f,
+			0x18180, 0x18181, 0x18182, 0x18183, 0x181d0,
+			0x181d1, 0x181d2, 0x181d3, 0x1818c, 0x1818d,
+			0x1818e, 0x1818f, 0x180c0, 0x180c1, 0x180c2,
+			0x180c3, 0x181c0, 0x181c1, 0x181c2, 0x181c3,
+			0x182c0, 0x182c1, 0x182c2, 0x182c3, 0x183c0,
+			0x183c1, 0x183c2, 0x183c3, 0x184c0, 0x184c1,
+			0x184c2, 0x184c3, 0x185c0, 0x185c1, 0x185c2,
+			0x185c3, 0x186c0, 0x186c1, 0x186c2, 0x186c3,
+			0x187c0, 0x187c1, 0x187c2, 0x187c3, 0x188c0,
+			0x188c1, 0x188c2, 0x188c3, 0x90201, 0x90202,
+			0x90203, 0x90204, 0x90205, 0x90206, 0x90207,
+			0x90208, 0x20077, 0x10040, 0x10030, 0x10140,
+			0x10130, 0x10240, 0x10230, 0x10340, 0x10330,
+			0x10440, 0x10430, 0x10540, 0x10530, 0x10640,
+			0x10630, 0x10740, 0x10730, 0x10840, 0x10830,
+			0x11040, 0x11030, 0x11140, 0x11130, 0x11240,
+			0x11230, 0x11340, 0x11330, 0x11440, 0x11430,
+			0x11540, 0x11530, 0x11640, 0x11630, 0x11740,
+			0x11730, 0x11840, 0x11830, 0x12040, 0x12030,
+			0x12140, 0x12130, 0x12240, 0x12230, 0x12340,
+			0x12330, 0x12440, 0x12430, 0x12540, 0x12530,
+			0x12640, 0x12630, 0x12740, 0x12730, 0x12840,
+			0x12830, 0x13040, 0x13030, 0x13140, 0x13130,
+			0x13240, 0x13230, 0x13340, 0x13330, 0x13440,
+			0x13430, 0x13540, 0x13530, 0x13640, 0x13630,
+			0x13740, 0x13730, 0x13840, 0x13830, 0x14040,
+			0x14030, 0x14140, 0x14130, 0x14240, 0x14230,
+			0x14340, 0x14330, 0x14440, 0x14430, 0x14540,
+			0x14530, 0x14640, 0x14630, 0x14740, 0x14730,
+			0x14840, 0x14830, 0x15040, 0x15030, 0x15140,
+			0x15130, 0x15240, 0x15230, 0x15340, 0x15330,
+			0x15440, 0x15430, 0x15540, 0x15530, 0x15640,
+			0x15630, 0x15740, 0x15730, 0x15840, 0x15830,
+			0x16040, 0x16030, 0x16140, 0x16130, 0x16240,
+			0x16230, 0x16340, 0x16330, 0x16440, 0x16430,
+			0x16540, 0x16530, 0x16640, 0x16630, 0x16740,
+			0x16730, 0x16840, 0x16830, 0x17040, 0x17030,
+			0x17140, 0x17130, 0x17240, 0x17230, 0x17340,
+			0x17330, 0x17440, 0x17430, 0x17540, 0x17530,
+			0x17640, 0x17630, 0x17740, 0x17730, 0x17840,
+			0x17830, 0x18040, 0x18030, 0x18140, 0x18130,
+			0x18240, 0x18230, 0x18340, 0x18330, 0x18440,
+			0x18430, 0x18540, 0x18530, 0x18640, 0x18630,
+			0x18740, 0x18730, 0x18840, 0x18830};
+	size_t phybak_num;
+	u32 *phybak_p = phybak;
+	u16 *data;
+	struct bac_cal_data_t *cal = (struct bac_cal_data_t *)
+					((uintptr_t)addr);
+
+	/* Enable access to the PHY configuration registers */
+	clrbits_le16(phy_base + DDR_PHY_APBONLY0_OFFSET,
+		     DDR_PHY_MICROCONTMUXSEL);
+
+	phybak_num = ARRAY_SIZE(phybak);
+
+	debug("%s: Total PHY backup %d registers\n", __func__,
+	      (int)phybak_num);
+
+	if (proc == STORE) {
+		debug("%s: Storing PHY bak calibration to OCRAM ...\n",
+		      __func__);
+		/* Creating header */
+		/* Write PHY magic number */
+		cal->header.header_magic = SOC64_HANDOFF_DDR_PHY_MAGIC;
+		cal->header.data_len = phybak_num * sizeof(*data);
+	} else if (proc == LOADING) {
+		debug("%s: Loading bak calibration to PHY from OCRAM ...\n",
+		      __func__);
+	}
+
+	data = &cal->data;
+	while (phybak_num) {
+		phy_ocram(phy_base, *phybak_p, data, proc);
+		phybak_p++;
+		phybak_num--;
+		data++;
+		WATCHDOG_RESET();
+	};
+
+	if (proc == STORE) {
+		/* Creating header */
+		/* Generate HASH384 from the DDR config */
+		sha384_csum_wd((u8 *)DDR_HANDOFF_IMG_ADDR,
+			       DDR_HANDOFF_IMG_LEN,
+			       cal->header.ddrconfig_hash,
+			       CHUNKSZ_PER_WD_RESET);
+
+		crc32_wd_buf((u8 *)&cal->data, cal->header.data_len,
+			     (u8 *)&cal->header.caldata_crc32,
+			     CHUNKSZ_PER_WD_RESET);
+	}
+
+	/* Isolate the APB access from internal CSRs */
+	setbits_le16(phy_base + DDR_PHY_APBONLY0_OFFSET,
+		     DDR_PHY_MICROCONTMUXSEL);
+}
+
+static bool is_ddrconfig_hash_match(const void *buffer)
+{
+	int ret;
+	u8 hash[SHA384_SUM_LEN];
+
+	/* Magic symbol in first 4 bytes of header */
+	struct cal_header_t *header = (struct cal_header_t *)buffer;
+
+	/* Generate HASH384 from the image */
+	sha384_csum_wd((u8 *)DDR_HANDOFF_IMG_ADDR, DDR_HANDOFF_IMG_LEN,
+		       hash, CHUNKSZ_PER_WD_RESET);
+
+	ret = memcmp((void *)hash, (const void *)header->ddrconfig_hash,
+		     SHA384_SUM_LEN);
+	if (ret) {
+		debug("%s: DDR config hash mismatch\n", __func__);
+		return false;
+	}
+
+	return true;
+}
+
+static ofnode get_ddr_ofnode(ofnode from)
+{
+	return ofnode_by_compatible(from, "intel,sdr-ctl-dm");
+}
+
+static const char *get_ddrcal_ddr_offset(void)
+{
+	const char *ddr_offset = NULL;
+
+	ofnode ddr_node = get_ddr_ofnode(ofnode_null());
+
+	if (ofnode_valid(ddr_node))
+		ddr_offset = ofnode_read_string(ddr_node,
+						"intel,ddrcal-ddr-offset");
+
+	return ddr_offset;
+}
+
+static const char *get_ddrcal_qspi_offset(void)
+{
+	const char *qspi_offset = NULL;
+
+	ofnode ddr_node = get_ddr_ofnode(ofnode_null());
+
+	if (ofnode_valid(ddr_node))
+		qspi_offset = ofnode_read_string(ddr_node,
+						 "intel,ddrcal-qspi-offset");
+
+	return qspi_offset;
+}
+
+static bool is_cal_bak_data_valid(void)
+{
+	int ret, size;
+	struct udevice *dev;
+	ofnode node;
+	const fdt32_t *phandle_p;
+	u32 phandle, crc32;
+	const char *qspi_offset = get_ddrcal_qspi_offset();
+	struct bac_cal_data_t *cal = (struct bac_cal_data_t *)((uintptr_t)
+					SOC64_OCRAM_PHY_BACKUP_BASE);
+
+	node = get_ddr_ofnode(ofnode_null());
+
+	if (ofnode_valid(node)) {
+		phandle_p = ofnode_get_property(node, "firmware-loader",
+						&size);
+		if (!phandle_p) {
+			node = ofnode_path("/chosen");
+			if (!ofnode_valid(node)) {
+				debug("%s: /chosen node was not found.\n",
+				      __func__);
+				return false;
+			}
+
+			phandle_p = ofnode_get_property(node,
+							"firmware-loader",
+							&size);
+			if (!phandle_p) {
+				debug("%s: firmware-loader property ",
+				      __func__);
+				debug("was not found.\n");
+				return false;
+			}
+		}
+	} else {
+		debug("%s: DDR node was not found.\n", __func__);
+		return false;
+	}
+
+	phandle = fdt32_to_cpu(*phandle_p);
+	ret = uclass_get_device_by_phandle_id(UCLASS_FS_FIRMWARE_LOADER,
+					      phandle, &dev);
+	if (ret)
+		return false;
+
+	/* Load DDR bak cal header into OCRAM buffer */
+	ret = request_firmware_into_buf(dev,
+					qspi_offset,
+					(void *)SOC64_OCRAM_PHY_BACKUP_BASE,
+					sizeof(struct cal_header_t), 0);
+	if (ret < 0) {
+		debug("%s: Failed to read bak cal header from flash.\n",
+		      __func__);
+		return false;
+	}
+
+	if (cal->header.header_magic != SOC64_HANDOFF_DDR_PHY_MAGIC) {
+		debug("%s: No magic found in cal backup header\n", __func__);
+		return false;
+	}
+
+	/* Load header + DDR bak cal into OCRAM buffer */
+	ret = request_firmware_into_buf(dev,
+					qspi_offset,
+					(void *)SOC64_OCRAM_PHY_BACKUP_BASE,
+					cal->header.data_len +
+					sizeof(struct cal_header_t),
+					0);
+	if (ret < 0) {
+		debug("FPGA: Failed to read DDR bak cal data from flash.\n");
+		return false;
+	}
+
+	crc32_wd_buf((u8 *)&cal->data, cal->header.data_len,
+		     (u8 *)&crc32, CHUNKSZ_PER_WD_RESET);
+	debug("%s: crc32 %x for bak calibration data from QSPI\n", __func__,
+	      crc32);
+	if (crc32 != cal->header.caldata_crc32) {
+		debug("%s: CRC32 mismatch for calibration backup data\n",
+		      __func__);
+		return false;
+	}
+
+	if (!is_ddrconfig_hash_match((const void *)
+		SOC64_OCRAM_PHY_BACKUP_BASE)) {
+		debug("%s: HASH mismatch for DDR config data\n", __func__);
+		return false;
+	}
+
+	return true;
+}
+
 static int init_phy(struct ddr_handoff *ddr_handoff_info)
 {
+	u32 handoff_table[ddr_handoff_info->phy_handoff_length];
+	u32 i, value;
+	u32 reg = readl(socfpga_get_sysmgr_addr() +
+			SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
 	int ret;
 
 	printf("Initializing DDR PHY ...\n");
@@ -950,6 +1582,31 @@  static int init_phy(struct ddr_handoff *ddr_handoff_info)
 	handoff_process(ddr_handoff_info, ddr_handoff_info->phy_handoff_base,
 			ddr_handoff_info->phy_handoff_length,
 			ddr_handoff_info->phy_base);
+	if (is_ddr_calibration_skipped(reg)) {
+		if (is_cal_bak_data_valid())
+			*need_calibrate = false;
+		else
+			*need_calibrate = true;
+	}
+
+	debug("%s: Need calibrate %d\n", __func__, *need_calibrate);
+
+	if (*need_calibrate) {
+		/* Execute PHY configuration handoff */
+		handoff_process(ddr_handoff_info, ddr_handoff_info->phy_handoff_base,
+			ddr_handoff_info->phy_handoff_length,
+			ddr_handoff_info->phy_base);
+	} else {
+		cal_data_ocram(ddr_handoff_info->phy_base,
+			       SOC64_OCRAM_PHY_BACKUP_BASE, LOADING);
+
+		/*
+		 * Invalidate the section used for processing the PHY
+		 * backup calibration data
+		 */
+		writel(SOC64_CRAM_PHY_BACKUP_SKIP_MAGIC,
+		       SOC64_OCRAM_PHY_BACKUP_BASE);
+	}
 
 	printf("DDR PHY configuration is completed\n");
 
@@ -1161,6 +1818,14 @@  int populate_ddr_handoff(struct ddr_handoff *handoff)
 			debug("user setting data\n");
 			return -ENOEXEC;
 		}
+
+		/* 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;
+		}
 	} else {
 		debug("%s: Wrong format for DDR handoff, expect PHY",
 		      __func__);
@@ -1334,7 +1999,8 @@  static int ddr_trigger_sdram_init(phys_addr_t umctl2_base,
 }
 
 static int ddr_post_handoff_config(phys_addr_t umctl2_base,
-				   enum ddr_type umctl2_type)
+				    enum ddr_type umctl2_type,
+				    bool *need_calibrate)
 {
 	int ret = 0;
 	u32 value;
@@ -1364,11 +2030,13 @@  static int ddr_post_handoff_config(phys_addr_t umctl2_base,
 
 	/* Checking ECC is enabled? */
 	value = readl(umctl2_base + DDR4_ECCCFG0_OFFSET) & DDR4_ECC_MODE;
-	if (value) {
+	if (value)
 		printf("ECC is enabled\n");
+
+	if (value && *need_calibrate) {
 		ret = scrubber_ddr_config(umctl2_base, umctl2_type);
 		if (ret)
-			printf("Failed to enable ECC\n");
+			printf("Failed to enable scrubber\n");
 	}
 
 	return ret;
@@ -2064,18 +2732,19 @@  static int trigger_sdram_init(struct ddr_handoff *handoff)
 	return ret;
 }
 
-static int ddr_post_config(struct ddr_handoff *handoff)
+static int ddr_post_config(struct ddr_handoff *handoff, bool *need_calibrate)
 {
 	int ret;
 
-	ret = ddr_post_handoff_config(handoff->cntlr_base,
-				      handoff->cntlr_t);
+	ret = ddr_post_handoff_config(handoff->cntlr_base, handoff->cntlr_t,
+				      need_calibrate);
 	if (ret)
 		return ret;
 
 	if (handoff->cntlr2_t == DDRTYPE_LPDDR4_1)
 		ret = ddr_post_handoff_config(handoff->cntlr2_base,
-					      handoff->cntlr2_t);
+					      handoff->cntlr2_t,
+					      need_calibrate);
 
 	return ret;
 }
@@ -2149,7 +2818,13 @@  bool is_ddr_init(void)
 int sdram_mmr_init_full(struct udevice *dev)
 {
 	u32 user_backup[2], user_backup_2nd[2];
+	u32 reg = readl(socfpga_get_sysmgr_addr() +
+			SYSMGR_SOC64_BOOT_SCRATCH_COLD0);
 	int ret;
+	bool need_calibrate = true;
+	ulong ddr_offset;
+	char *endptr;
+	const char *offset = get_ddrcal_ddr_offset();
 	struct bd_info bd;
 	struct ddr_handoff ddr_handoff_info;
 	struct altera_sdram_priv *priv = dev_get_priv(dev);
@@ -2165,13 +2840,19 @@  int sdram_mmr_init_full(struct udevice *dev)
 	/* Set the MPFE NoC mux to correct DDR controller type */
 	use_ddr4(ddr_handoff_info.cntlr_t);
 
-	if (is_ddr_init()) {
+	/*
+	 * Invalidate the section used for processing the PHY backup
+	 * calibration data
+	 */
+	writel(SOC64_CRAM_PHY_BACKUP_SKIP_MAGIC, SOC64_OCRAM_PHY_BACKUP_BASE);
+
+	if (!is_ddr_init_skipped(reg)) {
 		printf("SDRAM init in progress ...\n");
 
 		/*
-		 * Polling reset complete, must be high to ensure DDR subsystem
-		 * in complete reset state before init DDR clock and DDR
-		 * controller
+		 * Polling reset complete, must be high to ensure DDR
+		 * subsystem in complete reset state before init DDR clock
+		 * and DDR controller
 		 */
 		ret = wait_for_bit_le32((const void *)((uintptr_t)(readl
 					(ddr_handoff_info.mem_reset_base) +
@@ -2206,7 +2887,7 @@  int sdram_mmr_init_full(struct udevice *dev)
 		printf("DDR controller configuration is completed\n");
 
 		/* Initialize DDR PHY */
-		ret = init_phy(&ddr_handoff_info);
+		ret = init_phy(&ddr_handoff_info, &need_calibrate);
 		if (ret) {
 			debug("%s: Failed to inilialize DDR PHY\n", __func__);
 			return ret;
@@ -2214,21 +2895,43 @@  int sdram_mmr_init_full(struct udevice *dev)
 
 		enable_phy_clk_for_csr_access(&ddr_handoff_info, true);
 
-		ret = start_ddr_calibration(&ddr_handoff_info);
-		if (ret) {
-			debug("%s: Failed to calibrate DDR\n", __func__);
-			return ret;
-		}
+		if (need_calibrate) {
+			ret = start_ddr_calibration(&ddr_handoff_info);
+			if (ret) {
+				debug("%s: Failed to calibrate DDR\n",
+				      __func__);
+				return ret;
+			}
 
-		enable_phy_clk_for_csr_access(&ddr_handoff_info, false);
+		/* DDR freq set to support DDR4-3200 */
+			phy_init_engine(&ddr_handoff_info);
 
-		/* Reset ARC processor when no using for security purpose */
+			/*
+			 * Backup calibration data to OCRAM first, these data
+			 * might be permanant stored to flash in later
+			 */
+			if (is_ddr_retention_enabled(reg))
+				cal_data_ocram(ddr_handoff_info.phy_base,
+					       SOC64_OCRAM_PHY_BACKUP_BASE,
+					       STORE);
+
+		} else {
+			/* Updating training result to DDR controller */
+			ret = update_training_result(&ddr_handoff_info);
+			if (ret)
+				return ret;
+		}
+
+		/*
+		 * Reset ARC processor when no using for security
+		 * purpose
+		 */
 		setbits_le16(ddr_handoff_info.phy_base +
 			     DDR_PHY_MICRORESET_OFFSET,
 			     DDR_PHY_MICRORESET_RESET);
 
 		/* DDR freq set to support DDR4-3200 */
-		phy_init_engine(&ddr_handoff_info);
+		enable_phy_clk_for_csr_access(&ddr_handoff_info, false);
 
 		ret = dfi_init(&ddr_handoff_info);
 		if (ret)
@@ -2242,7 +2945,7 @@  int sdram_mmr_init_full(struct udevice *dev)
 		if (ret)
 			return ret;
 
-		ret = ddr_post_config(&ddr_handoff_info);
+		ret = ddr_post_config(&ddr_handoff_info, &need_calibrate);
 		if (ret)
 			return ret;
 
@@ -2291,8 +2994,12 @@  int sdram_mmr_init_full(struct udevice *dev)
 	priv->info.size = gd->ram_size;
 
 	sdram_size_check(&bd);
-
 	sdram_set_firewall(&bd);
 
+	ddr_offset = simple_strtoul(offset, &endptr, 16);
+	if (!(offset == endptr || *endptr != '\0'))
+		memcpy((void *)ddr_offset,
+		       (const void *)SOC64_OCRAM_PHY_BACKUP_BASE, SZ_4K);
+
 	return 0;
 }