Message ID | 20240206151927.2925585-1-jacek.lawrynowicz@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | accel/ivpu: Fix DevTLB errors on suspend/resume | expand |
Applied to drm-misc-fixes On 06.02.2024 16:19, Jacek Lawrynowicz wrote: > Issue IP reset before shutdown in order to > complete all upstream requests to the SOC. > Without this DevTLB is complaining about > incomplete transactions and NPU cannot resume from > suspend. > This problem is only happening on recent IFWI > releases. > > IP reset in rare corner cases can mess up PCI > configuration, so save it before the reset. > After this happens it is also impossible to > issue PLL requests and D0->D3->D0 cycle is needed > to recover the NPU. Add WP 0 request on power up, > so the PUNIT is always notified about NPU reset. > > Fixes: 3f7c0634926d ("accel/ivpu/37xx: Fix hangs related to MMIO reset") > Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> > --- > drivers/accel/ivpu/ivpu_hw_37xx.c | 44 ++++++++++++++++++++++--------- > drivers/accel/ivpu/ivpu_pm.c | 12 ++++----- > 2 files changed, 38 insertions(+), 18 deletions(-) > > diff --git a/drivers/accel/ivpu/ivpu_hw_37xx.c b/drivers/accel/ivpu/ivpu_hw_37xx.c > index 77accd029c4a..89af1006df55 100644 > --- a/drivers/accel/ivpu/ivpu_hw_37xx.c > +++ b/drivers/accel/ivpu/ivpu_hw_37xx.c > @@ -510,16 +510,6 @@ static int ivpu_boot_pwr_domain_enable(struct ivpu_device *vdev) > return ret; > } > > -static int ivpu_boot_pwr_domain_disable(struct ivpu_device *vdev) > -{ > - ivpu_boot_dpu_active_drive(vdev, false); > - ivpu_boot_pwr_island_isolation_drive(vdev, true); > - ivpu_boot_pwr_island_trickle_drive(vdev, false); > - ivpu_boot_pwr_island_drive(vdev, false); > - > - return ivpu_boot_wait_for_pwr_island_status(vdev, 0x0); > -} > - > static void ivpu_boot_no_snoop_enable(struct ivpu_device *vdev) > { > u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES); > @@ -616,12 +606,37 @@ static int ivpu_hw_37xx_info_init(struct ivpu_device *vdev) > return 0; > } > > +static int ivpu_hw_37xx_ip_reset(struct ivpu_device *vdev) > +{ > + int ret; > + u32 val; > + > + if (IVPU_WA(punit_disabled)) > + return 0; > + > + ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); > + if (ret) { > + ivpu_err(vdev, "Timed out waiting for TRIGGER bit\n"); > + return ret; > + } > + > + val = REGB_RD32(VPU_37XX_BUTTRESS_VPU_IP_RESET); > + val = REG_SET_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, val); > + REGB_WR32(VPU_37XX_BUTTRESS_VPU_IP_RESET, val); > + > + ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); > + if (ret) > + ivpu_err(vdev, "Timed out waiting for RESET completion\n"); > + > + return ret; > +} > + > static int ivpu_hw_37xx_reset(struct ivpu_device *vdev) > { > int ret = 0; > > - if (ivpu_boot_pwr_domain_disable(vdev)) { > - ivpu_err(vdev, "Failed to disable power domain\n"); > + if (ivpu_hw_37xx_ip_reset(vdev)) { > + ivpu_err(vdev, "Failed to reset NPU\n"); > ret = -EIO; > } > > @@ -661,6 +676,11 @@ static int ivpu_hw_37xx_power_up(struct ivpu_device *vdev) > { > int ret; > > + /* PLL requests may fail when powering down, so issue WP 0 here */ > + ret = ivpu_pll_disable(vdev); > + if (ret) > + ivpu_warn(vdev, "Failed to disable PLL: %d\n", ret); > + > ret = ivpu_hw_37xx_d0i3_disable(vdev); > if (ret) > ivpu_warn(vdev, "Failed to disable D0I3: %d\n", ret); > diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c > index f501f27ebafd..fcc319ee0018 100644 > --- a/drivers/accel/ivpu/ivpu_pm.c > +++ b/drivers/accel/ivpu/ivpu_pm.c > @@ -58,11 +58,14 @@ static int ivpu_suspend(struct ivpu_device *vdev) > { > int ret; > > + /* Save PCI state before powering down as it sometimes gets corrupted if NPU hangs */ > + pci_save_state(to_pci_dev(vdev->drm.dev)); > + > ret = ivpu_shutdown(vdev); > - if (ret) { > + if (ret) > ivpu_err(vdev, "Failed to shutdown VPU: %d\n", ret); > - return ret; > - } > + > + pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot); > > return ret; > } > @@ -200,9 +203,6 @@ int ivpu_pm_suspend_cb(struct device *dev) > ivpu_suspend(vdev); > ivpu_pm_prepare_warm_boot(vdev); > > - pci_save_state(to_pci_dev(dev)); > - pci_set_power_state(to_pci_dev(dev), PCI_D3hot); > - > ivpu_dbg(vdev, PM, "Suspend done.\n"); > > return 0;
diff --git a/drivers/accel/ivpu/ivpu_hw_37xx.c b/drivers/accel/ivpu/ivpu_hw_37xx.c index 77accd029c4a..89af1006df55 100644 --- a/drivers/accel/ivpu/ivpu_hw_37xx.c +++ b/drivers/accel/ivpu/ivpu_hw_37xx.c @@ -510,16 +510,6 @@ static int ivpu_boot_pwr_domain_enable(struct ivpu_device *vdev) return ret; } -static int ivpu_boot_pwr_domain_disable(struct ivpu_device *vdev) -{ - ivpu_boot_dpu_active_drive(vdev, false); - ivpu_boot_pwr_island_isolation_drive(vdev, true); - ivpu_boot_pwr_island_trickle_drive(vdev, false); - ivpu_boot_pwr_island_drive(vdev, false); - - return ivpu_boot_wait_for_pwr_island_status(vdev, 0x0); -} - static void ivpu_boot_no_snoop_enable(struct ivpu_device *vdev) { u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES); @@ -616,12 +606,37 @@ static int ivpu_hw_37xx_info_init(struct ivpu_device *vdev) return 0; } +static int ivpu_hw_37xx_ip_reset(struct ivpu_device *vdev) +{ + int ret; + u32 val; + + if (IVPU_WA(punit_disabled)) + return 0; + + ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); + if (ret) { + ivpu_err(vdev, "Timed out waiting for TRIGGER bit\n"); + return ret; + } + + val = REGB_RD32(VPU_37XX_BUTTRESS_VPU_IP_RESET); + val = REG_SET_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, val); + REGB_WR32(VPU_37XX_BUTTRESS_VPU_IP_RESET, val); + + ret = REGB_POLL_FLD(VPU_37XX_BUTTRESS_VPU_IP_RESET, TRIGGER, 0, TIMEOUT_US); + if (ret) + ivpu_err(vdev, "Timed out waiting for RESET completion\n"); + + return ret; +} + static int ivpu_hw_37xx_reset(struct ivpu_device *vdev) { int ret = 0; - if (ivpu_boot_pwr_domain_disable(vdev)) { - ivpu_err(vdev, "Failed to disable power domain\n"); + if (ivpu_hw_37xx_ip_reset(vdev)) { + ivpu_err(vdev, "Failed to reset NPU\n"); ret = -EIO; } @@ -661,6 +676,11 @@ static int ivpu_hw_37xx_power_up(struct ivpu_device *vdev) { int ret; + /* PLL requests may fail when powering down, so issue WP 0 here */ + ret = ivpu_pll_disable(vdev); + if (ret) + ivpu_warn(vdev, "Failed to disable PLL: %d\n", ret); + ret = ivpu_hw_37xx_d0i3_disable(vdev); if (ret) ivpu_warn(vdev, "Failed to disable D0I3: %d\n", ret); diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index f501f27ebafd..fcc319ee0018 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -58,11 +58,14 @@ static int ivpu_suspend(struct ivpu_device *vdev) { int ret; + /* Save PCI state before powering down as it sometimes gets corrupted if NPU hangs */ + pci_save_state(to_pci_dev(vdev->drm.dev)); + ret = ivpu_shutdown(vdev); - if (ret) { + if (ret) ivpu_err(vdev, "Failed to shutdown VPU: %d\n", ret); - return ret; - } + + pci_set_power_state(to_pci_dev(vdev->drm.dev), PCI_D3hot); return ret; } @@ -200,9 +203,6 @@ int ivpu_pm_suspend_cb(struct device *dev) ivpu_suspend(vdev); ivpu_pm_prepare_warm_boot(vdev); - pci_save_state(to_pci_dev(dev)); - pci_set_power_state(to_pci_dev(dev), PCI_D3hot); - ivpu_dbg(vdev, PM, "Suspend done.\n"); return 0;
Issue IP reset before shutdown in order to complete all upstream requests to the SOC. Without this DevTLB is complaining about incomplete transactions and NPU cannot resume from suspend. This problem is only happening on recent IFWI releases. IP reset in rare corner cases can mess up PCI configuration, so save it before the reset. After this happens it is also impossible to issue PLL requests and D0->D3->D0 cycle is needed to recover the NPU. Add WP 0 request on power up, so the PUNIT is always notified about NPU reset. Fixes: 3f7c0634926d ("accel/ivpu/37xx: Fix hangs related to MMIO reset") Signed-off-by: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com> --- drivers/accel/ivpu/ivpu_hw_37xx.c | 44 ++++++++++++++++++++++--------- drivers/accel/ivpu/ivpu_pm.c | 12 ++++----- 2 files changed, 38 insertions(+), 18 deletions(-)