Skip to content

Commit 63b13e6

Browse files
avpatelpalmer-dabbelt
authored andcommitted
RISC-V: Add arch functions for non-retentive suspend entry/exit
The hart registers and CSRs are not preserved in non-retentative suspend state so we provide arch specific helper functions which will save/restore hart context upon entry/exit to non-retentive suspend state. These helper functions can be used by cpuidle drivers for non-retentive suspend entry/exit. Signed-off-by: Anup Patel <anup.patel@wdc.com> Signed-off-by: Anup Patel <apatel@ventanamicro.com> Reviewed-by: Guo Ren <guoren@kernel.org> Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent e1de2c9 commit 63b13e6

7 files changed

Lines changed: 279 additions & 21 deletions

File tree

arch/riscv/include/asm/asm.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,31 @@
6767
#error "Unexpected __SIZEOF_SHORT__"
6868
#endif
6969

70+
#ifdef __ASSEMBLY__
71+
72+
/* Common assembly source macros */
73+
74+
#ifdef CONFIG_XIP_KERNEL
75+
.macro XIP_FIXUP_OFFSET reg
76+
REG_L t0, _xip_fixup
77+
add \reg, \reg, t0
78+
.endm
79+
.macro XIP_FIXUP_FLASH_OFFSET reg
80+
la t1, __data_loc
81+
li t0, XIP_OFFSET_MASK
82+
and t1, t1, t0
83+
li t1, XIP_OFFSET
84+
sub t0, t0, t1
85+
sub \reg, \reg, t0
86+
.endm
87+
_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
88+
#else
89+
.macro XIP_FIXUP_OFFSET reg
90+
.endm
91+
.macro XIP_FIXUP_FLASH_OFFSET reg
92+
.endm
93+
#endif /* CONFIG_XIP_KERNEL */
94+
95+
#endif /* __ASSEMBLY__ */
96+
7097
#endif /* _ASM_RISCV_ASM_H */

arch/riscv/include/asm/suspend.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
4+
* Copyright (c) 2022 Ventana Micro Systems Inc.
5+
*/
6+
7+
#ifndef _ASM_RISCV_SUSPEND_H
8+
#define _ASM_RISCV_SUSPEND_H
9+
10+
#include <asm/ptrace.h>
11+
12+
struct suspend_context {
13+
/* Saved and restored by low-level functions */
14+
struct pt_regs regs;
15+
/* Saved and restored by high-level functions */
16+
unsigned long scratch;
17+
unsigned long tvec;
18+
unsigned long ie;
19+
#ifdef CONFIG_MMU
20+
unsigned long satp;
21+
#endif
22+
};
23+
24+
/* Low-level CPU suspend entry function */
25+
int __cpu_suspend_enter(struct suspend_context *context);
26+
27+
/* High-level CPU suspend which will save context and call finish() */
28+
int cpu_suspend(unsigned long arg,
29+
int (*finish)(unsigned long arg,
30+
unsigned long entry,
31+
unsigned long context));
32+
33+
/* Low-level CPU resume entry function */
34+
int __cpu_resume_enter(unsigned long hartid, unsigned long context);
35+
36+
#endif

arch/riscv/kernel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ obj-$(CONFIG_RISCV_BOOT_SPINWAIT) += cpu_ops_spinwait.o
4848
obj-$(CONFIG_MODULES) += module.o
4949
obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
5050

51+
obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o
52+
5153
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
5254
obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
5355

arch/riscv/kernel/asm-offsets.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <asm/thread_info.h>
1414
#include <asm/ptrace.h>
1515
#include <asm/cpu_ops_sbi.h>
16+
#include <asm/suspend.h>
1617

1718
void asm_offsets(void);
1819

@@ -113,6 +114,8 @@ void asm_offsets(void)
113114
OFFSET(PT_BADADDR, pt_regs, badaddr);
114115
OFFSET(PT_CAUSE, pt_regs, cause);
115116

117+
OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs);
118+
116119
OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero);
117120
OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra);
118121
OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp);

arch/riscv/kernel/head.S

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,6 @@
1616
#include <asm/image.h>
1717
#include "efi-header.S"
1818

