// SPDX-License-Identifier: MIT /* * Copyright © 2019 Intel Corporation */ #include #include "intel_gt_clock_utils.h" #include "selftest_llc.h" #include "selftest_rc6.h" #include "selftest_rps.h" static int cmp_u64(const void *A, const void *B) { const u64 *a = A, *b = B; if (a < b) return -1; else if (a > b) return 1; else return 0; } static int cmp_u32(const void *A, const void *B) { const u32 *a = A, *b = B; if (a < b) return -1; else if (a > b) return 1; else return 0; } static void measure_clocks(struct intel_engine_cs *engine, u32 *out_cycles, ktime_t *out_dt) { ktime_t dt[5]; u32 cycles[5]; int i; for (i = 0; i < 5; i++) { preempt_disable(); cycles[i] = -ENGINE_READ_FW(engine, RING_TIMESTAMP); dt[i] = ktime_get(); udelay(1000); dt[i] = ktime_sub(ktime_get(), dt[i]); cycles[i] += ENGINE_READ_FW(engine, RING_TIMESTAMP); preempt_enable(); } /* Use the median of both cycle/dt; close enough */ sort(cycles, 5, sizeof(*cycles), cmp_u32, NULL); *out_cycles = (cycles[1] + 2 * cycles[2] + cycles[3]) / 4; sort(dt, 5, sizeof(*dt), cmp_u64, NULL); *out_dt = div_u64(dt[1] + 2 * dt[2] + dt[3], 4); } static int live_gt_clocks(void *arg) { struct intel_gt *gt = arg; struct intel_engine_cs *engine; enum intel_engine_id id; int err = 0; if (!gt->clock_frequency) { /* unknown */ pr_info("CS_TIMESTAMP frequency unknown\n"); return 0; } if (GRAPHICS_VER(gt->i915) < 4) /* Any CS_TIMESTAMP? */ return 0; if (GRAPHICS_VER(gt->i915) == 5) /* * XXX CS_TIMESTAMP low dword is dysfunctional? * * Ville's experiments indicate the high dword still works, * but at a correspondingly reduced frequency. */ return 0; if (GRAPHICS_VER(gt->i915) == 4) /* * XXX CS_TIMESTAMP appears gibberish * * Ville's experiments indicate that it mostly appears 'stuck' * in that we see the register report the same cycle count * for a couple of reads. */ return 0; intel_gt_pm_get(gt); intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL); for_each_engine(engine, gt, id) { u32 cycles; u32 expected; u64 time; u64 dt; if (GRAPHICS_VER(engine->i915) < 7 && engine->id != RCS0) continue; measure_clocks(engine, &cycles, &dt); time = intel_gt_clock_interval_to_ns(engine->gt, cycles); expected = intel_gt_ns_to_clock_interval(engine->gt, dt); pr_info("%s: TIMESTAMP %d cycles [%lldns] in %lldns [%d cycles], using CS clock frequency of %uKHz\n", engine->name, cycles, time, dt, expected, engine->gt->clock_frequency / 1000); if (9 * time < 8 * dt || 8 * time > 9 * dt) { pr_err("%s: CS ticks did not match walltime!\n", engine->name); err = -EINVAL; break; } if (9 * expected < 8 * cycles || 8 * expected > 9 * cycles) { pr_err("%s: walltime did not match CS ticks!\n", engine->name); err = -EINVAL; break; } } intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL); intel_gt_pm_put(gt); return err; } static int live_gt_resume(void *arg) { struct intel_gt *gt = arg; IGT_TIMEOUT(end_time); int err; /* Do several suspend/resume cycles to check we don't explode! */ do { intel_gt_suspend_prepare(gt); intel_gt_suspend_late(gt); if (gt->rc6.enabled) { pr_err("rc6 still enabled after suspend!\n"); intel_gt_set_wedged_on_init(gt); err = -EINVAL; break; } err = intel_gt_resume(gt); if (err) break; if (gt->rc6.supported && !gt->rc6.enabled) { pr_err("rc6 not enabled upon resume!\n"); intel_gt_set_wedged_on_init(gt); err = -EINVAL; break; } err = st_llc_verify(>->llc); if (err) { pr_err("llc state not restored upon resume!\n"); intel_gt_set_wedged_on_init(gt); break; } } while (!__igt_timeout(end_time, NULL)); return err; } int intel_gt_pm_live_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { SUBTEST(live_gt_clocks), SUBTEST(live_rc6_manual), SUBTEST(live_rps_clock_interval), SUBTEST(live_rps_control), SUBTEST(live_rps_frequency_cs), SUBTEST(live_rps_frequency_srm), SUBTEST(live_rps_power), SUBTEST(live_rps_interrupt), SUBTEST(live_rps_dynamic), SUBTEST(live_gt_resume), }; if (intel_gt_is_wedged(&i915->gt)) return 0; return intel_gt_live_subtests(tests, &i915->gt); } int intel_gt_pm_late_selftests(struct drm_i915_private *i915) { static const struct i915_subtest tests[] = { /* * These tests may leave the system in an undesirable state. * They are intended to be run last in CI and the system * rebooted afterwards. */ SUBTEST(live_rc6_ctx_wa), }; if (intel_gt_is_wedged(&i915->gt)) return 0; return intel_gt_live_subtests(tests, &i915->gt); }