1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
4 */
5 #include <libcflat.h>
6 #include <bitops.h>
7 #include <devicetree.h>
8 #include <string.h>
9 #include <asm/isa.h>
10 #include <asm/setup.h>
11
12 typedef void (*isa_func_t)(const char *, int, void *);
13
14 struct isa_info {
15 unsigned long hartid;
16 isa_func_t func;
17 void *data;
18 };
19
isa_match(const char * ext,const char * name,int len)20 static bool isa_match(const char *ext, const char *name, int len)
21 {
22 return len == strlen(ext) && !strncasecmp(name, ext, len);
23 }
24
25 struct isa_check {
26 const char *ext;
27 bool found;
28 };
29
isa_name(const char * name,int len,void * data)30 static void isa_name(const char *name, int len, void *data)
31 {
32 struct isa_check *check = (struct isa_check *)data;
33
34 if (isa_match(check->ext, name, len))
35 check->found = true;
36 }
37
isa_bit(const char * name,int len,void * data)38 static void isa_bit(const char *name, int len, void *data)
39 {
40 struct thread_info *info = (struct thread_info *)data;
41
42 if (isa_match("sstc", name, len))
43 set_bit(ISA_SSTC, info->isa);
44 }
45
isa_parse(const char * isa_string,int len,struct isa_info * info)46 static void isa_parse(const char *isa_string, int len, struct isa_info *info)
47 {
48 assert(isa_string[0] == 'r' && isa_string[1] == 'v');
49 #if __riscv_xlen == 32
50 assert(isa_string[2] == '3' && isa_string[3] == '2');
51 #else
52 assert(isa_string[2] == '6' && isa_string[3] == '4');
53 #endif
54
55 for (int i = 4; i < len; ++i) {
56 if (isa_string[i] == '_') {
57 const char *multi = &isa_string[++i];
58 int start = i;
59
60 while (i < len - 1 && isa_string[i] != '_')
61 ++i;
62 info->func(multi, i - start, info->data);
63 if (i < len - 1)
64 --i;
65 } else {
66 info->func(&isa_string[i], 1, info->data);
67 }
68 }
69 }
70
isa_parse_fdt(int cpu_node,u64 hartid,void * data)71 static void isa_parse_fdt(int cpu_node, u64 hartid, void *data)
72 {
73 struct isa_info *info = (struct isa_info *)data;
74 const struct fdt_property *prop;
75 int len;
76
77 if (hartid != info->hartid)
78 return;
79
80 prop = fdt_get_property(dt_fdt(), cpu_node, "riscv,isa", &len);
81 assert(prop);
82
83 isa_parse(prop->data, len, info);
84 }
85
isa_init_acpi(void)86 static void isa_init_acpi(void)
87 {
88 assert_msg(false, "ACPI not available");
89 }
90
isa_init(struct thread_info * ti)91 void isa_init(struct thread_info *ti)
92 {
93 struct isa_info info = {
94 .hartid = ti->hartid,
95 .func = isa_bit,
96 .data = ti,
97 };
98 int ret;
99
100 if (dt_available()) {
101 ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
102 assert(ret == 0);
103 } else {
104 isa_init_acpi();
105 }
106 }
107
cpu_has_extension_name(int cpu,const char * ext)108 bool cpu_has_extension_name(int cpu, const char *ext)
109 {
110 struct isa_info info = {
111 .hartid = cpus[cpu].hartid,
112 .func = isa_name,
113 .data = &(struct isa_check){ .ext = ext, },
114 };
115 struct isa_check *check = info.data;
116 int ret;
117
118 if (dt_available()) {
119 ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
120 assert(ret == 0);
121 } else {
122 assert_msg(false, "ACPI not available");
123 }
124
125 return check->found;
126 }
127