ARM: 8011/1: ARM hibernation / suspend-to-disk
Enable hibernation for ARM architectures and provide ARM
architecture specific calls used during hibernation.
The swsusp hibernation framework depends on the
platform first having functional suspend/resume.
Then, in order to enable hibernation on a given platform, a
platform_hibernation_ops structure may need to be registered with
the system in order to save/restore any SoC-specific / cpu specific
state needing (re)init over a suspend-to-disk/resume-from-disk cycle.
For example:
     - "secure" SoCs that have different sets of control registers
       and/or different CR reg access patterns.
     - SoCs with L2 caches as the activation sequence there is
       SoC-dependent; a full off-on cycle for L2 is not done
       by the hibernation support code.
     - SoCs requiring steps on wakeup _before_ the "generic" parts
       done by cpu_suspend / cpu_resume can work correctly.
     - SoCs having persistent state which is maintained during suspend
       and resume, but will be lost during the power off cycle after
       suspend-to-disk.
This is a rebase/rework of Frank Hofmann's v5 hibernation patchset.
Acked-by: Russ Dill <Russ.Dill@ti.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: Sebastian Capella <sebastian.capella@linaro.org>
Acked-by: Pavel Machek <pavel@ucw.cz>
Reviewed-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
[fixed duplicate virt_to_pfn() definition --rmk]
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
	
	
This commit is contained in:
		
					parent
					
						
							
								44ae903b96
							
						
					
				
			
			
				commit
				
					
						603fb42a66
					
				
			
		
					 4 changed files with 115 additions and 0 deletions
				
			
		|  | @ -2294,6 +2294,11 @@ config ARCH_SUSPEND_POSSIBLE | ||||||
| config ARM_CPU_SUSPEND | config ARM_CPU_SUSPEND | ||||||
| 	def_bool PM_SLEEP | 	def_bool PM_SLEEP | ||||||
| 
 | 
 | ||||||
|  | config ARCH_HIBERNATION_POSSIBLE | ||||||
|  | 	bool | ||||||
|  | 	depends on MMU | ||||||
|  | 	default y if ARCH_SUSPEND_POSSIBLE | ||||||
|  | 
 | ||||||
| endmenu | endmenu | ||||||
| 
 | 
 | ||||||
| source "net/Kconfig" | source "net/Kconfig" | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR)		+= arthur.o | ||||||
| obj-$(CONFIG_ISA_DMA)		+= dma-isa.o | obj-$(CONFIG_ISA_DMA)		+= dma-isa.o | ||||||
| obj-$(CONFIG_PCI)		+= bios32.o isa.o | obj-$(CONFIG_PCI)		+= bios32.o isa.o | ||||||
| obj-$(CONFIG_ARM_CPU_SUSPEND)	+= sleep.o suspend.o | obj-$(CONFIG_ARM_CPU_SUSPEND)	+= sleep.o suspend.o | ||||||
|  | obj-$(CONFIG_HIBERNATION)	+= hibernate.o | ||||||
| obj-$(CONFIG_SMP)		+= smp.o | obj-$(CONFIG_SMP)		+= smp.o | ||||||
| ifdef CONFIG_MMU | ifdef CONFIG_MMU | ||||||
| obj-$(CONFIG_SMP)		+= smp_tlb.o | obj-$(CONFIG_SMP)		+= smp_tlb.o | ||||||
|  |  | ||||||
							
								
								
									
										107
									
								
								arch/arm/kernel/hibernate.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								arch/arm/kernel/hibernate.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,107 @@ | ||||||
|  | /*
 | ||||||
|  |  * 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> | ||||||
|  | 
 | ||||||
|  | extern const void __nosave_begin, __nosave_end; | ||||||
|  | 
 | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  | @ -320,6 +320,8 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); | ||||||
| extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); | extern void hibernation_set_ops(const struct platform_hibernation_ops *ops); | ||||||
| extern int hibernate(void); | extern int hibernate(void); | ||||||
| extern bool system_entering_hibernation(void); | extern bool system_entering_hibernation(void); | ||||||
|  | asmlinkage int swsusp_save(void); | ||||||
|  | extern struct pbe *restore_pblist; | ||||||
| #else /* CONFIG_HIBERNATION */ | #else /* CONFIG_HIBERNATION */ | ||||||
| static inline void register_nosave_region(unsigned long b, unsigned long e) {} | static inline void register_nosave_region(unsigned long b, unsigned long e) {} | ||||||
| static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} | static inline void register_nosave_region_late(unsigned long b, unsigned long e) {} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sebastian Capella
				Sebastian Capella