|
3 | 3 | * Exceptions for specific devices. Usually work-arounds for fatal design flaws.
|
4 | 4 | */
|
5 | 5 |
|
| 6 | +#include <linux/bitfield.h> |
6 | 7 | #include <linux/delay.h>
|
7 | 8 | #include <linux/dmi.h>
|
8 | 9 | #include <linux/pci.h>
|
| 10 | +#include <linux/suspend.h> |
9 | 11 | #include <linux/vgaarb.h>
|
10 | 12 | #include <asm/amd_nb.h>
|
11 | 13 | #include <asm/hpet.h>
|
@@ -904,3 +906,60 @@ static void chromeos_fixup_apl_pci_l1ss_capability(struct pci_dev *dev)
|
904 | 906 | }
|
905 | 907 | DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_save_apl_pci_l1ss_capability);
|
906 | 908 | 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