aboutsummaryrefslogtreecommitdiff
path: root/core/arch/arm/plat-imx/pm/psci-suspend-imx7.S
diff options
context:
space:
mode:
Diffstat (limited to 'core/arch/arm/plat-imx/pm/psci-suspend-imx7.S')
-rw-r--r--core/arch/arm/plat-imx/pm/psci-suspend-imx7.S718
1 files changed, 718 insertions, 0 deletions
diff --git a/core/arch/arm/plat-imx/pm/psci-suspend-imx7.S b/core/arch/arm/plat-imx/pm/psci-suspend-imx7.S
new file mode 100644
index 00000000..cb48a1b4
--- /dev/null
+++ b/core/arch/arm/plat-imx/pm/psci-suspend-imx7.S
@@ -0,0 +1,718 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <arm.h>
+#include <arm32_macros.S>
+#include <asm.S>
+#include <imx_pm.h>
+#include <imx-regs.h>
+#include <kernel/cache_helpers.h>
+#include <kernel/tz_proc_def.h>
+#include <kernel/tz_ssvce_def.h>
+#include <kernel/unwind.h>
+#include <platform_config.h>
+
+#define MX7_SRC_GPR1 0x74
+#define MX7_SRC_GPR2 0x78
+#define GPC_PGC_C0 0x800
+#define GPC_PGC_FM 0xa00
+#define ANADIG_SNVS_MISC_CTRL 0x380
+#define ANADIG_SNVS_MISC_CTRL_SET 0x384
+#define ANADIG_SNVS_MISC_CTRL_CLR 0x388
+#define ANADIG_DIGPROG 0x800
+#define DDRC_STAT 0x4
+#define DDRC_PWRCTL 0x30
+#define DDRC_PSTAT 0x3fc
+#define DDRC_PCTRL_0 0x490
+#define DDRC_DFIMISC 0x1b0
+#define DDRC_SWCTL 0x320
+#define DDRC_SWSTAT 0x324
+#define DDRPHY_LP_CON0 0x18
+
+#define CCM_SNVS_LPCG 0x250
+#define MX7D_GPC_IMR1 0x30
+#define MX7D_GPC_IMR2 0x34
+#define MX7D_GPC_IMR3 0x38
+#define MX7D_GPC_IMR4 0x3c
+
+/*
+ * The code in this file is copied to coherent on-chip ram memory,
+ * without any dependency on code/data in tee memory(DDR).
+ */
+ .section .text.psci.suspend
+ .align 3
+
+ .macro disable_l1_dcache
+
+ /*
+ * flush L1 data cache before clearing SCTLR.C bit.
+ */
+ push {r0 - r10, lr}
+ ldr r1, =dcache_op_all
+ mov r0, #DCACHE_OP_CLEAN_INV
+ mov lr, pc
+ bx r1
+ pop {r0 - r10, lr}
+
+ /* disable d-cache */
+ read_sctlr r7
+ bic r7, r7, #SCTLR_C
+ write_sctlr r7
+ dsb
+ isb
+
+ push {r0 - r10, lr}
+ ldr r1, =dcache_op_all
+ mov r0, #DCACHE_OP_CLEAN_INV
+ mov lr, pc
+ bx r1
+ pop {r0 - r10, lr}
+
+ .endm
+
+ .macro store_ttbr
+
+ /* Store TTBR1 to pm_info->ttbr1 */
+ read_ttbr1 r7
+ str r7, [r0, #PM_INFO_MX7_TTBR1_OFF]
+
+ /* Store TTBR0 to pm_info->ttbr1 */
+ read_ttbr0 r7
+ str r7, [r0, #PM_INFO_MX7_TTBR0_OFF]
+
+ /* Disable Branch Prediction */
+ read_sctlr r6
+ bic r6, r6, #SCTLR_Z
+ write_sctlr r6
+
+ /* Flush the BTAC. */
+ write_bpiallis
+
+ ldr r6, =iram_tbl_phys_addr
+ ldr r6, [r6]
+ dsb
+ isb
+
+ /* Store the IRAM table in TTBR1/0 */
+ write_ttbr1 r6
+ write_ttbr0 r6
+
+ /* Read TTBCR and set PD0=1 */
+ read_ttbcr r6
+ orr r6, r6, #TTBCR_PD0
+ write_ttbcr r6
+
+ dsb
+ isb
+
+ /* flush the TLB */
+ write_tlbiallis
+ isb
+ write_tlbiall
+ isb
+
+ .endm
+
+ .macro restore_ttbr
+
+ /* Enable L1 data cache. */
+ read_sctlr r6
+ orr r6, r6, #SCTLR_C
+ write_sctlr r6
+
+ dsb
+ isb
+
+ /* Restore TTBCR */
+ /* Read TTBCR and set PD0=0 */
+ read_ttbcr r6
+ bic r6, r6, #TTBCR_PD0
+ write_ttbcr r6
+ dsb
+ isb
+
+ /* flush the TLB */
+ write_tlbiallis
+
+ /* Enable Branch Prediction */
+ read_sctlr r6
+ orr r6, r6, #SCTLR_Z
+ write_sctlr r6
+
+ /* Flush the Branch Target Address Cache (BTAC) */
+ write_bpiallis
+
+ /* Restore TTBR1/0, get the origin ttbr1/0 from pm info */
+ ldr r7, [r0, #PM_INFO_MX7_TTBR1_OFF]
+ write_ttbr1 r7
+ ldr r7, [r0, #PM_INFO_MX7_TTBR0_OFF]
+ write_ttbr0 r7
+ isb
+
+ .endm
+
+ .macro ddrc_enter_self_refresh
+
+ ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFF]
+
+ /* let DDR out of self-refresh */
+ ldr r7, =0x0
+ str r7, [r11, #DDRC_PWRCTL]
+
+ /* wait rw port_busy clear */
+ ldr r6, =BIT32(16)
+ orr r6, r6, #0x1
+1:
+ ldr r7, [r11, #DDRC_PSTAT]
+ ands r7, r7, r6
+ bne 1b
+
+ /* enter self-refresh bit 5 */
+ ldr r7, =BIT32(5)
+ str r7, [r11, #DDRC_PWRCTL]
+
+ /* wait until self-refresh mode entered */
+2:
+ ldr r7, [r11, #DDRC_STAT]
+ and r7, r7, #0x3
+ cmp r7, #0x3
+ bne 2b
+3:
+ ldr r7, [r11, #DDRC_STAT]
+ ands r7, r7, #0x20
+ beq 3b
+
+ /* disable dram clk */
+ ldr r7, [r11, #DDRC_PWRCTL]
+ orr r7, r7, #BIT32(3)
+ str r7, [r11, #DDRC_PWRCTL]
+
+ .endm
+
+ .macro ddrc_exit_self_refresh
+
+ cmp r5, #0x0
+ ldreq r11, [r0, #PM_INFO_MX7_DDRC_V_OFF]
+ ldrne r11, [r0, #PM_INFO_MX7_DDRC_P_OFF]
+
+ /* let DDR out of self-refresh */
+ ldr r7, =0x0
+ str r7, [r11, #DDRC_PWRCTL]
+
+ /* wait until self-refresh mode entered */
+4:
+ ldr r7, [r11, #DDRC_STAT]
+ and r7, r7, #0x3
+ cmp r7, #0x3
+ beq 4b
+
+ /* enable auto self-refresh */
+ ldr r7, [r11, #DDRC_PWRCTL]
+ orr r7, r7, #BIT32(0)
+ str r7, [r11, #DDRC_PWRCTL]
+
+ .endm
+
+ .macro wait_delay
+5:
+ subs r6, r6, #0x1
+ bne 5b
+
+ .endm
+
+ .macro ddr_enter_retention
+
+ ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFF]
+
+ /* let DDR out of self-refresh */
+ ldr r7, =0x0
+ str r7, [r11, #DDRC_PCTRL_0]
+
+ /* wait rw port_busy clear */
+ ldr r6, =BIT32(16)
+ orr r6, r6, #0x1
+6:
+ ldr r7, [r11, #DDRC_PSTAT]
+ ands r7, r7, r6
+ bne 6b
+
+ ldr r11, [r0, #PM_INFO_MX7_DDRC_V_OFF]
+ /* enter self-refresh bit 5 */
+ ldr r7, =BIT32(5)
+ str r7, [r11, #DDRC_PWRCTL]
+
+ /* wait until self-refresh mode entered */
+7:
+ ldr r7, [r11, #DDRC_STAT]
+ and r7, r7, #0x3
+ cmp r7, #0x3
+ bne 7b
+8:
+ ldr r7, [r11, #DDRC_STAT]
+ ands r7, r7, #0x20
+ beq 8b
+
+ /* disable dram clk */
+ ldr r7, =BIT32(5)
+ orr r7, r7, #BIT32(3)
+ str r7, [r11, #DDRC_PWRCTL]
+
+ ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFF]
+ ldr r7, [r11, #ANADIG_DIGPROG]
+ and r7, r7, #0xff
+ cmp r7, #0x11
+ bne 10f
+
+ /* TO 1.1 */
+ ldr r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFF]
+ ldr r7, =0x38000000
+ str r7, [r11]
+
+ /* LPSR mode need to use TO1.0 flow as IOMUX lost power */
+ ldr r10, [r0, #PM_INFO_MX7_LPSR_V_OFF]
+ ldr r7, [r10]
+ cmp r7, #0x0
+ beq 11f
+10:
+ /* reset ddr_phy */
+ ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFF]
+ ldr r7, =0x0
+ str r7, [r11, #ANADIG_SNVS_MISC_CTRL]
+
+ /* delay 7 us */
+ ldr r6, =6000
+ wait_delay
+
+ ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFF]
+ ldr r6, =0x1000
+ ldr r7, [r11, r6]
+ orr r7, r7, #0x1
+ str r7, [r11, r6]
+11:
+ /* turn off ddr power */
+ ldr r11, [r0, #PM_INFO_MX7_ANATOP_V_OFF]
+ ldr r7, =(0x1 << 29)
+ str r7, [r11, #ANADIG_SNVS_MISC_CTRL_SET]
+
+ ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFF]
+ ldr r6, =0x1000
+ ldr r7, [r11, r6]
+ orr r7, r7, #0x1
+ str r7, [r11, r6]
+
+ .endm
+
+ .macro ddr_exit_retention
+
+ cmp r5, #0x0
+ ldreq r1, [r0, #PM_INFO_MX7_ANATOP_V_OFF]
+ ldrne r1, [r0, #PM_INFO_MX7_ANATOP_P_OFF]
+ ldreq r2, [r0, #PM_INFO_MX7_SRC_V_OFF]
+ ldrne r2, [r0, #PM_INFO_MX7_SRC_P_OFF]
+ ldreq r3, [r0, #PM_INFO_MX7_DDRC_V_OFF]
+ ldrne r3, [r0, #PM_INFO_MX7_DDRC_P_OFF]
+ ldreq r4, [r0, #PM_INFO_MX7_DDRC_PHY_V_OFF]
+ ldrne r4, [r0, #PM_INFO_MX7_DDRC_PHY_P_OFF]
+ ldreq r10, [r0, #PM_INFO_MX7_CCM_V_OFF]
+ ldrne r10, [r0, #PM_INFO_MX7_CCM_P_OFF]
+ ldreq r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_V_OFF]
+ ldrne r11, [r0, #PM_INFO_MX7_IOMUXC_GPR_P_OFF]
+
+ /* turn on ddr power */
+ ldr r7, =BIT32(29)
+ str r7, [r1, #ANADIG_SNVS_MISC_CTRL_CLR]
+
+ ldr r6, =50
+ wait_delay
+
+ /* clear ddr_phy reset */
+ ldr r6, =0x1000
+ ldr r7, [r2, r6]
+ orr r7, r7, #0x3
+ str r7, [r2, r6]
+ ldr r7, [r2, r6]
+ bic r7, r7, #0x1
+ str r7, [r2, r6]
+13:
+ ldr r6, [r0, #PM_INFO_MX7_DDRC_REG_NUM_OFF]
+ ldr r7, =PM_INFO_MX7_DDRC_REG_OFF
+ add r7, r7, r0
+14:
+ ldr r8, [r7], #0x4
+ ldr r9, [r7], #0x4
+ str r9, [r3, r8]
+ subs r6, r6, #0x1
+ bne 14b
+ ldr r7, =0x20
+ str r7, [r3, #DDRC_PWRCTL]
+ ldr r7, =0x0
+ str r7, [r3, #DDRC_DFIMISC]
+
+ /* do PHY, clear ddr_phy reset */
+ ldr r6, =0x1000
+ ldr r7, [r2, r6]
+ bic r7, r7, #0x2
+ str r7, [r2, r6]
+
+ ldr r7, [r1, #ANADIG_DIGPROG]
+ and r7, r7, #0xff
+ cmp r7, #0x11
+ bne 12f
+
+ /*
+ * TKT262940:
+ * System hang when press RST for DDR PAD is
+ * in retention mode, fixed on TO1.1
+ */
+ ldr r7, [r11]
+ bic r7, r7, #BIT32(27)
+ str r7, [r11]
+ ldr r7, [r11]
+ bic r7, r7, #BIT32(29)
+ str r7, [r11]
+12:
+ ldr r7, =BIT32(30)
+ str r7, [r1, #ANADIG_SNVS_MISC_CTRL_SET]
+
+ /* need to delay ~5mS */
+ ldr r6, =0x100000
+ wait_delay
+
+ ldr r6, [r0, #PM_INFO_MX7_DDRC_PHY_REG_NUM_OFF]
+ ldr r7, =PM_INFO_MX7_DDRC_PHY_REG_OFF
+ add r7, r7, r0
+
+15:
+ ldr r8, [r7], #0x4
+ ldr r9, [r7], #0x4
+ str r9, [r4, r8]
+ subs r6, r6, #0x1
+ bne 15b
+
+ ldr r7, =0x0
+ add r9, r10, #0x4000
+ str r7, [r9, #0x130]
+
+ ldr r7, =0x170
+ orr r7, r7, #0x8
+ str r7, [r11, #0x20]
+
+ ldr r7, =0x2
+ add r9, r10, #0x4000
+ str r7, [r9, #0x130]
+
+ ldr r7, =0xf
+ str r7, [r4, #DDRPHY_LP_CON0]
+
+ /* wait until self-refresh mode entered */
+16:
+ ldr r7, [r3, #DDRC_STAT]
+ and r7, r7, #0x3
+ cmp r7, #0x3
+ bne 16b
+ ldr r7, =0x0
+ str r7, [r3, #DDRC_SWCTL]
+ ldr r7, =0x1
+ str r7, [r3, #DDRC_DFIMISC]
+ ldr r7, =0x1
+ str r7, [r3, #DDRC_SWCTL]
+17:
+ ldr r7, [r3, #DDRC_SWSTAT]
+ and r7, r7, #0x1
+ cmp r7, #0x1
+ bne 17b
+18:
+ ldr r7, [r3, #DDRC_STAT]
+ and r7, r7, #0x20
+ cmp r7, #0x20
+ bne 18b
+
+ /* let DDR out of self-refresh */
+ ldr r7, =0x0
+ str r7, [r3, #DDRC_PWRCTL]
+19:
+ ldr r7, [r3, #DDRC_STAT]
+ and r7, r7, #0x30
+ cmp r7, #0x0
+ bne 19b
+
+20:
+ ldr r7, [r3, #DDRC_STAT]
+ and r7, r7, #0x3
+ cmp r7, #0x1
+ bne 20b
+
+ /* enable port */
+ ldr r7, =0x1
+ str r7, [r3, #DDRC_PCTRL_0]
+
+ /* enable auto self-refresh */
+ ldr r7, [r3, #DDRC_PWRCTL]
+ orr r7, r7, #(1 << 0)
+ str r7, [r3, #DDRC_PWRCTL]
+
+ .endm
+
+FUNC imx7_suspend, :
+UNWIND( .fnstart)
+UNWIND( .cantunwind)
+ push {r4-r12}
+
+ /* make sure SNVS clk is enabled */
+ ldr r11, [r0, #PM_INFO_MX7_CCM_V_OFF]
+ add r11, r11, #0x4000
+ ldr r7, =0x3
+ str r7, [r11, #CCM_SNVS_LPCG]
+
+ /* check whether it is a standby mode */
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r7, [r11, #GPC_PGC_C0]
+ cmp r7, #0
+ beq ddr_only_self_refresh
+
+ /*
+ * The value of r0 is mapped the same in origin table and IRAM table,
+ * thus no need to care r0 here.
+ */
+ ldr r1, [r0, #PM_INFO_MX7_PBASE_OFF]
+ ldr r4, [r0, #PM_INFO_MX7_SIZE_OFF]
+
+ /*
+ * counting the resume address in iram
+ * to set it in SRC register.
+ */
+ ldr r6, =imx7_suspend
+ ldr r7, =resume
+ sub r7, r7, r6
+ add r8, r1, r4
+ add r9, r8, r7
+
+ ldr r11, [r0, #PM_INFO_MX7_SRC_V_OFF]
+ /* store physical resume addr and pm_info address. */
+ str r9, [r11, #MX7_SRC_GPR1]
+ str r1, [r11, #MX7_SRC_GPR2]
+
+ disable_l1_dcache
+
+ store_ttbr
+
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r7, [r11, #GPC_PGC_FM]
+ cmp r7, #0
+ beq ddr_only_self_refresh
+
+ ddr_enter_retention
+ /* enter LPSR mode if resume addr is valid */
+ ldr r11, [r0, #PM_INFO_MX7_LPSR_V_OFF]
+ ldr r7, [r11]
+ cmp r7, #0x0
+ beq ddr_retention_enter_out
+
+ /* disable STOP mode before entering LPSR */
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r7, [r11]
+ bic r7, #0xf
+ str r7, [r11]
+
+ /* shut down vddsoc to enter lpsr mode */
+ ldr r11, [r0, #PM_INFO_MX7_SNVS_V_OFF]
+ ldr r7, [r11, #0x38]
+ orr r7, r7, #0x60
+ str r7, [r11, #0x38]
+ dsb
+wait_shutdown:
+ wfi
+ b wait_shutdown
+
+ddr_only_self_refresh:
+ ddrc_enter_self_refresh
+ b wfi
+ddr_retention_enter_out:
+ ldr r11, [r0, #PM_INFO_MX7_GIC_DIST_V_OFF]
+ ldr r7, =0x0
+ ldr r8, =0x1000
+ str r7, [r11, r8]
+
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r4, [r11, #MX7D_GPC_IMR1]
+ ldr r5, [r11, #MX7D_GPC_IMR2]
+ ldr r6, [r11, #MX7D_GPC_IMR3]
+ ldr r7, [r11, #MX7D_GPC_IMR4]
+
+ ldr r8, =0xffffffff
+ str r8, [r11, #MX7D_GPC_IMR1]
+ str r8, [r11, #MX7D_GPC_IMR2]
+ str r8, [r11, #MX7D_GPC_IMR3]
+ str r8, [r11, #MX7D_GPC_IMR4]
+
+ /*
+ * enable the RBC bypass counter here
+ * to hold off the interrupts. RBC counter
+ * = 8 (240us). With this setting, the latency
+ * from wakeup interrupt to ARM power up
+ * is ~250uS.
+ */
+ ldr r8, [r11, #0x14]
+ bic r8, r8, #(0x3f << 24)
+ orr r8, r8, #(0x8 << 24)
+ str r8, [r11, #0x14]
+
+ /* enable the counter. */
+ ldr r8, [r11, #0x14]
+ orr r8, r8, #(0x1 << 30)
+ str r8, [r11, #0x14]
+
+ /* unmask all the GPC interrupts. */
+ str r4, [r11, #MX7D_GPC_IMR1]
+ str r5, [r11, #MX7D_GPC_IMR2]
+ str r6, [r11, #MX7D_GPC_IMR3]
+ str r7, [r11, #MX7D_GPC_IMR4]
+
+ /*
+ * now delay for a short while (3usec)
+ * ARM is at 1GHz at this point
+ * so a short loop should be enough.
+ * this delay is required to ensure that
+ * the RBC counter can start counting in
+ * case an interrupt is already pending
+ * or in case an interrupt arrives just
+ * as ARM is about to assert DSM_request.
+ */
+ ldr r7, =2000
+rbc_loop:
+ subs r7, r7, #0x1
+ bne rbc_loop
+wfi:
+ dsb
+ /* Enter stop mode */
+ wfi
+
+ mov r5, #0x0
+
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r7, [r11, #GPC_PGC_FM]
+ cmp r7, #0
+ beq wfi_ddr_self_refresh_out
+
+ ddr_exit_retention
+ b wfi_ddr_retention_out
+wfi_ddr_self_refresh_out:
+ ddrc_exit_self_refresh
+wfi_ddr_retention_out:
+
+ /* check whether it is a standby mode */
+ ldr r11, [r0, #PM_INFO_MX7_GPC_V_OFF]
+ ldr r7, [r11, #GPC_PGC_C0]
+ cmp r7, #0
+ beq standby_out
+
+ ldr r11, [r0, #PM_INFO_MX7_GIC_DIST_V_OFF]
+ ldr r7, =0x1
+ ldr r8, =0x1000
+ str r7, [r11, r8]
+
+ restore_ttbr
+standby_out:
+ pop {r4-r12}
+ /* return to suspend finish */
+ bx lr
+
+resume:
+ write_iciallu
+ write_bpiall
+ dsb
+ isb
+
+ mov r6, #(SCTLR_I | SCTLR_Z)
+ write_sctlr r6
+ isb
+
+ /*
+ * After resume back, rom run in SVC mode,
+ * so we need to switch to monitor mode.
+ */
+ cps #CPSR_MODE_MON
+
+ /* get physical resume address from pm_info. */
+ ldr lr, [r0, #PM_INFO_MX7_RESUME_ADDR_OFF]
+ /* clear core0's entry and parameter */
+ ldr r11, [r0, #PM_INFO_MX7_SRC_P_OFF]
+ mov r7, #0x0
+ str r7, [r11, #MX7_SRC_GPR1]
+ str r7, [r11, #MX7_SRC_GPR2]
+
+ mov r5, #0x1
+
+ ldr r11, [r0, #PM_INFO_MX7_GPC_P_OFF]
+ ldr r7, [r11, #GPC_PGC_FM]
+ cmp r7, #0
+ beq dsm_ddr_self_refresh_out
+
+ ddr_exit_retention
+ b dsm_ddr_retention_out
+dsm_ddr_self_refresh_out:
+ ddrc_exit_self_refresh
+dsm_ddr_retention_out:
+
+ bx lr
+UNWIND( .fnend)
+END_FUNC imx7_suspend
+
+FUNC ca7_cpu_resume, :
+UNWIND( .fnstart)
+UNWIND( .cantunwind)
+ mov r0, #0 @ ; write the cache size selection register to be
+ write_csselr r0 @ ; sure we address the data cache
+ isb @ ; isb to sync the change to the cachesizeid reg
+
+_inv_dcache_off:
+ mov r0, #0 @ ; set way number to 0
+_inv_nextway:
+ mov r1, #0 @ ; set line number (=index) to 0
+_inv_nextline:
+ orr r2, r0, r1 @ ; construct way/index value
+ write_dcisw r2 @ ; invalidate data or unified cache line by set/way
+ add r1, r1, #1 << LINE_FIELD_OFFSET @ ; increment the index
+ cmp r1, #1 << LINE_FIELD_OVERFLOW @ ; overflow out of set field?
+ bne _inv_nextline
+ add r0, r0, #1 << WAY_FIELD_OFFSET @ ; increment the way number
+ cmp r0, #0 @ ; overflow out of way field?
+ bne _inv_nextway
+
+ dsb @ ; synchronise
+ isb
+
+ /*
+ * No stack, scratch r0-r3
+ * TODO: Need to use specific configure, but not plat_xxx.
+ * Because plat_xx maybe changed in future, we can not rely on it.
+ * Need handle sp carefully.
+ */
+ blx plat_cpu_reset_early
+
+ b sm_pm_cpu_resume
+UNWIND( .fnend)
+END_FUNC ca7_cpu_resume