mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2025-10-09 10:02:19 +01:00

A platform can have multiple IPI devices (such as ACLINT MSWI, AIA IMSIC, etc). Currently, OpenSBI rely on platform calling the sbi_ipi_set_device() function in correct order and prefer the first avaiable IPI device which is fragile. Instead of the above, introduce IPI device rating and prefer the highest rated IPI device. This further allows extending the sbi_ipi_raw_clear() to clear all available IPI devices. Signed-off-by: Anup Patel <apatel@ventanamicro.com> Tested-by: Nick Hu <nick.hu@sifive.com> Link: https://lore.kernel.org/r/20250904052410.546818-2-apatel@ventanamicro.com Signed-off-by: Anup Patel <anup@brainfault.org>
107 lines
2.6 KiB
C
107 lines
2.6 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2022 Andes Technology Corporation
|
|
*
|
|
* Authors:
|
|
* Zong Li <zong@andestech.com>
|
|
* Nylon Chen <nylon7@andestech.com>
|
|
* Leo Yu-Chi Liang <ycliang@andestech.com>
|
|
* Yu Chien Peter Lin <peterlin@andestech.com>
|
|
*/
|
|
|
|
#include <sbi/riscv_asm.h>
|
|
#include <sbi/riscv_io.h>
|
|
#include <sbi/sbi_domain.h>
|
|
#include <sbi/sbi_ipi.h>
|
|
#include <sbi_utils/ipi/andes_plicsw.h>
|
|
|
|
struct plicsw_data plicsw;
|
|
|
|
static void plicsw_ipi_send(u32 hart_index)
|
|
{
|
|
ulong pending_reg;
|
|
u32 interrupt_id, word_index, pending_bit;
|
|
u32 target_hart = sbi_hartindex_to_hartid(hart_index);
|
|
|
|
if (plicsw.hart_count <= target_hart)
|
|
ebreak();
|
|
|
|
/*
|
|
* We assign a single bit for each hart.
|
|
* Bit 0 is hardwired to 0, thus unavailable.
|
|
* Bit(X+1) indicates that IPI is sent to hartX.
|
|
*/
|
|
interrupt_id = target_hart + 1;
|
|
word_index = interrupt_id / 32;
|
|
pending_bit = interrupt_id % 32;
|
|
pending_reg = plicsw.addr + PLICSW_PENDING_BASE + word_index * 4;
|
|
|
|
/* Set target hart's mip.MSIP */
|
|
writel_relaxed(BIT(pending_bit), (void *)pending_reg);
|
|
}
|
|
|
|
static void plicsw_ipi_clear(void)
|
|
{
|
|
u32 target_hart = current_hartid();
|
|
ulong reg = plicsw.addr + PLICSW_CONTEXT_BASE + PLICSW_CONTEXT_CLAIM +
|
|
PLICSW_CONTEXT_STRIDE * target_hart;
|
|
|
|
if (plicsw.hart_count <= target_hart)
|
|
ebreak();
|
|
|
|
/* Claim */
|
|
u32 source = readl((void *)reg);
|
|
|
|
/* A successful claim will clear mip.MSIP */
|
|
|
|
/* Complete */
|
|
writel(source, (void *)reg);
|
|
}
|
|
|
|
static struct sbi_ipi_device plicsw_ipi = {
|
|
.name = "andes_plicsw",
|
|
.rating = 200,
|
|
.ipi_send = plicsw_ipi_send,
|
|
.ipi_clear = plicsw_ipi_clear
|
|
};
|
|
|
|
int plicsw_cold_ipi_init(struct plicsw_data *plicsw)
|
|
{
|
|
int rc;
|
|
u32 interrupt_id, word_index, enable_bit;
|
|
ulong enable_reg, priority_reg;
|
|
|
|
/* Setup source priority */
|
|
for (int i = 0; i < plicsw->hart_count; i++) {
|
|
priority_reg = plicsw->addr + PLICSW_PRIORITY_BASE + i * 4;
|
|
writel(1, (void *)priority_reg);
|
|
}
|
|
|
|
/*
|
|
* Setup enable for each hart, skip non-existent interrupt ID 0
|
|
* which is hardwired to 0.
|
|
*/
|
|
for (int i = 0; i < plicsw->hart_count; i++) {
|
|
interrupt_id = i + 1;
|
|
word_index = interrupt_id / 32;
|
|
enable_bit = interrupt_id % 32;
|
|
enable_reg = plicsw->addr + PLICSW_ENABLE_BASE +
|
|
PLICSW_ENABLE_STRIDE * i + 4 * word_index;
|
|
writel(BIT(enable_bit), (void *)enable_reg);
|
|
}
|
|
|
|
/* Add PLICSW region to the root domain */
|
|
rc = sbi_domain_root_add_memrange(plicsw->addr, plicsw->size,
|
|
PLICSW_REGION_ALIGN,
|
|
SBI_DOMAIN_MEMREGION_MMIO |
|
|
SBI_DOMAIN_MEMREGION_M_READABLE |
|
|
SBI_DOMAIN_MEMREGION_M_WRITABLE);
|
|
if (rc)
|
|
return rc;
|
|
|
|
sbi_ipi_add_device(&plicsw_ipi);
|
|
|
|
return 0;
|
|
}
|