Although register information in the bspec includes a field that is supposed to reflect a register's reset characteristics (i.e., whether a register maintains its value through engine resets), it's been discovered that this information is incorrect for some register ranges (i.e., registers that are not affected by engine resets are tagged in a way that indicates they would be). We can sanity check workaround registers placed on the RCS/CCS engine workaround lists (including those placed there via the general_render_compute_wa_init() function) by comparing against the forcewake table. As far as we know, there's never a case where a register that lives outside the RENDER powerwell will be reset by an RCS/CCS engine reset. Signed-off-by: Matt Roper <matthew.d.roper@xxxxxxxxx> --- .../gpu/drm/i915/gt/selftest_workarounds.c | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/gpu/drm/i915/gt/selftest_workarounds.c b/drivers/gpu/drm/i915/gt/selftest_workarounds.c index 14a8b25b6204..1bc8febc5c1d 100644 --- a/drivers/gpu/drm/i915/gt/selftest_workarounds.c +++ b/drivers/gpu/drm/i915/gt/selftest_workarounds.c @@ -1362,12 +1362,64 @@ live_engine_reset_workarounds(void *arg) return ret; } +/* + * The bspec's documentation for register reset behavior can be unreliable for + * some MMIO ranges. But in general we do not expect registers outside the + * RENDER forcewake domain to be reset by RCS/CCS engine resets. If we find + * workaround registers on an RCS or CCS engine's list, it likely indicates + * the register is misdocumented in the bspec and the workaround implementation + * should be moved to the GT workaround list instead. + */ +static int +live_check_engine_workarounds_fw(void *arg) +{ + struct intel_gt *gt = arg; + struct intel_engine_cs *engine; + struct wa_lists *lists; + enum intel_engine_id id; + int ret = 0; + + lists = kzalloc(sizeof(*lists), GFP_KERNEL); + if (!lists) + return -ENOMEM; + + reference_lists_init(gt, lists); + + for_each_engine(engine, gt, id) { + struct i915_wa_list *wal = &lists->engine[id].wa_list; + struct i915_wa *wa; + int i; + + if (engine->class != RENDER_CLASS && + engine->class != COMPUTE_CLASS) + continue; + + for (i = 0, wa = wal->list; i < wal->count; i++, wa++) { + enum forcewake_domains fw; + + fw = intel_uncore_forcewake_for_reg(gt->uncore, wa->reg, + FW_REG_READ | FW_REG_WRITE); + if ((fw & FORCEWAKE_RENDER) == 0) { + pr_err("%s: Register %#x not in RENDER forcewake domain!\n", + engine->name, i915_mmio_reg_offset(wa->reg)); + ret = -EINVAL; + } + } + } + + reference_lists_fini(gt, lists); + kfree(lists); + + return ret; +} + int intel_workarounds_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(live_dirty_whitelist), SUBTEST(live_reset_whitelist), SUBTEST(live_isolated_whitelist), + SUBTEST(live_check_engine_workarounds_fw), SUBTEST(live_gpu_reset_workarounds), SUBTEST(live_engine_reset_workarounds), }; -- 2.39.1