xref: /linux/drivers/gpu/drm/xe/xe_pcode.c (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2022 Intel Corporation
4  */
5 
6 #include "xe_pcode.h"
7 
8 #include <linux/delay.h>
9 #include <linux/errno.h>
10 #include <linux/error-injection.h>
11 
12 #include <drm/drm_managed.h>
13 
14 #include "regs/xe_pmt.h"
15 #include "xe_assert.h"
16 #include "xe_device.h"
17 #include "xe_mmio.h"
18 #include "xe_pcode_api.h"
19 #include "xe_pm.h"
20 #include "xe_printk.h"
21 #include "xe_vsec.h"
22 
23 /**
24  * DOC: PCODE
25  *
26  * Xe PCODE is the component responsible for interfacing with the PCODE
27  * firmware.
28  * It shall provide a very simple ABI to other Xe components, but be the
29  * single and consolidated place that will communicate with PCODE. All read
30  * and write operations to PCODE will be internal and private to this component.
31  *
32  * What's next:
33  * - PCODE hw metrics
34  * - PCODE for display operations
35  */
36 
37 static int pcode_mailbox_status(struct xe_tile *tile)
38 {
39 	const char *err_str;
40 	int err_decode;
41 	u32 err;
42 
43 #define CASE_ERR(_err, _err_decode, _err_str)	\
44 	case _err:				\
45 		err_decode = _err_decode;	\
46 		err_str = _err_str;		\
47 		break
48 
49 	err = xe_mmio_read32(&tile->mmio, PCODE_MAILBOX) & PCODE_ERROR_MASK;
50 	switch (err) {
51 	CASE_ERR(PCODE_ILLEGAL_CMD,           -ENXIO,     "Illegal Command");
52 	CASE_ERR(PCODE_TIMEOUT,               -ETIMEDOUT, "Timed out");
53 	CASE_ERR(PCODE_ILLEGAL_DATA,          -EINVAL,    "Illegal Data");
54 	CASE_ERR(PCODE_ILLEGAL_SUBCOMMAND,    -ENXIO,     "Illegal Subcommand");
55 	CASE_ERR(PCODE_LOCKED,                -EBUSY,     "PCODE Locked");
56 	CASE_ERR(PCODE_GT_RATIO_OUT_OF_RANGE, -EOVERFLOW, "GT ratio out of range");
57 	CASE_ERR(PCODE_REJECTED,              -EACCES,    "PCODE Rejected");
58 	default:
59 		err_decode = -EPROTO;
60 		err_str = "Unknown";
61 	}
62 
63 	if (err) {
64 		drm_err(&tile_to_xe(tile)->drm, "PCODE Mailbox failed: %d %s",
65 			err_decode, err_str);
66 
67 		return err_decode;
68 	}
69 
70 	return 0;
71 #undef CASE_ERR
72 }
73 
74 static int __pcode_mailbox_rw(struct xe_tile *tile, u32 mbox, u32 *data0, u32 *data1,
75 			      unsigned int timeout_ms, bool return_data,
76 			      bool atomic)
77 {
78 	struct xe_mmio *mmio = &tile->mmio;
79 	int err;
80 
81 	if (tile_to_xe(tile)->info.skip_pcode)
82 		return 0;
83 
84 	if ((xe_mmio_read32(mmio, PCODE_MAILBOX) & PCODE_READY) != 0)
85 		return -EAGAIN;
86 
87 	xe_mmio_write32(mmio, PCODE_DATA0, *data0);
88 	xe_mmio_write32(mmio, PCODE_DATA1, data1 ? *data1 : 0);
89 	xe_mmio_write32(mmio, PCODE_MAILBOX, PCODE_READY | mbox);
90 
91 	err = xe_mmio_wait32(mmio, PCODE_MAILBOX, PCODE_READY, 0,
92 			     timeout_ms * USEC_PER_MSEC, NULL, atomic);
93 	if (err)
94 		return err;
95 
96 	if (return_data) {
97 		*data0 = xe_mmio_read32(mmio, PCODE_DATA0);
98 		if (data1)
99 			*data1 = xe_mmio_read32(mmio, PCODE_DATA1);
100 	}
101 
102 	return pcode_mailbox_status(tile);
103 }
104 
105 static int pcode_mailbox_rw(struct xe_tile *tile, u32 mbox, u32 *data0, u32 *data1,
106 			    unsigned int timeout_ms, bool return_data,
107 			    bool atomic)
108 {
109 	if (tile_to_xe(tile)->info.skip_pcode)
110 		return 0;
111 
112 	lockdep_assert_held(&tile->pcode.lock);
113 
114 	return __pcode_mailbox_rw(tile, mbox, data0, data1, timeout_ms, return_data, atomic);
115 }
116 
117 int xe_pcode_write_timeout(struct xe_tile *tile, u32 mbox, u32 data, int timeout)
118 {
119 	int err;
120 
121 	mutex_lock(&tile->pcode.lock);
122 	err = pcode_mailbox_rw(tile, mbox, &data, NULL, timeout, false, false);
123 	mutex_unlock(&tile->pcode.lock);
124 
125 	return err;
126 }
127 
128 int xe_pcode_write64_timeout(struct xe_tile *tile, u32 mbox, u32 data0, u32 data1, int timeout)
129 {
130 	int err;
131 
132 	mutex_lock(&tile->pcode.lock);
133 	err = pcode_mailbox_rw(tile, mbox, &data0, &data1, timeout, false, false);
134 	mutex_unlock(&tile->pcode.lock);
135 
136 	return err;
137 }
138 
139 int xe_pcode_read(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1)
140 {
141 	int err;
142 
143 	mutex_lock(&tile->pcode.lock);
144 	err = pcode_mailbox_rw(tile, mbox, val, val1, 1, true, false);
145 	mutex_unlock(&tile->pcode.lock);
146 
147 	return err;
148 }
149 
150 static int pcode_try_request(struct xe_tile *tile, u32 mbox,
151 			     u32 request, u32 reply_mask, u32 reply,
152 			     u32 *status, bool atomic, int timeout_us, bool locked)
153 {
154 	int slept, wait = 10;
155 
156 	xe_tile_assert(tile, timeout_us > 0);
157 
158 	for (slept = 0; slept < timeout_us; slept += wait) {
159 		if (locked)
160 			*status = pcode_mailbox_rw(tile, mbox, &request, NULL, 1, true,
161 						   atomic);
162 		else
163 			*status = __pcode_mailbox_rw(tile, mbox, &request, NULL, 1, true,
164 						     atomic);
165 		if ((*status == 0) && ((request & reply_mask) == reply))
166 			return 0;
167 
168 		if (atomic)
169 			udelay(wait);
170 		else
171 			usleep_range(wait, wait << 1);
172 		wait <<= 1;
173 	}
174 
175 	return -ETIMEDOUT;
176 }
177 
178 /**
179  * xe_pcode_request - send PCODE request until acknowledgment
180  * @tile: tile
181  * @mbox: PCODE mailbox ID the request is targeted for
182  * @request: request ID
183  * @reply_mask: mask used to check for request acknowledgment
184  * @reply: value used to check for request acknowledgment
185  * @timeout_base_ms: timeout for polling with preemption enabled
186  *
187  * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
188  * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
189  * The request is acknowledged once the PCODE reply dword equals @reply after
190  * applying @reply_mask. Polling is first attempted with preemption enabled
191  * for @timeout_base_ms and if this times out for another 50 ms with
192  * preemption disabled.
193  *
194  * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
195  * other error as reported by PCODE.
196  */
197 int xe_pcode_request(struct xe_tile *tile, u32 mbox, u32 request,
198 		     u32 reply_mask, u32 reply, int timeout_base_ms)
199 {
200 	u32 status;
201 	int ret;
202 
203 	xe_tile_assert(tile, timeout_base_ms <= 3);
204 
205 	mutex_lock(&tile->pcode.lock);
206 
207 	ret = pcode_try_request(tile, mbox, request, reply_mask, reply, &status,
208 				false, timeout_base_ms * 1000, true);
209 	if (!ret)
210 		goto out;
211 
212 	/*
213 	 * The above can time out if the number of requests was low (2 in the
214 	 * worst case) _and_ PCODE was busy for some reason even after a
215 	 * (queued) request and @timeout_base_ms delay. As a workaround retry
216 	 * the poll with preemption disabled to maximize the number of
217 	 * requests. Increase the timeout from @timeout_base_ms to 50ms to
218 	 * account for interrupts that could reduce the number of these
219 	 * requests, and for any quirks of the PCODE firmware that delays
220 	 * the request completion.
221 	 */
222 	drm_err(&tile_to_xe(tile)->drm,
223 		"PCODE timeout, retrying with preemption disabled\n");
224 	preempt_disable();
225 	ret = pcode_try_request(tile, mbox, request, reply_mask, reply, &status,
226 				true, 50 * 1000, true);
227 	preempt_enable();
228 
229 out:
230 	mutex_unlock(&tile->pcode.lock);
231 	return status ? status : ret;
232 }
233 /**
234  * xe_pcode_init_min_freq_table - Initialize PCODE's QOS frequency table
235  * @tile: tile instance
236  * @min_gt_freq: Minimal (RPn) GT frequency in units of 50MHz.
237  * @max_gt_freq: Maximal (RP0) GT frequency in units of 50MHz.
238  *
239  * This function initialize PCODE's QOS frequency table for a proper minimal
240  * frequency/power steering decision, depending on the current requested GT
241  * frequency. For older platforms this was a more complete table including
242  * the IA freq. However for the latest platforms this table become a simple
243  * 1-1 Ring vs GT frequency. Even though, without setting it, PCODE might
244  * not take the right decisions for some memory frequencies and affect latency.
245  *
246  * It returns 0 on success, and -ERROR number on failure, -EINVAL if max
247  * frequency is higher then the minimal, and other errors directly translated
248  * from the PCODE Error returns:
249  * - -ENXIO: "Illegal Command"
250  * - -ETIMEDOUT: "Timed out"
251  * - -EINVAL: "Illegal Data"
252  * - -ENXIO, "Illegal Subcommand"
253  * - -EBUSY: "PCODE Locked"
254  * - -EOVERFLOW, "GT ratio out of range"
255  * - -EACCES, "PCODE Rejected"
256  * - -EPROTO, "Unknown"
257  */
258 int xe_pcode_init_min_freq_table(struct xe_tile *tile, u32 min_gt_freq,
259 				 u32 max_gt_freq)
260 {
261 	int ret;
262 	u32 freq;
263 
264 	if (!tile_to_xe(tile)->info.has_llc)
265 		return 0;
266 
267 	if (max_gt_freq <= min_gt_freq)
268 		return -EINVAL;
269 
270 	mutex_lock(&tile->pcode.lock);
271 	for (freq = min_gt_freq; freq <= max_gt_freq; freq++) {
272 		u32 data = freq << PCODE_FREQ_RING_RATIO_SHIFT | freq;
273 
274 		ret = pcode_mailbox_rw(tile, PCODE_WRITE_MIN_FREQ_TABLE,
275 				       &data, NULL, 1, false, false);
276 		if (ret)
277 			goto unlock;
278 	}
279 
280 unlock:
281 	mutex_unlock(&tile->pcode.lock);
282 	return ret;
283 }
284 
285 /**
286  * xe_pcode_ready - Ensure PCODE is initialized
287  * @xe: xe instance
288  * @locked: true if lock held, false otherwise
289  *
290  * PCODE init mailbox is polled only on root gt of root tile
291  * as the root tile provides the initialization is complete only
292  * after all the tiles have completed the initialization.
293  * Called only on early probe without locks and with locks in
294  * resume path.
295  *
296  * Returns 0 on success, and -error number on failure.
297  */
298 int xe_pcode_ready(struct xe_device *xe, bool locked)
299 {
300 	u32 status, request = DGFX_GET_INIT_STATUS;
301 	struct xe_tile *tile = xe_device_get_root_tile(xe);
302 	int timeout_us = 180000000; /* 3 min */
303 	int ret;
304 
305 	if (xe->info.skip_pcode)
306 		return 0;
307 
308 	if (!IS_DGFX(xe))
309 		return 0;
310 
311 	if (locked)
312 		mutex_lock(&tile->pcode.lock);
313 
314 	ret = pcode_try_request(tile, DGFX_PCODE_STATUS, request,
315 				DGFX_INIT_STATUS_COMPLETE,
316 				DGFX_INIT_STATUS_COMPLETE,
317 				&status, false, timeout_us, locked);
318 
319 	if (locked)
320 		mutex_unlock(&tile->pcode.lock);
321 
322 	if (ret)
323 		drm_err(&xe->drm,
324 			"PCODE initialization timedout after: 3 min\n");
325 
326 	return ret;
327 }
328 
329 /**
330  * xe_pcode_init_early() - Initialize components of PCODE
331  * @tile: tile instance
332  *
333  * This function initializes the xe_pcode component.
334  * To be called once only during probe.
335  *
336  * Return: 0 on success or a negative error code on failure.
337  */
338 int xe_pcode_init_early(struct xe_tile *tile)
339 {
340 	return drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock);
341 }
342 
343 /**
344  * xe_pcode_probe_early: initializes PCODE
345  * @xe: xe instance
346  *
347  * This function checks the initialization status of PCODE
348  * To be called once only during early probe without locks.
349  *
350  * Returns 0 on success, error code otherwise
351  */
352 int xe_pcode_probe_early(struct xe_device *xe)
353 {
354 	return xe_pcode_ready(xe, false);
355 }
356 ALLOW_ERROR_INJECTION(xe_pcode_probe_early, ERRNO); /* See xe_pci_probe */
357 
358 /**
359  * xe_get_pcode_version - Read pcode version via PMT telemetry
360  * @xe: xe instance
361  * @version: pointer to struct xe_pcode_version to store version info
362  *
363  * Reads the pcode version from PMT telemetry and fills the
364  * provided @version structure.
365  *
366  * Return: 0 on success, negative error code on failure.
367  */
368 int xe_get_pcode_version(struct xe_device *xe, struct xe_pcode_version *version)
369 {
370 	int ret = 0;
371 
372 	guard(xe_pm_runtime)(xe);
373 
374 	ret = xe_pmt_telem_read(xe->drm.dev,
375 				xe_mmio_read32(xe_root_tile_mmio(xe), PUNIT_TELEMETRY_GUID),
376 				(u64 *)version, PUNIT_VERSION_OFFSET, sizeof(*version));
377 	if (ret != sizeof(*version)) {
378 		xe_warn(xe, "pcode version read from PMT failed, ret %pe\n", ERR_PTR(ret));
379 		return ret;
380 	}
381 	xe_dbg(xe, "pcode version major %u minor %u engg %u\n", version->major,
382 	       version->minor, version->engg);
383 	return 0;
384 }
385