1// SPDX-License-Identifier: GPL-2.0-only 2/* 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 0x43505043 13 14/* CPPC interfaces defined in SBI spec */ 15#define SBI_CPPC_PROBE 0x0 16#define SBI_CPPC_READ 0x1 17#define SBI_CPPC_READ_HI 0x2 18#define SBI_CPPC_WRITE 0x3 19 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 0x1 26#define FFH_CPPC_CSR 0x2 27 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 functions 92 * 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