2018-09-26 13:55:14 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
2018-12-12 14:12:34 +00:00
|
|
|
#include <cpu.h>
|
2018-12-12 14:12:38 +00:00
|
|
|
#include <dm.h>
|
2021-09-12 19:11:46 +00:00
|
|
|
#include <dm/lists.h>
|
2022-03-04 15:43:05 +00:00
|
|
|
#include <event.h>
|
2020-05-10 17:40:02 +00:00
|
|
|
#include <init.h>
|
2018-12-12 14:12:34 +00:00
|
|
|
#include <log.h>
|
2018-12-12 14:12:40 +00:00
|
|
|
#include <asm/encoding.h>
|
2022-03-04 15:43:05 +00:00
|
|
|
#include <asm/system.h>
|
2018-12-12 14:12:38 +00:00
|
|
|
#include <dm/uclass-internal.h>
|
2020-05-10 17:40:13 +00:00
|
|
|
#include <linux/bitops.h>
|
2018-09-26 13:55:14 +00:00
|
|
|
|
2018-11-22 10:26:29 +00:00
|
|
|
/*
|
2019-03-17 18:28:37 +00:00
|
|
|
* The variables here must be stored in the data section since they are used
|
2018-11-22 10:26:29 +00:00
|
|
|
* before the bss section is available.
|
|
|
|
*/
|
2022-09-02 08:47:39 +00:00
|
|
|
#if !CONFIG_IS_ENABLED(XIP)
|
2021-05-20 11:23:52 +00:00
|
|
|
u32 hart_lottery __section(".data") = 0;
|
2019-03-17 18:28:37 +00:00
|
|
|
|
2022-09-21 06:34:54 +00:00
|
|
|
#ifdef CONFIG_AVAILABLE_HARTS
|
2019-03-17 18:28:37 +00:00
|
|
|
/*
|
|
|
|
* The main hart running U-Boot has acquired available_harts_lock until it has
|
|
|
|
* finished initialization of global data.
|
|
|
|
*/
|
|
|
|
u32 available_harts_lock = 1;
|
2019-04-30 05:49:33 +00:00
|
|
|
#endif
|
2022-09-21 06:34:54 +00:00
|
|
|
#endif
|
2018-11-22 10:26:29 +00:00
|
|
|
|
2018-09-26 13:55:14 +00:00
|
|
|
static inline bool supports_extension(char ext)
|
|
|
|
{
|
2022-12-14 05:58:43 +00:00
|
|
|
#if CONFIG_IS_ENABLED(RISCV_MMODE)
|
|
|
|
return csr_read(CSR_MISA) & (1 << (ext - 'a'));
|
|
|
|
#elif CONFIG_CPU
|
2018-12-12 14:12:38 +00:00
|
|
|
struct udevice *dev;
|
|
|
|
char desc[32];
|
2022-11-05 06:02:14 +00:00
|
|
|
int i;
|
2018-12-12 14:12:38 +00:00
|
|
|
|
|
|
|
uclass_find_first_device(UCLASS_CPU, &dev);
|
|
|
|
if (!dev) {
|
|
|
|
debug("unable to find the RISC-V cpu device\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!cpu_get_desc(dev, desc, sizeof(desc))) {
|
2022-11-05 06:02:14 +00:00
|
|
|
/*
|
|
|
|
* skip the first 4 characters (rv32|rv64) and
|
|
|
|
* check until underscore
|
|
|
|
*/
|
|
|
|
for (i = 4; i < sizeof(desc); i++) {
|
|
|
|
if (desc[i] == '_' || desc[i] == '\0')
|
|
|
|
break;
|
|
|
|
if (desc[i] == ext)
|
|
|
|
return true;
|
|
|
|
}
|
2018-12-12 14:12:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
#else /* !CONFIG_CPU */
|
|
|
|
#warning "There is no way to determine the available extensions in S-mode."
|
|
|
|
#warning "Please convert your board to use the RISC-V CPU driver."
|
|
|
|
return false;
|
|
|
|
#endif /* CONFIG_CPU */
|
2018-09-26 13:55:14 +00:00
|
|
|
}
|
|
|
|
|
2018-12-12 14:12:34 +00:00
|
|
|
static int riscv_cpu_probe(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_CPU
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* probe cpus so that RISC-V timer can be bound */
|
|
|
|
ret = cpu_probe_all();
|
|
|
|
if (ret)
|
|
|
|
return log_msg_ret("RISC-V cpus probe failed\n", ret);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-21 11:51:38 +00:00
|
|
|
/*
|
|
|
|
* This is called on secondary harts just after the IPI is init'd. Currently
|
|
|
|
* there's nothing to do, since we just need to clear any existing IPIs, and
|
|
|
|
* that is handled by the sending of an ipi itself.
|
|
|
|
*/
|
|
|
|
#if CONFIG_IS_ENABLED(SMP)
|
|
|
|
static void dummy_pending_ipi_clear(ulong hart, ulong arg0, ulong arg1)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-03-04 15:43:05 +00:00
|
|
|
int riscv_cpu_setup(void *ctx, struct event *event)
|
2018-12-12 14:12:34 +00:00
|
|
|
{
|
2018-12-12 14:12:40 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = riscv_cpu_probe();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Enable FPU */
|
|
|
|
if (supports_extension('d') || supports_extension('f')) {
|
|
|
|
csr_set(MODE_PREFIX(status), MSTATUS_FS);
|
2019-07-11 06:43:13 +00:00
|
|
|
csr_write(CSR_FCSR, 0);
|
2018-12-12 14:12:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (CONFIG_IS_ENABLED(RISCV_MMODE)) {
|
|
|
|
/*
|
|
|
|
* Enable perf counters for cycle, time,
|
|
|
|
* and instret counters only
|
|
|
|
*/
|
2022-12-14 05:58:43 +00:00
|
|
|
if (supports_extension('u')) {
|
2020-06-24 10:41:19 +00:00
|
|
|
#ifdef CONFIG_RISCV_PRIV_1_9
|
2022-12-14 05:58:43 +00:00
|
|
|
csr_write(CSR_MSCOUNTEREN, GENMASK(2, 0));
|
|
|
|
csr_write(CSR_MUCOUNTEREN, GENMASK(2, 0));
|
2020-06-24 10:41:19 +00:00
|
|
|
#else
|
2022-12-14 05:58:43 +00:00
|
|
|
csr_write(CSR_MCOUNTEREN, GENMASK(2, 0));
|
2020-06-24 10:41:19 +00:00
|
|
|
#endif
|
2022-12-14 05:58:43 +00:00
|
|
|
}
|
2018-12-12 14:12:40 +00:00
|
|
|
|
|
|
|
/* Disable paging */
|
|
|
|
if (supports_extension('s'))
|
2020-06-24 10:41:19 +00:00
|
|
|
#ifdef CONFIG_RISCV_PRIV_1_9
|
|
|
|
csr_read_clear(CSR_MSTATUS, SR_VM);
|
|
|
|
#else
|
2019-07-11 06:43:13 +00:00
|
|
|
csr_write(CSR_SATP, 0);
|
2020-06-24 10:41:19 +00:00
|
|
|
#endif
|
2018-12-12 14:12:40 +00:00
|
|
|
}
|
|
|
|
|
2020-07-20 06:17:07 +00:00
|
|
|
#if CONFIG_IS_ENABLED(SMP)
|
2020-06-24 10:41:18 +00:00
|
|
|
ret = riscv_init_ipi();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-09-21 11:51:38 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear all pending IPIs on secondary harts. We don't do anything on
|
|
|
|
* the boot hart, since we never send an IPI to ourselves, and no
|
|
|
|
* interrupts are enabled
|
|
|
|
*/
|
|
|
|
ret = smp_call_function((ulong)dummy_pending_ipi_clear, 0, 0, 0);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-06-24 10:41:18 +00:00
|
|
|
#endif
|
|
|
|
|
2018-12-12 14:12:40 +00:00
|
|
|
return 0;
|
2018-12-12 14:12:34 +00:00
|
|
|
}
|
2022-03-04 15:43:05 +00:00
|
|
|
EVENT_SPY(EVT_DM_POST_INIT, riscv_cpu_setup);
|
2018-12-12 14:12:34 +00:00
|
|
|
|
|
|
|
int arch_early_init_r(void)
|
|
|
|
{
|
2021-09-12 19:11:46 +00:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = riscv_cpu_probe();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_SYSRESET_SBI))
|
|
|
|
device_bind_driver(gd->dm_root, "sbi-sysreset",
|
|
|
|
"sbi-sysreset", NULL);
|
|
|
|
|
|
|
|
return 0;
|
2018-12-12 14:12:34 +00:00
|
|
|
}
|
2021-05-03 06:23:04 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* harts_early_init() - A callback function called by start.S to configure
|
|
|
|
* feature settings of each hart.
|
|
|
|
*
|
|
|
|
* In a multi-core system, memory access shall be careful here, it shall
|
|
|
|
* take care of race conditions.
|
|
|
|
*/
|
|
|
|
__weak void harts_early_init(void)
|
|
|
|
{
|
|
|
|
}
|