From 51c8f766124487f6e84076c272645e5f29d32f5e Mon Sep 17 00:00:00 2001
From: Birger Koblitz <git@birger-koblitz.de>
Date: Mon, 17 Jan 2022 13:19:13 +0100
Subject: [PATCH] realtek: Improve MAC config handling for all SoCs

Adds a rtl931x_phylink_mac_config for the RTL931X and improve
the handling of the RTL930X phylink configuration. Add separate
handling of the RTL839x since some configurations are different
from the RTL838X.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
---
 .../files-5.10/drivers/net/dsa/rtl83xx/dsa.c  | 173 +++++++++++++++---
 .../drivers/net/dsa/rtl83xx/rtl838x.h         |  63 ++++++-
 .../drivers/net/dsa/rtl83xx/rtl83xx.h         |   3 +
 3 files changed, 205 insertions(+), 34 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 12334dff9f..8afb503b23 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
@@ -38,7 +38,7 @@ static void rtl83xx_enable_phy_polling(struct rtl838x_switch_priv *priv)
 			v |= BIT_ULL(i);
 	}
 
-	pr_debug("%s: %16llx\n", __func__, v);
+	pr_info("%s: %16llx\n", __func__, v);
 	priv->r->set_port_reg_le(v, priv->r->smi_poll_ctrl);
 
 	/* PHY update complete, there is no global PHY polling enable bit on the 9300 */
@@ -131,6 +131,11 @@ static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
 	info.hash_mc_fid = false;	// Do the same for Multicast packets
 	info.profile_id = 0;		// Use default Vlan Profile 0
 	info.tagged_ports = 0;		// Initially no port members
+	if (priv->family_id == RTL9310_FAMILY_ID) {
+		info.if_id = 0;
+		info.multicast_grp_mask = 0;
+		info.l2_tunnel_list_id = -1;
+	}
 
 	// Initialize all vlans 0-4095
 	for (i = 0; i < MAX_VLANS; i ++)
@@ -195,7 +200,7 @@ static int rtl83xx_setup(struct dsa_switch *ds)
 	return 0;
 }
 
-static int rtl930x_setup(struct dsa_switch *ds)
+static int rtl93xx_setup(struct dsa_switch *ds)
 {
 	int i;
 	struct rtl838x_switch_priv *priv = ds->priv;
@@ -203,11 +208,14 @@ static int rtl930x_setup(struct dsa_switch *ds)
 
 	pr_info("%s called\n", __func__);
 
-	// Enable CSTI STP mode
-//	sw_w32(1, RTL930X_ST_CTRL);
-
 	/* Disable MAC polling the PHY so that we can start configuration */
-	sw_w32(0, RTL930X_SMI_POLL_CTRL);
+	if (priv->family_id == RTL9300_FAMILY_ID)
+		sw_w32(0, RTL930X_SMI_POLL_CTRL);
+
+	if (priv->family_id == RTL9310_FAMILY_ID) {
+		sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL);
+		sw_w32(0, RTL931X_SMI_PORT_POLLING_CTRL + 4);
+	}
 
 	// Disable all ports except CPU port
 	for (i = 0; i < ds->num_ports; i++)
@@ -322,6 +330,7 @@ static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
 				     unsigned long *supported,
 				     struct phylink_link_state *state)
 {
+	struct rtl838x_switch_priv *priv = ds->priv;
 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
 
 	pr_debug("In %s port %d, state is %d (%s)", __func__, port, state->interface,
@@ -362,11 +371,24 @@ static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
 		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)
+	// Internal phys of the RTL93xx family provide 10G
+	if (priv->ports[port].phy_is_integrated
+		&& state->interface == PHY_INTERFACE_MODE_1000BASEX) {
+		phylink_set(mask, 1000baseX_Full);
+	} else if (priv->ports[port].phy_is_integrated) {
 		phylink_set(mask, 1000baseX_Full);
-	if (port >= 26 && port <= 27)
 		phylink_set(mask, 10000baseKR_Full);
+		phylink_set(mask, 10000baseSR_Full);
+		phylink_set(mask, 10000baseCR_Full);
+	}
+	if (state->interface == PHY_INTERFACE_MODE_INTERNAL) {
+		phylink_set(mask, 1000baseX_Full);
+		phylink_set(mask, 1000baseT_Full);
+		phylink_set(mask, 10000baseKR_Full);
+		phylink_set(mask, 10000baseT_Full);
+		phylink_set(mask, 10000baseSR_Full);
+		phylink_set(mask, 10000baseCR_Full);
+	}
 
 	phylink_set(mask, 10baseT_Half);
 	phylink_set(mask, 10baseT_Full);
@@ -377,6 +399,7 @@ static void rtl93xx_phylink_validate(struct dsa_switch *ds, int port,
 		   __ETHTOOL_LINK_MODE_MASK_NBITS);
 	bitmap_and(state->advertising, state->advertising, mask,
 		   __ETHTOOL_LINK_MODE_MASK_NBITS);
+	pr_debug("%s leaving supported: %*pb", __func__, __ETHTOOL_LINK_MODE_MASK_NBITS, supported);
 }
 
 static int rtl83xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
