Commit 733f5c28 authored by Max Filippov's avatar Max Filippov
Browse files

xtensa: add hibernation support



Define ARCH_HIBERNATION_POSSIBLE in Kconfig and implement hibernation
callbacks.

Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
parent 11e969bc
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -787,6 +787,9 @@ endmenu

menu "Power management options"

config ARCH_HIBERNATION_POSSIBLE
	def_bool y

source "kernel/power/Kconfig"

endmenu
+1 −0
Original line number Diff line number Diff line
@@ -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.
+8 −0
Original line number Diff line number Diff line
@@ -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;
}
+92 −0
Original line number Diff line number Diff line
@@ -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
+25 −0
Original line number Diff line number Diff line
// 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)
{
}