Merge branch 'pci/ptm'
authorBjorn Helgaas <bhelgaas@google.com>
Tue, 15 Dec 2020 21:11:09 +0000 (15:11 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 15 Dec 2020 21:11:09 +0000 (15:11 -0600)
- Save/restore Precision Time Measurement Capability for suspend/resume
  (David E. Box)

- Disable PTM during suspend to save power (David E. Box)

* pci/ptm:
  PCI: Disable PTM during suspend to save power
  PCI/PTM: Save/restore Precision Time Measurement Capability for suspend/resume

drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/pcie/ptm.c

index 6cdd7e6..b9fecc2 100644 (file)
@@ -1561,6 +1561,7 @@ int pci_save_state(struct pci_dev *dev)
        pci_save_aspm_l1ss_state(dev);
        pci_save_dpc_state(dev);
        pci_save_aer_state(dev);
+       pci_save_ptm_state(dev);
        return pci_save_vc_state(dev);
 }
 EXPORT_SYMBOL(pci_save_state);
@@ -1673,6 +1674,7 @@ void pci_restore_state(struct pci_dev *dev)
        pci_restore_vc_state(dev);
        pci_restore_rebar_state(dev);
        pci_restore_dpc_state(dev);
+       pci_restore_ptm_state(dev);
 
        pci_aer_clear_status(dev);
        pci_restore_aer_state(dev);
@@ -2602,12 +2604,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;
 }
@@ -2645,12 +2659,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;
        }
 
index bb5df3f..5c59365 100644 (file)
@@ -524,6 +524,16 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
 
 #endif /* CONFIG_PCI_IOV */
 
+#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 *);
 
 static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
index 357a454..95d4eef 100644 (file)
@@ -29,6 +29,64 @@ 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;
+       struct pci_cap_saved_state *save_state;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
+       if (!ptm)
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM);
+       if (!save_state) {
+               pci_err(dev, "no suspend buffer for PTM\n");
+               return;
+       }
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_read_config_word(dev, ptm + PCI_PTM_CTRL, cap);
+}
+
+void pci_restore_ptm_state(struct pci_dev *dev)
+{
+       struct pci_cap_saved_state *save_state;
+       int ptm;
+       u16 *cap;
+
+       if (!pci_is_pcie(dev))
+               return;
+
+       save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_PTM);
+       ptm = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PTM);
+       if (!save_state || !ptm)
+               return;
+
+       cap = (u16 *)&save_state->cap.data[0];
+       pci_write_config_word(dev, ptm + PCI_PTM_CTRL, *cap);
+}
+
 void pci_ptm_init(struct pci_dev *dev)
 {
        int pos;
@@ -65,6 +123,8 @@ void pci_ptm_init(struct pci_dev *dev)
        if (!pos)
                return;
 
+       pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u16));
+
        pci_read_config_dword(dev, pos + PCI_PTM_CAP, &cap);
        local_clock = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;