[openwrt/openwrt] realtek: Add phylink configuration routines for RTL93xx

LEDE Commits lede-commits at lists.infradead.org
Fri Oct 8 23:26:23 PDT 2021


blogic pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/9d9bf16aa8d966834ac1280f96c37d22552c33d1

commit 9d9bf16aa8d966834ac1280f96c37d22552c33d1
Author: Birger Koblitz <git at birger-koblitz.de>
AuthorDate: Wed Sep 8 16:13:18 2021 +0200

    realtek: Add phylink configuration routines for RTL93xx
    
    This adds RTL93xx-specific MAC configuration routines that allow also configuration
    of 10GBit links for phylink. There is support for the Realtek-specific HISGMI
    protocol.
    
    Signed-off-by: Birger Koblitz <git at birger-koblitz.de>
---
 .../files-5.10/drivers/net/dsa/rtl83xx/dsa.c       | 260 +++++++++++++++++++--
 .../files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h   |   1 +
 .../704-include-linux-add-phy-hsgmii-mode.patch    |  19 ++
 3 files changed, 260 insertions(+), 20 deletions(-)

diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
index c79b4b2c81..3a0806e70a 100644
--- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
+++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
@@ -280,7 +280,7 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
 		phylink_set(mask, 1000baseX_Full);
 
 	/* On the RTL839x family of SoCs, ports 48 to 51 are SFP ports */
-	if (port >=48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
+	if (port >= 48 && port <= 51 && priv->family_id == RTL8390_FAMILY_ID)
 		phylink_set(mask, 1000baseX_Full);
 
 	phylink_set(mask, 10baseT_Half);
@@ -294,6 +294,67 @@ static void rtl83xx_phylink_validate(struct dsa_switch *ds, int port,
 		   __ETHTOOL_LINK_MODE_MASK_NBITS);
 }
 
