diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/serdes.c')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/serdes.c | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 72faec8f44dc..80167d53212f 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -683,7 +683,8 @@ int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX || cmode == MV88E6393X_PORT_STS_CMODE_5GBASER || - cmode == MV88E6393X_PORT_STS_CMODE_10GBASER) + cmode == MV88E6393X_PORT_STS_CMODE_10GBASER || + cmode == MV88E6393X_PORT_STS_CMODE_USXGMII) lane = port; return lane; @@ -984,7 +985,42 @@ static int mv88e6393x_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip, state->speed = SPEED_10000; state->duplex = DUPLEX_FULL; } + return 0; +} + +/* USXGMII registers for Marvell switch 88e639x are undocumented and this function is based + * on some educated guesses. It appears that there are no status bits related to + * autonegotiation complete or flow control. + */ +static int mv88e639x_serdes_pcs_get_state_usxgmii(struct mv88e6xxx_chip *chip, + int port, int lane, + struct phylink_link_state *state) +{ + u16 status, lp_status; + int err; + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_USXGMII_PHY_STATUS, &status); + if (err) { + dev_err(chip->dev, "can't read Serdes USXGMII PHY status: %d\n", err); + return err; + } + dev_dbg(chip->dev, "USXGMII PHY status: 0x%x\n", status); + + state->link = !!(status & MDIO_USXGMII_LINK); + state->an_complete = state->link; + + if (state->link) { + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_USXGMII_LP_STATUS, &lp_status); + if (err) { + dev_err(chip->dev, "can't read Serdes USXGMII LP status: %d\n", err); + return err; + } + dev_dbg(chip->dev, "USXGMII LP status: 0x%x\n", lp_status); + /* lp_status appears to include the "link" bit as per USXGMII spec. */ + phylink_decode_usxgmii_word(state, lp_status); + } return 0; } @@ -1020,6 +1056,9 @@ int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, case PHY_INTERFACE_MODE_10GBASER: return mv88e6393x_serdes_pcs_get_state_10g(chip, port, lane, state); + case PHY_INTERFACE_MODE_USXGMII: + return mv88e639x_serdes_pcs_get_state_usxgmii(chip, port, lane, + state); default: return -EOPNOTSUPP; @@ -1173,6 +1212,7 @@ int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); case MV88E6393X_PORT_STS_CMODE_5GBASER: case MV88E6393X_PORT_STS_CMODE_10GBASER: + case MV88E6393X_PORT_STS_CMODE_USXGMII: return mv88e6393x_serdes_irq_enable_10g(chip, lane, enable); } @@ -1213,6 +1253,7 @@ irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, break; case MV88E6393X_PORT_STS_CMODE_5GBASER: case MV88E6393X_PORT_STS_CMODE_10GBASER: + case MV88E6393X_PORT_STS_CMODE_USXGMII: err = mv88e6393x_serdes_irq_status_10g(chip, lane, &status); if (err) return err; @@ -1477,7 +1518,8 @@ static int mv88e6393x_serdes_erratum_5_2(struct mv88e6xxx_chip *chip, int lane, * 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) + if (cmode != MV88E6393X_PORT_STS_CMODE_10GBASER && + cmode != MV88E6393X_PORT_STS_CMODE_USXGMII) return 0; for (i = 0; i < ARRAY_SIZE(fixes); ++i) { @@ -1582,6 +1624,7 @@ int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane, break; case MV88E6393X_PORT_STS_CMODE_5GBASER: case MV88E6393X_PORT_STS_CMODE_10GBASER: + case MV88E6393X_PORT_STS_CMODE_USXGMII: err = mv88e6390_serdes_power_10g(chip, lane, on); break; default: |