diff options
author | Huang Rui <ray.huang@amd.com> | 2017-03-03 18:37:23 -0500 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-03-29 23:54:48 -0400 |
commit | 0e5ca0d1ac07ef8b3a52d3b0404482207cb4da5a (patch) | |
tree | f3fb189ffa872cbe031a3d369b30478e65cd98b4 /drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | |
parent | c1dc356a116c992433c5c68e7c493b0fa3e6f9b1 (diff) |
drm/amdgpu: add PSP driver for vega10 (v2)
PSP is responsible for firmware loading on SOC-15 asics.
v2: fix memory leak (Ken)
Acked-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Huang Rui <ray.huang@amd.com>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c new file mode 100644 index 000000000000..4731015f6101 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | |||
@@ -0,0 +1,481 @@ | |||
1 | /* | ||
2 | * Copyright 2016 Advanced Micro Devices, Inc. | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
5 | * copy of this software and associated documentation files (the "Software"), | ||
6 | * to deal in the Software without restriction, including without limitation | ||
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
8 | * and/or sell copies of the Software, and to permit persons to whom the | ||
9 | * Software is furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
20 | * OTHER DEALINGS IN THE SOFTWARE. | ||
21 | * | ||
22 | * Author: Huang Rui | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/firmware.h> | ||
27 | #include "drmP.h" | ||
28 | #include "amdgpu.h" | ||
29 | #include "amdgpu_psp.h" | ||
30 | #include "amdgpu_ucode.h" | ||
31 | #include "soc15_common.h" | ||
32 | #include "psp_v3_1.h" | ||
33 | |||
34 | static void psp_set_funcs(struct amdgpu_device *adev); | ||
35 | |||
36 | static int psp_early_init(void *handle) | ||
37 | { | ||
38 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | ||
39 | |||
40 | psp_set_funcs(adev); | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | static int psp_sw_init(void *handle) | ||
46 | { | ||
47 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | ||
48 | struct psp_context *psp = &adev->psp; | ||
49 | int ret; | ||
50 | |||
51 | switch (adev->asic_type) { | ||
52 | case CHIP_VEGA10: | ||
53 | psp->init_microcode = psp_v3_1_init_microcode; | ||
54 | psp->bootloader_load_sysdrv = psp_v3_1_bootloader_load_sysdrv; | ||
55 | psp->bootloader_load_sos = psp_v3_1_bootloader_load_sos; | ||
56 | psp->prep_cmd_buf = psp_v3_1_prep_cmd_buf; | ||
57 | psp->ring_init = psp_v3_1_ring_init; | ||
58 | psp->cmd_submit = psp_v3_1_cmd_submit; | ||
59 | psp->compare_sram_data = psp_v3_1_compare_sram_data; | ||
60 | psp->smu_reload_quirk = psp_v3_1_smu_reload_quirk; | ||
61 | break; | ||
62 | default: | ||
63 | return -EINVAL; | ||
64 | } | ||
65 | |||
66 | psp->adev = adev; | ||
67 | |||
68 | ret = psp_init_microcode(psp); | ||
69 | if (ret) { | ||
70 | DRM_ERROR("Failed to load psp firmware!\n"); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static int psp_sw_fini(void *handle) | ||
78 | { | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | int psp_wait_for(struct psp_context *psp, uint32_t reg_index, | ||
83 | uint32_t reg_val, uint32_t mask, bool check_changed) | ||
84 | { | ||
85 | uint32_t val; | ||
86 | int i; | ||
87 | struct amdgpu_device *adev = psp->adev; | ||
88 | |||
89 | val = RREG32(reg_index); | ||
90 | |||
91 | for (i = 0; i < adev->usec_timeout; i++) { | ||
92 | if (check_changed) { | ||
93 | if (val != reg_val) | ||
94 | return 0; | ||
95 | } else { | ||
96 | if ((val & mask) == reg_val) | ||
97 | return 0; | ||
98 | } | ||
99 | udelay(1); | ||
100 | } | ||
101 | |||
102 | return -ETIME; | ||
103 | } | ||
104 | |||
105 | static int | ||
106 | psp_cmd_submit_buf(struct psp_context *psp, | ||
107 | struct amdgpu_firmware_info *ucode, | ||
108 | struct psp_gfx_cmd_resp *cmd, uint64_t fence_mc_addr, | ||
109 | int index) | ||
110 | { | ||
111 | int ret; | ||
112 | struct amdgpu_bo *cmd_buf_bo; | ||
113 | uint64_t cmd_buf_mc_addr; | ||
114 | struct psp_gfx_cmd_resp *cmd_buf_mem; | ||
115 | struct amdgpu_device *adev = psp->adev; | ||
116 | |||
117 | ret = amdgpu_bo_create_kernel(adev, PSP_CMD_BUFFER_SIZE, PAGE_SIZE, | ||
118 | AMDGPU_GEM_DOMAIN_VRAM, | ||
119 | &cmd_buf_bo, &cmd_buf_mc_addr, | ||
120 | (void **)&cmd_buf_mem); | ||
121 | if (ret) | ||
122 | return ret; | ||
123 | |||
124 | memset(cmd_buf_mem, 0, PSP_CMD_BUFFER_SIZE); | ||
125 | |||
126 | memcpy(cmd_buf_mem, cmd, sizeof(struct psp_gfx_cmd_resp)); | ||
127 | |||
128 | ret = psp_cmd_submit(psp, ucode, cmd_buf_mc_addr, | ||
129 | fence_mc_addr, index); | ||
130 | |||
131 | while (*((unsigned int *)psp->fence_buf) != index) { | ||
132 | msleep(1); | ||
133 | }; | ||
134 | |||
135 | amdgpu_bo_free_kernel(&cmd_buf_bo, | ||
136 | &cmd_buf_mc_addr, | ||
137 | (void **)&cmd_buf_mem); | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | static void psp_prep_tmr_cmd_buf(struct psp_gfx_cmd_resp *cmd, | ||
143 | uint64_t tmr_mc, uint32_t size) | ||
144 | { | ||
145 | cmd->cmd_id = GFX_CMD_ID_SETUP_TMR; | ||
146 | cmd->cmd.cmd_setup_tmr.buf_phy_addr_lo = (uint32_t)tmr_mc; | ||
147 | cmd->cmd.cmd_setup_tmr.buf_phy_addr_hi = (uint32_t)(tmr_mc >> 32); | ||
148 | cmd->cmd.cmd_setup_tmr.buf_size = size; | ||
149 | } | ||
150 | |||
151 | /* Set up Trusted Memory Region */ | ||
152 | static int psp_tmr_init(struct psp_context *psp) | ||
153 | { | ||
154 | int ret; | ||
155 | struct psp_gfx_cmd_resp *cmd; | ||
156 | |||
157 | cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL); | ||
158 | if (!cmd) | ||
159 | return -ENOMEM; | ||
160 | |||
161 | /* | ||
162 | * Allocate 3M memory aligned to 1M from Frame Buffer (local | ||
163 | * physical). | ||
164 | * | ||
165 | * Note: this memory need be reserved till the driver | ||
166 | * uninitializes. | ||
167 | */ | ||
168 | ret = amdgpu_bo_create_kernel(psp->adev, 0x300000, 0x100000, | ||
169 | AMDGPU_GEM_DOMAIN_VRAM, | ||
170 | &psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf); | ||
171 | if (ret) | ||
172 | goto failed; | ||
173 | |||
174 | psp_prep_tmr_cmd_buf(cmd, psp->tmr_mc_addr, 0x300000); | ||
175 | |||
176 | ret = psp_cmd_submit_buf(psp, NULL, cmd, | ||
177 | psp->fence_buf_mc_addr, 1); | ||
178 | if (ret) | ||
179 | goto failed_mem; | ||
180 | |||
181 | kfree(cmd); | ||
182 | |||
183 | return 0; | ||
184 | |||
185 | failed_mem: | ||
186 | amdgpu_bo_free_kernel(&psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf); | ||
187 | failed: | ||
188 | kfree(cmd); | ||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | static void psp_prep_asd_cmd_buf(struct psp_gfx_cmd_resp *cmd, | ||
193 | uint64_t asd_mc, uint64_t asd_mc_shared, | ||
194 | uint32_t size, uint32_t shared_size) | ||
195 | { | ||
196 | cmd->cmd_id = GFX_CMD_ID_LOAD_ASD; | ||
197 | cmd->cmd.cmd_load_ta.app_phy_addr_lo = lower_32_bits(asd_mc); | ||
198 | cmd->cmd.cmd_load_ta.app_phy_addr_hi = upper_32_bits(asd_mc); | ||
199 | cmd->cmd.cmd_load_ta.app_len = size; | ||
200 | |||
201 | cmd->cmd.cmd_load_ta.cmd_buf_phy_addr_lo = lower_32_bits(asd_mc_shared); | ||
202 | cmd->cmd.cmd_load_ta.cmd_buf_phy_addr_hi = upper_32_bits(asd_mc_shared); | ||
203 | cmd->cmd.cmd_load_ta.cmd_buf_len = shared_size; | ||
204 | } | ||
205 | |||
206 | static int psp_asd_load(struct psp_context *psp) | ||
207 | { | ||
208 | int ret; | ||
209 | struct amdgpu_bo *asd_bo, *asd_shared_bo; | ||
210 | uint64_t asd_mc_addr, asd_shared_mc_addr; | ||
211 | void *asd_buf, *asd_shared_buf; | ||
212 | struct psp_gfx_cmd_resp *cmd; | ||
213 | |||
214 | cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL); | ||
215 | if (!cmd) | ||
216 | return -ENOMEM; | ||
217 | |||
218 | /* | ||
219 | * Allocate 16k memory aligned to 4k from Frame Buffer (local | ||
220 | * physical) for shared ASD <-> Driver | ||
221 | */ | ||
222 | ret = amdgpu_bo_create_kernel(psp->adev, PSP_ASD_SHARED_MEM_SIZE, PAGE_SIZE, | ||
223 | AMDGPU_GEM_DOMAIN_VRAM, | ||
224 | &asd_shared_bo, &asd_shared_mc_addr, &asd_buf); | ||
225 | if (ret) | ||
226 | goto failed; | ||
227 | |||
228 | /* | ||
229 | * Allocate 256k memory aligned to 4k from Frame Buffer (local | ||
230 | * physical) for ASD firmware | ||
231 | */ | ||
232 | ret = amdgpu_bo_create_kernel(psp->adev, PSP_ASD_BIN_SIZE, PAGE_SIZE, | ||
233 | AMDGPU_GEM_DOMAIN_VRAM, | ||
234 | &asd_bo, &asd_mc_addr, &asd_buf); | ||
235 | if (ret) | ||
236 | goto failed_mem; | ||
237 | |||
238 | memcpy(asd_buf, psp->asd_start_addr, psp->asd_ucode_size); | ||
239 | |||
240 | psp_prep_asd_cmd_buf(cmd, asd_mc_addr, asd_shared_mc_addr, | ||
241 | psp->asd_ucode_size, PSP_ASD_SHARED_MEM_SIZE); | ||
242 | |||
243 | ret = psp_cmd_submit_buf(psp, NULL, cmd, | ||
244 | psp->fence_buf_mc_addr, 2); | ||
245 | if (ret) | ||
246 | goto failed_mem1; | ||
247 | |||
248 | amdgpu_bo_free_kernel(&asd_bo, &asd_mc_addr, &asd_buf); | ||
249 | amdgpu_bo_free_kernel(&asd_shared_bo, &asd_shared_mc_addr, &asd_shared_buf); | ||
250 | kfree(cmd); | ||
251 | |||
252 | return 0; | ||
253 | |||
254 | failed_mem1: | ||
255 | amdgpu_bo_free_kernel(&asd_bo, &asd_mc_addr, &asd_buf); | ||
256 | failed_mem: | ||
257 | amdgpu_bo_free_kernel(&asd_shared_bo, &asd_shared_mc_addr, &asd_shared_buf); | ||
258 | failed: | ||
259 | kfree(cmd); | ||
260 | return ret; | ||
261 | } | ||
262 | |||
263 | static int psp_load_fw(struct amdgpu_device *adev) | ||
264 | { | ||
265 | int ret; | ||
266 | struct psp_gfx_cmd_resp *cmd; | ||
267 | int i; | ||
268 | struct amdgpu_firmware_info *ucode; | ||
269 | struct psp_context *psp = &adev->psp; | ||
270 | |||
271 | cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL); | ||
272 | if (!cmd) | ||
273 | return -ENOMEM; | ||
274 | |||
275 | ret = psp_bootloader_load_sysdrv(psp); | ||
276 | if (ret) | ||
277 | goto failed; | ||
278 | |||
279 | ret = psp_bootloader_load_sos(psp); | ||
280 | if (ret) | ||
281 | goto failed; | ||
282 | |||
283 | ret = psp_ring_init(psp, PSP_RING_TYPE__KM); | ||
284 | if (ret) | ||
285 | goto failed; | ||
286 | |||
287 | ret = amdgpu_bo_create_kernel(adev, PSP_FENCE_BUFFER_SIZE, PAGE_SIZE, | ||
288 | AMDGPU_GEM_DOMAIN_VRAM, | ||
289 | &psp->fence_buf_bo, | ||
290 | &psp->fence_buf_mc_addr, | ||
291 | &psp->fence_buf); | ||
292 | if (ret) | ||
293 | goto failed; | ||
294 | |||
295 | memset(psp->fence_buf, 0, PSP_FENCE_BUFFER_SIZE); | ||
296 | |||
297 | ret = psp_tmr_init(psp); | ||
298 | if (ret) | ||
299 | goto failed_mem; | ||
300 | |||
301 | ret = psp_asd_load(psp); | ||
302 | if (ret) | ||
303 | goto failed_mem; | ||
304 | |||
305 | for (i = 0; i < adev->firmware.max_ucodes; i++) { | ||
306 | ucode = &adev->firmware.ucode[i]; | ||
307 | if (!ucode->fw) | ||
308 | continue; | ||
309 | |||
310 | if (ucode->ucode_id == AMDGPU_UCODE_ID_SMC && | ||
311 | psp_smu_reload_quirk(psp)) | ||
312 | continue; | ||
313 | |||
314 | ret = psp_prep_cmd_buf(ucode, cmd); | ||
315 | if (ret) | ||
316 | goto failed_mem; | ||
317 | |||
318 | ret = psp_cmd_submit_buf(psp, ucode, cmd, | ||
319 | psp->fence_buf_mc_addr, i + 3); | ||
320 | if (ret) | ||
321 | goto failed_mem; | ||
322 | |||
323 | #if 0 | ||
324 | /* check if firmware loaded sucessfully */ | ||
325 | if (!amdgpu_psp_check_fw_loading_status(adev, i)) | ||
326 | return -EINVAL; | ||
327 | #endif | ||
328 | } | ||
329 | |||
330 | amdgpu_bo_free_kernel(&psp->fence_buf_bo, | ||
331 | &psp->fence_buf_mc_addr, &psp->fence_buf); | ||
332 | kfree(cmd); | ||
333 | |||
334 | return 0; | ||
335 | |||
336 | failed_mem: | ||
337 | amdgpu_bo_free_kernel(&psp->fence_buf_bo, | ||
338 | &psp->fence_buf_mc_addr, &psp->fence_buf); | ||
339 | failed: | ||
340 | kfree(cmd); | ||
341 | return ret; | ||
342 | } | ||
343 | |||
344 | static int psp_hw_init(void *handle) | ||
345 | { | ||
346 | int ret; | ||
347 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | ||
348 | |||
349 | |||
350 | if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) | ||
351 | return 0; | ||
352 | |||
353 | mutex_lock(&adev->firmware.mutex); | ||
354 | /* | ||
355 | * This sequence is just used on hw_init only once, no need on | ||
356 | * resume. | ||
357 | */ | ||
358 | ret = amdgpu_ucode_init_bo(adev); | ||
359 | if (ret) | ||
360 | goto failed; | ||
361 | |||
362 | ret = psp_load_fw(adev); | ||
363 | if (ret) { | ||
364 | DRM_ERROR("PSP firmware loading failed\n"); | ||
365 | goto failed; | ||
366 | } | ||
367 | |||
368 | mutex_unlock(&adev->firmware.mutex); | ||
369 | return 0; | ||
370 | |||
371 | failed: | ||
372 | adev->firmware.load_type = AMDGPU_FW_LOAD_DIRECT; | ||
373 | mutex_unlock(&adev->firmware.mutex); | ||
374 | return -EINVAL; | ||
375 | } | ||
376 | |||
377 | static int psp_hw_fini(void *handle) | ||
378 | { | ||
379 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | ||
380 | struct psp_context *psp = &adev->psp; | ||
381 | |||
382 | if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) | ||
383 | amdgpu_ucode_fini_bo(adev); | ||
384 | |||
385 | if (psp->tmr_buf) | ||
386 | amdgpu_bo_free_kernel(&psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf); | ||
387 | |||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static int psp_suspend(void *handle) | ||
392 | { | ||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static int psp_resume(void *handle) | ||
397 | { | ||
398 | int ret; | ||
399 | struct amdgpu_device *adev = (struct amdgpu_device *)handle; | ||
400 | |||
401 | if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) | ||
402 | return 0; | ||
403 | |||
404 | mutex_lock(&adev->firmware.mutex); | ||
405 | |||
406 | ret = psp_load_fw(adev); | ||
407 | if (ret) | ||
408 | DRM_ERROR("PSP resume failed\n"); | ||
409 | |||
410 | mutex_unlock(&adev->firmware.mutex); | ||
411 | |||
412 | return ret; | ||
413 | } | ||
414 | |||
415 | static bool psp_check_fw_loading_status(struct amdgpu_device *adev, | ||
416 | enum AMDGPU_UCODE_ID ucode_type) | ||
417 | { | ||
418 | struct amdgpu_firmware_info *ucode = NULL; | ||
419 | |||
420 | if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { | ||
421 | DRM_INFO("firmware is not loaded by PSP\n"); | ||
422 | return true; | ||
423 | } | ||
424 | |||
425 | if (!adev->firmware.fw_size) | ||
426 | return false; | ||
427 | |||
428 | ucode = &adev->firmware.ucode[ucode_type]; | ||
429 | if (!ucode->fw || !ucode->ucode_size) | ||
430 | return false; | ||
431 | |||
432 | return psp_compare_sram_data(&adev->psp, ucode, ucode_type); | ||
433 | } | ||
434 | |||
435 | static int psp_set_clockgating_state(void *handle, | ||
436 | enum amd_clockgating_state state) | ||
437 | { | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static int psp_set_powergating_state(void *handle, | ||
442 | enum amd_powergating_state state) | ||
443 | { | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | const struct amd_ip_funcs psp_ip_funcs = { | ||
448 | .name = "psp", | ||
449 | .early_init = psp_early_init, | ||
450 | .late_init = NULL, | ||
451 | .sw_init = psp_sw_init, | ||
452 | .sw_fini = psp_sw_fini, | ||
453 | .hw_init = psp_hw_init, | ||
454 | .hw_fini = psp_hw_fini, | ||
455 | .suspend = psp_suspend, | ||
456 | .resume = psp_resume, | ||
457 | .is_idle = NULL, | ||
458 | .wait_for_idle = NULL, | ||
459 | .soft_reset = NULL, | ||
460 | .set_clockgating_state = psp_set_clockgating_state, | ||
461 | .set_powergating_state = psp_set_powergating_state, | ||
462 | }; | ||
463 | |||
464 | static const struct amdgpu_psp_funcs psp_funcs = { | ||
465 | .check_fw_loading_status = psp_check_fw_loading_status, | ||
466 | }; | ||
467 | |||
468 | static void psp_set_funcs(struct amdgpu_device *adev) | ||
469 | { | ||
470 | if (NULL == adev->firmware.funcs) | ||
471 | adev->firmware.funcs = &psp_funcs; | ||
472 | } | ||
473 | |||
474 | const struct amdgpu_ip_block_version psp_v3_1_ip_block = | ||
475 | { | ||
476 | .type = AMD_IP_BLOCK_TYPE_PSP, | ||
477 | .major = 3, | ||
478 | .minor = 1, | ||
479 | .rev = 0, | ||
480 | .funcs = &psp_ip_funcs, | ||
481 | }; | ||