Files
opensbi/lib/utils/irqchip/fdt_irqchip_plic.c
Samuel Holland 2dd6eaf680 lib: sbi_irqchip: Call driver warm_init from SBI core
Currently, each platform keeps track of which irqchip driver is in use
and calls its warm init function. Since the generic platform may use
multiple irqchip drivers, it has logic to track an array of drivers.

The code is simplified and made common across platforms by treating warm
init and exit as properties of the driver, not the platform. Then the
platform's only role is to select and prepare a driver during cold boot.

For now, only add a .warm_init hook, since none of the existing drivers
need an .exit hook. It could be added in the future if needed.

Signed-off-by: Samuel Holland <samuel.holland@sifive.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
2024-11-28 11:40:17 +05:30

114 lines
2.6 KiB
C

/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2020 Western Digital Corporation or its affiliates.
*
* Authors:
* Anup Patel <anup.patel@wdc.com>
*/
#include <libfdt.h>
#include <sbi/riscv_asm.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_heap.h>
#include <sbi/sbi_platform.h>
#include <sbi/sbi_scratch.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/irqchip/fdt_irqchip.h>
#include <sbi_utils/irqchip/plic.h>
static int irqchip_plic_update_context_map(const void *fdt, int nodeoff,
struct plic_data *pd)
{
const fdt32_t *val;
u32 phandle, hwirq, hartid, hartindex;
int i, err, count, cpu_offset, cpu_intc_offset;
val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &count);
if (!val || count < sizeof(fdt32_t))
return SBI_EINVAL;
count = count / sizeof(fdt32_t);
for (i = 0; i < count; i += 2) {
phandle = fdt32_to_cpu(val[i]);
hwirq = fdt32_to_cpu(val[i + 1]);
cpu_intc_offset = fdt_node_offset_by_phandle(fdt, phandle);
if (cpu_intc_offset < 0)
continue;
cpu_offset = fdt_parent_offset(fdt, cpu_intc_offset);
if (cpu_offset < 0)
continue;
err = fdt_parse_hart_id(fdt, cpu_offset, &hartid);
if (err)
continue;
hartindex = sbi_hartid_to_hartindex(hartid);
if (hartindex == -1U)
continue;
switch (hwirq) {
case IRQ_M_EXT:
pd->context_map[hartindex][PLIC_M_CONTEXT] = i / 2;
break;
case IRQ_S_EXT:
pd->context_map[hartindex][PLIC_S_CONTEXT] = i / 2;
break;
}
}
return 0;
}
static int irqchip_plic_cold_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
const struct sbi_platform *plat = sbi_platform_ptr(scratch);
int rc;
struct plic_data *pd;
pd = sbi_zalloc(PLIC_DATA_SIZE(plat->hart_count));
if (!pd)
return SBI_ENOMEM;
rc = fdt_parse_plic_node(fdt, nodeoff, pd);
if (rc)
goto fail_free_data;
pd->flags = (unsigned long)match->data;
rc = irqchip_plic_update_context_map(fdt, nodeoff, pd);
if (rc)
goto fail_free_data;
rc = plic_cold_irqchip_init(pd);
if (rc)
goto fail_free_data;
return 0;
fail_free_data:
sbi_free(pd);
return rc;
}
static const struct fdt_match irqchip_plic_match[] = {
{ .compatible = "andestech,nceplic100" },
{ .compatible = "riscv,plic0" },
{ .compatible = "sifive,plic-1.0.0" },
{ .compatible = "thead,c900-plic",
.data = (void *)(PLIC_FLAG_THEAD_DELEGATION | PLIC_FLAG_ENABLE_PM) },
{ /* sentinel */ }
};
struct fdt_irqchip fdt_irqchip_plic = {
.match_table = irqchip_plic_match,
.cold_init = irqchip_plic_cold_init,
.warm_init = NULL,
.exit = NULL,
};