diff options
Diffstat (limited to 'drivers/acpi/riscv/cppc.c')
| -rw-r--r-- | drivers/acpi/riscv/cppc.c | 157 | 
1 files changed, 157 insertions, 0 deletions
diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c new file mode 100644 index 000000000000..4cdff387deff --- /dev/null +++ b/drivers/acpi/riscv/cppc.c @@ -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; +}  | 
