arm: armv7: introduce cpu suspend and resume support
Just like linux, it supports cpu save and restore context during enter and exit low power mode. With this patch, cpu is able to suspend with core power off. Workflow for trap into ATF for system suspend: cpu_suspend -> cpu_do_suspend -> arch specific fn: int (*fn)(unsigned long) -> psci_system_suspend(deliver 'cpu_resume()' address to ATF) -> ATF system suspend <- ATF system resume <- cpu_resume <- cpu_do_resume next instruction Notice: If needed, you should remember to save and restore GIC by yourself. Change-Id: I5cb6fb6ac5b6a7f4ec4a975b0fc38250b000b28e Signed-off-by: Joseph Chen <chenjh@rock-chips.com>
This commit is contained in:
parent
6f00aaa207
commit
5492555290
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/arm32_macros.S>
|
||||
#include <asm/macro.h>
|
||||
#include <asm-offsets.h>
|
||||
#include <asm/psci.h>
|
||||
#include <config.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
.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
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <asm/suspend.h>
|
||||
#include <common.h>
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* (C) Copyright 2017 Rockchip Electronics Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#ifndef SUSPEND_H
|
||||
#define SUSPEND_H
|
||||
#include <common.h>
|
||||
#include <asm-generic/global_data.h>
|
||||
|
||||
/* 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
|
||||
|
|
@ -24,6 +24,13 @@
|
|||
#include <membuff.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue