drm/i915/gen8+: Add RC6 CTX corruption WA
authorImre Deak <imre.deak@intel.com>
Mon, 9 Jul 2018 15:24:27 +0000 (18:24 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 12 Nov 2019 18:13:36 +0000 (19:13 +0100)
commit 7e34f4e4aad3fd34c02b294a3cf2321adf5b4438 upstream.

In some circumstances the RC6 context can get corrupted. We can detect
this and take the required action, that is disable RC6 and runtime PM.
The HW recovers from the corrupted state after a system suspend/resume
cycle, so detect the recovery and re-enable RC6 and runtime PM.

v2: rebase (Mika)
v3:
- Move intel_suspend_gt_powersave() to the end of the GEM suspend
  sequence.
- Add commit message.
v4:
- Rebased on intel_uncore_forcewake_put(i915->uncore, ...) API
  change.
v5: rebased on gem/gt split (Mika)

Signed-off-by: Imre Deak <imre.deak@intel.com>
Signed-off-by: Mika Kuoppala <mika.kuoppala@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_pm.c

index a6ad938f44a68f736e30921ad04094a73cab0668..697b2499c7a1646018dde65a280a7e030f022834 100644 (file)
@@ -698,6 +698,8 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
                return ret;
        }
 
+       i915_rc6_ctx_wa_suspend(dev_priv);
+
        pci_disable_device(drm_dev->pdev);
        /*
         * During hibernation on some platforms the BIOS may try to access
@@ -849,6 +851,8 @@ static int i915_drm_resume_early(struct drm_device *dev)
        intel_uncore_sanitize(dev);
        intel_power_domains_init_hw(dev_priv);
 
+       i915_rc6_ctx_wa_resume(dev_priv);
+
        return ret;
 }
 
index 2a815dc2af918114119716837c5df2bcc4cf3b99..adbbcaf14af676597c65fef6b27e0c297cf6dd86 100644 (file)
@@ -1159,6 +1159,7 @@ struct intel_gen6_power_mgmt {
        bool client_boost;
 
        bool enabled;
+       bool ctx_corrupted;
        struct delayed_work delayed_resume_work;
        unsigned boosts;
 
@@ -2570,6 +2571,10 @@ struct drm_i915_cmd_table {
 
 /* Early gen2 have a totally busted CS tlb and require pinned batches. */
 #define HAS_BROKEN_CS_TLB(dev)         (IS_I830(dev) || IS_845G(dev))
+
+#define NEEDS_RC6_CTX_CORRUPTION_WA(dev)       \
+       (IS_BROADWELL(dev) || INTEL_INFO(dev)->gen == 9)
+
 /*
  * dp aux and gmbus irq on gen4 seems to be able to generate legacy interrupts
  * even when in MSI mode. This results in spurious interrupt warnings if the
index fb54d6e6cfc659f3ba32f69b0a0e0abd094b2e46..603d8cdfc5f1f14e417aca16432cf0f3d784d68c 100644 (file)
 #define   ECOCHK_PPGTT_WT_HSW          (0x2<<3)
 #define   ECOCHK_PPGTT_WB_HSW          (0x3<<3)
 
+#define GEN8_RC6_CTX_INFO              0x8504
+
 #define GAC_ECO_BITS                   0x14090
 #define   ECOBITS_SNB_BIT              (1<<13)
 #define   ECOBITS_PPGTT_CACHE64B       (3<<8)
index 4f5d07bb35118e483b9d5f06e557eaca049052a1..a9166ff48a26d6129a32629e9e5522d409228874 100644 (file)
@@ -10747,6 +10747,10 @@ void intel_mark_busy(struct drm_device *dev)
                return;
 
        intel_runtime_pm_get(dev_priv);
+
+       if (NEEDS_RC6_CTX_CORRUPTION_WA(dev_priv))
+               intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+
        i915_update_gfx_val(dev_priv);
        if (INTEL_INFO(dev)->gen >= 6)
                gen6_rps_busy(dev_priv);
@@ -10765,6 +10769,11 @@ void intel_mark_idle(struct drm_device *dev)
        if (INTEL_INFO(dev)->gen >= 6)
                gen6_rps_idle(dev->dev_private);
 
+       if (NEEDS_RC6_CTX_CORRUPTION_WA(dev_priv)) {
+               i915_rc6_ctx_wa_check(dev_priv);
+               intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+       }
+
        intel_runtime_pm_put(dev_priv);
 }
 
index 722aa159cd28a3154e6bccb6e69b8c478b5bc21b..78503e481313d641289768939c4065babb21fd41 100644 (file)
@@ -1410,6 +1410,9 @@ void intel_enable_gt_powersave(struct drm_device *dev);
 void intel_disable_gt_powersave(struct drm_device *dev);
 void intel_suspend_gt_powersave(struct drm_device *dev);
 void intel_reset_gt_powersave(struct drm_device *dev);
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915);
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915);
 void gen6_update_ring_freq(struct drm_device *dev);
 void gen6_rps_busy(struct drm_i915_private *dev_priv);
 void gen6_rps_reset_ei(struct drm_i915_private *dev_priv);
index 922c0c815b302dc251a39f81fb82a7eeb86271d7..81bd84f9156ba036209aa5d1522e8fcdc3fa9582 100644 (file)
@@ -4599,30 +4599,42 @@ void intel_set_rps(struct drm_device *dev, u8 val)
                gen6_set_rps(dev, val);
 }
 
-static void gen9_disable_rps(struct drm_device *dev)
+static void gen9_disable_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        I915_WRITE(GEN6_RC_CONTROL, 0);
+}
+
+static void gen9_disable_rps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
        I915_WRITE(GEN9_PG_ENABLE, 0);
 }
 
-static void gen6_disable_rps(struct drm_device *dev)
+static void gen6_disable_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        I915_WRITE(GEN6_RC_CONTROL, 0);
+}
+
+static void gen6_disable_rps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
        I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
 }
 
-static void cherryview_disable_rps(struct drm_device *dev)
+static void cherryview_disable_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        I915_WRITE(GEN6_RC_CONTROL, 0);
 }
 
-static void valleyview_disable_rps(struct drm_device *dev)
+static void valleyview_disable_rc6(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -4826,7 +4838,8 @@ static void gen9_enable_rc6(struct drm_device *dev)
        I915_WRITE(GEN9_RENDER_PG_IDLE_HYSTERESIS, 25);
 
        /* 3a: Enable RC6 */
