feat(imx8ulp): ddrc switch auto low power and software interface

Enable switch between DDRC Auto low power and software/hardware
control modes DDRC Auto low-power mode is used when system is
active, software/hardware control mode is used when going into
suspend. Enable switching between Auto mode and SW/HW mode in
enter/exit retention routines.

Set LPI_SRPD_LONG_MCCLK_GATE_WAKEUP_F2 Max setting to allow
LPDDR_EN_CLKGATE reload LPI_SRPD_LONG_MCCLK_GATE_WAKEUP_F2 to
exit retention mode

Signed-off-by: Pascal Mareau <pascal.mareau@nxp.com>
Signed-off-by: Adrian Alonso <adrian.alonso@nxp.com>
Signed-off-by: Hongting Ting <hongting.dong@nxp.com>
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
Change-Id: I3c4b6f7bc6ca02649ff27cd3d9a0c50dab3a3ad0
diff --git a/plat/imx/imx8ulp/dram.c b/plat/imx/imx8ulp/dram.c
index 8a8ef44..d8738ae 100644
--- a/plat/imx/imx8ulp/dram.c
+++ b/plat/imx/imx8ulp/dram.c
@@ -25,13 +25,22 @@
 
 #define IMX_DDRC_BASE			U(0x2E060000)
 #define SAVED_DRAM_DATA_BASE		U(0x20055000)
-#define DENALI_CTL_144			0x240
+#define IMX_DRAM_BASE			U(0x80000000)
+#define DENALI_CTL_143			U(0x23C)
+#define DENALI_CTL_144			U(0x240)
+#define DENALI_CTL_146			U(0x248)
+#define LP_STATE_CS_IDLE		U(0x404000)
+#define LP_STATE_CS_PD_CG		U(0x4F4F00)
 #define LPI_WAKEUP_EN_SHIFT		U(8)
 #define IMX_LPAV_SIM_BASE		0x2DA50000
 #define LPDDR_CTRL			0x14
 #define LPDDR_AUTO_LP_MODE_DISABLE	BIT(24)
 #define SOC_LP_CMD_SHIFT		U(15)
 #define LPDDR_CTRL2			0x18
+#define LPDDR_EN_CLKGATE		(0x1<<17)
+#define LPDDR_MAX_CLKDIV_EN		(0x1 << 16)
+#define LP_AUTO_ENTRY_EN		0x4
+#define LP_AUTO_EXIT_EN			0xF
 
 #define DENALI_CTL_00			U(0x0)
 #define DENALI_CTL_23			U(0x5c)
@@ -106,6 +115,9 @@
 	/* phy freq2 config */
 	struct dram_cfg_param *phy_f2_cfg;
 	unsigned int phy_f2_cfg_num;
+	/* automatic low power config */
+	struct dram_cfg_param *auto_lp_cfg;
+	unsigned int auto_lp_cfg_num;
 	/* initialized drate table */
 	unsigned int fsp_table[3];
 };
@@ -114,11 +126,13 @@
 #define PI_NUM		U(298)
 #define PHY_NUM		U(1654)
 #define PHY_DIFF_NUM	U(49)
+#define AUTO_LP_NUM	U(3)
 struct dram_cfg {
 	uint32_t ctl_cfg[CTL_NUM];
 	uint32_t pi_cfg[PI_NUM];
 	uint32_t phy_full[PHY_NUM];
 	uint32_t phy_diff[PHY_DIFF_NUM];
+	uint32_t auto_lp_cfg[AUTO_LP_NUM];
 };
 
 struct dram_timing_info *info;
@@ -126,7 +140,8 @@
 
 /* mark if dram cfg is already saved */
 static bool dram_cfg_saved;
-static uint32_t dram_class;
+static bool dram_auto_lp_true;
+static uint32_t dram_class, dram_ctl_143;
 
 /* PHY register index for frequency diff */
 uint32_t freq_specific_reg_array[PHY_DIFF_NUM] = {
@@ -198,10 +213,115 @@
 	mmio_write_32(IMX_DDRC_BASE + DENALI_PHY_1537, PHY_FREQ_MULTICAST_EN(1));
 }
 
