--- /dev/null
+From 93fd8207bed80ce19aaf59932cbe1c03d418a37d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:49 +0100
+Subject: net: dsa: mv88e6xxx: Add fix for erratum 5.2 of 88E6393X family
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit 93fd8207bed80ce19aaf59932cbe1c03d418a37d upstream.
+
+Add fix for erratum 5.2 of the 88E6393X (Amethyst) family: for 10gbase-r
+mode, some undocumented registers need to be written some special
+values.
+
+Fixes: de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family")
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 48 +++++++++++++++++++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -1375,6 +1375,50 @@ static int mv88e6393x_serdes_erratum_4_8
+ MV88E6393X_ERRATA_4_8_REG, reg);
+ }
+
++static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane,
++ u8 cmode)
++{
++ static const struct {
++ u16 dev, reg, val, mask;
++ } fixes[] = {
++ { MDIO_MMD_VEND1, 0x8093, 0xcb5a, 0xffff },
++ { MDIO_MMD_VEND1, 0x8171, 0x7088, 0xffff },
++ { MDIO_MMD_VEND1, 0x80c9, 0x311a, 0xffff },
++ { MDIO_MMD_VEND1, 0x80a2, 0x8000, 0xff7f },
++ { MDIO_MMD_VEND1, 0x80a9, 0x0000, 0xfff0 },
++ { MDIO_MMD_VEND1, 0x80a3, 0x0000, 0xf8ff },
++ { MDIO_MMD_PHYXS, MV88E6393X_SERDES_POC,
++ MV88E6393X_SERDES_POC_RESET, MV88E6393X_SERDES_POC_RESET },
++ };
++ int err, i;
++ u16 reg;
++
++ /* mv88e6393x family errata 5.2:
++ * For optimal signal integrity the following sequence should be applied
++ * to SERDES operating in 10G mode. These registers only apply to 10G
++ * operation and have no effect on other speeds.
++ */
++ if (cmode != MV88E6393X_PORT_STS_CMODE_10GBASER)
++ return 0;
++
++ for (i = 0; i < ARRAY_SIZE(fixes); ++i) {
++ err = mv88e6390_serdes_read(chip, lane, fixes[i].dev,
++ fixes[i].reg, ®);
++ if (err)
++ return err;
++
++ reg &= ~fixes[i].mask;
++ reg |= fixes[i].val;
++
++ err = mv88e6390_serdes_write(chip, lane, fixes[i].dev,
++ fixes[i].reg, reg);
++ if (err)
++ return err;
++ }
++
++ return 0;
++}
++
+ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ bool on)
+ {
+@@ -1389,6 +1433,10 @@ int mv88e6393x_serdes_power(struct mv88e
+ if (err)
+ return err;
+
++ err = mv88e6393x_serdes_erratum_5_2(chip, lane, cmode);
++ if (err)
++ return err;
++
+ err = mv88e6393x_serdes_power_lane(chip, lane, true);
+ if (err)
+ return err;
--- /dev/null
+From 8c3318b4874e2dee867f5ae8f6d38f78e044bf71 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:47 +0100
+Subject: net: dsa: mv88e6xxx: Drop unnecessary check in mv88e6393x_serdes_erratum_4_6()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit 8c3318b4874e2dee867f5ae8f6d38f78e044bf71 upstream.
+
+The check for lane is unnecessary, since the function is called only
+with allowed lane argument.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 28 +++++++++++-----------------
+ 1 file changed, 11 insertions(+), 17 deletions(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -1284,26 +1284,20 @@ static int mv88e6393x_serdes_erratum_4_6
+ * It seems that after this workaround the SERDES is automatically
+ * powered up (the bit is cleared), so power it down.
+ */
+- if (lane == MV88E6393X_PORT0_LANE || lane == MV88E6393X_PORT9_LANE ||
+- lane == MV88E6393X_PORT10_LANE) {
+- err = mv88e6390_serdes_read(chip, lane,
+- MDIO_MMD_PHYXS,
+- MV88E6393X_SERDES_POC, ®);
+- if (err)
+- return err;
++ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_POC, ®);
++ if (err)
++ return err;
+
+- reg &= ~MV88E6393X_SERDES_POC_PDOWN;
+- reg |= MV88E6393X_SERDES_POC_RESET;
++ reg &= ~MV88E6393X_SERDES_POC_PDOWN;
++ reg |= MV88E6393X_SERDES_POC_RESET;
+
+- err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+- MV88E6393X_SERDES_POC, reg);
+- if (err)
+- return err;
++ err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_POC, reg);
++ if (err)
++ return err;
+
+- return mv88e6390_serdes_power_sgmii(chip, lane, false);
+- }
+-
+- return 0;
++ return mv88e6390_serdes_power_sgmii(chip, lane, false);
+ }
+
+ int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
--- /dev/null
+From 21635d9203e1cf2b73b67e9a86059a62f62a3563 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:46 +0100
+Subject: net: dsa: mv88e6xxx: Fix application of erratum 4.8 for 88E6393X
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit 21635d9203e1cf2b73b67e9a86059a62f62a3563 upstream.
+
+According to SERDES scripts for 88E6393X, erratum 4.8 has to be applied
+every time before SerDes is powered on.
+
+Split the code for erratum 4.8 into separate function and call it in
+mv88e6393x_serdes_power().
+
+Fixes: de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family")
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 53 +++++++++++++++++++++++--------------
+ 1 file changed, 33 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -1271,9 +1271,9 @@ void mv88e6390_serdes_get_regs(struct mv
+ }
+ }
+
+-static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
++static int mv88e6393x_serdes_erratum_4_6(struct mv88e6xxx_chip *chip, int lane)
+ {
+- u16 reg, pcs;
++ u16 reg;
+ int err;
+
+ /* mv88e6393x family errata 4.6:
+@@ -1300,11 +1300,32 @@ static int mv88e6393x_serdes_port_errata
+ if (err)
+ return err;
+
+- err = mv88e6390_serdes_power_sgmii(chip, lane, false);
+- if (err)
+- return err;
++ return mv88e6390_serdes_power_sgmii(chip, lane, false);
+ }
+
++ return 0;
++}
++
++int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
++{
++ int err;
++
++ err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT0_LANE);
++ if (err)
++ return err;
++
++ err = mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT9_LANE);
++ if (err)
++ return err;
++
++ return mv88e6393x_serdes_erratum_4_6(chip, MV88E6393X_PORT10_LANE);
++}
++
++static int mv88e6393x_serdes_erratum_4_8(struct mv88e6xxx_chip *chip, int lane)
++{
++ u16 reg, pcs;
++ int err;
++
+ /* mv88e6393x family errata 4.8:
+ * When a SERDES port is operating in 1000BASE-X or SGMII mode link may
+ * not come up after hardware reset or software reset of SERDES core.
+@@ -1334,29 +1355,21 @@ static int mv88e6393x_serdes_port_errata
+ MV88E6393X_ERRATA_4_8_REG, reg);
+ }
+
+-int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
+-{
+- int err;
+-
+- err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT0_LANE);
+- if (err)
+- return err;
+-
+- err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT9_LANE);
+- if (err)
+- return err;
+-
+- return mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT10_LANE);
+-}
+-
+ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ bool on)
+ {
+ u8 cmode = chip->ports[port].cmode;
++ int err;
+
+ if (port != 0 && port != 9 && port != 10)
+ return -EOPNOTSUPP;
+
++ if (on) {
++ err = mv88e6393x_serdes_erratum_4_8(chip, lane);
++ if (err)
++ return err;
++ }
++
+ switch (cmode) {
+ case MV88E6XXX_PORT_STS_CMODE_SGMII:
+ case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
--- /dev/null
+From 163000dbc772c1eae9bdfe7c8fe30155db1efd74 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:50 +0100
+Subject: net: dsa: mv88e6xxx: Fix inband AN for 2500base-x on 88E6393X family
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit 163000dbc772c1eae9bdfe7c8fe30155db1efd74 upstream.
+
+Inband AN is broken on Amethyst in 2500base-x mode when set by standard
+mechanism (via cmode).
+
+(There probably is some weird setting done by default in the switch for
+ this mode that make it cycle in some state or something, because when
+ the peer is the mvneta controller, it receives link change interrupts
+ every ~0.3ms, but the link is always down.)
+
+Get around this by configuring the PCS mode to 1000base-x (where inband
+AN works), and then changing the SerDes frequency while SerDes
+transmitter and receiver are disabled, before enabling SerDes PHY. After
+disabling SerDes PHY, change the PCS mode back to 2500base-x, to avoid
+confusing the device (if we leave it at 1000base-x PCS mode but with
+different frequency, and then change cmode to sgmii, the device won't
+change the frequency because it thinks it already has the correct one).
+
+The register which changes the frequency is undocumented. I discovered
+it by going through all registers in the ranges 4.f000-4.f100 and
+1e.8000-1e.8200 for all SerDes cmodes (sgmii, 1000base-x, 2500base-x,
+5gbase-r, 10gbase-r, usxgmii) and filtering out registers that didn't
+make sense (the value was the same for modes which have different
+frequency). The result of this was:
+
+ reg sgmii 1000base-x 2500base-x 5gbase-r 10gbase-r usxgmii
+ 04.f002 005b 0058 0059 005c 005d 005f
+ 04.f076 3000 0000 1000 4000 5000 7000
+ 04.f07c 0950 0950 1850 0550 0150 0150
+ 1e.8000 0059 0059 0058 0055 0051 0051
+ 1e.8140 0e20 0e20 0e28 0e21 0e42 0e42
+
+Register 04.f002 is the documented Port Operational Confiuration
+register, it's last 3 bits select PCS type, so changing this register
+also changes the frequency to the appropriate value.
+
+Registers 04.f076 and 04.f07c are not writable.
+
+Undocumented register 1e.8000 was the one: changing bits 3:0 from 9 to 8
+changed SerDes frequency to 3.125 GHz, while leaving the value of PCS
+mode in register 04.f002.2:0 at 1000base-x. Inband autonegotiation
+started working correctly.
+
+(I didn't try anything with register 1e.8140 since 1e.8000 solved the
+ problem.)
+
+Since I don't have documentation for this register 1e.8000.3:0, I am
+using the constants without names, but my hypothesis is that this
+register selects PHY frequency. If in the future I have access to an
+oscilloscope able to handle these frequencies, I will try to test this
+hypothesis.
+
+Fixes: de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family")
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 61 ++++++++++++++++++++++++++++++++++++-
+ drivers/net/dsa/mv88e6xxx/serdes.h | 1
+ 2 files changed, 61 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -1419,6 +1419,54 @@ static int mv88e6393x_serdes_erratum_5_2
+ return 0;
+ }
+
++static int mv88e6393x_serdes_fix_2500basex_an(struct mv88e6xxx_chip *chip,
++ int lane, u8 cmode, bool on)
++{
++ u16 reg;
++ int err;
++
++ if (cmode != MV88E6XXX_PORT_STS_CMODE_2500BASEX)
++ return 0;
++
++ /* Inband AN is broken on Amethyst in 2500base-x mode when set by
++ * standard mechanism (via cmode).
++ * We can get around this by configuring the PCS mode to 1000base-x
++ * and then writing value 0x58 to register 1e.8000. (This must be done
++ * while SerDes receiver and transmitter are disabled, which is, when
++ * this function is called.)
++ * It seem that when we do this configuration to 2500base-x mode (by
++ * changing PCS mode to 1000base-x and frequency to 3.125 GHz from
++ * 1.25 GHz) and then configure to sgmii or 1000base-x, the device
++ * thinks that it already has SerDes at 1.25 GHz and does not change
++ * the 1e.8000 register, leaving SerDes at 3.125 GHz.
++ * To avoid this, change PCS mode back to 2500base-x when disabling
++ * SerDes from 2500base-x mode.
++ */
++ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_POC, ®);
++ if (err)
++ return err;
++
++ reg &= ~(MV88E6393X_SERDES_POC_PCS_MASK | MV88E6393X_SERDES_POC_AN);
++ if (on)
++ reg |= MV88E6393X_SERDES_POC_PCS_1000BASEX |
++ MV88E6393X_SERDES_POC_AN;
++ else
++ reg |= MV88E6393X_SERDES_POC_PCS_2500BASEX;
++ reg |= MV88E6393X_SERDES_POC_RESET;
++
++ err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_POC, reg);
++ if (err)
++ return err;
++
++ err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_VEND1, 0x8000, 0x58);
++ if (err)
++ return err;
++
++ return 0;
++}
++
+ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
+ bool on)
+ {
+@@ -1437,6 +1485,11 @@ int mv88e6393x_serdes_power(struct mv88e
+ if (err)
+ return err;
+
++ err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
++ true);
++ if (err)
++ return err;
++
+ err = mv88e6393x_serdes_power_lane(chip, lane, true);
+ if (err)
+ return err;
+@@ -1457,8 +1510,14 @@ int mv88e6393x_serdes_power(struct mv88e
+ if (err)
+ return err;
+
+- if (!on)
++ if (!on) {
+ err = mv88e6393x_serdes_power_lane(chip, lane, false);
++ if (err)
++ return err;
++
++ err = mv88e6393x_serdes_fix_2500basex_an(chip, lane, cmode,
++ false);
++ }
+
+ return err;
+ }
+--- a/drivers/net/dsa/mv88e6xxx/serdes.h
++++ b/drivers/net/dsa/mv88e6xxx/serdes.h
+@@ -93,6 +93,7 @@
+ #define MV88E6393X_SERDES_POC_PCS_MASK 0x0007
+ #define MV88E6393X_SERDES_POC_RESET BIT(15)
+ #define MV88E6393X_SERDES_POC_PDOWN BIT(5)
++#define MV88E6393X_SERDES_POC_AN BIT(3)
+ #define MV88E6393X_SERDES_CTRL1 0xf003
+ #define MV88E6393X_SERDES_CTRL1_TX_PDOWN BIT(9)
+ #define MV88E6393X_SERDES_CTRL1_RX_PDOWN BIT(8)
--- /dev/null
+From ede359d8843a2779d232ed30bc36089d4b5962e4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:51 +0100
+Subject: net: dsa: mv88e6xxx: Link in pcs_get_state() if AN is bypassed
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit ede359d8843a2779d232ed30bc36089d4b5962e4 upstream.
+
+Function mv88e6xxx_serdes_pcs_get_state() currently does not report link
+up if AN is enabled, Link bit is set, but Speed and Duplex Resolved bit
+is not set, which testing shows is the case for when auto-negotiation
+was bypassed (we have AN enabled but link partner does not).
+
+An example of such link partner is Marvell 88X3310 PHY, when put into
+the mode where host interface changes between 10gbase-r, 5gbase-r,
+2500base-x and sgmii according to copper speed. The 88X3310 does not
+enable AN in 2500base-x, and so SerDes on mv88e6xxx currently does not
+link with it.
+
+Fix this.
+
+Fixes: a5a6858b793f ("net: dsa: mv88e6xxx: extend phylink to Serdes PHYs")
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 48 ++++++++++++++++++++++++++++++++-----
+ 1 file changed, 42 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -50,11 +50,22 @@ static int mv88e6390_serdes_write(struct
+ }
+
+ static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
+- u16 status, u16 lpa,
++ u16 ctrl, u16 status, u16 lpa,
+ struct phylink_link_state *state)
+ {
++ state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
++
+ if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
+- state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
++ /* The Spped and Duplex Resolved register is 1 if AN is enabled
++ * and complete, or if AN is disabled. So with disabled AN we
++ * still get here on link up. But we want to set an_complete
++ * only if AN was enabled, thus we look at BMCR_ANENABLE.
++ * (According to 802.3-2008 section 22.2.4.2.10, we should be
++ * able to get this same value from BMSR_ANEGCAPABLE, but tests
++ * show that these Marvell PHYs don't conform to this part of
++ * the specificaion - BMSR_ANEGCAPABLE is simply always 1.)
++ */
++ state->an_complete = !!(ctrl & BMCR_ANENABLE);
+ state->duplex = status &
+ MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
+ DUPLEX_FULL : DUPLEX_HALF;
+@@ -81,6 +92,18 @@ static int mv88e6xxx_serdes_pcs_get_stat
+ dev_err(chip->dev, "invalid PHY speed\n");
+ return -EINVAL;
+ }
++ } else if (state->link &&
++ state->interface != PHY_INTERFACE_MODE_SGMII) {
++ /* If Speed and Duplex Resolved register is 0 and link is up, it
++ * means that AN was enabled, but link partner had it disabled
++ * and the PHY invoked the Auto-Negotiation Bypass feature and
++ * linked anyway.
++ */
++ state->duplex = DUPLEX_FULL;
++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
++ state->speed = SPEED_2500;
++ else
++ state->speed = SPEED_1000;
+ } else {
+ state->link = false;
+ }
+@@ -168,9 +191,15 @@ int mv88e6352_serdes_pcs_config(struct m
+ int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+ int lane, struct phylink_link_state *state)
+ {
+- u16 lpa, status;
++ u16 lpa, status, ctrl;
+ int err;
+
++ err = mv88e6352_serdes_read(chip, MII_BMCR, &ctrl);
++ if (err) {
++ dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
++ return err;
++ }
++
+ err = mv88e6352_serdes_read(chip, 0x11, &status);
+ if (err) {
+ dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
+@@ -183,7 +212,7 @@ int mv88e6352_serdes_pcs_get_state(struc
+ return err;
+ }
+
+- return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
++ return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
+ }
+
+ int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+@@ -883,10 +912,17 @@ int mv88e6390_serdes_pcs_config(struct m
+ static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
+ int port, int lane, struct phylink_link_state *state)
+ {
+- u16 lpa, status;
++ u16 lpa, status, ctrl;
+ int err;
+
+ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6390_SGMII_BMCR, &ctrl);
++ if (err) {
++ dev_err(chip->dev, "can't read Serdes PHY control: %d\n", err);
++ return err;
++ }
++
++ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+ MV88E6390_SGMII_PHY_STATUS, &status);
+ if (err) {
+ dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
+@@ -900,7 +936,7 @@ static int mv88e6390_serdes_pcs_get_stat
+ return err;
+ }
+
+- return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
++ return mv88e6xxx_serdes_pcs_get_state(chip, ctrl, status, lpa, state);
+ }
+
+ static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
--- /dev/null
+From 7527d66260ac0c603c6baca5146748061fcddbd6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Marek=20Beh=C3=BAn?= <kabel@kernel.org>
+Date: Tue, 30 Nov 2021 18:01:48 +0100
+Subject: net: dsa: mv88e6xxx: Save power by disabling SerDes trasmitter and receiver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Marek Behún <kabel@kernel.org>
+
+commit 7527d66260ac0c603c6baca5146748061fcddbd6 upstream.
+
+Save power on 88E6393X by disabling SerDes receiver and transmitter
+after SerDes is SerDes is disabled.
+
+Signed-off-by: Marek Behún <kabel@kernel.org>
+Cc: stable@vger.kernel.org # de776d0d316f ("net: dsa: mv88e6xxx: add support for mv88e6393x family")
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/dsa/mv88e6xxx/serdes.c | 46 +++++++++++++++++++++++++++++++++----
+ drivers/net/dsa/mv88e6xxx/serdes.h | 3 ++
+ 2 files changed, 45 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/dsa/mv88e6xxx/serdes.c
++++ b/drivers/net/dsa/mv88e6xxx/serdes.c
+@@ -1271,6 +1271,28 @@ void mv88e6390_serdes_get_regs(struct mv
+ }
+ }
+
++static int mv88e6393x_serdes_power_lane(struct mv88e6xxx_chip *chip, int lane,
++ bool on)
++{
++ u16 reg;
++ int err;
++
++ err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_CTRL1, ®);
++ if (err)
++ return err;
++
++ if (on)
++ reg &= ~(MV88E6393X_SERDES_CTRL1_TX_PDOWN |
++ MV88E6393X_SERDES_CTRL1_RX_PDOWN);
++ else
++ reg |= MV88E6393X_SERDES_CTRL1_TX_PDOWN |
++ MV88E6393X_SERDES_CTRL1_RX_PDOWN;
++
++ return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
++ MV88E6393X_SERDES_CTRL1, reg);
++}
++
+ static int mv88e6393x_serdes_erratum_4_6(struct mv88e6xxx_chip *chip, int lane)
+ {
+ u16 reg;
+@@ -1297,7 +1319,11 @@ static int mv88e6393x_serdes_erratum_4_6
+ if (err)
+ return err;
+
+- return mv88e6390_serdes_power_sgmii(chip, lane, false);
++ err = mv88e6390_serdes_power_sgmii(chip, lane, false);
++ if (err)
++ return err;
++
++ return mv88e6393x_serdes_power_lane(chip, lane, false);
+ }
+
+ int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
+@@ -1362,17 +1388,29 @@ int mv88e6393x_serdes_power(struct mv88e
+ err = mv88e6393x_serdes_erratum_4_8(chip, lane);
+ if (err)
+ return err;
++
++ err = mv88e6393x_serdes_power_lane(chip, lane, true);
++ if (err)
++ return err;
+ }
+
+ switch (cmode) {
+ case MV88E6XXX_PORT_STS_CMODE_SGMII:
+ case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
+ case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+- return mv88e6390_serdes_power_sgmii(chip, lane, on);
++ err = mv88e6390_serdes_power_sgmii(chip, lane, on);
++ break;
+ case MV88E6393X_PORT_STS_CMODE_5GBASER:
+ case MV88E6393X_PORT_STS_CMODE_10GBASER:
+- return mv88e6390_serdes_power_10g(chip, lane, on);
++ err = mv88e6390_serdes_power_10g(chip, lane, on);
++ break;
+ }
+
+- return 0;
++ if (err)
++ return err;
++
++ if (!on)
++ err = mv88e6393x_serdes_power_lane(chip, lane, false);
++
++ return err;
+ }
+--- a/drivers/net/dsa/mv88e6xxx/serdes.h
++++ b/drivers/net/dsa/mv88e6xxx/serdes.h
+@@ -93,6 +93,9 @@
+ #define MV88E6393X_SERDES_POC_PCS_MASK 0x0007
+ #define MV88E6393X_SERDES_POC_RESET BIT(15)
+ #define MV88E6393X_SERDES_POC_PDOWN BIT(5)
++#define MV88E6393X_SERDES_CTRL1 0xf003
++#define MV88E6393X_SERDES_CTRL1_TX_PDOWN BIT(9)
++#define MV88E6393X_SERDES_CTRL1_RX_PDOWN BIT(8)
+
+ #define MV88E6393X_ERRATA_4_8_REG 0xF074
+ #define MV88E6393X_ERRATA_4_8_BIT BIT(14)
kvm-x86-use-vcpu-arch.walk_mmu-for-kvm_mmu_invlpg.patch
kvm-x86-check-pir-even-for-vcpus-with-disabled-apicv.patch
tracing-histograms-string-compares-should-not-care-about-signed-values.patch
+net-dsa-mv88e6xxx-fix-application-of-erratum-4.8-for-88e6393x.patch
+net-dsa-mv88e6xxx-drop-unnecessary-check-in-mv88e6393x_serdes_erratum_4_6.patch
+net-dsa-mv88e6xxx-save-power-by-disabling-serdes-trasmitter-and-receiver.patch
+net-dsa-mv88e6xxx-add-fix-for-erratum-5.2-of-88e6393x-family.patch
+net-dsa-mv88e6xxx-fix-inband-an-for-2500base-x-on-88e6393x-family.patch
+net-dsa-mv88e6xxx-link-in-pcs_get_state-if-an-is-bypassed.patch