[PATCH 1/2] Fix the imx8mm gpu hang due to adb400 reset wrongly

Jacky Bai ping.bai at nxp.com
Mon Jan 19 00:53:41 PST 2026


Due to the HW limitation on i.MX8MM, the gpumix, gpu2d and
gpu3d share the same reset domain, that means when gpu2d/3d
go through the power off/on cycle, the gpu2d/3d reset will
also reset the gpumix domain, The GPUMIX ADB400 port also be
reset. But the ADB400 must be put into power down before reset
it.

currently, gpumix, gpu2d/3d power domain use the pm runtime_pm
to handle these power domain dependency, but in some corner case,
the gpumix power off will be skipped, then the ADB400 port will
be in active while gpu2d/3d doing the reset. The GPUMIX the ADB400
port will be reset wrongly, so lead to unpredictable bus behavior.

To simplify these domain on/off order & dependency, refine the
code to directly handle GPUMIX domain on/off along with the
gpu2d/3d power on/off flow.

Signed-off-by: Jacky Bai <ping.bai at nxp.com>
---
 drivers/pmdomain/imx/gpcv2.c | 100 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 84 insertions(+), 16 deletions(-)

diff --git a/drivers/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c
index b7cea89140ee8923f32486eab953c0e1a36bf06d..3a13aa7f1888863048106c2432eec80c7364c462 100644
--- a/drivers/pmdomain/imx/gpcv2.c
+++ b/drivers/pmdomain/imx/gpcv2.c
@@ -53,8 +53,7 @@
 #define IMX8MM_VPUG1_A53_DOMAIN			BIT(13)
 #define IMX8MM_DISPMIX_A53_DOMAIN		BIT(12)
 #define IMX8MM_VPUMIX_A53_DOMAIN		BIT(10)
-#define IMX8MM_GPUMIX_A53_DOMAIN		BIT(9)
-#define IMX8MM_GPU_A53_DOMAIN			(BIT(8) | BIT(11))
+#define IMX8MM_GPU_A53_DOMAIN			(BIT(8) | BIT(9) | BIT(11))
 #define IMX8MM_DDR1_A53_DOMAIN			BIT(7)
 #define IMX8MM_OTG2_A53_DOMAIN			BIT(5)
 #define IMX8MM_OTG1_A53_DOMAIN			BIT(4)
@@ -292,6 +291,13 @@ struct imx_pgc_domain {
 		u32 hskack;
 	} bits;
 
+	const struct {
+		u32 pxx;
+		u32 hskreq;
+		u32 hskack;
+		unsigned long pgc;
+	} parent_bits;
+
 	const int voltage;
 	const bool keep_clocks;
 	struct device *dev;
@@ -335,6 +341,30 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 		}
 	}
 
+	/* Need to do special handling for parent domain like GPUMIX on i.MX8MM */
+	if (domain->parent_bits.pxx) {
+			/* request the domain to power up */
+		regmap_update_bits(domain->regmap, domain->regs->pup,
+				   domain->parent_bits.pxx, domain->parent_bits.pxx);
+		/*
+		 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+		 * for PUP_REQ/PDN_REQ bit to be cleared
+		 */
+		ret = regmap_read_poll_timeout(domain->regmap,
+					       domain->regs->pup, reg_val,
+					       !(reg_val & domain->parent_bits.pxx),
+					       0, USEC_PER_MSEC);
+		if (ret)
+			dev_err(domain->dev, "failed to command parent PGC\n");
+
+		/* disable power control */
+		for_each_set_bit(pgc, &domain->parent_bits.pgc, 32) {
+			regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+					  GPC_PGC_CTRL_PCR);
+		}
+
+	}
+
 	reset_control_assert(domain->reset);
 
 	/* Enable reset clocks for all devices in the domain */
@@ -376,6 +406,11 @@ static int imx_pgc_power_up(struct generic_pm_domain *genpd)
 
 	reset_control_deassert(domain->reset);
 
+	/* request parent ADB400 to power up */
+	if (domain->parent_bits.hskreq)
+		regmap_update_bits(domain->regmap, domain->regs->hsk,
+				   domain->parent_bits.hskreq, domain->parent_bits.hskreq);
+
 	/* request the ADB400 to power up */
 	if (domain->bits.hskreq) {
 		regmap_update_bits(domain->regmap, domain->regs->hsk,
@@ -438,6 +473,21 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 		}
 	}
 
+	/* request the Parent domain ADB400 to power down */
+	if (domain->parent_bits.hskreq) {
+		regmap_clear_bits(domain->regmap, domain->regs->hsk,
+				  domain->parent_bits.hskreq);
+
+		ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk,
+					       reg_val,
+					       !(reg_val & domain->parent_bits.hskack),
+					       0, USEC_PER_MSEC);
+		if (ret) {
+			dev_err(domain->dev, "failed to power down parent ADB400\n");
+			goto out_clk_disable;
+		}
+	}
+
 	/* request the ADB400 to power down */
 	if (domain->bits.hskreq) {
 		regmap_clear_bits(domain->regmap, domain->regs->hsk,
@@ -477,6 +527,30 @@ static int imx_pgc_power_down(struct generic_pm_domain *genpd)
 		}
 	}
 
+	if (domain->parent_bits.pxx) {
+		/* enable power control */
+		for_each_set_bit(pgc, &domain->parent_bits.pgc, 32) {
+			regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc),
+					   GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR);
+		}
+
+		/* request the domain to power down */
+		regmap_update_bits(domain->regmap, domain->regs->pdn,
+				   domain->parent_bits.pxx, domain->parent_bits.pxx);
+		/*
+		 * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait
+		 * for PUP_REQ/PDN_REQ bit to be cleared
+		 */
+		ret = regmap_read_poll_timeout(domain->regmap,
+					       domain->regs->pdn, reg_val,
+					       !(reg_val & domain->parent_bits.pxx),
+					       0, USEC_PER_MSEC);
+		if (ret) {
+			dev_err(domain->dev, "failed to command Parent PGC\n");
+			goto out_clk_disable;
+		}
+	}
+
 	/* Disable reset clocks for all devices in the domain */
 	clk_bulk_disable_unprepare(domain->num_clks, domain->clks);
 
@@ -787,20 +861,6 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
 		.pgc   = BIT(IMX8MM_PGC_OTG2),
 	},
 
-	[IMX8MM_POWER_DOMAIN_GPUMIX] = {
-		.genpd = {
-			.name = "gpumix",
-		},
-		.bits  = {
-			.pxx = IMX8MM_GPUMIX_SW_Pxx_REQ,
-			.map = IMX8MM_GPUMIX_A53_DOMAIN,
-			.hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
-			.hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
-		},
-		.pgc   = BIT(IMX8MM_PGC_GPUMIX),
-		.keep_clocks = true,
-	},
-
 	[IMX8MM_POWER_DOMAIN_GPU] = {
 		.genpd = {
 			.name = "gpu",
@@ -811,6 +871,14 @@ static const struct imx_pgc_domain imx8mm_pgc_domains[] = {
 			.hskreq = IMX8MM_GPU_HSK_PWRDNREQN,
 			.hskack = IMX8MM_GPU_HSK_PWRDNACKN,
 		},
+
+		.parent_bits = {
+			.pxx = IMX8MM_GPUMIX_SW_Pxx_REQ,
+			.hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN,
+			.hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN,
+			.pgc = BIT(IMX8MM_PGC_GPUMIX),
+		},
+
 		.pgc   = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D),
 	},
 

-- 
2.34.1




More information about the linux-arm-kernel mailing list