+void dram_lp_auto_disable(void)
+{
+	uint32_t lp_auto_en;
+
+	dram_timing_cfg = (struct dram_cfg *)(SAVED_DRAM_DATA_BASE +
+					      sizeof(struct dram_timing_info));
+	lp_auto_en = (mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) & (LP_AUTO_ENTRY_EN << 24));
+	/* Save initial config */
+	dram_ctl_143 = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_143);
+
+	if (lp_auto_en && !dram_auto_lp_true) {
+		/* 0.a Save DDRC auto low-power mode parameter */
+		dram_timing_cfg->auto_lp_cfg[0] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_144);
+		dram_timing_cfg->auto_lp_cfg[1] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_147);
+		dram_timing_cfg->auto_lp_cfg[2] = mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146);
+		/* Set LPI_SRPD_LONG_MCCLK_GATE_WAKEUP_F2 to Maximum */
+		mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_143, 0xF << 24);
+		/* 0.b Disable DDRC auto low-power mode interface */
+		mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_146, LP_AUTO_ENTRY_EN << 24);
+		/* 0.c Read any location to get DRAM out of Self-refresh */
+		mmio_read_32(IMX_DRAM_BASE);
+		/* 0.d Confirm DRAM is out of Self-refresh */
+		while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) &
+			LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) {
+			;
+		}
+		/* 0.e Disable DDRC auto low-power exit */
+		mmio_clrbits_32(IMX_DDRC_BASE + DENALI_CTL_147, LP_AUTO_EXIT_EN);
+		/* dram low power mode flag */
+		dram_auto_lp_true = true;
+	}
+}
+
+void dram_lp_auto_enable(void)
+{
+	/* Switch back to Auto Low-power mode */
+	if (dram_auto_lp_true) {
+		/* 12.a Confirm DRAM is out of Self-refresh */
+		while ((mmio_read_32(IMX_DDRC_BASE + DENALI_CTL_146) &
+			LP_STATE_CS_PD_CG) != LP_STATE_CS_IDLE) {
+			;
+		}
+		/* 12.b Enable DDRC auto low-power exit */
+		/*
+		 * 12.c TBC! : Set DENALI_CTL_144 [LPI_CTRL_REQ_EN[24]] and
+		 * [DFI_LP_VERSION[16]] back to default settings = 1b'1.
+		 */
+		/*
+		 * 12.d Reconfigure DENALI_CTL_144 [LPI_WAKEUP_EN[5:0]] bit
+		 * LPI_WAKEUP_EN[3] = 1b'1.
+		 */
+		mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_144, dram_timing_cfg->auto_lp_cfg[0]);
+		mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_147, dram_timing_cfg->auto_lp_cfg[1]);
+		/* 12.e Re-enable DDRC auto low-power mode interface */
+		mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_146, dram_timing_cfg->auto_lp_cfg[2]);
+		/* restore ctl config */
+		mmio_write_32(IMX_DDRC_BASE + DENALI_CTL_143, dram_ctl_143);
+		/* dram low power mode flag */
+		dram_auto_lp_true = false;
+	}
+}
+
+void dram_enter_self_refresh(void)
+{
+	/* disable auto low power interface */
+	dram_lp_auto_disable();
+	/* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */
+	mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22);
+	/* 1.a Clock gate PCC_LPDDR4[CGC] and no software reset PCC_LPDDR4[SWRST] */
+	mmio_setbits_32(IMX_PCC5_BASE + 0x108, (BIT(30) | BIT(28)));
+
+	/*
+	 * 2. Make sure the DENALI_CTL_144[LPI_WAKEUP_EN[5:0]] has the bit
+	 * LPI_WAKEUP_EN[3] = 1b'1. This enables the option 'self-refresh
+	 * long with mem and ctlr clk gating or self-refresh power-down long
+	 * with mem and ctlr clk gating'
+	 */
+	mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(3) << LPI_WAKEUP_EN_SHIFT);
+	/* TODO: Needed ? 2.a DENALI_CTL_144[LPI_TIMER_WAKEUP_F2] */
+	//mmio_setbits_32(IMX_DDRC_BASE + DENALI_CTL_144, BIT(0));
+
+	/*
+	 * 3a. Config SIM_LPAV LPDDR_CTRL[LPDDR_AUTO_LP_MODE_DISABLE] to 1b'0(enable
+	 * the logic to automatic handles low power entry/exit. This is the recommended
+	 * option over handling through software.
+	 * 3b. Config the SIM_LPAV LPDDR_CTRL[SOC_LP_CMD] to 6b'101001(encoding for
+	 * self_refresh with both DDR controller and DRAM clock gate. THis is mandatory
+	 * since LPPDR logic will be power gated).
+	 */
+	mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL, LPDDR_AUTO_LP_MODE_DISABLE);
+	mmio_clrsetbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL,
+			   0x3f << SOC_LP_CMD_SHIFT, 0x29 << SOC_LP_CMD_SHIFT);
+	/* 3.c clock gate ddr controller */
+	mmio_setbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_EN_CLKGATE);
+	/* 3.d lpddr max clk div en */
+	mmio_clrbits_32(IMX_LPAV_SIM_BASE + LPDDR_CTRL2, LPDDR_MAX_CLKDIV_EN);
+}
+
+void dram_exit_self_refresh(void)
+{
+	dram_lp_auto_enable();
+}
+
 void dram_enter_retention(void)
 {
 	unsigned int i;
 
+	dram_lp_auto_disable();
+
 	/* 1. config the PCC_LPDDR4[SSADO] to 2b'11 for ACK domain 0/1's STOP */
 	mmio_setbits_32(IMX_PCC5_BASE + 0x108, 0x2 << 22);
 
@@ -438,6 +558,8 @@
 			;
 		}
 	}
