[PATCH v2 5/6] lib: utils/irqchip/imsic: track IRQ enable state and restore EIE on warm init

Pawandeep Oza pawandeep.oza at oss.qualcomm.com
Sat Jul 11 12:32:19 PDT 2026


On Thu, Jul 9, 2026 at 1:34 AM Anup Patel <anup.patel at oss.qualcomm.com> wrote:
>
> On Thu, Jun 18, 2026 at 12:16 AM Pawandeep Oza
> <pawandeep.oza at oss.qualcomm.com> wrote:
> >
> > Add an irq_state field to struct sbi_irqchip_hwirq_data with a single
> > IRQ_ENABLED flag (bit 0) to track whether a hardware interrupt has been
> > enabled via the irqchip framework. Set IRQ_ENABLED in
> > sbi_irqchip_unmask_hwirq() when the unmask callback is invoked.
> >
> > Add sbi_irqchip_get_irq_state() as a private inline accessor and expose
> > sbi_irqchip_is_irq_enabled() as a public API for drivers to query the
> > enabled state of a hardware interrupt by chip pointer and hwirq number.
> >
> > Refactor imsic_local_eix_update() to operate on a single interrupt ID
> > instead of a base+count range, simplifying the CSR bit manipulation to
> > a direct BIT(id) write without the inner loop. Update all call sites
> > accordingly.
> >
> > Use sbi_irqchip_is_irq_enabled() in imsic_warm_irqchip_init() to
> > restore per-EIID EIE CSR state on warm boot and HSM resume based on
> > the saved irq_state, replacing the previous blanket disable of all
> > interrupts. This ensures that EIIDs enabled during hotplug/warminit
> > cycle are correctly re-enabled on the resuming hart without requiring
> > software to re-register or re-unmask each interrupt.
> >
> > Signed-off-by: Oza Pawandeep <pawandeep.oza at oss.qualcomm.com>
> > ---
> >  include/sbi/sbi_irqchip.h |  4 +++
> >  lib/sbi/sbi_irqchip.c     | 25 ++++++++++++++++++
> >  lib/utils/irqchip/imsic.c | 55 ++++++++++++++++++---------------------
> >  3 files changed, 55 insertions(+), 29 deletions(-)
> >
> > diff --git a/include/sbi/sbi_irqchip.h b/include/sbi/sbi_irqchip.h
> > index e778d747..14b79cbf 100644
> > --- a/include/sbi/sbi_irqchip.h
> > +++ b/include/sbi/sbi_irqchip.h
> > @@ -162,4 +162,8 @@ int sbi_irqchip_init(struct sbi_scratch *scratch, bool cold_boot);
> >  /** Exit interrupt controllers */
> >  void sbi_irqchip_exit(struct sbi_scratch *scratch);
> >
> > +/** Check if the interrupt is enabled */
> > +bool sbi_irqchip_is_irq_enabled(struct sbi_irqchip_device *chip,
> > +                                              u32 hwirq);
> > +
> >  #endif
> > diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c
> > index 6d0df02e..49fa4806 100644
> > --- a/lib/sbi/sbi_irqchip.c
> > +++ b/lib/sbi/sbi_irqchip.c
> > @@ -20,6 +20,11 @@ struct sbi_irqchip_hwirq_data {
> >         /** raw hardware interrupt handler */
> >         int (*raw_handler)(struct sbi_irqchip_device *chip, u32 hwirq);
> >
> > +#define IRQ_ENABLED    BIT(0)
> > +       /** interrupt state
> > +        * bit 0 - 1: enabled, 0: disabled */
> > +       u32 irq_state;
> > +
> >         /** target hart index */
> >         u32 hart_index;
> >  };
> > @@ -52,6 +57,21 @@ struct sbi_irqchip_hart_data {
> >  static unsigned long irqchip_hart_data_off;
> >  static SBI_LIST_HEAD(irqchip_list);
> >
> > +static inline u32 sbi_irqchip_get_irq_state(struct sbi_irqchip_device *chip,
> > +                                            u32 hwirq)
> > +{
> > +       if (!chip || !chip->hwirqs || hwirq >= chip->num_hwirq)
> > +               return 0;
> > +
> > +       return chip->hwirqs[hwirq].irq_state;
> > +}
> > +
> > +bool sbi_irqchip_is_irq_enabled(struct sbi_irqchip_device *chip,
> > +                                              u32 hwirq)
> > +{
> > +       return !!(sbi_irqchip_get_irq_state(chip, hwirq) & IRQ_ENABLED);
> > +}
> > +
> >  int sbi_irqchip_process(void)
> >  {
> >         struct sbi_irqchip_hart_data *hd;
> > @@ -79,11 +99,16 @@ int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq)
> >
> >  int sbi_irqchip_unmask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq)
> >  {
> > +       struct sbi_irqchip_hwirq_data *data;
> > +
> >         if (!chip || chip->num_hwirq <= hwirq)
> >                 return SBI_EINVAL;
>
> This function should fail if the hwirq was already unmasked.
>
> >
> >         if (chip->hwirq_unmask)
> >                 chip->hwirq_unmask(chip, hwirq);
> > +
> > +       data = &chip->hwirqs[hwirq];
> > +       data->irq_state = data->irq_state | IRQ_ENABLED;
> >         return 0;
> >  }
>
> You need to clear IRQ_ENABLED in sbi_irqchip_mask_hwirq()
Oza: how did I miss this ? yeah sure.
>
> >
> > diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c
> > index b26fd9ca..dbe76645 100644
> > --- a/lib/utils/irqchip/imsic.c
> > +++ b/lib/utils/irqchip/imsic.c
> > @@ -223,29 +223,20 @@ static struct sbi_ipi_device imsic_ipi_device = {
> >         .ipi_send       = imsic_ipi_send
> >  };
> >
> > -static void imsic_local_eix_update(unsigned long base_id,
> > -                                  unsigned long num_id, bool pend, bool val)
> > +static void imsic_local_eix_update(unsigned long id,
> > +                                  bool pend, bool val)
> >  {
> > -       unsigned long i, isel, ireg;
> > -       unsigned long id = base_id, last_id = base_id + num_id;
> > -
> > -       while (id < last_id) {
> > -               isel = id / __riscv_xlen;
> > -               isel *= __riscv_xlen / IMSIC_EIPx_BITS;
> > -               isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> > -
> > -               ireg = 0;
> > -               for (i = id & (__riscv_xlen - 1);
> > -                    (id < last_id) && (i < __riscv_xlen); i++) {
> > -                       ireg |= BIT(i);
> > -                       id++;
> > -               }
> > +       unsigned long isel, ireg = 0;
> >
> > -               if (val)
> > -                       imsic_csr_set(isel, ireg);
> > -               else
> > -                       imsic_csr_clear(isel, ireg);
> > -       }
> > +       isel = id / __riscv_xlen;
> > +       isel *= __riscv_xlen / IMSIC_EIPx_BITS;
> > +       isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0;
> > +       ireg |= BIT(id);
> > +
> > +       if (val)
> > +               imsic_csr_set(isel, ireg);
> > +       else
> > +               imsic_csr_clear(isel, ireg);
> >  }
> >
> >  void imsic_local_irqchip_init(void)
> > @@ -273,13 +264,14 @@ void imsic_local_irqchip_init(void)
> >         imsic_csr_write(IMSIC_EIDELIVERY, IMSIC_ENABLE_EIDELIVERY);
> >
> >         /* Enable IPI */
> > -       imsic_local_eix_update(IMSIC_IPI_ID, 1, false, true);
> > +       imsic_local_eix_update(IMSIC_IPI_ID, false, true);
> >  }
> >
> >  static int imsic_warm_irqchip_init(struct sbi_irqchip_device *dev)
> >  {
> >         struct imsic_data *imsic;
> >         struct imsic_data *hart_imsic;
> > +       int i;
> >
> >         imsic = container_of(dev, struct imsic_data, irqchip);
> >         hart_imsic = imsic_get_data(current_hartindex());
> > @@ -289,11 +281,16 @@ static int imsic_warm_irqchip_init(struct sbi_irqchip_device *dev)
> >                 !hart_imsic->targets_mmode)
> >                 return SBI_EINVAL;
> >
> > -       /* Disable all interrupts */
> > -       imsic_local_eix_update(1, imsic->num_ids, false, false);
> > +       /* enable interrutps based on the irq state */
> > +       for (i = 1; i < imsic->num_ids; i++) {
> > +               if (sbi_irqchip_is_irq_enabled(&imsic->irqchip, i) == true)
> > +                       imsic_local_eix_update(i, true, true);
> > +               else
> > +                       imsic_local_eix_update(i, false, false);
> > +       }
> >
> >         /* Clear IPI pending */
> > -       imsic_local_eix_update(IMSIC_IPI_ID, 1, true, false);
> > +       imsic_local_eix_update(IMSIC_IPI_ID, true, false);
> >
> >         /* Local IMSIC initialization */
> >         imsic_local_irqchip_init();
> > @@ -393,8 +390,8 @@ static void imsic_hwirq_cleanup(struct sbi_irqchip_device *chip, u32 hwirq)
> >         if (!imsic || !imsic->targets_mmode)
> >                 return;
> >
> > -       imsic_local_eix_update(hwirq, 1, false, false);
> > -       imsic_local_eix_update(hwirq, 1, true, false);
> > +       imsic_local_eix_update(hwirq, false, false);
> > +       imsic_local_eix_update(hwirq, true, false);
> >  }
> >
> >  static void imsic_hwirq_eoi(struct sbi_irqchip_device *chip, u32 hwirq)
> > @@ -533,7 +530,7 @@ static void imsic_hwirq_mask(struct sbi_irqchip_device *chip, u32 hwirq)
> >         if (!imsic || !imsic->targets_mmode)
> >                 return;
> >
> > -       imsic_local_eix_update(hwirq, 1, false, false);
> > +       imsic_local_eix_update(hwirq, false, false);
> >  }
> >
> >  static void imsic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq)
> > @@ -550,7 +547,7 @@ static void imsic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq)
> >         if (!hwirq || hwirq == IMSIC_IPI_ID)
> >                 return;
> >
> > -       imsic_local_eix_update(hwirq, 1, false, true);
> > +       imsic_local_eix_update(hwirq, false, true);
> >
> >         sbi_printf("imsic_hwirq_unmask: hart=%lu eiid=%u\n",
> >                    (unsigned long)current_hartindex(), hwirq);
> > --
> > 2.43.0
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
>
> Regards,
> Anup



More information about the opensbi mailing list