diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index 847918e48ed7..4e3ffb446ef3 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -2274,6 +2274,19 @@ static int msm_pcie_is_link_up(struct msm_pcie_dev_t *dev) PCIE20_CAP_LINKCTRLSTATUS) & BIT(29); } +static bool msm_pcie_check_ltssm_state(struct msm_pcie_dev_t *dev, u32 state) +{ + u32 ltssm; + + ltssm = readl_relaxed(dev->parf + PCIE20_PARF_LTSSM) & + MSM_PCIE_LTSSM_MASK; + + if (ltssm == state) + return true; + + return false; +} + /** * msm_pcie_iatu_config - configure outbound address translation region * @dev: root commpex @@ -5770,9 +5783,9 @@ int msm_pcie_prevent_l1(struct pci_dev *pci_dev) PCI_EXP_LNKCTL_ASPM_L1, 0); msm_pcie_write_mask(pcie_dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(5)); - /* confirm link is in L0 */ - while (((readl_relaxed(pcie_dev->parf + PCIE20_PARF_LTSSM) & - MSM_PCIE_LTSSM_MASK)) != MSM_PCIE_LTSSM_L0) { + /* confirm link is in L0/L0s */ + while (!msm_pcie_check_ltssm_state(pcie_dev, MSM_PCIE_LTSSM_L0) && + !msm_pcie_check_ltssm_state(pcie_dev, MSM_PCIE_LTSSM_L0S)) { if (unlikely(cnt++ >= cnt_max)) { PCIE_ERR(pcie_dev, "PCIe: RC%d: %02x:%02x.%01x: failed to transition to L0\n", @@ -5800,6 +5813,24 @@ err: } EXPORT_SYMBOL(msm_pcie_prevent_l1); +static int msm_pcie_read_devid_all(struct pci_dev *pdev, void *dev) +{ + u16 device_id; + + pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + + return 0; +} + +static void msm_pcie_poll_for_l0_from_l0s(struct msm_pcie_dev_t *dev) +{ + if (!dev->l0s_supported) + return; + + while (!msm_pcie_check_ltssm_state(dev, MSM_PCIE_LTSSM_L0)) + pci_walk_bus(dev->dev->bus, msm_pcie_read_devid_all, dev); +} + int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed, u16 target_link_width) { @@ -5873,6 +5904,11 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed, if (ret) return ret; + msm_pcie_config_l0s_disable_all(pcie_dev, root_pci_dev->bus); + + /* in case link is already in L0s bring link back to L0 */ + msm_pcie_poll_for_l0_from_l0s(pcie_dev); + if (target_link_speed > current_link_speed) msm_pcie_scale_link_bandwidth(pcie_dev, target_link_speed); @@ -5897,6 +5933,7 @@ int msm_pcie_set_link_bandwidth(struct pci_dev *pci_dev, u16 target_link_speed, PCIE_DBG(pcie_dev, "PCIe: RC%d: successfully switched link bandwidth\n", pcie_dev->rc_idx); out: + msm_pcie_config_l0s_enable_all(pcie_dev); msm_pcie_allow_l1(root_pci_dev); return ret;