@@ -482,6 +505,13 @@ static int rtl93xx_phylink_mac_link_state(struct dsa_switch *ds, int port,
 		pr_err("%s: unknown speed: %d\n", __func__, (u32)speed & 0xf);
 	}
 
+	if (priv->family_id == RTL9310_FAMILY_ID
+		&& (port >= 52 || port <= 55)) { /* Internal serdes */
+			state->speed = SPEED_10000;
+			state->link = 1;
+			state->duplex = 1;
+	}
+
 	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))
@@ -559,7 +589,7 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	if (priv->family_id == RTL8380_FAMILY_ID) {
 		if (mode == MLO_AN_PHY || phylink_autoneg_inband(mode)) {
 			pr_debug("PHY autonegotiates\n");
-			reg |= BIT(2);
+			reg |= RTL838X_NWAY_EN;
 			sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
 			rtl83xx_config_interface(port, state->interface);
 			return;
@@ -569,19 +599,25 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	if (mode != MLO_AN_FIXED)
 		pr_debug("Fixed state.\n");
 
+	/* Clear id_mode_dis bit, and the existing port mode, let
+		 * RGMII_MODE_EN bet set by mac_link_{up,down}  */
 	if (priv->family_id == RTL8380_FAMILY_ID) {
-		/* Clear id_mode_dis bit, and the existing port mode, let
-		 * RGMII_MODE_EN bet set by mac_link_{up,down}
-		 */
-		reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN);
-
+		reg &= ~(RTL838X_RX_PAUSE_EN | RTL838X_TX_PAUSE_EN);
+		if (state->pause & MLO_PAUSE_TXRX_MASK) {
+			if (state->pause & MLO_PAUSE_TX)
+				reg |= RTL838X_TX_PAUSE_EN;
+			reg |= RTL838X_RX_PAUSE_EN;
+		}
+	} else if (priv->family_id == RTL8390_FAMILY_ID) {
+		reg &= ~(RTL839X_RX_PAUSE_EN | RTL839X_TX_PAUSE_EN);
 		if (state->pause & MLO_PAUSE_TXRX_MASK) {
 			if (state->pause & MLO_PAUSE_TX)
-				reg |= TX_PAUSE_EN;
-			reg |= RX_PAUSE_EN;
+				reg |= RTL839X_TX_PAUSE_EN;
+			reg |= RTL839X_RX_PAUSE_EN;
 		}
 	}
 
+
 	reg &= ~(3 << speed_bit);
 	switch (state->speed) {
 	case SPEED_1000:
@@ -590,22 +626,98 @@ static void rtl83xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	case SPEED_100:
 		reg |= 1 << speed_bit;
 		break;
+	default:
+		break; // Ignore, including 10MBit which has a speed value of 0
 	}
 
 	if (priv->family_id == RTL8380_FAMILY_ID) {
-		reg &= ~(DUPLEX_FULL | FORCE_LINK_EN);
+		reg &= ~(RTL838X_DUPLEX_MODE | RTL838X_FORCE_LINK_EN);
+		if (state->link)
+			reg |= RTL838X_FORCE_LINK_EN;
+		if (state->duplex == RTL838X_DUPLEX_MODE)
+			reg |= RTL838X_DUPLEX_MODE;
+	} else if (priv->family_id == RTL8390_FAMILY_ID) {
+		reg &= ~(RTL839X_DUPLEX_MODE | RTL839X_FORCE_LINK_EN);
 		if (state->link)
-			reg |= FORCE_LINK_EN;
-		if (state->duplex == DUPLEX_FULL)
-			reg |= DUPLX_MODE;
+			reg |= RTL839X_FORCE_LINK_EN;
+		if (state->duplex == RTL839X_DUPLEX_MODE)
+			reg |= RTL839X_DUPLEX_MODE;
 	}
-
 	// Disable AN
 	if (priv->family_id == RTL8380_FAMILY_ID)
-		reg &= ~BIT(2);
+		reg &= ~RTL838X_NWAY_EN;
 	sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
 }
 
