diff options
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.S | 718 |
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 |