2 * SPDX-License-Identifier: MIT
4 * Copyright © 2019 Intel Corporation
7 #include "intel_context.h"
8 #include "intel_engine_pm.h"
9 #include "intel_gt_requests.h"
10 #include "intel_ring.h"
11 #include "selftest_rc6.h"
13 #include "selftests/i915_random.h"
15 static u64 energy_uJ(struct intel_rc6 *rc6)
17 unsigned long long power;
20 if (rdmsrl_safe(MSR_RAPL_POWER_UNIT, &power))
23 units = (power & 0x1f00) >> 8;
25 if (rdmsrl_safe(MSR_PP1_ENERGY_STATUS, &power))
28 return (1000000 * power) >> units; /* convert to uJ */
31 static u64 rc6_residency(struct intel_rc6 *rc6)
35 /* XXX VLV_GT_MEDIA_RC6? */
37 result = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
38 if (HAS_RC6p(rc6_to_i915(rc6)))
39 result += intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6p);
40 if (HAS_RC6pp(rc6_to_i915(rc6)))
41 result += intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6pp);
46 int live_rc6_manual(void *arg)
48 struct intel_gt *gt = arg;
49 struct intel_rc6 *rc6 = >->rc6;
50 u64 rc0_power, rc6_power;
51 intel_wakeref_t wakeref;
57 * Our claim is that we can "encourage" the GPU to enter rc6 at will.
64 /* bsw/byt use a PCU and decouple RC6 from our manual control */
65 if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915))
68 wakeref = intel_runtime_pm_get(gt->uncore->rpm);
70 /* Force RC6 off for starters */
71 __intel_rc6_disable(rc6);
72 msleep(1); /* wakeup is not immediate, takes about 100us on icl */
74 res[0] = rc6_residency(rc6);
77 rc0_power = energy_uJ(rc6);
79 rc0_power = energy_uJ(rc6) - rc0_power;
80 dt = ktime_sub(ktime_get(), dt);
81 res[1] = rc6_residency(rc6);
82 if ((res[1] - res[0]) >> 10) {
83 pr_err("RC6 residency increased by %lldus while disabled for 250ms!\n",
84 (res[1] - res[0]) >> 10);
89 rc0_power = div64_u64(NSEC_PER_SEC * rc0_power, ktime_to_ns(dt));
91 pr_err("No power measured while in RC0\n");
96 /* Manually enter RC6 */
99 res[0] = rc6_residency(rc6);
100 intel_uncore_forcewake_flush(rc6_to_uncore(rc6), FORCEWAKE_ALL);
102 rc6_power = energy_uJ(rc6);
104 rc6_power = energy_uJ(rc6) - rc6_power;
105 dt = ktime_sub(ktime_get(), dt);
106 res[1] = rc6_residency(rc6);
107 if (res[1] == res[0]) {
108 pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x, residency=%lld\n",
109 intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE),
110 intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL),
115 rc6_power = div64_u64(NSEC_PER_SEC * rc6_power, ktime_to_ns(dt));
116 pr_info("GPU consumed %llduW in RC0 and %llduW in RC6\n",
117 rc0_power, rc6_power);
118 if (2 * rc6_power > rc0_power) {
119 pr_err("GPU leaked energy while in RC6!\n");
124 /* Restore what should have been the original state! */
125 intel_rc6_unpark(rc6);
128 intel_runtime_pm_put(gt->uncore->rpm, wakeref);
132 static const u32 *__live_rc6_ctx(struct intel_context *ce)
134 struct i915_request *rq;
139 rq = intel_context_create_request(ce);
143 cs = intel_ring_begin(rq, 4);
145 i915_request_add(rq);
149 cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT;
150 if (INTEL_GEN(rq->i915) >= 8)
154 *cs++ = i915_mmio_reg_offset(GEN8_RC6_CTX_INFO);
155 *cs++ = ce->timeline->hwsp_offset + 8;
157 intel_ring_advance(rq, cs);
159 result = rq->hwsp_seqno + 2;
160 i915_request_add(rq);
165 static struct intel_engine_cs **
166 randomised_engines(struct intel_gt *gt,
167 struct rnd_state *prng,
170 struct intel_engine_cs *engine, **engines;
171 enum intel_engine_id id;
175 for_each_engine(engine, gt, id)
180 engines = kmalloc_array(n, sizeof(*engines), GFP_KERNEL);
185 for_each_engine(engine, gt, id)
186 engines[n++] = engine;
188 i915_prandom_shuffle(engines, sizeof(*engines), n, prng);
194 int live_rc6_ctx_wa(void *arg)
196 struct intel_gt *gt = arg;
197 struct intel_engine_cs **engines;
198 unsigned int n, count;
199 I915_RND_STATE(prng);
202 /* A read of CTX_INFO upsets rc6. Poke the bear! */
203 if (INTEL_GEN(gt->i915) < 8)
206 engines = randomised_engines(gt, &prng, &count);
210 for (n = 0; n < count; n++) {
211 struct intel_engine_cs *engine = engines[n];
214 for (pass = 0; pass < 2; pass++) {
215 struct intel_context *ce;
216 unsigned int resets =
217 i915_reset_engine_count(>->i915->gpu_error,
221 /* Use a sacrifical context */
222 ce = intel_context_create(engine);
228 intel_engine_pm_get(engine);
229 res = __live_rc6_ctx(ce);
230 intel_engine_pm_put(engine);
231 intel_context_put(ce);
237 if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) {
238 intel_gt_set_wedged(gt);
243 intel_gt_pm_wait_for_idle(gt);
244 pr_debug("%s: CTX_INFO=%0x\n",
245 engine->name, READ_ONCE(*res));
248 i915_reset_engine_count(>->i915->gpu_error,
250 pr_err("%s: GPU reset required\n",
252 add_taint_for_CI(TAINT_WARN);