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