xtensa: add hibernation support

Define ARCH_HIBERNATION_POSSIBLE in Kconfig and implement hibernation
callbacks.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
This commit is contained in:
Max Filippov 2022-04-20 06:07:20 -07:00
parent 11e969bc96
commit 733f5c28e5
5 changed files with 129 additions and 0 deletions

View File

@ -787,6 +787,9 @@ endmenu
menu "Power management options"
config ARCH_HIBERNATION_POSSIBLE
def_bool y
source "kernel/power/Kconfig"
endmenu

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_XTENSA_VARIANT_HAVE_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
obj-$(CONFIG_S32C1I_SELFTEST) += s32c1i_selftest.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
obj-$(CONFIG_HIBERNATION) += hibernate.o
# In the Xtensa architecture, assembly generates literals which must always
# precede the L32R instruction with a relative offset less than 256 kB.

View File

@ -21,6 +21,7 @@
#include <linux/ptrace.h>
#include <linux/mm.h>
#include <linux/kbuild.h>
#include <linux/suspend.h>
#include <asm/ptrace.h>
#include <asm/traps.h>
@ -149,5 +150,12 @@ int main(void)
offsetof(struct exc_table, fast_kernel_handler));
DEFINE(EXC_TABLE_DEFAULT, offsetof(struct exc_table, default_handler));
#ifdef CONFIG_HIBERNATION
DEFINE(PBE_ADDRESS, offsetof(struct pbe, address));
DEFINE(PBE_ORIG_ADDRESS, offsetof(struct pbe, orig_address));
DEFINE(PBE_NEXT, offsetof(struct pbe, next));
DEFINE(PBE_SIZE, sizeof(struct pbe));
#endif
return 0;
}

View File

@ -2155,3 +2155,95 @@ ENTRY(ret_from_kernel_thread)
j common_exception_return
ENDPROC(ret_from_kernel_thread)
#ifdef CONFIG_HIBERNATION
.bss
.align 4
.Lsaved_regs:
#if defined(__XTENSA_WINDOWED_ABI__)
.fill 2, 4
#elif defined(__XTENSA_CALL0_ABI__)
.fill 6, 4
#else
#error Unsupported Xtensa ABI
#endif
.align XCHAL_NCP_SA_ALIGN
.Lsaved_user_regs:
.fill XTREGS_USER_SIZE, 1
.previous
ENTRY(swsusp_arch_suspend)
abi_entry_default
movi a2, .Lsaved_regs
movi a3, .Lsaved_user_regs
s32i a0, a2, 0
s32i a1, a2, 4
save_xtregs_user a3 a4 a5 a6 a7 a8 0
#if defined(__XTENSA_WINDOWED_ABI__)
spill_registers_kernel
#elif defined(__XTENSA_CALL0_ABI__)
s32i a12, a2, 8
s32i a13, a2, 12
s32i a14, a2, 16
s32i a15, a2, 20
#else
#error Unsupported Xtensa ABI
#endif
abi_call swsusp_save
mov a2, abi_rv
abi_ret_default
ENDPROC(swsusp_arch_suspend)
ENTRY(swsusp_arch_resume)
abi_entry_default
#if defined(__XTENSA_WINDOWED_ABI__)
spill_registers_kernel
#endif
movi a2, restore_pblist
l32i a2, a2, 0
.Lcopy_pbe:
l32i a3, a2, PBE_ADDRESS
l32i a4, a2, PBE_ORIG_ADDRESS
__loopi a3, a9, PAGE_SIZE, 16
l32i a5, a3, 0
l32i a6, a3, 4
l32i a7, a3, 8
l32i a8, a3, 12
addi a3, a3, 16
s32i a5, a4, 0
s32i a6, a4, 4
s32i a7, a4, 8
s32i a8, a4, 12
addi a4, a4, 16
__endl a3, a9
l32i a2, a2, PBE_NEXT
bnez a2, .Lcopy_pbe
movi a2, .Lsaved_regs
movi a3, .Lsaved_user_regs
l32i a0, a2, 0
l32i a1, a2, 4
load_xtregs_user a3 a4 a5 a6 a7 a8 0
#if defined(__XTENSA_CALL0_ABI__)
l32i a12, a2, 8
l32i a13, a2, 12
l32i a14, a2, 16
l32i a15, a2, 20
#endif
movi a2, 0
abi_ret_default
ENDPROC(swsusp_arch_resume)
#endif

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/mm.h>
#include <linux/suspend.h>
#include <asm/coprocessor.h>
int pfn_is_nosave(unsigned long pfn)
{
unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
unsigned long nosave_end_pfn = PFN_UP(__pa(&__nosave_end));
return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
}
void notrace save_processor_state(void)
{
WARN_ON(num_online_cpus() != 1);
#if XTENSA_HAVE_COPROCESSORS
local_coprocessors_flush_release_all();
#endif
}
void notrace restore_processor_state(void)
{
}