f27d448864d1a7e32d9b4e0f94323a46c532aacb
[openwrt/staging/blocktrron.git] /
1 From 4ae861da5eaf53e5b4303f080bff0e34e22da0d9 Mon Sep 17 00:00:00 2001
2 From: Lukas Wunner <lukas@wunner.de>
3 Date: Tue, 4 Feb 2020 15:50:41 +0100
4 Subject: [PATCH] irqchip/bcm2835: Quiesce IRQs left enabled by
5 bootloader
6
7 [ Upstream commit bd59b343a9c902c522f006e6d71080f4893bbf42 ]
8
9 Per the spec, the BCM2835's IRQs are all disabled when coming out of
10 power-on reset. Its IRQ driver assumes that's still the case when the
11 kernel boots and does not perform any initialization of the registers.
12 However the Raspberry Pi Foundation's bootloader leaves the USB
13 interrupt enabled when handing over control to the kernel.
14
15 Quiesce IRQs and the FIQ if they were left enabled and log a message to
16 let users know that they should update the bootloader once a fixed
17 version is released.
18
19 If the USB interrupt is not quiesced and the USB driver later on claims
20 the FIQ (as it does on the Raspberry Pi Foundation's downstream kernel),
21 interrupt latency for all other peripherals increases and occasional
22 lockups occur. That's because both the FIQ and the normal USB interrupt
23 fire simultaneously:
24
25 On a multicore Raspberry Pi, if normal interrupts are routed to CPU 0
26 and the FIQ to CPU 1 (hardcoded in the Foundation's kernel), then a USB
27 interrupt causes CPU 0 to spin in bcm2836_chained_handle_irq() until the
28 FIQ on CPU 1 has cleared it. Other peripherals' interrupts are starved
29 as long. I've seen CPU 0 blocked for up to 2.9 msec. eMMC throughput
30 on a Compute Module 3 irregularly dips to 23.0 MB/s without this commit
31 but remains relatively constant at 23.5 MB/s with this commit.
32
33 The lockups occur when CPU 0 receives a USB interrupt while holding a
34 lock which CPU 1 is trying to acquire while the FIQ is temporarily
35 disabled on CPU 1. At best users get RCU CPU stall warnings, but most
36 of the time the system just freezes.
37
38 Fixes: 89214f009c1d ("ARM: bcm2835: add interrupt controller driver")
39 Signed-off-by: Lukas Wunner <lukas@wunner.de>
40 Signed-off-by: Marc Zyngier <maz@kernel.org>
41 Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
42 Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
43 Link: https://lore.kernel.org/r/f97868ba4e9b86ddad71f44ec9d8b3b7d8daa1ea.1582618537.git.lukas@wunner.de
44 ---
45 drivers/irqchip/irq-bcm2835.c | 21 +++++++++++++++++----
46 1 file changed, 17 insertions(+), 4 deletions(-)
47
48 --- a/drivers/irqchip/irq-bcm2835.c
49 +++ b/drivers/irqchip/irq-bcm2835.c
50 @@ -67,8 +67,7 @@
51 #define ARM_LOCAL_GPU_INT_ROUTING 0x0c
52
53 #define REG_FIQ_CONTROL 0x0c
54 -#define REG_FIQ_ENABLE 0x80
55 -#define REG_FIQ_DISABLE 0
56 +#define FIQ_CONTROL_ENABLE BIT(7)
57
58 #define NR_BANKS 3
59 #define IRQS_PER_BANK 32
60 @@ -116,7 +115,7 @@ static inline unsigned int hwirq_to_fiq(
61 static void armctrl_mask_irq(struct irq_data *d)
62 {
63 if (d->hwirq >= NUMBER_IRQS)
64 - writel_relaxed(REG_FIQ_DISABLE, intc.base + REG_FIQ_CONTROL);
65 + writel_relaxed(0, intc.base + REG_FIQ_CONTROL);
66 else
67 writel_relaxed(HWIRQ_BIT(d->hwirq),
68 intc.disable[HWIRQ_BANK(d->hwirq)]);
69 @@ -143,7 +142,7 @@ static void armctrl_unmask_irq(struct ir
70 ARM_LOCAL_GPU_INT_ROUTING);
71 }
72
73 - writel_relaxed(REG_FIQ_ENABLE | hwirq_to_fiq(d->hwirq),
74 + writel_relaxed(FIQ_CONTROL_ENABLE | hwirq_to_fiq(d->hwirq),
75 intc.base + REG_FIQ_CONTROL);
76 } else {
77 writel_relaxed(HWIRQ_BIT(d->hwirq),
78 @@ -201,6 +200,7 @@ static int __init armctrl_of_init(struct
79 {
80 void __iomem *base;
81 int irq = 0, last_irq, b, i;
82 + u32 reg;
83
84 base = of_iomap(node, 0);
85 if (!base)
86 @@ -224,6 +224,19 @@ static int __init armctrl_of_init(struct
87 handle_level_irq);
88 irq_set_probe(irq);
89 }
90 +
91 + reg = readl_relaxed(intc.enable[b]);
92 + if (reg) {
93 + writel_relaxed(reg, intc.disable[b]);
94 + pr_err(FW_BUG "Bootloader left irq enabled: "
95 + "bank %d irq %*pbl\n", b, IRQS_PER_BANK, &reg);
96 + }
97 + }
98 +
99 + reg = readl_relaxed(base + REG_FIQ_CONTROL);
100 + if (reg & FIQ_CONTROL_ENABLE) {
101 + writel_relaxed(0, base + REG_FIQ_CONTROL);
102 + pr_err(FW_BUG "Bootloader left fiq enabled\n");
103 }
104
105 last_irq = irq;