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
Cc: Matthijs van Duin
Cc: Sekhar Nori
Cc: Tero Kristo
Signed-off-by: Tony Lindgren
---
Changes since v1:
- Do the write in two steps as noted by Vignesh
- Update the comments for clkctrl status bits, it's two bits
instead of just one bit
---
arch/arm/mach-actions/platsmp.c | 6 +++---
arch/arm/mach-exynos/platsmp.c | 12 ++--
arch/arm/mach-hisi/platmcpm.c | 22 +++---
arch/arm/mach-omap2/omap-smp.c | 10 +-
arch/arm/mach-prima2/platsmp.c | 10 +-
arch/arm/mach-qcom/platsmp.c| 10 +-
arch/arm/mach-spear/platsmp.c | 10 +-
arch/arm/mach-sti/platsmp.c | 10 +-
arch/arm/mach-sunxi/mc_smp.c| 20 ++--
arch/arm/plat-versatile/platsmp.c | 10 +-
drivers/tty/serial/8250/8250_omap.c | 16 +++-
11 files changed, 75 insertions(+), 61 deletions(-)
diff --git a/arch/arm/mach-actions/platsmp.c b/arch/arm/mach-actions/platsmp.c
--- a/arch/arm/mach-actions/platsmp.c
+++ b/arch/arm/mach-actions/platsmp.c
@@ -39,7 +39,7 @@ static void __iomem *sps_base_addr;
static void __iomem *timer_base_addr;
static int ncores;
-static DEFINE_SPINLOCK(boot_lock);
+static DEFINE_RAW_SPINLOCK(boot_lock);
void owl_secondary_startup(void);
@@ -93,7 +93,7 @@ static int s500_smp_boot_secondary(unsigned int cpu, struct
task_struct *idle)
udelay(10);
- spin_lock(&boot_lock);
+ raw_spin_lock(&boot_lock);
smp_send_reschedule(cpu);
@@ -106,7 +106,7 @@ static int s500_smp_boot_secondary(unsigned int cpu, struct
task_struct *idle)
writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
- spin_unlock(&boot_lock);
+ raw_spin_unlock(&boot_lock);
return 0;
}
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -224,7 +224,7 @@ static void __iomem *scu_base_addr(void)
return (void __iomem *)(S5P_VA_SCU);
}
-static DEFINE_SPINLOCK(boot_lock);
+static DEFINE_RAW_SPINLOCK(boot_lock);
static void exynos_secondary_init(unsigned int cpu)
{
@@ -237,8 +237,8 @@ static void exynos_secondary_init(unsigned int cpu)
/*
* Synchronise with the boot thread.
*/
- spin_lock(&boot_lock);
- spin_unlock(&boot_lock);
+ raw_spin_lock(&boot_lock);
+ raw_spin_unlock(&boot_lock);
}
int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
@@ -302,7 +302,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct
task_struct *idle)
* Set synchronisation state between this boot processor
* and the secondary one
*/
- spin_lock(&boot_lock);
+ raw_spin_lock(&boot_lock);
/*
* The secondary processor is waiting to be released from
@@ -329,7 +329,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct
task_struct *idle)
if (timeout == 0) {
printk(KERN_ERR "cpu1 power enable failed");
- spin_unlock(&boot_lock);
+ raw_spin_unlock(&boot_lock);