From 4ca55c87f91c75f2c048046cdd40ed56e210b69e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 3 Sep 2024 11:26:09 +0800 Subject: [PATCH] PCI: rockchip: dw: Add retrain link support Speed change is set via dw_pcie_setup_rc(), so if both of links support gen2 or gen3, auto speed change will happen. However, if it's not, provide a manual speed change for EP function driver. Signed-off-by: Shawn Lin Change-Id: Ib0dc765452aef0723968c5d48b5b44de24ca141e --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 31 +++++++++++++++++++ include/linux/aspm_ext.h | 1 + 2 files changed, 32 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 1729a0bce2dc..d8f4a3c2c811 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -304,6 +304,34 @@ static int rk_pcie_disable_power(struct rk_pcie *rk_pcie) return ret; } +static void rk_pcie_retrain(struct dw_pcie *pci) +{ + u32 pcie_cap_off; + u16 lnk_stat, lnk_ctl; + int ret; + + /* + * Set retrain bit if current speed is 2.5 GB/s, + * but the PCIe root port support is > 2.5 GB/s. + */ + if (pci->link_gen < 2) + return; + + dev_info(pci->dev, "Retrain link..\n"); + pcie_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + lnk_stat = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = dw_pcie_readw_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + dw_pcie_writew_dbi(pci, pcie_cap_off + PCI_EXP_LNKCTL, lnk_ctl); + + ret = readw_poll_timeout(pci->dbi_base + pcie_cap_off + PCI_EXP_LNKSTA, + lnk_stat, ((lnk_stat & PCI_EXP_LNKSTA_LT) == 0), 1000, HZ); + if (ret) + dev_err(pci->dev, "Retrain link timeout\n"); + } +} + static int rk_pcie_establish_link(struct dw_pcie *pci) { int retries, power; @@ -1684,6 +1712,9 @@ int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm rockchip_dw_pcie_suspend(rk_pcie->pci->dev); rockchip_dw_pcie_resume(rk_pcie->pci->dev); break; + case ROCKCHIP_PCIE_PM_RETRAIN_LINK: + rk_pcie_retrain(pci); + break; default: dev_err(rk_pcie->pci->dev, "%s flag=%d invalid\n", __func__, flag); return -EINVAL; diff --git a/include/linux/aspm_ext.h b/include/linux/aspm_ext.h index c4580dfc80f7..140de4389796 100644 --- a/include/linux/aspm_ext.h +++ b/include/linux/aspm_ext.h @@ -17,6 +17,7 @@ static inline bool pcie_aspm_ext_is_in_l1sub_state(struct pci_dev *pdev) { retur enum rockchip_pcie_pm_ctrl_flag { ROCKCHIP_PCIE_PM_CTRL_RESET = 1, + ROCKCHIP_PCIE_PM_RETRAIN_LINK = 2, }; int rockchip_dw_pcie_pm_ctrl_for_user(struct pci_dev *dev, enum rockchip_pcie_pm_ctrl_flag flag);