[PATCH v9 13/21] drm/i915/guc: Provide register list to be saved/restored during engine reset

Michel Thierry
2017-06-15T20:18:32Z

GuC expects a list of registers from the driver which are saved/restored
during engine reset. The type of value to be saved is controlled by
flags. We provide a minimal set of registers that we want GuC to save and
restore. This is not an issue in case of engine reset as driver initializes
most of them following an engine reset, but in case of media reset (aka
watchdog reset) which is completely internal to GuC (including resubmission
of hung workload), it is necessary to provide this list, otherwise GuC won't
be able to schedule further workloads after a reset. This is the minimal
set of registers identified for things to work as expected but if we see
any new issues, this register list can be expanded.

In order to not loose any existing workarounds, we have to let GuC know
the registers and its values. These will be reapplied after the reset.
Note that we can't just read the current value because most of these
registers are masked (so we have a workaround for a workaround for a
workaround).

v2: REGSET_MASKED is too difficult for GuC, use REGSET_SAVE_DEFAULT_VALUE
and current value from RING_MODE reg instead; no need to preserve
head/tail either, be extra paranoid and save whitelisted registers (Daniele).

v3: Workarounds added only once during _init_workarounds also have to
been restored, or we risk loosing them after internal GuC reset
(Daniele).

v4: Rename macro used to keep track the workaround registers we will
have to restore after reset (s/I915_GUC_REG_WRITE/WA_REG_WR_GUC_RESTORE).

v5: Only ask guc to reapply workarounds in case of render reset (Daniele).

Cc: Daniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Signed-off-by: Arun Siluvery <arun.siluvery@linux.intel.com>
Signed-off-by: Jeff McGee <jeff.mcgee@intel.com>
Signed-off-by: Michel Thierry <michel.thierry@intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 3 ++
drivers/gpu/drm/i915/i915_guc_submission.c | 70 +++++++++++++++++++++++++++++-
drivers/gpu/drm/i915/intel_engine_cs.c | 65 ++++++++++++++++++---------
3 files changed, 116 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6e98ab53f55e..f68cfdf640f3 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1873,7 +1873,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];
};

diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c
index 4913ec991f4d..2bb18d24ed42 100644
--- a/drivers/gpu/drm/i915/i915_guc_submission.c
+++ b/drivers/gpu/drm/i915/i915_guc_submission.c
@@ -1018,6 +1018,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);
@@ -1031,6 +1049,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;

@@ -1050,6 +1069,49 @@ 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 */
+ if (engine->id == RCS) {
+ 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));

@@ -1059,9 +1121,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));
+ }
}

/*
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
index bc38bd128b76..5c4133abae0a 100644
--- a/drivers/gpu/drm/i915/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/intel_engine_cs.c
@@ -708,6 +708,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;
@@ -815,15 +838,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 */
@@ -894,8 +918,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) ||
@@ -908,8 +932,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);
@@ -984,12 +1008,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);
@@ -1019,12 +1043,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 */
@@ -1060,8 +1084,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))
@@ -1086,8 +1110,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))
@@ -1148,6 +1172,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))