Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156// 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;}