123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286 |
- From fa33f08fd657b8568ede929df8a79bd113e9c5b1 Mon Sep 17 00:00:00 2001
- From: Lukas Auer <lukas.auer@aisec.fraunhofer.de>
- Date: Sun, 17 Mar 2019 19:28:32 +0100
- Subject: [PATCH 01/18] riscv: add infrastructure for calling functions on
- other harts
- 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>
- ---
- arch/riscv/Kconfig | 19 +++++
- arch/riscv/include/asm/global_data.h | 6 ++
- arch/riscv/include/asm/smp.h | 53 ++++++++++++
- arch/riscv/lib/Makefile | 1 +
- arch/riscv/lib/smp.c | 118 +++++++++++++++++++++++++++
- 5 files changed, 197 insertions(+)
- create mode 100644 arch/riscv/include/asm/smp.h
- create mode 100644 arch/riscv/lib/smp.c
- diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
- index 36512a8995..4d7a115569 100644
- --- a/arch/riscv/Kconfig
- +++ b/arch/riscv/Kconfig
- @@ -120,4 +120,23 @@ config RISCV_RDTIME
- config SYS_MALLOC_F_LEN
- default 0x1000
-
- +config SMP
- + bool "Symmetric Multi-Processing"
- + help
- + This enables support for systems with more than one CPU. If
- + you say N here, U-Boot will run on single and multiprocessor
- + machines, but will use only one CPU of a multiprocessor
- + machine. If you say Y here, U-Boot will run on many, but not
- + all, single processor machines.
- +
- +config NR_CPUS
- + int "Maximum number of CPUs (2-32)"
- + range 2 32
- + depends on SMP
- + default 8
- + help
- + On multiprocessor machines, U-Boot sets up a stack for each CPU.
- + Stack memory is pre-allocated. U-Boot must therefore know the
- + maximum number of CPUs that may be present.
- +
- endmenu
- diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h
- index a3a342c6e1..80e3165e39 100644
- --- a/arch/riscv/include/asm/global_data.h
- +++ b/arch/riscv/include/asm/global_data.h
- @@ -10,12 +10,18 @@
- #ifndef __ASM_GBL_DATA_H
- #define __ASM_GBL_DATA_H
-
- +#include <asm/smp.h>
- +
- /* Architecture-specific global data */
- struct arch_global_data {
- long boot_hart; /* boot hart id */
- #ifdef CONFIG_SIFIVE_CLINT
- void __iomem *clint; /* clint base address */
- #endif
- +#ifdef CONFIG_SMP
- + struct ipi_data ipi[CONFIG_NR_CPUS];
- +#endif
- + ulong available_harts;
- };
-
- #include <asm-generic/global_data.h>
- diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
- new file mode 100644
- index 0000000000..bc863fdbaf
- --- /dev/null
- +++ b/arch/riscv/include/asm/smp.h
- @@ -0,0 +1,53 @@
- +/* SPDX-License-Identifier: GPL-2.0 */
- +/*
- + * Copyright (C) 2019 Fraunhofer AISEC,
- + * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
- + */
- +
- +#ifndef _ASM_RISCV_SMP_H
- +#define _ASM_RISCV_SMP_H
- +
- +/**
- + * struct ipi_data - Inter-processor interrupt (IPI) data structure
- + *
- + * IPIs are used for SMP support to communicate to other harts what function to
- + * call. Functions are in the form
- + * void (*addr)(ulong hart, ulong arg0, ulong arg1).
- + *
- + * The function address and the two arguments, arg0 and arg1, are stored in the
- + * IPI data structure. The hart ID is inserted by the hart handling the IPI and
- + * calling the function.
- + *
- + * @addr: Address of function
- + * @arg0: First argument of function
- + * @arg1: Second argument of function
- + */
- +struct ipi_data {
- + ulong addr;
- + ulong arg0;
- + ulong arg1;
- +};
- +
- +/**
- + * handle_ipi() - interrupt handler for software interrupts
- + *
- + * The IPI interrupt handler must be called to handle software interrupts. It
- + * calls the function specified in the hart's IPI data structure.
- + *
- + * @hart: Hart ID of the current hart
- + */
- +void handle_ipi(ulong hart);
- +
- +/**
- + * smp_call_function() - Call a function on all other harts
- + *
- + * Send IPIs with the specified function call to all harts.
- + *
- + * @addr: Address of function
- + * @arg0: First argument of function
- + * @arg1: Second argument of function
- + * @return 0 if OK, -ve on error
- + */
- +int smp_call_function(ulong addr, ulong arg0, ulong arg1);
- +
- +#endif
- diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
- index edfa61690c..19370f9749 100644
- --- a/arch/riscv/lib/Makefile
- +++ b/arch/riscv/lib/Makefile
- @@ -14,6 +14,7 @@ obj-$(CONFIG_SIFIVE_CLINT) += sifive_clint.o
- obj-y += interrupts.o
- obj-y += reset.o
- obj-y += setjmp.o
- +obj-$(CONFIG_SMP) += smp.o
-
- # For building EFI apps
- CFLAGS_$(EFI_CRT0) := $(CFLAGS_EFI)
- diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c
- new file mode 100644
- index 0000000000..caa292ccd2
- --- /dev/null
- +++ b/arch/riscv/lib/smp.c
- @@ -0,0 +1,118 @@
- +// 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;
- +}
- --
- 2.21.0
|