+static void rtl931x_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;
+	u32 reg, band;
+
+	sds_num = priv->ports[port].sds_num;
+	pr_info("%s: speed %d sds_num %d\n", __func__, state->speed, sds_num);
+
+	switch (state->interface) {
+	case PHY_INTERFACE_MODE_HSGMII:
+		pr_info("%s setting mode PHY_INTERFACE_MODE_HSGMII\n", __func__);
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_HSGMII);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_HSGMII);
+		band = rtl931x_sds_cmu_band_set(sds_num, true, 62, PHY_INTERFACE_MODE_HSGMII);
+		break;
+	case PHY_INTERFACE_MODE_1000BASEX:
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_1000BASEX);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_1000BASEX);
+		break;
+	case PHY_INTERFACE_MODE_XGMII:
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_XGMII);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_XGMII);
+		break;
+	case PHY_INTERFACE_MODE_10GBASER:
+	case PHY_INTERFACE_MODE_10GKR:
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_10GBASER);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_10GBASER);
+		break;
+	case PHY_INTERFACE_MODE_USXGMII:
+		// Translates to MII_USXGMII_10GSXGMII
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_USXGMII);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_USXGMII);
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		pr_info("%s setting mode PHY_INTERFACE_MODE_SGMII\n", __func__);
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_SGMII);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_SGMII);
+		band = rtl931x_sds_cmu_band_set(sds_num, true, 62, PHY_INTERFACE_MODE_SGMII);
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+		band = rtl931x_sds_cmu_band_get(sds_num, PHY_INTERFACE_MODE_QSGMII);
+		rtl931x_sds_init(sds_num, PHY_INTERFACE_MODE_QSGMII);
+		break;
+	default:
+		pr_err("%s: unknown serdes mode: %s\n",
+			__func__, phy_modes(state->interface));
+		return;
+	}
+
+	reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
+	pr_info("%s reading FORCE_MODE_CTRL: %08x\n", __func__, reg);
+
+	reg &= ~(RTL931X_DUPLEX_MODE | RTL931X_FORCE_EN | RTL931X_FORCE_LINK_EN);
+
+	reg &= ~(0xf << 12);
+	reg |= 0x2 << 12; // Set SMI speed to 0x2
+
+	reg |= BIT(17) | BIT(16); // Enable RX pause and TX pause
+
+	if (state->duplex == DUPLEX_FULL)
+		reg |= RTL931X_DUPLEX_MODE;
+
+	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)
@@ -621,6 +733,9 @@ static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	if (port == priv->cpu_port)
 		return;
 
+	if (priv->family_id == RTL9310_FAMILY_ID)
+		return rtl931x_phylink_mac_config(ds, port, mode, state);
+
 	reg = sw_r32(priv->r->mac_force_mode_ctrl(port));
 	reg &= ~(0xf << 3);
 
@@ -672,12 +787,16 @@ static void rtl93xx_phylink_mac_config(struct dsa_switch *ds, int port,
 	}
 
 	if (state->link)
-		reg |= FORCE_LINK_EN;
+		reg |= RTL930X_FORCE_LINK_EN;
 
 	if (state->duplex == DUPLEX_FULL)
-			reg |= BIT(2);
+		reg |= RTL930X_DUPLEX_MODE;
+
+	if (priv->ports[port].phy_is_integrated)
+		reg &= ~RTL930X_FORCE_EN; // Clear MAC_FORCE_EN to allow SDS-MAC link
+	else
+		reg |= RTL930X_FORCE_EN;
 
-	reg |= 1; // Force Link up
 	sw_w32(reg, priv->r->mac_force_mode_ctrl(port));
 }
 
