1dd08ebf6SMatthew Brost // SPDX-License-Identifier: MIT
2dd08ebf6SMatthew Brost /*
3dd08ebf6SMatthew Brost * Copyright © 2022 Intel Corporation
4dd08ebf6SMatthew Brost */
5dd08ebf6SMatthew Brost
6dd08ebf6SMatthew Brost #include "xe_pcode.h"
7dd08ebf6SMatthew Brost
8ea9f879dSLucas De Marchi #include <linux/delay.h>
9ea9f879dSLucas De Marchi #include <linux/errno.h>
10ea9f879dSLucas De Marchi
1128b1d915SBommithi Sakeena #include <drm/drm_managed.h>
1228b1d915SBommithi Sakeena
134c0be90eSHimal Prasad Ghimiray #include "xe_assert.h"
14933fd5ffSRiana Tauro #include "xe_device.h"
15dd08ebf6SMatthew Brost #include "xe_mmio.h"
16ea9f879dSLucas De Marchi #include "xe_pcode_api.h"
170f06dc10SRodrigo Vivi
18dd08ebf6SMatthew Brost /**
19dd08ebf6SMatthew Brost * DOC: PCODE
20dd08ebf6SMatthew Brost *
21dd08ebf6SMatthew Brost * Xe PCODE is the component responsible for interfacing with the PCODE
22dd08ebf6SMatthew Brost * firmware.
23dd08ebf6SMatthew Brost * It shall provide a very simple ABI to other Xe components, but be the
24dd08ebf6SMatthew Brost * single and consolidated place that will communicate with PCODE. All read
25dd08ebf6SMatthew Brost * and write operations to PCODE will be internal and private to this component.
26dd08ebf6SMatthew Brost *
27dd08ebf6SMatthew Brost * What's next:
28dd08ebf6SMatthew Brost * - PCODE hw metrics
29dd08ebf6SMatthew Brost * - PCODE for display operations
30dd08ebf6SMatthew Brost */
31dd08ebf6SMatthew Brost
pcode_mailbox_status(struct xe_tile * tile)323034cc81SMatt Roper static int pcode_mailbox_status(struct xe_tile *tile)
33dd08ebf6SMatthew Brost {
34dd08ebf6SMatthew Brost u32 err;
35dd08ebf6SMatthew Brost static const struct pcode_err_decode err_decode[] = {
36dd08ebf6SMatthew Brost [PCODE_ILLEGAL_CMD] = {-ENXIO, "Illegal Command"},
37dd08ebf6SMatthew Brost [PCODE_TIMEOUT] = {-ETIMEDOUT, "Timed out"},
38dd08ebf6SMatthew Brost [PCODE_ILLEGAL_DATA] = {-EINVAL, "Illegal Data"},
39dd08ebf6SMatthew Brost [PCODE_ILLEGAL_SUBCOMMAND] = {-ENXIO, "Illegal Subcommand"},
40dd08ebf6SMatthew Brost [PCODE_LOCKED] = {-EBUSY, "PCODE Locked"},
41dd08ebf6SMatthew Brost [PCODE_GT_RATIO_OUT_OF_RANGE] = {-EOVERFLOW,
42dd08ebf6SMatthew Brost "GT ratio out of range"},
43dd08ebf6SMatthew Brost [PCODE_REJECTED] = {-EACCES, "PCODE Rejected"},
44dd08ebf6SMatthew Brost [PCODE_ERROR_MASK] = {-EPROTO, "Unknown"},
45dd08ebf6SMatthew Brost };
46dd08ebf6SMatthew Brost
47a3e7fcefSMatt Roper err = xe_mmio_read32(&tile->mmio, PCODE_MAILBOX) & PCODE_ERROR_MASK;
48dd08ebf6SMatthew Brost if (err) {
493034cc81SMatt Roper drm_err(&tile_to_xe(tile)->drm, "PCODE Mailbox failed: %d %s", err,
50dd08ebf6SMatthew Brost err_decode[err].str ?: "Unknown");
51dd08ebf6SMatthew Brost return err_decode[err].errno ?: -EPROTO;
52dd08ebf6SMatthew Brost }
53dd08ebf6SMatthew Brost
54dd08ebf6SMatthew Brost return 0;
55dd08ebf6SMatthew Brost }
56dd08ebf6SMatthew Brost
__pcode_mailbox_rw(struct xe_tile * tile,u32 mbox,u32 * data0,u32 * data1,unsigned int timeout_ms,bool return_data,bool atomic)573034cc81SMatt Roper static int __pcode_mailbox_rw(struct xe_tile *tile, u32 mbox, u32 *data0, u32 *data1,
587dc9b92dSRodrigo Vivi unsigned int timeout_ms, bool return_data,
597dc9b92dSRodrigo Vivi bool atomic)
60dd08ebf6SMatthew Brost {
61a3e7fcefSMatt Roper struct xe_mmio *mmio = &tile->mmio;
627dc9b92dSRodrigo Vivi int err;
633e8e7ee6SFrancois Dugast
643034cc81SMatt Roper if (tile_to_xe(tile)->info.skip_pcode)
65082802a3SKoby Elbaz return 0;
66082802a3SKoby Elbaz
673034cc81SMatt Roper if ((xe_mmio_read32(mmio, PCODE_MAILBOX) & PCODE_READY) != 0)
68dd08ebf6SMatthew Brost return -EAGAIN;
69dd08ebf6SMatthew Brost
703034cc81SMatt Roper xe_mmio_write32(mmio, PCODE_DATA0, *data0);
713034cc81SMatt Roper xe_mmio_write32(mmio, PCODE_DATA1, data1 ? *data1 : 0);
723034cc81SMatt Roper xe_mmio_write32(mmio, PCODE_MAILBOX, PCODE_READY | mbox);
73dd08ebf6SMatthew Brost
743034cc81SMatt Roper err = xe_mmio_wait32(mmio, PCODE_MAILBOX, PCODE_READY, 0,
75f20b7671SHimal Prasad Ghimiray timeout_ms * USEC_PER_MSEC, NULL, atomic);
767dc9b92dSRodrigo Vivi if (err)
777dc9b92dSRodrigo Vivi return err;
78dd08ebf6SMatthew Brost
79dd08ebf6SMatthew Brost if (return_data) {
803034cc81SMatt Roper *data0 = xe_mmio_read32(mmio, PCODE_DATA0);
81dd08ebf6SMatthew Brost if (data1)
823034cc81SMatt Roper *data1 = xe_mmio_read32(mmio, PCODE_DATA1);
83dd08ebf6SMatthew Brost }
84dd08ebf6SMatthew Brost
853034cc81SMatt Roper return pcode_mailbox_status(tile);
86dd08ebf6SMatthew Brost }
87dd08ebf6SMatthew Brost
pcode_mailbox_rw(struct xe_tile * tile,u32 mbox,u32 * data0,u32 * data1,unsigned int timeout_ms,bool return_data,bool atomic)883034cc81SMatt Roper static int pcode_mailbox_rw(struct xe_tile *tile, u32 mbox, u32 *data0, u32 *data1,
89933fd5ffSRiana Tauro unsigned int timeout_ms, bool return_data,
90933fd5ffSRiana Tauro bool atomic)
91933fd5ffSRiana Tauro {
923034cc81SMatt Roper if (tile_to_xe(tile)->info.skip_pcode)
93933fd5ffSRiana Tauro return 0;
94933fd5ffSRiana Tauro
953034cc81SMatt Roper lockdep_assert_held(&tile->pcode.lock);
96933fd5ffSRiana Tauro
973034cc81SMatt Roper return __pcode_mailbox_rw(tile, mbox, data0, data1, timeout_ms, return_data, atomic);
98933fd5ffSRiana Tauro }
99933fd5ffSRiana Tauro
xe_pcode_write_timeout(struct xe_tile * tile,u32 mbox,u32 data,int timeout)1003034cc81SMatt Roper int xe_pcode_write_timeout(struct xe_tile *tile, u32 mbox, u32 data, int timeout)
101dd08ebf6SMatthew Brost {
102dd08ebf6SMatthew Brost int err;
103dd08ebf6SMatthew Brost
1043034cc81SMatt Roper mutex_lock(&tile->pcode.lock);
1053034cc81SMatt Roper err = pcode_mailbox_rw(tile, mbox, &data, NULL, timeout, false, false);
1063034cc81SMatt Roper mutex_unlock(&tile->pcode.lock);
107dd08ebf6SMatthew Brost
108dd08ebf6SMatthew Brost return err;
109dd08ebf6SMatthew Brost }
110dd08ebf6SMatthew Brost
xe_pcode_read(struct xe_tile * tile,u32 mbox,u32 * val,u32 * val1)1113034cc81SMatt Roper int xe_pcode_read(struct xe_tile *tile, u32 mbox, u32 *val, u32 *val1)
112dd08ebf6SMatthew Brost {
113dd08ebf6SMatthew Brost int err;
114dd08ebf6SMatthew Brost
1153034cc81SMatt Roper mutex_lock(&tile->pcode.lock);
1163034cc81SMatt Roper err = pcode_mailbox_rw(tile, mbox, val, val1, 1, true, false);
1173034cc81SMatt Roper mutex_unlock(&tile->pcode.lock);
118dd08ebf6SMatthew Brost
119dd08ebf6SMatthew Brost return err;
120dd08ebf6SMatthew Brost }
121dd08ebf6SMatthew Brost
pcode_try_request(struct xe_tile * tile,u32 mbox,u32 request,u32 reply_mask,u32 reply,u32 * status,bool atomic,int timeout_us,bool locked)1223034cc81SMatt Roper static int pcode_try_request(struct xe_tile *tile, u32 mbox,
123dd08ebf6SMatthew Brost u32 request, u32 reply_mask, u32 reply,
124933fd5ffSRiana Tauro u32 *status, bool atomic, int timeout_us, bool locked)
125dd08ebf6SMatthew Brost {
1267dc9b92dSRodrigo Vivi int slept, wait = 10;
127dd08ebf6SMatthew Brost
1283034cc81SMatt Roper xe_tile_assert(tile, timeout_us > 0);
1294c0be90eSHimal Prasad Ghimiray
1307dc9b92dSRodrigo Vivi for (slept = 0; slept < timeout_us; slept += wait) {
131933fd5ffSRiana Tauro if (locked)
1323034cc81SMatt Roper *status = pcode_mailbox_rw(tile, mbox, &request, NULL, 1, true,
1337dc9b92dSRodrigo Vivi atomic);
134933fd5ffSRiana Tauro else
1353034cc81SMatt Roper *status = __pcode_mailbox_rw(tile, mbox, &request, NULL, 1, true,
136933fd5ffSRiana Tauro atomic);
1377dc9b92dSRodrigo Vivi if ((*status == 0) && ((request & reply_mask) == reply))
1387dc9b92dSRodrigo Vivi return 0;
1397dc9b92dSRodrigo Vivi
1407dc9b92dSRodrigo Vivi if (atomic)
1417dc9b92dSRodrigo Vivi udelay(wait);
1427dc9b92dSRodrigo Vivi else
1437dc9b92dSRodrigo Vivi usleep_range(wait, wait << 1);
1447dc9b92dSRodrigo Vivi wait <<= 1;
1457dc9b92dSRodrigo Vivi }
1467dc9b92dSRodrigo Vivi
1477dc9b92dSRodrigo Vivi return -ETIMEDOUT;
148dd08ebf6SMatthew Brost }
149dd08ebf6SMatthew Brost
150dd08ebf6SMatthew Brost /**
151dd08ebf6SMatthew Brost * xe_pcode_request - send PCODE request until acknowledgment
1523034cc81SMatt Roper * @tile: tile
153dd08ebf6SMatthew Brost * @mbox: PCODE mailbox ID the request is targeted for
154dd08ebf6SMatthew Brost * @request: request ID
155dd08ebf6SMatthew Brost * @reply_mask: mask used to check for request acknowledgment
156dd08ebf6SMatthew Brost * @reply: value used to check for request acknowledgment
157dd08ebf6SMatthew Brost * @timeout_base_ms: timeout for polling with preemption enabled
158dd08ebf6SMatthew Brost *
159dd08ebf6SMatthew Brost * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
160dd08ebf6SMatthew Brost * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
161dd08ebf6SMatthew Brost * The request is acknowledged once the PCODE reply dword equals @reply after
162dd08ebf6SMatthew Brost * applying @reply_mask. Polling is first attempted with preemption enabled
163dd08ebf6SMatthew Brost * for @timeout_base_ms and if this times out for another 50 ms with
164dd08ebf6SMatthew Brost * preemption disabled.
165dd08ebf6SMatthew Brost *
166dd08ebf6SMatthew Brost * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
167dd08ebf6SMatthew Brost * other error as reported by PCODE.
168dd08ebf6SMatthew Brost */
xe_pcode_request(struct xe_tile * tile,u32 mbox,u32 request,u32 reply_mask,u32 reply,int timeout_base_ms)1693034cc81SMatt Roper int xe_pcode_request(struct xe_tile *tile, u32 mbox, u32 request,
170dd08ebf6SMatthew Brost u32 reply_mask, u32 reply, int timeout_base_ms)
171dd08ebf6SMatthew Brost {
172dd08ebf6SMatthew Brost u32 status;
173dd08ebf6SMatthew Brost int ret;
174dd08ebf6SMatthew Brost
1753034cc81SMatt Roper xe_tile_assert(tile, timeout_base_ms <= 3);
1764c0be90eSHimal Prasad Ghimiray
1773034cc81SMatt Roper mutex_lock(&tile->pcode.lock);
178dd08ebf6SMatthew Brost
1793034cc81SMatt Roper ret = pcode_try_request(tile, mbox, request, reply_mask, reply, &status,
180933fd5ffSRiana Tauro false, timeout_base_ms * 1000, true);
181d8731500SMatthew Auld if (!ret)
182dd08ebf6SMatthew Brost goto out;
183dd08ebf6SMatthew Brost
184dd08ebf6SMatthew Brost /*
185dd08ebf6SMatthew Brost * The above can time out if the number of requests was low (2 in the
186dd08ebf6SMatthew Brost * worst case) _and_ PCODE was busy for some reason even after a
187dd08ebf6SMatthew Brost * (queued) request and @timeout_base_ms delay. As a workaround retry
188dd08ebf6SMatthew Brost * the poll with preemption disabled to maximize the number of
189dd08ebf6SMatthew Brost * requests. Increase the timeout from @timeout_base_ms to 50ms to
190dd08ebf6SMatthew Brost * account for interrupts that could reduce the number of these
191dd08ebf6SMatthew Brost * requests, and for any quirks of the PCODE firmware that delays
192dd08ebf6SMatthew Brost * the request completion.
193dd08ebf6SMatthew Brost */
1943034cc81SMatt Roper drm_err(&tile_to_xe(tile)->drm,
195dd08ebf6SMatthew Brost "PCODE timeout, retrying with preemption disabled\n");
196dd08ebf6SMatthew Brost preempt_disable();
1973034cc81SMatt Roper ret = pcode_try_request(tile, mbox, request, reply_mask, reply, &status,
198c81858ebSHimal Prasad Ghimiray true, 50 * 1000, true);
199dd08ebf6SMatthew Brost preempt_enable();
200dd08ebf6SMatthew Brost
201dd08ebf6SMatthew Brost out:
2023034cc81SMatt Roper mutex_unlock(&tile->pcode.lock);
203dd08ebf6SMatthew Brost return status ? status : ret;
204dd08ebf6SMatthew Brost }
205dd08ebf6SMatthew Brost /**
206dd08ebf6SMatthew Brost * xe_pcode_init_min_freq_table - Initialize PCODE's QOS frequency table
2073034cc81SMatt Roper * @tile: tile instance
208dd08ebf6SMatthew Brost * @min_gt_freq: Minimal (RPn) GT frequency in units of 50MHz.
209dd08ebf6SMatthew Brost * @max_gt_freq: Maximal (RP0) GT frequency in units of 50MHz.
210dd08ebf6SMatthew Brost *
211dd08ebf6SMatthew Brost * This function initialize PCODE's QOS frequency table for a proper minimal
212dd08ebf6SMatthew Brost * frequency/power steering decision, depending on the current requested GT
213dd08ebf6SMatthew Brost * frequency. For older platforms this was a more complete table including
214dd08ebf6SMatthew Brost * the IA freq. However for the latest platforms this table become a simple
215dd08ebf6SMatthew Brost * 1-1 Ring vs GT frequency. Even though, without setting it, PCODE might
216dd08ebf6SMatthew Brost * not take the right decisions for some memory frequencies and affect latency.
217dd08ebf6SMatthew Brost *
218dd08ebf6SMatthew Brost * It returns 0 on success, and -ERROR number on failure, -EINVAL if max
219dd08ebf6SMatthew Brost * frequency is higher then the minimal, and other errors directly translated
220*75fd04f2SNitin Gote * from the PCODE Error returns:
221dd08ebf6SMatthew Brost * - -ENXIO: "Illegal Command"
222dd08ebf6SMatthew Brost * - -ETIMEDOUT: "Timed out"
223dd08ebf6SMatthew Brost * - -EINVAL: "Illegal Data"
224dd08ebf6SMatthew Brost * - -ENXIO, "Illegal Subcommand"
225dd08ebf6SMatthew Brost * - -EBUSY: "PCODE Locked"
226dd08ebf6SMatthew Brost * - -EOVERFLOW, "GT ratio out of range"
227dd08ebf6SMatthew Brost * - -EACCES, "PCODE Rejected"
228dd08ebf6SMatthew Brost * - -EPROTO, "Unknown"
229dd08ebf6SMatthew Brost */
xe_pcode_init_min_freq_table(struct xe_tile * tile,u32 min_gt_freq,u32 max_gt_freq)2303034cc81SMatt Roper int xe_pcode_init_min_freq_table(struct xe_tile *tile, u32 min_gt_freq,
231dd08ebf6SMatthew Brost u32 max_gt_freq)
232dd08ebf6SMatthew Brost {
233dd08ebf6SMatthew Brost int ret;
234dd08ebf6SMatthew Brost u32 freq;
235dd08ebf6SMatthew Brost
2363034cc81SMatt Roper if (!tile_to_xe(tile)->info.has_llc)
237dd08ebf6SMatthew Brost return 0;
238dd08ebf6SMatthew Brost
239dd08ebf6SMatthew Brost if (max_gt_freq <= min_gt_freq)
240dd08ebf6SMatthew Brost return -EINVAL;
241dd08ebf6SMatthew Brost
2423034cc81SMatt Roper mutex_lock(&tile->pcode.lock);
243dd08ebf6SMatthew Brost for (freq = min_gt_freq; freq <= max_gt_freq; freq++) {
244dd08ebf6SMatthew Brost u32 data = freq << PCODE_FREQ_RING_RATIO_SHIFT | freq;
245dd08ebf6SMatthew Brost
2463034cc81SMatt Roper ret = pcode_mailbox_rw(tile, PCODE_WRITE_MIN_FREQ_TABLE,
247dd08ebf6SMatthew Brost &data, NULL, 1, false, false);
248dd08ebf6SMatthew Brost if (ret)
249dd08ebf6SMatthew Brost goto unlock;
250dd08ebf6SMatthew Brost }
251dd08ebf6SMatthew Brost
252dd08ebf6SMatthew Brost unlock:
2533034cc81SMatt Roper mutex_unlock(&tile->pcode.lock);
254dd08ebf6SMatthew Brost return ret;
255dd08ebf6SMatthew Brost }
256dd08ebf6SMatthew Brost
257dd08ebf6SMatthew Brost /**
258933fd5ffSRiana Tauro * xe_pcode_ready - Ensure PCODE is initialized
259933fd5ffSRiana Tauro * @xe: xe instance
260933fd5ffSRiana Tauro * @locked: true if lock held, false otherwise
261dd08ebf6SMatthew Brost *
262933fd5ffSRiana Tauro * PCODE init mailbox is polled only on root gt of root tile
263933fd5ffSRiana Tauro * as the root tile provides the initialization is complete only
264933fd5ffSRiana Tauro * after all the tiles have completed the initialization.
265933fd5ffSRiana Tauro * Called only on early probe without locks and with locks in
266933fd5ffSRiana Tauro * resume path.
267dd08ebf6SMatthew Brost *
268933fd5ffSRiana Tauro * Returns 0 on success, and -error number on failure.
269dd08ebf6SMatthew Brost */
xe_pcode_ready(struct xe_device * xe,bool locked)270933fd5ffSRiana Tauro int xe_pcode_ready(struct xe_device *xe, bool locked)
271dd08ebf6SMatthew Brost {
2727dc9b92dSRodrigo Vivi u32 status, request = DGFX_GET_INIT_STATUS;
2733034cc81SMatt Roper struct xe_tile *tile = xe_device_get_root_tile(xe);
2747dc9b92dSRodrigo Vivi int timeout_us = 180000000; /* 3 min */
275dd08ebf6SMatthew Brost int ret;
276dd08ebf6SMatthew Brost
277933fd5ffSRiana Tauro if (xe->info.skip_pcode)
278082802a3SKoby Elbaz return 0;
279082802a3SKoby Elbaz
280933fd5ffSRiana Tauro if (!IS_DGFX(xe))
281dd08ebf6SMatthew Brost return 0;
282dd08ebf6SMatthew Brost
283933fd5ffSRiana Tauro if (locked)
2843034cc81SMatt Roper mutex_lock(&tile->pcode.lock);
285933fd5ffSRiana Tauro
2863034cc81SMatt Roper ret = pcode_try_request(tile, DGFX_PCODE_STATUS, request,
2877dc9b92dSRodrigo Vivi DGFX_INIT_STATUS_COMPLETE,
2887dc9b92dSRodrigo Vivi DGFX_INIT_STATUS_COMPLETE,
289933fd5ffSRiana Tauro &status, false, timeout_us, locked);
290933fd5ffSRiana Tauro
291933fd5ffSRiana Tauro if (locked)
2923034cc81SMatt Roper mutex_unlock(&tile->pcode.lock);
293dd08ebf6SMatthew Brost
294dd08ebf6SMatthew Brost if (ret)
295933fd5ffSRiana Tauro drm_err(&xe->drm,
2967dc9b92dSRodrigo Vivi "PCODE initialization timedout after: 3 min\n");
297dd08ebf6SMatthew Brost
298dd08ebf6SMatthew Brost return ret;
299dd08ebf6SMatthew Brost }
300dd08ebf6SMatthew Brost
301dd08ebf6SMatthew Brost /**
302933fd5ffSRiana Tauro * xe_pcode_init: initialize components of PCODE
3033034cc81SMatt Roper * @tile: tile instance
304dd08ebf6SMatthew Brost *
305933fd5ffSRiana Tauro * This function initializes the xe_pcode component.
306933fd5ffSRiana Tauro * To be called once only during probe.
307dd08ebf6SMatthew Brost */
xe_pcode_init(struct xe_tile * tile)3083034cc81SMatt Roper void xe_pcode_init(struct xe_tile *tile)
309dd08ebf6SMatthew Brost {
3103034cc81SMatt Roper drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock);
311933fd5ffSRiana Tauro }
312dd08ebf6SMatthew Brost
313933fd5ffSRiana Tauro /**
314933fd5ffSRiana Tauro * xe_pcode_probe_early: initializes PCODE
315933fd5ffSRiana Tauro * @xe: xe instance
316933fd5ffSRiana Tauro *
317933fd5ffSRiana Tauro * This function checks the initialization status of PCODE
318933fd5ffSRiana Tauro * To be called once only during early probe without locks.
319933fd5ffSRiana Tauro *
320933fd5ffSRiana Tauro * Returns 0 on success, error code otherwise
321933fd5ffSRiana Tauro */
xe_pcode_probe_early(struct xe_device * xe)322933fd5ffSRiana Tauro int xe_pcode_probe_early(struct xe_device *xe)
323933fd5ffSRiana Tauro {
324933fd5ffSRiana Tauro return xe_pcode_ready(xe, false);
325dd08ebf6SMatthew Brost }
326