1 // SPDX-License-Identifier: GPL-2.0-only
3 * Implement CPPC FFH helper routines for RISC-V.
5 * Copyright (C) 2024 Ventana Micro Systems Inc.
8 #include <acpi/cppc_acpi.h>
12 #define SBI_EXT_CPPC 0x43505043
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
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))
25 #define FFH_CPPC_SBI 0x1
26 #define FFH_CPPC_CSR 0x2
28 struct sbi_cppc_data
{
34 static bool cppc_ext_present
;
36 static int __init
sbi_cppc_init(void)
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;
43 pr_info("SBI CPPC extension NOT detected!!\n");
44 cppc_ext_present
= false;
49 device_initcall(sbi_cppc_init
);
51 static void sbi_cppc_read(void *read_data
)
53 struct sbi_cppc_data
*data
= (struct sbi_cppc_data
*)read_data
;
55 data
->ret
= sbi_ecall(SBI_EXT_CPPC
, SBI_CPPC_READ
,
56 data
->reg
, 0, 0, 0, 0, 0);
59 static void sbi_cppc_write(void *write_data
)
61 struct sbi_cppc_data
*data
= (struct sbi_cppc_data
*)write_data
;
63 data
->ret
= sbi_ecall(SBI_EXT_CPPC
, SBI_CPPC_WRITE
,
64 data
->reg
, data
->val
, 0, 0, 0, 0);
67 static void cppc_ffh_csr_read(void *read_data
)
69 struct sbi_cppc_data
*data
= (struct sbi_cppc_data
*)read_data
;
72 /* Support only TIME CSR for now */
74 data
->ret
.value
= csr_read(CSR_TIME
);
78 data
->ret
.error
= -EINVAL
;
83 static void cppc_ffh_csr_write(void *write_data
)
85 struct sbi_cppc_data
*data
= (struct sbi_cppc_data
*)write_data
;
87 data
->ret
.error
= -EINVAL
;
91 * Refer to drivers/acpi/cppc_acpi.c for the description of the functions
94 bool cpc_ffh_supported(void)
99 int cpc_read_ffh(int cpu
, struct cpc_reg
*reg
, u64
*val
)
101 struct sbi_cppc_data data
;
103 if (WARN_ON_ONCE(irqs_disabled()))
106 if (FFH_CPPC_TYPE(reg
->address
) == FFH_CPPC_SBI
) {
107 if (!cppc_ext_present
)
110 data
.reg
= FFH_CPPC_SBI_REG(reg
->address
);
112 smp_call_function_single(cpu
, sbi_cppc_read
, &data
, 1);
114 *val
= data
.ret
.value
;
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
);
120 smp_call_function_single(cpu
, cppc_ffh_csr_read
, &data
, 1);
122 *val
= data
.ret
.value
;
124 return (data
.ret
.error
) ? sbi_err_map_linux_errno(data
.ret
.error
) : 0;
130 int cpc_write_ffh(int cpu
, struct cpc_reg
*reg
, u64 val
)
132 struct sbi_cppc_data data
;
134 if (WARN_ON_ONCE(irqs_disabled()))
137 if (FFH_CPPC_TYPE(reg
->address
) == FFH_CPPC_SBI
) {
138 if (!cppc_ext_present
)
141 data
.reg
= FFH_CPPC_SBI_REG(reg
->address
);
144 smp_call_function_single(cpu
, sbi_cppc_write
, &data
, 1);
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
);
151 smp_call_function_single(cpu
, cppc_ffh_csr_write
, &data
, 1);
153 return (data
.ret
.error
) ? sbi_err_map_linux_errno(data
.ret
.error
) : 0;