diff options
-rw-r--r-- | drivers/pci/pci.c | 25 | ||||
-rw-r--r-- | drivers/pci/pci.h | 2 | ||||
-rw-r--r-- | drivers/pci/pcie/ptm.c | 17 |
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; |