ag7xxx: add initial support for s17

S17 ethernet support is for QCA8337N, which used on
AP152 (QCA9563) board. It is a 7 ports GbE switch.

Signed-off-by: Rosy Song <rosysong@rosinson.com>

Changes for v2-v3:
   - add more commit message for s17

Changes for v4-v5:
   - coding style cleanup
diff --git a/drivers/net/ag7xxx.c b/drivers/net/ag7xxx.c
index 403eb64..7f1dee4 100644
--- a/drivers/net/ag7xxx.c
+++ b/drivers/net/ag7xxx.c
@@ -3,6 +3,7 @@
  * Atheros AR71xx / AR9xxx GMAC driver
  *
  * Copyright (C) 2016 Marek Vasut <marex@denx.de>
+ * Copyright (C) 2019 Rosy Song <rosysong@rosinson.com>
  */
 
 #include <common.h>
@@ -23,7 +24,8 @@
 enum ag7xxx_model {
 	AG7XXX_MODEL_AG933X,
 	AG7XXX_MODEL_AG934X,
-	AG7XXX_MODEL_AG953X
+	AG7XXX_MODEL_AG953X,
+	AG7XXX_MODEL_AG956X
 };
 
 /* MAC Configuration 1 */
@@ -219,6 +221,7 @@
 	u32 reg_addr;
 	u32 phy_temp;
 	u32 reg_temp;
+	u32 reg_temp_w = (reg & 0xfffffffc) >> 1;
 	u16 rv = 0;
 	int ret;
 
@@ -226,18 +229,25 @@
 	    priv->model == AG7XXX_MODEL_AG953X) {
 		phy_addr = 0x1f;
 		reg_addr = 0x10;
-	} else if (priv->model == AG7XXX_MODEL_AG934X) {
+	} else if (priv->model == AG7XXX_MODEL_AG934X ||
+		   priv->model == AG7XXX_MODEL_AG956X) {
 		phy_addr = 0x18;
 		reg_addr = 0x00;
 	} else
 		return -EINVAL;
 
-	ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9);
+	if (priv->model == AG7XXX_MODEL_AG956X)
+		ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, (reg >> 9) & 0x1ff);
+	else
+		ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9);
 	if (ret)
 		return ret;
 
 	phy_temp = ((reg >> 6) & 0x7) | 0x10;
-	reg_temp = (reg >> 1) & 0x1e;
+	if (priv->model == AG7XXX_MODEL_AG956X)
+		reg_temp = reg_temp_w & 0x1f;
+	else
+		reg_temp = (reg >> 1) & 0x1e;
 	*val = 0;
 
 	ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 0, &rv);
@@ -245,7 +255,13 @@
 		return ret;
 	*val |= rv;
 
-	ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 1, &rv);
+	if (priv->model == AG7XXX_MODEL_AG956X) {
+		phy_temp = (((reg_temp_w + 1) >> 5) & 0x7) | 0x10;
+		reg_temp = (reg_temp_w + 1) & 0x1f;
+		ret = ag7xxx_switch_read(bus, phy_temp, reg_temp, &rv);
+	} else {
+		ret = ag7xxx_switch_read(bus, phy_temp, reg_temp | 1, &rv);
+	}
 	if (ret < 0)
 		return ret;
 	*val |= (rv << 16);
@@ -260,24 +276,34 @@
 	u32 reg_addr;
 	u32 phy_temp;
 	u32 reg_temp;
+	u32 reg_temp_w = (reg & 0xfffffffc) >> 1;
 	int ret;
 
 	if (priv->model == AG7XXX_MODEL_AG933X ||
 	    priv->model == AG7XXX_MODEL_AG953X) {
 		phy_addr = 0x1f;
 		reg_addr = 0x10;
-	} else if (priv->model == AG7XXX_MODEL_AG934X) {
+	} else if (priv->model == AG7XXX_MODEL_AG934X ||
+		   priv->model == AG7XXX_MODEL_AG956X) {
 		phy_addr = 0x18;
 		reg_addr = 0x00;
 	} else
 		return -EINVAL;
 
-	ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9);
+	if (priv->model == AG7XXX_MODEL_AG956X)
+		ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, (reg >> 9) & 0x1ff);
+	else
+		ret = ag7xxx_switch_write(bus, phy_addr, reg_addr, reg >> 9);
 	if (ret)
 		return ret;
 
-	phy_temp = ((reg >> 6) & 0x7) | 0x10;
-	reg_temp = (reg >> 1) & 0x1e;
+	if (priv->model == AG7XXX_MODEL_AG956X) {
+		reg_temp = (reg_temp_w + 1) & 0x1f;
+		phy_temp = (((reg_temp_w + 1) >> 5) & 0x7) | 0x10;
+	} else {
+		phy_temp = ((reg >> 6) & 0x7) | 0x10;
+		reg_temp = (reg >> 1) & 0x1e;
+	}
 
 	/*
 	 * The switch on AR933x has some special register behavior, which
@@ -296,10 +322,18 @@
 		if (ret < 0)
 			return ret;
 	} else {
-		ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 1, val >> 16);
+		if (priv->model == AG7XXX_MODEL_AG956X)
+			ret = ag7xxx_switch_write(bus, phy_temp, reg_temp, val >> 16);
+		else
+			ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 1, val >> 16);
 		if (ret < 0)
 			return ret;
 
+		if (priv->model == AG7XXX_MODEL_AG956X) {
+			phy_temp = ((reg_temp_w >> 5) & 0x7) | 0x10;
+			reg_temp = reg_temp_w & 0x1f;
+		}
+
 		ret = ag7xxx_switch_write(bus, phy_temp, reg_temp | 0, val & 0xffff);
 		if (ret < 0)
 			return ret;
@@ -626,9 +660,12 @@
 		reg = 0x4;
 	else if (priv->model == AG7XXX_MODEL_AG953X)
 		reg = 0x2;
+	else if (priv->model == AG7XXX_MODEL_AG956X)
+		reg = 0x7;
 
 	if (priv->model == AG7XXX_MODEL_AG934X ||
-	    priv->model == AG7XXX_MODEL_AG953X) {
+	    priv->model == AG7XXX_MODEL_AG953X ||
+	    priv->model == AG7XXX_MODEL_AG956X) {
 		writel(AG7XXX_ETH_MII_MGMT_CFG_RESET | reg,
 		       priv->regs + AG7XXX_ETH_MII_MGMT_CFG);
 		writel(reg, priv->regs + AG7XXX_ETH_MII_MGMT_CFG);
@@ -839,7 +876,8 @@
 	struct ar7xxx_eth_priv *priv = dev_get_priv(dev);
 	int ret;
 
-	if (priv->model == AG7XXX_MODEL_AG953X) {
+	if (priv->model == AG7XXX_MODEL_AG953X ||
+	    priv->model == AG7XXX_MODEL_AG956X) {
 		ret = ag7xxx_switch_write(priv->bus, port, MII_ADVERTISE,
 					ADVERTISE_ALL);
 	} else {
@@ -855,9 +893,15 @@
 					ADVERTISE_1000FULL);
 		if (ret)
 			return ret;
+	} else if (priv->model == AG7XXX_MODEL_AG956X) {
+		ret = ag7xxx_switch_write(priv->bus, port, MII_CTRL1000,
+					  ADVERTISE_1000FULL);
+		if (ret)
+			return ret;
 	}
 
-	if (priv->model == AG7XXX_MODEL_AG953X)
+	if (priv->model == AG7XXX_MODEL_AG953X ||
+	    priv->model == AG7XXX_MODEL_AG956X)
 		return ag7xxx_switch_write(priv->bus, port, MII_BMCR,
 					 BMCR_ANENABLE | BMCR_RESET);
 
@@ -871,7 +915,8 @@
 	int ret;
 	u16 reg;
 
-	if (priv->model == AG7XXX_MODEL_AG953X) {
+	if (priv->model == AG7XXX_MODEL_AG953X ||
+	    priv->model == AG7XXX_MODEL_AG956X) {
 		do {
 			ret = ag7xxx_switch_read(priv->bus, port, MII_BMCR, &reg);
 			if (ret < 0)
@@ -899,7 +944,8 @@
 	if (priv->model == AG7XXX_MODEL_AG933X)
 		phymax = 4;
 	else if (priv->model == AG7XXX_MODEL_AG934X ||
-		priv->model == AG7XXX_MODEL_AG953X)
+		priv->model == AG7XXX_MODEL_AG953X ||
+		priv->model == AG7XXX_MODEL_AG956X)
 		phymax = 5;
 	else
 		return -EINVAL;
@@ -939,7 +985,8 @@
 
 	for (i = 0; i < phymax; i++) {
 		/* Read out link status */
