]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
serial: 8250_omap: Implementation of Errata i2310
authorUdit Kumar <u-kumar1@ti.com>
Wed, 19 Jun 2024 10:59:03 +0000 (16:29 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 24 Jun 2024 14:12:41 +0000 (16:12 +0200)
As per Errata i2310[0], Erroneous timeout can be triggered,
if this Erroneous interrupt is not cleared then it may leads
to storm of interrupts, therefore apply Errata i2310 solution.

[0] https://www.ti.com/lit/pdf/sprz536 page 23

Fixes: b67e830d38fa ("serial: 8250: 8250_omap: Fix possible interrupt storm on K3 SoCs")
Cc: stable@vger.kernel.org
Signed-off-by: Udit Kumar <u-kumar1@ti.com>
Link: https://lore.kernel.org/r/20240619105903.165434-1-u-kumar1@ti.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_omap.c

index 170639d12b2a0f5764649a3a708aeeea0aca2bfd..ddac0a13cf84002fea6a6b423f2352daaba0d941 100644 (file)
 /* RX FIFO occupancy indicator */
 #define UART_OMAP_RX_LVL               0x19
 
+/* Timeout low and High */
+#define UART_OMAP_TO_L                 0x26
+#define UART_OMAP_TO_H                 0x27
+
 /*
  * Copy of the genpd flags for the console.
  * Only used if console suspend is disabled
@@ -663,13 +667,24 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id)
 
        /*
         * On K3 SoCs, it is observed that RX TIMEOUT is signalled after
-        * FIFO has been drained, in which case a dummy read of RX FIFO
-        * is required to clear RX TIMEOUT condition.
+        * FIFO has been drained or erroneously.
+        * So apply solution of Errata i2310 as mentioned in
+        * https://www.ti.com/lit/pdf/sprz536
         */
        if (priv->habit & UART_RX_TIMEOUT_QUIRK &&
-           (iir & UART_IIR_RX_TIMEOUT) == UART_IIR_RX_TIMEOUT &&
-           serial_port_in(port, UART_OMAP_RX_LVL) == 0) {
-               serial_port_in(port, UART_RX);
+               (iir & UART_IIR_RX_TIMEOUT) == UART_IIR_RX_TIMEOUT) {
+               unsigned char efr2, timeout_h, timeout_l;
+
+               efr2 = serial_in(up, UART_OMAP_EFR2);
+               timeout_h = serial_in(up, UART_OMAP_TO_H);
+               timeout_l = serial_in(up, UART_OMAP_TO_L);
+               serial_out(up, UART_OMAP_TO_H, 0xFF);
+               serial_out(up, UART_OMAP_TO_L, 0xFF);
+               serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE);
+               serial_in(up, UART_IIR);
+               serial_out(up, UART_OMAP_EFR2, efr2);
+               serial_out(up, UART_OMAP_TO_H, timeout_h);
+               serial_out(up, UART_OMAP_TO_L, timeout_l);
        }
 
        /* Stop processing interrupts on input overrun */