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

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

 



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.

First disable TINT enable and then set TINT source. After that set edge
type and then clear the interrupt status register.

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)
+{
+	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;
+	if (tint != val) {
+		reg |= tint << TSSEL_SHIFT(tssr_offset);
+		writel_relaxed(reg, priv->base + TSSR(tssr_index));
+	}
+}
+
+static bool rzg2l_is_tint_enabled(struct rzg2l_irqc_priv *priv, u32 reg,
+				  u32 tssr_offset)
+{
+	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);
 
 	return 0;
-- 
2.25.1





[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