openwrt/target/linux/ipq806x/patches-4.9/0071-3-PCI-qcom-Fixed-IPQ806x-PCIE-init-changes.patch
Pavel Kubelun 36a96a4493 ipq806x: fix wireless regression
In current state there's huge regression on ipq806x target that causes the device to transmit broken/malformed frames that are not corrected/detected by error control mechanisms and other less severe issues.
https://bugs.lede-project.org/index.php?do=details&task_id=1197

This finally had been narrowed down to patch 0071-pcie-qcom-fixes.patch

Meanwhile QSDK contains a handful of commits that add support for ipq806x to upstream qcom pcie driver
https://source.codeaurora.org/quic/qsdk/oss/kernel/linux-msm/log/drivers/pci/host/pcie-qcom.c?h=eggplant
Unfortunately qca developers do not bother to push it upstream.

Using those commits instead of lede 0071 patch fixes mentioned issue and probably many others as it seems that corrupted data has been originating within pcie misconfiguration.

Fixes: FS#1197 and probably others
Signed-off-by: Pavel Kubelun <be.dissent@gmail.com>
2018-01-17 11:02:05 +01:00

128 lines
3.8 KiB
Diff

From eddd13215d0f2b549ebc5f0e8796d5b1231f90a0 Mon Sep 17 00:00:00 2001
From: Sham Muthayyan <smuthayy@codeaurora.org>
Date: Tue, 19 Jul 2016 19:58:22 +0530
Subject: PCI: qcom: Fixed IPQ806x PCIE init changes
Change-Id: Ic319b1aec27a47809284759f8fcb6a8815b7cf7e
Signed-off-by: Sham Muthayyan <smuthayy@codeaurora.org>
---
drivers/pci/host/pcie-qcom.c | 62 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 53 insertions(+), 9 deletions(-)
--- a/drivers/pci/host/pcie-qcom.c
+++ b/drivers/pci/host/pcie-qcom.c
@@ -37,7 +37,13 @@
#include "pcie-designware.h"
#define PCIE20_PARF_PHY_CTRL 0x40
+#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK (0x1f << 16)
+#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) (x << 16)
+
#define PCIE20_PARF_PHY_REFCLK 0x4C
+#define REF_SSP_EN BIT(16)
+#define REF_USE_PAD BIT(12)
+
#define PCIE20_PARF_DBI_BASE_ADDR 0x168
#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178
@@ -48,6 +54,18 @@
#define PCIE20_CAP 0x70
#define PERST_DELAY_US 1000
+/* PARF registers */
+#define PCIE20_PARF_PCS_DEEMPH 0x34
+#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16)
+#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8)
+#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0)
+
+#define PCIE20_PARF_PCS_SWING 0x38
+#define PCS_SWING_TX_SWING_FULL(x) (x << 8)
+#define PCS_SWING_TX_SWING_LOW(x) (x << 0)
+
+#define PCIE20_PARF_CONFIG_BITS 0x50
+#define PHY_RX0_EQ(x) (x << 24)
struct qcom_pcie_resources_v0 {
struct clk *iface_clk;
@@ -64,6 +82,7 @@ struct qcom_pcie_resources_v0 {
struct regulator *vdda;
struct regulator *vdda_phy;
struct regulator *vdda_refclk;
+ uint8_t phy_tx0_term_offset;
};
struct qcom_pcie_resources_v1 {
@@ -100,6 +119,16 @@ struct qcom_pcie {
#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp)
+static inline void
+writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask)
+{
+ u32 val = readl(addr);
+
+ val &= ~clear_mask;
+ val |= set_mask;
+ writel(val, addr);
+}
+
static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
{
gpiod_set_value(pcie->reset, 1);
@@ -195,6 +224,10 @@ static int qcom_pcie_get_resources_v0(st
if (IS_ERR(res->ext_reset))
return PTR_ERR(res->ext_reset);
+ if (of_property_read_u8(dev->of_node, "phy-tx0-term-offset",
+ &res->phy_tx0_term_offset))
+ res->phy_tx0_term_offset = 0;
+
return 0;
}
@@ -254,7 +287,6 @@ static int qcom_pcie_init_v0(struct qcom
{
struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
struct device *dev = pcie->pp.dev;
- u32 val;
int ret;
ret = reset_control_assert(res->ahb_reset);
@@ -323,15 +355,27 @@ static int qcom_pcie_init_v0(struct qcom
goto err_deassert_ahb;
}
- /* enable PCIe clocks and resets */
- val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
- val &= ~BIT(0);
- writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
-
- /* enable external reference clock */
- val = readl(pcie->parf + PCIE20_PARF_PHY_REFCLK);
- val |= BIT(16);
- writel(val, pcie->parf + PCIE20_PARF_PHY_REFCLK);
+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
+
+ /* Set Tx termination offset */
+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL,
+ PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
+ PHY_CTRL_PHY_TX0_TERM_OFFSET(res->phy_tx0_term_offset));
+
+ /* PARF programming */
+ writel(PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) |
+ PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) |
+ PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22),
+ pcie->parf + PCIE20_PARF_PCS_DEEMPH);
+ writel(PCS_SWING_TX_SWING_FULL(0x78) |
+ PCS_SWING_TX_SWING_LOW(0x78),
+ pcie->parf + PCIE20_PARF_PCS_SWING);
+ writel(PHY_RX0_EQ(0x4), pcie->parf + PCIE20_PARF_CONFIG_BITS);
+
+ /* Enable reference clock */
+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK,
+ REF_USE_PAD, REF_SSP_EN);
+
ret = reset_control_deassert(res->phy_reset);
if (ret) {