irqchip/csky: fixup handle_irq_perbit break irq

The handle_irq_perbit function loop every bit in hwirq local variable.

handle_irq_perbit(hwirq) {
  for_everyt_bit_in(hwirq) {
	handle_domain_irq()
		->irq_exit()
		->invoke_softirq()
		->__do_softirq()
		->local_irq_enable() // Here will cause new interrupt.
  }
}

When new interrupt coming at local_irq_enable, it will finish another
interrupt handler and pull down the interrupt source. But hwirq is the
local variable for handle_irq_perbit(), it can't get new interrupt
controller pending reg status. So we need update hwirq with pending reg
in every loop.

Also change write_relax to writel could prevent stw from fast retire.
When local_irq is enabled, intc regs is really set-in.

Signed-off-by: Guo Ren <ren_guo@c-sky.com>
Cc: Lu Baoquan <lu.baoquan@intellif.com>
This commit is contained in:
Guo Ren 2019-01-08 20:49:24 +08:00
parent 2a60aa14a9
commit 56752b2175
1 changed files with 42 additions and 35 deletions

View File

@ -95,7 +95,7 @@ static inline void setup_irq_channel(u32 magic, void __iomem *reg_addr)
/* Setup 64 channel slots */
for (i = 0; i < INTC_IRQS; i += 4)
writel_relaxed(build_channel_val(i, magic), reg_addr + i);
writel(build_channel_val(i, magic), reg_addr + i);
}
static int __init
@ -135,16 +135,10 @@ ck_intc_init_comm(struct device_node *node, struct device_node *parent)
static inline bool handle_irq_perbit(struct pt_regs *regs, u32 hwirq,
u32 irq_base)
{
u32 irq;
if (hwirq == 0)
return 0;
while (hwirq) {
irq = __ffs(hwirq);
hwirq &= ~BIT(irq);
handle_domain_irq(root_domain, irq_base + irq, regs);
}
handle_domain_irq(root_domain, irq_base + __fls(hwirq), regs);
return 1;
}
@ -154,12 +148,16 @@ static void gx_irq_handler(struct pt_regs *regs)
{
bool ret;
do {
ret = handle_irq_perbit(regs,
readl_relaxed(reg_base + GX_INTC_PEN31_00), 0);
ret |= handle_irq_perbit(regs,
readl_relaxed(reg_base + GX_INTC_PEN63_32), 32);
} while (ret);
retry:
ret = handle_irq_perbit(regs,
readl(reg_base + GX_INTC_PEN63_32), 32);
if (ret)
goto retry;
ret = handle_irq_perbit(regs,
readl(reg_base + GX_INTC_PEN31_00), 0);
if (ret)
goto retry;
}
static int __init
@ -174,14 +172,14 @@ gx_intc_init(struct device_node *node, struct device_node *parent)
/*
* Initial enable reg to disable all interrupts
*/
writel_relaxed(0x0, reg_base + GX_INTC_NEN31_00);
writel_relaxed(0x0, reg_base + GX_INTC_NEN63_32);
writel(0x0, reg_base + GX_INTC_NEN31_00);
writel(0x0, reg_base + GX_INTC_NEN63_32);
/*
* Initial mask reg with all unmasked, because we only use enalbe reg
*/
writel_relaxed(0x0, reg_base + GX_INTC_NMASK31_00);
writel_relaxed(0x0, reg_base + GX_INTC_NMASK63_32);
writel(0x0, reg_base + GX_INTC_NMASK31_00);
writel(0x0, reg_base + GX_INTC_NMASK63_32);
setup_irq_channel(0x03020100, reg_base + GX_INTC_SOURCE);
@ -204,20 +202,29 @@ static void ck_irq_handler(struct pt_regs *regs)
void __iomem *reg_pen_lo = reg_base + CK_INTC_PEN31_00;
void __iomem *reg_pen_hi = reg_base + CK_INTC_PEN63_32;
do {
/* handle 0 - 31 irqs */
ret = handle_irq_perbit(regs, readl_relaxed(reg_pen_lo), 0);
ret |= handle_irq_perbit(regs, readl_relaxed(reg_pen_hi), 32);
retry:
/* handle 0 - 63 irqs */
ret = handle_irq_perbit(regs, readl(reg_pen_hi), 32);
if (ret)
goto retry;
if (nr_irq == INTC_IRQS)
continue;
ret = handle_irq_perbit(regs, readl(reg_pen_lo), 0);
if (ret)
goto retry;
/* handle 64 - 127 irqs */
ret |= handle_irq_perbit(regs,
readl_relaxed(reg_pen_lo + CK_INTC_DUAL_BASE), 64);
ret |= handle_irq_perbit(regs,
readl_relaxed(reg_pen_hi + CK_INTC_DUAL_BASE), 96);
} while (ret);
if (nr_irq == INTC_IRQS)
return;
/* handle 64 - 127 irqs */
ret = handle_irq_perbit(regs,
readl(reg_pen_hi + CK_INTC_DUAL_BASE), 96);
if (ret)
goto retry;
ret = handle_irq_perbit(regs,
readl(reg_pen_lo + CK_INTC_DUAL_BASE), 64);
if (ret)
goto retry;
}
static int __init
@ -230,11 +237,11 @@ ck_intc_init(struct device_node *node, struct device_node *parent)
return ret;
/* Initial enable reg to disable all interrupts */
writel_relaxed(0, reg_base + CK_INTC_NEN31_00);
writel_relaxed(0, reg_base + CK_INTC_NEN63_32);
writel(0, reg_base + CK_INTC_NEN31_00);
writel(0, reg_base + CK_INTC_NEN63_32);
/* Enable irq intc */
writel_relaxed(BIT(31), reg_base + CK_INTC_ICR);
writel(BIT(31), reg_base + CK_INTC_ICR);
ck_set_gc(node, reg_base, CK_INTC_NEN31_00, 0);
ck_set_gc(node, reg_base, CK_INTC_NEN63_32, 32);
@ -260,8 +267,8 @@ ck_dual_intc_init(struct device_node *node, struct device_node *parent)
return ret;
/* Initial enable reg to disable all interrupts */
writel_relaxed(0, reg_base + CK_INTC_NEN31_00 + CK_INTC_DUAL_BASE);
writel_relaxed(0, reg_base + CK_INTC_NEN63_32 + CK_INTC_DUAL_BASE);
writel(0, reg_base + CK_INTC_NEN31_00 + CK_INTC_DUAL_BASE);
writel(0, reg_base + CK_INTC_NEN63_32 + CK_INTC_DUAL_BASE);
ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN31_00, 64);
ck_set_gc(node, reg_base + CK_INTC_DUAL_BASE, CK_INTC_NEN63_32, 96);