From f5be107c333878b09c2f682967e92f98f7fc80be Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Tue, 24 Nov 2020 17:34:38 +1300 Subject: net: dsa: mv88e6xxx: Support serdes ports on MV88E6097/6095/6185 Implement serdes_power, serdes_get_lane and serdes_pcs_get_state ops for the MV88E6097/6095/6185 so that ports 8 & 9 can be supported as serdes ports and directly connected to other network interfaces or to SFPs without a PHY. Signed-off-by: Chris Packham Reviewed-by: Andrew Lunn Signed-off-by: Jakub Kicinski --- drivers/net/dsa/mv88e6xxx/chip.c | 9 ++++++ drivers/net/dsa/mv88e6xxx/serdes.c | 62 ++++++++++++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/serdes.h | 5 +++ 3 files changed, 76 insertions(+) (limited to 'drivers/net/dsa/mv88e6xxx') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 296932b2b80d..545eb9c6c3fc 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3263,6 +3263,9 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .stats_get_strings = mv88e6095_stats_get_strings, .stats_get_stats = mv88e6095_stats_get_stats, .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, + .serdes_power = mv88e6185_serdes_power, + .serdes_get_lane = mv88e6185_serdes_get_lane, + .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, .reset = mv88e6185_g1_reset, @@ -3302,6 +3305,9 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu, + .serdes_power = mv88e6185_serdes_power, + .serdes_get_lane = mv88e6185_serdes_get_lane, + .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .pot_clear = mv88e6xxx_g2_pot_clear, .reset = mv88e6352_g1_reset, .rmu_disable = mv88e6085_g1_rmu_disable, @@ -3736,6 +3742,9 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .set_egress_port = mv88e6095_g1_set_egress_port, .watchdog_ops = &mv88e6097_watchdog_ops, .mgmt_rsvd2cpu = mv88e6185_g2_mgmt_rsvd2cpu, + .serdes_power = mv88e6185_serdes_power, + .serdes_get_lane = mv88e6185_serdes_get_lane, + .serdes_pcs_get_state = mv88e6185_serdes_pcs_get_state, .set_cascade_port = mv88e6185_g1_set_cascade_port, .ppu_enable = mv88e6185_g1_ppu_enable, .ppu_disable = mv88e6185_g1_ppu_disable, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 9c07b4f3d345..d4f40a739b17 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -428,6 +428,68 @@ u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) return lane; } +int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool up) +{ + /* The serdes power can't be controlled on this switch chip but we need + * to supply this function to avoid returning -EOPNOTSUPP in + * mv88e6xxx_serdes_power_up/mv88e6xxx_serdes_power_down + */ + return 0; +} + +u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + /* There are no configurable serdes lanes on this switch chip but we + * need to return non-zero so that callers of + * mv88e6xxx_serdes_get_lane() know this is a serdes port. + */ + switch (chip->ports[port].cmode) { + case MV88E6185_PORT_STS_CMODE_SERDES: + case MV88E6185_PORT_STS_CMODE_1000BASE_X: + return 0xff; + default: + return 0; + } +} + +int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state) +{ + int err; + u16 status; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status); + if (err) + return err; + + state->link = !!(status & MV88E6XXX_PORT_STS_LINK); + + if (state->link) { + state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ? DUPLEX_FULL : DUPLEX_HALF; + + switch (status & MV88E6XXX_PORT_STS_SPEED_MASK) { + case MV88E6XXX_PORT_STS_SPEED_1000: + state->speed = SPEED_1000; + break; + case MV88E6XXX_PORT_STS_SPEED_100: + state->speed = SPEED_100; + break; + case MV88E6XXX_PORT_STS_SPEED_10: + state->speed = SPEED_10; + break; + default: + dev_err(chip->dev, "invalid PHY speed\n"); + return -EINVAL; + } + } else { + state->duplex = DUPLEX_UNKNOWN; + state->speed = SPEED_UNKNOWN; + } + + return 0; +} + u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode = chip->ports[port].cmode; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 14315f26228a..c24ec4122c9e 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -73,6 +73,7 @@ #define MV88E6390_PG_CONTROL 0xf010 #define MV88E6390_PG_CONTROL_ENABLE_PC BIT(0) +u8 mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); @@ -85,6 +86,8 @@ int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, u8 lane, unsigned int mode, phy_interface_t interface, const unsigned long *advertise); +int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, + u8 lane, struct phylink_link_state *state); int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, u8 lane, struct phylink_link_state *state); int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, @@ -101,6 +104,8 @@ unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port); unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port); +int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, + bool up); int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, bool on); int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, -- cgit