diff options
Diffstat (limited to 'drivers/gpu/nvgpu/gm20b/acr_gm20b.c')
-rw-r--r-- | drivers/gpu/nvgpu/gm20b/acr_gm20b.c | 1444 |
1 files changed, 1444 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gm20b/acr_gm20b.c b/drivers/gpu/nvgpu/gm20b/acr_gm20b.c new file mode 100644 index 00000000..a39cdf2c --- /dev/null +++ b/drivers/gpu/nvgpu/gm20b/acr_gm20b.c | |||
@@ -0,0 +1,1444 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved. | ||
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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
20 | * DEALINGS IN THE SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #include <nvgpu/types.h> | ||
24 | #include <nvgpu/dma.h> | ||
25 | #include <nvgpu/gmmu.h> | ||
26 | #include <nvgpu/timers.h> | ||
27 | #include <nvgpu/nvgpu_common.h> | ||
28 | #include <nvgpu/kmem.h> | ||
29 | #include <nvgpu/nvgpu_mem.h> | ||
30 | #include <nvgpu/acr/nvgpu_acr.h> | ||
31 | #include <nvgpu/firmware.h> | ||
32 | #include <nvgpu/pmu.h> | ||
33 | #include <nvgpu/falcon.h> | ||
34 | #include <nvgpu/enabled.h> | ||
35 | #include <nvgpu/mm.h> | ||
36 | |||
37 | #include "gk20a/gk20a.h" | ||
38 | #include "gk20a/pmu_gk20a.h" | ||
39 | #include "mm_gm20b.h" | ||
40 | #include "acr_gm20b.h" | ||
41 | |||
42 | #include <nvgpu/hw/gm20b/hw_pwr_gm20b.h> | ||
43 | |||
44 | /*Defines*/ | ||
45 | #define gm20b_dbg_pmu(fmt, arg...) \ | ||
46 | gk20a_dbg(gpu_dbg_pmu, fmt, ##arg) | ||
47 | |||
48 | typedef int (*get_ucode_details)(struct gk20a *g, struct flcn_ucode_img *udata); | ||
49 | |||
50 | /*Externs*/ | ||
51 | |||
52 | /*Forwards*/ | ||
53 | static int pmu_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img); | ||
54 | static int fecs_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img); | ||
55 | static int gpccs_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img); | ||
56 | static int lsfm_discover_ucode_images(struct gk20a *g, | ||
57 | struct ls_flcn_mgr *plsfm); | ||
58 | static int lsfm_add_ucode_img(struct gk20a *g, struct ls_flcn_mgr *plsfm, | ||
59 | struct flcn_ucode_img *ucode_image, u32 falcon_id); | ||
60 | static void lsfm_free_ucode_img_res(struct gk20a *g, | ||
61 | struct flcn_ucode_img *p_img); | ||
62 | static void lsfm_free_nonpmu_ucode_img_res(struct gk20a *g, | ||
63 | struct flcn_ucode_img *p_img); | ||
64 | static int lsf_gen_wpr_requirements(struct gk20a *g, struct ls_flcn_mgr *plsfm); | ||
65 | static void lsfm_init_wpr_contents(struct gk20a *g, struct ls_flcn_mgr *plsfm, | ||
66 | struct nvgpu_mem *nonwpr); | ||
67 | static void free_acr_resources(struct gk20a *g, struct ls_flcn_mgr *plsfm); | ||
68 | |||
69 | /*Globals*/ | ||
70 | static get_ucode_details pmu_acr_supp_ucode_list[] = { | ||
71 | pmu_ucode_details, | ||
72 | fecs_ucode_details, | ||
73 | gpccs_ucode_details, | ||
74 | }; | ||
75 | |||
76 | /*Once is LS mode, cpuctl_alias is only accessible*/ | ||
77 | static void start_gm20b_pmu(struct gk20a *g) | ||
78 | { | ||
79 | /*disable irqs for hs falcon booting as we will poll for halt*/ | ||
80 | nvgpu_mutex_acquire(&g->pmu.isr_mutex); | ||
81 | pmu_enable_irq(&g->pmu, true); | ||
82 | g->pmu.isr_enabled = true; | ||
83 | nvgpu_mutex_release(&g->pmu.isr_mutex); | ||
84 | gk20a_writel(g, pwr_falcon_cpuctl_alias_r(), | ||
85 | pwr_falcon_cpuctl_startcpu_f(1)); | ||
86 | } | ||
87 | |||
88 | void gm20b_wpr_info(struct gk20a *g, struct wpr_carveout_info *inf) | ||
89 | { | ||
90 | g->ops.fb.read_wpr_info(g, inf); | ||
91 | } | ||
92 | |||
93 | bool gm20b_is_pmu_supported(struct gk20a *g) | ||
94 | { | ||
95 | return true; | ||
96 | } | ||
97 | |||
98 | static int pmu_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img) | ||
99 | { | ||
100 | struct nvgpu_firmware *pmu_fw, *pmu_desc, *pmu_sig; | ||
101 | struct nvgpu_pmu *pmu = &g->pmu; | ||
102 | struct lsf_ucode_desc *lsf_desc; | ||
103 | int err; | ||
104 | gm20b_dbg_pmu("requesting PMU ucode in GM20B\n"); | ||
105 | pmu_fw = nvgpu_request_firmware(g, GM20B_PMU_UCODE_IMAGE, 0); | ||
106 | if (!pmu_fw) { | ||
107 | nvgpu_err(g, "failed to load pmu ucode!!"); | ||
108 | return -ENOENT; | ||
109 | } | ||
110 | g->acr.pmu_fw = pmu_fw; | ||
111 | gm20b_dbg_pmu("Loaded PMU ucode in for blob preparation"); | ||
112 | |||
113 | gm20b_dbg_pmu("requesting PMU ucode desc in GM20B\n"); | ||
114 | pmu_desc = nvgpu_request_firmware(g, GM20B_PMU_UCODE_DESC, 0); | ||
115 | if (!pmu_desc) { | ||
116 | nvgpu_err(g, "failed to load pmu ucode desc!!"); | ||
117 | err = -ENOENT; | ||
118 | goto release_img_fw; | ||
119 | } | ||
120 | pmu_sig = nvgpu_request_firmware(g, GM20B_PMU_UCODE_SIG, 0); | ||
121 | if (!pmu_sig) { | ||
122 | nvgpu_err(g, "failed to load pmu sig!!"); | ||
123 | err = -ENOENT; | ||
124 | goto release_desc; | ||
125 | } | ||
126 | pmu->desc = (struct pmu_ucode_desc *)pmu_desc->data; | ||
127 | pmu->ucode_image = (u32 *)pmu_fw->data; | ||
128 | g->acr.pmu_desc = pmu_desc; | ||
129 | |||
130 | err = nvgpu_init_pmu_fw_support(pmu); | ||
131 | if (err) { | ||
132 | gm20b_dbg_pmu("failed to set function pointers\n"); | ||
133 | goto release_sig; | ||
134 | } | ||
135 | |||
136 | lsf_desc = nvgpu_kzalloc(g, sizeof(struct lsf_ucode_desc)); | ||
137 | if (!lsf_desc) { | ||
138 | err = -ENOMEM; | ||
139 | goto release_sig; | ||
140 | } | ||
141 | memcpy(lsf_desc, (void *)pmu_sig->data, sizeof(struct lsf_ucode_desc)); | ||
142 | lsf_desc->falcon_id = LSF_FALCON_ID_PMU; | ||
143 | |||
144 | p_img->desc = pmu->desc; | ||
145 | p_img->data = pmu->ucode_image; | ||
146 | p_img->data_size = pmu->desc->image_size; | ||
147 | p_img->fw_ver = NULL; | ||
148 | p_img->header = NULL; | ||
149 | p_img->lsf_desc = (struct lsf_ucode_desc *)lsf_desc; | ||
150 | gm20b_dbg_pmu("requesting PMU ucode in GM20B exit\n"); | ||
151 | nvgpu_release_firmware(g, pmu_sig); | ||
152 | return 0; | ||
153 | release_sig: | ||
154 | nvgpu_release_firmware(g, pmu_sig); | ||
155 | release_desc: | ||
156 | nvgpu_release_firmware(g, pmu_desc); | ||
157 | g->acr.pmu_desc = NULL; | ||
158 | release_img_fw: | ||
159 | nvgpu_release_firmware(g, pmu_fw); | ||
160 | g->acr.pmu_fw = NULL; | ||
161 | return err; | ||
162 | } | ||
163 | |||
164 | static int fecs_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img) | ||
165 | { | ||
166 | struct lsf_ucode_desc *lsf_desc; | ||
167 | struct nvgpu_firmware *fecs_sig; | ||
168 | int err; | ||
169 | |||
170 | fecs_sig = nvgpu_request_firmware(g, GM20B_FECS_UCODE_SIG, 0); | ||
171 | if (!fecs_sig) { | ||
172 | nvgpu_err(g, "failed to load fecs sig"); | ||
173 | return -ENOENT; | ||
174 | } | ||
175 | lsf_desc = nvgpu_kzalloc(g, sizeof(struct lsf_ucode_desc)); | ||
176 | if (!lsf_desc) { | ||
177 | err = -ENOMEM; | ||
178 | goto rel_sig; | ||
179 | } | ||
180 | memcpy(lsf_desc, (void *)fecs_sig->data, sizeof(struct lsf_ucode_desc)); | ||
181 | lsf_desc->falcon_id = LSF_FALCON_ID_FECS; | ||
182 | |||
183 | p_img->desc = nvgpu_kzalloc(g, sizeof(struct pmu_ucode_desc)); | ||
184 | if (p_img->desc == NULL) { | ||
185 | err = -ENOMEM; | ||
186 | goto free_lsf_desc; | ||
187 | } | ||
188 | |||
189 | p_img->desc->bootloader_start_offset = | ||
190 | g->ctxsw_ucode_info.fecs.boot.offset; | ||
191 | p_img->desc->bootloader_size = | ||
192 | ALIGN(g->ctxsw_ucode_info.fecs.boot.size, 256); | ||
193 | p_img->desc->bootloader_imem_offset = | ||
194 | g->ctxsw_ucode_info.fecs.boot_imem_offset; | ||
195 | p_img->desc->bootloader_entry_point = | ||
196 | g->ctxsw_ucode_info.fecs.boot_entry; | ||
197 | |||
198 | p_img->desc->image_size = | ||
199 | ALIGN(g->ctxsw_ucode_info.fecs.boot.size, 256) + | ||
200 | ALIGN(g->ctxsw_ucode_info.fecs.code.size, 256) + | ||
201 | ALIGN(g->ctxsw_ucode_info.fecs.data.size, 256); | ||
202 | p_img->desc->app_size = ALIGN(g->ctxsw_ucode_info.fecs.code.size, 256) + | ||
203 | ALIGN(g->ctxsw_ucode_info.fecs.data.size, 256); | ||
204 | p_img->desc->app_start_offset = g->ctxsw_ucode_info.fecs.code.offset; | ||
205 | p_img->desc->app_imem_offset = 0; | ||
206 | p_img->desc->app_imem_entry = 0; | ||
207 | p_img->desc->app_dmem_offset = 0; | ||
208 | p_img->desc->app_resident_code_offset = 0; | ||
209 | p_img->desc->app_resident_code_size = | ||
210 | g->ctxsw_ucode_info.fecs.code.size; | ||
211 | p_img->desc->app_resident_data_offset = | ||
212 | g->ctxsw_ucode_info.fecs.data.offset - | ||
213 | g->ctxsw_ucode_info.fecs.code.offset; | ||
214 | p_img->desc->app_resident_data_size = | ||
215 | g->ctxsw_ucode_info.fecs.data.size; | ||
216 | p_img->data = g->ctxsw_ucode_info.surface_desc.cpu_va; | ||
217 | p_img->data_size = p_img->desc->image_size; | ||
218 | |||
219 | p_img->fw_ver = NULL; | ||
220 | p_img->header = NULL; | ||
221 | p_img->lsf_desc = (struct lsf_ucode_desc *)lsf_desc; | ||
222 | gm20b_dbg_pmu("fecs fw loaded\n"); | ||
223 | nvgpu_release_firmware(g, fecs_sig); | ||
224 | return 0; | ||
225 | free_lsf_desc: | ||
226 | nvgpu_kfree(g, lsf_desc); | ||
227 | rel_sig: | ||
228 | nvgpu_release_firmware(g, fecs_sig); | ||
229 | return err; | ||
230 | } | ||
231 | static int gpccs_ucode_details(struct gk20a *g, struct flcn_ucode_img *p_img) | ||
232 | { | ||
233 | struct lsf_ucode_desc *lsf_desc; | ||
234 | struct nvgpu_firmware *gpccs_sig; | ||
235 | int err; | ||
236 | |||
237 | if (!nvgpu_is_enabled(g, NVGPU_SEC_SECUREGPCCS)) | ||
238 | return -ENOENT; | ||
239 | |||
240 | gpccs_sig = nvgpu_request_firmware(g, T18x_GPCCS_UCODE_SIG, 0); | ||
241 | if (!gpccs_sig) { | ||
242 | nvgpu_err(g, "failed to load gpccs sig"); | ||
243 | return -ENOENT; | ||
244 | } | ||
245 | lsf_desc = nvgpu_kzalloc(g, sizeof(struct lsf_ucode_desc)); | ||
246 | if (!lsf_desc) { | ||
247 | err = -ENOMEM; | ||
248 | goto rel_sig; | ||
249 | } | ||
250 | memcpy(lsf_desc, (void *)gpccs_sig->data, | ||
251 | sizeof(struct lsf_ucode_desc)); | ||
252 | lsf_desc->falcon_id = LSF_FALCON_ID_GPCCS; | ||
253 | |||
254 | p_img->desc = nvgpu_kzalloc(g, sizeof(struct pmu_ucode_desc)); | ||
255 | if (p_img->desc == NULL) { | ||
256 | err = -ENOMEM; | ||
257 | goto free_lsf_desc; | ||
258 | } | ||
259 | |||
260 | p_img->desc->bootloader_start_offset = | ||
261 | 0; | ||
262 | p_img->desc->bootloader_size = | ||
263 | ALIGN(g->ctxsw_ucode_info.gpccs.boot.size, 256); | ||
264 | p_img->desc->bootloader_imem_offset = | ||
265 | g->ctxsw_ucode_info.gpccs.boot_imem_offset; | ||
266 | p_img->desc->bootloader_entry_point = | ||
267 | g->ctxsw_ucode_info.gpccs.boot_entry; | ||
268 | |||
269 | p_img->desc->image_size = | ||
270 | ALIGN(g->ctxsw_ucode_info.gpccs.boot.size, 256) + | ||
271 | ALIGN(g->ctxsw_ucode_info.gpccs.code.size, 256) + | ||
272 | ALIGN(g->ctxsw_ucode_info.gpccs.data.size, 256); | ||
273 | p_img->desc->app_size = ALIGN(g->ctxsw_ucode_info.gpccs.code.size, 256) | ||
274 | + ALIGN(g->ctxsw_ucode_info.gpccs.data.size, 256); | ||
275 | p_img->desc->app_start_offset = p_img->desc->bootloader_size; | ||
276 | p_img->desc->app_imem_offset = 0; | ||
277 | p_img->desc->app_imem_entry = 0; | ||
278 | p_img->desc->app_dmem_offset = 0; | ||
279 | p_img->desc->app_resident_code_offset = 0; | ||
280 | p_img->desc->app_resident_code_size = | ||
281 | ALIGN(g->ctxsw_ucode_info.gpccs.code.size, 256); | ||
282 | p_img->desc->app_resident_data_offset = | ||
283 | ALIGN(g->ctxsw_ucode_info.gpccs.data.offset, 256) - | ||
284 | ALIGN(g->ctxsw_ucode_info.gpccs.code.offset, 256); | ||
285 | p_img->desc->app_resident_data_size = | ||
286 | ALIGN(g->ctxsw_ucode_info.gpccs.data.size, 256); | ||
287 | p_img->data = (u32 *)((u8 *)g->ctxsw_ucode_info.surface_desc.cpu_va + | ||
288 | g->ctxsw_ucode_info.gpccs.boot.offset); | ||
289 | p_img->data_size = ALIGN(p_img->desc->image_size, 256); | ||
290 | p_img->fw_ver = NULL; | ||
291 | p_img->header = NULL; | ||
292 | p_img->lsf_desc = (struct lsf_ucode_desc *)lsf_desc; | ||
293 | gm20b_dbg_pmu("gpccs fw loaded\n"); | ||
294 | nvgpu_release_firmware(g, gpccs_sig); | ||
295 | return 0; | ||
296 | free_lsf_desc: | ||
297 | nvgpu_kfree(g, lsf_desc); | ||
298 | rel_sig: | ||
299 | nvgpu_release_firmware(g, gpccs_sig); | ||
300 | return err; | ||
301 | } | ||
302 | |||
303 | bool gm20b_is_lazy_bootstrap(u32 falcon_id) | ||
304 | { | ||
305 | bool enable_status = false; | ||
306 | |||
307 | switch (falcon_id) { | ||
308 | case LSF_FALCON_ID_FECS: | ||
309 | enable_status = false; | ||
310 | break; | ||
311 | case LSF_FALCON_ID_GPCCS: | ||
312 | enable_status = false; | ||
313 | break; | ||
314 | default: | ||
315 | break; | ||
316 | } | ||
317 | |||
318 | return enable_status; | ||
319 | } | ||
320 | |||
321 | bool gm20b_is_priv_load(u32 falcon_id) | ||
322 | { | ||
323 | bool enable_status = false; | ||
324 | |||
325 | switch (falcon_id) { | ||
326 | case LSF_FALCON_ID_FECS: | ||
327 | enable_status = false; | ||
328 | break; | ||
329 | case LSF_FALCON_ID_GPCCS: | ||
330 | enable_status = false; | ||
331 | break; | ||
332 | default: | ||
333 | break; | ||
334 | } | ||
335 | |||
336 | return enable_status; | ||
337 | } | ||
338 | |||
339 | int gm20b_alloc_blob_space(struct gk20a *g, | ||
340 | size_t size, struct nvgpu_mem *mem) | ||
341 | { | ||
342 | int err; | ||
343 | |||
344 | err = nvgpu_dma_alloc_sys(g, size, mem); | ||
345 | |||
346 | return err; | ||
347 | } | ||
348 | |||
349 | int prepare_ucode_blob(struct gk20a *g) | ||
350 | { | ||
351 | |||
352 | int err; | ||
353 | struct ls_flcn_mgr lsfm_l, *plsfm; | ||
354 | struct nvgpu_pmu *pmu = &g->pmu; | ||
355 | struct wpr_carveout_info wpr_inf; | ||
356 | |||
357 | if (g->acr.ucode_blob.cpu_va) { | ||
358 | /*Recovery case, we do not need to form | ||
359 | non WPR blob of ucodes*/ | ||
360 | err = nvgpu_init_pmu_fw_support(pmu); | ||
361 | if (err) { | ||
362 | gm20b_dbg_pmu("failed to set function pointers\n"); | ||
363 | return err; | ||
364 | } | ||
365 | return 0; | ||
366 | } | ||
367 | plsfm = &lsfm_l; | ||
368 | memset((void *)plsfm, 0, sizeof(struct ls_flcn_mgr)); | ||
369 | gm20b_dbg_pmu("fetching GMMU regs\n"); | ||
370 | g->ops.fb.vpr_info_fetch(g); | ||
371 | gr_gk20a_init_ctxsw_ucode(g); | ||
372 | |||
373 | g->ops.pmu.get_wpr(g, &wpr_inf); | ||
374 | gm20b_dbg_pmu("wpr carveout base:%llx\n", wpr_inf.wpr_base); | ||
375 | gm20b_dbg_pmu("wpr carveout size :%llx\n", wpr_inf.size); | ||
376 | |||
377 | /* Discover all managed falcons*/ | ||
378 | err = lsfm_discover_ucode_images(g, plsfm); | ||
379 | gm20b_dbg_pmu(" Managed Falcon cnt %d\n", plsfm->managed_flcn_cnt); | ||
380 | if (err) | ||
381 | goto free_sgt; | ||
382 | |||
383 | if (plsfm->managed_flcn_cnt && !g->acr.ucode_blob.cpu_va) { | ||
384 | /* Generate WPR requirements*/ | ||
385 | err = lsf_gen_wpr_requirements(g, plsfm); | ||
386 | if (err) | ||
387 | goto free_sgt; | ||
388 | |||
389 | /*Alloc memory to hold ucode blob contents*/ | ||
390 | err = g->ops.pmu.alloc_blob_space(g, plsfm->wpr_size | ||
391 | , &g->acr.ucode_blob); | ||
392 | if (err) | ||
393 | goto free_sgt; | ||
394 | |||
395 | gm20b_dbg_pmu("managed LS falcon %d, WPR size %d bytes.\n", | ||
396 | plsfm->managed_flcn_cnt, plsfm->wpr_size); | ||
397 | lsfm_init_wpr_contents(g, plsfm, &g->acr.ucode_blob); | ||
398 | } else { | ||
399 | gm20b_dbg_pmu("LSFM is managing no falcons.\n"); | ||
400 | } | ||
401 | gm20b_dbg_pmu("prepare ucode blob return 0\n"); | ||
402 | free_acr_resources(g, plsfm); | ||
403 | free_sgt: | ||
404 | return err; | ||
405 | } | ||
406 | |||
407 | static u8 lsfm_falcon_disabled(struct gk20a *g, struct ls_flcn_mgr *plsfm, | ||
408 | u32 falcon_id) | ||
409 | { | ||
410 | return (plsfm->disable_mask >> falcon_id) & 0x1; | ||
411 | } | ||
412 | |||
413 | /* Discover all managed falcon ucode images */ | ||
414 | static int lsfm_discover_ucode_images(struct gk20a *g, | ||
415 | struct ls_flcn_mgr *plsfm) | ||
416 | { | ||
417 | struct nvgpu_pmu *pmu = &g->pmu; | ||
418 | struct flcn_ucode_img ucode_img; | ||
419 | u32 falcon_id; | ||
420 | u32 i; | ||
421 | int status; | ||
422 | |||
423 | /* LSFM requires a secure PMU, discover it first.*/ | ||
424 | /* Obtain the PMU ucode image and add it to the list if required*/ | ||
425 | memset(&ucode_img, 0, sizeof(ucode_img)); | ||
426 | status = pmu_ucode_details(g, &ucode_img); | ||
427 | if (status) | ||
428 | return status; | ||
429 | |||
430 | /* The falon_id is formed by grabbing the static base | ||
431 | * falon_id from the image and adding the | ||
432 | * engine-designated falcon instance.*/ | ||
433 | pmu->pmu_mode |= PMU_SECURE_MODE; | ||
434 | falcon_id = ucode_img.lsf_desc->falcon_id + | ||
435 | ucode_img.flcn_inst; | ||
436 | |||
437 | if (!lsfm_falcon_disabled(g, plsfm, falcon_id)) { | ||
438 | pmu->falcon_id = falcon_id; | ||
439 | if (lsfm_add_ucode_img(g, plsfm, &ucode_img, | ||
440 | pmu->falcon_id) == 0) | ||
441 | pmu->pmu_mode |= PMU_LSFM_MANAGED; | ||
442 | |||
443 | plsfm->managed_flcn_cnt++; | ||
444 | } else { | ||
445 | gm20b_dbg_pmu("id not managed %d\n", | ||
446 | ucode_img.lsf_desc->falcon_id); | ||
447 | } | ||
448 | |||
449 | /*Free any ucode image resources if not managing this falcon*/ | ||
450 | if (!(pmu->pmu_mode & PMU_LSFM_MANAGED)) { | ||
451 | gm20b_dbg_pmu("pmu is not LSFM managed\n"); | ||
452 | lsfm_free_ucode_img_res(g, &ucode_img); | ||
453 | } | ||
454 | |||
455 | /* Enumerate all constructed falcon objects, | ||
456 | as we need the ucode image info and total falcon count.*/ | ||
457 | |||
458 | /*0th index is always PMU which is already handled in earlier | ||
459 | if condition*/ | ||
460 | for (i = 1; i < (MAX_SUPPORTED_LSFM); i++) { | ||
461 | memset(&ucode_img, 0, sizeof(ucode_img)); | ||
462 | if (pmu_acr_supp_ucode_list[i](g, &ucode_img) == 0) { | ||
463 | if (ucode_img.lsf_desc != NULL) { | ||
464 | /* We have engine sigs, ensure that this falcon | ||
465 | is aware of the secure mode expectations | ||
466 | (ACR status)*/ | ||
467 | |||
468 | /* falon_id is formed by grabbing the static | ||
469 | base falonId from the image and adding the | ||
470 | engine-designated falcon instance. */ | ||
471 | falcon_id = ucode_img.lsf_desc->falcon_id + | ||
472 | ucode_img.flcn_inst; | ||
473 | |||
474 | if (!lsfm_falcon_disabled(g, plsfm, | ||
475 | falcon_id)) { | ||
476 | /* Do not manage non-FB ucode*/ | ||
477 | if (lsfm_add_ucode_img(g, | ||
478 | plsfm, &ucode_img, falcon_id) | ||
479 | == 0) | ||
480 | plsfm->managed_flcn_cnt++; | ||
481 | } else { | ||
482 | gm20b_dbg_pmu("not managed %d\n", | ||
483 | ucode_img.lsf_desc->falcon_id); | ||
484 | lsfm_free_nonpmu_ucode_img_res(g, | ||
485 | &ucode_img); | ||
486 | } | ||
487 | } | ||
488 | } else { | ||
489 | /* Consumed all available falcon objects */ | ||
490 | gm20b_dbg_pmu("Done checking for ucodes %d\n", i); | ||
491 | break; | ||
492 | } | ||
493 | } | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | |||
498 | int gm20b_pmu_populate_loader_cfg(struct gk20a *g, | ||
499 | void *lsfm, u32 *p_bl_gen_desc_size) | ||
500 | { | ||
501 | struct wpr_carveout_info wpr_inf; | ||
502 | struct nvgpu_pmu *pmu = &g->pmu; | ||
503 | struct lsfm_managed_ucode_img *p_lsfm = | ||
504 | (struct lsfm_managed_ucode_img *)lsfm; | ||
505 | struct flcn_ucode_img *p_img = &(p_lsfm->ucode_img); | ||
506 | struct loader_config *ldr_cfg = &(p_lsfm->bl_gen_desc.loader_cfg); | ||
507 | u64 addr_base; | ||
508 | struct pmu_ucode_desc *desc; | ||
509 | u64 addr_code, addr_data; | ||
510 | u32 addr_args; | ||
511 | |||
512 | if (p_img->desc == NULL) /*This means its a header based ucode, | ||
513 | and so we do not fill BL gen desc structure*/ | ||
514 | return -EINVAL; | ||
515 | desc = p_img->desc; | ||
516 | /* | ||
517 | Calculate physical and virtual addresses for various portions of | ||
518 | the PMU ucode image | ||
519 | Calculate the 32-bit addresses for the application code, application | ||
520 | data, and bootloader code. These values are all based on IM_BASE. | ||
521 | The 32-bit addresses will be the upper 32-bits of the virtual or | ||
522 | physical addresses of each respective segment. | ||
523 | */ | ||
524 | addr_base = p_lsfm->lsb_header.ucode_off; | ||
525 | g->ops.pmu.get_wpr(g, &wpr_inf); | ||
526 | addr_base += wpr_inf.wpr_base; | ||
527 | gm20b_dbg_pmu("pmu loader cfg u32 addrbase %x\n", (u32)addr_base); | ||
528 | /*From linux*/ | ||
529 | addr_code = u64_lo32((addr_base + | ||
530 | desc->app_start_offset + | ||
531 | desc->app_resident_code_offset) >> 8); | ||
532 | gm20b_dbg_pmu("app start %d app res code off %d\n", | ||
533 | desc->app_start_offset, desc->app_resident_code_offset); | ||
534 | addr_data = u64_lo32((addr_base + | ||
535 | desc->app_start_offset + | ||
536 | desc->app_resident_data_offset) >> 8); | ||
537 | gm20b_dbg_pmu("app res data offset%d\n", | ||
538 | desc->app_resident_data_offset); | ||
539 | gm20b_dbg_pmu("bl start off %d\n", desc->bootloader_start_offset); | ||
540 | |||
541 | addr_args = ((pwr_falcon_hwcfg_dmem_size_v( | ||
542 | gk20a_readl(g, pwr_falcon_hwcfg_r()))) | ||
543 | << GK20A_PMU_DMEM_BLKSIZE2); | ||
544 | addr_args -= g->ops.pmu_ver.get_pmu_cmdline_args_size(pmu); | ||
545 | |||
546 | gm20b_dbg_pmu("addr_args %x\n", addr_args); | ||
547 | |||
548 | /* Populate the loader_config state*/ | ||
549 | ldr_cfg->dma_idx = GK20A_PMU_DMAIDX_UCODE; | ||
550 | ldr_cfg->code_dma_base = addr_code; | ||
551 | ldr_cfg->code_dma_base1 = 0x0; | ||
552 | ldr_cfg->code_size_total = desc->app_size; | ||
553 | ldr_cfg->code_size_to_load = desc->app_resident_code_size; | ||
554 | ldr_cfg->code_entry_point = desc->app_imem_entry; | ||
555 | ldr_cfg->data_dma_base = addr_data; | ||
556 | ldr_cfg->data_dma_base1 = 0; | ||
557 | ldr_cfg->data_size = desc->app_resident_data_size; | ||
558 | ldr_cfg->overlay_dma_base = addr_code; | ||
559 | ldr_cfg->overlay_dma_base1 = 0x0; | ||
560 | |||
561 | /* Update the argc/argv members*/ | ||
562 | ldr_cfg->argc = 1; | ||
563 | ldr_cfg->argv = addr_args; | ||
564 | |||
565 | *p_bl_gen_desc_size = sizeof(struct loader_config); | ||
566 | g->acr.pmu_args = addr_args; | ||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | int gm20b_flcn_populate_bl_dmem_desc(struct gk20a *g, | ||
571 | void *lsfm, u32 *p_bl_gen_desc_size, u32 falconid) | ||
572 | { | ||
573 | struct wpr_carveout_info wpr_inf; | ||
574 | struct lsfm_managed_ucode_img *p_lsfm = | ||
575 | (struct lsfm_managed_ucode_img *)lsfm; | ||
576 | struct flcn_ucode_img *p_img = &(p_lsfm->ucode_img); | ||
577 | struct flcn_bl_dmem_desc *ldr_cfg = | ||
578 | &(p_lsfm->bl_gen_desc.bl_dmem_desc); | ||
579 | u64 addr_base; | ||
580 | struct pmu_ucode_desc *desc; | ||
581 | u64 addr_code, addr_data; | ||
582 | |||
583 | if (p_img->desc == NULL) /*This means its a header based ucode, | ||
584 | and so we do not fill BL gen desc structure*/ | ||
585 | return -EINVAL; | ||
586 | desc = p_img->desc; | ||
587 | |||
588 | /* | ||
589 | Calculate physical and virtual addresses for various portions of | ||
590 | the PMU ucode image | ||
591 | Calculate the 32-bit addresses for the application code, application | ||
592 | data, and bootloader code. These values are all based on IM_BASE. | ||
593 | The 32-bit addresses will be the upper 32-bits of the virtual or | ||
594 | physical addresses of each respective segment. | ||
595 | */ | ||
596 | addr_base = p_lsfm->lsb_header.ucode_off; | ||
597 | g->ops.pmu.get_wpr(g, &wpr_inf); | ||
598 | addr_base += wpr_inf.wpr_base; | ||
599 | |||
600 | gm20b_dbg_pmu("gen loader cfg %x u32 addrbase %x ID\n", (u32)addr_base, | ||
601 | p_lsfm->wpr_header.falcon_id); | ||
602 | addr_code = u64_lo32((addr_base + | ||
603 | desc->app_start_offset + | ||
604 | desc->app_resident_code_offset) >> 8); | ||
605 | addr_data = u64_lo32((addr_base + | ||
606 | desc->app_start_offset + | ||
607 | desc->app_resident_data_offset) >> 8); | ||
608 | |||
609 | gm20b_dbg_pmu("gen cfg %x u32 addrcode %x & data %x load offset %xID\n", | ||
610 | (u32)addr_code, (u32)addr_data, desc->bootloader_start_offset, | ||
611 | p_lsfm->wpr_header.falcon_id); | ||
612 | |||
613 | /* Populate the LOADER_CONFIG state */ | ||
614 | memset((void *) ldr_cfg, 0, sizeof(struct flcn_bl_dmem_desc)); | ||
615 | ldr_cfg->ctx_dma = GK20A_PMU_DMAIDX_UCODE; | ||
616 | ldr_cfg->code_dma_base = addr_code; | ||
617 | ldr_cfg->non_sec_code_size = desc->app_resident_code_size; | ||
618 | ldr_cfg->data_dma_base = addr_data; | ||
619 | ldr_cfg->data_size = desc->app_resident_data_size; | ||
620 | ldr_cfg->code_entry_point = desc->app_imem_entry; | ||
621 | *p_bl_gen_desc_size = sizeof(struct flcn_bl_dmem_desc); | ||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | /* Populate falcon boot loader generic desc.*/ | ||
626 | static int lsfm_fill_flcn_bl_gen_desc(struct gk20a *g, | ||
627 | struct lsfm_managed_ucode_img *pnode) | ||
628 | { | ||
629 | |||
630 | struct nvgpu_pmu *pmu = &g->pmu; | ||
631 | if (pnode->wpr_header.falcon_id != pmu->falcon_id) { | ||
632 | gm20b_dbg_pmu("non pmu. write flcn bl gen desc\n"); | ||
633 | g->ops.pmu.flcn_populate_bl_dmem_desc(g, | ||
634 | pnode, &pnode->bl_gen_desc_size, | ||
635 | pnode->wpr_header.falcon_id); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | if (pmu->pmu_mode & PMU_LSFM_MANAGED) { | ||
640 | gm20b_dbg_pmu("pmu write flcn bl gen desc\n"); | ||
641 | if (pnode->wpr_header.falcon_id == pmu->falcon_id) | ||
642 | return g->ops.pmu.pmu_populate_loader_cfg(g, pnode, | ||
643 | &pnode->bl_gen_desc_size); | ||
644 | } | ||
645 | |||
646 | /* Failed to find the falcon requested. */ | ||
647 | return -ENOENT; | ||
648 | } | ||
649 | |||
650 | /* Initialize WPR contents */ | ||
651 | static void lsfm_init_wpr_contents(struct gk20a *g, struct ls_flcn_mgr *plsfm, | ||
652 | struct nvgpu_mem *ucode) | ||
653 | { | ||
654 | struct lsfm_managed_ucode_img *pnode = plsfm->ucode_img_list; | ||
655 | struct lsf_wpr_header last_wpr_hdr; | ||
656 | u32 i; | ||
657 | |||
658 | /* The WPR array is at the base of the WPR */ | ||
659 | pnode = plsfm->ucode_img_list; | ||
660 | memset(&last_wpr_hdr, 0, sizeof(struct lsf_wpr_header)); | ||
661 | i = 0; | ||
662 | |||
663 | /* | ||
664 | * Walk the managed falcons, flush WPR and LSB headers to FB. | ||
665 | * flush any bl args to the storage area relative to the | ||
666 | * ucode image (appended on the end as a DMEM area). | ||
667 | */ | ||
668 | while (pnode) { | ||
669 | /* Flush WPR header to memory*/ | ||
670 | nvgpu_mem_wr_n(g, ucode, i * sizeof(pnode->wpr_header), | ||
671 | &pnode->wpr_header, sizeof(pnode->wpr_header)); | ||
672 | |||
673 | gm20b_dbg_pmu("wpr header"); | ||
674 | gm20b_dbg_pmu("falconid :%d", | ||
675 | pnode->wpr_header.falcon_id); | ||
676 | gm20b_dbg_pmu("lsb_offset :%x", | ||
677 | pnode->wpr_header.lsb_offset); | ||
678 | gm20b_dbg_pmu("bootstrap_owner :%d", | ||
679 | pnode->wpr_header.bootstrap_owner); | ||
680 | gm20b_dbg_pmu("lazy_bootstrap :%d", | ||
681 | pnode->wpr_header.lazy_bootstrap); | ||
682 | gm20b_dbg_pmu("status :%d", | ||
683 | pnode->wpr_header.status); | ||
684 | |||
685 | /*Flush LSB header to memory*/ | ||
686 | nvgpu_mem_wr_n(g, ucode, pnode->wpr_header.lsb_offset, | ||
687 | &pnode->lsb_header, sizeof(pnode->lsb_header)); | ||
688 | |||
689 | gm20b_dbg_pmu("lsb header"); | ||
690 | gm20b_dbg_pmu("ucode_off :%x", | ||
691 | pnode->lsb_header.ucode_off); | ||
692 | gm20b_dbg_pmu("ucode_size :%x", | ||
693 | pnode->lsb_header.ucode_size); | ||
694 | gm20b_dbg_pmu("data_size :%x", | ||
695 | pnode->lsb_header.data_size); | ||
696 | gm20b_dbg_pmu("bl_code_size :%x", | ||
697 | pnode->lsb_header.bl_code_size); | ||
698 | gm20b_dbg_pmu("bl_imem_off :%x", | ||
699 | pnode->lsb_header.bl_imem_off); | ||
700 | gm20b_dbg_pmu("bl_data_off :%x", | ||
701 | pnode->lsb_header.bl_data_off); | ||
702 | gm20b_dbg_pmu("bl_data_size :%x", | ||
703 | pnode->lsb_header.bl_data_size); | ||
704 | gm20b_dbg_pmu("app_code_off :%x", | ||
705 | pnode->lsb_header.app_code_off); | ||
706 | gm20b_dbg_pmu("app_code_size :%x", | ||
707 | pnode->lsb_header.app_code_size); | ||
708 | gm20b_dbg_pmu("app_data_off :%x", | ||
709 | pnode->lsb_header.app_data_off); | ||
710 | gm20b_dbg_pmu("app_data_size :%x", | ||
711 | pnode->lsb_header.app_data_size); | ||
712 | gm20b_dbg_pmu("flags :%x", | ||
713 | pnode->lsb_header.flags); | ||
714 | |||
715 | /*If this falcon has a boot loader and related args, | ||
716 | * flush them.*/ | ||
717 | if (!pnode->ucode_img.header) { | ||
718 | /*Populate gen bl and flush to memory*/ | ||
719 | lsfm_fill_flcn_bl_gen_desc(g, pnode); | ||
720 | nvgpu_mem_wr_n(g, ucode, | ||
721 | pnode->lsb_header.bl_data_off, | ||
722 | &pnode->bl_gen_desc, | ||
723 | pnode->bl_gen_desc_size); | ||
724 | } | ||
725 | /*Copying of ucode*/ | ||
726 | nvgpu_mem_wr_n(g, ucode, pnode->lsb_header.ucode_off, | ||
727 | pnode->ucode_img.data, | ||
728 | pnode->ucode_img.data_size); | ||
729 | pnode = pnode->next; | ||
730 | i++; | ||
731 | } | ||
732 | |||
733 | /* Tag the terminator WPR header with an invalid falcon ID. */ | ||
734 | last_wpr_hdr.falcon_id = LSF_FALCON_ID_INVALID; | ||
735 | nvgpu_mem_wr_n(g, ucode, | ||
736 | plsfm->managed_flcn_cnt * sizeof(struct lsf_wpr_header), | ||
737 | &last_wpr_hdr, | ||
738 | sizeof(struct lsf_wpr_header)); | ||
739 | } | ||
740 | |||
741 | /*! | ||
742 | * lsfm_parse_no_loader_ucode: parses UCODE header of falcon | ||
743 | * | ||
744 | * @param[in] p_ucodehdr : UCODE header | ||
745 | * @param[out] lsb_hdr : updates values in LSB header | ||
746 | * | ||
747 | * @return 0 | ||
748 | */ | ||
749 | static int lsfm_parse_no_loader_ucode(u32 *p_ucodehdr, | ||
750 | struct lsf_lsb_header *lsb_hdr) | ||
751 | { | ||
752 | |||
753 | u32 code_size = 0; | ||
754 | u32 data_size = 0; | ||
755 | u32 i = 0; | ||
756 | u32 total_apps = p_ucodehdr[FLCN_NL_UCODE_HDR_NUM_APPS_IND]; | ||
757 | |||
758 | /* Lets calculate code size*/ | ||
759 | code_size += p_ucodehdr[FLCN_NL_UCODE_HDR_OS_CODE_SIZE_IND]; | ||
760 | for (i = 0; i < total_apps; i++) { | ||
761 | code_size += p_ucodehdr[FLCN_NL_UCODE_HDR_APP_CODE_SIZE_IND | ||
762 | (total_apps, i)]; | ||
763 | } | ||
764 | code_size += p_ucodehdr[FLCN_NL_UCODE_HDR_OS_OVL_SIZE_IND(total_apps)]; | ||
765 | |||
766 | /* Calculate data size*/ | ||
767 | data_size += p_ucodehdr[FLCN_NL_UCODE_HDR_OS_DATA_SIZE_IND]; | ||
768 | for (i = 0; i < total_apps; i++) { | ||
769 | data_size += p_ucodehdr[FLCN_NL_UCODE_HDR_APP_DATA_SIZE_IND | ||
770 | (total_apps, i)]; | ||
771 | } | ||
772 | |||
773 | lsb_hdr->ucode_size = code_size; | ||
774 | lsb_hdr->data_size = data_size; | ||
775 | lsb_hdr->bl_code_size = p_ucodehdr[FLCN_NL_UCODE_HDR_OS_CODE_SIZE_IND]; | ||
776 | lsb_hdr->bl_imem_off = 0; | ||
777 | lsb_hdr->bl_data_off = p_ucodehdr[FLCN_NL_UCODE_HDR_OS_DATA_OFF_IND]; | ||
778 | lsb_hdr->bl_data_size = p_ucodehdr[FLCN_NL_UCODE_HDR_OS_DATA_SIZE_IND]; | ||
779 | return 0; | ||
780 | } | ||
781 | |||
782 | /*! | ||
783 | * @brief lsfm_fill_static_lsb_hdr_info | ||
784 | * Populate static LSB header infomation using the provided ucode image | ||
785 | */ | ||
786 | static void lsfm_fill_static_lsb_hdr_info(struct gk20a *g, | ||
787 | u32 falcon_id, struct lsfm_managed_ucode_img *pnode) | ||
788 | { | ||
789 | |||
790 | struct nvgpu_pmu *pmu = &g->pmu; | ||
791 | u32 full_app_size = 0; | ||
792 | u32 data = 0; | ||
793 | |||
794 | if (pnode->ucode_img.lsf_desc) | ||
795 | memcpy(&pnode->lsb_header.signature, pnode->ucode_img.lsf_desc, | ||
796 | sizeof(struct lsf_ucode_desc)); | ||
797 | pnode->lsb_header.ucode_size = pnode->ucode_img.data_size; | ||
798 | |||
799 | /* The remainder of the LSB depends on the loader usage */ | ||
800 | if (pnode->ucode_img.header) { | ||
801 | /* Does not use a loader */ | ||
802 | pnode->lsb_header.data_size = 0; | ||
803 | pnode->lsb_header.bl_code_size = 0; | ||
804 | pnode->lsb_header.bl_data_off = 0; | ||
805 | pnode->lsb_header.bl_data_size = 0; | ||
806 | |||
807 | lsfm_parse_no_loader_ucode(pnode->ucode_img.header, | ||
808 | &(pnode->lsb_header)); | ||
809 | |||
810 | /* Load the first 256 bytes of IMEM. */ | ||
811 | /* Set LOAD_CODE_AT_0 and DMACTL_REQ_CTX. | ||
812 | True for all method based falcons */ | ||
813 | data = NV_FLCN_ACR_LSF_FLAG_LOAD_CODE_AT_0_TRUE | | ||
814 | NV_FLCN_ACR_LSF_FLAG_DMACTL_REQ_CTX_TRUE; | ||
815 | pnode->lsb_header.flags = data; | ||
816 | } else { | ||
817 | /* Uses a loader. that is has a desc */ | ||
818 | pnode->lsb_header.data_size = 0; | ||
819 | |||
820 | /* The loader code size is already aligned (padded) such that | ||
821 | the code following it is aligned, but the size in the image | ||
822 | desc is not, bloat it up to be on a 256 byte alignment. */ | ||
823 | pnode->lsb_header.bl_code_size = ALIGN( | ||
824 | pnode->ucode_img.desc->bootloader_size, | ||
825 | LSF_BL_CODE_SIZE_ALIGNMENT); | ||
826 | full_app_size = ALIGN(pnode->ucode_img.desc->app_size, | ||
827 | LSF_BL_CODE_SIZE_ALIGNMENT) + | ||
828 | pnode->lsb_header.bl_code_size; | ||
829 | pnode->lsb_header.ucode_size = ALIGN( | ||
830 | pnode->ucode_img.desc->app_resident_data_offset, | ||
831 | LSF_BL_CODE_SIZE_ALIGNMENT) + | ||
832 | pnode->lsb_header.bl_code_size; | ||
833 | pnode->lsb_header.data_size = full_app_size - | ||
834 | pnode->lsb_header.ucode_size; | ||
835 | /* Though the BL is located at 0th offset of the image, the VA | ||
836 | is different to make sure that it doesnt collide the actual OS | ||
837 | VA range */ | ||
838 | pnode->lsb_header.bl_imem_off = | ||
839 | pnode->ucode_img.desc->bootloader_imem_offset; | ||
840 | |||
841 | /* TODO: OBJFLCN should export properties using which the below | ||
842 | flags should be populated.*/ | ||
843 | pnode->lsb_header.flags = 0; | ||
844 | |||
845 | if (falcon_id == pmu->falcon_id) { | ||
846 | data = NV_FLCN_ACR_LSF_FLAG_DMACTL_REQ_CTX_TRUE; | ||
847 | pnode->lsb_header.flags = data; | ||
848 | } | ||
849 | |||
850 | if (g->ops.pmu.is_priv_load(falcon_id)) { | ||
851 | pnode->lsb_header.flags |= | ||
852 | NV_FLCN_ACR_LSF_FLAG_FORCE_PRIV_LOAD_TRUE; | ||
853 | } | ||
854 | } | ||
855 | } | ||
856 | |||
857 | /* Adds a ucode image to the list of managed ucode images managed. */ | ||
858 | static int lsfm_add_ucode_img(struct gk20a *g, struct ls_flcn_mgr *plsfm, | ||
859 | struct flcn_ucode_img *ucode_image, u32 falcon_id) | ||
860 | { | ||
861 | |||
862 | struct lsfm_managed_ucode_img *pnode; | ||
863 | pnode = nvgpu_kzalloc(g, sizeof(struct lsfm_managed_ucode_img)); | ||
864 | if (pnode == NULL) | ||
865 | return -ENOMEM; | ||
866 | |||
867 | /* Keep a copy of the ucode image info locally */ | ||
868 | memcpy(&pnode->ucode_img, ucode_image, sizeof(struct flcn_ucode_img)); | ||
869 | |||
870 | /* Fill in static WPR header info*/ | ||
871 | pnode->wpr_header.falcon_id = falcon_id; | ||
872 | pnode->wpr_header.bootstrap_owner = LSF_BOOTSTRAP_OWNER_DEFAULT; | ||
873 | pnode->wpr_header.status = LSF_IMAGE_STATUS_COPY; | ||
874 | |||
875 | pnode->wpr_header.lazy_bootstrap = | ||
876 | g->ops.pmu.is_lazy_bootstrap(falcon_id); | ||
877 | |||
878 | /*TODO to check if PDB_PROP_FLCN_LAZY_BOOTSTRAP is to be supported by | ||
879 | Android */ | ||
880 | /* Fill in static LSB header info elsewhere */ | ||
881 | lsfm_fill_static_lsb_hdr_info(g, falcon_id, pnode); | ||
882 | pnode->next = plsfm->ucode_img_list; | ||
883 | plsfm->ucode_img_list = pnode; | ||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | /* Free any ucode image structure resources. */ | ||
888 | static void lsfm_free_ucode_img_res(struct gk20a *g, | ||
889 | struct flcn_ucode_img *p_img) | ||
890 | { | ||
891 | if (p_img->lsf_desc != NULL) { | ||
892 | nvgpu_kfree(g, p_img->lsf_desc); | ||
893 | p_img->lsf_desc = NULL; | ||
894 | } | ||
895 | } | ||
896 | |||
897 | /* Free any ucode image structure resources. */ | ||
898 | static void lsfm_free_nonpmu_ucode_img_res(struct gk20a *g, | ||
899 | struct flcn_ucode_img *p_img) | ||
900 | { | ||
901 | if (p_img->lsf_desc != NULL) { | ||
902 | nvgpu_kfree(g, p_img->lsf_desc); | ||
903 | p_img->lsf_desc = NULL; | ||
904 | } | ||
905 | if (p_img->desc != NULL) { | ||
906 | nvgpu_kfree(g, p_img->desc); | ||
907 | p_img->desc = NULL; | ||
908 | } | ||
909 | } | ||
910 | |||
911 | static void free_acr_resources(struct gk20a *g, struct ls_flcn_mgr *plsfm) | ||
912 | { | ||
913 | u32 cnt = plsfm->managed_flcn_cnt; | ||
914 | struct lsfm_managed_ucode_img *mg_ucode_img; | ||
915 | while (cnt) { | ||
916 | mg_ucode_img = plsfm->ucode_img_list; | ||
917 | if (mg_ucode_img->ucode_img.lsf_desc->falcon_id == | ||
918 | LSF_FALCON_ID_PMU) | ||
919 | lsfm_free_ucode_img_res(g, &mg_ucode_img->ucode_img); | ||
920 | else | ||
921 | lsfm_free_nonpmu_ucode_img_res(g, | ||
922 | &mg_ucode_img->ucode_img); | ||
923 | plsfm->ucode_img_list = mg_ucode_img->next; | ||
924 | nvgpu_kfree(g, mg_ucode_img); | ||
925 | cnt--; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | /* Generate WPR requirements for ACR allocation request */ | ||
930 | static int lsf_gen_wpr_requirements(struct gk20a *g, struct ls_flcn_mgr *plsfm) | ||
931 | { | ||
932 | struct lsfm_managed_ucode_img *pnode = plsfm->ucode_img_list; | ||
933 | u32 wpr_offset; | ||
934 | |||
935 | /* Calculate WPR size required */ | ||
936 | |||
937 | /* Start with an array of WPR headers at the base of the WPR. | ||
938 | The expectation here is that the secure falcon will do a single DMA | ||
939 | read of this array and cache it internally so it's OK to pack these. | ||
940 | Also, we add 1 to the falcon count to indicate the end of the array.*/ | ||
941 | wpr_offset = sizeof(struct lsf_wpr_header) * | ||
942 | (plsfm->managed_flcn_cnt+1); | ||
943 | |||
944 | /* Walk the managed falcons, accounting for the LSB structs | ||
945 | as well as the ucode images. */ | ||
946 | while (pnode) { | ||
947 | /* Align, save off, and include an LSB header size */ | ||
948 | wpr_offset = ALIGN(wpr_offset, | ||
949 | LSF_LSB_HEADER_ALIGNMENT); | ||
950 | pnode->wpr_header.lsb_offset = wpr_offset; | ||
951 | wpr_offset += sizeof(struct lsf_lsb_header); | ||
952 | |||
953 | /* Align, save off, and include the original (static) | ||
954 | ucode image size */ | ||
955 | wpr_offset = ALIGN(wpr_offset, | ||
956 | LSF_UCODE_DATA_ALIGNMENT); | ||
957 | pnode->lsb_header.ucode_off = wpr_offset; | ||
958 | wpr_offset += pnode->ucode_img.data_size; | ||
959 | |||
960 | /* For falcons that use a boot loader (BL), we append a loader | ||
961 | desc structure on the end of the ucode image and consider this | ||
962 | the boot loader data. The host will then copy the loader desc | ||
963 | args to this space within the WPR region (before locking down) | ||
964 | and the HS bin will then copy them to DMEM 0 for the loader. */ | ||
965 | if (!pnode->ucode_img.header) { | ||
966 | /* Track the size for LSB details filled in later | ||
967 | Note that at this point we don't know what kind of i | ||
968 | boot loader desc, so we just take the size of the | ||
969 | generic one, which is the largest it will will ever be. | ||
970 | */ | ||
971 | /* Align (size bloat) and save off generic | ||
972 | descriptor size*/ | ||
973 | pnode->lsb_header.bl_data_size = ALIGN( | ||
974 | sizeof(pnode->bl_gen_desc), | ||
975 | LSF_BL_DATA_SIZE_ALIGNMENT); | ||
976 | |||
977 | /*Align, save off, and include the additional BL data*/ | ||
978 | wpr_offset = ALIGN(wpr_offset, | ||
979 | LSF_BL_DATA_ALIGNMENT); | ||
980 | pnode->lsb_header.bl_data_off = wpr_offset; | ||
981 | wpr_offset += pnode->lsb_header.bl_data_size; | ||
982 | } else { | ||
983 | /* bl_data_off is already assigned in static | ||
984 | information. But that is from start of the image */ | ||
985 | pnode->lsb_header.bl_data_off += | ||
986 | (wpr_offset - pnode->ucode_img.data_size); | ||
987 | } | ||
988 | |||
989 | /* Finally, update ucode surface size to include updates */ | ||
990 | pnode->full_ucode_size = wpr_offset - | ||
991 | pnode->lsb_header.ucode_off; | ||
992 | if (pnode->wpr_header.falcon_id != LSF_FALCON_ID_PMU) { | ||
993 | pnode->lsb_header.app_code_off = | ||
994 | pnode->lsb_header.bl_code_size; | ||
995 | pnode->lsb_header.app_code_size = | ||
996 | pnode->lsb_header.ucode_size - | ||
997 | pnode->lsb_header.bl_code_size; | ||
998 | pnode->lsb_header.app_data_off = | ||
999 | pnode->lsb_header.ucode_size; | ||
1000 | pnode->lsb_header.app_data_size = | ||
1001 | pnode->lsb_header.data_size; | ||
1002 | } | ||
1003 | pnode = pnode->next; | ||
1004 | } | ||
1005 | plsfm->wpr_size = wpr_offset; | ||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | /*Loads ACR bin to FB mem and bootstraps PMU with bootloader code | ||
1010 | * start and end are addresses of ucode blob in non-WPR region*/ | ||
1011 | int gm20b_bootstrap_hs_flcn(struct gk20a *g) | ||
1012 | { | ||
1013 | struct mm_gk20a *mm = &g->mm; | ||
1014 | struct vm_gk20a *vm = mm->pmu.vm; | ||
1015 | int err = 0; | ||
1016 | u64 *acr_dmem; | ||
1017 | u32 img_size_in_bytes = 0; | ||
1018 | u32 status, size; | ||
1019 | u64 start; | ||
1020 | struct acr_desc *acr = &g->acr; | ||
1021 | struct nvgpu_firmware *acr_fw = acr->acr_fw; | ||
1022 | struct flcn_bl_dmem_desc *bl_dmem_desc = &acr->bl_dmem_desc; | ||
1023 | u32 *acr_ucode_header_t210_load; | ||
1024 | u32 *acr_ucode_data_t210_load; | ||
1025 | |||
1026 | start = nvgpu_mem_get_addr(g, &acr->ucode_blob); | ||
1027 | size = acr->ucode_blob.size; | ||
1028 | |||
1029 | gm20b_dbg_pmu(""); | ||
1030 | |||
1031 | if (!acr_fw) { | ||
1032 | /*First time init case*/ | ||
1033 | acr_fw = nvgpu_request_firmware(g, GM20B_HSBIN_PMU_UCODE_IMAGE, 0); | ||
1034 | if (!acr_fw) { | ||
1035 | nvgpu_err(g, "pmu ucode get fail"); | ||
1036 | return -ENOENT; | ||
1037 | } | ||
1038 | acr->acr_fw = acr_fw; | ||
1039 | acr->hsbin_hdr = (struct bin_hdr *)acr_fw->data; | ||
1040 | acr->fw_hdr = (struct acr_fw_header *)(acr_fw->data + | ||
1041 | acr->hsbin_hdr->header_offset); | ||
1042 | acr_ucode_data_t210_load = (u32 *)(acr_fw->data + | ||
1043 | acr->hsbin_hdr->data_offset); | ||
1044 | acr_ucode_header_t210_load = (u32 *)(acr_fw->data + | ||
1045 | acr->fw_hdr->hdr_offset); | ||
1046 | img_size_in_bytes = ALIGN((acr->hsbin_hdr->data_size), 256); | ||
1047 | |||
1048 | /* Lets patch the signatures first.. */ | ||
1049 | if (acr_ucode_patch_sig(g, acr_ucode_data_t210_load, | ||
1050 | (u32 *)(acr_fw->data + | ||
1051 | acr->fw_hdr->sig_prod_offset), | ||
1052 | (u32 *)(acr_fw->data + | ||
1053 | acr->fw_hdr->sig_dbg_offset), | ||
1054 | (u32 *)(acr_fw->data + | ||
1055 | acr->fw_hdr->patch_loc), | ||
1056 | (u32 *)(acr_fw->data + | ||
1057 | acr->fw_hdr->patch_sig)) < 0) { | ||
1058 | nvgpu_err(g, "patch signatures fail"); | ||
1059 | err = -1; | ||
1060 | goto err_release_acr_fw; | ||
1061 | } | ||
1062 | err = nvgpu_dma_alloc_map_sys(vm, img_size_in_bytes, | ||
1063 | &acr->acr_ucode); | ||
1064 | if (err) { | ||
1065 | err = -ENOMEM; | ||
1066 | goto err_release_acr_fw; | ||
1067 | } | ||
1068 | |||
1069 | acr_dmem = (u64 *) | ||
1070 | &(((u8 *)acr_ucode_data_t210_load)[ | ||
1071 | acr_ucode_header_t210_load[2]]); | ||
1072 | acr->acr_dmem_desc = (struct flcn_acr_desc *)((u8 *)( | ||
1073 | acr->acr_ucode.cpu_va) + acr_ucode_header_t210_load[2]); | ||
1074 | ((struct flcn_acr_desc *)acr_dmem)->nonwpr_ucode_blob_start = | ||
1075 | start; | ||
1076 | ((struct flcn_acr_desc *)acr_dmem)->nonwpr_ucode_blob_size = | ||
1077 | size; | ||
1078 | ((struct flcn_acr_desc *)acr_dmem)->regions.no_regions = 2; | ||
1079 | ((struct flcn_acr_desc *)acr_dmem)->wpr_offset = 0; | ||
1080 | |||
1081 | nvgpu_mem_wr_n(g, &acr->acr_ucode, 0, | ||
1082 | acr_ucode_data_t210_load, img_size_in_bytes); | ||
1083 | /* | ||
1084 | * In order to execute this binary, we will be using | ||
1085 | * a bootloader which will load this image into PMU IMEM/DMEM. | ||
1086 | * Fill up the bootloader descriptor for PMU HAL to use.. | ||
1087 | * TODO: Use standard descriptor which the generic bootloader is | ||
1088 | * checked in. | ||
1089 | */ | ||
1090 | |||
1091 | bl_dmem_desc->signature[0] = 0; | ||
1092 | bl_dmem_desc->signature[1] = 0; | ||
1093 | bl_dmem_desc->signature[2] = 0; | ||
1094 | bl_dmem_desc->signature[3] = 0; | ||
1095 | bl_dmem_desc->ctx_dma = GK20A_PMU_DMAIDX_VIRT; | ||
1096 | bl_dmem_desc->code_dma_base = | ||
1097 | (unsigned int)(((u64)acr->acr_ucode.gpu_va >> 8)); | ||
1098 | bl_dmem_desc->code_dma_base1 = 0x0; | ||
1099 | bl_dmem_desc->non_sec_code_off = acr_ucode_header_t210_load[0]; | ||
1100 | bl_dmem_desc->non_sec_code_size = acr_ucode_header_t210_load[1]; | ||
1101 | bl_dmem_desc->sec_code_off = acr_ucode_header_t210_load[5]; | ||
1102 | bl_dmem_desc->sec_code_size = acr_ucode_header_t210_load[6]; | ||
1103 | bl_dmem_desc->code_entry_point = 0; /* Start at 0th offset */ | ||
1104 | bl_dmem_desc->data_dma_base = | ||
1105 | bl_dmem_desc->code_dma_base + | ||
1106 | ((acr_ucode_header_t210_load[2]) >> 8); | ||
1107 | bl_dmem_desc->data_dma_base1 = 0x0; | ||
1108 | bl_dmem_desc->data_size = acr_ucode_header_t210_load[3]; | ||
1109 | } else | ||
1110 | acr->acr_dmem_desc->nonwpr_ucode_blob_size = 0; | ||
1111 | status = pmu_exec_gen_bl(g, bl_dmem_desc, 1); | ||
1112 | if (status != 0) { | ||
1113 | err = status; | ||
1114 | goto err_free_ucode_map; | ||
1115 | } | ||
1116 | return 0; | ||
1117 | err_free_ucode_map: | ||
1118 | nvgpu_dma_unmap_free(vm, &acr->acr_ucode); | ||
1119 | err_release_acr_fw: | ||
1120 | nvgpu_release_firmware(g, acr_fw); | ||
1121 | acr->acr_fw = NULL; | ||
1122 | return err; | ||
1123 | } | ||
1124 | |||
1125 | static u8 pmu_is_debug_mode_en(struct gk20a *g) | ||
1126 | { | ||
1127 | u32 ctl_stat = gk20a_readl(g, pwr_pmu_scpctl_stat_r()); | ||
1128 | return pwr_pmu_scpctl_stat_debug_mode_v(ctl_stat); | ||
1129 | } | ||
1130 | |||
1131 | /* | ||
1132 | * @brief Patch signatures into ucode image | ||
1133 | */ | ||
1134 | int acr_ucode_patch_sig(struct gk20a *g, | ||
1135 | unsigned int *p_img, | ||
1136 | unsigned int *p_prod_sig, | ||
1137 | unsigned int *p_dbg_sig, | ||
1138 | unsigned int *p_patch_loc, | ||
1139 | unsigned int *p_patch_ind) | ||
1140 | { | ||
1141 | unsigned int i, *p_sig; | ||
1142 | gm20b_dbg_pmu(""); | ||
1143 | |||
1144 | if (!pmu_is_debug_mode_en(g)) { | ||
1145 | p_sig = p_prod_sig; | ||
1146 | gm20b_dbg_pmu("PRODUCTION MODE\n"); | ||
1147 | } else { | ||
1148 | p_sig = p_dbg_sig; | ||
1149 | gm20b_dbg_pmu("DEBUG MODE\n"); | ||
1150 | } | ||
1151 | |||
1152 | /* Patching logic:*/ | ||
1153 | for (i = 0; i < sizeof(*p_patch_loc)>>2; i++) { | ||
1154 | p_img[(p_patch_loc[i]>>2)] = p_sig[(p_patch_ind[i]<<2)]; | ||
1155 | p_img[(p_patch_loc[i]>>2)+1] = p_sig[(p_patch_ind[i]<<2)+1]; | ||
1156 | p_img[(p_patch_loc[i]>>2)+2] = p_sig[(p_patch_ind[i]<<2)+2]; | ||
1157 | p_img[(p_patch_loc[i]>>2)+3] = p_sig[(p_patch_ind[i]<<2)+3]; | ||
1158 | } | ||
1159 | return 0; | ||
1160 | } | ||
1161 | |||
1162 | static int bl_bootstrap(struct nvgpu_pmu *pmu, | ||
1163 | struct flcn_bl_dmem_desc *pbl_desc, u32 bl_sz) | ||
1164 | { | ||
1165 | struct gk20a *g = gk20a_from_pmu(pmu); | ||
1166 | struct acr_desc *acr = &g->acr; | ||
1167 | struct mm_gk20a *mm = &g->mm; | ||
1168 | u32 virt_addr = 0; | ||
1169 | struct hsflcn_bl_desc *pmu_bl_gm10x_desc = g->acr.pmu_hsbl_desc; | ||
1170 | u32 dst; | ||
1171 | |||
1172 | gk20a_dbg_fn(""); | ||
1173 | gk20a_writel(g, pwr_falcon_itfen_r(), | ||
1174 | gk20a_readl(g, pwr_falcon_itfen_r()) | | ||
1175 | pwr_falcon_itfen_ctxen_enable_f()); | ||
1176 | gk20a_writel(g, pwr_pmu_new_instblk_r(), | ||
1177 | pwr_pmu_new_instblk_ptr_f( | ||
1178 | nvgpu_inst_block_addr(g, &mm->pmu.inst_block) >> 12) | | ||
1179 | pwr_pmu_new_instblk_valid_f(1) | | ||
1180 | pwr_pmu_new_instblk_target_sys_coh_f()); | ||
1181 | |||
1182 | /*copy bootloader interface structure to dmem*/ | ||
1183 | nvgpu_flcn_copy_to_dmem(pmu->flcn, 0, (u8 *)pbl_desc, | ||
1184 | sizeof(struct flcn_bl_dmem_desc), 0); | ||
1185 | |||
1186 | /* copy bootloader to TOP of IMEM */ | ||
1187 | dst = (pwr_falcon_hwcfg_imem_size_v( | ||
1188 | gk20a_readl(g, pwr_falcon_hwcfg_r())) << 8) - bl_sz; | ||
1189 | |||
1190 | nvgpu_flcn_copy_to_imem(pmu->flcn, dst, | ||
1191 | (u8 *)(acr->hsbl_ucode.cpu_va), bl_sz, 0, 0, | ||
1192 | pmu_bl_gm10x_desc->bl_start_tag); | ||
1193 | |||
1194 | gm20b_dbg_pmu("Before starting falcon with BL\n"); | ||
1195 | |||
1196 | virt_addr = pmu_bl_gm10x_desc->bl_start_tag << 8; | ||
1197 | |||
1198 | nvgpu_flcn_bootstrap(pmu->flcn, virt_addr); | ||
1199 | |||
1200 | return 0; | ||
1201 | } | ||
1202 | |||
1203 | int gm20b_init_nspmu_setup_hw1(struct gk20a *g) | ||
1204 | { | ||
1205 | struct nvgpu_pmu *pmu = &g->pmu; | ||
1206 | int err = 0; | ||
1207 | |||
1208 | gk20a_dbg_fn(""); | ||
1209 | |||
1210 | nvgpu_mutex_acquire(&pmu->isr_mutex); | ||
1211 | nvgpu_flcn_reset(pmu->flcn); | ||
1212 | pmu->isr_enabled = true; | ||
1213 | nvgpu_mutex_release(&pmu->isr_mutex); | ||
1214 | |||
1215 | /* setup apertures - virtual */ | ||
1216 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_UCODE), | ||
1217 | pwr_fbif_transcfg_mem_type_virtual_f()); | ||
1218 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_VIRT), | ||
1219 | pwr_fbif_transcfg_mem_type_virtual_f()); | ||
1220 | /* setup apertures - physical */ | ||
1221 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_VID), | ||
1222 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1223 | pwr_fbif_transcfg_target_local_fb_f()); | ||
1224 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_SYS_COH), | ||
1225 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1226 | pwr_fbif_transcfg_target_coherent_sysmem_f()); | ||
1227 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_SYS_NCOH), | ||
1228 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1229 | pwr_fbif_transcfg_target_noncoherent_sysmem_f()); | ||
1230 | |||
1231 | err = g->ops.pmu.pmu_nsbootstrap(pmu); | ||
1232 | |||
1233 | return err; | ||
1234 | } | ||
1235 | |||
1236 | int gm20b_init_pmu_setup_hw1(struct gk20a *g, | ||
1237 | void *desc, u32 bl_sz) | ||
1238 | { | ||
1239 | |||
1240 | struct nvgpu_pmu *pmu = &g->pmu; | ||
1241 | int err; | ||
1242 | |||
1243 | gk20a_dbg_fn(""); | ||
1244 | |||
1245 | nvgpu_mutex_acquire(&pmu->isr_mutex); | ||
1246 | nvgpu_flcn_reset(pmu->flcn); | ||
1247 | pmu->isr_enabled = true; | ||
1248 | nvgpu_mutex_release(&pmu->isr_mutex); | ||
1249 | |||
1250 | /* setup apertures - virtual */ | ||
1251 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_UCODE), | ||
1252 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1253 | pwr_fbif_transcfg_target_local_fb_f()); | ||
1254 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_VIRT), | ||
1255 | pwr_fbif_transcfg_mem_type_virtual_f()); | ||
1256 | /* setup apertures - physical */ | ||
1257 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_VID), | ||
1258 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1259 | pwr_fbif_transcfg_target_local_fb_f()); | ||
1260 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_SYS_COH), | ||
1261 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1262 | pwr_fbif_transcfg_target_coherent_sysmem_f()); | ||
1263 | gk20a_writel(g, pwr_fbif_transcfg_r(GK20A_PMU_DMAIDX_PHYS_SYS_NCOH), | ||
1264 | pwr_fbif_transcfg_mem_type_physical_f() | | ||
1265 | pwr_fbif_transcfg_target_noncoherent_sysmem_f()); | ||
1266 | |||
1267 | /*Copying pmu cmdline args*/ | ||
1268 | g->ops.pmu_ver.set_pmu_cmdline_args_cpu_freq(pmu, | ||
1269 | g->ops.clk.get_rate(g, CTRL_CLK_DOMAIN_PWRCLK)); | ||
1270 | g->ops.pmu_ver.set_pmu_cmdline_args_secure_mode(pmu, 1); | ||
1271 | g->ops.pmu_ver.set_pmu_cmdline_args_trace_size( | ||
1272 | pmu, GK20A_PMU_TRACE_BUFSIZE); | ||
1273 | g->ops.pmu_ver.set_pmu_cmdline_args_trace_dma_base(pmu); | ||
1274 | g->ops.pmu_ver.set_pmu_cmdline_args_trace_dma_idx( | ||
1275 | pmu, GK20A_PMU_DMAIDX_VIRT); | ||
1276 | nvgpu_flcn_copy_to_dmem(pmu->flcn, g->acr.pmu_args, | ||
1277 | (u8 *)(g->ops.pmu_ver.get_pmu_cmdline_args_ptr(pmu)), | ||
1278 | g->ops.pmu_ver.get_pmu_cmdline_args_size(pmu), 0); | ||
1279 | /*disable irqs for hs falcon booting as we will poll for halt*/ | ||
1280 | nvgpu_mutex_acquire(&pmu->isr_mutex); | ||
1281 | pmu_enable_irq(pmu, false); | ||
1282 | pmu->isr_enabled = false; | ||
1283 | nvgpu_mutex_release(&pmu->isr_mutex); | ||
1284 | /*Clearing mailbox register used to reflect capabilities*/ | ||
1285 | gk20a_writel(g, pwr_falcon_mailbox1_r(), 0); | ||
1286 | err = bl_bootstrap(pmu, desc, bl_sz); | ||
1287 | if (err) | ||
1288 | return err; | ||
1289 | return 0; | ||
1290 | } | ||
1291 | |||
1292 | /* | ||
1293 | * Executes a generic bootloader and wait for PMU to halt. | ||
1294 | * This BL will be used for those binaries that are loaded | ||
1295 | * and executed at times other than RM PMU Binary execution. | ||
1296 | * | ||
1297 | * @param[in] g gk20a pointer | ||
1298 | * @param[in] desc Bootloader descriptor | ||
1299 | * @param[in] dma_idx DMA Index | ||
1300 | * @param[in] b_wait_for_halt Wait for PMU to HALT | ||
1301 | */ | ||
1302 | int pmu_exec_gen_bl(struct gk20a *g, void *desc, u8 b_wait_for_halt) | ||
1303 | { | ||
1304 | struct mm_gk20a *mm = &g->mm; | ||
1305 | struct vm_gk20a *vm = mm->pmu.vm; | ||
1306 | int err = 0; | ||
1307 | u32 bl_sz; | ||
1308 | struct acr_desc *acr = &g->acr; | ||
1309 | struct nvgpu_firmware *hsbl_fw = acr->hsbl_fw; | ||
1310 | struct hsflcn_bl_desc *pmu_bl_gm10x_desc; | ||
1311 | u32 *pmu_bl_gm10x = NULL; | ||
1312 | gm20b_dbg_pmu(""); | ||
1313 | |||
1314 | if (!hsbl_fw) { | ||
1315 | hsbl_fw = nvgpu_request_firmware(g, | ||
1316 | GM20B_HSBIN_PMU_BL_UCODE_IMAGE, 0); | ||
1317 | if (!hsbl_fw) { | ||
1318 | nvgpu_err(g, "pmu ucode load fail"); | ||
1319 | return -ENOENT; | ||
1320 | } | ||
1321 | acr->hsbl_fw = hsbl_fw; | ||
1322 | acr->bl_bin_hdr = (struct bin_hdr *)hsbl_fw->data; | ||
1323 | acr->pmu_hsbl_desc = (struct hsflcn_bl_desc *)(hsbl_fw->data + | ||
1324 | acr->bl_bin_hdr->header_offset); | ||
1325 | pmu_bl_gm10x_desc = acr->pmu_hsbl_desc; | ||
1326 | pmu_bl_gm10x = (u32 *)(hsbl_fw->data + | ||
1327 | acr->bl_bin_hdr->data_offset); | ||
1328 | bl_sz = ALIGN(pmu_bl_gm10x_desc->bl_img_hdr.bl_code_size, | ||
1329 | 256); | ||
1330 | acr->hsbl_ucode.size = bl_sz; | ||
1331 | gm20b_dbg_pmu("Executing Generic Bootloader\n"); | ||
1332 | |||
1333 | /*TODO in code verify that enable PMU is done, | ||
1334 | scrubbing etc is done*/ | ||
1335 | /*TODO in code verify that gmmu vm init is done*/ | ||
1336 | err = nvgpu_dma_alloc_flags_sys(g, | ||
1337 | NVGPU_DMA_READ_ONLY, bl_sz, &acr->hsbl_ucode); | ||
1338 | if (err) { | ||
1339 | nvgpu_err(g, "failed to allocate memory"); | ||
1340 | goto err_done; | ||
1341 | } | ||
1342 | |||
1343 | acr->hsbl_ucode.gpu_va = nvgpu_gmmu_map(vm, | ||
1344 | &acr->hsbl_ucode, | ||
1345 | bl_sz, | ||
1346 | 0, /* flags */ | ||
1347 | gk20a_mem_flag_read_only, false, | ||
1348 | acr->hsbl_ucode.aperture); | ||
1349 | if (!acr->hsbl_ucode.gpu_va) { | ||
1350 | nvgpu_err(g, "failed to map pmu ucode memory!!"); | ||
1351 | goto err_free_ucode; | ||
1352 | } | ||
1353 | |||
1354 | nvgpu_mem_wr_n(g, &acr->hsbl_ucode, 0, pmu_bl_gm10x, bl_sz); | ||
1355 | gm20b_dbg_pmu("Copied bl ucode to bl_cpuva\n"); | ||
1356 | } | ||
1357 | /* | ||
1358 | * Disable interrupts to avoid kernel hitting breakpoint due | ||
1359 | * to PMU halt | ||
1360 | */ | ||
1361 | |||
1362 | if (g->ops.pmu.falcon_clear_halt_interrupt_status(g, | ||
1363 | gk20a_get_gr_idle_timeout(g))) | ||
1364 | goto err_unmap_bl; | ||
1365 | |||
1366 | gm20b_dbg_pmu("phys sec reg %x\n", gk20a_readl(g, | ||
1367 | pwr_falcon_mmu_phys_sec_r())); | ||
1368 | gm20b_dbg_pmu("sctl reg %x\n", gk20a_readl(g, pwr_falcon_sctl_r())); | ||
1369 | |||
1370 | g->ops.pmu.init_falcon_setup_hw(g, desc, acr->hsbl_ucode.size); | ||
1371 | |||
1372 | /* Poll for HALT */ | ||
1373 | if (b_wait_for_halt) { | ||
1374 | err = g->ops.pmu.falcon_wait_for_halt(g, | ||
1375 | ACR_COMPLETION_TIMEOUT_MS); | ||
1376 | if (err == 0) { | ||
1377 | /* Clear the HALT interrupt */ | ||
1378 | if (g->ops.pmu.falcon_clear_halt_interrupt_status(g, | ||
1379 | gk20a_get_gr_idle_timeout(g))) | ||
1380 | goto err_unmap_bl; | ||
1381 | } | ||
1382 | else | ||
1383 | goto err_unmap_bl; | ||
1384 | } | ||
1385 | gm20b_dbg_pmu("after waiting for halt, err %x\n", err); | ||
1386 | gm20b_dbg_pmu("phys sec reg %x\n", gk20a_readl(g, | ||
1387 | pwr_falcon_mmu_phys_sec_r())); | ||
1388 | gm20b_dbg_pmu("sctl reg %x\n", gk20a_readl(g, pwr_falcon_sctl_r())); | ||
1389 | start_gm20b_pmu(g); | ||
1390 | return 0; | ||
1391 | err_unmap_bl: | ||
1392 | nvgpu_gmmu_unmap(vm, &acr->hsbl_ucode, acr->hsbl_ucode.gpu_va); | ||
1393 | err_free_ucode: | ||
1394 | nvgpu_dma_free(g, &acr->hsbl_ucode); | ||
1395 | err_done: | ||
1396 | nvgpu_release_firmware(g, hsbl_fw); | ||
1397 | return err; | ||
1398 | } | ||
1399 | |||
1400 | /*! | ||
1401 | * Wait for PMU to halt | ||
1402 | * @param[in] g GPU object pointer | ||
1403 | * @param[in] timeout_ms Timeout in msec for PMU to halt | ||
1404 | * @return '0' if PMU halts | ||
1405 | */ | ||
1406 | int pmu_wait_for_halt(struct gk20a *g, unsigned int timeout_ms) | ||
1407 | { | ||
1408 | struct nvgpu_pmu *pmu = &g->pmu; | ||
1409 | u32 data = 0; | ||
1410 | int ret = -EBUSY; | ||
1411 | |||
1412 | ret = nvgpu_flcn_wait_for_halt(pmu->flcn, timeout_ms); | ||
1413 | if (ret) { | ||
1414 | nvgpu_err(g, "ACR boot timed out"); | ||
1415 | return ret; | ||
1416 | } | ||
1417 | |||
1418 | g->acr.capabilities = gk20a_readl(g, pwr_falcon_mailbox1_r()); | ||
1419 | gm20b_dbg_pmu("ACR capabilities %x\n", g->acr.capabilities); | ||
1420 | data = gk20a_readl(g, pwr_falcon_mailbox0_r()); | ||
1421 | if (data) { | ||
1422 | nvgpu_err(g, "ACR boot failed, err %x", data); | ||
1423 | ret = -EAGAIN; | ||
1424 | } | ||
1425 | |||
1426 | return ret; | ||
1427 | } | ||
1428 | |||
1429 | /*! | ||
1430 | * Wait for PMU halt interrupt status to be cleared | ||
1431 | * @param[in] g GPU object pointer | ||
1432 | * @param[in] timeout_ms Timeout in msec for halt to clear | ||
1433 | * @return '0' if PMU halt irq status is clear | ||
1434 | */ | ||
1435 | int clear_halt_interrupt_status(struct gk20a *g, unsigned int timeout_ms) | ||
1436 | { | ||
1437 | struct nvgpu_pmu *pmu = &g->pmu; | ||
1438 | int status = 0; | ||
1439 | |||
1440 | if (nvgpu_flcn_clear_halt_intr_status(pmu->flcn, timeout_ms)) | ||
1441 | status = -EBUSY; | ||
1442 | |||
1443 | return status; | ||
1444 | } | ||