From: Marc Zyngier <maz@kernel.org>
To: Sander Vanheule <sander@svanheule.net>
Cc: Thomas Gleixner <tglx@linutronix.de>,
Rob Herring <robh+dt@kernel.org>,
devicetree@vger.kernel.org,
Birger Koblitz <mail@birger-koblitz.de>,
Bert Vermeulen <bert@biot.com>, John Crispin <john@phrozen.org>,
linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH v2 3/5] irqchip/realtek-rtl: use per-parent irq handling
Date: Mon, 27 Dec 2021 10:38:20 +0000 [thread overview]
Message-ID: <87sfuez61v.wl-maz@kernel.org> (raw)
In-Reply-To: <73789385f470b7630c19b4c632d60ef7b89a46d0.1640548009.git.sander@svanheule.net>
On Sun, 26 Dec 2021 19:59:26 +0000,
Sander Vanheule <sander@svanheule.net> wrote:
>
> The driver handled all SoC interrupts equally, independent of which
> parent interrupt it is routed to. Between all configured inputs, the use
> of __ffs actually gives higher priority to lower input lines,
> effectively bypassing any priority there might be among the parent
> interrupts.
>
> Rework the driver to use a separate handler for each parent interrupt,
> to respect the order in which those parents interrupt are handled.
>
> Signed-off-by: Sander Vanheule <sander@svanheule.net>
> ---
> With switching back to chained handlers, it became impossible to route a
> SoC interrupt to the timer interrupt (CPU IRQ 7) on systems using the
> CEVT-R4K timer. If a chained handler is already set for the timer
> interrupt, the timer cannot request it anymore (due to IRQ_NOREQUEST)
> and the system hangs. It is probably a terrible idea to also run e.g.
> the console on the timer interrupt, but it is possible. If there are no
> solutions to this, I can live with it though; there are still 5 other
> interrupts.
Shared interrupts and chaining don't mix. You can look at it any way
you want, there is always something that breaks eventually.
>
> Changes since v1:
> - Limit scope to per-parent handling
> - Replace the "priority" naming with the more generic "output"
> - Don't request interrupts, but stick to chained handlers
> ---
> drivers/irqchip/irq-realtek-rtl.c | 109 ++++++++++++++++++++----------
> 1 file changed, 74 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/irqchip/irq-realtek-rtl.c b/drivers/irqchip/irq-realtek-rtl.c
> index 568614edd88f..1f8f21a0bd1a 100644
> --- a/drivers/irqchip/irq-realtek-rtl.c
> +++ b/drivers/irqchip/irq-realtek-rtl.c
> @@ -7,6 +7,7 @@
>
> #include <linux/of_irq.h>
> #include <linux/irqchip.h>
> +#include <linux/interrupt.h>
> #include <linux/spinlock.h>
> #include <linux/of_address.h>
> #include <linux/irqchip/chained_irq.h>
> @@ -21,10 +22,45 @@
> #define RTL_ICTL_IRR2 0x10
> #define RTL_ICTL_IRR3 0x14
>
> +#define RTL_ICTL_NUM_OUTPUTS 6
> +
> #define REG(x) (realtek_ictl_base + x)
>
> static DEFINE_RAW_SPINLOCK(irq_lock);
> static void __iomem *realtek_ictl_base;
> +static struct irq_domain *realtek_ictl_domain;
> +
> +struct realtek_ictl_output {
> + unsigned int routing_value;
> + u32 child_mask;
> +};
> +
> +static struct realtek_ictl_output realtek_ictl_outputs[RTL_ICTL_NUM_OUTPUTS];
> +
> +/*
> + * IRR0-IRR3 store 4 bits per interrupt, but Realtek uses inverted numbering,
> + * placing IRQ 31 in the first four bits. A routing value of '0' means the
> + * interrupt is left disconnected. Routing values {1..15} connect to output
> + * lines {0..14}.
> + */
> +#define IRR_OFFSET(idx) (4 * (3 - (idx * 4) / 32))
> +#define IRR_SHIFT(idx) ((idx * 4) % 32)
> +
> +static inline u32 read_irr(void __iomem *irr0, int idx)
> +{
> + return (readl(irr0 + IRR_OFFSET(idx)) >> IRR_SHIFT(idx)) & 0xf;
> +}
> +
> +static inline void write_irr(void __iomem *irr0, int idx, u32 value)
> +{
> + unsigned int offset = IRR_OFFSET(idx);
> + unsigned int shift = IRR_SHIFT(idx);
> + u32 irr;
> +
> + irr = readl(irr0 + offset) & ~(0xf << shift);
> + irr |= (value & 0xf) << shift;
> + writel(irr, irr0 + offset);
> +}
>
> static void realtek_ictl_unmask_irq(struct irq_data *i)
> {
> @@ -74,42 +110,45 @@ static const struct irq_domain_ops irq_domain_ops = {
>
> static void realtek_irq_dispatch(struct irq_desc *desc)
> {
> + struct realtek_ictl_output *parent = irq_desc_get_handler_data(desc);
> struct irq_chip *chip = irq_desc_get_chip(desc);
> - struct irq_domain *domain;
> unsigned int pending;
>
> chained_irq_enter(chip, desc);
> - pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR));
> + pending = readl(REG(RTL_ICTL_GIMR)) & readl(REG(RTL_ICTL_GISR))
> + & parent->child_mask;
> +
> if (unlikely(!pending)) {
> spurious_interrupt();
> goto out;
> }
> - domain = irq_desc_get_handler_data(desc);
> - generic_handle_domain_irq(domain, __ffs(pending));
> + generic_handle_domain_irq(realtek_ictl_domain, __ffs(pending));
You were complaining about the use of __ffs() creating artificial
priorities. And yet you keep using it, recreating the same issue for a
smaller set of interrupts. Why do we need all the complexity of
registering multiple handlers when a simple loop on the pending bits
would ensure some level of fairness?
It looks to me that you are solving a different problem, where you'd
deliver interrupts that have may not yet been signalled to the CPU
yet. And you definitely should consider consuming all the pending
bits before exiting.
>
> out:
> chained_irq_exit(chip, 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. 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)
> +static void __init set_routing(struct realtek_ictl_output *output, unsigned int soc_int)
> {
> + unsigned int routing_old;
> +
> + routing_old = read_irr(REG(RTL_ICTL_IRR0), soc_int);
> + if (routing_old) {
> + pr_warn("int %d already routed to %d, not updating\n", soc_int, routing_old);
> + return;
> + }
> +
> + output->child_mask |= BIT(soc_int);
> + write_irr(REG(RTL_ICTL_IRR0), soc_int, output->routing_value);
> +}
> +
> +static int __init map_interrupts(struct device_node *node)
> +{
> + struct realtek_ictl_output *output;
> struct device_node *cpu_ictl;
> const __be32 *imap;
> - u32 imaplen, soc_int, cpu_int, tmp, regs[4];
> - int ret, i, irr_regs[] = {
> - RTL_ICTL_IRR3,
> - RTL_ICTL_IRR2,
> - RTL_ICTL_IRR1,
> - RTL_ICTL_IRR0,
> - };
> - u8 mips_irqs_set;
> + u32 imaplen, soc_int, cpu_int, tmp;
> + int ret, i;
>
> ret = of_property_read_u32(node, "#address-cells", &tmp);
> if (ret || tmp)
> @@ -119,8 +158,6 @@ static int __init map_interrupts(struct device_node *node, struct irq_domain *do
> if (!imap || imaplen % 3)
> return -EINVAL;
>
> - mips_irqs_set = 0;
> - memset(regs, 0, sizeof(regs));
> for (i = 0; i < imaplen; i += 3 * sizeof(u32)) {
> soc_int = be32_to_cpup(imap);
> if (soc_int > 31)
> @@ -138,39 +175,41 @@ static int __init map_interrupts(struct device_node *node, struct irq_domain *do
> if (cpu_int > 7 || cpu_int < 2)
> return -EINVAL;
>
> - if (!(mips_irqs_set & BIT(cpu_int))) {
> - irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch,
> - domain);
> - mips_irqs_set |= BIT(cpu_int);
> + output = &realtek_ictl_outputs[cpu_int - 2];
> +
> + if (!output->routing_value) {
> + irq_set_chained_handler_and_data(cpu_int, realtek_irq_dispatch, output);
> + /* Use routing values (1..6) for CPU interrupts (2..7) */
> + output->routing_value = cpu_int - 1;
Why do you keep this routing_value around? Its only purpose is to be
read by set_routing(), which already checks for a programmed value.
M.
--
Without deviation from the norm, progress is not possible.
next prev parent reply other threads:[~2021-12-27 10:38 UTC|newest]
Thread overview: 19+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-12-26 19:59 [RFC PATCH v2 0/5] Rework realtek-rtl IRQ driver Sander Vanheule
2021-12-26 19:59 ` [RFC PATCH v2 1/5] irqchip/realtek-rtl: map control data to virq Sander Vanheule
2021-12-26 19:59 ` [RFC PATCH v2 2/5] irqchip/realtek-rtl: fix off-by-one in routing Sander Vanheule
2021-12-27 10:16 ` Marc Zyngier
2021-12-28 10:13 ` Sander Vanheule
2021-12-28 10:59 ` Marc Zyngier
2021-12-28 16:21 ` Sander Vanheule
2021-12-26 19:59 ` [RFC PATCH v2 3/5] irqchip/realtek-rtl: use per-parent irq handling Sander Vanheule
2021-12-27 10:38 ` Marc Zyngier [this message]
2021-12-26 19:59 ` [RFC PATCH v2 4/5] dt-bindings: interrupt-controller: realtek,rtl-intc: map output lines Sander Vanheule
2021-12-27 11:17 ` Marc Zyngier
2021-12-28 16:21 ` Sander Vanheule
2021-12-28 16:53 ` Birger Koblitz
2021-12-29 19:32 ` Sander Vanheule
2021-12-26 19:59 ` [RFC PATCH v2 5/5] irqchip/realtek-rtl: add explicit output routing Sander Vanheule
2021-12-27 9:06 ` [RFC PATCH v2 0/5] Rework realtek-rtl IRQ driver Birger Koblitz
2021-12-27 10:39 ` Sander Vanheule
2021-12-28 8:09 ` Birger Koblitz
2021-12-29 20:03 ` Sander Vanheule
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87sfuez61v.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=bert@biot.com \
--cc=devicetree@vger.kernel.org \
--cc=john@phrozen.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mail@birger-koblitz.de \
--cc=robh+dt@kernel.org \
--cc=sander@svanheule.net \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.