From 79caf9d2b74491245a6fd21a863e0857fa8bc238 Mon Sep 17 00:00:00 2001 From: Fabio Estevam 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 Signed-off-by: Fabio Estevam --- 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 #include +#include #include +#include +#include #include #include #include @@ -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