diff --git a/arch/arm/cpu/armv7/Makefile b/arch/arm/cpu/armv7/Makefile index b14ee54519..aca89142f0 100644 --- a/arch/arm/cpu/armv7/Makefile +++ b/arch/arm/cpu/armv7/Makefile @@ -12,6 +12,11 @@ obj-y += cache_v7.o cache_v7_asm.o obj-y += cpu.o cp15.o obj-y += syslib.o +ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),) +obj-y += suspend.o +obj-y += sleep.o +endif + ifneq ($(CONFIG_SKIP_LOWLEVEL_INIT),y) obj-y += lowlevel_init.o endif diff --git a/arch/arm/cpu/armv7/sleep.S b/arch/arm/cpu/armv7/sleep.S new file mode 100644 index 0000000000..b3ea3c1b88 --- /dev/null +++ b/arch/arm/cpu/armv7/sleep.S @@ -0,0 +1,185 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + + .globl cpu_suspend + .globl cpu_do_suspend + .globl cpu_suspend_save + .globl cpu_resume + .globl cpu_do_resume + +/* + * int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) + * @arg will be passed to fn as argument + * return value: 0 - cpu resumed from suspended state. + * -1 - cpu not suspended. + */ +ENTRY(cpu_suspend) + push {r4 - r12, lr} + + mov r5, sp + sub sp, sp, #PM_CTX_SIZE + push {r0, r1} + + /* r9 is gd, save it to _suspend_gd !!! */ + adr r4, _suspend_gd + str r9, [r4] + + mov r1, r5 + add r0, sp, #8 + blx cpu_suspend_save + + adr lr, aborted + /* Jump to arch specific suspend */ + pop {r0, pc} + +aborted: + /* cpu not suspended */ + add sp, sp, #PM_CTX_SIZE + /* Return -1 to the caller */ + mov r0, #(-1) + +suspend_return: + pop {r4 - r12, pc} +ENDPROC(cpu_suspend) + +ENTRY(cpu_do_suspend) + push {r4 - r11} + + read_midr r4 + ubfx r5, r4, #4, #12 + ldr r4, CORTEX_A7_PART_NUM + cmp r5, r4 + beq a7_suspend + ldr r4, CORTEX_A9_PART_NUM + cmp r5, r4 + beq a9_suspend + + /* cpu not supported */ + b . + + /* A9 needs PCR/DIAG */ +a9_suspend: + read_pcr r4 + read_diag r5 + stmia r0!, {r4 - r5} + +a7_suspend: + read_fcseidr r4 + read_tpidruro r5 + stmia r0!, {r4 - r5} + + read_dacr r4 + read_ttbr0 r5 + read_ttbr1 r6 + read_ttbcr r7 + read_sctlr r8 + read_actlr r9 + read_cpacr r10 + stmia r0!, {r4 - r10} + + read_prrr r4 + read_nmrr r5 + read_vbar r6 + mrs r7, CPSR + stmia r0, {r4 - r7} + + pop {r4 - r11} + bx lr +ENDPROC(cpu_do_suspend) + +ENTRY(cpu_resume) + /* Disable interrupt */ + cpsid aif + + /* Load gd !! */ + adr r1, _suspend_gd + ldr r2, [r1] + + /* Get pm_ctx */ + add r2, r2, #PM_CTX_PHYS + ldr r0, [r2] + + /* Need to use r0!, because cpu_do_resume needs it */ + ldmia r0!, {sp, pc} +ENDPROC(cpu_resume) + +/* + * void sm_do_cpu_do_resume(paddr suspend_regs) __noreturn; + * Restore the registers stored when cpu_do_suspend + * r0 points to the physical base address of the suspend_regs + * field of struct pm_ctx. + */ +ENTRY(cpu_do_resume) + read_midr r4 + ubfx r5, r4, #4, #12 + ldr r4, CORTEX_A7_PART_NUM + cmp r5, r4 + beq a7_resume + + /* + * A9 needs PCR/DIAG + */ + ldmia r0!, {r4 - r5} + write_pcr r4 + write_diag r5 + +a7_resume: + /* v7 resume */ + mov ip, #0 + + /* Invalidate icache to PoU */ + write_iciallu + /* set reserved context */ + write_contextidr ip + + ldmia r0!, {r4 - r5} + write_fcseidr r4 + write_tpidruro r5 + + ldmia r0!, {r4 - r10} + /* Invalidate entire TLB */ + write_tlbiall + write_dacr r4 + write_ttbr0 r5 + write_ttbr1 r6 + write_ttbcr r7 + + ldmia r0, {r4 - r7} + write_prrr r4 + write_nmrr r5 + write_vbar r6 + + write_actlr r9 + write_cpacr r10 + write_bpiall + isb + dsb + + /* MMU will be enabled here */ + write_sctlr r8 + isb + + /* Restore interrupt */ + msr CPSR_c, r7 + + mov r0, #0 + b suspend_return +ENDPROC(cpu_do_resume) + +.align 4 +_suspend_gd: + .word 0x0 +CORTEX_A7_PART_NUM: + .word 0xC07 +CORTEX_A9_PART_NUM: + .word 0xC09 diff --git a/arch/arm/cpu/armv7/suspend.c b/arch/arm/cpu/armv7/suspend.c new file mode 100644 index 0000000000..2b97561255 --- /dev/null +++ b/arch/arm/cpu/armv7/suspend.c @@ -0,0 +1,23 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +void cpu_suspend_save(struct pm_ctx *ctx, unsigned long sp) +{ + gd->pm_ctx_phys = (phys_addr_t)ctx; + + /* The content will be passed to cpu_do_resume as register sp */ + ctx->sp = sp; + ctx->cpu_resume_addr = (phys_addr_t)cpu_do_resume; + + cpu_do_suspend(ctx->suspend_regs); + + flush_dcache_all(); +} diff --git a/arch/arm/include/asm/arm32_macros.S b/arch/arm/include/asm/arm32_macros.S new file mode 100644 index 0000000000..e6d4f0745e --- /dev/null +++ b/arch/arm/include/asm/arm32_macros.S @@ -0,0 +1,264 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + + /* Please keep them sorted based on the CRn register */ + .macro read_midr reg + mrc p15, 0, \reg, c0, c0, 0 + .endm + + .macro read_ctr reg + mrc p15, 0, \reg, c0, c0, 1 + .endm + + .macro read_mpidr reg + mrc p15, 0, \reg, c0, c0, 5 + .endm + + .macro read_sctlr reg + mrc p15, 0, \reg, c1, c0, 0 + .endm + + .macro write_sctlr reg + mcr p15, 0, \reg, c1, c0, 0 + .endm + + .macro write_actlr reg + mcr p15, 0, \reg, c1, c0, 1 + .endm + + .macro read_actlr reg + mrc p15, 0, \reg, c1, c0, 1 + .endm + + .macro write_cpacr reg + mcr p15, 0, \reg, c1, c0, 2 + .endm + + .macro read_cpacr reg + mrc p15, 0, \reg, c1, c0, 2 + .endm + + .macro read_scr reg + mrc p15, 0, \reg, c1, c1, 0 + .endm + + .macro write_scr reg + mcr p15, 0, \reg, c1, c1, 0 + .endm + + .macro write_nsacr reg + mcr p15, 0, \reg, c1, c1, 2 + .endm + + .macro read_nsacr reg + mrc p15, 0, \reg, c1, c1, 2 + .endm + + .macro write_ttbr0 reg + mcr p15, 0, \reg, c2, c0, 0 + .endm + + .macro read_ttbr0 reg + mrc p15, 0, \reg, c2, c0, 0 + .endm + + .macro write_ttbr1 reg + mcr p15, 0, \reg, c2, c0, 1 + .endm + + .macro read_ttbr1 reg + mrc p15, 0, \reg, c2, c0, 1 + .endm + + .macro write_ttbcr reg + mcr p15, 0, \reg, c2, c0, 2 + .endm + + .macro read_ttbcr reg + mrc p15, 0, \reg, c2, c0, 2 + .endm + + + .macro write_dacr reg + mcr p15, 0, \reg, c3, c0, 0 + .endm + + .macro read_dacr reg + mrc p15, 0, \reg, c3, c0, 0 + .endm + + .macro read_dfsr reg + mrc p15, 0, \reg, c5, c0, 0 + .endm + + .macro write_icialluis + /* + * Invalidate all instruction caches to PoU, Inner Shareable + * (register ignored) + */ + mcr p15, 0, r0, c7, c1, 0 + .endm + + .macro write_bpiallis + /* + * Invalidate entire branch predictor array, Inner Shareable + * (register ignored) + */ + mcr p15, 0, r0, c7, c1, 6 + .endm + + .macro write_iciallu + /* Invalidate all instruction caches to PoU (register ignored) */ + mcr p15, 0, r0, c7, c5, 0 + .endm + + .macro write_icimvau reg + /* Instruction cache invalidate by MVA */ + mcr p15, 0, \reg, c7, c5, 1 + .endm + + .macro write_bpiall + /* Invalidate entire branch predictor array (register ignored) */ + mcr p15, 0, r0, c7, c5, 6 + .endm + + .macro write_dcimvac reg + /* Data cache invalidate by MVA */ + mcr p15, 0, \reg, c7, c6, 1 + .endm + + .macro write_dcisw reg + /* Data cache invalidate by set/way */ + mcr p15, 0, \reg, c7, c6, 2 + .endm + + .macro write_dccmvac reg + /* Data cache clean by MVA */ + mcr p15, 0, \reg, c7, c10, 1 + .endm + + .macro write_dccsw reg + /* Data cache clean by set/way */ + mcr p15, 0, \reg, c7, c10, 2 + .endm + + .macro write_dccimvac reg + /* Data cache invalidate by MVA */ + mcr p15, 0, \reg, c7, c14, 1 + .endm + + .macro write_dccisw reg + /* Data cache clean and invalidate by set/way */ + mcr p15, 0, \reg, c7, c14, 2 + .endm + + .macro write_tlbiall + /* Invalidate entire unified TLB (register ignored) */ + mcr p15, 0, r0, c8, c7, 0 + .endm + + .macro write_tlbiallis + /* Invalidate entire unified TLB Inner Sharable (register ignored) */ + mcr p15, 0, r0, c8, c3, 0 + .endm + + .macro write_tlbiasidis reg + /* Invalidate unified TLB by ASID Inner Sharable */ + mcr p15, 0, \reg, c8, c3, 2 + .endm + + .macro write_tlbimvaais reg + /* Invalidate unified TLB by MVA all ASID Inner Sharable */ + mcr p15, 0, \reg, c8, c3, 3 + .endm + + .macro write_prrr reg + mcr p15, 0, \reg, c10, c2, 0 + .endm + + .macro read_prrr reg + mrc p15, 0, \reg, c10, c2, 0 + .endm + + .macro write_nmrr reg + mcr p15, 0, \reg, c10, c2, 1 + .endm + + .macro read_nmrr reg + mrc p15, 0, \reg, c10, c2, 1 + .endm + + .macro read_vbar reg + mrc p15, 0, \reg, c12, c0, 0 + .endm + + .macro write_vbar reg + mcr p15, 0, \reg, c12, c0, 0 + .endm + + .macro write_mvbar reg + mcr p15, 0, \reg, c12, c0, 1 + .endm + + .macro read_mvbar reg + mrc p15, 0, \reg, c12, c0, 1 + .endm + + .macro write_fcseidr reg + mcr p15, 0, \reg, c13, c0, 0 + .endm + + .macro read_fcseidr reg + mrc p15, 0, \reg, c13, c0, 0 + .endm + + .macro write_contextidr reg + mcr p15, 0, \reg, c13, c0, 1 + .endm + + .macro read_contextidr reg + mrc p15, 0, \reg, c13, c0, 1 + .endm + + .macro write_tpidruro reg + mcr p15, 0, \reg, c13, c0, 3 + .endm + + .macro read_tpidruro reg + mrc p15, 0, \reg, c13, c0, 3 + .endm + + .macro read_clidr reg + /* Cache Level ID Register */ + mrc p15, 1, \reg, c0, c0, 1 + .endm + + .macro read_ccsidr reg + /* Cache Size ID Registers */ + mrc p15, 1, \reg, c0, c0, 0 + .endm + + .macro write_csselr reg + /* Cache Size Selection Register */ + mcr p15, 2, \reg, c0, c0, 0 + .endm + + /* Cortex A9: pcr, diag registers */ + .macro write_pcr reg + mcr p15, 0, \reg, c15, c0, 0 + .endm + + .macro read_pcr reg + mrc p15, 0, \reg, c15, c0, 0 + .endm + + .macro write_diag reg + mcr p15, 0, \reg, c15, c0, 1 + .endm + + .macro read_diag reg + mrc p15, 0, \reg, c15, c0, 1 + .endm diff --git a/arch/arm/include/asm/suspend.h b/arch/arm/include/asm/suspend.h new file mode 100644 index 0000000000..ac7ba35753 --- /dev/null +++ b/arch/arm/include/asm/suspend.h @@ -0,0 +1,25 @@ +/* + * (C) Copyright 2017 Rockchip Electronics Co., Ltd + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef SUSPEND_H +#define SUSPEND_H +#include +#include + +/* suspend/resume core functions */ +void cpu_suspend_save(struct pm_ctx *ptr, unsigned long sp); +void cpu_do_suspend(unsigned long *ptr); + +void cpu_resume(void); +void cpu_do_resume(void); + +/* + * Exported to platform suspend, arg will be passed to fn as r0 + * Return value: 0 - cpu resumed from suspended state. + * -1 - cpu not suspended. + */ +int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)); +#endif diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 944f58195c..dc2d6e8ce4 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -24,6 +24,13 @@ #include #include +/* Never change the sequence of members !!! */ +struct pm_ctx { + unsigned long sp; + phys_addr_t cpu_resume_addr; + unsigned long suspend_regs[15]; +}; + typedef struct global_data { bd_t *bd; unsigned long flags; @@ -114,6 +121,7 @@ typedef struct global_data { struct bootstage_data *bootstage; /* Bootstage information */ struct bootstage_data *new_bootstage; /* Relocated bootstage info */ #endif + phys_addr_t pm_ctx_phys; } gd_t; #endif diff --git a/lib/asm-offsets.c b/lib/asm-offsets.c index b04f7c6297..07b15497df 100644 --- a/lib/asm-offsets.c +++ b/lib/asm-offsets.c @@ -38,5 +38,8 @@ int main(void) DEFINE(GD_START_ADDR_SP, offsetof(struct global_data, start_addr_sp)); + DEFINE(PM_CTX_SIZE, sizeof(struct pm_ctx)); + DEFINE(PM_CTX_PHYS, offsetof(struct global_data, pm_ctx_phys)); + return 0; }