/* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright 2017 NXP */ #include #include #include #include #include #include #include #include #include #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