Merge tag 'acpi-5.15-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Sep 2021 23:33:21 +0000 (16:33 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 8 Sep 2021 23:33:21 +0000 (16:33 -0700)
Pull more ACPI updates from Rafael Wysocki:
 "These add ACPI support to the PCI VMD driver, improve suspend-to-idle
  support for AMD platforms and update documentation.

  Specifics:

   - Add ACPI support to the PCI VMD driver (Rafael Wysocki)

   - Rearrange suspend-to-idle support code to reflect the platform
     firmware expectations on some AMD platforms (Mario Limonciello)

   - Make SSDT overlays documentation follow the code documented by it
     more closely (Andy Shevchenko)"

* tag 'acpi-5.15-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI: PM: s2idle: Run both AMD and Microsoft methods if both are supported
  Documentation: ACPI: Align the SSDT overlays file with the code
  PCI: VMD: ACPI: Make ACPI companion lookup work for VMD bus

1  2 
drivers/pci/pci-acpi.c

diff --combined drivers/pci/pci-acpi.c
@@@ -17,6 -17,7 +17,7 @@@
  #include <linux/pci-acpi.h>
  #include <linux/pm_runtime.h>
  #include <linux/pm_qos.h>
+ #include <linux/rwsem.h>
  #include "pci.h"
  
  /*
@@@ -934,77 -935,58 +935,77 @@@ static pci_power_t acpi_pci_choose_stat
  
  static struct acpi_device *acpi_pci_find_companion(struct device *dev);
  
 +void pci_set_acpi_fwnode(struct pci_dev *dev)
 +{
 +      if (!ACPI_COMPANION(&dev->dev) && !pci_dev_is_added(dev))
 +              ACPI_COMPANION_SET(&dev->dev,
 +                                 acpi_pci_find_companion(&dev->dev));
 +}
 +
 +/**
 + * pci_dev_acpi_reset - do a function level reset using _RST method
 + * @dev: device to reset
 + * @probe: if true, return 0 if device supports _RST
 + */
 +int pci_dev_acpi_reset(struct pci_dev *dev, bool probe)
 +{
 +      acpi_handle handle = ACPI_HANDLE(&dev->dev);
 +
 +      if (!handle || !acpi_has_method(handle, "_RST"))
 +              return -ENOTTY;
 +
 +      if (probe)
 +              return 0;
 +
 +      if (ACPI_FAILURE(acpi_evaluate_object(handle, "_RST", NULL, NULL))) {
 +              pci_warn(dev, "ACPI _RST failed\n");
 +              return -ENOTTY;
 +      }
 +
 +      return 0;
 +}
 +
 +static bool acpi_pci_power_manageable(struct pci_dev *dev)
 +{
 +      struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
 +
 +      if (!adev)
 +              return false;
 +      return acpi_device_power_manageable(adev);
 +}
 +
  static bool acpi_pci_bridge_d3(struct pci_dev *dev)
  {
 -      const struct fwnode_handle *fwnode;
 +      const union acpi_object *obj;
        struct acpi_device *adev;
 -      struct pci_dev *root;
 -      u8 val;
 +      struct pci_dev *rpdev;
  
        if (!dev->is_hotplug_bridge)
                return false;
  
        /* Assume D3 support if the bridge is power-manageable by ACPI. */
 -      adev = ACPI_COMPANION(&dev->dev);
 -      if (!adev && !pci_dev_is_added(dev)) {
 -              adev = acpi_pci_find_companion(&dev->dev);
 -              ACPI_COMPANION_SET(&dev->dev, adev);
 -      }
 -
 -      if (adev && acpi_device_power_manageable(adev))
 +      if (acpi_pci_power_manageable(dev))
                return true;
  
        /*
 -       * Look for a special _DSD property for the root port and if it
 -       * is set we know the hierarchy behind it supports D3 just fine.
 +       * The ACPI firmware will provide the device-specific properties through
 +       * _DSD configuration object. Look for the 'HotPlugSupportInD3' property
 +       * for the root port and if it is set we know the hierarchy behind it
 +       * supports D3 just fine.
         */
 -      root = pcie_find_root_port(dev);
 -      if (!root)
 +      rpdev = pcie_find_root_port(dev);
 +      if (!rpdev)
                return false;
  
 -      adev = ACPI_COMPANION(&root->dev);
 -      if (root == dev) {
 -              /*
 -               * It is possible that the ACPI companion is not yet bound
 -               * for the root port so look it up manually here.
 -               */
 -              if (!adev && !pci_dev_is_added(root))
 -                      adev = acpi_pci_find_companion(&root->dev);
 -      }
 -
 +      adev = ACPI_COMPANION(&rpdev->dev);
        if (!adev)
                return false;
  
 -      fwnode = acpi_fwnode_handle(adev);
 -      if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val))
 +      if (acpi_dev_get_property(adev, "HotPlugSupportInD3",
 +                                 ACPI_TYPE_INTEGER, &obj) < 0)
                return false;
  
 -      return val == 1;
 -}
 -
 -static bool acpi_pci_power_manageable(struct pci_dev *dev)
 -{
 -      struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
 -      return adev ? acpi_device_power_manageable(adev) : false;
 +      return obj->integer.value == 1;
  }
  
  static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
