[linux-pm] [RFC PATCH v4] ARM hibernation/suspend-to-disk support
Frank Hofmann
frank.hofmann at tomtom.com
Fri Jun 10 08:22:24 EDT 2011
On Thu, 9 Jun 2011, Russell King - ARM Linux wrote:
> On Thu, Jun 09, 2011 at 10:23:07PM +0530, Santosh Shilimkar wrote:
[ ... ]
>> 3. Avoid direct write to AUXCTRL in generic suspend code.
>
> This is the only problematical one that I can see. We need to restore
> this on systems running in secure mode. What we could do is rather than
> writing to the register, read it first and compare its value with what
> was saved to see whether we need to write it.
>
> Then, if platforms run in non-secure mode, they are responsible for
> restoring that register back to its pre-suspend value before their
> assembly calls cpu_resume().
While this is ok from the point of view of having cpu_suspend / resume
being service functions for the platform-specific idle/off-mode code, it
also illustrates the difficulty this creates for the hibernation code.
If it's not possible to call cpu_suspend / cpu_resume (or something like
it - not tied to names ...) as a full-featured generic interface, then
creating a true snapshot capability becomes problematic.
>
>> 4. Before MMU is enabled in resume a callback to restore
>> secure register, setup auxctrl etc.
>
> You can do this before your assembly calls cpu_resume().
Only that it's known at the point of call to cpu_suspend/resume.
Again, I admit to being biased regarding the usecase here ...
See below.
>
>> Additionally the L2 cache handling isn't part of
>> these common suspend hooks.
>
> L2 cache handling can't fit into the generic code - it doesn't really
> belong there either. It needs to be in the parent or hooked into the
> syscore_ops stuff as I've said previously.
For the "take things down" side, dode like machine_restart() already is
able to flush/inval/disable all these things on the way down, without any
SoC-specific knowledge.
OMAP suspend/resume has, just recently, gone half-way there (use the
provided flush/inval instead of the home-grown table walker code),
Agree with that. Cache flushing / disabling / invalidation has interfaces
(the outer_*, l2* and cache ops) already, and e.g. the OMAP code has
recently started to use some of those (kernel_flush instead of the
home-grown inval loop). Looks like that part is on its way.
On the resume side, there's actually a problem here - the generic way of
enabling a cache is through initcalls - which don't happen on resume, so
if the system comes out of a "low enough" state there's some work to do
here - which generic cpu_resume() does not do.
>
> So:
>
> ENTRY(my_soc_suspend)
> stmfd sp!, {r4 - r12, lr}
> ldr r3, =resume
> bl cpu_suspend
> /*
> * Insert whatever code is required here for suspend
> * eg, save secure mode, then jump to sram to call WFI function
> */
> resume:
> ldmfd sp!, {r4 - r12, pc}
> ENDPROC(my_soc_suspend)
>
> ENTRY(my_soc_resume)
> /*
> * Insert whatever other code is required to be run before resume
> * eg, WFI function returns to this symbol after DDR becomes
> * accessible. restore secure mode state
> */
> b cpu_resume
> ENDPROC(my_soc_resume)
That alone doesn't accommodate the following situations:
a) there might be pre-suspend / post-resume activities necessary, i.e.
the assumption that any SoC-specific "go down" activity can be done
after cpu_suspend() and any SoC-specific "bring up" activity before
cpu_resume() might not be sufficient.
Case in point: Reenabling L2 caches after resume.
b) my bias - snapshotting state (for hibernation).
Delegating this to a SoC-specific method risks creating code like
the OMAP stuff - where state saving, power management, off mode
and whatnot is all interwoven and interdependent.
It also creates the problem that _generic_ (platform-independent)
hibernation code becomes impossible to do ...
A clean thing are separate steps:
<mach preparation for suspend state snapshot>
<generic state snapshot>
<mach state snapshot>
...
<wfi> /* or not ... */
...
<mach prep for resume / basic initialization>
<generic resume>
<mach resume>
So why not have those hooks _inside_ cpu_suspend / cpu_resume, i.e. like:
.data
ENTRY(cpu_suspend)
mov r9, lr
#ifdef CONFIG_ARCH_NEEDS_SPAGHETTI_SUSPEND
mov lr, pc
ldr pc, mach_pre_suspend_hook
#endif
...
#ifdef CONFIG_ARCH_NEEDS_SPAGHETTI_SUSPEND
mov lr, r9
ldr r4, mach_post_suspend_hook
b r4
#else
mov pc, r9
END(cpu_suspend)
#ifdef CONFIG_ARCH_NEEDS_SPAGHETTI_SUSPEND
mach_pre_suspend_hook:
.long 0
mach_post_suspend_hook:
.long 0
#endif
and let the SoC initialization set them if it so desires ?
This would allow to use them for snapshotting the state as well.
Key point, again, from the hibernation bias, is really to have that
stuff _separate_ from power-down / wfi / whatever-to-enter-lowpower-mode.
As many of these activities as possible should be dealt with by sysdev /
syscore, agreed; but unfortunately there might be certain things,
especially around secure state, that are too closely tied in to delegate
it to those ?
>
> What makes it far more complicated in the OMAP case is all that "is l1 state
> lost? is l2 state lost?" stuff.
>
It looks like it's structured this way:
omap_cpu_suspend()
{
switch (state) {
case 3:
case 1:
/* save context */
case 2:
/* clean caches */
case 0:
wfi();
}
}
omap_cpu_resume() /* from OFF, case 2 / 0 never happens */
{
if (state == 3)
/* disable / inval L2 */
/* restore context */
/* reenable L2 */
}
But the code doesn't perfectly match the comments in it.
FrankH.
More information about the linux-arm-kernel
mailing list