Merge tag 'drm-xe-next-2025-07-10' of https://gitlab.freedesktop.org/drm/xe/kernel into drm-next
UAPI Changes: - Documentation fixes (Shuicheng) Cross-subsystem Changes: - MTD intel-dg driver for dgfx non-volatile memory device (Sasha) - i2c: designware changes to allow i2c integration with BMG (Heikki) Core Changes: - Restructure migration in preparation for multi-device (Brost, Thomas) - Expose fan control and voltage regulator version on sysfs (Raag) Driver Changes: - Add WildCat Lake support (Roper) - Add aux bus child device driver for NVM on DGFX (Sasha) - Some refactor and fixes to allow cleaner BMG w/a (Lucas, Maarten, Auld) - BMG w/a (Vinay) - Improve handling of aborted probe (Michal) - Do not wedge device on killed exec queues (Brost) - Init changes for flicker-free boot (Maarten) - Fix out-of-bounds field write in MI_STORE_DATA_IMM (Jia) - Enable the GuC Dynamic Inhibit Context Switch optimization (Daniele) - Drop bo->size (Brost) - Builds and KConfig fixes (Harry, Maarten) - Consolidate LRC offset calculations (Tvrtko) - Fix potential leak in hw_engine_group (Michal) - Future-proof for multi-tile + multi-GT cases (Roper) - Validate gt in pmu event (Riana) - SRIOV PF: Clear all LMTT pages on alloc (Michal) - Allocate PF queue size on pow2 boundary (Brost) - SRIOV VF: Make multi-GT migration less error prone (Tomasz) - Revert indirect ring state patch to fix random LRC context switches failures (Brost) - Fix compressed VRAM handling (Auld) - Add one additional BMG PCI ID (Ravi) - Recommend GuC v70.46.2 for BMG, LNL, DG2 (Julia) - Add GuC and HuC to PTL (Daniele) - Drop PTL force_probe requirement (Atwood) - Fix error flow in display suspend (Shuicheng) - Disable GuC communication on hardware initialization error (Zhanjun) - Devcoredump fixes and clean up (Shuicheng) - SRIOV PF: Downgrade some info to debug (Michal) - Don't allocate temporary GuC policies object (Michal) - Support for I2C attached MCUs (Heikki, Raag, Riana) - Add GPU memory bo trace points (Juston) - SRIOV VF: Skip some W/a (Michal) - Correct comment of xe_pm_set_vram_threshold (Shuicheng) - Cancel ongoing H2G requests when stopping CT (Michal) Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch> From: Rodrigo Vivi <rodrigo.vivi@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/aHA7184UnWlONORU@intel.com
This commit is contained in:
@@ -73,15 +73,21 @@ Overview of baseline design
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
|
||||
:doc: Locking
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
|
||||
:doc: Migration
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
|
||||
:doc: Partial Unmapping of Ranges
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
|
||||
:doc: Examples
|
||||
|
||||
Overview of drm_pagemap design
|
||||
==============================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_pagemap.c
|
||||
:doc: Overview
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_pagemap.c
|
||||
:doc: Migration
|
||||
|
||||
Possible future design features
|
||||
===============================
|
||||
|
||||
|
||||
@@ -12089,6 +12089,13 @@ L: linux-kernel@vger.kernel.org
|
||||
S: Supported
|
||||
F: arch/x86/include/asm/intel-family.h
|
||||
|
||||
INTEL DISCRETE GRAPHICS NVM MTD DRIVER
|
||||
M: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
S: Supported
|
||||
F: drivers/mtd/devices/mtd_intel_dg.c
|
||||
F: include/linux/intel_dg_nvm_aux.h
|
||||
|
||||
INTEL DRM DISPLAY FOR XE AND I915 DRIVERS
|
||||
M: Jani Nikula <jani.nikula@linux.intel.com>
|
||||
M: Rodrigo Vivi <rodrigo.vivi@intel.com>
|
||||
|
||||
@@ -104,7 +104,11 @@ obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
|
||||
#
|
||||
obj-$(CONFIG_DRM_EXEC) += drm_exec.o
|
||||
obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o
|
||||
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm.o
|
||||
|
||||
drm_gpusvm_helper-y := \
|
||||
drm_gpusvm.o\
|
||||
drm_pagemap.o
|
||||
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm_helper.o
|
||||
|
||||
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
|
||||
|
||||
|
||||
+13
-749
@@ -9,10 +9,9 @@
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/hmm.h>
|
||||
#include <linux/hugetlb_inline.h>
|
||||
#include <linux/memremap.h>
|
||||
#include <linux/migrate.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
@@ -108,21 +107,6 @@
|
||||
* to add annotations to GPU SVM.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Migration
|
||||
*
|
||||
* The migration support is quite simple, allowing migration between RAM and
|
||||
* device memory at the range granularity. For example, GPU SVM currently does
|
||||
* not support mixing RAM and device memory pages within a range. This means
|
||||
* that upon GPU fault, the entire range can be migrated to device memory, and
|
||||
* upon CPU fault, the entire range is migrated to RAM. Mixed RAM and device
|
||||
* memory storage within a range could be added in the future if required.
|
||||
*
|
||||
* The reasoning for only supporting range granularity is as follows: it
|
||||
* simplifies the implementation, and range sizes are driver-defined and should
|
||||
* be relatively small.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Partial Unmapping of Ranges
|
||||
*
|
||||
@@ -192,12 +176,9 @@
|
||||
* }
|
||||
*
|
||||
* if (driver_migration_policy(range)) {
|
||||
* mmap_read_lock(mm);
|
||||
* devmem = driver_alloc_devmem();
|
||||
* err = drm_gpusvm_migrate_to_devmem(gpusvm, range,
|
||||
* devmem_allocation,
|
||||
* &ctx);
|
||||
* mmap_read_unlock(mm);
|
||||
* err = drm_pagemap_populate_mm(driver_choose_drm_pagemap(),
|
||||
* gpuva_start, gpuva_end, gpusvm->mm,
|
||||
* ctx->timeslice_ms);
|
||||
* if (err) // CPU mappings may have changed
|
||||
* goto retry;
|
||||
* }
|
||||
@@ -289,97 +270,6 @@ npages_in_range(unsigned long start, unsigned long end)
|
||||
return (end - start) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct drm_gpusvm_zdd - GPU SVM zone device data
|
||||
*
|
||||
* @refcount: Reference count for the zdd
|
||||
* @devmem_allocation: device memory allocation
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
*
|
||||
* This structure serves as a generic wrapper installed in
|
||||
* page->zone_device_data. It provides infrastructure for looking up a device
|
||||
* memory allocation upon CPU page fault and asynchronously releasing device
|
||||
* memory once the CPU has no page references. Asynchronous release is useful
|
||||
* because CPU page references can be dropped in IRQ contexts, while releasing
|
||||
* device memory likely requires sleeping locks.
|
||||
*/
|
||||
struct drm_gpusvm_zdd {
|
||||
struct kref refcount;
|
||||
struct drm_gpusvm_devmem *devmem_allocation;
|
||||
void *device_private_page_owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_gpusvm_zdd_alloc() - Allocate a zdd structure.
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
*
|
||||
* This function allocates and initializes a new zdd structure. It sets up the
|
||||
* reference count and initializes the destroy work.
|
||||
*
|
||||
* Return: Pointer to the allocated zdd on success, ERR_PTR() on failure.
|
||||
*/
|
||||
static struct drm_gpusvm_zdd *
|
||||
drm_gpusvm_zdd_alloc(void *device_private_page_owner)
|
||||
{
|
||||
struct drm_gpusvm_zdd *zdd;
|
||||
|
||||
zdd = kmalloc(sizeof(*zdd), GFP_KERNEL);
|
||||
if (!zdd)
|
||||
return NULL;
|
||||
|
||||
kref_init(&zdd->refcount);
|
||||
zdd->devmem_allocation = NULL;
|
||||
zdd->device_private_page_owner = device_private_page_owner;
|
||||
|
||||
return zdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_zdd_get() - Get a reference to a zdd structure.
|
||||
* @zdd: Pointer to the zdd structure.
|
||||
*
|
||||
* This function increments the reference count of the provided zdd structure.
|
||||
*
|
||||
* Return: Pointer to the zdd structure.
|
||||
*/
|
||||
static struct drm_gpusvm_zdd *drm_gpusvm_zdd_get(struct drm_gpusvm_zdd *zdd)
|
||||
{
|
||||
kref_get(&zdd->refcount);
|
||||
return zdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_zdd_destroy() - Destroy a zdd structure.
|
||||
* @ref: Pointer to the reference count structure.
|
||||
*
|
||||
* This function queues the destroy_work of the zdd for asynchronous destruction.
|
||||
*/
|
||||
static void drm_gpusvm_zdd_destroy(struct kref *ref)
|
||||
{
|
||||
struct drm_gpusvm_zdd *zdd =
|
||||
container_of(ref, struct drm_gpusvm_zdd, refcount);
|
||||
struct drm_gpusvm_devmem *devmem = zdd->devmem_allocation;
|
||||
|
||||
if (devmem) {
|
||||
complete_all(&devmem->detached);
|
||||
if (devmem->ops->devmem_release)
|
||||
devmem->ops->devmem_release(devmem);
|
||||
}
|
||||
kfree(zdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_zdd_put() - Put a zdd reference.
|
||||
* @zdd: Pointer to the zdd structure.
|
||||
*
|
||||
* This function decrements the reference count of the provided zdd structure
|
||||
* and schedules its destruction if the count drops to zero.
|
||||
*/
|
||||
static void drm_gpusvm_zdd_put(struct drm_gpusvm_zdd *zdd)
|
||||
{
|
||||
kref_put(&zdd->refcount, drm_gpusvm_zdd_destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_range_find() - Find GPU SVM range from GPU SVM notifier
|
||||
* @notifier: Pointer to the GPU SVM notifier structure.
|
||||
@@ -946,7 +836,7 @@ retry:
|
||||
* process-many-malloc' fails. In the failure case, each process
|
||||
* mallocs 16k but the CPU VMA is ~128k which results in 64k SVM
|
||||
* ranges. When migrating the SVM ranges, some processes fail in
|
||||
* drm_gpusvm_migrate_to_devmem with 'migrate.cpages != npages'
|
||||
* drm_pagemap_migrate_to_devmem with 'migrate.cpages != npages'
|
||||
* and then upon drm_gpusvm_range_get_pages device pages from
|
||||
* other processes are collected + faulted in which creates all
|
||||
* sorts of problems. Unsure exactly how this happening, also
|
||||
@@ -1364,7 +1254,7 @@ int drm_gpusvm_range_get_pages(struct drm_gpusvm *gpusvm,
|
||||
.dev_private_owner = gpusvm->device_private_page_owner,
|
||||
};
|
||||
struct mm_struct *mm = gpusvm->mm;
|
||||
struct drm_gpusvm_zdd *zdd;
|
||||
void *zdd;
|
||||
unsigned long timeout =
|
||||
jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
|
||||
unsigned long i, j;
|
||||
@@ -1447,6 +1337,7 @@ map_pages:
|
||||
}
|
||||
|
||||
zdd = NULL;
|
||||
pagemap = NULL;
|
||||
num_dma_mapped = 0;
|
||||
for (i = 0, j = 0; i < npages; ++j) {
|
||||
struct page *page = hmm_pfn_to_page(pfns[i]);
|
||||
@@ -1466,7 +1357,7 @@ map_pages:
|
||||
}
|
||||
|
||||
pagemap = page_pgmap(page);
|
||||
dpagemap = zdd->devmem_allocation->dpagemap;
|
||||
dpagemap = drm_pagemap_page_to_dpagemap(page);
|
||||
if (drm_WARN_ON(gpusvm->drm, !dpagemap)) {
|
||||
/*
|
||||
* Raced. This is not supposed to happen
|
||||
@@ -1490,7 +1381,7 @@ map_pages:
|
||||
} else {
|
||||
dma_addr_t addr;
|
||||
|
||||
if (is_zone_device_page(page) || zdd) {
|
||||
if (is_zone_device_page(page) || pagemap) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_unmap;
|
||||
}
|
||||
@@ -1518,7 +1409,7 @@ map_pages:
|
||||
flags.has_dma_mapping = true;
|
||||
}
|
||||
|
||||
if (zdd) {
|
||||
if (pagemap) {
|
||||
flags.has_devmem_pages = true;
|
||||
range->dpagemap = dpagemap;
|
||||
}
|
||||
@@ -1546,6 +1437,7 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_range_get_pages);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_range_unmap_pages() - Unmap pages associated with a GPU SVM range
|
||||
* drm_gpusvm_range_evict() - Evict GPU SVM range
|
||||
* @gpusvm: Pointer to the GPU SVM structure
|
||||
* @range: Pointer to the GPU SVM range structure
|
||||
* @ctx: GPU SVM context
|
||||
@@ -1576,562 +1468,11 @@ void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm,
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_range_unmap_pages);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migration_unlock_put_page() - Put a migration page
|
||||
* @page: Pointer to the page to put
|
||||
*
|
||||
* This function unlocks and puts a page.
|
||||
*/
|
||||
static void drm_gpusvm_migration_unlock_put_page(struct page *page)
|
||||
{
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migration_unlock_put_pages() - Put migration pages
|
||||
* @npages: Number of pages
|
||||
* @migrate_pfn: Array of migrate page frame numbers
|
||||
*
|
||||
* This function unlocks and puts an array of pages.
|
||||
*/
|
||||
static void drm_gpusvm_migration_unlock_put_pages(unsigned long npages,
|
||||
unsigned long *migrate_pfn)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page;
|
||||
|
||||
if (!migrate_pfn[i])
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(migrate_pfn[i]);
|
||||
drm_gpusvm_migration_unlock_put_page(page);
|
||||
migrate_pfn[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_get_devmem_page() - Get a reference to a device memory page
|
||||
* @page: Pointer to the page
|
||||
* @zdd: Pointer to the GPU SVM zone device data
|
||||
*
|
||||
* This function associates the given page with the specified GPU SVM zone
|
||||
* device data and initializes it for zone device usage.
|
||||
*/
|
||||
static void drm_gpusvm_get_devmem_page(struct page *page,
|
||||
struct drm_gpusvm_zdd *zdd)
|
||||
{
|
||||
page->zone_device_data = drm_gpusvm_zdd_get(zdd);
|
||||
zone_device_page_init(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migrate_map_pages() - Map migration pages for GPU SVM migration
|
||||
* @dev: The device for which the pages are being mapped
|
||||
* @dma_addr: Array to store DMA addresses corresponding to mapped pages
|
||||
* @migrate_pfn: Array of migrate page frame numbers to map
|
||||
* @npages: Number of pages to map
|
||||
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
|
||||
*
|
||||
* This function maps pages of memory for migration usage in GPU SVM. It
|
||||
* iterates over each page frame number provided in @migrate_pfn, maps the
|
||||
* corresponding page, and stores the DMA address in the provided @dma_addr
|
||||
* array.
|
||||
*
|
||||
* Return: 0 on success, -EFAULT if an error occurs during mapping.
|
||||
*/
|
||||
static int drm_gpusvm_migrate_map_pages(struct device *dev,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long *migrate_pfn,
|
||||
unsigned long npages,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(is_zone_device_page(page)))
|
||||
return -EFAULT;
|
||||
|
||||
dma_addr[i] = dma_map_page(dev, page, 0, PAGE_SIZE, dir);
|
||||
if (dma_mapping_error(dev, dma_addr[i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
|
||||
* @dev: The device for which the pages were mapped
|
||||
* @dma_addr: Array of DMA addresses corresponding to mapped pages
|
||||
* @npages: Number of pages to unmap
|
||||
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
|
||||
*
|
||||
* This function unmaps previously mapped pages of memory for GPU Shared Virtual
|
||||
* Memory (SVM). It iterates over each DMA address provided in @dma_addr, checks
|
||||
* if it's valid and not already unmapped, and unmaps the corresponding page.
|
||||
*/
|
||||
static void drm_gpusvm_migrate_unmap_pages(struct device *dev,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
if (!dma_addr[i] || dma_mapping_error(dev, dma_addr[i]))
|
||||
continue;
|
||||
|
||||
dma_unmap_page(dev, dma_addr[i], PAGE_SIZE, dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migrate_to_devmem() - Migrate GPU SVM range to device memory
|
||||
* drm_gpusvm_range_evict() - Evict GPU SVM range
|
||||
* @gpusvm: Pointer to the GPU SVM structure
|
||||
* @range: Pointer to the GPU SVM range structure
|
||||
* @devmem_allocation: Pointer to the device memory allocation. The caller
|
||||
* should hold a reference to the device memory allocation,
|
||||
* which should be dropped via ops->devmem_release or upon
|
||||
* the failure of this function.
|
||||
* @ctx: GPU SVM context
|
||||
*
|
||||
* This function migrates the specified GPU SVM range to device memory. It
|
||||
* performs the necessary setup and invokes the driver-specific operations for
|
||||
* migration to device memory. Upon successful return, @devmem_allocation can
|
||||
* safely reference @range until ops->devmem_release is called which only upon
|
||||
* successful return. Expected to be called while holding the mmap lock in read
|
||||
* mode.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_gpusvm_migrate_to_devmem(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range,
|
||||
struct drm_gpusvm_devmem *devmem_allocation,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
{
|
||||
const struct drm_gpusvm_devmem_ops *ops = devmem_allocation->ops;
|
||||
unsigned long start = drm_gpusvm_range_start(range),
|
||||
end = drm_gpusvm_range_end(range);
|
||||
struct migrate_vma migrate = {
|
||||
.start = start,
|
||||
.end = end,
|
||||
.pgmap_owner = gpusvm->device_private_page_owner,
|
||||
.flags = MIGRATE_VMA_SELECT_SYSTEM,
|
||||
};
|
||||
struct mm_struct *mm = gpusvm->mm;
|
||||
unsigned long i, npages = npages_in_range(start, end);
|
||||
struct vm_area_struct *vas;
|
||||
struct drm_gpusvm_zdd *zdd = NULL;
|
||||
struct page **pages;
|
||||
dma_addr_t *dma_addr;
|
||||
void *buf;
|
||||
int err;
|
||||
|
||||
mmap_assert_locked(gpusvm->mm);
|
||||
|
||||
if (!range->flags.migrate_devmem)
|
||||
return -EINVAL;
|
||||
|
||||
if (!ops->populate_devmem_pfn || !ops->copy_to_devmem ||
|
||||
!ops->copy_to_ram)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
vas = vma_lookup(mm, start);
|
||||
if (!vas) {
|
||||
err = -ENOENT;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (end > vas->vm_end || start < vas->vm_start) {
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!vma_is_anonymous(vas)) {
|
||||
err = -EBUSY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
|
||||
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
zdd = drm_gpusvm_zdd_alloc(gpusvm->device_private_page_owner);
|
||||
if (!zdd) {
|
||||
err = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
migrate.vma = vas;
|
||||
migrate.src = buf;
|
||||
migrate.dst = migrate.src + npages;
|
||||
|
||||
err = migrate_vma_setup(&migrate);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
if (!migrate.cpages) {
|
||||
err = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (migrate.cpages != npages) {
|
||||
err = -EBUSY;
|
||||
goto err_finalize;
|
||||
}
|
||||
|
||||
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_gpusvm_migrate_map_pages(devmem_allocation->dev, dma_addr,
|
||||
migrate.src, npages, DMA_TO_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = pfn_to_page(migrate.dst[i]);
|
||||
|
||||
pages[i] = page;
|
||||
migrate.dst[i] = migrate_pfn(migrate.dst[i]);
|
||||
drm_gpusvm_get_devmem_page(page, zdd);
|
||||
}
|
||||
|
||||
err = ops->copy_to_devmem(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
/* Upon success bind devmem allocation to range and zdd */
|
||||
devmem_allocation->timeslice_expiration = get_jiffies_64() +
|
||||
msecs_to_jiffies(ctx->timeslice_ms);
|
||||
zdd->devmem_allocation = devmem_allocation; /* Owns ref */
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_gpusvm_migration_unlock_put_pages(npages, migrate.dst);
|
||||
migrate_vma_pages(&migrate);
|
||||
migrate_vma_finalize(&migrate);
|
||||
drm_gpusvm_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
|
||||
DMA_TO_DEVICE);
|
||||
err_free:
|
||||
if (zdd)
|
||||
drm_gpusvm_zdd_put(zdd);
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_migrate_to_devmem);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migrate_populate_ram_pfn() - Populate RAM PFNs for a VM area
|
||||
* @vas: Pointer to the VM area structure, can be NULL
|
||||
* @fault_page: Fault page
|
||||
* @npages: Number of pages to populate
|
||||
* @mpages: Number of pages to migrate
|
||||
* @src_mpfn: Source array of migrate PFNs
|
||||
* @mpfn: Array of migrate PFNs to populate
|
||||
* @addr: Start address for PFN allocation
|
||||
*
|
||||
* This function populates the RAM migrate page frame numbers (PFNs) for the
|
||||
* specified VM area structure. It allocates and locks pages in the VM area for
|
||||
* RAM usage. If vas is non-NULL use alloc_page_vma for allocation, if NULL use
|
||||
* alloc_page for allocation.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int drm_gpusvm_migrate_populate_ram_pfn(struct vm_area_struct *vas,
|
||||
struct page *fault_page,
|
||||
unsigned long npages,
|
||||
unsigned long *mpages,
|
||||
unsigned long *src_mpfn,
|
||||
unsigned long *mpfn,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
|
||||
struct page *page, *src_page;
|
||||
|
||||
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
src_page = migrate_pfn_to_page(src_mpfn[i]);
|
||||
if (!src_page)
|
||||
continue;
|
||||
|
||||
if (fault_page) {
|
||||
if (src_page->zone_device_data !=
|
||||
fault_page->zone_device_data)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vas)
|
||||
page = alloc_page_vma(GFP_HIGHUSER, vas, addr);
|
||||
else
|
||||
page = alloc_page(GFP_HIGHUSER);
|
||||
|
||||
if (!page)
|
||||
goto free_pages;
|
||||
|
||||
mpfn[i] = migrate_pfn(page_to_pfn(page));
|
||||
}
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(mpfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(!trylock_page(page));
|
||||
++*mpages;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_pages:
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(mpfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
put_page(page);
|
||||
mpfn[i] = 0;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_evict_to_ram() - Evict GPU SVM range to RAM
|
||||
* @devmem_allocation: Pointer to the device memory allocation
|
||||
*
|
||||
* Similar to __drm_gpusvm_migrate_to_ram but does not require mmap lock and
|
||||
* migration done via migrate_device_* functions.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_gpusvm_evict_to_ram(struct drm_gpusvm_devmem *devmem_allocation)
|
||||
{
|
||||
const struct drm_gpusvm_devmem_ops *ops = devmem_allocation->ops;
|
||||
unsigned long npages, mpages = 0;
|
||||
struct page **pages;
|
||||
unsigned long *src, *dst;
|
||||
dma_addr_t *dma_addr;
|
||||
void *buf;
|
||||
int i, err = 0;
|
||||
unsigned int retry_count = 2;
|
||||
|
||||
npages = devmem_allocation->size >> PAGE_SHIFT;
|
||||
|
||||
retry:
|
||||
if (!mmget_not_zero(devmem_allocation->mm))
|
||||
return -EFAULT;
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
src = buf;
|
||||
dst = buf + (sizeof(*src) * npages);
|
||||
dma_addr = buf + (2 * sizeof(*src) * npages);
|
||||
pages = buf + (2 * sizeof(*src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
err = ops->populate_devmem_pfn(devmem_allocation, npages, src);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
err = migrate_device_pfns(src, npages);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
err = drm_gpusvm_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
|
||||
src, dst, 0);
|
||||
if (err || !mpages)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_gpusvm_migrate_map_pages(devmem_allocation->dev, dma_addr,
|
||||
dst, npages, DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i)
|
||||
pages[i] = migrate_pfn_to_page(src[i]);
|
||||
|
||||
err = ops->copy_to_ram(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_gpusvm_migration_unlock_put_pages(npages, dst);
|
||||
migrate_device_pages(src, dst, npages);
|
||||
migrate_device_finalize(src, dst, npages);
|
||||
drm_gpusvm_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
err_free:
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
mmput_async(devmem_allocation->mm);
|
||||
|
||||
if (completion_done(&devmem_allocation->detached))
|
||||
return 0;
|
||||
|
||||
if (retry_count--) {
|
||||
cond_resched();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return err ?: -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_evict_to_ram);
|
||||
|
||||
/**
|
||||
* __drm_gpusvm_migrate_to_ram() - Migrate GPU SVM range to RAM (internal)
|
||||
* @vas: Pointer to the VM area structure
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
* @page: Pointer to the page for fault handling (can be NULL)
|
||||
* @fault_addr: Fault address
|
||||
* @size: Size of migration
|
||||
*
|
||||
* This internal function performs the migration of the specified GPU SVM range
|
||||
* to RAM. It sets up the migration, populates + dma maps RAM PFNs, and
|
||||
* invokes the driver-specific operations for migration to RAM.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int __drm_gpusvm_migrate_to_ram(struct vm_area_struct *vas,
|
||||
void *device_private_page_owner,
|
||||
struct page *page,
|
||||
unsigned long fault_addr,
|
||||
unsigned long size)
|
||||
{
|
||||
struct migrate_vma migrate = {
|
||||
.vma = vas,
|
||||
.pgmap_owner = device_private_page_owner,
|
||||
.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE |
|
||||
MIGRATE_VMA_SELECT_DEVICE_COHERENT,
|
||||
.fault_page = page,
|
||||
};
|
||||
struct drm_gpusvm_zdd *zdd;
|
||||
const struct drm_gpusvm_devmem_ops *ops;
|
||||
struct device *dev = NULL;
|
||||
unsigned long npages, mpages = 0;
|
||||
struct page **pages;
|
||||
dma_addr_t *dma_addr;
|
||||
unsigned long start, end;
|
||||
void *buf;
|
||||
int i, err = 0;
|
||||
|
||||
if (page) {
|
||||
zdd = page->zone_device_data;
|
||||
if (time_before64(get_jiffies_64(),
|
||||
zdd->devmem_allocation->timeslice_expiration))
|
||||
return 0;
|
||||
}
|
||||
|
||||
start = ALIGN_DOWN(fault_addr, size);
|
||||
end = ALIGN(fault_addr + 1, size);
|
||||
|
||||
/* Corner where VMA area struct has been partially unmapped */
|
||||
if (start < vas->vm_start)
|
||||
start = vas->vm_start;
|
||||
if (end > vas->vm_end)
|
||||
end = vas->vm_end;
|
||||
|
||||
migrate.start = start;
|
||||
migrate.end = end;
|
||||
npages = npages_in_range(start, end);
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
|
||||
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
migrate.vma = vas;
|
||||
migrate.src = buf;
|
||||
migrate.dst = migrate.src + npages;
|
||||
|
||||
err = migrate_vma_setup(&migrate);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
/* Raced with another CPU fault, nothing to do */
|
||||
if (!migrate.cpages)
|
||||
goto err_free;
|
||||
|
||||
if (!page) {
|
||||
for (i = 0; i < npages; ++i) {
|
||||
if (!(migrate.src[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(migrate.src[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!page)
|
||||
goto err_finalize;
|
||||
}
|
||||
zdd = page->zone_device_data;
|
||||
ops = zdd->devmem_allocation->ops;
|
||||
dev = zdd->devmem_allocation->dev;
|
||||
|
||||
err = drm_gpusvm_migrate_populate_ram_pfn(vas, page, npages, &mpages,
|
||||
migrate.src, migrate.dst,
|
||||
start);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_gpusvm_migrate_map_pages(dev, dma_addr, migrate.dst, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i)
|
||||
pages[i] = migrate_pfn_to_page(migrate.src[i]);
|
||||
|
||||
err = ops->copy_to_ram(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_gpusvm_migration_unlock_put_pages(npages, migrate.dst);
|
||||
migrate_vma_pages(&migrate);
|
||||
migrate_vma_finalize(&migrate);
|
||||
if (dev)
|
||||
drm_gpusvm_migrate_unmap_pages(dev, dma_addr, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
err_free:
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_range_evict - Evict GPU SVM range
|
||||
* @range: Pointer to the GPU SVM range to be removed
|
||||
*
|
||||
* This function evicts the specified GPU SVM range. This function will not
|
||||
* evict coherent pages.
|
||||
* This function evicts the specified GPU SVM range.
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
@@ -2183,60 +1524,6 @@ int drm_gpusvm_range_evict(struct drm_gpusvm *gpusvm,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_range_evict);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_page_free() - Put GPU SVM zone device data associated with a page
|
||||
* @page: Pointer to the page
|
||||
*
|
||||
* This function is a callback used to put the GPU SVM zone device data
|
||||
* associated with a page when it is being released.
|
||||
*/
|
||||
static void drm_gpusvm_page_free(struct page *page)
|
||||
{
|
||||
drm_gpusvm_zdd_put(page->zone_device_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gpusvm_migrate_to_ram() - Migrate GPU SVM range to RAM (page fault handler)
|
||||
* @vmf: Pointer to the fault information structure
|
||||
*
|
||||
* This function is a page fault handler used to migrate a GPU SVM range to RAM.
|
||||
* It retrieves the GPU SVM range information from the faulting page and invokes
|
||||
* the internal migration function to migrate the range back to RAM.
|
||||
*
|
||||
* Return: VM_FAULT_SIGBUS on failure, 0 on success.
|
||||
*/
|
||||
static vm_fault_t drm_gpusvm_migrate_to_ram(struct vm_fault *vmf)
|
||||
{
|
||||
struct drm_gpusvm_zdd *zdd = vmf->page->zone_device_data;
|
||||
int err;
|
||||
|
||||
err = __drm_gpusvm_migrate_to_ram(vmf->vma,
|
||||
zdd->device_private_page_owner,
|
||||
vmf->page, vmf->address,
|
||||
zdd->devmem_allocation->size);
|
||||
|
||||
return err ? VM_FAULT_SIGBUS : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gpusvm_pagemap_ops - Device page map operations for GPU SVM
|
||||
*/
|
||||
static const struct dev_pagemap_ops drm_gpusvm_pagemap_ops = {
|
||||
.page_free = drm_gpusvm_page_free,
|
||||
.migrate_to_ram = drm_gpusvm_migrate_to_ram,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_gpusvm_pagemap_ops_get() - Retrieve GPU SVM device page map operations
|
||||
*
|
||||
* Return: Pointer to the GPU SVM device page map operations structure.
|
||||
*/
|
||||
const struct dev_pagemap_ops *drm_gpusvm_pagemap_ops_get(void)
|
||||
{
|
||||
return &drm_gpusvm_pagemap_ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_pagemap_ops_get);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_has_mapping() - Check if GPU SVM has mapping for the given address range
|
||||
* @gpusvm: Pointer to the GPU SVM structure.
|
||||
@@ -2281,28 +1568,5 @@ void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_range_set_unmapped);
|
||||
|
||||
/**
|
||||
* drm_gpusvm_devmem_init() - Initialize a GPU SVM device memory allocation
|
||||
*
|
||||
* @dev: Pointer to the device structure which device memory allocation belongs to
|
||||
* @mm: Pointer to the mm_struct for the address space
|
||||
* @ops: Pointer to the operations structure for GPU SVM device memory
|
||||
* @dpagemap: The struct drm_pagemap we're allocating from.
|
||||
* @size: Size of device memory allocation
|
||||
*/
|
||||
void drm_gpusvm_devmem_init(struct drm_gpusvm_devmem *devmem_allocation,
|
||||
struct device *dev, struct mm_struct *mm,
|
||||
const struct drm_gpusvm_devmem_ops *ops,
|
||||
struct drm_pagemap *dpagemap, size_t size)
|
||||
{
|
||||
init_completion(&devmem_allocation->detached);
|
||||
devmem_allocation->dev = dev;
|
||||
devmem_allocation->mm = mm;
|
||||
devmem_allocation->ops = ops;
|
||||
devmem_allocation->dpagemap = dpagemap;
|
||||
devmem_allocation->size = size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gpusvm_devmem_init);
|
||||
|
||||
MODULE_DESCRIPTION("DRM GPUSVM");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* Copyright © 2024-2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/migrate.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_pagemap.h>
|
||||
|
||||
/**
|
||||
* DOC: Overview
|
||||
*
|
||||
* The DRM pagemap layer is intended to augment the dev_pagemap functionality by
|
||||
* providing a way to populate a struct mm_struct virtual range with device
|
||||
* private pages and to provide helpers to abstract device memory allocations,
|
||||
* to migrate memory back and forth between device memory and system RAM and
|
||||
* to handle access (and in the future migration) between devices implementing
|
||||
* a fast interconnect that is not necessarily visible to the rest of the
|
||||
* system.
|
||||
*
|
||||
* Typically the DRM pagemap receives requests from one or more DRM GPU SVM
|
||||
* instances to populate struct mm_struct virtual ranges with memory, and the
|
||||
* migration is best effort only and may thus fail. The implementation should
|
||||
* also handle device unbinding by blocking (return an -ENODEV) error for new
|
||||
* population requests and after that migrate all device pages to system ram.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Migration
|
||||
*
|
||||
* Migration granularity typically follows the GPU SVM range requests, but
|
||||
* if there are clashes, due to races or due to the fact that multiple GPU
|
||||
* SVM instances have different views of the ranges used, and because of that
|
||||
* parts of a requested range is already present in the requested device memory,
|
||||
* the implementation has a variety of options. It can fail and it can choose
|
||||
* to populate only the part of the range that isn't already in device memory,
|
||||
* and it can evict the range to system before trying to migrate. Ideally an
|
||||
* implementation would just try to migrate the missing part of the range and
|
||||
* allocate just enough memory to do so.
|
||||
*
|
||||
* When migrating to system memory as a response to a cpu fault or a device
|
||||
* memory eviction request, currently a full device memory allocation is
|
||||
* migrated back to system. Moving forward this might need improvement for
|
||||
* situations where a single page needs bouncing between system memory and
|
||||
* device memory due to, for example, atomic operations.
|
||||
*
|
||||
* Key DRM pagemap components:
|
||||
*
|
||||
* - Device Memory Allocations:
|
||||
* Embedded structure containing enough information for the drm_pagemap to
|
||||
* migrate to / from device memory.
|
||||
*
|
||||
* - Device Memory Operations:
|
||||
* Define the interface for driver-specific device memory operations
|
||||
* release memory, populate pfns, and copy to / from device memory.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct drm_pagemap_zdd - GPU SVM zone device data
|
||||
*
|
||||
* @refcount: Reference count for the zdd
|
||||
* @devmem_allocation: device memory allocation
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
*
|
||||
* This structure serves as a generic wrapper installed in
|
||||
* page->zone_device_data. It provides infrastructure for looking up a device
|
||||
* memory allocation upon CPU page fault and asynchronously releasing device
|
||||
* memory once the CPU has no page references. Asynchronous release is useful
|
||||
* because CPU page references can be dropped in IRQ contexts, while releasing
|
||||
* device memory likely requires sleeping locks.
|
||||
*/
|
||||
struct drm_pagemap_zdd {
|
||||
struct kref refcount;
|
||||
struct drm_pagemap_devmem *devmem_allocation;
|
||||
void *device_private_page_owner;
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_pagemap_zdd_alloc() - Allocate a zdd structure.
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
*
|
||||
* This function allocates and initializes a new zdd structure. It sets up the
|
||||
* reference count and initializes the destroy work.
|
||||
*
|
||||
* Return: Pointer to the allocated zdd on success, ERR_PTR() on failure.
|
||||
*/
|
||||
static struct drm_pagemap_zdd *
|
||||
drm_pagemap_zdd_alloc(void *device_private_page_owner)
|
||||
{
|
||||
struct drm_pagemap_zdd *zdd;
|
||||
|
||||
zdd = kmalloc(sizeof(*zdd), GFP_KERNEL);
|
||||
if (!zdd)
|
||||
return NULL;
|
||||
|
||||
kref_init(&zdd->refcount);
|
||||
zdd->devmem_allocation = NULL;
|
||||
zdd->device_private_page_owner = device_private_page_owner;
|
||||
|
||||
return zdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_zdd_get() - Get a reference to a zdd structure.
|
||||
* @zdd: Pointer to the zdd structure.
|
||||
*
|
||||
* This function increments the reference count of the provided zdd structure.
|
||||
*
|
||||
* Return: Pointer to the zdd structure.
|
||||
*/
|
||||
static struct drm_pagemap_zdd *drm_pagemap_zdd_get(struct drm_pagemap_zdd *zdd)
|
||||
{
|
||||
kref_get(&zdd->refcount);
|
||||
return zdd;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_zdd_destroy() - Destroy a zdd structure.
|
||||
* @ref: Pointer to the reference count structure.
|
||||
*
|
||||
* This function queues the destroy_work of the zdd for asynchronous destruction.
|
||||
*/
|
||||
static void drm_pagemap_zdd_destroy(struct kref *ref)
|
||||
{
|
||||
struct drm_pagemap_zdd *zdd =
|
||||
container_of(ref, struct drm_pagemap_zdd, refcount);
|
||||
struct drm_pagemap_devmem *devmem = zdd->devmem_allocation;
|
||||
|
||||
if (devmem) {
|
||||
complete_all(&devmem->detached);
|
||||
if (devmem->ops->devmem_release)
|
||||
devmem->ops->devmem_release(devmem);
|
||||
}
|
||||
kfree(zdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_zdd_put() - Put a zdd reference.
|
||||
* @zdd: Pointer to the zdd structure.
|
||||
*
|
||||
* This function decrements the reference count of the provided zdd structure
|
||||
* and schedules its destruction if the count drops to zero.
|
||||
*/
|
||||
static void drm_pagemap_zdd_put(struct drm_pagemap_zdd *zdd)
|
||||
{
|
||||
kref_put(&zdd->refcount, drm_pagemap_zdd_destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migration_unlock_put_page() - Put a migration page
|
||||
* @page: Pointer to the page to put
|
||||
*
|
||||
* This function unlocks and puts a page.
|
||||
*/
|
||||
static void drm_pagemap_migration_unlock_put_page(struct page *page)
|
||||
{
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migration_unlock_put_pages() - Put migration pages
|
||||
* @npages: Number of pages
|
||||
* @migrate_pfn: Array of migrate page frame numbers
|
||||
*
|
||||
* This function unlocks and puts an array of pages.
|
||||
*/
|
||||
static void drm_pagemap_migration_unlock_put_pages(unsigned long npages,
|
||||
unsigned long *migrate_pfn)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page;
|
||||
|
||||
if (!migrate_pfn[i])
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(migrate_pfn[i]);
|
||||
drm_pagemap_migration_unlock_put_page(page);
|
||||
migrate_pfn[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_get_devmem_page() - Get a reference to a device memory page
|
||||
* @page: Pointer to the page
|
||||
* @zdd: Pointer to the GPU SVM zone device data
|
||||
*
|
||||
* This function associates the given page with the specified GPU SVM zone
|
||||
* device data and initializes it for zone device usage.
|
||||
*/
|
||||
static void drm_pagemap_get_devmem_page(struct page *page,
|
||||
struct drm_pagemap_zdd *zdd)
|
||||
{
|
||||
page->zone_device_data = drm_pagemap_zdd_get(zdd);
|
||||
zone_device_page_init(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migrate_map_pages() - Map migration pages for GPU SVM migration
|
||||
* @dev: The device for which the pages are being mapped
|
||||
* @dma_addr: Array to store DMA addresses corresponding to mapped pages
|
||||
* @migrate_pfn: Array of migrate page frame numbers to map
|
||||
* @npages: Number of pages to map
|
||||
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
|
||||
*
|
||||
* This function maps pages of memory for migration usage in GPU SVM. It
|
||||
* iterates over each page frame number provided in @migrate_pfn, maps the
|
||||
* corresponding page, and stores the DMA address in the provided @dma_addr
|
||||
* array.
|
||||
*
|
||||
* Returns: 0 on success, -EFAULT if an error occurs during mapping.
|
||||
*/
|
||||
static int drm_pagemap_migrate_map_pages(struct device *dev,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long *migrate_pfn,
|
||||
unsigned long npages,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(is_zone_device_page(page)))
|
||||
return -EFAULT;
|
||||
|
||||
dma_addr[i] = dma_map_page(dev, page, 0, PAGE_SIZE, dir);
|
||||
if (dma_mapping_error(dev, dma_addr[i]))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
|
||||
* @dev: The device for which the pages were mapped
|
||||
* @dma_addr: Array of DMA addresses corresponding to mapped pages
|
||||
* @npages: Number of pages to unmap
|
||||
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
|
||||
*
|
||||
* This function unmaps previously mapped pages of memory for GPU Shared Virtual
|
||||
* Memory (SVM). It iterates over each DMA address provided in @dma_addr, checks
|
||||
* if it's valid and not already unmapped, and unmaps the corresponding page.
|
||||
*/
|
||||
static void drm_pagemap_migrate_unmap_pages(struct device *dev,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
if (!dma_addr[i] || dma_mapping_error(dev, dma_addr[i]))
|
||||
continue;
|
||||
|
||||
dma_unmap_page(dev, dma_addr[i], PAGE_SIZE, dir);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long
|
||||
npages_in_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
return (end - start) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migrate_to_devmem() - Migrate a struct mm_struct range to device memory
|
||||
* @devmem_allocation: The device memory allocation to migrate to.
|
||||
* The caller should hold a reference to the device memory allocation,
|
||||
* and the reference is consumed by this function unless it returns with
|
||||
* an error.
|
||||
* @mm: Pointer to the struct mm_struct.
|
||||
* @start: Start of the virtual address range to migrate.
|
||||
* @end: End of the virtual address range to migrate.
|
||||
* @timeslice_ms: The time requested for the migrated pagemap pages to
|
||||
* be present in @mm before being allowed to be migrated back.
|
||||
* @pgmap_owner: Not used currently, since only system memory is considered.
|
||||
*
|
||||
* This function migrates the specified virtual address range to device memory.
|
||||
* It performs the necessary setup and invokes the driver-specific operations for
|
||||
* migration to device memory. Expected to be called while holding the mmap lock in
|
||||
* at least read mode.
|
||||
*
|
||||
* Note: The @timeslice_ms parameter can typically be used to force data to
|
||||
* remain in pagemap pages long enough for a GPU to perform a task and to prevent
|
||||
* a migration livelock. One alternative would be for the GPU driver to block
|
||||
* in a mmu_notifier for the specified amount of time, but adding the
|
||||
* functionality to the pagemap is likely nicer to the system as a whole.
|
||||
*
|
||||
* Return: %0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end,
|
||||
unsigned long timeslice_ms,
|
||||
void *pgmap_owner)
|
||||
{
|
||||
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
|
||||
struct migrate_vma migrate = {
|
||||
.start = start,
|
||||
.end = end,
|
||||
.pgmap_owner = pgmap_owner,
|
||||
.flags = MIGRATE_VMA_SELECT_SYSTEM,
|
||||
};
|
||||
unsigned long i, npages = npages_in_range(start, end);
|
||||
struct vm_area_struct *vas;
|
||||
struct drm_pagemap_zdd *zdd = NULL;
|
||||
struct page **pages;
|
||||
dma_addr_t *dma_addr;
|
||||
void *buf;
|
||||
int err;
|
||||
|
||||
mmap_assert_locked(mm);
|
||||
|
||||
if (!ops->populate_devmem_pfn || !ops->copy_to_devmem ||
|
||||
!ops->copy_to_ram)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
vas = vma_lookup(mm, start);
|
||||
if (!vas) {
|
||||
err = -ENOENT;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (end > vas->vm_end || start < vas->vm_start) {
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!vma_is_anonymous(vas)) {
|
||||
err = -EBUSY;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
|
||||
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
zdd = drm_pagemap_zdd_alloc(pgmap_owner);
|
||||
if (!zdd) {
|
||||
err = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
migrate.vma = vas;
|
||||
migrate.src = buf;
|
||||
migrate.dst = migrate.src + npages;
|
||||
|
||||
err = migrate_vma_setup(&migrate);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
if (!migrate.cpages) {
|
||||
err = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (migrate.cpages != npages) {
|
||||
err = -EBUSY;
|
||||
goto err_finalize;
|
||||
}
|
||||
|
||||
err = ops->populate_devmem_pfn(devmem_allocation, npages, migrate.dst);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_pagemap_migrate_map_pages(devmem_allocation->dev, dma_addr,
|
||||
migrate.src, npages, DMA_TO_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = pfn_to_page(migrate.dst[i]);
|
||||
|
||||
pages[i] = page;
|
||||
migrate.dst[i] = migrate_pfn(migrate.dst[i]);
|
||||
drm_pagemap_get_devmem_page(page, zdd);
|
||||
}
|
||||
|
||||
err = ops->copy_to_devmem(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
/* Upon success bind devmem allocation to range and zdd */
|
||||
devmem_allocation->timeslice_expiration = get_jiffies_64() +
|
||||
msecs_to_jiffies(timeslice_ms);
|
||||
zdd->devmem_allocation = devmem_allocation; /* Owns ref */
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
|
||||
migrate_vma_pages(&migrate);
|
||||
migrate_vma_finalize(&migrate);
|
||||
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
|
||||
DMA_TO_DEVICE);
|
||||
err_free:
|
||||
if (zdd)
|
||||
drm_pagemap_zdd_put(zdd);
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_pagemap_migrate_to_devmem);
|
||||
|
||||
/**
|
||||
* drm_pagemap_migrate_populate_ram_pfn() - Populate RAM PFNs for a VM area
|
||||
* @vas: Pointer to the VM area structure, can be NULL
|
||||
* @fault_page: Fault page
|
||||
* @npages: Number of pages to populate
|
||||
* @mpages: Number of pages to migrate
|
||||
* @src_mpfn: Source array of migrate PFNs
|
||||
* @mpfn: Array of migrate PFNs to populate
|
||||
* @addr: Start address for PFN allocation
|
||||
*
|
||||
* This function populates the RAM migrate page frame numbers (PFNs) for the
|
||||
* specified VM area structure. It allocates and locks pages in the VM area for
|
||||
* RAM usage. If vas is non-NULL use alloc_page_vma for allocation, if NULL use
|
||||
* alloc_page for allocation.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
|
||||
struct page *fault_page,
|
||||
unsigned long npages,
|
||||
unsigned long *mpages,
|
||||
unsigned long *src_mpfn,
|
||||
unsigned long *mpfn,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned long i;
|
||||
|
||||
for (i = 0; i < npages; ++i, addr += PAGE_SIZE) {
|
||||
struct page *page, *src_page;
|
||||
|
||||
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
src_page = migrate_pfn_to_page(src_mpfn[i]);
|
||||
if (!src_page)
|
||||
continue;
|
||||
|
||||
if (fault_page) {
|
||||
if (src_page->zone_device_data !=
|
||||
fault_page->zone_device_data)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vas)
|
||||
page = alloc_page_vma(GFP_HIGHUSER, vas, addr);
|
||||
else
|
||||
page = alloc_page(GFP_HIGHUSER);
|
||||
|
||||
if (!page)
|
||||
goto free_pages;
|
||||
|
||||
mpfn[i] = migrate_pfn(page_to_pfn(page));
|
||||
}
|
||||
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(mpfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(!trylock_page(page));
|
||||
++*mpages;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_pages:
|
||||
for (i = 0; i < npages; ++i) {
|
||||
struct page *page = migrate_pfn_to_page(mpfn[i]);
|
||||
|
||||
if (!page)
|
||||
continue;
|
||||
|
||||
put_page(page);
|
||||
mpfn[i] = 0;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM
|
||||
* @devmem_allocation: Pointer to the device memory allocation
|
||||
*
|
||||
* Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and
|
||||
* migration done via migrate_device_* functions.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
|
||||
{
|
||||
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
|
||||
unsigned long npages, mpages = 0;
|
||||
struct page **pages;
|
||||
unsigned long *src, *dst;
|
||||
dma_addr_t *dma_addr;
|
||||
void *buf;
|
||||
int i, err = 0;
|
||||
unsigned int retry_count = 2;
|
||||
|
||||
npages = devmem_allocation->size >> PAGE_SHIFT;
|
||||
|
||||
retry:
|
||||
if (!mmget_not_zero(devmem_allocation->mm))
|
||||
return -EFAULT;
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
src = buf;
|
||||
dst = buf + (sizeof(*src) * npages);
|
||||
dma_addr = buf + (2 * sizeof(*src) * npages);
|
||||
pages = buf + (2 * sizeof(*src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
err = ops->populate_devmem_pfn(devmem_allocation, npages, src);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
err = migrate_device_pfns(src, npages);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
err = drm_pagemap_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
|
||||
src, dst, 0);
|
||||
if (err || !mpages)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_pagemap_migrate_map_pages(devmem_allocation->dev, dma_addr,
|
||||
dst, npages, DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i)
|
||||
pages[i] = migrate_pfn_to_page(src[i]);
|
||||
|
||||
err = ops->copy_to_ram(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_pagemap_migration_unlock_put_pages(npages, dst);
|
||||
migrate_device_pages(src, dst, npages);
|
||||
migrate_device_finalize(src, dst, npages);
|
||||
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, dma_addr, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
err_free:
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
mmput_async(devmem_allocation->mm);
|
||||
|
||||
if (completion_done(&devmem_allocation->detached))
|
||||
return 0;
|
||||
|
||||
if (retry_count--) {
|
||||
cond_resched();
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return err ?: -EBUSY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_pagemap_evict_to_ram);
|
||||
|
||||
/**
|
||||
* __drm_pagemap_migrate_to_ram() - Migrate GPU SVM range to RAM (internal)
|
||||
* @vas: Pointer to the VM area structure
|
||||
* @device_private_page_owner: Device private pages owner
|
||||
* @page: Pointer to the page for fault handling (can be NULL)
|
||||
* @fault_addr: Fault address
|
||||
* @size: Size of migration
|
||||
*
|
||||
* This internal function performs the migration of the specified GPU SVM range
|
||||
* to RAM. It sets up the migration, populates + dma maps RAM PFNs, and
|
||||
* invokes the driver-specific operations for migration to RAM.
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure.
|
||||
*/
|
||||
static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
|
||||
void *device_private_page_owner,
|
||||
struct page *page,
|
||||
unsigned long fault_addr,
|
||||
unsigned long size)
|
||||
{
|
||||
struct migrate_vma migrate = {
|
||||
.vma = vas,
|
||||
.pgmap_owner = device_private_page_owner,
|
||||
.flags = MIGRATE_VMA_SELECT_DEVICE_PRIVATE |
|
||||
MIGRATE_VMA_SELECT_DEVICE_COHERENT,
|
||||
.fault_page = page,
|
||||
};
|
||||
struct drm_pagemap_zdd *zdd;
|
||||
const struct drm_pagemap_devmem_ops *ops;
|
||||
struct device *dev = NULL;
|
||||
unsigned long npages, mpages = 0;
|
||||
struct page **pages;
|
||||
dma_addr_t *dma_addr;
|
||||
unsigned long start, end;
|
||||
void *buf;
|
||||
int i, err = 0;
|
||||
|
||||
if (page) {
|
||||
zdd = page->zone_device_data;
|
||||
if (time_before64(get_jiffies_64(),
|
||||
zdd->devmem_allocation->timeslice_expiration))
|
||||
return 0;
|
||||
}
|
||||
|
||||
start = ALIGN_DOWN(fault_addr, size);
|
||||
end = ALIGN(fault_addr + 1, size);
|
||||
|
||||
/* Corner where VMA area struct has been partially unmapped */
|
||||
if (start < vas->vm_start)
|
||||
start = vas->vm_start;
|
||||
if (end > vas->vm_end)
|
||||
end = vas->vm_end;
|
||||
|
||||
migrate.start = start;
|
||||
migrate.end = end;
|
||||
npages = npages_in_range(start, end);
|
||||
|
||||
buf = kvcalloc(npages, 2 * sizeof(*migrate.src) + sizeof(*dma_addr) +
|
||||
sizeof(*pages), GFP_KERNEL);
|
||||
if (!buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dma_addr = buf + (2 * sizeof(*migrate.src) * npages);
|
||||
pages = buf + (2 * sizeof(*migrate.src) + sizeof(*dma_addr)) * npages;
|
||||
|
||||
migrate.vma = vas;
|
||||
migrate.src = buf;
|
||||
migrate.dst = migrate.src + npages;
|
||||
|
||||
err = migrate_vma_setup(&migrate);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
/* Raced with another CPU fault, nothing to do */
|
||||
if (!migrate.cpages)
|
||||
goto err_free;
|
||||
|
||||
if (!page) {
|
||||
for (i = 0; i < npages; ++i) {
|
||||
if (!(migrate.src[i] & MIGRATE_PFN_MIGRATE))
|
||||
continue;
|
||||
|
||||
page = migrate_pfn_to_page(migrate.src[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!page)
|
||||
goto err_finalize;
|
||||
}
|
||||
zdd = page->zone_device_data;
|
||||
ops = zdd->devmem_allocation->ops;
|
||||
dev = zdd->devmem_allocation->dev;
|
||||
|
||||
err = drm_pagemap_migrate_populate_ram_pfn(vas, page, npages, &mpages,
|
||||
migrate.src, migrate.dst,
|
||||
start);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err = drm_pagemap_migrate_map_pages(dev, dma_addr, migrate.dst, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
for (i = 0; i < npages; ++i)
|
||||
pages[i] = migrate_pfn_to_page(migrate.src[i]);
|
||||
|
||||
err = ops->copy_to_ram(pages, dma_addr, npages);
|
||||
if (err)
|
||||
goto err_finalize;
|
||||
|
||||
err_finalize:
|
||||
if (err)
|
||||
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
|
||||
migrate_vma_pages(&migrate);
|
||||
migrate_vma_finalize(&migrate);
|
||||
if (dev)
|
||||
drm_pagemap_migrate_unmap_pages(dev, dma_addr, npages,
|
||||
DMA_FROM_DEVICE);
|
||||
err_free:
|
||||
kvfree(buf);
|
||||
err_out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_page_free() - Put GPU SVM zone device data associated with a page
|
||||
* @page: Pointer to the page
|
||||
*
|
||||
* This function is a callback used to put the GPU SVM zone device data
|
||||
* associated with a page when it is being released.
|
||||
*/
|
||||
static void drm_pagemap_page_free(struct page *page)
|
||||
{
|
||||
drm_pagemap_zdd_put(page->zone_device_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_pagemap_migrate_to_ram() - Migrate a virtual range to RAM (page fault handler)
|
||||
* @vmf: Pointer to the fault information structure
|
||||
*
|
||||
* This function is a page fault handler used to migrate a virtual range
|
||||
* to ram. The device memory allocation in which the device page is found is
|
||||
* migrated in its entirety.
|
||||
*
|
||||
* Returns:
|
||||
* VM_FAULT_SIGBUS on failure, 0 on success.
|
||||
*/
|
||||
static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf)
|
||||
{
|
||||
struct drm_pagemap_zdd *zdd = vmf->page->zone_device_data;
|
||||
int err;
|
||||
|
||||
err = __drm_pagemap_migrate_to_ram(vmf->vma,
|
||||
zdd->device_private_page_owner,
|
||||
vmf->page, vmf->address,
|
||||
zdd->devmem_allocation->size);
|
||||
|
||||
return err ? VM_FAULT_SIGBUS : 0;
|
||||
}
|
||||
|
||||
static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = {
|
||||
.page_free = drm_pagemap_page_free,
|
||||
.migrate_to_ram = drm_pagemap_migrate_to_ram,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_pagemap_pagemap_ops_get() - Retrieve GPU SVM device page map operations
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to the GPU SVM device page map operations structure.
|
||||
*/
|
||||
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void)
|
||||
{
|
||||
return &drm_pagemap_pagemap_ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_pagemap_pagemap_ops_get);
|
||||
|
||||
/**
|
||||
* drm_pagemap_devmem_init() - Initialize a drm_pagemap device memory allocation
|
||||
*
|
||||
* @devmem_allocation: The struct drm_pagemap_devmem to initialize.
|
||||
* @dev: Pointer to the device structure which device memory allocation belongs to
|
||||
* @mm: Pointer to the mm_struct for the address space
|
||||
* @ops: Pointer to the operations structure for GPU SVM device memory
|
||||
* @dpagemap: The struct drm_pagemap we're allocating from.
|
||||
* @size: Size of device memory allocation
|
||||
*/
|
||||
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
|
||||
struct device *dev, struct mm_struct *mm,
|
||||
const struct drm_pagemap_devmem_ops *ops,
|
||||
struct drm_pagemap *dpagemap, size_t size)
|
||||
{
|
||||
init_completion(&devmem_allocation->detached);
|
||||
devmem_allocation->dev = dev;
|
||||
devmem_allocation->mm = mm;
|
||||
devmem_allocation->ops = ops;
|
||||
devmem_allocation->dpagemap = dpagemap;
|
||||
devmem_allocation->size = size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_pagemap_devmem_init);
|
||||
|
||||
/**
|
||||
* drm_pagemap_page_to_dpagemap() - Return a pointer the drm_pagemap of a page
|
||||
* @page: The struct page.
|
||||
*
|
||||
* Return: A pointer to the struct drm_pagemap of a device private page that
|
||||
* was populated from the struct drm_pagemap. If the page was *not* populated
|
||||
* from a struct drm_pagemap, the result is undefined and the function call
|
||||
* may result in dereferencing and invalid address.
|
||||
*/
|
||||
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
|
||||
{
|
||||
struct drm_pagemap_zdd *zdd = page->zone_device_data;
|
||||
|
||||
return zdd->devmem_allocation->dpagemap;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_pagemap_page_to_dpagemap);
|
||||
|
||||
/**
|
||||
* drm_pagemap_populate_mm() - Populate a virtual range with device memory pages
|
||||
* @dpagemap: Pointer to the drm_pagemap managing the device memory
|
||||
* @start: Start of the virtual range to populate.
|
||||
* @end: End of the virtual range to populate.
|
||||
* @mm: Pointer to the virtual address space.
|
||||
* @timeslice_ms: The time requested for the migrated pagemap pages to
|
||||
* be present in @mm before being allowed to be migrated back.
|
||||
*
|
||||
* Attempt to populate a virtual range with device memory pages,
|
||||
* clearing them or migrating data from the existing pages if necessary.
|
||||
* The function is best effort only, and implementations may vary
|
||||
* in how hard they try to satisfy the request.
|
||||
*
|
||||
* Return: %0 on success, negative error code on error. If the hardware
|
||||
* device was removed / unbound the function will return %-ENODEV.
|
||||
*/
|
||||
int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
|
||||
unsigned long start, unsigned long end,
|
||||
struct mm_struct *mm,
|
||||
unsigned long timeslice_ms)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!mmget_not_zero(mm))
|
||||
return -EFAULT;
|
||||
mmap_read_lock(mm);
|
||||
err = dpagemap->ops->populate_mm(dpagemap, start, end, mm,
|
||||
timeslice_ms);
|
||||
mmap_read_unlock(mm);
|
||||
mmput(mm);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_pagemap_populate_mm);
|
||||
@@ -1,7 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config DRM_XE
|
||||
tristate "Intel Xe2 Graphics"
|
||||
depends on DRM && PCI && (m || (y && KUNIT=y))
|
||||
depends on DRM && PCI
|
||||
depends on KUNIT || !KUNIT
|
||||
depends on INTEL_VSEC || !INTEL_VSEC
|
||||
depends on X86_PLATFORM_DEVICES || !(X86 && ACPI)
|
||||
select INTERVAL_TREE
|
||||
@@ -44,6 +45,7 @@ config DRM_XE
|
||||
select WANT_DEV_COREDUMP
|
||||
select AUXILIARY_BUS
|
||||
select HMM_MIRROR
|
||||
select REGMAP if I2C
|
||||
help
|
||||
Driver for Intel Xe2 series GPUs and later. Experimental support
|
||||
for Xe series is also available.
|
||||
@@ -85,16 +87,18 @@ config DRM_XE_GPUSVM
|
||||
Enable this option if you want support for CPU to GPU address
|
||||
mirroring.
|
||||
|
||||
If in doubut say "Y".
|
||||
If in doubt say "Y".
|
||||
|
||||
config DRM_XE_DEVMEM_MIRROR
|
||||
bool "Enable device memory mirror"
|
||||
config DRM_XE_PAGEMAP
|
||||
bool "Enable device memory pool for SVM"
|
||||
depends on DRM_XE_GPUSVM
|
||||
select GET_FREE_REGION
|
||||
default y
|
||||
help
|
||||
Disable this option only if you want to compile out without device
|
||||
memory mirror. Will reduce KMD memory footprint when disabled.
|
||||
Disable this option only if you don't want to expose local device
|
||||
memory for SVM. Will reduce KMD memory footprint when disabled.
|
||||
|
||||
If in doubt say "Y".
|
||||
|
||||
config DRM_XE_FORCE_PROBE
|
||||
string "Force probe xe for selected Intel hardware IDs"
|
||||
|
||||
@@ -80,6 +80,7 @@ xe-y += xe_bb.o \
|
||||
xe_mmio.o \
|
||||
xe_mocs.o \
|
||||
xe_module.o \
|
||||
xe_nvm.o \
|
||||
xe_oa.o \
|
||||
xe_observation.o \
|
||||
xe_pat.o \
|
||||
@@ -124,6 +125,7 @@ xe-y += xe_bb.o \
|
||||
xe_wait_user_fence.o \
|
||||
xe_wopcm.o
|
||||
|
||||
xe-$(CONFIG_I2C) += xe_i2c.o
|
||||
xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
|
||||
xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ enum xe_guc_action {
|
||||
XE_GUC_ACTION_SET_ENG_UTIL_BUFF = 0x550A,
|
||||
XE_GUC_ACTION_SET_DEVICE_ENGINE_ACTIVITY_BUFFER = 0x550C,
|
||||
XE_GUC_ACTION_SET_FUNCTION_ENGINE_ACTIVITY_BUFFER = 0x550D,
|
||||
XE_GUC_ACTION_OPT_IN_FEATURE_KLV = 0x550E,
|
||||
XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR = 0x6000,
|
||||
XE_GUC_ACTION_REPORT_PAGE_FAULT_REQ_DESC = 0x6002,
|
||||
XE_GUC_ACTION_PAGE_FAULT_RES_DESC = 0x6003,
|
||||
@@ -271,4 +272,7 @@ enum xe_guc_g2g_type {
|
||||
#define XE_G2G_DEREGISTER_TILE REG_GENMASK(15, 12)
|
||||
#define XE_G2G_DEREGISTER_TYPE REG_GENMASK(11, 8)
|
||||
|
||||
/* invalid type for XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR */
|
||||
#define XE_GUC_CAT_ERR_TYPE_INVALID 0xdeadbeef
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* +===+=======+==============================================================+
|
||||
* | 0 | 31:16 | **KEY** - KLV key identifier |
|
||||
* | | | - `GuC Self Config KLVs`_ |
|
||||
* | | | - `GuC Opt In Feature KLVs`_ |
|
||||
* | | | - `GuC VGT Policy KLVs`_ |
|
||||
* | | | - `GuC VF Configuration KLVs`_ |
|
||||
* | | | |
|
||||
@@ -124,6 +125,33 @@ enum {
|
||||
GUC_CONTEXT_POLICIES_KLV_NUM_IDS = 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* DOC: GuC Opt In Feature KLVs
|
||||
*
|
||||
* `GuC KLV`_ keys available for use with OPT_IN_FEATURE_KLV
|
||||
*
|
||||
* _`GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE` : 0x4001
|
||||
* Adds an extra dword to the XE_GUC_ACTION_NOTIFY_MEMORY_CAT_ERROR G2H
|
||||
* containing the type of the CAT error. On HW that does not support
|
||||
* reporting the CAT error type, the extra dword is set to 0xdeadbeef.
|
||||
*
|
||||
* _`GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH` : 0x4003
|
||||
* This KLV enables the Dynamic Inhibit Context Switch optimization, which
|
||||
* consists in the GuC setting the CTX_CTRL_INHIBIT_SYN_CTX_SWITCH bit to
|
||||
* zero in the CTX_CONTEXT_CONTROL register of LRCs that are submitted
|
||||
* to an oversubscribed engine. This will cause those contexts to be
|
||||
* switched out immediately if they hit an unsatisfied semaphore wait
|
||||
* (instead of waiting the full timeslice duration). The bit is instead set
|
||||
* to one if a single context is queued on the engine, to avoid it being
|
||||
* switched out if there isn't another context that can run in its place.
|
||||
*/
|
||||
|
||||
#define GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE_KEY 0x4001
|
||||
#define GUC_KLV_OPT_IN_FEATURE_EXT_CAT_ERR_TYPE_LEN 0u
|
||||
|
||||
#define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_KEY 0x4003
|
||||
#define GUC_KLV_OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH_LEN 0u
|
||||
|
||||
/**
|
||||
* DOC: GuC VGT Policy KLVs
|
||||
*
|
||||
|
||||
@@ -17,10 +17,7 @@ u32 intel_dsb_buffer_ggtt_offset(struct intel_dsb_buffer *dsb_buf)
|
||||
|
||||
void intel_dsb_buffer_write(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val)
|
||||
{
|
||||
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
|
||||
|
||||
iosys_map_wr(&dsb_buf->vma->bo->vmap, idx * 4, u32, val);
|
||||
xe_device_l2_flush(xe);
|
||||
}
|
||||
|
||||
u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
|
||||
@@ -30,12 +27,9 @@ u32 intel_dsb_buffer_read(struct intel_dsb_buffer *dsb_buf, u32 idx)
|
||||
|
||||
void intel_dsb_buffer_memset(struct intel_dsb_buffer *dsb_buf, u32 idx, u32 val, size_t size)
|
||||
{
|
||||
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
|
||||
|
||||
WARN_ON(idx > (dsb_buf->buf_size - size) / sizeof(*dsb_buf->cmd_buf));
|
||||
|
||||
iosys_map_memset(&dsb_buf->vma->bo->vmap, idx * 4, val, size);
|
||||
xe_device_l2_flush(xe);
|
||||
}
|
||||
|
||||
bool intel_dsb_buffer_create(struct intel_crtc *crtc, struct intel_dsb_buffer *dsb_buf, size_t size)
|
||||
@@ -74,9 +68,12 @@ void intel_dsb_buffer_cleanup(struct intel_dsb_buffer *dsb_buf)
|
||||
|
||||
void intel_dsb_buffer_flush_map(struct intel_dsb_buffer *dsb_buf)
|
||||
{
|
||||
struct xe_device *xe = dsb_buf->vma->bo->tile->xe;
|
||||
|
||||
/*
|
||||
* The memory barrier here is to ensure coherency of DSB vs MMIO,
|
||||
* both for weak ordering archs and discrete cards.
|
||||
*/
|
||||
xe_device_wmb(dsb_buf->vma->bo->tile->xe);
|
||||
xe_device_wmb(xe);
|
||||
xe_device_l2_flush(xe);
|
||||
}
|
||||
|
||||
@@ -163,6 +163,9 @@ static int __xe_pin_fb_vma_dpt(const struct intel_framebuffer *fb,
|
||||
|
||||
vma->dpt = dpt;
|
||||
vma->node = dpt->ggtt_node[tile0->id];
|
||||
|
||||
/* Ensure DPT writes are flushed */
|
||||
xe_device_l2_flush(xe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -224,7 +227,7 @@ static int __xe_pin_fb_vma_ggtt(const struct intel_framebuffer *fb,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = xe_ggtt_node_insert_locked(vma->node, bo->size, align, 0);
|
||||
ret = xe_ggtt_node_insert_locked(vma->node, xe_bo_size(bo), align, 0);
|
||||
if (ret) {
|
||||
xe_ggtt_node_fini(vma->node);
|
||||
goto out_unlock;
|
||||
@@ -326,8 +329,6 @@ static struct i915_vma *__xe_pin_fb_vma(const struct intel_framebuffer *fb,
|
||||
if (ret)
|
||||
goto err_unpin;
|
||||
|
||||
/* Ensure DPT writes are flushed */
|
||||
xe_device_l2_flush(xe);
|
||||
return vma;
|
||||
|
||||
err_unpin:
|
||||
|
||||
@@ -85,7 +85,7 @@ static int intel_hdcp_gsc_initialize_message(struct xe_device *xe,
|
||||
|
||||
cmd_in = xe_bo_ggtt_addr(bo);
|
||||
cmd_out = cmd_in + PAGE_SIZE;
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0, bo->size);
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0, xe_bo_size(bo));
|
||||
|
||||
gsc_context->hdcp_bo = bo;
|
||||
gsc_context->hdcp_cmd_in = cmd_in;
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#define MTL_GSC_HECI1_BASE 0x00116000
|
||||
#define MTL_GSC_HECI2_BASE 0x00117000
|
||||
|
||||
#define DG1_GSC_HECI2_BASE 0x00259000
|
||||
#define PVC_GSC_HECI2_BASE 0x00285000
|
||||
#define DG2_GSC_HECI2_BASE 0x00374000
|
||||
|
||||
#define HECI_H_CSR(base) XE_REG((base) + 0x4)
|
||||
#define HECI_H_CSR_IE REG_BIT(0)
|
||||
#define HECI_H_CSR_IS REG_BIT(1)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
#ifndef _XE_I2C_REGS_H_
|
||||
#define _XE_I2C_REGS_H_
|
||||
|
||||
#include <linux/pci_regs.h>
|
||||
|
||||
#include "xe_reg_defs.h"
|
||||
#include "xe_regs.h"
|
||||
|
||||
#define I2C_BRIDGE_OFFSET (SOC_BASE + 0xd9000)
|
||||
#define I2C_CONFIG_SPACE_OFFSET (SOC_BASE + 0xf6000)
|
||||
#define I2C_MEM_SPACE_OFFSET (SOC_BASE + 0xf7400)
|
||||
|
||||
#define REG_SG_REMAP_ADDR_PREFIX XE_REG(SOC_BASE + 0x0164)
|
||||
#define REG_SG_REMAP_ADDR_POSTFIX XE_REG(SOC_BASE + 0x0168)
|
||||
|
||||
#define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND)
|
||||
#define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84)
|
||||
|
||||
#endif /* _XE_I2C_REGS_H_ */
|
||||
@@ -19,6 +19,7 @@
|
||||
#define MASTER_IRQ REG_BIT(31)
|
||||
#define GU_MISC_IRQ REG_BIT(29)
|
||||
#define DISPLAY_IRQ REG_BIT(16)
|
||||
#define I2C_IRQ REG_BIT(12)
|
||||
#define GT_DW_IRQ(x) REG_BIT(x)
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#ifndef _XE_PMT_H_
|
||||
#define _XE_PMT_H_
|
||||
|
||||
#define SOC_BASE 0x280000
|
||||
#include "xe_regs.h"
|
||||
|
||||
#define BMG_PMT_BASE_OFFSET 0xDB000
|
||||
#define BMG_DISCOVERY_OFFSET (SOC_BASE + BMG_PMT_BASE_OFFSET)
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "regs/xe_reg_defs.h"
|
||||
|
||||
#define SOC_BASE 0x280000
|
||||
|
||||
#define GU_CNTL_PROTECTED XE_REG(0x10100C)
|
||||
#define DRIVERINT_FLR_DIS REG_BIT(31)
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ static int ccs_test_migrate(struct xe_tile *tile, struct xe_bo *bo,
|
||||
}
|
||||
|
||||
/* Check last CCS value, or at least last value in page. */
|
||||
offset = xe_device_ccs_bytes(tile_to_xe(tile), bo->size);
|
||||
offset = xe_device_ccs_bytes(tile_to_xe(tile), xe_bo_size(bo));
|
||||
offset = min_t(u32, offset, PAGE_SIZE) / sizeof(u64) - 1;
|
||||
if (cpu_map[offset] != get_val) {
|
||||
KUNIT_FAIL(test,
|
||||
|
||||
@@ -32,7 +32,7 @@ static struct xe_bo *replacement_xe_managed_bo_create_pin_map(struct xe_device *
|
||||
|
||||
bo->tile = tile;
|
||||
bo->ttm.bdev = &xe->ttm;
|
||||
bo->size = size;
|
||||
bo->ttm.base.size = size;
|
||||
iosys_map_set_vaddr(&bo->vmap, buf);
|
||||
|
||||
if (flags & XE_BO_FLAG_GGTT) {
|
||||
@@ -43,7 +43,7 @@ static struct xe_bo *replacement_xe_managed_bo_create_pin_map(struct xe_device *
|
||||
|
||||
KUNIT_ASSERT_EQ(test, 0,
|
||||
xe_ggtt_node_insert(bo->ggtt_node[tile->id],
|
||||
bo->size, SZ_4K));
|
||||
xe_bo_size(bo), SZ_4K));
|
||||
}
|
||||
|
||||
return bo;
|
||||
|
||||
@@ -74,13 +74,13 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
|
||||
{
|
||||
struct xe_device *xe = tile_to_xe(m->tile);
|
||||
u64 retval, expected = 0;
|
||||
bool big = bo->size >= SZ_2M;
|
||||
bool big = xe_bo_size(bo) >= SZ_2M;
|
||||
struct dma_fence *fence;
|
||||
const char *str = big ? "Copying big bo" : "Copying small bo";
|
||||
int err;
|
||||
|
||||
struct xe_bo *remote = xe_bo_create_locked(xe, m->tile, NULL,
|
||||
bo->size,
|
||||
xe_bo_size(bo),
|
||||
ttm_bo_type_kernel,
|
||||
region |
|
||||
XE_BO_FLAG_NEEDS_CPU_ACCESS |
|
||||
@@ -105,7 +105,7 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xd0, remote->size);
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xd0, xe_bo_size(remote));
|
||||
fence = xe_migrate_clear(m, remote, remote->ttm.resource,
|
||||
XE_MIGRATE_CLEAR_FLAG_FULL);
|
||||
if (!sanity_fence_failed(xe, fence, big ? "Clearing remote big bo" :
|
||||
@@ -113,15 +113,15 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
|
||||
retval = xe_map_rd(xe, &remote->vmap, 0, u64);
|
||||
check(retval, expected, "remote first offset should be cleared",
|
||||
test);
|
||||
retval = xe_map_rd(xe, &remote->vmap, remote->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &remote->vmap, xe_bo_size(remote) - 8, u64);
|
||||
check(retval, expected, "remote last offset should be cleared",
|
||||
test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
|
||||
/* Try to copy 0xc0 from remote to vram with 2MB or 64KiB/4KiB pages */
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xc0, remote->size);
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0xd0, bo->size);
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xc0, xe_bo_size(remote));
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0xd0, xe_bo_size(bo));
|
||||
|
||||
expected = 0xc0c0c0c0c0c0c0c0;
|
||||
fence = xe_migrate_copy(m, remote, bo, remote->ttm.resource,
|
||||
@@ -131,15 +131,15 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
|
||||
retval = xe_map_rd(xe, &bo->vmap, 0, u64);
|
||||
check(retval, expected,
|
||||
"remote -> vram bo first offset should be copied", test);
|
||||
retval = xe_map_rd(xe, &bo->vmap, bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &bo->vmap, xe_bo_size(bo) - 8, u64);
|
||||
check(retval, expected,
|
||||
"remote -> vram bo offset should be copied", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
|
||||
/* And other way around.. slightly hacky.. */
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xd0, remote->size);
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0xc0, bo->size);
|
||||
xe_map_memset(xe, &remote->vmap, 0, 0xd0, xe_bo_size(remote));
|
||||
xe_map_memset(xe, &bo->vmap, 0, 0xc0, xe_bo_size(bo));
|
||||
|
||||
fence = xe_migrate_copy(m, bo, remote, bo->ttm.resource,
|
||||
remote->ttm.resource, false);
|
||||
@@ -148,7 +148,7 @@ static void test_copy(struct xe_migrate *m, struct xe_bo *bo,
|
||||
retval = xe_map_rd(xe, &remote->vmap, 0, u64);
|
||||
check(retval, expected,
|
||||
"vram -> remote bo first offset should be copied", test);
|
||||
retval = xe_map_rd(xe, &remote->vmap, bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &remote->vmap, xe_bo_size(bo) - 8, u64);
|
||||
check(retval, expected,
|
||||
"vram -> remote bo last offset should be copied", test);
|
||||
}
|
||||
@@ -245,9 +245,9 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
|
||||
if (m->q->vm->flags & XE_VM_FLAG_64K)
|
||||
expected |= XE_PTE_PS64;
|
||||
if (xe_bo_is_vram(pt))
|
||||
xe_res_first(pt->ttm.resource, 0, pt->size, &src_it);
|
||||
xe_res_first(pt->ttm.resource, 0, xe_bo_size(pt), &src_it);
|
||||
else
|
||||
xe_res_first_sg(xe_bo_sg(pt), 0, pt->size, &src_it);
|
||||
xe_res_first_sg(xe_bo_sg(pt), 0, xe_bo_size(pt), &src_it);
|
||||
|
||||
emit_pte(m, bb, NUM_KERNEL_PDE - 1, xe_bo_is_vram(pt), false,
|
||||
&src_it, XE_PAGE_SIZE, pt->ttm.resource);
|
||||
@@ -276,7 +276,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
|
||||
|
||||
/* Clear a small bo */
|
||||
kunit_info(test, "Clearing small buffer object\n");
|
||||
xe_map_memset(xe, &tiny->vmap, 0, 0x22, tiny->size);
|
||||
xe_map_memset(xe, &tiny->vmap, 0, 0x22, xe_bo_size(tiny));
|
||||
expected = 0;
|
||||
fence = xe_migrate_clear(m, tiny, tiny->ttm.resource,
|
||||
XE_MIGRATE_CLEAR_FLAG_FULL);
|
||||
@@ -286,7 +286,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
|
||||
dma_fence_put(fence);
|
||||
retval = xe_map_rd(xe, &tiny->vmap, 0, u32);
|
||||
check(retval, expected, "Command clear small first value", test);
|
||||
retval = xe_map_rd(xe, &tiny->vmap, tiny->size - 4, u32);
|
||||
retval = xe_map_rd(xe, &tiny->vmap, xe_bo_size(tiny) - 4, u32);
|
||||
check(retval, expected, "Command clear small last value", test);
|
||||
|
||||
kunit_info(test, "Copying small buffer object to system\n");
|
||||
@@ -298,7 +298,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
|
||||
|
||||
/* Clear a big bo */
|
||||
kunit_info(test, "Clearing big buffer object\n");
|
||||
xe_map_memset(xe, &big->vmap, 0, 0x11, big->size);
|
||||
xe_map_memset(xe, &big->vmap, 0, 0x11, xe_bo_size(big));
|
||||
expected = 0;
|
||||
fence = xe_migrate_clear(m, big, big->ttm.resource,
|
||||
XE_MIGRATE_CLEAR_FLAG_FULL);
|
||||
@@ -308,7 +308,7 @@ static void xe_migrate_sanity_test(struct xe_migrate *m, struct kunit *test)
|
||||
dma_fence_put(fence);
|
||||
retval = xe_map_rd(xe, &big->vmap, 0, u32);
|
||||
check(retval, expected, "Command clear big first value", test);
|
||||
retval = xe_map_rd(xe, &big->vmap, big->size - 4, u32);
|
||||
retval = xe_map_rd(xe, &big->vmap, xe_bo_size(big) - 4, u32);
|
||||
check(retval, expected, "Command clear big last value", test);
|
||||
|
||||
kunit_info(test, "Copying big buffer object to system\n");
|
||||
@@ -370,7 +370,7 @@ static struct dma_fence *blt_copy(struct xe_tile *tile,
|
||||
struct xe_migrate *m = tile->migrate;
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
struct dma_fence *fence = NULL;
|
||||
u64 size = src_bo->size;
|
||||
u64 size = xe_bo_size(src_bo);
|
||||
struct xe_res_cursor src_it, dst_it;
|
||||
struct ttm_resource *src = src_bo->ttm.resource, *dst = dst_bo->ttm.resource;
|
||||
u64 src_L0_ofs, dst_L0_ofs;
|
||||
@@ -498,7 +498,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
|
||||
long ret;
|
||||
|
||||
expected = 0xd0d0d0d0d0d0d0d0;
|
||||
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, sys_bo->size);
|
||||
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, xe_bo_size(sys_bo));
|
||||
|
||||
fence = blt_copy(tile, sys_bo, vram_bo, false, "Blit copy from sysmem to vram", test);
|
||||
if (!sanity_fence_failed(xe, fence, "Blit copy from sysmem to vram", test)) {
|
||||
@@ -523,7 +523,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
|
||||
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, 0, u64);
|
||||
check(retval, expected, "Clear evicted vram data first value", test);
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, vram_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, xe_bo_size(vram_bo) - 8, u64);
|
||||
check(retval, expected, "Clear evicted vram data last value", test);
|
||||
|
||||
fence = blt_copy(tile, vram_bo, ccs_bo,
|
||||
@@ -532,7 +532,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, 0, u64);
|
||||
check(retval, 0, "Clear ccs data first value", test);
|
||||
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, ccs_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, xe_bo_size(ccs_bo) - 8, u64);
|
||||
check(retval, 0, "Clear ccs data last value", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
@@ -562,7 +562,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
|
||||
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, 0, u64);
|
||||
check(retval, expected, "Restored value must be equal to initial value", test);
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, vram_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &vram_bo->vmap, xe_bo_size(vram_bo) - 8, u64);
|
||||
check(retval, expected, "Restored value must be equal to initial value", test);
|
||||
|
||||
fence = blt_copy(tile, vram_bo, ccs_bo,
|
||||
@@ -570,7 +570,7 @@ static void test_migrate(struct xe_device *xe, struct xe_tile *tile,
|
||||
if (!sanity_fence_failed(xe, fence, "Clear ccs buffer data", test)) {
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, 0, u64);
|
||||
check(retval, 0, "Clear ccs data first value", test);
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, ccs_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &ccs_bo->vmap, xe_bo_size(ccs_bo) - 8, u64);
|
||||
check(retval, 0, "Clear ccs data last value", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
@@ -583,7 +583,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
|
||||
u64 expected, retval;
|
||||
|
||||
expected = 0xd0d0d0d0d0d0d0d0;
|
||||
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, sys_bo->size);
|
||||
xe_map_memset(xe, &sys_bo->vmap, 0, 0xd0, xe_bo_size(sys_bo));
|
||||
|
||||
fence = blt_copy(tile, sys_bo, vram_bo, false, "Blit copy from sysmem to vram", test);
|
||||
if (!sanity_fence_failed(xe, fence, "Blit copy from sysmem to vram", test)) {
|
||||
@@ -597,7 +597,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
|
||||
if (!sanity_fence_failed(xe, fence, "Blit copy from vram to sysmem", test)) {
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
|
||||
check(retval, expected, "Decompressed value must be equal to initial value", test);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
|
||||
check(retval, expected, "Decompressed value must be equal to initial value", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
@@ -615,7 +615,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
|
||||
if (!sanity_fence_failed(xe, fence, "Clear main buffer data", test)) {
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
|
||||
check(retval, expected, "Clear main buffer first value", test);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
|
||||
check(retval, expected, "Clear main buffer last value", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
@@ -625,7 +625,7 @@ static void test_clear(struct xe_device *xe, struct xe_tile *tile,
|
||||
if (!sanity_fence_failed(xe, fence, "Clear ccs buffer data", test)) {
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, 0, u64);
|
||||
check(retval, expected, "Clear ccs data first value", test);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, sys_bo->size - 8, u64);
|
||||
retval = xe_map_rd(xe, &sys_bo->vmap, xe_bo_size(sys_bo) - 8, u64);
|
||||
check(retval, expected, "Clear ccs data last value", test);
|
||||
}
|
||||
dma_fence_put(fence);
|
||||
|
||||
@@ -21,6 +21,18 @@ static void xe_ip_kunit_desc(const struct xe_ip *param, char *desc)
|
||||
KUNIT_ARRAY_PARAM(graphics_ip, graphics_ips, xe_ip_kunit_desc);
|
||||
KUNIT_ARRAY_PARAM(media_ip, media_ips, xe_ip_kunit_desc);
|
||||
|
||||
static void xe_pci_id_kunit_desc(const struct pci_device_id *param, char *desc)
|
||||
{
|
||||
const struct xe_device_desc *dev_desc =
|
||||
(const struct xe_device_desc *)param->driver_data;
|
||||
|
||||
if (dev_desc)
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "0x%X (%s)",
|
||||
param->device, dev_desc->platform_name);
|
||||
}
|
||||
|
||||
KUNIT_ARRAY_PARAM(pci_id, pciidlist, xe_pci_id_kunit_desc);
|
||||
|
||||
/**
|
||||
* xe_pci_graphics_ip_gen_param - Generate graphics struct xe_ip parameters
|
||||
* @prev: the pointer to the previous parameter to iterate from or NULL
|
||||
@@ -55,6 +67,25 @@ const void *xe_pci_media_ip_gen_param(const void *prev, char *desc)
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
|
||||
|
||||
/**
|
||||
* xe_pci_id_gen_param - Generate struct pci_device_id parameters
|
||||
* @prev: the pointer to the previous parameter to iterate from or NULL
|
||||
* @desc: output buffer with minimum size of KUNIT_PARAM_DESC_SIZE
|
||||
*
|
||||
* This function prepares struct pci_device_id parameter.
|
||||
*
|
||||
* To be used only as a parameter generator function in &KUNIT_CASE_PARAM.
|
||||
*
|
||||
* Return: pointer to the next parameter or NULL if no more parameters
|
||||
*/
|
||||
const void *xe_pci_id_gen_param(const void *prev, char *desc)
|
||||
{
|
||||
const struct pci_device_id *pci = pci_id_gen_params(prev, desc);
|
||||
|
||||
return pci->driver_data ? pci : NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param);
|
||||
|
||||
static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
|
||||
u32 *ver, u32 *revid)
|
||||
{
|
||||
|
||||
@@ -44,9 +44,21 @@ static void check_media_ip(struct kunit *test)
|
||||
KUNIT_ASSERT_EQ(test, mask, 0);
|
||||
}
|
||||
|
||||
static void check_platform_gt_count(struct kunit *test)
|
||||
{
|
||||
const struct pci_device_id *pci = test->param_value;
|
||||
const struct xe_device_desc *desc =
|
||||
(const struct xe_device_desc *)pci->driver_data;
|
||||
int max_gt = desc->max_gt_per_tile;
|
||||
|
||||
KUNIT_ASSERT_GT(test, max_gt, 0);
|
||||
KUNIT_ASSERT_LE(test, max_gt, XE_MAX_GT_PER_TILE);
|
||||
}
|
||||
|
||||
static struct kunit_case xe_pci_tests[] = {
|
||||
KUNIT_CASE_PARAM(check_graphics_ip, xe_pci_graphics_ip_gen_param),
|
||||
KUNIT_CASE_PARAM(check_media_ip, xe_pci_media_ip_gen_param),
|
||||
KUNIT_CASE_PARAM(check_platform_gt_count, xe_pci_id_gen_param),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ int xe_pci_fake_device_init(struct xe_device *xe);
|
||||
|
||||
const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_media_ip_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_id_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_live_device_gen_param(const void *prev, char *desc);
|
||||
|
||||
#endif
|
||||
|
||||
+27
-10
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <kunit/static_stub.h>
|
||||
|
||||
#include <trace/events/gpu_mem.h>
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_dma_buf.h"
|
||||
#include "xe_drm_client.h"
|
||||
@@ -418,6 +420,19 @@ static void xe_ttm_tt_account_subtract(struct xe_device *xe, struct ttm_tt *tt)
|
||||
xe_shrinker_mod_pages(xe->mem.shrinker, -(long)tt->num_pages, 0);
|
||||
}
|
||||
|
||||
static void update_global_total_pages(struct ttm_device *ttm_dev,
|
||||
long num_pages)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_TRACE_GPU_MEM)
|
||||
struct xe_device *xe = ttm_to_xe_device(ttm_dev);
|
||||
u64 global_total_pages =
|
||||
atomic64_add_return(num_pages, &xe->global_total_pages);
|
||||
|
||||
trace_gpu_mem_total(xe->drm.primary->index, 0,
|
||||
global_total_pages << PAGE_SHIFT);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
|
||||
u32 page_flags)
|
||||
{
|
||||
@@ -437,7 +452,7 @@ static struct ttm_tt *xe_ttm_tt_create(struct ttm_buffer_object *ttm_bo,
|
||||
|
||||
extra_pages = 0;
|
||||
if (xe_bo_needs_ccs_pages(bo))
|
||||
extra_pages = DIV_ROUND_UP(xe_device_ccs_bytes(xe, bo->size),
|
||||
extra_pages = DIV_ROUND_UP(xe_device_ccs_bytes(xe, xe_bo_size(bo)),
|
||||
PAGE_SIZE);
|
||||
|
||||
/*
|
||||
@@ -525,6 +540,7 @@ static int xe_ttm_tt_populate(struct ttm_device *ttm_dev, struct ttm_tt *tt,
|
||||
|
||||
xe_tt->purgeable = false;
|
||||
xe_ttm_tt_account_add(ttm_to_xe_device(ttm_dev), tt);
|
||||
update_global_total_pages(ttm_dev, tt->num_pages);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -541,6 +557,7 @@ static void xe_ttm_tt_unpopulate(struct ttm_device *ttm_dev, struct ttm_tt *tt)
|
||||
|
||||
ttm_pool_free(&ttm_dev->pool, tt);
|
||||
xe_ttm_tt_account_subtract(xe, tt);
|
||||
update_global_total_pages(ttm_dev, -(long)tt->num_pages);
|
||||
}
|
||||
|
||||
static void xe_ttm_tt_destroy(struct ttm_device *ttm_dev, struct ttm_tt *tt)
|
||||
@@ -1122,7 +1139,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)
|
||||
if (bo->flags & XE_BO_FLAG_PINNED_NORESTORE)
|
||||
goto out_unlock_bo;
|
||||
|
||||
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, bo->size,
|
||||
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
|
||||
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
|
||||
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
|
||||
XE_BO_FLAG_PINNED);
|
||||
@@ -1200,7 +1217,8 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
|
||||
goto out_unlock_bo;
|
||||
|
||||
if (!backup) {
|
||||
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, bo->size,
|
||||
backup = ___xe_bo_create_locked(xe, NULL, NULL, bo->ttm.base.resv,
|
||||
NULL, xe_bo_size(bo),
|
||||
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
|
||||
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
|
||||
XE_BO_FLAG_PINNED);
|
||||
@@ -1254,7 +1272,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
|
||||
}
|
||||
|
||||
xe_map_memcpy_from(xe, backup->vmap.vaddr, &bo->vmap, 0,
|
||||
bo->size);
|
||||
xe_bo_size(bo));
|
||||
}
|
||||
|
||||
if (!bo->backup_obj)
|
||||
@@ -1347,7 +1365,7 @@ int xe_bo_restore_pinned(struct xe_bo *bo)
|
||||
}
|
||||
|
||||
xe_map_memcpy_to(xe, &bo->vmap, 0, backup->vmap.vaddr,
|
||||
bo->size);
|
||||
xe_bo_size(bo));
|
||||
}
|
||||
|
||||
bo->backup_obj = NULL;
|
||||
@@ -1558,7 +1576,7 @@ static int xe_ttm_access_memory(struct ttm_buffer_object *ttm_bo,
|
||||
|
||||
vram = res_to_mem_region(ttm_bo->resource);
|
||||
xe_res_first(ttm_bo->resource, offset & PAGE_MASK,
|
||||
bo->size - (offset & PAGE_MASK), &cursor);
|
||||
xe_bo_size(bo) - (offset & PAGE_MASK), &cursor);
|
||||
|
||||
do {
|
||||
unsigned long page_offset = (offset & ~PAGE_MASK);
|
||||
@@ -1858,7 +1876,6 @@ struct xe_bo *___xe_bo_create_locked(struct xe_device *xe, struct xe_bo *bo,
|
||||
|
||||
bo->ccs_cleared = false;
|
||||
bo->tile = tile;
|
||||
bo->size = size;
|
||||
bo->flags = flags;
|
||||
bo->cpu_caching = cpu_caching;
|
||||
bo->ttm.base.funcs = &xe_gem_object_funcs;
|
||||
@@ -2036,7 +2053,7 @@ __xe_bo_create_locked(struct xe_device *xe,
|
||||
|
||||
if (flags & XE_BO_FLAG_FIXED_PLACEMENT) {
|
||||
err = xe_ggtt_insert_bo_at(t->mem.ggtt, bo,
|
||||
start + bo->size, U64_MAX);
|
||||
start + xe_bo_size(bo), U64_MAX);
|
||||
} else {
|
||||
err = xe_ggtt_insert_bo(t->mem.ggtt, bo);
|
||||
}
|
||||
@@ -2234,7 +2251,7 @@ int xe_managed_bo_reinit_in_vram(struct xe_device *xe, struct xe_tile *tile, str
|
||||
xe_assert(xe, !(*src)->vmap.is_iomem);
|
||||
|
||||
bo = xe_managed_bo_create_from_data(xe, tile, (*src)->vmap.vaddr,
|
||||
(*src)->size, dst_flags);
|
||||
xe_bo_size(*src), dst_flags);
|
||||
if (IS_ERR(bo))
|
||||
return PTR_ERR(bo);
|
||||
|
||||
@@ -2524,7 +2541,7 @@ int xe_bo_vmap(struct xe_bo *bo)
|
||||
* TODO: Fix up ttm_bo_vmap to do that, or fix up ttm_bo_kmap
|
||||
* to use struct iosys_map.
|
||||
*/
|
||||
ret = ttm_bo_kmap(&bo->ttm, 0, bo->size >> PAGE_SHIFT, &bo->kmap);
|
||||
ret = ttm_bo_kmap(&bo->ttm, 0, xe_bo_size(bo) >> PAGE_SHIFT, &bo->kmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -238,6 +238,19 @@ xe_bo_main_addr(struct xe_bo *bo, size_t page_size)
|
||||
return xe_bo_addr(bo, 0, page_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_bo_size() - Xe BO size
|
||||
* @bo: The bo object.
|
||||
*
|
||||
* Simple helper to return Xe BO's size.
|
||||
*
|
||||
* Return: Xe BO's size
|
||||
*/
|
||||
static inline size_t xe_bo_size(struct xe_bo *bo)
|
||||
{
|
||||
return bo->ttm.base.size;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
__xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
|
||||
{
|
||||
@@ -246,7 +259,7 @@ __xe_bo_ggtt_addr(struct xe_bo *bo, u8 tile_id)
|
||||
if (XE_WARN_ON(!ggtt_node))
|
||||
return 0;
|
||||
|
||||
XE_WARN_ON(ggtt_node->base.size > bo->size);
|
||||
XE_WARN_ON(ggtt_node->base.size > xe_bo_size(bo));
|
||||
XE_WARN_ON(ggtt_node->base.start + ggtt_node->base.size > (1ull << 32));
|
||||
return ggtt_node->base.start;
|
||||
}
|
||||
@@ -300,7 +313,7 @@ bool xe_bo_needs_ccs_pages(struct xe_bo *bo);
|
||||
|
||||
static inline size_t xe_bo_ccs_pages_start(struct xe_bo *bo)
|
||||
{
|
||||
return PAGE_ALIGN(bo->ttm.base.size);
|
||||
return PAGE_ALIGN(xe_bo_size(bo));
|
||||
}
|
||||
|
||||
static inline bool xe_bo_has_pages(struct xe_bo *bo)
|
||||
|
||||
@@ -32,8 +32,6 @@ struct xe_bo {
|
||||
struct xe_bo *backup_obj;
|
||||
/** @parent_obj: Ref to parent bo if this a backup_obj */
|
||||
struct xe_bo *parent_obj;
|
||||
/** @size: Size of this buffer object */
|
||||
size_t size;
|
||||
/** @flags: flags for this buffer object */
|
||||
u32 flags;
|
||||
/** @vm: VM this BO is attached to, for extobj this will be NULL */
|
||||
@@ -86,7 +84,7 @@ struct xe_bo {
|
||||
u16 cpu_caching;
|
||||
|
||||
/** @devmem_allocation: SVM device memory allocation */
|
||||
struct drm_gpusvm_devmem devmem_allocation;
|
||||
struct drm_pagemap_devmem devmem_allocation;
|
||||
|
||||
/** @vram_userfault_link: Link into @mem_access.vram_userfault.list */
|
||||
struct list_head vram_userfault_link;
|
||||
|
||||
@@ -171,14 +171,32 @@ static void xe_devcoredump_snapshot_free(struct xe_devcoredump_snapshot *ss)
|
||||
|
||||
#define XE_DEVCOREDUMP_CHUNK_MAX (SZ_512M + SZ_1G)
|
||||
|
||||
/**
|
||||
* xe_devcoredump_read() - Read data from the Xe device coredump snapshot
|
||||
* @buffer: Destination buffer to copy the coredump data into
|
||||
* @offset: Offset in the coredump data to start reading from
|
||||
* @count: Number of bytes to read
|
||||
* @data: Pointer to the xe_devcoredump structure
|
||||
* @datalen: Length of the data (unused)
|
||||
*
|
||||
* Reads a chunk of the coredump snapshot data into the provided buffer.
|
||||
* If the devcoredump is smaller than 1.5 GB (XE_DEVCOREDUMP_CHUNK_MAX),
|
||||
* it is read directly from a pre-written buffer. For larger devcoredumps,
|
||||
* the pre-written buffer must be periodically repopulated from the snapshot
|
||||
* state due to kmalloc size limitations.
|
||||
*
|
||||
* Return: Number of bytes copied on success, or a negative error code on failure.
|
||||
*/
|
||||
static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
|
||||
size_t count, void *data, size_t datalen)
|
||||
{
|
||||
struct xe_devcoredump *coredump = data;
|
||||
struct xe_devcoredump_snapshot *ss;
|
||||
ssize_t byte_copied;
|
||||
ssize_t byte_copied = 0;
|
||||
u32 chunk_offset;
|
||||
ssize_t new_chunk_position;
|
||||
bool pm_needed = false;
|
||||
int ret = 0;
|
||||
|
||||
if (!coredump)
|
||||
return -ENODEV;
|
||||
@@ -188,20 +206,19 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
|
||||
/* Ensure delayed work is captured before continuing */
|
||||
flush_work(&ss->work);
|
||||
|
||||
if (ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX)
|
||||
pm_needed = ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX;
|
||||
if (pm_needed)
|
||||
xe_pm_runtime_get(gt_to_xe(ss->gt));
|
||||
|
||||
mutex_lock(&coredump->lock);
|
||||
|
||||
if (!ss->read.buffer) {
|
||||
mutex_unlock(&coredump->lock);
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (offset >= ss->read.size) {
|
||||
mutex_unlock(&coredump->lock);
|
||||
return 0;
|
||||
}
|
||||
if (offset >= ss->read.size)
|
||||
goto unlock;
|
||||
|
||||
new_chunk_position = div_u64_rem(offset,
|
||||
XE_DEVCOREDUMP_CHUNK_MAX,
|
||||
@@ -221,12 +238,13 @@ static ssize_t xe_devcoredump_read(char *buffer, loff_t offset,
|
||||
ss->read.size - offset;
|
||||
memcpy(buffer, ss->read.buffer + chunk_offset, byte_copied);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&coredump->lock);
|
||||
|
||||
if (ss->read.size > XE_DEVCOREDUMP_CHUNK_MAX)
|
||||
if (pm_needed)
|
||||
xe_pm_runtime_put(gt_to_xe(ss->gt));
|
||||
|
||||
return byte_copied;
|
||||
return byte_copied ? byte_copied : ret;
|
||||
}
|
||||
|
||||
static void xe_devcoredump_free(void *data)
|
||||
@@ -313,13 +331,9 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
{
|
||||
struct xe_devcoredump_snapshot *ss = &coredump->snapshot;
|
||||
struct xe_guc *guc = exec_queue_to_guc(q);
|
||||
u32 adj_logical_mask = q->logical_mask;
|
||||
u32 width_mask = (0x1 << q->width) - 1;
|
||||
const char *process_name = "no process";
|
||||
|
||||
unsigned int fw_ref;
|
||||
bool cookie;
|
||||
int i;
|
||||
|
||||
ss->snapshot_time = ktime_get_real();
|
||||
ss->boot_time = ktime_get_boottime();
|
||||
@@ -335,14 +349,6 @@ static void devcoredump_snapshot(struct xe_devcoredump *coredump,
|
||||
INIT_WORK(&ss->work, xe_devcoredump_deferred_snap_work);
|
||||
|
||||
cookie = dma_fence_begin_signalling();
|
||||
for (i = 0; q->width > 1 && i < XE_HW_ENGINE_MAX_INSTANCE;) {
|
||||
if (adj_logical_mask & BIT(i)) {
|
||||
adj_logical_mask |= width_mask << i;
|
||||
i += q->width;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
/* keep going if fw fails as we still want to save the memory and SW data */
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
|
||||
|
||||
@@ -40,12 +40,14 @@
|
||||
#include "xe_gt_printk.h"
|
||||
#include "xe_gt_sriov_vf.h"
|
||||
#include "xe_guc.h"
|
||||
#include "xe_guc_pc.h"
|
||||
#include "xe_hw_engine_group.h"
|
||||
#include "xe_hwmon.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_irq.h"
|
||||
#include "xe_memirq.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_module.h"
|
||||
#include "xe_nvm.h"
|
||||
#include "xe_oa.h"
|
||||
#include "xe_observation.h"
|
||||
#include "xe_pat.h"
|
||||
@@ -783,46 +785,16 @@ int xe_device_probe(struct xe_device *xe)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_ttm_sys_mgr_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
err = xe_gt_init_early(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Only after this point can GT-specific MMIO operations
|
||||
* (including things like communication with the GuC)
|
||||
* be performed.
|
||||
*/
|
||||
xe_gt_mmio_init(gt);
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
xe_guc_comm_init_early(>->uc.guc);
|
||||
err = xe_gt_sriov_vf_bootstrap(gt);
|
||||
if (err)
|
||||
return err;
|
||||
err = xe_gt_sriov_vf_query_config(gt);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_tile(tile, xe, id) {
|
||||
err = xe_ggtt_init_early(tile->mem.ggtt);
|
||||
if (err)
|
||||
return err;
|
||||
err = xe_memirq_init(&tile->memirq);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
err = xe_gt_init_hwconfig(gt);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = xe_devcoredump_init(xe);
|
||||
@@ -850,6 +822,14 @@ int xe_device_probe(struct xe_device *xe)
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow allocations only now to ensure xe_display_init_early()
|
||||
* is the first to allocate, always.
|
||||
*/
|
||||
err = xe_ttm_sys_mgr_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Allocate and map stolen after potential VRAM resize */
|
||||
err = xe_ttm_stolen_mgr_init(xe);
|
||||
if (err)
|
||||
@@ -881,6 +861,8 @@ int xe_device_probe(struct xe_device *xe)
|
||||
return err;
|
||||
}
|
||||
|
||||
xe_nvm_init(xe);
|
||||
|
||||
err = xe_heci_gsc_init(xe);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -921,6 +903,10 @@ int xe_device_probe(struct xe_device *xe)
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
err = xe_i2c_probe(xe);
|
||||
if (err)
|
||||
goto err_unregister_display;
|
||||
|
||||
for_each_gt(gt, xe, id)
|
||||
xe_gt_sanitize_freq(gt);
|
||||
|
||||
@@ -938,6 +924,8 @@ void xe_device_remove(struct xe_device *xe)
|
||||
{
|
||||
xe_display_unregister(xe);
|
||||
|
||||
xe_nvm_fini(xe);
|
||||
|
||||
drm_dev_unplug(&xe->drm);
|
||||
|
||||
xe_bo_pci_dev_remove_all(xe);
|
||||
@@ -981,38 +969,15 @@ void xe_device_wmb(struct xe_device *xe)
|
||||
xe_mmio_write32(xe_root_tile_mmio(xe), VF_CAP_REG, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_device_td_flush() - Flush transient L3 cache entries
|
||||
* @xe: The device
|
||||
*
|
||||
* Display engine has direct access to memory and is never coherent with L3/L4
|
||||
* caches (or CPU caches), however KMD is responsible for specifically flushing
|
||||
* transient L3 GPU cache entries prior to the flip sequence to ensure scanout
|
||||
* can happen from such a surface without seeing corruption.
|
||||
*
|
||||
* Display surfaces can be tagged as transient by mapping it using one of the
|
||||
* various L3:XD PAT index modes on Xe2.
|
||||
*
|
||||
* Note: On non-discrete xe2 platforms, like LNL, the entire L3 cache is flushed
|
||||
* at the end of each submission via PIPE_CONTROL for compute/render, since SA
|
||||
* Media is not coherent with L3 and we want to support render-vs-media
|
||||
* usescases. For other engines like copy/blt the HW internally forces uncached
|
||||
* behaviour, hence why we can skip the TDF on such platforms.
|
||||
/*
|
||||
* Issue a TRANSIENT_FLUSH_REQUEST and wait for completion on each gt.
|
||||
*/
|
||||
void xe_device_td_flush(struct xe_device *xe)
|
||||
static void tdf_request_sync(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int fw_ref;
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) < 20)
|
||||
return;
|
||||
|
||||
if (XE_WA(xe_root_mmio_gt(xe), 16023588340)) {
|
||||
xe_device_l2_flush(xe);
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (xe_gt_is_media_type(gt))
|
||||
continue;
|
||||
@@ -1022,6 +987,7 @@ void xe_device_td_flush(struct xe_device *xe)
|
||||
return;
|
||||
|
||||
xe_mmio_write32(>->mmio, XE2_TDF_CTRL, TRANSIENT_FLUSH_REQUEST);
|
||||
|
||||
/*
|
||||
* FIXME: We can likely do better here with our choice of
|
||||
* timeout. Currently we just assume the worst case, i.e. 150us,
|
||||
@@ -1052,15 +1018,52 @@ void xe_device_l2_flush(struct xe_device *xe)
|
||||
return;
|
||||
|
||||
spin_lock(>->global_invl_lock);
|
||||
xe_mmio_write32(>->mmio, XE2_GLOBAL_INVAL, 0x1);
|
||||
|
||||
xe_mmio_write32(>->mmio, XE2_GLOBAL_INVAL, 0x1);
|
||||
if (xe_mmio_wait32(>->mmio, XE2_GLOBAL_INVAL, 0x1, 0x0, 500, NULL, true))
|
||||
xe_gt_err_once(gt, "Global invalidation timeout\n");
|
||||
|
||||
spin_unlock(>->global_invl_lock);
|
||||
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_device_td_flush() - Flush transient L3 cache entries
|
||||
* @xe: The device
|
||||
*
|
||||
* Display engine has direct access to memory and is never coherent with L3/L4
|
||||
* caches (or CPU caches), however KMD is responsible for specifically flushing
|
||||
* transient L3 GPU cache entries prior to the flip sequence to ensure scanout
|
||||
* can happen from such a surface without seeing corruption.
|
||||
*
|
||||
* Display surfaces can be tagged as transient by mapping it using one of the
|
||||
* various L3:XD PAT index modes on Xe2.
|
||||
*
|
||||
* Note: On non-discrete xe2 platforms, like LNL, the entire L3 cache is flushed
|
||||
* at the end of each submission via PIPE_CONTROL for compute/render, since SA
|
||||
* Media is not coherent with L3 and we want to support render-vs-media
|
||||
* usescases. For other engines like copy/blt the HW internally forces uncached
|
||||
* behaviour, hence why we can skip the TDF on such platforms.
|
||||
*/
|
||||
void xe_device_td_flush(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *root_gt;
|
||||
|
||||
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) < 20)
|
||||
return;
|
||||
|
||||
root_gt = xe_root_mmio_gt(xe);
|
||||
if (XE_WA(root_gt, 16023588340)) {
|
||||
/* A transient flush is not sufficient: flush the L2 */
|
||||
xe_device_l2_flush(xe);
|
||||
} else {
|
||||
xe_guc_pc_apply_flush_freq_limit(&root_gt->uc.guc.pc);
|
||||
tdf_request_sync(xe);
|
||||
xe_guc_pc_remove_flush_freq_limit(&root_gt->uc.guc.pc);
|
||||
}
|
||||
}
|
||||
|
||||
u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size)
|
||||
{
|
||||
return xe_device_has_flat_ccs(xe) ?
|
||||
|
||||
@@ -60,35 +60,32 @@ static inline struct xe_tile *xe_device_get_root_tile(struct xe_device *xe)
|
||||
return &xe->tiles[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Highest GT/tile count for any platform. Used only for memory allocation
|
||||
* sizing. Any logic looping over GTs or mapping userspace GT IDs into GT
|
||||
* structures should use the per-platform xe->info.max_gt_per_tile instead.
|
||||
*/
|
||||
#define XE_MAX_GT_PER_TILE 2
|
||||
|
||||
static inline struct xe_gt *xe_tile_get_gt(struct xe_tile *tile, u8 gt_id)
|
||||
{
|
||||
if (drm_WARN_ON(&tile_to_xe(tile)->drm, gt_id >= XE_MAX_GT_PER_TILE))
|
||||
gt_id = 0;
|
||||
|
||||
return gt_id ? tile->media_gt : tile->primary_gt;
|
||||
}
|
||||
|
||||
static inline struct xe_gt *xe_device_get_gt(struct xe_device *xe, u8 gt_id)
|
||||
{
|
||||
struct xe_tile *root_tile = xe_device_get_root_tile(xe);
|
||||
struct xe_tile *tile;
|
||||
struct xe_gt *gt;
|
||||
|
||||
/*
|
||||
* FIXME: This only works for now because multi-tile and standalone
|
||||
* media are mutually exclusive on the platforms we have today.
|
||||
*
|
||||
* id => GT mapping may change once we settle on how we want to handle
|
||||
* our UAPI.
|
||||
*/
|
||||
if (MEDIA_VER(xe) >= 13) {
|
||||
gt = xe_tile_get_gt(root_tile, gt_id);
|
||||
} else {
|
||||
if (drm_WARN_ON(&xe->drm, gt_id >= XE_MAX_TILES_PER_DEVICE))
|
||||
gt_id = 0;
|
||||
if (gt_id >= xe->info.tile_count * xe->info.max_gt_per_tile)
|
||||
return NULL;
|
||||
|
||||
gt = xe->tiles[gt_id].primary_gt;
|
||||
tile = &xe->tiles[gt_id / xe->info.max_gt_per_tile];
|
||||
switch (gt_id % xe->info.max_gt_per_tile) {
|
||||
default:
|
||||
xe_assert(xe, false);
|
||||
fallthrough;
|
||||
case 0:
|
||||
gt = tile->primary_gt;
|
||||
break;
|
||||
case 1:
|
||||
gt = tile->media_gt;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gt)
|
||||
@@ -130,12 +127,8 @@ static inline bool xe_device_uc_enabled(struct xe_device *xe)
|
||||
for ((id__) = 1; (id__) < (xe__)->info.tile_count; (id__)++) \
|
||||
for_each_if((tile__) = &(xe__)->tiles[(id__)])
|
||||
|
||||
/*
|
||||
* FIXME: This only works for now since multi-tile and standalone media
|
||||
* happen to be mutually exclusive. Future platforms may change this...
|
||||
*/
|
||||
#define for_each_gt(gt__, xe__, id__) \
|
||||
for ((id__) = 0; (id__) < (xe__)->info.gt_count; (id__)++) \
|
||||
for ((id__) = 0; (id__) < (xe__)->info.tile_count * (xe__)->info.max_gt_per_tile; (id__)++) \
|
||||
for_each_if((gt__) = xe_device_get_gt((xe__), (id__)))
|
||||
|
||||
static inline struct xe_force_wake *gt_to_fw(struct xe_gt *gt)
|
||||
|
||||
@@ -24,6 +24,12 @@
|
||||
*
|
||||
* vram_d3cold_threshold - Report/change vram used threshold(in MB) below
|
||||
* which vram save/restore is permissible during runtime D3cold entry/exit.
|
||||
*
|
||||
* lb_fan_control_version - Fan control version provisioned by late binding.
|
||||
* Exposed only if supported by the device.
|
||||
*
|
||||
* lb_voltage_regulator_version - Voltage regulator version provisioned by late
|
||||
* binding. Exposed only if supported by the device.
|
||||
*/
|
||||
|
||||
static ssize_t
|
||||
@@ -65,6 +71,135 @@ vram_d3cold_threshold_store(struct device *dev, struct device_attribute *attr,
|
||||
|
||||
static DEVICE_ATTR_RW(vram_d3cold_threshold);
|
||||
|
||||
static ssize_t
|
||||
lb_fan_control_version_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
|
||||
struct xe_tile *root = xe_device_get_root_tile(xe);
|
||||
u32 cap, ver_low = FAN_TABLE, ver_high = FAN_TABLE;
|
||||
u16 major = 0, minor = 0, hotfix = 0, build = 0;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (REG_FIELD_GET(V1_FAN_PROVISIONED, cap)) {
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
|
||||
&ver_low, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
|
||||
&ver_high, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
|
||||
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
|
||||
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
|
||||
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
|
||||
}
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(lb_fan_control_version);
|
||||
|
||||
static ssize_t
|
||||
lb_voltage_regulator_version_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
|
||||
struct xe_tile *root = xe_device_get_root_tile(xe);
|
||||
u32 cap, ver_low = VR_CONFIG, ver_high = VR_CONFIG;
|
||||
u16 major = 0, minor = 0, hotfix = 0, build = 0;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (REG_FIELD_GET(VR_PARAMS_PROVISIONED, cap)) {
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_LOW, 0),
|
||||
&ver_low, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_VERSION_HIGH, 0),
|
||||
&ver_high, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
major = REG_FIELD_GET(MAJOR_VERSION_MASK, ver_low);
|
||||
minor = REG_FIELD_GET(MINOR_VERSION_MASK, ver_low);
|
||||
hotfix = REG_FIELD_GET(HOTFIX_VERSION_MASK, ver_high);
|
||||
build = REG_FIELD_GET(BUILD_VERSION_MASK, ver_high);
|
||||
}
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret ?: sysfs_emit(buf, "%u.%u.%u.%u\n", major, minor, hotfix, build);
|
||||
}
|
||||
static DEVICE_ATTR_ADMIN_RO(lb_voltage_regulator_version);
|
||||
|
||||
static int late_bind_create_files(struct device *dev)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
|
||||
struct xe_tile *root = xe_device_get_root_tile(xe);
|
||||
u32 cap;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (REG_FIELD_GET(V1_FAN_SUPPORTED, cap)) {
|
||||
ret = sysfs_create_file(&dev->kobj, &dev_attr_lb_fan_control_version.attr);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (REG_FIELD_GET(VR_PARAMS_SUPPORTED, cap))
|
||||
ret = sysfs_create_file(&dev->kobj, &dev_attr_lb_voltage_regulator_version.attr);
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void late_bind_remove_files(struct device *dev)
|
||||
{
|
||||
struct xe_device *xe = pdev_to_xe_device(to_pci_dev(dev));
|
||||
struct xe_tile *root = xe_device_get_root_tile(xe);
|
||||
u32 cap;
|
||||
int ret;
|
||||
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
|
||||
&cap, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (REG_FIELD_GET(V1_FAN_SUPPORTED, cap))
|
||||
sysfs_remove_file(&dev->kobj, &dev_attr_lb_fan_control_version.attr);
|
||||
|
||||
if (REG_FIELD_GET(VR_PARAMS_SUPPORTED, cap))
|
||||
sysfs_remove_file(&dev->kobj, &dev_attr_lb_voltage_regulator_version.attr);
|
||||
out:
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOC: PCIe Gen5 Limitations
|
||||
*
|
||||
@@ -151,8 +286,10 @@ static void xe_device_sysfs_fini(void *arg)
|
||||
if (xe->d3cold.capable)
|
||||
sysfs_remove_file(&xe->drm.dev->kobj, &dev_attr_vram_d3cold_threshold.attr);
|
||||
|
||||
if (xe->info.platform == XE_BATTLEMAGE)
|
||||
if (xe->info.platform == XE_BATTLEMAGE) {
|
||||
sysfs_remove_files(&xe->drm.dev->kobj, auto_link_downgrade_attrs);
|
||||
late_bind_remove_files(xe->drm.dev);
|
||||
}
|
||||
}
|
||||
|
||||
int xe_device_sysfs_init(struct xe_device *xe)
|
||||
@@ -170,6 +307,10 @@ int xe_device_sysfs_init(struct xe_device *xe)
|
||||
ret = sysfs_create_files(&dev->kobj, auto_link_downgrade_attrs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = late_bind_create_files(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(dev, xe_device_sysfs_fini, xe);
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
|
||||
struct dram_info;
|
||||
struct intel_display;
|
||||
struct intel_dg_nvm_dev;
|
||||
struct xe_ggtt;
|
||||
struct xe_i2c;
|
||||
struct xe_pat_ops;
|
||||
struct xe_pxp;
|
||||
|
||||
@@ -105,7 +107,7 @@ struct xe_vram_region {
|
||||
void __iomem *mapping;
|
||||
/** @ttm: VRAM TTM manager */
|
||||
struct xe_ttm_vram_mgr ttm;
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
|
||||
/** @pagemap: Used to remap device memory as ZONE_DEVICE */
|
||||
struct dev_pagemap pagemap;
|
||||
/**
|
||||
@@ -293,6 +295,8 @@ struct xe_device {
|
||||
u8 vram_flags;
|
||||
/** @info.tile_count: Number of tiles */
|
||||
u8 tile_count;
|
||||
/** @info.max_gt_per_tile: Number of GT IDs allocated to each tile */
|
||||
u8 max_gt_per_tile;
|
||||
/** @info.gt_count: Total number of GTs for entire device */
|
||||
u8 gt_count;
|
||||
/** @info.vm_max_level: Max VM level */
|
||||
@@ -316,6 +320,8 @@ struct xe_device {
|
||||
u8 has_fan_control:1;
|
||||
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
|
||||
u8 has_flat_ccs:1;
|
||||
/** @info.has_gsc_nvm: Device has gsc non-volatile memory */
|
||||
u8 has_gsc_nvm:1;
|
||||
/** @info.has_heci_cscfi: device has heci cscfi */
|
||||
u8 has_heci_cscfi:1;
|
||||
/** @info.has_heci_gscfi: device has heci gscfi */
|
||||
@@ -549,6 +555,9 @@ struct xe_device {
|
||||
/** @heci_gsc: graphics security controller */
|
||||
struct xe_heci_gsc heci_gsc;
|
||||
|
||||
/** @nvm: discrete graphics non-volatile memory */
|
||||
struct intel_dg_nvm_dev *nvm;
|
||||
|
||||
/** @oa: oa observation subsystem */
|
||||
struct xe_oa oa;
|
||||
|
||||
@@ -577,6 +586,9 @@ struct xe_device {
|
||||
/** @pmu: performance monitoring unit */
|
||||
struct xe_pmu pmu;
|
||||
|
||||
/** @i2c: I2C host controller */
|
||||
struct xe_i2c *i2c;
|
||||
|
||||
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
|
||||
u32 atomic_svm_timeslice_ms;
|
||||
|
||||
@@ -588,6 +600,14 @@ struct xe_device {
|
||||
u8 vm_inject_error_position;
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_TRACE_GPU_MEM)
|
||||
/**
|
||||
* @global_total_pages: global GPU page usage tracked for gpu_mem
|
||||
* tracepoints
|
||||
*/
|
||||
atomic64_t global_total_pages;
|
||||
#endif
|
||||
|
||||
/* private: */
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
|
||||
|
||||
@@ -167,7 +167,7 @@ void xe_drm_client_remove_bo(struct xe_bo *bo)
|
||||
static void bo_meminfo(struct xe_bo *bo,
|
||||
struct drm_memory_stats stats[TTM_NUM_MEM_TYPES])
|
||||
{
|
||||
u64 sz = bo->size;
|
||||
u64 sz = xe_bo_size(bo);
|
||||
u32 mem_type = bo->ttm.resource->mem_type;
|
||||
|
||||
xe_bo_assert_held(bo);
|
||||
|
||||
@@ -258,11 +258,13 @@ static int set_prop_eu_stall_wait_num_reports(struct xe_device *xe, u64 value,
|
||||
static int set_prop_eu_stall_gt_id(struct xe_device *xe, u64 value,
|
||||
struct eu_stall_open_properties *props)
|
||||
{
|
||||
if (value >= xe->info.gt_count) {
|
||||
struct xe_gt *gt = xe_device_get_gt(xe, value);
|
||||
|
||||
if (!gt) {
|
||||
drm_dbg(&xe->drm, "Invalid GT ID %llu for EU stall sampling\n", value);
|
||||
return -EINVAL;
|
||||
}
|
||||
props->gt = xe_device_get_gt(xe, value);
|
||||
props->gt = gt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -610,7 +610,7 @@ int xe_exec_queue_create_ioctl(struct drm_device *dev, void *data,
|
||||
if (XE_IOCTL_DBG(xe, err))
|
||||
return -EFAULT;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, eci[0].gt_id >= xe->info.gt_count))
|
||||
if (XE_IOCTL_DBG(xe, !xe_device_get_gt(xe, eci[0].gt_id)))
|
||||
return -EINVAL;
|
||||
|
||||
if (args->flags & DRM_XE_EXEC_QUEUE_LOW_LATENCY_HINT)
|
||||
|
||||
@@ -238,6 +238,13 @@ int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_ggtt_init_kunit);
|
||||
|
||||
static void dev_fini_ggtt(void *arg)
|
||||
{
|
||||
struct xe_ggtt *ggtt = arg;
|
||||
|
||||
drain_workqueue(ggtt->wq);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_ggtt_init_early - Early GGTT initialization
|
||||
* @ggtt: the &xe_ggtt to be initialized
|
||||
@@ -290,6 +297,10 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_add_action_or_reset(xe->drm.dev, dev_fini_ggtt, ggtt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
err = xe_tile_sriov_vf_prepare_ggtt(ggtt->tile);
|
||||
if (err)
|
||||
@@ -410,7 +421,7 @@ int xe_ggtt_init(struct xe_ggtt *ggtt)
|
||||
goto err;
|
||||
}
|
||||
|
||||
xe_map_memset(xe, &ggtt->scratch->vmap, 0, 0, ggtt->scratch->size);
|
||||
xe_map_memset(xe, &ggtt->scratch->vmap, 0, 0, xe_bo_size(ggtt->scratch));
|
||||
|
||||
xe_ggtt_initial_clear(ggtt);
|
||||
|
||||
@@ -682,13 +693,13 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
|
||||
return;
|
||||
|
||||
start = node->base.start;
|
||||
end = start + bo->size;
|
||||
end = start + xe_bo_size(bo);
|
||||
|
||||
pte = ggtt->pt_ops->pte_encode_flags(bo, pat_index);
|
||||
if (!xe_bo_is_vram(bo) && !xe_bo_is_stolen(bo)) {
|
||||
xe_assert(xe_bo_device(bo), bo->ttm.ttm);
|
||||
|
||||
for (xe_res_first_sg(xe_bo_sg(bo), 0, bo->size, &cur);
|
||||
for (xe_res_first_sg(xe_bo_sg(bo), 0, xe_bo_size(bo), &cur);
|
||||
cur.remaining; xe_res_next(&cur, XE_PAGE_SIZE))
|
||||
ggtt->pt_ops->ggtt_set_pte(ggtt, end - cur.remaining,
|
||||
pte | xe_res_dma(&cur));
|
||||
@@ -696,7 +707,7 @@ void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
|
||||
/* Prepend GPU offset */
|
||||
pte |= vram_region_gpu_offset(bo->ttm.resource);
|
||||
|
||||
for (xe_res_first(bo->ttm.resource, 0, bo->size, &cur);
|
||||
for (xe_res_first(bo->ttm.resource, 0, xe_bo_size(bo), &cur);
|
||||
cur.remaining; xe_res_next(&cur, XE_PAGE_SIZE))
|
||||
ggtt->pt_ops->ggtt_set_pte(ggtt, end - cur.remaining,
|
||||
pte + cur.start);
|
||||
@@ -732,7 +743,7 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
|
||||
|
||||
if (XE_WARN_ON(bo->ggtt_node[tile_id])) {
|
||||
/* Someone's already inserted this BO in the GGTT */
|
||||
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
|
||||
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == xe_bo_size(bo));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -751,7 +762,7 @@ static int __xe_ggtt_insert_bo_at(struct xe_ggtt *ggtt, struct xe_bo *bo,
|
||||
|
||||
mutex_lock(&ggtt->lock);
|
||||
err = drm_mm_insert_node_in_range(&ggtt->mm, &bo->ggtt_node[tile_id]->base,
|
||||
bo->size, alignment, 0, start, end, 0);
|
||||
xe_bo_size(bo), alignment, 0, start, end, 0);
|
||||
if (err) {
|
||||
xe_ggtt_node_fini(bo->ggtt_node[tile_id]);
|
||||
bo->ggtt_node[tile_id] = NULL;
|
||||
@@ -812,7 +823,7 @@ void xe_ggtt_remove_bo(struct xe_ggtt *ggtt, struct xe_bo *bo)
|
||||
return;
|
||||
|
||||
/* This BO is not currently in the GGTT */
|
||||
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == bo->size);
|
||||
xe_tile_assert(ggtt->tile, bo->ggtt_node[tile_id]->base.size == xe_bo_size(bo));
|
||||
|
||||
xe_ggtt_node_remove(bo->ggtt_node[tile_id],
|
||||
bo->flags & XE_BO_FLAG_GGTT_INVALIDATE);
|
||||
|
||||
@@ -59,7 +59,8 @@ static int memcpy_fw(struct xe_gsc *gsc)
|
||||
|
||||
xe_map_memcpy_from(xe, storage, &gsc->fw.bo->vmap, 0, fw_size);
|
||||
xe_map_memcpy_to(xe, &gsc->private->vmap, 0, storage, fw_size);
|
||||
xe_map_memset(xe, &gsc->private->vmap, fw_size, 0, gsc->private->size - fw_size);
|
||||
xe_map_memset(xe, &gsc->private->vmap, fw_size, 0,
|
||||
xe_bo_size(gsc->private) - fw_size);
|
||||
|
||||
kfree(storage);
|
||||
|
||||
@@ -82,7 +83,8 @@ static int emit_gsc_upload(struct xe_gsc *gsc)
|
||||
bb->cs[bb->len++] = GSC_FW_LOAD;
|
||||
bb->cs[bb->len++] = lower_32_bits(offset);
|
||||
bb->cs[bb->len++] = upper_32_bits(offset);
|
||||
bb->cs[bb->len++] = (gsc->private->size / SZ_4K) | GSC_FW_LOAD_LIMIT_VALID;
|
||||
bb->cs[bb->len++] = (xe_bo_size(gsc->private) / SZ_4K) |
|
||||
GSC_FW_LOAD_LIMIT_VALID;
|
||||
|
||||
job = xe_bb_create_job(gsc->q, bb);
|
||||
if (IS_ERR(job)) {
|
||||
|
||||
+38
-42
@@ -390,6 +390,7 @@ put_exec_queue:
|
||||
|
||||
int xe_gt_init_early(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
if (IS_SRIOV_PF(gt_to_xe(gt))) {
|
||||
@@ -419,6 +420,25 @@ int xe_gt_init_early(struct xe_gt *gt)
|
||||
|
||||
xe_mocs_init_early(gt);
|
||||
|
||||
/*
|
||||
* Only after this point can GT-specific MMIO operations
|
||||
* (including things like communication with the GuC)
|
||||
* be performed.
|
||||
*/
|
||||
xe_gt_mmio_init(gt);
|
||||
|
||||
err = xe_uc_init_noalloc(>->uc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
xe_gt_mcr_init_early(gt);
|
||||
xe_pat_init(gt);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -433,7 +453,7 @@ static void dump_pat_on_error(struct xe_gt *gt)
|
||||
xe_pat_dump(gt, &p);
|
||||
}
|
||||
|
||||
static int gt_fw_domain_init(struct xe_gt *gt)
|
||||
static int gt_init_with_gt_forcewake(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
@@ -442,6 +462,14 @@ static int gt_fw_domain_init(struct xe_gt *gt)
|
||||
if (!fw_ref)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
err = xe_uc_init(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
|
||||
xe_gt_topology_init(gt);
|
||||
xe_gt_mcr_init(gt);
|
||||
xe_gt_enable_host_l2_vram(gt);
|
||||
|
||||
if (!xe_gt_is_media_type(gt)) {
|
||||
err = xe_ggtt_init(gt_to_tile(gt)->mem.ggtt);
|
||||
if (err)
|
||||
@@ -457,8 +485,10 @@ static int gt_fw_domain_init(struct xe_gt *gt)
|
||||
xe_gt_mcr_init(gt);
|
||||
|
||||
err = xe_hw_engines_init_early(gt);
|
||||
if (err)
|
||||
if (err) {
|
||||
dump_pat_on_error(gt);
|
||||
goto err_force_wake;
|
||||
}
|
||||
|
||||
err = xe_hw_engine_class_sysfs_init(gt);
|
||||
if (err)
|
||||
@@ -479,13 +509,12 @@ static int gt_fw_domain_init(struct xe_gt *gt)
|
||||
return 0;
|
||||
|
||||
err_force_wake:
|
||||
dump_pat_on_error(gt);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int all_fw_domain_init(struct xe_gt *gt)
|
||||
static int gt_init_with_all_forcewake(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
@@ -544,7 +573,7 @@ static int all_fw_domain_init(struct xe_gt *gt)
|
||||
}
|
||||
}
|
||||
|
||||
err = xe_uc_init_hw(>->uc);
|
||||
err = xe_uc_load_hw(>->uc);
|
||||
if (err)
|
||||
goto err_force_wake;
|
||||
|
||||
@@ -572,39 +601,6 @@ err_force_wake:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize enough GT to be able to load GuC in order to obtain hwconfig and
|
||||
* enable CTB communication.
|
||||
*/
|
||||
int xe_gt_init_hwconfig(struct xe_gt *gt)
|
||||
{
|
||||
unsigned int fw_ref;
|
||||
int err;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
|
||||
if (!fw_ref)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
xe_gt_mcr_init_early(gt);
|
||||
xe_pat_init(gt);
|
||||
|
||||
err = xe_uc_init(>->uc);
|
||||
if (err)
|
||||
goto out_fw;
|
||||
|
||||
err = xe_uc_init_hwconfig(>->uc);
|
||||
if (err)
|
||||
goto out_fw;
|
||||
|
||||
xe_gt_topology_init(gt);
|
||||
xe_gt_mcr_init(gt);
|
||||
xe_gt_enable_host_l2_vram(gt);
|
||||
|
||||
out_fw:
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xe_gt_fini(void *arg)
|
||||
{
|
||||
struct xe_gt *gt = arg;
|
||||
@@ -640,7 +636,7 @@ int xe_gt_init(struct xe_gt *gt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = gt_fw_domain_init(gt);
|
||||
err = gt_init_with_gt_forcewake(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -654,7 +650,7 @@ int xe_gt_init(struct xe_gt *gt)
|
||||
|
||||
xe_force_wake_init_engines(gt, gt_to_fw(gt));
|
||||
|
||||
err = all_fw_domain_init(gt);
|
||||
err = gt_init_with_all_forcewake(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -742,7 +738,7 @@ static int vf_gt_restart(struct xe_gt *gt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_uc_init_hw(>->uc);
|
||||
err = xe_uc_load_hw(>->uc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -780,7 +776,7 @@ static int do_gt_restart(struct xe_gt *gt)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_uc_init_hw(>->uc);
|
||||
err = xe_uc_load_hw(>->uc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ static inline bool xe_fault_inject_gt_reset(void)
|
||||
}
|
||||
|
||||
struct xe_gt *xe_gt_alloc(struct xe_tile *tile);
|
||||
int xe_gt_init_hwconfig(struct xe_gt *gt);
|
||||
int xe_gt_init_early(struct xe_gt *gt);
|
||||
int xe_gt_init(struct xe_gt *gt);
|
||||
void xe_gt_mmio_init(struct xe_gt *gt);
|
||||
|
||||
@@ -420,12 +420,6 @@ static void init_steering_sqidi_psmi(struct xe_gt *gt)
|
||||
gt->steering[SQIDI_PSMI].instance_target = select & 0x1;
|
||||
}
|
||||
|
||||
static void init_steering_inst0(struct xe_gt *gt)
|
||||
{
|
||||
gt->steering[INSTANCE0].group_target = 0; /* unused */
|
||||
gt->steering[INSTANCE0].instance_target = 0; /* unused */
|
||||
}
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
void (*init)(struct xe_gt *gt);
|
||||
@@ -436,7 +430,7 @@ static const struct {
|
||||
[DSS] = { "DSS", init_steering_dss },
|
||||
[OADDRM] = { "OADDRM / GPMXMT", init_steering_oaddrm },
|
||||
[SQIDI_PSMI] = { "SQIDI_PSMI", init_steering_sqidi_psmi },
|
||||
[INSTANCE0] = { "INSTANCE 0", init_steering_inst0 },
|
||||
[INSTANCE0] = { "INSTANCE 0", NULL },
|
||||
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
|
||||
};
|
||||
|
||||
@@ -446,25 +440,17 @@ static const struct {
|
||||
*
|
||||
* Perform early software only initialization of the MCR lock to allow
|
||||
* the synchronization on accessing the STEER_SEMAPHORE register and
|
||||
* use the xe_gt_mcr_multicast_write() function.
|
||||
* use the xe_gt_mcr_multicast_write() function, plus the minimum
|
||||
* safe MCR registers required for VRAM/CCS probing.
|
||||
*/
|
||||
void xe_gt_mcr_init_early(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
|
||||
BUILD_BUG_ON(IMPLICIT_STEERING + 1 != NUM_STEERING_TYPES);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(xe_steering_types) != NUM_STEERING_TYPES);
|
||||
|
||||
spin_lock_init(>->mcr_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_gt_mcr_init - Normal initialization of the MCR support
|
||||
* @gt: GT structure
|
||||
*
|
||||
* Perform normal initialization of the MCR for all usages.
|
||||
*/
|
||||
void xe_gt_mcr_init(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return;
|
||||
@@ -505,10 +491,27 @@ void xe_gt_mcr_init(struct xe_gt *gt)
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark instance 0 as initialized, we need this early for VRAM and CCS probe. */
|
||||
gt->steering[INSTANCE0].initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_gt_mcr_init - Normal initialization of the MCR support
|
||||
* @gt: GT structure
|
||||
*
|
||||
* Perform normal initialization of the MCR for all usages.
|
||||
*/
|
||||
void xe_gt_mcr_init(struct xe_gt *gt)
|
||||
{
|
||||
if (IS_SRIOV_VF(gt_to_xe(gt)))
|
||||
return;
|
||||
|
||||
/* Select non-terminated steering target for each type */
|
||||
for (int i = 0; i < NUM_STEERING_TYPES; i++)
|
||||
for (int i = 0; i < NUM_STEERING_TYPES; i++) {
|
||||
gt->steering[i].initialized = true;
|
||||
if (gt->steering[i].ranges && xe_steering_types[i].init)
|
||||
xe_steering_types[i].init(gt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -570,6 +573,10 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt,
|
||||
|
||||
for (int i = 0; gt->steering[type].ranges[i].end > 0; i++) {
|
||||
if (xe_mmio_in_range(>->mmio, >->steering[type].ranges[i], reg)) {
|
||||
drm_WARN(>_to_xe(gt)->drm, !gt->steering[type].initialized,
|
||||
"Uninitialized usage of MCR register %s/%#x\n",
|
||||
xe_steering_types[type].name, reg.addr);
|
||||
|
||||
*group = gt->steering[type].group_target;
|
||||
*instance = gt->steering[type].instance_target;
|
||||
return true;
|
||||
|
||||
@@ -419,6 +419,7 @@ static int xe_alloc_pf_queue(struct xe_gt *gt, struct pf_queue *pf_queue)
|
||||
#define PF_MULTIPLIER 8
|
||||
pf_queue->num_dw =
|
||||
(num_eus + XE_NUM_HW_ENGINES) * PF_MSG_LEN_DW * PF_MULTIPLIER;
|
||||
pf_queue->num_dw = roundup_pow_of_two(pf_queue->num_dw);
|
||||
#undef PF_MULTIPLIER
|
||||
|
||||
pf_queue->gt = gt;
|
||||
|
||||
@@ -104,13 +104,13 @@ static int pf_push_vf_buf_klvs(struct xe_gt *gt, unsigned int vfid, u32 num_klvs
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
|
||||
struct drm_printer p = xe_gt_info_printer(gt);
|
||||
struct drm_printer p = xe_gt_dbg_printer(gt);
|
||||
void *klvs = xe_guc_buf_cpu_ptr(buf);
|
||||
char name[8];
|
||||
|
||||
xe_gt_sriov_info(gt, "pushed %s config with %u KLV%s:\n",
|
||||
xe_sriov_function_name(vfid, name, sizeof(name)),
|
||||
num_klvs, str_plural(num_klvs));
|
||||
xe_gt_sriov_dbg(gt, "pushed %s config with %u KLV%s:\n",
|
||||
xe_sriov_function_name(vfid, name, sizeof(name)),
|
||||
num_klvs, str_plural(num_klvs));
|
||||
xe_guc_klv_print(klvs, num_dwords, &p);
|
||||
}
|
||||
|
||||
@@ -282,8 +282,8 @@ static u32 encode_config(u32 *cfg, const struct xe_gt_sriov_config *config, bool
|
||||
|
||||
if (config->lmem_obj) {
|
||||
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_LMEM_SIZE);
|
||||
cfg[n++] = lower_32_bits(config->lmem_obj->size);
|
||||
cfg[n++] = upper_32_bits(config->lmem_obj->size);
|
||||
cfg[n++] = lower_32_bits(xe_bo_size(config->lmem_obj));
|
||||
cfg[n++] = upper_32_bits(xe_bo_size(config->lmem_obj));
|
||||
}
|
||||
|
||||
cfg[n++] = PREP_GUC_KLV_TAG(VF_CFG_EXEC_QUANTUM);
|
||||
@@ -1299,7 +1299,7 @@ static u64 pf_get_vf_config_lmem(struct xe_gt *gt, unsigned int vfid)
|
||||
struct xe_bo *bo;
|
||||
|
||||
bo = config->lmem_obj;
|
||||
return bo ? bo->size : 0;
|
||||
return bo ? xe_bo_size(bo) : 0;
|
||||
}
|
||||
|
||||
static int pf_distribute_config_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
|
||||
@@ -1388,7 +1388,7 @@ static int pf_update_vf_lmtt(struct xe_device *xe, unsigned int vfid)
|
||||
err = xe_lmtt_populate_pages(lmtt, vfid, bo, offset);
|
||||
if (err)
|
||||
goto fail;
|
||||
offset += bo->size;
|
||||
offset += xe_bo_size(bo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1469,12 +1469,12 @@ static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
|
||||
goto release;
|
||||
}
|
||||
|
||||
err = pf_push_vf_cfg_lmem(gt, vfid, bo->size);
|
||||
err = pf_push_vf_cfg_lmem(gt, vfid, xe_bo_size(bo));
|
||||
if (unlikely(err))
|
||||
goto reset_lmtt;
|
||||
|
||||
xe_gt_sriov_dbg_verbose(gt, "VF%u LMEM %zu (%zuM)\n",
|
||||
vfid, bo->size, bo->size / SZ_1M);
|
||||
vfid, xe_bo_size(bo), xe_bo_size(bo) / SZ_1M);
|
||||
return 0;
|
||||
|
||||
reset_lmtt:
|
||||
@@ -2349,7 +2349,7 @@ int xe_gt_sriov_pf_config_restore(struct xe_gt *gt, unsigned int vfid,
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
|
||||
struct drm_printer p = xe_gt_info_printer(gt);
|
||||
struct drm_printer p = xe_gt_dbg_printer(gt);
|
||||
|
||||
drm_printf(&p, "restoring VF%u config:\n", vfid);
|
||||
xe_guc_klv_print(buf, size / sizeof(u32), &p);
|
||||
@@ -2552,10 +2552,10 @@ int xe_gt_sriov_pf_config_print_lmem(struct xe_gt *gt, struct drm_printer *p)
|
||||
if (!config->lmem_obj)
|
||||
continue;
|
||||
|
||||
string_get_size(config->lmem_obj->size, 1, STRING_UNITS_2,
|
||||
string_get_size(xe_bo_size(config->lmem_obj), 1, STRING_UNITS_2,
|
||||
buf, sizeof(buf));
|
||||
drm_printf(p, "VF%u:\t%zu\t(%s)\n",
|
||||
n, config->lmem_obj->size, buf);
|
||||
n, xe_bo_size(config->lmem_obj), buf);
|
||||
}
|
||||
|
||||
mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
|
||||
|
||||
@@ -266,7 +266,7 @@ static void pf_prepare_runtime_info(struct xe_gt *gt)
|
||||
read_many(gt, size, regs, values);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
|
||||
struct drm_printer p = xe_gt_info_printer(gt);
|
||||
struct drm_printer p = xe_gt_dbg_printer(gt);
|
||||
|
||||
xe_gt_sriov_pf_service_print_runtime(gt, &p);
|
||||
}
|
||||
|
||||
@@ -966,7 +966,6 @@ u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg)
|
||||
struct vf_runtime_reg *rr;
|
||||
|
||||
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
|
||||
xe_gt_assert(gt, gt->sriov.vf.pf_version.major);
|
||||
xe_gt_assert(gt, !reg.vf);
|
||||
|
||||
if (reg.addr == GMD_ID.addr) {
|
||||
|
||||
@@ -377,6 +377,8 @@ struct xe_gt {
|
||||
u16 group_target;
|
||||
/** @steering.instance_target: instance to steer accesses to */
|
||||
u16 instance_target;
|
||||
/** @steering.initialized: Whether this steering range is initialized */
|
||||
bool initialized;
|
||||
} steering[NUM_STEERING_TYPES];
|
||||
|
||||
/**
|
||||
|
||||
+129
-18
@@ -29,6 +29,7 @@
|
||||
#include "xe_guc_db_mgr.h"
|
||||
#include "xe_guc_engine_activity.h"
|
||||
#include "xe_guc_hwconfig.h"
|
||||
#include "xe_guc_klv_helpers.h"
|
||||
#include "xe_guc_log.h"
|
||||
#include "xe_guc_pc.h"
|
||||
#include "xe_guc_relay.h"
|
||||
@@ -59,7 +60,7 @@ static u32 guc_bo_ggtt_addr(struct xe_guc *guc,
|
||||
/* GuC addresses above GUC_GGTT_TOP don't map through the GTT */
|
||||
xe_assert(xe, addr >= xe_wopcm_size(guc_to_xe(guc)));
|
||||
xe_assert(xe, addr < GUC_GGTT_TOP);
|
||||
xe_assert(xe, bo->size <= GUC_GGTT_TOP - addr);
|
||||
xe_assert(xe, xe_bo_size(bo) <= GUC_GGTT_TOP - addr);
|
||||
|
||||
return addr;
|
||||
}
|
||||
@@ -420,7 +421,7 @@ static int guc_g2g_register(struct xe_guc *near_guc, struct xe_gt *far_gt, u32 t
|
||||
buf = base + G2G_DESC_AREA_SIZE + slot * G2G_BUFFER_SIZE;
|
||||
|
||||
xe_assert(xe, (desc - base + G2G_DESC_SIZE) <= G2G_DESC_AREA_SIZE);
|
||||
xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= g2g_bo->size);
|
||||
xe_assert(xe, (buf - base + G2G_BUFFER_SIZE) <= xe_bo_size(g2g_bo));
|
||||
|
||||
return guc_action_register_g2g_buffer(near_guc, type, far_tile, far_dev,
|
||||
desc, buf, G2G_BUFFER_SIZE);
|
||||
@@ -570,6 +571,86 @@ err_deregister:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __guc_opt_in_features_enable(struct xe_guc *guc, u64 addr, u32 num_dwords)
|
||||
{
|
||||
u32 action[] = {
|
||||
XE_GUC_ACTION_OPT_IN_FEATURE_KLV,
|
||||
lower_32_bits(addr),
|
||||
upper_32_bits(addr),
|
||||
num_dwords
|
||||
};
|
||||
|
||||
return xe_guc_ct_send_block(&guc->ct, action, ARRAY_SIZE(action));
|
||||
}
|
||||
|
||||
static bool supports_dynamic_ics(struct xe_guc *guc)
|
||||
{
|
||||
struct xe_device *xe = guc_to_xe(guc);
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
|
||||
/* Dynamic ICS is available for PVC and Xe2 and newer platforms. */
|
||||
if (xe->info.platform != XE_PVC && GRAPHICS_VER(xe) < 20)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The feature is currently not compatible with multi-lrc, so the GuC
|
||||
* does not support it at all on the media engines (which are the main
|
||||
* users of mlrc). On the primary GT side, to avoid it being used in
|
||||
* conjunction with mlrc, we only enable it if we are in single CCS
|
||||
* mode.
|
||||
*/
|
||||
if (xe_gt_is_media_type(gt) || gt->ccs_mode > 1)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Dynamic ICS requires GuC v70.40.1, which maps to compatibility
|
||||
* version v1.18.4.
|
||||
*/
|
||||
return GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 18, 4);
|
||||
}
|
||||
|
||||
#define OPT_IN_MAX_DWORDS 16
|
||||
int xe_guc_opt_in_features_enable(struct xe_guc *guc)
|
||||
{
|
||||
struct xe_device *xe = guc_to_xe(guc);
|
||||
CLASS(xe_guc_buf, buf)(&guc->buf, OPT_IN_MAX_DWORDS);
|
||||
u32 count = 0;
|
||||
u32 *klvs;
|
||||
int ret;
|
||||
|
||||
if (!xe_guc_buf_is_valid(buf))
|
||||
return -ENOBUFS;
|
||||
|
||||
klvs = xe_guc_buf_cpu_ptr(buf);
|
||||
|
||||
/*
|
||||
* The extra CAT error type opt-in was added in GuC v70.17.0, which maps
|
||||
* to compatibility version v1.7.0.
|
||||
* Note that the GuC allows enabling this KLV even on platforms that do
|
||||
* not support the extra type; in such case the returned type variable
|
||||
* will be set to a known invalid value which we can check against.
|
||||
*/
|
||||
if (GUC_SUBMIT_VER(guc) >= MAKE_GUC_VER(1, 7, 0))
|
||||
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_EXT_CAT_ERR_TYPE);
|
||||
|
||||
if (supports_dynamic_ics(guc))
|
||||
klvs[count++] = PREP_GUC_KLV_TAG(OPT_IN_FEATURE_DYNAMIC_INHIBIT_CONTEXT_SWITCH);
|
||||
|
||||
if (count) {
|
||||
xe_assert(xe, count <= OPT_IN_MAX_DWORDS);
|
||||
|
||||
ret = __guc_opt_in_features_enable(guc, xe_guc_buf_flush(buf), count);
|
||||
if (ret < 0) {
|
||||
xe_gt_err(guc_to_gt(guc),
|
||||
"failed to enable GuC opt-in features: %pe\n",
|
||||
ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void guc_fini_hw(void *arg)
|
||||
{
|
||||
struct xe_guc *guc = arg;
|
||||
@@ -577,7 +658,7 @@ static void guc_fini_hw(void *arg)
|
||||
unsigned int fw_ref;
|
||||
|
||||
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
|
||||
xe_uc_fini_hw(&guc_to_gt(guc)->uc);
|
||||
xe_uc_sanitize_reset(&guc_to_gt(guc)->uc);
|
||||
xe_force_wake_put(gt_to_fw(gt), fw_ref);
|
||||
|
||||
guc_g2g_fini(guc);
|
||||
@@ -627,23 +708,51 @@ static int xe_guc_realloc_post_hwconfig(struct xe_guc *guc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf_guc_init(struct xe_guc *guc)
|
||||
static int vf_guc_init_noalloc(struct xe_guc *guc)
|
||||
{
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
int err;
|
||||
|
||||
xe_guc_comm_init_early(guc);
|
||||
|
||||
err = xe_guc_ct_init(&guc->ct);
|
||||
err = xe_gt_sriov_vf_bootstrap(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_guc_relay_init(&guc->relay);
|
||||
err = xe_gt_sriov_vf_query_config(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_guc_init_noalloc(struct xe_guc *guc)
|
||||
{
|
||||
struct xe_device *xe = guc_to_xe(guc);
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
int ret;
|
||||
|
||||
xe_guc_comm_init_early(guc);
|
||||
|
||||
ret = xe_guc_ct_init_noalloc(&guc->ct);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = xe_guc_relay_init(&guc->relay);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
ret = vf_guc_init_noalloc(guc);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
xe_gt_err(gt, "GuC init failed with %pe\n", ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xe_guc_init(struct xe_guc *guc)
|
||||
{
|
||||
struct xe_device *xe = guc_to_xe(guc);
|
||||
@@ -653,13 +762,13 @@ int xe_guc_init(struct xe_guc *guc)
|
||||
guc->fw.type = XE_UC_FW_TYPE_GUC;
|
||||
ret = xe_uc_fw_init(&guc->fw);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
if (!xe_uc_fw_is_enabled(&guc->fw))
|
||||
return 0;
|
||||
|
||||
if (IS_SRIOV_VF(xe)) {
|
||||
ret = vf_guc_init(guc);
|
||||
ret = xe_guc_ct_init(&guc->ct);
|
||||
if (ret)
|
||||
goto out;
|
||||
return 0;
|
||||
@@ -681,10 +790,6 @@ int xe_guc_init(struct xe_guc *guc)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = xe_guc_relay_init(&guc->relay);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
xe_uc_fw_change_status(&guc->fw, XE_UC_FIRMWARE_LOADABLE);
|
||||
|
||||
ret = devm_add_action_or_reset(xe->drm.dev, guc_fini_hw, guc);
|
||||
@@ -693,8 +798,6 @@ int xe_guc_init(struct xe_guc *guc)
|
||||
|
||||
guc_init_params(guc);
|
||||
|
||||
xe_guc_comm_init_early(guc);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -767,6 +870,10 @@ int xe_guc_post_load_init(struct xe_guc *guc)
|
||||
|
||||
xe_guc_ads_populate_post_load(&guc->ads);
|
||||
|
||||
ret = xe_guc_opt_in_features_enable(guc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (xe_guc_g2g_wanted(guc_to_xe(guc))) {
|
||||
ret = guc_g2g_start(guc);
|
||||
if (ret)
|
||||
@@ -1112,13 +1219,17 @@ static int vf_guc_min_load_for_hwconfig(struct xe_guc *guc)
|
||||
|
||||
ret = xe_gt_sriov_vf_connect(gt);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_out;
|
||||
|
||||
ret = xe_gt_sriov_vf_query_runtime(gt);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
xe_guc_sanitize(guc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
struct drm_printer;
|
||||
|
||||
void xe_guc_comm_init_early(struct xe_guc *guc);
|
||||
int xe_guc_init_noalloc(struct xe_guc *guc);
|
||||
int xe_guc_init(struct xe_guc *guc);
|
||||
int xe_guc_init_post_hwconfig(struct xe_guc *guc);
|
||||
int xe_guc_post_load_init(struct xe_guc *guc);
|
||||
@@ -33,6 +34,7 @@ int xe_guc_reset(struct xe_guc *guc);
|
||||
int xe_guc_upload(struct xe_guc *guc);
|
||||
int xe_guc_min_load_for_hwconfig(struct xe_guc *guc);
|
||||
int xe_guc_enable_communication(struct xe_guc *guc);
|
||||
int xe_guc_opt_in_features_enable(struct xe_guc *guc);
|
||||
int xe_guc_suspend(struct xe_guc *guc);
|
||||
void xe_guc_notify(struct xe_guc *guc);
|
||||
int xe_guc_auth_huc(struct xe_guc *guc, u32 rsa_addr);
|
||||
|
||||
@@ -890,7 +890,7 @@ void xe_guc_ads_populate_minimal(struct xe_guc_ads *ads)
|
||||
|
||||
xe_gt_assert(gt, ads->bo);
|
||||
|
||||
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, ads->bo->size);
|
||||
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, xe_bo_size(ads->bo));
|
||||
guc_policies_init(ads);
|
||||
guc_golden_lrc_init(ads);
|
||||
guc_mapping_table_init_invalid(gt, &info_map);
|
||||
@@ -914,7 +914,7 @@ void xe_guc_ads_populate(struct xe_guc_ads *ads)
|
||||
|
||||
xe_gt_assert(gt, ads->bo);
|
||||
|
||||
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, ads->bo->size);
|
||||
xe_map_memset(ads_to_xe(ads), ads_to_map(ads), 0, 0, xe_bo_size(ads->bo));
|
||||
guc_policies_init(ads);
|
||||
fill_engine_enable_masks(gt, &info_map);
|
||||
guc_mmio_reg_state_init(ads);
|
||||
@@ -995,16 +995,6 @@ static int guc_ads_action_update_policies(struct xe_guc_ads *ads, u32 policy_off
|
||||
return xe_guc_ct_send(ct, action, ARRAY_SIZE(action), 0, 0);
|
||||
}
|
||||
|
||||
static int guc_ads_update_policies(struct xe_guc_ads *ads, const struct guc_policies *policies)
|
||||
{
|
||||
CLASS(xe_guc_buf_from_data, buf)(&ads_to_guc(ads)->buf, policies, sizeof(*policies));
|
||||
|
||||
if (!xe_guc_buf_is_valid(buf))
|
||||
return -ENOBUFS;
|
||||
|
||||
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_ads_scheduler_policy_toggle_reset - Toggle reset policy
|
||||
* @ads: Additional data structures object
|
||||
@@ -1015,13 +1005,16 @@ static int guc_ads_update_policies(struct xe_guc_ads *ads, const struct guc_poli
|
||||
*/
|
||||
int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
|
||||
{
|
||||
struct xe_device *xe = ads_to_xe(ads);
|
||||
struct guc_policies *policies;
|
||||
int ret;
|
||||
struct xe_guc *guc = ads_to_guc(ads);
|
||||
struct xe_device *xe = ads_to_xe(ads);
|
||||
CLASS(xe_guc_buf, buf)(&guc->buf, sizeof(*policies));
|
||||
|
||||
policies = kmalloc(sizeof(*policies), GFP_KERNEL);
|
||||
if (!policies)
|
||||
return -ENOMEM;
|
||||
if (!xe_guc_buf_is_valid(buf))
|
||||
return -ENOBUFS;
|
||||
|
||||
policies = xe_guc_buf_cpu_ptr(buf);
|
||||
memset(policies, 0, sizeof(*policies));
|
||||
|
||||
policies->dpc_promote_time = ads_blob_read(ads, policies.dpc_promote_time);
|
||||
policies->max_num_work_items = ads_blob_read(ads, policies.max_num_work_items);
|
||||
@@ -1031,7 +1024,5 @@ int xe_guc_ads_scheduler_policy_toggle_reset(struct xe_guc_ads *ads)
|
||||
else
|
||||
policies->global_flags &= ~GLOBAL_POLICY_DISABLE_ENGINE_RESET;
|
||||
|
||||
ret = guc_ads_update_policies(ads, policies);
|
||||
kfree(policies);
|
||||
return ret;
|
||||
return guc_ads_action_update_policies(ads, xe_guc_buf_flush(buf));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@
|
||||
#include "xe_pm.h"
|
||||
#include "xe_trace_guc.h"
|
||||
|
||||
static void receive_g2h(struct xe_guc_ct *ct);
|
||||
static void g2h_worker_func(struct work_struct *w);
|
||||
static void safe_mode_worker_func(struct work_struct *w);
|
||||
static void ct_exit_safe_mode(struct xe_guc_ct *ct);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
|
||||
enum {
|
||||
/* Internal states, not error conditions */
|
||||
@@ -80,6 +85,7 @@ struct g2h_fence {
|
||||
u16 error;
|
||||
u16 hint;
|
||||
u16 reason;
|
||||
bool cancel;
|
||||
bool retry;
|
||||
bool fail;
|
||||
bool done;
|
||||
@@ -98,6 +104,13 @@ static void g2h_fence_init(struct g2h_fence *g2h_fence, u32 *response_buffer)
|
||||
g2h_fence->seqno = ~0x0;
|
||||
}
|
||||
|
||||
static void g2h_fence_cancel(struct g2h_fence *g2h_fence)
|
||||
{
|
||||
g2h_fence->cancel = true;
|
||||
g2h_fence->fail = true;
|
||||
g2h_fence->done = true;
|
||||
}
|
||||
|
||||
static bool g2h_fence_needs_alloc(struct g2h_fence *g2h_fence)
|
||||
{
|
||||
return g2h_fence->seqno == ~0x0;
|
||||
@@ -189,14 +202,11 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
|
||||
{
|
||||
struct xe_guc_ct *ct = arg;
|
||||
|
||||
ct_exit_safe_mode(ct);
|
||||
destroy_workqueue(ct->g2h_wq);
|
||||
xa_destroy(&ct->fence_lookup);
|
||||
}
|
||||
|
||||
static void receive_g2h(struct xe_guc_ct *ct);
|
||||
static void g2h_worker_func(struct work_struct *w);
|
||||
static void safe_mode_worker_func(struct work_struct *w);
|
||||
|
||||
static void primelockdep(struct xe_guc_ct *ct)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_LOCKDEP))
|
||||
@@ -207,12 +217,10 @@ static void primelockdep(struct xe_guc_ct *ct)
|
||||
fs_reclaim_release(GFP_KERNEL);
|
||||
}
|
||||
|
||||
int xe_guc_ct_init(struct xe_guc_ct *ct)
|
||||
int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
|
||||
{
|
||||
struct xe_device *xe = ct_to_xe(ct);
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
struct xe_tile *tile = gt_to_tile(gt);
|
||||
struct xe_bo *bo;
|
||||
int err;
|
||||
|
||||
xe_gt_assert(gt, !(guc_ct_size() % PAGE_SIZE));
|
||||
@@ -238,6 +246,23 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
|
||||
|
||||
primelockdep(ct);
|
||||
|
||||
err = drmm_add_action_or_reset(&xe->drm, guc_ct_fini, ct);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_gt_assert(gt, ct->state == XE_GUC_CT_STATE_NOT_INITIALIZED);
|
||||
ct->state = XE_GUC_CT_STATE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(xe_guc_ct_init_noalloc, ERRNO); /* See xe_pci_probe() */
|
||||
|
||||
int xe_guc_ct_init(struct xe_guc_ct *ct)
|
||||
{
|
||||
struct xe_device *xe = ct_to_xe(ct);
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
struct xe_tile *tile = gt_to_tile(gt);
|
||||
struct xe_bo *bo;
|
||||
|
||||
bo = xe_managed_bo_create_pin_map(xe, tile, guc_ct_size(),
|
||||
XE_BO_FLAG_SYSTEM |
|
||||
XE_BO_FLAG_GGTT |
|
||||
@@ -247,13 +272,6 @@ int xe_guc_ct_init(struct xe_guc_ct *ct)
|
||||
return PTR_ERR(bo);
|
||||
|
||||
ct->bo = bo;
|
||||
|
||||
err = drmm_add_action_or_reset(&xe->drm, guc_ct_fini, ct);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
xe_gt_assert(gt, ct->state == XE_GUC_CT_STATE_NOT_INITIALIZED);
|
||||
ct->state = XE_GUC_CT_STATE_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
ALLOW_ERROR_INJECTION(xe_guc_ct_init, ERRNO); /* See xe_pci_probe() */
|
||||
@@ -374,9 +392,13 @@ static int guc_ct_control_toggle(struct xe_guc_ct *ct, bool enable)
|
||||
return ret > 0 ? -EPROTO : ret;
|
||||
}
|
||||
|
||||
static void xe_guc_ct_set_state(struct xe_guc_ct *ct,
|
||||
static void guc_ct_change_state(struct xe_guc_ct *ct,
|
||||
enum xe_guc_ct_state state)
|
||||
{
|
||||
struct xe_gt *gt = ct_to_gt(ct);
|
||||
struct g2h_fence *g2h_fence;
|
||||
unsigned long idx;
|
||||
|
||||
mutex_lock(&ct->lock); /* Serialise dequeue_one_g2h() */
|
||||
spin_lock_irq(&ct->fast_lock); /* Serialise CT fast-path */
|
||||
|
||||
@@ -388,8 +410,20 @@ static void xe_guc_ct_set_state(struct xe_guc_ct *ct,
|
||||
ct->g2h_outstanding = 0;
|
||||
ct->state = state;
|
||||
|
||||
xe_gt_dbg(gt, "GuC CT communication channel %s\n",
|
||||
state == XE_GUC_CT_STATE_STOPPED ? "stopped" :
|
||||
str_enabled_disabled(state == XE_GUC_CT_STATE_ENABLED));
|
||||
|
||||
spin_unlock_irq(&ct->fast_lock);
|
||||
|
||||
/* cancel all in-flight send-recv requests */
|
||||
xa_for_each(&ct->fence_lookup, idx, g2h_fence)
|
||||
g2h_fence_cancel(g2h_fence);
|
||||
|
||||
/* make sure guc_ct_send_recv() will see g2h_fence changes */
|
||||
smp_mb();
|
||||
wake_up_all(&ct->g2h_fence_wq);
|
||||
|
||||
/*
|
||||
* Lockdep doesn't like this under the fast lock and he destroy only
|
||||
* needs to be serialized with the send path which ct lock provides.
|
||||
@@ -443,7 +477,7 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
|
||||
|
||||
xe_gt_assert(gt, !xe_guc_ct_enabled(ct));
|
||||
|
||||
xe_map_memset(xe, &ct->bo->vmap, 0, 0, ct->bo->size);
|
||||
xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
|
||||
guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
|
||||
guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
|
||||
|
||||
@@ -459,11 +493,10 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_ENABLED);
|
||||
guc_ct_change_state(ct, XE_GUC_CT_STATE_ENABLED);
|
||||
|
||||
smp_mb();
|
||||
wake_up_all(&ct->wq);
|
||||
xe_gt_dbg(gt, "GuC CT communication channel enabled\n");
|
||||
|
||||
if (ct_needs_safe_mode(ct))
|
||||
ct_enter_safe_mode(ct);
|
||||
@@ -504,7 +537,7 @@ static void stop_g2h_handler(struct xe_guc_ct *ct)
|
||||
*/
|
||||
void xe_guc_ct_disable(struct xe_guc_ct *ct)
|
||||
{
|
||||
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_DISABLED);
|
||||
guc_ct_change_state(ct, XE_GUC_CT_STATE_DISABLED);
|
||||
ct_exit_safe_mode(ct);
|
||||
stop_g2h_handler(ct);
|
||||
}
|
||||
@@ -520,7 +553,7 @@ void xe_guc_ct_stop(struct xe_guc_ct *ct)
|
||||
if (!xe_guc_ct_initialized(ct))
|
||||
return;
|
||||
|
||||
xe_guc_ct_set_state(ct, XE_GUC_CT_STATE_STOPPED);
|
||||
guc_ct_change_state(ct, XE_GUC_CT_STATE_STOPPED);
|
||||
stop_g2h_handler(ct);
|
||||
}
|
||||
|
||||
@@ -1083,6 +1116,11 @@ retry_same_fence:
|
||||
goto retry;
|
||||
}
|
||||
if (g2h_fence.fail) {
|
||||
if (g2h_fence.cancel) {
|
||||
xe_gt_dbg(gt, "H2G request %#x canceled!\n", action[0]);
|
||||
ret = -ECANCELED;
|
||||
goto unlock;
|
||||
}
|
||||
xe_gt_err(gt, "H2G request %#x failed: error %#x hint %#x\n",
|
||||
action[0], g2h_fence.error, g2h_fence.hint);
|
||||
ret = -EIO;
|
||||
@@ -1091,6 +1129,7 @@ retry_same_fence:
|
||||
if (ret > 0)
|
||||
ret = response_buffer ? g2h_fence.response_len : g2h_fence.response_data;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ct->lock);
|
||||
|
||||
return ret;
|
||||
@@ -1897,7 +1936,7 @@ static struct xe_guc_ct_snapshot *guc_ct_snapshot_alloc(struct xe_guc_ct *ct, bo
|
||||
return NULL;
|
||||
|
||||
if (ct->bo && want_ctb) {
|
||||
snapshot->ctb_size = ct->bo->size;
|
||||
snapshot->ctb_size = xe_bo_size(ct->bo);
|
||||
snapshot->ctb = kmalloc(snapshot->ctb_size, atomic ? GFP_ATOMIC : GFP_KERNEL);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
struct drm_printer;
|
||||
struct xe_device;
|
||||
|
||||
int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct);
|
||||
int xe_guc_ct_init(struct xe_guc_ct *ct);
|
||||
int xe_guc_ct_enable(struct xe_guc_ct *ct);
|
||||
void xe_guc_ct_disable(struct xe_guc_ct *ct);
|
||||
|
||||
@@ -79,7 +79,7 @@ static struct xe_guc_log_snapshot *xe_guc_log_snapshot_alloc(struct xe_guc_log *
|
||||
* Also, can't use vmalloc as might be called from atomic context. So need
|
||||
* to break the buffer up into smaller chunks that can be allocated.
|
||||
*/
|
||||
snapshot->size = log->bo->size;
|
||||
snapshot->size = xe_bo_size(log->bo);
|
||||
snapshot->num_chunks = DIV_ROUND_UP(snapshot->size, GUC_LOG_CHUNK_SIZE);
|
||||
|
||||
snapshot->copy = kcalloc(snapshot->num_chunks, sizeof(*snapshot->copy),
|
||||
|
||||
+212
-71
@@ -5,8 +5,11 @@
|
||||
|
||||
#include "xe_guc_pc.h"
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/wait_bit.h>
|
||||
|
||||
#include <drm/drm_managed.h>
|
||||
#include <drm/drm_print.h>
|
||||
@@ -52,9 +55,11 @@
|
||||
#define LNL_MERT_FREQ_CAP 800
|
||||
#define BMG_MERT_FREQ_CAP 2133
|
||||
#define BMG_MIN_FREQ 1200
|
||||
#define BMG_MERT_FLUSH_FREQ_CAP 2600
|
||||
|
||||
#define SLPC_RESET_TIMEOUT_MS 5 /* roughly 5ms, but no need for precision */
|
||||
#define SLPC_RESET_EXTENDED_TIMEOUT_MS 1000 /* To be used only at pc_start */
|
||||
#define SLPC_ACT_FREQ_TIMEOUT_MS 100
|
||||
|
||||
/**
|
||||
* DOC: GuC Power Conservation (PC)
|
||||
@@ -142,6 +147,36 @@ static int wait_for_pc_state(struct xe_guc_pc *pc,
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int wait_for_flush_complete(struct xe_guc_pc *pc)
|
||||
{
|
||||
const unsigned long timeout = msecs_to_jiffies(30);
|
||||
|
||||
if (!wait_var_event_timeout(&pc->flush_freq_limit,
|
||||
!atomic_read(&pc->flush_freq_limit),
|
||||
timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wait_for_act_freq_limit(struct xe_guc_pc *pc, u32 freq)
|
||||
{
|
||||
int timeout_us = SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC;
|
||||
int slept, wait = 10;
|
||||
|
||||
for (slept = 0; slept < timeout_us;) {
|
||||
if (xe_guc_pc_get_act_freq(pc) <= freq)
|
||||
return 0;
|
||||
|
||||
usleep_range(wait, wait << 1);
|
||||
slept += wait;
|
||||
wait <<= 1;
|
||||
if (slept + wait > timeout_us)
|
||||
wait = timeout_us - slept;
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
static int pc_action_reset(struct xe_guc_pc *pc)
|
||||
{
|
||||
struct xe_guc_ct *ct = pc_to_ct(pc);
|
||||
@@ -554,6 +589,25 @@ u32 xe_guc_pc_get_rpn_freq(struct xe_guc_pc *pc)
|
||||
return pc->rpn_freq;
|
||||
}
|
||||
|
||||
static int xe_guc_pc_get_min_freq_locked(struct xe_guc_pc *pc, u32 *freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
lockdep_assert_held(&pc->freq_lock);
|
||||
|
||||
/* Might be in the middle of a gt reset */
|
||||
if (!pc->freq_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = pc_action_query_task_state(pc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*freq = pc_get_min_freq(pc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_pc_get_min_freq - Get the min operational frequency
|
||||
* @pc: The GuC PC
|
||||
@@ -563,27 +617,29 @@ u32 xe_guc_pc_get_rpn_freq(struct xe_guc_pc *pc)
|
||||
* -EAGAIN if GuC PC not ready (likely in middle of a reset).
|
||||
*/
|
||||
int xe_guc_pc_get_min_freq(struct xe_guc_pc *pc, u32 *freq)
|
||||
{
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
return xe_guc_pc_get_min_freq_locked(pc, freq);
|
||||
}
|
||||
|
||||
static int xe_guc_pc_set_min_freq_locked(struct xe_guc_pc *pc, u32 freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
xe_device_assert_mem_access(pc_to_xe(pc));
|
||||
lockdep_assert_held(&pc->freq_lock);
|
||||
|
||||
mutex_lock(&pc->freq_lock);
|
||||
if (!pc->freq_ready) {
|
||||
/* Might be in the middle of a gt reset */
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
/* Might be in the middle of a gt reset */
|
||||
if (!pc->freq_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = pc_action_query_task_state(pc);
|
||||
ret = pc_set_min_freq(pc, freq);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
*freq = pc_get_min_freq(pc);
|
||||
pc->user_requested_min = freq;
|
||||
|
||||
out:
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,25 +652,29 @@ out:
|
||||
* -EINVAL if value out of bounds.
|
||||
*/
|
||||
int xe_guc_pc_set_min_freq(struct xe_guc_pc *pc, u32 freq)
|
||||
{
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
return xe_guc_pc_set_min_freq_locked(pc, freq);
|
||||
}
|
||||
|
||||
static int xe_guc_pc_get_max_freq_locked(struct xe_guc_pc *pc, u32 *freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pc->freq_lock);
|
||||
if (!pc->freq_ready) {
|
||||
/* Might be in the middle of a gt reset */
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
lockdep_assert_held(&pc->freq_lock);
|
||||
|
||||
ret = pc_set_min_freq(pc, freq);
|
||||
/* Might be in the middle of a gt reset */
|
||||
if (!pc->freq_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = pc_action_query_task_state(pc);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
pc->user_requested_min = freq;
|
||||
*freq = pc_get_max_freq(pc);
|
||||
|
||||
out:
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -626,25 +686,29 @@ out:
|
||||
* -EAGAIN if GuC PC not ready (likely in middle of a reset).
|
||||
*/
|
||||
int xe_guc_pc_get_max_freq(struct xe_guc_pc *pc, u32 *freq)
|
||||
{
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
return xe_guc_pc_get_max_freq_locked(pc, freq);
|
||||
}
|
||||
|
||||
static int xe_guc_pc_set_max_freq_locked(struct xe_guc_pc *pc, u32 freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pc->freq_lock);
|
||||
if (!pc->freq_ready) {
|
||||
/* Might be in the middle of a gt reset */
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
lockdep_assert_held(&pc->freq_lock);
|
||||
|
||||
ret = pc_action_query_task_state(pc);
|
||||
/* Might be in the middle of a gt reset */
|
||||
if (!pc->freq_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = pc_set_max_freq(pc, freq);
|
||||
if (ret)
|
||||
goto out;
|
||||
return ret;
|
||||
|
||||
*freq = pc_get_max_freq(pc);
|
||||
pc->user_requested_max = freq;
|
||||
|
||||
out:
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -658,24 +722,14 @@ out:
|
||||
*/
|
||||
int xe_guc_pc_set_max_freq(struct xe_guc_pc *pc, u32 freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&pc->freq_lock);
|
||||
if (!pc->freq_ready) {
|
||||
/* Might be in the middle of a gt reset */
|
||||
ret = -EAGAIN;
|
||||
goto out;
|
||||
if (XE_WA(pc_to_gt(pc), 22019338487)) {
|
||||
if (wait_for_flush_complete(pc) != 0)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
ret = pc_set_max_freq(pc, freq);
|
||||
if (ret)
|
||||
goto out;
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
pc->user_requested_max = freq;
|
||||
|
||||
out:
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
return ret;
|
||||
return xe_guc_pc_set_max_freq_locked(pc, freq);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -873,29 +927,116 @@ static int pc_adjust_requested_freq(struct xe_guc_pc *pc)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
|
||||
static bool needs_flush_freq_limit(struct xe_guc_pc *pc)
|
||||
{
|
||||
struct xe_gt *gt = pc_to_gt(pc);
|
||||
|
||||
return XE_WA(gt, 22019338487) &&
|
||||
pc->rp0_freq > BMG_MERT_FLUSH_FREQ_CAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_pc_apply_flush_freq_limit() - Limit max GT freq during L2 flush
|
||||
* @pc: the xe_guc_pc object
|
||||
*
|
||||
* As per the WA, reduce max GT frequency during L2 cache flush
|
||||
*/
|
||||
void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc)
|
||||
{
|
||||
struct xe_gt *gt = pc_to_gt(pc);
|
||||
u32 max_freq;
|
||||
int ret;
|
||||
|
||||
if (!needs_flush_freq_limit(pc))
|
||||
return;
|
||||
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
ret = xe_guc_pc_get_max_freq_locked(pc, &max_freq);
|
||||
if (!ret && max_freq > BMG_MERT_FLUSH_FREQ_CAP) {
|
||||
ret = pc_set_max_freq(pc, BMG_MERT_FLUSH_FREQ_CAP);
|
||||
if (ret) {
|
||||
xe_gt_err_once(gt, "Failed to cap max freq on flush to %u, %pe\n",
|
||||
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_set(&pc->flush_freq_limit, 1);
|
||||
|
||||
/*
|
||||
* If user has previously changed max freq, stash that value to
|
||||
* restore later, otherwise use the current max. New user
|
||||
* requests wait on flush.
|
||||
*/
|
||||
if (pc->user_requested_max != 0)
|
||||
pc->stashed_max_freq = pc->user_requested_max;
|
||||
else
|
||||
pc->stashed_max_freq = max_freq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for actual freq to go below the flush cap: even if the previous
|
||||
* max was below cap, the current one might still be above it
|
||||
*/
|
||||
ret = wait_for_act_freq_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
|
||||
if (ret)
|
||||
xe_gt_err_once(gt, "Actual freq did not reduce to %u, %pe\n",
|
||||
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_guc_pc_remove_flush_freq_limit() - Remove max GT freq limit after L2 flush completes.
|
||||
* @pc: the xe_guc_pc object
|
||||
*
|
||||
* Retrieve the previous GT max frequency value.
|
||||
*/
|
||||
void xe_guc_pc_remove_flush_freq_limit(struct xe_guc_pc *pc)
|
||||
{
|
||||
struct xe_gt *gt = pc_to_gt(pc);
|
||||
int ret = 0;
|
||||
|
||||
if (XE_WA(pc_to_gt(pc), 22019338487)) {
|
||||
/*
|
||||
* Get updated min/max and stash them.
|
||||
*/
|
||||
ret = xe_guc_pc_get_min_freq(pc, &pc->stashed_min_freq);
|
||||
if (!ret)
|
||||
ret = xe_guc_pc_get_max_freq(pc, &pc->stashed_max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!needs_flush_freq_limit(pc))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
|
||||
*/
|
||||
mutex_lock(&pc->freq_lock);
|
||||
ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
|
||||
if (!ret)
|
||||
ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
}
|
||||
if (!atomic_read(&pc->flush_freq_limit))
|
||||
return;
|
||||
|
||||
mutex_lock(&pc->freq_lock);
|
||||
|
||||
ret = pc_set_max_freq(>->uc.guc.pc, pc->stashed_max_freq);
|
||||
if (ret)
|
||||
xe_gt_err_once(gt, "Failed to restore max freq %u:%d",
|
||||
pc->stashed_max_freq, ret);
|
||||
|
||||
atomic_set(&pc->flush_freq_limit, 0);
|
||||
mutex_unlock(&pc->freq_lock);
|
||||
wake_up_var(&pc->flush_freq_limit);
|
||||
}
|
||||
|
||||
static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!XE_WA(pc_to_gt(pc), 22019338487))
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&pc->freq_lock);
|
||||
|
||||
/*
|
||||
* Get updated min/max and stash them.
|
||||
*/
|
||||
ret = xe_guc_pc_get_min_freq_locked(pc, &pc->stashed_min_freq);
|
||||
if (!ret)
|
||||
ret = xe_guc_pc_get_max_freq_locked(pc, &pc->stashed_max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
|
||||
*/
|
||||
ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
|
||||
if (!ret)
|
||||
ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,7 @@ u64 xe_guc_pc_mc6_residency(struct xe_guc_pc *pc);
|
||||
void xe_guc_pc_init_early(struct xe_guc_pc *pc);
|
||||
int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc);
|
||||
void xe_guc_pc_raise_unslice(struct xe_guc_pc *pc);
|
||||
void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc);
|
||||
void xe_guc_pc_remove_flush_freq_limit(struct xe_guc_pc *pc);
|
||||
|
||||
#endif /* _XE_GUC_PC_H_ */
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
struct xe_guc_pc {
|
||||
/** @bo: GGTT buffer object that is shared with GuC PC */
|
||||
struct xe_bo *bo;
|
||||
/** @flush_freq_limit: 1 when max freq changes are limited by driver */
|
||||
atomic_t flush_freq_limit;
|
||||
/** @rp0_freq: HW RP0 frequency - The Maximum one */
|
||||
u32 rp0_freq;
|
||||
/** @rpa_freq: HW RPa frequency - The Achievable one */
|
||||
|
||||
@@ -908,12 +908,13 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
|
||||
struct xe_exec_queue *q = ge->q;
|
||||
struct xe_guc *guc = exec_queue_to_guc(q);
|
||||
struct xe_gpu_scheduler *sched = &ge->sched;
|
||||
bool wedged;
|
||||
bool wedged = false;
|
||||
|
||||
xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q));
|
||||
trace_xe_exec_queue_lr_cleanup(q);
|
||||
|
||||
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
|
||||
if (!exec_queue_killed(q))
|
||||
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
|
||||
|
||||
/* Kill the run_job / process_msg entry points */
|
||||
xe_sched_submission_stop(sched);
|
||||
@@ -1084,7 +1085,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
|
||||
int err = -ETIME;
|
||||
pid_t pid = -1;
|
||||
int i = 0;
|
||||
bool wedged, skip_timeout_check;
|
||||
bool wedged = false, skip_timeout_check;
|
||||
|
||||
/*
|
||||
* TDR has fired before free job worker. Common if exec queue
|
||||
@@ -1130,7 +1131,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
|
||||
* doesn't work for SRIOV. For now assuming timeouts in wedged mode are
|
||||
* genuine timeouts.
|
||||
*/
|
||||
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
|
||||
if (!exec_queue_killed(q))
|
||||
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
|
||||
|
||||
/* Engine state now stable, disable scheduling to check timestamp */
|
||||
if (!wedged && exec_queue_registered(q)) {
|
||||
@@ -2090,12 +2092,16 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
|
||||
struct xe_gt *gt = guc_to_gt(guc);
|
||||
struct xe_exec_queue *q;
|
||||
u32 guc_id;
|
||||
u32 type = XE_GUC_CAT_ERR_TYPE_INVALID;
|
||||
|
||||
if (unlikely(len < 1))
|
||||
if (unlikely(!len || len > 2))
|
||||
return -EPROTO;
|
||||
|
||||
guc_id = msg[0];
|
||||
|
||||
if (len == 2)
|
||||
type = msg[1];
|
||||
|
||||
if (guc_id == GUC_ID_UNKNOWN) {
|
||||
/*
|
||||
* GuC uses GUC_ID_UNKNOWN if it can not map the CAT fault to any PF/VF
|
||||
@@ -2109,8 +2115,19 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
|
||||
if (unlikely(!q))
|
||||
return -EPROTO;
|
||||
|
||||
xe_gt_dbg(gt, "Engine memory cat error: engine_class=%s, logical_mask: 0x%x, guc_id=%d",
|
||||
xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
|
||||
/*
|
||||
* The type is HW-defined and changes based on platform, so we don't
|
||||
* decode it in the kernel and only check if it is valid.
|
||||
* See bspec 54047 and 72187 for details.
|
||||
*/
|
||||
if (type != XE_GUC_CAT_ERR_TYPE_INVALID)
|
||||
xe_gt_dbg(gt,
|
||||
"Engine memory CAT error [%u]: class=%s, logical_mask: 0x%x, guc_id=%d",
|
||||
type, xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
|
||||
else
|
||||
xe_gt_dbg(gt,
|
||||
"Engine memory CAT error: class=%s, logical_mask: 0x%x, guc_id=%d",
|
||||
xe_hw_engine_class_to_str(q->class), q->logical_mask, guc_id);
|
||||
|
||||
trace_xe_exec_queue_memory_cat_error(q);
|
||||
|
||||
|
||||
@@ -11,15 +11,12 @@
|
||||
#include "xe_device_types.h"
|
||||
#include "xe_drv.h"
|
||||
#include "xe_heci_gsc.h"
|
||||
#include "regs/xe_gsc_regs.h"
|
||||
#include "xe_platform_types.h"
|
||||
#include "xe_survivability_mode.h"
|
||||
|
||||
#define GSC_BAR_LENGTH 0x00000FFC
|
||||
|
||||
#define DG1_GSC_HECI2_BASE 0x259000
|
||||
#define PVC_GSC_HECI2_BASE 0x285000
|
||||
#define DG2_GSC_HECI2_BASE 0x374000
|
||||
|
||||
static void heci_gsc_irq_mask(struct irq_data *d)
|
||||
{
|
||||
/* generic irq handling */
|
||||
|
||||
@@ -171,7 +171,7 @@ static int huc_auth_via_gsccs(struct xe_huc *huc)
|
||||
sizeof(struct pxp43_new_huc_auth_in));
|
||||
wr_offset = huc_emit_pxp_auth_msg(xe, &pkt->vmap, wr_offset,
|
||||
xe_bo_ggtt_addr(huc->fw.bo),
|
||||
huc->fw.bo->size);
|
||||
xe_bo_size(huc->fw.bo));
|
||||
do {
|
||||
err = xe_gsc_pkt_submit_kernel(>->uc.gsc, ggtt_offset, wr_offset,
|
||||
ggtt_offset + PXP43_HUC_AUTH_INOUT_SIZE,
|
||||
|
||||
@@ -1059,12 +1059,13 @@ struct xe_hw_engine *
|
||||
xe_hw_engine_lookup(struct xe_device *xe,
|
||||
struct drm_xe_engine_class_instance eci)
|
||||
{
|
||||
struct xe_gt *gt = xe_device_get_gt(xe, eci.gt_id);
|
||||
unsigned int idx;
|
||||
|
||||
if (eci.engine_class >= ARRAY_SIZE(user_to_xe_engine_class))
|
||||
return NULL;
|
||||
|
||||
if (eci.gt_id >= xe->info.gt_count)
|
||||
if (!gt)
|
||||
return NULL;
|
||||
|
||||
idx = array_index_nospec(eci.engine_class,
|
||||
|
||||
@@ -12,15 +12,6 @@
|
||||
#include "xe_hw_engine_group.h"
|
||||
#include "xe_vm.h"
|
||||
|
||||
static void
|
||||
hw_engine_group_free(struct drm_device *drm, void *arg)
|
||||
{
|
||||
struct xe_hw_engine_group *group = arg;
|
||||
|
||||
destroy_workqueue(group->resume_wq);
|
||||
kfree(group);
|
||||
}
|
||||
|
||||
static void
|
||||
hw_engine_group_resume_lr_jobs_func(struct work_struct *w)
|
||||
{
|
||||
@@ -53,7 +44,7 @@ hw_engine_group_alloc(struct xe_device *xe)
|
||||
struct xe_hw_engine_group *group;
|
||||
int err;
|
||||
|
||||
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||
group = drmm_kzalloc(&xe->drm, sizeof(*group), GFP_KERNEL);
|
||||
if (!group)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@@ -61,14 +52,14 @@ hw_engine_group_alloc(struct xe_device *xe)
|
||||
if (!group->resume_wq)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = drmm_add_action_or_reset(&xe->drm, __drmm_workqueue_release, group->resume_wq);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
init_rwsem(&group->mode_sem);
|
||||
INIT_WORK(&group->resume_work, hw_engine_group_resume_lr_jobs_func);
|
||||
INIT_LIST_HEAD(&group->exec_queue_list);
|
||||
|
||||
err = drmm_add_action_or_reset(&xe->drm, hw_engine_group_free, group);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
||||
/*
|
||||
* Intel Xe I2C attached Microcontroller Units (MCU)
|
||||
*
|
||||
* Copyright (C) 2025 Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sprintf.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "regs/xe_i2c_regs.h"
|
||||
#include "regs/xe_irq_regs.h"
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_device_types.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_platform_types.h"
|
||||
|
||||
/**
|
||||
* DOC: Xe I2C devices
|
||||
*
|
||||
* Register a platform device for the I2C host controller (Synpsys DesignWare
|
||||
* I2C) if the registers of that controller are mapped to the MMIO, and also the
|
||||
* I2C client device for the Add-In Management Controller (the MCU) attached to
|
||||
* the host controller.
|
||||
*
|
||||
* See drivers/i2c/busses/i2c-designware-* for more information on the I2C host
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static const char adapter_name[] = "i2c_designware";
|
||||
|
||||
static const struct property_entry xe_i2c_adapter_properties[] = {
|
||||
PROPERTY_ENTRY_STRING("compatible", "intel,xe-i2c"),
|
||||
PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_PLUS_FREQ),
|
||||
{ }
|
||||
};
|
||||
|
||||
static inline void xe_i2c_read_endpoint(struct xe_mmio *mmio, void *ep)
|
||||
{
|
||||
u32 *val = ep;
|
||||
|
||||
val[0] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_PREFIX);
|
||||
val[1] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_POSTFIX);
|
||||
}
|
||||
|
||||
static void xe_i2c_client_work(struct work_struct *work)
|
||||
{
|
||||
struct xe_i2c *i2c = container_of(work, struct xe_i2c, work);
|
||||
struct i2c_board_info info = {
|
||||
.type = "amc",
|
||||
.flags = I2C_CLIENT_HOST_NOTIFY,
|
||||
.addr = i2c->ep.addr[1],
|
||||
};
|
||||
|
||||
i2c->client[0] = i2c_new_client_device(i2c->adapter, &info);
|
||||
}
|
||||
|
||||
static int xe_i2c_notifier(struct notifier_block *nb, unsigned long action, void *data)
|
||||
{
|
||||
struct xe_i2c *i2c = container_of(nb, struct xe_i2c, bus_notifier);
|
||||
struct i2c_adapter *adapter = i2c_verify_adapter(data);
|
||||
struct device *dev = data;
|
||||
|
||||
if (action == BUS_NOTIFY_ADD_DEVICE &&
|
||||
adapter && dev->parent == &i2c->pdev->dev) {
|
||||
i2c->adapter = adapter;
|
||||
schedule_work(&i2c->work);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int xe_i2c_register_adapter(struct xe_i2c *i2c)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
|
||||
struct platform_device *pdev;
|
||||
struct fwnode_handle *fwnode;
|
||||
int ret;
|
||||
|
||||
fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
|
||||
if (!fwnode)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Not using platform_device_register_full() here because we don't have
|
||||
* a handle to the platform_device before it returns. xe_i2c_notifier()
|
||||
* uses that handle, but it may be called before
|
||||
* platform_device_register_full() is done.
|
||||
*/
|
||||
pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
|
||||
if (!pdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err_fwnode_remove;
|
||||
}
|
||||
|
||||
if (i2c->adapter_irq) {
|
||||
struct resource res;
|
||||
|
||||
res = DEFINE_RES_IRQ_NAMED(i2c->adapter_irq, "xe_i2c");
|
||||
|
||||
ret = platform_device_add_resources(pdev, &res, 1);
|
||||
if (ret)
|
||||
goto err_pdev_put;
|
||||
}
|
||||
|
||||
pdev->dev.parent = i2c->drm_dev;
|
||||
pdev->dev.fwnode = fwnode;
|
||||
i2c->adapter_node = fwnode;
|
||||
i2c->pdev = pdev;
|
||||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret)
|
||||
goto err_pdev_put;
|
||||
|
||||
return 0;
|
||||
|
||||
err_pdev_put:
|
||||
platform_device_put(pdev);
|
||||
err_fwnode_remove:
|
||||
fwnode_remove_software_node(fwnode);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
|
||||
{
|
||||
platform_device_unregister(i2c->pdev);
|
||||
fwnode_remove_software_node(i2c->adapter_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_i2c_irq_handler: Handler for I2C interrupts
|
||||
* @xe: xe device instance
|
||||
* @master_ctl: interrupt register
|
||||
*
|
||||
* Forward interrupts generated by the I2C host adapter to the I2C host adapter
|
||||
* driver.
|
||||
*/
|
||||
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl)
|
||||
{
|
||||
if (!xe->i2c || !xe->i2c->adapter_irq)
|
||||
return;
|
||||
|
||||
if (master_ctl & I2C_IRQ)
|
||||
generic_handle_irq_safe(xe->i2c->adapter_irq);
|
||||
}
|
||||
|
||||
static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw_irq_num)
|
||||
{
|
||||
irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops xe_i2c_irq_ops = {
|
||||
.map = xe_i2c_irq_map,
|
||||
};
|
||||
|
||||
static int xe_i2c_create_irq(struct xe_i2c *i2c)
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ))
|
||||
return 0;
|
||||
|
||||
domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL);
|
||||
if (!domain)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c->adapter_irq = irq_create_mapping(domain, 0);
|
||||
i2c->irqdomain = domain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xe_i2c_remove_irq(struct xe_i2c *i2c)
|
||||
{
|
||||
if (!i2c->irqdomain)
|
||||
return;
|
||||
|
||||
irq_dispose_mapping(i2c->adapter_irq);
|
||||
irq_domain_remove(i2c->irqdomain);
|
||||
}
|
||||
|
||||
static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
struct xe_i2c *i2c = context;
|
||||
|
||||
*val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xe_i2c_write(void *context, unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct xe_i2c *i2c = context;
|
||||
|
||||
xe_mmio_write32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET), val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config i2c_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_read = xe_i2c_read,
|
||||
.reg_write = xe_i2c_write,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
void xe_i2c_pm_suspend(struct xe_device *xe)
|
||||
{
|
||||
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
|
||||
|
||||
if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
|
||||
return;
|
||||
|
||||
xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot);
|
||||
drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR));
|
||||
}
|
||||
|
||||
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
|
||||
{
|
||||
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
|
||||
|
||||
if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
|
||||
return;
|
||||
|
||||
if (d3cold)
|
||||
xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_MEMORY);
|
||||
|
||||
xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0);
|
||||
drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR));
|
||||
}
|
||||
|
||||
static void xe_i2c_remove(void *data)
|
||||
{
|
||||
struct xe_i2c *i2c = data;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < XE_I2C_MAX_CLIENTS; i++)
|
||||
i2c_unregister_device(i2c->client[i]);
|
||||
|
||||
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
|
||||
xe_i2c_unregister_adapter(i2c);
|
||||
xe_i2c_remove_irq(i2c);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_i2c_probe: Probe the I2C host adapter and the I2C clients attached to it
|
||||
* @xe: xe device instance
|
||||
*
|
||||
* Register all the I2C devices described in the I2C Endpoint data structure.
|
||||
*
|
||||
* Return: 0 on success, error code on failure
|
||||
*/
|
||||
int xe_i2c_probe(struct xe_device *xe)
|
||||
{
|
||||
struct device *drm_dev = xe->drm.dev;
|
||||
struct xe_i2c_endpoint ep;
|
||||
struct regmap *regmap;
|
||||
struct xe_i2c *i2c;
|
||||
int ret;
|
||||
|
||||
if (xe->info.platform != XE_BATTLEMAGE)
|
||||
return 0;
|
||||
|
||||
xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep);
|
||||
if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
|
||||
return 0;
|
||||
|
||||
i2c = devm_kzalloc(drm_dev, sizeof(*i2c), GFP_KERNEL);
|
||||
if (!i2c)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&i2c->work, xe_i2c_client_work);
|
||||
i2c->mmio = xe_root_tile_mmio(xe);
|
||||
i2c->drm_dev = drm_dev;
|
||||
i2c->ep = ep;
|
||||
xe->i2c = i2c;
|
||||
|
||||
/* PCI PM isn't aware of this device, bring it up and match it with SGUnit state. */
|
||||
xe_i2c_pm_resume(xe, true);
|
||||
|
||||
regmap = devm_regmap_init(drm_dev, NULL, i2c, &i2c_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
i2c->bus_notifier.notifier_call = xe_i2c_notifier;
|
||||
ret = bus_register_notifier(&i2c_bus_type, &i2c->bus_notifier);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = xe_i2c_create_irq(i2c);
|
||||
if (ret)
|
||||
goto err_unregister_notifier;
|
||||
|
||||
ret = xe_i2c_register_adapter(i2c);
|
||||
if (ret)
|
||||
goto err_remove_irq;
|
||||
|
||||
return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c);
|
||||
|
||||
err_remove_irq:
|
||||
xe_i2c_remove_irq(i2c);
|
||||
|
||||
err_unregister_notifier:
|
||||
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
#ifndef _XE_I2C_H_
|
||||
#define _XE_I2C_H_
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct device;
|
||||
struct fwnode_handle;
|
||||
struct i2c_adapter;
|
||||
struct i2c_client;
|
||||
struct irq_domain;
|
||||
struct platform_device;
|
||||
struct xe_device;
|
||||
struct xe_mmio;
|
||||
|
||||
#define XE_I2C_MAX_CLIENTS 3
|
||||
|
||||
#define XE_I2C_EP_COOKIE_DEVICE 0xde
|
||||
|
||||
/* Endpoint Capabilities */
|
||||
#define XE_I2C_EP_CAP_IRQ BIT(0)
|
||||
|
||||
struct xe_i2c_endpoint {
|
||||
u8 cookie;
|
||||
u8 capabilities;
|
||||
u16 addr[XE_I2C_MAX_CLIENTS];
|
||||
};
|
||||
|
||||
struct xe_i2c {
|
||||
struct fwnode_handle *adapter_node;
|
||||
struct platform_device *pdev;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client[XE_I2C_MAX_CLIENTS];
|
||||
|
||||
struct notifier_block bus_notifier;
|
||||
struct work_struct work;
|
||||
|
||||
struct irq_domain *irqdomain;
|
||||
int adapter_irq;
|
||||
|
||||
struct xe_i2c_endpoint ep;
|
||||
struct device *drm_dev;
|
||||
|
||||
struct xe_mmio *mmio;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
int xe_i2c_probe(struct xe_device *xe);
|
||||
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl);
|
||||
void xe_i2c_pm_suspend(struct xe_device *xe);
|
||||
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold);
|
||||
#else
|
||||
static inline int xe_i2c_probe(struct xe_device *xe) { return 0; }
|
||||
static inline void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { }
|
||||
static inline void xe_i2c_pm_suspend(struct xe_device *xe) { }
|
||||
static inline void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) { }
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "xe_gt.h"
|
||||
#include "xe_guc.h"
|
||||
#include "xe_hw_engine.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_memirq.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_pxp.h"
|
||||
@@ -476,6 +477,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
|
||||
if (xe->info.has_heci_cscfi)
|
||||
xe_heci_csc_irq_handler(xe, master_ctl);
|
||||
xe_display_irq_handler(xe, master_ctl);
|
||||
xe_i2c_irq_handler(xe, master_ctl);
|
||||
gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,9 @@ static struct xe_lmtt_pt *lmtt_pt_alloc(struct xe_lmtt *lmtt, unsigned int level
|
||||
}
|
||||
|
||||
lmtt_assert(lmtt, xe_bo_is_vram(bo));
|
||||
lmtt_debug(lmtt, "level=%u addr=%#llx\n", level, (u64)xe_bo_main_addr(bo, XE_PAGE_SIZE));
|
||||
|
||||
xe_map_memset(lmtt_to_xe(lmtt), &bo->vmap, 0, 0, xe_bo_size(bo));
|
||||
|
||||
pt->level = level;
|
||||
pt->bo = bo;
|
||||
@@ -91,6 +94,9 @@ out:
|
||||
|
||||
static void lmtt_pt_free(struct xe_lmtt_pt *pt)
|
||||
{
|
||||
lmtt_debug(&pt->bo->tile->sriov.pf.lmtt, "level=%u addr=%llx\n",
|
||||
pt->level, (u64)xe_bo_main_addr(pt->bo, XE_PAGE_SIZE));
|
||||
|
||||
xe_bo_unpin_map_no_vm(pt->bo);
|
||||
kfree(pt);
|
||||
}
|
||||
@@ -226,9 +232,14 @@ static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
|
||||
|
||||
switch (lmtt->ops->lmtt_pte_size(level)) {
|
||||
case sizeof(u32):
|
||||
lmtt_assert(lmtt, !overflows_type(pte, u32));
|
||||
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u32), u32));
|
||||
|
||||
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u32), u32, pte);
|
||||
break;
|
||||
case sizeof(u64):
|
||||
lmtt_assert(lmtt, !pte || !iosys_map_rd(&pt->bo->vmap, idx * sizeof(u64), u64));
|
||||
|
||||
xe_map_wr(lmtt_to_xe(lmtt), &pt->bo->vmap, idx * sizeof(u64), u64, pte);
|
||||
break;
|
||||
default:
|
||||
@@ -386,11 +397,11 @@ static void lmtt_insert_bo(struct xe_lmtt *lmtt, unsigned int vfid, struct xe_bo
|
||||
u64 addr, vram_offset;
|
||||
|
||||
lmtt_assert(lmtt, IS_ALIGNED(start, page_size));
|
||||
lmtt_assert(lmtt, IS_ALIGNED(bo->size, page_size));
|
||||
lmtt_assert(lmtt, IS_ALIGNED(xe_bo_size(bo), page_size));
|
||||
lmtt_assert(lmtt, xe_bo_is_vram(bo));
|
||||
|
||||
vram_offset = vram_region_gpu_offset(bo->ttm.resource);
|
||||
xe_res_first(bo->ttm.resource, 0, bo->size, &cur);
|
||||
xe_res_first(bo->ttm.resource, 0, xe_bo_size(bo), &cur);
|
||||
while (cur.remaining) {
|
||||
addr = xe_res_dma(&cur);
|
||||
addr += vram_offset; /* XXX */
|
||||
|
||||
+18
-23
@@ -717,8 +717,12 @@ static u32 __xe_lrc_ctx_timestamp_udw_offset(struct xe_lrc *lrc)
|
||||
|
||||
static inline u32 __xe_lrc_indirect_ring_offset(struct xe_lrc *lrc)
|
||||
{
|
||||
/* Indirect ring state page is at the very end of LRC */
|
||||
return lrc->size - LRC_INDIRECT_RING_STATE_SIZE;
|
||||
return xe_bo_size(lrc->bo) - LRC_WA_BB_SIZE - LRC_INDIRECT_RING_STATE_SIZE;
|
||||
}
|
||||
|
||||
static inline u32 __xe_lrc_wa_bb_offset(struct xe_lrc *lrc)
|
||||
{
|
||||
return xe_bo_size(lrc->bo) - LRC_WA_BB_SIZE;
|
||||
}
|
||||
|
||||
#define DECL_MAP_ADDR_HELPERS(elem) \
|
||||
@@ -973,11 +977,6 @@ struct wa_bb_setup {
|
||||
u32 *batch, size_t max_size);
|
||||
};
|
||||
|
||||
static size_t wa_bb_offset(struct xe_lrc *lrc)
|
||||
{
|
||||
return lrc->bo->size - LRC_WA_BB_SIZE;
|
||||
}
|
||||
|
||||
static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
|
||||
{
|
||||
const size_t max_size = LRC_WA_BB_SIZE;
|
||||
@@ -993,7 +992,7 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
|
||||
return -ENOMEM;
|
||||
cmd = buf;
|
||||
} else {
|
||||
cmd = lrc->bo->vmap.vaddr + wa_bb_offset(lrc);
|
||||
cmd = lrc->bo->vmap.vaddr + __xe_lrc_wa_bb_offset(lrc);
|
||||
}
|
||||
|
||||
remain = max_size / sizeof(*cmd);
|
||||
@@ -1017,13 +1016,13 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
|
||||
|
||||
if (buf) {
|
||||
xe_map_memcpy_to(gt_to_xe(lrc->gt), &lrc->bo->vmap,
|
||||
wa_bb_offset(lrc), buf,
|
||||
__xe_lrc_wa_bb_offset(lrc), buf,
|
||||
(cmd - buf) * sizeof(*cmd));
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
xe_lrc_write_ctx_reg(lrc, CTX_BB_PER_CTX_PTR, xe_bo_ggtt_addr(lrc->bo) +
|
||||
wa_bb_offset(lrc) + 1);
|
||||
__xe_lrc_wa_bb_offset(lrc) + 1);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1040,19 +1039,22 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
u32 init_flags)
|
||||
{
|
||||
struct xe_gt *gt = hwe->gt;
|
||||
const u32 lrc_size = xe_gt_lrc_size(gt, hwe->class);
|
||||
const u32 bo_size = ring_size + lrc_size + LRC_WA_BB_SIZE;
|
||||
struct xe_tile *tile = gt_to_tile(gt);
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
struct iosys_map map;
|
||||
void *init_data = NULL;
|
||||
u32 arb_enable;
|
||||
u32 lrc_size;
|
||||
u32 bo_flags;
|
||||
int err;
|
||||
|
||||
kref_init(&lrc->refcount);
|
||||
lrc->gt = gt;
|
||||
lrc->size = lrc_size;
|
||||
lrc->flags = 0;
|
||||
lrc_size = ring_size + xe_gt_lrc_size(gt, hwe->class);
|
||||
lrc->ring.size = ring_size;
|
||||
lrc->ring.tail = 0;
|
||||
if (xe_gt_has_indirect_ring_state(gt))
|
||||
lrc->flags |= XE_LRC_FLAG_INDIRECT_RING_STATE;
|
||||
|
||||
@@ -1065,17 +1067,12 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
* FIXME: Perma-pinning LRC as we don't yet support moving GGTT address
|
||||
* via VM bind calls.
|
||||
*/
|
||||
lrc->bo = xe_bo_create_pin_map(xe, tile, NULL,
|
||||
lrc_size + LRC_WA_BB_SIZE,
|
||||
lrc->bo = xe_bo_create_pin_map(xe, tile, NULL, bo_size,
|
||||
ttm_bo_type_kernel,
|
||||
bo_flags);
|
||||
if (IS_ERR(lrc->bo))
|
||||
return PTR_ERR(lrc->bo);
|
||||
|
||||
lrc->size = lrc_size;
|
||||
lrc->ring.size = ring_size;
|
||||
lrc->ring.tail = 0;
|
||||
|
||||
xe_hw_fence_ctx_init(&lrc->fence_ctx, hwe->gt,
|
||||
hwe->fence_irq, hwe->name);
|
||||
|
||||
@@ -1096,10 +1093,9 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
|
||||
xe_map_memset(xe, &map, 0, 0, LRC_PPHWSP_SIZE); /* PPHWSP */
|
||||
xe_map_memcpy_to(xe, &map, LRC_PPHWSP_SIZE,
|
||||
gt->default_lrc[hwe->class] + LRC_PPHWSP_SIZE,
|
||||
xe_gt_lrc_size(gt, hwe->class) - LRC_PPHWSP_SIZE);
|
||||
lrc_size - LRC_PPHWSP_SIZE);
|
||||
} else {
|
||||
xe_map_memcpy_to(xe, &map, 0, init_data,
|
||||
xe_gt_lrc_size(gt, hwe->class));
|
||||
xe_map_memcpy_to(xe, &map, 0, init_data, lrc_size);
|
||||
kfree(init_data);
|
||||
}
|
||||
|
||||
@@ -1859,8 +1855,7 @@ struct xe_lrc_snapshot *xe_lrc_snapshot_capture(struct xe_lrc *lrc)
|
||||
snapshot->seqno = xe_lrc_seqno(lrc);
|
||||
snapshot->lrc_bo = xe_bo_get(lrc->bo);
|
||||
snapshot->lrc_offset = xe_lrc_pphwsp_offset(lrc);
|
||||
snapshot->lrc_size = lrc->bo->size - snapshot->lrc_offset -
|
||||
LRC_WA_BB_SIZE;
|
||||
snapshot->lrc_size = lrc->size;
|
||||
snapshot->lrc_snapshot = NULL;
|
||||
snapshot->ctx_timestamp = lower_32_bits(xe_lrc_ctx_timestamp(lrc));
|
||||
snapshot->ctx_job_timestamp = xe_lrc_ctx_job_timestamp(lrc);
|
||||
|
||||
@@ -22,7 +22,7 @@ struct xe_lrc {
|
||||
*/
|
||||
struct xe_bo *bo;
|
||||
|
||||
/** @size: size of lrc including any indirect ring state page */
|
||||
/** @size: size of the lrc and optional indirect ring state */
|
||||
u32 size;
|
||||
|
||||
/** @gt: gt which this LRC belongs to */
|
||||
|
||||
@@ -82,7 +82,7 @@ struct xe_migrate {
|
||||
* of the instruction. Subtracting the instruction header (1 dword) and
|
||||
* address (2 dwords), that leaves 0x3FD dwords (0x1FE qwords) for PTE values.
|
||||
*/
|
||||
#define MAX_PTE_PER_SDI 0x1FE
|
||||
#define MAX_PTE_PER_SDI 0x1FEU
|
||||
|
||||
/**
|
||||
* xe_tile_migrate_exec_queue() - Get this tile's migrate exec queue.
|
||||
@@ -203,7 +203,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
BUILD_BUG_ON(!(NUM_KERNEL_PDE & 1));
|
||||
|
||||
/* Need to be sure everything fits in the first PT, or create more */
|
||||
xe_tile_assert(tile, m->batch_base_ofs + batch->size < SZ_2M);
|
||||
xe_tile_assert(tile, m->batch_base_ofs + xe_bo_size(batch) < SZ_2M);
|
||||
|
||||
bo = xe_bo_create_pin_map(vm->xe, tile, vm,
|
||||
num_entries * XE_PAGE_SIZE,
|
||||
@@ -214,7 +214,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
return PTR_ERR(bo);
|
||||
|
||||
/* PT30 & PT31 reserved for 2M identity map */
|
||||
pt29_ofs = bo->size - 3 * XE_PAGE_SIZE;
|
||||
pt29_ofs = xe_bo_size(bo) - 3 * XE_PAGE_SIZE;
|
||||
entry = vm->pt_ops->pde_encode_bo(bo, pt29_ofs, pat_index);
|
||||
xe_pt_write(xe, &vm->pt_root[id]->bo->vmap, 0, entry);
|
||||
|
||||
@@ -236,7 +236,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
if (!IS_DGFX(xe)) {
|
||||
/* Write out batch too */
|
||||
m->batch_base_ofs = NUM_PT_SLOTS * XE_PAGE_SIZE;
|
||||
for (i = 0; i < batch->size;
|
||||
for (i = 0; i < xe_bo_size(batch);
|
||||
i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE :
|
||||
XE_PAGE_SIZE) {
|
||||
entry = vm->pt_ops->pte_encode_bo(batch, i,
|
||||
@@ -247,13 +247,13 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
level++;
|
||||
}
|
||||
if (xe->info.has_usm) {
|
||||
xe_tile_assert(tile, batch->size == SZ_1M);
|
||||
xe_tile_assert(tile, xe_bo_size(batch) == SZ_1M);
|
||||
|
||||
batch = tile->primary_gt->usm.bb_pool->bo;
|
||||
m->usm_batch_base_ofs = m->batch_base_ofs + SZ_1M;
|
||||
xe_tile_assert(tile, batch->size == SZ_512K);
|
||||
xe_tile_assert(tile, xe_bo_size(batch) == SZ_512K);
|
||||
|
||||
for (i = 0; i < batch->size;
|
||||
for (i = 0; i < xe_bo_size(batch);
|
||||
i += vm->flags & XE_VM_FLAG_64K ? XE_64K_PAGE_SIZE :
|
||||
XE_PAGE_SIZE) {
|
||||
entry = vm->pt_ops->pte_encode_bo(batch, i,
|
||||
@@ -306,7 +306,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
|
||||
/* Identity map the entire vram at 256GiB offset */
|
||||
if (IS_DGFX(xe)) {
|
||||
u64 pt30_ofs = bo->size - 2 * XE_PAGE_SIZE;
|
||||
u64 pt30_ofs = xe_bo_size(bo) - 2 * XE_PAGE_SIZE;
|
||||
|
||||
xe_migrate_program_identity(xe, vm, bo, map_ofs, IDENTITY_OFFSET,
|
||||
pat_index, pt30_ofs);
|
||||
@@ -321,7 +321,7 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
|
||||
u16 comp_pat_index = xe->pat.idx[XE_CACHE_NONE_COMPRESSION];
|
||||
u64 vram_offset = IDENTITY_OFFSET +
|
||||
DIV_ROUND_UP_ULL(xe->mem.vram.actual_physical_size, SZ_1G);
|
||||
u64 pt31_ofs = bo->size - XE_PAGE_SIZE;
|
||||
u64 pt31_ofs = xe_bo_size(bo) - XE_PAGE_SIZE;
|
||||
|
||||
xe_assert(xe, xe->mem.vram.actual_physical_size <= (MAX_NUM_PTE -
|
||||
IDENTITY_OFFSET - IDENTITY_OFFSET / 2) * SZ_1G);
|
||||
@@ -768,7 +768,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
|
||||
struct xe_gt *gt = m->tile->primary_gt;
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
struct dma_fence *fence = NULL;
|
||||
u64 size = src_bo->size;
|
||||
u64 size = xe_bo_size(src_bo);
|
||||
struct xe_res_cursor src_it, dst_it, ccs_it;
|
||||
u64 src_L0_ofs, dst_L0_ofs;
|
||||
u32 src_L0_pt, dst_L0_pt;
|
||||
@@ -791,7 +791,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
|
||||
if (XE_WARN_ON(copy_ccs && src_bo != dst_bo))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (src_bo != dst_bo && XE_WARN_ON(src_bo->size != dst_bo->size))
|
||||
if (src_bo != dst_bo && XE_WARN_ON(xe_bo_size(src_bo) != xe_bo_size(dst_bo)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!src_is_vram)
|
||||
@@ -863,7 +863,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
|
||||
if (src_is_vram && xe_migrate_allow_identity(src_L0, &src_it))
|
||||
xe_res_next(&src_it, src_L0);
|
||||
else
|
||||
emit_pte(m, bb, src_L0_pt, src_is_vram, copy_system_ccs,
|
||||
emit_pte(m, bb, src_L0_pt, src_is_vram, copy_system_ccs || use_comp_pat,
|
||||
&src_it, src_L0, src);
|
||||
|
||||
if (dst_is_vram && xe_migrate_allow_identity(src_L0, &dst_it))
|
||||
@@ -1064,7 +1064,7 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
|
||||
struct xe_device *xe = gt_to_xe(gt);
|
||||
bool clear_only_system_ccs = false;
|
||||
struct dma_fence *fence = NULL;
|
||||
u64 size = bo->size;
|
||||
u64 size = xe_bo_size(bo);
|
||||
struct xe_res_cursor src_it;
|
||||
struct ttm_resource *src = dst;
|
||||
int err;
|
||||
@@ -1076,9 +1076,9 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
|
||||
clear_only_system_ccs = true;
|
||||
|
||||
if (!clear_vram)
|
||||
xe_res_first_sg(xe_bo_sg(bo), 0, bo->size, &src_it);
|
||||
xe_res_first_sg(xe_bo_sg(bo), 0, xe_bo_size(bo), &src_it);
|
||||
else
|
||||
xe_res_first(src, 0, bo->size, &src_it);
|
||||
xe_res_first(src, 0, xe_bo_size(bo), &src_it);
|
||||
|
||||
while (size) {
|
||||
u64 clear_L0_ofs;
|
||||
@@ -1407,7 +1407,7 @@ __xe_migrate_update_pgtables(struct xe_migrate *m,
|
||||
if (idx == chunk)
|
||||
goto next_cmd;
|
||||
|
||||
xe_tile_assert(tile, pt_bo->size == SZ_4K);
|
||||
xe_tile_assert(tile, xe_bo_size(pt_bo) == SZ_4K);
|
||||
|
||||
/* Map a PT at most once */
|
||||
if (pt_bo->update_index < 0)
|
||||
@@ -1553,15 +1553,17 @@ static u32 pte_update_cmd_size(u64 size)
|
||||
u64 entries = DIV_U64_ROUND_UP(size, XE_PAGE_SIZE);
|
||||
|
||||
XE_WARN_ON(size > MAX_PREEMPTDISABLE_TRANSFER);
|
||||
|
||||
/*
|
||||
* MI_STORE_DATA_IMM command is used to update page table. Each
|
||||
* instruction can update maximumly 0x1ff pte entries. To update
|
||||
* n (n <= 0x1ff) pte entries, we need:
|
||||
* 1 dword for the MI_STORE_DATA_IMM command header (opcode etc)
|
||||
* 2 dword for the page table's physical location
|
||||
* 2*n dword for value of pte to fill (each pte entry is 2 dwords)
|
||||
* instruction can update maximumly MAX_PTE_PER_SDI pte entries. To
|
||||
* update n (n <= MAX_PTE_PER_SDI) pte entries, we need:
|
||||
*
|
||||
* - 1 dword for the MI_STORE_DATA_IMM command header (opcode etc)
|
||||
* - 2 dword for the page table's physical location
|
||||
* - 2*n dword for value of pte to fill (each pte entry is 2 dwords)
|
||||
*/
|
||||
num_dword = (1 + 2) * DIV_U64_ROUND_UP(entries, 0x1ff);
|
||||
num_dword = (1 + 2) * DIV_U64_ROUND_UP(entries, MAX_PTE_PER_SDI);
|
||||
num_dword += entries * 2;
|
||||
|
||||
return num_dword;
|
||||
@@ -1577,7 +1579,7 @@ static void build_pt_update_batch_sram(struct xe_migrate *m,
|
||||
|
||||
ptes = DIV_ROUND_UP(size, XE_PAGE_SIZE);
|
||||
while (ptes) {
|
||||
u32 chunk = min(0x1ffU, ptes);
|
||||
u32 chunk = min(MAX_PTE_PER_SDI, ptes);
|
||||
|
||||
bb->cs[bb->len++] = MI_STORE_DATA_IMM | MI_SDI_NUM_QW(chunk);
|
||||
bb->cs[bb->len++] = pt_offset;
|
||||
@@ -1866,7 +1868,7 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
|
||||
if (IS_ERR(dma_addr))
|
||||
return PTR_ERR(dma_addr);
|
||||
|
||||
xe_res_first(bo->ttm.resource, offset, bo->size - offset, &cursor);
|
||||
xe_res_first(bo->ttm.resource, offset, xe_bo_size(bo) - offset, &cursor);
|
||||
|
||||
do {
|
||||
struct dma_fence *__fence;
|
||||
|
||||
@@ -55,6 +55,7 @@ static void tiles_fini(void *arg)
|
||||
static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
|
||||
{
|
||||
struct xe_tile *tile;
|
||||
struct xe_gt *gt;
|
||||
u8 id;
|
||||
|
||||
/*
|
||||
@@ -67,7 +68,7 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
|
||||
/* Possibly override number of tile based on configuration register */
|
||||
if (!xe->info.skip_mtcfg) {
|
||||
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
|
||||
u8 tile_count;
|
||||
u8 tile_count, gt_count;
|
||||
u32 mtcfg;
|
||||
|
||||
/*
|
||||
@@ -84,12 +85,15 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size)
|
||||
xe->info.tile_count = tile_count;
|
||||
|
||||
/*
|
||||
* FIXME: Needs some work for standalone media, but
|
||||
* should be impossible with multi-tile for now:
|
||||
* multi-tile platform with standalone media doesn't
|
||||
* exist
|
||||
* We've already setup gt_count according to the full
|
||||
* tile count. Re-calculate it to only include the GTs
|
||||
* that belong to the remaining tile(s).
|
||||
*/
|
||||
xe->info.gt_count = xe->info.tile_count;
|
||||
gt_count = 0;
|
||||
for_each_gt(gt, xe, id)
|
||||
if (gt->info.id < tile_count * xe->info.max_gt_per_tile)
|
||||
gt_count++;
|
||||
xe->info.gt_count = gt_count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/intel_dg_nvm_aux.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "xe_device.h"
|
||||
#include "xe_device_types.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_nvm.h"
|
||||
#include "regs/xe_gsc_regs.h"
|
||||
#include "xe_sriov.h"
|
||||
|
||||
#define GEN12_GUNIT_NVM_BASE 0x00102040
|
||||
#define GEN12_DEBUG_NVM_BASE 0x00101018
|
||||
|
||||
#define GEN12_CNTL_PROTECTED_NVM_REG 0x0010100C
|
||||
|
||||
#define GEN12_GUNIT_NVM_SIZE 0x80
|
||||
#define GEN12_DEBUG_NVM_SIZE 0x4
|
||||
|
||||
#define NVM_NON_POSTED_ERASE_CHICKEN_BIT BIT(13)
|
||||
|
||||
#define HECI_FW_STATUS_2_NVM_ACCESS_MODE BIT(3)
|
||||
|
||||
static const struct intel_dg_nvm_region regions[INTEL_DG_NVM_REGIONS] = {
|
||||
[0] = { .name = "DESCRIPTOR", },
|
||||
[2] = { .name = "GSC", },
|
||||
[9] = { .name = "PADDING", },
|
||||
[11] = { .name = "OptionROM", },
|
||||
[12] = { .name = "DAM", },
|
||||
};
|
||||
|
||||
static void xe_nvm_release_dev(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static bool xe_nvm_non_posted_erase(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt = xe_root_mmio_gt(xe);
|
||||
|
||||
if (xe->info.platform != XE_BATTLEMAGE)
|
||||
return false;
|
||||
return !(xe_mmio_read32(>->mmio, XE_REG(GEN12_CNTL_PROTECTED_NVM_REG)) &
|
||||
NVM_NON_POSTED_ERASE_CHICKEN_BIT);
|
||||
}
|
||||
|
||||
static bool xe_nvm_writable_override(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt = xe_root_mmio_gt(xe);
|
||||
bool writable_override;
|
||||
resource_size_t base;
|
||||
|
||||
switch (xe->info.platform) {
|
||||
case XE_BATTLEMAGE:
|
||||
base = DG2_GSC_HECI2_BASE;
|
||||
break;
|
||||
case XE_PVC:
|
||||
base = PVC_GSC_HECI2_BASE;
|
||||
break;
|
||||
case XE_DG2:
|
||||
base = DG2_GSC_HECI2_BASE;
|
||||
break;
|
||||
case XE_DG1:
|
||||
base = DG1_GSC_HECI2_BASE;
|
||||
break;
|
||||
default:
|
||||
drm_err(&xe->drm, "Unknown platform\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
writable_override =
|
||||
!(xe_mmio_read32(>->mmio, HECI_FWSTS2(base)) &
|
||||
HECI_FW_STATUS_2_NVM_ACCESS_MODE);
|
||||
if (writable_override)
|
||||
drm_info(&xe->drm, "NVM access overridden by jumper\n");
|
||||
return writable_override;
|
||||
}
|
||||
|
||||
int xe_nvm_init(struct xe_device *xe)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
|
||||
struct auxiliary_device *aux_dev;
|
||||
struct intel_dg_nvm_dev *nvm;
|
||||
int ret;
|
||||
|
||||
if (!xe->info.has_gsc_nvm)
|
||||
return 0;
|
||||
|
||||
/* No access to internal NVM from VFs */
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return 0;
|
||||
|
||||
/* Nvm pointer should be NULL here */
|
||||
if (WARN_ON(xe->nvm))
|
||||
return -EFAULT;
|
||||
|
||||
xe->nvm = kzalloc(sizeof(*nvm), GFP_KERNEL);
|
||||
if (!xe->nvm)
|
||||
return -ENOMEM;
|
||||
|
||||
nvm = xe->nvm;
|
||||
|
||||
nvm->writable_override = xe_nvm_writable_override(xe);
|
||||
nvm->non_posted_erase = xe_nvm_non_posted_erase(xe);
|
||||
nvm->bar.parent = &pdev->resource[0];
|
||||
nvm->bar.start = GEN12_GUNIT_NVM_BASE + pdev->resource[0].start;
|
||||
nvm->bar.end = nvm->bar.start + GEN12_GUNIT_NVM_SIZE - 1;
|
||||
nvm->bar.flags = IORESOURCE_MEM;
|
||||
nvm->bar.desc = IORES_DESC_NONE;
|
||||
nvm->regions = regions;
|
||||
|
||||
nvm->bar2.parent = &pdev->resource[0];
|
||||
nvm->bar2.start = GEN12_DEBUG_NVM_BASE + pdev->resource[0].start;
|
||||
nvm->bar2.end = nvm->bar2.start + GEN12_DEBUG_NVM_SIZE - 1;
|
||||
nvm->bar2.flags = IORESOURCE_MEM;
|
||||
nvm->bar2.desc = IORES_DESC_NONE;
|
||||
|
||||
aux_dev = &nvm->aux_dev;
|
||||
|
||||
aux_dev->name = "nvm";
|
||||
aux_dev->id = (pci_domain_nr(pdev->bus) << 16) | pci_dev_id(pdev);
|
||||
aux_dev->dev.parent = &pdev->dev;
|
||||
aux_dev->dev.release = xe_nvm_release_dev;
|
||||
|
||||
ret = auxiliary_device_init(aux_dev);
|
||||
if (ret) {
|
||||
drm_err(&xe->drm, "xe-nvm aux init failed %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = auxiliary_device_add(aux_dev);
|
||||
if (ret) {
|
||||
drm_err(&xe->drm, "xe-nvm aux add failed %d\n", ret);
|
||||
auxiliary_device_uninit(aux_dev);
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(nvm);
|
||||
xe->nvm = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void xe_nvm_fini(struct xe_device *xe)
|
||||
{
|
||||
struct intel_dg_nvm_dev *nvm = xe->nvm;
|
||||
|
||||
if (!xe->info.has_gsc_nvm)
|
||||
return;
|
||||
|
||||
/* No access to internal NVM from VFs */
|
||||
if (IS_SRIOV_VF(xe))
|
||||
return;
|
||||
|
||||
/* Nvm pointer should not be NULL here */
|
||||
if (WARN_ON(!nvm))
|
||||
return;
|
||||
|
||||
auxiliary_device_delete(&nvm->aux_dev);
|
||||
auxiliary_device_uninit(&nvm->aux_dev);
|
||||
kfree(nvm);
|
||||
xe->nvm = NULL;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright(c) 2019-2025 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __XE_NVM_H__
|
||||
#define __XE_NVM_H__
|
||||
|
||||
struct xe_device;
|
||||
|
||||
int xe_nvm_init(struct xe_device *xe);
|
||||
|
||||
void xe_nvm_fini(struct xe_device *xe);
|
||||
|
||||
#endif
|
||||
@@ -403,7 +403,7 @@ static int xe_oa_append_reports(struct xe_oa_stream *stream, char __user *buf,
|
||||
static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
|
||||
{
|
||||
u32 gtt_offset = xe_bo_ggtt_addr(stream->oa_buffer.bo);
|
||||
int size_exponent = __ffs(stream->oa_buffer.bo->size);
|
||||
int size_exponent = __ffs(xe_bo_size(stream->oa_buffer.bo));
|
||||
u32 oa_buf = gtt_offset | OAG_OABUFFER_MEMORY_SELECT;
|
||||
struct xe_mmio *mmio = &stream->gt->mmio;
|
||||
unsigned long flags;
|
||||
@@ -435,7 +435,7 @@ static void xe_oa_init_oa_buffer(struct xe_oa_stream *stream)
|
||||
spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags);
|
||||
|
||||
/* Zero out the OA buffer since we rely on zero report id and timestamp fields */
|
||||
memset(stream->oa_buffer.vaddr, 0, stream->oa_buffer.bo->size);
|
||||
memset(stream->oa_buffer.vaddr, 0, xe_bo_size(stream->oa_buffer.bo));
|
||||
}
|
||||
|
||||
static u32 __format_to_oactrl(const struct xe_oa_format *format, int counter_sel_mask)
|
||||
@@ -1065,7 +1065,7 @@ static u32 oag_report_ctx_switches(const struct xe_oa_stream *stream)
|
||||
static u32 oag_buf_size_select(const struct xe_oa_stream *stream)
|
||||
{
|
||||
return _MASKED_FIELD(OAG_OA_DEBUG_BUF_SIZE_SELECT,
|
||||
stream->oa_buffer.bo->size > SZ_16M ?
|
||||
xe_bo_size(stream->oa_buffer.bo) > SZ_16M ?
|
||||
OAG_OA_DEBUG_BUF_SIZE_SELECT : 0);
|
||||
}
|
||||
|
||||
@@ -1582,7 +1582,7 @@ static long xe_oa_status_locked(struct xe_oa_stream *stream, unsigned long arg)
|
||||
|
||||
static long xe_oa_info_locked(struct xe_oa_stream *stream, unsigned long arg)
|
||||
{
|
||||
struct drm_xe_oa_stream_info info = { .oa_buf_size = stream->oa_buffer.bo->size, };
|
||||
struct drm_xe_oa_stream_info info = { .oa_buf_size = xe_bo_size(stream->oa_buffer.bo), };
|
||||
void __user *uaddr = (void __user *)arg;
|
||||
|
||||
if (copy_to_user(uaddr, &info, sizeof(info)))
|
||||
@@ -1668,7 +1668,7 @@ static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
}
|
||||
|
||||
/* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */
|
||||
if (vma->vm_end - vma->vm_start != stream->oa_buffer.bo->size) {
|
||||
if (vma->vm_end - vma->vm_start != xe_bo_size(stream->oa_buffer.bo)) {
|
||||
drm_dbg(&stream->oa->xe->drm, "Wrong mmap size, must be OA buffer size\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+28
-49
@@ -38,43 +38,6 @@ enum toggle_d3cold {
|
||||
D3COLD_ENABLE,
|
||||
};
|
||||
|
||||
struct xe_subplatform_desc {
|
||||
enum xe_subplatform subplatform;
|
||||
const char *name;
|
||||
const u16 *pciidlist;
|
||||
};
|
||||
|
||||
struct xe_device_desc {
|
||||
/* Should only ever be set for platforms without GMD_ID */
|
||||
const struct xe_ip *pre_gmdid_graphics_ip;
|
||||
/* Should only ever be set for platforms without GMD_ID */
|
||||
const struct xe_ip *pre_gmdid_media_ip;
|
||||
|
||||
const char *platform_name;
|
||||
const struct xe_subplatform_desc *subplatforms;
|
||||
|
||||
enum xe_platform platform;
|
||||
|
||||
u8 dma_mask_size;
|
||||
u8 max_remote_tiles:2;
|
||||
|
||||
u8 require_force_probe:1;
|
||||
u8 is_dgfx:1;
|
||||
|
||||
u8 has_display:1;
|
||||
u8 has_fan_control:1;
|
||||
u8 has_heci_gscfi:1;
|
||||
u8 has_heci_cscfi:1;
|
||||
u8 has_llc:1;
|
||||
u8 has_mbx_power_limits:1;
|
||||
u8 has_pxp:1;
|
||||
u8 has_sriov:1;
|
||||
u8 needs_scratch:1;
|
||||
u8 skip_guc_pc:1;
|
||||
u8 skip_mtcfg:1;
|
||||
u8 skip_pcode:1;
|
||||
};
|
||||
|
||||
__diag_push();
|
||||
__diag_ignore_all("-Woverride-init", "Allow field overrides in table");
|
||||
|
||||
@@ -140,7 +103,6 @@ static const struct xe_graphics_desc graphics_xelpg = {
|
||||
.has_asid = 1, \
|
||||
.has_atomic_enable_pte_bit = 1, \
|
||||
.has_flat_ccs = 1, \
|
||||
.has_indirect_ring_state = 1, \
|
||||
.has_range_tlb_invalidation = 1, \
|
||||
.has_usm = 1, \
|
||||
.has_64bit_timestamp = 1, \
|
||||
@@ -184,6 +146,7 @@ static const struct xe_ip graphics_ips[] = {
|
||||
{ 2004, "Xe2_LPG", &graphics_xe2 },
|
||||
{ 3000, "Xe3_LPG", &graphics_xe2 },
|
||||
{ 3001, "Xe3_LPG", &graphics_xe2 },
|
||||
{ 3003, "Xe3_LPG", &graphics_xe2 },
|
||||
};
|
||||
|
||||
/* Pre-GMDID Media IPs */
|
||||
@@ -196,6 +159,7 @@ static const struct xe_ip media_ips[] = {
|
||||
{ 1301, "Xe2_HPM", &media_xelpmp },
|
||||
{ 2000, "Xe2_LPM", &media_xelpmp },
|
||||
{ 3000, "Xe3_LPM", &media_xelpmp },
|
||||
{ 3002, "Xe3_LPM", &media_xelpmp },
|
||||
};
|
||||
|
||||
static const struct xe_device_desc tgl_desc = {
|
||||
@@ -205,6 +169,7 @@ static const struct xe_device_desc tgl_desc = {
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_llc = true,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
};
|
||||
|
||||
@@ -215,6 +180,7 @@ static const struct xe_device_desc rkl_desc = {
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_llc = true,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
};
|
||||
|
||||
@@ -227,6 +193,7 @@ static const struct xe_device_desc adl_s_desc = {
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_llc = true,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
.subplatforms = (const struct xe_subplatform_desc[]) {
|
||||
{ XE_SUBPLATFORM_ALDERLAKE_S_RPLS, "RPLS", adls_rpls_ids },
|
||||
@@ -243,6 +210,7 @@ static const struct xe_device_desc adl_p_desc = {
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_llc = true,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
.subplatforms = (const struct xe_subplatform_desc[]) {
|
||||
{ XE_SUBPLATFORM_ALDERLAKE_P_RPLU, "RPLU", adlp_rplu_ids },
|
||||
@@ -257,6 +225,7 @@ static const struct xe_device_desc adl_n_desc = {
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_llc = true,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
};
|
||||
|
||||
@@ -270,7 +239,9 @@ static const struct xe_device_desc dg1_desc = {
|
||||
PLATFORM(DG1),
|
||||
.dma_mask_size = 39,
|
||||
.has_display = true,
|
||||
.has_gsc_nvm = 1,
|
||||
.has_heci_gscfi = 1,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
};
|
||||
|
||||
@@ -281,6 +252,7 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
|
||||
#define DG2_FEATURES \
|
||||
DGFX_FEATURES, \
|
||||
PLATFORM(DG2), \
|
||||
.has_gsc_nvm = 1, \
|
||||
.has_heci_gscfi = 1, \
|
||||
.subplatforms = (const struct xe_subplatform_desc[]) { \
|
||||
{ XE_SUBPLATFORM_DG2_G10, "G10", dg2_g10_ids }, \
|
||||
@@ -293,6 +265,7 @@ static const struct xe_device_desc ats_m_desc = {
|
||||
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
|
||||
.pre_gmdid_media_ip = &media_ip_xehpm,
|
||||
.dma_mask_size = 46,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
|
||||
DG2_FEATURES,
|
||||
@@ -303,6 +276,7 @@ static const struct xe_device_desc dg2_desc = {
|
||||
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
|
||||
.pre_gmdid_media_ip = &media_ip_xehpm,
|
||||
.dma_mask_size = 46,
|
||||
.max_gt_per_tile = 1,
|
||||
.require_force_probe = true,
|
||||
|
||||
DG2_FEATURES,
|
||||
@@ -317,7 +291,9 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
|
||||
PLATFORM(PVC),
|
||||
.dma_mask_size = 52,
|
||||
.has_display = false,
|
||||
.has_gsc_nvm = 1,
|
||||
.has_heci_gscfi = 1,
|
||||
.max_gt_per_tile = 1,
|
||||
.max_remote_tiles = 1,
|
||||
.require_force_probe = true,
|
||||
.has_mbx_power_limits = false,
|
||||
@@ -330,6 +306,7 @@ static const struct xe_device_desc mtl_desc = {
|
||||
.dma_mask_size = 46,
|
||||
.has_display = true,
|
||||
.has_pxp = true,
|
||||
.max_gt_per_tile = 2,
|
||||
};
|
||||
|
||||
static const struct xe_device_desc lnl_desc = {
|
||||
@@ -337,6 +314,7 @@ static const struct xe_device_desc lnl_desc = {
|
||||
.dma_mask_size = 46,
|
||||
.has_display = true,
|
||||
.has_pxp = true,
|
||||
.max_gt_per_tile = 2,
|
||||
.needs_scratch = true,
|
||||
};
|
||||
|
||||
@@ -347,7 +325,9 @@ static const struct xe_device_desc bmg_desc = {
|
||||
.has_display = true,
|
||||
.has_fan_control = true,
|
||||
.has_mbx_power_limits = true,
|
||||
.has_gsc_nvm = 1,
|
||||
.has_heci_cscfi = 1,
|
||||
.max_gt_per_tile = 2,
|
||||
.needs_scratch = true,
|
||||
};
|
||||
|
||||
@@ -356,7 +336,7 @@ static const struct xe_device_desc ptl_desc = {
|
||||
.dma_mask_size = 46,
|
||||
.has_display = true,
|
||||
.has_sriov = true,
|
||||
.require_force_probe = true,
|
||||
.max_gt_per_tile = 2,
|
||||
.needs_scratch = true,
|
||||
};
|
||||
|
||||
@@ -590,6 +570,7 @@ static int xe_info_init_early(struct xe_device *xe,
|
||||
xe->info.is_dgfx = desc->is_dgfx;
|
||||
xe->info.has_fan_control = desc->has_fan_control;
|
||||
xe->info.has_mbx_power_limits = desc->has_mbx_power_limits;
|
||||
xe->info.has_gsc_nvm = desc->has_gsc_nvm;
|
||||
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
|
||||
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
|
||||
xe->info.has_llc = desc->has_llc;
|
||||
@@ -603,6 +584,10 @@ static int xe_info_init_early(struct xe_device *xe,
|
||||
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
|
||||
xe_modparam.probe_display &&
|
||||
desc->has_display;
|
||||
|
||||
xe_assert(xe, desc->max_gt_per_tile > 0);
|
||||
xe_assert(xe, desc->max_gt_per_tile <= XE_MAX_GT_PER_TILE);
|
||||
xe->info.max_gt_per_tile = desc->max_gt_per_tile;
|
||||
xe->info.tile_count = 1 + desc->max_remote_tiles;
|
||||
|
||||
err = xe_tile_init_early(xe_device_get_root_tile(xe), xe, 0);
|
||||
@@ -702,10 +687,11 @@ static int xe_info_init(struct xe_device *xe,
|
||||
*/
|
||||
for_each_tile(tile, xe, id) {
|
||||
gt = tile->primary_gt;
|
||||
gt->info.id = xe->info.gt_count++;
|
||||
gt->info.type = XE_GT_TYPE_MAIN;
|
||||
gt->info.id = tile->id * xe->info.max_gt_per_tile;
|
||||
gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
|
||||
gt->info.engine_mask = graphics_desc->hw_engine_mask;
|
||||
xe->info.gt_count++;
|
||||
|
||||
if (MEDIA_VER(xe) < 13 && media_desc)
|
||||
gt->info.engine_mask |= media_desc->hw_engine_mask;
|
||||
@@ -723,17 +709,10 @@ static int xe_info_init(struct xe_device *xe,
|
||||
|
||||
gt = tile->media_gt;
|
||||
gt->info.type = XE_GT_TYPE_MEDIA;
|
||||
gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
|
||||
gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
|
||||
gt->info.engine_mask = media_desc->hw_engine_mask;
|
||||
|
||||
/*
|
||||
* FIXME: At the moment multi-tile and standalone media are
|
||||
* mutually exclusive on current platforms. We'll need to
|
||||
* come up with a better way to number GTs if we ever wind
|
||||
* up with platforms that support both together.
|
||||
*/
|
||||
drm_WARN_ON(&xe->drm, id != 0);
|
||||
gt->info.id = xe->info.gt_count++;
|
||||
xe->info.gt_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -8,6 +8,47 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "xe_platform_types.h"
|
||||
|
||||
struct xe_subplatform_desc {
|
||||
enum xe_subplatform subplatform;
|
||||
const char *name;
|
||||
const u16 *pciidlist;
|
||||
};
|
||||
|
||||
struct xe_device_desc {
|
||||
/* Should only ever be set for platforms without GMD_ID */
|
||||
const struct xe_ip *pre_gmdid_graphics_ip;
|
||||
/* Should only ever be set for platforms without GMD_ID */
|
||||
const struct xe_ip *pre_gmdid_media_ip;
|
||||
|
||||
const char *platform_name;
|
||||
const struct xe_subplatform_desc *subplatforms;
|
||||
|
||||
enum xe_platform platform;
|
||||
|
||||
u8 dma_mask_size;
|
||||
u8 max_remote_tiles:2;
|
||||
u8 max_gt_per_tile:2;
|
||||
|
||||
u8 require_force_probe:1;
|
||||
u8 is_dgfx:1;
|
||||
|
||||
u8 has_display:1;
|
||||
u8 has_fan_control:1;
|
||||
u8 has_gsc_nvm:1;
|
||||
u8 has_heci_gscfi:1;
|
||||
u8 has_heci_cscfi:1;
|
||||
u8 has_llc:1;
|
||||
u8 has_mbx_power_limits:1;
|
||||
u8 has_pxp:1;
|
||||
u8 has_sriov:1;
|
||||
u8 needs_scratch:1;
|
||||
u8 skip_guc_pc:1;
|
||||
u8 skip_mtcfg:1;
|
||||
u8 skip_pcode:1;
|
||||
};
|
||||
|
||||
struct xe_graphics_desc {
|
||||
u8 va_bits;
|
||||
u8 vm_max_level;
|
||||
|
||||
@@ -50,6 +50,21 @@
|
||||
#define READ_PL_FROM_FW 0x1
|
||||
#define READ_PL_FROM_PCODE 0x0
|
||||
|
||||
#define PCODE_LATE_BINDING 0x5C
|
||||
#define GET_CAPABILITY_STATUS 0x0
|
||||
#define V1_FAN_SUPPORTED REG_BIT(0)
|
||||
#define VR_PARAMS_SUPPORTED REG_BIT(3)
|
||||
#define V1_FAN_PROVISIONED REG_BIT(16)
|
||||
#define VR_PARAMS_PROVISIONED REG_BIT(19)
|
||||
#define GET_VERSION_LOW 0x1
|
||||
#define GET_VERSION_HIGH 0x2
|
||||
#define MAJOR_VERSION_MASK REG_GENMASK(31, 16)
|
||||
#define MINOR_VERSION_MASK REG_GENMASK(15, 0)
|
||||
#define HOTFIX_VERSION_MASK REG_GENMASK(31, 16)
|
||||
#define BUILD_VERSION_MASK REG_GENMASK(15, 0)
|
||||
#define FAN_TABLE 1
|
||||
#define VR_CONFIG 2
|
||||
|
||||
#define PCODE_FREQUENCY_CONFIG 0x6e
|
||||
/* Frequency Config Sub Commands (param1) */
|
||||
#define PCODE_MBOX_FC_SC_READ_FUSED_P0 0x0
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "xe_ggtt.h"
|
||||
#include "xe_gt.h"
|
||||
#include "xe_guc.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_irq.h"
|
||||
#include "xe_pcode.h"
|
||||
#include "xe_pxp.h"
|
||||
@@ -134,7 +135,7 @@ int xe_pm_suspend(struct xe_device *xe)
|
||||
/* FIXME: Super racey... */
|
||||
err = xe_bo_evict_all(xe);
|
||||
if (err)
|
||||
goto err_pxp;
|
||||
goto err_display;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
err = xe_gt_suspend(gt);
|
||||
@@ -146,12 +147,13 @@ int xe_pm_suspend(struct xe_device *xe)
|
||||
|
||||
xe_display_pm_suspend_late(xe);
|
||||
|
||||
xe_i2c_pm_suspend(xe);
|
||||
|
||||
drm_dbg(&xe->drm, "Device suspended\n");
|
||||
return 0;
|
||||
|
||||
err_display:
|
||||
xe_display_pm_resume(xe);
|
||||
err_pxp:
|
||||
xe_pxp_pm_resume(xe->pxp);
|
||||
err:
|
||||
drm_dbg(&xe->drm, "Device suspend failed %d\n", err);
|
||||
@@ -191,6 +193,8 @@ int xe_pm_resume(struct xe_device *xe)
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
xe_i2c_pm_resume(xe, xe->d3cold.allowed);
|
||||
|
||||
xe_irq_resume(xe);
|
||||
|
||||
for_each_gt(gt, xe, id)
|
||||
@@ -488,6 +492,8 @@ int xe_pm_runtime_suspend(struct xe_device *xe)
|
||||
|
||||
xe_display_pm_runtime_suspend_late(xe);
|
||||
|
||||
xe_i2c_pm_suspend(xe);
|
||||
|
||||
xe_rpm_lockmap_release(xe);
|
||||
xe_pm_write_callback_task(xe, NULL);
|
||||
return 0;
|
||||
@@ -535,6 +541,8 @@ int xe_pm_runtime_resume(struct xe_device *xe)
|
||||
goto out;
|
||||
}
|
||||
|
||||
xe_i2c_pm_resume(xe, xe->d3cold.allowed);
|
||||
|
||||
xe_irq_resume(xe);
|
||||
|
||||
for_each_gt(gt, xe, id)
|
||||
@@ -753,11 +761,13 @@ void xe_pm_assert_unbounded_bridge(struct xe_device *xe)
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_pm_set_vram_threshold - Set a vram threshold for allowing/blocking D3Cold
|
||||
* xe_pm_set_vram_threshold - Set a VRAM threshold for allowing/blocking D3Cold
|
||||
* @xe: xe device instance
|
||||
* @threshold: VRAM size in bites for the D3cold threshold
|
||||
* @threshold: VRAM size in MiB for the D3cold threshold
|
||||
*
|
||||
* Returns 0 for success, negative error code otherwise.
|
||||
* Return:
|
||||
* * 0 - success
|
||||
* * -EINVAL - invalid argument
|
||||
*/
|
||||
int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold)
|
||||
{
|
||||
|
||||
@@ -157,10 +157,13 @@ static bool event_gt_forcewake(struct perf_event *event)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool event_supported(struct xe_pmu *pmu, unsigned int gt,
|
||||
static bool event_supported(struct xe_pmu *pmu, unsigned int gt_id,
|
||||
unsigned int id)
|
||||
{
|
||||
if (gt >= XE_MAX_GT_PER_TILE)
|
||||
struct xe_device *xe = container_of(pmu, typeof(*xe), pmu);
|
||||
struct xe_gt *gt = xe_device_get_gt(xe, gt_id);
|
||||
|
||||
if (!gt)
|
||||
return false;
|
||||
|
||||
return id < sizeof(pmu->supported_events) * BITS_PER_BYTE &&
|
||||
|
||||
@@ -141,7 +141,7 @@ query_engine_cycles(struct xe_device *xe,
|
||||
return -EINVAL;
|
||||
|
||||
eci = &resp.eci;
|
||||
if (eci->gt_id >= XE_MAX_GT_PER_TILE)
|
||||
if (eci->gt_id >= xe->info.max_gt_per_tile)
|
||||
return -EINVAL;
|
||||
|
||||
gt = xe_device_get_gt(xe, eci->gt_id);
|
||||
@@ -368,6 +368,7 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
|
||||
struct drm_xe_query_gt_list __user *query_ptr =
|
||||
u64_to_user_ptr(query->data);
|
||||
struct drm_xe_query_gt_list *gt_list;
|
||||
int iter = 0;
|
||||
u8 id;
|
||||
|
||||
if (query->size == 0) {
|
||||
@@ -385,12 +386,12 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (xe_gt_is_media_type(gt))
|
||||
gt_list->gt_list[id].type = DRM_XE_QUERY_GT_TYPE_MEDIA;
|
||||
gt_list->gt_list[iter].type = DRM_XE_QUERY_GT_TYPE_MEDIA;
|
||||
else
|
||||
gt_list->gt_list[id].type = DRM_XE_QUERY_GT_TYPE_MAIN;
|
||||
gt_list->gt_list[id].tile_id = gt_to_tile(gt)->id;
|
||||
gt_list->gt_list[id].gt_id = gt->info.id;
|
||||
gt_list->gt_list[id].reference_clock = gt->info.reference_clock;
|
||||
gt_list->gt_list[iter].type = DRM_XE_QUERY_GT_TYPE_MAIN;
|
||||
gt_list->gt_list[iter].tile_id = gt_to_tile(gt)->id;
|
||||
gt_list->gt_list[iter].gt_id = gt->info.id;
|
||||
gt_list->gt_list[iter].reference_clock = gt->info.reference_clock;
|
||||
/*
|
||||
* The mem_regions indexes in the mask below need to
|
||||
* directly identify the struct
|
||||
@@ -406,19 +407,21 @@ static int query_gt_list(struct xe_device *xe, struct drm_xe_device_query *query
|
||||
* assumption.
|
||||
*/
|
||||
if (!IS_DGFX(xe))
|
||||
gt_list->gt_list[id].near_mem_regions = 0x1;
|
||||
gt_list->gt_list[iter].near_mem_regions = 0x1;
|
||||
else
|
||||
gt_list->gt_list[id].near_mem_regions =
|
||||
gt_list->gt_list[iter].near_mem_regions =
|
||||
BIT(gt_to_tile(gt)->id) << 1;
|
||||
gt_list->gt_list[id].far_mem_regions = xe->info.mem_region_mask ^
|
||||
gt_list->gt_list[id].near_mem_regions;
|
||||
gt_list->gt_list[iter].far_mem_regions = xe->info.mem_region_mask ^
|
||||
gt_list->gt_list[iter].near_mem_regions;
|
||||
|
||||
gt_list->gt_list[id].ip_ver_major =
|
||||
gt_list->gt_list[iter].ip_ver_major =
|
||||
REG_FIELD_GET(GMD_ID_ARCH_MASK, gt->info.gmdid);
|
||||
gt_list->gt_list[id].ip_ver_minor =
|
||||
gt_list->gt_list[iter].ip_ver_minor =
|
||||
REG_FIELD_GET(GMD_ID_RELEASE_MASK, gt->info.gmdid);
|
||||
gt_list->gt_list[id].ip_ver_rev =
|
||||
gt_list->gt_list[iter].ip_ver_rev =
|
||||
REG_FIELD_GET(GMD_ID_REVID, gt->info.gmdid);
|
||||
|
||||
iter++;
|
||||
}
|
||||
|
||||
if (copy_to_user(query_ptr, gt_list, size)) {
|
||||
|
||||
@@ -147,127 +147,113 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
|
||||
xe_sriov_info(xe, "migration not supported by this module version\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* vf_post_migration_requery_guc - Re-query GuC for current VF provisioning.
|
||||
static bool gt_vf_post_migration_needed(struct xe_gt *gt)
|
||||
{
|
||||
return test_bit(gt->info.id, >_to_xe(gt)->sriov.vf.migration.gt_flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify GuCs marked in flags about resource fixups apply finished.
|
||||
* @xe: the &xe_device struct instance
|
||||
* @gt_flags: flags marking to which GTs the notification shall be sent
|
||||
*/
|
||||
static int vf_post_migration_notify_resfix_done(struct xe_device *xe, unsigned long gt_flags)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
int err = 0;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (!test_bit(id, >_flags))
|
||||
continue;
|
||||
/* skip asking GuC for RESFIX exit if new recovery request arrived */
|
||||
if (gt_vf_post_migration_needed(gt))
|
||||
continue;
|
||||
err = xe_gt_sriov_vf_notify_resfix_done(gt);
|
||||
if (err)
|
||||
break;
|
||||
clear_bit(id, >_flags);
|
||||
}
|
||||
|
||||
if (gt_flags && !err)
|
||||
drm_dbg(&xe->drm, "another recovery imminent, skipped some notifications\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
static int vf_get_next_migrated_gt_id(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (test_and_clear_bit(id, &xe->sriov.vf.migration.gt_flags))
|
||||
return id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform post-migration fixups on a single GT.
|
||||
*
|
||||
* After migration, we need to re-query all VF configuration to make sure
|
||||
* they match previous provisioning. Note that most of VF provisioning
|
||||
* shall be the same, except GGTT range, since GGTT is not virtualized per-VF.
|
||||
* After migration, GuC needs to be re-queried for VF configuration to check
|
||||
* if it matches previous provisioning. Most of VF provisioning shall be the
|
||||
* same, except GGTT range, since GGTT is not virtualized per-VF. If GGTT
|
||||
* range has changed, we have to perform fixups - shift all GGTT references
|
||||
* used anywhere within the driver. After the fixups in this function succeed,
|
||||
* it is allowed to ask the GuC bound to this GT to continue normal operation.
|
||||
*
|
||||
* Returns: 0 if the operation completed successfully, or a negative error
|
||||
* code otherwise.
|
||||
*/
|
||||
static int vf_post_migration_requery_guc(struct xe_device *xe)
|
||||
static int gt_vf_post_migration_fixups(struct xe_gt *gt)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
int err, ret = 0;
|
||||
s64 shift;
|
||||
int err;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
err = xe_gt_sriov_vf_query_config(gt);
|
||||
ret = ret ?: err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vf_post_migration_fixup_ctb(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
|
||||
xe_assert(xe, IS_SRIOV_VF(xe));
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
s32 shift = xe_gt_sriov_vf_ggtt_shift(gt);
|
||||
err = xe_gt_sriov_vf_query_config(gt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
shift = xe_gt_sriov_vf_ggtt_shift(gt);
|
||||
if (shift) {
|
||||
xe_tile_sriov_vf_fixup_ggtt_nodes(gt_to_tile(gt), shift);
|
||||
/* FIXME: add the recovery steps */
|
||||
xe_guc_ct_fixup_messages_with_ggtt(>->uc.guc.ct, shift);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vf_post_migration_imminent - Check if post-restore recovery is coming.
|
||||
* @xe: the &xe_device struct instance
|
||||
*
|
||||
* Return: True if migration recovery worker will soon be running. Any worker currently
|
||||
* executing does not affect the result.
|
||||
*/
|
||||
static bool vf_post_migration_imminent(struct xe_device *xe)
|
||||
{
|
||||
return xe->sriov.vf.migration.gt_flags != 0 ||
|
||||
work_pending(&xe->sriov.vf.migration.worker);
|
||||
}
|
||||
|
||||
static bool vf_post_migration_fixup_ggtt_nodes(struct xe_device *xe)
|
||||
{
|
||||
bool need_fixups = false;
|
||||
struct xe_tile *tile;
|
||||
unsigned int id;
|
||||
|
||||
for_each_tile(tile, xe, id) {
|
||||
struct xe_gt *gt = tile->primary_gt;
|
||||
s64 shift;
|
||||
|
||||
shift = xe_gt_sriov_vf_ggtt_shift(gt);
|
||||
if (shift) {
|
||||
need_fixups = true;
|
||||
xe_tile_sriov_vf_fixup_ggtt_nodes(tile, shift);
|
||||
}
|
||||
}
|
||||
return need_fixups;
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify all GuCs about resource fixups apply finished.
|
||||
*/
|
||||
static void vf_post_migration_notify_resfix_done(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (vf_post_migration_imminent(xe))
|
||||
goto skip;
|
||||
xe_gt_sriov_vf_notify_resfix_done(gt);
|
||||
}
|
||||
return;
|
||||
|
||||
skip:
|
||||
drm_dbg(&xe->drm, "another recovery imminent, skipping notifications\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vf_post_migration_recovery(struct xe_device *xe)
|
||||
{
|
||||
bool need_fixups;
|
||||
int err;
|
||||
unsigned long fixed_gts = 0;
|
||||
int id, err;
|
||||
|
||||
drm_dbg(&xe->drm, "migration recovery in progress\n");
|
||||
xe_pm_runtime_get(xe);
|
||||
err = vf_post_migration_requery_guc(xe);
|
||||
if (vf_post_migration_imminent(xe))
|
||||
goto defer;
|
||||
if (unlikely(err))
|
||||
goto fail;
|
||||
|
||||
if (!vf_migration_supported(xe)) {
|
||||
xe_sriov_err(xe, "migration not supported by this module version\n");
|
||||
err = -ENOTRECOVERABLE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
need_fixups = vf_post_migration_fixup_ggtt_nodes(xe);
|
||||
/* FIXME: add the recovery steps */
|
||||
if (need_fixups)
|
||||
vf_post_migration_fixup_ctb(xe);
|
||||
while (id = vf_get_next_migrated_gt_id(xe), id >= 0) {
|
||||
struct xe_gt *gt = xe_device_get_gt(xe, id);
|
||||
|
||||
err = gt_vf_post_migration_fixups(gt);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
set_bit(id, &fixed_gts);
|
||||
}
|
||||
|
||||
err = vf_post_migration_notify_resfix_done(xe, fixed_gts);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
vf_post_migration_notify_resfix_done(xe);
|
||||
xe_pm_runtime_put(xe);
|
||||
drm_notice(&xe->drm, "migration recovery ended\n");
|
||||
return;
|
||||
defer:
|
||||
xe_pm_runtime_put(xe);
|
||||
drm_dbg(&xe->drm, "migration recovery deferred\n");
|
||||
return;
|
||||
fail:
|
||||
xe_pm_runtime_put(xe);
|
||||
drm_err(&xe->drm, "migration recovery failed (%pe)\n", ERR_PTR(err));
|
||||
@@ -282,18 +268,23 @@ static void migration_worker_func(struct work_struct *w)
|
||||
vf_post_migration_recovery(xe);
|
||||
}
|
||||
|
||||
static bool vf_ready_to_recovery_on_all_gts(struct xe_device *xe)
|
||||
/*
|
||||
* Check if post-restore recovery is coming on any of GTs.
|
||||
* @xe: the &xe_device struct instance
|
||||
*
|
||||
* Return: True if migration recovery worker will soon be running. Any worker currently
|
||||
* executing does not affect the result.
|
||||
*/
|
||||
static bool vf_ready_to_recovery_on_any_gts(struct xe_device *xe)
|
||||
{
|
||||
struct xe_gt *gt;
|
||||
unsigned int id;
|
||||
|
||||
for_each_gt(gt, xe, id) {
|
||||
if (!test_bit(id, &xe->sriov.vf.migration.gt_flags)) {
|
||||
xe_gt_sriov_dbg_verbose(gt, "still not ready to recover\n");
|
||||
return false;
|
||||
}
|
||||
if (test_bit(id, &xe->sriov.vf.migration.gt_flags))
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,13 +299,9 @@ void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
|
||||
|
||||
xe_assert(xe, IS_SRIOV_VF(xe));
|
||||
|
||||
if (!vf_ready_to_recovery_on_all_gts(xe))
|
||||
if (!vf_ready_to_recovery_on_any_gts(xe))
|
||||
return;
|
||||
|
||||
WRITE_ONCE(xe->sriov.vf.migration.gt_flags, 0);
|
||||
/* Ensure other threads see that no flags are set now. */
|
||||
smp_mb();
|
||||
|
||||
started = queue_work(xe->sriov.wq, &xe->sriov.vf.migration.worker);
|
||||
drm_info(&xe->drm, "VF migration recovery %s\n", started ?
|
||||
"scheduled" : "already in progress");
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "xe_step.h"
|
||||
|
||||
#include <kunit/visibility.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include "xe_device.h"
|
||||
@@ -255,3 +256,4 @@ const char *xe_step_name(enum xe_step step)
|
||||
return "**";
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_step_name);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "xe_device.h"
|
||||
#include "xe_gt.h"
|
||||
#include "xe_heci_gsc.h"
|
||||
#include "xe_i2c.h"
|
||||
#include "xe_mmio.h"
|
||||
#include "xe_pcode_api.h"
|
||||
#include "xe_vsec.h"
|
||||
@@ -173,20 +174,22 @@ static int enable_survivability_mode(struct pci_dev *pdev)
|
||||
survivability->mode = true;
|
||||
|
||||
ret = xe_heci_gsc_init(xe);
|
||||
if (ret) {
|
||||
/*
|
||||
* But if it fails, device can't enter survivability
|
||||
* so move it back for correct error handling
|
||||
*/
|
||||
survivability->mode = false;
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
xe_vsec_init(xe);
|
||||
|
||||
ret = xe_i2c_probe(xe);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
dev_err(dev, "In Survivability Mode\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
survivability->mode = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+75
-50
@@ -3,13 +3,17 @@
|
||||
* Copyright © 2024 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
|
||||
#include "xe_bo.h"
|
||||
#include "xe_gt_stats.h"
|
||||
#include "xe_gt_tlb_invalidation.h"
|
||||
#include "xe_migrate.h"
|
||||
#include "xe_module.h"
|
||||
#include "xe_pm.h"
|
||||
#include "xe_pt.h"
|
||||
#include "xe_svm.h"
|
||||
#include "xe_tile.h"
|
||||
#include "xe_ttm_vram_mgr.h"
|
||||
#include "xe_vm.h"
|
||||
#include "xe_vm_types.h"
|
||||
@@ -295,7 +299,7 @@ static void xe_svm_garbage_collector_work_func(struct work_struct *w)
|
||||
up_write(&vm->lock);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
|
||||
|
||||
static struct xe_vram_region *page_to_vr(struct page *page)
|
||||
{
|
||||
@@ -483,16 +487,18 @@ static int xe_svm_copy_to_ram(struct page **pages, dma_addr_t *dma_addr,
|
||||
return xe_svm_copy(pages, dma_addr, npages, XE_SVM_COPY_TO_SRAM);
|
||||
}
|
||||
|
||||
static struct xe_bo *to_xe_bo(struct drm_gpusvm_devmem *devmem_allocation)
|
||||
static struct xe_bo *to_xe_bo(struct drm_pagemap_devmem *devmem_allocation)
|
||||
{
|
||||
return container_of(devmem_allocation, struct xe_bo, devmem_allocation);
|
||||
}
|
||||
|
||||
static void xe_svm_devmem_release(struct drm_gpusvm_devmem *devmem_allocation)
|
||||
static void xe_svm_devmem_release(struct drm_pagemap_devmem *devmem_allocation)
|
||||
{
|
||||
struct xe_bo *bo = to_xe_bo(devmem_allocation);
|
||||
struct xe_device *xe = xe_bo_device(bo);
|
||||
|
||||
xe_bo_put_async(bo);
|
||||
xe_pm_runtime_put(xe);
|
||||
}
|
||||
|
||||
static u64 block_offset_to_pfn(struct xe_vram_region *vr, u64 offset)
|
||||
@@ -505,7 +511,7 @@ static struct drm_buddy *tile_to_buddy(struct xe_tile *tile)
|
||||
return &tile->mem.vram.ttm.mm;
|
||||
}
|
||||
|
||||
static int xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem *devmem_allocation,
|
||||
static int xe_svm_populate_devmem_pfn(struct drm_pagemap_devmem *devmem_allocation,
|
||||
unsigned long npages, unsigned long *pfn)
|
||||
{
|
||||
struct xe_bo *bo = to_xe_bo(devmem_allocation);
|
||||
@@ -528,7 +534,7 @@ static int xe_svm_populate_devmem_pfn(struct drm_gpusvm_devmem *devmem_allocatio
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_gpusvm_devmem_ops gpusvm_devmem_ops = {
|
||||
static const struct drm_pagemap_devmem_ops dpagemap_devmem_ops = {
|
||||
.devmem_release = xe_svm_devmem_release,
|
||||
.populate_devmem_pfn = xe_svm_populate_devmem_pfn,
|
||||
.copy_to_devmem = xe_svm_copy_to_devmem,
|
||||
@@ -676,75 +682,69 @@ u64 xe_svm_find_vma_start(struct xe_vm *vm, u64 start, u64 end, struct xe_vma *v
|
||||
min(end, xe_vma_end(vma)));
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
|
||||
static struct xe_vram_region *tile_to_vr(struct xe_tile *tile)
|
||||
{
|
||||
return &tile->mem.vram;
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_svm_alloc_vram()- Allocate device memory pages for range,
|
||||
* migrating existing data.
|
||||
* @vm: The VM.
|
||||
* @tile: tile to allocate vram from
|
||||
* @range: SVM range
|
||||
* @ctx: DRM GPU SVM context
|
||||
*
|
||||
* Return: 0 on success, error code on failure.
|
||||
*/
|
||||
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
|
||||
struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
|
||||
unsigned long start, unsigned long end,
|
||||
struct mm_struct *mm,
|
||||
unsigned long timeslice_ms)
|
||||
{
|
||||
struct mm_struct *mm = vm->svm.gpusvm.mm;
|
||||
struct xe_tile *tile = container_of(dpagemap, typeof(*tile), mem.vram.dpagemap);
|
||||
struct xe_device *xe = tile_to_xe(tile);
|
||||
struct device *dev = xe->drm.dev;
|
||||
struct xe_vram_region *vr = tile_to_vr(tile);
|
||||
struct drm_buddy_block *block;
|
||||
struct list_head *blocks;
|
||||
struct xe_bo *bo;
|
||||
ktime_t end = 0;
|
||||
int err;
|
||||
ktime_t time_end = 0;
|
||||
int err, idx;
|
||||
|
||||
range_debug(range, "ALLOCATE VRAM");
|
||||
if (!drm_dev_enter(&xe->drm, &idx))
|
||||
return -ENODEV;
|
||||
|
||||
if (!mmget_not_zero(mm))
|
||||
return -EFAULT;
|
||||
mmap_read_lock(mm);
|
||||
xe_pm_runtime_get(xe);
|
||||
|
||||
retry:
|
||||
bo = xe_bo_create_locked(tile_to_xe(tile), NULL, NULL,
|
||||
xe_svm_range_size(range),
|
||||
retry:
|
||||
bo = xe_bo_create_locked(tile_to_xe(tile), NULL, NULL, end - start,
|
||||
ttm_bo_type_device,
|
||||
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
|
||||
XE_BO_FLAG_CPU_ADDR_MIRROR);
|
||||
if (IS_ERR(bo)) {
|
||||
err = PTR_ERR(bo);
|
||||
if (xe_vm_validate_should_retry(NULL, err, &end))
|
||||
if (xe_vm_validate_should_retry(NULL, err, &time_end))
|
||||
goto retry;
|
||||
goto unlock;
|
||||
goto out_pm_put;
|
||||
}
|
||||
|
||||
drm_gpusvm_devmem_init(&bo->devmem_allocation,
|
||||
vm->xe->drm.dev, mm,
|
||||
&gpusvm_devmem_ops,
|
||||
&tile->mem.vram.dpagemap,
|
||||
xe_svm_range_size(range));
|
||||
drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm,
|
||||
&dpagemap_devmem_ops,
|
||||
&tile->mem.vram.dpagemap,
|
||||
end - start);
|
||||
|
||||
blocks = &to_xe_ttm_vram_mgr_resource(bo->ttm.resource)->blocks;
|
||||
list_for_each_entry(block, blocks, link)
|
||||
block->private = vr;
|
||||
|
||||
xe_bo_get(bo);
|
||||
err = drm_gpusvm_migrate_to_devmem(&vm->svm.gpusvm, &range->base,
|
||||
&bo->devmem_allocation, ctx);
|
||||
|
||||
/* Ensure the device has a pm ref while there are device pages active. */
|
||||
xe_pm_runtime_get_noresume(xe);
|
||||
err = drm_pagemap_migrate_to_devmem(&bo->devmem_allocation, mm,
|
||||
start, end, timeslice_ms,
|
||||
xe_svm_devm_owner(xe));
|
||||
if (err)
|
||||
xe_svm_devmem_release(&bo->devmem_allocation);
|
||||
|
||||
xe_bo_unlock(bo);
|
||||
xe_bo_put(bo);
|
||||
|
||||
unlock:
|
||||
mmap_read_unlock(mm);
|
||||
mmput(mm);
|
||||
out_pm_put:
|
||||
xe_pm_runtime_put(xe);
|
||||
drm_dev_exit(idx);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -810,13 +810,13 @@ int xe_svm_handle_pagefault(struct xe_vm *vm, struct xe_vma *vma,
|
||||
struct drm_gpusvm_ctx ctx = {
|
||||
.read_only = xe_vma_read_only(vma),
|
||||
.devmem_possible = IS_DGFX(vm->xe) &&
|
||||
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
|
||||
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP),
|
||||
.check_pages_threshold = IS_DGFX(vm->xe) &&
|
||||
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ? SZ_64K : 0,
|
||||
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) ? SZ_64K : 0,
|
||||
.devmem_only = atomic && IS_DGFX(vm->xe) &&
|
||||
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR),
|
||||
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP),
|
||||
.timeslice_ms = atomic && IS_DGFX(vm->xe) &&
|
||||
IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR) ?
|
||||
IS_ENABLED(CONFIG_DRM_XE_PAGEMAP) ?
|
||||
vm->xe->atomic_svm_timeslice_ms : 0,
|
||||
};
|
||||
struct xe_svm_range *range;
|
||||
@@ -852,7 +852,7 @@ retry:
|
||||
|
||||
if (--migrate_try_count >= 0 &&
|
||||
xe_svm_range_needs_migrate_to_vram(range, vma, IS_DGFX(vm->xe))) {
|
||||
err = xe_svm_alloc_vram(vm, tile, range, &ctx);
|
||||
err = xe_svm_alloc_vram(tile, range, &ctx);
|
||||
ctx.timeslice_ms <<= 1; /* Double timeslice if we have to retry */
|
||||
if (err) {
|
||||
if (migrate_try_count || !ctx.devmem_only) {
|
||||
@@ -944,7 +944,7 @@ bool xe_svm_has_mapping(struct xe_vm *vm, u64 start, u64 end)
|
||||
*/
|
||||
int xe_svm_bo_evict(struct xe_bo *bo)
|
||||
{
|
||||
return drm_gpusvm_evict_to_ram(&bo->devmem_allocation);
|
||||
return drm_pagemap_evict_to_ram(&bo->devmem_allocation);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -997,7 +997,31 @@ int xe_svm_range_get_pages(struct xe_vm *vm, struct xe_svm_range *range,
|
||||
return err;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_DEVMEM_MIRROR)
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
|
||||
|
||||
/**
|
||||
* xe_svm_alloc_vram()- Allocate device memory pages for range,
|
||||
* migrating existing data.
|
||||
* @tile: tile to allocate vram from
|
||||
* @range: SVM range
|
||||
* @ctx: DRM GPU SVM context
|
||||
*
|
||||
* Return: 0 on success, error code on failure.
|
||||
*/
|
||||
int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
{
|
||||
struct drm_pagemap *dpagemap;
|
||||
|
||||
xe_assert(tile_to_xe(tile), range->base.flags.migrate_devmem);
|
||||
range_debug(range, "ALLOCATE VRAM");
|
||||
|
||||
dpagemap = xe_tile_local_pagemap(tile);
|
||||
return drm_pagemap_populate_mm(dpagemap, xe_svm_range_start(range),
|
||||
xe_svm_range_end(range),
|
||||
range->base.gpusvm->mm,
|
||||
ctx->timeslice_ms);
|
||||
}
|
||||
|
||||
static struct drm_pagemap_device_addr
|
||||
xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
|
||||
@@ -1023,6 +1047,7 @@ xe_drm_pagemap_device_map(struct drm_pagemap *dpagemap,
|
||||
|
||||
static const struct drm_pagemap_ops xe_drm_pagemap_ops = {
|
||||
.device_map = xe_drm_pagemap_device_map,
|
||||
.populate_mm = xe_drm_pagemap_populate_mm,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1054,7 +1079,7 @@ int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
|
||||
vr->pagemap.range.start = res->start;
|
||||
vr->pagemap.range.end = res->end;
|
||||
vr->pagemap.nr_range = 1;
|
||||
vr->pagemap.ops = drm_gpusvm_pagemap_ops_get();
|
||||
vr->pagemap.ops = drm_pagemap_pagemap_ops_get();
|
||||
vr->pagemap.owner = xe_svm_devm_owner(xe);
|
||||
addr = devm_memremap_pages(dev, &vr->pagemap);
|
||||
|
||||
@@ -1075,7 +1100,7 @@ int xe_devm_add(struct xe_tile *tile, struct xe_vram_region *vr)
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
|
||||
int xe_svm_alloc_vram(struct xe_tile *tile,
|
||||
struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
{
|
||||
|
||||
@@ -70,8 +70,7 @@ int xe_svm_bo_evict(struct xe_bo *bo);
|
||||
|
||||
void xe_svm_range_debug(struct xe_svm_range *range, const char *operation);
|
||||
|
||||
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
|
||||
struct xe_svm_range *range,
|
||||
int xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx);
|
||||
|
||||
struct xe_svm_range *xe_svm_range_find_or_insert(struct xe_vm *vm, u64 addr,
|
||||
@@ -237,10 +236,9 @@ void xe_svm_range_debug(struct xe_svm_range *range, const char *operation)
|
||||
{
|
||||
}
|
||||
|
||||
static inline
|
||||
int xe_svm_alloc_vram(struct xe_vm *vm, struct xe_tile *tile,
|
||||
struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
static inline int
|
||||
xe_svm_alloc_vram(struct xe_tile *tile, struct xe_svm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "xe_device.h"
|
||||
#include "xe_ggtt.h"
|
||||
#include "xe_gt.h"
|
||||
#include "xe_memirq.h"
|
||||
#include "xe_migrate.h"
|
||||
#include "xe_pcode.h"
|
||||
#include "xe_sa.h"
|
||||
@@ -174,6 +175,12 @@ int xe_tile_init_noalloc(struct xe_tile *tile)
|
||||
|
||||
int xe_tile_init(struct xe_tile *tile)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = xe_memirq_init(&tile->memirq);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
tile->mem.kernel_bb_pool = xe_sa_bo_manager_init(tile, SZ_1M, 16);
|
||||
if (IS_ERR(tile->mem.kernel_bb_pool))
|
||||
return PTR_ERR(tile->mem.kernel_bb_pool);
|
||||
|
||||
@@ -16,4 +16,15 @@ int xe_tile_init(struct xe_tile *tile);
|
||||
|
||||
void xe_tile_migrate_wait(struct xe_tile *tile);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_XE_PAGEMAP)
|
||||
static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
|
||||
{
|
||||
return &tile->mem.vram.dpagemap;
|
||||
}
|
||||
#else
|
||||
static inline struct drm_pagemap *xe_tile_local_pagemap(struct xe_tile *tile)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -33,7 +33,7 @@ DECLARE_EVENT_CLASS(xe_bo,
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(dev);
|
||||
__entry->size = bo->size;
|
||||
__entry->size = xe_bo_size(bo);
|
||||
__entry->flags = bo->flags;
|
||||
__entry->vm = bo->vm;
|
||||
),
|
||||
@@ -73,7 +73,7 @@ TRACE_EVENT(xe_bo_move,
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->bo = bo;
|
||||
__entry->size = bo->size;
|
||||
__entry->size = xe_bo_size(bo);
|
||||
__assign_str(new_placement_name);
|
||||
__assign_str(old_placement_name);
|
||||
__assign_str(device_id);
|
||||
|
||||
+41
-37
@@ -33,6 +33,22 @@ uc_to_xe(struct xe_uc *uc)
|
||||
}
|
||||
|
||||
/* Should be called once at driver load only */
|
||||
int xe_uc_init_noalloc(struct xe_uc *uc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = xe_guc_init_noalloc(&uc->guc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* HuC and GSC have no early dependencies and will be initialized during xe_uc_init(). */
|
||||
return 0;
|
||||
|
||||
err:
|
||||
xe_gt_err(uc_to_gt(uc), "Failed to early initialize uC (%pe)\n", ERR_PTR(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xe_uc_init(struct xe_uc *uc)
|
||||
{
|
||||
int ret;
|
||||
@@ -56,15 +72,17 @@ int xe_uc_init(struct xe_uc *uc)
|
||||
if (!xe_device_uc_enabled(uc_to_xe(uc)))
|
||||
return 0;
|
||||
|
||||
if (IS_SRIOV_VF(uc_to_xe(uc)))
|
||||
return 0;
|
||||
if (!IS_SRIOV_VF(uc_to_xe(uc))) {
|
||||
ret = xe_wopcm_init(&uc->wopcm);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = xe_wopcm_init(&uc->wopcm);
|
||||
ret = xe_guc_min_load_for_hwconfig(&uc->guc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
xe_gt_err(uc_to_gt(uc), "Failed to initialize uC (%pe)\n", ERR_PTR(ret));
|
||||
return ret;
|
||||
@@ -126,28 +144,7 @@ int xe_uc_sanitize_reset(struct xe_uc *uc)
|
||||
return uc_reset(uc);
|
||||
}
|
||||
|
||||
/**
|
||||
* xe_uc_init_hwconfig - minimally init Uc, read and parse hwconfig
|
||||
* @uc: The UC object
|
||||
*
|
||||
* Return: 0 on success, negative error code on error.
|
||||
*/
|
||||
int xe_uc_init_hwconfig(struct xe_uc *uc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* GuC submission not enabled, nothing to do */
|
||||
if (!xe_device_uc_enabled(uc_to_xe(uc)))
|
||||
return 0;
|
||||
|
||||
ret = xe_guc_min_load_for_hwconfig(&uc->guc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf_uc_init_hw(struct xe_uc *uc)
|
||||
static int vf_uc_load_hw(struct xe_uc *uc)
|
||||
{
|
||||
int err;
|
||||
|
||||
@@ -161,22 +158,30 @@ static int vf_uc_init_hw(struct xe_uc *uc)
|
||||
|
||||
err = xe_gt_sriov_vf_connect(uc_to_gt(uc));
|
||||
if (err)
|
||||
return err;
|
||||
goto err_out;
|
||||
|
||||
uc->guc.submission_state.enabled = true;
|
||||
|
||||
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
|
||||
err = xe_guc_opt_in_features_enable(&uc->guc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = xe_gt_record_default_lrcs(uc_to_gt(uc));
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
xe_guc_sanitize(&uc->guc);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Should be called during driver load, after every GT reset, and after every
|
||||
* suspend to reload / auth the firmwares.
|
||||
*/
|
||||
int xe_uc_init_hw(struct xe_uc *uc)
|
||||
int xe_uc_load_hw(struct xe_uc *uc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -185,7 +190,7 @@ int xe_uc_init_hw(struct xe_uc *uc)
|
||||
return 0;
|
||||
|
||||
if (IS_SRIOV_VF(uc_to_xe(uc)))
|
||||
return vf_uc_init_hw(uc);
|
||||
return vf_uc_load_hw(uc);
|
||||
|
||||
ret = xe_huc_upload(&uc->huc);
|
||||
if (ret)
|
||||
@@ -201,15 +206,15 @@ int xe_uc_init_hw(struct xe_uc *uc)
|
||||
|
||||
ret = xe_gt_record_default_lrcs(uc_to_gt(uc));
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_out;
|
||||
|
||||
ret = xe_guc_post_load_init(&uc->guc);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_out;
|
||||
|
||||
ret = xe_guc_pc_start(&uc->guc.pc);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_out;
|
||||
|
||||
xe_guc_engine_activity_enable_stats(&uc->guc);
|
||||
|
||||
@@ -221,11 +226,10 @@ int xe_uc_init_hw(struct xe_uc *uc)
|
||||
xe_gsc_load_start(&uc->gsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_uc_fini_hw(struct xe_uc *uc)
|
||||
{
|
||||
return xe_uc_sanitize_reset(uc);
|
||||
err_out:
|
||||
xe_guc_sanitize(&uc->guc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int xe_uc_reset_prepare(struct xe_uc *uc)
|
||||
|
||||
@@ -8,11 +8,10 @@
|
||||
|
||||
struct xe_uc;
|
||||
|
||||
int xe_uc_init_noalloc(struct xe_uc *uc);
|
||||
int xe_uc_init(struct xe_uc *uc);
|
||||
int xe_uc_init_hwconfig(struct xe_uc *uc);
|
||||
int xe_uc_init_post_hwconfig(struct xe_uc *uc);
|
||||
int xe_uc_init_hw(struct xe_uc *uc);
|
||||
int xe_uc_fini_hw(struct xe_uc *uc);
|
||||
int xe_uc_load_hw(struct xe_uc *uc);
|
||||
void xe_uc_gucrc_disable(struct xe_uc *uc);
|
||||
int xe_uc_reset_prepare(struct xe_uc *uc);
|
||||
void xe_uc_stop_prepare(struct xe_uc *uc);
|
||||
|
||||
@@ -115,10 +115,11 @@ struct fw_blobs_by_type {
|
||||
#define XE_GT_TYPE_ANY XE_GT_TYPE_UNINITIALIZED
|
||||
|
||||
#define XE_GUC_FIRMWARE_DEFS(fw_def, mmp_ver, major_ver) \
|
||||
fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 44, 1)) \
|
||||
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 44, 1)) \
|
||||
fw_def(PANTHERLAKE, GT_TYPE_ANY, major_ver(xe, guc, ptl, 70, 47, 0)) \
|
||||
fw_def(BATTLEMAGE, GT_TYPE_ANY, major_ver(xe, guc, bmg, 70, 45, 2)) \
|
||||
fw_def(LUNARLAKE, GT_TYPE_ANY, major_ver(xe, guc, lnl, 70, 45, 2)) \
|
||||
fw_def(METEORLAKE, GT_TYPE_ANY, major_ver(i915, guc, mtl, 70, 44, 1)) \
|
||||
fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 44, 1)) \
|
||||
fw_def(DG2, GT_TYPE_ANY, major_ver(i915, guc, dg2, 70, 45, 2)) \
|
||||
fw_def(DG1, GT_TYPE_ANY, major_ver(i915, guc, dg1, 70, 44, 1)) \
|
||||
fw_def(ALDERLAKE_N, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1)) \
|
||||
fw_def(ALDERLAKE_P, GT_TYPE_ANY, major_ver(i915, guc, adlp, 70, 44, 1)) \
|
||||
@@ -127,6 +128,7 @@ struct fw_blobs_by_type {
|
||||
fw_def(TIGERLAKE, GT_TYPE_ANY, major_ver(i915, guc, tgl, 70, 44, 1))
|
||||
|
||||
#define XE_HUC_FIRMWARE_DEFS(fw_def, mmp_ver, no_ver) \
|
||||
fw_def(PANTHERLAKE, GT_TYPE_ANY, no_ver(xe, huc, ptl)) \
|
||||
fw_def(BATTLEMAGE, GT_TYPE_ANY, no_ver(xe, huc, bmg)) \
|
||||
fw_def(LUNARLAKE, GT_TYPE_ANY, no_ver(xe, huc, lnl)) \
|
||||
fw_def(METEORLAKE, GT_TYPE_ANY, no_ver(i915, huc_gsc, mtl)) \
|
||||
|
||||
@@ -2913,7 +2913,7 @@ static int prefetch_ranges(struct xe_vm *vm, struct xe_vma_op *op)
|
||||
|
||||
if (xe_svm_range_needs_migrate_to_vram(svm_range, vma, region)) {
|
||||
tile = &vm->xe->tiles[region_to_mem_type[region] - XE_PL_VRAM0];
|
||||
err = xe_svm_alloc_vram(vm, tile, svm_range, &ctx);
|
||||
err = xe_svm_alloc_vram(tile, svm_range, &ctx);
|
||||
if (err) {
|
||||
drm_dbg(&vm->xe->drm, "VRAM allocation failed, retry from userspace, asid=%u, gpusvm=%p, errno=%pe\n",
|
||||
vm->usm.asid, &vm->svm.gpusvm, ERR_PTR(err));
|
||||
@@ -3466,9 +3466,9 @@ static int xe_vm_bind_ioctl_validate_bo(struct xe_device *xe, struct xe_bo *bo,
|
||||
{
|
||||
u16 coh_mode;
|
||||
|
||||
if (XE_IOCTL_DBG(xe, range > bo->size) ||
|
||||
if (XE_IOCTL_DBG(xe, range > xe_bo_size(bo)) ||
|
||||
XE_IOCTL_DBG(xe, obj_offset >
|
||||
bo->size - range)) {
|
||||
xe_bo_size(bo) - range)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -3771,7 +3771,7 @@ struct dma_fence *xe_vm_bind_kernel_bo(struct xe_vm *vm, struct xe_bo *bo,
|
||||
|
||||
xe_vma_ops_init(&vops, vm, q, NULL, 0);
|
||||
|
||||
ops = vm_bind_ioctl_ops_create(vm, &vops, bo, 0, addr, bo->size,
|
||||
ops = vm_bind_ioctl_ops_create(vm, &vops, bo, 0, addr, xe_bo_size(bo),
|
||||
DRM_XE_VM_BIND_OP_MAP, 0, 0,
|
||||
vm->xe->pat.idx[cache_lvl]);
|
||||
if (IS_ERR(ops)) {
|
||||
|
||||
@@ -285,6 +285,18 @@ static const struct xe_rtp_entry_sr gt_was[] = {
|
||||
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
|
||||
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
|
||||
},
|
||||
{ XE_RTP_NAME("16021865536"),
|
||||
XE_RTP_RULES(MEDIA_VERSION(3002),
|
||||
ENGINE_CLASS(VIDEO_DECODE)),
|
||||
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
|
||||
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
|
||||
},
|
||||
{ XE_RTP_NAME("16021867713"),
|
||||
XE_RTP_RULES(MEDIA_VERSION(3002),
|
||||
ENGINE_CLASS(VIDEO_DECODE)),
|
||||
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
|
||||
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
|
||||
},
|
||||
{ XE_RTP_NAME("14021486841"),
|
||||
XE_RTP_RULES(MEDIA_VERSION(3000), MEDIA_STEP(A0, B0),
|
||||
ENGINE_CLASS(VIDEO_DECODE)),
|
||||
@@ -644,6 +656,10 @@ static const struct xe_rtp_entry_sr engine_was[] = {
|
||||
XE_RTP_ACTIONS(SET(RING_PSMI_CTL(0), RC_SEMA_IDLE_MSG_DISABLE,
|
||||
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
|
||||
},
|
||||
{ XE_RTP_NAME("14021402888"),
|
||||
XE_RTP_RULES(GRAPHICS_VERSION(3003), FUNC(xe_rtp_match_first_render_or_compute)),
|
||||
XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
|
||||
},
|
||||
};
|
||||
|
||||
static const struct xe_rtp_entry_sr lrc_was[] = {
|
||||
|
||||
@@ -34,14 +34,16 @@
|
||||
14022293748 GRAPHICS_VERSION_RANGE(2001, 2002)
|
||||
GRAPHICS_VERSION(2004)
|
||||
GRAPHICS_VERSION_RANGE(3000, 3001)
|
||||
GRAPHICS_VERSION(3003)
|
||||
22019794406 GRAPHICS_VERSION_RANGE(2001, 2002)
|
||||
GRAPHICS_VERSION(2004)
|
||||
GRAPHICS_VERSION_RANGE(3000, 3001)
|
||||
GRAPHICS_VERSION(3003)
|
||||
22019338487 MEDIA_VERSION(2000)
|
||||
GRAPHICS_VERSION(2001)
|
||||
GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
|
||||
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_not_sriov_vf)
|
||||
22019338487_display PLATFORM(LUNARLAKE)
|
||||
16023588340 GRAPHICS_VERSION(2001)
|
||||
16023588340 GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
|
||||
14019789679 GRAPHICS_VERSION(1255)
|
||||
GRAPHICS_VERSION_RANGE(1270, 2004)
|
||||
no_media_l3 MEDIA_VERSION(3000)
|
||||
@@ -58,8 +60,12 @@ no_media_l3 MEDIA_VERSION(3000)
|
||||
GRAPHICS_VERSION(1260), GRAPHICS_STEP(A0, B0)
|
||||
16023105232 GRAPHICS_VERSION_RANGE(2001, 3001)
|
||||
MEDIA_VERSION_RANGE(1301, 3000)
|
||||
MEDIA_VERSION(3002)
|
||||
GRAPHICS_VERSION(3003)
|
||||
16026508708 GRAPHICS_VERSION_RANGE(1200, 3001)
|
||||
MEDIA_VERSION_RANGE(1300, 3000)
|
||||
MEDIA_VERSION(3002)
|
||||
GRAPHICS_VERSION(3003)
|
||||
|
||||
# SoC workaround - currently applies to all platforms with the following
|
||||
# primary GT GMDID
|
||||
|
||||
@@ -101,7 +101,7 @@ static int bt1_i2c_request_regs(struct dw_i2c_dev *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int txgbe_i2c_request_regs(struct dw_i2c_dev *dev)
|
||||
static int dw_i2c_get_parent_regmap(struct dw_i2c_dev *dev)
|
||||
{
|
||||
dev->map = dev_get_regmap(dev->dev->parent, NULL);
|
||||
if (!dev->map)
|
||||
@@ -123,12 +123,15 @@ static int dw_i2c_plat_request_regs(struct dw_i2c_dev *dev)
|
||||
struct platform_device *pdev = to_platform_device(dev->dev);
|
||||
int ret;
|
||||
|
||||
if (device_is_compatible(dev->dev, "intel,xe-i2c"))
|
||||
return dw_i2c_get_parent_regmap(dev);
|
||||
|
||||
switch (dev->flags & MODEL_MASK) {
|
||||
case MODEL_BAIKAL_BT1:
|
||||
ret = bt1_i2c_request_regs(dev);
|
||||
break;
|
||||
case MODEL_WANGXUN_SP:
|
||||
ret = txgbe_i2c_request_regs(dev);
|
||||
ret = dw_i2c_get_parent_regmap(dev);
|
||||
break;
|
||||
default:
|
||||
dev->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
@@ -205,25 +208,28 @@ static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev)
|
||||
|
||||
static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
|
||||
struct device *device = &pdev->dev;
|
||||
struct i2c_adapter *adap;
|
||||
struct dw_i2c_dev *dev;
|
||||
int irq, ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
irq = platform_get_irq_optional(pdev, 0);
|
||||
if (irq == -ENXIO)
|
||||
flags |= ACCESS_POLLING;
|
||||
else if (irq < 0)
|
||||
return irq;
|
||||
|
||||
dev = devm_kzalloc(device, sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->flags = (uintptr_t)device_get_match_data(device);
|
||||
if (device_property_present(device, "wx,i2c-snps-model"))
|
||||
dev->flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
|
||||
flags = MODEL_WANGXUN_SP | ACCESS_POLLING;
|
||||
|
||||
dev->dev = device;
|
||||
dev->irq = irq;
|
||||
dev->flags = flags;
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
ret = dw_i2c_plat_request_regs(dev);
|
||||
|
||||
@@ -183,6 +183,17 @@ config MTD_POWERNV_FLASH
|
||||
platforms from Linux. This device abstracts away the
|
||||
firmware interface for flash access.
|
||||
|
||||
config MTD_INTEL_DG
|
||||
tristate "Intel Discrete Graphics non-volatile memory driver"
|
||||
depends on AUXILIARY_BUS
|
||||
depends on MTD
|
||||
help
|
||||
This provides an MTD device to access Intel Discrete Graphics
|
||||
non-volatile memory.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mtd-intel-dg.
|
||||
|
||||
comment "Disk-On-Chip Device Drivers"
|
||||
|
||||
config MTD_DOCG3
|
||||
|
||||
@@ -18,6 +18,7 @@ obj-$(CONFIG_MTD_SST25L) += sst25l.o
|
||||
obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o
|
||||
obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o
|
||||
obj-$(CONFIG_MTD_POWERNV_FLASH) += powernv_flash.o
|
||||
obj-$(CONFIG_MTD_INTEL_DG) += mtd_intel_dg.o
|
||||
|
||||
|
||||
CFLAGS_docg3.o += -I$(src)
|
||||
|
||||
@@ -0,0 +1,830 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/intel_dg_nvm_aux.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct intel_dg_nvm {
|
||||
struct kref refcnt;
|
||||
struct mtd_info mtd;
|
||||
struct mutex lock; /* region access lock */
|
||||
void __iomem *base;
|
||||
void __iomem *base2;
|
||||
bool non_posted_erase;
|
||||
|
||||
size_t size;
|
||||
unsigned int nregions;
|
||||
struct {
|
||||
const char *name;
|
||||
u8 id;
|
||||
u64 offset;
|
||||
u64 size;
|
||||
unsigned int is_readable:1;
|
||||
unsigned int is_writable:1;
|
||||
} regions[] __counted_by(nregions);
|
||||
};
|
||||
|
||||
#define NVM_TRIGGER_REG 0x00000000
|
||||
#define NVM_VALSIG_REG 0x00000010
|
||||
#define NVM_ADDRESS_REG 0x00000040
|
||||
#define NVM_REGION_ID_REG 0x00000044
|
||||
#define NVM_DEBUG_REG 0x00000000
|
||||
/*
|
||||
* [15:0]-Erase size = 0x0010 4K 0x0080 32K 0x0100 64K
|
||||
* [23:16]-Reserved
|
||||
* [31:24]-Erase MEM RegionID
|
||||
*/
|
||||
#define NVM_ERASE_REG 0x00000048
|
||||
#define NVM_ACCESS_ERROR_REG 0x00000070
|
||||
#define NVM_ADDRESS_ERROR_REG 0x00000074
|
||||
|
||||
/* Flash Valid Signature */
|
||||
#define NVM_FLVALSIG 0x0FF0A55A
|
||||
|
||||
#define NVM_MAP_ADDR_MASK GENMASK(7, 0)
|
||||
#define NVM_MAP_ADDR_SHIFT 0x00000004
|
||||
|
||||
#define NVM_REGION_ID_DESCRIPTOR 0
|
||||
/* Flash Region Base Address */
|
||||
#define NVM_FRBA 0x40
|
||||
/* Flash Region __n - Flash Descriptor Record */
|
||||
#define NVM_FLREG(__n) (NVM_FRBA + ((__n) * 4))
|
||||
/* Flash Map 1 Register */
|
||||
#define NVM_FLMAP1_REG 0x18
|
||||
#define NVM_FLMSTR4_OFFSET 0x00C
|
||||
|
||||
#define NVM_ACCESS_ERROR_PCIE_MASK 0x7
|
||||
|
||||
#define NVM_FREG_BASE_MASK GENMASK(15, 0)
|
||||
#define NVM_FREG_ADDR_MASK GENMASK(31, 16)
|
||||
#define NVM_FREG_ADDR_SHIFT 12
|
||||
#define NVM_FREG_MIN_REGION_SIZE 0xFFF
|
||||
|
||||
#define NVM_NON_POSTED_ERASE_DONE BIT(23)
|
||||
#define NVM_NON_POSTED_ERASE_DONE_ITER 3000
|
||||
|
||||
static inline void idg_nvm_set_region_id(struct intel_dg_nvm *nvm, u8 region)
|
||||
{
|
||||
iowrite32((u32)region, nvm->base + NVM_REGION_ID_REG);
|
||||
}
|
||||
|
||||
static inline u32 idg_nvm_error(struct intel_dg_nvm *nvm)
|
||||
{
|
||||
void __iomem *base = nvm->base;
|
||||
|
||||
u32 reg = ioread32(base + NVM_ACCESS_ERROR_REG) & NVM_ACCESS_ERROR_PCIE_MASK;
|
||||
|
||||
/* reset error bits */
|
||||
if (reg)
|
||||
iowrite32(reg, base + NVM_ACCESS_ERROR_REG);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static inline u32 idg_nvm_read32(struct intel_dg_nvm *nvm, u32 address)
|
||||
{
|
||||
void __iomem *base = nvm->base;
|
||||
|
||||
iowrite32(address, base + NVM_ADDRESS_REG);
|
||||
|
||||
return ioread32(base + NVM_TRIGGER_REG);
|
||||
}
|
||||
|
||||
static inline u64 idg_nvm_read64(struct intel_dg_nvm *nvm, u32 address)
|
||||
{
|
||||
void __iomem *base = nvm->base;
|
||||
|
||||
iowrite32(address, base + NVM_ADDRESS_REG);
|
||||
|
||||
return readq(base + NVM_TRIGGER_REG);
|
||||
}
|
||||
|
||||
static void idg_nvm_write32(struct intel_dg_nvm *nvm, u32 address, u32 data)
|
||||
{
|
||||
void __iomem *base = nvm->base;
|
||||
|
||||
iowrite32(address, base + NVM_ADDRESS_REG);
|
||||
|
||||
iowrite32(data, base + NVM_TRIGGER_REG);
|
||||
}
|
||||
|
||||
static void idg_nvm_write64(struct intel_dg_nvm *nvm, u32 address, u64 data)
|
||||
{
|
||||
void __iomem *base = nvm->base;
|
||||
|
||||
iowrite32(address, base + NVM_ADDRESS_REG);
|
||||
|
||||
writeq(data, base + NVM_TRIGGER_REG);
|
||||
}
|
||||
|
||||
static int idg_nvm_get_access_map(struct intel_dg_nvm *nvm, u32 *access_map)
|
||||
{
|
||||
u32 fmstr4_addr;
|
||||
u32 fmstr4;
|
||||
u32 flmap1;
|
||||
u32 fmba;
|
||||
|
||||
idg_nvm_set_region_id(nvm, NVM_REGION_ID_DESCRIPTOR);
|
||||
|
||||
flmap1 = idg_nvm_read32(nvm, NVM_FLMAP1_REG);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
/* Get Flash Master Baser Address (FMBA) */
|
||||
fmba = (FIELD_GET(NVM_MAP_ADDR_MASK, flmap1) << NVM_MAP_ADDR_SHIFT);
|
||||
fmstr4_addr = fmba + NVM_FLMSTR4_OFFSET;
|
||||
|
||||
fmstr4 = idg_nvm_read32(nvm, fmstr4_addr);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
|
||||
*access_map = fmstr4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Region read/write access encoded in the access map
|
||||
* in the following order from the lower bit:
|
||||
* [3:0] regions 12-15 read state
|
||||
* [7:4] regions 12-15 write state
|
||||
* [19:8] regions 0-11 read state
|
||||
* [31:20] regions 0-11 write state
|
||||
*/
|
||||
static bool idg_nvm_region_readable(u32 access_map, u8 region)
|
||||
{
|
||||
if (region < 12)
|
||||
return access_map & BIT(region + 8); /* [19:8] */
|
||||
else
|
||||
return access_map & BIT(region - 12); /* [3:0] */
|
||||
}
|
||||
|
||||
static bool idg_nvm_region_writable(u32 access_map, u8 region)
|
||||
{
|
||||
if (region < 12)
|
||||
return access_map & BIT(region + 20); /* [31:20] */
|
||||
else
|
||||
return access_map & BIT(region - 8); /* [7:4] */
|
||||
}
|
||||
|
||||
static int idg_nvm_is_valid(struct intel_dg_nvm *nvm)
|
||||
{
|
||||
u32 is_valid;
|
||||
|
||||
idg_nvm_set_region_id(nvm, NVM_REGION_ID_DESCRIPTOR);
|
||||
|
||||
is_valid = idg_nvm_read32(nvm, NVM_VALSIG_REG);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
|
||||
if (is_valid != NVM_FLVALSIG)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int idg_nvm_get_region(const struct intel_dg_nvm *nvm, loff_t from)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < nvm->nregions; i++) {
|
||||
if ((nvm->regions[i].offset + nvm->regions[i].size - 1) >= from &&
|
||||
nvm->regions[i].offset <= from &&
|
||||
nvm->regions[i].size != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static ssize_t idg_nvm_rewrite_partial(struct intel_dg_nvm *nvm, loff_t to,
|
||||
loff_t offset, size_t len, const u32 *newdata)
|
||||
{
|
||||
u32 data = idg_nvm_read32(nvm, to);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
|
||||
memcpy((u8 *)&data + offset, newdata, len);
|
||||
|
||||
idg_nvm_write32(nvm, to, data);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t idg_write(struct intel_dg_nvm *nvm, u8 region,
|
||||
loff_t to, size_t len, const unsigned char *buf)
|
||||
{
|
||||
size_t len_s = len;
|
||||
size_t to_shift;
|
||||
size_t len8;
|
||||
size_t len4;
|
||||
ssize_t ret;
|
||||
size_t to4;
|
||||
size_t i;
|
||||
|
||||
idg_nvm_set_region_id(nvm, region);
|
||||
|
||||
to4 = ALIGN_DOWN(to, sizeof(u32));
|
||||
to_shift = min(sizeof(u32) - ((size_t)to - to4), len);
|
||||
if (to - to4) {
|
||||
ret = idg_nvm_rewrite_partial(nvm, to4, to - to4, to_shift, (u32 *)&buf[0]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
buf += to_shift;
|
||||
to += to_shift;
|
||||
len_s -= to_shift;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(to, sizeof(u64)) &&
|
||||
((to ^ (to + len_s)) & GENMASK(31, 10))) {
|
||||
/*
|
||||
* Workaround reads/writes across 1k-aligned addresses
|
||||
* (start u32 before 1k, end u32 after)
|
||||
* as this fails on hardware.
|
||||
*/
|
||||
u32 data;
|
||||
|
||||
memcpy(&data, &buf[0], sizeof(u32));
|
||||
idg_nvm_write32(nvm, to, data);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
buf += sizeof(u32);
|
||||
to += sizeof(u32);
|
||||
len_s -= sizeof(u32);
|
||||
}
|
||||
|
||||
len8 = ALIGN_DOWN(len_s, sizeof(u64));
|
||||
for (i = 0; i < len8; i += sizeof(u64)) {
|
||||
u64 data;
|
||||
|
||||
memcpy(&data, &buf[i], sizeof(u64));
|
||||
idg_nvm_write64(nvm, to + i, data);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
len4 = len_s - len8;
|
||||
if (len4 >= sizeof(u32)) {
|
||||
u32 data;
|
||||
|
||||
memcpy(&data, &buf[i], sizeof(u32));
|
||||
idg_nvm_write32(nvm, to + i, data);
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
i += sizeof(u32);
|
||||
len4 -= sizeof(u32);
|
||||
}
|
||||
|
||||
if (len4 > 0) {
|
||||
ret = idg_nvm_rewrite_partial(nvm, to + i, 0, len4, (u32 *)&buf[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t idg_read(struct intel_dg_nvm *nvm, u8 region,
|
||||
loff_t from, size_t len, unsigned char *buf)
|
||||
{
|
||||
size_t len_s = len;
|
||||
size_t from_shift;
|
||||
size_t from4;
|
||||
size_t len8;
|
||||
size_t len4;
|
||||
size_t i;
|
||||
|
||||
idg_nvm_set_region_id(nvm, region);
|
||||
|
||||
from4 = ALIGN_DOWN(from, sizeof(u32));
|
||||
from_shift = min(sizeof(u32) - ((size_t)from - from4), len);
|
||||
|
||||
if (from - from4) {
|
||||
u32 data = idg_nvm_read32(nvm, from4);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
memcpy(&buf[0], (u8 *)&data + (from - from4), from_shift);
|
||||
len_s -= from_shift;
|
||||
buf += from_shift;
|
||||
from += from_shift;
|
||||
}
|
||||
|
||||
if (!IS_ALIGNED(from, sizeof(u64)) &&
|
||||
((from ^ (from + len_s)) & GENMASK(31, 10))) {
|
||||
/*
|
||||
* Workaround reads/writes across 1k-aligned addresses
|
||||
* (start u32 before 1k, end u32 after)
|
||||
* as this fails on hardware.
|
||||
*/
|
||||
u32 data = idg_nvm_read32(nvm, from);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
memcpy(&buf[0], &data, sizeof(data));
|
||||
len_s -= sizeof(u32);
|
||||
buf += sizeof(u32);
|
||||
from += sizeof(u32);
|
||||
}
|
||||
|
||||
len8 = ALIGN_DOWN(len_s, sizeof(u64));
|
||||
for (i = 0; i < len8; i += sizeof(u64)) {
|
||||
u64 data = idg_nvm_read64(nvm, from + i);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
|
||||
memcpy(&buf[i], &data, sizeof(data));
|
||||
}
|
||||
|
||||
len4 = len_s - len8;
|
||||
if (len4 >= sizeof(u32)) {
|
||||
u32 data = idg_nvm_read32(nvm, from + i);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
memcpy(&buf[i], &data, sizeof(data));
|
||||
i += sizeof(u32);
|
||||
len4 -= sizeof(u32);
|
||||
}
|
||||
|
||||
if (len4 > 0) {
|
||||
u32 data = idg_nvm_read32(nvm, from + i);
|
||||
|
||||
if (idg_nvm_error(nvm))
|
||||
return -EIO;
|
||||
memcpy(&buf[i], &data, len4);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_addr)
|
||||
{
|
||||
void __iomem *base2 = nvm->base2;
|
||||
void __iomem *base = nvm->base;
|
||||
const u32 block = 0x10;
|
||||
u32 iter = 0;
|
||||
u32 reg;
|
||||
u64 i;
|
||||
|
||||
for (i = 0; i < len; i += SZ_4K) {
|
||||
iowrite32(from + i, base + NVM_ADDRESS_REG);
|
||||
iowrite32(region << 24 | block, base + NVM_ERASE_REG);
|
||||
if (nvm->non_posted_erase) {
|
||||
/* Wait for Erase Done */
|
||||
reg = ioread32(base2 + NVM_DEBUG_REG);
|
||||
while (!(reg & NVM_NON_POSTED_ERASE_DONE) &&
|
||||
++iter < NVM_NON_POSTED_ERASE_DONE_ITER) {
|
||||
msleep(10);
|
||||
reg = ioread32(base2 + NVM_DEBUG_REG);
|
||||
}
|
||||
if (reg & NVM_NON_POSTED_ERASE_DONE) {
|
||||
/* Clear Erase Done */
|
||||
iowrite32(reg, base2 + NVM_DEBUG_REG);
|
||||
} else {
|
||||
*fail_addr = from + i;
|
||||
return -ETIME;
|
||||
}
|
||||
}
|
||||
/* Since the writes are via sgunit
|
||||
* we cannot do back to back erases.
|
||||
*/
|
||||
msleep(50);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
|
||||
bool non_posted_erase)
|
||||
{
|
||||
u32 access_map = 0;
|
||||
unsigned int i, n;
|
||||
int ret;
|
||||
|
||||
/* clean error register, previous errors are ignored */
|
||||
idg_nvm_error(nvm);
|
||||
|
||||
ret = idg_nvm_is_valid(nvm);
|
||||
if (ret) {
|
||||
dev_err(device, "The MEM is not valid %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (idg_nvm_get_access_map(nvm, &access_map))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0, n = 0; i < nvm->nregions; i++) {
|
||||
u32 address, base, limit, region;
|
||||
u8 id = nvm->regions[i].id;
|
||||
|
||||
address = NVM_FLREG(id);
|
||||
region = idg_nvm_read32(nvm, address);
|
||||
|
||||
base = FIELD_GET(NVM_FREG_BASE_MASK, region) << NVM_FREG_ADDR_SHIFT;
|
||||
limit = (FIELD_GET(NVM_FREG_ADDR_MASK, region) << NVM_FREG_ADDR_SHIFT) |
|
||||
NVM_FREG_MIN_REGION_SIZE;
|
||||
|
||||
dev_dbg(device, "[%d] %s: region: 0x%08X base: 0x%08x limit: 0x%08x\n",
|
||||
id, nvm->regions[i].name, region, base, limit);
|
||||
|
||||
if (base >= limit || (i > 0 && limit == 0)) {
|
||||
dev_dbg(device, "[%d] %s: disabled\n",
|
||||
id, nvm->regions[i].name);
|
||||
nvm->regions[i].is_readable = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nvm->size < limit)
|
||||
nvm->size = limit;
|
||||
|
||||
nvm->regions[i].offset = base;
|
||||
nvm->regions[i].size = limit - base + 1;
|
||||
/* No write access to descriptor; mask it out*/
|
||||
nvm->regions[i].is_writable = idg_nvm_region_writable(access_map, id);
|
||||
|
||||
nvm->regions[i].is_readable = idg_nvm_region_readable(access_map, id);
|
||||
dev_dbg(device, "Registered, %s id=%d offset=%lld size=%lld rd=%d wr=%d\n",
|
||||
nvm->regions[i].name,
|
||||
nvm->regions[i].id,
|
||||
nvm->regions[i].offset,
|
||||
nvm->regions[i].size,
|
||||
nvm->regions[i].is_readable,
|
||||
nvm->regions[i].is_writable);
|
||||
|
||||
if (nvm->regions[i].is_readable)
|
||||
n++;
|
||||
}
|
||||
|
||||
nvm->non_posted_erase = non_posted_erase;
|
||||
|
||||
dev_dbg(device, "Registered %d regions\n", n);
|
||||
dev_dbg(device, "Non posted erase %d\n", nvm->non_posted_erase);
|
||||
|
||||
/* Need to add 1 to the amount of memory
|
||||
* so it is reported as an even block
|
||||
*/
|
||||
nvm->size += 1;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
|
||||
{
|
||||
struct intel_dg_nvm *nvm = mtd->priv;
|
||||
size_t total_len;
|
||||
unsigned int idx;
|
||||
ssize_t bytes;
|
||||
loff_t from;
|
||||
size_t len;
|
||||
u8 region;
|
||||
u64 addr;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ALIGNED(info->addr, SZ_4K) || !IS_ALIGNED(info->len, SZ_4K)) {
|
||||
dev_err(&mtd->dev, "unaligned erase %llx %llx\n",
|
||||
info->addr, info->len);
|
||||
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
total_len = info->len;
|
||||
addr = info->addr;
|
||||
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
while (total_len > 0) {
|
||||
if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) {
|
||||
dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len);
|
||||
info->fail_addr = addr;
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
idx = idg_nvm_get_region(nvm, addr);
|
||||
if (idx >= nvm->nregions) {
|
||||
dev_err(&mtd->dev, "out of range");
|
||||
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
from = addr - nvm->regions[idx].offset;
|
||||
region = nvm->regions[idx].id;
|
||||
len = total_len;
|
||||
if (len > nvm->regions[idx].size - from)
|
||||
len = nvm->regions[idx].size - from;
|
||||
|
||||
dev_dbg(&mtd->dev, "erasing region[%d] %s from %llx len %zx\n",
|
||||
region, nvm->regions[idx].name, from, len);
|
||||
|
||||
bytes = idg_erase(nvm, region, from, len, &info->fail_addr);
|
||||
if (bytes < 0) {
|
||||
dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes);
|
||||
info->fail_addr += nvm->regions[idx].offset;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
addr += len;
|
||||
total_len -= len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct intel_dg_nvm *nvm = mtd->priv;
|
||||
unsigned int idx;
|
||||
ssize_t ret;
|
||||
u8 region;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return -EINVAL;
|
||||
|
||||
idx = idg_nvm_get_region(nvm, from);
|
||||
|
||||
dev_dbg(&mtd->dev, "reading region[%d] %s from %lld len %zd\n",
|
||||
nvm->regions[idx].id, nvm->regions[idx].name, from, len);
|
||||
|
||||
if (idx >= nvm->nregions) {
|
||||
dev_err(&mtd->dev, "out of range");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
from -= nvm->regions[idx].offset;
|
||||
region = nvm->regions[idx].id;
|
||||
if (len > nvm->regions[idx].size - from)
|
||||
len = nvm->regions[idx].size - from;
|
||||
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
ret = idg_read(nvm, region, from, len, buf);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&mtd->dev, "read failed with %zd\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*retlen = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct intel_dg_nvm *nvm = mtd->priv;
|
||||
unsigned int idx;
|
||||
ssize_t ret;
|
||||
u8 region;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return -EINVAL;
|
||||
|
||||
idx = idg_nvm_get_region(nvm, to);
|
||||
|
||||
dev_dbg(&mtd->dev, "writing region[%d] %s to %lld len %zd\n",
|
||||
nvm->regions[idx].id, nvm->regions[idx].name, to, len);
|
||||
|
||||
if (idx >= nvm->nregions) {
|
||||
dev_err(&mtd->dev, "out of range");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
to -= nvm->regions[idx].offset;
|
||||
region = nvm->regions[idx].id;
|
||||
if (len > nvm->regions[idx].size - to)
|
||||
len = nvm->regions[idx].size - to;
|
||||
|
||||
guard(mutex)(&nvm->lock);
|
||||
|
||||
ret = idg_write(nvm, region, to, len, buf);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&mtd->dev, "write failed with %zd\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*retlen = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_dg_nvm_release(struct kref *kref)
|
||||
{
|
||||
struct intel_dg_nvm *nvm = container_of(kref, struct intel_dg_nvm, refcnt);
|
||||
int i;
|
||||
|
||||
pr_debug("freeing intel_dg nvm\n");
|
||||
for (i = 0; i < nvm->nregions; i++)
|
||||
kfree(nvm->regions[i].name);
|
||||
mutex_destroy(&nvm->lock);
|
||||
kfree(nvm);
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_get_device(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_info *master = mtd_get_master(mtd);
|
||||
struct intel_dg_nvm *nvm = master->priv;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return -EINVAL;
|
||||
pr_debug("get mtd %s %d\n", mtd->name, kref_read(&nvm->refcnt));
|
||||
kref_get(&nvm->refcnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_dg_mtd_put_device(struct mtd_info *mtd)
|
||||
{
|
||||
struct mtd_info *master = mtd_get_master(mtd);
|
||||
struct intel_dg_nvm *nvm = master->priv;
|
||||
|
||||
if (WARN_ON(!nvm))
|
||||
return;
|
||||
pr_debug("put mtd %s %d\n", mtd->name, kref_read(&nvm->refcnt));
|
||||
kref_put(&nvm->refcnt, intel_dg_nvm_release);
|
||||
}
|
||||
|
||||
static int intel_dg_nvm_init_mtd(struct intel_dg_nvm *nvm, struct device *device,
|
||||
unsigned int nparts, bool writable_override)
|
||||
{
|
||||
struct mtd_partition *parts = NULL;
|
||||
unsigned int i, n;
|
||||
int ret;
|
||||
|
||||
dev_dbg(device, "registering with mtd\n");
|
||||
|
||||
nvm->mtd.owner = THIS_MODULE;
|
||||
nvm->mtd.dev.parent = device;
|
||||
nvm->mtd.flags = MTD_CAP_NORFLASH;
|
||||
nvm->mtd.type = MTD_DATAFLASH;
|
||||
nvm->mtd.priv = nvm;
|
||||
nvm->mtd._write = intel_dg_mtd_write;
|
||||
nvm->mtd._read = intel_dg_mtd_read;
|
||||
nvm->mtd._erase = intel_dg_mtd_erase;
|
||||
nvm->mtd._get_device = intel_dg_mtd_get_device;
|
||||
nvm->mtd._put_device = intel_dg_mtd_put_device;
|
||||
nvm->mtd.writesize = SZ_1; /* 1 byte granularity */
|
||||
nvm->mtd.erasesize = SZ_4K; /* 4K bytes granularity */
|
||||
nvm->mtd.size = nvm->size;
|
||||
|
||||
parts = kcalloc(nvm->nregions, sizeof(*parts), GFP_KERNEL);
|
||||
if (!parts)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0, n = 0; i < nvm->nregions && n < nparts; i++) {
|
||||
if (!nvm->regions[i].is_readable)
|
||||
continue;
|
||||
parts[n].name = nvm->regions[i].name;
|
||||
parts[n].offset = nvm->regions[i].offset;
|
||||
parts[n].size = nvm->regions[i].size;
|
||||
if (!nvm->regions[i].is_writable && !writable_override)
|
||||
parts[n].mask_flags = MTD_WRITEABLE;
|
||||
n++;
|
||||
}
|
||||
|
||||
ret = mtd_device_register(&nvm->mtd, parts, n);
|
||||
|
||||
kfree(parts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
|
||||
const struct auxiliary_device_id *aux_dev_id)
|
||||
{
|
||||
struct intel_dg_nvm_dev *invm = auxiliary_dev_to_intel_dg_nvm_dev(aux_dev);
|
||||
struct intel_dg_nvm *nvm;
|
||||
struct device *device;
|
||||
unsigned int nregions;
|
||||
unsigned int i, n;
|
||||
int ret;
|
||||
|
||||
device = &aux_dev->dev;
|
||||
|
||||
/* count available regions */
|
||||
for (nregions = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
|
||||
if (invm->regions[i].name)
|
||||
nregions++;
|
||||
}
|
||||
|
||||
if (!nregions) {
|
||||
dev_err(device, "no regions defined\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
nvm = kzalloc(struct_size(nvm, regions, nregions), GFP_KERNEL);
|
||||
if (!nvm)
|
||||
return -ENOMEM;
|
||||
|
||||
kref_init(&nvm->refcnt);
|
||||
mutex_init(&nvm->lock);
|
||||
|
||||
for (n = 0, i = 0; i < INTEL_DG_NVM_REGIONS; i++) {
|
||||
if (!invm->regions[i].name)
|
||||
continue;
|
||||
|
||||
char *name = kasprintf(GFP_KERNEL, "%s.%s",
|
||||
dev_name(&aux_dev->dev), invm->regions[i].name);
|
||||
if (!name)
|
||||
continue;
|
||||
nvm->regions[n].name = name;
|
||||
nvm->regions[n].id = i;
|
||||
n++;
|
||||
}
|
||||
nvm->nregions = n; /* in case where kasprintf fail */
|
||||
|
||||
nvm->base = devm_ioremap_resource(device, &invm->bar);
|
||||
if (IS_ERR(nvm->base)) {
|
||||
ret = PTR_ERR(nvm->base);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (invm->non_posted_erase) {
|
||||
nvm->base2 = devm_ioremap_resource(device, &invm->bar2);
|
||||
if (IS_ERR(nvm->base2)) {
|
||||
ret = PTR_ERR(nvm->base2);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ret = intel_dg_nvm_init(nvm, device, invm->non_posted_erase);
|
||||
if (ret < 0) {
|
||||
dev_err(device, "cannot initialize nvm %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = intel_dg_nvm_init_mtd(nvm, device, ret, invm->writable_override);
|
||||
if (ret) {
|
||||
dev_err(device, "failed init mtd %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&aux_dev->dev, nvm);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kref_put(&nvm->refcnt, intel_dg_nvm_release);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void intel_dg_mtd_remove(struct auxiliary_device *aux_dev)
|
||||
{
|
||||
struct intel_dg_nvm *nvm = dev_get_drvdata(&aux_dev->dev);
|
||||
|
||||
if (!nvm)
|
||||
return;
|
||||
|
||||
mtd_device_unregister(&nvm->mtd);
|
||||
|
||||
dev_set_drvdata(&aux_dev->dev, NULL);
|
||||
|
||||
kref_put(&nvm->refcnt, intel_dg_nvm_release);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id intel_dg_mtd_id_table[] = {
|
||||
{
|
||||
.name = "i915.nvm",
|
||||
},
|
||||
{
|
||||
.name = "xe.nvm",
|
||||
},
|
||||
{
|
||||
/* sentinel */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, intel_dg_mtd_id_table);
|
||||
|
||||
static struct auxiliary_driver intel_dg_mtd_driver = {
|
||||
.probe = intel_dg_mtd_probe,
|
||||
.remove = intel_dg_mtd_remove,
|
||||
.driver = {
|
||||
/* auxiliary_driver_register() sets .name to be the modname */
|
||||
},
|
||||
.id_table = intel_dg_mtd_id_table
|
||||
};
|
||||
module_auxiliary_driver(intel_dg_mtd_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel DGFX MTD driver");
|
||||
@@ -16,91 +16,9 @@ struct drm_gpusvm;
|
||||
struct drm_gpusvm_notifier;
|
||||
struct drm_gpusvm_ops;
|
||||
struct drm_gpusvm_range;
|
||||
struct drm_gpusvm_devmem;
|
||||
struct drm_pagemap;
|
||||
struct drm_pagemap_device_addr;
|
||||
|
||||
/**
|
||||
* struct drm_gpusvm_devmem_ops - Operations structure for GPU SVM device memory
|
||||
*
|
||||
* This structure defines the operations for GPU Shared Virtual Memory (SVM)
|
||||
* device memory. These operations are provided by the GPU driver to manage device memory
|
||||
* allocations and perform operations such as migration between device memory and system
|
||||
* RAM.
|
||||
*/
|
||||
struct drm_gpusvm_devmem_ops {
|
||||
/**
|
||||
* @devmem_release: Release device memory allocation (optional)
|
||||
* @devmem_allocation: device memory allocation
|
||||
*
|
||||
* Release device memory allocation and drop a reference to device
|
||||
* memory allocation.
|
||||
*/
|
||||
void (*devmem_release)(struct drm_gpusvm_devmem *devmem_allocation);
|
||||
|
||||
/**
|
||||
* @populate_devmem_pfn: Populate device memory PFN (required for migration)
|
||||
* @devmem_allocation: device memory allocation
|
||||
* @npages: Number of pages to populate
|
||||
* @pfn: Array of page frame numbers to populate
|
||||
*
|
||||
* Populate device memory page frame numbers (PFN).
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*populate_devmem_pfn)(struct drm_gpusvm_devmem *devmem_allocation,
|
||||
unsigned long npages, unsigned long *pfn);
|
||||
|
||||
/**
|
||||
* @copy_to_devmem: Copy to device memory (required for migration)
|
||||
* @pages: Pointer to array of device memory pages (destination)
|
||||
* @dma_addr: Pointer to array of DMA addresses (source)
|
||||
* @npages: Number of pages to copy
|
||||
*
|
||||
* Copy pages to device memory.
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*copy_to_devmem)(struct page **pages,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages);
|
||||
|
||||
/**
|
||||
* @copy_to_ram: Copy to system RAM (required for migration)
|
||||
* @pages: Pointer to array of device memory pages (source)
|
||||
* @dma_addr: Pointer to array of DMA addresses (destination)
|
||||
* @npages: Number of pages to copy
|
||||
*
|
||||
* Copy pages to system RAM.
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*copy_to_ram)(struct page **pages,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_gpusvm_devmem - Structure representing a GPU SVM device memory allocation
|
||||
*
|
||||
* @dev: Pointer to the device structure which device memory allocation belongs to
|
||||
* @mm: Pointer to the mm_struct for the address space
|
||||
* @detached: device memory allocations is detached from device pages
|
||||
* @ops: Pointer to the operations structure for GPU SVM device memory
|
||||
* @dpagemap: The struct drm_pagemap of the pages this allocation belongs to.
|
||||
* @size: Size of device memory allocation
|
||||
* @timeslice_expiration: Timeslice expiration in jiffies
|
||||
*/
|
||||
struct drm_gpusvm_devmem {
|
||||
struct device *dev;
|
||||
struct mm_struct *mm;
|
||||
struct completion detached;
|
||||
const struct drm_gpusvm_devmem_ops *ops;
|
||||
struct drm_pagemap *dpagemap;
|
||||
size_t size;
|
||||
u64 timeslice_expiration;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_gpusvm_ops - Operations structure for GPU SVM
|
||||
*
|
||||
@@ -361,15 +279,6 @@ void drm_gpusvm_range_unmap_pages(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range,
|
||||
const struct drm_gpusvm_ctx *ctx);
|
||||
|
||||
int drm_gpusvm_migrate_to_devmem(struct drm_gpusvm *gpusvm,
|
||||
struct drm_gpusvm_range *range,
|
||||
struct drm_gpusvm_devmem *devmem_allocation,
|
||||
const struct drm_gpusvm_ctx *ctx);
|
||||
|
||||
int drm_gpusvm_evict_to_ram(struct drm_gpusvm_devmem *devmem_allocation);
|
||||
|
||||
const struct dev_pagemap_ops *drm_gpusvm_pagemap_ops_get(void);
|
||||
|
||||
bool drm_gpusvm_has_mapping(struct drm_gpusvm *gpusvm, unsigned long start,
|
||||
unsigned long end);
|
||||
|
||||
@@ -380,11 +289,6 @@ drm_gpusvm_range_find(struct drm_gpusvm_notifier *notifier, unsigned long start,
|
||||
void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range,
|
||||
const struct mmu_notifier_range *mmu_range);
|
||||
|
||||
void drm_gpusvm_devmem_init(struct drm_gpusvm_devmem *devmem_allocation,
|
||||
struct device *dev, struct mm_struct *mm,
|
||||
const struct drm_gpusvm_devmem_ops *ops,
|
||||
struct drm_pagemap *dpagemap, size_t size);
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
/**
|
||||
* drm_gpusvm_driver_set_lock() - Set the lock protecting accesses to GPU SVM
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
struct drm_pagemap;
|
||||
struct drm_pagemap_zdd;
|
||||
struct device;
|
||||
|
||||
/**
|
||||
@@ -91,6 +92,35 @@ struct drm_pagemap_ops {
|
||||
struct device *dev,
|
||||
struct drm_pagemap_device_addr addr);
|
||||
|
||||
/**
|
||||
* @populate_mm: Populate part of the mm with @dpagemap memory,
|
||||
* migrating existing data.
|
||||
* @dpagemap: The struct drm_pagemap managing the memory.
|
||||
* @start: The virtual start address in @mm
|
||||
* @end: The virtual end address in @mm
|
||||
* @mm: Pointer to a live mm. The caller must have an mmget()
|
||||
* reference.
|
||||
*
|
||||
* The caller will have the mm lock at least in read mode.
|
||||
* Note that there is no guarantee that the memory is resident
|
||||
* after the function returns, it's best effort only.
|
||||
* When the mm is not using the memory anymore,
|
||||
* it will be released. The struct drm_pagemap might have a
|
||||
* mechanism in place to reclaim the memory and the data will
|
||||
* then be migrated. Typically to system memory.
|
||||
* The implementation should hold sufficient runtime power-
|
||||
* references while pages are used in an address space and
|
||||
* should ideally guard against hardware device unbind in
|
||||
* a way such that device pages are migrated back to system
|
||||
* followed by device page removal. The implementation should
|
||||
* return -ENODEV after device removal.
|
||||
*
|
||||
* Return: 0 if successful. Negative error code on error.
|
||||
*/
|
||||
int (*populate_mm)(struct drm_pagemap *dpagemap,
|
||||
unsigned long start, unsigned long end,
|
||||
struct mm_struct *mm,
|
||||
unsigned long timeslice_ms);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -104,4 +134,109 @@ struct drm_pagemap {
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
struct drm_pagemap_devmem;
|
||||
|
||||
/**
|
||||
* struct drm_pagemap_devmem_ops - Operations structure for GPU SVM device memory
|
||||
*
|
||||
* This structure defines the operations for GPU Shared Virtual Memory (SVM)
|
||||
* device memory. These operations are provided by the GPU driver to manage device memory
|
||||
* allocations and perform operations such as migration between device memory and system
|
||||
* RAM.
|
||||
*/
|
||||
struct drm_pagemap_devmem_ops {
|
||||
/**
|
||||
* @devmem_release: Release device memory allocation (optional)
|
||||
* @devmem_allocation: device memory allocation
|
||||
*
|
||||
* Release device memory allocation and drop a reference to device
|
||||
* memory allocation.
|
||||
*/
|
||||
void (*devmem_release)(struct drm_pagemap_devmem *devmem_allocation);
|
||||
|
||||
/**
|
||||
* @populate_devmem_pfn: Populate device memory PFN (required for migration)
|
||||
* @devmem_allocation: device memory allocation
|
||||
* @npages: Number of pages to populate
|
||||
* @pfn: Array of page frame numbers to populate
|
||||
*
|
||||
* Populate device memory page frame numbers (PFN).
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*populate_devmem_pfn)(struct drm_pagemap_devmem *devmem_allocation,
|
||||
unsigned long npages, unsigned long *pfn);
|
||||
|
||||
/**
|
||||
* @copy_to_devmem: Copy to device memory (required for migration)
|
||||
* @pages: Pointer to array of device memory pages (destination)
|
||||
* @dma_addr: Pointer to array of DMA addresses (source)
|
||||
* @npages: Number of pages to copy
|
||||
*
|
||||
* Copy pages to device memory.
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*copy_to_devmem)(struct page **pages,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages);
|
||||
|
||||
/**
|
||||
* @copy_to_ram: Copy to system RAM (required for migration)
|
||||
* @pages: Pointer to array of device memory pages (source)
|
||||
* @dma_addr: Pointer to array of DMA addresses (destination)
|
||||
* @npages: Number of pages to copy
|
||||
*
|
||||
* Copy pages to system RAM.
|
||||
*
|
||||
* Return: 0 on success, a negative error code on failure.
|
||||
*/
|
||||
int (*copy_to_ram)(struct page **pages,
|
||||
dma_addr_t *dma_addr,
|
||||
unsigned long npages);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation
|
||||
*
|
||||
* @dev: Pointer to the device structure which device memory allocation belongs to
|
||||
* @mm: Pointer to the mm_struct for the address space
|
||||
* @detached: device memory allocations is detached from device pages
|
||||
* @ops: Pointer to the operations structure for GPU SVM device memory
|
||||
* @dpagemap: The struct drm_pagemap of the pages this allocation belongs to.
|
||||
* @size: Size of device memory allocation
|
||||
* @timeslice_expiration: Timeslice expiration in jiffies
|
||||
*/
|
||||
struct drm_pagemap_devmem {
|
||||
struct device *dev;
|
||||
struct mm_struct *mm;
|
||||
struct completion detached;
|
||||
const struct drm_pagemap_devmem_ops *ops;
|
||||
struct drm_pagemap *dpagemap;
|
||||
size_t size;
|
||||
u64 timeslice_expiration;
|
||||
};
|
||||
|
||||
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
|
||||
struct mm_struct *mm,
|
||||
unsigned long start, unsigned long end,
|
||||
unsigned long timeslice_ms,
|
||||
void *pgmap_owner);
|
||||
|
||||
int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation);
|
||||
|
||||
const struct dev_pagemap_ops *drm_pagemap_pagemap_ops_get(void);
|
||||
|
||||
struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
|
||||
|
||||
void drm_pagemap_devmem_init(struct drm_pagemap_devmem *devmem_allocation,
|
||||
struct device *dev, struct mm_struct *mm,
|
||||
const struct drm_pagemap_devmem_ops *ops,
|
||||
struct drm_pagemap *dpagemap, size_t size);
|
||||
|
||||
int drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
|
||||
unsigned long start, unsigned long end,
|
||||
struct mm_struct *mm,
|
||||
unsigned long timeslice_ms);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -846,6 +846,7 @@
|
||||
/* BMG */
|
||||
#define INTEL_BMG_IDS(MACRO__, ...) \
|
||||
MACRO__(0xE202, ## __VA_ARGS__), \
|
||||
MACRO__(0xE209, ## __VA_ARGS__), \
|
||||
MACRO__(0xE20B, ## __VA_ARGS__), \
|
||||
MACRO__(0xE20C, ## __VA_ARGS__), \
|
||||
MACRO__(0xE20D, ## __VA_ARGS__), \
|
||||
@@ -871,6 +872,8 @@
|
||||
MACRO__(0xB08F, ## __VA_ARGS__), \
|
||||
MACRO__(0xB090, ## __VA_ARGS__), \
|
||||
MACRO__(0xB0A0, ## __VA_ARGS__), \
|
||||
MACRO__(0xB0B0, ## __VA_ARGS__)
|
||||
MACRO__(0xB0B0, ## __VA_ARGS__), \
|
||||
MACRO__(0xFD80, ## __VA_ARGS__), \
|
||||
MACRO__(0xFD81, ## __VA_ARGS__)
|
||||
|
||||
#endif /* __PCIIDS_H__ */
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright(c) 2019-2025, Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_DG_NVM_AUX_H__
|
||||
#define __INTEL_DG_NVM_AUX_H__
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/container_of.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define INTEL_DG_NVM_REGIONS 13
|
||||
|
||||
struct intel_dg_nvm_region {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct intel_dg_nvm_dev {
|
||||
struct auxiliary_device aux_dev;
|
||||
bool writable_override;
|
||||
bool non_posted_erase;
|
||||
struct resource bar;
|
||||
struct resource bar2;
|
||||
const struct intel_dg_nvm_region *regions;
|
||||
};
|
||||
|
||||
#define auxiliary_dev_to_intel_dg_nvm_dev(auxiliary_dev) \
|
||||
container_of(auxiliary_dev, struct intel_dg_nvm_dev, aux_dev)
|
||||
|
||||
#endif /* __INTEL_DG_NVM_AUX_H__ */
|
||||
@@ -925,9 +925,9 @@ struct drm_xe_gem_mmap_offset {
|
||||
* - %DRM_XE_VM_CREATE_FLAG_LR_MODE - An LR, or Long Running VM accepts
|
||||
* exec submissions to its exec_queues that don't have an upper time
|
||||
* limit on the job execution time. But exec submissions to these
|
||||
* don't allow any of the flags DRM_XE_SYNC_FLAG_SYNCOBJ,
|
||||
* DRM_XE_SYNC_FLAG_TIMELINE_SYNCOBJ, DRM_XE_SYNC_FLAG_DMA_BUF,
|
||||
* used as out-syncobjs, that is, together with DRM_XE_SYNC_FLAG_SIGNAL.
|
||||
* don't allow any of the sync types DRM_XE_SYNC_TYPE_SYNCOBJ,
|
||||
* DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ, used as out-syncobjs, that is,
|
||||
* together with sync flag DRM_XE_SYNC_FLAG_SIGNAL.
|
||||
* LR VMs can be created in recoverable page-fault mode using
|
||||
* DRM_XE_VM_CREATE_FLAG_FAULT_MODE, if the device supports it.
|
||||
* If that flag is omitted, the UMD can not rely on the slightly
|
||||
@@ -1394,7 +1394,7 @@ struct drm_xe_sync {
|
||||
|
||||
/**
|
||||
* @timeline_value: Input for the timeline sync object. Needs to be
|
||||
* different than 0 when used with %DRM_XE_SYNC_FLAG_TIMELINE_SYNCOBJ.
|
||||
* different than 0 when used with %DRM_XE_SYNC_TYPE_TIMELINE_SYNCOBJ.
|
||||
*/
|
||||
__u64 timeline_value;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user