From c7fe47148242729ff87d8a3d6b3d17184d69fa67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kope=C4=87?= Date: Tue, 11 Mar 2025 18:33:08 +0100 Subject: [PATCH] mb/novacustom/mtl-h/var/dgpu: Add NVIDIA dGPU ASL code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ACPI ASL code for the discrete NVIDIA GN21 GPU. Based on google/brya, but adapted for Meteor Lake and the specific GPU found in Clevo V5x0TNx. NVIDIA did not provide us with the required documentation, so this driver is entirely based on the Brya implementation, and poking Clevo ACPI from Linux. Without this patch, the NVIDIA driver would not load at all under Windows, while Ubuntu would load the driver, but would be unable to power off the dGPU, causing increased power draw. TEST=Boot to Windows 11 via MrChromebox uefipayload_2502 and observe that the NVIDIA Driver loads correctly, that the dGPU powers off when unused and powers on while needed. Do the same in Ubuntu 24.04 under a Gnome X11 session. Change-Id: I9e5715cb4129a005cc9374fd53eaacd7d1a7f68e Signed-off-by: Michał Kopeć Reviewed-on: https://review.coreboot.org/c/coreboot/+/86824 Tested-by: build bot (Jenkins) Reviewed-by: Sergii Dmytruk --- .../novacustom/mtl-h/acpi/dgpu/gps.asl | 96 ++++++ .../novacustom/mtl-h/acpi/dgpu/gpu_defines.h | 60 ++++ .../novacustom/mtl-h/acpi/dgpu/gpu_ec.asl | 11 + .../novacustom/mtl-h/acpi/dgpu/gpu_top.asl | 92 +++++ .../novacustom/mtl-h/acpi/dgpu/nbci.asl | 27 ++ .../novacustom/mtl-h/acpi/dgpu/nvjt.asl | 128 +++++++ .../novacustom/mtl-h/acpi/dgpu/nvop.asl | 68 ++++ .../novacustom/mtl-h/acpi/dgpu/peg.asl | 99 ++++++ .../novacustom/mtl-h/acpi/dgpu/power.asl | 315 ++++++++++++++++++ .../novacustom/mtl-h/acpi/dgpu/utility.asl | 58 ++++ src/mainboard/novacustom/mtl-h/dsdt.asl | 4 + 11 files changed, 958 insertions(+) create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/gps.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_defines.h create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_ec.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_top.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/nbci.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/nvjt.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/nvop.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/peg.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/power.asl create mode 100644 src/mainboard/novacustom/mtl-h/acpi/dgpu/utility.asl diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/gps.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gps.asl new file mode 100644 index 0000000000..8bf8954b82 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gps.asl @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define GPS_FUNC_SUPPORT 0 +#define GPS_FUNC_GETCALLBACKS 0x13 +#define GPS_FUNC_PSHARESTATUS 0x20 +#define GPS_FUNC_PSHAREPARAMS 0x2a +#define GPS_FUNC_REQUESTDXSTATE 0x12 + +#define QUERY_GET_STATUS 0 +#define QUERY_GET_SUPPORTED_FIELDS 1 +#define QUERY_GET_CURRENT_LIMITS 2 + +/* GPS return Package */ +Name (GPSP, Buffer (0x24) {0x0}) +CreateDWordField (GPSP, 0, RETN) +CreateDWordField (GPSP, 4, VRV1) +CreateDWordField (GPSP, 8, TGPU) + +/* GETCALLBACKS return value + [0]: Callback for post-mode set + [1]: Callback for pre-mode set + [2]: Callback for post power state transition */ +Name (GPSR, Buffer (4) { 0x4, 0x0, 0x0, 0x0 }) + +Method (GPS, 2, Serialized) +{ + Switch (ToInteger (Arg0)) + { + Case (GPS_FUNC_SUPPORT) + { + Return (LTOB( + (1 << GPS_FUNC_SUPPORT) | + (1 << GPS_FUNC_GETCALLBACKS) | + (1 << GPS_FUNC_PSHARESTATUS) | + (1 << GPS_FUNC_PSHAREPARAMS) | + (1 << GPS_FUNC_REQUESTDXSTATE))) + } + Case (GPS_FUNC_GETCALLBACKS) + { + CreateDWordField (Arg1, 0, QURY) + + /* Driver querying for which callbacks the ACPI code + wants callbacks for. */ + If (QURY == 0) + { + Return (GPSR) + } + + If (QURY & 0x4) + { + Printf("GPS: Kernel driver callback post power state transition") + Return (GPSR) + } + } + Case (GPS_FUNC_PSHARESTATUS) + { + Return (ITOB(0)) + } + Case (GPS_FUNC_PSHAREPARAMS) + { + CreateField (Arg1, 0, 4, QUTY) /* Query type */ + + /* Version of return value */ + VRV1 = 0x10000 + RETN = QUTY + + Switch (ToInteger (QUTY)) + { + Case (QUERY_GET_STATUS) + { + Return (GPSP) + } + Case (QUERY_GET_SUPPORTED_FIELDS) + { + RETN = 0x300 | ToInteger (QUTY) + CreateDWordField (GPSP, 0x0C, PDTS) + PDTS = 0x03e8 + Return (GPSP) + } + Case (QUERY_GET_CURRENT_LIMITS) + { + /* No limits */ + Return (GPSP) + } + } + } + Case (GPS_FUNC_REQUESTDXSTATE) + { + Local0 = ToInteger(\_SB.PCI0.LPCB.EC0.GPUD) + \_SB.PCI0.RP12.PXSX.DNOT (Local0, 1) + Return (NV_ERROR_SUCCESS) + } + } + + Return (NV_ERROR_UNSUPPORTED) +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_defines.h b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_defines.h new file mode 100644 index 0000000000..1cb03145b0 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_defines.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define NV_ERROR_SUCCESS 0x0 +#define NV_ERROR_UNSPECIFIED 0x80000001 +#define NV_ERROR_UNSUPPORTED 0x80000002 + +#define PCI_OWNER_SBIOS 0x0 +#define PCI_OWNER_DRIVER 0x1 + +#define OPTIMUS_POWER_CONTROL_DISABLE 0x2 +#define OPTIMUS_POWER_CONTROL_ENABLE 0x3 + +#define OPTIMUS_CONTROL_NO_RUN_PS0 0x0 +#define OPTIMUS_CONTROL_RUN_PS0 0x1 + +#define GC6_STATE_EXITED 0x0 +#define GC6_STATE_ENTERED 0x1 +#define GC6_STATE_TRANSITION 0x2 + +#define GC6_DEFER_DISABLE 0x0 +#define GC6_DEFER_ENABLE 0x1 + +#define NOTIFY_GPS_EVENT_STATUS_CHANGE 0xc0 +#define NOTIFY_GPS_NVPCF_STATUS_CHANGE 0xc5 +#define NOTIFY_GPS_EVENT_LIMIT_POWER_0 0xd1 +#define NOTIFY_GPS_EVENT_LIMIT_POWER_1 0xd2 +#define NOTIFY_GPS_EVENT_LIMIT_POWER_2 0xd3 +#define NOTIFY_GPS_EVENT_LIMIT_POWER_3 0xd4 +#define NOTIFY_GPS_EVENT_LIMIT_POWER_4 0xd5 + +/* Defines for NVJT subfunction */ +#define NVJT_GPC_GSS 0 +#define NVJT_GPC_EGNS 1 +#define NVJT_GPC_EGIS 2 +#define NVJT_GPS_XGXS 3 +#define NVJT_GPS_XGIS 4 + +#define UUID_NVOP "a486d8f8-0bda-471b-a72b-6042a6b5bee0" +#define UUID_NVJT "cbeca351-067b-4924-9cbd-b46b00b86f34" +#define UUID_NBCI "d4a50b75-65c7-46f7-bfb7-41514cea0244" +#define UUID_NVPCF "36b49710-2483-11e7-9598-0800200c9a66" +#define UUID_GPS "a3132d01-8cda-49ba-a52e-bc9d46df6b81" + +#define REVISION_MIN_NVOP 0x100 +#define REVISION_MIN_NVJT 0x100 +#define REVISION_MIN_NBCI 0x102 +#define REVISION_MIN_NVPCF 0x200 +#define REVISION_MIN_GPS 0x200 + +#define D1_EC 0 +#define D2_EC 1 +#define D3_EC 2 +#define D4_EC 3 +#define D5_EC 4 + +#define D1_GPU 0xD1 +#define D2_GPU 0xD2 +#define D3_GPU 0xD3 +#define D4_GPU 0xD4 +#define D5_GPU 0xD5 diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_ec.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_ec.asl new file mode 100644 index 0000000000..13b04294b6 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_ec.asl @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Scope (\_SB.PCI0.LPCB.EC0) +{ + /* EC has data for GPU in memmap */ + Method (_QA0, 0, Serialized) + { + Local0 = ToInteger(GPUD) + \_SB.PCI0.RP12.PXSX.DNOT (Local0, 0) + } +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_top.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_top.asl new file mode 100644 index 0000000000..baee47dcca --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/gpu_top.asl @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_defines.h" + +Scope (\_SB.PCI0.RP12) +{ + #include "peg.asl" + + Device (PXSX) + { + Name (_ADR, 0x0) + OperationRegion (PCIC, PCI_Config, 0x00, 0x100) + Field (PCIC, DWordAcc, NoLock, Preserve) + { + NVID, 16, + NDID, 16, + Offset (0x40), + NVSS, 32 + } + + #include "utility.asl" + #include "power.asl" + #include "nvop.asl" + #include "nvjt.asl" + #include "nbci.asl" + #include "gps.asl" + #include "gpu_ec.asl" + + /* Convert D Notify from EC to GPU */ + Method (CNVD, 1, NotSerialized) + { + Switch (ToInteger(Arg0)) { + Case (D1_EC) { Return (D1_GPU) } + Case (D2_EC) { Return (D2_GPU) } + Case (D3_EC) { Return (D3_GPU) } + Case (D4_EC) { Return (D4_GPU) } + Case (D5_EC) { Return (D5_GPU) } + Default { Return (D5_GPU) } + } + } + + /* Current D Notify Value, defaults to D1 + * Arg0 == Shared value + * Arg1 == force notification if no change (0 or 1) + */ + Name (CDNV, D1_EC) + Method (DNOT, 2, Serialized) + { + Printf ("EC: GPU D-Notify, %o", Arg0) + If ((Arg0 != CDNV) || (Arg1 == 1)) + { + CDNV = Arg0 + Local0 = CNVD (Arg0) + Notify (\_SB.PCI0.RP12.PXSX, Local0) + } + } + + Method (_DSM, 4, Serialized) + { + If (Arg0 == ToUUID (UUID_NVOP)) + { + If (ToInteger(Arg1) >= REVISION_MIN_NVOP) + { + Return (NVOP (Arg2, Arg3)) + } + } + ElseIf (Arg0 == ToUUID (UUID_NVJT)) + { + If (ToInteger (Arg1) >= REVISION_MIN_NVJT) + { + Return (NVJT (Arg2, Arg3)) + } + } + ElseIf (Arg0 == ToUUID (UUID_NBCI)) + { + If (ToInteger (Arg1) >= REVISION_MIN_NBCI) + { + Return (NBCI (Arg2, Arg3)) + } + } + ElseIf (Arg0 == ToUUID (UUID_GPS)) + { + If (ToInteger (Arg1) >= REVISION_MIN_GPS) + { + Return (GPS (Arg2, Arg3)) + } + } + + Return (NV_ERROR_UNSUPPORTED) + } + } +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/nbci.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nbci.asl new file mode 100644 index 0000000000..1546928aef --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nbci.asl @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define NBCI_FUNC_SUPPORT 0 +#define NBCI_FUNC_GETOBJBYTYPE 16 +#define NBCI_FUNC_GETCALLBACKS 19 + +#define GPS_FUNC_GETCALLBACKS 0x13 + +Method (NBCI, 2, Serialized) +{ + Switch (ToInteger (Arg0)) + { + Case (NBCI_FUNC_SUPPORT) + { + Return (ITOB( + (1 << NBCI_FUNC_SUPPORT) | + (1 << NBCI_FUNC_GETCALLBACKS))) + } + Case (NBCI_FUNC_GETCALLBACKS) + { + /* Re-use the GPS subfunction's GETCALLBACKS Method */ + Return (GPS (GPS_FUNC_GETCALLBACKS, Arg1)) + } + } + + Return (NV_ERROR_UNSUPPORTED) +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvjt.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvjt.asl new file mode 100644 index 0000000000..0526dc92a4 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvjt.asl @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define JT_FUNC_SUPPORT 0 +#define JT_FUNC_CAPS 1 +#define JT_FUNC_POWERCONTROL 3 +#define JT_FUNC_PLATPOLICY 4 + +Method (NVJT, 2, Serialized) +{ + Switch (ToInteger(Arg0)) + { + Case (JT_FUNC_SUPPORT) + { + Return (ITOB( + (1 << JT_FUNC_SUPPORT) | + (1 << JT_FUNC_CAPS) | + (1 << JT_FUNC_POWERCONTROL))) + } + Case (JT_FUNC_CAPS) + { + Return (ITOB( + (1 << 0) | /* JTE: G-Sync NVSR Power Features Enabled */ + (1 << 1) | /* NVSE: NVSR Disabled */ + (2 << 3) | /* PPR: Panel Power Rail */ + (0 << 5) | /* SRPR: Self-Refresh Controller Power Rail */ + (0 << 6) | /* FBPR: FB Power Rail */ + (0 << 8) | /* GPR: GPU Power Rail */ + (0 << 10) | /* GCR: GC6 ROM */ + (1 << 11) | /* PTH: No SMI Handler */ + (0 << 12) | /* NOT: Supports Notify on GC6 State done */ + (1 << 13) | /* MHYB: MS Hybrid Support (deferred GC6) */ + (1 << 14) | /* RPC: Root Port Control */ + (2 << 15) | /* GC6 Version (GC6-R) */ + (0 << 17) | /* GEI: GC6 Exit ISR Support */ + (0 << 18) | /* GSW: GC6 Self Wakeup */ + (0x200 << 20))) /* MXRV: Highest Revision */ + } + Case (JT_FUNC_POWERCONTROL) + { + CreateField (Arg1, 0, 3, GPC) /* GPU Power Control */ + CreateField (Arg1, 4, 1, PPC) /* Panel Power Control */ + CreateField (Arg1, 14, 2, DFGC) /* Defer GC6 enter/exit */ + CreateField (Arg1, 16, 3, GPCX) /* Deferred GC6 exit */ + + /* Deferred GC6 entry/exit is requested */ + If (ToInteger(GPC) != 0 || ToInteger(DFGC) != 0) + { + DFEN = ToInteger(DFGC) + DFCI = ToInteger(GPC) + DFCO = ToInteger(GPCX) + } + + Local0 = Buffer (4) { 0x0 } + CreateField (Local0, 0, 3, CGCS) /* Current GC State */ + CreateField (Local0, 3, 1, CGPS) /* Current GPU power status */ + CreateField (Local0, 7, 1, CPSS) /* Current panel and SRC state */ + + /* Leave early if deferred GC6 is requested */ + If (DFEN != 0) + { + CGCS = 1 + CGPS = 1 + Return (Local0) + } + + Switch (ToInteger(GPC)) + { + /* Get GCU GCx Sleep Status */ + Case (NVJT_GPC_GSS) + { + If (PSTA () != 0) + { + CGPS = 1 + CGCS = 1 + } + Else + { + CGPS = 0 + CGCS = 3 + } + } + Case (NVJT_GPC_EGNS) + { + /* Enter GC6; no self-refresh */ + GC6I () + CPSS = 1 + CGCS = 0 + } + Case (NVJT_GPC_EGIS) + { + /* Enter GC6; enable self-refresh */ + GC6I () + If (ToInteger (PPC) == 0) + { + CPSS = 0 + } + CGCS = 0 + } + Case (NVJT_GPS_XGXS) + { + /* Exit GC6; stop self-refresh */ + GC6O () + CGCS = 1 + CGPS = 1 + If (ToInteger (PPC) != 0) + { + CPSS = 0 + } + } + Case (NVJT_GPS_XGIS) + { + /* Exit GC6 for self-refresh */ + GC6O () + CGCS = 1 + CGPS = 1 + If (ToInteger (PPC) != 0) + { + CPSS = 0 + } + } + } + + Return (Local0) + } + } + + Return (NV_ERROR_UNSUPPORTED) +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvop.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvop.asl new file mode 100644 index 0000000000..1f534be549 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/nvop.asl @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define NVOP_FUNC_SUPPORT 0x00 +#define NVOP_FUNC_OPTIMUS_CAPS 0x1a +#define NVOP_FUNC_OPTIMUS_STATUS 0x1b + +Method (NVOP, 2, Serialized) +{ + Switch (ToInteger (Arg0)) + { + Case (NVOP_FUNC_SUPPORT) + { + Return (ITOB ( + (1 << NVOP_FUNC_SUPPORT) | + (1 << NVOP_FUNC_OPTIMUS_CAPS) | + (1 << NVOP_FUNC_OPTIMUS_STATUS))) + } + Case (NVOP_FUNC_OPTIMUS_CAPS) + { + CreateField(Arg1, 0, 1, FLUP) /* Flag Update */ + CreateField(Arg1, 1, 1, CSOT) /* Change configuration Space Owner Target */ + CreateField(Arg1, 2, 1, CSOW) /* Change configuration Space Owner Write */ + CreateField(Arg1, 24, 2, NPCE) /* New Power Control Enable setting */ + + /* Change Optimus power control capabilities */ + If (ToInteger (FLUP) != 0 && ToInteger (NPCE) != 0) + { + OPCS = NPCE + } + + /* Change PCI configuration space save/restore owner */ + If (ToInteger (CSOW) == 1) + { + PCIO = CSOT + } + + /* Current GPU Control Status */ + If (PSTA () == 1) + { + Local0 = 3 + } + Else + { + Local0 = 0 + } + + Return (ITOB ( + (1 << 0) | /* Optimus Enabled */ + (Local0 << 3) | /* Current GPU Control Status */ + (1 << 6) | /* Shared Discrete GPU Hotplug Capabilities */ + (0 << 7) | /* MUXed DDC/Aux Capabilities */ + (PCIO << 8) | /* PCIe Configuration Space Owner */ + (1 << 24) | /* Platform Optimus Power Capabilities */ + (3 << 27))) /* Optimus HD Audio Codec Capabilities */ + } + Case (NVOP_FUNC_OPTIMUS_STATUS) + { + Return (ITOB ( + (1 << 0) | /* Optimus Audio Codec Control */ + (0 << 2) | /* Request GPU Power State */ + (0 << 4) | /* Evaluate Requested GPU Power State */ + (0 << 5) | /* Request Optimus Adapter Policy */ + (0 << 7))) /* Evaluate Requested Optimus Adapter Selection */ + } + } + + Return (NV_ERROR_UNSUPPORTED) +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/peg.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/peg.asl new file mode 100644 index 0000000000..ac0c17cbb2 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/peg.asl @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +External (\_SB.PCI0.RP12.PXSX.NPON, MethodObj) +External (\_SB.PCI0.RP12.PXSX.NPOF, MethodObj) + +OperationRegion (PCIC, PCI_Config, 0x00, 0x100) +Field (PCIC, AnyAcc, NoLock, Preserve) +{ + Offset (0x4a), + CEDR, 1, /* Correctable Error Detected, RW/1C/V */ + Offset (0x52), + , 13, + LASX, 1, /* Link Active Status */ + Offset (0x69), + , 2, + LREN, 1, /* LTR Enabled */ + Offset (0xe0), + , 7, + NCB7, 1, /* Scratch bit to save L2/3 state */ + Offset (0xe2), + , 2, + L23E, 1, /* L23_Rdy Entry request */ + L23R, 1 /* L23_Rdy Detect Transition */ +} + +/* L2/3 Entry sequence */ +Method (DL23, 0, Serialized) +{ + L23E = 1 + Local0 = 8 + While (Local0 > 0) + { + If (!L23E) + { + Break + } + + Sleep (2) + Local0-- + } + NCB7 = 1 +} + +/* L2/3 exit seqeuence */ +Method (LD23, 0, Serialized) +{ + If (!NCB7) + { + Return + } + + L23R = 1 + Local0 = 20 + While (Local0 > 0) + { + If (!L23R) + { + Break + } + + Sleep (2) + Local0-- + } + + NCB7 = 0 + Local0 = 8 + While (Local0 > 0) + { + If (LASX == 1) + { + Break + } + + Sleep (2) + Local0-- + } +} + +/* PEG Power Resource */ +PowerResource (PGPR, 0, 0) +{ + Method (_ON, 0, Serialized) + { + /* Power up GPU from GCOFF (or GC6 exit if deferred) */ + \_SB.PCI0.RP12.PXSX.NPON () + _STA = 1 + } + Method (_OFF, 0, Serialized) + { + /* Power down GPU to GCOFF (or GC6 entry if deferred) */ + _STA = 0 + \_SB.PCI0.RP12.PXSX.NPOF () + } + Name (_STA, 0) +} + +Name (_PR0, Package() { PGPR }) +Name (_PR2, Package() { PGPR }) +Name (_PR3, Package() { PGPR }) diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/power.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/power.asl new file mode 100644 index 0000000000..496fd2c147 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/power.asl @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +External (\_SB.PCI0.SPCO, MethodObj) + + +/* Board specific defines */ + +/* PCI Subsystem ID to restore after wake */ +#define GPU_SSID 0xa7611558 + +/* Power control signals */ +#define GPIO_GPU_PERST_L GPP_B09 +#define GPIO_GPU_PWR_EN GPP_D03 +#define GPIO_GPU_PWR_GD GPP_C15 +#define GPIO_GPU_NVVDD_EN GPP_F16 + +/* GPU clock pin */ +#define GPU_SRCCLK_PIN 0x6 + +#define GC6_DEFER_TYPE_EXIT_GC6 3 + +/* 250ms in "Timer" units (i.e. 100ns increments) */ +#define MIN_OFF_TIME_TIMERS 2500000 + +#define SRCCLK_DISABLE 0 +#define SRCCLK_ENABLE 1 + +#define GPU_POWER_STATE_OFF 0 +#define GPU_POWER_STATE_ON 1 + +/* Optimus Power Control State */ +Name (OPCS, OPTIMUS_POWER_CONTROL_DISABLE) + +/* PCI configuration space Owner */ +Name (PCIO, PCI_OWNER_SBIOS) + +/* Deferred GPU State */ +Name (OPS0, OPTIMUS_CONTROL_NO_RUN_PS0) + +/* GC6 Entry/Exit state */ +Name (GC6E, GC6_STATE_EXITED) + +/* Power State, GCOFF, GCON */ +Name (GPPS, GPU_POWER_STATE_ON) + +/* Defer GC6 entry / exit until D3-cold request */ +Name (DFEN, 0) +/* Deferred GC6 Enter control */ +Name (DFCI, 0) +/* Deferred GC6 Exit control */ +Name (DFCO, 0) +/* GCOFF Timer */ +Name (GCOT, 0) + +/* Copy of LTR enable bit from PEG port */ +Name (SLTR, 0) + +/* Control the PCIe SRCCLK# for dGPU */ +Method (SRCC, 1, Serialized) +{ + /* Control clock via P2SB interface */ + \_SB.PCI0.SPCO (GPU_SRCCLK_PIN, Arg0) +} + +/* "GC6 In", i.e. GC6 Entry Sequence */ +Method (GC6I, 0, Serialized) +{ + GC6E = GC6_STATE_TRANSITION + + /* Save the PEG port's LTR setting */ + SLTR = LREN + + /* Put PCIe link into L2/3 */ + \_SB.PCI0.RP12.DL23 () + + /* Wait for GPU to deassert its GPIO4, i.e. GPU_NVVDD_EN */ + GPPL (GPIO_GPU_NVVDD_EN, 0, 20) + + /* Assert GPU_PERST_L */ + CTXS (GPIO_GPU_PERST_L) + + /* Disable PCIe SRCCLK# */ + SRCC (SRCCLK_DISABLE) + + GC6E = GC6_STATE_ENTERED +} + +/* "GC6 Out", i.e. GC6 Exit Sequence */ +Method (GC6O, 0, Serialized) +{ + GC6E = GC6_STATE_TRANSITION + + /* Re-enable PCIe SRCCLK# */ + SRCC (SRCCLK_ENABLE) + + /* Deassert GPU_PERST_L */ + STXS (GPIO_GPU_PERST_L) + + /* Wait for GPU to assert GPU_NVVDD_EN */ + GPPL (GPIO_GPU_NVVDD_EN, 1, 20) + + /* Restore PCIe link back to L0 state */ + \_SB.PCI0.RP12.LD23 () + + /* Wait for dGPU to reappear on the bus */ + Local0 = 50 + While (NVID != PCI_VID_NVIDIA) + { + Stall (100) + Local0-- + If (Local0 == 0) + { + Break + } + } + + /* Restore the PEG LTR enable bit */ + LREN = SLTR + + /* Clear recoverable errors detected bit */ + CEDR = 1 + + GC6E = GC6_STATE_EXITED +} + +/* GCOFF exit sequence */ +Method (PGON, 0, Serialized) +{ + Local0 = Timer - GCOT + If (Local0 < MIN_OFF_TIME_TIMERS) + { + Local1 = (MIN_OFF_TIME_TIMERS - Local0) / 10000 + Printf("Sleeping %o to ensure min GCOFF time", Local1) + Sleep (Local1) + } + + /* Assert PERST# */ + CTXS (GPIO_GPU_PERST_L) + + /* Enable power rails */ + STXS (GPIO_GPU_PWR_EN) + + /* Wait 200ms for PWRGD */ + GPPL (GPIO_GPU_PWR_GD, 1, 200) + + /* Deassert PERST# */ + STXS (GPIO_GPU_PERST_L) + + GC6E = GC6_STATE_EXITED + GPPS = GPU_POWER_STATE_ON +} + +/* GCOFF entry sequence */ +Method (PGOF, 0, Serialized) +{ + /* Assert PERST# */ + CTXS (GPIO_GPU_PERST_L) + + /* Disable power rails */ + CTXS (GPIO_GPU_PWR_EN) + + GCOT = Timer + + GPPS = GPU_POWER_STATE_OFF +} + +/* GCOFF Out, i.e. full power-on sequence */ +Method (GCOO, 0, Serialized) +{ + If (GPPS == GPU_POWER_STATE_ON) + { + Printf ("PGON: GPU already on") + Return + } + + SRCC (SRCCLK_ENABLE) + PGON () + \_SB.PCI0.RP12.LD23 () + + /* Wait for dGPU to reappear on the bus */ + Local0 = 50 + While (NVID != PCI_VID_NVIDIA) + { + Stall (100) + Local0-- + If (Local0 == 0) + { + Break + } + } + + /* Restore the PEG LTR enable bit */ + LREN = SLTR + + /* Clear recoverable errors detected bit */ + CEDR = 1 + + /* Restore the PEG LTR enable bit */ + LREN = SLTR + + /* Clear recoverable errors detected bit */ + CEDR = 1 +} + +/* GCOFF In, i.e. full power-off sequence */ +Method (GCOI, 0, Serialized) +{ + If (GPPS == GPU_POWER_STATE_OFF) + { + Printf ("GPU already off") + Return + } + + /* Save the PEG port's LTR setting */ + SLTR = LREN + \_SB.PCI0.RP12.DL23 () + PGOF () + SRCC (SRCCLK_DISABLE) +} + +/* Handle deferred GC6 vs. poweron request */ +Method (NPON, 0, Serialized) +{ + If (DFEN == GC6_DEFER_ENABLE) + { + If (DFCO == GC6_DEFER_TYPE_EXIT_GC6) + { + GC6O () + } + + DFEN = GC6_DEFER_DISABLE + } + Else + { + GCOO () + } + NVSS = GPU_SSID +} + +/* Handle deferred GC6 vs. poweroff request */ +Method (NPOF, 0, Serialized) +{ + /* Don't touch the `DFEN` flag until the GC6 exit. */ + If (DFEN == GC6_DEFER_ENABLE) + { + /* Deferred GC6 entry */ + If (DFCI == NVJT_GPC_EGNS || DFCI == NVJT_GPC_EGIS) + { + GC6I () + } + } + Else + { + GCOI () + } +} + +Method (_ON, 0, Serialized) +{ + PGON () + NVSS = GPU_SSID +} + +Method (_OFF, 0, Serialized) +{ + PGOF () +} + +/* Put device into D0 */ +Method (_PS0, 0, NotSerialized) +{ + If (OPS0 == OPTIMUS_CONTROL_RUN_PS0) + { + /* Poweron or deferred GC6 exit */ + NPON () + + OPS0 = OPTIMUS_CONTROL_NO_RUN_PS0 + } +} + +/* Put device into D3 */ +Method (_PS3, 0, NotSerialized) +{ + If (OPCS == OPTIMUS_POWER_CONTROL_ENABLE) + { + /* Poweroff or deferred GC6 entry */ + NPOF () + + /* Because _PS3 ran NPOF, _PS0 must run NPON */ + OPS0 = OPTIMUS_CONTROL_RUN_PS0 + + /* OPCS is one-shot, so reset it */ + OPCS = OPTIMUS_POWER_CONTROL_DISABLE + } +} + +Method (PSTA, 0, Serialized) +{ + If (GC6E == GC6_STATE_EXITED && + \_SB.PCI0.GTXS(GPIO_GPU_PWR_GD) == 1) + { + Return (1) + } + Else + { + Return (0) + } +} + +Method (_STA, 0, Serialized) +{ + Return (0xF) +} diff --git a/src/mainboard/novacustom/mtl-h/acpi/dgpu/utility.asl b/src/mainboard/novacustom/mtl-h/acpi/dgpu/utility.asl new file mode 100644 index 0000000000..7bf97d4af4 --- /dev/null +++ b/src/mainboard/novacustom/mtl-h/acpi/dgpu/utility.asl @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * Poll a GPIO until it goes to the specified state + * Arg0 == GPIO # + * Arg1 == state (0 or 1) + * Arg2 == timeout in ms + */ +Method (GPPL, 3, Serialized) +{ + Local0 = 0 + Local1 = Arg2 * 10 + While (Local0 < Local1) + { + If (\_SB.PCI0.GRXS (Arg0) == Arg1) + { + Return (0) + } + Else + { + Local0++ + } + Stall (100) + } + + If (Local0 == Arg2) + { + Printf("[ERROR] GPPL for %o timed out", Arg0) + } + + Return (0xFF) +} + +/* Convert from 32-bit integer to 4-byte buffer (little-endian) */ +Method (ITOB, 1) +{ + Local0 = Buffer(4) { 0, 0, 0, 0 } + Local0[0] = Arg0 & 0xFF + Local0[1] = (Arg0 >> 8) & 0xFF + Local0[2] = (Arg0 >> 16) & 0xFF + Local0[3] = (Arg0 >> 24) & 0xFF + Return (Local0) +} + +/* Convert from 64-bit integer to 8-byte buffer (little-endian) */ +Method (LTOB, 1) +{ + Local0 = Buffer(8) { 0, 0, 0, 0, 0, 0, 0, 0 } + Local0[0] = Arg0 & 0xFF + Local0[1] = (Arg0 >> 8) & 0xFF + Local0[2] = (Arg0 >> 16) & 0xFF + Local0[3] = (Arg0 >> 24) & 0xFF + Local0[4] = (Arg0 >> 32) & 0xFF + Local0[5] = (Arg0 >> 40) & 0xFF + Local0[6] = (Arg0 >> 48) & 0xFF + Local0[7] = (Arg0 >> 56) & 0xFF + Return (Local0) +} diff --git a/src/mainboard/novacustom/mtl-h/dsdt.asl b/src/mainboard/novacustom/mtl-h/dsdt.asl index 06e7263d07..2b2bcb8fc8 100644 --- a/src/mainboard/novacustom/mtl-h/dsdt.asl +++ b/src/mainboard/novacustom/mtl-h/dsdt.asl @@ -31,4 +31,8 @@ DefinitionBlock( } #include "acpi/mainboard.asl" + +#if CONFIG(BOARD_NOVACUSTOM_V5X0TNX_BASE) + #include "acpi/dgpu/gpu_top.asl" +#endif }