[PATCH 08/10] lib: irqchip: support deferred completion and per-HWIRQ APLIC targets
Raymond Mao <[email protected]>
| Newsgroups | org.infradead.lists.opensbi |
|---|---|
| Message-ID | <[email protected]> |
From: Raymond Mao <[email protected]> Lazily resolve the active irqchip provider for each hart when the scratch-local provider is not yet populated. Program APLIC target IDCs from the precomputed per-HWIRQ hart map, and treat SBI_EALREADY as deferred completion so the normal process path does not complete an interrupt that will be finished later by the VIRQ flow. Signed-off-by: Raymond Mao <[email protected]> --- lib/sbi/sbi_irqchip.c | 21 ++++++++++++++ lib/utils/irqchip/aplic.c | 60 ++++++++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c index e022d534..45b2992f 100644 --- a/lib/sbi/sbi_irqchip.c +++ b/lib/sbi/sbi_irqchip.c @@ -45,11 +45,28 @@ struct sbi_irqchip_hart_data { static unsigned long irqchip_hart_data_off; static SBI_LIST_HEAD(irqchip_list); +static struct sbi_irqchip_device *sbi_irqchip_find_hart_device(u32 hartindex) +{ + struct sbi_irqchip_device *chip; + + sbi_list_for_each_entry(chip, &irqchip_list, node) { + if (!chip->process_hwirqs) + continue; + if (!sbi_hartmask_test_hartindex(hartindex, &chip->target_harts)) + continue; + return chip; + } + + return NULL; +} + int sbi_irqchip_process(void) { struct sbi_irqchip_hart_data *hd; hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (!hd || !hd->chip || !hd->chip->process_hwirqs) return SBI_ENODEV; @@ -306,6 +323,8 @@ int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot) } hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (hd && hd->chip && hd->chip->process_hwirqs) csr_set(CSR_MIE, MIP_MEIP); @@ -317,6 +336,8 @@ void sbi_irqchip_exit(struct sbi_scratch *scratch) struct sbi_irqchip_hart_data *hd; hd = sbi_scratch_thishart_offset_ptr(irqchip_hart_data_off); + if (hd && !hd->chip) + hd->chip = sbi_irqchip_find_hart_device(current_hartindex()); if (hd && hd->chip && hd->chip->process_hwirqs) csr_clear(CSR_MIE, MIP_MEIP); } diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index 82efdb71..77743685 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -306,6 +306,8 @@ static inline struct aplic_data *aplic_irqchip_to_data(struct sbi_irqchip_device return container_of(chip, struct aplic_data, irqchip); } +static bool aplic_mmode_direct(const struct aplic_data *aplic); + static bool aplic_hwirq_delegated(const struct aplic_data *aplic, u32 hwirq) { u32 i; @@ -441,10 +443,49 @@ static int aplic_hwirq_claim(struct sbi_irqchip_device *chip, u32 *hwirq) return SBI_OK; } +static void aplic_set_target(struct aplic_data *aplic, u32 hwirq, u32 idc_index) +{ + unsigned long idc; + + if (!aplic->addr || !hwirq || hwirq > aplic->num_source) + return; + if (aplic->num_idc <= idc_index) + return; + + idc = aplic->addr + APLIC_IDC_BASE + + (unsigned long)idc_index * APLIC_IDC_SIZE; + + writel((idc_index << APLIC_TARGET_HART_IDX_SHIFT) | + APLIC_DEFAULT_PRIORITY, + (void *)(aplic->addr + APLIC_TARGET_BASE + (hwirq - 1) * 4)); + + /* IDC delivery */ + writel(APLIC_ENABLE_IDELIVERY, (void *)(idc + APLIC_IDC_IDELIVERY)); + writel(APLIC_ENABLE_ITHRESHOLD, (void *)(idc + APLIC_IDC_ITHRESHOLD)); +} + +static int aplic_hwirq_setup_target_idc_index(struct aplic_data *aplic, + struct sbi_irqchip_device *chip, + u32 hwirq) +{ + u32 hartindex; + int idc_index; + + if (aplic->hwirq_target_hartindex) { + hartindex = aplic->hwirq_target_hartindex[hwirq]; + if (hartindex != -1U) { + idc_index = aplic_hartindex_to_idc_index(aplic, hartindex); + if (idc_index >= 0) + return idc_index; + } + } + + return aplic_hwirq_target_idc_index(chip); +} + static int aplic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) { struct aplic_data *aplic = aplic_irqchip_to_data(chip); - unsigned long idc; int idc_index; if (!hwirq || hwirq > aplic->num_source) @@ -454,29 +495,19 @@ static int aplic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) if (aplic_hwirq_delegated(aplic, hwirq)) return SBI_ENOTSUPP; - idc_index = aplic_hwirq_target_idc_index(chip); + idc_index = aplic_hwirq_setup_target_idc_index(aplic, chip, hwirq); if (idc_index < 0) return idc_index; - idc = aplic->addr + APLIC_IDC_BASE + idc_index * APLIC_IDC_SIZE; - /* APLIC: sourcecfg/target/enable */ writel(APLIC_SOURCECFG_SM_LEVEL_HIGH, (void *)(aplic->addr + APLIC_SOURCECFG_BASE + (hwirq - 1) * 4)); - - writel(((u32)idc_index << APLIC_TARGET_HART_IDX_SHIFT) | - APLIC_DEFAULT_PRIORITY, - (void *)(aplic->addr + APLIC_TARGET_BASE + (hwirq - 1) * 4)); - + aplic_set_target(aplic, hwirq, (u32)idc_index); writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM)); /* Direct mode for aia=aplic: DM=0 => don't set DM bit */ writel(aplic_domaincfg_value(), (void *)(aplic->addr + APLIC_DOMAINCFG)); - /* IDC delivery */ - writel(APLIC_ENABLE_IDELIVERY, (void *)(idc + APLIC_IDC_IDELIVERY)); - writel(APLIC_ENABLE_ITHRESHOLD, (void *)(idc + APLIC_IDC_ITHRESHOLD)); - return SBI_OK; } @@ -502,6 +533,9 @@ static int aplic_process_hwirqs(struct sbi_irqchip_device *chip) } rc = sbi_irqchip_process_hwirq(chip, hwirq); + /* Deferred completion paths consume the IRQ without EOI here. */ + if (rc == SBI_EALREADY) + return SBI_OK; if (rc) return rc; } -- 2.25.1 -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi