From 54186b91bde1711080d0b23ce25f0bee5a058fc9 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:37 +0200 Subject: [PATCH 01/13] net: dsa: mv88e6xxx: Add support to enabling pause The 6185 can enable/disable 802.3z pause be setting the MyPause bit in the port status register. Add an op to support this. Signed-off-by: Russell King Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 20 ++++++++++++++++---- drivers/net/dsa/mv88e6xxx/chip.h | 7 +++++++ drivers/net/dsa/mv88e6xxx/port.c | 23 +++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/port.h | 2 ++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 0b5a2c31f3951..f7522d0013654 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,7 +590,8 @@ 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) @@ -615,7 +622,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 +636,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); @@ -2087,10 +2095,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; @@ -2729,6 +2739,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3021,6 +3032,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 6aa6197ddc109..92ebfd2711683 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -351,6 +351,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 diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 429d0ebcd5b16..c0701deaca6a6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -36,6 +36,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. diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 5e1db1b221ca1..44916251567b2 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -242,6 +242,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, From 624c0f0239f04cce78c86afa95eb2841c84fbab1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 9 Aug 2018 15:38:38 +0200 Subject: [PATCH 02/13] phylink: add helper for configuring 2500BaseX modes Add a helper for MAC drivers to use in their validate callback to deal with 2500BaseX vs 1000BaseX modes, where the hardware supports both but it is not possible to automatically select between them. This helper defaults to 1000BaseX, as that is the 802.3 standard, and will allow users to select 2500BaseX either by forcing the speed if AN is disabled, or by changing the advertising mask if AN is enabled. Disabling AN is not recommended as it is only the speed that we're interested in controlling, not the duplex or pause mode parameters. Signed-off-by: Russell King Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 30 ++++++++++++++++++++++++++++++ include/linux/phylink.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index af4dc4425be28..3ba5cf2a8a5fb 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 50eeae025f1e1..021fc6595856e 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 From 6c422e34b1b6533af5730280835365d4b50786d3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 9 Aug 2018 15:38:39 +0200 Subject: [PATCH 03/13] net: dsa: mv88e6xxx: add phylink support Add rudimentary phylink support to mv88e6xxx. TODO: - needs to call phylink_mac_change() when the port link comes up/goes down. Signed-off-by: Russell King Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 139 ++++++++++++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/chip.h | 8 ++ drivers/net/dsa/mv88e6xxx/port.c | 56 ++++++++++++- drivers/net/dsa/mv88e6xxx/port.h | 18 +++- 4 files changed, 217 insertions(+), 4 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index f7522d0013654..1427541df3168 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -598,10 +598,92 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, 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, @@ -611,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; @@ -2611,6 +2696,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2627,6 +2713,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 = { @@ -2642,6 +2729,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2653,6 +2741,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 = { @@ -2675,6 +2764,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2689,6 +2779,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 = { @@ -2706,6 +2797,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2719,6 +2811,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 = { @@ -2740,6 +2833,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2755,6 +2849,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 = { @@ -2780,6 +2875,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -2795,6 +2891,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 = { @@ -2817,6 +2914,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2832,6 +2930,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 = { @@ -2847,6 +2946,7 @@ 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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2862,6 +2962,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 = { @@ -2885,6 +2986,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2898,6 +3000,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 = { @@ -2923,6 +3026,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2939,6 +3043,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 = { @@ -2962,6 +3067,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2975,6 +3081,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 = { @@ -3000,6 +3107,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3016,6 +3124,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 = { @@ -3033,6 +3142,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .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, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3048,6 +3158,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 = { @@ -3069,6 +3180,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3085,6 +3197,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3106,6 +3219,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3122,6 +3236,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390x_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3143,6 +3258,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3160,6 +3276,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .serdes_power = mv88e6390_serdes_power, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3185,6 +3302,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3203,6 +3321,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 = { @@ -3225,6 +3344,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3243,6 +3363,7 @@ static const struct mv88e6xxx_ops mv88e6290_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 mv88e6320_ops = { @@ -3267,6 +3388,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3282,6 +3404,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 = { @@ -3306,6 +3429,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3319,6 +3443,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 = { @@ -3344,6 +3469,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3361,6 +3487,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 = { @@ -3384,6 +3511,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3397,6 +3525,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 = { @@ -3420,6 +3549,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3435,6 +3565,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 = { @@ -3460,6 +3591,7 @@ 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, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3481,6 +3613,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 = { @@ -3505,6 +3638,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3523,6 +3657,7 @@ static const struct mv88e6xxx_ops mv88e6390_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 mv88e6390x_ops = { @@ -3547,6 +3682,7 @@ 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, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3565,6 +3701,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .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 92ebfd2711683..cdc028fcdf964 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -396,6 +396,9 @@ struct mv88e6xxx_ops { */ 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. @@ -451,6 +454,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 c0701deaca6a6..2ff370cb2f3c5 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -388,6 +388,19 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, return 0; } +/* mv88e6185 only has 3 bits for CMODE */ +static int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + return reg & MV88E6185_PORT_STS_CMODE_MASK; +} + int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) { int err; @@ -402,7 +415,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; @@ -423,7 +436,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 @@ -440,6 +453,45 @@ 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) { + int cmode = mv88e6185_port_get_cmode(chip, port); + + if (cmode < 0) + return 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 44916251567b2..9b8d2b2299076 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 @@ -298,7 +312,9 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, 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_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, From a8c01c0d941d2f220a5a31ded77a67d89ddec3b0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:40 +0200 Subject: [PATCH 04/13] net: dsa: mv88e6xxx: Refactor SERDES lane code The 6390 family has 8 SERDES lanes. What ports use these lanes depends on how ports 9 and 10 are configured. If 9 and 10 does not make use of a line, one of the lower ports can use it. Add a function to return the lane a port is using, if any, and simplify the code to power up/down the lane. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/serdes.c | 195 +++++++++++++++++------------ 1 file changed, 114 insertions(+), 81 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 880b2cf0a5301..a218870971fe7 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -174,8 +174,97 @@ 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. 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. + */ +static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + u8 cmode_port9, cmode_port10, cmode_port; + int err; + + err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9); + if (err) + return err; + + err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10); + if (err) + return err; + + err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port); + if (err) + return err; + + 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_10g(struct mv88e6xxx_chip *chip, int lane, bool on) { u16 val, new_val; int reg_c45; @@ -183,7 +272,7 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | MV88E6390_PCS_CONTROL_1; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6xxx_phy_read(chip, lane, reg_c45, &val); if (err) return err; @@ -195,13 +284,13 @@ 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 = mv88e6xxx_phy_write(chip, lane, reg_c45, 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, +/* Set the power on/off for SGMII and 1000Base-X */ +static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int lane, bool on) { u16 val, new_val; @@ -210,7 +299,7 @@ static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | MV88E6390_SGMII_CONTROL; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6xxx_phy_read(chip, lane, reg_c45, &val); if (err) return err; @@ -222,63 +311,29 @@ 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 = mv88e6xxx_phy_write(chip, lane, reg_c45, 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) { + u8 cmode; int err; - u8 cmode_donor; - err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); + err = mv88e6xxx_port_get_cmode(chip, port, &cmode); if (err) return err; - switch (cmode_donor) { - case MV88E6XXX_PORT_STS_CMODE_RXAUI: - if (!rxaui) - break; - /* Fall through */ - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: - case MV88E6XXX_PORT_STS_CMODE_SGMII: - 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 0; -} - -static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) -{ 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_1000BASE_X: + return mv88e6390_serdes_sgmii(chip, lane, on); case MV88E6XXX_PORT_STS_CMODE_XAUI: case MV88E6XXX_PORT_STS_CMODE_RXAUI: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); - } - - return 0; -} - -static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) -{ - 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); + return mv88e6390_serdes_10g(chip, lane, on); } return 0; @@ -286,42 +341,20 @@ static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { - u8 cmode; - int err; + int lane; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + lane = mv88e6390_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; 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); + case 2 ... 4: + case 5 ... 7: + case 9 ... 10: + return mv88e6390_serdes_power_lane(chip, port, lane, on); } return 0; From 07ffbd74d1786d13a4f3a6bc01400ea59e8b19c0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:41 +0200 Subject: [PATCH 05/13] net: dsa: mv88e6xxx: 6390 vs 6390X SERDES support The 6390 has two SERDES interfaces, used by ports 9 and 10. The 6390X has eight SERDES interfaces. These allow ports 9 and 10 to do 10G. Or if lower speeds are used, some of the SERDES interfaces can be used by ports 2-8 for 1000Base-X. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 4 +-- drivers/net/dsa/mv88e6xxx/serdes.c | 51 +++++++++++++++++++++++++++++- drivers/net/dsa/mv88e6xxx/serdes.h | 1 + 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1427541df3168..4c9ae5b9440b0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3234,7 +3234,7 @@ 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, .gpio_ops = &mv88e6352_gpio_ops, .phylink_validate = mv88e6390x_phylink_validate, }; @@ -3697,7 +3697,7 @@ 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, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, .ptp_ops = &mv88e6352_ptp_ops, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index a218870971fe7..c534749fb1b60 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -174,11 +174,41 @@ 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; + int err; + + err = mv88e6xxx_port_get_cmode(chip, port, &cmode); + if (err) + return err; + + 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. */ -static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode_port9, cmode_port10, cmode_port; int err; @@ -350,6 +380,25 @@ int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 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: diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index b6e5fbd46b5e3..05c4825c36e49 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -47,6 +47,7 @@ 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 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); From 23ef57d8230b0f3a92443fc4c88b1ec52994a70a Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:42 +0200 Subject: [PATCH 06/13] net: dsa: mv88e6xxx: Rename sgmii/10g power functions There is a need to add more functions manipulating the SERDES interfaces. Cleanup the namespace. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/serdes.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index c534749fb1b60..496d422170eff 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -294,7 +294,8 @@ static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) } /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int lane, bool on) +static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; int reg_c45; @@ -320,8 +321,8 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int lane, bool on) } /* Set the power on/off for SGMII and 1000Base-X */ -static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int lane, - bool on) +static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; int reg_c45; @@ -359,11 +360,11 @@ static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: - 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: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, lane, on); + return mv88e6390_serdes_power_10g(chip, lane, on); } return 0; @@ -424,7 +425,8 @@ int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 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; } From e6891c76dd89418d08efbf72f65c93efc4a94556 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:43 +0200 Subject: [PATCH 07/13] net: dsa: mv88e6xxx: Add serdes register read/write helper Add a helper for accessing SERDES registers of the 6390 family. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/serdes.c | 35 +++++++++++++++++++++--------- drivers/net/dsa/mv88e6xxx/serdes.h | 1 - 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 496d422170eff..36050e4299243 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -35,6 +35,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; @@ -298,12 +314,11 @@ 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, lane, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, &val); + if (err) return err; @@ -315,7 +330,8 @@ static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, lane, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, new_val); return err; } @@ -325,12 +341,10 @@ 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, lane, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, &val); if (err) return err; @@ -342,7 +356,8 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, lane, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, new_val); return err; } diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 05c4825c36e49..a64ca19749884 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 From f8236a0835597b166a9f0bab38b9b31bd932ce98 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:44 +0200 Subject: [PATCH 08/13] net: dsa: mv88e6xxx: 2500Base-X uses the 1000Base-X SERDES The 6390 has three different SERDES interface types. 2500Base-X is implemented by the SGMII/1000Base-X SERDES. So power on/off the correct SERDES. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/serdes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 36050e4299243..b57d4271acdbb 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -375,10 +375,10 @@ static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: return mv88e6390_serdes_power_sgmii(chip, lane, on); case MV88E6XXX_PORT_STS_CMODE_XAUI: case MV88E6XXX_PORT_STS_CMODE_RXAUI: - case MV88E6XXX_PORT_STS_CMODE_2500BASEX: return mv88e6390_serdes_power_10g(chip, lane, on); } From 2d2e1dd29962ce0e6bc2c35fe804d919bf3e7f68 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:45 +0200 Subject: [PATCH 09/13] net: dsa: mv88e6xxx: Cache the port cmode The ports CMODE indicates the type of link between the MAC and the PHY. It is used often in the SERDES code. Rather than read it each time, cache its value. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 38 +++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/chip.h | 2 ++ drivers/net/dsa/mv88e6xxx/port.c | 16 +++++------ drivers/net/dsa/mv88e6xxx/port.h | 3 +- drivers/net/dsa/mv88e6xxx/serdes.c | 45 +++++------------------------- 5 files changed, 57 insertions(+), 47 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 4c9ae5b9440b0..66e0281604dfa 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2381,6 +2381,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; @@ -2389,6 +2390,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)) @@ -2697,6 +2709,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .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, @@ -2730,6 +2743,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .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, @@ -2765,6 +2779,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .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, @@ -2798,6 +2813,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .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, @@ -2834,6 +2850,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .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, @@ -2876,6 +2893,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .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, @@ -2915,6 +2933,7 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .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, @@ -2947,6 +2966,7 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .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, @@ -2987,6 +3007,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .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, @@ -3027,6 +3048,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .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, @@ -3068,6 +3090,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .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, @@ -3108,6 +3131,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .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, @@ -3143,6 +3167,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .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, @@ -3181,6 +3206,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .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, @@ -3220,6 +3246,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .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, @@ -3259,6 +3286,7 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .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, @@ -3303,6 +3331,7 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .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, @@ -3345,6 +3374,7 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .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, @@ -3389,6 +3419,7 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .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, @@ -3430,6 +3461,7 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .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, @@ -3470,6 +3502,7 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .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, @@ -3512,6 +3545,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .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, @@ -3550,6 +3584,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .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, @@ -3592,6 +3627,7 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .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, @@ -3639,6 +3675,7 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .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, @@ -3683,6 +3720,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .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, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index cdc028fcdf964..08c74c88dbde2 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -197,6 +197,7 @@ struct mv88e6xxx_port { u64 atu_full_violation; u64 vtu_member_violation; u64 vtu_miss_violation; + u8 cmode; }; struct mv88e6xxx_chip { @@ -390,6 +391,7 @@ 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. diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 2ff370cb2f3c5..d236f3420f2d6 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -385,11 +385,12 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, return err; } + chip->ports[port].cmode = cmode; + return 0; } -/* mv88e6185 only has 3 bits for CMODE */ -static int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port) +int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) { int err; u16 reg; @@ -398,10 +399,12 @@ static int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port) if (err) return err; - return reg & MV88E6185_PORT_STS_CMODE_MASK; + *cmode = reg & MV88E6185_PORT_STS_CMODE_MASK; + + return 0; } -int mv88e6xxx_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 err; u16 reg; @@ -457,10 +460,7 @@ int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state) { if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { - int cmode = mv88e6185_port_get_cmode(chip, port); - - if (cmode < 0) - return cmode; + 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 diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 9b8d2b2299076..f32f56af8e35d 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -311,7 +311,8 @@ 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 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, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index b57d4271acdbb..064d0bb8fe028 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -73,14 +73,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) || @@ -195,12 +188,7 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, */ static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { - u8 cmode; - int err; - - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + u8 cmode = chip->ports[port].cmode; switch (port) { case 9: @@ -227,19 +215,10 @@ static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode_port9, cmode_port10, cmode_port; - int err; - err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9); - if (err) - return err; - - err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10); - if (err) - return err; - - err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port); - if (err) - return err; + cmode_port9 = chip->ports[9].cmode; + cmode_port10 = chip->ports[10].cmode; + cmode_port = chip->ports[port].cmode; switch (port) { case 2: @@ -365,12 +344,7 @@ static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, int lane, bool on) { - u8 cmode; - int err; - - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + u8 cmode = chip->ports[port].cmode; switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: @@ -427,16 +401,11 @@ int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { - int err; - u8 cmode; + u8 cmode = chip->ports[port].cmode; if (port != 5) return 0; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) From 364e9d7776a389941cea6c40783f90c236b402ce Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:46 +0200 Subject: [PATCH 10/13] net: dsa: mv88e6xxx: Power on/off SERDES on cmode change The 6390 family has a number of SERDES interfaces per port. When the cmode changes, eg 1000Base-X to XAUI, the SERDES interface in use will also change. Power down the old SERDES interface and power up the new SERDES interface. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/port.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index d236f3420f2d6..977b4cb822992 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) @@ -372,6 +373,10 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, cmode = 0; } + err = mv88e6390_serdes_power(chip, port, false); + if (err) + return err; + if (cmode) { err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); if (err) @@ -383,6 +388,10 @@ 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; } chip->ports[port].cmode = cmode; From 7b898469b91ea08e42dbed52acf8dfcb4f5914d0 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:47 +0200 Subject: [PATCH 11/13] net: dsa: mv88e6xxx: link mv88e6xxx_port to mv88e6xxx_chip An up coming change will register interrupts for individual switch ports, using the mv88e6xxx_port as the interrupt context information. Add members to the mv88e6xxx_port structure so we can link it back to the mv88e6xxx_chip member the port belongs to and the port number of the port. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 3 +++ drivers/net/dsa/mv88e6xxx/chip.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 66e0281604dfa..5845cbf7f0965 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2173,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. diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 08c74c88dbde2..577398fe36df8 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -191,6 +191,8 @@ 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; From efd1ba6af93ff63d40f92515a83405133145c028 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:48 +0200 Subject: [PATCH 12/13] net: dsa: mv88e6xxx: Add SERDES phydev_mac_change up for 6390 phylink wants to know when the MAC layers notices a change in the link. For the 6390 family, this is a change in the SERDES state. Add interrupt support for the SERDES interface used to implement SGMII/1000Base-X/2500Base-X. This is currently limited to ports 9 and 10. Support for the 10G SERDES and other ports will be added later, building on this basic framework. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 22 ++++ drivers/net/dsa/mv88e6xxx/chip.h | 5 + drivers/net/dsa/mv88e6xxx/serdes.c | 179 +++++++++++++++++++++++++++++ drivers/net/dsa/mv88e6xxx/serdes.h | 16 +++ 4 files changed, 222 insertions(+) diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 5845cbf7f0965..17752316ab102 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2337,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; @@ -2349,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); } @@ -3225,6 +3235,8 @@ 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, }; @@ -3265,6 +3277,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .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, }; @@ -3305,6 +3319,8 @@ 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, @@ -3393,6 +3409,8 @@ 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, @@ -3694,6 +3712,8 @@ 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, @@ -3739,6 +3759,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .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, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 577398fe36df8..f9ecb7872d32c 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -200,6 +200,7 @@ struct mv88e6xxx_port { u64 vtu_member_violation; u64 vtu_miss_violation; u8 cmode; + int serdes_irq; }; struct mv88e6xxx_chip { @@ -434,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, diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 064d0bb8fe028..519346b81b871 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" @@ -399,6 +401,183 @@ int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) return 0; } +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_SGMII: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); + } + + return err; +} + +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: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); + } + + return err; +} + +static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, + int lane, u16 *status) +{ + int err; + + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_STATUS, status); + + 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 ret; +} + +int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) +{ + int lane; + int err; + + /* Only support ports 9 and 10 at the moment */ + if (port < 9) + return 0; + + 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); +} + int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) { u8 cmode = chip->ports[port].cmode; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index a64ca19749884..09da08cb52612 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -42,11 +42,27 @@ #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 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); From 734447d4ed7b1f31617c28959fd5adfbd9e39d13 Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Thu, 9 Aug 2018 15:38:49 +0200 Subject: [PATCH 13/13] net: dsa: mv88e6xxx: Re-setup interrupts on CMODE change. When a port changes CMODE, the SERDES interface being used can change. Disable interrupts for the old SERDES interface, and enable interrupts on the new. Signed-off-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/port.c | 19 ++++++++++++++++++- drivers/net/dsa/mv88e6xxx/serdes.c | 4 +++- drivers/net/dsa/mv88e6xxx/serdes.h | 6 ++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 977b4cb822992..92945841c8e88 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -342,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) @@ -373,6 +374,16 @@ 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; @@ -392,6 +403,12 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, 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; diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 519346b81b871..f007d109b3856 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.c +++ b/drivers/net/dsa/mv88e6xxx/serdes.c @@ -214,7 +214,7 @@ static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) * use multiple lanes. If so, return the first lane the port uses. * Returns -ENODEV if a port does not have a lane. */ -static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) { u8 cmode_port9, cmode_port10, cmode_port; @@ -576,6 +576,8 @@ void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) 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) diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index 09da08cb52612..b1496de9c6fef 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -57,6 +57,7 @@ #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); @@ -68,4 +69,9 @@ 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