From f5fa68d9674156ddaafa12a058ccc93c8866d5f9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 27 Aug 2011 11:17:36 +0100 Subject: ARM: pm: force non-zero return value from __cpu_suspend when aborting Ensure that the return value from __cpu_suspend is non-zero when aborting. Zero indicates a successful suspend occurred. Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/sleep.S | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index dc902f2c684..46a9f460db8 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -61,6 +61,8 @@ ENDPROC(__cpu_suspend) cpu_suspend_abort: ldmia sp!, {r1 - r3} @ pop v:p, virt SP, phys resume fn + teq r0, #0 + moveq r0, #1 @ force non-zero value mov sp, r2 ldmfd sp!, {r4 - r11, pc} ENDPROC(cpu_suspend_abort) -- cgit v1.2.2 From e8ce0eb5e2254b85415e4b58e73f24a5d13846a1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 26 Aug 2011 20:28:52 +0100 Subject: ARM: pm: preallocate a page table for suspend/resume Preallocate a page table and setup an identity mapping for the MMU enable code. This means we don't have to "borrow" a page table to do this, avoiding complexities with L2 cache coherency. Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/Makefile | 2 +- arch/arm/kernel/sleep.S | 33 ++++++++++++-------------------- arch/arm/kernel/suspend.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 arch/arm/kernel/suspend.c (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index f7887dc53c1..787b88823a5 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -29,7 +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) += sleep.o +obj-$(CONFIG_PM_SLEEP) += sleep.o suspend.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 diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index 46a9f460db8..8cf13de1e36 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -27,7 +27,7 @@ ENTRY(__cpu_suspend) sub sp, sp, r5 @ allocate CPU state on stack mov r0, sp @ save pointer to CPU save block add ip, ip, r1 @ convert resume fn to phys - stmfd sp!, {r1, r6, ip} @ save v:p, virt SP, phys resume fn + stmfd sp!, {r6, ip} @ save virt SP, phys resume fn ldr r5, =sleep_save_sp add r6, sp, r1 @ convert SP to phys stmfd sp!, {r2, r3} @ save suspend func arg and pointer @@ -60,7 +60,7 @@ ENDPROC(__cpu_suspend) .ltorg cpu_suspend_abort: - ldmia sp!, {r1 - r3} @ pop v:p, virt SP, phys resume fn + ldmia sp!, {r2 - r3} @ pop virt SP, phys resume fn teq r0, #0 moveq r0, #1 @ force non-zero value mov sp, r2 @@ -74,28 +74,19 @@ ENDPROC(cpu_suspend_abort) * 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 +ENTRY(cpu_resume_turn_mmu_on) + mcr p15, 0, r0, c1, c0, 0 @ turn on MMU, I-cache, etc + mrc p15, 0, r0, c0, c0, 0 @ read id reg + mov r0, r0 + mov r0, r0 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 bl cpu_init @ restore the und/abt/irq banked regs mov r0, #0 @ return zero on success ldmfd sp!, {r4 - r11, pc} @@ -121,11 +112,11 @@ ENTRY(cpu_resume) ldr r0, sleep_save_sp @ stack phys addr #endif setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off - @ load v:p, stack, resume fn - ARM( ldmia r0!, {r1, sp, pc} ) -THUMB( ldmia r0!, {r1, r2, r3} ) -THUMB( mov sp, r2 ) -THUMB( bx r3 ) + @ load stack, resume fn + ARM( ldmia r0!, {sp, pc} ) +THUMB( ldmia r0!, {r2, r3} ) +THUMB( mov sp, r2 ) +THUMB( bx r3 ) ENDPROC(cpu_resume) sleep_save_sp: diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c new file mode 100644 index 00000000000..0a33f109549 --- /dev/null +++ b/arch/arm/kernel/suspend.c @@ -0,0 +1,48 @@ +#include + +#include +#include +#include +#include +#include + +static pgd_t *suspend_pgd; + +extern int __cpu_suspend(int, long, unsigned long, int (*)(unsigned long)); +extern void cpu_resume_turn_mmu_on(void); + +/* + * Hide the first two arguments to __cpu_suspend - these are an implementation + * detail which platform code shouldn't have to know about. + */ +int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) +{ + struct mm_struct *mm = current->active_mm; + int ret; + + if (!suspend_pgd) + return -EINVAL; + + /* + * Temporarily switch the page tables to our suspend page + * tables, which contain the temporary identity mapping + * required for resuming. + */ + cpu_switch_mm(suspend_pgd, mm); + ret = __cpu_suspend(0, PHYS_OFFSET - PAGE_OFFSET, arg, fn); + cpu_switch_mm(mm->pgd, mm); + local_flush_tlb_all(); + + return ret; +} + +static int __init cpu_suspend_init(void) +{ + suspend_pgd = pgd_alloc(&init_mm); + if (suspend_pgd) { + unsigned long addr = virt_to_phys(cpu_resume_turn_mmu_on); + identity_mapping_add(suspend_pgd, addr, addr + SECTION_SIZE); + } + return suspend_pgd ? 0 : -ENOMEM; +} +core_initcall(cpu_suspend_init); -- cgit v1.2.2 From de8e71ca4f2e17329f6718ae88d5c8336cb249ee Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 27 Aug 2011 22:39:09 +0100 Subject: ARM: pm: only use preallocated page table during resume Only use the preallocated page table during the resume, not while suspending. This avoids the overhead of having to switch unnecessarily to the resume page table in the suspend path. Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/sleep.S | 19 +++++++++---------- arch/arm/kernel/suspend.c | 17 ++++++++++------- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index 8cf13de1e36..25d42dfb0a9 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -9,12 +9,14 @@ /* * Save CPU state for a suspend + * r0 = phys addr of temporary page tables * r1 = v:p offset * r2 = suspend function arg0 * r3 = suspend function */ ENTRY(__cpu_suspend) stmfd sp!, {r4 - r11, lr} + mov r4, r0 #ifdef MULTI_CPU ldr r10, =processor ldr r5, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state @@ -27,7 +29,7 @@ ENTRY(__cpu_suspend) sub sp, sp, r5 @ allocate CPU state on stack mov r0, sp @ save pointer to CPU save block add ip, ip, r1 @ convert resume fn to phys - stmfd sp!, {r6, ip} @ save virt SP, phys resume fn + stmfd sp!, {r4, r6, ip} @ save phys pgd, virt SP, phys resume fn ldr r5, =sleep_save_sp add r6, sp, r1 @ convert SP to phys stmfd sp!, {r2, r3} @ save suspend func arg and pointer @@ -60,7 +62,7 @@ ENDPROC(__cpu_suspend) .ltorg cpu_suspend_abort: - ldmia sp!, {r2 - r3} @ pop virt SP, phys resume fn + ldmia sp!, {r1 - r3} @ pop phys pgd, virt SP, phys resume fn teq r0, #0 moveq r0, #1 @ force non-zero value mov sp, r2 @@ -69,9 +71,6 @@ ENDPROC(cpu_suspend_abort) /* * 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) ldr r3, =cpu_resume_after_mmu @@ -112,11 +111,11 @@ ENTRY(cpu_resume) ldr r0, sleep_save_sp @ stack phys addr #endif setmode PSR_I_BIT | PSR_F_BIT | SVC_MODE, r1 @ set SVC, irqs off - @ load stack, resume fn - ARM( ldmia r0!, {sp, pc} ) -THUMB( ldmia r0!, {r2, r3} ) -THUMB( mov sp, r2 ) -THUMB( bx r3 ) + @ load phys pgd, stack, resume fn + ARM( ldmia r0!, {r1, sp, pc} ) +THUMB( ldmia r0!, {r1, r2, r3} ) +THUMB( mov sp, r2 ) +THUMB( bx r3 ) ENDPROC(cpu_resume) sleep_save_sp: diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index 0a33f109549..2beda56e457 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -24,14 +24,17 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) return -EINVAL; /* - * Temporarily switch the page tables to our suspend page - * tables, which contain the temporary identity mapping - * required for resuming. + * Provide a temporary page table with an identity mapping for + * the MMU-enable code, required for resuming. On successful + * resume (indicated by a zero return code), we need to switch + * back to the correct page tables. */ - cpu_switch_mm(suspend_pgd, mm); - ret = __cpu_suspend(0, PHYS_OFFSET - PAGE_OFFSET, arg, fn); - cpu_switch_mm(mm->pgd, mm); - local_flush_tlb_all(); + ret = __cpu_suspend(virt_to_phys(suspend_pgd), + PHYS_OFFSET - PAGE_OFFSET, arg, fn); + if (ret == 0) { + cpu_switch_mm(mm->pgd, mm); + local_flush_tlb_all(); + } return ret; } -- cgit v1.2.2 From 62b2d07c0ea9db40a1787d2d0ab49f03c3e0613c Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 31 Aug 2011 23:26:18 +0100 Subject: ARM: pm: get rid of cpu_resume_turn_mmu_on We don't require cpu_resume_turn_mmu_on as we can combine the ldr instruction with the following code provided we ensure that cpu_resume_mmu is aligned for older CPUs. Note that we also align to a 32-byte boundary to ensure that the code can't cross a section boundary. Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/sleep.S | 8 ++------ arch/arm/kernel/suspend.c | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index 25d42dfb0a9..c9a43caaea8 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -72,19 +72,15 @@ ENDPROC(cpu_suspend_abort) /* * r0 = control register value */ + .align 5 ENTRY(cpu_resume_mmu) ldr r3, =cpu_resume_after_mmu - b cpu_resume_turn_mmu_on -ENDPROC(cpu_resume_mmu) - .ltorg - .align 5 -ENTRY(cpu_resume_turn_mmu_on) mcr p15, 0, r0, c1, c0, 0 @ turn on MMU, I-cache, etc mrc p15, 0, r0, c0, c0, 0 @ read id reg mov r0, r0 mov r0, r0 mov pc, r3 @ jump to virtual address -ENDPROC(cpu_resume_turn_mmu_on) +ENDPROC(cpu_resume_mmu) cpu_resume_after_mmu: bl cpu_init @ restore the und/abt/irq banked regs mov r0, #0 @ return zero on success diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index 2beda56e457..ed4160b64e6 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -9,7 +9,7 @@ static pgd_t *suspend_pgd; extern int __cpu_suspend(int, long, unsigned long, int (*)(unsigned long)); -extern void cpu_resume_turn_mmu_on(void); +extern void cpu_resume_mmu(void); /* * Hide the first two arguments to __cpu_suspend - these are an implementation @@ -43,7 +43,7 @@ static int __init cpu_suspend_init(void) { suspend_pgd = pgd_alloc(&init_mm); if (suspend_pgd) { - unsigned long addr = virt_to_phys(cpu_resume_turn_mmu_on); + unsigned long addr = virt_to_phys(cpu_resume_mmu); identity_mapping_add(suspend_pgd, addr, addr + SECTION_SIZE); } return suspend_pgd ? 0 : -ENOMEM; -- cgit v1.2.2 From abda1bd5f4e04054ce083c298fcd68a743e9df03 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 1 Sep 2011 11:52:33 +0100 Subject: ARM: pm: convert some assembly to C Convert some of the sleep.S guts to C code, which makes it easier to use our macros and to add L2 cache handling. We provide a helper function, __cpu_suspend_save(), which deals with saving the common state, setting up for resume, and flushing caches. The remainder left as assembly code is the saving of the CPU general purpose registers, and allocating space on the stack to save the CPU specific registers and resume state. Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/sleep.S | 53 +++++++++++++++-------------------------------- arch/arm/kernel/suspend.c | 24 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 39 deletions(-) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/sleep.S b/arch/arm/kernel/sleep.S index c9a43caaea8..020e99c845e 100644 --- a/arch/arm/kernel/sleep.S +++ b/arch/arm/kernel/sleep.S @@ -8,54 +8,35 @@ .text /* - * Save CPU state for a suspend - * r0 = phys addr of temporary page tables - * r1 = v:p offset - * r2 = suspend function arg0 - * r3 = suspend function + * Save CPU state for a suspend. This saves the CPU general purpose + * registers, and allocates space on the kernel stack to save the CPU + * specific registers and some other data for resume. + * r0 = suspend function arg0 + * r1 = suspend function */ ENTRY(__cpu_suspend) stmfd sp!, {r4 - r11, lr} - mov r4, r0 #ifdef MULTI_CPU ldr r10, =processor - ldr r5, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state - ldr ip, [r10, #CPU_DO_RESUME] @ virtual resume function + ldr r4, [r10, #CPU_SLEEP_SIZE] @ size of CPU sleep state #else - ldr r5, =cpu_suspend_size - ldr ip, =cpu_do_resume + ldr r4, =cpu_suspend_size #endif - mov r6, sp @ current virtual SP - sub sp, sp, r5 @ allocate CPU state on stack - mov r0, sp @ save pointer to CPU save block - add ip, ip, r1 @ convert resume fn to phys - stmfd sp!, {r4, r6, ip} @ save phys pgd, virt SP, phys resume fn - ldr r5, =sleep_save_sp - add r6, sp, r1 @ convert SP to phys - stmfd sp!, {r2, r3} @ save suspend func arg and pointer + mov r5, sp @ current virtual SP + add r4, r4, #12 @ Space for pgd, virt sp, phys resume fn + sub sp, sp, r4 @ allocate CPU state on stack + stmfd sp!, {r0, r1} @ save suspend func arg and pointer + add r0, sp, #8 @ save pointer to save block + mov r1, r4 @ size of save block + mov r2, r5 @ virtual SP + ldr r3, =sleep_save_sp #ifdef CONFIG_SMP ALT_SMP(mrc p15, 0, lr, c0, c0, 5) ALT_UP(mov lr, #0) and lr, lr, #15 - str r6, [r5, lr, lsl #2] @ save phys SP -#else - str r6, [r5] @ save phys SP -#endif -#ifdef MULTI_CPU - mov lr, pc - ldr pc, [r10, #CPU_DO_SUSPEND] @ save CPU state -#else - bl cpu_do_suspend -#endif - - @ flush data cache -#ifdef MULTI_CACHE - ldr r10, =cpu_cache - mov lr, pc - ldr pc, [r10, #CACHE_FLUSH_KERN_ALL] -#else - bl __cpuc_flush_kern_all + add r3, r3, lr, lsl #2 #endif + bl __cpu_suspend_save adr lr, BSYM(cpu_suspend_abort) ldmfd sp!, {r0, pc} @ call suspend fn ENDPROC(__cpu_suspend) diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index ed4160b64e6..2d60f190320 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -8,9 +8,28 @@ static pgd_t *suspend_pgd; -extern int __cpu_suspend(int, long, unsigned long, int (*)(unsigned long)); +extern int __cpu_suspend(unsigned long, int (*)(unsigned long)); extern void cpu_resume_mmu(void); +/* + * This is called by __cpu_suspend() to save the state, and do whatever + * flushing is required to ensure that when the CPU goes to sleep we have + * the necessary data available when the caches are not searched. + */ +void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr) +{ + *save_ptr = virt_to_phys(ptr); + + /* This must correspond to the LDM in cpu_resume() assembly */ + *ptr++ = virt_to_phys(suspend_pgd); + *ptr++ = sp; + *ptr++ = virt_to_phys(cpu_do_resume); + + cpu_do_suspend(ptr); + + flush_cache_all(); +} + /* * Hide the first two arguments to __cpu_suspend - these are an implementation * detail which platform code shouldn't have to know about. @@ -29,8 +48,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long)) * resume (indicated by a zero return code), we need to switch * back to the correct page tables. */ - ret = __cpu_suspend(virt_to_phys(suspend_pgd), - PHYS_OFFSET - PAGE_OFFSET, arg, fn); + ret = __cpu_suspend(arg, fn); if (ret == 0) { cpu_switch_mm(mm->pgd, mm); local_flush_tlb_all(); -- cgit v1.2.2 From 8e6f83bbdf770014c070c5a41c8e89617cb2a66b Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 1 Sep 2011 11:57:59 +0100 Subject: ARM: pm: add L2 cache cleaning for suspend We need to ensure that state is pushed out from the L2 cache when suspending so that the resume paths can access their data before the MMU and caches have been re-initialized. Add the necessary calls to __cpu_suspend_save(). Tested-by: Santosh Shilimkar Tested-by: Shawn Guo Tested-by: Lorenzo Pieralisi Signed-off-by: Russell King --- arch/arm/kernel/suspend.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arch/arm/kernel') diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c index 2d60f190320..93a22d282c1 100644 --- a/arch/arm/kernel/suspend.c +++ b/arch/arm/kernel/suspend.c @@ -28,6 +28,9 @@ void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr) cpu_do_suspend(ptr); flush_cache_all(); + outer_clean_range(*save_ptr, *save_ptr + ptrsz); + outer_clean_range(virt_to_phys(save_ptr), + virt_to_phys(save_ptr) + sizeof(*save_ptr)); } /* -- cgit v1.2.2