diff options
| author | Pawandeep Oza <[email protected]> | 2026-07-21 14:48:29 -0700 |
|---|---|---|
| committer | Anup Patel <[email protected]> | 2026-07-22 13:20:20 +0530 |
| commit | b2ead3b037c0b864b0770891586d121c0dc68761 (patch) | |
| tree | e571489573e30dfa54271ea5273efc3f9d16f931 | |
| parent | 193b1d9e7ecb06c6a374665bcc177f6997a58505 (diff) | |
lib: utils/irqchip/imsic: track IRQ enable state and restore EIE on warm init
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 <[email protected]>
Reviewed-by: Anup Patel <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Anup Patel <[email protected]>
| -rw-r--r-- | include/sbi/sbi_irqchip.h | 4 | ||||
| -rw-r--r-- | lib/sbi/sbi_irqchip.c | 36 | ||||
| -rw-r--r-- | lib/utils/irqchip/imsic.c | 53 |
3 files changed, 65 insertions, 28 deletions
diff --git a/include/sbi/sbi_irqchip.h b/include/sbi/sbi_irqchip.h index ea012635..f5dbf1c7 100644 --- a/include/sbi/sbi_irqchip.h +++ b/include/sbi/sbi_irqchip.h @@ -168,4 +168,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_hwirq_enabled(struct sbi_irqchip_device *chip, + u32 hwirq); + #endif diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c index 0973f064..d6771fa8 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; @@ -80,23 +85,54 @@ int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq) return data->raw_handler(chip, hwirq); } +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_hwirq_enabled(struct sbi_irqchip_device *chip, + u32 hwirq) +{ + return !!(sbi_irqchip_get_irq_state(chip, hwirq) & IRQ_ENABLED); +} + 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; + data = &chip->hwirqs[hwirq]; + if (sbi_irqchip_is_hwirq_enabled(chip, hwirq)) + return SBI_EALREADY; + if (chip->hwirq_unmask) chip->hwirq_unmask(chip, hwirq); + + data->irq_state |= IRQ_ENABLED; return 0; } int sbi_irqchip_mask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq) { + struct sbi_irqchip_hwirq_data *data; + if (!chip || chip->num_hwirq <= hwirq) return SBI_EINVAL; + if (!sbi_irqchip_is_hwirq_enabled(chip, hwirq)) + return SBI_EALREADY; + if (chip->hwirq_mask) chip->hwirq_mask(chip, hwirq); + + data = &chip->hwirqs[hwirq]; + data->irq_state &= ~IRQ_ENABLED; return 0; } diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c index fef77203..f6da9f7e 100644 --- a/lib/utils/irqchip/imsic.c +++ b/lib/utils/irqchip/imsic.c @@ -225,29 +225,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; + unsigned long isel, ireg = 0; - while (id < last_id) { - isel = id / __riscv_xlen; - isel *= __riscv_xlen / IMSIC_EIPx_BITS; - isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0; + isel = id / __riscv_xlen; + isel *= __riscv_xlen / IMSIC_EIPx_BITS; + isel += (pend) ? IMSIC_EIP0 : IMSIC_EIE0; + ireg |= BIT(id); - ireg = 0; - for (i = id & (__riscv_xlen - 1); - (id < last_id) && (i < __riscv_xlen); i++) { - ireg |= BIT(i); - id++; - } - - if (val) - imsic_csr_set(isel, ireg); - else - imsic_csr_clear(isel, ireg); - } + if (val) + imsic_csr_set(isel, ireg); + else + imsic_csr_clear(isel, ireg); } void imsic_local_irqchip_init(void) @@ -275,13 +266,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()); @@ -291,11 +283,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_hwirq_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(); @@ -395,8 +392,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) @@ -529,7 +526,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) @@ -546,7 +543,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); } static struct sbi_irqchip_device imsic_device = { |
