summaryrefslogtreecommitdiffstats
path: root/src/mainboard/google/brya/variants/agah/variant.c
blob: 9acee54284800949922481ba3c71b3398db1d3ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/* SPDX-License-Identifier: GPL-2.0-only */

#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <baseboard/variants.h>
#include <boardid.h>
#include <delay.h>
#include <device/pci.h>
#include <gpio.h>
#include <timer.h>
#include <types.h>

#define GPU_1V8_PWR_EN		GPP_F12
#define GPU_1V8_PG		GPP_E20
#define NV33_PWR_EN		GPP_A21
#define NV33_PG			GPP_A22
#define NVVDD_PWR_EN		GPP_E0
#define NVVDD_PG		GPP_E3
#define PEXVDD_PWR_EN		GPP_E10
#define PEXVDD_PG		GPP_E17
#define FBVDD_PWR_EN		GPP_A19
#define FBVDD_PG		GPP_E4
#define GPU_PERST_L		GPP_B3
#define GPU_ALLRAILS_PG		GPP_E5

#define DEFAULT_PG_TIMEOUT_US	20000

#define VGAR_BYTE_OFFSET	5

/* Maximum size of PCI config space to save. */
#define GPU_CONFIG_SAVE_SPACE_BYTES	0x100

static bool gpu_powered_on;

struct power_rail_sequence {
	const char *name;

	/* This is the GPIO (output) connected to the VR's enable pin. */
	gpio_t pwr_en_gpio;
	bool pwr_en_active_low;

	/* This is the GPIO (input) connected to the VR's power-good pin. */
	gpio_t pg_gpio;

	/* Delay after sequencing this rail. */
	unsigned int delay_ms;
};

/* In GCOFF exit order (i.e., power-on order) */
static struct power_rail_sequence gpu_on_seq[] = {
	{ "GPU 1.8V",		GPU_1V8_PWR_EN,	false, GPU_1V8_PG, },
	{ "NV3_3",		NV33_PWR_EN,	false, NV33_PG, },
	{ "NVVDD+MSVDD",	NVVDD_PWR_EN,	false, NVVDD_PG, },
	{ "PEXVDD",		PEXVDD_PWR_EN,	false, PEXVDD_PG, },
	{ "FBVDD",		FBVDD_PWR_EN,	true,  FBVDD_PG, },
};

/* In GCOFF entry order (i.e., power-off order) */
static struct power_rail_sequence gpu_off_seq[] = {
	{ "FBVDD",		FBVDD_PWR_EN,	true,  FBVDD_PG,	0,},
	{ "PEXVDD",		PEXVDD_PWR_EN,	false, PEXVDD_PG,	10,},
	{ "NVVDD+MSVDD",	NVVDD_PWR_EN,	false, NVVDD_PG,	2,},
	{ "NV3_3",		NV33_PWR_EN,	false, NV33_PG,		4,},
	{ "GPU 1.8V",		GPU_1V8_PWR_EN,	false, GPU_1V8_PG,	0,},
};

enum rail_state {
	RAIL_OFF = 0,
	RAIL_ON = 1,
};

/* Assert the VR's enable pin, and wait until the VR's power-good is asserted. */
static bool sequence_rail(const struct power_rail_sequence *seq, enum rail_state state)
{
	enum rail_state pwr_en_state = state;
	bool result;

	if (seq->pwr_en_active_low)
		pwr_en_state = !pwr_en_state;

	gpio_output(seq->pwr_en_gpio, pwr_en_state);
	result = wait_us(DEFAULT_PG_TIMEOUT_US, gpio_get(seq->pg_gpio) == state) >= 0;
	if (seq->delay_ms)
		mdelay(seq->delay_ms);

	return result;
}

static void dgpu_power_sequence_off(void)
{
	/* Assert reset and clear power-good */
	gpio_output(GPU_PERST_L, 0);

	/* Inform the GPU that the power is no longer good. */
	gpio_output(GPU_ALLRAILS_PG, 0);

	for (size_t i = 0; i < ARRAY_SIZE(gpu_off_seq); i++) {
		if (!sequence_rail(&gpu_off_seq[i], RAIL_OFF)) {
			printk(BIOS_ERR, "Failed to disable %s rail, continuing!\n",
			       gpu_off_seq[i].name);
		}
	}
}

