1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28252ca87Slouis.wang #include <linux/ftrace.h>
3e8ce0eb5SRussell King #include <linux/init.h>
47604537bSLorenzo Pieralisi #include <linux/slab.h>
5589ee628SIngo Molnar #include <linux/mm_types.h>
665fddcfcSMike Rapoport #include <linux/pgtable.h>
7e8ce0eb5SRussell King
826602161SRussell King #include <asm/bugs.h>
97604537bSLorenzo Pieralisi #include <asm/cacheflush.h>
10e6eadc67SWill Deacon #include <asm/idmap.h>
11*a9ff6961SLinus Walleij #include <asm/page.h>
127604537bSLorenzo Pieralisi #include <asm/smp_plat.h>
13e8ce0eb5SRussell King #include <asm/suspend.h>
14e8ce0eb5SRussell King #include <asm/tlbflush.h>
15e8ce0eb5SRussell King
1671a8986dSNicolas Pitre extern int __cpu_suspend(unsigned long, int (*)(unsigned long), u32 cpuid);
1762b2d07cSRussell King extern void cpu_resume_mmu(void);
18e8ce0eb5SRussell King
19aa1aadc3SWill Deacon #ifdef CONFIG_MMU
cpu_suspend(unsigned long arg,int (* fn)(unsigned long))20aa1aadc3SWill Deacon int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
21aa1aadc3SWill Deacon {
22aa1aadc3SWill Deacon struct mm_struct *mm = current->active_mm;
2371a8986dSNicolas Pitre u32 __mpidr = cpu_logical_map(smp_processor_id());
24aa1aadc3SWill Deacon int ret;
25aa1aadc3SWill Deacon
26aa1aadc3SWill Deacon if (!idmap_pgd)
27aa1aadc3SWill Deacon return -EINVAL;
28aa1aadc3SWill Deacon
29aa1aadc3SWill Deacon /*
308252ca87Slouis.wang * Function graph tracer state gets incosistent when the kernel
318252ca87Slouis.wang * calls functions that never return (aka suspend finishers) hence
328252ca87Slouis.wang * disable graph tracing during their execution.
338252ca87Slouis.wang */
348252ca87Slouis.wang pause_graph_tracing();
358252ca87Slouis.wang
368252ca87Slouis.wang /*
37aa1aadc3SWill Deacon * Provide a temporary page table with an identity mapping for
38aa1aadc3SWill Deacon * the MMU-enable code, required for resuming. On successful
39aa1aadc3SWill Deacon * resume (indicated by a zero return code), we need to switch
40aa1aadc3SWill Deacon * back to the correct page tables.
41aa1aadc3SWill Deacon */
4271a8986dSNicolas Pitre ret = __cpu_suspend(arg, fn, __mpidr);
438252ca87Slouis.wang
448252ca87Slouis.wang unpause_graph_tracing();
458252ca87Slouis.wang
46aa1aadc3SWill Deacon if (ret == 0) {
47aa1aadc3SWill Deacon cpu_switch_mm(mm->pgd, mm);
48aa1aadc3SWill Deacon local_flush_bp_all();
49aa1aadc3SWill Deacon local_flush_tlb_all();
5026602161SRussell King check_other_bugs();
51aa1aadc3SWill Deacon }
52aa1aadc3SWill Deacon
53aa1aadc3SWill Deacon return ret;
54aa1aadc3SWill Deacon }
55aa1aadc3SWill Deacon #else
cpu_suspend(unsigned long arg,int (* fn)(unsigned long))56aa1aadc3SWill Deacon int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
57aa1aadc3SWill Deacon {
5871a8986dSNicolas Pitre u32 __mpidr = cpu_logical_map(smp_processor_id());
598252ca87Slouis.wang int ret;
608252ca87Slouis.wang
618252ca87Slouis.wang pause_graph_tracing();
628252ca87Slouis.wang ret = __cpu_suspend(arg, fn, __mpidr);
638252ca87Slouis.wang unpause_graph_tracing();
648252ca87Slouis.wang
658252ca87Slouis.wang return ret;
66aa1aadc3SWill Deacon }
67aa1aadc3SWill Deacon #define idmap_pgd NULL
68aa1aadc3SWill Deacon #endif
69aa1aadc3SWill Deacon
70e8ce0eb5SRussell King /*
71abda1bd5SRussell King * This is called by __cpu_suspend() to save the state, and do whatever
72abda1bd5SRussell King * flushing is required to ensure that when the CPU goes to sleep we have
73abda1bd5SRussell King * the necessary data available when the caches are not searched.
74abda1bd5SRussell King */
__cpu_suspend_save(u32 * ptr,u32 ptrsz,u32 sp,u32 * save_ptr)75abda1bd5SRussell King void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr)
76abda1bd5SRussell King {
77dbee0c6fSLorenzo Pieralisi u32 *ctx = ptr;
78dbee0c6fSLorenzo Pieralisi
79abda1bd5SRussell King *save_ptr = virt_to_phys(ptr);
80abda1bd5SRussell King
81abda1bd5SRussell King /* This must correspond to the LDM in cpu_resume() assembly */
82e6eadc67SWill Deacon *ptr++ = virt_to_phys(idmap_pgd);
83abda1bd5SRussell King *ptr++ = sp;
84abda1bd5SRussell King *ptr++ = virt_to_phys(cpu_do_resume);
85abda1bd5SRussell King
86abda1bd5SRussell King cpu_do_suspend(ptr);
87abda1bd5SRussell King
88dbee0c6fSLorenzo Pieralisi flush_cache_louis();
89dbee0c6fSLorenzo Pieralisi
90dbee0c6fSLorenzo Pieralisi /*
91dbee0c6fSLorenzo Pieralisi * flush_cache_louis does not guarantee that
92dbee0c6fSLorenzo Pieralisi * save_ptr and ptr are cleaned to main memory,
93dbee0c6fSLorenzo Pieralisi * just up to the Level of Unification Inner Shareable.
94dbee0c6fSLorenzo Pieralisi * Since the context pointer and context itself
95dbee0c6fSLorenzo Pieralisi * are to be retrieved with the MMU off that
96dbee0c6fSLorenzo Pieralisi * data must be cleaned from all cache levels
97dbee0c6fSLorenzo Pieralisi * to main memory using "area" cache primitives.
98dbee0c6fSLorenzo Pieralisi */
99dbee0c6fSLorenzo Pieralisi __cpuc_flush_dcache_area(ctx, ptrsz);
100dbee0c6fSLorenzo Pieralisi __cpuc_flush_dcache_area(save_ptr, sizeof(*save_ptr));
101dbee0c6fSLorenzo Pieralisi
1028e6f83bbSRussell King outer_clean_range(*save_ptr, *save_ptr + ptrsz);
1038e6f83bbSRussell King outer_clean_range(virt_to_phys(save_ptr),
1048e6f83bbSRussell King virt_to_phys(save_ptr) + sizeof(*save_ptr));
105abda1bd5SRussell King }
1067604537bSLorenzo Pieralisi
1077604537bSLorenzo Pieralisi extern struct sleep_save_sp sleep_save_sp;
1087604537bSLorenzo Pieralisi
cpu_suspend_alloc_sp(void)1097604537bSLorenzo Pieralisi static int cpu_suspend_alloc_sp(void)
1107604537bSLorenzo Pieralisi {
1117604537bSLorenzo Pieralisi void *ctx_ptr;
1127604537bSLorenzo Pieralisi /* ctx_ptr is an array of physical addresses */
1137604537bSLorenzo Pieralisi ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(u32), GFP_KERNEL);
1147604537bSLorenzo Pieralisi
1157604537bSLorenzo Pieralisi if (WARN_ON(!ctx_ptr))
1167604537bSLorenzo Pieralisi return -ENOMEM;
1177604537bSLorenzo Pieralisi sleep_save_sp.save_ptr_stash = ctx_ptr;
1187604537bSLorenzo Pieralisi sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
1197604537bSLorenzo Pieralisi sync_cache_w(&sleep_save_sp);
1207604537bSLorenzo Pieralisi return 0;
1217604537bSLorenzo Pieralisi }
1227604537bSLorenzo Pieralisi early_initcall(cpu_suspend_alloc_sp);
123