blob: bc1c9c72045cf2eb75e28b021e30fe99276782bc [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
*/
#include <libcflat.h>
#include <bitops.h>
#include <devicetree.h>
#include <string.h>
#include <asm/isa.h>
#include <asm/setup.h>
typedef void (*isa_func_t)(const char *, int, void *);
struct isa_info {
unsigned long hartid;
isa_func_t func;
void *data;
};
static bool isa_match(const char *ext, const char *name, int len)
{
return len == strlen(ext) && !strncasecmp(name, ext, len);
}
struct isa_check {
const char *ext;
bool found;
};
static void isa_name(const char *name, int len, void *data)
{
struct isa_check *check = (struct isa_check *)data;
if (isa_match(check->ext, name, len))
check->found = true;
}
static void isa_bit(const char *name, int len, void *data)
{
struct thread_info *info = (struct thread_info *)data;
if (isa_match("sstc", name, len))
set_bit(ISA_SSTC, info->isa);
}
static void isa_parse(const char *isa_string, int len, struct isa_info *info)
{
assert(isa_string[0] == 'r' && isa_string[1] == 'v');
#if __riscv_xlen == 32
assert(isa_string[2] == '3' && isa_string[3] == '2');
#else
assert(isa_string[2] == '6' && isa_string[3] == '4');
#endif
for (int i = 4; i < len; ++i) {
if (isa_string[i] == '_') {
const char *multi = &isa_string[++i];
int start = i;
while (i < len - 1 && isa_string[i] != '_')
++i;
info->func(multi, i - start, info->data);
if (i < len - 1)
--i;
} else {
info->func(&isa_string[i], 1, info->data);
}
}
}
static void isa_parse_fdt(int cpu_node, u64 hartid, void *data)
{
struct isa_info *info = (struct isa_info *)data;
const struct fdt_property *prop;
int len;
if (hartid != info->hartid)
return;
prop = fdt_get_property(dt_fdt(), cpu_node, "riscv,isa", &len);
assert(prop);
isa_parse(prop->data, len, info);
}
static void isa_init_acpi(void)
{
assert_msg(false, "ACPI not available");
}
void isa_init(struct thread_info *ti)
{
struct isa_info info = {
.hartid = ti->hartid,
.func = isa_bit,
.data = ti,
};
int ret;
if (dt_available()) {
ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
assert(ret == 0);
} else {
isa_init_acpi();
}
}
bool cpu_has_extension_name(int cpu, const char *ext)
{
struct isa_info info = {
.hartid = cpus[cpu].hartid,
.func = isa_name,
.data = &(struct isa_check){ .ext = ext, },
};
struct isa_check *check = info.data;
int ret;
if (dt_available()) {
ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
assert(ret == 0);
} else {
assert_msg(false, "ACPI not available");
}
return check->found;
}