[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