2 * SPDX-License-Identifier: MIT
4 * Copyright © 2017-2018 Intel Corporation
7 #include "intel_wopcm.h"
13 * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and
14 * offset registers whose values are calculated and determined by HuC/GuC
15 * firmware size and set of hardware requirements/restrictions as shown below:
19 * +=========> +====================+ <== WOPCM Top
20 * ^ | HW contexts RSVD |
21 * | +===> +====================+ <== GuC WOPCM Top
27 * | Size +--------------------+
28 * WOPCM | | GuC FW RSVD |
29 * | | +--------------------+
30 * | | | GuC Stack RSVD |
31 * | | +------------------- +
32 * | v | GuC WOPCM RSVD |
33 * | +===> +====================+ <== GuC WOPCM base
35 * | +------------------- + <== HuC Firmware Top
37 * +=========> +====================+ <== WOPCM Base
39 * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top.
40 * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6
44 /* Default WOPCM size 1MB. */
45 #define GEN9_WOPCM_SIZE (1024 * 1024)
46 /* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */
47 #define WOPCM_RESERVED_SIZE (16 * 1024)
49 /* 16KB reserved at the beginning of GuC WOPCM. */
50 #define GUC_WOPCM_RESERVED (16 * 1024)
51 /* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */
52 #define GUC_WOPCM_STACK_RESERVED (8 * 1024)
54 /* GuC WOPCM Offset value needs to be aligned to 16KB. */
55 #define GUC_WOPCM_OFFSET_ALIGNMENT (1UL << GUC_WOPCM_OFFSET_SHIFT)
57 /* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */
58 #define BXT_WOPCM_RC6_CTX_RESERVED (24 * 1024)
59 /* 36KB WOPCM reserved at the end of WOPCM on CNL. */
60 #define CNL_WOPCM_HW_CTX_RESERVED (36 * 1024)
62 /* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */
63 #define GEN9_GUC_FW_RESERVED (128 * 1024)
64 #define GEN9_GUC_WOPCM_OFFSET (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED)
67 * intel_wopcm_init_early() - Early initialization of the WOPCM.
68 * @wopcm: pointer to intel_wopcm.
70 * Setup the size of WOPCM which will be used by later on WOPCM partitioning.
72 void intel_wopcm_init_early(struct intel_wopcm
*wopcm
)
74 wopcm
->size
= GEN9_WOPCM_SIZE
;
76 DRM_DEBUG_DRIVER("WOPCM size: %uKiB\n", wopcm
->size
/ 1024);
79 static inline u32
context_reserved_size(struct drm_i915_private
*i915
)
82 return BXT_WOPCM_RC6_CTX_RESERVED
;
83 else if (INTEL_GEN(i915
) >= 10)
84 return CNL_WOPCM_HW_CTX_RESERVED
;
89 static inline int gen9_check_dword_gap(u32 guc_wopcm_base
, u32 guc_wopcm_size
)
94 * GuC WOPCM size shall be at least a dword larger than the offset from
95 * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET)
96 * due to hardware limitation on Gen9.
98 offset
= guc_wopcm_base
+ GEN9_GUC_WOPCM_OFFSET
;
99 if (offset
> guc_wopcm_size
||
100 (guc_wopcm_size
- offset
) < sizeof(u32
)) {
101 DRM_ERROR("GuC WOPCM size %uKiB is too small. %uKiB needed.\n",
102 guc_wopcm_size
/ 1024,
103 (u32
)(offset
+ sizeof(u32
)) / 1024);
110 static inline int gen9_check_huc_fw_fits(u32 guc_wopcm_size
, u32 huc_fw_size
)
113 * On Gen9 & CNL A0, hardware requires the total available GuC WOPCM
114 * size to be larger than or equal to HuC firmware size. Otherwise,
115 * firmware uploading would fail.
117 if (huc_fw_size
> guc_wopcm_size
- GUC_WOPCM_RESERVED
) {
118 DRM_ERROR("HuC FW (%uKiB) won't fit in GuC WOPCM (%uKiB).\n",
120 (guc_wopcm_size
- GUC_WOPCM_RESERVED
) / 1024);
127 static inline int check_hw_restriction(struct drm_i915_private
*i915
,
128 u32 guc_wopcm_base
, u32 guc_wopcm_size
,
134 err
= gen9_check_dword_gap(guc_wopcm_base
, guc_wopcm_size
);
137 (IS_GEN9(i915
) || IS_CNL_REVID(i915
, CNL_REVID_A0
, CNL_REVID_A0
)))
138 err
= gen9_check_huc_fw_fits(guc_wopcm_size
, huc_fw_size
);
144 * intel_wopcm_init() - Initialize the WOPCM structure.
145 * @wopcm: pointer to intel_wopcm.
147 * This function will partition WOPCM space based on GuC and HuC firmware sizes
148 * and will allocate max remaining for use by GuC. This function will also
149 * enforce platform dependent hardware restrictions on GuC WOPCM offset and
150 * size. It will fail the WOPCM init if any of these checks were failed, so that
151 * the following GuC firmware uploading would be aborted.
153 * Return: 0 on success, non-zero error code on failure.
155 int intel_wopcm_init(struct intel_wopcm
*wopcm
)
157 struct drm_i915_private
*i915
= wopcm_to_i915(wopcm
);
158 u32 guc_fw_size
= intel_uc_fw_get_upload_size(&i915
->guc
.fw
);
159 u32 huc_fw_size
= intel_uc_fw_get_upload_size(&i915
->huc
.fw
);
160 u32 ctx_rsvd
= context_reserved_size(i915
);
166 GEM_BUG_ON(!wopcm
->size
);
168 if (guc_fw_size
>= wopcm
->size
) {
169 DRM_ERROR("GuC FW (%uKiB) is too big to fit in WOPCM.",
174 if (huc_fw_size
>= wopcm
->size
) {
175 DRM_ERROR("HuC FW (%uKiB) is too big to fit in WOPCM.",
180 guc_wopcm_base
= ALIGN(huc_fw_size
+ WOPCM_RESERVED_SIZE
,
181 GUC_WOPCM_OFFSET_ALIGNMENT
);
182 if ((guc_wopcm_base
+ ctx_rsvd
) >= wopcm
->size
) {
183 DRM_ERROR("GuC WOPCM base (%uKiB) is too big.\n",
184 guc_wopcm_base
/ 1024);
188 guc_wopcm_size
= wopcm
->size
- guc_wopcm_base
- ctx_rsvd
;
189 guc_wopcm_size
&= GUC_WOPCM_SIZE_MASK
;
191 DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n",
192 guc_wopcm_base
/ 1024, guc_wopcm_size
/ 1024);
194 guc_wopcm_rsvd
= GUC_WOPCM_RESERVED
+ GUC_WOPCM_STACK_RESERVED
;
195 if ((guc_fw_size
+ guc_wopcm_rsvd
) > guc_wopcm_size
) {
196 DRM_ERROR("Need %uKiB WOPCM for GuC, %uKiB available.\n",
197 (guc_fw_size
+ guc_wopcm_rsvd
) / 1024,
198 guc_wopcm_size
/ 1024);
202 err
= check_hw_restriction(i915
, guc_wopcm_base
, guc_wopcm_size
,
207 wopcm
->guc
.base
= guc_wopcm_base
;
208 wopcm
->guc
.size
= guc_wopcm_size
;
213 static inline int write_and_verify(struct drm_i915_private
*dev_priv
,
214 i915_reg_t reg
, u32 val
, u32 mask
,
219 GEM_BUG_ON(val
& ~mask
);
221 I915_WRITE(reg
, val
);
223 reg_val
= I915_READ(reg
);
225 return (reg_val
& mask
) != (val
| locked_bit
) ? -EIO
: 0;
229 * intel_wopcm_init_hw() - Setup GuC WOPCM registers.
230 * @wopcm: pointer to intel_wopcm.
232 * Setup the GuC WOPCM size and offset registers with the calculated values. It
233 * will verify the register values to make sure the registers are locked with
236 * Return: 0 on success. -EIO if registers were locked with incorrect values.
238 int intel_wopcm_init_hw(struct intel_wopcm
*wopcm
)
240 struct drm_i915_private
*dev_priv
= wopcm_to_i915(wopcm
);
245 if (!USES_GUC(dev_priv
))
248 GEM_BUG_ON(!HAS_GUC(dev_priv
));
249 GEM_BUG_ON(!wopcm
->guc
.size
);
250 GEM_BUG_ON(!wopcm
->guc
.base
);
252 err
= write_and_verify(dev_priv
, GUC_WOPCM_SIZE
, wopcm
->guc
.size
,
253 GUC_WOPCM_SIZE_MASK
| GUC_WOPCM_SIZE_LOCKED
,
254 GUC_WOPCM_SIZE_LOCKED
);
258 huc_agent
= USES_HUC(dev_priv
) ? HUC_LOADING_AGENT_GUC
: 0;
259 mask
= GUC_WOPCM_OFFSET_MASK
| GUC_WOPCM_OFFSET_VALID
| huc_agent
;
260 err
= write_and_verify(dev_priv
, DMA_GUC_WOPCM_OFFSET
,
261 wopcm
->guc
.base
| huc_agent
, mask
,
262 GUC_WOPCM_OFFSET_VALID
);
269 DRM_ERROR("Failed to init WOPCM registers:\n");
270 DRM_ERROR("DMA_GUC_WOPCM_OFFSET=%#x\n",
271 I915_READ(DMA_GUC_WOPCM_OFFSET
));
272 DRM_ERROR("GUC_WOPCM_SIZE=%#x\n", I915_READ(GUC_WOPCM_SIZE
));