]> Git Repo - J-linux.git/blob - drivers/acpi/riscv/cppc.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / acpi / riscv / cppc.c
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 }
This page took 0.035243 seconds and 4 git commands to generate.