[PATCH 8/8] lib: sbi_irqchip: Allow registering interrupt handlers
Andrew Jones
andrew.jones at oss.qualcomm.com
Mon Feb 9 08:52:28 PST 2026
On Sat, Feb 07, 2026 at 03:56:02PM +0530, Anup Patel wrote:
> 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;
We should loop over all hwirq numbers from first to the end to ensure no
other handler is already registered for numbers inside the range. Same
comment for sbi_irqchip_unregister_handler() below.
Thanks,
drew
> +
> + 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