// SPDX-License-Identifier: MIT /* * Copyright © 2023 Intel Corporation */ #include <drm/drm_managed.h> #include "regs/xe_gt_regs.h" #include "regs/xe_guc_regs.h" #include "regs/xe_regs.h" #include "xe_assert.h" #include "xe_bo.h" #include "xe_device.h" #include "xe_device_types.h" #include "xe_gt.h" #include "xe_gt_printk.h" #include "xe_guc.h" #include "xe_hw_engine.h" #include "xe_map.h" #include "xe_memirq.h" #include "xe_sriov.h" #include "xe_sriov_printk.h" #define memirq_assert(m, condition) … #define memirq_debug(m, msg...) … static struct xe_tile *memirq_to_tile(struct xe_memirq *memirq) { … } static struct xe_device *memirq_to_xe(struct xe_memirq *memirq) { … } static const char *guc_name(struct xe_guc *guc) { … } /** * DOC: Memory Based Interrupts * * MMIO register based interrupts infrastructure used for non-virtualized mode * or SRIOV-8 (which supports 8 Virtual Functions) does not scale efficiently * to allow delivering interrupts to a large number of Virtual machines or * containers. Memory based interrupt status reporting provides an efficient * and scalable infrastructure. * * For memory based interrupt status reporting hardware sequence is: * * Engine writes the interrupt event to memory * (Pointer to memory location is provided by SW. This memory surface must * be mapped to system memory and must be marked as un-cacheable (UC) on * Graphics IP Caches) * * Engine triggers an interrupt to host. */ /** * DOC: Memory Based Interrupts Page Layout * * `Memory Based Interrupts`_ requires three different objects, which are * called "page" in the specs, even if they aren't page-sized or aligned. * * To simplify the code we allocate a single page size object and then use * offsets to embedded "pages". The address of those "pages" are then * programmed in the HW via LRI and LRM in the context image. * * - _`Interrupt Status Report Page`: this page contains the interrupt * status vectors for each unit. Each bit in the interrupt vectors is * converted to a byte, with the byte being set to 0xFF when an * interrupt is triggered; interrupt vectors are 16b big so each unit * gets 16B. One space is reserved for each bit in one of the * GT_INTR_DWx registers, so this object needs a total of 1024B. * This object needs to be 4KiB aligned. * * - _`Interrupt Source Report Page`: this is the equivalent of the * GEN11_GT_INTR_DWx registers, with each bit in those registers being * mapped to a byte here. The offsets are the same, just bytes instead * of bits. This object needs to be cacheline aligned. * * - Interrupt Mask: the HW needs a location to fetch the interrupt * mask vector to be used by the LRM in the context, so we just use * the next available space in the interrupt page. * * :: * * 0x0000 +===========+ <== Interrupt Status Report Page * | | * | | ____ +----+----------------+ * | | / | 0 | USER INTERRUPT | * +-----------+ __/ | 1 | | * | HWE(n) | __ | | CTX SWITCH | * +-----------+ \ | | WAIT SEMAPHORE | * | | \____ | 15 | | * | | +----+----------------+ * | | * 0x0400 +===========+ <== Interrupt Source Report Page * | HWE(0) | * | HWE(1) | * | | * | HWE(x) | * 0x0440 +===========+ <== Interrupt Enable Mask * | | * | | * +-----------+ */ static void __release_xe_bo(struct drm_device *drm, void *arg) { … } static int memirq_alloc_pages(struct xe_memirq *memirq) { … } static void memirq_set_enable(struct xe_memirq *memirq, bool enable) { … } /** * xe_memirq_init - Initialize data used by `Memory Based Interrupts`_. * @memirq: the &xe_memirq to initialize * * Allocate `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_ * used by `Memory Based Interrupts`_. * * These allocations are managed and will be implicitly released on unload. * * Note: This function shall be called only by the VF driver. * * If this function fails then VF driver won't be able to operate correctly. * If `Memory Based Interrupts`_ are not used this function will return 0. * * Return: 0 on success or a negative error code on failure. */ int xe_memirq_init(struct xe_memirq *memirq) { … } /** * xe_memirq_source_ptr - Get GGTT's offset of the `Interrupt Source Report Page`_. * @memirq: the &xe_memirq to query * * Shall be called only on VF driver when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the `Interrupt Source Report Page`_. */ u32 xe_memirq_source_ptr(struct xe_memirq *memirq) { … } /** * xe_memirq_status_ptr - Get GGTT's offset of the `Interrupt Status Report Page`_. * @memirq: the &xe_memirq to query * * Shall be called only on VF driver when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the `Interrupt Status Report Page`_. */ u32 xe_memirq_status_ptr(struct xe_memirq *memirq) { … } /** * xe_memirq_enable_ptr - Get GGTT's offset of the Interrupt Enable Mask. * @memirq: the &xe_memirq to query * * Shall be called only on VF driver when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: GGTT's offset of the Interrupt Enable Mask. */ u32 xe_memirq_enable_ptr(struct xe_memirq *memirq) { … } /** * xe_memirq_init_guc - Prepare GuC for `Memory Based Interrupts`_. * @memirq: the &xe_memirq * @guc: the &xe_guc to setup * * Register `Interrupt Source Report Page`_ and `Interrupt Status Report Page`_ * to be used by the GuC when `Memory Based Interrupts`_ are required. * * Shall be called only on VF driver when `Memory Based Interrupts`_ are used * and xe_memirq_init() didn't fail. * * Return: 0 on success or a negative error code on failure. */ int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc) { … } /** * xe_memirq_reset - Disable processing of `Memory Based Interrupts`_. * @memirq: struct xe_memirq * * This is part of the driver IRQ setup flow. * * This function shall only be used by the VF driver on platforms that use * `Memory Based Interrupts`_. */ void xe_memirq_reset(struct xe_memirq *memirq) { … } /** * xe_memirq_postinstall - Enable processing of `Memory Based Interrupts`_. * @memirq: the &xe_memirq * * This is part of the driver IRQ setup flow. * * This function shall only be used by the VF driver on platforms that use * `Memory Based Interrupts`_. */ void xe_memirq_postinstall(struct xe_memirq *memirq) { … } static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector, u16 offset, const char *name) { … } static void memirq_dispatch_engine(struct xe_memirq *memirq, struct iosys_map *status, struct xe_hw_engine *hwe) { … } static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *status, struct xe_guc *guc) { … } /** * xe_memirq_handler - The `Memory Based Interrupts`_ Handler. * @memirq: the &xe_memirq * * This function reads and dispatches `Memory Based Interrupts`. */ void xe_memirq_handler(struct xe_memirq *memirq) { … }