2014-01-24 17:56:19 +07:00
|
|
|
#include <linux/percpu.h>
|
arm64: kernel: cpu_{suspend/resume} implementation
Kernel subsystems like CPU idle and suspend to RAM require a generic
mechanism to suspend a processor, save its context and put it into
a quiescent state. The cpu_{suspend}/{resume} implementation provides
such a framework through a kernel interface allowing to save/restore
registers, flush the context to DRAM and suspend/resume to/from
low-power states where processor context may be lost.
The CPU suspend implementation relies on the suspend protocol registered
in CPU operations to carry out a suspend request after context is
saved and flushed to DRAM. The cpu_suspend interface:
int cpu_suspend(unsigned long arg);
allows to pass an opaque parameter that is handed over to the suspend CPU
operations back-end so that it can take action according to the
semantics attached to it. The arg parameter allows suspend to RAM and CPU
idle drivers to communicate to suspend protocol back-ends; it requires
standardization so that the interface can be reused seamlessly across
systems, paving the way for generic drivers.
Context memory is allocated on the stack, whose address is stashed in a
per-cpu variable to keep track of it and passed to core functions that
save/restore the registers required by the architecture.
Even though, upon successful execution, the cpu_suspend function shuts
down the suspending processor, the warm boot resume mechanism, based
on the cpu_resume function, makes the resume path operate as a
cpu_suspend function return, so that cpu_suspend can be treated as a C
function by the caller, which simplifies coding the PM drivers that rely
on the cpu_suspend API.
Upon context save, the minimal amount of memory is flushed to DRAM so
that it can be retrieved when the MMU is off and caches are not searched.
The suspend CPU operation, depending on the required operations (eg CPU vs
Cluster shutdown) is in charge of flushing the cache hierarchy either
implicitly (by calling firmware implementations like PSCI) or explicitly
by executing the required cache maintainance functions.
Debug exceptions are disabled during cpu_{suspend}/{resume} operations
so that debug registers can be saved and restored properly preventing
preemption from debug agents enabled in the kernel.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2013-07-22 18:22:13 +07:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
#include <asm/cpu_ops.h>
|
|
|
|
#include <asm/debug-monitors.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/memory.h>
|
|
|
|
#include <asm/smp_plat.h>
|
|
|
|
#include <asm/suspend.h>
|
|
|
|
#include <asm/tlbflush.h>
|
|
|
|
|
|
|
|
extern int __cpu_suspend(unsigned long);
|
|
|
|
/*
|
|
|
|
* This is called by __cpu_suspend() to save the state, and do whatever
|
|
|
|
* flushing is required to ensure that when the CPU goes to sleep we have
|
|
|
|
* the necessary data available when the caches are not searched.
|
|
|
|
*
|
|
|
|
* @arg: Argument to pass to suspend operations
|
|
|
|
* @ptr: CPU context virtual address
|
|
|
|
* @save_ptr: address of the location where the context physical address
|
|
|
|
* must be saved
|
|
|
|
*/
|
|
|
|
int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr,
|
|
|
|
phys_addr_t *save_ptr)
|
|
|
|
{
|
|
|
|
int cpu = smp_processor_id();
|
|
|
|
|
|
|
|
*save_ptr = virt_to_phys(ptr);
|
|
|
|
|
|
|
|
cpu_do_suspend(ptr);
|
|
|
|
/*
|
|
|
|
* Only flush the context that must be retrieved with the MMU
|
|
|
|
* off. VA primitives ensure the flush is applied to all
|
|
|
|
* cache levels so context is pushed to DRAM.
|
|
|
|
*/
|
|
|
|
__flush_dcache_area(ptr, sizeof(*ptr));
|
|
|
|
__flush_dcache_area(save_ptr, sizeof(*save_ptr));
|
|
|
|
|
|
|
|
return cpu_ops[cpu]->cpu_suspend(arg);
|
|
|
|
}
|
|
|
|
|
2014-01-10 20:15:05 +07:00
|
|
|
/*
|
|
|
|
* This hook is provided so that cpu_suspend code can restore HW
|
|
|
|
* breakpoints as early as possible in the resume path, before reenabling
|
|
|
|
* debug exceptions. Code cannot be run from a CPU PM notifier since by the
|
|
|
|
* time the notifier runs debug exceptions might have been enabled already,
|
|
|
|
* with HW breakpoints registers content still in an unknown state.
|
|
|
|
*/
|
|
|
|
void (*hw_breakpoint_restore)(void *);
|
|
|
|
void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *))
|
|
|
|
{
|
|
|
|
/* Prevent multiple restore hook initializations */
|
|
|
|
if (WARN_ON(hw_breakpoint_restore))
|
|
|
|
return;
|
|
|
|
hw_breakpoint_restore = hw_bp_restore;
|
|
|
|
}
|
|
|
|
|
arm64: kernel: cpu_{suspend/resume} implementation
Kernel subsystems like CPU idle and suspend to RAM require a generic
mechanism to suspend a processor, save its context and put it into
a quiescent state. The cpu_{suspend}/{resume} implementation provides
such a framework through a kernel interface allowing to save/restore
registers, flush the context to DRAM and suspend/resume to/from
low-power states where processor context may be lost.
The CPU suspend implementation relies on the suspend protocol registered
in CPU operations to carry out a suspend request after context is
saved and flushed to DRAM. The cpu_suspend interface:
int cpu_suspend(unsigned long arg);
allows to pass an opaque parameter that is handed over to the suspend CPU
operations back-end so that it can take action according to the
semantics attached to it. The arg parameter allows suspend to RAM and CPU
idle drivers to communicate to suspend protocol back-ends; it requires
standardization so that the interface can be reused seamlessly across
systems, paving the way for generic drivers.
Context memory is allocated on the stack, whose address is stashed in a
per-cpu variable to keep track of it and passed to core functions that
save/restore the registers required by the architecture.
Even though, upon successful execution, the cpu_suspend function shuts
down the suspending processor, the warm boot resume mechanism, based
on the cpu_resume function, makes the resume path operate as a
cpu_suspend function return, so that cpu_suspend can be treated as a C
function by the caller, which simplifies coding the PM drivers that rely
on the cpu_suspend API.
Upon context save, the minimal amount of memory is flushed to DRAM so
that it can be retrieved when the MMU is off and caches are not searched.
The suspend CPU operation, depending on the required operations (eg CPU vs
Cluster shutdown) is in charge of flushing the cache hierarchy either
implicitly (by calling firmware implementations like PSCI) or explicitly
by executing the required cache maintainance functions.
Debug exceptions are disabled during cpu_{suspend}/{resume} operations
so that debug registers can be saved and restored properly preventing
preemption from debug agents enabled in the kernel.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2013-07-22 18:22:13 +07:00
|
|
|
/**
|
|
|
|
* cpu_suspend
|
|
|
|
*
|
|
|
|
* @arg: argument to pass to the finisher function
|
|
|
|
*/
|
|
|
|
int cpu_suspend(unsigned long arg)
|
|
|
|
{
|
|
|
|
struct mm_struct *mm = current->active_mm;
|
|
|
|
int ret, cpu = smp_processor_id();
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If cpu_ops have not been registered or suspend
|
|
|
|
* has not been initialized, cpu_suspend call fails early.
|
|
|
|
*/
|
|
|
|
if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* From this point debug exceptions are disabled to prevent
|
|
|
|
* updates to mdscr register (saved and restored along with
|
|
|
|
* general purpose registers) from kernel debuggers.
|
|
|
|
*/
|
|
|
|
local_dbg_save(flags);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* mm context saved on the stack, it will be restored when
|
|
|
|
* the cpu comes out of reset through the identity mapped
|
|
|
|
* page tables, so that the thread address space is properly
|
|
|
|
* set-up on function return.
|
|
|
|
*/
|
|
|
|
ret = __cpu_suspend(arg);
|
|
|
|
if (ret == 0) {
|
|
|
|
cpu_switch_mm(mm->pgd, mm);
|
|
|
|
flush_tlb_all();
|
2014-01-24 17:56:19 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore per-cpu offset before any kernel
|
|
|
|
* subsystem relying on it has a chance to run.
|
|
|
|
*/
|
|
|
|
set_my_cpu_offset(per_cpu_offset(cpu));
|
|
|
|
|
2014-01-10 20:15:05 +07:00
|
|
|
/*
|
|
|
|
* Restore HW breakpoint registers to sane values
|
|
|
|
* before debug exceptions are possibly reenabled
|
|
|
|
* through local_dbg_restore.
|
|
|
|
*/
|
|
|
|
if (hw_breakpoint_restore)
|
|
|
|
hw_breakpoint_restore(NULL);
|
arm64: kernel: cpu_{suspend/resume} implementation
Kernel subsystems like CPU idle and suspend to RAM require a generic
mechanism to suspend a processor, save its context and put it into
a quiescent state. The cpu_{suspend}/{resume} implementation provides
such a framework through a kernel interface allowing to save/restore
registers, flush the context to DRAM and suspend/resume to/from
low-power states where processor context may be lost.
The CPU suspend implementation relies on the suspend protocol registered
in CPU operations to carry out a suspend request after context is
saved and flushed to DRAM. The cpu_suspend interface:
int cpu_suspend(unsigned long arg);
allows to pass an opaque parameter that is handed over to the suspend CPU
operations back-end so that it can take action according to the
semantics attached to it. The arg parameter allows suspend to RAM and CPU
idle drivers to communicate to suspend protocol back-ends; it requires
standardization so that the interface can be reused seamlessly across
systems, paving the way for generic drivers.
Context memory is allocated on the stack, whose address is stashed in a
per-cpu variable to keep track of it and passed to core functions that
save/restore the registers required by the architecture.
Even though, upon successful execution, the cpu_suspend function shuts
down the suspending processor, the warm boot resume mechanism, based
on the cpu_resume function, makes the resume path operate as a
cpu_suspend function return, so that cpu_suspend can be treated as a C
function by the caller, which simplifies coding the PM drivers that rely
on the cpu_suspend API.
Upon context save, the minimal amount of memory is flushed to DRAM so
that it can be retrieved when the MMU is off and caches are not searched.
The suspend CPU operation, depending on the required operations (eg CPU vs
Cluster shutdown) is in charge of flushing the cache hierarchy either
implicitly (by calling firmware implementations like PSCI) or explicitly
by executing the required cache maintainance functions.
Debug exceptions are disabled during cpu_{suspend}/{resume} operations
so that debug registers can be saved and restored properly preventing
preemption from debug agents enabled in the kernel.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2013-07-22 18:22:13 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restore pstate flags. OS lock and mdscr have been already
|
|
|
|
* restored, so from this point onwards, debugging is fully
|
|
|
|
* renabled if it was enabled when core started shutdown.
|
|
|
|
*/
|
|
|
|
local_dbg_restore(flags);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern struct sleep_save_sp sleep_save_sp;
|
|
|
|
extern phys_addr_t sleep_idmap_phys;
|
|
|
|
|
2014-07-18 00:19:20 +07:00
|
|
|
static int __init cpu_suspend_init(void)
|
arm64: kernel: cpu_{suspend/resume} implementation
Kernel subsystems like CPU idle and suspend to RAM require a generic
mechanism to suspend a processor, save its context and put it into
a quiescent state. The cpu_{suspend}/{resume} implementation provides
such a framework through a kernel interface allowing to save/restore
registers, flush the context to DRAM and suspend/resume to/from
low-power states where processor context may be lost.
The CPU suspend implementation relies on the suspend protocol registered
in CPU operations to carry out a suspend request after context is
saved and flushed to DRAM. The cpu_suspend interface:
int cpu_suspend(unsigned long arg);
allows to pass an opaque parameter that is handed over to the suspend CPU
operations back-end so that it can take action according to the
semantics attached to it. The arg parameter allows suspend to RAM and CPU
idle drivers to communicate to suspend protocol back-ends; it requires
standardization so that the interface can be reused seamlessly across
systems, paving the way for generic drivers.
Context memory is allocated on the stack, whose address is stashed in a
per-cpu variable to keep track of it and passed to core functions that
save/restore the registers required by the architecture.
Even though, upon successful execution, the cpu_suspend function shuts
down the suspending processor, the warm boot resume mechanism, based
on the cpu_resume function, makes the resume path operate as a
cpu_suspend function return, so that cpu_suspend can be treated as a C
function by the caller, which simplifies coding the PM drivers that rely
on the cpu_suspend API.
Upon context save, the minimal amount of memory is flushed to DRAM so
that it can be retrieved when the MMU is off and caches are not searched.
The suspend CPU operation, depending on the required operations (eg CPU vs
Cluster shutdown) is in charge of flushing the cache hierarchy either
implicitly (by calling firmware implementations like PSCI) or explicitly
by executing the required cache maintainance functions.
Debug exceptions are disabled during cpu_{suspend}/{resume} operations
so that debug registers can be saved and restored properly preventing
preemption from debug agents enabled in the kernel.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
2013-07-22 18:22:13 +07:00
|
|
|
{
|
|
|
|
void *ctx_ptr;
|
|
|
|
|
|
|
|
/* ctx_ptr is an array of physical addresses */
|
|
|
|
ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(phys_addr_t), GFP_KERNEL);
|
|
|
|
|
|
|
|
if (WARN_ON(!ctx_ptr))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
sleep_save_sp.save_ptr_stash = ctx_ptr;
|
|
|
|
sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
|
|
|
|
sleep_idmap_phys = virt_to_phys(idmap_pg_dir);
|
|
|
|
__flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp));
|
|
|
|
__flush_dcache_area(&sleep_idmap_phys, sizeof(sleep_idmap_phys));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
early_initcall(cpu_suspend_init);
|