drm/i915/bxt: Add HW state verification for DDI PHY and CDCLK
authorImre Deak <imre.deak@intel.com>
Mon, 4 Apr 2016 14:27:10 +0000 (17:27 +0300)
committerImre Deak <imre.deak@intel.com>
Fri, 15 Apr 2016 11:48:19 +0000 (14:48 +0300)
I caught a few errors in our current PHY/CDCLK programming by sanity
checking the actual programmed state, so I thought it would be also
useful for the future. In addition to verifying the state after
programming it also verify it after exiting DC5, to make sure DMC
restored/kept intact everything related.

v2:
- Inlining __phy_reg_verify_state() doesn't make sense and also
  incorrect, so don't do it (PW/CI gcc)
v3:
- Rebase on latest -nightly

Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: David Weinehall <david.weinehall@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1459780030-15781-1-git-send-email-imre.deak@intel.com
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_runtime_pm.c

index b9ed1b305bebe29cf2df8e047be1c1955a2be689..946b5df7b7dfb0f52323e4cd8931c21967ddf573 100644 (file)
@@ -1924,6 +1924,7 @@ struct drm_i915_private {
         * crappiness (can't read out DPLL_MD for pipes B & C).
         */
        u32 chv_dpll_md[I915_MAX_PIPES];
+       u32 bxt_phy_grc;
 
        u32 suspend_count;
        bool suspended_to_idle;
index a3db4daec844992b28accc1f3adf21da820ec19c..96234c58985074647c6943e7199cd9777c858701 100644 (file)
@@ -1753,6 +1753,13 @@ static bool broxton_phy_is_enabled(struct drm_i915_private *dev_priv,
        return true;
 }
 
+static u32 broxton_get_grc(struct drm_i915_private *dev_priv, enum dpio_phy phy)
+{
+       u32 val = I915_READ(BXT_PORT_REF_DW6(phy));
+
+       return (val & GRC_CODE_MASK) >> GRC_CODE_SHIFT;
+}
+
 static void broxton_phy_init(struct drm_i915_private *dev_priv,
                             enum dpio_phy phy)
 {
@@ -1762,6 +1769,9 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv,
        if (broxton_phy_is_enabled(dev_priv, phy)) {
                DRM_DEBUG_DRIVER("DDI PHY %d already enabled, "
                                 "won't reprogram it\n", phy);
+               /* Still read out the GRC value for state verification */
+               if (phy == DPIO_PHY1)
+                       dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv, phy);
 
                return;
        }
@@ -1857,8 +1867,8 @@ static void broxton_phy_init(struct drm_i915_private *dev_priv,
                             10))
                        DRM_ERROR("timeout waiting for PHY1 GRC\n");
 
-               val = I915_READ(BXT_PORT_REF_DW6(DPIO_PHY1));
-               val = (val & GRC_CODE_MASK) >> GRC_CODE_SHIFT;
+               val = dev_priv->bxt_phy_grc = broxton_get_grc(dev_priv,
+                                                             DPIO_PHY1);
                grc_code = val << GRC_CODE_FAST_SHIFT |
                           val << GRC_CODE_SLOW_SHIFT |
                           val;
@@ -1901,6 +1911,116 @@ void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv)
        broxton_phy_uninit(dev_priv, DPIO_PHY0);
 }
 
