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(>->uc.guc.fw); 2258c2ecf20Sopenharmony_ci u32 huc_fw_size = intel_uc_fw_get_upload_size(>->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