Skip to content

Commit

Permalink
ARM: pm: add generic CPU suspend/resume support
Browse files Browse the repository at this point in the history
This adds core support for saving and restoring CPU coprocessor
registers for suspend/resume support.  This contains support for suspend
with ARM920, ARM926, SA11x0, PXA25x, PXA27x, PXA3xx, V6 and V7 CPUs.
Tested on Assabet and Tegra 2.

Tested-by: Colin Cross <ccross@android.com>
Tested-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Russell King committed Feb 22, 2011
1 parent 753790e commit f6b0fa0
Show file tree
Hide file tree
Showing 29 changed files with 522 additions and 33 deletions.
3 changes: 3 additions & 0 deletions arch/arm/include/asm/glue-proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@
#define cpu_dcache_clean_area __glue(CPU_NAME,_dcache_clean_area)
#define cpu_do_switch_mm __glue(CPU_NAME,_switch_mm)
#define cpu_set_pte_ext __glue(CPU_NAME,_set_pte_ext)
#define cpu_suspend_size __glue(CPU_NAME,_suspend_size)
#define cpu_do_suspend __glue(CPU_NAME,_do_suspend)
#define cpu_do_resume __glue(CPU_NAME,_do_resume)
#endif

#endif
7 changes: 7 additions & 0 deletions arch/arm/include/asm/proc-fns.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ extern struct processor {
* ignore 'ext'.
*/
void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext);

/* Suspend/resume */
unsigned int suspend_size;
void (*do_suspend)(void *);
void (*do_resume)(void *);
} processor;

#ifndef MULTI_CPU
Expand All @@ -86,6 +91,8 @@ extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
#define cpu_do_switch_mm(pgd,mm) processor.switch_mm(pgd,mm)
#endif

extern void cpu_resume(void);

#include <asm/memory.h>

#ifdef CONFIG_MMU
Expand Down
1 change: 1 addition & 0 deletions arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ obj-$(CONFIG_MODULES) += armksyms.o module.o
obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_PM) += sleep.o
obj-$(CONFIG_HAVE_SCHED_CLOCK) += sched_clock.o
obj-$(CONFIG_SMP) += smp.o smp_tlb.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
Expand Down
9 changes: 9 additions & 0 deletions arch/arm/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <asm/cacheflush.h>
#include <asm/glue-df.h>
#include <asm/glue-pf.h>
#include <asm/mach/arch.h>
Expand Down Expand Up @@ -115,6 +116,14 @@ int main(void)
#endif
#ifdef MULTI_PABORT
DEFINE(PROCESSOR_PABT_FUNC, offsetof(struct processor, _prefetch_abort));
#endif
#ifdef MULTI_CPU
DEFINE(CPU_SLEEP_SIZE, offsetof(struct processor, suspend_size));
DEFINE(CPU_DO_SUSPEND, offsetof(struct processor, do_suspend));
DEFINE(CPU_DO_RESUME, offsetof(struct processor, do_resume));
#endif
#ifdef MULTI_CACHE
DEFINE(CACHE_FLUSH_KERN_ALL, offsetof(struct cpu_cache_fns, flush_kern_all));
#endif
BLANK();
DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
Expand Down
109 changes: 109 additions & 0 deletions arch/arm/kernel/sleep.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/assembler.h>
#include <asm/glue-cache.h>
#include <asm/glue-proc.h>
#include <asm/system.h>
.text