+static bool __printf(6, 7)
+__phy_reg_verify_state(struct drm_i915_private *dev_priv, enum dpio_phy phy,
+                      i915_reg_t reg, u32 mask, u32 expected,
+                      const char *reg_fmt, ...)
+{
+       struct va_format vaf;
+       va_list args;
+       u32 val;
+
+       val = I915_READ(reg);
+       if ((val & mask) == expected)
+               return true;
+
+       va_start(args, reg_fmt);
+       vaf.fmt = reg_fmt;
+       vaf.va = &args;
+
+       DRM_DEBUG_DRIVER("DDI PHY %d reg %pV [%08x] state mismatch: "
+                        "current %08x, expected %08x (mask %08x)\n",
+                        phy, &vaf, reg.reg, val, (val & ~mask) | expected,
+                        mask);
+
+       va_end(args);
+
+       return false;
+}
+
+static bool broxton_phy_verify_state(struct drm_i915_private *dev_priv,
+                                    enum dpio_phy phy)
+{
+       enum port port;
+       u32 ports;
+       uint32_t mask;
+       bool ok;
+
+#define _CHK(reg, mask, exp, fmt, ...)                                 \
+       __phy_reg_verify_state(dev_priv, phy, reg, mask, exp, fmt,      \
+                              ## __VA_ARGS__)
+
+       /* We expect the PHY to be always enabled */
+       if (!broxton_phy_is_enabled(dev_priv, phy))
+               return false;
+
+       ok = true;
+
+       if (phy == DPIO_PHY0)
+               ports = BIT(PORT_B) | BIT(PORT_C);
+       else
+               ports = BIT(PORT_A);
+
+       for_each_port_masked(port, ports) {
+               int lane;
+
+               for (lane = 0; lane < 4; lane++)
+                       ok &= _CHK(BXT_PORT_TX_DW14_LN(port, lane),
+                                   LATENCY_OPTIM,
+                                   lane != 1 ? LATENCY_OPTIM : 0,
+                                   "BXT_PORT_TX_DW14_LN(%d, %d)", port, lane);
+       }
+
+       /* PLL Rcomp code offset */
+       ok &= _CHK(BXT_PORT_CL1CM_DW9(phy),
+                   IREF0RC_OFFSET_MASK, 0xe4 << IREF0RC_OFFSET_SHIFT,
+                   "BXT_PORT_CL1CM_DW9(%d)", phy);
+       ok &= _CHK(BXT_PORT_CL1CM_DW10(phy),
+                   IREF1RC_OFFSET_MASK, 0xe4 << IREF1RC_OFFSET_SHIFT,
+                   "BXT_PORT_CL1CM_DW10(%d)", phy);
+
+       /* Power gating */
+       mask = OCL1_POWER_DOWN_EN | DW28_OLDO_DYN_PWR_DOWN_EN | SUS_CLK_CONFIG;
+       ok &= _CHK(BXT_PORT_CL1CM_DW28(phy), mask, mask,
+                   "BXT_PORT_CL1CM_DW28(%d)", phy);
+
+       if (phy == DPIO_PHY0)
+               ok &= _CHK(BXT_PORT_CL2CM_DW6_BC,
+                          DW6_OLDO_DYN_PWR_DOWN_EN, DW6_OLDO_DYN_PWR_DOWN_EN,
+                          "BXT_PORT_CL2CM_DW6_BC");
+
+       /*
+        * TODO: Verify BXT_PORT_CL1CM_DW30 bit OCL2_LDOFUSE_PWR_DIS,
+        * at least on stepping A this bit is read-only and fixed at 0.
+        */
+
+       if (phy == DPIO_PHY0) {
+               u32 grc_code = dev_priv->bxt_phy_grc;
+
+               grc_code = grc_code << GRC_CODE_FAST_SHIFT |
+                          grc_code << GRC_CODE_SLOW_SHIFT |
+                          grc_code;
+               mask = GRC_CODE_FAST_MASK | GRC_CODE_SLOW_MASK |
+                      GRC_CODE_NOM_MASK;
+               ok &= _CHK(BXT_PORT_REF_DW6(DPIO_PHY0), mask, grc_code,
+                           "BXT_PORT_REF_DW6(%d)", DPIO_PHY0);
+
+               mask = GRC_DIS | GRC_RDY_OVRD;
+               ok &= _CHK(BXT_PORT_REF_DW8(DPIO_PHY0), mask, mask,
+                           "BXT_PORT_REF_DW8(%d)", DPIO_PHY0);
+       }
+
+       return ok;
+#undef _CHK
+}
+
+void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv)
+{
+       if (!broxton_phy_verify_state(dev_priv, DPIO_PHY0) ||
+           !broxton_phy_verify_state(dev_priv, DPIO_PHY1))
+               i915_report_error(dev_priv, "DDI PHY state mismatch\n");
+}
+
 void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
index 089cf603a6f7b478e9d2b8b0e056a27e999fa298..f587f7dd8ad9854c650c2df0bc053f24622eeb2e 100644 (file)
@@ -5468,6 +5468,11 @@ static bool broxton_cdclk_is_enabled(struct drm_i915_private *dev_priv)
        return true;
 }
 
+bool broxton_cdclk_verify_state(struct drm_i915_private *dev_priv)
+{
+       return broxton_cdclk_is_enabled(dev_priv);
+}
+
 void broxton_init_cdclk(struct drm_i915_private *dev_priv)
 {
        /* check if cd clock is enabled */
index f8c26a5b715ac240eb313f11a15327f74b181fae..72153359cbb3c26ee8925ca6f0ac62a1478dd6c7 100644 (file)
@@ -1226,8 +1226,10 @@ void hsw_enable_pc8(struct drm_i915_private *dev_priv);
 void hsw_disable_pc8(struct drm_i915_private *dev_priv);
 void broxton_init_cdclk(struct drm_i915_private *dev_priv);
 void broxton_uninit_cdclk(struct drm_i915_private *dev_priv);
+bool broxton_cdclk_verify_state(struct drm_i915_private *dev_priv);
 void broxton_ddi_phy_init(struct drm_i915_private *dev_priv);
 void broxton_ddi_phy_uninit(struct drm_i915_private *dev_priv);
+void broxton_ddi_phy_verify_state(struct drm_i915_private *dev_priv);
 void bxt_enable_dc9(struct drm_i915_private *dev_priv);
 void bxt_disable_dc9(struct drm_i915_private *dev_priv);
 void skl_init_cdclk(struct drm_i915_private *dev_priv);
index e3f70c53c57ee2beb5799611b961e6f9215cb04d..cb20606d7d57fca41508e3bdda7bfe88a010eb97 100644 (file)
@@ -799,6 +799,11 @@ static void gen9_dc_off_power_well_enable(struct drm_i915_private *dev_priv,
                                          struct i915_power_well *power_well)
 {
        gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+       if (IS_BROXTON(dev_priv)) {
+               broxton_cdclk_verify_state(dev_priv);
+               broxton_ddi_phy_verify_state(dev_priv);
+       }
 }
 
 static void gen9_dc_off_power_well_disable(struct drm_i915_private *dev_priv,
@@ -2199,6 +2204,9 @@ void bxt_display_core_init(struct drm_i915_private *dev_priv,
        broxton_init_cdclk(dev_priv);
        broxton_ddi_phy_init(dev_priv);
 
+       broxton_cdclk_verify_state(dev_priv);
+       broxton_ddi_phy_verify_state(dev_priv);
+
        if (resume && dev_priv->csr.dmc_payload)
                intel_csr_load_program(dev_priv);
 }