xref: /linux/drivers/gpu/drm/xe/xe_pat.c (revision c5288cda69ee2d8607f5026bd599a5cebf0ee783)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023 Intel Corporation
4  */
5 
6 #include "xe_pat.h"
7 
8 #include <drm/xe_drm.h>
9 
10 #include "regs/xe_reg_defs.h"
11 #include "xe_assert.h"
12 #include "xe_device.h"
13 #include "xe_gt.h"
14 #include "xe_gt_mcr.h"
15 #include "xe_mmio.h"
16 #include "xe_sriov.h"
17 
18 #define _PAT_ATS				0x47fc
19 #define _PAT_INDEX(index)			_PICK_EVEN_2RANGES(index, 8, \
20 								   0x4800, 0x4804, \
21 								   0x4848, 0x484c)
22 #define _PAT_PTA				0x4820
23 
24 #define XE2_NO_PROMOTE				REG_BIT(10)
25 #define XE2_COMP_EN				REG_BIT(9)
26 #define XE2_L3_CLOS				REG_GENMASK(7, 6)
27 #define XE2_L3_POLICY				REG_GENMASK(5, 4)
28 #define XE2_L4_POLICY				REG_GENMASK(3, 2)
29 #define XE2_COH_MODE				REG_GENMASK(1, 0)
30 
31 #define XELPG_L4_POLICY_MASK			REG_GENMASK(3, 2)
32 #define XELPG_PAT_3_UC				REG_FIELD_PREP(XELPG_L4_POLICY_MASK, 3)
33 #define XELPG_PAT_1_WT				REG_FIELD_PREP(XELPG_L4_POLICY_MASK, 1)
34 #define XELPG_PAT_0_WB				REG_FIELD_PREP(XELPG_L4_POLICY_MASK, 0)
35 #define XELPG_INDEX_COH_MODE_MASK		REG_GENMASK(1, 0)
36 #define XELPG_3_COH_2W				REG_FIELD_PREP(XELPG_INDEX_COH_MODE_MASK, 3)
37 #define XELPG_2_COH_1W				REG_FIELD_PREP(XELPG_INDEX_COH_MODE_MASK, 2)
38 #define XELPG_0_COH_NON				REG_FIELD_PREP(XELPG_INDEX_COH_MODE_MASK, 0)
39 
40 #define XEHPC_CLOS_LEVEL_MASK			REG_GENMASK(3, 2)
41 #define XEHPC_PAT_CLOS(x)			REG_FIELD_PREP(XEHPC_CLOS_LEVEL_MASK, x)
42 
43 #define XELP_MEM_TYPE_MASK			REG_GENMASK(1, 0)
44 #define XELP_PAT_WB				REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 3)
45 #define XELP_PAT_WT				REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 2)
46 #define XELP_PAT_WC				REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 1)
47 #define XELP_PAT_UC				REG_FIELD_PREP(XELP_MEM_TYPE_MASK, 0)
48 
49 static const char *XELP_MEM_TYPE_STR_MAP[] = { "UC", "WC", "WT", "WB" };
50 
51 struct xe_pat_ops {
52 	void (*program_graphics)(struct xe_gt *gt, const struct xe_pat_table_entry table[],
53 				 int n_entries);
54 	void (*program_media)(struct xe_gt *gt, const struct xe_pat_table_entry table[],
55 			      int n_entries);
56 	void (*dump)(struct xe_gt *gt, struct drm_printer *p);
57 };
58 
59 static const struct xe_pat_table_entry xelp_pat_table[] = {
60 	[0] = { XELP_PAT_WB, XE_COH_AT_LEAST_1WAY },
61 	[1] = { XELP_PAT_WC, XE_COH_NONE },
62 	[2] = { XELP_PAT_WT, XE_COH_NONE },
63 	[3] = { XELP_PAT_UC, XE_COH_NONE },
64 };
65 
66 static const struct xe_pat_table_entry xehpc_pat_table[] = {
67 	[0] = { XELP_PAT_UC, XE_COH_NONE },
68 	[1] = { XELP_PAT_WC, XE_COH_NONE },
69 	[2] = { XELP_PAT_WT, XE_COH_NONE },
70 	[3] = { XELP_PAT_WB, XE_COH_AT_LEAST_1WAY },
71 	[4] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WT, XE_COH_NONE },
72 	[5] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WB, XE_COH_AT_LEAST_1WAY },
73 	[6] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WT, XE_COH_NONE },
74 	[7] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WB, XE_COH_AT_LEAST_1WAY },
75 };
76 
77 static const struct xe_pat_table_entry xelpg_pat_table[] = {
78 	[0] = { XELPG_PAT_0_WB, XE_COH_NONE },
79 	[1] = { XELPG_PAT_1_WT, XE_COH_NONE },
80 	[2] = { XELPG_PAT_3_UC, XE_COH_NONE },
81 	[3] = { XELPG_PAT_0_WB | XELPG_2_COH_1W, XE_COH_AT_LEAST_1WAY },
82 	[4] = { XELPG_PAT_0_WB | XELPG_3_COH_2W, XE_COH_AT_LEAST_1WAY },
83 };
84 
85 /*
86  * The Xe2 table is getting large/complicated so it's easier to review if
87  * provided in a form that exactly matches the bspec's formatting.  The meaning
88  * of the fields here are:
89  *   - no_promote:  0=promotable, 1=no promote
90  *   - comp_en:     0=disable, 1=enable
91  *   - l3clos:      L3 class of service (0-3)
92  *   - l3_policy:   0=WB, 1=XD ("WB - Transient Display"), 3=UC
93  *   - l4_policy:   0=WB, 1=WT, 3=UC
94  *   - coh_mode:    0=no snoop, 2=1-way coherent, 3=2-way coherent
95  *
96  * Reserved entries should be programmed with the maximum caching, minimum
97  * coherency (which matches an all-0's encoding), so we can just omit them
98  * in the table.
99  */
100 #define XE2_PAT(no_promote, comp_en, l3clos, l3_policy, l4_policy, __coh_mode) \
101 	{ \
102 		.value = (no_promote ? XE2_NO_PROMOTE : 0) | \
103 			(comp_en ? XE2_COMP_EN : 0) | \
104 			REG_FIELD_PREP(XE2_L3_CLOS, l3clos) | \
105 			REG_FIELD_PREP(XE2_L3_POLICY, l3_policy) | \
106 			REG_FIELD_PREP(XE2_L4_POLICY, l4_policy) | \
107 			REG_FIELD_PREP(XE2_COH_MODE, __coh_mode), \
108 		.coh_mode = __coh_mode ? XE_COH_AT_LEAST_1WAY : XE_COH_NONE \
109 	}
110 
111 static const struct xe_pat_table_entry xe2_pat_table[] = {
112 	[ 0] = XE2_PAT( 0, 0, 0, 0, 3, 0 ),
113 	[ 1] = XE2_PAT( 0, 0, 0, 0, 3, 2 ),
114 	[ 2] = XE2_PAT( 0, 0, 0, 0, 3, 3 ),
115 	[ 3] = XE2_PAT( 0, 0, 0, 3, 3, 0 ),
116 	[ 4] = XE2_PAT( 0, 0, 0, 3, 0, 2 ),
117 	[ 5] = XE2_PAT( 0, 0, 0, 3, 3, 2 ),
118 	[ 6] = XE2_PAT( 1, 0, 0, 1, 3, 0 ),
119 	[ 7] = XE2_PAT( 0, 0, 0, 3, 0, 3 ),
120 	[ 8] = XE2_PAT( 0, 0, 0, 3, 0, 0 ),
121 	[ 9] = XE2_PAT( 0, 1, 0, 0, 3, 0 ),
122 	[10] = XE2_PAT( 0, 1, 0, 3, 0, 0 ),
123 	[11] = XE2_PAT( 1, 1, 0, 1, 3, 0 ),
124 	[12] = XE2_PAT( 0, 1, 0, 3, 3, 0 ),
125 	[13] = XE2_PAT( 0, 0, 0, 0, 0, 0 ),
126 	[14] = XE2_PAT( 0, 1, 0, 0, 0, 0 ),
127 	[15] = XE2_PAT( 1, 1, 0, 1, 1, 0 ),
128 	/* 16..19 are reserved; leave set to all 0's */
129 	[20] = XE2_PAT( 0, 0, 1, 0, 3, 0 ),
130 	[21] = XE2_PAT( 0, 1, 1, 0, 3, 0 ),
131 	[22] = XE2_PAT( 0, 0, 1, 0, 3, 2 ),
132 	[23] = XE2_PAT( 0, 0, 1, 0, 3, 3 ),
133 	[24] = XE2_PAT( 0, 0, 2, 0, 3, 0 ),
134 	[25] = XE2_PAT( 0, 1, 2, 0, 3, 0 ),
135 	[26] = XE2_PAT( 0, 0, 2, 0, 3, 2 ),
136 	[27] = XE2_PAT( 0, 0, 2, 0, 3, 3 ),
137 	[28] = XE2_PAT( 0, 0, 3, 0, 3, 0 ),
138 	[29] = XE2_PAT( 0, 1, 3, 0, 3, 0 ),
139 	[30] = XE2_PAT( 0, 0, 3, 0, 3, 2 ),
140 	[31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ),
141 };
142 
143 /* Special PAT values programmed outside the main table */
144 static const struct xe_pat_table_entry xe2_pat_ats = XE2_PAT( 0, 0, 0, 0, 3, 3 );
145 static const struct xe_pat_table_entry xe2_pat_pta = XE2_PAT( 0, 0, 0, 0, 3, 0 );
146 
147 u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index)
148 {
149 	WARN_ON(pat_index >= xe->pat.n_entries);
150 	return xe->pat.table[pat_index].coh_mode;
151 }
152 
153 static void program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[],
154 			int n_entries)
155 {
156 	for (int i = 0; i < n_entries; i++) {
157 		struct xe_reg reg = XE_REG(_PAT_INDEX(i));
158 
159 		xe_mmio_write32(gt, reg, table[i].value);
160 	}
161 }
162 
163 static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry table[],
164 			    int n_entries)
165 {
166 	for (int i = 0; i < n_entries; i++) {
167 		struct xe_reg_mcr reg_mcr = XE_REG_MCR(_PAT_INDEX(i));
168 
169 		xe_gt_mcr_multicast_write(gt, reg_mcr, table[i].value);
170 	}
171 }
172 
173 static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
174 {
175 	struct xe_device *xe = gt_to_xe(gt);
176 	int i, err;
177 
178 	err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
179 	if (err)
180 		goto err_fw;
181 
182 	drm_printf(p, "PAT table:\n");
183 
184 	for (i = 0; i < xe->pat.n_entries; i++) {
185 		u32 pat = xe_mmio_read32(gt, XE_REG(_PAT_INDEX(i)));
186 		u8 mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat);
187 
188 		drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i,
189 			   XELP_MEM_TYPE_STR_MAP[mem_type], pat);
190 	}
191 
192 	err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
193 err_fw:
194 	xe_assert(xe, !err);
195 }
196 
197 static const struct xe_pat_ops xelp_pat_ops = {
198 	.program_graphics = program_pat,
199 	.dump = xelp_dump,
200 };
201 
202 static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
203 {
204 	struct xe_device *xe = gt_to_xe(gt);
205 	int i, err;
206 
207 	err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
208 	if (err)
209 		goto err_fw;
210 
211 	drm_printf(p, "PAT table:\n");
212 
213 	for (i = 0; i < xe->pat.n_entries; i++) {
214 		u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
215 		u8 mem_type;
216 
217 		mem_type = REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat);
218 
219 		drm_printf(p, "PAT[%2d] = %s (%#8x)\n", i,
220 			   XELP_MEM_TYPE_STR_MAP[mem_type], pat);
221 	}
222 
223 	err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
224 err_fw:
225 	xe_assert(xe, !err);
226 }
227 
228 static const struct xe_pat_ops xehp_pat_ops = {
229 	.program_graphics = program_pat_mcr,
230 	.dump = xehp_dump,
231 };
232 
233 static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
234 {
235 	struct xe_device *xe = gt_to_xe(gt);
236 	int i, err;
237 
238 	err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
239 	if (err)
240 		goto err_fw;
241 
242 	drm_printf(p, "PAT table:\n");
243 
244 	for (i = 0; i < xe->pat.n_entries; i++) {
245 		u32 pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
246 
247 		drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i,
248 			   REG_FIELD_GET(XELP_MEM_TYPE_MASK, pat),
249 			   REG_FIELD_GET(XEHPC_CLOS_LEVEL_MASK, pat), pat);
250 	}
251 
252 	err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
253 err_fw:
254 	xe_assert(xe, !err);
255 }
256 
257 static const struct xe_pat_ops xehpc_pat_ops = {
258 	.program_graphics = program_pat_mcr,
259 	.dump = xehpc_dump,
260 };
261 
262 static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
263 {
264 	struct xe_device *xe = gt_to_xe(gt);
265 	int i, err;
266 
267 	err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
268 	if (err)
269 		goto err_fw;
270 
271 	drm_printf(p, "PAT table:\n");
272 
273 	for (i = 0; i < xe->pat.n_entries; i++) {
274 		u32 pat;
275 
276 		if (xe_gt_is_media_type(gt))
277 			pat = xe_mmio_read32(gt, XE_REG(_PAT_INDEX(i)));
278 		else
279 			pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
280 
281 		drm_printf(p, "PAT[%2d] = [ %u, %u ] (%#8x)\n", i,
282 			   REG_FIELD_GET(XELPG_L4_POLICY_MASK, pat),
283 			   REG_FIELD_GET(XELPG_INDEX_COH_MODE_MASK, pat), pat);
284 	}
285 
286 	err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
287 err_fw:
288 	xe_assert(xe, !err);
289 }
290 
291 /*
292  * SAMedia register offsets are adjusted by the write methods and they target
293  * registers that are not MCR, while for normal GT they are MCR
294  */
295 static const struct xe_pat_ops xelpg_pat_ops = {
296 	.program_graphics = program_pat,
297 	.program_media = program_pat_mcr,
298 	.dump = xelpg_dump,
299 };
300 
301 static void xe2lpg_program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[],
302 			       int n_entries)
303 {
304 	program_pat_mcr(gt, table, n_entries);
305 	xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_ATS), xe2_pat_ats.value);
306 
307 	if (IS_DGFX(gt_to_xe(gt)))
308 		xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_PTA), xe2_pat_pta.value);
309 }
310 
311 static void xe2lpm_program_pat(struct xe_gt *gt, const struct xe_pat_table_entry table[],
312 			       int n_entries)
313 {
314 	program_pat(gt, table, n_entries);
315 	xe_mmio_write32(gt, XE_REG(_PAT_ATS), xe2_pat_ats.value);
316 
317 	if (IS_DGFX(gt_to_xe(gt)))
318 		xe_mmio_write32(gt, XE_REG(_PAT_PTA), xe2_pat_pta.value);
319 }
320 
321 static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
322 {
323 	struct xe_device *xe = gt_to_xe(gt);
324 	int i, err;
325 	u32 pat;
326 
327 	err = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
328 	if (err)
329 		goto err_fw;
330 
331 	drm_printf(p, "PAT table:\n");
332 
333 	for (i = 0; i < xe->pat.n_entries; i++) {
334 		if (xe_gt_is_media_type(gt))
335 			pat = xe_mmio_read32(gt, XE_REG(_PAT_INDEX(i)));
336 		else
337 			pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
338 
339 		drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u, %u ]  (%#8x)\n", i,
340 			   !!(pat & XE2_NO_PROMOTE),
341 			   !!(pat & XE2_COMP_EN),
342 			   REG_FIELD_GET(XE2_L3_CLOS, pat),
343 			   REG_FIELD_GET(XE2_L3_POLICY, pat),
344 			   REG_FIELD_GET(XE2_L4_POLICY, pat),
345 			   REG_FIELD_GET(XE2_COH_MODE, pat),
346 			   pat);
347 	}
348 
349 	/*
350 	 * Also print PTA_MODE, which describes how the hardware accesses
351 	 * PPGTT entries.
352 	 */
353 	if (xe_gt_is_media_type(gt))
354 		pat = xe_mmio_read32(gt, XE_REG(_PAT_PTA));
355 	else
356 		pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA));
357 
358 	drm_printf(p, "Page Table Access:\n");
359 	drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u, %u ]  (%#8x)\n",
360 		   !!(pat & XE2_NO_PROMOTE),
361 		   !!(pat & XE2_COMP_EN),
362 		   REG_FIELD_GET(XE2_L3_CLOS, pat),
363 		   REG_FIELD_GET(XE2_L3_POLICY, pat),
364 		   REG_FIELD_GET(XE2_L4_POLICY, pat),
365 		   REG_FIELD_GET(XE2_COH_MODE, pat),
366 		   pat);
367 
368 	err = xe_force_wake_put(gt_to_fw(gt), XE_FW_GT);
369 err_fw:
370 	xe_assert(xe, !err);
371 }
372 
373 static const struct xe_pat_ops xe2_pat_ops = {
374 	.program_graphics = xe2lpg_program_pat,
375 	.program_media = xe2lpm_program_pat,
376 	.dump = xe2_dump,
377 };
378 
379 void xe_pat_init_early(struct xe_device *xe)
380 {
381 	if (GRAPHICS_VER(xe) == 20) {
382 		xe->pat.ops = &xe2_pat_ops;
383 		xe->pat.table = xe2_pat_table;
384 		xe->pat.n_entries = ARRAY_SIZE(xe2_pat_table);
385 		xe->pat.idx[XE_CACHE_NONE] = 3;
386 		xe->pat.idx[XE_CACHE_WT] = 15;
387 		xe->pat.idx[XE_CACHE_WB] = 2;
388 		xe->pat.idx[XE_CACHE_NONE_COMPRESSION] = 12; /*Applicable on xe2 and beyond */
389 	} else if (xe->info.platform == XE_METEORLAKE) {
390 		xe->pat.ops = &xelpg_pat_ops;
391 		xe->pat.table = xelpg_pat_table;
392 		xe->pat.n_entries = ARRAY_SIZE(xelpg_pat_table);
393 		xe->pat.idx[XE_CACHE_NONE] = 2;
394 		xe->pat.idx[XE_CACHE_WT] = 1;
395 		xe->pat.idx[XE_CACHE_WB] = 3;
396 	} else if (xe->info.platform == XE_PVC) {
397 		xe->pat.ops = &xehpc_pat_ops;
398 		xe->pat.table = xehpc_pat_table;
399 		xe->pat.n_entries = ARRAY_SIZE(xehpc_pat_table);
400 		xe->pat.idx[XE_CACHE_NONE] = 0;
401 		xe->pat.idx[XE_CACHE_WT] = 2;
402 		xe->pat.idx[XE_CACHE_WB] = 3;
403 	} else if (xe->info.platform == XE_DG2) {
404 		/*
405 		 * Table is the same as previous platforms, but programming
406 		 * method has changed.
407 		 */
408 		xe->pat.ops = &xehp_pat_ops;
409 		xe->pat.table = xelp_pat_table;
410 		xe->pat.n_entries = ARRAY_SIZE(xelp_pat_table);
411 		xe->pat.idx[XE_CACHE_NONE] = 3;
412 		xe->pat.idx[XE_CACHE_WT] = 2;
413 		xe->pat.idx[XE_CACHE_WB] = 0;
414 	} else if (GRAPHICS_VERx100(xe) <= 1210) {
415 		WARN_ON_ONCE(!IS_DGFX(xe) && !xe->info.has_llc);
416 		xe->pat.ops = &xelp_pat_ops;
417 		xe->pat.table = xelp_pat_table;
418 		xe->pat.n_entries = ARRAY_SIZE(xelp_pat_table);
419 		xe->pat.idx[XE_CACHE_NONE] = 3;
420 		xe->pat.idx[XE_CACHE_WT] = 2;
421 		xe->pat.idx[XE_CACHE_WB] = 0;
422 	} else {
423 		/*
424 		 * Going forward we expect to need new PAT settings for most
425 		 * new platforms; failure to provide a new table can easily
426 		 * lead to subtle, hard-to-debug problems.  If none of the
427 		 * conditions above match the platform we're running on we'll
428 		 * raise an error rather than trying to silently inherit the
429 		 * most recent platform's behavior.
430 		 */
431 		drm_err(&xe->drm, "Missing PAT table for platform with graphics version %d.%02d!\n",
432 			GRAPHICS_VER(xe), GRAPHICS_VERx100(xe) % 100);
433 	}
434 
435 	/* VFs can't program nor dump PAT settings */
436 	if (IS_SRIOV_VF(xe))
437 		xe->pat.ops = NULL;
438 
439 	xe_assert(xe, !xe->pat.ops || xe->pat.ops->dump);
440 	xe_assert(xe, !xe->pat.ops || xe->pat.ops->program_graphics);
441 	xe_assert(xe, !xe->pat.ops || MEDIA_VER(xe) < 13 || xe->pat.ops->program_media);
442 }
443 
444 void xe_pat_init(struct xe_gt *gt)
445 {
446 	struct xe_device *xe = gt_to_xe(gt);
447 
448 	if (!xe->pat.ops)
449 		return;
450 
451 	if (xe_gt_is_media_type(gt))
452 		xe->pat.ops->program_media(gt, xe->pat.table, xe->pat.n_entries);
453 	else
454 		xe->pat.ops->program_graphics(gt, xe->pat.table, xe->pat.n_entries);
455 }
456 
457 void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p)
458 {
459 	struct xe_device *xe = gt_to_xe(gt);
460 
461 	if (!xe->pat.ops)
462 		return;
463 
464 	xe->pat.ops->dump(gt, p);
465 }
466