net: dsa: mv88e6xxx: implement port_link_state for mv88e6250
authorRasmus Villemoes <rasmus.villemoes@prevas.dk>
Tue, 4 Jun 2019 07:34:30 +0000 (07:34 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 5 Jun 2019 03:07:57 +0000 (20:07 -0700)
The mv88e6250 has a rather different way of reporting the link, speed
and duplex status. A simple difference is that the link bit is bit 12
rather than bit 11 of the port status register.

It gets more complicated for speed and duplex, which do not have
separate fields. Instead, there's a four-bit PortMode field, and
decoding that depends on whether it's a phy or mii port. For the phy
ports, only four of the 16 values have defined meaning; the rest are
called "reserved", so returning {SPEED,DUPLEX}_UNKNOWN seems
reasonable.

For the mii ports, most possible values are documented (0x3 and 0x5
are reserved), but I'm unable to make sense of them all. Since the
bits simply reflect the Px_MODE[3:0] configuration pins, just support
the subset that I'm certain about. Support for other setups can be
added later.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/port.c
drivers/net/dsa/mv88e6xxx/port.h

index a41bca1..a9a3080 100644 (file)
@@ -533,6 +533,71 @@ int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode)
        return 0;
 }
 
+int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port,
+                             struct phylink_link_state *state)
+{
+       int err;
+       u16 reg;
+
+       err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
+       if (err)
+               return err;
+
+       if (port < 5) {
+               switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) {
+               case MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF:
+                       state->speed = SPEED_10;
+                       state->duplex = DUPLEX_HALF;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF:
+                       state->speed = SPEED_100;
+                       state->duplex = DUPLEX_HALF;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL:
+                       state->speed = SPEED_10;
+                       state->duplex = DUPLEX_FULL;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL:
+                       state->speed = SPEED_100;
+                       state->duplex = DUPLEX_FULL;
+                       break;
+               default:
+                       state->speed = SPEED_UNKNOWN;
+                       state->duplex = DUPLEX_UNKNOWN;
+                       break;
+               }
+       } else {
+               switch (reg & MV88E6250_PORT_STS_PORTMODE_MASK) {
+               case MV88E6250_PORT_STS_PORTMODE_MII_10_HALF:
+                       state->speed = SPEED_10;
+                       state->duplex = DUPLEX_HALF;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_MII_100_HALF:
+                       state->speed = SPEED_100;
+                       state->duplex = DUPLEX_HALF;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_MII_10_FULL:
+                       state->speed = SPEED_10;
+                       state->duplex = DUPLEX_FULL;
+                       break;
+               case MV88E6250_PORT_STS_PORTMODE_MII_100_FULL:
+                       state->speed = SPEED_100;
+                       state->duplex = DUPLEX_FULL;
+                       break;
+               default:
+                       state->speed = SPEED_UNKNOWN;
+                       state->duplex = DUPLEX_UNKNOWN;
+                       break;
+               }
+       }
+
+       state->link = !!(reg & MV88E6250_PORT_STS_LINK);
+       state->an_enabled = 1;
+       state->an_complete = state->link;
+
+       return 0;
+}
+
 int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
                              struct phylink_link_state *state)
 {
index 1957e3e..3a89c28 100644 (file)
 #define MV88E6XXX_PORT_STS_MY_PAUSE            0x4000
 #define MV88E6XXX_PORT_STS_HD_FLOW             0x2000
 #define MV88E6XXX_PORT_STS_PHY_DETECT          0x1000
+#define MV88E6250_PORT_STS_LINK                                0x1000
+#define MV88E6250_PORT_STS_PORTMODE_MASK               0x0f00
+#define MV88E6250_PORT_STS_PORTMODE_PHY_10_HALF                0x0800
+#define MV88E6250_PORT_STS_PORTMODE_PHY_100_HALF       0x0900
+#define MV88E6250_PORT_STS_PORTMODE_PHY_10_FULL                0x0a00
+#define MV88E6250_PORT_STS_PORTMODE_PHY_100_FULL       0x0b00
+#define MV88E6250_PORT_STS_PORTMODE_MII_10_HALF                0x0c00
+#define MV88E6250_PORT_STS_PORTMODE_MII_100_HALF       0x0d00
+#define MV88E6250_PORT_STS_PORTMODE_MII_10_FULL                0x0e00
+#define MV88E6250_PORT_STS_PORTMODE_MII_100_FULL       0x0f00
 #define MV88E6XXX_PORT_STS_LINK                        0x0800
 #define MV88E6XXX_PORT_STS_DUPLEX              0x0400
 #define MV88E6XXX_PORT_STS_SPEED_MASK          0x0300
@@ -333,6 +343,8 @@ int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
 int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode);
 int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port,
                              struct phylink_link_state *state);
+int mv88e6250_port_link_state(struct mv88e6xxx_chip *chip, int port,
+                             struct phylink_link_state *state);
 int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port,
                              struct phylink_link_state *state);
 int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port);