xref: /openbmc/linux/arch/arm/kernel/suspend.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
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