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(cpuid: cpu, func: sbi_cppc_read, info: &data, wait: 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(cpuid: cpu, func: cppc_ffh_csr_read, info: &data, wait: 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(cpuid: cpu, func: sbi_cppc_write, info: &data, wait: 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(cpuid: cpu, func: cppc_ffh_csr_write, info: &data, wait: 1); |
152 | |
153 | return (data.ret.error) ? sbi_err_map_linux_errno(data.ret.error) : 0; |
154 | } |
155 | |
156 | return -EINVAL; |
157 | } |
158 | |