Message ID | 20220524172214.5104-3-prabhakar.mahadev-lad.rj@bp.renesas.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add PLIC support for Renesas RZ/Five SoC | expand |
Hi Prabhakar, On Tue, May 24, 2022 at 7:22 PM Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > edge until the previous completion message has been received and > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > interrupts if not acknowledged in time. > > So the workaround for edge-triggered interrupts to be handled correctly > and without losing is that it needs to be acknowledged first and then > handler must be run so that we don't miss on the next edge-triggered > interrupt. > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > support to change interrupt flow based on the interrupt type. It also > implements irq_ack and irq_set_type callbacks. > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Thanks for your patch! > --- a/drivers/irqchip/irq-sifive-plic.c > +++ b/drivers/irqchip/irq-sifive-plic.c > @@ -60,10 +60,13 @@ > #define PLIC_DISABLE_THRESHOLD 0x7 > #define PLIC_ENABLE_THRESHOLD 0 > > +#define RENESAS_R9A07G043_PLIC 1 > + > struct plic_priv { > struct cpumask lmask; > struct irq_domain *irqdomain; > void __iomem *regs; > + u8 of_data; Usually it's cleaner to use feature bits instead of enum types. > }; > > struct plic_handler { > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > } > #endif > > +static void plic_irq_ack(struct irq_data *d) > +{ > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > + No check for RZ/Five or irq type? .irq_ack() seems to be called for level interrupts, too (from handle_level_irq() through mask_ack_irq()). > + if (irqd_irq_masked(d)) { > + plic_irq_unmask(d); > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > + plic_irq_mask(d); > + } else { > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > + } > +} The above is identical to the old plic_irq_eoi()... > + > static void plic_irq_eoi(struct irq_data *d) > { > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > + /* > + * For Renesas R9A07G043 SoC if the interrupt type is EDGE > + * we have already acknowledged it in ack callback. > + */ > + if (handler->priv->of_data == RENESAS_R9A07G043_PLIC && > + !irqd_is_level_type(d)) > + return; > + ... so you can just call into plic_irq_ack() here? > if (irqd_irq_masked(d)) { > plic_irq_unmask(d); > writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) > } > } > > +static int plic_irq_set_type(struct irq_data *d, unsigned int type) > +{ > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > + > + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) > + return 0; > + > + switch (type) { > + case IRQ_TYPE_LEVEL_HIGH: > + irq_set_handler_locked(d, handle_fasteoi_irq); > + break; > + > + case IRQ_TYPE_EDGE_RISING: > + irq_set_handler_locked(d, handle_fasteoi_ack_irq); > + break; > + > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > static struct irq_chip plic_chip = { I think this can be const. > .name = "SiFive PLIC", > .irq_mask = plic_irq_mask, > .irq_unmask = plic_irq_unmask, > + .irq_ack = plic_irq_ack, This causes extra processing on non-affected PLICs. Perhaps use a separate irq_chip instance? > .irq_eoi = plic_irq_eoi, > + .irq_set_type = plic_irq_set_type, > + > #ifdef CONFIG_SMP > .irq_set_affinity = plic_set_affinity, > #endif > @@ -198,6 +248,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq, > return 0; > } > > +static int plic_irq_domain_translate(struct irq_domain *d, > + struct irq_fwspec *fwspec, > + unsigned long *hwirq, > + unsigned int *type) > +{ > + struct plic_priv *priv = d->host_data; > + > + if (priv->of_data == RENESAS_R9A07G043_PLIC) > + return irq_domain_translate_twocell(d, fwspec, hwirq, type); > + > + return irq_domain_translate_onecell(d, fwspec, hwirq, type); This one clearly shows the discerning feature: onecell or twocell... > +} > + > static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > unsigned int nr_irqs, void *arg) > { > @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, > if (!priv) > return -ENOMEM; > > + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) > + priv->of_data = RENESAS_R9A07G043_PLIC; > + So perhaps instead just look at #interrupt-cells, and use the onecell or twocell irq_chip/irq_domain_ops based on that? > priv->regs = of_iomap(node, 0); > if (WARN_ON(!priv->regs)) { > error = -EIO; Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi Geert, Thank you for the review. On Wed, May 25, 2022 at 9:01 AM Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > Hi Prabhakar, > > On Tue, May 24, 2022 at 7:22 PM Lad Prabhakar > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > edge until the previous completion message has been received and > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > interrupts if not acknowledged in time. > > > > So the workaround for edge-triggered interrupts to be handled correctly > > and without losing is that it needs to be acknowledged first and then > > handler must be run so that we don't miss on the next edge-triggered > > interrupt. > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > support to change interrupt flow based on the interrupt type. It also > > implements irq_ack and irq_set_type callbacks. > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > Thanks for your patch! > > > --- a/drivers/irqchip/irq-sifive-plic.c > > +++ b/drivers/irqchip/irq-sifive-plic.c > > @@ -60,10 +60,13 @@ > > #define PLIC_DISABLE_THRESHOLD 0x7 > > #define PLIC_ENABLE_THRESHOLD 0 > > > > +#define RENESAS_R9A07G043_PLIC 1 > > + > > struct plic_priv { > > struct cpumask lmask; > > struct irq_domain *irqdomain; > > void __iomem *regs; > > + u8 of_data; > > Usually it's cleaner to use feature bits instead of enum types. > Agreed. > > }; > > > > struct plic_handler { > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > } > > #endif > > > > +static void plic_irq_ack(struct irq_data *d) > > +{ > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > + > > No check for RZ/Five or irq type? That is because we set the handle_fasteoi_ack_irq() only in case of RZ/Five and it is already checked in set_type() callback. > .irq_ack() seems to be called for level interrupts, too > (from handle_level_irq() through mask_ack_irq()). > Right but we are using handle_fasteoi_irq() for level interrupt which doesn't call mask_ack_irq(). And I have confirmed by adding a print in ack callback and just enabling the serial (which has level interrupts). > > + if (irqd_irq_masked(d)) { > > + plic_irq_unmask(d); > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > + plic_irq_mask(d); > > + } else { > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > + } > > +} > > The above is identical to the old plic_irq_eoi()... > Indeed.. > > + > > static void plic_irq_eoi(struct irq_data *d) > > { > > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > > + /* > > + * For Renesas R9A07G043 SoC if the interrupt type is EDGE > > + * we have already acknowledged it in ack callback. > > + */ > > + if (handler->priv->of_data == RENESAS_R9A07G043_PLIC && > > + !irqd_is_level_type(d)) > > + return; > > + > > ... so you can just call into plic_irq_ack() here? > ... yes it can be done. > > if (irqd_irq_masked(d)) { > > plic_irq_unmask(d); > > writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) > > } > > } > > > > +static int plic_irq_set_type(struct irq_data *d, unsigned int type) > > +{ > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > + > > + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) > > + return 0; > > + > > + switch (type) { > > + case IRQ_TYPE_LEVEL_HIGH: > > + irq_set_handler_locked(d, handle_fasteoi_irq); > > + break; > > + > > + case IRQ_TYPE_EDGE_RISING: > > + irq_set_handler_locked(d, handle_fasteoi_ack_irq); > > + break; > > + > > + default: > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > static struct irq_chip plic_chip = { > > I think this can be const. > Yes, I will update it. > > .name = "SiFive PLIC", > > .irq_mask = plic_irq_mask, > > .irq_unmask = plic_irq_unmask, > > + .irq_ack = plic_irq_ack, > > This causes extra processing on non-affected PLICs. > Perhaps use a separate irq_chip instance? > I don't think so as the handle_fasteoi_ack_irq() is installed only in case of RZ/Five, so irq_ack() will not be called for non-affected PLIC's. Please correct me if I am wrong. > > .irq_eoi = plic_irq_eoi, > > + .irq_set_type = plic_irq_set_type, > > + > > #ifdef CONFIG_SMP > > .irq_set_affinity = plic_set_affinity, > > #endif > > @@ -198,6 +248,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq, > > return 0; > > } > > > > +static int plic_irq_domain_translate(struct irq_domain *d, > > + struct irq_fwspec *fwspec, > > + unsigned long *hwirq, > > + unsigned int *type) > > +{ > > + struct plic_priv *priv = d->host_data; > > + > > + if (priv->of_data == RENESAS_R9A07G043_PLIC) > > + return irq_domain_translate_twocell(d, fwspec, hwirq, type); > > + > > + return irq_domain_translate_onecell(d, fwspec, hwirq, type); > > This one clearly shows the discerning feature: onecell or twocell... > > > +} > > + > > static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > > unsigned int nr_irqs, void *arg) > > { > > > @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, > > if (!priv) > > return -ENOMEM; > > > > + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) > > + priv->of_data = RENESAS_R9A07G043_PLIC; > > + > > So perhaps instead just look at #interrupt-cells, and use the onecell > or twocell irq_chip/irq_domain_ops based on that? > But we do call plic_irq_domain_translate() in the alloc callback and don't have a node pointer in there to check the interrupt cell count. Or maybe we can store the interrupt cell count in priv and use it accordingly above? Cheers, Prabhakar > > priv->regs = of_iomap(node, 0); > > if (WARN_ON(!priv->regs)) { > > error = -EIO; > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds
Hi Prabhakar, On Wed, May 25, 2022 at 11:01 AM Lad, Prabhakar <prabhakar.csengg@gmail.com> wrote: > On Wed, May 25, 2022 at 9:01 AM Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > On Tue, May 24, 2022 at 7:22 PM Lad Prabhakar > > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > > edge until the previous completion message has been received and > > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > > interrupts if not acknowledged in time. > > > > > > So the workaround for edge-triggered interrupts to be handled correctly > > > and without losing is that it needs to be acknowledged first and then > > > handler must be run so that we don't miss on the next edge-triggered > > > interrupt. > > > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > > support to change interrupt flow based on the interrupt type. It also > > > implements irq_ack and irq_set_type callbacks. > > > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > > > Thanks for your patch! > > > > > --- a/drivers/irqchip/irq-sifive-plic.c > > > +++ b/drivers/irqchip/irq-sifive-plic.c > > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > > } > > > #endif > > > > > > +static void plic_irq_ack(struct irq_data *d) > > > +{ > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > + > > > > No check for RZ/Five or irq type? > That is because we set the handle_fasteoi_ack_irq() only in case of > RZ/Five and it is already checked in set_type() callback. > > > .irq_ack() seems to be called for level interrupts, too > > (from handle_level_irq() through mask_ack_irq()). > > > Right but we are using handle_fasteoi_irq() for level interrupt which > doesn't call mask_ack_irq(). And I have confirmed by adding a print in > ack callback and just enabling the serial (which has level > interrupts). But handle_fasteoi_irq() is configured only on RZ/Five below? Which handler is used on non-RZ/Five? I have to admit I'm not that deep into irq handling, and adding a print indeed doesn't trigger on Starlight Beta. > > > @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) > > > } > > > } > > > > > > +static int plic_irq_set_type(struct irq_data *d, unsigned int type) > > > +{ > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > + > > > + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) > > > + return 0; > > > + > > > + switch (type) { > > > + case IRQ_TYPE_LEVEL_HIGH: > > > + irq_set_handler_locked(d, handle_fasteoi_irq); > > > + break; > > > + > > > + case IRQ_TYPE_EDGE_RISING: > > > + irq_set_handler_locked(d, handle_fasteoi_ack_irq); > > > + break; > > > + > > > + default: > > > + return -EINVAL; > > > + } > > > + > > > + return 0; > > > +} > > > + > > > static struct irq_chip plic_chip = { > > > .name = "SiFive PLIC", > > > .irq_mask = plic_irq_mask, > > > .irq_unmask = plic_irq_unmask, > > > + .irq_ack = plic_irq_ack, > > > > This causes extra processing on non-affected PLICs. > > Perhaps use a separate irq_chip instance? > > > I don't think so as the handle_fasteoi_ack_irq() is installed only in > case of RZ/Five, so irq_ack() will not be called for non-affected > PLIC's. Please correct me if I am wrong. Hence I'll leave this to the irq maintainer... > > > @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, > > > if (!priv) > > > return -ENOMEM; > > > > > > + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) > > > + priv->of_data = RENESAS_R9A07G043_PLIC; > > > + > > > > So perhaps instead just look at #interrupt-cells, and use the onecell > > or twocell irq_chip/irq_domain_ops based on that? > > > But we do call plic_irq_domain_translate() in the alloc callback and > don't have a node pointer in there to check the interrupt cell count. > Or maybe we can store the interrupt cell count in priv and use it > accordingly above? That's a reasonable option. Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi Geert, On Wed, May 25, 2022 at 10:35 AM Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > Hi Prabhakar, > > On Wed, May 25, 2022 at 11:01 AM Lad, Prabhakar > <prabhakar.csengg@gmail.com> wrote: > > On Wed, May 25, 2022 at 9:01 AM Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > > On Tue, May 24, 2022 at 7:22 PM Lad Prabhakar > > > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > > > edge until the previous completion message has been received and > > > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > > > interrupts if not acknowledged in time. > > > > > > > > So the workaround for edge-triggered interrupts to be handled correctly > > > > and without losing is that it needs to be acknowledged first and then > > > > handler must be run so that we don't miss on the next edge-triggered > > > > interrupt. > > > > > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > > > support to change interrupt flow based on the interrupt type. It also > > > > implements irq_ack and irq_set_type callbacks. > > > > > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > > > > > Thanks for your patch! > > > > > > > --- a/drivers/irqchip/irq-sifive-plic.c > > > > +++ b/drivers/irqchip/irq-sifive-plic.c > > > > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > > > } > > > > #endif > > > > > > > > +static void plic_irq_ack(struct irq_data *d) > > > > +{ > > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > > + > > > > > > No check for RZ/Five or irq type? > > That is because we set the handle_fasteoi_ack_irq() only in case of > > RZ/Five and it is already checked in set_type() callback. > > > > > .irq_ack() seems to be called for level interrupts, too > > > (from handle_level_irq() through mask_ack_irq()). > > > > > Right but we are using handle_fasteoi_irq() for level interrupt which > > doesn't call mask_ack_irq(). And I have confirmed by adding a print in > > ack callback and just enabling the serial (which has level > > interrupts). > > But handle_fasteoi_irq() is configured only on RZ/Five below? > Which handler is used on non-RZ/Five? > For non RZ/Five, handle_fasteoi_irq() [0] is used for both edge/level interrupts. [0] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/irqchip/irq-sifive-plic.c?h=next-20220525#n195 > I have to admit I'm not that deep into irq handling, and > adding a print indeed doesn't trigger on Starlight Beta. > > > > > @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) > > > > } > > > > } > > > > > > > > +static int plic_irq_set_type(struct irq_data *d, unsigned int type) > > > > +{ > > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > > + > > > > + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) > > > > + return 0; > > > > + > > > > + switch (type) { > > > > + case IRQ_TYPE_LEVEL_HIGH: > > > > + irq_set_handler_locked(d, handle_fasteoi_irq); > > > > + break; > > > > + > > > > + case IRQ_TYPE_EDGE_RISING: > > > > + irq_set_handler_locked(d, handle_fasteoi_ack_irq); > > > > + break; > > > > + > > > > + default: > > > > + return -EINVAL; > > > > + } > > > > + > > > > + return 0; > > > > +} > > > > + > > > > static struct irq_chip plic_chip = { > > > > .name = "SiFive PLIC", > > > > .irq_mask = plic_irq_mask, > > > > .irq_unmask = plic_irq_unmask, > > > > + .irq_ack = plic_irq_ack, > > > > > > This causes extra processing on non-affected PLICs. > > > Perhaps use a separate irq_chip instance? > > > > > I don't think so as the handle_fasteoi_ack_irq() is installed only in > > case of RZ/Five, so irq_ack() will not be called for non-affected > > PLIC's. Please correct me if I am wrong. > > Hence I'll leave this to the irq maintainer... > > > > > @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, > > > > if (!priv) > > > > return -ENOMEM; > > > > > > > > + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) > > > > + priv->of_data = RENESAS_R9A07G043_PLIC; > > > > + > > > > > > So perhaps instead just look at #interrupt-cells, and use the onecell > > > or twocell irq_chip/irq_domain_ops based on that? > > > > > But we do call plic_irq_domain_translate() in the alloc callback and > > don't have a node pointer in there to check the interrupt cell count. > > Or maybe we can store the interrupt cell count in priv and use it > > accordingly above? > > That's a reasonable option. > Ok I will update this in v2. Cheers, Prabhakar > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds
Hi Prabhakar, On Wed, May 25, 2022 at 11:43 AM Lad, Prabhakar <prabhakar.csengg@gmail.com> wrote: > On Wed, May 25, 2022 at 10:35 AM Geert Uytterhoeven > <geert@linux-m68k.org> wrote: > > On Wed, May 25, 2022 at 11:01 AM Lad, Prabhakar > > <prabhakar.csengg@gmail.com> wrote: > > > On Wed, May 25, 2022 at 9:01 AM Geert Uytterhoeven <geert@linux-m68k.org> wrote: > > > > On Tue, May 24, 2022 at 7:22 PM Lad Prabhakar > > > > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > > > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > > > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > > > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > > > > edge until the previous completion message has been received and > > > > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > > > > interrupts if not acknowledged in time. > > > > > > > > > > So the workaround for edge-triggered interrupts to be handled correctly > > > > > and without losing is that it needs to be acknowledged first and then > > > > > handler must be run so that we don't miss on the next edge-triggered > > > > > interrupt. > > > > > > > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > > > > support to change interrupt flow based on the interrupt type. It also > > > > > implements irq_ack and irq_set_type callbacks. > > > > > > > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > > > > > > > Thanks for your patch! > > > > > > > > > --- a/drivers/irqchip/irq-sifive-plic.c > > > > > +++ b/drivers/irqchip/irq-sifive-plic.c > > > > > > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > > > > } > > > > > #endif > > > > > > > > > > +static void plic_irq_ack(struct irq_data *d) > > > > > +{ > > > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > > > + > > > > > > > > No check for RZ/Five or irq type? > > > That is because we set the handle_fasteoi_ack_irq() only in case of > > > RZ/Five and it is already checked in set_type() callback. > > > > > > > .irq_ack() seems to be called for level interrupts, too > > > > (from handle_level_irq() through mask_ack_irq()). > > > > > > > Right but we are using handle_fasteoi_irq() for level interrupt which > > > doesn't call mask_ack_irq(). And I have confirmed by adding a print in > > > ack callback and just enabling the serial (which has level > > > interrupts). > > > > But handle_fasteoi_irq() is configured only on RZ/Five below? > > Which handler is used on non-RZ/Five? > > > For non RZ/Five, handle_fasteoi_irq() [0] is used for both edge/level > interrupts. > > [0] https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/irqchip/irq-sifive-plic.c?h=next-20220525#n195 Thanks, that was the missing piece! Due to the new "select IRQ_FASTEOI_HIERARCHY_HANDLERS", I thought your new call to handle_fasteoi_irq() had to be the first one in this file... But that config symbol protects handle_fasteoi_ack_irq(), not handle_fasteoi_irq(). Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi, On Tue, May 24, 2022 at 6:22 PM Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > edge until the previous completion message has been received and > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > interrupts if not acknowledged in time. > > So the workaround for edge-triggered interrupts to be handled correctly > and without losing is that it needs to be acknowledged first and then > handler must be run so that we don't miss on the next edge-triggered > interrupt. > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > support to change interrupt flow based on the interrupt type. It also > implements irq_ack and irq_set_type callbacks. > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > --- > drivers/irqchip/Kconfig | 1 + > drivers/irqchip/irq-sifive-plic.c | 71 ++++++++++++++++++++++++++++++- > 2 files changed, 70 insertions(+), 2 deletions(-) > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > index f3d071422f3b..aea0e4e7e547 100644 > --- a/drivers/irqchip/Kconfig > +++ b/drivers/irqchip/Kconfig > @@ -537,6 +537,7 @@ config SIFIVE_PLIC > bool "SiFive Platform-Level Interrupt Controller" > depends on RISCV > select IRQ_DOMAIN_HIERARCHY > + select IRQ_FASTEOI_HIERARCHY_HANDLERS > help > This enables support for the PLIC chip found in SiFive (and > potentially other) RISC-V systems. The PLIC controls devices > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c > index bb87e4c3b88e..abffce48e69c 100644 > --- a/drivers/irqchip/irq-sifive-plic.c > +++ b/drivers/irqchip/irq-sifive-plic.c > @@ -60,10 +60,13 @@ > #define PLIC_DISABLE_THRESHOLD 0x7 > #define PLIC_ENABLE_THRESHOLD 0 > > +#define RENESAS_R9A07G043_PLIC 1 > + > struct plic_priv { > struct cpumask lmask; > struct irq_domain *irqdomain; > void __iomem *regs; > + u8 of_data; > }; > > struct plic_handler { > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > } > #endif > > +static void plic_irq_ack(struct irq_data *d) > +{ > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > + > + if (irqd_irq_masked(d)) { > + plic_irq_unmask(d); > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > + plic_irq_mask(d); > + } else { > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > + } > +} > + I sometimes still see an interrupt miss! As per [0], we first need to claim the interrupt by reading the claim register which needs to be done in the ack callback (which should be doable) for edge interrupts, but the problem arises in the chained handler callback where it does claim the interrupt by reading the claim register. static void plic_handle_irq(struct irq_desc *desc) { struct plic_handler *handler = this_cpu_ptr(&plic_handlers); struct irq_chip *chip = irq_desc_get_chip(desc); void __iomem *claim = handler->hart_base + CONTEXT_CLAIM; irq_hw_number_t hwirq; WARN_ON_ONCE(!handler->present); chained_irq_enter(chip, desc); while ((hwirq = readl(claim))) { int err = generic_handle_domain_irq(handler->priv->irqdomain, hwirq); if (unlikely(err)) pr_warn_ratelimited("can't find mapping for hwirq %lu\n", hwirq); } chained_irq_exit(chip, desc); } I was thinking I would get around by getting the irqdata in plic_handle_irq() callback using the irq_desc (struct irq_data *d = &desc->irq_data;) and check the d->hwirq but this will be always 9. plic: interrupt-controller@12c00000 { compatible = "renesas-r9a07g043-plic"; #interrupt-cells = <2>; #address-cells = <0>; riscv,ndev = <543>; interrupt-controller; reg = <0x0 0x12c00000 0 0x400000>; clocks = <&cpg CPG_MOD R9A07G043_NCEPLIC_ACLK>; clock-names = "plic100ss"; power-domains = <&cpg>; resets = <&cpg R9A07G043_NCEPLIC_ARESETN>; interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9>; }; Any pointers on how this could be done sanely. [0] https://github.com/riscv/riscv-plic-spec/blob/master/images/PLICInterruptFlow.jpg Cheers, Prabhakar > static void plic_irq_eoi(struct irq_data *d) > { > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > + /* > + * For Renesas R9A07G043 SoC if the interrupt type is EDGE > + * we have already acknowledged it in ack callback. > + */ > + if (handler->priv->of_data == RENESAS_R9A07G043_PLIC && > + !irqd_is_level_type(d)) > + return; > + > if (irqd_irq_masked(d)) { > plic_irq_unmask(d); > writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) > } > } > > +static int plic_irq_set_type(struct irq_data *d, unsigned int type) > +{ > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > + > + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) > + return 0; > + > + switch (type) { > + case IRQ_TYPE_LEVEL_HIGH: > + irq_set_handler_locked(d, handle_fasteoi_irq); > + break; > + > + case IRQ_TYPE_EDGE_RISING: > + irq_set_handler_locked(d, handle_fasteoi_ack_irq); > + break; > + > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > static struct irq_chip plic_chip = { > .name = "SiFive PLIC", > .irq_mask = plic_irq_mask, > .irq_unmask = plic_irq_unmask, > + .irq_ack = plic_irq_ack, > .irq_eoi = plic_irq_eoi, > + .irq_set_type = plic_irq_set_type, > + > #ifdef CONFIG_SMP > .irq_set_affinity = plic_set_affinity, > #endif > @@ -198,6 +248,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq, > return 0; > } > > +static int plic_irq_domain_translate(struct irq_domain *d, > + struct irq_fwspec *fwspec, > + unsigned long *hwirq, > + unsigned int *type) > +{ > + struct plic_priv *priv = d->host_data; > + > + if (priv->of_data == RENESAS_R9A07G043_PLIC) > + return irq_domain_translate_twocell(d, fwspec, hwirq, type); > + > + return irq_domain_translate_onecell(d, fwspec, hwirq, type); > +} > + > static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > unsigned int nr_irqs, void *arg) > { > @@ -206,7 +269,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > unsigned int type; > struct irq_fwspec *fwspec = arg; > > - ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); > + ret = plic_irq_domain_translate(domain, fwspec, &hwirq, &type); > if (ret) > return ret; > > @@ -220,7 +283,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > } > > static const struct irq_domain_ops plic_irqdomain_ops = { > - .translate = irq_domain_translate_onecell, > + .translate = plic_irq_domain_translate, > .alloc = plic_irq_domain_alloc, > .free = irq_domain_free_irqs_top, > }; > @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, > if (!priv) > return -ENOMEM; > > + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) > + priv->of_data = RENESAS_R9A07G043_PLIC; > + > priv->regs = of_iomap(node, 0); > if (WARN_ON(!priv->regs)) { > error = -EIO; > @@ -411,5 +477,6 @@ static int __init plic_init(struct device_node *node, > } > > IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init); > +IRQCHIP_DECLARE(renesas_r9a07g043_plic, "renesas-r9a07g043-plic", plic_init); > IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */ > IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init); /* for firmware driver */ > -- > 2.25.1 >
On Fri, 27 May 2022 12:05:38 +0100, "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote: > > Hi, > > On Tue, May 24, 2022 at 6:22 PM Lad Prabhakar > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > edge until the previous completion message has been received and > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > interrupts if not acknowledged in time. > > > > So the workaround for edge-triggered interrupts to be handled correctly > > and without losing is that it needs to be acknowledged first and then > > handler must be run so that we don't miss on the next edge-triggered > > interrupt. > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > support to change interrupt flow based on the interrupt type. It also > > implements irq_ack and irq_set_type callbacks. > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > --- > > drivers/irqchip/Kconfig | 1 + > > drivers/irqchip/irq-sifive-plic.c | 71 ++++++++++++++++++++++++++++++- > > 2 files changed, 70 insertions(+), 2 deletions(-) > > > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > > index f3d071422f3b..aea0e4e7e547 100644 > > --- a/drivers/irqchip/Kconfig > > +++ b/drivers/irqchip/Kconfig > > @@ -537,6 +537,7 @@ config SIFIVE_PLIC > > bool "SiFive Platform-Level Interrupt Controller" > > depends on RISCV > > select IRQ_DOMAIN_HIERARCHY > > + select IRQ_FASTEOI_HIERARCHY_HANDLERS > > help > > This enables support for the PLIC chip found in SiFive (and > > potentially other) RISC-V systems. The PLIC controls devices > > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c > > index bb87e4c3b88e..abffce48e69c 100644 > > --- a/drivers/irqchip/irq-sifive-plic.c > > +++ b/drivers/irqchip/irq-sifive-plic.c > > @@ -60,10 +60,13 @@ > > #define PLIC_DISABLE_THRESHOLD 0x7 > > #define PLIC_ENABLE_THRESHOLD 0 > > > > +#define RENESAS_R9A07G043_PLIC 1 > > + > > struct plic_priv { > > struct cpumask lmask; > > struct irq_domain *irqdomain; > > void __iomem *regs; > > + u8 of_data; > > }; > > > > struct plic_handler { > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > } > > #endif > > > > +static void plic_irq_ack(struct irq_data *d) > > +{ > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > + > > + if (irqd_irq_masked(d)) { > > + plic_irq_unmask(d); > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > + plic_irq_mask(d); > > + } else { > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > + } > > +} > > + > I sometimes still see an interrupt miss! > > As per [0], we first need to claim the interrupt by reading the claim > register which needs to be done in the ack callback (which should be > doable) for edge interrupts, but the problem arises in the chained > handler callback where it does claim the interrupt by reading the > claim register. > > static void plic_handle_irq(struct irq_desc *desc) > { > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > struct irq_chip *chip = irq_desc_get_chip(desc); > void __iomem *claim = handler->hart_base + CONTEXT_CLAIM; > irq_hw_number_t hwirq; > > WARN_ON_ONCE(!handler->present); > > chained_irq_enter(chip, desc); > > while ((hwirq = readl(claim))) { > int err = generic_handle_domain_irq(handler->priv->irqdomain, > hwirq); > if (unlikely(err)) > pr_warn_ratelimited("can't find mapping for hwirq %lu\n", > hwirq); > } > > chained_irq_exit(chip, desc); > } > > I was thinking I would get around by getting the irqdata in > plic_handle_irq() callback using the irq_desc (struct irq_data *d = > &desc->irq_data;) and check the d->hwirq but this will be always 9. > > plic: interrupt-controller@12c00000 { > compatible = "renesas-r9a07g043-plic"; > #interrupt-cells = <2>; > #address-cells = <0>; > riscv,ndev = <543>; > interrupt-controller; > reg = <0x0 0x12c00000 0 0x400000>; > clocks = <&cpg CPG_MOD R9A07G043_NCEPLIC_ACLK>; > clock-names = "plic100ss"; > power-domains = <&cpg>; > resets = <&cpg R9A07G043_NCEPLIC_ARESETN>; > interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9>; > }; > > Any pointers on how this could be done sanely. Why doesn't the chained interrupt also get the ack-aware irq_chip? M.
Hi Marc, On Mon, Jun 6, 2022 at 4:41 PM Marc Zyngier <maz@kernel.org> wrote: > > On Fri, 27 May 2022 12:05:38 +0100, > "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote: > > > > Hi, > > > > On Tue, May 24, 2022 at 6:22 PM Lad Prabhakar > > <prabhakar.mahadev-lad.rj@bp.renesas.com> wrote: > > > > > > The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The > > > NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In > > > case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt > > > edge until the previous completion message has been received and > > > NCEPLIC100 doesn't support pending interrupt counter, hence losing the > > > interrupts if not acknowledged in time. > > > > > > So the workaround for edge-triggered interrupts to be handled correctly > > > and without losing is that it needs to be acknowledged first and then > > > handler must be run so that we don't miss on the next edge-triggered > > > interrupt. > > > > > > This patch adds a new compatible string for Renesas RZ/Five SoC and adds > > > support to change interrupt flow based on the interrupt type. It also > > > implements irq_ack and irq_set_type callbacks. > > > > > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> > > > --- > > > drivers/irqchip/Kconfig | 1 + > > > drivers/irqchip/irq-sifive-plic.c | 71 ++++++++++++++++++++++++++++++- > > > 2 files changed, 70 insertions(+), 2 deletions(-) > > > > > > diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig > > > index f3d071422f3b..aea0e4e7e547 100644 > > > --- a/drivers/irqchip/Kconfig > > > +++ b/drivers/irqchip/Kconfig > > > @@ -537,6 +537,7 @@ config SIFIVE_PLIC > > > bool "SiFive Platform-Level Interrupt Controller" > > > depends on RISCV > > > select IRQ_DOMAIN_HIERARCHY > > > + select IRQ_FASTEOI_HIERARCHY_HANDLERS > > > help > > > This enables support for the PLIC chip found in SiFive (and > > > potentially other) RISC-V systems. The PLIC controls devices > > > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c > > > index bb87e4c3b88e..abffce48e69c 100644 > > > --- a/drivers/irqchip/irq-sifive-plic.c > > > +++ b/drivers/irqchip/irq-sifive-plic.c > > > @@ -60,10 +60,13 @@ > > > #define PLIC_DISABLE_THRESHOLD 0x7 > > > #define PLIC_ENABLE_THRESHOLD 0 > > > > > > +#define RENESAS_R9A07G043_PLIC 1 > > > + > > > struct plic_priv { > > > struct cpumask lmask; > > > struct irq_domain *irqdomain; > > > void __iomem *regs; > > > + u8 of_data; > > > }; > > > > > > struct plic_handler { > > > @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, > > > } > > > #endif > > > > > > +static void plic_irq_ack(struct irq_data *d) > > > +{ > > > + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > + > > > + if (irqd_irq_masked(d)) { > > > + plic_irq_unmask(d); > > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > > + plic_irq_mask(d); > > > + } else { > > > + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); > > > + } > > > +} > > > + > > I sometimes still see an interrupt miss! > > > > As per [0], we first need to claim the interrupt by reading the claim > > register which needs to be done in the ack callback (which should be > > doable) for edge interrupts, but the problem arises in the chained > > handler callback where it does claim the interrupt by reading the > > claim register. > > > > static void plic_handle_irq(struct irq_desc *desc) > > { > > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > struct irq_chip *chip = irq_desc_get_chip(desc); > > void __iomem *claim = handler->hart_base + CONTEXT_CLAIM; > > irq_hw_number_t hwirq; > > > > WARN_ON_ONCE(!handler->present); > > > > chained_irq_enter(chip, desc); > > > > while ((hwirq = readl(claim))) { > > int err = generic_handle_domain_irq(handler->priv->irqdomain, > > hwirq); > > if (unlikely(err)) > > pr_warn_ratelimited("can't find mapping for hwirq %lu\n", > > hwirq); > > } > > > > chained_irq_exit(chip, desc); > > } > > > > I was thinking I would get around by getting the irqdata in > > plic_handle_irq() callback using the irq_desc (struct irq_data *d = > > &desc->irq_data;) and check the d->hwirq but this will be always 9. > > > > plic: interrupt-controller@12c00000 { > > compatible = "renesas-r9a07g043-plic"; > > #interrupt-cells = <2>; > > #address-cells = <0>; > > riscv,ndev = <543>; > > interrupt-controller; > > reg = <0x0 0x12c00000 0 0x400000>; > > clocks = <&cpg CPG_MOD R9A07G043_NCEPLIC_ACLK>; > > clock-names = "plic100ss"; > > power-domains = <&cpg>; > > resets = <&cpg R9A07G043_NCEPLIC_ARESETN>; > > interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9>; > > }; > > > > Any pointers on how this could be done sanely. > > Why doesn't the chained interrupt also get the ack-aware irq_chip? > Sorry for being naive, could you please elaborate on this. Cheers, Prabhakar > M. > > -- > Without deviation from the norm, progress is not possible.
On Tue, 07 Jun 2022 13:41:16 +0100, "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote: > > Hi Marc, > > On Mon, Jun 6, 2022 at 4:41 PM Marc Zyngier <maz@kernel.org> wrote: > > > > On Fri, 27 May 2022 12:05:38 +0100, > > "Lad, Prabhakar" <prabhakar.csengg@gmail.com> wrote: > > > > > > I sometimes still see an interrupt miss! > > > > > > As per [0], we first need to claim the interrupt by reading the claim > > > register which needs to be done in the ack callback (which should be > > > doable) for edge interrupts, but the problem arises in the chained > > > handler callback where it does claim the interrupt by reading the > > > claim register. > > > > > > static void plic_handle_irq(struct irq_desc *desc) > > > { > > > struct plic_handler *handler = this_cpu_ptr(&plic_handlers); > > > struct irq_chip *chip = irq_desc_get_chip(desc); > > > void __iomem *claim = handler->hart_base + CONTEXT_CLAIM; > > > irq_hw_number_t hwirq; > > > > > > WARN_ON_ONCE(!handler->present); > > > > > > chained_irq_enter(chip, desc); > > > > > > while ((hwirq = readl(claim))) { > > > int err = generic_handle_domain_irq(handler->priv->irqdomain, > > > hwirq); > > > if (unlikely(err)) > > > pr_warn_ratelimited("can't find mapping for hwirq %lu\n", > > > hwirq); > > > } > > > > > > chained_irq_exit(chip, desc); > > > } > > > > > > I was thinking I would get around by getting the irqdata in > > > plic_handle_irq() callback using the irq_desc (struct irq_data *d = > > > &desc->irq_data;) and check the d->hwirq but this will be always 9. > > > > > > plic: interrupt-controller@12c00000 { > > > compatible = "renesas-r9a07g043-plic"; > > > #interrupt-cells = <2>; > > > #address-cells = <0>; > > > riscv,ndev = <543>; > > > interrupt-controller; > > > reg = <0x0 0x12c00000 0 0x400000>; > > > clocks = <&cpg CPG_MOD R9A07G043_NCEPLIC_ACLK>; > > > clock-names = "plic100ss"; > > > power-domains = <&cpg>; > > > resets = <&cpg R9A07G043_NCEPLIC_ARESETN>; > > > interrupts-extended = <&cpu0_intc 11 &cpu0_intc 9>; > > > }; > > > > > > Any pointers on how this could be done sanely. > > > > Why doesn't the chained interrupt also get the ack-aware irq_chip? > > > Sorry for being naive, could you please elaborate on this. There are two main reasons why the above code fails: these interrupts are not using either - the irqchip you think they are using (which one then?), - the interrupt flow they should be using. Dumping /sys/kernel/debug/irq/irqs/$IRQ should give you a clue. Thanks, M.
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index f3d071422f3b..aea0e4e7e547 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -537,6 +537,7 @@ config SIFIVE_PLIC bool "SiFive Platform-Level Interrupt Controller" depends on RISCV select IRQ_DOMAIN_HIERARCHY + select IRQ_FASTEOI_HIERARCHY_HANDLERS help This enables support for the PLIC chip found in SiFive (and potentially other) RISC-V systems. The PLIC controls devices diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index bb87e4c3b88e..abffce48e69c 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -60,10 +60,13 @@ #define PLIC_DISABLE_THRESHOLD 0x7 #define PLIC_ENABLE_THRESHOLD 0 +#define RENESAS_R9A07G043_PLIC 1 + struct plic_priv { struct cpumask lmask; struct irq_domain *irqdomain; void __iomem *regs; + u8 of_data; }; struct plic_handler { @@ -163,10 +166,31 @@ static int plic_set_affinity(struct irq_data *d, } #endif +static void plic_irq_ack(struct irq_data *d) +{ + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); + + if (irqd_irq_masked(d)) { + plic_irq_unmask(d); + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); + plic_irq_mask(d); + } else { + writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); + } +} + static void plic_irq_eoi(struct irq_data *d) { struct plic_handler *handler = this_cpu_ptr(&plic_handlers); + /* + * For Renesas R9A07G043 SoC if the interrupt type is EDGE + * we have already acknowledged it in ack callback. + */ + if (handler->priv->of_data == RENESAS_R9A07G043_PLIC && + !irqd_is_level_type(d)) + return; + if (irqd_irq_masked(d)) { plic_irq_unmask(d); writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM); @@ -176,11 +200,37 @@ static void plic_irq_eoi(struct irq_data *d) } } +static int plic_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct plic_handler *handler = this_cpu_ptr(&plic_handlers); + + if (handler->priv->of_data != RENESAS_R9A07G043_PLIC) + return 0; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler_locked(d, handle_fasteoi_irq); + break; + + case IRQ_TYPE_EDGE_RISING: + irq_set_handler_locked(d, handle_fasteoi_ack_irq); + break; + + default: + return -EINVAL; + } + + return 0; +} + static struct irq_chip plic_chip = { .name = "SiFive PLIC", .irq_mask = plic_irq_mask, .irq_unmask = plic_irq_unmask, + .irq_ack = plic_irq_ack, .irq_eoi = plic_irq_eoi, + .irq_set_type = plic_irq_set_type, + #ifdef CONFIG_SMP .irq_set_affinity = plic_set_affinity, #endif @@ -198,6 +248,19 @@ static int plic_irqdomain_map(struct irq_domain *d, unsigned int irq, return 0; } +static int plic_irq_domain_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + unsigned long *hwirq, + unsigned int *type) +{ + struct plic_priv *priv = d->host_data; + + if (priv->of_data == RENESAS_R9A07G043_PLIC) + return irq_domain_translate_twocell(d, fwspec, hwirq, type); + + return irq_domain_translate_onecell(d, fwspec, hwirq, type); +} + static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int nr_irqs, void *arg) { @@ -206,7 +269,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, unsigned int type; struct irq_fwspec *fwspec = arg; - ret = irq_domain_translate_onecell(domain, fwspec, &hwirq, &type); + ret = plic_irq_domain_translate(domain, fwspec, &hwirq, &type); if (ret) return ret; @@ -220,7 +283,7 @@ static int plic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, } static const struct irq_domain_ops plic_irqdomain_ops = { - .translate = irq_domain_translate_onecell, + .translate = plic_irq_domain_translate, .alloc = plic_irq_domain_alloc, .free = irq_domain_free_irqs_top, }; @@ -293,6 +356,9 @@ static int __init plic_init(struct device_node *node, if (!priv) return -ENOMEM; + if (of_device_is_compatible(node, "renesas-r9a07g043-plic")) + priv->of_data = RENESAS_R9A07G043_PLIC; + priv->regs = of_iomap(node, 0); if (WARN_ON(!priv->regs)) { error = -EIO; @@ -411,5 +477,6 @@ static int __init plic_init(struct device_node *node, } IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init); +IRQCHIP_DECLARE(renesas_r9a07g043_plic, "renesas-r9a07g043-plic", plic_init); IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */ IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_init); /* for firmware driver */
The Renesas RZ/Five SoC has a RISC-V AX45MP AndesCore with NCEPLIC100. The NCEPLIC100 supports both edge-triggered and level-triggered interrupts. In case of edge-triggered interrupts NCEPLIC100 ignores the next interrupt edge until the previous completion message has been received and NCEPLIC100 doesn't support pending interrupt counter, hence losing the interrupts if not acknowledged in time. So the workaround for edge-triggered interrupts to be handled correctly and without losing is that it needs to be acknowledged first and then handler must be run so that we don't miss on the next edge-triggered interrupt. This patch adds a new compatible string for Renesas RZ/Five SoC and adds support to change interrupt flow based on the interrupt type. It also implements irq_ack and irq_set_type callbacks. Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> --- drivers/irqchip/Kconfig | 1 + drivers/irqchip/irq-sifive-plic.c | 71 ++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 2 deletions(-)