[PATCH 8/8] lib: sbi_irqchip: Allow registering interrupt handlers

Anup Patel anup.patel at oss.qualcomm.com
Sat Feb 7 02:26:02 PST 2026


To handle external interrupts in M-mode, the sbi_irqchip framework
must allow registering interrupt handlers from device drivers.

Signed-off-by: Anup Patel <anup.patel at oss.qualcomm.com>
---
 include/sbi/sbi_irqchip.h |  56 ++++++++++-
 lib/sbi/sbi_irqchip.c     | 202 +++++++++++++++++++++++++++++++++++++-
 lib/utils/irqchip/aplic.c |   1 +
 lib/utils/irqchip/imsic.c |   9 ++
 lib/utils/irqchip/plic.c  |   1 +
 5 files changed, 267 insertions(+), 2 deletions(-)

diff --git a/include/sbi/sbi_irqchip.h b/include/sbi/sbi_irqchip.h
index d2c47ae8..77b54110 100644
--- a/include/sbi/sbi_irqchip.h
+++ b/include/sbi/sbi_irqchip.h
@@ -18,12 +18,21 @@ struct sbi_scratch;
 
 /** irqchip hardware device */
 struct sbi_irqchip_device {
-	/** Node in the list of irqchip devices */
+	/** Node in the list of irqchip devices (private) */
 	struct sbi_dlist node;
 
+	/** Internal data of all hardware interrupts of this irqchip (private) */
+	struct sbi_irqchip_hwirq_data *hwirqs;
+
+	/** List of interrupt handlers */
+	struct sbi_dlist handler_list;
+
 	/** Unique ID of this irqchip */
 	u32 id;
 
+	/** Number of hardware IRQs of this irqchip */
+	u32 num_hwirq;
+
 	/** Set of harts targetted by this irqchip */
 	struct sbi_hartmask target_harts;
 
@@ -32,6 +41,21 @@ struct sbi_irqchip_device {
 
 	/** Process hardware interrupts from this irqchip */
 	int (*process_hwirqs)(struct sbi_irqchip_device *chip);
+
+	/** Setup a hardware interrupt of this irqchip */
+	int (*hwirq_setup)(struct sbi_irqchip_device *chip, u32 hwirq);
+
+	/** Cleanup a hardware interrupt of this irqchip */
+	void (*hwirq_cleanup)(struct sbi_irqchip_device *chip, u32 hwirq);
+
+	/** End of hardware interrupt of this irqchip */
+	void (*hwirq_eoi)(struct sbi_irqchip_device *chip, u32 hwirq);
+
+	/** Mask a hardware interrupt of this irqchip */
+	void (*hwirq_mask)(struct sbi_irqchip_device *chip, u32 hwirq);
+
+	/** Unmask a hardware interrupt of this irqchip */
+	void (*hwirq_unmask)(struct sbi_irqchip_device *chip, u32 hwirq);
 };
 
 /**
@@ -44,6 +68,36 @@ struct sbi_irqchip_device {
  */
 int sbi_irqchip_process(void);
 
+/**
+ * Process a hwirq of an irqchip device
+ *
+ * This function is called by irqchip drivers to handle hardware
+ * interrupts of the irqchip.
+ */
+int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq);
+
+/** Unmask a hardware interrupt */
+int sbi_irqchip_unmask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq);
+
+/** Mask a hardware interrupt */
+int sbi_irqchip_mask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq);
+
+/** Default raw hardware interrupt handler */
+int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq);
+
+/** Set raw hardware interrupt handler */
+int sbi_irqchip_set_raw_handler(struct sbi_irqchip_device *chip, u32 hwirq,
+				int (*raw_hndl)(struct sbi_irqchip_device *, u32));
+
+/** Register a hardware interrupt handler */
+int sbi_irqchip_register_handler(struct sbi_irqchip_device *chip,
+				 u32 first_hwirq, u32 num_hwirq,
+				 int (*callback)(u32 hwirq, void *opaque), void *opaque);
+
+/** Unregister a hardware interrupt handler */
+int sbi_irqchip_unregister_handler(struct sbi_irqchip_device *chip,
+				   u32 first_hwirq, u32 num_hwirq);
+
 /** Find an irqchip device based on unique ID */
 struct sbi_irqchip_device *sbi_irqchip_find_device(u32 id);
 
diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c
index fb3357f3..736dde6c 100644
--- a/lib/sbi/sbi_irqchip.c
+++ b/lib/sbi/sbi_irqchip.c
@@ -7,10 +7,35 @@
  *   Anup Patel <apatel at ventanamicro.com>
  */
 
+#include <sbi/sbi_heap.h>
 #include <sbi/sbi_irqchip.h>
 #include <sbi/sbi_list.h>
 #include <sbi/sbi_platform.h>
 