19-
#ifdef CONFIG_XIP_KERNEL
20-
.macro XIP_FIXUP_OFFSET reg
21-
REG_L t0, _xip_fixup
22-
add \reg, \reg, t0
23-
.endm
24-
.macro XIP_FIXUP_FLASH_OFFSET reg
25-
la t1, __data_loc
26-
li t0, XIP_OFFSET_MASK
27-
and t1, t1, t0
28-
li t1, XIP_OFFSET
29-
sub t0, t0, t1
30-
sub \reg, \reg, t0
31-
.endm
32-
_xip_fixup: .dword CONFIG_PHYS_RAM_BASE - CONFIG_XIP_PHYS_ADDR - XIP_OFFSET
33-
#else
34-
.macro XIP_FIXUP_OFFSET reg
35-
.endm
36-
.macro XIP_FIXUP_FLASH_OFFSET reg
37-
.endm
38-
#endif /* CONFIG_XIP_KERNEL */
39-
4019
__HEAD
4120
ENTRY(_start)
4221
/*

arch/riscv/kernel/suspend.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
4+
* Copyright (c) 2022 Ventana Micro Systems Inc.
5+
*/
6+
7+
#include <linux/ftrace.h>
8+
#include <asm/csr.h>
9+
#include <asm/suspend.h>
10+
11+
static void suspend_save_csrs(struct suspend_context *context)
12+
{
13+
context->scratch = csr_read(CSR_SCRATCH);
14+
context->tvec = csr_read(CSR_TVEC);
15+
context->ie = csr_read(CSR_IE);
16+
17+
/*
18+
* No need to save/restore IP CSR (i.e. MIP or SIP) because:
19+
*
20+
* 1. For no-MMU (M-mode) kernel, the bits in MIP are set by
21+
* external devices (such as interrupt controller, timer, etc).
22+
* 2. For MMU (S-mode) kernel, the bits in SIP are set by
23+
* M-mode firmware and external devices (such as interrupt
24+
* controller, etc).
25+
*/
26+
27+
#ifdef CONFIG_MMU
28+
context->satp = csr_read(CSR_SATP);
29+
#endif
30+
}
31+
32+
static void suspend_restore_csrs(struct suspend_context *context)
33+
{
34+
csr_write(CSR_SCRATCH, context->scratch);
35+
csr_write(CSR_TVEC, context->tvec);
36+
csr_write(CSR_IE, context->ie);
37+
38+
#ifdef CONFIG_MMU
39+
csr_write(CSR_SATP, context->satp);
40+
#endif
41+
}
42+
43+
int cpu_suspend(unsigned long arg,
44+
int (*finish)(unsigned long arg,
45+
unsigned long entry,
46+
unsigned long context))
47+
{
48+
int rc = 0;
49+
struct suspend_context context = { 0 };
50+
51+
/* Finisher should be non-NULL */
52+
if (!finish)
53+
return -EINVAL;
54+
55+
/* Save additional CSRs*/
56+
suspend_save_csrs(&context);
57+
58+
/*
59+
* Function graph tracer state gets incosistent when the kernel
60+
* calls functions that never return (aka finishers) hence disable
61+
* graph tracing during their execution.
62+
*/
63+
pause_graph_tracing();
64+
65+
/* Save context on stack */
66+
if (__cpu_suspend_enter(&context)) {
67+
/* Call the finisher */
68+
rc = finish(arg, __pa_symbol(__cpu_resume_enter),
69+
(ulong)&context);
70+
71+
/*
72+
* Should never reach here, unless the suspend finisher
73+
* fails. Successful cpu_suspend() should return from
74+
* __cpu_resume_entry()
75+
*/
76+
if (!rc)
77+
rc = -EOPNOTSUPP;
78+
}
79+
80+
/* Enable function graph tracer */
81+
unpause_graph_tracing();
82+
83+
/* Restore additional CSRs */
84+
suspend_restore_csrs(&context);
85+
86+
return rc;
87+
}

arch/riscv/kernel/suspend_entry.S

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
4+
* Copyright (c) 2022 Ventana Micro Systems Inc.
5+
*/
6+
7+
#include <linux/linkage.h>
8+
#include <asm/asm.h>
9+
#include <asm/asm-offsets.h>
10+
#include <asm/csr.h>
11+
12+
.text
13+
.altmacro
14+
.option norelax
15+
16+
ENTRY(__cpu_suspend_enter)
17+
/* Save registers (except A0 and T0-T6) */
18+
REG_S ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0)
19+
REG_S sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0)
20+
REG_S gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0)
21+
REG_S tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0)
22+
REG_S s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0)
23+
REG_S s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0)
24+
REG_S a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0)
25+
REG_S a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0)
26+
REG_S a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0)
27+
REG_S a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0)
28+
REG_S a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0)
29+
REG_S a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0)
30+
REG_S a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0)
31+
REG_S s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0)
32+
REG_S s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0)
33+
REG_S s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0)
34+
REG_S s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0)
35+
REG_S s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0)
36+
REG_S s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0)
37+
REG_S s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0)
38+
REG_S s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0)
39+
REG_S s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0)
40+
REG_S s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0)
41+
42+
/* Save CSRs */
43+
csrr t0, CSR_EPC
44+
REG_S t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0)
45+
csrr t0, CSR_STATUS
46+
REG_S t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0)
47+
csrr t0, CSR_TVAL
48+
REG_S t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0)
49+
csrr t0, CSR_CAUSE
50+
REG_S t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0)
51+
52+
/* Return non-zero value */
53+
li a0, 1
54+
55+
/* Return to C code */
56+
ret
57+
END(__cpu_suspend_enter)
58+
59+
ENTRY(__cpu_resume_enter)
60+
/* Load the global pointer */
61+
.option push
62+
.option norelax
63+
la gp, __global_pointer$
64+
.option pop
65+
66+
#ifdef CONFIG_MMU
67+
/* Save A0 and A1 */
68+
add t0, a0, zero
69+
add t1, a1, zero
70+
71+
/* Enable MMU */
72+
la a0, swapper_pg_dir
73+
XIP_FIXUP_OFFSET a0
74+
call relocate_enable_mmu
75+
76+
/* Restore A0 and A1 */
77+
add a0, t0, zero
78+
add a1, t1, zero
79+
#endif
80+
81+
/* Make A0 point to suspend context */
82+
add a0, a1, zero
83+
84+
/* Restore CSRs */
85+
REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0)
86+
csrw CSR_EPC, t0
87+
REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0)
88+
csrw CSR_STATUS, t0
89+
REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0)
90+
csrw CSR_TVAL, t0
91+
REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0)
92+
csrw CSR_CAUSE, t0
93+
94+
/* Restore registers (except A0 and T0-T6) */
95+
REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0)
96+
REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0)
97+
REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0)
98+
REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0)
99+
REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0)
100+
REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0)
101+
REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0)
102+
REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0)
103+
REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0)
104+
REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0)
105+
REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0)
106+
REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0)
107+
REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0)
108+
REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0)
109+
REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0)
110+
REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0)
111+
REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0)
112+
REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0)
113+
REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0)
114+
REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0)
115+
REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0)
116+
REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0)
117+
REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0)
118+
119+
/* Return zero value */
120+
add a0, zero, zero
121+
122+
/* Return to C code */
123+
ret
124+
END(__cpu_resume_enter)

0 commit comments

Comments
 (0)