ACPI: RISC-V: Add CPPC driver
authorSunil V L <sunilvl@ventanamicro.com>
Thu, 8 Feb 2024 03:44:12 +0000 (09:14 +0530)
committerPalmer Dabbelt <palmer@rivosinc.com>
Wed, 20 Mar 2024 01:30:54 +0000 (18:30 -0700)
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>
drivers/acpi/riscv/Makefile
drivers/acpi/riscv/cppc.c [new file with mode: 0644]

index 7309d92dd4772bc8ec7b263ef5cba59540fc923f..86b0925f612d98b91f4801591768dc88aa9ae88d 100644 (file)
@@ -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
diff --git a/drivers/acpi/riscv/cppc.c b/drivers/acpi/riscv/cppc.c
new file mode 100644 (file)
index 0000000..4cdff38
--- /dev/null
@@ -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;
+}