aboutsummaryrefslogtreecommitdiff
path: root/src/drivers
diff options
context:
space:
mode:
authorRaul E Rangel <rrangel@chromium.org>2021-05-26 16:58:30 -0600
committerPatrick Georgi <pgeorgi@google.com>2021-06-07 11:35:47 +0000
commit6b4b4a8349209b4131f4524823c392bec64c7418 (patch)
tree0de6b062aa39bf45d9dbe7890a236b5189f66c3b /src/drivers
parentb9b6f4d0132b0ae77a52261efbf0c2f5b6011d0d (diff)
drivers/pcie/rtd3/device: Add PCIe RTD3 driver
This driver was inspired from soc/intel/common/block/pci/rtd3. I decided to copy and modify it because the Intel driver has a lot of Intel specific code. This driver has been stripped down to only provide a power resource and set the StorageD3Enable property. This driver is SoC agnostic and does not handle suspending the actual PCIe root port. That should be implemented by an SoC specific driver. This is required for Guybrush to suspend/resume properly because the NVMe power is tied to the S0 power rails, so the kernel needs to place the device into D3. BUG=b:184617186 TEST=Guybrush is able to suspend/resume properly. Also see power resource get enabled / disabled. [ 56.075559] power-0416 __acpi_power_off : Power resource [RTD3] turned off [ 56.075562] device_pm-0279 device_set_power : Device [PXSX] transitioned to D3cold [ 56.075567] pci_pm_suspend_noirq: nvme 0000:02:00.0: PCI PM: Suspend power state: D3cold [ 56.075569] nvme 0000:02:00.0: pci_pm_suspend_noirq+0x0/0x413 returned 0 after 15978 usecs [ 123.464874] nvme 0000:02:00.0: calling pci_pm_resume_noirq+0x0/0x11d @ 7, parent: 0000:00:02.4 [ 123.464891] acpi_device_set_power: ACPI: \_SB_.PCI0.GP14.PXSX: Power state change: D3cold -> D0 [ 123.464982] power-0360 __acpi_power_on : Power resource [RTD3] turned on [ 123.464984] device_pm-0279 device_set_power : Device [PXSX] transitioned to D0 [ 123.465039] nvme 0000:02:00.0: pci_pm_resume_noirq+0x0/0x11d returned 0 after 158 usecs Signed-off-by: Raul E Rangel <rrangel@chromium.org> Change-Id: I2adfc925183ff7a19ab97e89212bc87c40d552d0 Reviewed-on: https://review.coreboot.org/c/coreboot/+/54966 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/pcie/rtd3/device/Kconfig6
-rw-r--r--src/drivers/pcie/rtd3/device/Makefile.inc1
-rw-r--r--src/drivers/pcie/rtd3/device/chip.c169
-rw-r--r--src/drivers/pcie/rtd3/device/chip.h28
4 files changed, 204 insertions, 0 deletions
diff --git a/src/drivers/pcie/rtd3/device/Kconfig b/src/drivers/pcie/rtd3/device/Kconfig
new file mode 100644
index 0000000000..7fecd57580
--- /dev/null
+++ b/src/drivers/pcie/rtd3/device/Kconfig
@@ -0,0 +1,6 @@
+config DRIVERS_PCIE_RTD3_DEVICE
+ bool
+ depends on HAVE_ACPI_TABLES
+ help
+ When enabled, this driver will add support for ACPI controlled
+ Runtime D3 using GPIOs for power/reset control of the PCIe device.
diff --git a/src/drivers/pcie/rtd3/device/Makefile.inc b/src/drivers/pcie/rtd3/device/Makefile.inc
new file mode 100644
index 0000000000..df4aaf63ab
--- /dev/null
+++ b/src/drivers/pcie/rtd3/device/Makefile.inc
@@ -0,0 +1 @@
+ramstage-$(CONFIG_DRIVERS_PCIE_RTD3_DEVICE) += chip.c
diff --git a/src/drivers/pcie/rtd3/device/chip.c b/src/drivers/pcie/rtd3/device/chip.c
new file mode 100644
index 0000000000..349dffd037
--- /dev/null
+++ b/src/drivers/pcie/rtd3/device/chip.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <acpi/acpi_device.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpigen_pci.h>
+#include <assert.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "chip.h"
+
+/*
+ * This UUID and the resulting ACPI Device Property is defined by the
+ * Power Management for Storage Hardware Devices:
+ *
+ * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/power-management-for-storage-hardware-devices-intro
+ */
+#define PCIE_RTD3_STORAGE_UUID "5025030F-842F-4AB4-A561-99A5189762D0"
+#define PCIE_RTD3_STORAGE_PROPERTY "StorageD3Enable"
+
+/*
+ * Writes the ACPI power resources for a PCI device so it can enter D3Cold.
+ *
+ * If the device is a storage class, then the StorageD3Enable _DSD will
+ * also be added.
+ *
+ * e.g.,
+ *
+ * Scope (\_SB.PCI0.GP14)
+ * {
+ * Device (NVME)
+ * {
+ * Name (_PR0, Package (0x01) // _PR0: Power Resources for D0
+ * {
+ * PRIC
+ * })
+ * Name (_PR3, Package (0x01) // _PR3: Power Resources for D3hot
+ * {
+ * PRIC
+ * })
+ * PowerResource (PRIC, 0x00, 0x0000)
+ * {
+ * Method (_STA, 0, NotSerialized) // _STA: Status
+ * {
+ * ...
+ * }
+ *
+ * Method (_ON, 0, Serialized) // _ON_: Power On
+ * {
+ * ...
+ * }
+ *
+ * Method (_OFF, 0, Serialized) // _OFF: Power Off
+ * {
+ * ...
+ * }
+ * }
+ *
+ * Name (_ADR, 0x0000000000000000) // _ADR: Address
+ * Method (_STA, 0, NotSerialized) // _STA: Status
+ * {
+ * Return (0x0F)
+ * }
+ *
+ * Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
+ * {
+ * ToUUID ("5025030f-842f-4ab4-a561-99a5189762d0"),
+ * Package (0x01)
+ * {
+ * Package (0x02)
+ * {
+ * "StorageD3Enable",
+ * One
+ * }
+ * }
+ * })
+ * }
+ * }
+ */
+static void pcie_rtd3_device_acpi_fill_ssdt(const struct device *dev)
+{
+ struct acpi_dp *dsd, *pkg;
+ const struct drivers_pcie_rtd3_device_config *config = config_of(dev);
+ /* Copy the GPIOs to avoid discards 'const' qualifier error */
+ struct acpi_gpio reset_gpio = config->reset_gpio;
+ struct acpi_gpio enable_gpio = config->enable_gpio;
+ const struct acpi_power_res_params power_res_params = {
+ .reset_gpio = &reset_gpio,
+ .reset_delay_ms = config->reset_delay_ms,
+ .reset_off_delay_ms = config->reset_off_delay_ms,
+ .enable_gpio = &enable_gpio,
+ .enable_delay_ms = config->enable_delay_ms,
+ .enable_off_delay_ms = config->enable_off_delay_ms,
+ .use_gpio_for_status = true,
+ };
+ const char *scope = acpi_device_scope(dev);
+ const char *name = acpi_device_name(dev);
+
+ assert(name);
+ assert(scope);
+
+ printk(BIOS_INFO, "%s.%s: Enable RTD3 for %s (%s)\n", scope, name, dev_path(dev),
+ dev->chip_ops->name);
+
+ acpigen_write_scope(scope);
+ acpigen_write_device(acpi_device_name(dev));
+
+ acpi_device_add_power_res(&power_res_params);
+
+ acpigen_write_ADR_pci_device(dev);
+ acpigen_write_STA(acpi_device_status(dev));
+
+ /* Storage devices won't enter D3 without this property */
+ if ((dev->class >> 16) == PCI_BASE_CLASS_STORAGE) {
+ dsd = acpi_dp_new_table("_DSD");
+ pkg = acpi_dp_new_table(PCIE_RTD3_STORAGE_UUID);
+ acpi_dp_add_integer(pkg, PCIE_RTD3_STORAGE_PROPERTY, 1);
+ acpi_dp_add_package(dsd, pkg);
+ acpi_dp_write(dsd);
+
+ printk(BIOS_INFO, "%s.%s: Added StorageD3Enable property\n", scope, name);
+ }
+
+ acpigen_write_device_end();
+ acpigen_write_scope_end();
+
+ /* Call the default PCI acpi_fill_ssdt */
+ pci_rom_ssdt(dev);
+}
+
+static const char *pcie_rtd3_device_acpi_name(const struct device *dev)
+{
+ const struct drivers_pcie_rtd3_device_config *config = config_of(dev);
+
+ return config->name;
+}
+
+static struct device_operations pcie_rtd3_device_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = pci_dev_init,
+ .ops_pci = &pci_dev_ops_pci,
+ .write_acpi_tables = pci_rom_write_acpi_tables,
+ .acpi_fill_ssdt = pcie_rtd3_device_acpi_fill_ssdt,
+ .acpi_name = pcie_rtd3_device_acpi_name,
+};
+
+static void pcie_rtd3_device_enable(struct device *dev)
+{
+ struct drivers_pcie_rtd3_device_config *config = dev ? dev->chip_info : NULL;
+
+ if (!config)
+ return;
+
+ if (dev->path.type != DEVICE_PATH_PCI) {
+ printk(BIOS_ERR, "%s: Invalid device type\n", dev_path(dev));
+ return;
+ }
+
+ dev->ops = &pcie_rtd3_device_ops;
+}
+
+struct chip_operations drivers_pcie_rtd3_device_ops = {
+ CHIP_NAME("PCIe Device w/ Runtime D3")
+ .enable_dev = pcie_rtd3_device_enable
+};
diff --git a/src/drivers/pcie/rtd3/device/chip.h b/src/drivers/pcie/rtd3/device/chip.h
new file mode 100644
index 0000000000..e1a8aa7833
--- /dev/null
+++ b/src/drivers/pcie/rtd3/device/chip.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DRIVERS_PCIE_RTD3_DEVICE_CHIP_H__
+#define __DRIVERS_PCIE_RTD3_DEVICE_CHIP_H__
+
+#include <acpi/acpi_device.h>
+
+/* Adds a GPIO controlled ACPI PowerResource for a PCIe device. */
+struct drivers_pcie_rtd3_device_config {
+ /* Name of ACPI node for the device */
+ const char *name;
+
+ /* GPIO used to enable device. */
+ struct acpi_gpio enable_gpio;
+ /* Delay to be inserted after device is enabled. */
+ unsigned int enable_delay_ms;
+ /* Delay to be inserted after device is disabled. */
+ unsigned int enable_off_delay_ms;
+
+ /* GPIO used to take device out of reset or to put it into reset. */
+ struct acpi_gpio reset_gpio;
+ /* Delay to be inserted after device is taken out of reset. */
+ unsigned int reset_delay_ms;
+ /* Delay to be inserted after device is put into reset. */
+ unsigned int reset_off_delay_ms;
+};
+
+#endif /* __DRIVERS_PCIE_RTD3_DEVICE_CHIP_H__ */