@@ -741,6 +741,49 @@ static int amd_pmc_czn_wa_irq1(struct amd_pmc_dev *pdev)
return 0;
}
+/*
+ * Constraints are specified in the ACPI LPS0 device and specify what the
+ * platform intended for the device.
+ *
+ * If a constraint is present and >= to ACPI_STATE_S3, then enable D3 for the
+ * device with the 'optin' callback.
+ * If a constraint is not present or < ACPI_STATE_S3, then disable D3 for the
+ * device with the 'veto' callback.
+ */
+static int amd_pmc_get_constraint(struct pci_dev *pci_dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&pci_dev->dev);
+
+ if (!adev)
+ return ACPI_STATE_UNKNOWN;
+
+ return acpi_get_lps0_constraint(adev);
+}
+
+static bool amd_pmc_d3_optin_check(struct pci_dev *pci_dev)
+{
+ int constraint = amd_pmc_get_constraint(pci_dev);
+
+ if (constraint == ACPI_STATE_UNKNOWN ||
+ constraint < ACPI_STATE_S3)
+ return false;
+
+ dev_dbg(&pci_dev->dev, "Opting in to D3 due to constraint %d\n", constraint);
+ return true;
+}
+
+static bool amd_pmc_d3_veto_check(struct pci_dev *pci_dev)
+{
+ int constraint = amd_pmc_get_constraint(pci_dev);
+
+ if (constraint != ACPI_STATE_UNKNOWN &&
+ constraint >= ACPI_STATE_S3)
+ return false;
+
+ dev_dbg(&pci_dev->dev, "Vetoing D3 due to constraint %d\n", constraint);
+ return true;
+}
+
static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
{
struct rtc_device *rtc_device;
@@ -881,6 +924,12 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
.restore = amd_pmc_s2idle_restore,
};
+static struct pci_d3_driver_ops amd_pmc_d3_veto_ops = {
+ .optin = amd_pmc_d3_optin_check,
+ .veto = amd_pmc_d3_veto_check,
+ .priority = 50,
+};
+
static int amd_pmc_suspend_handler(struct device *dev)
{
struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
@@ -1074,10 +1123,17 @@ static int amd_pmc_probe(struct platform_device *pdev)
amd_pmc_quirks_init(dev);
}
+ err = pci_register_d3_possible_cb(&amd_pmc_d3_veto_ops);
+ if (err)
+ goto err_register_lps0;
+
amd_pmc_dbgfs_register(dev);
pm_report_max_hw_sleep(U64_MAX);
return 0;
+err_register_lps0:
+ if (IS_ENABLED(CONFIG_SUSPEND))
+ acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
err_pci_dev_put:
pci_dev_put(rdev);
return err;
@@ -1089,6 +1145,7 @@ static void amd_pmc_remove(struct platform_device *pdev)
if (IS_ENABLED(CONFIG_SUSPEND))
acpi_unregister_lps0_dev(&amd_pmc_s2idle_dev_ops);
+ pci_unregister_d3_possible_cb(&amd_pmc_d3_veto_ops);
amd_pmc_dbgfs_unregister(dev);
pci_dev_put(dev->rdev);
mutex_destroy(&dev->lock);
Iain reports that USB devices can't be used to wake a Lenovo Z13 from suspend. This is because the PCIe root port has been put into D3hot and AMD's platform can't handle USB devices waking from a hardware sleep state in this case. This problem only occurs on Linux, and only when the AMD PMC driver is utilized to put the device into a hardware sleep state. When the AMD PMC driver is enabled it will notify the hardware that the OS is ready for it to try to enter a hardware sleep state. Comparing the behavior on Windows and Linux, Windows doesn't put the root ports into D3. This is because the Windows uPEP driver takes into account constraints that are advertised by the platform in an ACPI device. The constraints are available for individual devices via `acpi_get_lps0_constraint`. Add support for the amd-pmc driver to fetch constraints for devices and report them as either an 'optin' or a 'veto' to the PCI core when it evaluates whether a device should support 'D3'. This interface intentionally doesn't specify D3hot or D3cold, it's collectively 'D3'. Any enabled constraints set to ACPI_STATE_S3 or greater will be reported to the 'optin' callback to ensure that they policy is set to select 'D3'. Any disabled constraints, missing constraints, or constraints set to less than ACPI_STATE_S3 will be reported to the 'veto' callback to ensure the policy matches the Windows behavior of disabling 'D3'. This behavior is necessary due to the policy enacted by commit 9d26d3a8f1b0 ("PCI: Put PCIe ports into D3 during suspend"). Fixes: 9d26d3a8f1b0 ("PCI: Put PCIe ports into D3 during suspend") Reported-by: Iain Lane <iain@orangesquash.org.uk> 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 <mario.limonciello@amd.com> --- drivers/platform/x86/amd/pmc/pmc.c | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+)