Skip to content

Commit 85ab6fd

Browse files
Merge patch series "RISC-V: ACPI: Add LPI support"
Sunil V L <sunilvl@ventanamicro.com> says: This series adds support for Low Power Idle (LPI) on ACPI based platforms. LPI is described in the ACPI spec [1]. RISC-V FFH spec required to enable this is available at [2]. [1] - https://uefi.org/specs/ACPI/6.5/08_Processor_Configuration_and_Control.html#lpi-low-power-idle-states [2] - https://github.com/riscv-non-isa/riscv-acpi-ffh/releases/download/v/riscv-ffh.pdf * b4-shazam-merge: ACPI: Enable ACPI_PROCESSOR for RISC-V ACPI: RISC-V: Add LPI driver cpuidle: RISC-V: Move few functions to arch/riscv Link: https://lore.kernel.org/r/20240118062930.245937-1-sunilvl@ventanamicro.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2 parents 728e7ea + 359df7c commit 85ab6fd

File tree

6 files changed

+141
-46
lines changed

6 files changed

+141
-46
lines changed

arch/riscv/include/asm/suspend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,7 @@ int hibernate_resume_nonboot_cpu_disable(void);
5555
asmlinkage void hibernate_restore_image(unsigned long resume_satp, unsigned long satp_temp,
5656
unsigned long cpu_resume);
5757
asmlinkage int hibernate_core_restore_code(void);
58+
bool riscv_sbi_hsm_is_supported(void);
59+
bool riscv_sbi_suspend_state_is_valid(u32 state);
60+
int riscv_sbi_hart_suspend(u32 state);
5861
#endif

arch/riscv/kernel/suspend.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,53 @@ static int __init sbi_system_suspend_init(void)
128128
}
129129

130130
arch_initcall(sbi_system_suspend_init);
131+
132+
static int sbi_suspend_finisher(unsigned long suspend_type,
133+
unsigned long resume_addr,
134+
unsigned long opaque)
135+
{
136+
struct sbiret ret;
137+
138+
ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND,
139+
suspend_type, resume_addr, opaque, 0, 0, 0);
140+
141+
return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0;
142+
}
143+
144+
int riscv_sbi_hart_suspend(u32 state)
145+
{
146+
if (state & SBI_HSM_SUSP_NON_RET_BIT)
147+
return cpu_suspend(state, sbi_suspend_finisher);
148+
else
149+
return sbi_suspend_finisher(state, 0, 0);
150+
}
151+
152+
bool riscv_sbi_suspend_state_is_valid(u32 state)
153+
{
154+
if (state > SBI_HSM_SUSPEND_RET_DEFAULT &&
155+
state < SBI_HSM_SUSPEND_RET_PLATFORM)
156+
return false;
157+
158+
if (state > SBI_HSM_SUSPEND_NON_RET_DEFAULT &&
159+
state < SBI_HSM_SUSPEND_NON_RET_PLATFORM)
160+
return false;
161+
162+
return true;
163+
}
164+
165+
bool riscv_sbi_hsm_is_supported(void)
166+
{
167+
/*
168+
* The SBI HSM suspend function is only available when:
169+
* 1) SBI version is 0.3 or higher
170+
* 2) SBI HSM extension is available
171+
*/
172+
if (sbi_spec_version < sbi_mk_version(0, 3) ||
173+
!sbi_probe_extension(SBI_EXT_HSM)) {
174+
pr_info("HSM suspend not available\n");
175+
return false;
176+
}
177+
178+
return true;
179+
}
131180
#endif /* CONFIG_RISCV_SBI */

drivers/acpi/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ config ACPI_CPPC_LIB
286286

287287
config ACPI_PROCESSOR
288288
tristate "Processor"
289-
depends on X86 || ARM64 || LOONGARCH
289+
depends on X86 || ARM64 || LOONGARCH || RISCV
290290
select ACPI_PROCESSOR_IDLE
291291
select ACPI_CPU_FREQ_PSS if X86 || LOONGARCH
292292
select THERMAL

drivers/acpi/riscv/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# SPDX-License-Identifier: GPL-2.0-only
2-
obj-y += rhct.o
2+
obj-y += rhct.o
3+
obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o