-       if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
+       if (!dev_priv->rps.ctx_corrupted &&
+           intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
                rc6_mask = GEN6_RC_CTL_RC6_ENABLE;
        DRM_INFO("RC6 %s\n", (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ?
                        "on" : "off");
@@ -4849,7 +4862,7 @@ static void gen9_enable_rc6(struct drm_device *dev)
         * WaRsDisableCoarsePowerGating:skl,bxt - Render/Media PG need to be disabled with RC6.
         */
        if ((IS_BROXTON(dev) && (INTEL_REVID(dev) < BXT_REVID_B0)) ||
-           ((IS_SKL_GT3(dev) || IS_SKL_GT4(dev)) && (INTEL_REVID(dev) <= SKL_REVID_F0)))
+           INTEL_INFO(dev)->gen == 9)
                I915_WRITE(GEN9_PG_ENABLE, 0);
        else
                I915_WRITE(GEN9_PG_ENABLE, (rc6_mask & GEN6_RC_CTL_RC6_ENABLE) ?
@@ -4892,7 +4905,8 @@ static void gen8_enable_rps(struct drm_device *dev)
                I915_WRITE(GEN6_RC6_THRESHOLD, 50000); /* 50/125ms per EI */
 
        /* 3: Enable RC6 */
-       if (intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
+       if (!dev_priv->rps.ctx_corrupted &&
+           intel_enable_rc6(dev) & INTEL_RC6_ENABLE)
                rc6_mask = GEN6_RC_CTL_RC6_ENABLE;
        intel_print_rc6_info(dev, rc6_mask);
        if (IS_BROADWELL(dev))
@@ -6136,10 +6150,101 @@ static void intel_init_emon(struct drm_device *dev)
        dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
 }
 
+static bool i915_rc6_ctx_corrupted(struct drm_i915_private *dev_priv)
+{
+       return !I915_READ(GEN8_RC6_CTX_INFO);
+}
+
+static void i915_rc6_ctx_wa_init(struct drm_i915_private *i915)
+{
+       if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+               return;
+
+       if (i915_rc6_ctx_corrupted(i915)) {
+               DRM_INFO("RC6 context corrupted, disabling runtime power management\n");
+               i915->rps.ctx_corrupted = true;
+               intel_runtime_pm_get(i915);
+       }
+}
+
+static void i915_rc6_ctx_wa_cleanup(struct drm_i915_private *i915)
+{
+       if (i915->rps.ctx_corrupted) {
+               intel_runtime_pm_put(i915);
+               i915->rps.ctx_corrupted = false;
+       }
+}
+
+/**
+ * i915_rc6_ctx_wa_suspend - system suspend sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to clean up the RC6 CTX WA before system suspend.
+ */
+void i915_rc6_ctx_wa_suspend(struct drm_i915_private *i915)
+{
+       if (i915->rps.ctx_corrupted)
+               intel_runtime_pm_put(i915);
+}
+
+/**
+ * i915_rc6_ctx_wa_resume - system resume sequence for the RC6 CTX WA
+ * @i915: i915 device
+ *
+ * Perform any steps needed to re-init the RC6 CTX WA after system resume.
+ */
+void i915_rc6_ctx_wa_resume(struct drm_i915_private *i915)
+{
+       if (!i915->rps.ctx_corrupted)
+               return;
+
+       if (i915_rc6_ctx_corrupted(i915)) {
+               intel_runtime_pm_get(i915);
+               return;
+       }
+
+       DRM_INFO("RC6 context restored, re-enabling runtime power management\n");
+       i915->rps.ctx_corrupted = false;
+}
+
+static void intel_disable_rc6(struct drm_device *dev);
+
+/**
+ * i915_rc6_ctx_wa_check - check for a new RC6 CTX corruption
+ * @i915: i915 device
+ *
+ * Check if an RC6 CTX corruption has happened since the last check and if so
+ * disable RC6 and runtime power management.
+ *
+ * Return false if no context corruption has happened since the last call of
+ * this function, true otherwise.
+*/
+bool i915_rc6_ctx_wa_check(struct drm_i915_private *i915)
+{
+       if (!NEEDS_RC6_CTX_CORRUPTION_WA(i915))
+               return false;
+
+       if (i915->rps.ctx_corrupted)
+               return false;
+
+       if (!i915_rc6_ctx_corrupted(i915))
+               return false;
+
+       DRM_NOTE("RC6 context corruption, disabling runtime power management\n");
+
+       intel_disable_rc6(i915->dev);
+       i915->rps.ctx_corrupted = true;
+       intel_runtime_pm_get_noresume(i915);
+
+       return true;
+}
+
 void intel_init_gt_powersave(struct drm_device *dev)
 {
        i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6);
 
+       i915_rc6_ctx_wa_init(to_i915(dev));
+
        if (IS_CHERRYVIEW(dev))
                cherryview_init_gt_powersave(dev);
        else if (IS_VALLEYVIEW(dev))
@@ -6152,6 +6257,8 @@ void intel_cleanup_gt_powersave(struct drm_device *dev)
                return;
        else if (IS_VALLEYVIEW(dev))
                valleyview_cleanup_gt_powersave(dev);
+
+       i915_rc6_ctx_wa_cleanup(to_i915(dev));
 }
 
 static void gen6_suspend_rps(struct drm_device *dev)
@@ -6184,6 +6291,38 @@ void intel_suspend_gt_powersave(struct drm_device *dev)
        gen6_rps_idle(dev_priv);
 }
 
