Skip to content

Commit 7d08f21

Browse files
superm1bjorn-helgaas
authored andcommitted
x86/PCI: Avoid PME from D3hot/D3cold for AMD Rembrandt and Phoenix USB4
Iain reports that USB devices can't be used to wake a Lenovo Z13 from suspend. This occurs because on some AMD platforms, even though the Root Ports advertise PME_Support for D3hot and D3cold, wakeup events from devices on a USB4 controller don't result in wakeup interrupts from the Root Port when amd-pmc has put the platform in a hardware sleep state. If amd-pmc will be involved in the suspend, remove D3hot and D3cold from the PME_Support mask of Root Ports above USB4 controllers so we avoid those states if we need wakeups. Restore D3 support at resume so that it can be used by runtime suspend. This affects both AMD Rembrandt and Phoenix SoCs. "pm_suspend_target_state == PM_SUSPEND_ON" means we're doing runtime suspend, and amd-pmc will not be involved. In that case PMEs work as advertised in D3hot/D3cold, so we don't need to do anything. Note that amd-pmc is technically optional, and there's no need for this quirk if it's not present, but we assume it's always present because power consumption is so high without it. Fixes: 9d26d3a ("PCI: Put PCIe ports into D3 during suspend") Link: https://lore.kernel.org/r/[email protected] Reported-by: Iain Lane <[email protected]> Closes: https://forums.lenovo.com/t5/Ubuntu/Z13-can-t-resume-from-suspend-with-external-USB-keyboard/m-p/5217121 Signed-off-by: Mario Limonciello <[email protected]> [bhelgaas: commit log, move to arch/x86/pci/fixup.c, add #includes] Signed-off-by: Bjorn Helgaas <[email protected]> Cc: [email protected]
1 parent 70b70a4 commit 7d08f21

File tree

1 file changed

+59
-0
lines changed

1 file changed

+59
-0
lines changed

arch/x86/pci/fixup.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
* Exceptions for specific devices. Usually work-arounds for fatal design flaws.
44
*/
55

6+
#include <linux/bitfield.h>
67
#include <linux/delay.h>
78
#include <linux/dmi.h>
89
#include <linux/pci.h>
10+
#include <linux/suspend.h>
911
#include <linux/vgaarb.h>
1012
#include <asm/amd_nb.h>
1113
#include <asm/hpet.h>
@@ -904,3 +906,60 @@ static void chromeos_fixup_apl_pci_l1ss_capability(struct pci_dev *dev)
904906
}
905907
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_save_apl_pci_l1ss_capability);
906908
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_fixup_apl_pci_l1ss_capability);
909+
910+
#ifdef CONFIG_SUSPEND
911+
/*
912+
* Root Ports on some AMD SoCs advertise PME_Support for D3hot and D3cold, but
913+
* if the SoC is put into a hardware sleep state by the amd-pmc driver, the
914+
* Root Ports don't generate wakeup interrupts for USB devices.
915+
*
916+
* When suspending, remove D3hot and D3cold from the PME_Support advertised
917+
* by the Root Port so we don't use those states if we're expecting wakeup
918+
* interrupts. Restore the advertised PME_Support when resuming.
919+
*/
920+
static void amd_rp_pme_suspend(struct pci_dev *dev)
921+
{
922+
struct pci_dev *rp;
923+
924+
/*
925+
* PM_SUSPEND_ON means we're doing runtime suspend, which means
926+
* amd-pmc will not be involved so PMEs during D3 work as advertised.
927+
*
928+
* The PMEs *do* work if amd-pmc doesn't put the SoC in the hardware
929+
* sleep state, but we assume amd-pmc is always present.
930+
*/
931+
if (pm_suspend_target_state == PM_SUSPEND_ON)
932+
return;
933+
934+
rp = pcie_find_root_port(dev);
935+
if (!rp->pm_cap)
936+
return;
937+
938+
rp->pme_support &= ~((PCI_PM_CAP_PME_D3hot|PCI_PM_CAP_PME_D3cold) >>
939+
PCI_PM_CAP_PME_SHIFT);
940+
dev_info_once(&rp->dev, "quirk: disabling D3cold for suspend\n");
941+
}
942+
943+
static void amd_rp_pme_resume(struct pci_dev *dev)
944+
{
945+
struct pci_dev *rp;
946+
u16 pmc;
947+
948+
rp = pcie_find_root_port(dev);
949+
if (!rp->pm_cap)
950+
return;
951+
952+
pci_read_config_word(rp, rp->pm_cap + PCI_PM_PMC, &pmc);
953+
rp->pme_support = FIELD_GET(PCI_PM_CAP_PME_MASK, pmc);
954+
}
955+
/* Rembrandt (yellow_carp) */
956+
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_suspend);
957+
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_resume);
958+
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_suspend);
959+
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_resume);
960+
/* Phoenix (pink_sardine) */
961+
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_suspend);
962+
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_resume);
963+
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_suspend);
964+
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_resume);
965+
#endif /* CONFIG_SUSPEND */

0 commit comments

Comments
 (0)