Jeremy Soller has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/43615 )
Change subject: drivers/system76/dgpu: Add driver for System76 switchable graphics ......................................................................
drivers/system76/dgpu: Add driver for System76 switchable graphics
This adds a driver for System76 systems with switchable NVIDIA graphics. The driver provides ACPI support for dynamically powering on and off the GPU, a function for enabling the GPU power in the bootblock, and a hook in ramstage for moving GPU prefetch resources above 4 GiB.
Tested on system76/addw2, system76/gaze15, and system76/oryp6, which will be submitted in future patches.
Signed-off-by: Jeremy Soller jeremy@system76.com Change-Id: I05833e88457bfc3612cc9a8cbf1eec68dcfc5566 --- A src/drivers/system76/dgpu/Kconfig A src/drivers/system76/dgpu/Makefile.inc A src/drivers/system76/dgpu/acpi/dgpu.asl A src/drivers/system76/dgpu/bootblock.c A src/drivers/system76/dgpu/ramstage.c 5 files changed, 313 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/15/43615/1
diff --git a/src/drivers/system76/dgpu/Kconfig b/src/drivers/system76/dgpu/Kconfig new file mode 100644 index 0000000..5096671 --- /dev/null +++ b/src/drivers/system76/dgpu/Kconfig @@ -0,0 +1,5 @@ +config DRIVERS_SYSTEM76_DGPU + bool + default n + help + System76 switchable graphics support diff --git a/src/drivers/system76/dgpu/Makefile.inc b/src/drivers/system76/dgpu/Makefile.inc new file mode 100644 index 0000000..a230f97 --- /dev/null +++ b/src/drivers/system76/dgpu/Makefile.inc @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +ramstage-$(CONFIG_DRIVERS_SYSTEM76_DGPU) += ramstage.c diff --git a/src/drivers/system76/dgpu/acpi/dgpu.asl b/src/drivers/system76/dgpu/acpi/dgpu.asl new file mode 100644 index 0000000..a721934 --- /dev/null +++ b/src/drivers/system76/dgpu/acpi/dgpu.asl @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (_SB.PCI0.PEGP) { + Name (_ADR, 0x00010000) + + PowerResource (PWRR, 0, 0) { + Name (_STA, 1) + + Method (_ON) { + Debug = "PEGP.PWRR._ON" + If (_STA != 1) { + _SB.PCI0.PEGP.DEV0._ON () + _STA = 1 + } + } + + Method (_OFF) { + Debug = "PEGP.PWRR._OFF" + If (_STA != 0) { + _SB.PCI0.PEGP.DEV0._OFF () + _STA = 0 + } + } + } + + Name (_PR0, Package () { _SB.PCI0.PEGP.PWRR }) + Name (_PR2, Package () { _SB.PCI0.PEGP.PWRR }) + Name (_PR3, Package () { _SB.PCI0.PEGP.PWRR }) +} + +Device (_SB.PCI0.PEGP.DEV0) { + Name(_ADR, 0x00000000) + Name (_STA, 0xF) + Name (LTRE, 0) + + // Memory mapped PCI express registers + // Not sure what this stuff is, but it is used to get into GC6 + OperationRegion (RPCX, SystemMemory, 0xE0008000, 0x1000) + Field (RPCX, ByteAcc, NoLock, Preserve) { + PVID, 16, + PDID, 16, + CMDR, 8, + Offset (0x19), + PRBN, 8, + Offset (0x84), + D0ST, 2, + Offset (0xAA), + CEDR, 1, + Offset (0xAC), + , 4, + CMLW, 6, + Offset (0xB0), + ASPM, 2, + , 2, + P0LD, 1, + RTLK, 1, + Offset (0xC9), + , 2, + LREN, 1, + Offset (0x11A), + , 1, + VCNP, 1, + Offset (0x214), + Offset (0x216), + P0LS, 4, + Offset (0x248), + , 7, + Q0L2, 1, + Q0L0, 1, + Offset (0x504), + Offset (0x506), + PCFG, 2, + Offset (0x508), + TREN, 1, + Offset (0xC20), + , 4, + P0AP, 2, + Offset (0xC38), + , 3, + P0RM, 1, + Offset (0xC74), + P0LT, 4, + Offset (0xD0C), + , 20, + LREV, 1 + } + + Method (_ON) { + Debug = "PEGP.DEV0._ON" + + If (_STA != 0xF) { + Debug = " If DGPU_PWR_EN low" + If (! GTXS (DGPU_PWR_EN)) { + Debug = " DGPU_PWR_EN high" + STXS (DGPU_PWR_EN) + + Debug = " Sleep 16" + Sleep (16) + } + + Debug = " DGPU_RST_N high" + STXS(DGPU_RST_N) + + Debug = " Sleep 10" + Sleep (10) + + Debug = " Q0L0 = 1" + Q0L0 = 1 + + Debug = " Sleep 16" + Sleep (16) + + Debug = " While Q0L0" + Local0 = 0 + While (Q0L0) { + If ((Local0 > 4)) { + Debug = " While Q0L0 timeout" + Break + } + + Sleep (16) + Local0++ + } + + Debug = " P0RM = 0" + P0RM = 0 + + Debug = " P0AP = 0" + P0AP = 0 + + Debug = Concatenate(" LREN = ", ToHexString(LTRE)) + LREN = LTRE + + Debug = " CEDR = 1" + CEDR = 1 + + Debug = " CMDR |= 7" + CMDR |= 7 + + Debug = " _STA = 0xF" + _STA = 0xF + } + } + + Method (_OFF) { + Debug = "PEGP.DEV0._OFF" + + If (_STA != 0x5) { + Debug = Concatenate(" LTRE = ", ToHexString(LREN)) + LTRE = LREN + + Debug = " Q0L2 = 1" + Q0L2 = 1 + + Debug = " Sleep 16" + Sleep (16) + + Debug = " While Q0L2" + Local0 = Zero + While (Q0L2) { + If ((Local0 > 4)) { + Debug = " While Q0L2 timeout" + Break + } + + Sleep (16) + Local0++ + } + + Debug = " P0RM = 1" + P0RM = 1 + + Debug = " P0AP = 3" + P0AP = 3 + + Debug = " Sleep 10" + Sleep (10) + + Debug = " DGPU_RST_N low" + CTXS(DGPU_RST_N) + + Debug = " While DGPU_GC6 low" + Local0 = Zero + While (! GRXS(DGPU_GC6)) { + If ((Local0 > 4)) { + Debug = " While DGPU_GC6 low timeout" + + Debug = " DGPU_PWR_EN low" + CTXS (DGPU_PWR_EN) + Break + } + + Sleep (16) + Local0++ + } + + Debug = " _STA = 0x5" + _STA = 0x5 + } + } +} diff --git a/src/drivers/system76/dgpu/bootblock.c b/src/drivers/system76/dgpu/bootblock.c new file mode 100644 index 0000000..61f7fbf --- /dev/null +++ b/src/drivers/system76/dgpu/bootblock.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +//TODO: do not require this to be included in mainboard bootblock.c + +#include <console/console.h> +#include <delay.h> +#include <gpio.h> + +static void dgpu_power_enable(int onoff) { + printk(BIOS_DEBUG, "system76: DGPU power %d\n", onoff); + if (onoff) { + gpio_set(DGPU_RST_N, 0); + mdelay(4); + gpio_set(DGPU_PWR_EN, 1); + mdelay(4); + gpio_set(DGPU_RST_N, 1); + } else { + gpio_set(DGPU_RST_N, 0); + mdelay(4); + gpio_set(DGPU_PWR_EN, 0); + } + mdelay(50); +} diff --git a/src/drivers/system76/dgpu/ramstage.c b/src/drivers/system76/dgpu/ramstage.c new file mode 100644 index 0000000..eeb1607 --- /dev/null +++ b/src/drivers/system76/dgpu/ramstage.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <bootstate.h> +#include <console/console.h> +#include <device/pci.h> + +static void dgpu_read_resources(struct device *dev) { + printk(BIOS_INFO, "system76: dgpu_read_resources %s\n", dev_path(dev)); + + pci_dev_read_resources(dev); + + int bar; + // Find all BARs on DGPU, mark them above 4g if prefetchable + for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) { + printk(BIOS_INFO, " BAR at 0x%02x\n", bar); + + struct resource *res; + res = probe_resource(dev, bar); + if (res) { + if (res->flags & IORESOURCE_PREFETCH) { + printk(BIOS_INFO, " marked above 4g\n"); + res->flags |= IORESOURCE_ABOVE_4G; + } else { + printk(BIOS_INFO, " not prefetch\n"); + } + } else { + printk(BIOS_INFO, " not found\n"); + } + } +} + +static void dgpu_enable_resources(struct device *dev) { + printk(BIOS_INFO, "system76: dgpu_enable_resources %s\n", dev_path(dev)); + + dev->subsystem_vendor = CONFIG_SUBSYSTEM_VENDOR_ID; + dev->subsystem_device = CONFIG_SUBSYSTEM_DEVICE_ID; + printk(BIOS_INFO, " subsystem <- %04x/%04x\n", dev->subsystem_vendor, dev->subsystem_device); + pci_write_config32(dev, 0x40, ((dev->subsystem_device & 0xffff) << 16) | (dev->subsystem_vendor & 0xffff)); + + pci_dev_enable_resources(dev); +} + +static struct device_operations dgpu_pci_ops_dev = { + .read_resources = dgpu_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = dgpu_enable_resources, +#if CONFIG(HAVE_ACPI_TABLES) + .write_acpi_tables = pci_rom_write_acpi_tables, + .acpi_fill_ssdt = pci_rom_ssdt, +#endif + .init = pci_dev_init, + .ops_pci = &pci_dev_ops_pci, +}; + +static void dgpu_above_4g(void *unused) { + struct device *pdev; + + // Find PEG0 + pdev = pcidev_on_root(1, 0); + if (!pdev) { + printk(BIOS_ERR, "system76: failed to find PEG0\n"); + return; + } + printk(BIOS_INFO, "system76: PEG0 at %p, %04x:%04x\n", pdev, pdev->vendor, pdev->device); + + int fn; + for (fn = 0; fn < 8; fn++) { + struct device *dev; + + // Find DGPU functions + dev = pcidev_path_behind(pdev->link_list, PCI_DEVFN(0, fn)); + if (dev) { + printk(BIOS_INFO, "system76: DGPU fn %d at %p, %04x:%04x\n", fn, dev, dev->vendor, dev->device); + dev->ops = &dgpu_pci_ops_dev; + } else { + printk(BIOS_ERR, "system76: failed to find DGPU fn %d\n", fn); + } + } +} + +BOOT_STATE_INIT_ENTRY(BS_DEV_RESOURCES, BS_ON_ENTRY, dgpu_above_4g, NULL);