1ba446f74SChris Wilson /*
2ba446f74SChris Wilson  * SPDX-License-Identifier: MIT
3ba446f74SChris Wilson  *
4ba446f74SChris Wilson  * Copyright © 2019 Intel Corporation
5ba446f74SChris Wilson  */
6ba446f74SChris Wilson 
7ba446f74SChris Wilson #include "intel_context.h"
8ba446f74SChris Wilson #include "intel_engine_pm.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"
14ba446f74SChris Wilson 
15730eaeb5SChris Wilson int live_rc6_manual(void *arg)
16730eaeb5SChris Wilson {
17730eaeb5SChris Wilson 	struct intel_gt *gt = arg;
18730eaeb5SChris Wilson 	struct intel_rc6 *rc6 = &gt->rc6;
19730eaeb5SChris Wilson 	intel_wakeref_t wakeref;
20730eaeb5SChris Wilson 	u64 res[2];
21730eaeb5SChris Wilson 	int err = 0;
22730eaeb5SChris Wilson 
23730eaeb5SChris Wilson 	/*
24730eaeb5SChris Wilson 	 * Our claim is that we can "encourage" the GPU to enter rc6 at will.
25730eaeb5SChris Wilson 	 * Let's try it!
26730eaeb5SChris Wilson 	 */
27730eaeb5SChris Wilson 
28730eaeb5SChris Wilson 	if (!rc6->enabled)
29730eaeb5SChris Wilson 		return 0;
30730eaeb5SChris Wilson 
31730eaeb5SChris Wilson 	/* bsw/byt use a PCU and decouple RC6 from our manual control */
32730eaeb5SChris Wilson 	if (IS_VALLEYVIEW(gt->i915) || IS_CHERRYVIEW(gt->i915))
33730eaeb5SChris Wilson 		return 0;
34730eaeb5SChris Wilson 
35730eaeb5SChris Wilson 	wakeref = intel_runtime_pm_get(gt->uncore->rpm);
36730eaeb5SChris Wilson 
37730eaeb5SChris Wilson 	/* Force RC6 off for starters */
38730eaeb5SChris Wilson 	__intel_rc6_disable(rc6);
39730eaeb5SChris Wilson 	msleep(1); /* wakeup is not immediate, takes about 100us on icl */
40730eaeb5SChris Wilson 
41730eaeb5SChris Wilson 	res[0] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
42730eaeb5SChris Wilson 	msleep(250);
43730eaeb5SChris Wilson 	res[1] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
44730eaeb5SChris Wilson 	if ((res[1] - res[0]) >> 10) {
45730eaeb5SChris Wilson 		pr_err("RC6 residency increased by %lldus while disabled for 250ms!\n",
46730eaeb5SChris Wilson 		       (res[1] - res[0]) >> 10);
47730eaeb5SChris Wilson 		err = -EINVAL;
48730eaeb5SChris Wilson 		goto out_unlock;
49730eaeb5SChris Wilson 	}
50730eaeb5SChris Wilson 
51730eaeb5SChris Wilson 	/* Manually enter RC6 */
52730eaeb5SChris Wilson 	intel_rc6_park(rc6);
53730eaeb5SChris Wilson 
54730eaeb5SChris Wilson 	res[0] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
55730eaeb5SChris Wilson 	msleep(100);
56730eaeb5SChris Wilson 	res[1] = intel_rc6_residency_ns(rc6, GEN6_GT_GFX_RC6);
57730eaeb5SChris Wilson 
58730eaeb5SChris Wilson 	if (res[1] == res[0]) {
59730eaeb5SChris Wilson 		pr_err("Did not enter RC6! RC6_STATE=%08x, RC6_CONTROL=%08x\n",
60730eaeb5SChris Wilson 		       intel_uncore_read_fw(gt->uncore, GEN6_RC_STATE),
61730eaeb5SChris Wilson 		       intel_uncore_read_fw(gt->uncore, GEN6_RC_CONTROL));
62730eaeb5SChris Wilson 		err = -EINVAL;
63730eaeb5SChris Wilson 	}
64730eaeb5SChris Wilson 
65730eaeb5SChris Wilson 	/* Restore what should have been the original state! */
66730eaeb5SChris Wilson 	intel_rc6_unpark(rc6);
67730eaeb5SChris Wilson 
68730eaeb5SChris Wilson out_unlock:
69730eaeb5SChris Wilson 	intel_runtime_pm_put(gt->uncore->rpm, wakeref);
70730eaeb5SChris Wilson 	return err;
71730eaeb5SChris Wilson }
72730eaeb5SChris Wilson 
73ba446f74SChris Wilson static const u32 *__live_rc6_ctx(struct intel_context *ce)
74ba446f74SChris Wilson {
75ba446f74SChris Wilson 	struct i915_request *rq;
76ba446f74SChris Wilson 	const u32 *result;
77ba446f74SChris Wilson 	u32 cmd;
78ba446f74SChris Wilson 	u32 *cs;
79ba446f74SChris Wilson 
80ba446f74SChris Wilson 	rq = intel_context_create_request(ce);
81ba446f74SChris Wilson 	if (IS_ERR(rq))
82ba446f74SChris Wilson 		return ERR_CAST(rq);
83ba446f74SChris Wilson 
84ba446f74SChris Wilson 	cs = intel_ring_begin(rq, 4);
85ba446f74SChris Wilson 	if (IS_ERR(cs)) {
86ba446f74SChris Wilson 		i915_request_add(rq);
87ba446f74SChris Wilson 		return cs;
88ba446f74SChris Wilson 	}
89ba446f74SChris Wilson 
90ba446f74SChris Wilson 	cmd = MI_STORE_REGISTER_MEM | MI_USE_GGTT;
91ba446f74SChris Wilson 	if (INTEL_GEN(rq->i915) >= 8)
92ba446f74SChris Wilson 		cmd++;
93ba446f74SChris Wilson 
94ba446f74SChris Wilson 	*cs++ = cmd;
95ba446f74SChris Wilson 	*cs++ = i915_mmio_reg_offset(GEN8_RC6_CTX_INFO);
96ba446f74SChris Wilson 	*cs++ = ce->timeline->hwsp_offset + 8;
97ba446f74SChris Wilson 	*cs++ = 0;
98ba446f74SChris Wilson 	intel_ring_advance(rq, cs);
99ba446f74SChris Wilson 
100ba446f74SChris Wilson 	result = rq->hwsp_seqno + 2;
101ba446f74SChris Wilson 	i915_request_add(rq);
102ba446f74SChris Wilson 
103ba446f74SChris Wilson 	return result;
104ba446f74SChris Wilson }
105ba446f74SChris Wilson 
106ba446f74SChris Wilson static struct intel_engine_cs **
107ba446f74SChris Wilson randomised_engines(struct intel_gt *gt,
108ba446f74SChris Wilson 		   struct rnd_state *prng,
109ba446f74SChris Wilson 		   unsigned int *count)
110ba446f74SChris Wilson {
111ba446f74SChris Wilson 	struct intel_engine_cs *engine, **engines;
112ba446f74SChris Wilson 	enum intel_engine_id id;
113ba446f74SChris Wilson 	int n;
114ba446f74SChris Wilson 
115ba446f74SChris Wilson 	n = 0;
116ba446f74SChris Wilson 	for_each_engine(engine, gt, id)
117ba446f74SChris Wilson 		n++;
118ba446f74SChris Wilson 	if (!n)
119ba446f74SChris Wilson 		return NULL;
120ba446f74SChris Wilson 
121ba446f74SChris Wilson 	engines = kmalloc_array(n, sizeof(*engines), GFP_KERNEL);
122ba446f74SChris Wilson 	if (!engines)
123ba446f74SChris Wilson 		return NULL;
124ba446f74SChris Wilson 
125ba446f74SChris Wilson 	n = 0;
126ba446f74SChris Wilson 	for_each_engine(engine, gt, id)
127ba446f74SChris Wilson 		engines[n++] = engine;
128ba446f74SChris Wilson 
129ba446f74SChris Wilson 	i915_prandom_shuffle(engines, sizeof(*engines), n, prng);
130ba446f74SChris Wilson 
131ba446f74SChris Wilson 	*count = n;
132ba446f74SChris Wilson 	return engines;
133ba446f74SChris Wilson }
134ba446f74SChris Wilson 
135ba446f74SChris Wilson int live_rc6_ctx_wa(void *arg)
136ba446f74SChris Wilson {
137ba446f74SChris Wilson 	struct intel_gt *gt = arg;
138ba446f74SChris Wilson 	struct intel_engine_cs **engines;
139ba446f74SChris Wilson 	unsigned int n, count;
140ba446f74SChris Wilson 	I915_RND_STATE(prng);
141ba446f74SChris Wilson 	int err = 0;
142ba446f74SChris Wilson 
143ba446f74SChris Wilson 	/* A read of CTX_INFO upsets rc6. Poke the bear! */
144ba446f74SChris Wilson 	if (INTEL_GEN(gt->i915) < 8)
145ba446f74SChris Wilson 		return 0;
146ba446f74SChris Wilson 
147ba446f74SChris Wilson 	engines = randomised_engines(gt, &prng, &count);
148ba446f74SChris Wilson 	if (!engines)
149ba446f74SChris Wilson 		return 0;
150ba446f74SChris Wilson 
151ba446f74SChris Wilson 	for (n = 0; n < count; n++) {
152ba446f74SChris Wilson 		struct intel_engine_cs *engine = engines[n];
153ba446f74SChris Wilson 		int pass;
154ba446f74SChris Wilson 
155ba446f74SChris Wilson 		for (pass = 0; pass < 2; pass++) {
156ba446f74SChris Wilson 			struct intel_context *ce;
157ba446f74SChris Wilson 			unsigned int resets =
158ba446f74SChris Wilson 				i915_reset_engine_count(&gt->i915->gpu_error,
159ba446f74SChris Wilson 							engine);
160ba446f74SChris Wilson 			const u32 *res;
161ba446f74SChris Wilson 
162ba446f74SChris Wilson 			/* Use a sacrifical context */
163e6ba7648SChris Wilson 			ce = intel_context_create(engine);
164ba446f74SChris Wilson 			if (IS_ERR(ce)) {
165ba446f74SChris Wilson 				err = PTR_ERR(ce);
166ba446f74SChris Wilson 				goto out;
167ba446f74SChris Wilson 			}
168ba446f74SChris Wilson 
169ba446f74SChris Wilson 			intel_engine_pm_get(engine);
170ba446f74SChris Wilson 			res = __live_rc6_ctx(ce);
171ba446f74SChris Wilson 			intel_engine_pm_put(engine);
172ba446f74SChris Wilson 			intel_context_put(ce);
173ba446f74SChris Wilson 			if (IS_ERR(res)) {
174ba446f74SChris Wilson 				err = PTR_ERR(res);
175ba446f74SChris Wilson 				goto out;
176ba446f74SChris Wilson 			}
177ba446f74SChris Wilson 
178ba446f74SChris Wilson 			if (intel_gt_wait_for_idle(gt, HZ / 5) == -ETIME) {
179ba446f74SChris Wilson 				intel_gt_set_wedged(gt);
180ba446f74SChris Wilson 				err = -ETIME;
181ba446f74SChris Wilson 				goto out;
182ba446f74SChris Wilson 			}
183ba446f74SChris Wilson 
184ba446f74SChris Wilson 			intel_gt_pm_wait_for_idle(gt);
185ba446f74SChris Wilson 			pr_debug("%s: CTX_INFO=%0x\n",
186ba446f74SChris Wilson 				 engine->name, READ_ONCE(*res));
187ba446f74SChris Wilson 
188ba446f74SChris Wilson 			if (resets !=
189ba446f74SChris Wilson 			    i915_reset_engine_count(&gt->i915->gpu_error,
190ba446f74SChris Wilson 						    engine)) {
191ba446f74SChris Wilson 				pr_err("%s: GPU reset required\n",
192ba446f74SChris Wilson 				       engine->name);
193ba446f74SChris Wilson 				add_taint_for_CI(TAINT_WARN);
194ba446f74SChris Wilson 				err = -EIO;
195ba446f74SChris Wilson 				goto out;
196ba446f74SChris Wilson 			}
197ba446f74SChris Wilson 		}
198ba446f74SChris Wilson 	}
199ba446f74SChris Wilson 
200ba446f74SChris Wilson out:
201ba446f74SChris Wilson 	kfree(engines);
202ba446f74SChris Wilson 	return err;
203ba446f74SChris Wilson }
204