summaryrefslogtreecommitdiffstats
path: root/src/mainboard/google/brya
diff options
context:
space:
mode:
authorTim Wawrzynczak <twawrzynczak@chromium.org>2022-02-17 11:42:34 -0700
committerTim Wawrzynczak <twawrzynczak@chromium.org>2022-05-05 14:45:51 +0000
commitf9734fc1420a7f3fc7adde26415dfb692e15c314 (patch)
tree4bfc351f939e0f78d3766086a811ea0621d3915e /src/mainboard/google/brya
parent45789141538030777f302a9c141e9d1a4c9fbd3b (diff)
downloadcoreboot-f9734fc1420a7f3fc7adde26415dfb692e15c314.tar.gz
coreboot-f9734fc1420a7f3fc7adde26415dfb692e15c314.tar.bz2
coreboot-f9734fc1420a7f3fc7adde26415dfb692e15c314.zip
mb/google/brya/var/agah: Add GPU power sequencing
This patch adds support for power sequencing of the Nvidia GN3050 for agah, which uses PCH GPIOs to control the 5 power rails required for the GPU. The GPU is power sequenced on during mainboard initialization, then it is enumerated on the PCI bus and its resources are assigned. This GPU will be used in a sort of "hybrid graphics" mode, therefore during finalization, since its PCI BARs are saved into ACPI memory and the GPU is not required upon initial boot, the GPU is power sequenced off. Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Change-Id: I1072be12ef58af5859e2a2d19c4a9c1adc0b0f88 Reviewed-on: https://review.coreboot.org/c/coreboot/+/62384 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Eric Lai <eric_lai@quanta.corp-partner.google.com>
Diffstat (limited to 'src/mainboard/google/brya')
-rw-r--r--src/mainboard/google/brya/variants/agah/Makefile.inc1
-rw-r--r--src/mainboard/google/brya/variants/agah/gpio.c4
-rw-r--r--src/mainboard/google/brya/variants/agah/overridetree.cb9
-rw-r--r--src/mainboard/google/brya/variants/agah/variant.c179
4 files changed, 191 insertions, 2 deletions
diff --git a/src/mainboard/google/brya/variants/agah/Makefile.inc b/src/mainboard/google/brya/variants/agah/Makefile.inc
index 139345f2602b..3d5212f98461 100644
--- a/src/mainboard/google/brya/variants/agah/Makefile.inc
+++ b/src/mainboard/google/brya/variants/agah/Makefile.inc
@@ -6,3 +6,4 @@ romstage-y += gpio.c
romstage-y += memory.c
ramstage-y += gpio.c
+ramstage-y += variant.c
diff --git a/src/mainboard/google/brya/variants/agah/gpio.c b/src/mainboard/google/brya/variants/agah/gpio.c
index 81c9724d5ac1..369e373808e1 100644
--- a/src/mainboard/google/brya/variants/agah/gpio.c
+++ b/src/mainboard/google/brya/variants/agah/gpio.c
@@ -75,8 +75,8 @@ static const struct pad_config override_gpio_table[] = {
/* D16 : ISH_UART0_CTS# ==> NC */
PAD_NC_LOCK(GPP_D16, NONE, LOCK_CONFIG),
- /* E0 : SATAXPCIE0 ==> EN_PPVAR_GPU_NVVDD_X_OD */
- PAD_CFG_GPO(GPP_E0, 0, DEEP),
+ /* E0 : SATAXPCIE0 ==> EN_PPVAR_GPU_NVVDD_X_ODL */
+ PAD_CFG_GPO(GPP_E0, 1, DEEP),
/* E3 : PROC_GP0 ==> NC */
PAD_NC(GPP_E3, NONE),
/* E4 : SATA_DEVSLP0 ==> PG_PPVAR_GPU_FBVDDQ_X_OD */
diff --git a/src/mainboard/google/brya/variants/agah/overridetree.cb b/src/mainboard/google/brya/variants/agah/overridetree.cb
index ad23f7b6f1dc..70bc09289d42 100644
--- a/src/mainboard/google/brya/variants/agah/overridetree.cb
+++ b/src/mainboard/google/brya/variants/agah/overridetree.cb
@@ -58,6 +58,15 @@ chip soc/intel/alderlake
}"
device domain 0 on
+ device ref pcie4_0 on
+ # Enable CPU PCIe RP 1 using CLKREQ 0 and CLKSRC 0
+ register "cpu_pcie_rp[CPU_RP(1)]" = "{
+ .clk_req = 0,
+ .clk_src = 0,
+ .flags = PCIE_RP_LTR | PCIE_RP_AER,
+ }"
+ device pci 00.0 alias dgpu on end
+ end
device ref dtt on
chip drivers/intel/dptf
## sensor information
diff --git a/src/mainboard/google/brya/variants/agah/variant.c b/src/mainboard/google/brya/variants/agah/variant.c
new file mode 100644
index 000000000000..979e4e02efd8
--- /dev/null
+++ b/src/mainboard/google/brya/variants/agah/variant.c
@@ -0,0 +1,179 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <acpi/acpi.h>
+#include <acpi/acpigen.h>
+#include <baseboard/variants.h>
+#include <delay.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_A17
+#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;
+};
+
+/* In GCOFF exit order (i.e., power-on order) */
+static const struct power_rail_sequence gpu_rails[] = {
+ { "GPU 1.8V", GPU_1V8_PWR_EN, false, GPU_1V8_PG, },
+ { "NV3_3", NV33_PWR_EN, false, NV33_PG, },
+ { "NVVDD+MSVDD", NVVDD_PWR_EN, true, NVVDD_PG, },
+ { "PEXVDD", PEXVDD_PWR_EN, false, PEXVDD_PG, },
+ { "FBVDD", FBVDD_PWR_EN, false, FBVDD_PG, },
+};
+
+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)
+{
+ if (seq->pwr_en_active_low)
+ state = !state;
+
+ gpio_output(seq->pwr_en_gpio, state);
+ return wait_us(DEFAULT_PG_TIMEOUT_US, gpio_get(seq->pg_gpio) == state) > 0;
+}
+
+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 (int i = (int)ARRAY_SIZE(gpu_rails) - 1; i >= 0; i--) {
+ if (!sequence_rail(&gpu_rails[i], RAIL_OFF)) {
+ printk(BIOS_ERR, "Failed to disable %s rail, continuing!\n",
+ gpu_rails[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_rails); i++) {
+ if (!sequence_rail(&gpu_rails[i], RAIL_ON)) {
+ printk(BIOS_ERR, "Failed to enable %s rail, sequencing back down!\n",
+ gpu_rails[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");
+ gpu_powered_on = true;
+}
+
+void variant_init(void)
+{
+ if (acpi_is_wakeup_s3())
+ return;
+
+ dgpu_power_sequence_on();
+}
+
+void variant_finalize(void)
+{
+ if (acpi_is_wakeup_s3() || !gpu_powered_on)
+ return;
+
+ /*
+ * Because the dGPU is used here in a way similar to "hybrid graphics"
+ * modes, it is powered down here. The DRIVERS_GFX_NVIDIA_SAVE_BARS
+ * option is selected for agah, so the BARs will be saved to ACPI memory
+ * during its finalize routine. Thus, it is powered down here, as the
+ * proper resources have already been allocated.
+ */
+ dgpu_power_sequence_off();
+
+ printk(BIOS_INFO, "GPU power sequenced off.\n");
+}
+
+/* Save PCI BARs to the ACPI copy of the "saved PCI config space" */
+void variant_fill_ssdt(const struct device *unused)
+{
+ if (!gpu_powered_on)
+ return;
+
+ const struct device *dgpu = DEV_PTR(dgpu);
+ acpigen_write_scope("\\_SB.PCI0.PEG0.PEGP");
+ acpigen_write_method("_INI", 0);
+ {
+ /* Local0 = VGAR */
+ acpigen_write_store();
+ acpigen_emit_namestring("VGAR");
+ acpigen_emit_byte(LOCAL0_OP);
+
+ /*
+ * CreateDWordField(Local0, 11, BAR0)
+ * BAR0 = bases[0]
+ * CreateDWordField(Local0, 15, BAR1)
+ * BAR1 = bases[1]
+ * ...
+ */
+ for (unsigned int idx = PCI_BASE_ADDRESS_0, i = 0; idx <= PCI_BASE_ADDRESS_5;
+ idx += sizeof(uint32_t), ++i) {
+ char name[ACPI_NAME_BUFFER_SIZE];
+ const struct resource *res;
+
+ res = probe_resource(dgpu, idx);
+ if (!res)
+ continue;
+
+ snprintf(name, sizeof(name), "BAR%1d", i);
+ acpigen_write_create_dword_field(LOCAL0_OP, idx - VGAR_BYTE_OFFSET,
+ name);
+ acpigen_write_store_int_to_namestr(res->base & 0xffffffff, name);
+ }
+
+ /* VGAR = Local0 */
+ acpigen_write_store_op_to_namestr(LOCAL0_OP, "VGAR");
+ }
+
+ acpigen_write_method_end();
+ acpigen_write_scope_end();
+}