| /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| |
| |
| /* Voltage rail control signals */ |
| #define GPIO_1V8_PWR_EN GPP_E18 |
| #define GPIO_1V8_PG GPP_E20 |
| #define GPIO_NV33_PWR_EN GPP_A21 |
| #define GPIO_NV33_PG GPP_A22 |
| #define GPIO_NVVDD_PWR_EN GPP_E0 |
| #define GPIO_NVVDD_PG GPP_E16 |
| #define GPIO_PEXVDD_PWR_EN GPP_E10 |
| #define GPIO_PEXVDD_PG GPP_E17 |
| #define GPIO_FBVDD_PWR_EN GPP_A19 |
| #define GPIO_FBVDD_PG GPP_E4 |
| |
| #define GPIO_GPU_PERST_L GPP_B3 |
| #define GPIO_GPU_ALLRAILS_PG GPP_E5 |
| #define GPIO_GPU_NVVDD_EN GPP_A17 |
| |
| #define GC6_DEFER_TYPE_EXIT_GC6 3 |
| |
| /* 250ms in "Timer" units (i.e. 100ns increments) */ |
| #define MIN_OFF_TIME_TIMERS 2500000 |
| |
| /* Optimus Power Control State */ |
| Name (OPCS, OPTIMUS_POWER_CONTROL_DISABLE) |
| |
| /* PCI configuration space Owner */ |
| Name (PCIO, PCI_OWNER_DRIVER) |
| |
| /* Saved PCI configuration space memory (VGA Buffer) */ |
| Name (VGAB, Buffer (0xfb) { 0x00 }) |
| |
| /* Deferred GPU State */ |
| Name (OPS0, OPTIMUS_CONTROL_NO_RUN_PS0) |
| |
| /* GC6 Entry/Exit state */ |
| Name (GC6E, GC6_STATE_EXITED) |
| |
| /* Power State, GCOFF, GCON */ |
| #define GPU_POWER_STATE_OFF 0 |
| #define GPU_POWER_STATE_ON 1 |
| 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) |
| |
| /* "GC6 In", i.e. GC6 Entry Sequence */ |
| Method (GC6I, 0, Serialized) |
| { |
| GC6E = GC6_STATE_TRANSITION |
| |
| /* Put PCIe link into L2/3 */ |
| \_SB.PCI0.PEG0.DL23 () |
| |
| /* Wait for GPU to deassert its GPIO4, i.e. GPU_NVVDD_EN */ |
| GPPL (GPIO_GPU_NVVDD_EN, 0, 20) |
| |
| /* Deassert PG_GPU_ALLRAILS */ |
| \_SB.PCI0.CTXS (GPIO_GPU_ALLRAILS_PG) |
| |
| /* Ramp down PEXVDD */ |
| \_SB.PCI0.CTXS (GPIO_PEXVDD_PWR_EN) |
| GPPL (GPIO_PEXVDD_PG, 0, 20) |
| Sleep (10) |
| |
| /* Deassert EN_PPVAR_GPU_NVVDD */ |
| \_SB.PCI0.CTXS (GPIO_NVVDD_PWR_EN) |
| GPPL (GPIO_NVVDD_PG, 0, 20) |
| Sleep (2) |
| |
| /* Assert GPU_PERST_L */ |
| \_SB.PCI0.CTXS (GPIO_GPU_PERST_L) |
| |
| Printf ("dGPU entered GC6") |
| GC6E = GC6_STATE_ENTERED |
| } |
| |
| /* "GC6 Out", i.e. GC6 Exit Sequence */ |
| Method (GC6O, 0, Serialized) |
| { |
| GC6E = GC6_STATE_TRANSITION |
| |
| /* Deassert GPU_PERST_L */ |
| \_SB.PCI0.STXS (GPIO_GPU_PERST_L) |
| |
| /* Wait for GPU to assert GPU_NVVDD_EN */ |
| GPPL (GPIO_GPU_NVVDD_EN, 1, 20) |
| |
| /* Ramp up NVVDD */ |
| \_SB.PCI0.STXS (GPIO_NVVDD_PWR_EN) |
| GPPL (GPIO_NVVDD_PG, 1, 4) |
| |
| /* Ramp up PEXVDD */ |
| \_SB.PCI0.STXS (GPIO_PEXVDD_PWR_EN) |
| GPPL (GPIO_PEXVDD_PG, 1, 4) |
| |
| /* Assert PG_GPU_ALLRAILS */ |
| \_SB.PCI0.STXS (GPIO_GPU_ALLRAILS_PG) |
| |
| /* Put PCIe link into L0 state */ |
| \_SB.PCI0.PEG0.LD23 () |
| |
| Printf ("dGPU exited GC6") |
| GC6E = GC6_STATE_EXITED |
| } |
| |
| /* GCOFF exit sequence */ |
| Method (PGON, 0, Serialized) |
| { |
| If (GPPS == GPU_POWER_STATE_ON) |
| { |
| Printf ("PGON: GPU already on") |
| Return |
| } |
| |
| 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# */ |
| \_SB.PCI0.CTXS (GPIO_GPU_PERST_L) |
| |
| /* Ramp up 1.8V rail */ |
| \_SB.PCI0.STXS (GPIO_1V8_PWR_EN) |
| GPPL (GPIO_1V8_PG, 1, 20) |
| |
| /* Ramp up NV33 rail */ |
| \_SB.PCI0.STXS (GPIO_NV33_PWR_EN) |
| GPPL (GPIO_NV33_PG, 1, 20) |
| |
| /* Ramp up NVVDD rail */ |
| \_SB.PCI0.STXS (GPIO_NVVDD_PWR_EN) |
| GPPL (GPIO_NVVDD_PG, 1, 5) |
| |
| /* Ramp up PEXVDD rail */ |
| \_SB.PCI0.STXS (GPIO_PEXVDD_PWR_EN) |
| GPPL (GPIO_PEXVDD_PG, 1, 5) |
| |
| /* Ramp up FBVDD rail (active low) */ |
| \_SB.PCI0.CTXS (GPIO_FBVDD_PWR_EN) |
| GPPL (GPIO_FBVDD_PG, 1, 5) |
| |
| /* All rails are good */ |
| \_SB.PCI0.STXS (GPIO_GPU_ALLRAILS_PG) |
| Sleep (1) |
| |
| /* Deassert PERST# */ |
| \_SB.PCI0.STXS (GPIO_GPU_PERST_L) |
| |
| GC6E = GC6_STATE_EXITED |
| GPPS = GPU_POWER_STATE_ON |
| Printf ("GPU Sequenced on") |
| } |
| |
| /* GCOFF entry sequence */ |
| Method (PGOF, 0, Serialized) |
| { |
| If (GPPS == GPU_POWER_STATE_OFF) |
| { |
| Printf ("GPU already off") |
| Return |
| } |
| |
| /* Assert PERST# */ |
| \_SB.PCI0.CTXS (GPIO_GPU_PERST_L) |
| |
| /* All rails are about to go down */ |
| \_SB.PCI0.CTXS (GPIO_GPU_ALLRAILS_PG) |
| Sleep (1) |
| |
| /* Ramp down FBVDD (active-low) and let rail discharge to <10% */ |
| \_SB.PCI0.STXS (GPIO_FBVDD_PWR_EN) |
| GPPL (GPIO_FBVDD_PG, 0, 20) |
| |
| /* Ramp down PEXVDD and let rail discharge to <10% */ |
| \_SB.PCI0.CTXS (GPIO_PEXVDD_PWR_EN) |
| GPPL (GPIO_PEXVDD_PG, 0, 20) |
| Sleep (10) |
| |
| /* Ramp down NVVDD and let rail discharge to <10% */ |
| \_SB.PCI0.CTXS (GPIO_NVVDD_PWR_EN) |
| GPPL (GPIO_NVVDD_PG, 0, 20) |
| Sleep (2) |
| |
| /* Ramp down NV33 and let rail discharge to <10% */ |
| \_SB.PCI0.CTXS (GPIO_NV33_PWR_EN) |
| GPPL (GPIO_NV33_PG, 0, 20) |
| Sleep (4) |
| |
| /* Ramp down 1.8V */ |
| \_SB.PCI0.CTXS (GPIO_1V8_PWR_EN) |
| GPPL (GPIO_1V8_PG, 0, 20) |
| |
| GCOT = Timer |
| |
| GPPS = GPU_POWER_STATE_OFF |
| Printf ("GPU sequenced off") |
| } |
| |
| /* Handle deferred GC6 vs. poweron request */ |
| Method (NPON, 0, Serialized) |
| { |
| If (DFEN == GC6_DEFER_ENABLE) /* 1 */ |
| { |
| If (DFCO == GC6_DEFER_TYPE_EXIT_GC6) /* 3 */ |
| { |
| GC6O () |
| } |
| |
| DFEN = GC6_DEFER_DISABLE |
| } |
| Else |
| { |
| PGON () |
| \_SB.PCI0.PEG0.LD23 () |
| } |
| } |
| |
| /* Handle deferred GC6 vs. poweroff request */ |
| Method (NPOF, 0, Serialized) |
| { |
| If (DFEN == GC6_DEFER_ENABLE) |
| { |
| /* Deferred GC6 entry */ |
| If (DFCI == NVJT_GPC_EGNS || DFCI == NVJT_GPC_EGIS) |
| { |
| GC6I () |
| } |
| |
| DFEN = GC6_DEFER_DISABLE |
| } |
| Else |
| { |
| \_SB.PCI0.PEG0.DL23 () |
| PGOF () |
| } |
| } |
| |
| Method (_ON, 0, Serialized) |
| { |
| PGON () |
| } |
| |
| Method (_OFF, 0, Serialized) |
| { |
| PGOF () |
| } |
| |
| /* Put device into D0 */ |
| Method (_PS0, 0, NotSerialized) |
| { |
| If (OPS0 == OPTIMUS_CONTROL_RUN_PS0) |
| { |
| /* Restore PCI config space */ |
| If (PCIO == PCI_OWNER_SBIOS) |
| { |
| VGAR = VGAB |
| } |
| |
| /* 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) |
| { |
| /* Save PCI config space to ACPI buffer */ |
| If (PCIO == PCI_OWNER_SBIOS) |
| { |
| VGAB = VGAR |
| } |
| |
| /* Poweroff or deferred GC6 entry */ |
| NPOF () |
| |
| /* Because _PS3 ran _OFF, _PS0 must run _ON */ |
| OPS0 = OPTIMUS_CONTROL_RUN_PS0 |
| |
| /* OPCS is one-shot, so reset it */ |
| OPCS = OPTIMUS_POWER_CONTROL_DISABLE |
| } |
| } |
| |
| /* |
| * Normally, _ON and _OFF of the power resources listed in _PRx will be |
| * evaluated before entering D0/D3. However, for Optimus, the package |
| * should refer to the PCIe controller itself, not a dependent device. |
| */ |
| Name (_PR0, Package() { \_SB.PCI0.PEG0 }) |
| Name (_PR3, Package() { \_SB.PCI0.PEG0 }) |
| |
| Method (_STA, 0, Serialized) |
| { |
| If (GC6E == GC6_STATE_EXITED && |
| \_SB.PCI0.GTXS(GPIO_GPU_ALLRAILS_PG) == 1) |
| { |
| Return (0xF) |
| } |
| Else |
| { |
| Return (0) |
| } |
| } |