diff options
Diffstat (limited to 'target/linux/mvebu/patches-5.4/017-PCI-aardvark-Improve-link-training.patch')
-rw-r--r-- | target/linux/mvebu/patches-5.4/017-PCI-aardvark-Improve-link-training.patch | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-5.4/017-PCI-aardvark-Improve-link-training.patch b/target/linux/mvebu/patches-5.4/017-PCI-aardvark-Improve-link-training.patch new file mode 100644 index 0000000000..2bf838432f --- /dev/null +++ b/target/linux/mvebu/patches-5.4/017-PCI-aardvark-Improve-link-training.patch @@ -0,0 +1,208 @@ +From 43fc679ced18006b12d918d7a8a4af392b7fbfe7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <marek.behun@nic.cz> +Date: Thu, 30 Apr 2020 10:06:17 +0200 +Subject: [PATCH] PCI: aardvark: Improve link training +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently the aardvark driver trains link in PCIe gen2 mode. This may +cause some buggy gen1 cards (such as Compex WLE900VX) to be unstable or +even not detected. Moreover when ASPM code tries to retrain link second +time, these cards may stop responding and link goes down. If gen1 is +used this does not happen. + +Unconditionally forcing gen1 is not a good solution since it may have +performance impact on gen2 cards. + +To overcome this, read 'max-link-speed' property (as defined in PCI +device tree bindings) and use this as max gen mode. Then iteratively try +link training at this mode or lower until successful. After successful +link training choose final controller gen based on Negotiated Link Speed +from Link Status register, which should match card speed. + +Link: https://lore.kernel.org/r/20200430080625.26070-5-pali@kernel.org +Tested-by: Tomasz Maciej Nowak <tmn505@gmail.com> +Signed-off-by: Pali Rohár <pali@kernel.org> +Signed-off-by: Marek Behún <marek.behun@nic.cz> +Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> +Reviewed-by: Rob Herring <robh@kernel.org> +Acked-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com> +--- + drivers/pci/controller/pci-aardvark.c | 114 ++++++++++++++++++++------ + 1 file changed, 89 insertions(+), 25 deletions(-) + +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -39,6 +39,7 @@ + #define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0 + #define PCIE_CORE_LINK_L0S_ENTRY BIT(0) + #define PCIE_CORE_LINK_TRAINING BIT(5) ++#define PCIE_CORE_LINK_SPEED_SHIFT 16 + #define PCIE_CORE_LINK_WIDTH_SHIFT 20 + #define PCIE_CORE_ERR_CAPCTL_REG 0x118 + #define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5) +@@ -201,6 +202,7 @@ struct advk_pcie { + struct mutex msi_used_lock; + u16 msi_msg; + int root_bus_nr; ++ int link_gen; + struct pci_bridge_emul bridge; + }; + +@@ -225,20 +227,16 @@ static int advk_pcie_link_up(struct advk + + static int advk_pcie_wait_for_link(struct advk_pcie *pcie) + { +- struct device *dev = &pcie->pdev->dev; + int retries; + + /* check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { +- if (advk_pcie_link_up(pcie)) { +- dev_info(dev, "link up\n"); ++ if (advk_pcie_link_up(pcie)) + return 0; +- } + + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + +- dev_err(dev, "link never came up\n"); + return -ETIMEDOUT; + } + +@@ -253,6 +251,85 @@ static void advk_pcie_wait_for_retrain(s + } + } + ++static int advk_pcie_train_at_gen(struct advk_pcie *pcie, int gen) ++{ ++ int ret, neg_gen; ++ u32 reg; ++ ++ /* Setup link speed */ ++ reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); ++ reg &= ~PCIE_GEN_SEL_MSK; ++ if (gen == 3) ++ reg |= SPEED_GEN_3; ++ else if (gen == 2) ++ reg |= SPEED_GEN_2; ++ else ++ reg |= SPEED_GEN_1; ++ advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); ++ ++ /* ++ * Enable link training. This is not needed in every call to this ++ * function, just once suffices, but it does not break anything either. ++ */ ++ reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); ++ reg |= LINK_TRAINING_EN; ++ advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); ++ ++ /* ++ * Start link training immediately after enabling it. ++ * This solves problems for some buggy cards. ++ */ ++ reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); ++ reg |= PCIE_CORE_LINK_TRAINING; ++ advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); ++ ++ ret = advk_pcie_wait_for_link(pcie); ++ if (ret) ++ return ret; ++ ++ reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); ++ neg_gen = (reg >> PCIE_CORE_LINK_SPEED_SHIFT) & 0xf; ++ ++ return neg_gen; ++} ++ ++static void advk_pcie_train_link(struct advk_pcie *pcie) ++{ ++ struct device *dev = &pcie->pdev->dev; ++ int neg_gen = -1, gen; ++ ++ /* ++ * Try link training at link gen specified by device tree property ++ * 'max-link-speed'. If this fails, iteratively train at lower gen. ++ */ ++ for (gen = pcie->link_gen; gen > 0; --gen) { ++ neg_gen = advk_pcie_train_at_gen(pcie, gen); ++ if (neg_gen > 0) ++ break; ++ } ++ ++ if (neg_gen < 0) ++ goto err; ++ ++ /* ++ * After successful training if negotiated gen is lower than requested, ++ * train again on negotiated gen. This solves some stability issues for ++ * some buggy gen1 cards. ++ */ ++ if (neg_gen < gen) { ++ gen = neg_gen; ++ neg_gen = advk_pcie_train_at_gen(pcie, gen); ++ } ++ ++ if (neg_gen == gen) { ++ dev_info(dev, "link up at gen %i\n", gen); ++ return; ++ } ++ ++err: ++ dev_err(dev, "link never came up\n"); ++} ++ + static void advk_pcie_setup_hw(struct advk_pcie *pcie) + { + u32 reg; +@@ -288,12 +365,6 @@ static void advk_pcie_setup_hw(struct ad + PCIE_CORE_CTRL2_TD_ENABLE; + advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG); + +- /* Set GEN2 */ +- reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); +- reg &= ~PCIE_GEN_SEL_MSK; +- reg |= SPEED_GEN_2; +- advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); +- + /* Set lane X1 */ + reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); + reg &= ~LANE_CNT_MSK; +@@ -341,20 +412,7 @@ static void advk_pcie_setup_hw(struct ad + */ + msleep(PCI_PM_D3COLD_WAIT); + +- /* Enable link training */ +- reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG); +- reg |= LINK_TRAINING_EN; +- advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG); +- +- /* +- * Start link training immediately after enabling it. +- * This solves problems for some buggy cards. +- */ +- reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG); +- reg |= PCIE_CORE_LINK_TRAINING; +- advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG); +- +- advk_pcie_wait_for_link(pcie); ++ advk_pcie_train_link(pcie); + + reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); + reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | +@@ -1035,6 +1093,12 @@ static int advk_pcie_probe(struct platfo + return ret; + } + ++ ret = of_pci_get_max_link_speed(dev->of_node); ++ if (ret <= 0 || ret > 3) ++ pcie->link_gen = 3; ++ else ++ pcie->link_gen = ret; ++ + advk_pcie_setup_hw(pcie); + + ret = advk_sw_pci_bridge_init(pcie); |