summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci.c25
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/pcie/ptm.c17
3 files changed, 43 insertions, 1 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 12ba6351c05b..71dd5d7cbded 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -2608,12 +2608,24 @@ int pci_prepare_to_sleep(struct pci_dev *dev)
if (target_state == PCI_POWER_ERROR)
return -EIO;
+ /*
+ * There are systems (for example, Intel mobile chips since Coffee
+ * Lake) where the power drawn while suspended can be significantly
+ * reduced by disabling PTM on PCIe root ports as this allows the
+ * port to enter a lower-power PM state and the SoC to reach a
+ * lower-power idle state as a whole.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_disable_ptm(dev);
+
pci_enable_wake(dev, target_state, wakeup);
error = pci_set_power_state(dev, target_state);
- if (error)
+ if (error) {
pci_enable_wake(dev, target_state, false);
+ pci_restore_ptm_state(dev);
+ }
return error;
}
@@ -2651,12 +2663,23 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
dev->runtime_d3cold = target_state == PCI_D3cold;
+ /*
+ * There are systems (for example, Intel mobile chips since Coffee
+ * Lake) where the power drawn while suspended can be significantly
+ * reduced by disabling PTM on PCIe root ports as this allows the
+ * port to enter a lower-power PM state and the SoC to reach a
+ * lower-power idle state as a whole.
+ */
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
+ pci_disable_ptm(dev);
+
__pci_enable_wake(dev, target_state, pci_dev_run_wake(dev));
error = pci_set_power_state(dev, target_state);
if (error) {
pci_enable_wake(dev, target_state, false);
+ pci_restore_ptm_state(dev);
dev->runtime_d3cold = false;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index e0f9e7259e47..f38ba693c69a 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -519,9 +519,11 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
#ifdef CONFIG_PCIE_PTM
void pci_save_ptm_state(struct pci_dev *dev);
void pci_restore_ptm_state(struct pci_dev *dev);
+void pci_disable_ptm(struct pci_dev *dev);
#else
static inline void pci_save_ptm_state(struct pci_dev *dev) { }
static inline void pci_restore_ptm_state(struct pci_dev *dev) { }
+static inline void pci_disable_ptm(struct pci_dev *dev) { }
#endif
unsigned long pci_cardbus_resource_alignment(struct resource *);
diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c
index 6b24a1c9327a..95d4eef2c9e8 100644
--- a/drivers/pci/pcie/ptm.c
+++ b/drivers/pci/pcie/ptm.c
@@ -29,6 +29,23 @@ static void pci_ptm_info(struct pci_dev *dev)
dev->ptm_root ? " (root)" : "", clock_desc);
}
+void pci_disable_ptm(struct pci_dev *dev)
+{
+ int ptm;
+ u16 ctrl;
+
+ if (!pci_is_pcie(dev))
+ return;
+
+ ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
+ if (!ptm)
+ return;
+
+ pci_read_config_word(dev, ptm + PCI_PTM_CTRL, &ctrl);
+ ctrl &= ~(PCI_PTM_CTRL_ENABLE | PCI_PTM_CTRL_ROOT);
+ pci_write_config_word(dev, ptm + PCI_PTM_CTRL, ctrl);
+}
+
void pci_save_ptm_state(struct pci_dev *dev)
{
int ptm;