linux/drivers/gpu/drm/i915/gt/selftest_gt_pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: MIT
   2/*
   3 * Copyright © 2019 Intel Corporation
   4 */
   5
   6#include <linux/sort.h>
   7
   8#include "intel_engine_regs.h"
   9#include "intel_gt_clock_utils.h"
  10
  11#include "selftest_llc.h"
  12#include "selftest_rc6.h"
  13#include "selftest_rps.h"
  14
  15static int cmp_u64(const void *A, const void *B)
  16{
  17        const u64 *a = A, *b = B;
  18
  19        if (a < b)
  20                return -1;
  21        else if (a > b)
  22                return 1;
  23        else
  24                return 0;
  25}
  26
  27static int cmp_u32(const void *A, const void *B)
  28{
  29        const u32 *a = A, *b = B;
  30
  31        if (a < b)
  32                return -1;
  33        else if (a > b)
  34                return 1;
  35        else
  36                return 0;
  37}
  38
  39static void measure_clocks(struct intel_engine_cs *engine,
  40                           u32 *out_cycles, ktime_t *out_dt)
  41{
  42        ktime_t dt[5];
  43        u32 cycles[5];
  44        int i;
  45
  46        for (i = 0; i < 5; i++) {
  47                local_irq_disable();
  48                cycles[i] = -ENGINE_READ_FW(engine, RING_TIMESTAMP);
  49                dt[i] = ktime_get();
  50
  51                udelay(1000);
  52
  53                dt[i] = ktime_sub(ktime_get(), dt[i]);
  54                cycles[i] += ENGINE_READ_FW(engine, RING_TIMESTAMP);
  55                local_irq_enable();
  56        }
  57
  58        /* Use the median of both cycle/dt; close enough */
  59        sort(cycles, 5, sizeof(*cycles), cmp_u32, NULL);
  60        *out_cycles = (cycles[1] + 2 * cycles[2] + cycles[3]) / 4;
  61
  62        sort(dt, 5, sizeof(*dt), cmp_u64, NULL);
  63        *out_dt = div_u64(dt[1] + 2 * dt[2] + dt[3], 4);
  64}
  65
  66static int live_gt_clocks(void *arg)
  67{
  68        struct intel_gt *gt = arg;
  69        struct intel_engine_cs *engine;
  70        enum intel_engine_id id;
  71        int err = 0;
  72
  73        if (!gt->clock_frequency) { /* unknown */
  74                pr_info("CS_TIMESTAMP frequency unknown\n");
  75                return 0;
  76        }
  77
  78        if (GRAPHICS_VER(gt->i915) < 4) /* Any CS_TIMESTAMP? */
  79                return 0;
  80
  81        if (GRAPHICS_VER(gt->i915) == 5)
  82                /*
  83                 * XXX CS_TIMESTAMP low dword is dysfunctional?
  84                 *
  85                 * Ville's experiments indicate the high dword still works,
  86                 * but at a correspondingly reduced frequency.
  87                 */
  88                return 0;
  89
  90        if (GRAPHICS_VER(gt->i915) == 4)
  91                /*
  92                 * XXX CS_TIMESTAMP appears gibberish
  93                 *
  94                 * Ville's experiments indicate that it mostly appears 'stuck'
  95                 * in that we see the register report the same cycle count
  96                 * for a couple of reads.
  97                 */
  98                return 0;
  99
 100        intel_gt_pm_get(gt);
 101        intel_uncore_forcewake_get(gt->uncore, FORCEWAKE_ALL);
 102
 103        for_each_engine(engine, gt, id) {
 104                u32 cycles;
 105                u32 expected;
 106                u64 time;
 107                u64 dt;
 108
 109                if (GRAPHICS_VER(engine->i915) < 7 && engine->id != RCS0)
 110                        continue;
 111
 112                measure_clocks(engine, &cycles, &dt);
 113
 114                time = intel_gt_clock_interval_to_ns(engine->gt, cycles);
 115                expected = intel_gt_ns_to_clock_interval(engine->gt, dt);
 116
 117                pr_info("%s: TIMESTAMP %d cycles [%lldns] in %lldns [%d cycles], using CS clock frequency of %uKHz\n",
 118                        engine->name, cycles, time, dt, expected,
 119                        engine->gt->clock_frequency / 1000);
 120
 121                if (9 * time < 8 * dt || 8 * time > 9 * dt) {
 122                        pr_err("%s: CS ticks did not match walltime!\n",
 123                               engine->name);
 124                        err = -EINVAL;
 125                        break;
 126                }
 127
 128                if (9 * expected < 8 * cycles || 8 * expected > 9 * cycles) {
 129                        pr_err("%s: walltime did not match CS ticks!\n",
 130                               engine->name);
 131                        err = -EINVAL;
 132                        break;
 133                }
 134        }
 135
 136        intel_uncore_forcewake_put(gt->uncore, FORCEWAKE_ALL);
 137        intel_gt_pm_put(gt);
 138
 139        return err;
 140}
 141
 142static int live_gt_resume(void *arg)
 143{
 144        struct intel_gt *gt = arg;
 145        IGT_TIMEOUT(end_time);
 146        int err;
 147
 148        /* Do several suspend/resume cycles to check we don't explode! */
 149        do {
 150                intel_gt_suspend_prepare(gt);
 151                intel_gt_suspend_late(gt);
 152
 153                if (gt->rc6.enabled) {
 154                        pr_err("rc6 still enabled after suspend!\n");
 155                        intel_gt_set_wedged_on_init(gt);
 156                        err = -EINVAL;
 157                        break;
 158                }
 159
 160                err = intel_gt_resume(gt);
 161                if (err)
 162                        break;
 163
 164                if (gt->rc6.supported && !gt->rc6.enabled) {
 165                        pr_err("rc6 not enabled upon resume!\n");
 166                        intel_gt_set_wedged_on_init(gt);
 167                        err = -EINVAL;
 168                        break;
 169                }
 170
 171                err = st_llc_verify(&gt->llc);
 172                if (err) {
 173                        pr_err("llc state not restored upon resume!\n");
 174                        intel_gt_set_wedged_on_init(gt);
 175                        break;
 176                }
 177        } while (!__igt_timeout(end_time, NULL));
 178
 179        return err;
 180}
 181
 182int intel_gt_pm_live_selftests(struct drm_i915_private *i915)
 183{
 184        static const struct i915_subtest tests[] = {
 185                SUBTEST(live_gt_clocks),
 186                SUBTEST(live_rc6_manual),
 187                SUBTEST(live_rps_clock_interval),
 188                SUBTEST(live_rps_control),
 189                SUBTEST(live_rps_frequency_cs),
 190                SUBTEST(live_rps_frequency_srm),
 191                SUBTEST(live_rps_power),
 192                SUBTEST(live_rps_interrupt),
 193                SUBTEST(live_rps_dynamic),
 194                SUBTEST(live_gt_resume),
 195        };
 196
 197        if (intel_gt_is_wedged(to_gt(i915)))
 198                return 0;
 199
 200        return intel_gt_live_subtests(tests, to_gt(i915));
 201}
 202
 203int intel_gt_pm_late_selftests(struct drm_i915_private *i915)
 204{
 205        static const struct i915_subtest tests[] = {
 206                /*
 207                 * These tests may leave the system in an undesirable state.
 208                 * They are intended to be run last in CI and the system
 209                 * rebooted afterwards.
 210                 */
 211                SUBTEST(live_rc6_ctx_wa),
 212        };
 213
 214        if (intel_gt_is_wedged(to_gt(i915)))
 215                return 0;
 216
 217        return intel_gt_live_subtests(tests, to_gt(i915));
 218}
 219