summaryrefslogtreecommitdiff
path: root/plat/imx/common/imx8m/ddr4_retention.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/imx/common/imx8m/ddr4_retention.c')
-rw-r--r--plat/imx/common/imx8m/ddr4_retention.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/plat/imx/common/imx8m/ddr4_retention.c b/plat/imx/common/imx8m/ddr4_retention.c
new file mode 100644
index 00000000..509b4a3e
--- /dev/null
+++ b/plat/imx/common/imx8m/ddr4_retention.c
@@ -0,0 +1,185 @@
+/*
+ * copyright 2018 nxp
+ *
+ * spdx-license-identifier: bsd-3-clause
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <debug.h>
+#include <stdbool.h>
+#include <ddrc.h>
+#include <dram.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <soc.h>
+
+#define SRC_IPS_BASE_ADDR IMX_SRC_BASE
+#define SRC_DDRC_RCR_ADDR (SRC_IPS_BASE_ADDR + 0x1000)
+
+#define GPC_PU_PWRHSK (IMX_GPC_BASE + 0x01FC)
+#define CCM_SRC_CTRL_OFFSET (IMX_CCM_BASE + 0x800)
+#define CCM_CCGR_OFFSET (IMX_CCM_BASE + 0x4000)
+#define CCM_SRC_CTRL(n) (CCM_SRC_CTRL_OFFSET + 0x10 * n)
+#define CCM_CCGR(n) (CCM_CCGR_OFFSET + 0x10 * n)
+
+void ddr4_enter_retention(void)
+{
+ volatile unsigned int tmp;
+
+ /* wait DBGCAM to be empty */
+ do {
+ tmp = mmio_read_32(DDRC_DBGCAM(0));
+ } while (tmp != 0x36000000);
+
+ /* Blocks AXI ports from taking anymore transactions */
+ mmio_write_32(DDRC_PCTRL_0(0), 0x00000000);
+
+ /* Waits unit all AXI ports are idle */
+ do {
+ tmp = mmio_read_32(DDRC_PSTAT(0));
+ } while (tmp & 0x10001);
+
+ /* enters self refresh */
+ mmio_write_32(DDRC_PWRCTL(0), 0x000000aa);
+
+ tmp=0;
+ /* self-refresh state */
+ while (tmp!=0x23) {
+ tmp = 0x3f & (mmio_read_32((DDRC_STAT(0))));
+ }
+
+ mmio_write_32(DDRC_DFIMISC(0), 0x00000000);
+
+ /* set SWCTL.sw_done to disable quasi-dynamic register programming outside reset. */
+ mmio_write_32(DDRC_SWCTL(0), 0x00000000);
+
+ mmio_write_32(DDRC_DFIMISC(0), 0x00001f00);
+ mmio_write_32(DDRC_DFIMISC(0), 0x00001f20);
+
+ /* wait DFISTAT.dfi_init_complete to 0 */
+ while (1 == (0x1 & mmio_read_32(DDRC_DFISTAT(0))))
+ ;
+
+ mmio_write_32(DDRC_DFIMISC(0), 0x00001f00);
+
+ /* wait DFISTAT.dfi_init_complete to 1 */
+ while (0 == (0x1 & mmio_read_32(DDRC_DFISTAT(0))))
+ ;
+
+ /* set SWCTL.sw_done to enable quasi-dynamic register programming outside reset. */
+ mmio_write_32(DDRC_SWCTL(0), 0x00000001);
+
+ /* should check PhyInLP3 pub reg */
+ dwc_ddrphy_apb_wr(0xd0000,0x0);
+ tmp = mmio_read_32(IP2APB_DDRPHY_IPS_BASE_ADDR(0)+4*0x00090028);
+ tmp = tmp & 0x1;
+ if(tmp)
+ printf("C: PhyInLP3 = 1\n");
+ else
+ printf("vt_fail\n");
+ dwc_ddrphy_apb_wr(0xd0000,0x1);
+
+ /* pwrdnreqn_async adbm/adbs of ddr */
+ mmio_clrbits_32(GPC_PU_PWRHSK, (1 << 2));
+ do {
+ tmp = mmio_read_32(GPC_PU_PWRHSK);
+ } while (tmp & (0x1 << 20)); /* wait untill pwrdnackn_async=0 */
+ mmio_setbits_32(GPC_PU_PWRHSK, (1 << 2));
+
+ /* remove PowerOk: assert [3]ddr1_phy_pwrokin_n */
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F000008);
+
+ mmio_write_32(CCM_CCGR(5), 0);
+ mmio_write_32(CCM_SRC_CTRL(15), 2);
+
+ mmio_setbits_32(0x303A0D40, 1);
+ mmio_setbits_32(0x303A0104, (1 << 5));
+}
+
+void ddr4_exit_retention(void)
+{
+ volatile unsigned int tmp, tmp_t, i;
+
+ for (i = 0; i < 20; i++){
+ }
+
+ /* assert all reset */
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F00003F);
+
+ mmio_write_32(CCM_CCGR(5), 2);
+ mmio_write_32(CCM_SRC_CTRL(15), 2);
+ printf("C: enable all DRAM clocks \n");
+
+ mmio_write_32(0x303A00EC, 0x0000ffff); /* PGC_CPU_MAPPING */
+ mmio_setbits_32(0x303A00F8, (1 << 5));
+
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F00000F); /* release [4]src_system_rst_b! */
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F000006); /* release [0]ddr1_preset_n, [3]ddr1_phy_pwrokin_n */
+ /* RESET: <core_ddrc_rstn> ASSERTED (ACTIVE LOW) */
+ /* RESET: <presetn> ASSERTED (ACTIVE LOW) */
+ /* RESET: <aresetn> for Port 0 ASSERTED (ACTIVE LOW) */
+ /* RESET: <presetn> DEASSERTED */
+
+ mmio_write_32(DDRC_DBG1(0), 0x00000001);
+ mmio_write_32(DDRC_PWRCTL(0), 0x00000001);
+ while (0 != (0x7 & mmio_read_32(DDRC_STAT(0))))
+ ;
+
+ /* ddrc init */
+ dram_umctl2_init();
+
+ /* Skips the DRAM init routine and starts up in selfrefresh mode */
+ /* Program INIT0.skip_dram_init = 2'b11 */
+ mmio_write_32(DDRC_INIT0(0), 0xc0000000 | (mmio_read_32(DDRC_INIT0(0))));
+ mmio_write_32(DDRC_MSTR2(0), 0x0);
+
+ /* Keeps the controller in self-refresh mode */
+ mmio_write_32(DDRC_PWRCTL(0), 0x000001a8);
+ mmio_clrbits_32(DDRC_DFIMISC(0), 1);
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F000004);
+ mmio_write_32(SRC_DDRC_RCR_ADDR, 0x8F000000); /* release all reset */
+
+ /* restore the ddr phy config */
+ dram_phy_init();
+
+ do {
+ tmp_t = mmio_read_32(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*0x00020097);
+ } while (tmp_t != 0);
+
+ /* before write Dynamic reg, sw_done should be 0 */
+ mmio_write_32(DDRC_SWCTL(0), 0x00000000);
+ mmio_write_32(DDRC_DFIMISC(0), 0x00000020);
+
+ /* wait DFISTAT.dfi_init_complete to 1 */
+ while(0 == (0x1 & mmio_read_32(DDRC_DFISTAT(0))))
+ ;
+
+ /* clear DFIMISC.dfi_init_complete_en */
+ mmio_write_32(DDRC_DFIMISC(0), 0x00000000);
+ /* set DFIMISC.dfi_init_complete_en again */
+ mmio_setbits_32(DDRC_DFIMISC(0), 1);
+ mmio_clrbits_32(DDRC_PWRCTL(0), (1 << 5));
+
+ /* set SWCTL.sw_done to enable quasi-dynamic register programming outside reset. */
+ mmio_write_32(DDRC_SWCTL(0), 0x00000001);
+ /* wait SWSTAT.sw_done_ack to 1 */
+ while(0 == (0x1 & mmio_read_32(DDRC_SWSTAT(0))));
+
+ /* wait STAT to normal state */
+ while(0x1 != (0x7 & mmio_read_32(DDRC_STAT(0))));
+
+ mmio_write_32(DDRC_PCTRL_0(0), 0x00000001);
+ /* dis_auto-refresh is set to 0 */
+ mmio_write_32(DDRC_RFSHCTL3(0), 0x00000010);
+
+ /* should check PhyInLP3 pub reg */
+ mmio_write_32(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*0xd0000, 0x0);
+ tmp = mmio_read_32(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*0x00090028);
+ tmp = tmp & 0x1;
+ if (!tmp)
+ printf("C: PhyInLP3 = 0\n");
+ else
+ printf("vt_fail\n");
+ mmio_write_32(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4*0xd0000, 0x1);
+}