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

Raymond Mao raymondmaoca at gmail.com
Mon Feb 9 12:34:31 PST 2026


Hi Anup,

On 2026-02-07 5:26 a.m., anup.patel at oss.qualcomm.com (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);


If hwirq_eoi() is an equivalent of claim(), then hwirq should be a 
pointer of memory, since we need to read and return the IDC_TOPI_ID from 
CLAIMI.

Regards,
Raymond


> +
> +	/** 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);
>   }



More information about the opensbi mailing list