RE: [PATCH 3/5] irqchip/renesas-rzg2l: Fix spurious TINT IRQ

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Thomas,

Thanks for the feedback.

> -----Original Message-----
> From: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
> Sent: Friday, March 1, 2024 3:36 PM
> Subject: Re: [PATCH 3/5] irqchip/renesas-rzg2l: Fix spurious TINT IRQ
> 
> On Mon, Feb 12 2024 at 11:37, Biju Das wrote:
> > As per RZ/G2L hardware manual Rev.1.45 section 8.8.3 Precaution when
> > changing interrupt settings, we need to mask the interrupts while
> > setting the interrupt detection method. Apart from this we need to
> > clear interrupt status after setting TINT interrupt detection method
> > to the edge type.
> 
> No 'we|us|...' please. See:
> 
OK.

> > First disable TINT enable and then set TINT source. After that set
> > edge type and then clear the interrupt status register.
> 
> Again the above novel about the manual really does not explain what the actual problem is. I can
> crystal ball it out from what the patch does, but that really wants to be written out here.

Ok, will update and send.

> 
> 
> > Fixes: 3fed09559cd8 ("irqchip: Add RZ/G2L IA55 Interrupt Controller
> > driver")
> > Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
> > ---
> >  drivers/irqchip/irq-renesas-rzg2l.c | 46
> > ++++++++++++++++++++++++++++-
> >  1 file changed, 45 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/irqchip/irq-renesas-rzg2l.c
> > b/drivers/irqchip/irq-renesas-rzg2l.c
> > index 74c8cbb790e9..c48c8e836dd1 100644
> > --- a/drivers/irqchip/irq-renesas-rzg2l.c
> > +++ b/drivers/irqchip/irq-renesas-rzg2l.c
> > @@ -117,6 +117,40 @@ static void rzg2l_clear_tint_int(struct rzg2l_irqc_priv *priv,
> >  	}
> >  }
> >
> > +static void rzg2l_tint_endisable(struct rzg2l_irqc_priv *priv, u32 reg,
> > +				 u32 tssr_offset, u32 tssr_index,
> > +				 bool enable)
> 
> The 80 character limit does not exist anymore. It's 100 today. Please get rid of the extra line breaks
> when adding new code.

Agreed.