-		if (priv->model == AG7XXX_MODEL_AG953X)
+		if (priv->model == AG7XXX_MODEL_AG953X ||
+		    priv->model == AG7XXX_MODEL_AG956X)
 			ret = ag7xxx_switch_read(priv->bus, i, MII_MIPSCR, &reg);
 		else
 			ret = ag7xxx_mdio_read(priv->bus, i, 0, MII_MIPSCR);
@@ -1004,6 +1051,63 @@
 	return 0;
 }
 
+static int ag956x_phy_setup(struct udevice *dev)
+{
+	struct ar7xxx_eth_priv *priv = dev_get_priv(dev);
+	int i, ret;
+	u32 reg, ctrl;
+
+	ret = ag7xxx_switch_reg_read(priv->bus, 0x0, &reg);
+	if (ret)
+		return ret;
+	if ((reg & 0xffff) >= 0x1301)
+		ctrl = 0xc74164de;
+	else
+		ctrl = 0xc74164d0;
+
+	ret = ag7xxx_switch_reg_write(priv->bus, 0x4, BIT(7));
+	if (ret)
+		return ret;
+
+	ret = ag7xxx_switch_reg_write(priv->bus, 0xe0, ctrl);
+	if (ret)
+		return ret;
+
+	ret = ag7xxx_switch_reg_write(priv->bus, 0x624, 0x7f7f7f7f);
+	if (ret)
+		return ret;
+
+	/*
+	 * Values suggested by the switch team when s17 in sgmii
+	 * configuration. 0x10(S17_PWS_REG) = 0x602613a0
+	 */
+	ret = ag7xxx_switch_reg_write(priv->bus, 0x10, 0x602613a0);
+	if (ret)
+		return ret;
+
+	ret = ag7xxx_switch_reg_write(priv->bus, 0x7c, 0x0000007e);
+	if (ret)
+		return ret;
+
+	/* AR8337/AR8334 v1.0 fixup */
+	ret = ag7xxx_switch_reg_read(priv->bus, 0, &reg);
+	if (ret)
+		return ret;
+	if ((reg & 0xffff) == 0x1301) {
+		for (i = 0; i < 5; i++) {
+			/* Turn on Gigabit clock */
+			ret = ag7xxx_switch_write(priv->bus, i, 0x1d, 0x3d);
+			if (ret)
+				return ret;
+			ret = ag7xxx_switch_write(priv->bus, i, 0x1e, 0x6820);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 static int ag7xxx_mac_probe(struct udevice *dev)
 {
 	struct ar7xxx_eth_priv *priv = dev_get_priv(dev);
@@ -1028,6 +1132,8 @@
 			ret = ag953x_phy_setup_lan(dev);
 	} else if (priv->model == AG7XXX_MODEL_AG934X) {
 		ret = ag934x_phy_setup(dev);
+	} else if (priv->model == AG7XXX_MODEL_AG956X) {
+		ret = ag956x_phy_setup(dev);
 	} else {
 		return -EINVAL;
 	}
@@ -1166,6 +1272,7 @@
 	{ .compatible = "qca,ag933x-mac", .data = AG7XXX_MODEL_AG933X },
 	{ .compatible = "qca,ag934x-mac", .data = AG7XXX_MODEL_AG934X },
 	{ .compatible = "qca,ag953x-mac", .data = AG7XXX_MODEL_AG953X },
+	{ .compatible = "qca,ag956x-mac", .data = AG7XXX_MODEL_AG956X },
 	{ }
 };