[V3,03/12] PCI: tegra: Retrain link for Gen2 speed

Message ID 1509371843-22931-4-git-send-email-mmaddireddy@nvidia.com
State New
Headers show
Series
  • Enable Tegra root port features and apply SW fixups
Related show

Commit Message

Manikanta Maddireddy Oct. 30, 2017, 1:57 p.m.
Tegra124, 132, 210 and 186 support Gen2 link speed. After the link is up
in Gen1, set target link speed as Gen2 and retrain link. Link switches to
Gen2 speed if Gen2 capable end point is connected, else link stays in Gen1.

Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
V3:
* Corrected commit log
* Replaced jiffies with ktime
V2:
* no change in this patch

 drivers/pci/host/pci-tegra.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

Patch

diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 068510b40c1a..ed5e8acfdc32 100644
--- a/drivers/pci/host/pci-tegra.c
+++ b/drivers/pci/host/pci-tegra.c
@@ -232,6 +232,8 @@ 
 #define PADS_REFCLK_CFG_PREDI_SHIFT		8  /* 11:8 */
 #define PADS_REFCLK_CFG_DRVI_SHIFT		12 /* 15:12 */
 
+#define LINK_RETRAIN_TIMEOUT 100000
+
 struct tegra_msi {
 	struct msi_controller chip;
 	DECLARE_BITMAP(used, INT_PCI_MSI_NR);
@@ -2134,6 +2136,42 @@  static void tegra_pcie_enable_ports(struct tegra_pcie *pcie)
 	}
 }
 
+static void tegra_pcie_change_link_speed(struct tegra_pcie *pcie,
+					 struct pci_dev *pci_dev)
+{
+	struct device *dev = pcie->dev;
+	ktime_t deadline;
+	unsigned short val;
+
+	/* Skip if the current device is not a root port */
+	if (pci_pcie_type(pci_dev) != PCI_EXP_TYPE_ROOT_PORT)
+		return;
+
+	pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL2, &val);
+	val &= ~PCI_EXP_LNKSTA_CLS;
+	val |= PCI_EXP_LNKSTA_CLS_5_0GB;
+	pcie_capability_write_word(pci_dev, PCI_EXP_LNKCTL2, val);
+
+	/* Retrain the link */
+	pcie_capability_read_word(pci_dev, PCI_EXP_LNKCTL, &val);
+	val |= PCI_EXP_LNKCTL_RL;
+	pcie_capability_write_word(pci_dev, PCI_EXP_LNKCTL, val);
+
+	deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT);
+	for (;;) {
+		pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &val);
+		if (!(val & PCI_EXP_LNKSTA_LT))
+			break;
+		if (ktime_after(ktime_get(), deadline))
+			break;
+		usleep_range(2000, 3000);
+	}
+
+	if (val & PCI_EXP_LNKSTA_LT)
+		dev_err(dev, "link retrain of PCIe slot %u failed\n",
+			PCI_SLOT(pci_dev->devfn));
+}
+
 static const struct tegra_pcie_soc tegra20_pcie = {
 	.num_ports = 2,
 	.msi_base_shift = 0,
@@ -2335,6 +2373,7 @@  static int tegra_pcie_probe(struct platform_device *pdev)
 	struct pci_host_bridge *host;
 	struct tegra_pcie *pcie;
 	struct pci_bus *child;
+	struct pci_dev *pci_dev = NULL;
 	int err;
 
 	host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
@@ -2400,6 +2439,9 @@  static int tegra_pcie_probe(struct platform_device *pdev)
 
 	pci_bus_add_devices(host->bus);
 
+	for_each_pci_dev(pci_dev)
+		tegra_pcie_change_link_speed(pcie, pci_dev);
+
 	if (IS_ENABLED(CONFIG_DEBUG_FS)) {
 		err = tegra_pcie_debugfs_init(pcie);
 		if (err < 0)