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 28 struct sbi_cppc_data { 29 u64 val; 30 u32 reg; 31 struct sbiret ret; 32 }; 33 34 static bool cppc_ext_present; 35 36 static 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 cppc_ext_present = true; 41 } else { 42 cppc_ext_present = false; 43 } 44 45 return 0; 46 } 47 device_initcall(sbi_cppc_init); 48 49 static void sbi_cppc_read(void *read_data) 50 { 51 struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data; 52 53 data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_READ, 54 data->reg, 0, 0, 0, 0, 0); 55 } 56 57 static void sbi_cppc_write(void *write_data) 58 { 59 struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data; 60 61 data->ret = sbi_ecall(SBI_EXT_CPPC, SBI_CPPC_WRITE, 62 data->reg, data->val, 0, 0, 0, 0); 63 } 64 65 static void cppc_ffh_csr_read(void *read_data) 66 { 67 struct sbi_cppc_data *data = (struct sbi_cppc_data *)read_data; 68 69 switch (data->reg) { 70 /* Support only TIME CSR for now */ 71 case CSR_TIME: 72 data->ret.value = csr_read(CSR_TIME); 73 data->ret.error = 0; 74 break; 75 default: 76 data->ret.error = -EINVAL; 77 break; 78 } 79 } 80 81 static void cppc_ffh_csr_write(void *write_data) 82 { 83 struct sbi_cppc_data *data = (struct sbi_cppc_data *)write_data; 84 85 data->ret.error = -EINVAL; 86 } 87 88 /* 89 * Refer to drivers/acpi/cppc_acpi.c for the description of the functions 90 * below. 91 */ 92 bool cpc_ffh_supported(void) 93 { 94 return true; 95 } 96 97 int cpc_read_ffh(int cpu, struct cpc_reg *reg, u64 *val) 98 { 99 struct sbi_cppc_data data; 100 101 if (WARN_ON_ONCE(irqs_disabled())) 102 return -EPERM; 103 104 if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) { 105 if (!cppc_ext_present) 106 return -EINVAL; 107 108 data.reg = FFH_CPPC_SBI_REG(reg->address); 109 110 smp_call_function_single(cpu, sbi_cppc_read, &data, 1); 111 112 *val = data.ret.value; 113 114 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; 115 } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) { 116 data.reg = FFH_CPPC_CSR_NUM(reg->address); 117 118 smp_call_function_single(cpu, cppc_ffh_csr_read, &data, 1); 119 120 *val = data.ret.value; 121 122 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; 123 } 124 125 return -EINVAL; 126 } 127 128 int cpc_write_ffh(int cpu, struct cpc_reg *reg, u64 val) 129 { 130 struct sbi_cppc_data data; 131 132 if (WARN_ON_ONCE(irqs_disabled())) 133 return -EPERM; 134 135 if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_SBI) { 136 if (!cppc_ext_present) 137 return -EINVAL; 138 139 data.reg = FFH_CPPC_SBI_REG(reg->address); 140 data.val = val; 141 142 smp_call_function_single(cpu, sbi_cppc_write, &data, 1); 143 144 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; 145 } else if (FFH_CPPC_TYPE(reg->address) == FFH_CPPC_CSR) { 146 data.reg = FFH_CPPC_CSR_NUM(reg->address); 147 data.val = val; 148 149 smp_call_function_single(cpu, cppc_ffh_csr_write, &data, 1); 150 151 return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; 152 } 153 154 return -EINVAL; 155 } 156