[PATCH v2 8/8] lib: sbi_irqchip: Allow registering interrupt handlers
Anup Patel
anup.patel at oss.qualcomm.com
Thu Feb 12 21:53:42 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 | 203 +++++++++++++++++++++++++++++++++++++-
lib/utils/irqchip/aplic.c | 1 +
lib/utils/irqchip/imsic.c | 9 ++
lib/utils/irqchip/plic.c | 1 +
5 files changed, 267 insertions(+), 3 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 5df6189b..f0744830 100644
--- a/lib/sbi/sbi_irqchip.c
+++ b/lib/sbi/sbi_irqchip.c
@@ -7,11 +7,36 @@
* 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>
#include <sbi/sbi_scratch.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;
+};
+
struct sbi_irqchip_hart_data {
struct sbi_irqchip_device *chip;
};
@@ -30,6 +55,172 @@ int sbi_irqchip_process(void)
return hd->chip->process_hwirqs(hd->chip);
}
+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;
@@ -46,9 +237,9 @@ int sbi_irqchip_add_device(struct sbi_irqchip_device *chip)
{
struct sbi_irqchip_hart_data *hd;
struct sbi_scratch *scratch;
- u32 h;
+ u32 i, h;
- 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))
@@ -68,6 +259,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