@@@ -1178,6 -1160,69 +1179,69 @@@ void acpi_pci_remove_bus(struct pci_bu
  }
  
  /* ACPI bus type */
+ static DECLARE_RWSEM(pci_acpi_companion_lookup_sem);
+ static struct acpi_device *(*pci_acpi_find_companion_hook)(struct pci_dev *);
+ /**
+  * pci_acpi_set_companion_lookup_hook - Set ACPI companion lookup callback.
+  * @func: ACPI companion lookup callback pointer or NULL.
+  *
+  * Set a special ACPI companion lookup callback for PCI devices whose companion
+  * objects in the ACPI namespace have _ADR with non-standard bus-device-function
+  * encodings.
+  *
+  * Return 0 on success or a negative error code on failure (in which case no
+  * changes are made).
+  *
+  * The caller is responsible for the appropriate ordering of the invocations of
+  * this function with respect to the enumeration of the PCI devices needing the
+  * callback installed by it.
+  */
+ int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *))
+ {
+       int ret;
+       if (!func)
+               return -EINVAL;
+       down_write(&pci_acpi_companion_lookup_sem);
+       if (pci_acpi_find_companion_hook) {
+               ret = -EBUSY;
+       } else {
+               pci_acpi_find_companion_hook = func;
+               ret = 0;
+       }
+       up_write(&pci_acpi_companion_lookup_sem);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(pci_acpi_set_companion_lookup_hook);
+ /**
+  * pci_acpi_clear_companion_lookup_hook - Clear ACPI companion lookup callback.
+  *
+  * Clear the special ACPI companion lookup callback previously set by
+  * pci_acpi_set_companion_lookup_hook().  Block until the last running instance
+  * of the callback returns before clearing it.
+  *
+  * The caller is responsible for the appropriate ordering of the invocations of
+  * this function with respect to the enumeration of the PCI devices needing the
+  * callback cleared by it.
+  */
+ void pci_acpi_clear_companion_lookup_hook(void)
+ {
+       down_write(&pci_acpi_companion_lookup_sem);
+       pci_acpi_find_companion_hook = NULL;
+       up_write(&pci_acpi_companion_lookup_sem);
+ }
+ EXPORT_SYMBOL_GPL(pci_acpi_clear_companion_lookup_hook);
  static struct acpi_device *acpi_pci_find_companion(struct device *dev)
  {
        struct pci_dev *pci_dev = to_pci_dev(dev);
        bool check_children;
        u64 addr;
  
+       down_read(&pci_acpi_companion_lookup_sem);
+       adev = pci_acpi_find_companion_hook ?
+               pci_acpi_find_companion_hook(pci_dev) : NULL;
+       up_read(&pci_acpi_companion_lookup_sem);
+       if (adev)
+               return adev;
        check_children = pci_is_bridge(pci_dev);
        /* Please ref to ACPI spec for the syntax of _ADR */
        addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);