@@ -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);
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(-)