18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: MIT
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright © 2017-2019 Intel Corporation
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "intel_wopcm.h"
78c2ecf20Sopenharmony_ci#include "i915_drv.h"
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/**
108c2ecf20Sopenharmony_ci * DOC: WOPCM Layout
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
138c2ecf20Sopenharmony_ci * offset registers whose values are calculated and determined by HuC/GuC
148c2ecf20Sopenharmony_ci * firmware size and set of hardware requirements/restrictions as shown below:
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * ::
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *    +=========> +====================+ <== WOPCM Top
198c2ecf20Sopenharmony_ci *    ^           |  HW contexts RSVD  |
208c2ecf20Sopenharmony_ci *    |     +===> +====================+ <== GuC WOPCM Top
218c2ecf20Sopenharmony_ci *    |     ^     |                    |
228c2ecf20Sopenharmony_ci *    |     |     |                    |
238c2ecf20Sopenharmony_ci *    |     |     |                    |
248c2ecf20Sopenharmony_ci *    |    GuC    |                    |
258c2ecf20Sopenharmony_ci *    |   WOPCM   |                    |
268c2ecf20Sopenharmony_ci *    |    Size   +--------------------+
278c2ecf20Sopenharmony_ci *  WOPCM   |     |    GuC FW RSVD     |
288c2ecf20Sopenharmony_ci *    |     |     +--------------------+
298c2ecf20Sopenharmony_ci *    |     |     |   GuC Stack RSVD   |
308c2ecf20Sopenharmony_ci *    |     |     +------------------- +
318c2ecf20Sopenharmony_ci *    |     v     |   GuC WOPCM RSVD   |
328c2ecf20Sopenharmony_ci *    |     +===> +====================+ <== GuC WOPCM base
338c2ecf20Sopenharmony_ci *    |           |     WOPCM RSVD     |
348c2ecf20Sopenharmony_ci *    |           +------------------- + <== HuC Firmware Top
358c2ecf20Sopenharmony_ci *    v           |      HuC FW        |
368c2ecf20Sopenharmony_ci *    +=========> +====================+ <== WOPCM Base
378c2ecf20Sopenharmony_ci *
388c2ecf20Sopenharmony_ci * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
398c2ecf20Sopenharmony_ci * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
408c2ecf20Sopenharmony_ci * context).
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
448c2ecf20Sopenharmony_ci#define GEN11_WOPCM_SIZE		SZ_2M
458c2ecf20Sopenharmony_ci#define GEN9_WOPCM_SIZE			SZ_1M
468c2ecf20Sopenharmony_ci/* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
478c2ecf20Sopenharmony_ci#define WOPCM_RESERVED_SIZE		SZ_16K
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* 16KB reserved at the beginning of GuC WOPCM. */
508c2ecf20Sopenharmony_ci#define GUC_WOPCM_RESERVED		SZ_16K
518c2ecf20Sopenharmony_ci/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
528c2ecf20Sopenharmony_ci#define GUC_WOPCM_STACK_RESERVED	SZ_8K
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* GuC WOPCM Offset value needs to be aligned to 16KB. */
558c2ecf20Sopenharmony_ci#define GUC_WOPCM_OFFSET_ALIGNMENT	(1UL << GUC_WOPCM_OFFSET_SHIFT)
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
588c2ecf20Sopenharmony_ci#define BXT_WOPCM_RC6_CTX_RESERVED	(SZ_16K + SZ_8K)
598c2ecf20Sopenharmony_ci/* 36KB WOPCM reserved at the end of WOPCM on CNL. */
608c2ecf20Sopenharmony_ci#define CNL_WOPCM_HW_CTX_RESERVED	(SZ_32K + SZ_4K)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
638c2ecf20Sopenharmony_ci#define GEN9_GUC_FW_RESERVED	SZ_128K
648c2ecf20Sopenharmony_ci#define GEN9_GUC_WOPCM_OFFSET	(GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic inline struct drm_i915_private *wopcm_to_i915(struct intel_wopcm *wopcm)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	return container_of(wopcm, struct drm_i915_private, wopcm);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/**
728c2ecf20Sopenharmony_ci * intel_wopcm_init_early() - Early initialization of the WOPCM.
738c2ecf20Sopenharmony_ci * @wopcm: pointer to intel_wopcm.
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_civoid intel_wopcm_init_early(struct intel_wopcm *wopcm)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (!HAS_GT_UC(i915))
828c2ecf20Sopenharmony_ci		return;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 11)
858c2ecf20Sopenharmony_ci		wopcm->size = GEN11_WOPCM_SIZE;
868c2ecf20Sopenharmony_ci	else
878c2ecf20Sopenharmony_ci		wopcm->size = GEN9_WOPCM_SIZE;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "WOPCM: %uK\n", wopcm->size / 1024);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic u32 context_reserved_size(struct drm_i915_private *i915)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	if (IS_GEN9_LP(i915))
958c2ecf20Sopenharmony_ci		return BXT_WOPCM_RC6_CTX_RESERVED;
968c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 10)
978c2ecf20Sopenharmony_ci		return CNL_WOPCM_HW_CTX_RESERVED;
988c2ecf20Sopenharmony_ci	else
998c2ecf20Sopenharmony_ci		return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic bool gen9_check_dword_gap(struct drm_i915_private *i915,
1038c2ecf20Sopenharmony_ci				 u32 guc_wopcm_base, u32 guc_wopcm_size)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	u32 offset;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/*
1088c2ecf20Sopenharmony_ci	 * GuC WOPCM size shall be at least a dword larger than the offset from
1098c2ecf20Sopenharmony_ci	 * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
1108c2ecf20Sopenharmony_ci	 * due to hardware limitation on Gen9.
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
1138c2ecf20Sopenharmony_ci	if (offset > guc_wopcm_size ||
1148c2ecf20Sopenharmony_ci	    (guc_wopcm_size - offset) < sizeof(u32)) {
1158c2ecf20Sopenharmony_ci		drm_err(&i915->drm,
1168c2ecf20Sopenharmony_ci			"WOPCM: invalid GuC region size: %uK < %uK\n",
1178c2ecf20Sopenharmony_ci			guc_wopcm_size / SZ_1K,
1188c2ecf20Sopenharmony_ci			(u32)(offset + sizeof(u32)) / SZ_1K);
1198c2ecf20Sopenharmony_ci		return false;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return true;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic bool gen9_check_huc_fw_fits(struct drm_i915_private *i915,
1268c2ecf20Sopenharmony_ci				   u32 guc_wopcm_size, u32 huc_fw_size)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	/*
1298c2ecf20Sopenharmony_ci	 * On Gen9 & CNL A0, hardware requires the total available GuC WOPCM
1308c2ecf20Sopenharmony_ci	 * size to be larger than or equal to HuC firmware size. Otherwise,
1318c2ecf20Sopenharmony_ci	 * firmware uploading would fail.
1328c2ecf20Sopenharmony_ci	 */
1338c2ecf20Sopenharmony_ci	if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
1348c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
1358c2ecf20Sopenharmony_ci			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
1368c2ecf20Sopenharmony_ci			(guc_wopcm_size - GUC_WOPCM_RESERVED) / SZ_1K,
1378c2ecf20Sopenharmony_ci			huc_fw_size / 1024);
1388c2ecf20Sopenharmony_ci		return false;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return true;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic bool check_hw_restrictions(struct drm_i915_private *i915,
1458c2ecf20Sopenharmony_ci				  u32 guc_wopcm_base, u32 guc_wopcm_size,
1468c2ecf20Sopenharmony_ci				  u32 huc_fw_size)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	if (IS_GEN(i915, 9) && !gen9_check_dword_gap(i915, guc_wopcm_base,
1498c2ecf20Sopenharmony_ci						     guc_wopcm_size))
1508c2ecf20Sopenharmony_ci		return false;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (IS_GEN(i915, 9) &&
1538c2ecf20Sopenharmony_ci	    !gen9_check_huc_fw_fits(i915, guc_wopcm_size, huc_fw_size))
1548c2ecf20Sopenharmony_ci		return false;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return true;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic bool __check_layout(struct drm_i915_private *i915, u32 wopcm_size,
1608c2ecf20Sopenharmony_ci			   u32 guc_wopcm_base, u32 guc_wopcm_size,
1618c2ecf20Sopenharmony_ci			   u32 guc_fw_size, u32 huc_fw_size)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	const u32 ctx_rsvd = context_reserved_size(i915);
1648c2ecf20Sopenharmony_ci	u32 size;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	size = wopcm_size - ctx_rsvd;
1678c2ecf20Sopenharmony_ci	if (unlikely(range_overflows(guc_wopcm_base, guc_wopcm_size, size))) {
1688c2ecf20Sopenharmony_ci		drm_err(&i915->drm,
1698c2ecf20Sopenharmony_ci			"WOPCM: invalid GuC region layout: %uK + %uK > %uK\n",
1708c2ecf20Sopenharmony_ci			guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K,
1718c2ecf20Sopenharmony_ci			size / SZ_1K);
1728c2ecf20Sopenharmony_ci		return false;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
1768c2ecf20Sopenharmony_ci	if (unlikely(guc_wopcm_size < size)) {
1778c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
1788c2ecf20Sopenharmony_ci			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_GUC),
1798c2ecf20Sopenharmony_ci			guc_wopcm_size / SZ_1K, size / SZ_1K);
1808c2ecf20Sopenharmony_ci		return false;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	size = huc_fw_size + WOPCM_RESERVED_SIZE;
1848c2ecf20Sopenharmony_ci	if (unlikely(guc_wopcm_base < size)) {
1858c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n",
1868c2ecf20Sopenharmony_ci			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
1878c2ecf20Sopenharmony_ci			guc_wopcm_base / SZ_1K, size / SZ_1K);
1888c2ecf20Sopenharmony_ci		return false;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return check_hw_restrictions(i915, guc_wopcm_base, guc_wopcm_size,
1928c2ecf20Sopenharmony_ci				     huc_fw_size);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic bool __wopcm_regs_locked(struct intel_uncore *uncore,
1968c2ecf20Sopenharmony_ci				u32 *guc_wopcm_base, u32 *guc_wopcm_size)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	u32 reg_base = intel_uncore_read(uncore, DMA_GUC_WOPCM_OFFSET);
1998c2ecf20Sopenharmony_ci	u32 reg_size = intel_uncore_read(uncore, GUC_WOPCM_SIZE);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) ||
2028c2ecf20Sopenharmony_ci	    !(reg_base & GUC_WOPCM_OFFSET_VALID))
2038c2ecf20Sopenharmony_ci		return false;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	*guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK;
2068c2ecf20Sopenharmony_ci	*guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK;
2078c2ecf20Sopenharmony_ci	return true;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci/**
2118c2ecf20Sopenharmony_ci * intel_wopcm_init() - Initialize the WOPCM structure.
2128c2ecf20Sopenharmony_ci * @wopcm: pointer to intel_wopcm.
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * This function will partition WOPCM space based on GuC and HuC firmware sizes
2158c2ecf20Sopenharmony_ci * and will allocate max remaining for use by GuC. This function will also
2168c2ecf20Sopenharmony_ci * enforce platform dependent hardware restrictions on GuC WOPCM offset and
2178c2ecf20Sopenharmony_ci * size. It will fail the WOPCM init if any of these checks fail, so that the
2188c2ecf20Sopenharmony_ci * following WOPCM registers setup and GuC firmware uploading would be aborted.
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_civoid intel_wopcm_init(struct intel_wopcm *wopcm)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
2238c2ecf20Sopenharmony_ci	struct intel_gt *gt = &i915->gt;
2248c2ecf20Sopenharmony_ci	u32 guc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.guc.fw);
2258c2ecf20Sopenharmony_ci	u32 huc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.huc.fw);
2268c2ecf20Sopenharmony_ci	u32 ctx_rsvd = context_reserved_size(i915);
2278c2ecf20Sopenharmony_ci	u32 guc_wopcm_base;
2288c2ecf20Sopenharmony_ci	u32 guc_wopcm_size;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	if (!guc_fw_size)
2318c2ecf20Sopenharmony_ci		return;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	GEM_BUG_ON(!wopcm->size);
2348c2ecf20Sopenharmony_ci	GEM_BUG_ON(wopcm->guc.base);
2358c2ecf20Sopenharmony_ci	GEM_BUG_ON(wopcm->guc.size);
2368c2ecf20Sopenharmony_ci	GEM_BUG_ON(guc_fw_size >= wopcm->size);
2378c2ecf20Sopenharmony_ci	GEM_BUG_ON(huc_fw_size >= wopcm->size);
2388c2ecf20Sopenharmony_ci	GEM_BUG_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (i915_inject_probe_failure(i915))
2418c2ecf20Sopenharmony_ci		return;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (__wopcm_regs_locked(gt->uncore, &guc_wopcm_base, &guc_wopcm_size)) {
2448c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm, "GuC WOPCM is already locked [%uK, %uK)\n",
2458c2ecf20Sopenharmony_ci			guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
2468c2ecf20Sopenharmony_ci		goto check;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/*
2508c2ecf20Sopenharmony_ci	 * Aligned value of guc_wopcm_base will determine available WOPCM space
2518c2ecf20Sopenharmony_ci	 * for HuC firmware and mandatory reserved area.
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci	guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE;
2548c2ecf20Sopenharmony_ci	guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * Need to clamp guc_wopcm_base now to make sure the following math is
2588c2ecf20Sopenharmony_ci	 * correct. Formal check of whole WOPCM layout will be done below.
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci	guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/* Aligned remainings of usable WOPCM space can be assigned to GuC. */
2638c2ecf20Sopenharmony_ci	guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base;
2648c2ecf20Sopenharmony_ci	guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "Calculated GuC WOPCM [%uK, %uK)\n",
2678c2ecf20Sopenharmony_ci		guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cicheck:
2708c2ecf20Sopenharmony_ci	if (__check_layout(i915, wopcm->size, guc_wopcm_base, guc_wopcm_size,
2718c2ecf20Sopenharmony_ci			   guc_fw_size, huc_fw_size)) {
2728c2ecf20Sopenharmony_ci		wopcm->guc.base = guc_wopcm_base;
2738c2ecf20Sopenharmony_ci		wopcm->guc.size = guc_wopcm_size;
2748c2ecf20Sopenharmony_ci		GEM_BUG_ON(!wopcm->guc.base);
2758c2ecf20Sopenharmony_ci		GEM_BUG_ON(!wopcm->guc.size);
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci}
278