158 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Implement CPPC FFH helper routines for RISC-V.4 *5 * Copyright (C) 2024 Ventana Micro Systems Inc.6 */7 8#include <acpi/cppc_acpi.h>9#include <asm/csr.h>10#include <asm/sbi.h>11 12#define SBI_EXT_CPPC 0x4350504313 14/* CPPC interfaces defined in SBI spec */15#define SBI_CPPC_PROBE 0x016#define SBI_CPPC_READ 0x117#define SBI_CPPC_READ_HI 0x218#define SBI_CPPC_WRITE 0x319 20/* RISC-V FFH definitions from RISC-V FFH spec */21#define FFH_CPPC_TYPE(r) (((r) & GENMASK_ULL(63, 60)) >> 60)22#define FFH_CPPC_SBI_REG(r) ((r) & GENMASK(31, 0))23#define FFH_CPPC_CSR_NUM(r) ((r) & GENMASK(11, 0))24 25#define FFH_CPPC_SBI 0x126#define FFH_CPPC_CSR 0x227 28struct sbi_cppc_data {29 u64 val;30 u32 reg;31 struct sbiret ret;32};33 34static bool cppc_ext_present;35 36static int __init sbi_cppc_init(void)37{38 if (sbi_spec_version >= sbi_mk_version(2, 0) &&39 sbi_probe_extension(SBI_EXT_CPPC) > 0) {40 pr_info("SBI CPPC extension detected\n");41 cppc_ext_present = true;42 } else {43 pr_info("SBI CPPC extension NOT detected!!\n");44 cppc_ext_present = false;45 }46 47 return 0;48}49device_initcall(sbi_cppc_init);50 51static void sbi_cppc_read(void *read_data)52{53 struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;54 55 data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ,56 data->reg, 0, 0, 0, 0, 0);57}58 59static void sbi_cppc_write(void *write_data)60{61 struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;62 63 data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE,64 data->reg, data->val, 0, 0, 0, 0);65}66 67static void cppc_ffh_csr_read(void *read_data)68{69 struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data;70 71 switch (data->reg) {72 /* Support only TIME CSR for now */73 case CSR_TIME:74 data->ret.value = csr_read(CSR_TIME);75 data->ret.error = 0;76 break;77 default:78 data->ret.error = -EINVAL;79 break;80 }81}82 83static void cppc_ffh_csr_write(void *write_data)84{85 struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data;86 87 data->ret.error = -EINVAL;88}89 90/*91 * Refer to drivers/acpi/cppc_acpi.c for the description of the functions92 * below.93 */94bool cpc_ffh_supported(void)95{96 return true;97}98 99int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val)100{101 struct sbi_cppc_data data;102 103 if (WARN_ON_ONCE(irqs_disabled()))104 return -EPERM;105 106 if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {107 if (!cppc_ext_present)108 return -EINVAL;109 110 data.reg = FFH_CPPC_SBI_REG(reg->address);111 112 smp_call_function_single(cpu, sbi_cppc_read, &data, 1);113 114 *val = data.ret.value;115 116 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;117 } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {118 data.reg = FFH_CPPC_CSR_NUM(reg->address);119 120 smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1);121 122 *val = data.ret.value;123 124 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;125 }126 127 return -EINVAL;128}129 130int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val)131{132 struct sbi_cppc_data data;133 134 if (WARN_ON_ONCE(irqs_disabled()))135 return -EPERM;136 137 if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) {138 if (!cppc_ext_present)139 return -EINVAL;140 141 data.reg = FFH_CPPC_SBI_REG(reg->address);142 data.val = val;143 144 smp_call_function_single(cpu, sbi_cppc_write, &data, 1);145 146 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;147 } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) {148 data.reg = FFH_CPPC_CSR_NUM(reg->address);149 data.val = val;150 151 smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1);152 153 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0;154 }155 156 return -EINVAL;157}158