+static void __intel_disable_rc6(struct drm_device *dev)
+{
+       if (INTEL_INFO(dev)->gen >= 9)
+               gen9_disable_rc6(dev);
+       else if (IS_CHERRYVIEW(dev))
+               cherryview_disable_rc6(dev);
+       else if (IS_VALLEYVIEW(dev))
+               valleyview_disable_rc6(dev);
+       else
+               gen6_disable_rc6(dev);
+}
+
+static void intel_disable_rc6(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+
+       mutex_lock(&dev_priv->rps.hw_lock);
+       __intel_disable_rc6(dev);
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void intel_disable_rps(struct drm_device *dev)
+{
+       if (IS_CHERRYVIEW(dev) || IS_VALLEYVIEW(dev))
+               return;
+
+       if (INTEL_INFO(dev)->gen >= 9)
+               gen9_disable_rps(dev);
+       else
+               gen6_disable_rps(dev);
+}
+
 void intel_disable_gt_powersave(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -6194,16 +6333,12 @@ void intel_disable_gt_powersave(struct drm_device *dev)
                intel_suspend_gt_powersave(dev);
 
                mutex_lock(&dev_priv->rps.hw_lock);
-               if (INTEL_INFO(dev)->gen >= 9)
-                       gen9_disable_rps(dev);
-               else if (IS_CHERRYVIEW(dev))
-                       cherryview_disable_rps(dev);
-               else if (IS_VALLEYVIEW(dev))
-                       valleyview_disable_rps(dev);
-               else
-                       gen6_disable_rps(dev);
+
+               __intel_disable_rc6(dev);
+               intel_disable_rps(dev);
 
                dev_priv->rps.enabled = false;
+
                mutex_unlock(&dev_priv->rps.hw_lock);
        }
 }