diff options
Diffstat (limited to 'include/gk20a/gk20a.c')
-rw-r--r-- | include/gk20a/gk20a.c | 595 |
1 files changed, 0 insertions, 595 deletions
diff --git a/include/gk20a/gk20a.c b/include/gk20a/gk20a.c deleted file mode 100644 index 1a11716..0000000 --- a/include/gk20a/gk20a.c +++ /dev/null | |||
@@ -1,595 +0,0 @@ | |||
1 | /* | ||
2 | * GK20A Graphics | ||
3 | * | ||
4 | * Copyright (c) 2011-2021, NVIDIA CORPORATION. All rights reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the "Software"), | ||
8 | * to deal in the Software without restriction, including without limitation | ||
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | * and/or sell copies of the Software, and to permit persons to whom the | ||
11 | * Software is furnished to do so, subject to the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice shall be included in | ||
14 | * all copies or substantial portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include <nvgpu/nvgpu_common.h> | ||
26 | #include <nvgpu/kmem.h> | ||
27 | #include <nvgpu/allocator.h> | ||
28 | #include <nvgpu/timers.h> | ||
29 | #include <nvgpu/soc.h> | ||
30 | #include <nvgpu/enabled.h> | ||
31 | #include <nvgpu/pmu.h> | ||
32 | #include <nvgpu/gmmu.h> | ||
33 | #include <nvgpu/ltc.h> | ||
34 | #include <nvgpu/vidmem.h> | ||
35 | #include <nvgpu/mm.h> | ||
36 | #include <nvgpu/ctxsw_trace.h> | ||
37 | #include <nvgpu/soc.h> | ||
38 | #include <nvgpu/clk_arb.h> | ||
39 | #include <nvgpu/therm.h> | ||
40 | #include <nvgpu/mc.h> | ||
41 | #include <nvgpu/channel_sync.h> | ||
42 | #include <nvgpu/nvgpu_err.h> | ||
43 | |||
44 | #include <trace/events/gk20a.h> | ||
45 | |||
46 | #include "gk20a.h" | ||
47 | |||
48 | #include "dbg_gpu_gk20a.h" | ||
49 | #include "pstate/pstate.h" | ||
50 | |||
51 | void __nvgpu_check_gpu_state(struct gk20a *g) | ||
52 | { | ||
53 | u32 boot_0 = 0xffffffff; | ||
54 | |||
55 | boot_0 = nvgpu_mc_boot_0(g, NULL, NULL, NULL); | ||
56 | if (boot_0 == 0xffffffff) { | ||
57 | nvgpu_err(g, "GPU has disappeared from bus!!"); | ||
58 | nvgpu_err(g, "Rebooting system!!"); | ||
59 | nvgpu_kernel_restart(NULL); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | void __gk20a_warn_on_no_regs(void) | ||
64 | { | ||
65 | WARN_ONCE(1, "Attempted access to GPU regs after unmapping!"); | ||
66 | } | ||
67 | |||
68 | static void gk20a_mask_interrupts(struct gk20a *g) | ||
69 | { | ||
70 | if (g->ops.mc.intr_mask != NULL) { | ||
71 | g->ops.mc.intr_mask(g); | ||
72 | } | ||
73 | |||
74 | if (g->ops.mc.log_pending_intrs != NULL) { | ||
75 | g->ops.mc.log_pending_intrs(g); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | int gk20a_prepare_poweroff(struct gk20a *g) | ||
80 | { | ||
81 | int ret = 0; | ||
82 | |||
83 | nvgpu_log_fn(g, " "); | ||
84 | |||
85 | if (g->ops.fifo.channel_suspend) { | ||
86 | ret = g->ops.fifo.channel_suspend(g); | ||
87 | if (ret) { | ||
88 | return ret; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | /* disable elpg before gr or fifo suspend */ | ||
93 | if (g->ops.pmu.is_pmu_supported(g)) { | ||
94 | ret |= nvgpu_pmu_destroy(g); | ||
95 | } | ||
96 | |||
97 | if (nvgpu_is_enabled(g, NVGPU_SUPPORT_SEC2_RTOS)) { | ||
98 | ret |= nvgpu_sec2_destroy(g); | ||
99 | } | ||
100 | |||
101 | ret |= gk20a_gr_suspend(g); | ||
102 | ret |= nvgpu_mm_suspend(g); | ||
103 | ret |= gk20a_fifo_suspend(g); | ||
104 | |||
105 | gk20a_ce_suspend(g); | ||
106 | |||
107 | /* Disable GPCPLL */ | ||
108 | if (g->ops.clk.suspend_clk_support) { | ||
109 | ret |= g->ops.clk.suspend_clk_support(g); | ||
110 | } | ||
111 | |||
112 | if (nvgpu_is_enabled(g, NVGPU_PMU_PSTATE)) { | ||
113 | gk20a_deinit_pstate_support(g); | ||
114 | } | ||
115 | |||
116 | gk20a_mask_interrupts(g); | ||
117 | |||
118 | g->power_on = false; | ||
119 | |||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | int gk20a_finalize_poweron(struct gk20a *g) | ||
124 | { | ||
125 | int err = 0; | ||
126 | #if defined(CONFIG_TEGRA_GK20A_NVHOST) | ||
127 | u32 nr_pages; | ||
128 | #endif | ||
129 | |||
130 | u32 fuse_status; | ||
131 | |||
132 | nvgpu_log_fn(g, " "); | ||
133 | |||
134 | if (g->power_on) { | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | g->power_on = true; | ||
139 | |||
140 | /* | ||
141 | * Before probing the GPU make sure the GPU's state is cleared. This is | ||
142 | * relevant for rebind operations. | ||
143 | */ | ||
144 | if (g->ops.xve.reset_gpu && !g->gpu_reset_done) { | ||
145 | g->ops.xve.reset_gpu(g); | ||
146 | g->gpu_reset_done = true; | ||
147 | } | ||
148 | |||
149 | if (g->ops.clock_gating.slcg_acb_load_gating_prod != NULL) { | ||
150 | g->ops.clock_gating.slcg_acb_load_gating_prod(g, true); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * Do this early so any early VMs that get made are capable of mapping | ||
155 | * buffers. | ||
156 | */ | ||
157 | err = nvgpu_pd_cache_init(g); | ||
158 | if (err) { | ||
159 | return err; | ||
160 | } | ||
161 | |||
162 | /* init interface layer support for PMU falcon */ | ||
163 | err = nvgpu_flcn_sw_init(g, FALCON_ID_PMU); | ||
164 | if (err != 0) { | ||
165 | nvgpu_err(g, "failed to sw init FALCON_ID_PMU"); | ||
166 | goto done; | ||
167 | } | ||
168 | err = nvgpu_flcn_sw_init(g, FALCON_ID_SEC2); | ||
169 | if (err != 0) { | ||
170 | nvgpu_err(g, "failed to sw init FALCON_ID_SEC2"); | ||
171 | goto done; | ||
172 | } | ||
173 | err = nvgpu_flcn_sw_init(g, FALCON_ID_NVDEC); | ||
174 | if (err != 0) { | ||
175 | nvgpu_err(g, "failed to sw init FALCON_ID_NVDEC"); | ||
176 | goto done; | ||
177 | } | ||
178 | err = nvgpu_flcn_sw_init(g, FALCON_ID_GSPLITE); | ||
179 | if (err != 0) { | ||
180 | nvgpu_err(g, "failed to sw init FALCON_ID_GSPLITE"); | ||
181 | goto done; | ||
182 | } | ||
183 | |||
184 | if (g->ops.acr.acr_sw_init != NULL && | ||
185 | nvgpu_is_enabled(g, NVGPU_SEC_PRIVSECURITY)) { | ||
186 | g->ops.acr.acr_sw_init(g, &g->acr); | ||
187 | } | ||
188 | |||
189 | if (g->ops.bios.init) { | ||
190 | err = g->ops.bios.init(g); | ||
191 | } | ||
192 | if (err) { | ||
193 | goto done; | ||
194 | } | ||
195 | |||
196 | g->ops.bus.init_hw(g); | ||
197 | |||
198 | if (g->ops.clk.disable_slowboot) { | ||
199 | g->ops.clk.disable_slowboot(g); | ||
200 | } | ||
201 | |||
202 | g->ops.priv_ring.enable_priv_ring(g); | ||
203 | |||
204 | /* TBD: move this after graphics init in which blcg/slcg is enabled. | ||
205 | This function removes SlowdownOnBoot which applies 32x divider | ||
206 | on gpcpll bypass path. The purpose of slowdown is to save power | ||
207 | during boot but it also significantly slows down gk20a init on | ||
208 | simulation and emulation. We should remove SOB after graphics power | ||
209 | saving features (blcg/slcg) are enabled. For now, do it here. */ | ||
210 | if (g->ops.clk.init_clk_support) { | ||
211 | err = g->ops.clk.init_clk_support(g); | ||
212 | if (err) { | ||
213 | nvgpu_err(g, "failed to init gk20a clk"); | ||
214 | goto done; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | if (nvgpu_is_enabled(g, NVGPU_SUPPORT_NVLINK)) { | ||
219 | err = g->ops.nvlink.init(g); | ||
220 | if (err) { | ||
221 | nvgpu_err(g, "failed to init nvlink"); | ||
222 | goto done; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | if (g->ops.fb.init_fbpa) { | ||
227 | err = g->ops.fb.init_fbpa(g); | ||
228 | if (err) { | ||
229 | nvgpu_err(g, "failed to init fbpa"); | ||
230 | goto done; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | if (g->ops.fb.mem_unlock) { | ||
235 | err = g->ops.fb.mem_unlock(g); | ||
236 | if (err) { | ||
237 | nvgpu_err(g, "failed to unlock memory"); | ||
238 | goto done; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | err = g->ops.fifo.reset_enable_hw(g); | ||
243 | |||
244 | if (err) { | ||
245 | nvgpu_err(g, "failed to reset gk20a fifo"); | ||
246 | goto done; | ||
247 | } | ||
248 | |||
249 | err = nvgpu_init_ltc_support(g); | ||
250 | if (err) { | ||
251 | nvgpu_err(g, "failed to init ltc"); | ||
252 | goto done; | ||
253 | } | ||
254 | |||
255 | err = nvgpu_init_mm_support(g); | ||
256 | if (err) { | ||
257 | nvgpu_err(g, "failed to init gk20a mm"); | ||
258 | goto done; | ||
259 | } | ||
260 | |||
261 | err = gk20a_init_fifo_support(g); | ||
262 | if (err) { | ||
263 | nvgpu_err(g, "failed to init gk20a fifo"); | ||
264 | goto done; | ||
265 | } | ||
266 | |||
267 | if (g->ops.therm.elcg_init_idle_filters) { | ||
268 | g->ops.therm.elcg_init_idle_filters(g); | ||
269 | } | ||
270 | |||
271 | g->ops.mc.intr_enable(g); | ||
272 | |||
273 | /* | ||
274 | * Power gate the chip as per the TPC PG mask | ||
275 | * and the fuse_status register. | ||
276 | * If TPC PG mask is invalid halt the GPU poweron. | ||
277 | */ | ||
278 | g->can_tpc_powergate = false; | ||
279 | fuse_status = g->ops.fuse.fuse_status_opt_tpc_gpc(g, 0); | ||
280 | |||
281 | if (g->ops.tpc.tpc_powergate) { | ||
282 | err = g->ops.tpc.tpc_powergate(g, fuse_status); | ||
283 | } | ||
284 | |||
285 | if (err) { | ||
286 | nvgpu_err(g, "failed to power ON GPU"); | ||
287 | goto done; | ||
288 | } | ||
289 | |||
290 | nvgpu_mutex_acquire(&g->tpc_pg_lock); | ||
291 | |||
292 | if (g->can_tpc_powergate) { | ||
293 | if (g->ops.gr.powergate_tpc != NULL) | ||
294 | g->ops.gr.powergate_tpc(g); | ||
295 | } | ||
296 | |||
297 | err = gk20a_enable_gr_hw(g); | ||
298 | if (err) { | ||
299 | nvgpu_err(g, "failed to enable gr"); | ||
300 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
301 | goto done; | ||
302 | } | ||
303 | |||
304 | if (g->ops.pmu.is_pmu_supported(g)) { | ||
305 | if (g->ops.pmu.prepare_ucode) { | ||
306 | err = g->ops.pmu.prepare_ucode(g); | ||
307 | } | ||
308 | if (err) { | ||
309 | nvgpu_err(g, "failed to init pmu ucode"); | ||
310 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
311 | goto done; | ||
312 | } | ||
313 | } | ||
314 | |||
315 | if (nvgpu_is_enabled(g, NVGPU_PMU_PSTATE)) { | ||
316 | err = gk20a_init_pstate_support(g); | ||
317 | if (err) { | ||
318 | nvgpu_err(g, "failed to init pstates"); | ||
319 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
320 | goto done; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | if (g->acr.bootstrap_hs_acr != NULL && | ||
325 | nvgpu_is_enabled(g, NVGPU_SEC_PRIVSECURITY)) { | ||
326 | err = g->acr.bootstrap_hs_acr(g, &g->acr, &g->acr.acr); | ||
327 | if (err != 0) { | ||
328 | nvgpu_err(g, "ACR bootstrap failed"); | ||
329 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
330 | goto done; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | if (nvgpu_is_enabled(g, NVGPU_SUPPORT_SEC2_RTOS)) { | ||
335 | err = nvgpu_init_sec2_support(g); | ||
336 | if (err != 0) { | ||
337 | nvgpu_err(g, "failed to init sec2"); | ||
338 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
339 | goto done; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | if (g->ops.pmu.is_pmu_supported(g)) { | ||
344 | err = nvgpu_init_pmu_support(g); | ||
345 | if (err) { | ||
346 | nvgpu_err(g, "failed to init gk20a pmu"); | ||
347 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
348 | goto done; | ||
349 | } | ||
350 | } | ||
351 | |||
352 | err = gk20a_init_gr_support(g); | ||
353 | if (err) { | ||
354 | nvgpu_err(g, "failed to init gk20a gr"); | ||
355 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
356 | goto done; | ||
357 | } | ||
358 | |||
359 | nvgpu_mutex_release(&g->tpc_pg_lock); | ||
360 | |||
361 | if (nvgpu_is_enabled(g, NVGPU_PMU_PSTATE)) { | ||
362 | err = gk20a_init_pstate_pmu_support(g); | ||
363 | if (err) { | ||
364 | nvgpu_err(g, "failed to init pstates"); | ||
365 | goto done; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | if (g->ops.pmu_ver.clk.clk_set_boot_clk && nvgpu_is_enabled(g, NVGPU_PMU_PSTATE)) { | ||
370 | g->ops.pmu_ver.clk.clk_set_boot_clk(g); | ||
371 | } else { | ||
372 | err = nvgpu_clk_arb_init_arbiter(g); | ||
373 | if (err) { | ||
374 | nvgpu_err(g, "failed to init clk arb"); | ||
375 | goto done; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | err = nvgpu_init_therm_support(g); | ||
380 | if (err) { | ||
381 | nvgpu_err(g, "failed to init gk20a therm"); | ||
382 | goto done; | ||
383 | } | ||
384 | |||
385 | err = g->ops.chip_init_gpu_characteristics(g); | ||
386 | if (err) { | ||
387 | nvgpu_err(g, "failed to init gk20a gpu characteristics"); | ||
388 | goto done; | ||
389 | } | ||
390 | |||
391 | #ifdef CONFIG_GK20A_CTXSW_TRACE | ||
392 | err = gk20a_ctxsw_trace_init(g); | ||
393 | if (err) | ||
394 | nvgpu_warn(g, "could not initialize ctxsw tracing"); | ||
395 | #endif | ||
396 | |||
397 | /* Restore the debug setting */ | ||
398 | g->ops.fb.set_debug_mode(g, g->mmu_debug_ctrl); | ||
399 | |||
400 | gk20a_init_ce_support(g); | ||
401 | |||
402 | if (g->ops.xve.available_speeds) { | ||
403 | u32 speed; | ||
404 | |||
405 | if (!nvgpu_is_enabled(g, NVGPU_SUPPORT_ASPM) && g->ops.xve.disable_aspm) { | ||
406 | g->ops.xve.disable_aspm(g); | ||
407 | } | ||
408 | |||
409 | g->ops.xve.available_speeds(g, &speed); | ||
410 | |||
411 | /* Set to max speed */ | ||
412 | speed = 1 << (fls(speed) - 1); | ||
413 | err = g->ops.xve.set_speed(g, speed); | ||
414 | if (err) { | ||
415 | nvgpu_err(g, "Failed to set PCIe bus speed!"); | ||
416 | goto done; | ||
417 | } | ||
418 | } | ||
419 | |||
420 | #if defined(CONFIG_TEGRA_GK20A_NVHOST) | ||
421 | if (nvgpu_has_syncpoints(g) && g->syncpt_unit_size) { | ||
422 | if (!nvgpu_mem_is_valid(&g->syncpt_mem)) { | ||
423 | nr_pages = DIV_ROUND_UP(g->syncpt_unit_size, PAGE_SIZE); | ||
424 | __nvgpu_mem_create_from_phys(g, &g->syncpt_mem, | ||
425 | g->syncpt_unit_base, nr_pages); | ||
426 | } | ||
427 | } | ||
428 | #endif | ||
429 | |||
430 | if (g->ops.fifo.channel_resume) { | ||
431 | g->ops.fifo.channel_resume(g); | ||
432 | } | ||
433 | |||
434 | done: | ||
435 | if (err) { | ||
436 | g->power_on = false; | ||
437 | } | ||
438 | |||
439 | return err; | ||
440 | } | ||
441 | |||
442 | int gk20a_wait_for_idle(struct gk20a *g) | ||
443 | { | ||
444 | int wait_length = 150; /* 3 second overall max wait. */ | ||
445 | int target_usage_count = 0; | ||
446 | |||
447 | if (!g) { | ||
448 | return -ENODEV; | ||
449 | } | ||
450 | |||
451 | while ((nvgpu_atomic_read(&g->usage_count) != target_usage_count) | ||
452 | && (wait_length-- >= 0)) { | ||
453 | nvgpu_msleep(20); | ||
454 | } | ||
455 | |||
456 | if (wait_length < 0) { | ||
457 | nvgpu_warn(g, "Timed out waiting for idle (%d)!\n", | ||
458 | nvgpu_atomic_read(&g->usage_count)); | ||
459 | return -ETIMEDOUT; | ||
460 | } | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | int gk20a_init_gpu_characteristics(struct gk20a *g) | ||
466 | { | ||
467 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_PARTIAL_MAPPINGS, true); | ||
468 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_MAP_DIRECT_KIND_CTRL, true); | ||
469 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_MAP_BUFFER_BATCH, true); | ||
470 | |||
471 | if (IS_ENABLED(CONFIG_SYNC)) { | ||
472 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_SYNC_FENCE_FDS, true); | ||
473 | } | ||
474 | |||
475 | if (g->ops.mm.support_sparse && g->ops.mm.support_sparse(g)) { | ||
476 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_SPARSE_ALLOCS, true); | ||
477 | } | ||
478 | |||
479 | /* | ||
480 | * Fast submits are supported as long as the user doesn't request | ||
481 | * anything that depends on job tracking. (Here, fast means strictly no | ||
482 | * metadata, just the gpfifo contents are copied and gp_put updated). | ||
483 | */ | ||
484 | __nvgpu_set_enabled(g, | ||
485 | NVGPU_SUPPORT_DETERMINISTIC_SUBMIT_NO_JOBTRACKING, | ||
486 | true); | ||
487 | |||
488 | /* | ||
489 | * Sync framework requires deferred job cleanup, wrapping syncs in FDs, | ||
490 | * and other heavy stuff, which prevents deterministic submits. This is | ||
491 | * supported otherwise, provided that the user doesn't request anything | ||
492 | * that depends on deferred cleanup. | ||
493 | */ | ||
494 | if (!nvgpu_channel_sync_needs_os_fence_framework(g)) { | ||
495 | __nvgpu_set_enabled(g, | ||
496 | NVGPU_SUPPORT_DETERMINISTIC_SUBMIT_FULL, | ||
497 | true); | ||
498 | } | ||
499 | |||
500 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_DETERMINISTIC_OPTS, true); | ||
501 | |||
502 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_USERSPACE_MANAGED_AS, true); | ||
503 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_TSG, true); | ||
504 | |||
505 | if (g->ops.clk_arb.get_arbiter_clk_domains != NULL && | ||
506 | g->ops.clk.support_clk_freq_controller) { | ||
507 | __nvgpu_set_enabled(g, NVGPU_SUPPORT_CLOCK_CONTROLS, true); | ||
508 | } | ||
509 | |||
510 | g->ops.gr.detect_sm_arch(g); | ||
511 | |||
512 | if (g->ops.gr.init_cyclestats) { | ||
513 | g->ops.gr.init_cyclestats(g); | ||
514 | } | ||
515 | |||
516 | g->ops.gr.get_rop_l2_en_mask(g); | ||
517 | |||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Free the gk20a struct. | ||
523 | */ | ||
524 | static void gk20a_free_cb(struct nvgpu_ref *refcount) | ||
525 | { | ||
526 | struct gk20a *g = container_of(refcount, | ||
527 | struct gk20a, refcount); | ||
528 | |||
529 | #ifdef CONFIG_NVGPU_SUPPORT_LINUX_ECC_ERROR_REPORTING | ||
530 | nvgpu_deinit_ecc_reporting(g); | ||
531 | #endif | ||
532 | |||
533 | nvgpu_log(g, gpu_dbg_shutdown, "Freeing GK20A struct!"); | ||
534 | |||
535 | gk20a_ce_destroy(g); | ||
536 | |||
537 | if (g->remove_support) { | ||
538 | g->remove_support(g); | ||
539 | } | ||
540 | |||
541 | if (g->free) { | ||
542 | g->free(g); | ||
543 | } | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * gk20a_get() - Increment ref count on driver | ||
548 | * | ||
549 | * @g The driver to increment | ||
550 | * This will fail if the driver is in the process of being released. In that | ||
551 | * case it will return NULL. Otherwise a pointer to the driver passed in will | ||
552 | * be returned. | ||
553 | */ | ||
554 | struct gk20a * __must_check gk20a_get(struct gk20a *g) | ||
555 | { | ||
556 | int success; | ||
557 | |||
558 | /* | ||
559 | * Handle the possibility we are still freeing the gk20a struct while | ||
560 | * gk20a_get() is called. Unlikely but plausible race condition. Ideally | ||
561 | * the code will never be in such a situation that this race is | ||
562 | * possible. | ||
563 | */ | ||
564 | success = nvgpu_ref_get_unless_zero(&g->refcount); | ||
565 | |||
566 | nvgpu_log(g, gpu_dbg_shutdown, "GET: refs currently %d %s", | ||
567 | nvgpu_atomic_read(&g->refcount.refcount), | ||
568 | success ? "" : "(FAILED)"); | ||
569 | |||
570 | return success ? g : NULL; | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * gk20a_put() - Decrement ref count on driver | ||
575 | * | ||
576 | * @g - The driver to decrement | ||
577 | * | ||
578 | * Decrement the driver ref-count. If neccesary also free the underlying driver | ||
579 | * memory | ||
580 | */ | ||
581 | void gk20a_put(struct gk20a *g) | ||
582 | { | ||
583 | /* | ||
584 | * Note - this is racy, two instances of this could run before the | ||
585 | * actual kref_put(0 runs, you could see something like: | ||
586 | * | ||
587 | * ... PUT: refs currently 2 | ||
588 | * ... PUT: refs currently 2 | ||
589 | * ... Freeing GK20A struct! | ||
590 | */ | ||
591 | nvgpu_log(g, gpu_dbg_shutdown, "PUT: refs currently %d", | ||
592 | nvgpu_atomic_read(&g->refcount.refcount)); | ||
593 | |||
594 | nvgpu_ref_put(&g->refcount, gk20a_free_cb); | ||
595 | } | ||