Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74e69e9f authored by Maulik Shah's avatar Maulik Shah
Browse files

arm: kernel: refactor the CPU suspend API for retention states



CPU suspend is the standard kernel interface to be used to enter
low-power states on ARM32 systems. Current cpu_suspend implementation
by default assumes that all low power states are losing the CPU context,
so the CPU registers must be saved and cleaned to DRAM upon state
entry. Furthermore, the current cpu_suspend() implementation assumes
that if the CPU suspend back-end method returns when called, this has
to be considered an error regardless of the return code (which can be
successful) since the CPU was not expected to return from a code path
that is different from cpu_resume code path - eg returning from the reset
vector.

All in all this means that the current API does not cope well with
low-power states that preserve the CPU context when entered
(ie retention states), since first of all the context is saved for nothing
on state entry for those states and a successful state entry can return as
a normal function return, which is considered an error by the current CPU
suspend implementation.

This patch refactors the cpu_suspend() API so that it can be split in
two separate functionalities. The arm32 cpu_suspend API just provides
a wrapper around CPU suspend operation hook. A new function is
introduced (for architecture code use only) for states that require
context saving upon entry:

__cpu_suspend(unsigned long arg, int (*fn)(unsigned long))

__cpu_suspend() saves the context on function entry and calls the
so called suspend finisher (ie fn) to complete the suspend operation.
The finisher is not expected to return, unless it fails in which case
the error is propagated back to the __cpu_suspend caller.

The API refactoring results in the following pseudo code call sequence
for a suspending CPU, when triggered from a kernel subsystem:

/*
 * int cpu_suspend(unsigned long idx)
 * @idx: idle state index
 */
{
-> cpu_suspend(idx)
	|---> CPU operations suspend hook called, if present
		|--> if (retention_state)
			|--> direct suspend back-end call (eg PSCI suspend)
		     else
			|--> __cpu_suspend(idx, &back_end_finisher);
}

By refactoring the cpu_suspend API this way, the CPU operations back-end
has a chance to detect whether idle states require state saving or not and
can call the required suspend operations accordingly either through simple
function call or indirectly through __cpu_suspend() which carries out
state saving and suspend finisher dispatching to complete idle state entry.

Change-Id: I02567729f882fb7c8e1f9ba054c55aeb8be4c31c
Signed-off-by: default avatarMaulik Shah <mkshah@codeaurora.org>
parent 84a40fce
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -7,6 +7,10 @@ struct sleep_save_sp {
};

extern void cpu_resume(void);
extern int cpu_suspend(unsigned long, int (*)(unsigned long));
extern int cpu_suspend(unsigned long);

extern int __cpu_suspend(unsigned long, int (*fn)(unsigned long));
extern int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long),
							unsigned int);

#endif
+2 −2
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@
 *  r1 = suspend function
 *  r2 = MPIDR value the resuming CPU will use
 */
ENTRY(__cpu_suspend)
ENTRY(__cpu_suspend_enter)
	stmfd	sp!, {r4 - r11, lr}
#ifdef MULTI_CPU
	ldr	r10, =processor
@@ -83,7 +83,7 @@ ENTRY(__cpu_suspend)
	bl	__cpu_suspend_save
	adr	lr, BSYM(cpu_suspend_abort)
	ldmfd	sp!, {r0, pc}		@ call suspend fn
ENDPROC(__cpu_suspend)
ENDPROC(__cpu_suspend_enter)
	.ltorg

cpu_suspend_abort:
+15 −5
Original line number Diff line number Diff line
@@ -9,8 +9,8 @@
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include <asm/tlbflush.h>
#include <asm/psci.h>

extern int __cpu_suspend(unsigned long, int (*)(unsigned long), u32 cpuid);
extern void cpu_resume_mmu(void);

#ifdef CONFIG_MMU
@@ -18,7 +18,7 @@ extern void cpu_resume_mmu(void);
 * Hide the first two arguments to __cpu_suspend - these are an implementation
 * detail which platform code shouldn't have to know about.
 */
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
	struct mm_struct *mm = current->active_mm;
	u32 __mpidr = cpu_logical_map(smp_processor_id());
@@ -33,7 +33,7 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
	 * resume (indicated by a zero return code), we need to switch
	 * back to the correct page tables.
	 */
	ret = __cpu_suspend(arg, fn, __mpidr);
	ret = __cpu_suspend_enter(arg, fn, __mpidr);
	if (ret == 0) {
		cpu_switch_mm(mm->pgd, mm);
		local_flush_bp_all();
@@ -43,10 +43,10 @@ int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
	return ret;
}
#else
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
int __cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
	u32 __mpidr = cpu_logical_map(smp_processor_id());
	return __cpu_suspend(arg, fn, __mpidr);
	return __cpu_suspend_enter(arg, fn, __mpidr);
}
#define	idmap_pgd	NULL
#endif
@@ -88,6 +88,16 @@ void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr)
			  virt_to_phys(save_ptr) + sizeof(*save_ptr));
}

int cpu_suspend(unsigned long arg)
{
#if defined(CONFIG_ARM_PSCI)
	return cpu_psci_cpu_suspend(arg);
#else
	WARN_ONCE(true, "PSCI is not enabled\n");
	return 0;
#endif
}

extern struct sleep_save_sp sleep_save_sp;

static int cpu_suspend_alloc_sp(void)
+0 −5
Original line number Diff line number Diff line
@@ -285,13 +285,8 @@ static bool __ref msm_pm_spm_power_collapse(

	msm_jtag_save_state();

#ifdef CONFIG_CPU_V7
	collapsed = save_cpu_regs ?
		!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
#else
	collapsed = save_cpu_regs ?
		!__cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
#endif

	msm_jtag_restore_state();