diff mbox series

[RFC,2/2] irqchip/sifive-plic: Add support for Renesas RZ/Five SoC

Message ID 20220524172214.5104-3-prabhakar.mahadev-lad.rj@bp.renesas.com (mailing list archive)
State Superseded
Delegated to: Geert Uytterhoeven
Headers show
Series Add PLIC support for Renesas RZ/Five SoC | expand

Commit Message

Lad Prabhakar May 24, 2022, 5:22 p.m. UTC
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(-)

Comments

Geert Uytterhoeven May 25, 2022, 8 a.m. UTC | #1
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
Lad, Prabhakar May 25, 2022, 9 a.m. UTC | #2
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
Geert Uytterhoeven May 25, 2022, 9:35 a.m. UTC | #3
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
Lad, Prabhakar May 25, 2022, 9:43 a.m. UTC | #4
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
Geert Uytterhoeven May 25, 2022, 11:46 a.m. UTC | #5
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
Lad, Prabhakar May 27, 2022, 11:05 a.m. UTC | #6
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
>
Marc Zyngier June 6, 2022, 3:41 p.m. UTC | #7
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.
Lad, Prabhakar June 7, 2022, 12:41 p.m. UTC | #8
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.
Marc Zyngier June 8, 2022, 10:27 a.m. UTC | #9
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 mbox series

Patch

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 */