diff mbox series

[RFC] ARM: dts: mx6sx: Handle shared VDD_ARM/VDD_SOC rail

Message ID 20220804131133.12746-1-marex@denx.de
State New
Headers show
Series [RFC] ARM: dts: mx6sx: Handle shared VDD_ARM/VDD_SOC rail | expand

Commit Message

Marek Vasut Aug. 4, 2022, 1:11 p.m. UTC
It seems that powering off the CPU core results in some stability bug
in case both the VDD_ARM and VDD_SOC power rails share the same supply.
Further investigation shows that the system hangs on WFI instruction,
which triggers pdn_req in GPC, which in turn powers down CPU core.

However, it turns out that not powering down the CPU core on pdn_req
is sufficient to always wake the system from WFI. This would indicate
that powering down the CPU core leads to some power leakage between
VDD_ARM and VDD_SOC, since VDD_ARM is off and VDD_SOC is still on.
This would in turn destabilize the CPU core and lead to the hang.

Further details at https://community.nxp.com/thread/504898

Signed-off-by: Marek Vasut <marex@denx.de>
---
Cc: Fabio Estevam <festevam@denx.de>
Cc: NXP Linux Team <linux-imx@nxp.com>
Cc: Peng Fan <peng.fan@nxp.com>
Cc: Shawn Guo <shawnguo@kernel.org>
To: linux-arm-kernel@lists.infradead.org
---
 arch/arm/mach-imx/gpc.c | 55 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 54 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index ebc4339b8be48..87bba68c237c8 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -28,6 +28,7 @@ 
 #define GPC_MAX_IRQS		(IMR_NUM * 32)
 
 static void __iomem *gpc_base;
+static bool shared_arm_soc_vdd_rail;
 static u32 gpc_wake_irqs[IMR_NUM];
 static u32 gpc_saved_imrs[IMR_NUM];
 
@@ -45,7 +46,17 @@  void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw)
 
 void imx_gpc_set_arm_power_in_lpm(bool power_off)
 {
-	writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN);
+	/*
+	 * FIXME: Do not power off the CPU core on pdn_req , as it
+	 * seems that on MX6SX, there is some sort of voltage leak
+	 * between the VDD_ARM and VDD_SOC through the CPU core in
+	 * case both are supplied from the same voltage rail.
+	 *
+	 * If VDD_ARM is off, then the leaking current from VDD_SOC
+	 * destabilizes the CPU core and it might fail to wake up.
+	 */
+	if (!shared_arm_soc_vdd_rail)
+		writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN);
 }
 
 void imx_gpc_set_l2_mem_power_in_lpm(bool power_off)
@@ -224,6 +235,46 @@  static const struct irq_domain_ops imx_gpc_domain_ops = {
 	.free		= irq_domain_free_irqs_common,
 };
 
+static void __init imx_gpc_check_shared_arm_soc_vdd_rail(void)
+{
+	struct device_node *np_vdd_arm, *np_vdd_soc, *np_vin_arm, *np_vin_soc;
+	struct device_node *cpus, *np;
+	static int once;
+
+	if (once++)
+		return;
+
+	cpus = of_find_node_by_path("/cpus");
+	if (!cpus)
+		return;
+
+	for_each_child_of_node(cpus, np) {
+		np_vdd_arm = of_parse_phandle(np, "arm-supply", 0);
+		if (!np_vdd_arm)
+			continue;
+
+		np_vin_arm = of_parse_phandle(np_vdd_arm, "vin-supply", 0);
+		if (!np_vin_arm)
+			continue;
+
+		np_vdd_soc = of_parse_phandle(np, "soc-supply", 0);
+		if (!np_vdd_soc)
+			continue;
+
+		np_vin_soc = of_parse_phandle(np_vdd_soc, "vin-supply", 0);
+		if (!np_vin_soc)
+			continue;
+
+		if (np_vin_arm != np_vin_soc)
+			continue;
+
+		/* VDD_ARM and VDD_SOC are connected to the same supply */
+		shared_arm_soc_vdd_rail = true;
+		pr_err("VDD_ARM/VDD_SOC share the same supply, cannot disable CPU core power in idle!\n");
+		return;
+	}
+}
+
 static int __init imx_gpc_init(struct device_node *node,
 			       struct device_node *parent)
 {
@@ -245,6 +296,8 @@  static int __init imx_gpc_init(struct device_node *node,
 	if (WARN_ON(!gpc_base))
 	        return -ENOMEM;
 
+	imx_gpc_check_shared_arm_soc_vdd_rail();
+
 	domain = irq_domain_add_hierarchy(parent_domain, 0, GPC_MAX_IRQS,
 					  node, &imx_gpc_domain_ops,
 					  NULL);