Home | History | Annotate | Line # | Download | only in i915
      1  1.3  riastrad /*	$NetBSD: intel_wopcm.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $	*/
      2  1.1  riastrad 
      3  1.1  riastrad // SPDX-License-Identifier: MIT
      4  1.1  riastrad /*
      5  1.1  riastrad  * Copyright  2017-2019 Intel Corporation
      6  1.1  riastrad  */
      7  1.1  riastrad 
      8  1.1  riastrad #include <sys/cdefs.h>
      9  1.3  riastrad __KERNEL_RCSID(0, "$NetBSD: intel_wopcm.c,v 1.3 2021/12/19 11:49:11 riastradh Exp $");
     10  1.1  riastrad 
     11  1.1  riastrad #include "intel_wopcm.h"
     12  1.1  riastrad #include "i915_drv.h"
     13  1.1  riastrad 
     14  1.3  riastrad #include <linux/nbsd-namespace.h>
     15  1.3  riastrad 
     16  1.1  riastrad /**
     17  1.1  riastrad  * DOC: WOPCM Layout
     18  1.1  riastrad  *
     19  1.1  riastrad  * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
     20  1.1  riastrad  * offset registers whose values are calculated and determined by HuC/GuC
     21  1.1  riastrad  * firmware size and set of hardware requirements/restrictions as shown below:
     22  1.1  riastrad  *
     23  1.1  riastrad  * ::
     24  1.1  riastrad  *
     25  1.1  riastrad  *    +=========> +====================+ <== WOPCM Top
     26  1.1  riastrad  *    ^           |  HW contexts RSVD  |
     27  1.1  riastrad  *    |     +===> +====================+ <== GuC WOPCM Top
     28  1.1  riastrad  *    |     ^     |                    |
     29  1.1  riastrad  *    |     |     |                    |
     30  1.1  riastrad  *    |     |     |                    |
     31  1.1  riastrad  *    |    GuC    |                    |
     32  1.1  riastrad  *    |   WOPCM   |                    |
     33  1.1  riastrad  *    |    Size   +--------------------+
     34  1.1  riastrad  *  WOPCM   |     |    GuC FW RSVD     |
     35  1.1  riastrad  *    |     |     +--------------------+
     36  1.1  riastrad  *    |     |     |   GuC Stack RSVD   |
     37  1.1  riastrad  *    |     |     +------------------- +
     38  1.1  riastrad  *    |     v     |   GuC WOPCM RSVD   |
     39  1.1  riastrad  *    |     +===> +====================+ <== GuC WOPCM base
     40  1.1  riastrad  *    |           |     WOPCM RSVD     |
     41  1.1  riastrad  *    |           +------------------- + <== HuC Firmware Top
     42  1.1  riastrad  *    v           |      HuC FW        |
     43  1.1  riastrad  *    +=========> +====================+ <== WOPCM Base
     44  1.1  riastrad  *
     45  1.1  riastrad  * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
     46  1.1  riastrad  * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
     47  1.1  riastrad  * context).
     48  1.1  riastrad  */
     49  1.1  riastrad 
     50  1.1  riastrad /* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
     51  1.1  riastrad #define GEN11_WOPCM_SIZE		SZ_2M
     52  1.1  riastrad #define GEN9_WOPCM_SIZE			SZ_1M
     53  1.1  riastrad /* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
     54  1.1  riastrad #define WOPCM_RESERVED_SIZE		SZ_16K
     55  1.1  riastrad 
     56  1.1  riastrad /* 16KB reserved at the beginning of GuC WOPCM. */
     57  1.1  riastrad #define GUC_WOPCM_RESERVED		SZ_16K
     58  1.1  riastrad /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
     59  1.1  riastrad #define GUC_WOPCM_STACK_RESERVED	SZ_8K
     60  1.1  riastrad 
     61  1.1  riastrad /* GuC WOPCM Offset value needs to be aligned to 16KB. */
     62  1.1  riastrad #define GUC_WOPCM_OFFSET_ALIGNMENT	(1UL << GUC_WOPCM_OFFSET_SHIFT)
     63  1.1  riastrad 
     64  1.1  riastrad /* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
     65  1.1  riastrad #define BXT_WOPCM_RC6_CTX_RESERVED	(SZ_16K + SZ_8K)
     66  1.1  riastrad /* 36KB WOPCM reserved at the end of WOPCM on CNL. */
     67  1.1  riastrad #define CNL_WOPCM_HW_CTX_RESERVED	(SZ_32K + SZ_4K)
     68  1.1  riastrad 
     69  1.1  riastrad /* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
     70  1.1  riastrad #define GEN9_GUC_FW_RESERVED	SZ_128K
     71  1.1  riastrad #define GEN9_GUC_WOPCM_OFFSET	(GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
     72  1.1  riastrad 
     73  1.1  riastrad static inline struct drm_i915_private *wopcm_to_i915(struct intel_wopcm *wopcm)
     74  1.1  riastrad {
     75  1.1  riastrad 	return container_of(wopcm, struct drm_i915_private, wopcm);
     76  1.1  riastrad }
     77  1.1  riastrad 
     78  1.1  riastrad /**
     79  1.1  riastrad  * intel_wopcm_init_early() - Early initialization of the WOPCM.
     80  1.1  riastrad  * @wopcm: pointer to intel_wopcm.
     81  1.1  riastrad  *
     82  1.1  riastrad  * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
     83  1.1  riastrad  */
     84  1.1  riastrad void intel_wopcm_init_early(struct intel_wopcm *wopcm)
     85  1.1  riastrad {
     86  1.1  riastrad 	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
     87  1.1  riastrad 
     88  1.1  riastrad 	if (!HAS_GT_UC(i915))
     89  1.1  riastrad 		return;
     90  1.1  riastrad 
     91  1.1  riastrad 	if (INTEL_GEN(i915) >= 11)
     92  1.1  riastrad 		wopcm->size = GEN11_WOPCM_SIZE;
     93  1.1  riastrad 	else
     94  1.1  riastrad 		wopcm->size = GEN9_WOPCM_SIZE;
     95  1.1  riastrad 
     96  1.1  riastrad 	DRM_DEV_DEBUG_DRIVER(i915->drm.dev, "WOPCM: %uK\n", wopcm->size / 1024);
     97  1.1  riastrad }
     98  1.1  riastrad 
     99  1.1  riastrad static inline u32 context_reserved_size(struct drm_i915_private *i915)
    100  1.1  riastrad {
    101  1.1  riastrad 	if (IS_GEN9_LP(i915))
    102  1.1  riastrad 		return BXT_WOPCM_RC6_CTX_RESERVED;
    103  1.1  riastrad 	else if (INTEL_GEN(i915) >= 10)
    104  1.1  riastrad 		return CNL_WOPCM_HW_CTX_RESERVED;
    105  1.1  riastrad 	else
    106  1.1  riastrad 		return 0;
    107  1.1  riastrad }
    108  1.1  riastrad 
    109  1.1  riastrad static inline bool gen9_check_dword_gap(struct drm_i915_private *i915,
    110  1.1  riastrad 					u32 guc_wopcm_base, u32 guc_wopcm_size)
    111  1.1  riastrad {
    112  1.1  riastrad 	u32 offset;
    113  1.1  riastrad 
    114  1.1  riastrad 	/*
    115  1.1  riastrad 	 * GuC WOPCM size shall be at least a dword larger than the offset from
    116  1.1  riastrad 	 * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
    117  1.1  riastrad 	 * due to hardware limitation on Gen9.
    118  1.1  riastrad 	 */
    119  1.1  riastrad 	offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET;
    120  1.1  riastrad 	if (offset > guc_wopcm_size ||
    121  1.1  riastrad 	    (guc_wopcm_size - offset) < sizeof(u32)) {
    122  1.1  riastrad 		dev_err(i915->drm.dev,
    123  1.1  riastrad 			"WOPCM: invalid GuC region size: %uK < %uK\n",
    124  1.1  riastrad 			guc_wopcm_size / SZ_1K,
    125  1.1  riastrad 			(u32)(offset + sizeof(u32)) / SZ_1K);
    126  1.1  riastrad 		return false;
    127  1.1  riastrad 	}
    128  1.1  riastrad 
    129  1.1  riastrad 	return true;
    130  1.1  riastrad }
    131  1.1  riastrad 
    132  1.1  riastrad static inline bool gen9_check_huc_fw_fits(struct drm_i915_private *i915,
    133  1.1  riastrad 					  u32 guc_wopcm_size, u32 huc_fw_size)
    134  1.1  riastrad {
    135  1.1  riastrad 	/*
    136  1.1  riastrad 	 * On Gen9 & CNL A0, hardware requires the total available GuC WOPCM
    137  1.1  riastrad 	 * size to be larger than or equal to HuC firmware size. Otherwise,
    138  1.1  riastrad 	 * firmware uploading would fail.
    139  1.1  riastrad 	 */
    140  1.1  riastrad 	if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) {
    141  1.1  riastrad 		dev_err(i915->drm.dev, "WOPCM: no space for %s: %uK < %uK\n",
    142  1.1  riastrad 			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
    143  1.1  riastrad 			(guc_wopcm_size - GUC_WOPCM_RESERVED) / SZ_1K,
    144  1.1  riastrad 			huc_fw_size / 1024);
    145  1.1  riastrad 		return false;
    146  1.1  riastrad 	}
    147  1.1  riastrad 
    148  1.1  riastrad 	return true;
    149  1.1  riastrad }
    150  1.1  riastrad 
    151  1.1  riastrad static inline bool check_hw_restrictions(struct drm_i915_private *i915,
    152  1.1  riastrad 					 u32 guc_wopcm_base, u32 guc_wopcm_size,
    153  1.1  riastrad 					 u32 huc_fw_size)
    154  1.1  riastrad {
    155  1.1  riastrad 	if (IS_GEN(i915, 9) && !gen9_check_dword_gap(i915, guc_wopcm_base,
    156  1.1  riastrad 						     guc_wopcm_size))
    157  1.1  riastrad 		return false;
    158  1.1  riastrad 
    159  1.1  riastrad 	if ((IS_GEN(i915, 9) ||
    160  1.1  riastrad 	     IS_CNL_REVID(i915, CNL_REVID_A0, CNL_REVID_A0)) &&
    161  1.1  riastrad 	    !gen9_check_huc_fw_fits(i915, guc_wopcm_size, huc_fw_size))
    162  1.1  riastrad 		return false;
    163  1.1  riastrad 
    164  1.1  riastrad 	return true;
    165  1.1  riastrad }
    166  1.1  riastrad 
    167  1.1  riastrad static inline bool __check_layout(struct drm_i915_private *i915, u32 wopcm_size,
    168  1.1  riastrad 				  u32 guc_wopcm_base, u32 guc_wopcm_size,
    169  1.1  riastrad 				  u32 guc_fw_size, u32 huc_fw_size)
    170  1.1  riastrad {
    171  1.1  riastrad 	const u32 ctx_rsvd = context_reserved_size(i915);
    172  1.1  riastrad 	u32 size;
    173  1.1  riastrad 
    174  1.1  riastrad 	size = wopcm_size - ctx_rsvd;
    175  1.1  riastrad 	if (unlikely(range_overflows(guc_wopcm_base, guc_wopcm_size, size))) {
    176  1.1  riastrad 		dev_err(i915->drm.dev,
    177  1.1  riastrad 			"WOPCM: invalid GuC region layout: %uK + %uK > %uK\n",
    178  1.1  riastrad 			guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K,
    179  1.1  riastrad 			size / SZ_1K);
    180  1.1  riastrad 		return false;
    181  1.1  riastrad 	}
    182  1.1  riastrad 
    183  1.1  riastrad 	size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED;
    184  1.1  riastrad 	if (unlikely(guc_wopcm_size < size)) {
    185  1.1  riastrad 		dev_err(i915->drm.dev, "WOPCM: no space for %s: %uK < %uK\n",
    186  1.1  riastrad 			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_GUC),
    187  1.1  riastrad 			guc_wopcm_size / SZ_1K, size / SZ_1K);
    188  1.1  riastrad 		return false;
    189  1.1  riastrad 	}
    190  1.1  riastrad 
    191  1.1  riastrad 	size = huc_fw_size + WOPCM_RESERVED_SIZE;
    192  1.1  riastrad 	if (unlikely(guc_wopcm_base < size)) {
    193  1.1  riastrad 		dev_err(i915->drm.dev, "WOPCM: no space for %s: %uK < %uK\n",
    194  1.1  riastrad 			intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC),
    195  1.1  riastrad 			guc_wopcm_base / SZ_1K, size / SZ_1K);
    196  1.1  riastrad 		return false;
    197  1.1  riastrad 	}
    198  1.1  riastrad 
    199  1.1  riastrad 	return check_hw_restrictions(i915, guc_wopcm_base, guc_wopcm_size,
    200  1.1  riastrad 				     huc_fw_size);
    201  1.1  riastrad }
    202  1.1  riastrad 
    203  1.1  riastrad static bool __wopcm_regs_locked(struct intel_uncore *uncore,
    204  1.1  riastrad 				u32 *guc_wopcm_base, u32 *guc_wopcm_size)
    205  1.1  riastrad {
    206  1.1  riastrad 	u32 reg_base = intel_uncore_read(uncore, DMA_GUC_WOPCM_OFFSET);
    207  1.1  riastrad 	u32 reg_size = intel_uncore_read(uncore, GUC_WOPCM_SIZE);
    208  1.1  riastrad 
    209  1.1  riastrad 	if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) ||
    210  1.1  riastrad 	    !(reg_base & GUC_WOPCM_OFFSET_VALID))
    211  1.1  riastrad 		return false;
    212  1.1  riastrad 
    213  1.1  riastrad 	*guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK;
    214  1.1  riastrad 	*guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK;
    215  1.1  riastrad 	return true;
    216  1.1  riastrad }
    217  1.1  riastrad 
    218  1.1  riastrad /**
    219  1.1  riastrad  * intel_wopcm_init() - Initialize the WOPCM structure.
    220  1.1  riastrad  * @wopcm: pointer to intel_wopcm.
    221  1.1  riastrad  *
    222  1.1  riastrad  * This function will partition WOPCM space based on GuC and HuC firmware sizes
    223  1.1  riastrad  * and will allocate max remaining for use by GuC. This function will also
    224  1.1  riastrad  * enforce platform dependent hardware restrictions on GuC WOPCM offset and
    225  1.1  riastrad  * size. It will fail the WOPCM init if any of these checks fail, so that the
    226  1.1  riastrad  * following WOPCM registers setup and GuC firmware uploading would be aborted.
    227  1.1  riastrad  */
    228  1.1  riastrad void intel_wopcm_init(struct intel_wopcm *wopcm)
    229  1.1  riastrad {
    230  1.1  riastrad 	struct drm_i915_private *i915 = wopcm_to_i915(wopcm);
    231  1.1  riastrad 	struct intel_gt *gt = &i915->gt;
    232  1.1  riastrad 	u32 guc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.guc.fw);
    233  1.1  riastrad 	u32 huc_fw_size = intel_uc_fw_get_upload_size(&gt->uc.huc.fw);
    234  1.1  riastrad 	u32 ctx_rsvd = context_reserved_size(i915);
    235  1.1  riastrad 	u32 guc_wopcm_base;
    236  1.1  riastrad 	u32 guc_wopcm_size;
    237  1.1  riastrad 
    238  1.1  riastrad 	if (!guc_fw_size)
    239  1.1  riastrad 		return;
    240  1.1  riastrad 
    241  1.1  riastrad 	GEM_BUG_ON(!wopcm->size);
    242  1.1  riastrad 	GEM_BUG_ON(wopcm->guc.base);
    243  1.1  riastrad 	GEM_BUG_ON(wopcm->guc.size);
    244  1.1  riastrad 	GEM_BUG_ON(guc_fw_size >= wopcm->size);
    245  1.1  riastrad 	GEM_BUG_ON(huc_fw_size >= wopcm->size);
    246  1.1  riastrad 	GEM_BUG_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm->size);
    247  1.1  riastrad 
    248  1.1  riastrad 	if (i915_inject_probe_failure(i915))
    249  1.1  riastrad 		return;
    250  1.1  riastrad 
    251  1.1  riastrad 	if (__wopcm_regs_locked(gt->uncore, &guc_wopcm_base, &guc_wopcm_size)) {
    252  1.1  riastrad 		DRM_DEV_DEBUG_DRIVER(i915->drm.dev,
    253  1.1  riastrad 				     "GuC WOPCM is already locked [%uK, %uK)\n",
    254  1.1  riastrad 				     guc_wopcm_base / SZ_1K,
    255  1.1  riastrad 				     guc_wopcm_size / SZ_1K);
    256  1.1  riastrad 		goto check;
    257  1.1  riastrad 	}
    258  1.1  riastrad 
    259  1.1  riastrad 	/*
    260  1.1  riastrad 	 * Aligned value of guc_wopcm_base will determine available WOPCM space
    261  1.1  riastrad 	 * for HuC firmware and mandatory reserved area.
    262  1.1  riastrad 	 */
    263  1.1  riastrad 	guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE;
    264  1.1  riastrad 	guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT);
    265  1.1  riastrad 
    266  1.1  riastrad 	/*
    267  1.1  riastrad 	 * Need to clamp guc_wopcm_base now to make sure the following math is
    268  1.1  riastrad 	 * correct. Formal check of whole WOPCM layout will be done below.
    269  1.1  riastrad 	 */
    270  1.1  riastrad 	guc_wopcm_base = min(guc_wopcm_base, wopcm->size - ctx_rsvd);
    271  1.1  riastrad 
    272  1.1  riastrad 	/* Aligned remainings of usable WOPCM space can be assigned to GuC. */
    273  1.1  riastrad 	guc_wopcm_size = wopcm->size - ctx_rsvd - guc_wopcm_base;
    274  1.1  riastrad 	guc_wopcm_size &= GUC_WOPCM_SIZE_MASK;
    275  1.1  riastrad 
    276  1.1  riastrad 	DRM_DEV_DEBUG_DRIVER(i915->drm.dev, "Calculated GuC WOPCM [%uK, %uK)\n",
    277  1.1  riastrad 			     guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K);
    278  1.1  riastrad 
    279  1.1  riastrad check:
    280  1.1  riastrad 	if (__check_layout(i915, wopcm->size, guc_wopcm_base, guc_wopcm_size,
    281  1.1  riastrad 			   guc_fw_size, huc_fw_size)) {
    282  1.1  riastrad 		wopcm->guc.base = guc_wopcm_base;
    283  1.1  riastrad 		wopcm->guc.size = guc_wopcm_size;
    284  1.1  riastrad 		GEM_BUG_ON(!wopcm->guc.base);
    285  1.1  riastrad 		GEM_BUG_ON(!wopcm->guc.size);
    286  1.1  riastrad 	}
    287  1.1  riastrad }
    288