124f90d66SChris Wilson // SPDX-License-Identifier: MIT
2ba446f74SChris Wilson /*
3ba446f74SChris Wilson * Copyright © 2019 Intel Corporation
4ba446f74SChris Wilson */
5ba446f74SChris Wilson
6ba446f74SChris Wilson #include "intel_context.h"
7ba446f74SChris Wilson #include "intel_engine_pm.h"
845233ab2SChris Wilson #include "intel_gpu_commands.h"
9ba446f74SChris Wilson #include "intel_gt_requests.h"
10ba446f74SChris Wilson #include "intel_ring.h"
11ba446f74SChris Wilson #include "selftest_rc6.h"
12ba446f74SChris Wilson
13ba446f74SChris Wilson #include "selftests/i915_random.h"
14d4e3d455SChris Wilson #include "selftests/librapl.h"
15032d992dSChris Wilson
rc6_residency(struct intel_rc6 * rc6)1613c5a577SChris Wilson static u64 rc6_residency(struct intel_rc6 *rc6)
1713c5a577SChris Wilson {
1813c5a577SChris Wilson u64 result;
1913c5a577SChris Wilson
2013c5a577SChris Wilson /* XXX VLV_GT_MEDIA_RC6? */
2113c5a577SChris Wilson
2278d0b455SAshutosh Dixit result = intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6);
2313c5a577SChris Wilson if (HAS_RC6p(rc6_to_i915(rc6)))
2478d0b455SAshutosh Dixit result += intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6p);
2513c5a577SChris Wilson if (HAS_RC6pp(rc6_to_i915(rc6)))
2678d0b455SAshutosh Dixit result += intel_rc6_residency_ns(rc6, INTEL_RC6_RES_RC6pp);
2713c5a577SChris Wilson
2813c5a577SChris Wilson return result;
2913c5a577SChris Wilson }
3013c5a577SChris Wilson
live_rc6_manual(void * arg)31730eaeb5SChris Wilson int live_rc6_manual(void *arg)
32730eaeb5SChris Wilson {
33730eaeb5SChris Wilson struct intel_gt *gt = arg;
34730eaeb5SChris Wilson struct intel_rc6 *rc6 = >->rc6;
35032d992dSChris Wilson u64 rc0_power, rc6_power;
36730eaeb5SChris Wilson intel_wakeref_t wakeref;
37a36a4749SChris Wilson bool has_power;
38032d992dSChris Wilson ktime_t dt;
39730eaeb5SChris Wilson u64 res[2];
40730eaeb5SChris Wilson int err = 0;
41730eaeb5SChris Wilson
42730eaeb5SChris Wilson /*
43730eaeb5SChris Wilson * Our claim is that we can "encourage" the GPU to enter rc6 at will.
44730eaeb5SChris Wilson * Let's try it!
45730eaeb5SChris Wilson */
46730eaeb5SChris Wilson
47730eaeb5SChris Wilson if (!rc6->enabled)
48730eaeb5SChris Wilson return 0;
49730eaeb5SChris Wilson
50730eaeb5SChris Wilson /* bsw/byt use a PCU and decouple RC6 from our manual control */
51730eaeb5SChris Wilson if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915))
52730eaeb5SChris Wilson return 0;
53730eaeb5SChris Wilson
54a36a4749SChris Wilson has_power = librapl_supported(gt->i915);
55730eaeb5SChris Wilson wakeref = intel_runtime_pm_get(gt->uncore->rpm);
56730eaeb5SChris Wilson
57730eaeb5SChris Wilson /* Force RC6 off for starters */
58730eaeb5SChris Wilson __intel_rc6_disable(rc6);
59730eaeb5SChris Wilson msleep(1); /* wakeup is not immediate, takes about 100us on icl */
60730eaeb5SChris Wilson
6113c5a577SChris Wilson res[0] = rc6_residency(rc6);
622b703bbdSJoonas Lahtinen
63032d992dSChris Wilson dt = ktime_get();
64d4e3d455SChris Wilson rc0_power = librapl_energy_uJ();
65*599b0d8cSAnirban Sk msleep(1000);
66d4e3d455SChris Wilson rc0_power = librapl_energy_uJ() - rc0_power;
67032d992dSChris Wilson dt = ktime_sub(ktime_get(), dt);
6813c5a577SChris Wilson res[1] = rc6_residency(rc6);
69730eaeb5SChris Wilson if ((res[1] - res[0]) >> 10) {
70*599b0d8cSAnirban Sk pr_err("RC6 residency increased by %lldus while disabled for 1000ms!\n",
71730eaeb5SChris Wilson (res[1] - res[0]) >> 10);
72730eaeb5SChris Wilson err = -EINVAL;
73730eaeb5SChris Wilson goto out_unlock;
74730eaeb5SChris Wilson }
75730eaeb5SChris Wilson
76a36a4749SChris Wilson if (has_power) {
77a36a4749SChris Wilson rc0_power = div64_u64(NSEC_PER_SEC * rc0_power,
78a36a4749SChris Wilson ktime_to_ns(dt));
79032d992dSChris Wilson if (!rc0_power) {
80032d992dSChris Wilson pr_err("No power measured while in RC0\n");
81032d992dSChris Wilson err = -EINVAL;
82032d992dSChris Wilson goto out_unlock;
83032d992dSChris Wilson }
84a36a4749SChris Wilson }
85032d992dSChris Wilson
86730eaeb5SChris Wilson /* Manually enter RC6 */
87730eaeb5SChris Wilson intel_rc6_park(rc6);
88730eaeb5SChris Wilson
8913c5a577SChris Wilson res[0] = rc6_residency(rc6);
90032d992dSChris Wilson intel_uncore_forcewake_flush(rc6_to_uncore(rc6), FORCEWAKE_ALL);
91032d992dSChris Wilson dt = ktime_get();
92d4e3d455SChris Wilson rc6_power = librapl_energy_uJ();
93730eaeb5SChris Wilson msleep(100);
94d4e3d455SChris Wilson rc6_power = librapl_energy_uJ() - rc6_power;
95032d992dSChris Wilson dt = ktime_sub(ktime_get(), dt);
9613c5a577SChris Wilson res[1] = rc6_residency(rc6);
97730eaeb5SChris Wilson if (res[1] == res[0]) {
9842317714SChris Wilson pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x, residency=%lld\n",
99730eaeb5SChris Wilson intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE),
10042317714SChris Wilson intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL),
10142317714SChris Wilson res[0]);
102730eaeb5SChris Wilson err = -EINVAL;
103730eaeb5SChris Wilson }
104730eaeb5SChris Wilson
105a36a4749SChris Wilson if (has_power) {
106a36a4749SChris Wilson rc6_power = div64_u64(NSEC_PER_SEC * rc6_power,
107a36a4749SChris Wilson ktime_to_ns(dt));
108032d992dSChris Wilson pr_info("GPU consumed %llduW in RC0 and %llduW in RC6\n",
109032d992dSChris Wilson rc0_power, rc6_power);
110032d992dSChris Wilson if (2 * rc6_power > rc0_power) {
111032d992dSChris Wilson pr_err("GPU leaked energy while in RC6!\n");
112032d992dSChris Wilson err = -EINVAL;
113032d992dSChris Wilson goto out_unlock;
114032d992dSChris Wilson }
115a36a4749SChris Wilson }
116032d992dSChris Wilson
117730eaeb5SChris Wilson /* Restore what should have been the original state! */
118730eaeb5SChris Wilson intel_rc6_unpark(rc6);
119730eaeb5SChris Wilson
120730eaeb5SChris Wilson out_unlock:
121730eaeb5SChris Wilson intel_runtime_pm_put(gt->uncore->rpm, wakeref);
122730eaeb5SChris Wilson return err;
123730eaeb5SChris Wilson }
124730eaeb5SChris Wilson
__live_rc6_ctx(struct intel_context * ce)125ba446f74SChris Wilson static const u32 *__live_rc6_ctx(struct intel_context *ce)
126ba446f74SChris Wilson {
127ba446f74SChris Wilson struct i915_request *rq;
128ba446f74SChris Wilson const u32 *result;
129ba446f74SChris Wilson u32 cmd;
130ba446f74SChris Wilson u32 *cs;
131ba446f74SChris Wilson
132ba446f74SChris Wilson rq = intel_context_create_request(ce);
133ba446f74SChris Wilson if (IS_ERR(rq))
134ba446f74SChris Wilson return ERR_CAST(rq);
135ba446f74SChris Wilson
136ba446f74SChris Wilson cs = intel_ring_begin(rq, 4);
137ba446f74SChris Wilson if (IS_ERR(cs)) {
138ba446f74SChris Wilson i915_request_add(rq);
139ba446f74SChris Wilson return cs;
140ba446f74SChris Wilson }
141ba446f74SChris Wilson
142ba446f74SChris Wilson cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT;
143d3f23ab9SAndrzej Hajda if (GRAPHICS_VER(rq->i915) >= 8)
144ba446f74SChris Wilson cmd++;
145ba446f74SChris Wilson
146ba446f74SChris Wilson *cs++ = cmd;
147ba446f74SChris Wilson *cs++ = i915_mmio_reg_offset(GEN8_RC6_CTX_INFO);
148ba446f74SChris Wilson *cs++ = ce->timeline->hwsp_offset + 8;
149ba446f74SChris Wilson *cs++ = 0;
150ba446f74SChris Wilson intel_ring_advance(rq, cs);
151ba446f74SChris Wilson
152ba446f74SChris Wilson result = rq->hwsp_seqno + 2;
153ba446f74SChris Wilson i915_request_add(rq);
154ba446f74SChris Wilson
155ba446f74SChris Wilson return result;
156ba446f74SChris Wilson }
157ba446f74SChris Wilson
158ba446f74SChris Wilson static struct intel_engine_cs **
randomised_engines(struct intel_gt * gt,struct rnd_state * prng,unsigned int * count)159ba446f74SChris Wilson randomised_engines(struct intel_gt *gt,
160ba446f74SChris Wilson struct rnd_state *prng,
161ba446f74SChris Wilson unsigned int *count)
162ba446f74SChris Wilson {
163ba446f74SChris Wilson struct intel_engine_cs *engine, **engines;
164ba446f74SChris Wilson enum intel_engine_id id;
165ba446f74SChris Wilson int n;
166ba446f74SChris Wilson
167ba446f74SChris Wilson n = 0;
168ba446f74SChris Wilson for_each_engine(engine, gt, id)
169ba446f74SChris Wilson n++;
170ba446f74SChris Wilson if (!n)
171ba446f74SChris Wilson return NULL;
172ba446f74SChris Wilson
173ba446f74SChris Wilson engines = kmalloc_array(n, sizeof(*engines), GFP_KERNEL);
174ba446f74SChris Wilson if (!engines)
175ba446f74SChris Wilson return NULL;
176ba446f74SChris Wilson
177ba446f74SChris Wilson n = 0;
178ba446f74SChris Wilson for_each_engine(engine, gt, id)
179ba446f74SChris Wilson engines[n++] = engine;
180ba446f74SChris Wilson
181ba446f74SChris Wilson i915_prandom_shuffle(engines, sizeof(*engines), n, prng);
182ba446f74SChris Wilson
183ba446f74SChris Wilson *count = n;
184ba446f74SChris Wilson return engines;
185ba446f74SChris Wilson }
186ba446f74SChris Wilson
live_rc6_ctx_wa(void * arg)187ba446f74SChris Wilson int live_rc6_ctx_wa(void *arg)
188ba446f74SChris Wilson {
189ba446f74SChris Wilson struct intel_gt *gt = arg;
190ba446f74SChris Wilson struct intel_engine_cs **engines;
191ba446f74SChris Wilson unsigned int n, count;
192ba446f74SChris Wilson I915_RND_STATE(prng);
193ba446f74SChris Wilson int err = 0;
194ba446f74SChris Wilson
195ba446f74SChris Wilson /* A read of CTX_INFO upsets rc6. Poke the bear! */
196c816723bSLucas De Marchi if (GRAPHICS_VER(gt->i915) < 8)
197ba446f74SChris Wilson return 0;
198ba446f74SChris Wilson
199ba446f74SChris Wilson engines = randomised_engines(gt, &prng, &count);
200ba446f74SChris Wilson if (!engines)
201ba446f74SChris Wilson return 0;
202ba446f74SChris Wilson
203ba446f74SChris Wilson for (n = 0; n < count; n++) {
204ba446f74SChris Wilson struct intel_engine_cs *engine = engines[n];
205ba446f74SChris Wilson int pass;
206ba446f74SChris Wilson
207ba446f74SChris Wilson for (pass = 0; pass < 2; pass++) {
2085a833995SChris Wilson struct i915_gpu_error *error = >->i915->gpu_error;
209ba446f74SChris Wilson struct intel_context *ce;
210ba446f74SChris Wilson unsigned int resets =
2115a833995SChris Wilson i915_reset_engine_count(error, engine);
212ba446f74SChris Wilson const u32 *res;
213ba446f74SChris Wilson
214ba446f74SChris Wilson /* Use a sacrifical context */
215e6ba7648SChris Wilson ce = intel_context_create(engine);
216ba446f74SChris Wilson if (IS_ERR(ce)) {
217ba446f74SChris Wilson err = PTR_ERR(ce);
218ba446f74SChris Wilson goto out;
219ba446f74SChris Wilson }
220ba446f74SChris Wilson
221ba446f74SChris Wilson intel_engine_pm_get(engine);
222ba446f74SChris Wilson res = __live_rc6_ctx(ce);
223ba446f74SChris Wilson intel_engine_pm_put(engine);
224ba446f74SChris Wilson intel_context_put(ce);
225ba446f74SChris Wilson if (IS_ERR(res)) {
226ba446f74SChris Wilson err = PTR_ERR(res);
227ba446f74SChris Wilson goto out;
228ba446f74SChris Wilson }
229ba446f74SChris Wilson
230ba446f74SChris Wilson if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) {
231ba446f74SChris Wilson intel_gt_set_wedged(gt);
232ba446f74SChris Wilson err = -ETIME;
233ba446f74SChris Wilson goto out;
234ba446f74SChris Wilson }
235ba446f74SChris Wilson
236ba446f74SChris Wilson intel_gt_pm_wait_for_idle(gt);
237ba446f74SChris Wilson pr_debug("%s: CTX_INFO=%0x\n",
238ba446f74SChris Wilson engine->name, READ_ONCE(*res));
239ba446f74SChris Wilson
240ba446f74SChris Wilson if (resets !=
2415a833995SChris Wilson i915_reset_engine_count(error, engine)) {
242ba446f74SChris Wilson pr_err("%s: GPU reset required\n",
243ba446f74SChris Wilson engine->name);
24465706203SMichał Winiarski add_taint_for_CI(gt->i915, TAINT_WARN);
245ba446f74SChris Wilson err = -EIO;
246ba446f74SChris Wilson goto out;
247ba446f74SChris Wilson }
248ba446f74SChris Wilson }
249ba446f74SChris Wilson }
250ba446f74SChris Wilson
251ba446f74SChris Wilson out:
252ba446f74SChris Wilson kfree(engines);
253ba446f74SChris Wilson return err;
254ba446f74SChris Wilson }
255