Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
28struct sbi_cppc_data {
29 u64 val;
30 u32 reg;
31 struct sbiret ret;
32};
33
34static bool cppc_ext_present;
35
36static 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}
47device_initcall(sbi_cppc_init);
48
49static 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
57static 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
65static 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
81static 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 */
92bool cpc_ffh_supported(void)
93{
94 return true;
95}
96
97int 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;
123 }
124
125 return -EINVAL;
126}
127
128int 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;
152 }
153
154 return -EINVAL;
155}