[PATCH 08/10] lib: irqchip: support deferred completion and per-HWIRQ APLIC targets
Raymond Mao
raymondmaoca at gmail.com
Thu May 14 15:57:54 PDT 2026
From: Raymond Mao <raymond.mao at riscstar.com>
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 <raymond.mao at riscstar.com>
---
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
More information about the opensbi
mailing list