--- /dev/null
+From ace6abaa0f9203083fe4c0a6a74da2d96410b625 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Sat, 13 Aug 2022 12:49:33 +0200
+Subject: [PATCH 01/10] net: phy: realtek: rtl8221: allow to configure SERDES
+ mode
+
+The rtl8221 supports multiple SERDES modes:
+- SGMII
+- 2500base-x
+- HiSGMII
+
+Further it supports rate adaption on SERDES links to allow
+slow ethernet speeds (10/100/1000mbit) to work on 2500base-x/HiSGMII
+links without reducing the SERDES speed.
+
+When operating without rate adapters the SERDES link will follow the
+ethernet speed.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/phy/realtek.c | 48 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -53,6 +53,15 @@
+ RTL8201F_ISR_LINK)
+ #define RTL8201F_IER 0x13
+
++#define RTL8221B_MMD_SERDES_CTRL MDIO_MMD_VEND1
++#define RTL8221B_MMD_PHY_CTRL MDIO_MMD_VEND2
++#define RTL8221B_SERDES_OPTION 0x697a
++#define RTL8221B_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII 0
++#define RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII 1
++#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2
++#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3
++
+ #define RTL8366RB_POWER_SAVE 0x15
+ #define RTL8366RB_POWER_SAVE_ON BIT(12)
+
+@@ -841,6 +850,43 @@ static irqreturn_t rtl9000a_handle_inter
+ return IRQ_HANDLED;
+ }
+
++static int rtl8221b_config_init(struct phy_device *phydev)
++{
++ u16 option_mode;
++
++ switch (phydev->interface) {
++ case PHY_INTERFACE_MODE_SGMII:
++ case PHY_INTERFACE_MODE_2500BASEX:
++ option_mode = RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII;
++ break;
++ default:
++ return 0;
++ }
++
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL,
++ 0x75f3, 0);
++
++ phy_modify_mmd_changed(phydev, RTL8221B_MMD_SERDES_CTRL,
++ RTL8221B_SERDES_OPTION,
++ RTL8221B_SERDES_OPTION_MODE_MASK, option_mode);
++ switch (option_mode) {
++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX_SGMII:
++ case RTL8221B_SERDES_OPTION_MODE_2500BASEX:
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd455);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
++ break;
++ case RTL8221B_SERDES_OPTION_MODE_HISGMII_SGMII:
++ case RTL8221B_SERDES_OPTION_MODE_HISGMII:
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6a04, 0x0503);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f10, 0xd433);
++ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, 0x6f11, 0x8020);
++ break;
++ }
++
++ return 0;
++}
++
+ static struct phy_driver realtek_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(0x00008201),
+@@ -981,6 +1027,7 @@ static struct phy_driver realtek_drvs[]
+ PHY_ID_MATCH_EXACT(0x001cc849),
+ .name = "RTL8221B-VB-CG 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
++ .config_init = rtl8221b_config_init,
+ .config_aneg = rtl822x_config_aneg,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+@@ -992,6 +1039,7 @@ static struct phy_driver realtek_drvs[]
+ .name = "RTL8221B-VM-CG 2.5Gbps PHY",
+ .get_features = rtl822x_get_features,
+ .config_aneg = rtl822x_config_aneg,
++ .config_init = rtl8221b_config_init,
+ .read_status = rtl822x_read_status,
+ .suspend = genphy_suspend,
+ .resume = rtlgen_resume,
--- /dev/null
+From 9fec662b54fc956b776df15c704e996c61292850 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Sat, 13 Aug 2022 13:05:09 +0200
+Subject: [PATCH 02/10] net: mt7531: only do PLL once after the reset
+
+Move the PLL init of the switch out of the pad configuration of the port
+6 (usally cpu port).
+
+Fix a unidirectional 100 mbit limitation on 1 gbit or 2.5 gbit links for
+outbound traffic on port 5 or port 6.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/dsa/mt7530.c | 15 +++++++++------
+ 1 file changed, 9 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -506,14 +506,19 @@ static bool mt7531_dual_sgmii_supported(
+ static int
+ mt7531_pad_setup(struct dsa_switch *ds, phy_interface_t interface)
+ {
+- struct mt7530_priv *priv = ds->priv;
++ return 0;
++}
++
++static void
++mt7531_pll_setup(struct mt7530_priv *priv)
++{
+ u32 top_sig;
+ u32 hwstrap;
+ u32 xtal;
+ u32 val;
+
+ if (mt7531_dual_sgmii_supported(priv))
+- return 0;
++ return;
+
+ val = mt7530_read(priv, MT7531_CREV);
+ top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR);
+@@ -592,8 +597,6 @@ mt7531_pad_setup(struct dsa_switch *ds,
+ val |= EN_COREPLL;
+ mt7530_write(priv, MT7531_PLLGP_EN, val);
+ usleep_range(25, 35);
+-
+- return 0;
+ }
+
+ static void
+@@ -2326,6 +2329,8 @@ mt7531_setup(struct dsa_switch *ds)
+ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
+ SYS_CTRL_REG_RST);
+
++ mt7531_pll_setup(priv);
++
+ if (mt7531_dual_sgmii_supported(priv)) {
+ priv->p5_intf_sel = P5_INTF_SEL_GMAC5_SGMII;
+
+@@ -2882,8 +2887,6 @@ mt7531_cpu_port_config(struct dsa_switch
+ case 6:
+ interface = PHY_INTERFACE_MODE_2500BASEX;
+
+- mt7531_pad_setup(ds, interface);
+-
+ priv->p6_interface = interface;
+ break;
+ default:
--- /dev/null
+From 3fb8841513c4ec3a2e5d366df86230c45f239a57 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Sat, 13 Aug 2022 13:08:22 +0200
+Subject: [PATCH 03/10] net: mt7531: ensure all MACs are powered down before
+ reset
+
+The datasheet [1] explicit describes it as requirement for a reset.
+
+[1] MT7531 Reference Manual for Development Board rev 1.0, page 735
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/dsa/mt7530.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/net/dsa/mt7530.c
++++ b/drivers/net/dsa/mt7530.c
+@@ -2324,6 +2324,10 @@ mt7531_setup(struct dsa_switch *ds)
+ return -ENODEV;
+ }
+
++ /* all MACs must be forced link-down before sw reset */
++ for (i = 0; i < MT7530_NUM_PORTS; i++)
++ mt7530_write(priv, MT7530_PMCR_P(i), MT7531_FORCE_LNK);
++
+ /* Reset the switch through internal reset */
+ mt7530_write(priv, MT7530_SYS_CTRL,
+ SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
--- /dev/null
+From cbfed00575d15eafd85efd9619b7ecc0836a4aa7 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Sat, 13 Aug 2022 14:42:12 +0200
+Subject: [PATCH 04/10] net: mtk_sgmii: implement mtk_pcs_ops
+
+Implement mtk_pcs_ops for the SGMII pcs to read the current state
+of the hardware.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+[added DUPLEX_FULL]
+Signed-off-by: Daniel Golle <daniel@makrotopia.org>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -122,10 +122,26 @@ static void mtk_pcs_link_up(struct phyli
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+ }
+
++static void mtk_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state)
++{
++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
++ unsigned int val;
++
++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
++ state->speed = val & RG_PHY_SPEED_3_125G ? SPEED_2500 : SPEED_1000;
++
++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
++ state->an_complete = !!(val & SGMII_AN_COMPLETE);
++ state->link = !!(val & SGMII_LINK_STATYS);
++ state->duplex = DUPLEX_FULL;
++ state->pause = 0;
++}
++
+ static const struct phylink_pcs_ops mtk_pcs_ops = {
+ .pcs_config = mtk_pcs_config,
+ .pcs_an_restart = mtk_pcs_restart_an,
+ .pcs_link_up = mtk_pcs_link_up,
++ .pcs_get_state = mtk_pcs_get_state,
+ };
+
+ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
--- /dev/null
+From 7f75f43fe2159123baa101fcc8c6faa0b0a4c598 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Sat, 13 Aug 2022 14:48:51 +0200
+Subject: [PATCH 05/10] net: mtk_sgmii: fix powering up the SGMII phy
+
+There are certain race condition when the SGMII_PHYA_PWD register still
+contains 0x9 which prevents the SGMII from working properly.
+
+The SGMII still shows link but no traffic can flow.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -36,9 +36,7 @@ static int mtk_pcs_setup_mode_an(struct
+ val |= SGMII_AN_RESTART;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+- val &= ~SGMII_PHYA_PWD;
+- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+ return 0;
+
+@@ -70,9 +68,7 @@ static int mtk_pcs_setup_mode_force(stru
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+ /* Release PHYA power down state */
+- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
+- val &= ~SGMII_PHYA_PWD;
+- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+ return 0;
+ }
--- /dev/null
+From 9daea9b71d060d93d7394ac465b2e5ee0b7e7bca Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Mon, 15 Aug 2022 16:02:01 +0200
+Subject: [PATCH 06/10] net: mtk_sgmii: ensure the SGMII PHY is powered down on
+ configuration
+
+The code expect the PHY to be in power down (which is only true after reset).
+Allow the changes of SGMII parameters more than once.
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -7,6 +7,7 @@
+ *
+ */
+
++#include <linux/delay.h>
+ #include <linux/mfd/syscon.h>
+ #include <linux/of.h>
+ #include <linux/phylink.h>
+@@ -24,6 +25,9 @@ static int mtk_pcs_setup_mode_an(struct
+ {
+ unsigned int val;
+
++ /* PHYA power down */
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
++
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+@@ -36,6 +40,10 @@ static int mtk_pcs_setup_mode_an(struct
+ val |= SGMII_AN_RESTART;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
++ /* Release PHYA power down state
++ * unknown how much the QPHY needs but it is racy without a sleep
++ */
++ usleep_range(50, 100);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+ return 0;
+@@ -50,6 +58,9 @@ static int mtk_pcs_setup_mode_force(stru
+ {
+ unsigned int val;
+
++ /* PHYA power down */
++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
++
+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
+ val &= ~RG_PHY_SPEED_MASK;
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+@@ -67,7 +78,10 @@ static int mtk_pcs_setup_mode_force(stru
+ val |= SGMII_SPEED_1000;
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+- /* Release PHYA power down state */
++ /* Release PHYA power down state
++ * unknown how much the QPHY needs but it is racy without a sleep
++ */
++ usleep_range(50, 100);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
+
+ return 0;
--- /dev/null
+From e4dca7affb8c03438b63bdb5fddefd6ad2431cfd Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Mon, 15 Aug 2022 14:59:29 +0200
+Subject: [PATCH 07/10] net: mtk_sgmii: mtk_pcs_setup_mode_an: don't rely on
+ register defaults
+
+Ensure autonegotiation is enabled.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -32,12 +32,13 @@ static int mtk_pcs_setup_mode_an(struct
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+
++ /* disable remote fault & enable auto neg */
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+- val |= SGMII_REMOTE_FAULT_DIS;
++ val |= SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN;
+ regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
+
+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
+- val |= SGMII_AN_RESTART;
++ val |= SGMII_AN_RESTART | SGMII_AN_ENABLE;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+ /* Release PHYA power down state
--- /dev/null
+From 952b64575613d26163a5afa5ff8bfdb57840091b Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Mon, 15 Aug 2022 15:00:14 +0200
+Subject: [PATCH 08/10] net: mtk_sgmii: set the speed according to the phy
+ interface in AN
+
+The non auto-negotioting code path is setting the correct speed for the
+interface. Ensure auto-negotiation code path is doing it as well.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -21,13 +21,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st
+ }
+
+ /* For SGMII interface mode */
+-static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
++static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs, phy_interface_t interface)
+ {
+ unsigned int val;
+
+ /* PHYA power down */
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
+
++ /* Set SGMII phy speed */
++ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
++ val &= ~RG_PHY_SPEED_MASK;
++ if (interface == PHY_INTERFACE_MODE_2500BASEX)
++ val |= RG_PHY_SPEED_3_125G;
++ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
++
+ /* Setup the link timer and QPHY power up inside SGMIISYS */
+ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
+ SGMII_LINK_TIMER_DEFAULT);
+@@ -100,7 +107,7 @@ static int mtk_pcs_config(struct phylink
+ if (interface != PHY_INTERFACE_MODE_SGMII)
+ err = mtk_pcs_setup_mode_force(mpcs, interface);
+ else if (phylink_autoneg_inband(mode))
+- err = mtk_pcs_setup_mode_an(mpcs);
++ err = mtk_pcs_setup_mode_an(mpcs, interface);
+
+ return err;
+ }
--- /dev/null
+From 06773f19cffd6c9d34dcbc8320169afef5ab60ba Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Mon, 15 Aug 2022 13:58:07 +0200
+Subject: [PATCH 09/10] net: mtk_eth_soc: improve comment
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -80,7 +80,8 @@ static int mtk_pcs_setup_mode_force(stru
+ val &= ~SGMII_AN_ENABLE;
+ regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
+
+- /* Set the speed etc but leave the duplex unchanged */
++ /* Set the speed etc but leave the duplex unchanged.
++ * The SGMII mode for 2.5gbit is the same as for 1gbit, expect the speed in ANA_RGC3 */
+ regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
+ val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
+ val |= SGMII_SPEED_1000;
--- /dev/null
+From 95dcd0f223d7cab6e25bc19088016e5eb4ca1804 Mon Sep 17 00:00:00 2001
+From: Alexander Couzens <lynxis@fe80.eu>
+Date: Tue, 16 Aug 2022 00:22:11 +0200
+Subject: [PATCH 10/10] mtk_sgmii: enable PCS polling to allow SFP work
+
+Currently there is no IRQ handling (even the SGMII supports it).
+Enable polling to support SFP ports.
+
+Signed-off-by: Alexander Couzens <lynxis@fe80.eu>
+---
+ drivers/net/ethernet/mediatek/mtk_sgmii.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
+@@ -180,6 +180,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss,
+ return PTR_ERR(ss->pcs[i].regmap);
+
+ ss->pcs[i].pcs.ops = &mtk_pcs_ops;
++ ss->pcs[i].pcs.poll = 1;
+ }
+
+ return 0;