[PATCH 06/10] lib: utils: fdt: parse sysirq routing from DT
Raymond Mao <[email protected]>
| Newsgroups | org.infradead.lists.opensbi |
|---|---|
| Message-ID | <[email protected]> |
From: Raymond Mao <[email protected]> Init VIRQ and parse mpxy-sysirq nodes under /chosen/opensbi-domains, pre-size per-channel VIRQ maps, and add HWIRQ routes from interrupts-extended. Mark sysirq domains to allow SEIP-based VIRQ notification. Signed-off-by: Raymond Mao <[email protected]> --- include/sbi_utils/fdt/fdt_helper.h | 17 +++++ lib/utils/fdt/fdt_domain.c | 119 ++++++++++++++++++++++++++++- lib/utils/fdt/fdt_helper.c | 49 ++++++++++++ 3 files changed, 183 insertions(+), 2 deletions(-) diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h index 04c850cc..e49a5bca 100644 --- a/include/sbi_utils/fdt/fdt_helper.h +++ b/include/sbi_utils/fdt/fdt_helper.h @@ -12,6 +12,7 @@ #include <sbi/sbi_types.h> #include <sbi/sbi_domain.h> +#include <sbi/sbi_irqchip.h> struct fdt_match { const char *compatible; @@ -38,6 +39,22 @@ int fdt_parse_phandle_with_args(const void *fdt, int nodeoff, const char *prop, const char *cells_prop, int index, struct fdt_phandle_args *out_args); +/* + * Parse one entry from "interrupts-extended" and return irqchip device, + * hwirq, and optional flags. + * + * @index: entry index within interrupts-extended + * @out_flags_count: in/out, on input capacity of out_flags array; + * on output number of flags returned (or total flags + * if out_flags is NULL). + */ +int fdt_parse_interrupts_extended_entry(const void *fdt, int nodeoff, + int index, + struct sbi_irqchip_device **out_chip, + u32 *out_hwirq, + u32 *out_flags, + u32 *out_flags_count); + int fdt_get_node_addr_size(const void *fdt, int node, int index, uint64_t *addr, uint64_t *size); diff --git a/lib/utils/fdt/fdt_domain.c b/lib/utils/fdt/fdt_domain.c index b2fa8633..4a75f25a 100644 --- a/lib/utils/fdt/fdt_domain.c +++ b/lib/utils/fdt/fdt_domain.c @@ -10,11 +10,14 @@ #include <libfdt.h> #include <libfdt_env.h> +#include <sbi/sbi_console.h> #include <sbi/sbi_domain.h> #include <sbi/sbi_error.h> #include <sbi/sbi_hartmask.h> #include <sbi/sbi_heap.h> +#include <sbi/sbi_irqchip.h> #include <sbi/sbi_scratch.h> +#include <sbi/sbi_virq.h> #include <sbi_utils/fdt/fdt_domain.h> #include <sbi_utils/fdt/fdt_helper.h> @@ -304,6 +307,7 @@ static int __fdt_parse_region(const void *fdt, int domain_offset, return 0; } + static int __fdt_parse_domain(const void *fdt, int domain_offset, void *opaque) { u32 val32; @@ -511,6 +515,113 @@ fail_free_domain: return err; } +static int __fdt_parse_mpxy_sysirq_node(const void *fdt, int nodeoff) +{ + const fdt32_t *val; + int len, rc, doff; + u32 channel_id; + u32 index; + struct sbi_domain *dom; + + if (!fdt || nodeoff < 0) + return SBI_EINVAL; + + val = fdt_getprop(fdt, nodeoff, "opensbi,mpxy-channel-id", &len); + if (!val || len < (int)sizeof(fdt32_t)) { + sbi_printf("[SYSIRQ] missing opensbi,mpxy-channel-id\n"); + return SBI_EINVAL; + } + channel_id = fdt32_to_cpu(*val); + + val = fdt_getprop(fdt, nodeoff, "opensbi,domain", &len); + if (!val || len < (int)sizeof(fdt32_t)) { + sbi_printf("[SYSIRQ] missing opensbi,domain\n"); + return SBI_EINVAL; + } + + doff = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*val)); + if (doff < 0) + return doff; + + dom = sbi_domain_find_by_name(fdt_get_name(fdt, doff, NULL)); + if (!dom) { + sbi_printf("[SYSIRQ] domain not found for node %s\n", + fdt_get_name(fdt, doff, NULL)); + return SBI_ENOENT; + } + dom->virq_seip_notify = true; + + /* Pre-allocate VIRQ map based on interrupts-extended count */ + for (index = 0; ; index++) { + rc = fdt_parse_interrupts_extended_entry(fdt, nodeoff, index, + NULL, NULL, + NULL, NULL); + if (rc == SBI_ENOENT) + break; + if (rc) + return rc; + } + + if (!sbi_virq_is_inited()) + rc = sbi_virq_init(index); + else + rc = sbi_virq_map_ensure_cap(channel_id, index); + if (rc) + return rc; + + for (index = 0; ; index++) { + struct sbi_irqchip_device *chip = NULL; + u32 hwirq = 0; + + rc = fdt_parse_interrupts_extended_entry(fdt, nodeoff, index, + &chip, &hwirq, + NULL, NULL); + if (rc == SBI_ENOENT) + break; + if (rc) + return rc; + if (!chip) + return SBI_ENODEV; + + rc = sbi_virq_map_set(channel_id, chip->id, hwirq, index); + if (rc) + return rc; + + rc = sbi_virq_route_add(dom, hwirq, channel_id); + if (rc) + return rc; + } + + return SBI_OK; +} + +static int __fdt_parse_mpxy_sysirq_nodes(const void *fdt) +{ + int poffset, noff, rc; + + if (!fdt) + return SBI_EINVAL; + + poffset = fdt_path_offset(fdt, "/chosen"); + if (poffset < 0) + return 0; + poffset = fdt_node_offset_by_compatible(fdt, poffset, + "opensbi,domain,config"); + if (poffset < 0) + return 0; + + fdt_for_each_subnode(noff, fdt, poffset) { + if (fdt_node_check_compatible(fdt, noff, + "opensbi,mpxy-sysirq")) + continue; + rc = __fdt_parse_mpxy_sysirq_node(fdt, noff); + if (rc) + return rc; + } + + return 0; +} + int fdt_domains_populate(const void *fdt) { const u32 *val; @@ -550,6 +661,10 @@ int fdt_domains_populate(const void *fdt) } /* Iterate over each domain in FDT and populate details */ - return fdt_iterate_each_domain_ro(fdt, &cold_domain_offset, - __fdt_parse_domain); + err = fdt_iterate_each_domain_ro(fdt, &cold_domain_offset, + __fdt_parse_domain); + if (err) + return err; + + return __fdt_parse_mpxy_sysirq_nodes(fdt); } diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c index b57eae1a..3d7c4eec 100644 --- a/lib/utils/fdt/fdt_helper.c +++ b/lib/utils/fdt/fdt_helper.c @@ -10,6 +10,7 @@ #include <sbi/riscv_asm.h> #include <sbi/sbi_console.h> #include <sbi/sbi_hartmask.h> +#include <sbi/sbi_irqchip.h> #include <sbi/sbi_platform.h> #include <sbi/sbi_scratch.h> #include <sbi/sbi_hart.h> @@ -80,6 +81,54 @@ int fdt_parse_phandle_with_args(const void *fdt, int nodeoff, return SBI_ENOENT; } +int fdt_parse_interrupts_extended_entry(const void *fdt, int nodeoff, + int index, + struct sbi_irqchip_device **out_chip, + u32 *out_hwirq, + u32 *out_flags, + u32 *out_flags_count) +{ + struct fdt_phandle_args args; + struct sbi_irqchip_device *chip; + u32 flags_cap = 0, flags_cnt = 0; + int rc, i; + + if (!fdt || nodeoff < 0) + return SBI_EINVAL; + + rc = fdt_parse_phandle_with_args(fdt, nodeoff, "interrupts-extended", + "#interrupt-cells", index, &args); + if (rc) + return rc; + + if (args.args_count < 1) + return SBI_EINVAL; + + if (out_hwirq) + *out_hwirq = args.args[0]; + + if (out_flags_count) { + flags_cap = *out_flags_count; + flags_cnt = (args.args_count > 1) ? (args.args_count - 1) : 0; + if (out_flags && flags_cap < flags_cnt) + flags_cnt = flags_cap; + if (out_flags) { + for (i = 0; i < (int)flags_cnt; i++) + out_flags[i] = args.args[i + 1]; + } + *out_flags_count = flags_cnt; + } + + if (out_chip) { + chip = sbi_irqchip_find_device((u32)args.node_offset); + if (!chip) + return SBI_ENODEV; + *out_chip = chip; + } + + return 0; +} + static int fdt_translate_address(const void *fdt, uint64_t reg, int parent, uint64_t *addr) { -- 2.25.1 -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi