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 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 } 49 device_initcall(sbi_cppc_init); 50 51 static 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 59 static 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 67 static 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 83 static 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 */ 94 bool cpc_ffh_supported(void) 95 { 96 return true; 97 } 98 99 int 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 130 int 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