summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTony Lindgren <tony@atomide.com>2018-05-04 20:44:09 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-16 10:52:34 +0300
commitf6e6f0c5423ab4b92b11544a4a6dec29ebd9ade2 (patch)
tree29a7a4b267d8da3fafaa066b20b3036998454702
parent5b91ae57b5cb612ad68516e70d517b303dd604d1 (diff)
downloadlinux-f6e6f0c5423ab4b92b11544a4a6dec29ebd9ade2.tar.xz
serial: 8250: omap: Fix idling of clocks for unused uarts
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>
-rw-r--r--drivers/tty/serial/8250/8250_omap.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index e8b34f16ba2c..a3adf21f9dce 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -1078,13 +1078,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, },
@@ -1326,6 +1327,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 */