RISC-V: provide Kconfig & commandline options to control parsing "riscv,isa"
[linux-2.6-block.git] / arch / riscv / kernel / cpu.c
CommitLineData
50acfb2b 1// SPDX-License-Identifier: GPL-2.0-only
76d2a049
PD
2/*
3 * Copyright (C) 2012 Regents of the University of California
76d2a049
PD
4 */
5
0b144c81 6#include <linux/acpi.h>
3baca1a4 7#include <linux/cpu.h>
255b34d7 8#include <linux/ctype.h>
76d2a049
PD
9#include <linux/init.h>
10#include <linux/seq_file.h>
11#include <linux/of.h>
0b144c81 12#include <asm/acpi.h>
ff77cf5b 13#include <asm/cpufeature.h>
3baca1a4 14#include <asm/csr.h>
a9b20260 15#include <asm/hwcap.h>
3baca1a4 16#include <asm/sbi.h>
f99fb607 17#include <asm/smp.h>
73c7c8f6 18#include <asm/pgtable.h>
76d2a049 19
b2f8cfa7 20/*
149820c6
JH
21 * Returns the hart ID of the given device tree node, or -ENODEV if the node
22 * isn't an enabled and valid RISC-V hart node.
b2f8cfa7 23 */
ad635e72 24int riscv_of_processor_hartid(struct device_node *node, unsigned long *hart)
2ac87434
CD
25{
26 int cpu;
27
28 *hart = (unsigned long)of_get_cpu_hwid(node, 0);
29 if (*hart == ~0UL) {
30 pr_warn("Found CPU without hart ID\n");
31 return -ENODEV;
32 }
33
34 cpu = riscv_hartid_to_cpuid(*hart);
35 if (cpu < 0)
36 return cpu;
37
38 if (!cpu_possible(cpu))
39 return -ENODEV;
40
41 return 0;
42}
43
496ea826 44int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned long *hart)
76d2a049 45{
e3d794d5 46 const char *isa;
76d2a049
PD
47
48 if (!of_device_is_compatible(node, "riscv")) {
49 pr_warn("Found incompatible CPU\n");
149820c6 50 return -ENODEV;
76d2a049
PD
51 }
52
2ac87434 53 *hart = (unsigned long)of_get_cpu_hwid(node, 0);
ad635e72 54 if (*hart == ~0UL) {
76d2a049 55 pr_warn("Found CPU without hart ID\n");
149820c6 56 return -ENODEV;
76d2a049 57 }
76d2a049 58
e3d794d5 59 if (!of_device_is_available(node)) {
ad635e72 60 pr_info("CPU with hartid=%lu is not available\n", *hart);
149820c6 61 return -ENODEV;
76d2a049
PD
62 }
63
c98f136a
CD
64 if (of_property_read_string(node, "riscv,isa-base", &isa))
65 goto old_interface;
66
67 if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32i", 5)) {
68 pr_warn("CPU with hartid=%lu does not support rv32i", *hart);
69 return -ENODEV;
70 }
71
72 if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64i", 5)) {
73 pr_warn("CPU with hartid=%lu does not support rv64i", *hart);
74 return -ENODEV;
75 }
76
77 if (!of_property_present(node, "riscv,isa-extensions"))
78 return -ENODEV;
79
80 if (of_property_match_string(node, "riscv,isa-extensions", "i") < 0 ||
81 of_property_match_string(node, "riscv,isa-extensions", "m") < 0 ||
82 of_property_match_string(node, "riscv,isa-extensions", "a") < 0) {
83 pr_warn("CPU with hartid=%lu does not support ima", *hart);
84 return -ENODEV;
85 }
86
87 return 0;
88
89old_interface:
496ea826
CD
90 if (!riscv_isa_fallback) {
91 pr_warn("CPU with hartid=%lu is invalid: this kernel does not parse \"riscv,isa\"",
92 *hart);
93 return -ENODEV;
94 }
95
76d2a049 96 if (of_property_read_string(node, "riscv,isa", &isa)) {
c98f136a
CD
97 pr_warn("CPU with hartid=%lu has no \"riscv,isa-base\" or \"riscv,isa\" property\n",
98 *hart);
149820c6 99 return -ENODEV;
76d2a049 100 }
069b0d51 101
23059893
PD
102 if (IS_ENABLED(CONFIG_32BIT) && strncasecmp(isa, "rv32ima", 7)) {
103 pr_warn("CPU with hartid=%lu does not support rv32ima", *hart);
069b0d51 104 return -ENODEV;
23059893 105 }
069b0d51 106
23059893
PD
107 if (IS_ENABLED(CONFIG_64BIT) && strncasecmp(isa, "rv64ima", 7)) {
108 pr_warn("CPU with hartid=%lu does not support rv64ima", *hart);
149820c6 109 return -ENODEV;
23059893 110 }
76d2a049 111
ad635e72 112 return 0;
76d2a049
PD
113}
114
d175d699
AP
115/*
116 * Find hart ID of the CPU DT node under which given DT node falls.
117 *
118 * To achieve this, we walk up the DT tree until we find an active
119 * RISC-V core (HART) node and extract the cpuid from it.
120 */
ad635e72 121int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid)
d175d699 122{
ad635e72
S
123 int rc;
124
d175d699 125 for (; node; node = node->parent) {
ad635e72
S
126 if (of_device_is_compatible(node, "riscv")) {
127 rc = riscv_of_processor_hartid(node, hartid);
128 if (!rc)
129 return 0;
130 }
d175d699
AP
131 }
132
133 return -1;
134}
135
ff77cf5b 136DEFINE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
3baca1a4 137
5e9c68ea
HS
138unsigned long riscv_cached_mvendorid(unsigned int cpu_id)
139{
140 struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
141
142 return ci->mvendorid;
143}
144EXPORT_SYMBOL(riscv_cached_mvendorid);
145
146unsigned long riscv_cached_marchid(unsigned int cpu_id)
147{
148 struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
149
150 return ci->marchid;
151}
152EXPORT_SYMBOL(riscv_cached_marchid);
153
154unsigned long riscv_cached_mimpid(unsigned int cpu_id)
155{
156 struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
157
158 return ci->mimpid;
159}
160EXPORT_SYMBOL(riscv_cached_mimpid);
161
3baca1a4
AP
162static int riscv_cpuinfo_starting(unsigned int cpu)
163{
164 struct riscv_cpuinfo *ci = this_cpu_ptr(&riscv_cpuinfo);
165
166#if IS_ENABLED(CONFIG_RISCV_SBI)
167 ci->mvendorid = sbi_spec_is_0_1() ? 0 : sbi_get_mvendorid();
168 ci->marchid = sbi_spec_is_0_1() ? 0 : sbi_get_marchid();
169 ci->mimpid = sbi_spec_is_0_1() ? 0 : sbi_get_mimpid();
170#elif IS_ENABLED(CONFIG_RISCV_M_MODE)
171 ci->mvendorid = csr_read(CSR_MVENDORID);
172 ci->marchid = csr_read(CSR_MARCHID);
173 ci->mimpid = csr_read(CSR_MIMPID);
174#else
175 ci->mvendorid = 0;
176 ci->marchid = 0;
177 ci->mimpid = 0;
178#endif
179
180 return 0;
181}
182
183static int __init riscv_cpuinfo_init(void)
184{
185 int ret;
186
187 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/cpuinfo:starting",
188 riscv_cpuinfo_starting, NULL);
189 if (ret < 0) {
190 pr_err("cpuinfo: failed to register hotplug callbacks.\n");
191 return ret;
192 }
193
194 return 0;
195}
5e9c68ea
HS
196arch_initcall(riscv_cpuinfo_init);
197
198#ifdef CONFIG_PROC_FS
3baca1a4 199
67270fb3 200static void print_isa(struct seq_file *f)
19ccf29b 201{
4b26d22f 202 seq_puts(f, "isa\t\t: ");
effc122a 203
67270fb3
HS
204 if (IS_ENABLED(CONFIG_32BIT))
205 seq_write(f, "rv32", 4);
206 else
207 seq_write(f, "rv64", 4);
208
effc122a
CD
209 for (int i = 0; i < riscv_isa_ext_count; i++) {
210 if (!__riscv_isa_extension_available(NULL, riscv_isa_ext[i].id))
211 continue;
212
213 /* Only multi-letter extensions are split by underscores */
214 if (strnlen(riscv_isa_ext[i].name, 2) != 1)
215 seq_puts(f, "_");
216
217 seq_printf(f, "%s", riscv_isa_ext[i].name);
a9b20260 218 }
effc122a 219
4b26d22f 220 seq_puts(f, "\n");
19ccf29b
PD
221}
222
73c7c8f6 223static void print_mmu(struct seq_file *f)
19ccf29b 224{
73c7c8f6
AG
225 char sv_type[16];
226
8810d7fe 227#ifdef CONFIG_MMU
19ccf29b 228#if defined(CONFIG_32BIT)
73c7c8f6 229 strncpy(sv_type, "sv32", 5);
19ccf29b 230#elif defined(CONFIG_64BIT)
011f09d1
QP
231 if (pgtable_l5_enabled)
232 strncpy(sv_type, "sv57", 5);
233 else if (pgtable_l4_enabled)
73c7c8f6
AG
234 strncpy(sv_type, "sv48", 5);
235 else
236 strncpy(sv_type, "sv39", 5);
19ccf29b 237#endif
8810d7fe
NC
238#else
239 strncpy(sv_type, "none", 5);
240#endif /* CONFIG_MMU */
73c7c8f6 241 seq_printf(f, "mmu\t\t: %s\n", sv_type);
19ccf29b
PD
242}
243
76d2a049
PD
244static void *c_start(struct seq_file *m, loff_t *pos)
245{
d14e99bf
AJ
246 if (*pos == nr_cpu_ids)
247 return NULL;
248
76d2a049
PD
249 *pos = cpumask_next(*pos - 1, cpu_online_mask);
250 if ((*pos) < nr_cpu_ids)
251 return (void *)(uintptr_t)(1 + *pos);
252 return NULL;
253}
254
255static void *c_next(struct seq_file *m, void *v, loff_t *pos)
256{
257 (*pos)++;
258 return c_start(m, pos);
259}
260
261static void c_stop(struct seq_file *m, void *v)
262{
263}
264
265static int c_show(struct seq_file *m, void *v)
266{
f99fb607 267 unsigned long cpu_id = (unsigned long)v - 1;
3baca1a4 268 struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id);
0b144c81 269 struct device_node *node;
67270fb3 270 const char *compat;
76d2a049 271
4b26d22f
AP
272 seq_printf(m, "processor\t: %lu\n", cpu_id);
273 seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id));
67270fb3
HS
274 print_isa(m);
275 print_mmu(m);
0b144c81
S
276
277 if (acpi_disabled) {
278 node = of_get_cpu_node(cpu_id, NULL);
0b144c81 279
0b144c81
S
280 if (!of_property_read_string(node, "compatible", &compat) &&
281 strcmp(compat, "riscv"))
282 seq_printf(m, "uarch\t\t: %s\n", compat);
283
284 of_node_put(node);
0b144c81
S
285 }
286
3baca1a4
AP
287 seq_printf(m, "mvendorid\t: 0x%lx\n", ci->mvendorid);
288 seq_printf(m, "marchid\t\t: 0x%lx\n", ci->marchid);
289 seq_printf(m, "mimpid\t\t: 0x%lx\n", ci->mimpid);
76d2a049
PD
290 seq_puts(m, "\n");
291
292 return 0;
293}
294
295const struct seq_operations cpuinfo_op = {
296 .start = c_start,
297 .next = c_next,
298 .stop = c_stop,
299 .show = c_show
300};
301
302#endif /* CONFIG_PROC_FS */