提交 84d1c8ea 编写于 作者: Z zhangtianyang 提交者: openeuler-sync-bot

LoongArch: Support Power Manager

LoongArch inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I6BWFP

--------------------------------
Signed-off-by: Nyangqiming <yangqiming@loongson.cn>
Signed-off-by: Nzhangtianyang <zhangtianyang@loongson.cn>
Change-Id: If6112fafdf5968ea731fb97ab6c764b34e4a7740
(cherry picked from commit f0c990f8)
上级 7a0009b2
...@@ -510,6 +510,14 @@ config ARCH_MMAP_RND_BITS_MAX ...@@ -510,6 +510,14 @@ config ARCH_MMAP_RND_BITS_MAX
menu "Power management options" menu "Power management options"
config ARCH_HIBERNATION_POSSIBLE
def_bool y
config ARCH_SUSPEND_POSSIBLE
def_bool y
source "kernel/power/Kconfig"
source "drivers/acpi/Kconfig" source "drivers/acpi/Kconfig"
endmenu endmenu
......
...@@ -96,9 +96,13 @@ endif ...@@ -96,9 +96,13 @@ endif
head-y := arch/loongarch/kernel/head.o head-y := arch/loongarch/kernel/head.o
core-y += arch/loongarch/ core-y += arch/loongarch/
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
......
...@@ -37,8 +37,8 @@ CONFIG_PERF_EVENTS=y ...@@ -37,8 +37,8 @@ CONFIG_PERF_EVENTS=y
# CONFIG_COMPAT_BRK is not set # CONFIG_COMPAT_BRK is not set
CONFIG_CPU_HAS_LSX=y CONFIG_CPU_HAS_LSX=y
CONFIG_CPU_HAS_LASX=y CONFIG_CPU_HAS_LASX=y
CONFIG_HOTPLUG_CPU=y
CONFIG_NUMA=y CONFIG_NUMA=y
CONFIG_HIBERNATION=y
CONFIG_ACPI_SPCR_TABLE=y CONFIG_ACPI_SPCR_TABLE=y
CONFIG_ACPI_DOCK=y CONFIG_ACPI_DOCK=y
CONFIG_ACPI_IPMI=m CONFIG_ACPI_IPMI=m
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#ifndef _ASM_LOONGARCH_ACPI_H #ifndef _ASM_LOONGARCH_ACPI_H
#define _ASM_LOONGARCH_ACPI_H #define _ASM_LOONGARCH_ACPI_H
#include <asm/suspend.h>
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
extern int acpi_strict; extern int acpi_strict;
extern int acpi_disabled; extern int acpi_disabled;
...@@ -35,4 +35,10 @@ extern struct list_head acpi_wakeup_device_list; ...@@ -35,4 +35,10 @@ 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
static inline unsigned long acpi_get_wakeup_address(void)
{
return (unsigned long)loongarch_wakeup_start;
}
extern int loongarch_acpi_suspend(void);
extern int (*acpi_suspend_lowlevel)(void);
#endif /* _ASM_LOONGARCH_ACPI_H */ #endif /* _ASM_LOONGARCH_ACPI_H */
...@@ -33,6 +33,10 @@ struct loongson_system_configuration { ...@@ -33,6 +33,10 @@ struct loongson_system_configuration {
int cores_per_package; int cores_per_package;
unsigned long cores_io_master; unsigned long cores_io_master;
const char *cpuname; const char *cpuname;
u64 suspend_addr;
u64 gpe0_ena_reg;
u8 pcie_wake_enabled;
u8 is_soc_cpu;
}; };
extern u64 efi_system_table; extern u64 efi_system_table;
......
...@@ -70,8 +70,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr) ...@@ -70,8 +70,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr)
#define LS7A_CHIPCFG_REG_BASE (LS7A_PCH_REG_BASE + 0x00010000) #define LS7A_CHIPCFG_REG_BASE (LS7A_PCH_REG_BASE + 0x00010000)
/* MISC reg base */ /* MISC reg base */
#define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000) #define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000)
/* ACPI regs */
#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000)
/* RTC regs */ /* RTC regs */
#define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100) #define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100)
...@@ -93,36 +91,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr) ...@@ -93,36 +91,6 @@ static inline void xconf_writeq(u64 val64, volatile void __iomem *addr)
#define LS7A_LPC_INT_CLR (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x200c) #define LS7A_LPC_INT_CLR (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x200c)
#define LS7A_LPC_INT_POL (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2010) #define LS7A_LPC_INT_POL (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2010)
#define LS7A_PMCON_SOC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x000)
#define LS7A_PMCON_RESUME_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x004)
#define LS7A_PMCON_RTC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x008)
#define LS7A_PM1_EVT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x00c)
#define LS7A_PM1_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x010)
#define LS7A_PM1_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x014)
#define LS7A_PM1_TMR_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x018)
#define LS7A_P_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x01c)
#define LS7A_GPE0_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x028)
#define LS7A_GPE0_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x02c)
#define LS7A_RST_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x030)
#define LS7A_WD_SET_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x034)
#define LS7A_WD_TIMER_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x038)
#define LS7A_THSENS_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x04c)
#define LS7A_GEN_RTC_1_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x050)
#define LS7A_GEN_RTC_2_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x054)
#define LS7A_DPM_CFG_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x400)
#define LS7A_DPM_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x404)
#define LS7A_DPM_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x408)
typedef enum {
ACPI_PCI_HOTPLUG_STATUS = 1 << 1,
ACPI_CPU_HOTPLUG_STATUS = 1 << 2,
ACPI_MEM_HOTPLUG_STATUS = 1 << 3,
ACPI_POWERBUTTON_STATUS = 1 << 8,
ACPI_RTC_WAKE_STATUS = 1 << 10,
ACPI_PCI_WAKE_STATUS = 1 << 14,
ACPI_ANY_WAKE_STATUS = 1 << 15,
} AcpiEventStatusBits;
#define HT1LO_OFFSET 0xe0000000000UL #define HT1LO_OFFSET 0xe0000000000UL
/* PCI Configuration Space Base */ /* PCI Configuration Space Base */
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_SUSPEND_H
#define _ASM_LOONGARCH_SUSPEND_H
void arch_common_resume(void);
void arch_common_suspend(void);
extern void loongarch_suspend_enter(void);
extern void loongarch_wakeup_start(void);
#endif /* _ASM_LOONGARCH_SUSPEND_H */
...@@ -13,6 +13,7 @@ extern u64 cpu_clock_freq; ...@@ -13,6 +13,7 @@ extern u64 cpu_clock_freq;
extern u64 const_clock_freq; extern u64 const_clock_freq;
extern void sync_counter(void); extern void sync_counter(void);
extern void save_counter(void);
static inline unsigned int calc_const_freq(void) static inline unsigned int calc_const_freq(void)
{ {
......
...@@ -7,7 +7,8 @@ extra-y := head.o vmlinux.lds ...@@ -7,7 +7,8 @@ extra-y := head.o vmlinux.lds
obj-y += cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ obj-y += cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \ traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
elf.o legacy_boot.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o elf.o legacy_boot.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o\
platform.o
obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ACPI) += acpi.o
obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_EFI) += efi.o
......
...@@ -74,6 +74,30 @@ void __init acpi_boot_table_init(void) ...@@ -74,6 +74,30 @@ void __init acpi_boot_table_init(void)
} }
} }
static int __init acpi_parse_fadt(struct acpi_table_header *table)
{
u64 gpe0_ena;
if (acpi_gbl_reduced_hardware)
return 0;
if (acpi_gbl_FADT.xgpe0_block.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
goto err;
gpe0_ena = acpi_gbl_FADT.xgpe0_block.address +
acpi_gbl_FADT.gpe0_block_length / 2;
if (!gpe0_ena)
goto err;
loongson_sysconf.gpe0_ena_reg = TO_UNCACHE(gpe0_ena);
return 0;
err:
pr_err(PREFIX "Invalid BIOS FADT, disabling ACPI\n");
disable_acpi();
return -1;
}
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
int set_processor_mask(u32 id, u32 flags) int set_processor_mask(u32 id, u32 flags)
{ {
...@@ -169,6 +193,12 @@ static void __init acpi_process_madt(void) ...@@ -169,6 +193,12 @@ static void __init acpi_process_madt(void)
loongson_sysconf.nr_cpus = num_processors; loongson_sysconf.nr_cpus = num_processors;
} }
#ifdef CONFIG_ACPI_SLEEP
int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend;
#else
int (*acpi_suspend_lowlevel)(void);
#endif
int __init acpi_boot_init(void) int __init acpi_boot_init(void)
{ {
/* /*
...@@ -179,6 +209,8 @@ int __init acpi_boot_init(void) ...@@ -179,6 +209,8 @@ int __init acpi_boot_init(void)
loongson_sysconf.boot_cpu_id = read_csr_cpuid(); loongson_sysconf.boot_cpu_id = read_csr_cpuid();
acpi_table_parse(ACPI_SIG_FADT, acpi_parse_fadt);
/* /*
* Process the Multiple APIC Description Table (MADT), if present * Process the Multiple APIC Description Table (MADT), if present
*/ */
......
...@@ -257,3 +257,15 @@ void output_smpboot_defines(void) ...@@ -257,3 +257,15 @@ void output_smpboot_defines(void)
BLANK(); BLANK();
} }
#endif #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
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2009 Lemote Inc.
* Author: Wu Zhangjin, wuzhangjin@gmail.com
* Copyright (C) 2020 Loongson Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/smp.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <asm/bootinfo.h>
#include <asm/loongson.h>
extern int loongson_acpi_init(void);
static int __init loongson3_acpi_suspend_init(void)
{
#ifdef CONFIG_ACPI
acpi_status status;
unsigned long long 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(TO_PHYS(suspend_addr));
#endif
return 0;
}
device_initcall(loongson3_acpi_suspend_init);
...@@ -198,6 +198,21 @@ static int __init early_parse_mem(char *p) ...@@ -198,6 +198,21 @@ static int __init early_parse_mem(char *p)
return 0; return 0;
} }
early_param("mem", early_parse_mem); early_param("mem", early_parse_mem);
static void __init set_pcie_wakeup(void)
{
acpi_status status;
u32 value;
if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware)
return;
status = acpi_read_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, &value);
if (ACPI_FAILURE(status)) {
return;
}
loongson_sysconf.pcie_wake_enabled = !value;
}
void __init platform_init(void) void __init platform_init(void)
{ {
...@@ -210,6 +225,7 @@ void __init platform_init(void) ...@@ -210,6 +225,7 @@ void __init platform_init(void)
acpi_boot_table_init(); acpi_boot_table_init();
acpi_boot_init(); acpi_boot_init();
#endif #endif
set_pcie_wakeup();
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
init_numa_memory(); init_numa_memory();
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/threads.h> #include <linux/threads.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/syscore_ops.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#include <linux/sched/hotplug.h> #include <linux/sched/hotplug.h>
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
......
...@@ -115,7 +115,12 @@ static unsigned long __init get_loops_per_jiffy(void) ...@@ -115,7 +115,12 @@ static unsigned long __init get_loops_per_jiffy(void)
return lpj; return lpj;
} }
static long init_timeval; static long init_timeval __nosavedata;
void save_counter(void)
{
init_timeval = drdtime();
}
void sync_counter(void) void sync_counter(void)
{ {
......
OBJECT_FILES_NON_STANDARD_suspend_asm.o := y
obj-$(CONFIG_SUSPEND) += suspend.o suspend_asm.o
obj-$(CONFIG_HIBERNATION) += cpu.o hibernate.o hibernate_asm.o
// SPDX-License-Identifier: GPL-2.0
/*
* Suspend support specific for loongarch.
*
* Licensed under the GPLv2
* Copyright (C) 2020 Loongson Technology Co., Ltd.
*/
#include <asm/sections.h>
#include <asm/fpu.h>
static u64 saved_crmd;
static u64 saved_prmd;
static u64 saved_euen;
static u64 saved_ecfg;
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);
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);
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);
}
// SPDX-License-Identifier: GPL-2.0
#include <asm/tlbflush.h>
extern int restore_image(void);
extern void enable_pcie_wakeup(void);
extern void swsusp_arch_save(void);
int swsusp_arch_suspend(void)
{
enable_pcie_wakeup();
swsusp_arch_save();
return 0;
}
int swsusp_arch_resume(void)
{
/* Avoid TLB mismatch during and after kernel resume */
local_flush_tlb_all();
return restore_image();
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Hibernation support specific for loongarch - temporary page tables
*
* Licensed under the GPLv2
*
* Copyright (C) 2009 Lemote Inc.
* Author: Hu Hongbing <huhb@lemote.com>
* Wu Zhangjin <wuzhangjin@gmail.com>
* Copyright (C) 2020 Loongson Technology Co., Ltd.
*/
#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/regdef.h>
#include <asm/asm.h>
.text
SYM_FUNC_START(swsusp_arch_save)
la.abs t0, saved_regs
PTR_S ra, t0, PT_R1
PTR_S sp, t0, PT_R3
PTR_S fp, t0, PT_R22
PTR_S tp, t0, PT_R2
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_arch_save)
SYM_FUNC_START(restore_image)
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 sp, t0, PT_R3
PTR_L fp, t0, PT_R22
PTR_L tp, t0, PT_R2
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(restore_image)
// SPDX-License-Identifier: GPL-2.0
/*
* loongson-specific suspend support
*
* Copyright (C) 2020 Loongson Technology Co., Ltd.
* Author: Huacai Chen <chenhuacai@loongson.cn>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/suspend.h>
#include <asm/acpi.h>
#include <asm/loongarch.h>
#include <asm/time.h>
#include <asm/tlbflush.h>
#include <asm/loongson.h>
#include <asm/suspend.h>
u64 loongarch_suspend_addr;
extern unsigned long eentry;
extern unsigned long tlbrentry;
struct saved_registers {
u32 ecfg;
u64 pgd;
u64 kpgd;
u32 pwctl0;
u32 pwctl1;
u32 euen;
};
static struct saved_registers saved_regs;
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;
}
void arch_common_resume(void)
{
sync_counter();
local_flush_tlb_all();
csr_write64(per_cpu_offset(0), PERCPU_BASE_KS);
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);
csr_write64(eentry, LOONGARCH_CSR_EENTRY);
csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
csr_write64(eentry, LOONGARCH_CSR_MERRENTRY);
}
static void enable_gpe_wakeup(void)
{
struct list_head *node, *next;
u32 data = 0;
data = readl((volatile void *)loongson_sysconf.gpe0_ena_reg);
list_for_each_safe(node, next, &acpi_wakeup_device_list) {
struct acpi_device *dev =
container_of(node, struct acpi_device, wakeup_list);
if (!dev->wakeup.flags.valid
|| ACPI_STATE_S3 > (u32) dev->wakeup.sleep_state
|| !(device_may_wakeup(&dev->dev)
|| dev->wakeup.prepare_count))
continue;
data |= (1 << dev->wakeup.gpe_number);
}
writel(data, (volatile void *)loongson_sysconf.gpe0_ena_reg);
}
void enable_pcie_wakeup(void)
{
u16 value;
if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware)
return;
acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_STATUS, 1);
if (loongson_sysconf.pcie_wake_enabled) {
acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0);
}
}
EXPORT_SYMBOL_GPL(enable_pcie_wakeup);
int loongarch_acpi_suspend(void)
{
arch_common_suspend();
enable_gpe_wakeup();
enable_pcie_wakeup();
/* processor specific suspend */
loongarch_suspend_enter();
arch_common_resume();
return 0;
}
static int plat_pm_callback(struct notifier_block *nb, unsigned long action, void *ptr)
{
int ret = 0;
switch (action) {
case PM_POST_SUSPEND:
enable_gpe_wakeup();
break;
default:
break;
}
return notifier_from_errno(ret);
}
static int __init plat_pm_post_init(void)
{
if (loongson_sysconf.is_soc_cpu || acpi_gbl_reduced_hardware)
return 0;
enable_gpe_wakeup();
pm_notifier(plat_pm_callback, -INT_MAX);
return 0;
}
late_initcall_sync(plat_pm_post_init);
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Sleep helper for Loongson-3 sleep mode.
*
* Copyright (C) 2020 Loongson Technology Co., Ltd.
* Author: Huacai Chen <chenhuacai@loongson.cn>
*/
#include <asm/asm.h>
#include <asm/asmmacro.h>
#include <asm/loongarch.h>
#include <asm/stackframe.h>
#include <asm/addrspace.h>
.extern loongarch_nr_nodes
.extern loongarch_suspend_addr
.extern loongarch_pcache_ways
.extern loongarch_pcache_sets
.extern loongarch_pcache_linesz
.extern loongarch_scache_ways
.extern loongarch_scache_sets
.extern loongarch_scache_linesz
.text
.align 5
/* 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 $r5, sp, PT_R5
st.d $r6, sp, PT_R6
st.d $r7, sp, PT_R7
st.d $r8, sp, PT_R8
st.d $r9, sp, PT_R9
st.d $r10, sp, PT_R10
st.d $r11, sp, PT_R11
st.d $r20, sp, PT_R20
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
#ifdef CONFIG_ACPI
la.pcrel t0, acpi_saved_sp
st.d sp, t0, 0
#endif
.endm
/* Sleep code for Loongson-3 */
SYM_CODE_START(loongarch_suspend_enter)
SETUP_SLEEP
bl cpu_flush_caches
/* Pass RA and SP to BIOS, for machines without CMOS RAM */
addi.d a1, sp, 0
la.pcrel a0, loongarch_wakeup_start
la.pcrel t0, loongarch_suspend_addr
ld.d t0, t0, 0 /* Call BIOS's STR sleep routine */
jr t0
nop
SYM_CODE_END(loongarch_suspend_enter)
.macro SETUP_WAKEUP
nop
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 $r5, sp, PT_R5
ld.d $r6, sp, PT_R6
ld.d $r7, sp, PT_R7
ld.d $r8, sp, PT_R8
ld.d $r9, sp, PT_R9
ld.d $r10, sp, PT_R10
ld.d $r11, sp, PT_R11
ld.d $r20, sp, PT_R20
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
/* This is where we return upon wakeup.
* Reload all of the registers and return.
*/
SYM_CODE_START(loongarch_wakeup_start)
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.pcrel t0, acpi_saved_sp
ld.d sp, t0, 0
SETUP_WAKEUP
addi.d sp, sp, PT_SIZE
jr ra
SYM_CODE_END(loongarch_wakeup_start)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册