drivers/acpi/riscv/cpuidle.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* Copyright (C) 2024, Ventana Micro Systems Inc
4+
* Author: Sunil V L <sunilvl@ventanamicro.com>
5+
*
6+
*/
7+
8+
#include <linux/acpi.h>
9+
#include <acpi/processor.h>
10+
#include <linux/cpu_pm.h>
11+
#include <linux/cpuidle.h>
12+
#include <linux/suspend.h>
13+
#include <asm/cpuidle.h>
14+
#include <asm/sbi.h>
15+
#include <asm/suspend.h>
16+
17+
#define RISCV_FFH_LPI_TYPE_MASK GENMASK_ULL(63, 60)
18+
#define RISCV_FFH_LPI_RSVD_MASK GENMASK_ULL(59, 32)
19+
20+
#define RISCV_FFH_LPI_TYPE_SBI BIT_ULL(60)
21+
22+
static int acpi_cpu_init_idle(unsigned int cpu)
23+
{
24+
int i;
25+
struct acpi_lpi_state *lpi;
26+
struct acpi_processor *pr = per_cpu(processors, cpu);
27+
28+
if (unlikely(!pr || !pr->flags.has_lpi))
29+
return -EINVAL;
30+
31+
if (!riscv_sbi_hsm_is_supported())
32+
return -ENODEV;
33+
34+
if (pr->power.count <= 1)
35+
return -ENODEV;
36+
37+
for (i = 1; i < pr->power.count; i++) {
38+
u32 state;
39+
40+
lpi = &pr->power.lpi_states[i];
41+
42+
/*
43+
* Validate Entry Method as per FFH spec.
44+
* bits[63:60] should be 0x1
45+
* bits[59:32] should be 0x0
46+
* bits[31:0] represent a SBI power_state
47+
*/
48+
if (((lpi->address & RISCV_FFH_LPI_TYPE_MASK) != RISCV_FFH_LPI_TYPE_SBI) ||
49+
(lpi->address & RISCV_FFH_LPI_RSVD_MASK)) {
50+
pr_warn("Invalid LPI entry method %#llx\n", lpi->address);
51+
return -EINVAL;
52+
}
53+
54+
state = lpi->address;
55+
if (!riscv_sbi_suspend_state_is_valid(state)) {
56+
pr_warn("Invalid SBI power state %#x\n", state);
57+
return -EINVAL;
58+
}
59+
}
60+
61+
return 0;
62+
}
63+
64+
int acpi_processor_ffh_lpi_probe(unsigned int cpu)
65+
{
66+
return acpi_cpu_init_idle(cpu);
67+
}
68+
69+
int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
70+
{
71+
u32 state = lpi->address;
72+
73+
if (state & SBI_HSM_SUSP_NON_RET_BIT)
74+
return CPU_PM_CPU_IDLE_ENTER_PARAM(riscv_sbi_hart_suspend,
75+
lpi->index,
76+
state);
77+
else
78+
return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(riscv_sbi_hart_suspend,
79+
lpi->index,
80+
state);
81+
}

drivers/cpuidle/cpuidle-riscv-sbi.c

Lines changed: 5 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -73,36 +73,16 @@ static inline bool sbi_is_domain_state_available(void)
7373
return data->available;
7474
}
7575

76-
static int sbi_suspend_finisher(unsigned long suspend_type,
77-
unsigned long resume_addr,
78-
unsigned long opaque)
79-
{
80-
struct sbiret ret;
81-
82-
ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_SUSPEND,
83-
suspend_type, resume_addr, opaque, 0, 0, 0);
84-
85-
return (ret.error) ? sbi_err_map_linux_errno(ret.error) : 0;
86-
}
87-
88-
static int sbi_suspend(u32 state)
89-
{
90-
if (state & SBI_HSM_SUSP_NON_RET_BIT)
91-
return cpu_suspend(state, sbi_suspend_finisher);
92-
else
93-
return sbi_suspend_finisher(state, 0, 0);
94-
}
95-
9676
static __cpuidle int sbi_cpuidle_enter_state(struct cpuidle_device *dev,
9777
struct cpuidle_driver *drv, int idx)
9878
{
9979
u32 *states = __this_cpu_read(sbi_cpuidle_data.states);
10080
u32 state = states[idx];
10181

10282
if (state & SBI_HSM_SUSP_NON_RET_BIT)
103-
return CPU_PM_CPU_IDLE_ENTER_PARAM(sbi_suspend, idx, state);
83+
return CPU_PM_CPU_IDLE_ENTER_PARAM(riscv_sbi_hart_suspend, idx, state);
10484
else
105-
return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(sbi_suspend,
85+
return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(riscv_sbi_hart_suspend,
10686
idx, state);
10787
}
10888

@@ -133,7 +113,7 @@ static __cpuidle int __sbi_enter_domain_idle_state(struct cpuidle_device *dev,
133113
else
134114
state = states[idx];
135115

136-
ret = sbi_suspend(state) ? -1 : idx;
116+
ret = riscv_sbi_hart_suspend(state) ? -1 : idx;
137117

138118
ct_cpuidle_exit();
139119

@@ -206,17 +186,6 @@ static const struct of_device_id sbi_cpuidle_state_match[] = {
206186
{ },
207187
};
208188

209-
static bool sbi_suspend_state_is_valid(u32 state)
210-
{
211-
if (state > SBI_HSM_SUSPEND_RET_DEFAULT &&
212-
state < SBI_HSM_SUSPEND_RET_PLATFORM)
213-
return false;
214-
if (state > SBI_HSM_SUSPEND_NON_RET_DEFAULT &&
215-
state < SBI_HSM_SUSPEND_NON_RET_PLATFORM)
216-
return false;
217-
return true;
218-
}
219-
220189
static int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
221190
{
222191
int err = of_property_read_u32(np, "riscv,sbi-suspend-param", state);
@@ -226,7 +195,7 @@ static int sbi_dt_parse_state_node(struct device_node *np, u32 *state)
226195
return err;
227196
}
228197

229-
if (!sbi_suspend_state_is_valid(*state)) {
198+
if (!riscv_sbi_suspend_state_is_valid(*state)) {
230199
pr_warn("Invalid SBI suspend state %#x\n", *state);
231200
return -EINVAL;
232201
}
@@ -607,16 +576,8 @@ static int __init sbi_cpuidle_init(void)
607576
int ret;
608577
struct platform_device *pdev;
609578

610-
/*
611-
* The SBI HSM suspend function is only available when:
612-
* 1) SBI version is 0.3 or higher
613-
* 2) SBI HSM extension is available
614-
*/
615-
if ((sbi_spec_version < sbi_mk_version(0, 3)) ||
616-
!sbi_probe_extension(SBI_EXT_HSM)) {
617-
pr_info("HSM suspend not available\n");
579+
if (!riscv_sbi_hsm_is_supported())
618580
return 0;
619-
}
620581

621582
ret = platform_driver_register(&sbi_cpuidle_driver);
622583
if (ret)

0 commit comments

Comments
 (0)