Merge tag 'pci-v6.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
[platform/kernel/linux-starfive.git] / drivers / pci / controller / dwc / pcie-designware-host.c
index 9952057..a7170fd 100644 (file)
@@ -8,6 +8,7 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/iopoll.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
@@ -16,6 +17,7 @@
 #include <linux/pci_regs.h>
 #include <linux/platform_device.h>
 
+#include "../../pci.h"
 #include "pcie-designware.h"
 
 static struct pci_ops dw_pcie_ops;
@@ -807,3 +809,72 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
        return 0;
 }
 EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
+
+int dw_pcie_suspend_noirq(struct dw_pcie *pci)
+{
+       u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       u32 val;
+       int ret;
+
+       /*
+        * If L1SS is supported, then do not put the link into L2 as some
+        * devices such as NVMe expect low resume latency.
+        */
+       if (dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKCTL) & PCI_EXP_LNKCTL_ASPM_L1)
+               return 0;
+
+       if (dw_pcie_get_ltssm(pci) <= DW_PCIE_LTSSM_DETECT_ACT)
+               return 0;
+
+       if (!pci->pp.ops->pme_turn_off)
+               return 0;
+
+       pci->pp.ops->pme_turn_off(&pci->pp);
+
+       ret = read_poll_timeout(dw_pcie_get_ltssm, val, val == DW_PCIE_LTSSM_L2_IDLE,
+                               PCIE_PME_TO_L2_TIMEOUT_US/10,
+                               PCIE_PME_TO_L2_TIMEOUT_US, false, pci);
+       if (ret) {
+               dev_err(pci->dev, "Timeout waiting for L2 entry! LTSSM: 0x%x\n", val);
+               return ret;
+       }
+
+       if (pci->pp.ops->host_deinit)
+               pci->pp.ops->host_deinit(&pci->pp);
+
+       pci->suspended = true;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_pcie_suspend_noirq);
+
+int dw_pcie_resume_noirq(struct dw_pcie *pci)
+{
+       int ret;
+
+       if (!pci->suspended)
+               return 0;
+
+       pci->suspended = false;
+
+       if (pci->pp.ops->host_init) {
+               ret = pci->pp.ops->host_init(&pci->pp);
+               if (ret) {
+                       dev_err(pci->dev, "Host init failed: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       dw_pcie_setup_rc(&pci->pp);
+
+       ret = dw_pcie_start_link(pci);
+       if (ret)
+               return ret;
+
+       ret = dw_pcie_wait_for_link(pci);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq);