diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 82fda76a2daf1d..3d771ff45feb21 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -206,6 +206,14 @@ v3d_clean_caches(struct v3d_dev *v3d) trace_v3d_cache_clean_begin(dev); + /* GFXH-1897: Ensure pending flushes complete before writing L2TCACTL */ + if (v3d->ver < V3D_GEN_71) { + if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & + V3D_L2TCACTL_L2TFLS), 100)) { + DRM_ERROR("Timeout waiting for L2T clean\n"); + } + } + V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & V3D_L2TCACTL_TMUWCF), 100)) { diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c index 01f0b7c7b864dc..b2c12a664d8aaa 100644 --- a/drivers/gpu/drm/v3d/v3d_mmu.c +++ b/drivers/gpu/drm/v3d/v3d_mmu.c @@ -35,13 +35,14 @@ static bool v3d_mmu_is_aligned(u32 page, u32 page_address, size_t alignment) IS_ALIGNED(page_address, alignment >> V3D_MMU_PAGE_SHIFT); } -int v3d_mmu_flush_all(struct v3d_dev *v3d) +/* + * Issue the MMUC flush and TLB clear unconditionally. The caller must + * already know that V3D is reachable. In particular, this is used from + * the runtime resume callback. + */ +static int v3d_mmu_flush_all_locked(struct v3d_dev *v3d) { - int ret = 0; - - /* Flush the PTs only if we're already awake */ - if (!pm_runtime_get_if_active(v3d->drm.dev)) - return 0; + int ret; V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_FLUSH | V3D_MMUC_CONTROL_ENABLE); @@ -50,7 +51,7 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d) V3D_MMUC_CONTROL_FLUSHING), 100); if (ret) { dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n"); - goto pm_put; + return ret; } V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) | @@ -61,7 +62,19 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d) if (ret) dev_err(v3d->drm.dev, "MMU TLB clear wait idle failed\n"); -pm_put: + return ret; +} + +int v3d_mmu_flush_all(struct v3d_dev *v3d) +{ + int ret; + + /* Flush the PTs only if we're already awake */ + if (!pm_runtime_get_if_active(v3d->drm.dev)) + return 0; + + ret = v3d_mmu_flush_all_locked(v3d); + v3d_pm_runtime_put(v3d); return ret; } @@ -83,7 +96,7 @@ int v3d_mmu_set_page_table(struct v3d_dev *v3d) V3D_MMU_ILLEGAL_ADDR_ENABLE); V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_ENABLE); - return v3d_mmu_flush_all(v3d); + return v3d_mmu_flush_all_locked(v3d); } void v3d_mmu_insert_ptes(struct v3d_bo *bo) diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c index f3d30ef5de4ea6..865fb9b7b365cc 100644 --- a/drivers/gpu/drm/v3d/v3d_power.c +++ b/drivers/gpu/drm/v3d/v3d_power.c @@ -2,53 +2,64 @@ /* Copyright (C) 2026 Raspberry Pi */ #include -#include #include #include "v3d_drv.h" #include "v3d_regs.h" -static void +static int v3d_resume_sms(struct v3d_dev *v3d) { if (v3d->ver < V3D_GEN_71) - return; + return 0; V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF); if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) { drm_err(&v3d->drm, "Failed to power up SMS\n"); + return -ETIMEDOUT; } v3d_reset_sms(v3d); + + return 0; } -static void +static int v3d_suspend_sms(struct v3d_dev *v3d) { if (v3d->ver < V3D_GEN_71) - return; + return 0; V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF); if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS), V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) { drm_err(&v3d->drm, "Failed to power off SMS\n"); + return -ETIMEDOUT; } + + return 0; } int v3d_power_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); struct v3d_dev *v3d = to_v3d_dev(drm); + int ret; v3d_irq_disable(v3d); - v3d_suspend_sms(v3d); - if (v3d->reset) - reset_control_assert(v3d->reset); + /* Always clean V3D caches on shutdown. */ + v3d_clean_caches(v3d); + + ret = v3d_suspend_sms(v3d); + if (ret) { + v3d_irq_enable(v3d); + return ret; + } clk_disable_unprepare(v3d->clk); @@ -65,19 +76,15 @@ int v3d_power_resume(struct device *dev) if (ret) return ret; - if (v3d->reset) { - ret = reset_control_deassert(v3d->reset); - if (ret) - goto clk_disable; + ret = v3d_resume_sms(v3d); + if (ret) { + clk_disable_unprepare(v3d->clk); + return ret; } - v3d_resume_sms(v3d); + v3d_init_hw_state(v3d); v3d_mmu_set_page_table(v3d); v3d_irq_enable(v3d); return 0; - -clk_disable: - clk_disable_unprepare(v3d->clk); - return ret; } diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c index 6c0ae6b6202771..b008b47a8ce495 100644 --- a/drivers/pmdomain/bcm/bcm2835-power.c +++ b/drivers/pmdomain/bcm/bcm2835-power.c @@ -175,7 +175,7 @@ static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable writel(PM_PASSWORD | val, base + reg); if (readl_poll_timeout_atomic(base + reg, val, - !!(val & ASB_ACK) != enable, 0, 5)) + !!(val & ASB_ACK) != enable, 0, 100)) return -ETIMEDOUT; return 0;