]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
LoongArch: Add hibernation (ACPI S4) support
authorHuacai Chen <chenhuacai@loongson.cn>
Sat, 10 Dec 2022 14:40:15 +0000 (22:40 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Wed, 14 Dec 2022 00:41:53 +0000 (08:41 +0800)
Add hibernation (Suspend to Disk, aka ACPI S4) support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/Kconfig
arch/loongarch/kernel/asm-offsets.c
arch/loongarch/kernel/reset.c
arch/loongarch/kernel/setup.c
arch/loongarch/power/Makefile
arch/loongarch/power/hibernate.c [new file with mode: 0644]
arch/loongarch/power/hibernate_asm.S [new file with mode: 0644]

index 0c8b2b1a96267d48002e5f2d6ea2770fec304943..576a649ac13c79605d13990e1d3d8be5cfe97395 100644 (file)
@@ -521,6 +521,9 @@ menu "Power management options"
 config ARCH_SUSPEND_POSSIBLE
        def_bool y
 
+config ARCH_HIBERNATION_POSSIBLE
+       def_bool y
+
 source "kernel/power/Kconfig"
 source "drivers/acpi/Kconfig"
 
index bdd88eda9513f62d7b1245eafcaaeed1b4a3f2a4..4ef494577813e7dab826d7758cbc047beabe660d 100644 (file)
@@ -257,3 +257,15 @@ void output_smpboot_defines(void)
        BLANK();
 }
 #endif
+
+#ifdef CONFIG_HIBERNATION
+void output_pbe_defines(void)
+{
+       COMMENT(" Linux struct pbe offsets. ");
+       OFFSET(PBE_ADDRESS, pbe, address);
+       OFFSET(PBE_ORIG_ADDRESS, pbe, orig_address);
+       OFFSET(PBE_NEXT, pbe, next);
+       DEFINE(PBE_SIZE, sizeof(struct pbe));
+       BLANK();
+}
+#endif
index 8c82021eb2f447d867560a77b3f5e6c276804bf4..1ef8c63835351ba7b04eb4838464927c1ac1c32c 100644 (file)
@@ -15,6 +15,7 @@
 #include <acpi/reboot.h>
 #include <asm/idle.h>
 #include <asm/loongarch.h>
+#include <asm/loongson.h>
 
 void (*pm_power_off)(void);
 EXPORT_SYMBOL(pm_power_off);
@@ -41,6 +42,10 @@ void machine_power_off(void)
 #ifdef CONFIG_SMP
        preempt_disable();
        smp_send_stop();
+#endif
+#ifdef CONFIG_PM
+       if (!acpi_disabled)
+               enable_pci_wakeup();
 #endif
        do_kernel_power_off();
 #ifdef CONFIG_EFI
index fdabf2ac1927dbd9730c7eb32dfd905eea778477..4344502c0b31780677a84cd5bb117004336df712 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/libfdt.h>
 #include <linux/of_fdt.h>
 #include <linux/of_address.h>
+#include <linux/suspend.h>
 #include <linux/swiotlb.h>
 
 #include <asm/addrspace.h>
@@ -370,6 +371,10 @@ static void __init arch_mem_init(char **cmdline_p)
 
        dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
 
+       /* Reserve for hibernation. */
+       register_nosave_region(PFN_DOWN(__pa_symbol(&__nosave_begin)),
+                                  PFN_UP(__pa_symbol(&__nosave_end)));
+
        memblock_dump_all();
 
        early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
index 6740117decaa27f41ba74432ba984ab0fe5ab1e6..58151d003e40ea17e82c02c74fafe16fa39bbf3b 100644 (file)
@@ -1,3 +1,4 @@
 obj-y  += platform.o
 
 obj-$(CONFIG_SUSPEND)          += suspend.o suspend_asm.o
