diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0b5a2c31f3951c6b3b10bb2dda008f5f5ad1a04a..17752316ab1027ad3f8bb54e6083f8b69de96ca0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -524,7 +524,7 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) } static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, - int link, int speed, int duplex, + int link, int speed, int duplex, int pause, phy_interface_t mode) { int err; @@ -543,6 +543,12 @@ static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, goto restore_link; } + if (chip->info->ops->port_set_pause) { + err = chip->info->ops->port_set_pause(chip, port, pause); + if (err) + goto restore_link; + } + if (chip->info->ops->port_set_duplex) { err = chip->info->ops->port_set_duplex(chip, port, duplex); if (err && err != -EOPNOTSUPP) @@ -584,17 +590,100 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, - phydev->duplex, phydev->interface); + phydev->duplex, phydev->pause, + phydev->interface); mutex_unlock(&chip->reg_lock); if (err && err != -EOPNOTSUPP) dev_err(ds->dev, "p%d: failed to configure MAC\n", port); } +static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (!phy_interface_mode_is_8023z(state->interface)) { + /* 10M and 100M are only supported in non-802.3z mode */ + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + } +} + +static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + /* FIXME: if the port is in 1000Base-X mode, then it only supports + * 1000M FD speeds. In this case, CMODE will indicate 5. + */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + /* No ethtool bits for 200Mbps */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (port >= 9) + phylink_set(mask, 2500baseX_Full); + + /* No ethtool bits for 200Mbps */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (port >= 9) { + phylink_set(mask, 10000baseT_Full); + phylink_set(mask, 10000baseKR_Full); + } + + mv88e6390_phylink_validate(chip, port, mask, state); +} + static void mv88e6xxx_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct mv88e6xxx_chip *chip = ds->priv; + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set(mask, Pause); + phylink_set_port_modes(mask); + + if (chip->info->ops->phylink_validate) + chip->info->ops->phylink_validate(chip, port, mask, state); + + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + /* We can only operate at 2500BaseX or 1000BaseX. If requested + * to advertise both, only report advertising at 2500BaseX. + */ + phylink_helper_basex_speed(state); } static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, @@ -604,7 +693,10 @@ static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_link_state(chip, port, state); + if (chip->info->ops->port_link_state) + err = chip->info->ops->port_link_state(chip, port, state); + else + err = -EOPNOTSUPP; mutex_unlock(&chip->reg_lock); return err; @@ -615,7 +707,7 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, const struct phylink_link_state *state) { struct mv88e6xxx_chip *chip = ds->priv; - int speed, duplex, link, err; + int speed, duplex, link, pause, err; if (mode == MLO_AN_PHY) return; @@ -629,9 +721,10 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, duplex = DUPLEX_UNFORCED; link = LINK_UNFORCED; } + pause = !!phylink_test(state->advertising, Pause); mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, + err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, pause, state->interface); mutex_unlock(&chip->reg_lock); @@ -2080,6 +2173,9 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) int err; u16 reg; + chip->ports[port].chip = chip; + chip->ports[port].port = port; + /* MAC Forcing register: don't force link, speed, duplex or flow control * state to any particular values on physical ports, but force the CPU * port and all DSA ports to their maximum bandwidth and full duplex. @@ -2087,10 +2183,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, SPEED_MAX, DUPLEX_FULL, + PAUSE_OFF, PHY_INTERFACE_MODE_NA); else err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, SPEED_UNFORCED, DUPLEX_UNFORCED, + PAUSE_ON, PHY_INTERFACE_MODE_NA); if (err) return err; @@ -2239,7 +2337,12 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, int err; mutex_lock(&chip->reg_lock); + err = mv88e6xxx_serdes_power(chip, port, true); + + if (!err && chip->info->ops->serdes_irq_setup) + err = chip->info->ops->serdes_irq_setup(chip, port); + mutex_unlock(&chip->reg_lock); return err; @@ -2251,8 +2354,13 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; mutex_lock(&chip->reg_lock); + + if (chip->info->ops->serdes_irq_free) + chip->info->ops->serdes_irq_free(chip, port); + if (mv88e6xxx_serdes_power(chip, port, false)) dev_err(chip->dev, "failed to power off SERDES\n"); + mutex_unlock(&chip->reg_lock); } @@ -2286,6 +2394,7 @@ static int mv88e6xxx_stats_setup(struct mv88e6xxx_chip *chip) static int mv88e6xxx_setup(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; + u8 cmode; int err; int i; @@ -2294,6 +2403,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) mutex_lock(&chip->reg_lock); + /* Cache the cmode of each port. */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (chip->info->ops->port_get_cmode) { + err = chip->info->ops->port_get_cmode(chip, i, &cmode); + if (err) + return err; + + chip->ports[i].cmode = cmode; + } + } + /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { if (dsa_is_unused_port(ds, i)) @@ -2601,6 +2721,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2617,6 +2739,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -2632,6 +2755,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, + .port_link_state = mv88e6185_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2643,6 +2768,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -2665,6 +2791,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2679,6 +2807,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -2696,6 +2825,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2709,6 +2840,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -2729,6 +2861,9 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, + .port_set_pause = mv88e6185_port_set_pause, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2744,6 +2879,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -2769,6 +2905,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -2784,6 +2922,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6341_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -2806,6 +2945,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2821,6 +2962,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6165_ops = { @@ -2836,6 +2978,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2851,6 +2995,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6165_avb_ops, .ptp_ops = &mv88e6165_ptp_ops, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6171_ops = { @@ -2874,6 +3019,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2887,6 +3034,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -2912,6 +3060,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2928,6 +3078,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -2951,6 +3102,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2964,6 +3117,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -2989,6 +3143,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3005,6 +3161,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3021,6 +3178,9 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, + .port_set_pause = mv88e6185_port_set_pause, + .port_link_state = mv88e6185_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3036,6 +3196,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3057,6 +3218,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3072,7 +3235,10 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3094,6 +3260,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3108,8 +3276,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390_serdes_power, + .serdes_power = mv88e6390x_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390x_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3131,6 +3302,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3146,8 +3319,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3173,6 +3349,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3191,6 +3369,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6290_ops = { @@ -3213,6 +3392,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3228,9 +3409,12 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3255,6 +3439,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3270,6 +3456,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6321_ops = { @@ -3294,6 +3481,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3307,6 +3496,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6341_ops = { @@ -3332,6 +3522,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3349,6 +3541,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6350_ops = { @@ -3372,6 +3565,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3385,6 +3580,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3408,6 +3604,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3423,6 +3621,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .avb_ops = &mv88e6352_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6352_ops = { @@ -3448,6 +3647,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3469,6 +3670,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_get_sset_count = mv88e6352_serdes_get_sset_count, .serdes_get_strings = mv88e6352_serdes_get_strings, .serdes_get_stats = mv88e6352_serdes_get_stats, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6390_ops = { @@ -3493,6 +3695,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3508,9 +3712,12 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3535,6 +3742,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3549,10 +3758,13 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390_serdes_power, + .serdes_power = mv88e6390x_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390x_phylink_validate, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 6aa6197ddc109f3979e0ecb5fc2de3d92340dbb9..f9ecb7872d32cd3a6f0807e176a8fc04f63692a7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -191,12 +191,16 @@ struct mv88e6xxx_port_hwtstamp { }; struct mv88e6xxx_port { + struct mv88e6xxx_chip *chip; + int port; u64 serdes_stats[2]; u64 atu_member_violation; u64 atu_miss_violation; u64 atu_full_violation; u64 vtu_member_violation; u64 vtu_miss_violation; + u8 cmode; + int serdes_irq; }; struct mv88e6xxx_chip { @@ -351,6 +355,13 @@ struct mv88e6xxx_ops { */ int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); +#define PAUSE_ON 1 +#define PAUSE_OFF 0 + + /* Enable/disable sending Pause */ + int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port, + int pause); + #define SPEED_MAX INT_MAX #define SPEED_UNFORCED -2 @@ -383,12 +394,16 @@ struct mv88e6xxx_ops { */ int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); + int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode); /* Some devices have a per port register indicating what is * the upstream port this port should forward to. */ int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port, int upstream_port); + /* Return the port link state, as required by phylink */ + int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. @@ -420,6 +435,10 @@ struct mv88e6xxx_ops { /* Power on/off a SERDES interface */ int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on); + /* SERDES interrupt handling */ + int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port); + void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port); + /* Statistics from the SERDES interface */ int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port); int (*serdes_get_strings)(struct mv88e6xxx_chip *chip, int port, @@ -444,6 +463,11 @@ struct mv88e6xxx_ops { /* Precision Time Protocol operations */ const struct mv88e6xxx_ptp_ops *ptp_ops; + + /* Phylink */ + void (*phylink_validate)(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state); }; struct mv88e6xxx_irq_ops { diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 429d0ebcd5b168631fe1f2cc5f732b8ab324b5f9..92945841c8e882e0e582372778d5c81603052236 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -19,6 +19,7 @@ #include "chip.h" #include "port.h" +#include "serdes.h" int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, u16 *val) @@ -36,6 +37,29 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } +/* Offset 0x00: MAC (or PCS or Physical) Status Register + * + * For most devices, this is read only. However the 6185 has the MyPause + * bit read/write. + */ +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + if (pause) + reg |= MV88E6XXX_PORT_STS_MY_PAUSE; + else + reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); +} + /* Offset 0x01: MAC (or PCS or Physical) Control Register * * Link, Duplex and Flow Control have one force bit, one value bit. @@ -318,8 +342,9 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode) { - u16 reg; + int lane; u16 cmode; + u16 reg; int err; if (mode == PHY_INTERFACE_MODE_NA) @@ -349,6 +374,20 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, cmode = 0; } + lane = mv88e6390x_serdes_get_lane(chip, port); + if (lane < 0) + return lane; + + if (chip->ports[port].serdes_irq) { + err = mv88e6390_serdes_irq_disable(chip, port, lane); + if (err) + return err; + } + + err = mv88e6390_serdes_power(chip, port, false); + if (err) + return err; + if (cmode) { err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); if (err) @@ -360,12 +399,38 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); if (err) return err; + + err = mv88e6390_serdes_power(chip, port, true); + if (err) + return err; + + if (chip->ports[port].serdes_irq) { + err = mv88e6390_serdes_irq_enable(chip, port, lane); + if (err) + return err; + } } + chip->ports[port].cmode = cmode; + return 0; } -int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) +int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + *cmode = reg & MV88E6185_PORT_STS_CMODE_MASK; + + return 0; +} + +int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) { int err; u16 reg; @@ -379,7 +444,7 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) return 0; } -int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state) { int err; @@ -400,7 +465,7 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, state->speed = SPEED_1000; break; case MV88E6XXX_PORT_STS_SPEED_10000: - if ((reg &MV88E6XXX_PORT_STS_CMODE_MASK) == + if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == MV88E6XXX_PORT_STS_CMODE_2500BASEX) state->speed = SPEED_2500; else @@ -417,6 +482,42 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, return 0; } +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + u8 cmode = chip->ports[port].cmode; + + /* When a port is in "Cross-chip serdes" mode, it uses + * 1000Base-X full duplex mode, but there is no automatic + * link detection. Use the sync OK status for link (as it + * would do for 1000Base-X mode.) + */ + if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { + u16 mac; + int err; + + err = mv88e6xxx_port_read(chip, port, + MV88E6XXX_PORT_MAC_CTL, &mac); + if (err) + return err; + + state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); + state->an_enabled = 1; + state->an_complete = + !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); + state->duplex = + state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; + state->speed = + state->link ? SPEED_1000 : SPEED_UNKNOWN; + + return 0; + } + } + + return mv88e6352_port_link_state(chip, port, state); +} + /* Offset 0x02: Jamming Control * * Do not limit the period of time that this port can be paused for by diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 5e1db1b221ca1d85d911eae054c4ad3c31503ca8..f32f56af8e35d3a1b735406c1561e98863051352 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -42,14 +42,28 @@ #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c #define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d +#define MV88E6185_PORT_STS_CDUPLEX 0x0008 +#define MV88E6185_PORT_STS_CMODE_MASK 0x0007 +#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 +#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS 0x0001 +#define MV88E6185_PORT_STS_CMODE_MII_100 0x0002 +#define MV88E6185_PORT_STS_CMODE_MII_10 0x0003 +#define MV88E6185_PORT_STS_CMODE_SERDES 0x0004 +#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005 +#define MV88E6185_PORT_STS_CMODE_PHY 0x0006 +#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007 /* Offset 0x01: MAC (or PCS or Physical) Control Register */ #define MV88E6XXX_PORT_MAC_CTL 0x01 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000 +#define MV88E6185_PORT_MAC_CTL_SYNC_OK 0x4000 #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 #define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 #define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 +#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400 +#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200 +#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100 #define MV88E6XXX_PORT_MAC_CTL_FC 0x0080 #define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040 #define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020 @@ -242,6 +256,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause); int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, @@ -295,8 +311,11 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); -int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); -int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, +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 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); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 880b2cf0a53019027299cb4ae5356100b94fc651..f007d109b3856f8dde333e13970b03f4690a318b 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -11,6 +11,8 @@ * (at your option) any later version. */ +#include +#include #include #include "chip.h" @@ -35,6 +37,22 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, reg, val); } +static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, + int lane, int device, int reg, u16 *val) +{ + int reg_c45 = MII_ADDR_C45 | device << 16 | reg; + + return mv88e6xxx_phy_read(chip, lane, reg_c45, val); +} + +static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, + int lane, int device, int reg, u16 val) +{ + int reg_c45 = MII_ADDR_C45 | device << 16 | reg; + + return mv88e6xxx_phy_write(chip, lane, reg_c45, val); +} + static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) { u16 val, new_val; @@ -57,14 +75,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) { - u8 cmode; - int err; - - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) { - dev_err(chip->dev, "failed to read cmode\n"); - return false; - } + u8 cmode = chip->ports[port].cmode; if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || @@ -174,16 +185,121 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } +/* Return the SERDES lane address a port is using. Only Ports 9 and 10 + * have SERDES lanes. Returns -ENODEV if a port does not have a lane. + */ +static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + u8 cmode = chip->ports[port].cmode; + + switch (port) { + case 9: + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + return MV88E6390_PORT9_LANE0; + return -ENODEV; + case 10: + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + return MV88E6390_PORT10_LANE0; + return -ENODEV; + default: + return -ENODEV; + } +} + +/* Return the SERDES lane address a port is using. Ports 9 and 10 can + * use multiple lanes. If so, return the first lane the port uses. + * Returns -ENODEV if a port does not have a lane. + */ +int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + u8 cmode_port9, cmode_port10, cmode_port; + + cmode_port9 = chip->ports[9].cmode; + cmode_port10 = chip->ports[10].cmode; + cmode_port = chip->ports[port].cmode; + + switch (port) { + case 2: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE1; + return -ENODEV; + case 3: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE2; + return -ENODEV; + case 4: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE3; + return -ENODEV; + case 5: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE1; + return -ENODEV; + case 6: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE2; + return -ENODEV; + case 7: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE3; + return -ENODEV; + case 9: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + return MV88E6390_PORT9_LANE0; + return -ENODEV; + case 10: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + return MV88E6390_PORT10_LANE0; + return -ENODEV; + default: + return -ENODEV; + } +} + /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) +static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; - int reg_c45; int err; - reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | - MV88E6390_PCS_CONTROL_1; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, &val); + if (err) return err; @@ -195,22 +311,21 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, new_val); return err; } -/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, - bool on) +/* Set the power on/off for SGMII and 1000Base-X */ +static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; - int reg_c45; int err; - reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | - MV88E6390_SGMII_CONTROL; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, &val); if (err) return err; @@ -222,127 +337,261 @@ static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, new_val); return err; } -static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, - int port_donor, int lane, bool rxaui, bool on) +static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, + int lane, bool on) { - int err; - u8 cmode_donor; - - err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); - if (err) - return err; + u8 cmode = chip->ports[port].cmode; - switch (cmode_donor) { - case MV88E6XXX_PORT_STS_CMODE_RXAUI: - if (!rxaui) - break; - /* Fall through */ - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || - cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) - return mv88e6390_serdes_sgmii(chip, lane, on); + return mv88e6390_serdes_power_sgmii(chip, lane, on); + case MV88E6XXX_PORT_STS_CMODE_XAUI: + case MV88E6XXX_PORT_STS_CMODE_RXAUI: + return mv88e6390_serdes_power_10g(chip, lane, on); + } + + return 0; +} + +int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + int lane; + + lane = mv88e6390_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + switch (port) { + case 9 ... 10: + return mv88e6390_serdes_power_lane(chip, port, lane, on); } + + return 0; +} + +int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + int lane; + + lane = mv88e6390x_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + switch (port) { + case 2 ... 4: + case 5 ... 7: + case 9 ... 10: + return mv88e6390_serdes_power_lane(chip, port, lane, on); + } + return 0; } -static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) +static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, + int port, int lane) { + struct dsa_switch *ds = chip->ds; + u16 status; + bool up; + + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_STATUS, &status); + + /* 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. + */ + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_STATUS, &status); + up = status & MV88E6390_SGMII_STATUS_LINK; + + dsa_port_phylink_mac_change(ds, port, up); +} + +static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, + int lane) +{ + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_ENABLE, + MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP); +} + +static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip, + int lane) +{ + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_ENABLE, 0); +} + +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, + int lane) +{ + u8 cmode = chip->ports[port].cmode; + int err = 0; + switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_SGMII: - return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); - case MV88E6XXX_PORT_STS_CMODE_XAUI: - case MV88E6XXX_PORT_STS_CMODE_RXAUI: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); + err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); } - return 0; + return err; } -static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) +int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, + int lane) { + u8 cmode = chip->ports[port].cmode; + int err = 0; + switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: - return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on); - case MV88E6XXX_PORT_STS_CMODE_XAUI: - case MV88E6XXX_PORT_STS_CMODE_RXAUI: case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); + err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); } - return 0; + return err; } -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, + int lane, u16 *status) { - u8 cmode; int err; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_STATUS, status); - switch (port) { - case 2: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE1, - false, on); - case 3: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE2, - true, on); - case 4: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE3, - true, on); - case 5: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE1, - false, on); - case 6: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE2, - true, on); - case 7: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE3, - true, on); - case 9: - return mv88e6390_serdes_port9(chip, cmode, on); - case 10: - return mv88e6390_serdes_port10(chip, cmode, on); + return err; +} + +static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_port *port = dev_id; + struct mv88e6xxx_chip *chip = port->chip; + irqreturn_t ret = IRQ_NONE; + u8 cmode = port->cmode; + u16 status; + int lane; + int err; + + lane = mv88e6390x_serdes_get_lane(chip, port->port); + + mutex_lock(&chip->reg_lock); + + switch (cmode) { + case MV88E6XXX_PORT_STS_CMODE_SGMII: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); + if (err) + goto out; + if (status && (MV88E6390_SGMII_INT_LINK_DOWN || + MV88E6390_SGMII_INT_LINK_UP)) { + ret = IRQ_HANDLED; + mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane); + } } +out: + mutex_unlock(&chip->reg_lock); - return 0; + return ret; } -int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) { + int lane; int err; - u8 cmode; - if (port != 5) + /* Only support ports 9 and 10 at the moment */ + if (port < 9) return 0; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) + lane = mv88e6390x_serdes_get_lane(chip, port); + + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, + port); + if (chip->ports[port].serdes_irq < 0) { + dev_err(chip->dev, "Unable to map SERDES irq: %d\n", + chip->ports[port].serdes_irq); + return chip->ports[port].serdes_irq; + } + + /* Requesting the IRQ will trigger irq callbacks. So we cannot + * hold the reg_lock. + */ + mutex_unlock(&chip->reg_lock); + err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, + mv88e6390_serdes_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-serdes", + &chip->ports[port]); + mutex_lock(&chip->reg_lock); + + if (err) { + dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", + err); return err; + } + + return mv88e6390_serdes_irq_enable(chip, port, lane); +} + +void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) +{ + int lane = mv88e6390x_serdes_get_lane(chip, port); + + if (port < 9) + return; + + if (lane < 0) + return; + + mv88e6390_serdes_irq_disable(chip, port, lane); + + /* Freeing the IRQ will trigger irq callbacks. So we cannot + * hold the reg_lock. + */ + mutex_unlock(&chip->reg_lock); + free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); + mutex_lock(&chip->reg_lock); + + chip->ports[port].serdes_irq = 0; +} + +int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + u8 cmode = chip->ports[port].cmode; + + if (port != 5) + return 0; if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return mv88e6390_serdes_sgmii(chip, MV88E6341_ADDR_SERDES, on); + return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES, + on); return 0; } diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index b6e5fbd46b5e37101023a18c56098748a1df1d4a..b1496de9c6fefff42a3c41fdd8c572272cc538f1 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -29,7 +29,6 @@ #define MV88E6390_PORT10_LANE1 0x15 #define MV88E6390_PORT10_LANE2 0x16 #define MV88E6390_PORT10_LANE3 0x17 -#define MV88E6390_SERDES_DEVICE (4 << 16) /* 10GBASE-R and 10GBASE-X4/X2 */ #define MV88E6390_PCS_CONTROL_1 0x1000 @@ -43,13 +42,36 @@ #define MV88E6390_SGMII_CONTROL_RESET BIT(15) #define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14) #define MV88E6390_SGMII_CONTROL_PDOWN BIT(11) +#define MV88E6390_SGMII_STATUS 0x2001 +#define MV88E6390_SGMII_STATUS_AN_DONE BIT(5) +#define MV88E6390_SGMII_STATUS_REMOTE_FAULT BIT(4) +#define MV88E6390_SGMII_STATUS_LINK BIT(2) +#define MV88E6390_SGMII_INT_ENABLE 0xa001 +#define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14) +#define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13) +#define MV88E6390_SGMII_INT_PAGE_RX BIT(12) +#define MV88E6390_SGMII_INT_AN_COMPLETE BIT(11) +#define MV88E6390_SGMII_INT_LINK_DOWN BIT(10) +#define MV88E6390_SGMII_INT_LINK_UP BIT(9) +#define MV88E6390_SGMII_INT_SYMBOL_ERROR BIT(8) +#define MV88E6390_SGMII_INT_FALSE_CARRIER BIT(7) +#define MV88E6390_SGMII_INT_STATUS 0xa002 +int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); +int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); +int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port); +void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, + int lane); +int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, + int lane); + #endif diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index af4dc4425be28101dd3e8e53b3327c3ef41f725f..3ba5cf2a8a5fbe4bd978a114a6f46b698f8c0b57 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -1688,4 +1688,34 @@ static const struct sfp_upstream_ops sfp_phylink_ops = { .disconnect_phy = phylink_sfp_disconnect_phy, }; +/* Helpers for MAC drivers */ + +/** + * phylink_helper_basex_speed() - 1000BaseX/2500BaseX helper + * @state: a pointer to a &struct phylink_link_state + * + * Inspect the interface mode, advertising mask or forced speed and + * decide whether to run at 2.5Gbit or 1Gbit appropriately, switching + * the interface mode to suit. @state->interface is appropriately + * updated, and the advertising mask has the "other" baseX_Full flag + * cleared. + */ +void phylink_helper_basex_speed(struct phylink_link_state *state) +{ + if (phy_interface_mode_is_8023z(state->interface)) { + bool want_2500 = state->an_enabled ? + phylink_test(state->advertising, 2500baseX_Full) : + state->speed == SPEED_2500; + + if (want_2500) { + phylink_clear(state->advertising, 1000baseX_Full); + state->interface = PHY_INTERFACE_MODE_2500BASEX; + } else { + phylink_clear(state->advertising, 2500baseX_Full); + state->interface = PHY_INTERFACE_MODE_1000BASEX; + } + } +} +EXPORT_SYMBOL_GPL(phylink_helper_basex_speed); + MODULE_LICENSE("GPL"); diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 50eeae025f1e1c0b56f9296d5cd8ffca9851cf74..021fc6595856e8ca4aabfda0fd72ce0551f05053 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -234,5 +234,6 @@ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int); #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) void phylink_set_port_modes(unsigned long *bits); +void phylink_helper_basex_speed(struct phylink_link_state *state); #endif