| 
									
										
										
										
											2014-03-25 01:20:29 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Hibernation support specific for ARM | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Derived from work on ARM hibernation support by: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Ubuntu project, hibernation support for mach-dove | 
					
						
							|  |  |  |  * Copyright (C) 2010 Nokia Corporation (Hiroshi Doyu) | 
					
						
							|  |  |  |  * Copyright (C) 2010 Texas Instruments, Inc. (Teerth Reddy et al.) | 
					
						
							|  |  |  |  *  https://lkml.org/lkml/2010/6/18/4
 | 
					
						
							|  |  |  |  *  https://lists.linux-foundation.org/pipermail/linux-pm/2010-June/027422.html
 | 
					
						
							|  |  |  |  *  https://patchwork.kernel.org/patch/96442/
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2006 Rafael J. Wysocki <rjw@sisk.pl> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * License terms: GNU General Public License (GPL) version 2 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/mm.h>
 | 
					
						
							|  |  |  | #include <linux/suspend.h>
 | 
					
						
							|  |  |  | #include <asm/system_misc.h>
 | 
					
						
							|  |  |  | #include <asm/idmap.h>
 | 
					
						
							|  |  |  | #include <asm/suspend.h>
 | 
					
						
							|  |  |  | #include <asm/memory.h>
 | 
					
						
							| 
									
										
										
										
											2014-10-09 15:30:30 -07:00
										 |  |  | #include <asm/sections.h>
 | 
					
						
							| 
									
										
										
										
											2014-03-25 01:20:29 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | int pfn_is_nosave(unsigned long pfn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin); | 
					
						
							|  |  |  | 	unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void notrace save_processor_state(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WARN_ON(num_online_cpus() != 1); | 
					
						
							|  |  |  | 	local_fiq_disable(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void notrace restore_processor_state(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	local_fiq_enable(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Snapshot kernel memory and reset the system. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * swsusp_save() is executed in the suspend finisher so that the CPU | 
					
						
							|  |  |  |  * context pointer and memory are part of the saved image, which is | 
					
						
							|  |  |  |  * required by the resume kernel image to restart execution from | 
					
						
							|  |  |  |  * swsusp_arch_suspend(). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * soft_restart is not technically needed, but is used to get success | 
					
						
							|  |  |  |  * returned from cpu_suspend. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * When soft reboot completes, the hibernation snapshot is written out. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int notrace arch_save_image(unsigned long unused) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = swsusp_save(); | 
					
						
							|  |  |  | 	if (ret == 0) | 
					
						
							|  |  |  | 		soft_restart(virt_to_phys(cpu_resume)); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Save the current CPU state before suspend / poweroff. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int notrace swsusp_arch_suspend(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return cpu_suspend(0, arch_save_image); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Restore page contents for physical pages that were in use during loading | 
					
						
							|  |  |  |  * hibernation image.  Switch to idmap_pgd so the physical page tables | 
					
						
							|  |  |  |  * are overwritten with the same contents. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void notrace arch_restore_image(void *unused) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct pbe *pbe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cpu_switch_mm(idmap_pgd, &init_mm); | 
					
						
							|  |  |  | 	for (pbe = restore_pblist; pbe; pbe = pbe->next) | 
					
						
							|  |  |  | 		copy_page(pbe->orig_address, pbe->address); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	soft_restart(virt_to_phys(cpu_resume)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u64 resume_stack[PAGE_SIZE/2/sizeof(u64)] __nosavedata; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Resume from the hibernation image. | 
					
						
							|  |  |  |  * Due to the kernel heap / data restore, stack contents change underneath | 
					
						
							|  |  |  |  * and that would make function calls impossible; switch to a temporary | 
					
						
							|  |  |  |  * stack within the nosave region to avoid that problem. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int swsusp_arch_resume(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	extern void call_with_stack(void (*fn)(void *), void *arg, void *sp); | 
					
						
							|  |  |  | 	call_with_stack(arch_restore_image, 0, | 
					
						
							|  |  |  | 		resume_stack + ARRAY_SIZE(resume_stack)); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } |