aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Dusser <julien.dusser@free.fr>2020-07-01 23:56:01 +0200
committerPetr Štetiar <ynezz@true.cz>2020-11-23 22:53:15 +0100
commit87627b2d760cd2ac341dd3181ce6f987d69777c2 (patch)
treefe86c3d2b3bdf2e5b9bcc55f1055d9791883e63b
parent8dd2a1715b0a59407cf90c0708fc2bc199e110b5 (diff)
downloadupstream-87627b2d760cd2ac341dd3181ce6f987d69777c2.tar.gz
upstream-87627b2d760cd2ac341dd3181ce6f987d69777c2.tar.bz2
upstream-87627b2d760cd2ac341dd3181ce6f987d69777c2.zip
ath79: add QCA956x SERDES init workaround
This commit add a workaround for non working SGMII link observed on some QCA956x SoCs. The workaround originates part from the U-Boot source code from QCA, part from the implementation from TP-Link found in the GPL tarball for the EAP245v1. Extends commit 0d416a8d3b990e3b78628f0e7546527709c877f7 for QCA956x. Note that reset is the same on QCA955x and QCA956x, same register offset and values. Auto calibration is done on u-boot, but always fall back to default value 0x7. Add a DTS entry serdes-cal in case a device require another value. Signed-off-by: Julien Dusser <julien.dusser@free.fr> [Sander Vanheule: Minor code style fixes, Remove hunk adding qca956x-serdes-fixup to a missing DTS, Remove variable err that was only assigned, Rename function to sgmii_serdes_init, Lower priority of serdes call message to pr_debug] Signed-off-by: Sander Vanheule <sander@svanheule.net>
-rw-r--r--target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c76
1 files changed, 76 insertions, 0 deletions
diff --git a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
index 7b292cc005..07d9992ca7 100644
--- a/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
+++ b/target/linux/ath79/files/drivers/net/ethernet/atheros/ag71xx/ag71xx_main.c
@@ -577,6 +577,77 @@ static void ag71xx_bit_clear(void __iomem *reg, u32 bit)
__raw_readl(reg);
}
+static void ag71xx_sgmii_serdes_init_qca956x(struct device_node *np)
+{
+ struct device_node *np_dev;
+ void __iomem *gmac_base;
+ u32 serdes_cal;
+ u32 t;
+
+ np = of_get_child_by_name(np, "gmac-config");
+ if (!np)
+ return;
+
+ if (of_property_read_u32(np, "serdes-cal", &serdes_cal))
+ /* By default, use middle value for resistor calibration */
+ serdes_cal = 0x7;
+
+ np_dev = of_parse_phandle(np, "device", 0);
+ if (!np_dev)
+ goto out;
+
+ gmac_base = of_iomap(np_dev, 0);
+ if (!gmac_base) {
+ pr_err("%pOF: can't map GMAC registers\n", np_dev);
+ goto err_iomap;
+ }
+
+ pr_debug("%pOF: fixup SERDES calibration to value %i\n",
+ np_dev, serdes_cal);
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+ t &= ~(QCA956X_SGMII_SERDES_RES_CALIBRATION_MASK
+ << QCA956X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
+ t |= (serdes_cal & QCA956X_SGMII_SERDES_RES_CALIBRATION_MASK)
+ << QCA956X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ ath79_pll_wr(QCA956X_PLL_ETH_SGMII_SERDES_REG,
+ QCA956X_PLL_ETH_SGMII_SERDES_LOCK_DETECT
+ | QCA956X_PLL_ETH_SGMII_SERDES_EN_PLL);
+
+ t = __raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ /* missing in QCA u-boot code, clear before setting */
+ t &= ~(QCA956X_SGMII_SERDES_CDR_BW_MASK
+ << QCA956X_SGMII_SERDES_CDR_BW_SHIFT |
+ QCA956X_SGMII_SERDES_TX_DR_CTRL_MASK
+ << QCA956X_SGMII_SERDES_TX_DR_CTRL_SHIFT |
+ QCA956X_SGMII_SERDES_VCO_REG_MASK
+ << QCA956X_SGMII_SERDES_VCO_REG_SHIFT);
+
+ t |= (3 << QCA956X_SGMII_SERDES_CDR_BW_SHIFT) |
+ (1 << QCA956X_SGMII_SERDES_TX_DR_CTRL_SHIFT) |
+ QCA956X_SGMII_SERDES_PLL_BW |
+ QCA956X_SGMII_SERDES_EN_SIGNAL_DETECT |
+ QCA956X_SGMII_SERDES_FIBER_SDO |
+ (3 << QCA956X_SGMII_SERDES_VCO_REG_SHIFT);
+
+ __raw_writel(t, gmac_base + QCA956X_GMAC_REG_SGMII_SERDES);
+
+ ath79_device_reset_clear(QCA956X_RESET_SGMII_ANALOG);
+ ath79_device_reset_clear(QCA956X_RESET_SGMII);
+
+ while (!(__raw_readl(gmac_base + QCA956X_GMAC_REG_SGMII_SERDES)
+ & QCA956X_SGMII_SERDES_LOCK_DETECT_STATUS))
+ ;
+
+ iounmap(gmac_base);
+err_iomap:
+ of_node_put(np_dev);
+out:
+ of_node_put(np);
+}
+
static void ag71xx_sgmii_init_qca955x(struct device_node *np)
{
struct device_node *np_dev;
@@ -1454,6 +1525,11 @@ static int ag71xx_probe(struct platform_device *pdev)
if (!res)
return -EINVAL;
+ if (of_property_read_bool(np, "qca956x-serdes-fixup")) {
+ ag71xx_sgmii_serdes_init_qca956x(np);
+ ag71xx_sgmii_init_qca955x(np);
+ }
+
err = ag71xx_setup_gmac(np);
if (err)
return err;