xref: /linux/drivers/gpu/drm/xe/xe_pcode.c (revision 08516de501fae647fb29bf3b62718de56cc24014)
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 
11 #include "xe_gt.h"
12 #include "xe_mmio.h"
13 #include "xe_pcode_api.h"
14 
15 /**
16  * DOC: PCODE
17  *
18  * Xe PCODE is the component responsible for interfacing with the PCODE
19  * firmware.
20  * It shall provide a very simple ABI to other Xe components, but be the
21  * single and consolidated place that will communicate with PCODE. All read
22  * and write operations to PCODE will be internal and private to this component.
23  *
24  * What's next:
25  * - PCODE hw metrics
26  * - PCODE for display operations
27  */
28 
29 static int pcode_mailbox_status(struct xe_gt *gt)
30 {
31 	u32 err;
32 	static const struct pcode_err_decode err_decode[] = {
33 		[PCODE_ILLEGAL_CMD] = {-ENXIO, "Illegal Command"},
34 		[PCODE_TIMEOUT] = {-ETIMEDOUT, "Timed out"},
35 		[PCODE_ILLEGAL_DATA] = {-EINVAL, "Illegal Data"},
36 		[PCODE_ILLEGAL_SUBCOMMAND] = {-ENXIO, "Illegal Subcommand"},
37 		[PCODE_LOCKED] = {-EBUSY, "PCODE Locked"},
38 		[PCODE_GT_RATIO_OUT_OF_RANGE] = {-EOVERFLOW,
39 			"GT ratio out of range"},
40 		[PCODE_REJECTED] = {-EACCES, "PCODE Rejected"},
41 		[PCODE_ERROR_MASK] = {-EPROTO, "Unknown"},
42 	};
43 
44 	lockdep_assert_held(&gt->pcode.lock);
45 
46 	err = xe_mmio_read32(gt, PCODE_MAILBOX) & PCODE_ERROR_MASK;
47 	if (err) {
48 		drm_err(&gt_to_xe(gt)->drm, "PCODE Mailbox failed: %d %s", err,
49 			err_decode[err].str ?: "Unknown");
50 		return err_decode[err].errno ?: -EPROTO;
51 	}
52 
53 	return 0;
54 }
55 
56 static int pcode_mailbox_rw(struct xe_gt *gt, u32 mbox, u32 *data0, u32 *data1,
57 			    unsigned int timeout_ms, bool return_data,
58 			    bool atomic)
59 {
60 	int err;
61 	lockdep_assert_held(&gt->pcode.lock);
62 
63 	if ((xe_mmio_read32(gt, PCODE_MAILBOX) & PCODE_READY) != 0)
64 		return -EAGAIN;
65 
66 	xe_mmio_write32(gt, PCODE_DATA0, *data0);
67 	xe_mmio_write32(gt, PCODE_DATA1, data1 ? *data1 : 0);
68 	xe_mmio_write32(gt, PCODE_MAILBOX, PCODE_READY | mbox);
69 
70 	err = xe_mmio_wait32(gt, PCODE_MAILBOX, 0, PCODE_READY,
71 			     timeout_ms * 1000, NULL, atomic);
72 	if (err)
73 		return err;
74 
75 	if (return_data) {
76 		*data0 = xe_mmio_read32(gt, PCODE_DATA0);
77 		if (data1)
78 			*data1 = xe_mmio_read32(gt, PCODE_DATA1);
79 	}
80 
81 	return pcode_mailbox_status(gt);
82 }
83 
84 int xe_pcode_write_timeout(struct xe_gt *gt, u32 mbox, u32 data, int timeout)
85 {
86 	int err;
87 
88 	mutex_lock(&gt->pcode.lock);
89 	err = pcode_mailbox_rw(gt, mbox, &data, NULL, timeout, false, false);
90 	mutex_unlock(&gt->pcode.lock);
91 
92 	return err;
93 }
94 
95 int xe_pcode_read(struct xe_gt *gt, u32 mbox, u32 *val, u32 *val1)
96 {
97 	int err;
98 
99 	mutex_lock(&gt->pcode.lock);
100 	err = pcode_mailbox_rw(gt, mbox, val, val1, 1, true, false);
101 	mutex_unlock(&gt->pcode.lock);
102 
103 	return err;
104 }
105 
106 static int xe_pcode_try_request(struct xe_gt *gt, u32 mbox,
107 				u32 request, u32 reply_mask, u32 reply,
108 				u32 *status, bool atomic, int timeout_us)
109 {
110 	int slept, wait = 10;
111 
112 	for (slept = 0; slept < timeout_us; slept += wait) {
113 		*status = pcode_mailbox_rw(gt, mbox, &request, NULL, 1, true,
114 					   atomic);
115 		if ((*status == 0) && ((request & reply_mask) == reply))
116 			return 0;
117 
118 		if (atomic)
119 			udelay(wait);
120 		else
121 			usleep_range(wait, wait << 1);
122 		wait <<= 1;
123 	}
124 
125 	return -ETIMEDOUT;
126 }
127 
128 /**
129  * xe_pcode_request - send PCODE request until acknowledgment
130  * @gt: gt
131  * @mbox: PCODE mailbox ID the request is targeted for
132  * @request: request ID
133  * @reply_mask: mask used to check for request acknowledgment
134  * @reply: value used to check for request acknowledgment
135  * @timeout_base_ms: timeout for polling with preemption enabled
136  *
137  * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
138  * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
139  * The request is acknowledged once the PCODE reply dword equals @reply after
140  * applying @reply_mask. Polling is first attempted with preemption enabled
141  * for @timeout_base_ms and if this times out for another 50 ms with
142  * preemption disabled.
143  *
144  * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
145  * other error as reported by PCODE.
146  */
147 int xe_pcode_request(struct xe_gt *gt, u32 mbox, u32 request,
148 		      u32 reply_mask, u32 reply, int timeout_base_ms)
149 {
150 	u32 status;
151 	int ret;
152 
153 	mutex_lock(&gt->pcode.lock);
154 
155 	ret = xe_pcode_try_request(gt, mbox, request, reply_mask, reply, &status,
156 				   false, timeout_base_ms * 1000);
157 	if (!ret)
158 		goto out;
159 
160 	/*
161 	 * The above can time out if the number of requests was low (2 in the
162 	 * worst case) _and_ PCODE was busy for some reason even after a
163 	 * (queued) request and @timeout_base_ms delay. As a workaround retry
164 	 * the poll with preemption disabled to maximize the number of
165 	 * requests. Increase the timeout from @timeout_base_ms to 50ms to
166 	 * account for interrupts that could reduce the number of these
167 	 * requests, and for any quirks of the PCODE firmware that delays
168 	 * the request completion.
169 	 */
170 	drm_err(&gt_to_xe(gt)->drm,
171 		"PCODE timeout, retrying with preemption disabled\n");
172 	drm_WARN_ON_ONCE(&gt_to_xe(gt)->drm, timeout_base_ms > 1);
173 	preempt_disable();
174 	ret = xe_pcode_try_request(gt, mbox, request, reply_mask, reply, &status,
175 				   true, timeout_base_ms * 1000);
176 	preempt_enable();
177 
178 out:
179 	mutex_unlock(&gt->pcode.lock);
180 	return status ? status : ret;
181 }
182 /**
183  * xe_pcode_init_min_freq_table - Initialize PCODE's QOS frequency table
184  * @gt: gt instance
185  * @min_gt_freq: Minimal (RPn) GT frequency in units of 50MHz.
186  * @max_gt_freq: Maximal (RP0) GT frequency in units of 50MHz.
187  *
188  * This function initialize PCODE's QOS frequency table for a proper minimal
189  * frequency/power steering decision, depending on the current requested GT
190  * frequency. For older platforms this was a more complete table including
191  * the IA freq. However for the latest platforms this table become a simple
192  * 1-1 Ring vs GT frequency. Even though, without setting it, PCODE might
193  * not take the right decisions for some memory frequencies and affect latency.
194  *
195  * It returns 0 on success, and -ERROR number on failure, -EINVAL if max
196  * frequency is higher then the minimal, and other errors directly translated
197  * from the PCODE Error returs:
198  * - -ENXIO: "Illegal Command"
199  * - -ETIMEDOUT: "Timed out"
200  * - -EINVAL: "Illegal Data"
201  * - -ENXIO, "Illegal Subcommand"
202  * - -EBUSY: "PCODE Locked"
203  * - -EOVERFLOW, "GT ratio out of range"
204  * - -EACCES, "PCODE Rejected"
205  * - -EPROTO, "Unknown"
206  */
207 int xe_pcode_init_min_freq_table(struct xe_gt *gt, u32 min_gt_freq,
208 				 u32 max_gt_freq)
209 {
210 	int ret;
211 	u32 freq;
212 
213 	if (!gt_to_xe(gt)->info.has_llc)
214 		return 0;
215 
216 	if (max_gt_freq <= min_gt_freq)
217 		return -EINVAL;
218 
219 	mutex_lock(&gt->pcode.lock);
220 	for (freq = min_gt_freq; freq <= max_gt_freq; freq++) {
221 		u32 data = freq << PCODE_FREQ_RING_RATIO_SHIFT | freq;
222 
223 		ret = pcode_mailbox_rw(gt, PCODE_WRITE_MIN_FREQ_TABLE,
224 				       &data, NULL, 1, false, false);
225 		if (ret)
226 			goto unlock;
227 	}
228 
229 unlock:
230 	mutex_unlock(&gt->pcode.lock);
231 	return ret;
232 }
233 
234 /**
235  * xe_pcode_init - Ensure PCODE is initialized
236  * @gt: gt instance
237  *
238  * This function ensures that PCODE is properly initialized. To be called during
239  * probe and resume paths.
240  *
241  * It returns 0 on success, and -error number on failure.
242  */
243 int xe_pcode_init(struct xe_gt *gt)
244 {
245 	u32 status, request = DGFX_GET_INIT_STATUS;
246 	int timeout_us = 180000000; /* 3 min */
247 	int ret;
248 
249 	if (!IS_DGFX(gt_to_xe(gt)))
250 		return 0;
251 
252 	mutex_lock(&gt->pcode.lock);
253 	ret = xe_pcode_try_request(gt, DGFX_PCODE_STATUS, request,
254 				   DGFX_INIT_STATUS_COMPLETE,
255 				   DGFX_INIT_STATUS_COMPLETE,
256 				   &status, false, timeout_us);
257 	mutex_unlock(&gt->pcode.lock);
258 
259 	if (ret)
260 		drm_err(&gt_to_xe(gt)->drm,
261 			"PCODE initialization timedout after: 3 min\n");
262 
263 	return ret;
264 }
265 
266 /**
267  * xe_pcode_probe - Prepare xe_pcode and also ensure PCODE is initialized.
268  * @gt: gt instance
269  *
270  * This function initializes the xe_pcode component, and when needed, it ensures
271  * that PCODE has properly performed its initialization and it is really ready
272  * to go. To be called once only during probe.
273  *
274  * It returns 0 on success, and -error number on failure.
275  */
276 int xe_pcode_probe(struct xe_gt *gt)
277 {
278 	mutex_init(&gt->pcode.lock);
279 
280 	if (!IS_DGFX(gt_to_xe(gt)))
281 		return 0;
282 
283 	return xe_pcode_init(gt);
284 }
285