static void dgpu_power_sequence_on(void)
{
	/* Assert PERST# */
	gpio_output(GPU_PERST_L, 0);

	for (size_t i = 0; i < ARRAY_SIZE(gpu_on_seq); i++) {
		if (!sequence_rail(&gpu_on_seq[i], RAIL_ON)) {
			printk(BIOS_ERR, "Failed to enable %s rail, sequencing back down!\n",
			       gpu_on_seq[i].name);

			/* If an error occurred, then perform the power-off sequence and
			   return early to avoid setting GPU_ALLRAILS_PG and PERST_L. */
			dgpu_power_sequence_off();
			return;
	       }
	}

	/* Set power-good and release PERST# */
	gpio_output(GPU_ALLRAILS_PG, 1);
	mdelay(1);
	gpio_output(GPU_PERST_L, 1);

	printk(BIOS_INFO, "Sequenced GPU successfully\n");
	mdelay(1);

	gpu_powered_on = true;
}

void variant_init(void)
{
	if (acpi_is_wakeup_s3())
		return;

	/* For board revs 3 and later, the power good pin for the
	   NVVDD VR moved from GPP_E16 to GPP_E3, and the GPU_1V8 enable
	   pin moved from GPP_E18 to GPP_F12, so patch up the table
	   for old board revs. */
	if (board_id() < 3) {
		const struct pad_config board_rev_2_gpios[] = {
			PAD_NC(GPP_E3, NONE),
			PAD_CFG_GPO(GPP_E18, 0, PLTRST),
			PAD_CFG_GPI(GPP_E16, NONE, PLTRST),
			PAD_NC(GPP_F12, NONE),
		};
		gpio_configure_pads(board_rev_2_gpios, ARRAY_SIZE(board_rev_2_gpios));

		gpu_on_seq[2].pg_gpio = GPP_E16;
		gpu_off_seq[2].pg_gpio = GPP_E16;

		gpu_on_seq[0].pwr_en_gpio = GPP_E18;
		gpu_off_seq[4].pwr_en_gpio = GPP_E18;
	} else {
		const struct pad_config board_rev_3_gpios[] = {
			PAD_CFG_GPI(GPP_E3, NONE, PLTRST),
			PAD_NC(GPP_E18, NONE),
			PAD_NC(GPP_E16, NONE),
			PAD_CFG_GPO(GPP_F12, 0, PLTRST),
		};
		gpio_configure_pads(board_rev_3_gpios, ARRAY_SIZE(board_rev_3_gpios));
	}

	dgpu_power_sequence_on();
}

/*
 * For board revs 3 and later, two pins moved:
 * - The PG pin for the NVVDD VR moved from GPP_E16 to GPP_E3.
 * - The enable pin for the GPU_1V8 VR moved from GPP_E18 to GPP_F12
 *
 * To accommodate this, the DSDT contains two Names that this code
 * will write the correct GPIO # to depending on the board rev, and
 * we'll use that instead.
 */
void variant_fill_ssdt(const struct device *dev)
{
	const int nvvdd_pg_gpio = board_id() < 3 ? GPP_E16 : GPP_E3;
	const int gpu_1v8_en_gpio = board_id() < 3 ? GPP_E18 : GPP_F12;
	acpigen_write_scope("\\_SB.PCI0.PEG0.PEGP");
	acpigen_write_method("_INI", 0);
	acpigen_write_store_int_to_namestr(nvvdd_pg_gpio, "NVPG");
	acpigen_write_store_int_to_namestr(gpu_1v8_en_gpio, "GPEN");
	acpigen_write_method_end();
	acpigen_write_scope_end();
}

void variant_finalize(void)
{
	/*
	 * Currently the `pch_pirq_init()` function in lpc_lib.c will program
	 * PIRQ IRQs for all PCI devices discovered during enumeration. This may
	 * not be correct for all devices, and causes strange behavior with the
	 * Nvidia dGPU; it will start out with IRQ 11 and then after a
	 * suspend/resume cycle, it will get programmed back to 16, so the Linux
	 * kernel must be doing some IRQ sanitization at some point.  To fix
	 * this anomaly, explicitly program the IRQ to 16 (which we know is what
	 * IRQ it will eventually take).
	*/
	const struct device *dgpu = DEV_PTR(dgpu);
	pci_write_config8(dgpu, PCI_INTERRUPT_LINE, 16);
}