f07e572f64
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
106 lines
3.9 KiB
Diff
106 lines
3.9 KiB
Diff
From 4ae861da5eaf53e5b4303f080bff0e34e22da0d9 Mon Sep 17 00:00:00 2001
|
|
From: Lukas Wunner <lukas@wunner.de>
|
|
Date: Tue, 4 Feb 2020 15:50:41 +0100
|
|
Subject: [PATCH] irqchip/bcm2835: Quiesce IRQs left enabled by
|
|
bootloader
|
|
|
|
[ Upstream commit bd59b343a9c902c522f006e6d71080f4893bbf42 ]
|
|
|
|
Per the spec, the BCM2835's IRQs are all disabled when coming out of
|
|
power-on reset. Its IRQ driver assumes that's still the case when the
|
|
kernel boots and does not perform any initialization of the registers.
|
|
However the Raspberry Pi Foundation's bootloader leaves the USB
|
|
interrupt enabled when handing over control to the kernel.
|
|
|
|
Quiesce IRQs and the FIQ if they were left enabled and log a message to
|
|
let users know that they should update the bootloader once a fixed
|
|
version is released.
|
|
|
|
If the USB interrupt is not quiesced and the USB driver later on claims
|
|
the FIQ (as it does on the Raspberry Pi Foundation's downstream kernel),
|
|
interrupt latency for all other peripherals increases and occasional
|
|
lockups occur. That's because both the FIQ and the normal USB interrupt
|
|
fire simultaneously:
|
|
|
|
On a multicore Raspberry Pi, if normal interrupts are routed to CPU 0
|
|
and the FIQ to CPU 1 (hardcoded in the Foundation's kernel), then a USB
|
|
interrupt causes CPU 0 to spin in bcm2836_chained_handle_irq() until the
|
|
FIQ on CPU 1 has cleared it. Other peripherals' interrupts are starved
|
|
as long. I've seen CPU 0 blocked for up to 2.9 msec. eMMC throughput
|
|
on a Compute Module 3 irregularly dips to 23.0 MB/s without this commit
|
|
but remains relatively constant at 23.5 MB/s with this commit.
|
|
|
|
The lockups occur when CPU 0 receives a USB interrupt while holding a
|
|
lock which CPU 1 is trying to acquire while the FIQ is temporarily
|
|
disabled on CPU 1. At best users get RCU CPU stall warnings, but most
|
|
of the time the system just freezes.
|
|
|
|
Fixes: 89214f009c1d ("ARM: bcm2835: add interrupt controller driver")
|
|
Signed-off-by: Lukas Wunner <lukas@wunner.de>
|
|
Signed-off-by: Marc Zyngier <maz@kernel.org>
|
|
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
|
|
Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
|
|
Link: https://lore.kernel.org/r/f97868ba4e9b86ddad71f44ec9d8b3b7d8daa1ea.1582618537.git.lukas@wunner.de
|
|
---
|
|
drivers/irqchip/irq-bcm2835.c | 21 +++++++++++++++++----
|
|
1 file changed, 17 insertions(+), 4 deletions(-)
|
|
|
|
--- a/drivers/irqchip/irq-bcm2835.c
|
|
+++ b/drivers/irqchip/irq-bcm2835.c
|
|
@@ -67,8 +67,7 @@
|
|
#define ARM_LOCAL_GPU_INT_ROUTING 0x0c
|
|
|
|
#define REG_FIQ_CONTROL 0x0c
|
|
-#define REG_FIQ_ENABLE 0x80
|
|
-#define REG_FIQ_DISABLE 0
|
|
+#define FIQ_CONTROL_ENABLE BIT(7)
|
|
|
|
#define NR_BANKS 3
|
|
#define IRQS_PER_BANK 32
|
|
@@ -116,7 +115,7 @@ static inline unsigned int hwirq_to_fiq(
|
|
static void armctrl_mask_irq(struct irq_data *d)
|
|
{
|
|
if (d->hwirq >= NUMBER_IRQS)
|
|
- writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
|
|
+ writel_relaxed(0, intc.base + REG_FIQ_CONTROL);
|
|
else
|
|
writel_relaxed(HWIRQ_BIT(d->hwirq),
|
|
intc.disable[HWIRQ_BANK(d->hwirq)]);
|
|
@@ -143,7 +142,7 @@ static void armctrl_unmask_irq(struct ir
|
|
ARM_LOCAL_GPU_INT_ROUTING);
|
|
}
|
|
|
|
- writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
|
|
+ writel_relaxed(FIQ_CONTROL_ENABLE | hwirq_to_fiq(d->hwirq),
|
|
intc.base + REG_FIQ_CONTROL);
|
|
} else {
|
|
writel_relaxed(HWIRQ_BIT(d->hwirq),
|
|
@@ -201,6 +200,7 @@ static int __init armctrl_of_init(struct
|
|
{
|
|
void __iomem *base;
|
|
int irq = 0, last_irq, b, i;
|
|
+ u32 reg;
|
|
|
|
base = of_iomap(node, 0);
|
|
if (!base)
|
|
@@ -224,6 +224,19 @@ static int __init armctrl_of_init(struct
|
|
handle_level_irq);
|
|
irq_set_probe(irq);
|
|
}
|
|
+
|
|
+ reg = readl_relaxed(intc.enable[b]);
|
|
+ if (reg) {
|
|
+ writel_relaxed(reg, intc.disable[b]);
|
|
+ pr_err(FW_BUG "Bootloader left irq enabled: "
|
|
+ "bank %d irq %*pbl\n", b, IRQS_PER_BANK, ®);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ reg = readl_relaxed(base + REG_FIQ_CONTROL);
|
|
+ if (reg & FIQ_CONTROL_ENABLE) {
|
|
+ writel_relaxed(0, base + REG_FIQ_CONTROL);
|
|
+ pr_err(FW_BUG "Bootloader left fiq enabled\n");
|
|
}
|
|
|
|
last_irq = irq;
|