+static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
+				     unsigned long *supported,
+				     struct phylink_link_state *state)
+{
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+	pr_debug("In %s port %d, state is %d (%s)", __func__, port, state->interface,
+		 phy_modes(state->interface));
+
+	if (!phy_interface_mode_is_rgmii(state->interface) &&
+	    state->interface != PHY_INTERFACE_MODE_NA &&
+	    state->interface != PHY_INTERFACE_MODE_1000BASEX &&
+	    state->interface != PHY_INTERFACE_MODE_MII &&
+	    state->interface != PHY_INTERFACE_MODE_REVMII &&
+	    state->interface != PHY_INTERFACE_MODE_GMII &&
+	    state->interface != PHY_INTERFACE_MODE_QSGMII &&
+	    state->interface != PHY_INTERFACE_MODE_XGMII &&
+	    state->interface != PHY_INTERFACE_MODE_HSGMII &&
+	    state->interface != PHY_INTERFACE_MODE_10GKR &&
+	    state->interface != PHY_INTERFACE_MODE_USXGMII &&
+	    state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+	    state->interface != PHY_INTERFACE_MODE_SGMII) {
+		bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+		dev_err(ds->dev,
+			"Unsupported interface: %d for port %d\n",
+			state->interface, port);
+		return;
+	}
+
+	/* Allow all the expected bits */
+	phylink_set(mask, Autoneg);
+	phylink_set_port_modes(mask);
+	phylink_set(mask, Pause);
+	phylink_set(mask, Asym_Pause);
+
+	/* With the exclusion of MII and Reverse MII, we support Gigabit,
+	 * including Half duplex
+	 */
+	if (state->interface != PHY_INTERFACE_MODE_MII &&
+	    state->interface != PHY_INTERFACE_MODE_REVMII) {
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 1000baseT_Half);
+	}
+
+	/* On the RTL9300 family of SoCs, ports 26 to 27 may be SFP ports TODO: take out of .dts */
+	if (port >= 26 && port <= 27)
+		phylink_set(mask, 1000baseX_Full);
+	if (port >= 26 && port <= 27)
+		phylink_set(mask, 10000baseKR_Full);
+
+	phylink_set(mask, 10baseT_Half);
+	phylink_set(mask, 10baseT_Full);
+	phylink_set(mask, 100baseT_Half);
+	phylink_set(mask, 100baseT_Full);
+
+	bitmap_and(supported, supported, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+	bitmap_and(state->advertising, state->advertising, mask,
+		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
 static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
 					  struct phylink_link_state *state)
 {
@@ -304,15 +365,8 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
 	if (port < 0 || port > priv->cpu_port)
 		return -EINVAL;
 
-	/*
-	 * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
-	 * state needs to be read twice in order to read a correct result.
-	 * This would not be necessary for ports connected e.g. to RTL8218D
-	 * PHYs.
-	 */
 	state->link = 0;
 	link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
-	link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
 	if (link & BIT_ULL(port))
 		state->link = 1;
 	pr_debug("%s: link state port %d: %llx\n", __func__, port, link & BIT_ULL(port));
@@ -349,6 +403,70 @@ static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
 	return 1;
 }
 
+static int rtl93xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
+					  struct phylink_link_state *state)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+	u64 speed;
+	u64 link;
+
+	if (port < 0 || port > priv->cpu_port)
+		return -EINVAL;
+
+	/*
+	 * On the RTL9300 for at least the RTL8226B PHY, the MAC-side link
+	 * state needs to be read twice in order to read a correct result.
+	 * This would not be necessary for ports connected e.g. to RTL8218D
+	 * PHYs.
+	 */
+	state->link = 0;
+	link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
+	link = priv->r->get_port_reg_le(priv->r->mac_link_sts);
+	if (link & BIT_ULL(port))
+		state->link = 1;
+	pr_debug("%s: link state port %d: %llx, media %08x\n", __func__, port,
+		 link & BIT_ULL(port), sw_r32(RTL930X_MAC_LINK_MEDIA_STS));
+
+	state->duplex = 0;
+	if (priv->r->get_port_reg_le(priv->r->mac_link_dup_sts) & BIT_ULL(port))
+		state->duplex = 1;
+
+	speed = priv->r->get_port_reg_le(priv->r->mac_link_spd_sts(port));
+	speed >>= (port % 8) << 2;
+	switch (speed & 0xf) {
+	case 0:
+		state->speed = SPEED_10;
+		break;
+	case 1:
+		state->speed = SPEED_100;
+		break;
+	case 2:
+	case 7:
+		state->speed = SPEED_1000;
+		break;
+	case 4:
+		state->speed = SPEED_10000;
+		break;
+	case 5:
+	case 8:
+		state->speed = SPEED_2500;
+		break;
+	case 6:
+		state->speed = SPEED_5000;
+		break;
+	default:
+		pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf);
+	}
+
+	pr_debug("%s: speed is: %d %d\n", __func__, (u32)speed & 0xf, state->speed);
+	state->pause &= (MLO_PAUSE_RX | MLO_PAUSE_TX);
+	if (priv->r->get_port_reg_le(priv->r->mac_rx_pause_sts) & BIT_ULL(port))
+		state->pause |= MLO_PAUSE_RX;
+	if (priv->r->get_port_reg_le(priv->r->mac_tx_pause_sts) & BIT_ULL(port))
+		state->pause |= MLO_PAUSE_TX;
+	return 1;
+}
+
 static void rtl83xx_config_interface(int port, phy_interface_t interface)
 {
 	u32 old, int_shift, sds_shift;
@@ -396,10 +514,6 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 
 	pr_debug("%s port %d, mode %x\n", __func__, port, mode);
 
-	// BUG: Make this work on RTL93XX
-	if (priv->family_id >= RTL9300_FAMILY_ID)
-		return;
-
 	if (port == priv->cpu_port) {
 		/* Set Speed, duplex, flow control
 		 * FORCE_EN | LINK_EN | NWAY_EN | DUP_SEL
@@ -468,6 +582,81 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
 }
 
+static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
+					unsigned int mode,
+					const struct phylink_link_state *state)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+	int sds_num, sds_mode;
+	u32 reg;
+
+	pr_debug("%s port %d, mode %x, phy-mode: %s, speed %d, link %d\n", __func__,
+		port, mode, phy_modes(state->interface), state->speed, state->link);
+
+	// Nothing to be done for the CPU-port
+	if (port == priv->cpu_port)
+		return;
+
+	reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
+	reg &= ~(0xf << 3);
+
+	// On the RTL930X, ports 24 to 27 are using an internal SerDes
+	if (port >=24 && port <= 27) {
+		sds_num = port - 18; // Port 24 mapped to SerDes 6, 25 to 7 ...
+		switch (state->interface) {
+		case PHY_INTERFACE_MODE_HSGMII:
+			sds_mode = 0x12;
+			break;
+		case PHY_INTERFACE_MODE_1000BASEX:
+			sds_mode = 0x1b;  // 10G 1000X Auto
+			break;
+		case PHY_INTERFACE_MODE_XGMII:
+			sds_mode = 0x10;
+			break;
+		case PHY_INTERFACE_MODE_10GKR:
+			sds_mode = 0x1a;
+			// We need to use media sel for fibre media:
+			reg |= BIT(16);
+			break;
+		case PHY_INTERFACE_MODE_USXGMII:
+			sds_mode = 0x0d;
+			break;
+		default:
+			pr_err("%s: unknown serdes mode: %s\n",
+			       __func__, phy_modes(state->interface));
+			return;
+		}
+		rtl9300_sds_rst(sds_num, sds_mode);
+	}
+
+	switch (state->speed) {
+	case SPEED_10000:
+		reg |= 4 << 3;
+		break;
+	case SPEED_5000:
+		reg |= 6 << 3;
+		break;
+	case SPEED_2500:
+		reg |= 5 << 3;
+		break;
+	case SPEED_1000:
+		reg |= 2 << 3;
+		break;
+	default:
+		reg |= 2 << 3;
+		break;
+	}
+
+	if (state->link)
+		reg |= FORCE_LINK_EN;
+
+	if (state->duplex == DUPLEX_FULL)
+			reg |= BIT(2);
+
+	reg |= 1; // Force Link up
+	sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
+}
+
 static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
 				     unsigned int mode,
 				     phy_interface_t interface)
@@ -477,6 +666,18 @@ static void rtl83xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
 	sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
 }
 
+static void rtl93xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+				     unsigned int mode,
+				     phy_interface_t interface)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+	/* Stop TX/RX to port */
+	sw_w32_mask(0x3, 0, priv->r->mac_port_ctrl(port));
+
+	// No longer force link
+	sw_w32_mask(3, 0, priv->r->mac_force_mode_ctrl(port));
+}
+
 static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
 				   unsigned int mode,
 				   phy_interface_t interface,
@@ -487,6 +688,21 @@ static void rtl83xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
 	struct rtl838x_switch_priv *priv = ds->priv;
 	/* Restart TX/RX to port */
 	sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
+	// TODO: Set speed/duplex/pauses
+}
+
+static void rtl93xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+				   unsigned int mode,
+				   phy_interface_t interface,
+				   struct phy_device *phydev,
+				   int speed, int duplex,
+				   bool tx_pause, bool rx_pause)
+{
+	struct rtl838x_switch_priv *priv = ds->priv;
+
+	/* Restart TX/RX to port */
+	sw_w32_mask(0, 0x3, priv->r->mac_port_ctrl(port));
+	// TODO: Set speed/duplex/pauses
 }
 
 static void rtl83xx_get_strings(struct dsa_switch *ds,
@@ -1183,7 +1399,10 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
 		if (!e.valid)
 			continue;
 
-		if (e.port == port) {
+		if (e.port == port || e.port == RTL930X_PORT_IGNORE) {
+			u64 seed;
+			u32 key;
+
 			fid = ((i >> 2) & 0x3ff) | (e.rvid & ~0x3ff);
 			mac = ether_addr_to_u64(&e.mac[0]);
 			pkey = priv->r->l2_hash_key(priv, priv->r->l2_hash_seed(mac, fid));
@@ -1191,13 +1410,14 @@ static int rtl83xx_port_fdb_dump(struct dsa_switch *ds, int port,
 			pr_info("-> index %d, key %x, bucket %d, dmac %016llx, fid: %x rvid: %x\n",
 				i, i >> 2, i & 0x3, mac, fid, e.rvid);
 			dump_l2_entry(&e);
-			u64 seed = priv->r->l2_hash_seed(mac, e.rvid);
-			u32 key = priv->r->l2_hash_key(priv, seed);
+			seed = priv->r->l2_hash_seed(mac, e.rvid);
+			key = priv->r->l2_hash_key(priv, seed);
 			pr_info("seed: %016llx, key based on rvid: %08x\n", seed, key);
 			cb(e.mac, e.vid, e.is_static, data);
 		}
 		if (e.type == L2_MULTICAST) {
 			u64 portmask = priv->r->read_mcast_pmask(e.mc_portmask_index);
+
 			if (portmask & BIT_ULL(port)) {
 				dump_l2_entry(&e);
 				pr_info("  PM: %016llx\n", portmask);
@@ -1558,11 +1778,11 @@ const struct dsa_switch_ops rtl930x_switch_ops = {
 	.phy_read		= dsa_phy_read,
 	.phy_write		= dsa_phy_write,
 
-	.phylink_validate	= rtl83xx_phylink_validate,
-	.phylink_mac_link_state	= rtl83xx_phylink_mac_link_state,
-	.phylink_mac_config	= rtl83xx_phylink_mac_config,
-	.phylink_mac_link_down	= rtl83xx_phylink_mac_link_down,
-	.phylink_mac_link_up	= rtl83xx_phylink_mac_link_up,
+	.phylink_validate	= rtl93xx_phylink_validate,
+	.phylink_mac_link_state	= rtl93xx_phylink_mac_link_state,
+	.phylink_mac_config	= rtl93xx_phylink_mac_config,
+	.phylink_mac_link_down	= rtl93xx_phylink_mac_link_down,
+	.phylink_mac_link_up	= rtl93xx_phylink_mac_link_up,
 
 	.get_strings		= rtl83xx_get_strings,
 	.get_ethtool_stats	= rtl83xx_get_ethtool_stats,
diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
index a3e169f288..e695879da1 100644
--- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
+++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl838x.h
@@ -145,6 +145,7 @@
 #define RTL839X_MAC_RX_PAUSE_STS		(0x03c0)
 #define RTL930X_MAC_RX_PAUSE_STS		(0xCB30)
 #define RTL931X_MAC_RX_PAUSE_STS		(0x0F00)
+#define RTL930X_MAC_LINK_MEDIA_STS		(0xCB14)
 
 /* MAC link state bits */
 #define FORCE_EN				(1 << 0)
diff --git a/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch b/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch
new file mode 100644
index 0000000000..9a61a70c44
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/704-include-linux-add-phy-hsgmii-mode.patch
@@ -0,0 +1,19 @@
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -93,6 +93,7 @@
+ 	PHY_INTERFACE_MODE_SMII,
+ 	PHY_INTERFACE_MODE_XGMII,
+ 	PHY_INTERFACE_MODE_MOCA,
++	PHY_INTERFACE_MODE_HSGMII,
+ 	PHY_INTERFACE_MODE_QSGMII,
+ 	PHY_INTERFACE_MODE_TRGMII,
+ 	PHY_INTERFACE_MODE_1000BASEX,
+@@ -163,6 +164,8 @@
+ 		return "xgmii";
+ 	case PHY_INTERFACE_MODE_MOCA:
+ 		return "moca";
++	case PHY_INTERFACE_MODE_HSGMII:
++		return "hsgmii";
+ 	case PHY_INTERFACE_MODE_QSGMII:
+ 		return "qsgmii";
+ 	case PHY_INTERFACE_MODE_TRGMII:



More information about the lede-commits mailing list