155 lines · 3.3 KB · C
RawDownload
// 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) {		cppc_ext_present = true;	} else {		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;	} 	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;	} 	return -EINVAL;}