]>
Commit | Line | Data |
---|---|---|
6b0478fb JL |
1 | /* |
2 | * SPDX-License-Identifier: MIT | |
3 | * | |
4 | * Copyright © 2017-2018 Intel Corporation | |
5 | */ | |
6 | ||
7 | #include "intel_wopcm.h" | |
8 | #include "i915_drv.h" | |
9 | ||
10 | /** | |
11 | * DOC: WOPCM Layout | |
12 | * | |
13 | * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and | |
fbe6f8f2 YL |
14 | * offset registers whose values are calculated and determined by HuC/GuC |
15 | * firmware size and set of hardware requirements/restrictions as shown below: | |
6b0478fb | 16 | * |
fbe6f8f2 YL |
17 | * :: |
18 | * | |
19 | * +=========> +====================+ <== WOPCM Top | |
20 | * ^ | HW contexts RSVD | | |
21 | * | +===> +====================+ <== GuC WOPCM Top | |
22 | * | ^ | | | |
23 | * | | | | | |
24 | * | | | | | |
25 | * | GuC | | | |
26 | * | WOPCM | | | |
27 | * | Size +--------------------+ | |
28 | * WOPCM | | GuC FW RSVD | | |
29 | * | | +--------------------+ | |
30 | * | | | GuC Stack RSVD | | |
31 | * | | +------------------- + | |
32 | * | v | GuC WOPCM RSVD | | |
33 | * | +===> +====================+ <== GuC WOPCM base | |
34 | * | | WOPCM RSVD | | |
35 | * | +------------------- + <== HuC Firmware Top | |
36 | * v | HuC FW | | |
37 | * +=========> +====================+ <== WOPCM Base | |
6b0478fb JL |
38 | * |
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 | |
41 | * context). | |
42 | */ | |
43 | ||
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) | |
48 | ||
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) | |
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 (24 * 1024) | |
5cbc1e2f JL |
59 | /* 36KB WOPCM reserved at the end of WOPCM on CNL. */ |
60 | #define CNL_WOPCM_HW_CTX_RESERVED (36 * 1024) | |
6b0478fb JL |
61 | |
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) | |
65 | ||
66 | /** | |
67 | * intel_wopcm_init_early() - Early initialization of the WOPCM. | |
68 | * @wopcm: pointer to intel_wopcm. | |
69 | * | |
70 | * Setup the size of WOPCM which will be used by later on WOPCM partitioning. | |
71 | */ | |
72 | void intel_wopcm_init_early(struct intel_wopcm *wopcm) | |
73 | { | |
74 | wopcm->size = GEN9_WOPCM_SIZE; | |
75 | ||
76 | DRM_DEBUG_DRIVER("WOPCM size: %uKiB\n", wopcm->size / 1024); | |
77 | } | |
78 | ||
79 | static inline u32 context_reserved_size(struct drm_i915_private *i915) | |
80 | { | |
81 | if (IS_GEN9_LP(i915)) | |
82 | return BXT_WOPCM_RC6_CTX_RESERVED; | |
5cbc1e2f JL |
83 | else if (INTEL_GEN(i915) >= 10) |
84 | return CNL_WOPCM_HW_CTX_RESERVED; | |
6b0478fb JL |
85 | else |
86 | return 0; | |
87 | } | |
88 | ||
89 | static inline int gen9_check_dword_gap(u32 guc_wopcm_base, u32 guc_wopcm_size) | |
90 | { | |
91 | u32 offset; | |
92 | ||
93 | /* | |
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. | |
97 | */ | |
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); | |
104 | return -E2BIG; | |
105 | } | |
106 | ||
107 | return 0; | |
108 | } | |
109 | ||
96c83d35 JL |
110 | static inline int gen9_check_huc_fw_fits(u32 guc_wopcm_size, u32 huc_fw_size) |
111 | { | |
112 | /* | |
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. | |
116 | */ | |
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", | |
119 | huc_fw_size / 1024, | |
120 | (guc_wopcm_size - GUC_WOPCM_RESERVED) / 1024); | |
121 | return -E2BIG; | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
6b0478fb | 127 | static inline int check_hw_restriction(struct drm_i915_private *i915, |
96c83d35 JL |
128 | u32 guc_wopcm_base, u32 guc_wopcm_size, |
129 | u32 huc_fw_size) | |
6b0478fb JL |
130 | { |
131 | int err = 0; | |
132 | ||
cf819eff | 133 | if (IS_GEN(i915, 9)) |
6b0478fb JL |
134 | err = gen9_check_dword_gap(guc_wopcm_base, guc_wopcm_size); |
135 | ||
96c83d35 | 136 | if (!err && |
cf819eff | 137 | (IS_GEN(i915, 9) || IS_CNL_REVID(i915, CNL_REVID_A0, CNL_REVID_A0))) |
96c83d35 JL |
138 | err = gen9_check_huc_fw_fits(guc_wopcm_size, huc_fw_size); |
139 | ||
6b0478fb JL |
140 | return err; |
141 | } | |
142 | ||
143 | /** | |
144 | * intel_wopcm_init() - Initialize the WOPCM structure. | |
145 | * @wopcm: pointer to intel_wopcm. | |
146 | * | |
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. | |
152 | * | |
153 | * Return: 0 on success, non-zero error code on failure. | |
154 | */ | |
155 | int intel_wopcm_init(struct intel_wopcm *wopcm) | |
156 | { | |
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); | |
161 | u32 guc_wopcm_base; | |
162 | u32 guc_wopcm_size; | |
163 | u32 guc_wopcm_rsvd; | |
164 | int err; | |
165 | ||
fce43315 | 166 | if (!USES_GUC(i915)) |
b6445e17 JB |
167 | return 0; |
168 | ||
6b0478fb JL |
169 | GEM_BUG_ON(!wopcm->size); |
170 | ||
905febf5 JB |
171 | if (i915_inject_load_failure()) |
172 | return -E2BIG; | |
173 | ||
6b0478fb JL |
174 | if (guc_fw_size >= wopcm->size) { |
175 | DRM_ERROR("GuC FW (%uKiB) is too big to fit in WOPCM.", | |
176 | guc_fw_size / 1024); | |
177 | return -E2BIG; | |
178 | } | |
179 | ||
180 | if (huc_fw_size >= wopcm->size) { | |
181 | DRM_ERROR("HuC FW (%uKiB) is too big to fit in WOPCM.", | |
182 | huc_fw_size / 1024); | |
183 | return -E2BIG; | |
184 | } | |
185 | ||
186 | guc_wopcm_base = ALIGN(huc_fw_size + WOPCM_RESERVED_SIZE, | |
187 | GUC_WOPCM_OFFSET_ALIGNMENT); | |
188 | if ((guc_wopcm_base + ctx_rsvd) >= wopcm->size) { | |
189 | DRM_ERROR("GuC WOPCM base (%uKiB) is too big.\n", | |
190 | guc_wopcm_base / 1024); | |
191 | return -E2BIG; | |
192 | } | |
193 | ||
194 | guc_wopcm_size = wopcm->size - guc_wopcm_base - ctx_rsvd; | |
195 | guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; | |
196 | ||
197 | DRM_DEBUG_DRIVER("Calculated GuC WOPCM Region: [%uKiB, %uKiB)\n", | |
198 | guc_wopcm_base / 1024, guc_wopcm_size / 1024); | |
199 | ||
200 | guc_wopcm_rsvd = GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; | |
201 | if ((guc_fw_size + guc_wopcm_rsvd) > guc_wopcm_size) { | |
202 | DRM_ERROR("Need %uKiB WOPCM for GuC, %uKiB available.\n", | |
203 | (guc_fw_size + guc_wopcm_rsvd) / 1024, | |
204 | guc_wopcm_size / 1024); | |
205 | return -E2BIG; | |
206 | } | |
207 | ||
96c83d35 JL |
208 | err = check_hw_restriction(i915, guc_wopcm_base, guc_wopcm_size, |
209 | huc_fw_size); | |
6b0478fb JL |
210 | if (err) |
211 | return err; | |
212 | ||
213 | wopcm->guc.base = guc_wopcm_base; | |
214 | wopcm->guc.size = guc_wopcm_size; | |
215 | ||
216 | return 0; | |
217 | } | |
f08e2035 JL |
218 | |
219 | static inline int write_and_verify(struct drm_i915_private *dev_priv, | |
220 | i915_reg_t reg, u32 val, u32 mask, | |
221 | u32 locked_bit) | |
222 | { | |
223 | u32 reg_val; | |
224 | ||
225 | GEM_BUG_ON(val & ~mask); | |
226 | ||
227 | I915_WRITE(reg, val); | |
228 | ||
229 | reg_val = I915_READ(reg); | |
230 | ||
231 | return (reg_val & mask) != (val | locked_bit) ? -EIO : 0; | |
232 | } | |
233 | ||
234 | /** | |
235 | * intel_wopcm_init_hw() - Setup GuC WOPCM registers. | |
236 | * @wopcm: pointer to intel_wopcm. | |
237 | * | |
238 | * Setup the GuC WOPCM size and offset registers with the calculated values. It | |
239 | * will verify the register values to make sure the registers are locked with | |
240 | * correct values. | |
241 | * | |
242 | * Return: 0 on success. -EIO if registers were locked with incorrect values. | |
243 | */ | |
244 | int intel_wopcm_init_hw(struct intel_wopcm *wopcm) | |
245 | { | |
246 | struct drm_i915_private *dev_priv = wopcm_to_i915(wopcm); | |
247 | u32 huc_agent; | |
248 | u32 mask; | |
249 | int err; | |
250 | ||
251 | if (!USES_GUC(dev_priv)) | |
252 | return 0; | |
253 | ||
254 | GEM_BUG_ON(!HAS_GUC(dev_priv)); | |
255 | GEM_BUG_ON(!wopcm->guc.size); | |
256 | GEM_BUG_ON(!wopcm->guc.base); | |
257 | ||
258 | err = write_and_verify(dev_priv, GUC_WOPCM_SIZE, wopcm->guc.size, | |
259 | GUC_WOPCM_SIZE_MASK | GUC_WOPCM_SIZE_LOCKED, | |
260 | GUC_WOPCM_SIZE_LOCKED); | |
261 | if (err) | |
262 | goto err_out; | |
263 | ||
264 | huc_agent = USES_HUC(dev_priv) ? HUC_LOADING_AGENT_GUC : 0; | |
265 | mask = GUC_WOPCM_OFFSET_MASK | GUC_WOPCM_OFFSET_VALID | huc_agent; | |
266 | err = write_and_verify(dev_priv, DMA_GUC_WOPCM_OFFSET, | |
267 | wopcm->guc.base | huc_agent, mask, | |
268 | GUC_WOPCM_OFFSET_VALID); | |
269 | if (err) | |
270 | goto err_out; | |
271 | ||
272 | return 0; | |
273 | ||
274 | err_out: | |
275 | DRM_ERROR("Failed to init WOPCM registers:\n"); | |
276 | DRM_ERROR("DMA_GUC_WOPCM_OFFSET=%#x\n", | |
277 | I915_READ(DMA_GUC_WOPCM_OFFSET)); | |
278 | DRM_ERROR("GUC_WOPCM_SIZE=%#x\n", I915_READ(GUC_WOPCM_SIZE)); | |
279 | ||
280 | return err; | |
281 | } |