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