]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
staging: nvec: make i2c controller register writes robust
authorMarc Dietrich <marvin24@gmx.de>
Sun, 26 May 2024 19:39:32 +0000 (21:39 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 4 Jun 2024 11:26:36 +0000 (13:26 +0200)
The i2c controller needs to read back the data written to its registers.
This way we can avoid the long delay in the interrupt handler.

Signed-off-by: Marc Dietrich <marvin24@gmx.de>
Reviewed-by: Thierry Reding <treding@nvidia.com>
Link: https://lore.kernel.org/r/20240526193932.57277-1-marvin24@gmx.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/nvec/nvec.c

index e5ca78e573841b73a479a4cd6a28efc27841e074..d67d2f3338b233bf003bbef28fc3dc93c4cae9c6 100644 (file)
@@ -570,6 +570,22 @@ static void nvec_tx_set(struct nvec_chip *nvec)
                (uint)nvec->tx->size, nvec->tx->data[1]);
 }
 
+/**
+ * tegra_i2c_writel - safely write to an I2C client controller register
+ * @val: value to be written
+ * @reg: register to write to
+ *
+ * A write to an I2C controller register needs to be read back to make sure
+ * that the value has arrived.
+ */
+static void tegra_i2c_writel(u32 val, void *reg)
+{
+       writel_relaxed(val, reg);
+
+       /* read back register to make sure that register writes completed */
+       readl_relaxed(reg);
+}
+
 /**
  * nvec_interrupt - Interrupt handler
  * @irq: The IRQ
@@ -604,7 +620,7 @@ static irqreturn_t nvec_interrupt(int irq, void *dev)
        if ((status & RNW) == 0) {
                received = readl(nvec->base + I2C_SL_RCVD);
                if (status & RCVD)
-                       writel(0, nvec->base + I2C_SL_RCVD);
+                       tegra_i2c_writel(0, nvec->base + I2C_SL_RCVD);
        }
 
        if (status == (I2C_SL_IRQ | RCVD))
@@ -696,7 +712,7 @@ static irqreturn_t nvec_interrupt(int irq, void *dev)
 
        /* Send data if requested, but not on end of transmission */
        if ((status & (RNW | END_TRANS)) == RNW)
-               writel(to_send, nvec->base + I2C_SL_RCVD);
+               tegra_i2c_writel(to_send, nvec->base + I2C_SL_RCVD);
 
        /* If we have send the first byte */
        if (status == (I2C_SL_IRQ | RNW | RCVD))
@@ -713,15 +729,6 @@ static irqreturn_t nvec_interrupt(int irq, void *dev)
                status & RCVD ? " RCVD" : "",
                status & RNW ? " RNW" : "");
 
-       /*
-        * TODO: replace the udelay with a read back after each writel above
-        * in order to work around a hardware issue, see i2c-tegra.c
-        *
-        * Unfortunately, this change causes an initialisation issue with the
-        * touchpad, which needs to be fixed first.
-        */
-       udelay(100);
-
        return IRQ_HANDLED;
 }
 
@@ -737,15 +744,15 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec)
 
        val = I2C_CNFG_NEW_MASTER_SFM | I2C_CNFG_PACKET_MODE_EN |
            (0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
-       writel(val, nvec->base + I2C_CNFG);
+       tegra_i2c_writel(val, nvec->base + I2C_CNFG);
 
        clk_set_rate(nvec->i2c_clk, 8 * 80000);
 
-       writel(I2C_SL_NEWSL, nvec->base + I2C_SL_CNFG);
-       writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT);
+       tegra_i2c_writel(I2C_SL_NEWSL, nvec->base + I2C_SL_CNFG);
+       tegra_i2c_writel(0x1E, nvec->base + I2C_SL_DELAY_COUNT);
 
-       writel(nvec->i2c_addr >> 1, nvec->base + I2C_SL_ADDR1);
-       writel(0, nvec->base + I2C_SL_ADDR2);
+       tegra_i2c_writel(nvec->i2c_addr >> 1, nvec->base + I2C_SL_ADDR1);
+       tegra_i2c_writel(0, nvec->base + I2C_SL_ADDR2);
 
        enable_irq(nvec->irq);
 }
@@ -754,7 +761,7 @@ static void tegra_init_i2c_slave(struct nvec_chip *nvec)
 static void nvec_disable_i2c_slave(struct nvec_chip *nvec)
 {
        disable_irq(nvec->irq);
-       writel(I2C_SL_NEWSL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG);
+       tegra_i2c_writel(I2C_SL_NEWSL | I2C_SL_NACK, nvec->base + I2C_SL_CNFG);
        clk_disable_unprepare(nvec->i2c_clk);
 }
 #endif