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 = &gt->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();
65730eaeb5SChris Wilson 	msleep(250);
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) {
70730eaeb5SChris Wilson 		pr_err("RC6 residency increased by %lldus while disabled for 250ms!\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;
143*d3f23ab9SAndrzej 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 = &gt->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