diff mbox series

[5/8] PCI: aardvark: Set final controller speed based on negotiated link speed

Message ID 20200415160348.1146-1-pali@kernel.org
State New
Headers show
Series PCI: aardvark: Fix support for Turris MOX and Compex wifi cards | expand

Commit Message

Pali Rohár April 15, 2020, 4:03 p.m. UTC
Some Compex WLE900VX gen1 cards are unstable or even not detected when
aardvark PCI controller speed is set to gen2. Moreover when ASPM code tries
to retrain link second time then these cards stop responding and link goes
down. If aardvark PCI controller is set to gen1 then these cards work fine
without any problem.

Unconditionally forcing aardvark controller to gen1 speed (either via DT
property max-link-speed or hardcoded value in driver itself) is not a
solution as it would have performance impact for fast gen2 sata cards.

To overcome this problem, try all 3 possible speeds (gen3, gen2, gen1)
supported by aardvark PCI controller with respect to max-link-speed setting
and after successful link training choose final controller speed based on
Negotiated Link Speed from Link Status register, which should match card
speed.

I tested this change with Compex cards WLE200NX (pcie 1.0, gen1, ath9k),
WLE900VX (pcie 1.1, gen1, ath10k) and WLE1216V5-20 (pcie 2.0, gen2, ath10k)
on Turris MOX. Tomasz Maciej Nowak tested JJPlus JWX6051 (ath9k), Intel
622ANHMW, MT76 U7612E-H1 and Compex WLE1216V2-20 cards on Espressobin.

Signed-off-by: Pali Rohár <pali@kernel.org>
---
 drivers/pci/controller/pci-aardvark.c | 35 +++++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

Comments

Marek Behún April 19, 2020, 3:17 a.m. UTC | #1
On Wed, 15 Apr 2020 18:03:45 +0200
Pali Rohár <pali@kernel.org> wrote:

> Some Compex WLE900VX gen1 cards are unstable or even not detected when
> aardvark PCI controller speed is set to gen2. Moreover when ASPM code tries
> to retrain link second time then these cards stop responding and link goes
> down. If aardvark PCI controller is set to gen1 then these cards work fine
> without any problem.
> 
> Unconditionally forcing aardvark controller to gen1 speed (either via DT
> property max-link-speed or hardcoded value in driver itself) is not a
> solution as it would have performance impact for fast gen2 sata cards.
> 
> To overcome this problem, try all 3 possible speeds (gen3, gen2, gen1)
> supported by aardvark PCI controller with respect to max-link-speed setting
> and after successful link training choose final controller speed based on
> Negotiated Link Speed from Link Status register, which should match card
> speed.
> 
> I tested this change with Compex cards WLE200NX (pcie 1.0, gen1, ath9k),
> WLE900VX (pcie 1.1, gen1, ath10k) and WLE1216V5-20 (pcie 2.0, gen2, ath10k)
> on Turris MOX. Tomasz Maciej Nowak tested JJPlus JWX6051 (ath9k), Intel
> 622ANHMW, MT76 U7612E-H1 and Compex WLE1216V2-20 cards on Espressobin.
> 
> Signed-off-by: Pali Rohár <pali@kernel.org>
> ---
>  drivers/pci/controller/pci-aardvark.c | 35 +++++++++++++++++++++++++--
>  1 file changed, 33 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
> index 02c69fc9aadf..a83bbc86e428 100644
> --- a/drivers/pci/controller/pci-aardvark.c
> +++ b/drivers/pci/controller/pci-aardvark.c
> @@ -40,6 +40,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)
> @@ -276,7 +277,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
>  {
>  	struct device *dev = &pcie->pdev->dev;
>  	struct device_node *node = dev->of_node;
> -	int max_link_speed;
> +	int max_link_speed, neg_link_speed;
>  	u32 reg;
>  
>  	/* Set to Direct mode */
> @@ -378,7 +379,37 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
>  	reg |= PCIE_CORE_LINK_TRAINING;
>  	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
>  
> -	advk_pcie_wait_for_link(pcie);
> +	do {
> +		if (advk_pcie_wait_for_link(pcie) < 0) {
> +			max_link_speed--;
> +		} else {
> +			reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
> +
> +			neg_link_speed =
> +				(reg >> PCIE_CORE_LINK_SPEED_SHIFT) & 0xf;
> +			dev_info(dev, "negotiated link speed %d\n",
> +				neg_link_speed);
> +
> +			if (neg_link_speed == max_link_speed)
> +				break;
> +
> +			if (neg_link_speed > 0 && neg_link_speed <= 3)
> +				max_link_speed = neg_link_speed;
> +			else
> +				max_link_speed--;
> +		}
> +
> +		if (max_link_speed == 0)
> +			break;
> +
> +		/* Set new decreased max link speed */
> +		advk_pcie_setup_link_speed(pcie, max_link_speed);
> +
> +		/* And start link training again */
> +		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);
> +	} while (1);
>  

This do {} while(1) should be a for cycle iterating the max_link_speed
variable, and probably in a separate function
advk_pcie_negotiate_link_speed.

A3700, which is the only SOC to use this driver, does not support PCIe
gen 3, so this shouldn't be tried, at least if
.compatible == "marvell,armada-3700-pcie".

Marek
diff mbox series

Patch

diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index 02c69fc9aadf..a83bbc86e428 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -40,6 +40,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)
@@ -276,7 +277,7 @@  static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 {
 	struct device *dev = &pcie->pdev->dev;
 	struct device_node *node = dev->of_node;
-	int max_link_speed;
+	int max_link_speed, neg_link_speed;
 	u32 reg;
 
 	/* Set to Direct mode */
@@ -378,7 +379,37 @@  static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 	reg |= PCIE_CORE_LINK_TRAINING;
 	advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
 
-	advk_pcie_wait_for_link(pcie);
+	do {
+		if (advk_pcie_wait_for_link(pcie) < 0) {
+			max_link_speed--;
+		} else {
+			reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
+
+			neg_link_speed =
+				(reg >> PCIE_CORE_LINK_SPEED_SHIFT) & 0xf;
+			dev_info(dev, "negotiated link speed %d\n",
+				neg_link_speed);
+
+			if (neg_link_speed == max_link_speed)
+				break;
+
+			if (neg_link_speed > 0 && neg_link_speed <= 3)
+				max_link_speed = neg_link_speed;
+			else
+				max_link_speed--;
+		}
+
+		if (max_link_speed == 0)
+			break;
+
+		/* Set new decreased max link speed */
+		advk_pcie_setup_link_speed(pcie, max_link_speed);
+
+		/* And start link training again */
+		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);
+	} while (1);
 
 	reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
 	reg |= PCIE_CORE_CMD_MEM_ACCESS_EN |