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
|
/* SPDX-License-Identifier: GPL-2.0-only */
#include <acpi/acpi.h>
#include <acpi/acpigen.h>
#include <baseboard/variants.h>
#include <delay.h>
#include <device/pci_ops.h>
#include <gpio.h>
#include <timer.h>
#include <types.h>
#define GPU_1V8_PWR_EN GPP_E18
#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_E16
#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 const 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 const struct power_rail_sequence gpu_off_seq[] = {
{ "FBVDD", FBVDD_PWR_EN, true, FBVDD_PG, 150,},
{ "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);
mdelay(5);
/* 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();
}
|