[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.