Branch data Line data Source code
1 : : #include <linux/init.h>
2 : : #include <linux/slab.h>
3 : :
4 : : #include <asm/cacheflush.h>
5 : : #include <asm/idmap.h>
6 : : #include <asm/pgalloc.h>
7 : : #include <asm/pgtable.h>
8 : : #include <asm/memory.h>
9 : : #include <asm/smp_plat.h>
10 : : #include <asm/suspend.h>
11 : : #include <asm/tlbflush.h>
12 : :
13 : : extern int __cpu_suspend(unsigned long, int (*)(unsigned long), u32 cpuid);
14 : : extern void cpu_resume_mmu(void);
15 : :
16 : : #ifdef CONFIG_MMU
17 : : /*
18 : : * Hide the first two arguments to __cpu_suspend - these are an implementation
19 : : * detail which platform code shouldn't have to know about.
20 : : */
21 : 0 : int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
22 : : {
23 : 4947329 : struct mm_struct *mm = current->active_mm;
24 : 4947329 : u32 __mpidr = cpu_logical_map(smp_processor_id());
25 : : int ret;
26 : :
27 [ + ]: 4947329 : if (!idmap_pgd)
28 : : return -EINVAL;
29 : :
30 : : /*
31 : : * Provide a temporary page table with an identity mapping for
32 : : * the MMU-enable code, required for resuming. On successful
33 : : * resume (indicated by a zero return code), we need to switch
34 : : * back to the correct page tables.
35 : : */
36 : 4953319 : ret = __cpu_suspend(arg, fn, __mpidr);
37 [ + + ]: 4918418 : if (ret == 0) {
38 : 4684142 : cpu_switch_mm(mm->pgd, mm);
39 : : local_flush_bp_all();
40 : : local_flush_tlb_all();
41 : : }
42 : :
43 : 4920316 : return ret;
44 : : }
45 : : #else
46 : : int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
47 : : {
48 : : u32 __mpidr = cpu_logical_map(smp_processor_id());
49 : : return __cpu_suspend(arg, fn, __mpidr);
50 : : }
51 : : #define idmap_pgd NULL
52 : : #endif
53 : :
54 : : /*
55 : : * This is called by __cpu_suspend() to save the state, and do whatever
56 : : * flushing is required to ensure that when the CPU goes to sleep we have
57 : : * the necessary data available when the caches are not searched.
58 : : */
59 : 0 : void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr)
60 : : {
61 : : u32 *ctx = ptr;
62 : :
63 : 4832870 : *save_ptr = virt_to_phys(ptr);
64 : :
65 : : /* This must correspond to the LDM in cpu_resume() assembly */
66 : 9665740 : *ptr++ = virt_to_phys(idmap_pgd);
67 : 4832870 : *ptr++ = sp;
68 : 9665740 : *ptr++ = virt_to_phys(cpu_do_resume);
69 : :
70 : 4832870 : cpu_do_suspend(ptr);
71 : :
72 : 4726829 : flush_cache_louis();
73 : :
74 : : /*
75 : : * flush_cache_louis does not guarantee that
76 : : * save_ptr and ptr are cleaned to main memory,
77 : : * just up to the Level of Unification Inner Shareable.
78 : : * Since the context pointer and context itself
79 : : * are to be retrieved with the MMU off that
80 : : * data must be cleaned from all cache levels
81 : : * to main memory using "area" cache primitives.
82 : : */
83 : 4968060 : __cpuc_flush_dcache_area(ctx, ptrsz);
84 : 4967483 : __cpuc_flush_dcache_area(save_ptr, sizeof(*save_ptr));
85 : :
86 : 4967901 : outer_clean_range(*save_ptr, *save_ptr + ptrsz);
87 : 4965794 : outer_clean_range(virt_to_phys(save_ptr),
88 : : virt_to_phys(save_ptr) + sizeof(*save_ptr));
89 : 4965794 : }
90 : :
91 : : extern struct sleep_save_sp sleep_save_sp;
92 : :
93 : 0 : static int cpu_suspend_alloc_sp(void)
94 : : {
95 : : void *ctx_ptr;
96 : : /* ctx_ptr is an array of physical addresses */
97 : : ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(u32), GFP_KERNEL);
98 : :
99 [ # # ][ # # ]: 0 : if (WARN_ON(!ctx_ptr))
100 : : return -ENOMEM;
101 : 0 : sleep_save_sp.save_ptr_stash = ctx_ptr;
102 : 0 : sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
103 : : sync_cache_w(&sleep_save_sp);
104 : : return 0;
105 : : }
106 : : early_initcall(cpu_suspend_alloc_sp);
|