net: dsa: mv88e6xxx: fix Serdes link changes
authorRussell King <rmk+kernel@armlinux.org.uk>
Sat, 14 Mar 2020 10:15:48 +0000 (10:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 16 Mar 2020 00:11:12 +0000 (17:11 -0700)
phylink_mac_change() is supposed to be called with a 'false' argument
if the link has gone down since it was last reported up; this is to
ensure that link events along with renegotiation events are always
correctly reported to userspace.

Read the BMSR once when we have an interrupt, and report the link
latched status to phylink via phylink_mac_change().  phylink will deal
automatically with re-reading the link state once it has processed the
link-down event.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/serdes.c
drivers/net/dsa/mv88e6xxx/serdes.h

index 6c7b031..2098f19 100644 (file)
@@ -340,26 +340,17 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
 
 static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
 {
-       struct dsa_switch *ds = chip->ds;
-       u16 status;
-       bool up;
+       u16 bmsr;
        int err;
 
-       err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
-       if (err)
-               return;
-
-       /* Status must be read twice in order to give the current link
-        * status. Otherwise the change in link status since the last
-        * read of the register is returned.
-        */
-       err = mv88e6352_serdes_read(chip, MII_BMSR, &status);
-       if (err)
+       /* If the link has dropped, we want to know about it. */
+       err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr);
+       if (err) {
+               dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
                return;
+       }
 
-       up = status & BMSR_LSTATUS;
-
-       dsa_port_phylink_mac_change(ds, port, up);
+       dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
 }
 
 irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
@@ -833,18 +824,18 @@ int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
 static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
                                            int port, u8 lane)
 {
-       u16 status;
+       u16 bmsr;
        int err;
 
+       /* If the link has dropped, we want to know about it. */
        err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
-                                   MV88E6390_SGMII_PHY_STATUS, &status);
+                                   MV88E6390_SGMII_BMSR, &bmsr);
        if (err) {
-               dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
+               dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
                return;
        }
 
-       dsa_port_phylink_mac_change(chip->ds, port,
-                               !!(status & MV88E6390_SGMII_PHY_STATUS_LINK));
+       dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
 }
 
 static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
index a0c9532..7990cad 100644 (file)
@@ -48,6 +48,7 @@
 
 /* 1000BASE-X and SGMII */
 #define MV88E6390_SGMII_BMCR           (0x2000 + MII_BMCR)
+#define MV88E6390_SGMII_BMSR           (0x2000 + MII_BMSR)
 #define MV88E6390_SGMII_ADVERTISE      (0x2000 + MII_ADVERTISE)
 #define MV88E6390_SGMII_LPA            (0x2000 + MII_LPA)
 #define MV88E6390_SGMII_INT_ENABLE     0xa001