Commit 2afbbc65 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/pm'

- Protect driver's D3cold preference from being overwritten by user space
  via sysfs (Lukas Wunner)

- Avoid PME from D3hot/D3cold for AMD Rembrandt and Phoenix USB4 to fix
  wakeup by USB4-attached devices (Mario Limonciello)

* pci/pm:
  x86/PCI: Avoid PME from D3hot/D3cold for AMD Rembrandt and Phoenix USB4
  PCI/sysfs: Protect driver's D3cold preference from user space
parents 20949188 7d08f21f
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
@@ -3,9 +3,11 @@
 * Exceptions for specific devices. Usually work-arounds for fatal design flaws.
 */

#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/pci.h>
#include <linux/suspend.h>
#include <linux/vgaarb.h>
#include <asm/amd_nb.h>
#include <asm/hpet.h>
@@ -904,3 +906,60 @@ static void chromeos_fixup_apl_pci_l1ss_capability(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_save_apl_pci_l1ss_capability);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_fixup_apl_pci_l1ss_capability);

#ifdef CONFIG_SUSPEND
/*
 * Root Ports on some AMD SoCs advertise PME_Support for D3hot and D3cold, but
 * if the SoC is put into a hardware sleep state by the amd-pmc driver, the
 * Root Ports don't generate wakeup interrupts for USB devices.
 *
 * When suspending, remove D3hot and D3cold from the PME_Support advertised
 * by the Root Port so we don't use those states if we're expecting wakeup
 * interrupts.  Restore the advertised PME_Support when resuming.
 */
static void amd_rp_pme_suspend(struct pci_dev *dev)
{
	struct pci_dev *rp;

	/*
	 * PM_SUSPEND_ON means we're doing runtime suspend, which means
	 * amd-pmc will not be involved so PMEs during D3 work as advertised.
	 *
	 * The PMEs *do* work if amd-pmc doesn't put the SoC in the hardware
	 * sleep state, but we assume amd-pmc is always present.
	 */
	if (pm_suspend_target_state == PM_SUSPEND_ON)
		return;

	rp = pcie_find_root_port(dev);
	if (!rp->pm_cap)
		return;

	rp->pme_support &= ~((PCI_PM_CAP_PME_D3hot|PCI_PM_CAP_PME_D3cold) >>
				    PCI_PM_CAP_PME_SHIFT);
	dev_info_once(&rp->dev, "quirk: disabling D3cold for suspend\n");
}

static void amd_rp_pme_resume(struct pci_dev *dev)
{
	struct pci_dev *rp;
	u16 pmc;

	rp = pcie_find_root_port(dev);
	if (!rp->pm_cap)
		return;

	pci_read_config_word(rp, rp->pm_cap + PCI_PM_PMC, &pmc);
	rp->pme_support = FIELD_GET(PCI_PM_CAP_PME_MASK, pmc);
}
/* Rembrandt (yellow_carp) */
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_suspend);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_resume);
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_suspend);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_resume);
/* Phoenix (pink_sardine) */
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_suspend);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_resume);
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_suspend);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_resume);
#endif /* CONFIG_SUSPEND */
+1 −1
Original line number Diff line number Diff line
@@ -911,7 +911,7 @@ pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
{
	int acpi_state, d_max;

	if (pdev->no_d3cold)
	if (pdev->no_d3cold || !pdev->d3cold_allowed)
		d_max = ACPI_STATE_D3_HOT;
	else
		d_max = ACPI_STATE_D3_COLD;
+1 −4
Original line number Diff line number Diff line
@@ -530,10 +530,7 @@ static ssize_t d3cold_allowed_store(struct device *dev,
		return -EINVAL;

	pdev->d3cold_allowed = !!val;
	if (pdev->d3cold_allowed)
		pci_d3cold_enable(pdev);
	else
		pci_d3cold_disable(pdev);
	pci_bridge_d3_update(pdev);

	pm_runtime_resume(dev);