> 
> > +{
> > +	if (enable)
> > +		reg |= TIEN << TSSEL_SHIFT(tssr_offset);
> > +	else
> > +		reg &= ~(TIEN << TSSEL_SHIFT(tssr_offset));
> > +
> > +	writel_relaxed(reg, priv->base + TSSR(tssr_index)); }
> > +
> > +static void rzg2l_disable_tint_and_set_tint_source(struct irq_data *d,
> > +						   struct rzg2l_irqc_priv *priv,
> > +						   u32 reg, u32 tssr_offset,
> > +						   u8 tssr_index)
> > +{
> > +	unsigned long tint = (uintptr_t)irq_data_get_irq_chip_data(d);
> > +	u32 val;
> > +
> > +	rzg2l_tint_endisable(priv, reg, tssr_offset, tssr_index, false);
> > +	val = (reg >> TSSEL_SHIFT(tssr_offset)) & ~TIEN;
> 
> So this shifts the byte which contains the control bit for the interrupt down to bit 0-7 and clears the
> TIEN bit (7).
> 
> > +	if (tint != val) {
> 
> And now it compares it to the TINT value which was associated when the interrupt was allocated. How is
> this correct?

val is without tien, so basically it is comparing tint source. This is correct, but I agree it is hard
to understand.

> 
> Depending on tssr_offset reg is shifted right by tssr_offset * 8. Right?
Yes.

Offset = 0, shifted by 0
Offset = 1, shifted by 8

> 
> So the comparison is only valid when tssr_offset == 3 because otherwise bit 8-31 contain uncleared
> values, no?

Oops, you are correct.

> 
> > +		reg |= tint << TSSEL_SHIFT(tssr_offset);
> > +		writel_relaxed(reg, priv->base + TSSR(tssr_index));
> 
> So this writes again to the same register as the unconditional write via rzg2l_tint_endisable(). What
> is all this conditional voodoo for?
> 
> 	u32 tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d);
> 
>         /* Clear the relevant byte in reg */
>         reg &= ~(TSSEL_MASK << TSSEL_SHIFT(tssr_offset));
>         /* Set TINT and leave TIEN clear */
>         reg |= tint << TSSEL_SHIFT(tssr_offset);
> 	writel_relaxed(reg, priv->base + TSSR(tssr_index));
> 
> Does exactly the correct thing without any conditional in a comprehensible way, no?

Yes it looks complex.

> 
> > +	}
> > +}
> > +
> > +static bool rzg2l_is_tint_enabled(struct rzg2l_irqc_priv *priv, u32 reg,
> > +				  u32 tssr_offset)
> > +{
> 
> The 'priv' argument is unused.
> 
> > +	return !!(reg & (TIEN << TSSEL_SHIFT(tssr_offset))); }
> 
> 
> > +
> >  static void rzg2l_irqc_eoi(struct irq_data *d)  {
> >  	struct rzg2l_irqc_priv *priv = irq_data_to_priv(d); @@ -214,8
> > +248,11 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
> >  	struct rzg2l_irqc_priv *priv = irq_data_to_priv(d);
> >  	unsigned int hwirq = irqd_to_hwirq(d);
> >  	u32 titseln = hwirq - IRQC_TINT_START;
> > +	u32 tssr_offset = TSSR_OFFSET(titseln);
> > +	u8 tssr_index = TSSR_INDEX(titseln);
> > +	bool tint_enable;
> >  	u8 index, sense;
> > -	u32 reg;
> > +	u32 reg, tssr;
> >
> >  	switch (type & IRQ_TYPE_SENSE_MASK) {
> >  	case IRQ_TYPE_EDGE_RISING:
> > @@ -237,10 +274,17 @@ static int rzg2l_tint_set_edge(struct irq_data *d, unsigned int type)
> >  	}
> >
> >  	raw_spin_lock(&priv->lock);
> > +	tssr = readl_relaxed(priv->base + TSSR(tssr_index));
> > +	tint_enable = rzg2l_is_tint_enabled(priv, tssr, tssr_offset);
> > +	rzg2l_disable_tint_and_set_tint_source(d, priv, tssr,
> > +					       tssr_offset, tssr_index);
> >  	reg = readl_relaxed(priv->base + TITSR(index));
> >  	reg &= ~(IRQ_MASK << (titseln * TITSEL_WIDTH));
> >  	reg |= sense << (titseln * TITSEL_WIDTH);
> >  	writel_relaxed(reg, priv->base + TITSR(index));
> > +	rzg2l_clear_tint_int(priv, hwirq);
> > +	if (tint_enable)
> > +		rzg2l_tint_endisable(priv, tssr, tssr_offset, tssr_index, true);
> >  	raw_spin_unlock(&priv->lock);
> 

> This whole tint_enable logic is overly complex.

I agree, it is complex.

> 
>   	raw_spin_lock(&priv->lock);
> 	tssr = readl_relaxed(priv->base + TSSR(tssr_index));
> 	tssr = rzg2l_disable_tint_and_set_tint_source(d, priv, tssr, tssr_offset, tssr_index);
>   	reg = readl_relaxed(priv->base + TITSR(index));
>   	reg &= ~(IRQ_MASK << (titseln * TITSEL_WIDTH));
>   	reg |= sense << (titseln * TITSEL_WIDTH);
>   	writel_relaxed(reg, priv->base + TITSR(index));
> 	rzg2l_clear_tint_int(priv, hwirq);
> 	writel_relaxed(tssr, priv->base + TSSR(tssr_index));
>  	raw_spin_unlock(&priv->lock);
> 
> All it needs is to let rzg2l_disable_tint_and_set_tint_source() return the proper value for writing
> back.
> 
> 	u32 tint = (u32)(uintptr_t)irq_data_get_irq_chip_data(d);
>         u32 tien = reg & (TIEN << TSSEL_SHIFT(tssr_offset));
> 
>         /* Clear the relevant byte in reg */
>         reg &= ~(TSSEL_MASK << TSSEL_SHIFT(tssr_offset));
>         /* Set TINT and leave TIEN clear */
>         reg |= tint << TSSEL_SHIFT(tssr_offset);
> 	writel_relaxed(reg, priv->base + TSSR(tssr_index));
>         return reg | tien;
> 
> The extra unconditional TSSR write at the end of rzg2l_tint_set_edge() is really not worth to optimize
> for.

Your logic is simple and correct. Will use it in next version.

Cheers,
Biju






[Index of Archives]     [Linux Samsung SOC]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux