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
memirq_to_tile(struct xe_memirq * memirq)35 static struct xe_tile *memirq_to_tile(struct xe_memirq *memirq)
36 {
37 return container_of(memirq, struct xe_tile, memirq);
38 }
39
memirq_to_xe(struct xe_memirq * memirq)40 static struct xe_device *memirq_to_xe(struct xe_memirq *memirq)
41 {
42 return tile_to_xe(memirq_to_tile(memirq));
43 }
44
guc_name(struct xe_guc * guc)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
hw_reports_to_instance_zero(struct xe_memirq * memirq)155 static inline bool hw_reports_to_instance_zero(struct xe_memirq *memirq)
156 {
157 /*
158 * When the HW engines are configured to use MSI-X,
159 * they report interrupt status and source to the offset of
160 * engine instance 0.
161 */
162 return xe_device_has_msix(memirq_to_xe(memirq));
163 }
164
memirq_alloc_pages(struct xe_memirq * memirq)165 static int memirq_alloc_pages(struct xe_memirq *memirq)
166 {
167 struct xe_device *xe = memirq_to_xe(memirq);
168 struct xe_tile *tile = memirq_to_tile(memirq);
169 size_t bo_size = hw_reports_to_instance_zero(memirq) ?
170 XE_HW_ENGINE_MAX_INSTANCE * SZ_4K : SZ_4K;
171 struct xe_bo *bo;
172 int err;
173
174 BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_SOURCE_OFFSET(0), SZ_64));
175 BUILD_BUG_ON(!IS_ALIGNED(XE_MEMIRQ_STATUS_OFFSET(0), SZ_4K));
176
177 bo = xe_managed_bo_create_pin_map(xe, tile, bo_size,
178 XE_BO_FLAG_SYSTEM |
179 XE_BO_FLAG_GGTT |
180 XE_BO_FLAG_GGTT_INVALIDATE |
181 XE_BO_FLAG_NEEDS_UC |
182 XE_BO_FLAG_NEEDS_CPU_ACCESS);
183 if (IS_ERR(bo)) {
184 err = PTR_ERR(bo);
185 goto out;
186 }
187
188 memirq_assert(memirq, !xe_bo_is_vram(bo));
189 memirq_assert(memirq, !memirq->bo);
190
191 iosys_map_memset(&bo->vmap, 0, 0, bo_size);
192
193 memirq->bo = bo;
194 memirq->source = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_SOURCE_OFFSET(0));
195 memirq->status = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_STATUS_OFFSET(0));
196 memirq->mask = IOSYS_MAP_INIT_OFFSET(&bo->vmap, XE_MEMIRQ_ENABLE_OFFSET);
197
198 memirq_assert(memirq, !memirq->source.is_iomem);
199 memirq_assert(memirq, !memirq->status.is_iomem);
200 memirq_assert(memirq, !memirq->mask.is_iomem);
201
202 memirq_debug(memirq, "page offsets: bo %#x bo_size %zu source %#x status %#x\n",
203 xe_bo_ggtt_addr(bo), bo_size, XE_MEMIRQ_SOURCE_OFFSET(0),
204 XE_MEMIRQ_STATUS_OFFSET(0));
205
206 return 0;
207
208 out:
209 memirq_err(memirq, "Failed to allocate memirq page (%pe)\n", ERR_PTR(err));
210 return err;
211 }
212
memirq_set_enable(struct xe_memirq * memirq,bool enable)213 static void memirq_set_enable(struct xe_memirq *memirq, bool enable)
214 {
215 iosys_map_wr(&memirq->mask, 0, u32, enable ? GENMASK(15, 0) : 0);
216
217 memirq->enabled = enable;
218 }
219
220 /**
221 * xe_memirq_init - Initialize data used by `Memory Based Interrupts`_.
222 * @memirq: the &xe_memirq to initialize
223 *
224 * Allocate `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_
225 * used by `Memory Based Interrupts`_.
226 *
227 * These allocations are managed and will be implicitly released on unload.
228 *
229 * If this function fails then the driver won't be able to operate correctly.
230 * If `Memory Based Interrupts`_ are not used this function will return 0.
231 *
232 * Return: 0 on success or a negative error code on failure.
233 */
xe_memirq_init(struct xe_memirq * memirq)234 int xe_memirq_init(struct xe_memirq *memirq)
235 {
236 struct xe_device *xe = memirq_to_xe(memirq);
237 int err;
238
239 if (!xe_device_uses_memirq(xe))
240 return 0;
241
242 err = memirq_alloc_pages(memirq);
243 if (unlikely(err))
244 return err;
245
246 /* we need to start with all irqs enabled */
247 memirq_set_enable(memirq, true);
248
249 return 0;
250 }
251
__memirq_source_page(struct xe_memirq * memirq,u16 instance)252 static u32 __memirq_source_page(struct xe_memirq *memirq, u16 instance)
253 {
254 memirq_assert(memirq, instance <= XE_HW_ENGINE_MAX_INSTANCE);
255 memirq_assert(memirq, memirq->bo);
256
257 instance = hw_reports_to_instance_zero(memirq) ? instance : 0;
258 return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_SOURCE_OFFSET(instance);
259 }
260
261 /**
262 * xe_memirq_source_ptr - Get GGTT's offset of the `Interrupt Source Report Page`_.
263 * @memirq: the &xe_memirq to query
264 * @hwe: the hw engine for which we want the report page
265 *
266 * Shall be called when `Memory Based Interrupts`_ are used
267 * and xe_memirq_init() didn't fail.
268 *
269 * Return: GGTT's offset of the `Interrupt Source Report Page`_.
270 */
xe_memirq_source_ptr(struct xe_memirq * memirq,struct xe_hw_engine * hwe)271 u32 xe_memirq_source_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
272 {
273 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
274
275 return __memirq_source_page(memirq, hwe->instance);
276 }
277
__memirq_status_page(struct xe_memirq * memirq,u16 instance)278 static u32 __memirq_status_page(struct xe_memirq *memirq, u16 instance)
279 {
280 memirq_assert(memirq, instance <= XE_HW_ENGINE_MAX_INSTANCE);
281 memirq_assert(memirq, memirq->bo);
282
283 instance = hw_reports_to_instance_zero(memirq) ? instance : 0;
284 return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_STATUS_OFFSET(instance);
285 }
286
287 /**
288 * xe_memirq_status_ptr - Get GGTT's offset of the `Interrupt Status Report Page`_.
289 * @memirq: the &xe_memirq to query
290 * @hwe: the hw engine for which we want the report page
291 *
292 * Shall be called when `Memory Based Interrupts`_ are used
293 * and xe_memirq_init() didn't fail.
294 *
295 * Return: GGTT's offset of the `Interrupt Status Report Page`_.
296 */
xe_memirq_status_ptr(struct xe_memirq * memirq,struct xe_hw_engine * hwe)297 u32 xe_memirq_status_ptr(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
298 {
299 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
300
301 return __memirq_status_page(memirq, hwe->instance);
302 }
303
304 /**
305 * xe_memirq_enable_ptr - Get GGTT's offset of the Interrupt Enable Mask.
306 * @memirq: the &xe_memirq to query
307 *
308 * Shall be called when `Memory Based Interrupts`_ are used
309 * and xe_memirq_init() didn't fail.
310 *
311 * Return: GGTT's offset of the Interrupt Enable Mask.
312 */
xe_memirq_enable_ptr(struct xe_memirq * memirq)313 u32 xe_memirq_enable_ptr(struct xe_memirq *memirq)
314 {
315 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
316 memirq_assert(memirq, memirq->bo);
317
318 return xe_bo_ggtt_addr(memirq->bo) + XE_MEMIRQ_ENABLE_OFFSET;
319 }
320
321 /**
322 * xe_memirq_init_guc - Prepare GuC for `Memory Based Interrupts`_.
323 * @memirq: the &xe_memirq
324 * @guc: the &xe_guc to setup
325 *
326 * Register `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_
327 * to be used by the GuC when `Memory Based Interrupts`_ are required.
328 *
329 * Shall be called when `Memory Based Interrupts`_ are used
330 * and xe_memirq_init() didn't fail.
331 *
332 * Return: 0 on success or a negative error code on failure.
333 */
xe_memirq_init_guc(struct xe_memirq * memirq,struct xe_guc * guc)334 int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc)
335 {
336 bool is_media = xe_gt_is_media_type(guc_to_gt(guc));
337 u32 offset = is_media ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
338 u32 source, status;
339 int err;
340
341 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
342
343 source = __memirq_source_page(memirq, 0) + offset;
344 status = __memirq_status_page(memirq, 0) + offset * SZ_16;
345
346 err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_SOURCE_ADDR_KEY,
347 source);
348 if (unlikely(err))
349 goto failed;
350
351 err = xe_guc_self_cfg64(guc, GUC_KLV_SELF_CFG_MEMIRQ_STATUS_ADDR_KEY,
352 status);
353 if (unlikely(err))
354 goto failed;
355
356 return 0;
357
358 failed:
359 memirq_err(memirq, "Failed to setup report pages in %s (%pe)\n",
360 guc_name(guc), ERR_PTR(err));
361 return err;
362 }
363
364 /**
365 * xe_memirq_reset - Disable processing of `Memory Based Interrupts`_.
366 * @memirq: struct xe_memirq
367 *
368 * This is part of the driver IRQ setup flow.
369 *
370 * This function shall only be used on platforms that use
371 * `Memory Based Interrupts`_.
372 */
xe_memirq_reset(struct xe_memirq * memirq)373 void xe_memirq_reset(struct xe_memirq *memirq)
374 {
375 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
376
377 if (memirq->bo)
378 memirq_set_enable(memirq, false);
379 }
380
381 /**
382 * xe_memirq_postinstall - Enable processing of `Memory Based Interrupts`_.
383 * @memirq: the &xe_memirq
384 *
385 * This is part of the driver IRQ setup flow.
386 *
387 * This function shall only be used on platforms that use
388 * `Memory Based Interrupts`_.
389 */
xe_memirq_postinstall(struct xe_memirq * memirq)390 void xe_memirq_postinstall(struct xe_memirq *memirq)
391 {
392 memirq_assert(memirq, xe_device_uses_memirq(memirq_to_xe(memirq)));
393
394 if (memirq->bo)
395 memirq_set_enable(memirq, true);
396 }
397
__memirq_received(struct xe_memirq * memirq,struct iosys_map * vector,u16 offset,const char * name,bool clear)398 static bool __memirq_received(struct xe_memirq *memirq,
399 struct iosys_map *vector, u16 offset,
400 const char *name, bool clear)
401 {
402 u8 value;
403
404 value = iosys_map_rd(vector, offset, u8);
405 if (value) {
406 if (value != 0xff)
407 memirq_err_ratelimited(memirq,
408 "Unexpected memirq value %#x from %s at %u\n",
409 value, name, offset);
410 if (clear)
411 iosys_map_wr(vector, offset, u8, 0x00);
412 }
413
414 return value;
415 }
416
memirq_received_noclear(struct xe_memirq * memirq,struct iosys_map * vector,u16 offset,const char * name)417 static bool memirq_received_noclear(struct xe_memirq *memirq,
418 struct iosys_map *vector,
419 u16 offset, const char *name)
420 {
421 return __memirq_received(memirq, vector, offset, name, false);
422 }
423
memirq_received(struct xe_memirq * memirq,struct iosys_map * vector,u16 offset,const char * name)424 static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
425 u16 offset, const char *name)
426 {
427 return __memirq_received(memirq, vector, offset, name, true);
428 }
429
memirq_assume_received(struct xe_memirq * memirq,const char * source,u16 offset,const char * status)430 static void memirq_assume_received(struct xe_memirq *memirq, const char *source,
431 u16 offset, const char *status)
432 {
433 memirq_debug(memirq, "ASSUME %s %s(%u)\n", source, status, offset);
434 }
435
memirq_dispatch_engine(struct xe_memirq * memirq,struct iosys_map * status,struct xe_hw_engine * hwe)436 static void memirq_dispatch_engine(struct xe_memirq *memirq, struct iosys_map *status,
437 struct xe_hw_engine *hwe)
438 {
439 memirq_debug(memirq, "STATUS %s %*ph\n", hwe->name, 16, status->vaddr);
440
441 /*
442 * The programming note says to assume that GT_MI_USER_INTERRUPT is always
443 * set. Check and clear related status byte just for a debug.
444 */
445 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEMIRQ) &&
446 !memirq_received(memirq, status, ilog2(GT_MI_USER_INTERRUPT), hwe->name))
447 memirq_assume_received(memirq, hwe->name, ilog2(GT_MI_USER_INTERRUPT), "USER");
448 xe_hw_engine_handle_irq(hwe, GT_MI_USER_INTERRUPT);
449 }
450
memirq_dispatch_guc(struct xe_memirq * memirq,struct iosys_map * status,struct xe_guc * guc)451 static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *status,
452 struct xe_guc *guc)
453 {
454 const char *name = guc_name(guc);
455
456 memirq_debug(memirq, "STATUS %s %*ph\n", name, 16, status->vaddr);
457
458 /*
459 * The programming note says to assume that GUC_INTR_GUC2HOST is always
460 * set. Check and clear related status byte just for a debug.
461 */
462 if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_MEMIRQ) &&
463 !memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
464 memirq_assume_received(memirq, name, ilog2(GUC_INTR_GUC2HOST), "GUC2HOST");
465 xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
466
467 /*
468 * This is a software interrupt that must be cleared after it's consumed
469 * to avoid race conditions where xe_gt_sriov_vf_recovery_pending()
470 * returns false.
471 */
472 if (memirq_received_noclear(memirq, status, ilog2(GUC_INTR_SW_INT_0),
473 name)) {
474 xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
475 iosys_map_wr(status, ilog2(GUC_INTR_SW_INT_0), u8, 0x00);
476 }
477 }
478
479 /**
480 * xe_memirq_hwe_handler - Check and process interrupts for a specific HW engine.
481 * @memirq: the &xe_memirq
482 * @hwe: the hw engine to process
483 *
484 * This function reads and dispatches `Memory Based Interrupts` for the provided HW engine.
485 */
xe_memirq_hwe_handler(struct xe_memirq * memirq,struct xe_hw_engine * hwe)486 void xe_memirq_hwe_handler(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
487 {
488 u16 offset = hwe->irq_offset;
489 u16 instance = hw_reports_to_instance_zero(memirq) ? hwe->instance : 0;
490 struct iosys_map src_offset = IOSYS_MAP_INIT_OFFSET(&memirq->bo->vmap,
491 XE_MEMIRQ_SOURCE_OFFSET(instance));
492
493 if (memirq_received(memirq, &src_offset, offset, "SRC")) {
494 struct iosys_map status_offset =
495 IOSYS_MAP_INIT_OFFSET(&memirq->bo->vmap,
496 XE_MEMIRQ_STATUS_OFFSET(instance) + offset * SZ_16);
497 memirq_dispatch_engine(memirq, &status_offset, hwe);
498 }
499 }
500
501 /**
502 * xe_memirq_guc_sw_int_0_irq_pending() - SW_INT_0 IRQ is pending
503 * @memirq: the &xe_memirq
504 * @guc: the &xe_guc to check for IRQ
505 *
506 * Return: True if SW_INT_0 IRQ is pending on @guc, False otherwise
507 */
xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq * memirq,struct xe_guc * guc)508 bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc)
509 {
510 struct xe_gt *gt = guc_to_gt(guc);
511 u32 offset = xe_gt_is_media_type(gt) ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
512 struct iosys_map map = IOSYS_MAP_INIT_OFFSET(&memirq->status, offset * SZ_16);
513
514 return memirq_received_noclear(memirq, &map, ilog2(GUC_INTR_SW_INT_0),
515 guc_name(guc));
516 }
517
518 /**
519 * xe_memirq_handler - The `Memory Based Interrupts`_ Handler.
520 * @memirq: the &xe_memirq
521 *
522 * This function reads and dispatches `Memory Based Interrupts`.
523 */
xe_memirq_handler(struct xe_memirq * memirq)524 void xe_memirq_handler(struct xe_memirq *memirq)
525 {
526 struct xe_device *xe = memirq_to_xe(memirq);
527 struct xe_tile *tile = memirq_to_tile(memirq);
528 struct xe_hw_engine *hwe;
529 enum xe_hw_engine_id id;
530 struct iosys_map map;
531 unsigned int gtid;
532 struct xe_gt *gt;
533
534 if (!memirq->bo)
535 return;
536
537 memirq_assert(memirq, !memirq->source.is_iomem);
538 memirq_debug(memirq, "SOURCE %*ph\n", 32, memirq->source.vaddr);
539 memirq_debug(memirq, "SOURCE %*ph\n", 32, memirq->source.vaddr + 32);
540
541 for_each_gt(gt, xe, gtid) {
542 if (gt->tile != tile)
543 continue;
544
545 for_each_hw_engine(hwe, gt, id)
546 xe_memirq_hwe_handler(memirq, hwe);
547 }
548
549 /* GuC and media GuC (if present) must be checked separately */
550
551 if (memirq_received(memirq, &memirq->source, ilog2(INTR_GUC), "SRC")) {
552 map = IOSYS_MAP_INIT_OFFSET(&memirq->status, ilog2(INTR_GUC) * SZ_16);
553 memirq_dispatch_guc(memirq, &map, &tile->primary_gt->uc.guc);
554 }
555
556 if (!tile->media_gt)
557 return;
558
559 if (memirq_received(memirq, &memirq->source, ilog2(INTR_MGUC), "SRC")) {
560 map = IOSYS_MAP_INIT_OFFSET(&memirq->status, ilog2(INTR_MGUC) * SZ_16);
561 memirq_dispatch_guc(memirq, &map, &tile->media_gt->uc.guc);
562 }
563 }
564