+/** Internal irqchip hardware interrupt data */
+struct sbi_irqchip_hwirq_data {
+	/** raw hardware interrupt handler */
+	int (*raw_handler)(struct sbi_irqchip_device *chip, u32 hwirq);
+};
+
+/** Internal irqchip interrupt handler */
+struct sbi_irqchip_handler {
+	/** Node in the list of irqchip handlers (private) */
+	struct sbi_dlist node;
+
+	/** First hardware IRQ handled by this handler */
+	u32 first_hwirq;
+
+	/** Number of consecutive hardware IRQs handled by this handler */
+	u32 num_hwirq;
+
+	/** Callback function of this handler */
+	int (*callback)(u32 hwirq, void *priv);
+
+	/** Callback private data */
+	void *priv;
+};
+
 static SBI_LIST_HEAD(irqchip_list);
 
 int sbi_irqchip_process(void)
@@ -31,6 +56,172 @@ int sbi_irqchip_process(void)
 	return rc;
 }
 
+int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+	struct sbi_irqchip_hwirq_data *data;
+
+	if (!chip || chip->num_hwirq <= hwirq)
+		return SBI_EINVAL;
+
+	data = &chip->hwirqs[hwirq];
+	if (!data->raw_handler)
+		return SBI_ENOENT;
+
+	return data->raw_handler(chip, hwirq);
+}
+
+int sbi_irqchip_unmask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+	if (!chip || chip->num_hwirq <= hwirq)
+		return SBI_EINVAL;
+
+	if (chip->hwirq_unmask)
+		chip->hwirq_unmask(chip, hwirq);
+	return 0;
+}
+
+int sbi_irqchip_mask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+	if (!chip || chip->num_hwirq <= hwirq)
+		return SBI_EINVAL;
+
+	if (chip->hwirq_mask)
+		chip->hwirq_mask(chip, hwirq);
+	return 0;
+}
+
+static struct sbi_irqchip_handler *sbi_irqchip_find_handler(struct sbi_irqchip_device *chip,
+							    u32 hwirq)
+{
+	struct sbi_irqchip_handler *h;
+
+	if (!chip || chip->num_hwirq <= hwirq)
+		return NULL;
+
+	sbi_list_for_each_entry(h, &chip->handler_list, node) {
+		if (h->first_hwirq <= hwirq && hwirq < (h->first_hwirq + h->num_hwirq))
+			return h;
+	}
+
+	return NULL;
+}
+
+int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+	struct sbi_irqchip_handler *h;
+	int rc;
+
+	if (!chip || chip->num_hwirq <= hwirq)
+		return SBI_EINVAL;
+
+	h = sbi_irqchip_find_handler(chip, hwirq);
+	rc = h->callback(hwirq, h->priv);
+
+	if (chip->hwirq_eoi)
+		chip->hwirq_eoi(chip, hwirq);
+
+	return rc;
+}
+
+int sbi_irqchip_set_raw_handler(struct sbi_irqchip_device *chip, u32 hwirq,
+				int (*raw_hndl)(struct sbi_irqchip_device *, u32))
+{
+	struct sbi_irqchip_hwirq_data *data;
+
+	if (!chip || chip->num_hwirq <= hwirq)
+		return SBI_EINVAL;
+
+	data = &chip->hwirqs[hwirq];
+	data->raw_handler = raw_hndl;
+	return 0;
+}
+
+int sbi_irqchip_register_handler(struct sbi_irqchip_device *chip,
+				 u32 first_hwirq, u32 num_hwirq,
+				 int (*callback)(u32 hwirq, void *opaque), void *priv)
+{
+	struct sbi_irqchip_handler *h;
+	u32 i, j;
+	int rc;
+
+	if (!chip || !num_hwirq || !callback)
+		return SBI_EINVAL;
+	if (chip->num_hwirq <= first_hwirq ||
+	    chip->num_hwirq <= (first_hwirq + num_hwirq - 1))
+		return SBI_EBAD_RANGE;
+
+	h = sbi_irqchip_find_handler(chip, first_hwirq);
+	if (h)
+		return SBI_EALREADY;
+	h = sbi_irqchip_find_handler(chip, first_hwirq + num_hwirq - 1);
+	if (h)
+		return SBI_EALREADY;
+
+	h = sbi_zalloc(sizeof(*h));
+	if (!h)
+		return SBI_ENOMEM;
+	h->first_hwirq = first_hwirq;
+	h->num_hwirq = num_hwirq;
+	h->callback = callback;
+	h->priv = priv;
+	sbi_list_add_tail(&h->node, &chip->handler_list);
+
+	if (chip->hwirq_setup) {
+		for (i = 0; i < h->num_hwirq; i++) {
+			rc = chip->hwirq_setup(chip, h->first_hwirq + i);
+			if (rc) {
+				if (chip->hwirq_cleanup) {
+					for (j = 0; j < i; j++)
+						chip->hwirq_cleanup(chip, h->first_hwirq + j);
+				}
+				sbi_list_del(&h->node);
+				sbi_free(h);
+				return rc;
+			}
+		}
+	}
+
+	if (chip->hwirq_unmask) {
+		for (i = 0; i < h->num_hwirq; i++)
+			chip->hwirq_unmask(chip, h->first_hwirq + i);
+	}
+
+	return 0;
+}
+
+int sbi_irqchip_unregister_handler(struct sbi_irqchip_device *chip,
+				   u32 first_hwirq, u32 num_hwirq)
+{
+	struct sbi_irqchip_handler *fh, *lh;
+	u32 i;
+
+	if (!chip || !num_hwirq)
+		return SBI_EINVAL;
+	if (chip->num_hwirq <= first_hwirq ||
+	    chip->num_hwirq <= (first_hwirq + num_hwirq - 1))
+		return SBI_EBAD_RANGE;
+
+	fh = sbi_irqchip_find_handler(chip, first_hwirq);
+	if (!fh || fh->first_hwirq != first_hwirq || fh->num_hwirq != num_hwirq)
+		return SBI_ENODEV;
+	lh = sbi_irqchip_find_handler(chip, first_hwirq + num_hwirq - 1);
+	if (!lh || lh != fh)
+		return SBI_ENODEV;
+
+	if (chip->hwirq_mask) {
+		for (i = 0; i < fh->num_hwirq; i++)
+			chip->hwirq_mask(chip, fh->first_hwirq + i);
+	}
+
+	if (chip->hwirq_cleanup) {
+		for (i = 0; i < fh->num_hwirq; i++)
+			chip->hwirq_cleanup(chip, fh->first_hwirq + i);
+	}
+
+	sbi_list_del(&fh->node);
+	return 0;
+}
+
 struct sbi_irqchip_device *sbi_irqchip_find_device(u32 id)
 {
 	struct sbi_irqchip_device *chip;
@@ -47,8 +238,9 @@ int sbi_irqchip_add_device(struct sbi_irqchip_device *chip)
 {
 	struct sbi_irqchip_device *c;
 	struct sbi_hartmask hm;
+	u32 i;
 
-	if (!chip || !sbi_hartmask_weight(&chip->target_harts))
+	if (!chip || !chip->num_hwirq || !sbi_hartmask_weight(&chip->target_harts))
 		return SBI_EINVAL;
 
 	if (sbi_irqchip_find_device(chip->id))
@@ -64,6 +256,14 @@ int sbi_irqchip_add_device(struct sbi_irqchip_device *chip)
 		}
 	}
 
