summaryrefslogtreecommitdiff
path: root/src/mainboard/google/brya/variants
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainboard/google/brya/variants')
-rw-r--r--src/mainboard/google/brya/variants/hades/Makefile.inc1
-rw-r--r--src/mainboard/google/brya/variants/hades/variant.c174
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);
+}