Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
285 lines
11 KiB
C
285 lines
11 KiB
C
/*
|
|
* RISC-V board helpers for FDT generation.
|
|
*
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include "qemu/error-report.h"
|
|
#include "system/device_tree.h"
|
|
#include "hw/core/boards.h"
|
|
#include "hw/riscv/fdt-common.h"
|
|
#include "target/riscv/cpu_bits.h"
|
|
|
|
void *create_board_device_tree(const char *model, const char *compatible,
|
|
int *fdt_size)
|
|
{
|
|
void *fdt = create_device_tree(fdt_size);
|
|
|
|
if (!fdt) {
|
|
error_report("create_device_tree() failed");
|
|
exit(1);
|
|
}
|
|
|
|
qemu_fdt_setprop_string(fdt, "/", "model", model);
|
|
qemu_fdt_setprop_string(fdt, "/", "compatible", compatible);
|
|
qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
|
|
qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
|
|
|
|
qemu_fdt_add_subnode(fdt, "/soc");
|
|
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
|
|
qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
|
|
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
|
|
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
|
|
|
|
return fdt;
|
|
}
|
|
|
|
void create_fdt_socket_memory(void *fdt, hwaddr addr, uint64_t size,
|
|
int socket_id, bool numa_enabled)
|
|
{
|
|
g_autofree char *mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr);
|
|
|
|
qemu_fdt_add_subnode(fdt, mem_name);
|
|
qemu_fdt_setprop_sized_cells(fdt, mem_name, "reg", 2, addr, 2, size);
|
|
qemu_fdt_setprop_string(fdt, mem_name, "device_type", "memory");
|
|
|
|
if (numa_enabled) {
|
|
qemu_fdt_setprop_cell(fdt, mem_name, "numa-node-id", socket_id);
|
|
}
|
|
}
|
|
|
|
void create_fdt_socket_clint(void *fdt, hwaddr addr, uint64_t size,
|
|
int socket_id, uint32_t *intc_phandles,
|
|
int num_harts, bool numa_enabled)
|
|
{
|
|
g_autofree uint32_t *clint_cells = g_new0(uint32_t, num_harts * 4);
|
|
g_autofree char *clint_name = NULL;
|
|
static const char * const clint_compat[2] = {
|
|
"sifive,clint0", "riscv,clint0"
|
|
};
|
|
|
|
for (int cpu = 0; cpu < num_harts; cpu++) {
|
|
clint_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
|
|
clint_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
|
|
clint_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
|
|
clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
|
|
}
|
|
|
|
clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, addr);
|
|
qemu_fdt_add_subnode(fdt, clint_name);
|
|
qemu_fdt_setprop_string_array(fdt, clint_name, "compatible",
|
|
(char **)&clint_compat,
|
|
ARRAY_SIZE(clint_compat));
|
|
qemu_fdt_setprop_sized_cells(fdt, clint_name, "reg",
|
|
2, addr, 2, size);
|
|
qemu_fdt_setprop(fdt, clint_name, "interrupts-extended",
|
|
clint_cells, num_harts * sizeof(uint32_t) * 4);
|
|
|
|
if (numa_enabled) {
|
|
qemu_fdt_setprop_cell(fdt, clint_name, "numa-node-id", socket_id);
|
|
}
|
|
}
|
|
|
|
void fdt_create_cpu_socket_subnode(void *fdt, uint64_t timebase_frequency)
|
|
{
|
|
qemu_fdt_add_subnode(fdt, "/cpus");
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
|
|
timebase_frequency);
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
|
|
qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
|
|
qemu_fdt_add_subnode(fdt, "/cpus/cpu-map");
|
|
}
|
|
|
|
static void
|
|
create_fdt_socket_cpu_internal(void *fdt, char *clust_name, RISCVCPU *cpu_ptr,
|
|
int cpu, int socket_id, int socket_hartid_base,
|
|
uint32_t *phandle, uint32_t *intc_phandles,
|
|
bool numa_enabled, bool is_32_bit)
|
|
{
|
|
g_autofree char *cpu_name = NULL;
|
|
g_autofree char *core_name = NULL;
|
|
g_autofree char *intc_name = NULL;
|
|
uint32_t cpu_phandle = (*phandle)++;
|
|
bool is_sifive_u = cpu_ptr == NULL;
|
|
|
|
cpu_name = g_strdup_printf("/cpus/cpu@%d", socket_hartid_base + cpu);
|
|
|
|
/*
|
|
* The sifive_u board has an exclusive satp and riscv,isa
|
|
* schema that can't be shared with other boards, so part
|
|
* of the CPU FDT creation (i.e. the /cpus/cpu@N subnode)
|
|
* is still being done by the board.
|
|
*/
|
|
if (!is_sifive_u) {
|
|
int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode;
|
|
|
|
qemu_fdt_add_subnode(fdt, cpu_name);
|
|
|
|
if (satp_mode_max != -1) {
|
|
g_autofree char *sv_name = NULL;
|
|
sv_name = g_strdup_printf("riscv,%s",
|
|
satp_mode_str(satp_mode_max, is_32_bit));
|
|
qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", sv_name);
|
|
}
|
|
riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name);
|
|
|
|
if (cpu_ptr->cfg.ext_zicbom) {
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size",
|
|
cpu_ptr->cfg.cbom_blocksize);
|
|
}
|
|
|
|
if (cpu_ptr->cfg.ext_zicboz) {
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size",
|
|
cpu_ptr->cfg.cboz_blocksize);
|
|
}
|
|
|
|
if (cpu_ptr->cfg.ext_zicbop) {
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size",
|
|
cpu_ptr->cfg.cbop_blocksize);
|
|
}
|
|
}
|
|
|
|
qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
|
|
qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "reg",
|
|
socket_hartid_base + cpu);
|
|
qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
|
|
if (numa_enabled) {
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "numa-node-id", socket_id);
|
|
}
|
|
qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
|
|
|
|
intc_phandles[cpu] = (*phandle)++;
|
|
|
|
intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
|
|
qemu_fdt_add_subnode(fdt, intc_name);
|
|
qemu_fdt_setprop_cell(fdt, intc_name, "phandle",
|
|
intc_phandles[cpu]);
|
|
qemu_fdt_setprop_string(fdt, intc_name, "compatible",
|
|
"riscv,cpu-intc");
|
|
qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
|
|
qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
|
|
|
|
core_name = g_strdup_printf("%s/core%d", clust_name, cpu);
|
|
qemu_fdt_add_subnode(fdt, core_name);
|
|
qemu_fdt_setprop_cell(fdt, core_name, "cpu", cpu_phandle);
|
|
}
|
|
|
|
void create_fdt_socket_cpus(void *fdt, RISCVCPU *socket_harts,
|
|
int socket_id, int num_harts_socket,
|
|
int socket_hartid_base, uint32_t *phandle,
|
|
uint32_t *intc_phandles, bool numa_enabled,
|
|
bool is_32_bit)
|
|
{
|
|
g_autofree char *clust_name = NULL;
|
|
|
|
clust_name = g_strdup_printf("/cpus/cpu-map/cluster%d", socket_id);
|
|
qemu_fdt_add_subnode(fdt, clust_name);
|
|
|
|
for (int cpu = num_harts_socket - 1; cpu >= 0; cpu--) {
|
|
RISCVCPU *cpu_ptr = &socket_harts[cpu];
|
|
|
|
create_fdt_socket_cpu_internal(fdt, clust_name, cpu_ptr, cpu,
|
|
socket_id, socket_hartid_base,
|
|
phandle, intc_phandles, numa_enabled,
|
|
is_32_bit);
|
|
}
|
|
}
|
|
|
|
void
|
|
create_fdt_socket_cpu_sifive(void *fdt, char *clust_name,
|
|
int cpu_id, int socket_id,
|
|
int socket_hartid_base, uint32_t *phandle,
|
|
uint32_t *intc_phandles)
|
|
{
|
|
create_fdt_socket_cpu_internal(fdt, clust_name, NULL, cpu_id,
|
|
socket_id, socket_hartid_base,
|
|
phandle, intc_phandles, false, false);
|
|
}
|
|
|
|
void create_fdt_plic(void *fdt, hwaddr addr, uint64_t size,
|
|
uint32_t plic_phandle, uint32_t int_cells,
|
|
uint32_t addr_cells, uint32_t *plic_cells,
|
|
uint32_t cells_size, uint32_t ndev_sources,
|
|
bool numa_enabled, int socket_id)
|
|
{
|
|
g_autofree char *nodename = NULL;
|
|
static const char * const plic_compat[2] = {
|
|
"sifive,plic-1.0.0", "riscv,plic0"
|
|
};
|
|
|
|
nodename = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIx, addr);
|
|
|
|
qemu_fdt_add_subnode(fdt, nodename);
|
|
qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", int_cells);
|
|
qemu_fdt_setprop_cell(fdt, nodename, "#address-cells", addr_cells);
|
|
qemu_fdt_setprop_string_array(fdt, nodename, "compatible",
|
|
(char **)&plic_compat, ARRAY_SIZE(plic_compat));
|
|
qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0);
|
|
qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
|
|
plic_cells, cells_size);
|
|
qemu_fdt_setprop_sized_cells(fdt, nodename, "reg",
|
|
2, addr, 2, size);
|
|
qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", ndev_sources);
|
|
if (numa_enabled) {
|
|
qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", socket_id);
|
|
}
|
|
qemu_fdt_setprop_cell(fdt, nodename, "phandle", plic_phandle);
|
|
}
|
|
|
|
/*
|
|
* To keep it simple, any event can be mapped to any programmable counters in
|
|
* QEMU. The generic cycle & instruction count events can also be monitored
|
|
* using programmable counters. In that case, mcycle & minstret must continue
|
|
* to provide the correct value as well. Heterogeneous PMU per hart is not
|
|
* supported yet. Thus, number of counters are same across all harts.
|
|
*/
|
|
void riscv_pmu_generate_fdt_node(void *fdt, uint32_t cmask, char *pmu_name)
|
|
{
|
|
uint32_t fdt_event_ctr_map[15] = {};
|
|
|
|
/*
|
|
* The event encoding is specified in the SBI specification
|
|
* Event idx is a 20bits wide number encoded as follows:
|
|
* event_idx[19:16] = type
|
|
* event_idx[15:0] = code
|
|
* The code field in cache events are encoded as follows:
|
|
* event_idx.code[15:3] = cache_id
|
|
* event_idx.code[2:1] = op_id
|
|
* event_idx.code[0:0] = result_id
|
|
*/
|
|
|
|
/* SBI_PMU_HW_CPU_CYCLES: 0x01 : type(0x00) */
|
|
fdt_event_ctr_map[0] = cpu_to_be32(0x00000001);
|
|
fdt_event_ctr_map[1] = cpu_to_be32(0x00000001);
|
|
fdt_event_ctr_map[2] = cpu_to_be32(cmask | 1 << 0);
|
|
|
|
/* SBI_PMU_HW_INSTRUCTIONS: 0x02 : type(0x00) */
|
|
fdt_event_ctr_map[3] = cpu_to_be32(0x00000002);
|
|
fdt_event_ctr_map[4] = cpu_to_be32(0x00000002);
|
|
fdt_event_ctr_map[5] = cpu_to_be32(cmask | 1 << 2);
|
|
|
|
/* SBI_PMU_HW_CACHE_DTLB : 0x03 READ : 0x00 MISS : 0x00 type(0x01) */
|
|
fdt_event_ctr_map[6] = cpu_to_be32(0x00010019);
|
|
fdt_event_ctr_map[7] = cpu_to_be32(0x00010019);
|
|
fdt_event_ctr_map[8] = cpu_to_be32(cmask);
|
|
|
|
/* SBI_PMU_HW_CACHE_DTLB : 0x03 WRITE : 0x01 MISS : 0x00 type(0x01) */
|
|
fdt_event_ctr_map[9] = cpu_to_be32(0x0001001B);
|
|
fdt_event_ctr_map[10] = cpu_to_be32(0x0001001B);
|
|
fdt_event_ctr_map[11] = cpu_to_be32(cmask);
|
|
|
|
/* SBI_PMU_HW_CACHE_ITLB : 0x04 READ : 0x00 MISS : 0x00 type(0x01) */
|
|
fdt_event_ctr_map[12] = cpu_to_be32(0x00010021);
|
|
fdt_event_ctr_map[13] = cpu_to_be32(0x00010021);
|
|
fdt_event_ctr_map[14] = cpu_to_be32(cmask);
|
|
|
|
/* This a OpenSBI specific DT property documented in OpenSBI docs */
|
|
qemu_fdt_setprop(fdt, pmu_name, "riscv,event-to-mhpmcounters",
|
|
fdt_event_ctr_map, sizeof(fdt_event_ctr_map));
|
|
}
|