forked from Mirrors/opensbi

sbi_ipi_init() expects the platform warm init function to clear IPIs on the local hart, but there is already a generic function to do this. After this change, none of the existing drivers need a warm init callback. Signed-off-by: Samuel Holland <samuel.holland@sifive.com> Reviewed-by: Anup Patel <anup@brainfault.org>
106 lines
2.6 KiB
C
106 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",
|
|
.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_set_device(&plicsw_ipi);
|
|
|
|
return 0;
|
|
}
|