summaryrefslogtreecommitdiff
path: root/src/mainboard/google/brya/variants/hades/variant.c
blob: 38ff79371e3e4625d0c2be922ea693e0a4da3fb6 (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
/* 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_1V2_PWR_EN		GPP_D0
#define GPU_1V2_PG		GPP_D1
#define GPU_1V8_PWR_EN		GPP_E11
#define GPU_1V8_PG		GPP_E20
#define GPU_3V3_PWR_EN		GPP_E1
#define GPU_3V3_PG		GPP_E2
#define NVVDD_PWR_EN		GPP_E0
#define NVVDD_PG		GPP_E8
#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.2V",		GPU_1V2_PWR_EN,	false, GPU_1V2_PG, },
	{ "GPU 1.8V",		GPU_1V8_PWR_EN,	false, GPU_1V8_PG, },
	{ "GPU 3.3V",		GPU_3V3_PWR_EN,	false, GPU_3V3_PG, },
	{ "NVVDD+MSVDD",	NVVDD_PWR_EN,	false, NVVDD_PG, },
	{ "PEXVDD",		PEXVDD_PWR_EN,	false, PEXVDD_PG, },
	{ "FBVDD",		FBVDD_PWR_EN,	false,  FBVDD_PG, },
};

/* In GCOFF entry order (i.e., power-off order) */
static struct power_rail_sequence gpu_off_seq[] = {
	{ "FBVDD",		FBVDD_PWR_EN,	false,  FBVDD_PG,	0,},
	{ "PEXVDD",		PEXVDD_PWR_EN,	false, PEXVDD_PG,	10,},
	{ "NVVDD+MSVDD",	NVVDD_PWR_EN,	false, NVVDD_PG,	2,},
	{ "GPU 3.3V",		GPU_3V3_PWR_EN,	false, GPU_3V3_PG,	4,},
	{ "GPU 1.8V",		GPU_1V8_PWR_EN,	false, GPU_1V8_PG,	0,},
	{ "GPU 1.2V",		GPU_1V2_PWR_EN,	false, GPU_1V2_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;

	dgpu_power_sequence_on();
}

/*
 * Pass the specific GPIO names
 */
void variant_fill_ssdt(const struct device *dev)
{
	const int nvvdd_pg_gpio = NVVDD_PG;
	const int gpu_1v8_en_gpio = GPU_1V8_PWR_EN;
	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);
}