Re: ARM iMX6sx board fails to boot with kernel 6.17
From: Fabio Estevam
Date: Mon Oct 13 2025 - 20:45:40 EST
Hi Sergey,
On Sat, Oct 11, 2025 at 5:57 PM Sergey Organov <sorganov@xxxxxxxxx> wrote:
> I'm not familiar with the code and can't figure what exactly I'm
> expected to check. Could you please prepare a patch, and I'll be happy
> to apply and check it.
Here is a patch you can try.
It's not a formal patch yet, as it needs to be split.
From 79caf9d2b74491245a6fd21a863e0857fa8bc238 Mon Sep 17 00:00:00 2001
From: Fabio Estevam <festevam@xxxxxxxxx>
Date: Mon, 13 Oct 2025 21:22:25 -0300
Subject: [PATCH] ARM: imx6sx: Initialize RC-OSC for TO1.2+ to fix low-power
idle hang
i.MX6SX silicon revision TO1.2 and later power off the 24 MHz XTAL
during low-power idle and switch to the internal RC oscillator.
Without proper RC-OSC initialization, this transition can hang early
in boot or resume from idle.
Boards based on TO1.0/TO1.1 do not exhibit this behavior since the
XTAL remains powered, so the initialization is only needed for TO1.2+.
This change conditionally enables and tunes the RC-OSC before cpuidle
setup for TO1.2 and newer silicon revisions.
Based on the following commit from the NXP vendor kernel:
https://github.com/nxp-imx/linux-imx/commit/816978f83b1d8dd6ce3bd5dd62223dbfdf74bdd3
Reported-by: Sergey Organov <sorganov@xxxxxxxxx>
Signed-off-by: Fabio Estevam <festevam@xxxxxxxxx>
---
arch/arm/mach-imx/cpuidle-imx6sx.c | 80 +++++++++++++++++++++++++++++-
drivers/clk/imx/clk-imx6sx.c | 2 +
2 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c
index 83c5cbd3748e..e694a01b83d4 100644
--- a/arch/arm/mach-imx/cpuidle-imx6sx.c
+++ b/arch/arm/mach-imx/cpuidle-imx6sx.c
@@ -5,7 +5,10 @@
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
+#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
#include <asm/cacheflush.h>
#include <asm/cpuidle.h>
#include <asm/suspend.h>
@@ -87,8 +90,8 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
* and some margin for SW execution, here set it
* to 300us.
*/
- .exit_latency = 300,
- .target_residency = 500,
+ .exit_latency = 800,
+ .target_residency = 1000,
.flags = CPUIDLE_FLAG_TIMER_STOP |
CPUIDLE_FLAG_RCU_IDLE,
.enter = imx6sx_enter_wait,
@@ -100,6 +103,78 @@ static struct cpuidle_driver imx6sx_cpuidle_driver = {
.safe_state_index = 0,
};
+#define PMU_LOW_PWR_CTRL 0x270
+#define XTALOSC24M_CONFIG0 0x2a0
+#define XTALOSC24M_CONFIG1 0x2b0
+#define XTALOSC24M_CONFIG2 0x2c0
+#define XTALOSC24M_CONFIG0_RC_PROG_CUR_SHIFT 24
+#define XTALOSC24M_CONFIG0_HYST_MINUS_MASK 0xf
+#define XTALOSC24M_CONFIG0_HYST_MINUS_SHIFT 16
+#define XTALOSC24M_CONFIG0_HYST_PLUS_MASK 0xf
+#define XTALOSC24M_CONFIG0_HYST_PLUS_SHIFT 12
+#define XTALOSC24M_CONFIG0_RC_PROG_SHIFT 4
+#define XTALOSC24M_CONFIG0_ENABLE_SHIFT 1
+#define XTALOSC24M_CONFIG0_START_SHIFT 0
+#define XTALOSC24M_CONFIG1_COUNT_RC_CUR_SHIFT 20
+#define XTALOSC24M_CONFIG1_COUNT_RC_TRG_SHIFT 0
+#define XTALOSC24M_CONFIG2_COUNT_1M_TRG_MASK 0xfff
+#define XTALOSC24M_CONFIG2_COUNT_1M_TRG_SHIFT 0
+
+static void __init imx6sx_init_rcosc(void)
+{
+ void __iomem *anatop_base;
+ struct device_node *np;
+ u32 val;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
+ anatop_base = of_iomap(np, 0);
+ WARN_ON(!anatop_base);
+
+ if (imx_get_soc_revision() < IMX_CHIP_REVISION_1_2)
+ return; /* Not needed for TO1.0/TO1.1 */
+
+ pr_info("i.MX6SX: Enabling and tuning RC-OSC for TO1.2+\n");
+
+ /* Enable RC-OSC power */
+ val = readl_relaxed(anatop_base + PMU_LOW_PWR_CTRL);
+ val |= 0x1;
+ writel_relaxed(val, anatop_base + PMU_LOW_PWR_CTRL);
+
+ /* Configure RC-OSC frequency tuning */
+ writel_relaxed(0x4 << XTALOSC24M_CONFIG0_RC_PROG_CUR_SHIFT |
+ 0xa7 << XTALOSC24M_CONFIG0_RC_PROG_SHIFT |
+ 0x1 << XTALOSC24M_CONFIG0_ENABLE_SHIFT |
+ 0x1 << XTALOSC24M_CONFIG0_START_SHIFT,
+ anatop_base + XTALOSC24M_CONFIG0);
+
+ /* count_rc_cur = 0x40, count_rc_trg = 0x2dc */
+ writel_relaxed(0x40 << XTALOSC24M_CONFIG1_COUNT_RC_CUR_SHIFT |
+ 0x2dc << XTALOSC24M_CONFIG1_COUNT_RC_TRG_SHIFT,
+ anatop_base + XTALOSC24M_CONFIG1);
+
+ /* wait 4ms for RC-OSC to stabilize */
+ msleep(4);
+
+ /* Add hysteresis: hyst_plus=3, hyst_minus=3 */
+ val = readl_relaxed(anatop_base + XTALOSC24M_CONFIG0);
+ val &= ~((XTALOSC24M_CONFIG0_HYST_MINUS_MASK << XTALOSC24M_CONFIG0_HYST_MINUS_SHIFT) |
+ (XTALOSC24M_CONFIG0_HYST_PLUS_MASK <<
+ XTALOSC24M_CONFIG0_HYST_PLUS_SHIFT));
+ val |= (0x3 << XTALOSC24M_CONFIG0_HYST_MINUS_SHIFT) |
+ (0x3 << XTALOSC24M_CONFIG0_HYST_PLUS_SHIFT);
+ writel_relaxed(val, anatop_base + XTALOSC24M_CONFIG0);
+
+ /* Set count_1m_trg = 0x2d7 */
+ val = readl_relaxed(anatop_base + XTALOSC24M_CONFIG2);
+ val &= ~(XTALOSC24M_CONFIG2_COUNT_1M_TRG_MASK << XTALOSC24M_CONFIG2_COUNT_1M_TRG_SHIFT);
+ val |= 0x2d7 << XTALOSC24M_CONFIG2_COUNT_1M_TRG_SHIFT;
+ writel_relaxed(val, anatop_base + XTALOSC24M_CONFIG2);
+
+ /* Hardware requires writing CONFIG1 after reading it */
+ val = readl_relaxed(anatop_base + XTALOSC24M_CONFIG1);
+ writel_relaxed(val, anatop_base + XTALOSC24M_CONFIG1);
+}
+
int __init imx6sx_cpuidle_init(void)
{
imx6_set_int_mem_clk_lpm(true);
@@ -113,6 +188,7 @@ int __init imx6sx_cpuidle_init(void)
*/
imx_gpc_set_arm_power_up_timing(cpu_is_imx6sx() ? 0xf : 0x2, 1);
imx_gpc_set_arm_power_down_timing(1, 1);
+ imx6sx_init_rcosc();
return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
}
diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c
index 69f8f6f9ca49..ff9bcbf30150 100644
--- a/drivers/clk/imx/clk-imx6sx.c
+++ b/drivers/clk/imx/clk-imx6sx.c
@@ -510,6 +510,8 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node)
hws[IMX6SX_CLK_LCDIF1_PODF]->clk);
}
+ clk_set_parent(hws[IMX6SX_CLK_PERCLK_SEL]->clk, hws[IMX6SX_CLK_OSC]->clk);
+
/* Set the parent clks of PCIe lvds1 and pcie_axi to be pcie ref, axi */
if (clk_set_parent(hws[IMX6SX_CLK_LVDS1_SEL]->clk, hws[IMX6SX_CLK_PCIE_REF_125M]->clk))
pr_err("Failed to set pcie bus parent clk.\n");
--
2.34.1