Commit 56752b21 authored by Guo Ren's avatar Guo Ren
Browse files

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: default avatarGuo Ren <ren_guo@c-sky.com>
Cc: Lu Baoquan <lu.baoquan@intellif.com>
parent 2a60aa14
Loading
Loading
Loading
Loading
+42 −35
Original line number Diff line number Diff line
@@ -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 {
retry:
	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);
			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;

	ret = handle_irq_perbit(regs, readl(reg_pen_lo), 0);
	if (ret)
		goto retry;

	if (nr_irq == INTC_IRQS)
			continue;
		return;

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