summaryrefslogtreecommitdiff
path: root/arch/arm/kernel/suspend.c
blob: 0a33f109549de4a8cde488053f28ec0c2e414532 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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);