diff mbox series

[RFC,v2,2/6] ath79: add QCA956x SERDES init workaround

Message ID 0580ec0fe299e6669b8e0744add37ac738874578.1595193767.git.sander@svanheule.net
State New
Headers show
Series ath79: support for TP-Link EAP2x5 1-port devices | expand

Commit Message

Sander Vanheule July 19, 2020, 9:24 p.m. UTC
From: Julien Dusser <julien.dusser@free.fr>

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>
Signed-off-by: Sander Vanheule <sander@svanheule.net>
---
 .../net/ethernet/atheros/ag71xx/ag71xx_main.c | 78 +++++++++++++++++++
 1 file changed, 78 insertions(+)
diff mbox series

Patch

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 e86dbfffcf..8874a3440e 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,79 @@  static void ag71xx_bit_clear(void __iomem *reg, u32 bit)
 	__raw_readl(reg);
 }
 
+/* Set SGMII VCO resistor value */
+static void ag71xx_serdes_cal_qca956x(struct device_node *np)
+{
+	struct device_node *np_dev;
+	void __iomem *gmac_base;
+	int err = 0;
+	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);
+		err = -ENOMEM;
+		goto err_iomap;
+	}
+
+	pr_info("%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;
@@ -1457,6 +1530,11 @@  static int ag71xx_probe(struct platform_device *pdev)
 	if (!res)
 		return -EINVAL;
 
+	if (of_property_read_bool(np, "qca956x-serdes-fixup")) {
+		ag71xx_serdes_cal_qca956x(np);
+		ag71xx_sgmii_init_qca955x(np);
+	}
+
 	err = ag71xx_setup_gmac(np);
 	if (err)
 		return err;