Skip to content

Commit

Permalink
ARM: 8011/1: ARM hibernation / suspend-to-disk
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Sebastian Capella authored and Russell King committed Apr 23, 2014
1 parent 44ae903 commit 603fb42
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
5 changes: 5 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2294,6 +2294,11 @@ config ARCH_SUSPEND_POSSIBLE
config ARM_CPU_SUSPEND
def_bool PM_SLEEP

config ARCH_HIBERNATION_POSSIBLE
bool
depends on MMU
default y if ARCH_SUSPEND_POSSIBLE

endmenu

source "net/Kconfig"
Expand Down
1 change: 1 addition & 0 deletions arch/arm/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o
obj-$(CONFIG_HIBERNATION) += hibernate.o
obj-$(CONFIG_SMP) += smp.o
ifdef CONFIG_MMU
obj-$(CONFIG_SMP) += smp_tlb.o
Expand Down
107 changes: 107 additions & 0 deletions arch/arm/kernel/hibernate.c
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 2 additions & 0 deletions include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 int hibernate(void);
extern bool system_entering_hibernation(void);
asmlinkage int swsusp_save(void);
extern struct pbe *restore_pblist;
#else /* CONFIG_HIBERNATION */
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) {}
Expand Down

0 comments on commit 603fb42

Please sign in to comment.