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 <pawandeep.oza@oss.qualcomm.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
Link: https://lore.kernel.org/r/20260721214833.687361-6-pawandeep.oza@oss.qualcomm.com
Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Pawandeep Oza
2026-07-22 13:20:20 +05:30
committed by Anup Patel
parent 193b1d9e7e
commit b2ead3b037
3 changed files with 65 additions and 28 deletions
+25 -28
View File
@@ -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 = {