xref: /linux/drivers/gpu/drm/xe/xe_memirq.c (revision f6e8dc9edf963dbc99085e54f6ced6da9daa6100)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2023 Intel Corporation
4  */
5 
6 #include <drm/drm_managed.h>
7 
8 #include "regs/xe_guc_regs.h"
9 #include "regs/xe_irq_regs.h"
10 #include "regs/xe_regs.h"
11 
12 #include "xe_assert.h"
13 #include "xe_bo.h"
14 #include "xe_device.h"
15 #include "xe_device_types.h"
16 #include "xe_gt.h"
17 #include "xe_guc.h"
18 #include "xe_hw_engine.h"
19 #include "xe_map.h"
20 #include "xe_memirq.h"
21 #include "xe_tile_printk.h"
22 
23 #define memirq_assert(m, condition)	xe_tile_assert(memirq_to_tile(m), condition)
24 #define memirq_printk(m, _level, _fmt, ...)			\
25 	xe_tile_##_level(memirq_to_tile(m), "MEMIRQ: " _fmt, ##__VA_ARGS__)
26 
27 #ifdef CONFIG_DRM_XE_DEBUG_MEMIRQ
28 #define memirq_debug(m, _fmt, ...)	memirq_printk(m, dbg, _fmt, ##__VA_ARGS__)
29 #else
30 #define memirq_debug(...)
31 #endif
32 
33 #define memirq_err(m, _fmt, ...)	memirq_printk(m, err, _fmt, ##__VA_ARGS__)
34 #define memirq_err_ratelimited(m, _fmt, ...)	\
35 	memirq_printk(m, err_ratelimited, _fmt, ##__VA_ARGS__)
36 
37 static struct xe_tile *memirq_to_tile(struct xe_memirq *memirq)
38 {
39 	return container_of(memirq, struct xe_tile, memirq);
40 }
41 
42 static struct xe_device *memirq_to_xe(struct xe_memirq *memirq)
43 {
44 	return tile_to_xe(memirq_to_tile(memirq));
45 }
46 
47 static const char *guc_name(struct xe_guc *guc)
48 {
49 	return xe_gt_is_media_type(guc_to_gt(guc)) ? "media GuC" : "GuC";
50 }
51 
52 /**
53  * DOC: Memory Based Interrupts
54  *
55  * MMIO register based interrupts infrastructure used for non-virtualized mode
56  * or SRIOV-8 (which supports 8 Virtual Functions) does not scale efficiently
57  * to allow delivering interrupts to a large number of Virtual machines or
58  * containers. Memory based interrupt status reporting provides an efficient
59  * and scalable infrastructure.
60  *
61  * For memory based interrupt status reporting hardware sequence is:
62  *  * Engine writes the interrupt event to memory
63  *    (Pointer to memory location is provided by SW. This memory surface must
64  *    be mapped to system memory and must be marked as un-cacheable (UC) on
65  *    Graphics IP Caches)
66  *  * Engine triggers an interrupt to host.
67  */
68 
69 /**
70  * DOC: Memory Based Interrupts Page Layout
71  *
72  * `Memory Based Interrupts`_ requires three different objects, which are
73  * called "page" in the specs, even if they aren't page-sized or aligned.
74  *
75  * To simplify the code we allocate a single page size object and then use
76  * offsets to embedded "pages". The address of those "pages" are then
77  * programmed in the HW via LRI and LRM in the context image.
78  *
79  * - _`Interrupt Status Report Page`: this page contains the interrupt
80  *   status vectors for each unit. Each bit in the interrupt vectors is
81  *   converted to a byte, with the byte being set to 0xFF when an
82  *   interrupt is triggered; interrupt vectors are 16b big so each unit
83  *   gets 16B. One space is reserved for each bit in one of the
84  *   GT_INTR_DWx registers, so this object needs a total of 1024B.
85  *   This object needs to be 4KiB aligned.
86  *
87  * - _`Interrupt Source Report Page`: this is the equivalent of the
88  *   GT_INTR_DWx registers, with each bit in those registers being
89  *   mapped to a byte here. The offsets are the same, just bytes instead
90  *   of bits. This object needs to be cacheline aligned.
91  *
92  * - Interrupt Mask: the HW needs a location to fetch the interrupt
93  *   mask vector to be used by the LRM in the context, so we just use
94  *   the next available space in the interrupt page.
95  *
96  * ::
97  *
98  *   0x0000   +===========+  <== Interrupt Status Report Page
99  *            |           |
100  *            |           |     ____ +----+----------------+
101  *            |           |    /     |  0 | USER INTERRUPT |
102  *            +-----------+ __/      |  1 |                |
103  *            |  HWE(n)   | __       |    | CTX SWITCH     |
104  *            +-----------+   \      |    | WAIT SEMAPHORE |
105  *            |           |    \____ | 15 |                |
106  *            |           |          +----+----------------+
107  *            |           |
108  *   0x0400   +===========+  <== Interrupt Source Report Page
109  *            |  HWE(0)   |
110  *            |  HWE(1)   |
111  *            |           |
112  *            |  HWE(x)   |
113  *   0x0440   +===========+  <== Interrupt Enable Mask
114  *            |           |
115  *            |           |
116  *            +-----------+
117  *
118  *
119  * MSI-X use case
120  *
121  * When using MSI-X, hw engines report interrupt status and source to engine
122  * instance 0. For this scenario, in order to differentiate between the
123  * engines, we need to pass different status/source pointers in the LRC.
124  *
125  * The requirements on those pointers are:
126  * - Interrupt status should be 4KiB aligned
127  * - Interrupt source should be 64 bytes aligned
128  *
129  * To accommodate this, we duplicate the memirq page layout above -
130  * allocating a page for each engine instance and pass this page in the LRC.
131  * Note that the same page can be reused for different engine types.
132  * For example, an LRC executing on CCS #x will have pointers to page #x,
133  * and an LRC executing on BCS #x will have the same pointers.
134  *
135  * ::
136  *
137  *   0x0000   +==============================+  <== page for instance 0 (BCS0, CCS0, etc.)
138  *            | Interrupt Status Report Page |
139  *   0x0400   +==============================+
140  *            | Interrupt Source Report Page |
141  *   0x0440   +==============================+
142  *            | Interrupt Enable Mask        |
143  *            +==============================+
144  *            | Not used                     |
145  *   0x1000   +==============================+  <== page for instance 1 (BCS1, CCS1, etc.)
146  *            | Interrupt Status Report Page |
147  *   0x1400   +==============================+
148  *            | Interrupt Source Report Page |
149  *   0x1440   +==============================+
150  *            | Not used                     |
151  *   0x2000   +==============================+  <== page for instance 2 (BCS2, CCS2, etc.)
152  *            | ...                          |
153  *            +==============================+
154  *
155  */
156 
157 static inline bool hw_reports_to_instance_zero(struct xe_memirq *memirq)
158 {
159 	/*
160 	 * When the HW engines are configured to use MSI-X,
161 	 * they report interrupt status and source to the offset of
162 	 * engine instance 0.
163 	 */
164 	return xe_device_has_msix(memirq_to_xe(memirq));
165 }
166 
167 static int memirq_alloc_pages(struct xe_memirq *memirq)
168 {
169 	struct xe_device *xe = memirq_to_xe(memirq);
170 	struct xe_tile *tile = memirq_to_tile(memirq);
171 	size_t bo_size = hw_reports_to_instance_zero(memirq) ?
172 		XE_HW_ENGINE_MAX_INSTANCE * SZ_4K : SZ_4K;
173 	struct xe_bo *bo;
174 	int err;
175 
176 	BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_SOURCE_OFFSET(0), SZ_64));
177 	BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_STATUS_OFFSET(0), SZ_4K));
178 
179 	bo = xe_managed_bo_create_pin_map(xe, tile, bo_size,
180 					  XE_BO_FLAG_SYSTEM |
181 					  XE_BO_FLAG_GGTT |
182 					  XE_BO_FLAG_GGTT_INVALIDATE |
183 					  XE_BO_FLAG_NEEDS_UC |
184 					  XE_BO_FLAG_NEEDS_CPU_ACCESS);
185 	if (IS_ERR(bo)) {
186 		err = PTR_ERR(bo);
187 		goto out;
188 	}
189 
190 	memirq_assert(memirq, !xe_bo_is_vram(bo));
191 	memirq_assert(memirq, !memirq->bo);
192 
193 	iosys_map_memset(&bo->vmap, 0, 0, bo_size);
194 
195 	memirq->bo = bo;
196 	memirq->source = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_SOURCE_OFFSET(0));
197 	memirq->status = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_STATUS_OFFSET(0));
198 	memirq->mask = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_ENABLE_OFFSET);
199 
200 	memirq_assert(memirq, !memirq->source.is_iomem);
201 	memirq_assert(memirq, !memirq->status.is_iomem);
202 	memirq_assert(memirq, !memirq->mask.is_iomem);
203 
204 	memirq_debug(memirq, "page offsets: bo %#x bo_size %zu source %#x status %#x\n",
205 		     xe_bo_ggtt_addr(bo), bo_size, XE_MEMIRQ_SOURCE_OFFSET(0),
206 		     XE_MEMIRQ_STATUS_OFFSET(0));
207 
208 	return 0;
209 
210 out:
211 	memirq_err(memirq, "Failed to allocate memirq page (%pe)\n", ERR_PTR(err));
212 	return err;
213 }
214 
215 static void memirq_set_enable(struct xe_memirq *memirq, bool enable)
216 {
217 	iosys_map_wr(&memirq->mask, 0, u32, enable ? GENMASK(15, 0) : 0);
218 
219 	memirq->enabled = enable;
220 }
221 
222 /**
223  * xe_memirq_init - Initialize data used by `Memory Based Interrupts`_.
224  * @memirq: the &xe_memirq to initialize
225  *
226  * Allocate `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_
227  * used by `Memory Based Interrupts`_.
228  *
229  * These allocations are managed and will be implicitly released on unload.
230  *
231  * If this function fails then the driver won't be able to operate correctly.
232  * If `Memory Based Interrupts`_ are not used this function will return 0.
233  *
234  * Return: 0 on success or a negative error code on failure.
235  */
236 int xe_memirq_init(struct xe_memirq *memirq)
237 {
238 	struct xe_device *xe = memirq_to_xe(memirq);
239 	int err;
240 
241 	if (!xe_device_uses_memirq(xe))
242 		return 0;
243 
244 	err = memirq_alloc_pages(memirq);
245 	if (unlikely(err))
246 		return err;
247 
248 	/* we need to start with all irqs enabled */
249 	memirq_set_enable(memirq, true);
250 
251 	return 0;
252 }
253 
254 static u32 __memirq_source_page(struct xe_memirq *memirq, u16 instance)
255 {
256 	memirq_assert(memirq, instance <= XE_HW_ENGINE_MAX_INSTANCE);
257 	memirq_assert(memirq, memirq->bo);
258 
259 	instance = hw_reports_to_instance_zero(memirq) ? instance : 0;
260 	return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_SOURCE_OFFSET(instance);
261 }
262 
263 /**
264  * xe_memirq_source_ptr - Get GGTT's offset of the `Interrupt Source Report Page`_.
265  * @memirq: the &xe_memirq to query
266  * @hwe: the hw engine for which we want the report page
267  *
268  * Shall be called when `Memory Based Interrupts`_ are used
269  * and xe_memirq_init() didn't fail.
270  *
271  * Return: GGTT's offset of the `Interrupt Source Report Page`_.
272  */
273 u32 xe_memirq_source_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
274 {
275 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
276 
277 	return __memirq_source_page(memirq, hwe->instance);
278 }
279 
280 static u32 __memirq_status_page(struct xe_memirq *memirq, u16 instance)
281 {
282 	memirq_assert(memirq, instance <= XE_HW_ENGINE_MAX_INSTANCE);
283 	memirq_assert(memirq, memirq->bo);
284 
285 	instance = hw_reports_to_instance_zero(memirq) ? instance : 0;
286 	return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_STATUS_OFFSET(instance);
287 }
288 
289 /**
290  * xe_memirq_status_ptr - Get GGTT's offset of the `Interrupt Status Report Page`_.
291  * @memirq: the &xe_memirq to query
292  * @hwe: the hw engine for which we want the report page
293  *
294  * Shall be called when `Memory Based Interrupts`_ are used
295  * and xe_memirq_init() didn't fail.
296  *
297  * Return: GGTT's offset of the `Interrupt Status Report Page`_.
298  */
299 u32 xe_memirq_status_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
300 {
301 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
302 
303 	return __memirq_status_page(memirq, hwe->instance);
304 }
305 
306 /**
307  * xe_memirq_enable_ptr - Get GGTT's offset of the Interrupt Enable Mask.
308  * @memirq: the &xe_memirq to query
309  *
310  * Shall be called when `Memory Based Interrupts`_ are used
311  * and xe_memirq_init() didn't fail.
312  *
313  * Return: GGTT's offset of the Interrupt Enable Mask.
314  */
315 u32 xe_memirq_enable_ptr(struct xe_memirq *memirq)
316 {
317 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
318 	memirq_assert(memirq, memirq->bo);
319 
320 	return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_ENABLE_OFFSET;
321 }
322 
323 /**
324  * xe_memirq_init_guc - Prepare GuC for `Memory Based Interrupts`_.
325  * @memirq: the &xe_memirq
326  * @guc: the &xe_guc to setup
327  *
328  * Register `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_
329  * to be used by the GuC when `Memory Based Interrupts`_ are required.
330  *
331  * Shall be called when `Memory Based Interrupts`_ are used
332  * and xe_memirq_init() didn't fail.
333  *
334  * Return: 0 on success or a negative error code on failure.
335  */
336 int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc)
337 {
338 	bool is_media = xe_gt_is_media_type(guc_to_gt(guc));
339 	u32 offset = is_media ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
340 	u32 source, status;
341 	int err;
342 
343 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
344 
345 	source = __memirq_source_page(memirq, 0) + offset;
346 	status = __memirq_status_page(memirq, 0) + offset * SZ_16;
347 
348 	err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_SOURCE_ADDR_KEY,
349 				source);
350 	if (unlikely(err))
351 		goto failed;
352 
353 	err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_STATUS_ADDR_KEY,
354 				status);
355 	if (unlikely(err))
356 		goto failed;
357 
358 	return 0;
359 
360 failed:
361 	memirq_err(memirq, "Failed to setup report pages in %s (%pe)\n",
362 		   guc_name(guc), ERR_PTR(err));
363 	return err;
364 }
365 
366 /**
367  * xe_memirq_reset - Disable processing of `Memory Based Interrupts`_.
368  * @memirq: struct xe_memirq
369  *
370  * This is part of the driver IRQ setup flow.
371  *
372  * This function shall only be used on platforms that use
373  * `Memory Based Interrupts`_.
374  */
375 void xe_memirq_reset(struct xe_memirq *memirq)
376 {
377 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
378 
379 	if (memirq->bo)
380 		memirq_set_enable(memirq, false);
381 }
382 
383 /**
384  * xe_memirq_postinstall - Enable processing of `Memory Based Interrupts`_.
385  * @memirq: the &xe_memirq
386  *
387  * This is part of the driver IRQ setup flow.
388  *
389  * This function shall only be used on platforms that use
390  * `Memory Based Interrupts`_.
391  */
392 void xe_memirq_postinstall(struct xe_memirq *memirq)
393 {
394 	memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
395 
396 	if (memirq->bo)
397 		memirq_set_enable(memirq, true);
398 }
399 
400 static bool __memirq_received(struct xe_memirq *memirq,
401 			      struct iosys_map *vector, u16 offset,
402 			      const char *name, bool clear)
403 {
404 	u8 value;
405 
406 	value = iosys_map_rd(vector, offset, u8);
407 	if (value) {
408 		if (value != 0xff)
409 			memirq_err_ratelimited(memirq,
410 					       "Unexpected memirq value %#x from %s at %u\n",
411 					       value, name, offset);
412 		if (clear)
413 			iosys_map_wr(vector, offset, u8, 0x00);
414 	}
415 
416 	return value;
417 }
418 
419 static bool memirq_received_noclear(struct xe_memirq *memirq,
420 				    struct iosys_map *vector,
421 				    u16 offset, const char *name)
422 {
423 	return __memirq_received(memirq, vector, offset, name, false);
424 }
425 
426 static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
427 			    u16 offset, const char *name)
428 {
429 	return __memirq_received(memirq, vector, offset, name, true);
430 }
431 
432 static void memirq_dispatch_engine(struct xe_memirq *memirq, struct iosys_map *status,
433 				   struct xe_hw_engine *hwe)
434 {
435 	memirq_debug(memirq, "STATUS %s %*ph\n", hwe->name, 16, status->vaddr);
436 
437 	if (memirq_received(memirq, status, ilog2(GT_MI_USER_INTERRUPT), hwe->name))
438 		xe_hw_engine_handle_irq(hwe, GT_MI_USER_INTERRUPT);
439 }
440 
441 static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *status,
442 				struct xe_guc *guc)
443 {
444 	const char *name = guc_name(guc);
445 
446 	memirq_debug(memirq, "STATUS %s %*ph\n", name, 16, status->vaddr);
447 
448 	if (memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
449 		xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
450 
451 	/*
452 	 * This is a software interrupt that must be cleared after it's consumed
453 	 * to avoid race conditions where xe_gt_sriov_vf_recovery_pending()
454 	 * returns false.
455 	 */
456 	if (memirq_received_noclear(memirq, status, ilog2(GUC_INTR_SW_INT_0),
457 				    name)) {
458 		xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
459 		iosys_map_wr(status, ilog2(GUC_INTR_SW_INT_0), u8, 0x00);
460 	}
461 }
462 
463 /**
464  * xe_memirq_hwe_handler - Check and process interrupts for a specific HW engine.
465  * @memirq: the &xe_memirq
466  * @hwe: the hw engine to process
467  *
468  * This function reads and dispatches `Memory Based Interrupts` for the provided HW engine.
469  */
470 void xe_memirq_hwe_handler(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
471 {
472 	u16 offset = hwe->irq_offset;
473 	u16 instance = hw_reports_to_instance_zero(memirq) ? hwe->instance : 0;
474 	struct iosys_map src_offset = IOSYS_MAP_INIT_OFFSET(&memirq->bo->vmap,
475 							    XE_MEMIRQ_SOURCE_OFFSET(instance));
476 
477 	if (memirq_received(memirq, &src_offset, offset, "SRC")) {
478 		struct iosys_map status_offset =
479 			IOSYS_MAP_INIT_OFFSET(&memirq->bo->vmap,
480 					      XE_MEMIRQ_STATUS_OFFSET(instance) + offset * SZ_16);
481 		memirq_dispatch_engine(memirq, &status_offset, hwe);
482 	}
483 }
484 
485 /**
486  * xe_memirq_guc_sw_int_0_irq_pending() - SW_INT_0 IRQ is pending
487  * @memirq: the &xe_memirq
488  * @guc: the &xe_guc to check for IRQ
489  *
490  * Return: True if SW_INT_0 IRQ is pending on @guc, False otherwise
491  */
492 bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc)
493 {
494 	struct xe_gt *gt = guc_to_gt(guc);
495 	u32 offset = xe_gt_is_media_type(gt) ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
496 	struct iosys_map map = IOSYS_MAP_INIT_OFFSET(&memirq->status, offset * SZ_16);
497 
498 	return memirq_received_noclear(memirq, &map, ilog2(GUC_INTR_SW_INT_0),
499 				       guc_name(guc));
500 }
501 
502 /**
503  * xe_memirq_handler - The `Memory Based Interrupts`_ Handler.
504  * @memirq: the &xe_memirq
505  *
506  * This function reads and dispatches `Memory Based Interrupts`.
507  */
508 void xe_memirq_handler(struct xe_memirq *memirq)
509 {
510 	struct xe_device *xe = memirq_to_xe(memirq);
511 	struct xe_tile *tile = memirq_to_tile(memirq);
512 	struct xe_hw_engine *hwe;
513 	enum xe_hw_engine_id id;
514 	struct iosys_map map;
515 	unsigned int gtid;
516 	struct xe_gt *gt;
517 
518 	if (!memirq->bo)
519 		return;
520 
521 	memirq_assert(memirq, !memirq->source.is_iomem);
522 	memirq_debug(memirq, "SOURCE %*ph\n", 32, memirq->source.vaddr);
523 	memirq_debug(memirq, "SOURCE %*ph\n", 32, memirq->source.vaddr + 32);
524 
525 	for_each_gt(gt, xe, gtid) {
526 		if (gt->tile != tile)
527 			continue;
528 
529 		for_each_hw_engine(hwe, gt, id)
530 			xe_memirq_hwe_handler(memirq, hwe);
531 	}
532 
533 	/* GuC and media GuC (if present) must be checked separately */
534 
535 	if (memirq_received(memirq, &memirq->source, ilog2(INTR_GUC), "SRC")) {
536 		map = IOSYS_MAP_INIT_OFFSET(&memirq->status, ilog2(INTR_GUC) * SZ_16);
537 		memirq_dispatch_guc(memirq, &map, &tile->primary_gt->uc.guc);
538 	}
539 
540 	if (!tile->media_gt)
541 		return;
542 
543 	if (memirq_received(memirq, &memirq->source, ilog2(INTR_MGUC), "SRC")) {
544 		map = IOSYS_MAP_INIT_OFFSET(&memirq->status, ilog2(INTR_MGUC) * SZ_16);
545 		memirq_dispatch_guc(memirq, &map, &tile->media_gt->uc.guc);
546 	}
547 }
548