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