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 ARCH_WANTS_NO_INSTR
|
||||||
select BUILDTIME_TABLE_SORT
|
select BUILDTIME_TABLE_SORT
|
||||||
select COMMON_CLK
|
select COMMON_CLK
|
||||||
|
select CPU_PM
|
||||||
select EFI
|
select EFI
|
||||||
select GENERIC_CLOCKEVENTS
|
select GENERIC_CLOCKEVENTS
|
||||||
select GENERIC_CMOS_UPDATE
|
select GENERIC_CMOS_UPDATE
|
||||||
@ -517,6 +518,10 @@ config ARCH_MMAP_RND_BITS_MAX
|
|||||||
|
|
||||||
menu "Power management options"
|
menu "Power management options"
|
||||||
|
|
||||||
|
config ARCH_SUSPEND_POSSIBLE
|
||||||
|
def_bool y
|
||||||
|
|
||||||
|
source "kernel/power/Kconfig"
|
||||||
source "drivers/acpi/Kconfig"
|
source "drivers/acpi/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -104,6 +104,9 @@ endif
|
|||||||
libs-y += arch/loongarch/lib/
|
libs-y += arch/loongarch/lib/
|
||||||
libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
|
||||||
|
|
||||||
|
# suspend and hibernation support
|
||||||
|
drivers-$(CONFIG_PM) += arch/loongarch/power/
|
||||||
|
|
||||||
ifeq ($(KBUILD_EXTMOD),)
|
ifeq ($(KBUILD_EXTMOD),)
|
||||||
prepare: vdso_prepare
|
prepare: vdso_prepare
|
||||||
vdso_prepare: prepare0
|
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
|
#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 */
|
#endif /* _ASM_LOONGARCH_ACPI_H */
|
||||||
|
@ -32,6 +32,7 @@ struct loongson_system_configuration {
|
|||||||
int cores_per_node;
|
int cores_per_node;
|
||||||
int cores_per_package;
|
int cores_per_package;
|
||||||
unsigned long cores_io_master;
|
unsigned long cores_io_master;
|
||||||
|
unsigned long suspend_addr;
|
||||||
const char *cpuname;
|
const char *cpuname;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,4 +136,7 @@ typedef enum {
|
|||||||
#define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val)
|
#define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val)
|
||||||
#define ls7a_writeq(val, addr) *(volatile unsigned long *)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 */
|
#endif /* __ASM_LOONGSON_H */
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
extern u64 cpu_clock_freq;
|
extern u64 cpu_clock_freq;
|
||||||
extern u64 const_clock_freq;
|
extern u64 const_clock_freq;
|
||||||
|
|
||||||
|
extern void save_counter(void);
|
||||||
extern void sync_counter(void);
|
extern void sync_counter(void);
|
||||||
|
|
||||||
static inline unsigned int calc_const_freq(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;
|
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)
|
void __init acpi_boot_table_init(void)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <linux/smp.h>
|
#include <linux/smp.h>
|
||||||
#include <linux/threads.h>
|
#include <linux/threads.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/syscore_ops.h>
|
||||||
#include <linux/time.h>
|
#include <linux/time.h>
|
||||||
#include <linux/tracepoint.h>
|
#include <linux/tracepoint.h>
|
||||||
#include <linux/sched/hotplug.h>
|
#include <linux/sched/hotplug.h>
|
||||||
|
@ -115,12 +115,17 @@ static unsigned long __init get_loops_per_jiffy(void)
|
|||||||
return lpj;
|
return lpj;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long init_timeval;
|
static long init_offset __nosavedata;
|
||||||
|
|
||||||
|
void save_counter(void)
|
||||||
|
{
|
||||||
|
init_offset = drdtime();
|
||||||
|
}
|
||||||
|
|
||||||
void sync_counter(void)
|
void sync_counter(void)
|
||||||
{
|
{
|
||||||
/* Ensure counter begin at 0 */
|
/* 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)
|
static int get_timer_irq(void)
|
||||||
@ -219,7 +224,7 @@ void __init time_init(void)
|
|||||||
else
|
else
|
||||||
const_clock_freq = calc_const_freq();
|
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_clockevent_init();
|
||||||
constant_clocksource_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…
x
Reference in New Issue
Block a user