+obj-$(CONFIG_HIBERNATION)      += hibernate.o hibernate_asm.o
diff --git a/arch/loongarch/power/hibernate.c b/arch/loongarch/power/hibernate.c
new file mode 100644 (file)
index 0000000..1e05905
--- /dev/null
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <asm/fpu.h>
+#include <asm/loongson.h>
+#include <asm/sections.h>
+#include <asm/tlbflush.h>
+#include <linux/suspend.h>
+
+static u32 saved_crmd;
+static u32 saved_prmd;
+static u32 saved_euen;
+static u32 saved_ecfg;
+static u64 saved_pcpu_base;
+struct pt_regs saved_regs;
+
+void save_processor_state(void)
+{
+       saved_crmd = csr_read32(LOONGARCH_CSR_CRMD);
+       saved_prmd = csr_read32(LOONGARCH_CSR_PRMD);
+       saved_euen = csr_read32(LOONGARCH_CSR_EUEN);
+       saved_ecfg = csr_read32(LOONGARCH_CSR_ECFG);
+       saved_pcpu_base = csr_read64(PERCPU_BASE_KS);
+
+       if (is_fpu_owner())
+               save_fp(current);
+}
+
+void restore_processor_state(void)
+{
+       csr_write32(saved_crmd, LOONGARCH_CSR_CRMD);
+       csr_write32(saved_prmd, LOONGARCH_CSR_PRMD);
+       csr_write32(saved_euen, LOONGARCH_CSR_EUEN);
+       csr_write32(saved_ecfg, LOONGARCH_CSR_ECFG);
+       csr_write64(saved_pcpu_base, PERCPU_BASE_KS);
+
+       if (is_fpu_owner())
+               restore_fp(current);
+}
+
+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);
+}
+
+extern int swsusp_asm_suspend(void);
+
+int swsusp_arch_suspend(void)
+{
+       enable_pci_wakeup();
+       return swsusp_asm_suspend();
+}
+
+extern int swsusp_asm_resume(void);
+
+int swsusp_arch_resume(void)
+{
+       /* Avoid TLB mismatch during and after kernel resume */
+       local_flush_tlb_all();
+       return swsusp_asm_resume();
+}
diff --git a/arch/loongarch/power/hibernate_asm.S b/arch/loongarch/power/hibernate_asm.S
new file mode 100644 (file)
index 0000000..3c747c0
--- /dev/null
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Hibernation support specific for LoongArch
+ *
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+ */
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/regdef.h>
+
+.text
+SYM_FUNC_START(swsusp_asm_suspend)
+       la.pcrel        t0, saved_regs
+       PTR_S           ra, t0, PT_R1
+       PTR_S           tp, t0, PT_R2
+       PTR_S           sp, t0, PT_R3
+       PTR_S           u0, t0, PT_R21
+       PTR_S           fp, t0, PT_R22
+       PTR_S           s0, t0, PT_R23
+       PTR_S           s1, t0, PT_R24
+       PTR_S           s2, t0, PT_R25
+       PTR_S           s3, t0, PT_R26
+       PTR_S           s4, t0, PT_R27
+       PTR_S           s5, t0, PT_R28
+       PTR_S           s6, t0, PT_R29
+       PTR_S           s7, t0, PT_R30
+       PTR_S           s8, t0, PT_R31
+       b               swsusp_save
+SYM_FUNC_END(swsusp_asm_suspend)
+
+SYM_FUNC_START(swsusp_asm_resume)
+       la.pcrel        t0, restore_pblist
+       PTR_L           t0, t0, 0
+0:
+       PTR_L           t1, t0, PBE_ADDRESS  /* source */
+       PTR_L           t2, t0, PBE_ORIG_ADDRESS /* destination */
+       PTR_LI          t3, _PAGE_SIZE
+       PTR_ADD         t3, t3, t1
+1:
+       REG_L           t8, t1, 0
+       REG_S           t8, t2, 0
+       PTR_ADDI        t1, t1, SZREG
+       PTR_ADDI        t2, t2, SZREG
+       bne             t1, t3, 1b
+       PTR_L           t0, t0, PBE_NEXT
+       bnez            t0, 0b
+       la.pcrel        t0, saved_regs
+       PTR_L           ra, t0, PT_R1
+       PTR_L           tp, t0, PT_R2
+       PTR_L           sp, t0, PT_R3
+       PTR_L           u0, t0, PT_R21
+       PTR_L           fp, t0, PT_R22
+       PTR_L           s0, t0, PT_R23
+       PTR_L           s1, t0, PT_R24
+       PTR_L           s2, t0, PT_R25
+       PTR_L           s3, t0, PT_R26
+       PTR_L           s4, t0, PT_R27
+       PTR_L           s5, t0, PT_R28
+       PTR_L           s6, t0, PT_R29
+       PTR_L           s7, t0, PT_R30
+       PTR_L           s8, t0, PT_R31
+       PTR_LI          a0, 0x0
+       jirl            zero, ra, 0
+SYM_FUNC_END(swsusp_asm_resume)