diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-08-26 20:28:52 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2011-09-20 23:33:36 +0100 |
commit | e8ce0eb5e2254b85415e4b58e73f24a5d13846a1 (patch) | |
tree | 26aaee04d5a4bb872eea215f65073825258ecd76 /arch/arm/kernel/suspend.c | |
parent | f5fa68d9674156ddaafa12a058ccc93c8866d5f9 (diff) |
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 <santosh.shilimkar@ti.com>
Tested-by: Shawn Guo <shawn.guo@linaro.org>
Tested-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/kernel/suspend.c')
-rw-r--r-- | arch/arm/kernel/suspend.c | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/arch/arm/kernel/suspend.c b/arch/arm/kernel/suspend.c new file mode 100644 index 000000000000..0a33f109549d --- /dev/null +++ b/arch/arm/kernel/suspend.c @@ -0,0 +1,48 @@ +#include <linux/init.h> + +#include <asm/pgalloc.h> +#include <asm/pgtable.h> +#include <asm/memory.h> +#include <asm/suspend.h> +#include <asm/tlbflush.h> + +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); |