serial: 8250: omap: Fix idling of clocks for unused uarts
authorTony Lindgren <tony@atomide.com>
Fri, 4 May 2018 17:44:09 +0000 (10:44 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 16 Jun 2018 07:45:16 +0000 (09:45 +0200)
commit 13dc04d0e5fdc25c8f713ad23fdce51cf2bf96ba upstream.

I noticed that unused UARTs won't necessarily idle properly always
unless at least one byte tx transfer is done first.

After some debugging I narrowed down the problem to the scr register
dma configuration bits that need to be set before softreset for the
clocks to idle. Unless we do this, the module clkctrl idlest bits
may be set to 1 instead of 3 meaning the clock will never idle and
is blocking deeper idle states for the whole domain.

This might be related to the configuration done by the bootloader
or kexec booting where certain configurations cause the 8250 or
the clkctrl clock to jam in a way where setting of the scr bits
and reset is needed to clear it. I've tried diffing the 8250
registers for the various modes, but did not see anything specific.
So far I've only seen this on omap4 but I'm suspecting this might
also happen on the other clkctrl using SoCs considering they
already have a quirk enabled for UART_ERRATA_CLOCK_DISABLE.

Let's fix the issue by configuring scr before reset for basic dma
even if we don't use it. The scr register will be reset when we do
softreset few lines after, and we restore scr on resume. We should
do this for all the SoCs with UART_ERRATA_CLOCK_DISABLE quirk flag
set since the ones with UART_ERRATA_CLOCK_DISABLE are all based
using clkctrl similar to omap4.

Looks like both OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL
bits are needed for the clkctrl to idle after a softreset.

And we need to add omap4 to also use the UART_ERRATA_CLOCK_DISABLE
for the related workaround to be enabled. This same compatible
value will also be used for omap5.

Fixes: cdb929e4452a ("serial: 8250_omap: workaround errata around idling UART after using DMA")
Cc: Keerthy <j-keerthy@ti.com>
Cc: Matthijs van Duin <matthijsvanduin@gmail.com>
Cc: Sekhar Nori <nsekhar@ti.com>
Cc: Tero Kristo <t-kristo@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_omap.c

index 833771bca0a593008967905d259c6780cb512cae..da04ba1ecf68a7c6235a8d35a565c2d150881da9 100644 (file)
@@ -1100,13 +1100,14 @@ static int omap8250_no_handle_irq(struct uart_port *port)
        return 0;
 }
 
+static const u8 omap4_habit = UART_ERRATA_CLOCK_DISABLE;
 static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
 static const u8 dra742_habit = UART_ERRATA_CLOCK_DISABLE;
 
 static const struct of_device_id omap8250_dt_ids[] = {
        { .compatible = "ti,omap2-uart" },
        { .compatible = "ti,omap3-uart" },
-       { .compatible = "ti,omap4-uart" },
+       { .compatible = "ti,omap4-uart", .data = &omap4_habit, },
        { .compatible = "ti,am3352-uart", .data = &am3352_habit, },
        { .compatible = "ti,am4372-uart", .data = &am3352_habit, },
        { .compatible = "ti,dra742-uart", .data = &dra742_habit, },
@@ -1343,6 +1344,19 @@ static int omap8250_soft_reset(struct device *dev)
        int sysc;
        int syss;
 
+       /*
+        * At least on omap4, unused uarts may not idle after reset without
+        * a basic scr dma configuration even with no dma in use. The
+        * module clkctrl status bits will be 1 instead of 3 blocking idle
+        * for the whole clockdomain. The softreset below will clear scr,
+        * and we restore it on resume so this is safe to do on all SoCs
+        * needing omap8250_soft_reset() quirk. Do it in two writes as
+        * recommended in the comment for omap8250_update_scr().
+        */
+       serial_out(up, UART_OMAP_SCR, OMAP_UART_SCR_DMAMODE_1);
+       serial_out(up, UART_OMAP_SCR,
+                  OMAP_UART_SCR_DMAMODE_1 | OMAP_UART_SCR_DMAMODE_CTL);
+
        sysc = serial_in(up, UART_OMAP_SYSC);
 
        /* softreset the UART */