[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