diff mbox series

[v3,10/16] irqchip/bcm2836: Configure mailbox interrupts as standard interrupts

Message ID 20200901144324.1071694-11-maz@kernel.org (mailing list archive)
State New, archived
Headers show
Series arm/arm64: Turning IPIs into normal interrupts | expand

Commit Message

Marc Zyngier Sept. 1, 2020, 2:43 p.m. UTC
In order to switch the bcm2836 driver to privide standard interrupts
for IPIs, it first needs to stop lying about the way things work.

The mailbox interrupt is actually a multiplexer, with enough
bits to store 32 pending interrupts per CPU. So let's turn it
into a chained irqchip.

Once this is done, we can instanciate the corresponding IPIs,
and pass them to the architecture code.

Signed-off-by: Marc Zyngier <maz@kernel.org>
---
 drivers/irqchip/irq-bcm2836.c | 151 ++++++++++++++++++++++++++++------
 1 file changed, 125 insertions(+), 26 deletions(-)

Comments

Marek Szyprowski Sept. 14, 2020, 2:32 p.m. UTC | #1
Hi Marc,

On 01.09.2020 16:43, Marc Zyngier wrote:
> In order to switch the bcm2836 driver to privide standard interrupts
> for IPIs, it first needs to stop lying about the way things work.
>
> The mailbox interrupt is actually a multiplexer, with enough
> bits to store 32 pending interrupts per CPU. So let's turn it
> into a chained irqchip.
>
> Once this is done, we can instanciate the corresponding IPIs,
> and pass them to the architecture code.
>
> Signed-off-by: Marc Zyngier <maz@kernel.org>

This one also fails. It breaks booting of Raspberry Pi 3b boards (both 
in ARM and ARM64 mode):

NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
8<--- cut here ---
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = (ptrval)
[00000000] *pgd=80000000004003, *pmd=00000000
Internal error: Oops: 80000206 [#1] SMP ARM
Modules linked in:
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.9.0-rc4+ #9166
Hardware name: BCM2835
PC is at 0x0
LR is at irq_percpu_enable+0x40/0x50
pc : [<00000000>]    lr : [<c0274638>]    psr: 600000d3
sp : c1201e00  ip : c1201e18  fp : c1201e14
r10: c0ef23dc  r9 : c120583c  r8 : 00000000
r7 : 00000011  r6 : eb032d00  r5 : 00000000  r4 : eb032d00
r3 : 00000000  r2 : 00000000  r1 : 00000000  r0 : eb032d18
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32  ISA ARM  Segment user
Control: 30c5383d  Table: 00003000  DAC: fffffffd
Process swapper/0 (pid: 0, stack limit = 0x(ptrval))
Stack: (0xc1201e00 to 0xc1202000)
...
Backtrace:
[<c02745f8>] (irq_percpu_enable) from [<c0272498>] 
(enable_percpu_irq+0xa4/0xd8)
  r5:c1204ec8 r4:00000000
[<c02723f4>] (enable_percpu_irq) from [<c020ded0>] 
(ipi_setup.part.0+0x3c/0x48)
  r8:c107ba80 r7:00000018 r6:00000011 r5:c1204ee0 r4:00000001
[<c020de94>] (ipi_setup.part.0) from [<c1005684>] 
(set_smp_ipi_range+0xd8/0xf8)
  r5:c1204ee0 r4:00000008
[<c10055ac>] (set_smp_ipi_range) from [<c1023388>] 
(bcm2836_arm_irqchip_l1_intc_of_init+0x1c0/0x22c)
  r8:c1366820 r7:c1204ec8 r6:00000010 r5:00000001 r4:00000000
[<c10231c8>] (bcm2836_arm_irqchip_l1_intc_of_init) from [<c102f28c>] 
(of_irq_init+0x18c/0x2dc)
  r9:00000000 r8:c1201f34 r7:c1204ec8 r6:c1201f2c r5:c1201f2c r4:eb004880
[<c102f100>] (of_irq_init) from [<c1022f4c>] (irqchip_init+0x1c/0x24)
  r10:0000006c r9:00000000 r8:00000000 r7:ffffffff r6:cccccccd r5:c0eb7abe
  r4:c103ba30
[<c1022f30>] (irqchip_init) from [<c1003960>] (init_IRQ+0x30/0x98)
[<c1003930>] (init_IRQ) from [<c1000ebc>] (start_kernel+0x3b4/0x628)
  r5:c0eb7abe r4:c12c1000
[<c1000b08>] (start_kernel) from [<00000000>] (0x0)
  r10:30c5387d r9:410fd034 r8:02600000 r7:00000000 r6:30c0387d r5:00000000
  r4:c1000330
Code: bad PC value
random: get_random_bytes called from init_oops_id+0x30/0x4c with crng_init=0
---[ end trace 0000000000000000 ]---
Kernel panic - not syncing: Attempted to kill the idle task!
---[ end Kernel panic - not syncing: Attempted to kill the idle task! ]---


> ---
>   drivers/irqchip/irq-bcm2836.c | 151 ++++++++++++++++++++++++++++------
>   1 file changed, 125 insertions(+), 26 deletions(-)
>
> ...

Best regards
Marc Zyngier Sept. 14, 2020, 4:10 p.m. UTC | #2
Hi Marek,

On 2020-09-14 15:32, Marek Szyprowski wrote:
> Hi Marc,
> 
> On 01.09.2020 16:43, Marc Zyngier wrote:
>> In order to switch the bcm2836 driver to privide standard interrupts
>> for IPIs, it first needs to stop lying about the way things work.
>> 
>> The mailbox interrupt is actually a multiplexer, with enough
>> bits to store 32 pending interrupts per CPU. So let's turn it
>> into a chained irqchip.
>> 
>> Once this is done, we can instanciate the corresponding IPIs,
>> and pass them to the architecture code.
>> 
>> Signed-off-by: Marc Zyngier <maz@kernel.org>
> 
> This one also fails. It breaks booting of Raspberry Pi 3b boards (both
> in ARM and ARM64 mode):

Damn. This used to work. Looks like I was eager to delete stuff at
some point. Can you give this a go and let me know if that works
for you (only tested in QEMU with the raspi2 model):

diff --git a/drivers/irqchip/irq-bcm2836.c 
b/drivers/irqchip/irq-bcm2836.c
index 85df6ddad9be..97838eb705f9 100644
--- a/drivers/irqchip/irq-bcm2836.c
+++ b/drivers/irqchip/irq-bcm2836.c
@@ -193,6 +193,8 @@ static void bcm2836_arm_irqchip_ipi_send_mask(struct 
irq_data *d,

  static struct irq_chip bcm2836_arm_irqchip_ipi = {
  	.name		= "IPI",
+	.irq_mask	= bcm2836_arm_irqchip_dummy_op,
+	.irq_unmask	= bcm2836_arm_irqchip_dummy_op,
  	.irq_eoi	= bcm2836_arm_irqchip_ipi_eoi,
  	.ipi_send_mask	= bcm2836_arm_irqchip_ipi_send_mask,
  };


Thanks again,

         M.
Marek Szyprowski Sept. 14, 2020, 7:13 p.m. UTC | #3
Hi Marc,

On 14.09.2020 18:10, Marc Zyngier wrote:
> On 2020-09-14 15:32, Marek Szyprowski wrote:
>> On 01.09.2020 16:43, Marc Zyngier wrote:
>>> In order to switch the bcm2836 driver to privide standard interrupts
>>> for IPIs, it first needs to stop lying about the way things work.
>>>
>>> The mailbox interrupt is actually a multiplexer, with enough
>>> bits to store 32 pending interrupts per CPU. So let's turn it
>>> into a chained irqchip.
>>>
>>> Once this is done, we can instanciate the corresponding IPIs,
>>> and pass them to the architecture code.
>>>
>>> Signed-off-by: Marc Zyngier <maz@kernel.org>
>>
>> This one also fails. It breaks booting of Raspberry Pi 3b boards (both
>> in ARM and ARM64 mode):
>
> Damn. This used to work. Looks like I was eager to delete stuff at
> some point. Can you give this a go and let me know if that works
> for you (only tested in QEMU with the raspi2 model):
>
> diff --git a/drivers/irqchip/irq-bcm2836.c 
> b/drivers/irqchip/irq-bcm2836.c
> index 85df6ddad9be..97838eb705f9 100644
> --- a/drivers/irqchip/irq-bcm2836.c
> +++ b/drivers/irqchip/irq-bcm2836.c
> @@ -193,6 +193,8 @@ static void 
> bcm2836_arm_irqchip_ipi_send_mask(struct irq_data *d,
>
>  static struct irq_chip bcm2836_arm_irqchip_ipi = {
>      .name        = "IPI",
> +    .irq_mask    = bcm2836_arm_irqchip_dummy_op,
> +    .irq_unmask    = bcm2836_arm_irqchip_dummy_op,
>      .irq_eoi    = bcm2836_arm_irqchip_ipi_eoi,
>      .ipi_send_mask    = bcm2836_arm_irqchip_ipi_send_mask,
>  };
>
>
> Thanks again,

This fixes boot on my RPi3b. Thanks!

Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>

Best regards
diff mbox series

Patch

diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c
index 2038693f074c..85df6ddad9be 100644
--- a/drivers/irqchip/irq-bcm2836.c
+++ b/drivers/irqchip/irq-bcm2836.c
@@ -10,6 +10,7 @@ 
 #include <linux/of_irq.h>
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
 #include <linux/irqchip/irq-bcm2836.h>
 
 #include <asm/exception.h>
@@ -89,12 +90,24 @@  static struct irq_chip bcm2836_arm_irqchip_gpu = {
 	.irq_unmask	= bcm2836_arm_irqchip_unmask_gpu_irq,
 };
 
+static void bcm2836_arm_irqchip_dummy_op(struct irq_data *d)
+{
+}
+
+static struct irq_chip bcm2836_arm_irqchip_dummy = {
+	.name		= "bcm2836-dummy",
+	.irq_eoi	= bcm2836_arm_irqchip_dummy_op,
+};
+
 static int bcm2836_map(struct irq_domain *d, unsigned int irq,
 		       irq_hw_number_t hw)
 {
 	struct irq_chip *chip;
 
 	switch (hw) {
+	case LOCAL_IRQ_MAILBOX0:
+		chip = &bcm2836_arm_irqchip_dummy;
+		break;
 	case LOCAL_IRQ_CNTPSIRQ:
 	case LOCAL_IRQ_CNTPNSIRQ:
 	case LOCAL_IRQ_CNTHPIRQ:
@@ -127,17 +140,7 @@  __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
 	u32 stat;
 
 	stat = readl_relaxed(intc.base + LOCAL_IRQ_PENDING0 + 4 * cpu);
-	if (stat & BIT(LOCAL_IRQ_MAILBOX0)) {
-#ifdef CONFIG_SMP
-		void __iomem *mailbox0 = (intc.base +
-					  LOCAL_MAILBOX0_CLR0 + 16 * cpu);
-		u32 mbox_val = readl(mailbox0);
-		u32 ipi = ffs(mbox_val) - 1;
-
-		writel(1 << ipi, mailbox0);
-		handle_IPI(ipi, regs);
-#endif
-	} else if (stat) {
+	if (stat) {
 		u32 hwirq = ffs(stat) - 1;
 
 		handle_domain_irq(intc.domain, hwirq, regs);
@@ -145,8 +148,35 @@  __exception_irq_entry bcm2836_arm_irqchip_handle_irq(struct pt_regs *regs)
 }
 
 #ifdef CONFIG_SMP
-static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
-					 unsigned int ipi)
+static struct irq_domain *ipi_domain;
+
+static void bcm2836_arm_irqchip_handle_ipi(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	int cpu = smp_processor_id();
+	u32 mbox_val;
+
+	chained_irq_enter(chip, desc);
+
+	mbox_val = readl_relaxed(intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
+	if (mbox_val) {
+		int hwirq = ffs(mbox_val) - 1;
+		generic_handle_irq(irq_find_mapping(ipi_domain, hwirq));
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm2836_arm_irqchip_ipi_eoi(struct irq_data *d)
+{
+	int cpu = smp_processor_id();
+
+	writel_relaxed(BIT(d->hwirq),
+		       intc.base + LOCAL_MAILBOX0_CLR0 + 16 * cpu);
+}
+
+static void bcm2836_arm_irqchip_ipi_send_mask(struct irq_data *d,
+					      const struct cpumask *mask)
 {
 	int cpu;
 	void __iomem *mailbox0_base = intc.base + LOCAL_MAILBOX0_SET0;
@@ -157,11 +187,45 @@  static void bcm2836_arm_irqchip_send_ipi(const struct cpumask *mask,
 	 */
 	smp_wmb();
 
-	for_each_cpu(cpu, mask)	{
-		writel(1 << ipi, mailbox0_base + 16 * cpu);
+	for_each_cpu(cpu, mask)
+		writel_relaxed(BIT(d->hwirq), mailbox0_base + 16 * cpu);
+}
+
+static struct irq_chip bcm2836_arm_irqchip_ipi = {
+	.name		= "IPI",
+	.irq_eoi	= bcm2836_arm_irqchip_ipi_eoi,
+	.ipi_send_mask	= bcm2836_arm_irqchip_ipi_send_mask,
+};
+
+static int bcm2836_arm_irqchip_ipi_alloc(struct irq_domain *d,
+					 unsigned int virq,
+					 unsigned int nr_irqs, void *args)
+{
+	int i;
+
+	for (i = 0; i < nr_irqs; i++) {
+		irq_set_percpu_devid(virq + i);
+		irq_domain_set_info(d, virq + i, i, &bcm2836_arm_irqchip_ipi,
+				    d->host_data,
+				    handle_percpu_devid_fasteoi_ipi,
+				    NULL, NULL);
 	}
+
+	return 0;
 }
 
+static void bcm2836_arm_irqchip_ipi_free(struct irq_domain *d,
+					 unsigned int virq,
+					 unsigned int nr_irqs)
+{
+	/* Not freeing IPIs */
+}
+
+static const struct irq_domain_ops ipi_domain_ops = {
+	.alloc	= bcm2836_arm_irqchip_ipi_alloc,
+	.free	= bcm2836_arm_irqchip_ipi_free,
+};
+
 static int bcm2836_cpu_starting(unsigned int cpu)
 {
 	bcm2836_arm_irqchip_unmask_per_cpu_irq(LOCAL_MAILBOX_INT_CONTROL0, 0,
@@ -175,25 +239,58 @@  static int bcm2836_cpu_dying(unsigned int cpu)
 					     cpu);
 	return 0;
 }
-#endif
 
-static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
-	.xlate = irq_domain_xlate_onetwocell,
-	.map = bcm2836_map,
-};
+#define BITS_PER_MBOX	32
 
-static void
-bcm2836_arm_irqchip_smp_init(void)
+static void bcm2836_arm_irqchip_smp_init(void)
 {
-#ifdef CONFIG_SMP
+	struct irq_fwspec ipi_fwspec = {
+		.fwnode		= intc.domain->fwnode,
+		.param_count	= 1,
+		.param		= {
+			[0]	= LOCAL_IRQ_MAILBOX0,
+		},
+	};
+	int base_ipi, mux_irq;
+
+	mux_irq = irq_create_fwspec_mapping(&ipi_fwspec);
+	if (WARN_ON(mux_irq <= 0))
+		return;
+
+	ipi_domain = irq_domain_create_linear(intc.domain->fwnode,
+					      BITS_PER_MBOX, &ipi_domain_ops,
+					      NULL);
+	if (WARN_ON(!ipi_domain))
+		return;
+
+	ipi_domain->flags |= IRQ_DOMAIN_FLAG_IPI_SINGLE;
+	irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
+
+	base_ipi = __irq_domain_alloc_irqs(ipi_domain, -1, BITS_PER_MBOX,
+					   NUMA_NO_NODE, NULL,
+					   false, NULL);
+
+	if (WARN_ON(!base_ipi))
+		return;
+
+	set_smp_ipi_range(base_ipi, BITS_PER_MBOX);
+
+	irq_set_chained_handler_and_data(mux_irq,
+					 bcm2836_arm_irqchip_handle_ipi, NULL);
+
 	/* Unmask IPIs to the boot CPU. */
 	cpuhp_setup_state(CPUHP_AP_IRQ_BCM2836_STARTING,
 			  "irqchip/bcm2836:starting", bcm2836_cpu_starting,
 			  bcm2836_cpu_dying);
-
-	set_smp_cross_call(bcm2836_arm_irqchip_send_ipi);
-#endif
 }
+#else
+#define bcm2836_arm_irqchip_smp_init()	do { } while(0)
+#endif
+
+static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = {
+	.xlate = irq_domain_xlate_onetwocell,
+	.map = bcm2836_map,
+};
 
 /*
  * The LOCAL_IRQ_CNT* timer firings are based off of the external
@@ -232,6 +329,8 @@  static int __init bcm2836_arm_irqchip_l1_intc_of_init(struct device_node *node,
 	if (!intc.domain)
 		panic("%pOF: unable to create IRQ domain\n", node);
 
+	irq_domain_update_bus_token(intc.domain, DOMAIN_BUS_WIRED);
+
 	bcm2836_arm_irqchip_smp_init();
 
 	set_handle_irq(bcm2836_arm_irqchip_handle_irq);