Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 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; } |