214 lines
5.3 KiB
C
214 lines
5.3 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <anup.patel@wdc.com>
|
|
*/
|
|
|
|
#include <libfdt.h>
|
|
#include <platform_override.h>
|
|
#include <sbi/riscv_asm.h>
|
|
#include <sbi/sbi_hartmask.h>
|
|
#include <sbi/sbi_platform.h>
|
|
#include <sbi/sbi_string.h>
|
|
#include <sbi_utils/fdt/fdt_fixup.h>
|
|
#include <sbi_utils/fdt/fdt_helper.h>
|
|
#include <sbi_utils/irqchip/fdt_irqchip.h>
|
|
#include <sbi_utils/serial/fdt_serial.h>
|
|
#include <sbi_utils/timer/fdt_timer.h>
|
|
#include <sbi_utils/ipi/fdt_ipi.h>
|
|
#include <sbi_utils/reset/fdt_reset.h>
|
|
|
|
extern const struct platform_override sifive_fu540;
|
|
|
|
static const struct platform_override *special_platforms[] = {
|
|
&sifive_fu540,
|
|
};
|
|
|
|
static const struct platform_override *generic_plat = NULL;
|
|
static const struct fdt_match *generic_plat_match = NULL;
|
|
|
|
static void fw_platform_lookup_special(void *fdt, int root_offset)
|
|
{
|
|
int pos, noff;
|
|
const struct platform_override *plat;
|
|
const struct fdt_match *match;
|
|
|
|
for (pos = 0; pos < array_size(special_platforms); pos++) {
|
|
plat = special_platforms[pos];
|
|
if (!plat->match_table)
|
|
continue;
|
|
|
|
noff = fdt_find_match(fdt, -1, plat->match_table, &match);
|
|
if (noff < 0)
|
|
continue;
|
|
|
|
generic_plat = plat;
|
|
generic_plat_match = match;
|
|
break;
|
|
}
|
|
}
|
|
|
|
extern struct sbi_platform platform;
|
|
static u32 generic_hart_index2id[SBI_HARTMASK_MAX_BITS] = { 0 };
|
|
|
|
/*
|
|
* The fw_platform_init() function is called very early on the boot HART
|
|
* OpenSBI reference firmwares so that platform specific code get chance
|
|
* to update "platform" instance before it is used.
|
|
*
|
|
* The arguments passed to fw_platform_init() function are boot time state
|
|
* of A0 to A4 register. The "arg0" will be boot HART id and "arg1" will
|
|
* be address of FDT passed by previous booting stage.
|
|
*/
|
|
void fw_platform_init(unsigned long arg0, unsigned long arg1,
|
|
unsigned long arg2, unsigned long arg3,
|
|
unsigned long arg4)
|
|
{
|
|
const char *model, *mmu_type;
|
|
void *fdt = (void *)arg1;
|
|
u32 hartid, hart_count = 0;
|
|
int rc, root_offset, cpus_offset, cpu_offset, len;
|
|
|
|
root_offset = fdt_path_offset(fdt, "/");
|
|
if (root_offset < 0)
|
|
goto fail;
|
|
|
|
fw_platform_lookup_special(fdt, root_offset);
|
|
|
|
model = fdt_getprop(fdt, root_offset, "model", &len);
|
|
if (model)
|
|
sbi_strncpy(platform.name, model, sizeof(platform.name));
|
|
|
|
if (generic_plat && generic_plat->features)
|
|
platform.features = generic_plat->features(generic_plat_match);
|
|
|
|
cpus_offset = fdt_path_offset(fdt, "/cpus");
|
|
if (cpus_offset < 0)
|
|
goto fail;
|
|
|
|
fdt_for_each_subnode(cpu_offset, fdt, cpus_offset) {
|
|
rc = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
|
|
if (rc)
|
|
continue;
|
|
|
|
if (SBI_HARTMASK_MAX_BITS <= hartid)
|
|
continue;
|
|
|
|
mmu_type = fdt_getprop(fdt, cpu_offset, "mmu-type", &len);
|
|
if (!mmu_type || !len)
|
|
hartid = -1U;
|
|
|
|
generic_hart_index2id[hart_count++] = hartid;
|
|
}
|
|
|
|
platform.hart_count = hart_count;
|
|
|
|
return;
|
|
|
|
fail:
|
|
while (1)
|
|
wfi();
|
|
}
|
|
|
|
static int generic_early_init(bool cold_boot)
|
|
{
|
|
int rc;
|
|
|
|
if (generic_plat && generic_plat->early_init) {
|
|
rc = generic_plat->early_init(cold_boot, generic_plat_match);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
if (!cold_boot)
|
|
return 0;
|
|
|
|
return fdt_reset_init();
|
|
}
|
|
|
|
static int generic_final_init(bool cold_boot)
|
|
{
|
|
void *fdt;
|
|
int rc;
|
|
|
|
if (generic_plat && generic_plat->final_init) {
|
|
rc = generic_plat->final_init(cold_boot, generic_plat_match);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
if (!cold_boot)
|
|
return 0;
|
|
|
|
fdt = sbi_scratch_thishart_arg1_ptr();
|
|
|
|
fdt_cpu_fixup(fdt);
|
|
fdt_fixups(fdt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void generic_early_exit(void)
|
|
{
|
|
if (generic_plat && generic_plat->early_exit)
|
|
generic_plat->early_exit(generic_plat_match);
|
|
}
|
|
|
|
static void generic_final_exit(void)
|
|
{
|
|
if (generic_plat && generic_plat->final_exit)
|
|
generic_plat->final_exit(generic_plat_match);
|
|
}
|
|
|
|
static u64 generic_tlbr_flush_limit(void)
|
|
{
|
|
if (generic_plat && generic_plat->tlbr_flush_limit)
|
|
return generic_plat->tlbr_flush_limit(generic_plat_match);
|
|
return SBI_PLATFORM_TLB_RANGE_FLUSH_LIMIT_DEFAULT;
|
|
}
|
|
|
|
static int generic_system_reset(u32 reset_type)
|
|
{
|
|
if (generic_plat && generic_plat->system_reset)
|
|
return generic_plat->system_reset(reset_type,
|
|
generic_plat_match);
|
|
return fdt_system_reset(reset_type);
|
|
}
|
|
|
|
const struct sbi_platform_operations platform_ops = {
|
|
.early_init = generic_early_init,
|
|
.final_init = generic_final_init,
|
|
.early_exit = generic_early_exit,
|
|
.final_exit = generic_final_exit,
|
|
.console_putc = fdt_serial_putc,
|
|
.console_getc = fdt_serial_getc,
|
|
.console_init = fdt_serial_init,
|
|
.irqchip_init = fdt_irqchip_init,
|
|
.irqchip_exit = fdt_irqchip_exit,
|
|
.ipi_send = fdt_ipi_send,
|
|
.ipi_clear = fdt_ipi_clear,
|
|
.ipi_init = fdt_ipi_init,
|
|
.ipi_exit = fdt_ipi_exit,
|
|
.get_tlbr_flush_limit = generic_tlbr_flush_limit,
|
|
.timer_value = fdt_timer_value,
|
|
.timer_event_stop = fdt_timer_event_stop,
|
|
.timer_event_start = fdt_timer_event_start,
|
|
.timer_init = fdt_timer_init,
|
|
.timer_exit = fdt_timer_exit,
|
|
.system_reset = generic_system_reset,
|
|
};
|
|
|
|
struct sbi_platform platform = {
|
|
.opensbi_version = OPENSBI_VERSION,
|
|
.platform_version = SBI_PLATFORM_VERSION(0x0, 0x01),
|
|
.name = "Generic",
|
|
.features = SBI_PLATFORM_DEFAULT_FEATURES,
|
|
.hart_count = SBI_HARTMASK_MAX_BITS,
|
|
.hart_index2id = generic_hart_index2id,
|
|
.hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
|
|
.platform_ops_addr = (unsigned long)&platform_ops
|
|
};
|