From: Raymond Mao <raymondmaoca@gmail.com>
To: opensbi@lists.infradead.org
Cc: scott@riscstar.com, dave.patel@riscstar.com,
raymond.mao@riscstar.com, robin.randhawa@sifive.com,
samuel.holland@sifive.com, anup.patel@qti.qualcomm.com,
anuppate@qti.qualcomm.com, anup@brainfault.org,
dhaval@rivosinc.com, peter.lin@sifive.com
Subject: [PATCH v2 1/3] lib: utils: irqchip: implement APLIC hwirq operation hooks
Date: Mon, 4 May 2026 13:13:40 -0400 [thread overview]
Message-ID: <20260504171342.1655882-1-raymondmaoca@gmail.com> (raw)
From: Raymond Mao <raymond.mao@riscstar.com>
Implement the APLIC hardware interrupt hooks used by the generic
irqchip framework for M-mode direct-mode wired interrupts.
Program a minimal APLIC direct-mode configuration during hwirq setup
and claim external interrupts via IDC.CLAIMI. Add the helper logic
needed to derive the source ID from CLAIMI, map hart index to IDC
index, skip delegated interrupts, and register process_hwirqs()
only for the M-mode direct-mode provider.
Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
---
lib/utils/irqchip/aplic.c | 227 ++++++++++++++++++++++++++++++++++++++
1 file changed, 227 insertions(+)
diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c
index ec69c82b..3f4991b5 100644
--- a/lib/utils/irqchip/aplic.c
+++ b/lib/utils/irqchip/aplic.c
@@ -245,6 +245,220 @@ static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
return 0;
}
+static inline void *aplic_idc_base(unsigned long aplic_addr, u32 idc_index)
+{
+ return (void *)(aplic_addr + APLIC_IDC_BASE +
+ (unsigned long)idc_index * APLIC_IDC_SIZE);
+}
+
+static inline struct aplic_data *aplic_irqchip_to_data(struct sbi_irqchip_device *chip)
+{
+ return container_of(chip, struct aplic_data, irqchip);
+}
+
+static bool aplic_hwirq_delegated(const struct aplic_data *aplic, u32 hwirq)
+{
+ u32 i;
+
+ for (i = 0; i < APLIC_MAX_DELEGATE; i++) {
+ const struct aplic_delegate_data *deleg = &aplic->delegate[i];
+
+ if (!deleg->first_irq || !deleg->last_irq)
+ continue;
+ if (deleg->first_irq <= hwirq && hwirq <= deleg->last_irq)
+ return true;
+ }
+
+ return false;
+}
+
+static bool aplic_mmode_direct(const struct aplic_data *aplic)
+{
+ return aplic->targets_mmode && aplic->num_idc;
+}
+
+static int aplic_hartindex_to_idc_index(const struct aplic_data *aplic,
+ u32 hartindex)
+{
+ u32 i;
+
+ if (!aplic->num_idc)
+ return SBI_ENODEV;
+
+ if (aplic->idc_map) {
+ for (i = 0; i < aplic->num_idc; i++) {
+ if (aplic->idc_map[i] == hartindex)
+ return i;
+ }
+
+ return SBI_ENODEV;
+ }
+
+ if (hartindex < aplic->num_idc)
+ return hartindex;
+
+ return SBI_ENODEV;
+}
+
+static int aplic_hwirq_target_idc_index(struct sbi_irqchip_device *chip)
+{
+ u32 hartindex = current_hartindex();
+
+ if (!sbi_hartmask_test_hartindex(hartindex, &chip->target_harts)) {
+ sbi_hartmask_for_each_hartindex(hartindex, &chip->target_harts)
+ break;
+ if (hartindex >= SBI_HARTMASK_MAX_BITS)
+ return SBI_ENODEV;
+ }
+
+ return aplic_hartindex_to_idc_index(aplic_irqchip_to_data(chip),
+ hartindex);
+}
+
+static u32 aplic_domaincfg_value(void)
+{
+ u32 val = APLIC_DOMAINCFG_IE;
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ val |= APLIC_DOMAINCFG_BE;
+#endif
+
+ return val;
+}
+
+static void aplic_hwirq_mask(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+ struct aplic_data *aplic = aplic_irqchip_to_data(chip);
+
+ if (!hwirq || aplic_hwirq_delegated(aplic, hwirq))
+ return;
+
+ if (!aplic->addr || hwirq > aplic->num_source)
+ return;
+
+ /* Disable source */
+ writel(hwirq, (void *)(aplic->addr + APLIC_CLRIENUM));
+}
+
+static void aplic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+ struct aplic_data *aplic = aplic_irqchip_to_data(chip);
+
+ if (!hwirq || aplic_hwirq_delegated(aplic, hwirq))
+ return;
+
+ if (!aplic->addr || hwirq > aplic->num_source)
+ return;
+
+ /* Enable source */
+ writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM));
+}
+
+static int aplic_hwirq_claim(struct sbi_irqchip_device *chip, u32 *hwirq)
+{
+ struct aplic_data *aplic = aplic_irqchip_to_data(chip);
+ int idc_index;
+ void *idc;
+ u32 v, id;
+
+ if (!hwirq)
+ return SBI_EINVAL;
+
+ idc_index = aplic_hartindex_to_idc_index(aplic, current_hartindex());
+ if (!aplic->addr || idc_index < 0)
+ return SBI_ENODEV;
+
+ idc = aplic_idc_base(aplic->addr, idc_index);
+
+ /*
+ * Read CLAIMI: returns TOPI value.
+ * ID==0 means spurious interrupt (spec-defined).
+ */
+ v = readl(idc + APLIC_IDC_CLAIMI); /* dequeue */
+
+ id = (v >> APLIC_IDC_TOPI_ID_SHIFT) & APLIC_IDC_TOPI_ID_MASK;
+
+ /* ID==0 means spurious / no pending wired interrupt */
+ if (!id)
+ return SBI_ENOENT;
+
+ /* Bound check against DT-discovered num_src */
+ if (id > aplic->num_source)
+ return SBI_EINVAL;
+
+ *hwirq = id;
+
+ return SBI_OK;
+}
+
+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)
+ return SBI_EINVAL;
+ if (!aplic_mmode_direct(aplic))
+ return SBI_ENOTSUPP;
+ if (aplic_hwirq_delegated(aplic, hwirq))
+ return SBI_ENOTSUPP;
+
+ idc_index = aplic_hwirq_target_idc_index(chip);
+ 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));
+
+ 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;
+}
+
+static int aplic_process_hwirqs(struct sbi_irqchip_device *chip)
+{
+ if (!chip)
+ return SBI_ENODEV;
+
+ for (;;) {
+ u32 hwirq = 0;
+ int rc = aplic_hwirq_claim(chip, &hwirq);
+
+ if (rc == SBI_ENOENT)
+ break;
+ if (rc)
+ return rc;
+
+ if (!hwirq)
+ break;
+
+ if (hwirq > chip->num_hwirq) {
+ return SBI_EINVAL;
+ }
+
+ rc = sbi_irqchip_process_hwirq(chip, hwirq);
+ if (rc)
+ return rc;
+ }
+
+ return SBI_OK;
+}
+
int aplic_cold_irqchip_init(struct aplic_data *aplic)
{
int rc;
@@ -308,6 +522,19 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic)
/* Register irqchip device */
aplic->irqchip.id = aplic->unique_id;
aplic->irqchip.num_hwirq = aplic->num_source + 1;
+ aplic->irqchip.hwirq_mask = aplic_hwirq_mask;
+ aplic->irqchip.hwirq_unmask = aplic_hwirq_unmask;
+ /*
+ * Only the domain that directly injects interrupts into M-mode external
+ * interrupt line should provide process_hwirqs().
+ *
+ * The other domain (e.g. S-mode) may still be registered so that its
+ * other ops (mask/unmask/config/etc.) can be used, but it must not
+ * claim to be the external interrupt line provider.
+ */
+ if (aplic_mmode_direct(aplic))
+ aplic->irqchip.process_hwirqs = aplic_process_hwirqs;
+ aplic->irqchip.hwirq_setup = aplic_hwirq_setup;
rc = sbi_irqchip_add_device(&aplic->irqchip);
if (rc)
return rc;
--
2.25.1
--
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi
next reply other threads:[~2026-05-04 17:14 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-04 17:13 Raymond Mao [this message]
2026-05-04 17:13 ` [PATCH v2 2/3] [NOT-FOR-UPSTREAM] lib: utils: irqchip: add QEMU virt test for APLIC wired IRQs Raymond Mao
2026-05-04 17:13 ` [PATCH v2 3/3] [NOT-FOR-UPSTREAM] platform: generic: virt: add APLIC M-mode IRQ test overlay Raymond Mao
2026-06-11 5:15 ` [PATCH v2 1/3] lib: utils: irqchip: implement APLIC hwirq operation hooks Anup Patel
2026-06-16 20:30 ` Raymond Mao
2026-06-16 20:49 ` Pawandeep Oza
2026-06-16 21:25 ` Raymond Mao
2026-06-16 22:08 ` Pawandeep Oza
2026-06-16 22:19 ` Raymond Mao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260504171342.1655882-1-raymondmaoca@gmail.com \
--to=raymondmaoca@gmail.com \
--cc=anup.patel@qti.qualcomm.com \
--cc=anup@brainfault.org \
--cc=anuppate@qti.qualcomm.com \
--cc=dave.patel@riscstar.com \
--cc=dhaval@rivosinc.com \
--cc=opensbi@lists.infradead.org \
--cc=peter.lin@sifive.com \
--cc=raymond.mao@riscstar.com \
--cc=robin.randhawa@sifive.com \
--cc=samuel.holland@sifive.com \
--cc=scott@riscstar.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox