blob: 0864ec007fc079d36b2bd6b89fc74b3d036a2003 [file] [log] [blame]
Tarun Tuli166387f72023-03-29 13:36:26 +00001/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <acpi/acpi.h>
4#include <acpi/acpigen.h>
5#include <baseboard/variants.h>
6#include <boardid.h>
7#include <delay.h>
8#include <device/pci.h>
9#include <gpio.h>
10#include <timer.h>
11#include <types.h>
12
13#define GPU_1V2_PWR_EN GPP_D0
14#define GPU_1V2_PG GPP_D1
15#define GPU_1V8_PWR_EN GPP_E11
16#define GPU_1V8_PG GPP_E20
17#define GPU_3V3_PWR_EN GPP_E1
18#define GPU_3V3_PG GPP_E2
19#define NVVDD_PWR_EN GPP_E0
Tarun Tuli15dd44e2023-04-14 19:32:24 +000020#define NVVDD_PG GPP_E8
Tarun Tuli166387f72023-03-29 13:36:26 +000021#define PEXVDD_PWR_EN GPP_E10
22#define PEXVDD_PG GPP_E17
23#define FBVDD_PWR_EN GPP_A19
24#define FBVDD_PG GPP_E4
25#define GPU_PERST_L GPP_B3
26#define GPU_ALLRAILS_PG GPP_E5
27
28#define DEFAULT_PG_TIMEOUT_US 20000
29
30#define VGAR_BYTE_OFFSET 5
31
32/* Maximum size of PCI config space to save. */
33#define GPU_CONFIG_SAVE_SPACE_BYTES 0x100
34
35static bool gpu_powered_on;
36
37struct power_rail_sequence {
38 const char *name;
39
40 /* This is the GPIO (output) connected to the VR's enable pin. */
41 gpio_t pwr_en_gpio;
42 bool pwr_en_active_low;
43
44 /* This is the GPIO (input) connected to the VR's power-good pin. */
45 gpio_t pg_gpio;
46
47 /* Delay after sequencing this rail. */
48 unsigned int delay_ms;
49};
50
51/* In GCOFF exit order (i.e., power-on order) */
52static struct power_rail_sequence gpu_on_seq[] = {
53 { "GPU 1.2V", GPU_1V2_PWR_EN, false, GPU_1V2_PG, },
54 { "GPU 1.8V", GPU_1V8_PWR_EN, false, GPU_1V8_PG, },
55 { "GPU 3.3V", GPU_3V3_PWR_EN, false, GPU_3V3_PG, },
56 { "NVVDD+MSVDD", NVVDD_PWR_EN, false, NVVDD_PG, },
57 { "PEXVDD", PEXVDD_PWR_EN, false, PEXVDD_PG, },
58 { "FBVDD", FBVDD_PWR_EN, false, FBVDD_PG, },
59};
60
61/* In GCOFF entry order (i.e., power-off order) */
62static struct power_rail_sequence gpu_off_seq[] = {
63 { "FBVDD", FBVDD_PWR_EN, false, FBVDD_PG, 0,},
Tarun Tuli3e304e52023-05-09 13:00:26 +000064 { "PEXVDD", PEXVDD_PWR_EN, false, PEXVDD_PG, 3,},
Tarun Tuli166387f72023-03-29 13:36:26 +000065 { "NVVDD+MSVDD", NVVDD_PWR_EN, false, NVVDD_PG, 2,},
66 { "GPU 3.3V", GPU_3V3_PWR_EN, false, GPU_3V3_PG, 4,},
67 { "GPU 1.8V", GPU_1V8_PWR_EN, false, GPU_1V8_PG, 0,},
68 { "GPU 1.2V", GPU_1V2_PWR_EN, false, GPU_1V2_PG, 0,},
69};
70
71enum rail_state {
72 RAIL_OFF = 0,
73 RAIL_ON = 1,
74};
75
76/* Assert the VR's enable pin, and wait until the VR's power-good is asserted. */
77static bool sequence_rail(const struct power_rail_sequence *seq, enum rail_state state)
78{
79 enum rail_state pwr_en_state = state;
80 bool result;
81
82 if (seq->pwr_en_active_low)
83 pwr_en_state = !pwr_en_state;
84
85 gpio_output(seq->pwr_en_gpio, pwr_en_state);
Tarun Tuli11ef8162023-06-07 21:23:13 +000086 result = wait_us(DEFAULT_PG_TIMEOUT_US, gpio_get(seq->pg_gpio) == state) > 0;
Tarun Tuli166387f72023-03-29 13:36:26 +000087 if (seq->delay_ms)
88 mdelay(seq->delay_ms);
89
90 return result;
91}
92
93static void dgpu_power_sequence_off(void)
94{
95 /* Assert reset and clear power-good */
96 gpio_output(GPU_PERST_L, 0);
97
98 /* Inform the GPU that the power is no longer good. */
99 gpio_output(GPU_ALLRAILS_PG, 0);
100
101 for (size_t i = 0; i < ARRAY_SIZE(gpu_off_seq); i++) {
102 if (!sequence_rail(&gpu_off_seq[i], RAIL_OFF)) {
103 printk(BIOS_ERR, "Failed to disable %s rail, continuing!\n",
104 gpu_off_seq[i].name);
105 }
106 }
107}
108
109static void dgpu_power_sequence_on(void)
110{
111 /* Assert PERST# */
112 gpio_output(GPU_PERST_L, 0);
113
114 for (size_t i = 0; i < ARRAY_SIZE(gpu_on_seq); i++) {
115 if (!sequence_rail(&gpu_on_seq[i], RAIL_ON)) {
116 printk(BIOS_ERR, "Failed to enable %s rail, sequencing back down!\n",
117 gpu_on_seq[i].name);
118
119 /* If an error occurred, then perform the power-off sequence and
120 return early to avoid setting GPU_ALLRAILS_PG and PERST_L. */
121 dgpu_power_sequence_off();
122 return;
123 }
124 }
125
126 /* Set power-good and release PERST# */
127 gpio_output(GPU_ALLRAILS_PG, 1);
128 mdelay(1);
129 gpio_output(GPU_PERST_L, 1);
130
131 printk(BIOS_INFO, "Sequenced GPU successfully\n");
132 mdelay(1);
133
134 gpu_powered_on = true;
135}
136
137void variant_init(void)
138{
139 if (acpi_is_wakeup_s3())
140 return;
141
142 dgpu_power_sequence_on();
143}
144
145/*
146 * Pass the specific GPIO names
147 */
148void variant_fill_ssdt(const struct device *dev)
149{
150 const int nvvdd_pg_gpio = NVVDD_PG;
151 const int gpu_1v8_en_gpio = GPU_1V8_PWR_EN;
152 acpigen_write_scope("\\_SB.PCI0.PEG0.PEGP");
153 acpigen_write_method("_INI", 0);
154 acpigen_write_store_int_to_namestr(nvvdd_pg_gpio, "NVPG");
155 acpigen_write_store_int_to_namestr(gpu_1v8_en_gpio, "GPEN");
156 acpigen_write_method_end();
157 acpigen_write_scope_end();
158}
159
160void variant_finalize(void)
161{
162 /*
163 * Currently the `pch_pirq_init()` function in lpc_lib.c will program
164 * PIRQ IRQs for all PCI devices discovered during enumeration. This may
165 * not be correct for all devices, and causes strange behavior with the
166 * Nvidia dGPU; it will start out with IRQ 11 and then after a
167 * suspend/resume cycle, it will get programmed back to 16, so the Linux
168 * kernel must be doing some IRQ sanitization at some point. To fix
169 * this anomaly, explicitly program the IRQ to 16 (which we know is what
170 * IRQ it will eventually take).
171 */
172 const struct device *dgpu = DEV_PTR(dgpu);
173 pci_write_config8(dgpu, PCI_INTERRUPT_LINE, 16);
174}