@@ -1913,7 +1913,10 @@ struct i915_wa_reg {
struct i915_workarounds {
struct i915_wa_reg reg[I915_MAX_WA_REGS];
+ /* list of registers (and their values) that GuC will have to restore */
+ struct i915_wa_reg guc_reg[GUC_REGSET_MAX_REGISTERS];
u32 count;
+ u32 guc_count;
u32 hw_whitelist_count[I915_NUM_ENGINES];
};
@@ -1001,6 +1001,24 @@ static void guc_policies_init(struct guc_policies *policies)
policies->is_valid = 1;
}
+/*
+ * In this macro it is highly unlikely to exceed max value but even if we did
+ * it is not an error so just throw a warning and continue. Only side effect
+ * in continuing further means some registers won't be added to save/restore
+ * list.
+ */
+#define GUC_ADD_MMIO_REG_ADS(node, reg_addr, _flags, defvalue) \
+ do { \
+ u32 __count = node->number_of_registers; \
+ if (WARN_ON(__count >= GUC_REGSET_MAX_REGISTERS)) \
+ continue; \
+ node->registers[__count].offset = reg_addr.reg; \
+ node->registers[__count].flags = (_flags); \
+ if (defvalue) \
+ node->registers[__count].value = (defvalue); \
+ node->number_of_registers++; \
+ } while (0)
+
static int guc_ads_create(struct intel_guc *guc)
{
struct drm_i915_private *dev_priv = guc_to_i915(guc);
@@ -1014,6 +1032,7 @@ static int guc_ads_create(struct intel_guc *guc)
u8 reg_state_buffer[GUC_S3_SAVE_SPACE_PAGES * PAGE_SIZE];
} __packed *blob;
struct intel_engine_cs *engine;
+ struct i915_workarounds *workarounds = &dev_priv->workarounds;
enum intel_engine_id id;
u32 base;
@@ -1033,6 +1052,47 @@ static int guc_ads_create(struct intel_guc *guc)
/* MMIO reg state */
for_each_engine(engine, dev_priv, id) {
+ u32 i;
+ struct guc_mmio_regset *eng_reg =
+ &blob->reg_state.engine_reg[engine->guc_id];
+
+ /*
+ * Provide a list of registers to be saved/restored during gpu
+ * reset. This is mainly required for Media reset (aka watchdog
+ * timeout) which is completely under the control of GuC
+ * (resubmission of hung workload is handled inside GuC).
+ */
+ GUC_ADD_MMIO_REG_ADS(eng_reg, RING_HWS_PGA(engine->mmio_base),
+ GUC_REGSET_ENGINERESET |
+ GUC_REGSET_SAVE_CURRENT_VALUE, 0);
+
+ /*
+ * Workaround the guc issue with masked registers, note that
+ * at this point guc submission is still disabled and the mode
+ * register doesnt have the irq_steering bit set, which we
+ * need to fwd irqs to GuC.
+ */
+ GUC_ADD_MMIO_REG_ADS(eng_reg, RING_MODE_GEN7(engine),
+ GUC_REGSET_ENGINERESET |
+ GUC_REGSET_SAVE_DEFAULT_VALUE,
+ I915_READ(RING_MODE_GEN7(engine)) |
+ GFX_INTERRUPT_STEERING | (0xFFFF<<16));
+
+ GUC_ADD_MMIO_REG_ADS(eng_reg, RING_IMR(engine->mmio_base),
+ GUC_REGSET_ENGINERESET |
+ GUC_REGSET_SAVE_CURRENT_VALUE, 0);
+
+ /* ask guc to re-apply workarounds set in *_init_workarounds */
+ for (i = 0; i < workarounds->guc_count; i++)
+ GUC_ADD_MMIO_REG_ADS(eng_reg,
+ workarounds->guc_reg[i].addr,
+ GUC_REGSET_ENGINERESET |
+ GUC_REGSET_SAVE_DEFAULT_VALUE,
+ workarounds->guc_reg[i].value);
+
+ DRM_DEBUG_DRIVER("%s register save/restore count: %u\n",
+ engine->name, eng_reg->number_of_registers);
+
blob->reg_state.white_list[engine->guc_id].mmio_start =
i915_mmio_reg_offset(RING_FORCE_TO_NONPRIV(engine->mmio_base, 0));
@@ -1042,9 +1102,13 @@ static int guc_ads_create(struct intel_guc *guc)
* inconsistencies with the handling of FORCE_TO_NONPRIV
* registers.
*/
- blob->reg_state.white_list[engine->guc_id].count = 0;
+ blob->reg_state.white_list[engine->guc_id].count =
+ workarounds->hw_whitelist_count[id];
- /* Nothing to be saved or restored for now. */
+ for (i = 0; i < workarounds->hw_whitelist_count[id]; i++) {
+ blob->reg_state.white_list[engine->guc_id].offsets[i] =
+ I915_READ(RING_FORCE_TO_NONPRIV(engine->mmio_base, i));
+ }
}
/*
@@ -623,6 +623,29 @@ static int wa_ring_whitelist_reg(struct intel_engine_cs *engine,
return 0;
}
+static int guc_wa_add(struct drm_i915_private *dev_priv,
+ i915_reg_t addr, const u32 val)
+{
+ const u32 idx = dev_priv->workarounds.guc_count;
+
+ I915_WRITE(addr, val);
+ if (WARN_ON(idx >= GUC_REGSET_MAX_REGISTERS))
+ return -ENOSPC;
+
+ dev_priv->workarounds.guc_reg[idx].addr = addr;
+ /* GuC can't handle masked regs, so we store the value|mask together */
+ dev_priv->workarounds.guc_reg[idx].value = val;
+ dev_priv->workarounds.guc_count++;
+
+ return 0;
+}
+
+#define WA_REG_WR_GUC_RESTORE(addr, val) do { \
+ const int r = guc_wa_add(dev_priv, (addr), (val)); \
+ if (r) \
+ return r; \
+ } while (0)
+
static int gen8_init_workarounds(struct intel_engine_cs *engine)
{
struct drm_i915_private *dev_priv = engine->i915;
@@ -730,15 +753,16 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
int ret;
/* WaConextSwitchWithConcurrentTLBInvalidate:skl,bxt,kbl,glk */
- I915_WRITE(GEN9_CSFE_CHICKEN1_RCS, _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
+ WA_REG_WR_GUC_RESTORE(GEN9_CSFE_CHICKEN1_RCS,
+ _MASKED_BIT_ENABLE(GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE));
/* WaEnableLbsSlaRetryTimerDecrement:skl,bxt,kbl,glk */
- I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
- GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
+ WA_REG_WR_GUC_RESTORE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) |
+ GEN9_LBS_SLA_RETRY_TIMER_DECREMENT_ENABLE);
/* WaDisableKillLogic:bxt,skl,kbl */
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- ECOCHK_DIS_TLB);
+ WA_REG_WR_GUC_RESTORE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ ECOCHK_DIS_TLB);
/* WaClearFlowControlGpgpuContextSave:skl,bxt,kbl,glk */
/* WaDisablePartialInstShootdown:skl,bxt,kbl,glk */
@@ -807,8 +831,8 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
HDC_FORCE_NON_COHERENT);
/* WaDisableHDCInvalidation:skl,bxt,kbl */
- I915_WRITE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
- BDW_DISABLE_HDC_INVALIDATION);
+ WA_REG_WR_GUC_RESTORE(GAM_ECOCHK, I915_READ(GAM_ECOCHK) |
+ BDW_DISABLE_HDC_INVALIDATION);
/* WaDisableSamplerPowerBypassForSOPingPong:skl,bxt,kbl */
if (IS_SKYLAKE(dev_priv) ||
@@ -821,8 +845,8 @@ static int gen9_init_workarounds(struct intel_engine_cs *engine)
WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE);
/* WaOCLCoherentLineFlush:skl,bxt,kbl */
- I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) |
- GEN8_LQSC_FLUSH_COHERENT_LINES));
+ WA_REG_WR_GUC_RESTORE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) |
+ GEN8_LQSC_FLUSH_COHERENT_LINES));
/* WaVFEStateAfterPipeControlwithMediaStateClear:skl,bxt,glk */
ret = wa_ring_whitelist_reg(engine, GEN9_CTX_PREEMPT_REG);
@@ -897,12 +921,12 @@ static int skl_init_workarounds(struct intel_engine_cs *engine)
* until D0 which is the default case so this is equivalent to
* !WaDisablePerCtxtPreemptionGranularityControl:skl
*/
- I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1,
- _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
+ WA_REG_WR_GUC_RESTORE(GEN7_FF_SLICE_CS_CHICKEN1,
+ _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL));
/* WaEnableGapsTsvCreditFix:skl */
- I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
- GEN9_GAPS_TSV_CREDIT_DISABLE));
+ WA_REG_WR_GUC_RESTORE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
+ GEN9_GAPS_TSV_CREDIT_DISABLE));
/* WaDisableGafsUnitClkGating:skl */
WA_SET_BIT(GEN7_UCGCTL4, GEN8_EU_GAUNIT_CLOCK_GATE_DISABLE);
@@ -932,12 +956,12 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine)
/* WaStoreMultiplePTEenable:bxt */
/* This is a requirement according to Hardware specification */
if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1))
- I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_TLBPF);
+ WA_REG_WR_GUC_RESTORE(TILECTL, I915_READ(TILECTL) | TILECTL_TLBPF);
/* WaSetClckGatingDisableMedia:bxt */
if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) {
- I915_WRITE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) &
- ~GEN8_DOP_CLOCK_GATE_MEDIA_ENABLE));
+ WA_REG_WR_GUC_RESTORE(GEN7_MISCCPCTL, (I915_READ(GEN7_MISCCPCTL) &
+ ~GEN8_DOP_CLOCK_GATE_MEDIA_ENABLE));
}
/* WaDisableThreadStallDopClockGating:bxt */
@@ -973,8 +997,8 @@ static int bxt_init_workarounds(struct intel_engine_cs *engine)
/* WaProgramL3SqcReg1DefaultForPerf:bxt */
if (IS_BXT_REVID(dev_priv, BXT_REVID_B0, REVID_FOREVER))
- I915_WRITE(GEN8_L3SQCREG1, L3_GENERAL_PRIO_CREDITS(62) |
- L3_HIGH_PRIO_CREDITS(2));
+ WA_REG_WR_GUC_RESTORE(GEN8_L3SQCREG1, L3_GENERAL_PRIO_CREDITS(62) |
+ L3_HIGH_PRIO_CREDITS(2));
/* WaToEnableHwFixForPushConstHWBug:bxt */
if (IS_BXT_REVID(dev_priv, BXT_REVID_C0, REVID_FOREVER))
@@ -999,8 +1023,8 @@ static int kbl_init_workarounds(struct intel_engine_cs *engine)
return ret;
/* WaEnableGapsTsvCreditFix:kbl */
- I915_WRITE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
- GEN9_GAPS_TSV_CREDIT_DISABLE));
+ WA_REG_WR_GUC_RESTORE(GEN8_GARBCNTL, (I915_READ(GEN8_GARBCNTL) |
+ GEN9_GAPS_TSV_CREDIT_DISABLE));
/* WaDisableDynamicCreditSharing:kbl */
if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_B0))
@@ -1061,6 +1085,7 @@ int init_workarounds_ring(struct intel_engine_cs *engine)
WARN_ON(engine->id != RCS);
dev_priv->workarounds.count = 0;
+ dev_priv->workarounds.guc_count = 0;
dev_priv->workarounds.hw_whitelist_count[engine->id] = 0;
if (IS_BROADWELL(dev_priv))