Skip to content

Commit

Permalink
LoongArch: Add suspend (ACPI S3) support
Browse files Browse the repository at this point in the history
Add suspend (Suspend To RAM, aka ACPI S3) support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
  • Loading branch information
chenhuacai committed Dec 14, 2022
1 parent 27cab43 commit 366bb35
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 3 deletions.
5 changes: 5 additions & 0 deletions arch/loongarch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions arch/loongarch/include/asm/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
1 change: 1 addition & 0 deletions arch/loongarch/include/asm/bootinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
3 changes: 3 additions & 0 deletions arch/loongarch/include/asm/loongson.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
1 change: 1 addition & 0 deletions arch/loongarch/include/asm/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions arch/loongarch/kernel/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
/*
Expand Down
1 change: 1 addition & 0 deletions arch/loongarch/kernel/smp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
11 changes: 8 additions & 3 deletions arch/loongarch/kernel/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down
3 changes: 3 additions & 0 deletions arch/loongarch/power/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
obj-y += platform.o

obj-$(CONFIG_SUSPEND) += suspend.o suspend_asm.o
57 changes: 57 additions & 0 deletions arch/loongarch/power/platform.c
Original file line number Diff line number Diff line change
@@ -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 changes: 73 additions & 0 deletions arch/loongarch/power/suspend.c
Original file line number Diff line number Diff line change
@@ -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 changes: 89 additions & 0 deletions arch/loongarch/power/suspend_asm.S
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 366bb35

Please sign in to comment.