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