/*
* Save CPU state for a suspend
* r1 = v:p offset
* r3 = virtual return function
* Note: sp is decremented to allocate space for CPU state on stack
* r0-r3,r9,r10,lr corrupted
*/
ENTRY(cpu_suspend)
mov r9, lr
#ifdef MULTI_CPU
ldr r10, =processor
mov r2, sp @ current virtual SP
ldr r0, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state
ldr ip, [r10, #CPU_DO_RESUME] @ virtual resume function
sub sp, sp, r0 @ allocate CPU state on stack
mov r0, sp @ save pointer
add ip, ip, r1 @ convert resume fn to phys
stmfd sp!, {r1, r2, r3, ip} @ save v:p, virt SP, retfn, phys resume fn
ldr r3, =sleep_save_sp
add r2, sp, r1 @ convert SP to phys
str r2, [r3] @ save phys SP
mov lr, pc
ldr pc, [r10, #CPU_DO_SUSPEND] @ save CPU state
#else
mov r2, sp @ current virtual SP
ldr r0, =cpu_suspend_size
sub sp, sp, r0 @ allocate CPU state on stack
mov r0, sp @ save pointer
stmfd sp!, {r1, r2, r3} @ save v:p, virt SP, return fn
ldr r3, =sleep_save_sp
add r2, sp, r1 @ convert SP to phys
str r2, [r3] @ save phys SP
bl cpu_do_suspend
#endif

@ flush data cache
#ifdef MULTI_CACHE
ldr r10, =cpu_cache
mov lr, r9
ldr pc, [r10, #CACHE_FLUSH_KERN_ALL]
#else
mov lr, r9
b __cpuc_flush_kern_all
#endif
ENDPROC(cpu_suspend)
.ltorg

/*
* r0 = control register value
* r1 = v:p offset (preserved by cpu_do_resume)
* r2 = phys page table base
* r3 = L1 section flags
*/
ENTRY(cpu_resume_mmu)
adr r4, cpu_resume_turn_mmu_on
mov r4, r4, lsr #20
orr r3, r3, r4, lsl #20
ldr r5, [r2, r4, lsl #2] @ save old mapping
str r3, [r2, r4, lsl #2] @ setup 1:1 mapping for mmu code
sub r2, r2, r1
ldr r3, =cpu_resume_after_mmu
bic r1, r0, #CR_C @ ensure D-cache is disabled
b cpu_resume_turn_mmu_on
ENDPROC(cpu_resume_mmu)
.ltorg
.align 5
cpu_resume_turn_mmu_on:
mcr p15, 0, r1, c1, c0, 0 @ turn on MMU, I-cache, etc
mrc p15, 0, r1, c0, c0, 0 @ read id reg
mov r1, r1
mov r1, r1
mov pc, r3 @ jump to virtual address
ENDPROC(cpu_resume_turn_mmu_on)
cpu_resume_after_mmu:
str r5, [r2, r4, lsl #2] @ restore old mapping
mcr p15, 0, r0, c1, c0, 0 @ turn on D-cache
mov pc, lr
ENDPROC(cpu_resume_after_mmu)

/*
* Note: Yes, part of the following code is located into the .data section.
* This is to allow sleep_save_sp to be accessed with a relative load
* while we can't rely on any MMU translation. We could have put
* sleep_save_sp in the .text section as well, but some setups might
* insist on it to be truly read-only.
*/
.data
.align
ENTRY(cpu_resume)
ldr r0, sleep_save_sp @ stack phys addr
msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE @ set SVC, irqs off
#ifdef MULTI_CPU
ldmia r0!, {r1, sp, lr, pc} @ load v:p, stack, return fn, resume fn
#else
ldmia r0!, {r1, sp, lr} @ load v:p, stack, return fn
b cpu_do_resume
#endif
ENDPROC(cpu_resume)

sleep_save_sp:
.word 0 @ preserve stack phys ptr here
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm1020.S
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,9 @@ arm1020_processor_functions:
.word cpu_arm1020_dcache_clean_area
.word cpu_arm1020_switch_mm
.word cpu_arm1020_set_pte_ext
.word 0
.word 0
.word 0
.size arm1020_processor_functions, . - arm1020_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm1020e.S
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,9 @@ arm1020e_processor_functions:
.word cpu_arm1020e_dcache_clean_area
.word cpu_arm1020e_switch_mm
.word cpu_arm1020e_set_pte_ext
.word 0
.word 0
.word 0
.size arm1020e_processor_functions, . - arm1020e_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm1022.S
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,9 @@ arm1022_processor_functions:
.word cpu_arm1022_dcache_clean_area
.word cpu_arm1022_switch_mm
.word cpu_arm1022_set_pte_ext
.word 0
.word 0
.word 0
.size arm1022_processor_functions, . - arm1022_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm1026.S
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ arm1026_processor_functions:
.word cpu_arm1026_dcache_clean_area
.word cpu_arm1026_switch_mm
.word cpu_arm1026_set_pte_ext
.word 0
.word 0
.word 0
.size arm1026_processor_functions, . - arm1026_processor_functions

.section .rodata
Expand Down
6 changes: 6 additions & 0 deletions arch/arm/mm/proc-arm6_7.S
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ ENTRY(arm6_processor_functions)
.word cpu_arm6_dcache_clean_area
.word cpu_arm6_switch_mm
.word cpu_arm6_set_pte_ext
.word 0
.word 0
.word 0
.size arm6_processor_functions, . - arm6_processor_functions

/*
Expand All @@ -301,6 +304,9 @@ ENTRY(arm7_processor_functions)
.word cpu_arm7_dcache_clean_area
.word cpu_arm7_switch_mm
.word cpu_arm7_set_pte_ext
.word 0
.word 0
.word 0
.size arm7_processor_functions, . - arm7_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm720.S
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,9 @@ ENTRY(arm720_processor_functions)
.word cpu_arm720_dcache_clean_area
.word cpu_arm720_switch_mm
.word cpu_arm720_set_pte_ext
.word 0
.word 0
.word 0
.size arm720_processor_functions, . - arm720_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm740.S
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ ENTRY(arm740_processor_functions)
.word cpu_arm740_dcache_clean_area
.word cpu_arm740_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm740_processor_functions, . - arm740_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm7tdmi.S
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ ENTRY(arm7tdmi_processor_functions)
.word cpu_arm7tdmi_dcache_clean_area
.word cpu_arm7tdmi_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm7tdmi_processor_functions, . - arm7tdmi_processor_functions

.section ".rodata"
Expand Down
37 changes: 37 additions & 0 deletions arch/arm/mm/proc-arm920.S
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,40 @@ ENTRY(cpu_arm920_set_pte_ext)
#endif
mov pc, lr

/* Suspend/resume support: taken from arch/arm/plat-s3c24xx/sleep.S */
.globl cpu_arm920_suspend_size
.equ cpu_arm920_suspend_size, 4 * 3
#ifdef CONFIG_PM
ENTRY(cpu_arm920_do_suspend)
stmfd sp!, {r4 - r7, lr}
mrc p15, 0, r4, c13, c0, 0 @ PID
mrc p15, 0, r5, c3, c0, 0 @ Domain ID
mrc p15, 0, r6, c2, c0, 0 @ TTB address
mrc p15, 0, r7, c1, c0, 0 @ Control register
stmia r0, {r4 - r7}
ldmfd sp!, {r4 - r7, pc}
ENDPROC(cpu_arm920_do_suspend)

ENTRY(cpu_arm920_do_resume)
mov ip, #0
mcr p15, 0, ip, c8, c7, 0 @ invalidate I+D TLBs
mcr p15, 0, ip, c7, c7, 0 @ invalidate I+D caches
ldmia r0, {r4 - r7}
mcr p15, 0, r4, c13, c0, 0 @ PID
mcr p15, 0, r5, c3, c0, 0 @ Domain ID
mcr p15, 0, r6, c2, c0, 0 @ TTB address
mov r0, r7 @ control register
mov r2, r6, lsr #14 @ get TTB0 base
mov r2, r2, lsl #14
ldr r3, =PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE
b cpu_resume_mmu
ENDPROC(cpu_arm920_do_resume)
#else
#define cpu_arm920_do_suspend 0
#define cpu_arm920_do_resume 0
#endif

__CPUINIT

.type __arm920_setup, #function
Expand Down Expand Up @@ -432,6 +466,9 @@ arm920_processor_functions:
.word cpu_arm920_dcache_clean_area
.word cpu_arm920_switch_mm
.word cpu_arm920_set_pte_ext
.word cpu_arm920_suspend_size
.word cpu_arm920_do_suspend
.word cpu_arm920_do_resume
.size arm920_processor_functions, . - arm920_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm922.S
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ arm922_processor_functions:
.word cpu_arm922_dcache_clean_area
.word cpu_arm922_switch_mm
.word cpu_arm922_set_pte_ext
.word 0
.word 0
.word 0
.size arm922_processor_functions, . - arm922_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm925.S
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,9 @@ arm925_processor_functions:
.word cpu_arm925_dcache_clean_area
.word cpu_arm925_switch_mm
.word cpu_arm925_set_pte_ext
.word 0
.word 0
.word 0
.size arm925_processor_functions, . - arm925_processor_functions

.section ".rodata"
Expand Down
37 changes: 37 additions & 0 deletions arch/arm/mm/proc-arm926.S
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,40 @@ ENTRY(cpu_arm926_set_pte_ext)
#endif
mov pc, lr

/* Suspend/resume support: taken from arch/arm/plat-s3c24xx/sleep.S */
.globl cpu_arm926_suspend_size
.equ cpu_arm926_suspend_size, 4 * 3
#ifdef CONFIG_PM
ENTRY(cpu_arm926_do_suspend)
stmfd sp!, {r4 - r7, lr}
mrc p15, 0, r4, c13, c0, 0 @ PID
mrc p15, 0, r5, c3, c0, 0 @ Domain ID
mrc p15, 0, r6, c2, c0, 0 @ TTB address
mrc p15, 0, r7, c1, c0, 0 @ Control register
stmia r0, {r4 - r7}
ldmfd sp!, {r4 - r7, pc}
ENDPROC(cpu_arm926_do_suspend)

ENTRY(cpu_arm926_do_resume)
mov ip, #0
mcr p15, 0, ip, c8, c7, 0 @ invalidate I+D TLBs
mcr p15, 0, ip, c7, c7, 0 @ invalidate I+D caches
ldmia r0, {r4 - r7}
mcr p15, 0, r4, c13, c0, 0 @ PID
mcr p15, 0, r5, c3, c0, 0 @ Domain ID
mcr p15, 0, r6, c2, c0, 0 @ TTB address
mov r0, r7 @ control register
mov r2, r6, lsr #14 @ get TTB0 base
mov r2, r2, lsl #14
ldr r3, =PMD_TYPE_SECT | PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | PMD_BIT4 | PMD_SECT_AP_WRITE
b cpu_resume_mmu
ENDPROC(cpu_arm926_do_resume)
#else
#define cpu_arm926_do_suspend 0
#define cpu_arm926_do_resume 0
#endif

__CPUINIT

.type __arm926_setup, #function
Expand Down Expand Up @@ -456,6 +490,9 @@ arm926_processor_functions:
.word cpu_arm926_dcache_clean_area
.word cpu_arm926_switch_mm
.word cpu_arm926_set_pte_ext
.word cpu_arm926_suspend_size
.word cpu_arm926_do_suspend
.word cpu_arm926_do_resume
.size arm926_processor_functions, . - arm926_processor_functions

.section ".rodata"
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mm/proc-arm940.S
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,9 @@ ENTRY(arm940_processor_functions)
.word cpu_arm940_dcache_clean_area
.word cpu_arm940_switch_mm
.word 0 @ cpu_*_set_pte
.word 0
.word 0
.word 0
.size arm940_processor_functions, . - arm940_processor_functions

.section ".rodata"
Expand Down
Loading

0 comments on commit f6b0fa0

Please sign in to comment.