mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-03 18:10:13 +00:00
fa33f08fd6
Harts on RISC-V boot independently, U-Boot is responsible for managing them. Functions are called on other harts with smp_call_function(), which sends inter-processor interrupts (IPIs) to all other available harts. Available harts are those marked as available in the device tree and present in the available_harts mask stored in global data. The available_harts mask is used to register all harts that have entered U-Boot. Functions are specified with their address and two function arguments (argument 2 and 3). The first function argument is always the hart ID of the hart calling the function. On the other harts, the IPI interrupt handler handle_ipi() must be called on software interrupts to handle the request and call the specified function. Functions are stored in the ipi_data data structure. Every hart has its own data structure in global data. While this is not required at the moment (all harts are expected to boot Linux), this does allow future expansion, where other harts may be used for monitoring or other tasks. Signed-off-by: Lukas Auer <lukas.auer@aisec.fraunhofer.de> Reviewed-by: Anup Patel <anup.patel@wdc.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Tested-by: Bin Meng <bmeng.cn@gmail.com>
118 lines
2.3 KiB
C
118 lines
2.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2019 Fraunhofer AISEC,
|
|
* Lukas Auer <lukas.auer@aisec.fraunhofer.de>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <asm/barrier.h>
|
|
#include <asm/smp.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/**
|
|
* riscv_send_ipi() - Send inter-processor interrupt (IPI)
|
|
*
|
|
* Platform code must provide this function.
|
|
*
|
|
* @hart: Hart ID of receiving hart
|
|
* @return 0 if OK, -ve on error
|
|
*/
|
|
extern int riscv_send_ipi(int hart);
|
|
|
|
/**
|
|
* riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
|
|
*
|
|
* Platform code must provide this function.
|
|
*
|
|
* @hart: Hart ID of hart to be cleared
|
|
* @return 0 if OK, -ve on error
|
|
*/
|
|
extern int riscv_clear_ipi(int hart);
|
|
|
|
static int send_ipi_many(struct ipi_data *ipi)
|
|
{
|
|
ofnode node, cpus;
|
|
u32 reg;
|
|
int ret;
|
|
|
|
cpus = ofnode_path("/cpus");
|
|
if (!ofnode_valid(cpus)) {
|
|
pr_err("Can't find cpus node!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ofnode_for_each_subnode(node, cpus) {
|
|
/* skip if hart is marked as not available in the device tree */
|
|
if (!ofnode_is_available(node))
|
|
continue;
|
|
|
|
/* read hart ID of CPU */
|
|
ret = ofnode_read_u32(node, "reg", ®);
|
|
if (ret)
|
|
continue;
|
|
|
|
/* skip if it is the hart we are running on */
|
|
if (reg == gd->arch.boot_hart)
|
|
continue;
|
|
|
|
if (reg >= CONFIG_NR_CPUS) {
|
|
pr_err("Hart ID %d is out of range, increase CONFIG_NR_CPUS\n",
|
|
reg);
|
|
continue;
|
|
}
|
|
|
|
/* skip if hart is not available */
|
|
if (!(gd->arch.available_harts & (1 << reg)))
|
|
continue;
|
|
|
|
gd->arch.ipi[reg].addr = ipi->addr;
|
|
gd->arch.ipi[reg].arg0 = ipi->arg0;
|
|
gd->arch.ipi[reg].arg1 = ipi->arg1;
|
|
|
|
ret = riscv_send_ipi(reg);
|
|
if (ret) {
|
|
pr_err("Cannot send IPI to hart %d\n", reg);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void handle_ipi(ulong hart)
|
|
{
|
|
int ret;
|
|
void (*smp_function)(ulong hart, ulong arg0, ulong arg1);
|
|
|
|
if (hart >= CONFIG_NR_CPUS)
|
|
return;
|
|
|
|
ret = riscv_clear_ipi(hart);
|
|
if (ret) {
|
|
pr_err("Cannot clear IPI of hart %ld\n", hart);
|
|
return;
|
|
}
|
|
|
|
__smp_mb();
|
|
|
|
smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
|
|
invalidate_icache_all();
|
|
|
|
smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
|
|
}
|
|
|
|
int smp_call_function(ulong addr, ulong arg0, ulong arg1)
|
|
{
|
|
int ret = 0;
|
|
struct ipi_data ipi;
|
|
|
|
ipi.addr = addr;
|
|
ipi.arg0 = arg0;
|
|
ipi.arg1 = arg1;
|
|
|
|
ret = send_ipi_many(&ipi);
|
|
|
|
return ret;
|
|
}
|