diff options
Diffstat (limited to 'src/mainboard/google/brya/variants')
-rw-r--r-- | src/mainboard/google/brya/variants/hades/Makefile.inc | 1 | ||||
-rw-r--r-- | src/mainboard/google/brya/variants/hades/variant.c | 174 |
2 files changed, 175 insertions, 0 deletions
diff --git a/src/mainboard/google/brya/variants/hades/Makefile.inc b/src/mainboard/google/brya/variants/hades/Makefile.inc index 837101ea25..81d298a708 100644 --- a/src/mainboard/google/brya/variants/hades/Makefile.inc +++ b/src/mainboard/google/brya/variants/hades/Makefile.inc @@ -2,3 +2,4 @@ bootblock-y += gpio.c romstage-y += gpio.c ramstage-y += gpio.c ramstage-y += ramstage.c +ramstage-y += variant.c diff --git a/src/mainboard/google/brya/variants/hades/variant.c b/src/mainboard/google/brya/variants/hades/variant.c new file mode 100644 index 0000000000..6e980977f3 --- /dev/null +++ b/src/mainboard/google/brya/variants/hades/variant.c @@ -0,0 +1,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_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.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); +} |