1 /* 2 * SPDX-FileCopyrightText: Copyright (c) 2000-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 * SPDX-License-Identifier: MIT 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 /****************************************************************************** 25 * 26 * Description: 27 * This file contains the implementations for Suspend to RAM ("Standby") 28 * and Suspend to Disk ("Hibernate") 29 * 30 ******************************************************************************/ 31 32 #include "platform/platform.h" 33 #include "platform/chipset/chipset.h" 34 #include "gpu/mem_mgr/mem_mgr.h" 35 #include "gpu/mem_mgr/fbsr.h" 36 #include "gpu/gsp/gsp_init_args.h" 37 #include "gpu/gsp/kernel_gsp.h" 38 #include "gpu/pmu/kern_pmu.h" 39 #include "rmgspseq.h" 40 #include "core/thread_state.h" 41 #include "vgpu/rpc.h" 42 #include "gpu/mem_sys/kern_mem_sys.h" 43 #include <gpu/fsp/kern_fsp.h> 44 45 // 46 // Helper functions 47 // 48 static NV_STATUS gpuPowerManagementEnter(OBJGPU *, NvU32 newLevel, NvU32 flags); 49 static NV_STATUS gpuPowerManagementResume(OBJGPU *, NvU32 oldLevel, NvU32 flags); 50 51 // XXX Needs to be further cleaned up. No new code should be placed in this 52 // routine. Please use the per-engine StateLoad() and StateUnload() routines 53 // instead 54 static NV_STATUS 55 gpuPowerManagementEnter(OBJGPU *pGpu, NvU32 newLevel, NvU32 flags) 56 { 57 NV_STATUS status = NV_OK; 58 MemoryManager *pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu); 59 60 // This is a no-op in CPU-RM 61 NV_ASSERT_OK_OR_GOTO(status, gpuPowerManagementEnterPreUnloadPhysical(pGpu), done); 62 63 NV_ASSERT_OK_OR_GOTO(status, gpuStateUnload(pGpu, 64 IS_GPU_GC6_STATE_ENTERING(pGpu) ? 65 GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION | GPU_STATE_FLAGS_GC6_TRANSITION : 66 GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION), done); 67 68 pGpu->setProperty(pGpu, PDB_PROP_GPU_VGA_ENABLED, NV_TRUE); 69 70 // This is a no-op in CPU-RM 71 NV_ASSERT_OK_OR_GOTO(status, gpuPowerManagementEnterPostUnloadPhysical(pGpu, newLevel), done); 72 73 if (IS_GSP_CLIENT(pGpu)) 74 { 75 // FB remains alive for GC6 cycle 76 if (!IS_GPU_GC6_STATE_ENTERING(pGpu)) 77 { 78 NV_ASSERT_OK_OR_GOTO(status, memmgrSavePowerMgmtState(pGpu, pMemoryManager), done); 79 } 80 81 KernelGsp *pKernelGsp = GPU_GET_KERNEL_GSP(pGpu); 82 83 84 NV_RM_RPC_UNLOADING_GUEST_DRIVER(pGpu, status, NV_TRUE, IS_GPU_GC6_STATE_ENTERING(pGpu), newLevel); 85 if (status != NV_OK) 86 goto done; 87 88 // Wait for GSP-RM to suspend 89 kgspWaitForProcessorSuspend_HAL(pGpu, pKernelGsp); 90 91 // Dump GSP-RM logs before resetting and invoking FWSEC-SB 92 kgspDumpGspLogs(pKernelGsp, NV_FALSE); 93 94 if (!IS_GPU_GC6_STATE_ENTERING(pGpu)) 95 { 96 // Because of COT, RM cannot reset GSP-RISCV. 97 if (!(pGpu->getProperty(pGpu, PDB_PROP_GPU_IS_COT_ENABLED))) 98 { 99 kflcnReset_HAL(pGpu, staticCast(pKernelGsp, KernelFalcon)); 100 } 101 102 // Invoke FWSEC-SB to load back PreOsApps. 103 status = kgspExecuteFwsecSb_HAL(pGpu, pKernelGsp, pKernelGsp->pFwsecUcode); 104 if (status != NV_OK) 105 { 106 NV_PRINTF(LEVEL_ERROR, "failed to execute FWSEC-SB for PreOsApps\n"); 107 goto done; 108 } 109 110 kpmuFreeLibosLoggingStructures(pGpu, GPU_GET_KERNEL_PMU(pGpu)); 111 112 { 113 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, 114 kgspSavePowerMgmtState_HAL(pGpu, pKernelGsp), done); 115 } 116 } 117 else 118 { 119 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, 120 kgspExecuteBooterUnloadIfNeeded_HAL(pGpu, pKernelGsp, 0), done); 121 } 122 } 123 124 done: 125 if ((status != NV_OK) && !IS_GPU_GC6_STATE_ENTERING(pGpu)) 126 { 127 memmgrFreeFbsrMemory(pGpu, pMemoryManager); 128 } 129 130 return status; 131 } 132 133 #ifdef DEBUG 134 int g_BreakOnResume = 0; 135 #endif 136 137 // XXX Needs to be further cleaned up. No new code should be placed in this 138 // routine. Please use the per-engine StateLoad() and StateUnload() routines 139 // instead 140 static NV_STATUS 141 gpuPowerManagementResume(OBJGPU *pGpu, NvU32 oldLevel, NvU32 flags) 142 { 143 NV_STATUS status = NV_OK; 144 OBJSYS *pSys = SYS_GET_INSTANCE(); 145 OBJCL *pCl = SYS_GET_CL(pSys); 146 MemoryManager *pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu); 147 148 #ifdef DEBUG 149 // 150 // This is useful for windbg debugging as it frequently doesn't reconnect to the 151 // target system until after the majority of the resume code has been ran. 152 // Placing an int 3 at the entrypoint to S/R resume code causes the target to 153 // halt until the debugger reconnects and the user is then able to place 154 // breakpoints / step through code. 155 // 156 if (g_BreakOnResume) 157 { 158 DBG_BREAKPOINT(); 159 } 160 #endif 161 162 if (pCl != NULL) 163 { 164 status = clResumeBridge(pCl); 165 NV_ASSERT(status == NV_OK); 166 } 167 168 if (IS_GSP_CLIENT(pGpu)) 169 { 170 // 171 // GSP-RM expects sysmem flush buffer address to be programmed by Kernel-RM. 172 // As this buffer is used to perform a system flush while resetting Gpu Falcons. 173 // 174 kmemsysProgramSysmemFlushBuffer_HAL(pGpu, GPU_GET_KERNEL_MEMORY_SYSTEM(pGpu)); 175 176 KernelGsp *pKernelGsp = GPU_GET_KERNEL_GSP(pGpu); 177 178 GSP_SR_INIT_ARGUMENTS gspSrInitArgs; 179 180 gspSrInitArgs.oldLevel = oldLevel; 181 gspSrInitArgs.flags = flags; 182 gspSrInitArgs.bInPMTransition = NV_TRUE; 183 184 kgspPopulateGspRmInitArgs(pGpu, pKernelGsp, &gspSrInitArgs); 185 186 // Wait for GFW_BOOT status 187 status = kgspWaitForGfwBootOk_HAL(pGpu, pKernelGsp); 188 if (status != NV_OK) 189 { 190 goto done; 191 } 192 193 if (!IS_GPU_GC6_STATE_EXITING(pGpu)) 194 { 195 // Not called when kgspShouldBootWithBooter_HAL() is called and returns NV_FALSE 196 { 197 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, 198 kgspRestorePowerMgmtState_HAL(pGpu, pKernelGsp), done); 199 } 200 201 status = kpmuInitLibosLoggingStructures(pGpu, GPU_GET_KERNEL_PMU(pGpu)); 202 if (status != NV_OK) 203 { 204 NV_PRINTF(LEVEL_ERROR, "cannot init libOS PMU logging structures: 0x%x\n", status); 205 goto done; 206 } 207 } 208 else 209 { 210 status = kgspExecuteBooterLoad_HAL(pGpu, pKernelGsp, 0); 211 if (status != NV_OK) 212 { 213 NV_PRINTF(LEVEL_ERROR, "cannot resume riscv/gsp from GC6: 0x%x\n", status); 214 goto done; 215 } 216 } 217 218 status = kgspWaitForRmInitDone(pGpu, pKernelGsp); 219 if (status != NV_OK) 220 { 221 NV_PRINTF(LEVEL_ERROR, "State load at resume for riscv/gsp failed: 0x%x\n", status); 222 goto done; 223 } 224 } 225 else 226 { 227 // Boot GSP-FMC for monolithic RM 228 KernelFsp *pKernelFsp = GPU_GET_KERNEL_FSP(pGpu); 229 if ((pKernelFsp != NULL) && !IS_VIRTUAL(pGpu)) 230 { 231 pKernelFsp->setProperty(pKernelFsp, PDB_PROP_KFSP_BOOT_COMMAND_OK, NV_FALSE); 232 233 status = kfspSendBootCommands_HAL(pGpu, pKernelFsp); 234 if (status != NV_OK) 235 { 236 NV_PRINTF(LEVEL_ERROR, "FSP boot command failed during resume.\n"); 237 goto done; 238 } 239 } 240 } 241 242 // This is a no-op in CPU-RM 243 NV_ASSERT_OK_OR_GOTO(status, gpuPowerManagementResumePreLoadPhysical(pGpu, oldLevel, flags), done); 244 245 pGpu->setProperty(pGpu, PDB_PROP_GPU_VGA_ENABLED, NV_FALSE); 246 247 NV_ASSERT_OK_OR_GOTO(status, gpuStateLoad(pGpu, 248 IS_GPU_GC6_STATE_EXITING(pGpu) ? 249 GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION | GPU_STATE_FLAGS_GC6_TRANSITION : 250 GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION), done); 251 252 // This is a no-op in CPU-RM 253 NV_ASSERT_OK_OR_GOTO(status, gpuPowerManagementResumePostLoadPhysical(pGpu), done); 254 255 NV_PRINTF(LEVEL_NOTICE, "Adapter now in D0 state\n"); 256 257 done: 258 if (!IS_GPU_GC6_STATE_EXITING(pGpu)) 259 { 260 memmgrFreeFbsrMemory(pGpu, pMemoryManager); 261 } 262 263 return status; 264 } 265 266 NV_STATUS 267 gpuEnterStandby_IMPL(OBJGPU *pGpu) 268 { 269 OBJSYS *pSys = SYS_GET_INSTANCE(); 270 OBJPFM *pPfm = SYS_GET_PFM(pSys); 271 NV_STATUS suspendStatus; 272 273 if ((pPfm != NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 274 { 275 NV_PRINTF(LEVEL_INFO, 276 "gpuPowerState NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3 Requested\n"); 277 NV_PRINTF(LEVEL_NOTICE, "Beginning transition from D0 to %s\n", 278 IS_GPU_GC6_STATE_ENTERING(pGpu) ? "GC6" : "D3"); 279 } 280 else 281 { 282 NV_PRINTF(LEVEL_INFO, 283 "gpuPowerState NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_4 Requested\n"); 284 NV_PRINTF(LEVEL_NOTICE, "Beginning transition to %s\n", 285 IS_GPU_GC6_STATE_ENTERING(pGpu) ? "GC6" : "APM Suspend"); 286 } 287 288 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_CODEPATH, NV_TRUE); 289 290 suspendStatus = gpuPowerManagementEnter(pGpu, NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3, GPU_STATE_FLAGS_PM_SUSPEND); 291 292 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_STANDBY, NV_TRUE); 293 pGpu->bInD3Cold = NV_TRUE; 294 295 if ((pPfm !=NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 296 { 297 NV_PRINTF(LEVEL_NOTICE, "Ending transition from D0 to %s\n", 298 IS_GPU_GC6_STATE_ENTERING(pGpu) ? "GC6" : "D3"); 299 } 300 else 301 { 302 NV_PRINTF(LEVEL_NOTICE, "Ending transition to %s\n", 303 IS_GPU_GC6_STATE_ENTERING(pGpu) ? "GC6" : "APM Suspend"); 304 } 305 306 return suspendStatus; 307 } 308 309 NV_STATUS 310 gpuResumeFromStandby_IMPL(OBJGPU *pGpu) 311 { 312 OBJSYS *pSys = SYS_GET_INSTANCE(); 313 OBJPFM *pPfm = SYS_GET_PFM(pSys); 314 NV_STATUS resumeStatus; 315 NvU32 state = 0; 316 317 if ((pPfm != NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 318 { 319 state = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3; 320 NV_PRINTF(LEVEL_INFO, 321 "gpuPowerState Transitioning from NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3\n"); 322 NV_PRINTF(LEVEL_NOTICE, "Beginning transition from %s to D0\n", 323 IS_GPU_GC6_STATE_EXITING(pGpu) ? "GC6" : "D3"); 324 } 325 else 326 { 327 state = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_4; 328 NV_PRINTF(LEVEL_INFO, 329 "gpuPowerState Transitioning from NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_4\n"); 330 NV_PRINTF(LEVEL_NOTICE, "Beginning resume from %s\n", 331 IS_GPU_GC6_STATE_EXITING(pGpu) ? "GC6" : "APM Suspend"); 332 } 333 334 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_RESUME_CODEPATH, NV_TRUE); 335 336 resumeStatus = gpuPowerManagementResume(pGpu, state, GPU_STATE_FLAGS_PM_SUSPEND); 337 338 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_STANDBY, NV_FALSE); 339 pGpu->bInD3Cold = NV_FALSE; 340 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_CODEPATH, NV_FALSE); 341 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_RESUME_CODEPATH, NV_FALSE); 342 343 if ((pPfm != NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 344 { 345 NV_PRINTF(LEVEL_NOTICE, "Ending transition from %s to D0\n", 346 IS_GPU_GC6_STATE_EXITING(pGpu) ? "GC6" : "D3"); 347 } 348 else 349 { 350 NV_PRINTF(LEVEL_NOTICE, "Ending resume from %s\n", 351 IS_GPU_GC6_STATE_EXITING(pGpu) ? "GC6" : "APM Suspend"); 352 } 353 354 return resumeStatus; 355 } 356 357 NV_STATUS gpuEnterHibernate_IMPL(OBJGPU *pGpu) 358 { 359 OBJSYS *pSys = SYS_GET_INSTANCE(); 360 OBJPFM *pPfm = SYS_GET_PFM(pSys); 361 NV_STATUS suspendStatus; 362 363 if ((pPfm != NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 364 { 365 NV_PRINTF(LEVEL_INFO, 366 "gpuPowerState NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7 Requested\n"); 367 NV_PRINTF(LEVEL_NOTICE, "Beginning transition from D0 to D4\n"); 368 } 369 else 370 { 371 NV_PRINTF(LEVEL_INFO, 372 "gpuPowerState NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_4 Requested\n"); 373 NV_PRINTF(LEVEL_NOTICE, "Beginning APM Suspend\n"); 374 } 375 376 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_CODEPATH, NV_TRUE); 377 378 suspendStatus = gpuPowerManagementEnter(pGpu, NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7, GPU_STATE_FLAGS_PM_HIBERNATE); 379 380 NV_PRINTF(LEVEL_INFO, 381 "gpuPowerState Saving clocks and throttling them down\n"); 382 383 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_HIBERNATE, NV_TRUE); 384 385 NV_PRINTF(LEVEL_NOTICE, "Ending transition from D0 to D4\n"); 386 387 return suspendStatus; 388 } 389 390 NV_STATUS gpuResumeFromHibernate_IMPL(OBJGPU *pGpu) 391 { 392 OBJSYS *pSys = SYS_GET_INSTANCE(); 393 OBJPFM *pPfm = SYS_GET_PFM(pSys); 394 NV_STATUS resumeStatus; 395 396 NV_PRINTF(LEVEL_INFO, 397 "gpuPowerState Transitioning from NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7\n"); 398 NV_PRINTF(LEVEL_NOTICE, "Beginning transition from D4 to D0\n"); 399 400 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_RESUME_CODEPATH, NV_TRUE); 401 402 resumeStatus = gpuPowerManagementResume(pGpu, NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7, GPU_STATE_FLAGS_PM_HIBERNATE); 403 404 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_HIBERNATE, NV_FALSE); 405 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_CODEPATH, NV_FALSE); 406 pGpu->setProperty(pGpu, PDB_PROP_GPU_IN_PM_RESUME_CODEPATH, NV_FALSE); 407 408 if ((pPfm != NULL) && pPfm->getProperty(pPfm, PDB_PROP_PFM_SUPPORTS_ACPI)) 409 { 410 NV_PRINTF(LEVEL_NOTICE, "Ending transition from D4 to D0\n"); 411 } 412 else 413 { 414 NV_PRINTF(LEVEL_NOTICE, "End resuming from APM Suspend\n"); 415 } 416 417 return resumeStatus; 418 } 419