Re: [RFC PATCH v2 2/5] irqchip/realtek-rtl: fix off-by-one in routing
From: Sander Vanheule
Date: Tue Dec 28 2021 - 11:21:12 EST
On Tue, 2021-12-28 at 10:59 +0000, Marc Zyngier wrote:
> On Tue, 28 Dec 2021 10:13:26 +0000,
> Sander Vanheule <sander@xxxxxxxxxxxxx> wrote:
> >
> > Hi Marc,
> >
> > On Mon, 2021-12-27 at 10:16 +0000, Marc Zyngier wrote:
> > > On Sun, 26 Dec 2021 19:59:25 +0000,
> > > Sander Vanheule <sander@xxxxxxxxxxxxx> wrote:
> > > >
> > > > There is an offset between routing values (1..6) and the connected MIPS
> > > > CPU interrupts (2..7), but no distinction was made between these two
> > > > values.
> > > >
> > > > This issue was previously hidden during testing, because an interrupt
> > > > mapping was used where for each required interrupt another (unused)
> > > > routing was configured, with an offset of +1.
> > >
> > > Where does this 'other routing' come from?
> >
> > When I reported the interrupt-map issue with this binding/driver, I
> > tried to reduce the mapping/routing to something as small as
> > possible, but I couldn't get away with anything less than the
> > following:
> >
> > interrupt-map =
> > <31 &cpuintc 2>, /* UART0 */
> > <20 &cpuintc 3>, /* SWCORE */
> > <19 &cpuintc 4>, /* WDT IP1 */
> > <18 &cpuintc 5>; /* WDT IP2 */
> >
> > The UART0 and WDT_IP1 mappings were required. These would cause the
> > driver to assign chained handlers to <&cpuint 2> and <&cpuint 4>,
> > and also write values "2" and "4" to the respective routing
> > registers.
> >
> > The SWCORE mapping was not required for any configured features, but
> > it was required to get the console to work. This is because a
> > routing register value of "2", actually causes that interrupt input
> > to be (electrically) cascaded into to <&cpuintc 3>. But <&cpuintc 3>
> > would only be assigned a chained handler because of the SWCORE
> > mapping.
> >
> > By then assigning the same handler to all parent interrupts, and not
> > caring about which parent interrupt caused the handler to be called,
> > this ended up actually working.
>
> Urgh... Pure luck, then.
>
> >
> > > >
> > > > Offset the CPU IRQ numbers by -1 to retrieve the correct routing value.
> > > >
> > > > Fixes: 9f3a0f34b84a ("irqchip: Add support for Realtek RTL838x/RTL839x interrupt
> > > > controller")
> > > > Signed-off-by: Sander Vanheule <sander@xxxxxxxxxxxxx>
> > > > ---
> > > > drivers/irqchip/irq-realtek-rtl.c | 8 +++++---
> > > > 1 file changed, 5 insertions(+), 3 deletions(-)
> > > >
> > > > diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c
> > > > index d6788dd93c7b..568614edd88f 100644
> > > > --- a/drivers/irqchip/irq-realtek-rtl.c
> > > > +++ b/drivers/irqchip/irq-realtek-rtl.c
> > > > @@ -95,7 +95,8 @@ static void realtek_irq_dispatch(struct irq_desc *desc)
> > > > * SoC interrupts are cascaded to MIPS CPU interrupts according to the
> > > > * interrupt-map in the device tree. Each SoC interrupt gets 4 bits for
> > > > * the CPU interrupt in an Interrupt Routing Register. Max 32 SoC interrupts
> > > > - * thus go into 4 IRRs.
> > > > + * thus go into 4 IRRs. A routing value of '0' means the interrupt is left
> > > > + * disconnected. Routing values {1..15} connect to output lines {0..14}.
> > > > */
> > > > static int __init map_interrupts(struct device_node *node, struct irq_domain
> > > > *domain)
> > > > {
> > > > @@ -134,7 +135,7 @@ static int __init map_interrupts(struct device_node *node,
> > > > struct
> > > > irq_domain *do
> > > > of_node_put(cpu_ictl);
> > > >
> > > > cpu_int = be32_to_cpup(imap + 2);
> > > > - if (cpu_int > 7)
> > > > + if (cpu_int > 7 || cpu_int < 2)
> > >
> > > How many output lines do you have? The comment above says something
> > > about having 15 output lines, but you limit it to 7...
> >
> > The SoCs we are using with this interrupt controller, connect their
> > output lines to CPU IRQ 2..7. If "interrupt-map" specifies parent HW
> > IRQ numbers, the driver needs to verify those numbers are valid. If
> > they are, it can derive the routing register values from that.
> >
> > On the other hand, routing register values have four bits. "0"
> > appears to disconnect an input interrupt, so that leaves 15
> > potential interrupt outputs (in theory, we don't have actual
> > hardware descriptions). Only 6 outputs are used here, but that could
> > be an implementation detail for these SoCs, rather than a limitation
> > of the interrupt router.
>
> It would be good to find out if there are more CPU interrupts that can
> be targeted. My impression is that this is a copy/paste effect from
> the original BSP, and that nobody actually checked. But maybe that's
> the wrong impression.
The BSP doesn't seems to refer to anything but the 6 CPU HW IRQs, so it appears to be a
standard mti,cpu-interrupt-controller. Those only have 6 HW (2-7) and 2 SW (0-1) IRQs.
> >
> > > > return -EINVAL;
> > > >
> > > > if (!(mips_irqs_set & BIT(cpu_int))) {
> > > > @@ -143,7 +144,8 @@ static int __init map_interrupts(struct device_node *node,
> > > > struct
> > > > irq_domain *do
> > > > mips_irqs_set |= BIT(cpu_int);
> > > > }
> > > >
> > > > - regs[(soc_int * 4) / 32] |= cpu_int << (soc_int * 4) % 32;
> > > > + /* Use routing values (1..6) for CPU interrupts (2..7) */
> > > > + regs[(soc_int * 4) / 32] |= (cpu_int - 1) << (soc_int * 4) % 32;
> > > > imap += 3;
> > > > }
> > > >
> > >
> > > What I don't understand is how this worked so far if all mappings were
> > > off my one. Or the mapping really doesn't matter, because this is all
> > > under SW control?
> >
> > The reason this worked, was due to a number of issues compensating
> > for each other, like I tried to explain above.
> >
> > The mapping is indeed arbitrary, and not designed into the
> > hardware. So it could (should?) just be put in the driver, instead
> > of the devicetree.
>
> Indeed. Which is what I was advocating for the first place. What I
> understand from the HW is that it is able to freely route any input to
> any output (muxing them as required), and to map each output to any
> CPU interrupt line.
Indeed input-to-output is runtime configurable, but output-to-parent is hard-wired AFAICT.
If the output-to-parent mapping is known, an input-to-parent mapping can be used (i.e.
"interrupt-map") to select the outputs.
> If my understanding is correct, the only thing you need from the DT is
> a description of which input an endpoint is targeting (the interrupt
> specifier), and a list of CPU interrupt lines. Everything else can be
> decided at runtime.
I think this hardware is similar to 'fsl,imx-intmux' (irq-imx-intmux.c). See also my
comments on the bindings patch.
> Anyway, I'll probably end-up queuing the first two patches for 5.17,
> and a Cc: to stable. The rest we can discuss ad-nauseam.
Would you like me to submit the first two separately or will you take them as they are
from this series?
Best,
Sander