diff options
-rw-r--r-- | include/sbi_utils/fdt/fdt_helper.h | 4 | ||||
-rw-r--r-- | lib/utils/fdt/fdt_helper.c | 159 | ||||
-rw-r--r-- | lib/utils/irqchip/fdt_irqchip.c | 2 | ||||
-rw-r--r-- | lib/utils/irqchip/fdt_irqchip_aplic.c | 56 | ||||
-rw-r--r-- | lib/utils/irqchip/objects.mk | 1 |
5 files changed, 222 insertions, 0 deletions
diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h index 4c8d29e..1232b26 100644 --- a/include/sbi_utils/fdt/fdt_helper.h +++ b/include/sbi_utils/fdt/fdt_helper.h @@ -68,6 +68,10 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset, int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart, const char *compatible); +struct aplic_data; + +int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic); + struct imsic_data; bool fdt_check_imsic_mlevel(void *fdt); diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index e179b79..3a306cb 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -13,6 +13,7 @@ #include <sbi/sbi_platform.h> #include <sbi/sbi_scratch.h> #include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/aplic.h> #include <sbi_utils/irqchip/imsic.h> #include <sbi_utils/irqchip/plic.h> @@ -466,6 +467,164 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart, return fdt_parse_uart8250_node(fdt, nodeoffset, uart); } +int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic) +{ + bool child_found; + const fdt32_t *val; + const fdt32_t *del; + struct imsic_data imsic; + int i, j, d, dcnt, len, noff, rc; + uint64_t reg_addr, reg_size; + struct aplic_delegate_data *deleg; + + if (nodeoff < 0 || !aplic || !fdt) + return SBI_ENODEV; + memset(aplic, 0, sizeof(*aplic)); + + rc = fdt_get_node_addr_size(fdt, nodeoff, 0, ®_addr, ®_size); + if (rc < 0 || !reg_addr || !reg_size) + return SBI_ENODEV; + aplic->addr = reg_addr; + aplic->size = reg_size; + + val = fdt_getprop(fdt, nodeoff, "riscv,num-sources", &len); + if (len > 0) + aplic->num_source = fdt32_to_cpu(*val); + + val = fdt_getprop(fdt, nodeoff, "interrupts-extended", &len); + if (val && len > sizeof(fdt32_t)) { + len = len / sizeof(fdt32_t); + for (i = 0; i < len; i += 2) { + if (fdt32_to_cpu(val[i + 1]) == IRQ_M_EXT) { + aplic->targets_mmode = true; + break; + } + } + aplic->num_idc = len / 2; + goto aplic_msi_parent_done; + } + + val = fdt_getprop(fdt, nodeoff, "msi-parent", &len); + if (val && len >= sizeof(fdt32_t)) { + noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (noff < 0) + return noff; + + rc = fdt_parse_imsic_node(fdt, noff, &imsic); + if (rc) + return rc; + + rc = imsic_data_check(&imsic); + if (rc) + return rc; + + aplic->targets_mmode = imsic.targets_mmode; + + if (imsic.targets_mmode) { + aplic->has_msicfg_mmode = true; + aplic->msicfg_mmode.lhxs = imsic.guest_index_bits; + aplic->msicfg_mmode.lhxw = imsic.hart_index_bits; + aplic->msicfg_mmode.hhxw = imsic.group_index_bits; + aplic->msicfg_mmode.hhxs = imsic.group_index_shift; + if (aplic->msicfg_mmode.hhxs < + (2 * IMSIC_MMIO_PAGE_SHIFT)) + return SBI_EINVAL; + aplic->msicfg_mmode.hhxs -= 24; + aplic->msicfg_mmode.base_addr = imsic.regs[0].addr; + } else { + goto aplic_msi_parent_done; + } + + val = fdt_getprop(fdt, nodeoff, "riscv,children", &len); + if (!val || len < sizeof(fdt32_t)) + goto aplic_msi_parent_done; + + noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (noff < 0) + return noff; + + val = fdt_getprop(fdt, noff, "msi-parent", &len); + if (!val || len < sizeof(fdt32_t)) + goto aplic_msi_parent_done; + + noff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (noff < 0) + return noff; + + rc = fdt_parse_imsic_node(fdt, noff, &imsic); + if (rc) + return rc; + + rc = imsic_data_check(&imsic); + if (rc) + return rc; + + if (!imsic.targets_mmode) { + aplic->has_msicfg_smode = true; + aplic->msicfg_smode.lhxs = imsic.guest_index_bits; + aplic->msicfg_smode.lhxw = imsic.hart_index_bits; + aplic->msicfg_smode.hhxw = imsic.group_index_bits; + aplic->msicfg_smode.hhxs = imsic.group_index_shift; + if (aplic->msicfg_smode.hhxs < + (2 * IMSIC_MMIO_PAGE_SHIFT)) + return SBI_EINVAL; + aplic->msicfg_smode.hhxs -= 24; + aplic->msicfg_smode.base_addr = imsic.regs[0].addr; + } + } +aplic_msi_parent_done: + + for (d = 0; d < APLIC_MAX_DELEGATE; d++) { + deleg = &aplic->delegate[d]; + deleg->first_irq = 0; + deleg->last_irq = 0; + deleg->child_index = 0; + } + + del = fdt_getprop(fdt, nodeoff, "riscv,delegate", &len); + if (!del || len < (3 * sizeof(fdt32_t))) + goto skip_delegate_parse; + d = 0; + dcnt = len / sizeof(fdt32_t); + for (i = 0; i < dcnt; i += 3) { + if (d >= APLIC_MAX_DELEGATE) + break; + deleg = &aplic->delegate[d]; + + deleg->first_irq = fdt32_to_cpu(del[i + 1]); + deleg->last_irq = fdt32_to_cpu(del[i + 2]); + deleg->child_index = 0; + + child_found = false; + val = fdt_getprop(fdt, nodeoff, "riscv,children", &len); + if (!val || len < sizeof(fdt32_t)) { + deleg->first_irq = 0; + deleg->last_irq = 0; + deleg->child_index = 0; + continue; + } + len = len / sizeof(fdt32_t); + for (j = 0; j < len; j++) { + if (del[i] != val[j]) + continue; + deleg->child_index = j; + child_found = true; + break; + } + + if (child_found) { + d++; + } else { + deleg->first_irq = 0; + deleg->last_irq = 0; + deleg->child_index = 0; + } + } +skip_delegate_parse: + + return 0; +} + bool fdt_check_imsic_mlevel(void *fdt) { const fdt32_t *val; diff --git a/lib/utils/irqchip/fdt_irqchip.c b/lib/utils/irqchip/fdt_irqchip.c index cf64a2e..6007755 100644 --- a/lib/utils/irqchip/fdt_irqchip.c +++ b/lib/utils/irqchip/fdt_irqchip.c @@ -12,10 +12,12 @@ #include <sbi_utils/fdt/fdt_helper.h> #include <sbi_utils/irqchip/fdt_irqchip.h> +extern struct fdt_irqchip fdt_irqchip_aplic; extern struct fdt_irqchip fdt_irqchip_imsic; extern struct fdt_irqchip fdt_irqchip_plic; static struct fdt_irqchip *irqchip_drivers[] = { + &fdt_irqchip_aplic, &fdt_irqchip_imsic, &fdt_irqchip_plic }; diff --git a/lib/utils/irqchip/fdt_irqchip_aplic.c b/lib/utils/irqchip/fdt_irqchip_aplic.c new file mode 100644 index 0000000..965f023 --- /dev/null +++ b/lib/utils/irqchip/fdt_irqchip_aplic.c @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <libfdt.h> +#include <sbi/riscv_asm.h> +#include <sbi/sbi_error.h> +#include <sbi_utils/fdt/fdt_helper.h> +#include <sbi_utils/irqchip/fdt_irqchip.h> +#include <sbi_utils/irqchip/aplic.h> + +#define APLIC_MAX_NR 16 + +static unsigned long aplic_count = 0; +static struct aplic_data aplic[APLIC_MAX_NR]; + +static int irqchip_aplic_warm_init(void) +{ + /* Nothing to do here. */ + return 0; +} + +static int irqchip_aplic_cold_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int rc; + struct aplic_data *pd; + + if (APLIC_MAX_NR <= aplic_count) + return SBI_ENOSPC; + pd = &aplic[aplic_count++]; + + rc = fdt_parse_aplic_node(fdt, nodeoff, pd); + if (rc) + return rc; + + return aplic_cold_irqchip_init(pd); +} + +static const struct fdt_match irqchip_aplic_match[] = { + { .compatible = "riscv,aplic" }, + { }, +}; + +struct fdt_irqchip fdt_irqchip_aplic = { + .match_table = irqchip_aplic_match, + .cold_init = irqchip_aplic_cold_init, + .warm_init = irqchip_aplic_warm_init, + .exit = NULL, +}; diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk index b2b3f79..fad4344 100644 --- a/lib/utils/irqchip/objects.mk +++ b/lib/utils/irqchip/objects.mk @@ -8,6 +8,7 @@ # libsbiutils-objs-y += irqchip/fdt_irqchip.o +libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o libsbiutils-objs-y += irqchip/aplic.o |