LoongArch: Add suspend (ACPI S3) support
Add suspend (Suspend To RAM, aka ACPI S3) support for LoongArch. Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
parent
27cab43156
commit
366bb35a8e
@ -56,6 +56,7 @@ config LOONGARCH
|
||||
select ARCH_WANTS_NO_INSTR
|
||||
select BUILDTIME_TABLE_SORT
|
||||
select COMMON_CLK
|
||||
select CPU_PM
|
||||
select EFI
|
||||
select GENERIC_CLOCKEVENTS
|
||||
select GENERIC_CMOS_UPDATE
|
||||
@ -517,6 +518,10 @@ config ARCH_MMAP_RND_BITS_MAX
|
||||
|
||||
menu "Power management options"
|
||||
|
||||
config ARCH_SUSPEND_POSSIBLE
|
||||
def_bool y
|
||||
|
||||
source "kernel/power/Kconfig"
|
||||
source "drivers/acpi/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -104,6 +104,9 @@ endif
|
||||
libs-y += arch/loongarch/lib/
|
||||
libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
||||
|
||||
# suspend and hibernation support
|
||||
drivers-$(CONFIG_PM) += arch/loongarch/power/
|
||||
|
||||
ifeq ($(KBUILD_EXTMOD),)
|
||||
prepare: vdso_prepare
|
||||
vdso_prepare: prepare0
|
||||
|
@ -35,4 +35,14 @@ extern struct list_head acpi_wakeup_device_list;
|
||||
|
||||
#define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
|
||||
|
||||
extern int loongarch_acpi_suspend(void);
|
||||
extern int (*acpi_suspend_lowlevel)(void);
|
||||
extern void loongarch_suspend_enter(void);
|
||||
|
||||
static inline unsigned long acpi_get_wakeup_address(void)
|
||||
{
|
||||
extern void loongarch_wakeup_start(void);
|
||||
return (unsigned long)loongarch_wakeup_start;
|
||||
}
|
||||
|
||||
#endif /* _ASM_LOONGARCH_ACPI_H */
|
||||
|
@ -32,6 +32,7 @@ struct loongson_system_configuration {
|
||||
int cores_per_node;
|
||||
int cores_per_package;
|
||||
unsigned long cores_io_master;
|
||||
unsigned long suspend_addr;
|
||||
const char *cpuname;
|
||||
};
|
||||
|
||||
|
@ -136,4 +136,7 @@ typedef enum {
|
||||
#define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val)
|
||||
#define ls7a_writeq(val, addr) *(volatile unsigned long *)TO_UNCACHE(addr) = (val)
|
||||
|
||||
void enable_gpe_wakeup(void);
|
||||
void enable_pci_wakeup(void);
|
||||
|
||||
#endif /* __ASM_LOONGSON_H */
|
||||
|
@ -12,6 +12,7 @@
|
||||
extern u64 cpu_clock_freq;
|
||||
extern u64 const_clock_freq;
|
||||
|
||||
extern void save_counter(void);
|
||||
extern void sync_counter(void);
|
||||
|
||||
static inline unsigned int calc_const_freq(void)
|
||||
|
@ -140,6 +140,12 @@ static void __init acpi_process_madt(void)
|
||||
loongson_sysconf.nr_cpus = num_processors;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SUSPEND
|
||||
int (*acpi_suspend_lowlevel)(void);
|
||||
#else
|
||||
int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend;
|
||||
#endif
|
||||
|
||||
void __init acpi_boot_table_init(void)
|
||||
{
|
||||
/*
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/smp.h>
|
||||
#include <linux/threads.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/tracepoint.h>
|
||||
#include <linux/sched/hotplug.h>
|
||||
|
@ -115,12 +115,17 @@ static unsigned long __init get_loops_per_jiffy(void)
|
||||
return lpj;
|
||||
}
|
||||
|
||||
static long init_timeval;
|
||||
static long init_offset __nosavedata;
|
||||
|
||||
void save_counter(void)
|
||||
{
|
||||
init_offset = drdtime();
|
||||
}
|
||||
|
||||
void sync_counter(void)
|
||||
{
|
||||
/* Ensure counter begin at 0 */
|
||||
csr_write64(-init_timeval, LOONGARCH_CSR_CNTC);
|
||||
csr_write64(init_offset, LOONGARCH_CSR_CNTC);
|
||||
}
|
||||
|
||||
static int get_timer_irq(void)
|
||||
@ -219,7 +224,7 @@ void __init time_init(void)
|
||||
else
|
||||
const_clock_freq = calc_const_freq();
|
||||
|
||||
init_timeval = drdtime() - csr_read64(LOONGARCH_CSR_CNTC);
|
||||
init_offset = -(drdtime() - csr_read64(LOONGARCH_CSR_CNTC));
|
||||
|
||||
constant_clockevent_init();
|
||||
constant_clocksource_init();
|
||||
|
3
arch/loongarch/power/Makefile
Normal file
3
arch/loongarch/power/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
obj-y += platform.o
|
||||
|
||||
obj-$(CONFIG_SUSPEND) += suspend.o suspend_asm.o
|
57
arch/loongarch/power/platform.c
Normal file
57
arch/loongarch/power/platform.c
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Author: Huacai Chen <chenhuacai@loongson.cn>
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/loongson.h>
|
||||
|
||||
void enable_gpe_wakeup(void)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
return;
|
||||
|
||||
if (acpi_gbl_reduced_hardware)
|
||||
return;
|
||||
|
||||
acpi_enable_all_wakeup_gpes();
|
||||
}
|
||||
|
||||
void enable_pci_wakeup(void)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
return;
|
||||
|
||||
if (acpi_gbl_reduced_hardware)
|
||||
return;
|
||||
|
||||
acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_STATUS, 1);
|
||||
|
||||
if (acpi_gbl_FADT.flags & ACPI_FADT_PCI_EXPRESS_WAKE)
|
||||
acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0);
|
||||
}
|
||||
|
||||
static int __init loongson3_acpi_suspend_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ACPI
|
||||
acpi_status status;
|
||||
uint64_t suspend_addr = 0;
|
||||
|
||||
if (acpi_disabled || acpi_gbl_reduced_hardware)
|
||||
return 0;
|
||||
|
||||
acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
|
||||
status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr);
|
||||
if (ACPI_FAILURE(status) || !suspend_addr) {
|
||||
pr_err("ACPI S3 is not support!\n");
|
||||
return -1;
|
||||
}
|
||||
loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(loongson3_acpi_suspend_init);
|
73
arch/loongarch/power/suspend.c
Normal file
73
arch/loongarch/power/suspend.c
Normal file
@ -0,0 +1,73 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* loongson-specific suspend support
|
||||
*
|
||||
* Author: Huacai Chen <chenhuacai@loongson.cn>
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/loongarch.h>
|
||||
#include <asm/loongson.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/time.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
u64 loongarch_suspend_addr;
|
||||
|
||||
struct saved_registers {
|
||||
u32 ecfg;
|
||||
u32 euen;
|
||||
u64 pgd;
|
||||
u64 kpgd;
|
||||
u32 pwctl0;
|
||||
u32 pwctl1;
|
||||
};
|
||||
static struct saved_registers saved_regs;
|
||||
|
||||
static void arch_common_suspend(void)
|
||||
{
|
||||
save_counter();
|
||||
saved_regs.pgd = csr_read64(LOONGARCH_CSR_PGDL);
|
||||
saved_regs.kpgd = csr_read64(LOONGARCH_CSR_PGDH);
|
||||
saved_regs.pwctl0 = csr_read32(LOONGARCH_CSR_PWCTL0);
|
||||
saved_regs.pwctl1 = csr_read32(LOONGARCH_CSR_PWCTL1);
|
||||
saved_regs.ecfg = csr_read32(LOONGARCH_CSR_ECFG);
|
||||
saved_regs.euen = csr_read32(LOONGARCH_CSR_EUEN);
|
||||
|
||||
loongarch_suspend_addr = loongson_sysconf.suspend_addr;
|
||||
}
|
||||
|
||||
static void arch_common_resume(void)
|
||||
{
|
||||
sync_counter();
|
||||
local_flush_tlb_all();
|
||||
csr_write64(per_cpu_offset(0), PERCPU_BASE_KS);
|
||||
csr_write64(eentry, LOONGARCH_CSR_EENTRY);
|
||||
csr_write64(eentry, LOONGARCH_CSR_MERRENTRY);
|
||||
csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
|
||||
|
||||
csr_write64(saved_regs.pgd, LOONGARCH_CSR_PGDL);
|
||||
csr_write64(saved_regs.kpgd, LOONGARCH_CSR_PGDH);
|
||||
csr_write32(saved_regs.pwctl0, LOONGARCH_CSR_PWCTL0);
|
||||
csr_write32(saved_regs.pwctl1, LOONGARCH_CSR_PWCTL1);
|
||||
csr_write32(saved_regs.ecfg, LOONGARCH_CSR_ECFG);
|
||||
csr_write32(saved_regs.euen, LOONGARCH_CSR_EUEN);
|
||||
}
|
||||
|
||||
int loongarch_acpi_suspend(void)
|
||||
{
|
||||
enable_gpe_wakeup();
|
||||
enable_pci_wakeup();
|
||||
|
||||
arch_common_suspend();
|
||||
|
||||
/* processor specific suspend */
|
||||
loongarch_suspend_enter();
|
||||
|
||||
arch_common_resume();
|
||||
|
||||
return 0;
|
||||
}
|
89
arch/loongarch/power/suspend_asm.S
Normal file
89
arch/loongarch/power/suspend_asm.S
Normal file
@ -0,0 +1,89 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Sleep helper for Loongson-3 sleep mode.
|
||||
*
|
||||
* Author: Huacai Chen <chenhuacai@loongson.cn>
|
||||
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
#include <asm/asm.h>
|
||||
#include <asm/asmmacro.h>
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/loongarch.h>
|
||||
#include <asm/stackframe.h>
|
||||
|
||||
/* preparatory stuff */
|
||||
.macro SETUP_SLEEP
|
||||
addi.d sp, sp, -PT_SIZE
|
||||
st.d $r1, sp, PT_R1
|
||||
st.d $r2, sp, PT_R2
|
||||
st.d $r3, sp, PT_R3
|
||||
st.d $r4, sp, PT_R4
|
||||
st.d $r21, sp, PT_R21
|
||||
st.d $r22, sp, PT_R22
|
||||
st.d $r23, sp, PT_R23
|
||||
st.d $r24, sp, PT_R24
|
||||
st.d $r25, sp, PT_R25
|
||||
st.d $r26, sp, PT_R26
|
||||
st.d $r27, sp, PT_R27
|
||||
st.d $r28, sp, PT_R28
|
||||
st.d $r29, sp, PT_R29
|
||||
st.d $r30, sp, PT_R30
|
||||
st.d $r31, sp, PT_R31
|
||||
|
||||
la.pcrel t0, acpi_saved_sp
|
||||
st.d sp, t0, 0
|
||||
.endm
|
||||
|
||||
.macro SETUP_WAKEUP
|
||||
ld.d $r1, sp, PT_R1
|
||||
ld.d $r2, sp, PT_R2
|
||||
ld.d $r3, sp, PT_R3
|
||||
ld.d $r4, sp, PT_R4
|
||||
ld.d $r21, sp, PT_R21
|
||||
ld.d $r22, sp, PT_R22
|
||||
ld.d $r23, sp, PT_R23
|
||||
ld.d $r24, sp, PT_R24
|
||||
ld.d $r25, sp, PT_R25
|
||||
ld.d $r26, sp, PT_R26
|
||||
ld.d $r27, sp, PT_R27
|
||||
ld.d $r28, sp, PT_R28
|
||||
ld.d $r29, sp, PT_R29
|
||||
ld.d $r30, sp, PT_R30
|
||||
ld.d $r31, sp, PT_R31
|
||||
.endm
|
||||
|
||||
.text
|
||||
.align 12
|
||||
|
||||
/* Sleep/wakeup code for Loongson-3 */
|
||||
SYM_FUNC_START(loongarch_suspend_enter)
|
||||
SETUP_SLEEP
|
||||
bl __flush_cache_all
|
||||
|
||||
/* Pass RA and SP to BIOS */
|
||||
addi.d a1, sp, 0
|
||||
la.pcrel a0, loongarch_wakeup_start
|
||||
la.pcrel t0, loongarch_suspend_addr
|
||||
ld.d t0, t0, 0
|
||||
jirl a0, t0, 0 /* Call BIOS's STR sleep routine */
|
||||
|
||||
/*
|
||||
* This is where we return upon wakeup.
|
||||
* Reload all of the registers and return.
|
||||
*/
|
||||
SYM_INNER_LABEL(loongarch_wakeup_start, SYM_L_GLOBAL)
|
||||
li.d t0, CSR_DMW0_INIT # UC, PLV0
|
||||
csrwr t0, LOONGARCH_CSR_DMWIN0
|
||||
li.d t0, CSR_DMW1_INIT # CA, PLV0
|
||||
csrwr t0, LOONGARCH_CSR_DMWIN1
|
||||
|
||||
la.abs t0, 0f
|
||||
jr t0
|
||||
0:
|
||||
la.pcrel t0, acpi_saved_sp
|
||||
ld.d sp, t0, 0
|
||||
SETUP_WAKEUP
|
||||
addi.d sp, sp, PT_SIZE
|
||||
jr ra
|
||||
SYM_FUNC_END(loongarch_suspend_enter)
|
Loading…
Reference in New Issue
Block a user