@@ -1799,7 +1918,7 @@ const struct dsa_switch_ops rtl83xx_switch_ops = {
 
 const struct dsa_switch_ops rtl930x_switch_ops = {
 	.get_tag_protocol	= rtl83xx_get_tag_protocol,
-	.setup			= rtl930x_setup,
+	.setup			= rtl93xx_setup,
 
 	.phy_read		= dsa_phy_read,
 	.phy_write		= dsa_phy_write,
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 1c55ff6fc9..a334a5ddbb 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
@@ -147,12 +147,34 @@
 #define RTL930X_MAC_LINK_MEDIA_STS		(0xCB14)
 
 /* MAC link state bits */
-#define FORCE_EN				(1 << 0)
-#define FORCE_LINK_EN				(1 << 1)
-#define NWAY_EN					(1 << 2)
-#define DUPLX_MODE				(1 << 3)
-#define TX_PAUSE_EN				(1 << 6)
-#define RX_PAUSE_EN				(1 << 7)
+#define RTL838X_FORCE_EN			(1 << 0)
+#define RTL838X_FORCE_LINK_EN			(1 << 1)
+#define RTL838X_NWAY_EN				(1 << 2)
+#define RTL838X_DUPLEX_MODE			(1 << 3)
+#define RTL838X_TX_PAUSE_EN			(1 << 6)
+#define RTL838X_RX_PAUSE_EN			(1 << 7)
+#define RTL838X_MAC_FORCE_FC_EN			(1 << 8)
+
+#define RTL839X_FORCE_EN			(1 << 0)
+#define RTL839X_FORCE_LINK_EN			(1 << 1)
+#define RTL839X_DUPLEX_MODE			(1 << 2)
+#define RTL839X_TX_PAUSE_EN			(1 << 5)
+#define RTL839X_RX_PAUSE_EN			(1 << 6)
+#define RTL839X_MAC_FORCE_FC_EN			(1 << 7)
+
+#define RTL930X_FORCE_EN			(1 << 0)
+#define RTL930X_FORCE_LINK_EN			(1 << 1)
+#define RTL930X_DUPLEX_MODE			(1 << 2)
+#define RTL930X_TX_PAUSE_EN			(1 << 7)
+#define RTL930X_RX_PAUSE_EN			(1 << 8)
+#define RTL930X_MAC_FORCE_FC_EN			(1 << 9)
+
+#define RTL931X_FORCE_EN			(1 << 9)
+#define RTL931X_FORCE_LINK_EN			(1 << 0)
+#define RTL931X_DUPLEX_MODE			(1 << 2)
+#define RTL931X_MAC_FORCE_FC_EN			(1 << 4)
+#define RTL931X_TX_PAUSE_EN			(1 << 16)
+#define RTL931X_RX_PAUSE_EN			(1 << 17)
 
 /* EEE */
 #define RTL838X_MAC_EEE_ABLTY			(0xa1a8)
@@ -433,6 +455,7 @@ enum phy_type {
 	PHY_RTL8218B_EXT = 3,
 	PHY_RTL8214FC = 4,
 	PHY_RTL839X_SDS = 5,
+	PHY_RTL930X_SDS = 6,
 };
 
 struct rtl838x_port {
@@ -441,6 +464,7 @@ struct rtl838x_port {
 	u16 pvid;
 	bool eee_enabled;
 	enum phy_type phy;
+	bool phy_is_integrated;
 	bool is10G;
 	bool is2G5;
 	int sds_num;
@@ -453,7 +477,12 @@ struct rtl838x_vlan_info {
 	u8 profile_id;
 	bool hash_mc_fid;
 	bool hash_uc_fid;
-	u8 fid;
+	u8 fid; // AKA MSTI
+
+	// The following fields are used only by the RTL931X
+	int if_id;		// Interface (index in L3_EGR_INTF_IDX)
+	u16 multicast_grp_mask;
+	int l2_tunnel_list_id;
 };
 
 enum l2_entry_type {
@@ -488,6 +517,15 @@ struct rtl838x_l2_entry {
 	u16 mc_mac_index;
 	u16 nh_route_id;
 	bool nh_vlan_target;  // Only RTL83xx: VLAN used for next hop
+
+	// The following is only valid on RTL931x
+	bool is_open_flow;
+	bool is_pe_forward;
+	bool is_local_forward;
+	bool is_remote_forward;
+	bool is_l2_tunnel;
+	int l2_tunnel_id;
+	int l2_tunnel_list_id;
 };
 
 enum fwd_rule_action {
@@ -500,6 +538,17 @@ enum pie_phase {
 	PHASE_IACL = 1,
 };
 
+enum igr_filter {
+	IGR_FORWARD = 0,
+	IGR_DROP = 1,
+	IGR_TRAP = 2,
+};
+
+enum egr_filter {
+	EGR_DISABLE = 0,
+	EGR_ENABLE = 1,
+};
+
 /* Intermediate representation of a  Packet Inspection Engine Rule
  * as suggested by the Kernel's tc flower offload subsystem
  * Field meaning is universal across SoC families, but data content is specific
diff --git a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl83xx.h b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl83xx.h
index ffd37aa5a8..70b1c4e09d 100644
--- a/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl83xx.h
+++ b/target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/rtl83xx.h
@@ -125,6 +125,9 @@ void rtl930x_print_matrix(void);
 
 /* RTL931x-specific */
 irqreturn_t rtl931x_switch_irq(int irq, void *dev_id);
+int rtl931x_sds_cmu_band_get(int sds, phy_interface_t mode);
+int rtl931x_sds_cmu_band_set(int sds, bool enable, u32 band, phy_interface_t mode);
+void rtl931x_sds_init(u32 sds, phy_interface_t mode);
 
 #endif /* _NET_DSA_RTL83XX_H */
 
-- 
2.30.2