diff --git a/src/drivers/gfx/nvidia/Kconfig b/src/drivers/gfx/nvidia/Kconfig new file mode 100644 index 0000000000..cdf68f6a2c --- /dev/null +++ b/src/drivers/gfx/nvidia/Kconfig @@ -0,0 +1,10 @@ +config DRIVERS_GFX_NVIDIA + bool + default n + help + Support for NVIDIA Optimus with GC6 3.0 + +config DRIVERS_GFX_NVIDIA_BRIDGE + hex "PCI bridge for the GPU device" + default 0x01 + depends on DRIVERS_GFX_NVIDIA diff --git a/src/drivers/gfx/nvidia/Makefile.inc b/src/drivers/gfx/nvidia/Makefile.inc new file mode 100644 index 0000000000..32ddf7e64e --- /dev/null +++ b/src/drivers/gfx/nvidia/Makefile.inc @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +romstage-$(CONFIG_DRIVERS_GFX_NVIDIA) += romstage.c + +ramstage-$(CONFIG_DRIVERS_GFX_NVIDIA) += nvidia.c diff --git a/src/drivers/gfx/nvidia/acpi/gpu.asl b/src/drivers/gfx/nvidia/acpi/gpu.asl new file mode 100644 index 0000000000..8c346fdd5b --- /dev/null +++ b/src/drivers/gfx/nvidia/acpi/gpu.asl @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (\_SB.PCI0.PEGP) { + Name (_ADR, CONFIG_DRIVERS_GFX_NVIDIA_BRIDGE << 16) + + 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 + // TODO: use GPU config to generate address + OperationRegion (RPCX, SystemMemory, CONFIG_ECAM_MMCONF_BASE_ADDRESS + 0x8000, 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/gfx/nvidia/chip.h b/src/drivers/gfx/nvidia/chip.h new file mode 100644 index 0000000000..b6d9c908eb --- /dev/null +++ b/src/drivers/gfx/nvidia/chip.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_GFX_NVIDIA_CHIP_H_ +#define _DRIVERS_GFX_NVIDIA_CHIP_H_ + +struct drivers_gfx_nvidia_config { + /* TODO: Set GPIOs in devicetree? */ +}; + +#endif /* _DRIVERS_GFX_NVIDIA_CHIP_H_ */ diff --git a/src/drivers/gfx/nvidia/gpu.h b/src/drivers/gfx/nvidia/gpu.h new file mode 100644 index 0000000000..68b6cd2d59 --- /dev/null +++ b/src/drivers/gfx/nvidia/gpu.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_GFX_NVIDIA_GPU_H_ +#define _DRIVERS_GFX_NVIDIA_GPU_H_ + +#include + +struct nvidia_gpu_config { + /* GPIO for GPU_PWR_EN */ + unsigned int power_gpio; + /* GPIO for GPU_RST# */ + unsigned int reset_gpio; + /* Enable or disable GPU power */ + bool enable; +}; + +void nvidia_set_power(const struct nvidia_gpu_config *config); + +#endif /* _DRIVERS_NVIDIA_GPU_H_ */ diff --git a/src/drivers/gfx/nvidia/nvidia.c b/src/drivers/gfx/nvidia/nvidia.c new file mode 100644 index 0000000000..a4f6b777c2 --- /dev/null +++ b/src/drivers/gfx/nvidia/nvidia.c @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "chip.h" +#include +#include +#include + +#define NVIDIA_SUBSYSTEM_ID_OFFSET 0x40 + +static void nvidia_read_resources(struct device *dev) +{ + printk(BIOS_DEBUG, "%s: %s\n", __func__, dev_path(dev)); + + pci_dev_read_resources(dev); + + // Find all BARs on GPU, mark them above 4g if prefetchable + for (int bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) { + struct resource *res = probe_resource(dev, bar); + + if (res) { + if (res->flags & IORESOURCE_PREFETCH) { + printk(BIOS_INFO, " BAR at 0x%02x marked above 4g\n", bar); + res->flags |= IORESOURCE_ABOVE_4G; + } else { + printk(BIOS_DEBUG, " BAR at 0x%02x not prefetch\n", bar); + } + } else { + printk(BIOS_DEBUG, " BAR at 0x%02x not found\n", bar); + } + } +} + +static void nvidia_set_subsystem(struct device *dev, unsigned int vendor, unsigned int device) +{ + pci_write_config32(dev, NVIDIA_SUBSYSTEM_ID_OFFSET, + ((device & 0xffff) << 16) | (vendor & 0xffff)); +} + +static struct pci_operations nvidia_device_ops_pci = { + .set_subsystem = nvidia_set_subsystem, +}; + +static struct device_operations nvidia_device_ops = { + .read_resources = nvidia_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_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 = &nvidia_device_ops_pci, + +}; + +static void nvidia_enable(struct device *dev) +{ + if (!dev->enabled || dev->path.type != DEVICE_PATH_PCI) + return; + + dev->ops = &nvidia_device_ops; +} + +struct chip_operations drivers_gfx_nvidia_ops = { + CHIP_NAME("NVIDIA Optimus graphics device") + .enable_dev = nvidia_enable +}; diff --git a/src/drivers/gfx/nvidia/romstage.c b/src/drivers/gfx/nvidia/romstage.c new file mode 100644 index 0000000000..f9364abbc8 --- /dev/null +++ b/src/drivers/gfx/nvidia/romstage.c @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include "chip.h" +#include "gpu.h" + +void nvidia_set_power(const struct nvidia_gpu_config *config) +{ + if (!config->power_gpio || !config->reset_gpio) { + printk(BIOS_ERR, "%s: GPU_PWR_EN and GPU_RST# must be set\n", __func__); + return; + } + + printk(BIOS_DEBUG, "%s: GPU_PWR_EN = %d\n", __func__, config->power_gpio); + printk(BIOS_DEBUG, "%s: GPU_RST# = %d\n", __func__, config->reset_gpio); + + gpio_set(config->reset_gpio, 0); + mdelay(4); + + if (config->enable) { + gpio_set(config->power_gpio, 1); + mdelay(4); + gpio_set(config->reset_gpio, 1); + } else { + gpio_set(config->power_gpio, 0); + } + + mdelay(4); +}