+
+	dram_lp_auto_enable();
 }
 
 #define LPDDR_DONE       (0x1<<4)
@@ -446,8 +568,6 @@
 #define LPI_WAKEUP_EN    (0x4<<8)
 #define SOC_FREQ_REQ     (0x1<<11)
 
-#define LPDDR_EN_CLKGATE (0x1<<17)
-
 static void set_cgc2_ddrclk(uint8_t src, uint8_t div)
 {
 
diff --git a/plat/imx/imx8ulp/imx8ulp_bl31_setup.c b/plat/imx/imx8ulp/imx8ulp_bl31_setup.c
index a89a110..696f4b6 100644
--- a/plat/imx/imx8ulp/imx8ulp_bl31_setup.c
+++ b/plat/imx/imx8ulp/imx8ulp_bl31_setup.c
@@ -39,8 +39,8 @@
 #define TRUSTY_PARAMS_LEN_BYTES      (4096*2)
 
 static const mmap_region_t imx_mmap[] = {
-	DEVICE0_MAP, DEVICE1_MAP, ELE_MAP,
-	SEC_SIM_MAP, SRAM0_MAP,
+	DEVICE0_MAP, DEVICE1_MAP, DEVICE2_MAP,
+	ELE_MAP, SEC_SIM_MAP, SRAM0_MAP,
 	{0}
 };
 
diff --git a/plat/imx/imx8ulp/include/platform_def.h b/plat/imx/imx8ulp/include/platform_def.h
index 7153726..31d4791 100644
--- a/plat/imx/imx8ulp/include/platform_def.h
+++ b/plat/imx/imx8ulp/include/platform_def.h
@@ -52,6 +52,8 @@
 #define DEVICE0_SIZE			U(0x10000000)
 #define DEVICE1_BASE			U(0x30000000)
 #define DEVICE1_SIZE			U(0x10000000)
+#define DEVICE2_BASE			U(0x80000000)
+#define DEVICE2_SIZE			U(0x01000000)
 #define IMX_LPUART4_BASE		U(0x29390000)
 #define IMX_LPUART5_BASE		U(0x293a0000)
 #define IMX_LPUART_BASE			IMX_LPUART5_BASE
@@ -106,6 +108,8 @@
 /* system memory map define */
 #define DEVICE0_MAP	MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW)
 #define DEVICE1_MAP	MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW)
+/* Map partial DRAM space for DRAM low-power mode control */
+#define DEVICE2_MAP	MAP_REGION_FLAT(DEVICE2_BASE, DEVICE2_SIZE, MT_DEVICE | MT_RW)
  /* MU and FSB */
 #define ELE_MAP		MAP_REGION_FLAT(0x27010000, 0x20000, MT_DEVICE | MT_RW | MT_NS)
 #define SEC_SIM_MAP	MAP_REGION_FLAT(0x2802B000, 0x1000, MT_DEVICE | MT_RW | MT_NS) /* SEC SIM */