-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add cpufreq driver based on ACPI CPPC for RISC-V. The driver uses either SBI CPPC interfaces or the CSRs to access the CPPC registers as defined by the RISC-V FFH spec. Signed-off-by: Sunil V L <sunilvl@ventanamicro.com> Reviewed-by: Pierre Gondois <pierre.gondois@arm.com> Acked-by: Rafael J. Wysocki <rafael@kernel.org> Acked-by: Sudeep Holla <sudeep.holla@arm.com> Link: https://lore.kernel.org/r/20240208034414.22579-2-sunilvl@ventanamicro.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
- Loading branch information
Sunil V L
authored and
Palmer Dabbelt
committed
Mar 20, 2024
1 parent
359df7c
commit 30f3ffb
Showing
2 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
# SPDX-License-Identifier: GPL-2.0-only | ||
obj-y += rhct.o | ||
obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o | ||
obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
// SPDX-License-Identifier: GPL-2.0-only | ||
/* | ||
* Implement CPPC FFH helper routines for RISC-V. | ||
* | ||
* Copyright (C) 2024 Ventana Micro Systems Inc. | ||
*/ | ||
|
||
#include <acpi/cppc_acpi.h> | ||
#include <asm/csr.h> | ||
#include <asm/sbi.h> | ||
|
||
#define SBI_EXT_CPPC 0x43505043 | ||
|
||
/* CPPC interfaces defined in SBI spec */ | ||
#define SBI_CPPC_PROBE 0x0 | ||
#define SBI_CPPC_READ 0x1 | ||
#define SBI_CPPC_READ_HI 0x2 | ||
#define SBI_CPPC_WRITE 0x3 | ||
|
||
/* RISC-V FFH definitions from RISC-V FFH spec */ | ||
#define FFH_CPPC_TYPE(r) (((r) & GENMASK_ULL(63, 60)) >> 60) | ||
#define FFH_CPPC_SBI_REG(r) ((r) & GENMASK(31, 0)) | ||
#define FFH_CPPC_CSR_NUM(r) ((r) & GENMASK(11, 0)) | ||
|
||
#define FFH_CPPC_SBI 0x1 | ||
#define FFH_CPPC_CSR 0x2 | ||
|
||
struct sbi_cppc_data { | ||
u64 val; | ||
u32 reg; | ||
struct sbiret ret; | ||
}; | ||
|
||
static bool cppc_ext_present; | ||
|
||
static int __init sbi_cppc_init(void) | ||
{ | ||
if (sbi_spec_version >= sbi_mk_version(2, 0) && | ||
sbi_probe_extension(SBI_EXT_CPPC) > 0) { | ||
pr_info("SBI CPPC extension detected\n"); | ||
cppc_ext_present = true; | ||
} else { | ||
pr_info("SBI CPPC extension NOT detected!!\n"); | ||
cppc_ext_present = false; | ||
} | ||
|
||
return 0; | ||
} | ||
device_initcall(sbi_cppc_init); | ||
|
||
static void sbi_cppc_read(void *read_data) | ||
{ | ||
struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data; | ||
|
||
data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ, | ||
data->reg, 0, 0, 0, 0, 0); | ||
} | ||
|
||
static void sbi_cppc_write(void *write_data) | ||
{ | ||
struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data; | ||
|
||
data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE, | ||
data->reg, data->val, 0, 0, 0, 0); | ||
} | ||
|
||
static void cppc_ffh_csr_read(void *read_data) | ||
{ | ||
struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data; | ||
|
||
switch (data->reg) { | ||
/* Support only TIME CSR for now */ | ||
case CSR_TIME: | ||
data->ret.value = csr_read(CSR_TIME); | ||
data->ret.error = 0; | ||
break; | ||
default: | ||
data->ret.error = -EINVAL; | ||
break; | ||
} | ||
} | ||
|
||
static void cppc_ffh_csr_write(void *write_data) | ||
{ | ||
struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data; | ||
|
||
data->ret.error = -EINVAL; | ||
} | ||
|
||
/* | ||
* Refer to drivers/acpi/cppc_acpi.c for the description of the functions | ||
* below. | ||
*/ | ||
bool cpc_ffh_supported(void) | ||
{ | ||
return true; | ||
} | ||
|
||
int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) | ||
{ | ||
struct sbi_cppc_data data; | ||
|
||
if (WARN_ON_ONCE(irqs_disabled())) | ||
return -EPERM; | ||
|
||
if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) { | ||
if (!cppc_ext_present) | ||
return -EINVAL; | ||
|
||
data.reg = FFH_CPPC_SBI_REG(reg->address); | ||
|
||
smp_call_function_single(cpu, sbi_cppc_read, &data, 1); | ||
|
||
*val = data.ret.value; | ||
|
||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; | ||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) { | ||
data.reg = FFH_CPPC_CSR_NUM(reg->address); | ||
|
||
smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1); | ||
|
||
*val = data.ret.value; | ||
|
||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; | ||
} | ||
|
||
return -EINVAL; | ||
} | ||
|
||
int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val) | ||
{ | ||
struct sbi_cppc_data data; | ||
|
||
if (WARN_ON_ONCE(irqs_disabled())) | ||
return -EPERM; | ||
|
||
if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) { | ||
if (!cppc_ext_present) | ||
return -EINVAL; | ||
|
||
data.reg = FFH_CPPC_SBI_REG(reg->address); | ||
data.val = val; | ||
|
||
smp_call_function_single(cpu, sbi_cppc_write, &data, 1); | ||
|
||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; | ||
} else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) { | ||
data.reg = FFH_CPPC_CSR_NUM(reg->address); | ||
data.val = val; | ||
|
||
smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1); | ||
|
||
return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; | ||
} | ||
|
||
return -EINVAL; | ||
} |