On 30.10.2017 06:18, Manikanta Maddireddy wrote:
Tegra124, 132, 210 and 186 supports Gen2 link speed. After the link is up
s/supports/support/
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@xxxxxxxxxx>
---
V2:
* Fixed alignment issue
drivers/pci/host/pci-tegra.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c
index 2c64eb6cc3cc..9f6d331c3571 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 HZ
I guess this originates in the pcie-altera driver or some common
ancestor, but it doesn't make much sense to me why this is bound to HZ.
It seems to me it should be just some constant.
+
struct tegra_msi {
struct msi_controller chip;
DECLARE_BITMAP(used, INT_PCI_MSI_NR);
@@ -2133,6 +2135,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;
+ unsigned long start_jiffies;
+ 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);
+
+ start_jiffies = jiffies;
+ for (;;) {
+ pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &val);
+ if (!(val & PCI_EXP_LNKSTA_LT))
+ break;
+ if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
+ break;
+ usleep_range(2000, 3000);
+ }
Please use the ktime series of functions instead of using jiffies
directly. E.g.
ktime_t deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT);
if (ktime_after(ktime_get(), deadline))
break;
(and replace LINK_RETRAIN_TIMEOUT with a timeout in microseconds)
You can take a look at the macros in linux/iopoll.h which do this but
for iomem access.
+
+ 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,
@@ -2334,6 +2372,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));
@@ -2399,6 +2438,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)
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html