@@ -45,6 +45,9 @@ static const guid_t prp_guids[] = {
/* Thunderbolt GUID for WAKE_SUPPORTED: 6c501103-c189-4296-ba72-9bf5a26ebe5d */
GUID_INIT(0x6c501103, 0xc189, 0x4296,
0xba, 0x72, 0x9b, 0xf5, 0xa2, 0x6e, 0xbe, 0x5d),
+ /* D3 Support for storage devivce: 5025030f-842f-4ab4-a561-99a5189762d0 */
+ GUID_INIT(0x5025030f, 0x842f, 0x4ab4,
+ 0xa5, 0x61, 0x99, 0xa5, 0x18, 0x97, 0x62, 0xd0),
};
/* ACPI _DSD data subnodes GUID: dbb8e3e6-5886-4ba6-8795-1319f52a966b */
@@ -972,6 +972,52 @@ static bool acpi_pci_bridge_d3(struct pci_dev *dev)
return val == 1;
}
+static bool acpi_pci_storage_d3(struct pci_dev *dev)
+{
+ const struct fwnode_handle *fwnode;
+ struct acpi_device *adev;
+ struct pci_dev *root;
+ acpi_handle handle;
+ acpi_status status;
+ u8 val;
+
+ /*
+ * Look for _DSD property specifying that the storage device on
+ * the port must use D3 to support deep platform power savings during
+ * suspend-to-idle
+ */
+ root = pci_find_pcie_root_port(dev);
+ if (!root)
+ 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);
+ }
+
+ if (!adev)
+ return false;
+
+ status = acpi_get_handle(adev->handle, "PXSX", &handle);
+ if (ACPI_FAILURE(status))
+ return false;
+
+ adev = acpi_bus_get_acpi_device(handle);
+ if (!adev)
+ return false;
+
+ fwnode = acpi_fwnode_handle(adev);
+ if (!fwnode_property_read_u8(fwnode, "StorageD3Enable", &val))
+ return val == 1;
+
+ return false;
+}
+
static bool acpi_pci_power_manageable(struct pci_dev *dev)
{
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
@@ -1100,6 +1146,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
.bridge_d3 = acpi_pci_bridge_d3,
+ .storage_d3 = acpi_pci_storage_d3,
.is_manageable = acpi_pci_power_manageable,
.set_state = acpi_pci_set_power_state,
.get_state = acpi_pci_get_power_state,
@@ -871,6 +871,11 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false;
}
+static inline bool platform_pci_storage_d3(struct pci_dev *dev)
+{
+ return pci_platform_pm->storage_d3(dev);
+}
+
/**
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
* given PCI device
@@ -2903,6 +2908,7 @@ void pci_pm_init(struct pci_dev *dev)
dev->d3_delay = PCI_PM_D3_WAIT;
dev->d3cold_delay = PCI_PM_D3COLD_WAIT;
dev->bridge_d3 = pci_bridge_d3_possible(dev);
+ dev->storage_d3 = platform_pci_storage_d3(dev);
dev->d3cold_allowed = true;
dev->d1_support = false;
@@ -53,6 +53,9 @@ int pci_bus_error_reset(struct pci_dev *dev);
*
* @bridge_d3: Does the bridge allow entering into D3
*
+ * @storage_d3: 'true' if the bridge contains a storage device that must use D3
+ * to support platform s2idle
+ *
* @is_manageable: returns 'true' if given device is power manageable by the
* platform firmware
*
@@ -77,6 +80,7 @@ int pci_bus_error_reset(struct pci_dev *dev);
*/
struct pci_platform_pm_ops {
bool (*bridge_d3)(struct pci_dev *dev);
+ bool (*storage_d3)(struct pci_dev *dev);
bool (*is_manageable)(struct pci_dev *dev);
int (*set_state)(struct pci_dev *dev, pci_power_t state);
pci_power_t (*get_state)(struct pci_dev *dev);
@@ -346,6 +346,7 @@ struct pci_dev {
unsigned int no_d1d2:1; /* D1 and D2 are forbidden */
unsigned int no_d3cold:1; /* D3cold is forbidden */
unsigned int bridge_d3:1; /* Allow D3 for bridge */
+ unsigned int storage_d3:1; /* Storage dev must use D3 for s2idle */
unsigned int d3cold_allowed:1; /* D3cold is allowed by user */
unsigned int mmio_always_on:1; /* Disallow turning off io/mem
decoding during BAR sizing */