+	chip->hwirqs = sbi_zalloc(sizeof(*chip->hwirqs) * chip->num_hwirq);
+	if (!chip->hwirqs)
+		return SBI_ENOMEM;
+	for (i = 0; i < chip->num_hwirq; i++)
+		sbi_irqchip_set_raw_handler(chip, i, sbi_irqchip_raw_handler_default);
+
+	SBI_INIT_LIST_HEAD(&chip->handler_list);
+
 	sbi_list_add_tail(&chip->node, &irqchip_list);
 	return 0;
 }
diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c
index d47a810b..ec69c82b 100644
--- a/lib/utils/irqchip/aplic.c
+++ b/lib/utils/irqchip/aplic.c
@@ -307,6 +307,7 @@ 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;
 	rc = sbi_irqchip_add_device(&aplic->irqchip);
 	if (rc)
 		return rc;
diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c
index 0f296c89..7559a069 100644
--- a/lib/utils/irqchip/imsic.c
+++ b/lib/utils/irqchip/imsic.c
@@ -346,9 +346,17 @@ int imsic_data_check(struct imsic_data *imsic)
 	return 0;
 }
 
+static int imsic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq)
+{
+	if (!hwirq || hwirq == IMSIC_IPI_ID)
+		return SBI_ENOTSUPP;
+	return 0;
+}
+
 static struct sbi_irqchip_device imsic_device = {
 	.warm_init	= imsic_warm_irqchip_init,
 	.process_hwirqs	= imsic_process_hwirqs,
+	.hwirq_setup	= imsic_hwirq_setup,
 };
 
 int imsic_cold_irqchip_init(struct imsic_data *imsic)
@@ -392,6 +400,7 @@ int imsic_cold_irqchip_init(struct imsic_data *imsic)
 
 	/* Register irqchip device */
 	imsic_device.id = imsic->unique_id;
+	imsic_device.num_hwirq = imsic->num_ids + 1;
 	sbi_hartmask_set_all(&imsic_device.target_harts);
 	rc = sbi_irqchip_add_device(&imsic_device);
 	if (rc)
diff --git a/lib/utils/irqchip/plic.c b/lib/utils/irqchip/plic.c
index 973f7c2a..2d721724 100644
--- a/lib/utils/irqchip/plic.c
+++ b/lib/utils/irqchip/plic.c
@@ -281,6 +281,7 @@ int plic_cold_irqchip_init(struct plic_data *plic)
 
 	/* Register irqchip device */
 	plic->irqchip.id = plic->unique_id;
+	plic->irqchip.num_hwirq = plic->num_src + 1;
 	plic->irqchip.warm_init = plic_warm_irqchip_init;
 	return sbi_irqchip_add_device(&plic->irqchip);
 }
-- 
2.43.0




More information about the opensbi mailing list