diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 243 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 47 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.h | 3 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2_avb.c | 25 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/hwtstamp.c | 134 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/hwtstamp.h | 14 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.c | 109 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/port.h | 23 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.c | 101 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/ptp.h | 71 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/serdes.c | 439 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/serdes.h | 24 |
12 files changed, 1070 insertions, 163 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bb28c701381a..8da3d39e3218 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -524,7 +524,7 @@ int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update) } static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, - int link, int speed, int duplex, + int link, int speed, int duplex, int pause, phy_interface_t mode) { int err; @@ -543,6 +543,12 @@ static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, goto restore_link; } + if (chip->info->ops->port_set_pause) { + err = chip->info->ops->port_set_pause(chip, port, pause); + if (err) + goto restore_link; + } + if (chip->info->ops->port_set_duplex) { err = chip->info->ops->port_set_duplex(chip, port, duplex); if (err && err != -EOPNOTSUPP) @@ -584,17 +590,100 @@ static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port, mutex_lock(&chip->reg_lock); err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed, - phydev->duplex, phydev->interface); + phydev->duplex, phydev->pause, + phydev->interface); mutex_unlock(&chip->reg_lock); if (err && err != -EOPNOTSUPP) dev_err(ds->dev, "p%d: failed to configure MAC\n", port); } +static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (!phy_interface_mode_is_8023z(state->interface)) { + /* 10M and 100M are only supported in non-802.3z mode */ + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + } +} + +static void mv88e6185_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + /* FIXME: if the port is in 1000Base-X mode, then it only supports + * 1000M FD speeds. In this case, CMODE will indicate 5. + */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6352_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + /* No ethtool bits for 200Mbps */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6390_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (port >= 9) + phylink_set(mask, 2500baseX_Full); + + /* No ethtool bits for 200Mbps */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + + mv88e6065_phylink_validate(chip, port, mask, state); +} + +static void mv88e6390x_phylink_validate(struct mv88e6xxx_chip *chip, int port, + unsigned long *mask, + struct phylink_link_state *state) +{ + if (port >= 9) { + phylink_set(mask, 10000baseT_Full); + phylink_set(mask, 10000baseKR_Full); + } + + mv88e6390_phylink_validate(chip, port, mask, state); +} + static void mv88e6xxx_validate(struct dsa_switch *ds, int port, unsigned long *supported, struct phylink_link_state *state) { + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct mv88e6xxx_chip *chip = ds->priv; + + /* Allow all the expected bits */ + phylink_set(mask, Autoneg); + phylink_set(mask, Pause); + phylink_set_port_modes(mask); + + if (chip->info->ops->phylink_validate) + chip->info->ops->phylink_validate(chip, port, mask, state); + + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); + + /* We can only operate at 2500BaseX or 1000BaseX. If requested + * to advertise both, only report advertising at 2500BaseX. + */ + phylink_helper_basex_speed(state); } static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, @@ -604,7 +693,10 @@ static int mv88e6xxx_link_state(struct dsa_switch *ds, int port, int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_link_state(chip, port, state); + if (chip->info->ops->port_link_state) + err = chip->info->ops->port_link_state(chip, port, state); + else + err = -EOPNOTSUPP; mutex_unlock(&chip->reg_lock); return err; @@ -615,7 +707,7 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, const struct phylink_link_state *state) { struct mv88e6xxx_chip *chip = ds->priv; - int speed, duplex, link, err; + int speed, duplex, link, pause, err; if (mode == MLO_AN_PHY) return; @@ -629,9 +721,10 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, duplex = DUPLEX_UNFORCED; link = LINK_UNFORCED; } + pause = !!phylink_test(state->advertising, Pause); mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, + err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex, pause, state->interface); mutex_unlock(&chip->reg_lock); @@ -2080,6 +2173,9 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) int err; u16 reg; + chip->ports[port].chip = chip; + chip->ports[port].port = port; + /* MAC Forcing register: don't force link, speed, duplex or flow control * state to any particular values on physical ports, but force the CPU * port and all DSA ports to their maximum bandwidth and full duplex. @@ -2087,10 +2183,12 @@ static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port) if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP, SPEED_MAX, DUPLEX_FULL, + PAUSE_OFF, PHY_INTERFACE_MODE_NA); else err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED, SPEED_UNFORCED, DUPLEX_UNFORCED, + PAUSE_ON, PHY_INTERFACE_MODE_NA); if (err) return err; @@ -2239,7 +2337,12 @@ static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port, int err; mutex_lock(&chip->reg_lock); + err = mv88e6xxx_serdes_power(chip, port, true); + + if (!err && chip->info->ops->serdes_irq_setup) + err = chip->info->ops->serdes_irq_setup(chip, port); + mutex_unlock(&chip->reg_lock); return err; @@ -2251,8 +2354,13 @@ static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; mutex_lock(&chip->reg_lock); + + if (chip->info->ops->serdes_irq_free) + chip->info->ops->serdes_irq_free(chip, port); + if (mv88e6xxx_serdes_power(chip, port, false)) dev_err(chip->dev, "failed to power off SERDES\n"); + mutex_unlock(&chip->reg_lock); } @@ -2286,6 +2394,7 @@ static int mv88e6xxx_stats_setup(struct mv88e6xxx_chip *chip) static int mv88e6xxx_setup(struct dsa_switch *ds) { struct mv88e6xxx_chip *chip = ds->priv; + u8 cmode; int err; int i; @@ -2294,6 +2403,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) mutex_lock(&chip->reg_lock); + /* Cache the cmode of each port. */ + for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { + if (chip->info->ops->port_get_cmode) { + err = chip->info->ops->port_get_cmode(chip, i, &cmode); + if (err) + goto unlock; + + chip->ports[i].cmode = cmode; + } + } + /* Setup Switch Port Registers */ for (i = 0; i < mv88e6xxx_num_ports(chip); i++) { if (dsa_is_unused_port(ds, i)) @@ -2601,6 +2721,8 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2617,6 +2739,7 @@ static const struct mv88e6xxx_ops mv88e6085_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6095_ops = { @@ -2632,6 +2755,8 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .port_set_frame_mode = mv88e6085_port_set_frame_mode, .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_set_upstream_port = mv88e6095_port_set_upstream_port, + .port_link_state = mv88e6185_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2643,6 +2768,7 @@ static const struct mv88e6xxx_ops mv88e6095_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6097_ops = { @@ -2665,6 +2791,8 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2679,6 +2807,7 @@ static const struct mv88e6xxx_ops mv88e6097_ops = { .rmu_disable = mv88e6085_g1_rmu_disable, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6123_ops = { @@ -2696,6 +2825,8 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .port_set_egress_floods = mv88e6352_port_set_egress_floods, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2709,6 +2840,7 @@ static const struct mv88e6xxx_ops mv88e6123_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6131_ops = { @@ -2729,6 +2861,9 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6097_port_pause_limit, + .port_set_pause = mv88e6185_port_set_pause, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2744,6 +2879,7 @@ static const struct mv88e6xxx_ops mv88e6131_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6141_ops = { @@ -2769,6 +2905,8 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -2784,6 +2922,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6341_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6161_ops = { @@ -2806,6 +2945,8 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2819,6 +2960,9 @@ static const struct mv88e6xxx_ops mv88e6161_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .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 = { @@ -2834,6 +2978,8 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .port_set_speed = mv88e6185_port_set_speed, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2847,6 +2993,9 @@ static const struct mv88e6xxx_ops mv88e6165_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .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 = { @@ -2870,6 +3019,8 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2883,6 +3034,7 @@ static const struct mv88e6xxx_ops mv88e6171_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6172_ops = { @@ -2908,6 +3060,8 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2924,6 +3078,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6175_ops = { @@ -2947,6 +3102,8 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -2960,6 +3117,7 @@ static const struct mv88e6xxx_ops mv88e6175_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6176_ops = { @@ -2985,6 +3143,8 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3001,6 +3161,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = { .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6352_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6185_ops = { @@ -3017,6 +3178,9 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .port_set_egress_floods = mv88e6185_port_set_egress_floods, .port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting, .port_set_upstream_port = mv88e6095_port_set_upstream_port, + .port_set_pause = mv88e6185_port_set_pause, + .port_link_state = mv88e6185_port_link_state, + .port_get_cmode = mv88e6185_port_get_cmode, .stats_snapshot = mv88e6xxx_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3032,6 +3196,7 @@ static const struct mv88e6xxx_ops mv88e6185_ops = { .reset = mv88e6185_g1_reset, .vtu_getnext = mv88e6185_g1_vtu_getnext, .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6190_ops = { @@ -3053,6 +3218,8 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3068,7 +3235,10 @@ static const struct mv88e6xxx_ops mv88e6190_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6190x_ops = { @@ -3090,6 +3260,8 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3104,8 +3276,11 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390_serdes_power, + .serdes_power = mv88e6390x_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, + .phylink_validate = mv88e6390x_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6191_ops = { @@ -3127,6 +3302,8 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .port_pause_limit = mv88e6390_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3142,6 +3319,11 @@ static const struct mv88e6xxx_ops mv88e6191_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, + .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6240_ops = { @@ -3167,6 +3349,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3184,6 +3368,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .serdes_power = mv88e6352_serdes_power, .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 = { @@ -3206,6 +3392,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3221,8 +3409,12 @@ static const struct mv88e6xxx_ops mv88e6290_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6320_ops = { @@ -3247,6 +3439,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3261,6 +3455,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = { .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .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 = { @@ -3285,6 +3481,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3297,6 +3495,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = { .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge, .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 = { @@ -3322,6 +3522,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3338,6 +3540,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = { .serdes_power = mv88e6341_serdes_power, .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 = { @@ -3361,6 +3565,8 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3374,6 +3580,7 @@ static const struct mv88e6xxx_ops mv88e6350_ops = { .reset = mv88e6352_g1_reset, .vtu_getnext = mv88e6352_g1_vtu_getnext, .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge, + .phylink_validate = mv88e6185_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6351_ops = { @@ -3397,6 +3604,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3411,6 +3620,8 @@ static const struct mv88e6xxx_ops mv88e6351_ops = { .vtu_getnext = mv88e6352_g1_vtu_getnext, .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 = { @@ -3436,6 +3647,8 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .port_pause_limit = mv88e6097_port_pause_limit, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6320_g1_stats_snapshot, .stats_set_histogram = mv88e6095_g1_stats_set_histogram, .stats_get_sset_count = mv88e6095_stats_get_sset_count, @@ -3453,9 +3666,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = { .serdes_power = mv88e6352_serdes_power, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6352_avb_ops, + .ptp_ops = &mv88e6352_ptp_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 = { @@ -3480,6 +3695,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3495,8 +3712,12 @@ static const struct mv88e6xxx_ops mv88e6390_ops = { .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, .serdes_power = mv88e6390_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390_phylink_validate, }; static const struct mv88e6xxx_ops mv88e6390x_ops = { @@ -3521,6 +3742,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, + .port_link_state = mv88e6352_port_link_state, + .port_get_cmode = mv88e6352_port_get_cmode, .stats_snapshot = mv88e6390_g1_stats_snapshot, .stats_set_histogram = mv88e6390_g1_stats_set_histogram, .stats_get_sset_count = mv88e6320_stats_get_sset_count, @@ -3535,9 +3758,13 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .rmu_disable = mv88e6390_g1_rmu_disable, .vtu_getnext = mv88e6390_g1_vtu_getnext, .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge, - .serdes_power = mv88e6390_serdes_power, + .serdes_power = mv88e6390x_serdes_power, + .serdes_irq_setup = mv88e6390_serdes_irq_setup, + .serdes_irq_free = mv88e6390_serdes_irq_free, .gpio_ops = &mv88e6352_gpio_ops, .avb_ops = &mv88e6390_avb_ops, + .ptp_ops = &mv88e6352_ptp_ops, + .phylink_validate = mv88e6390x_phylink_validate, }; static const struct mv88e6xxx_info mv88e6xxx_table[] = { @@ -3689,6 +3916,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .pvt = true, .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_EDSA, + .ptp_support = true, .ops = &mv88e6161_ops, }, @@ -3711,6 +3939,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .pvt = true, .multi_chip = true, .tag_protocol = DSA_TAG_PROTO_DSA, + .ptp_support = true, .ops = &mv88e6165_ops, }, diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 8ac3fbb15352..f9ecb7872d32 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -155,6 +155,7 @@ struct mv88e6xxx_bus_ops; struct mv88e6xxx_irq_ops; struct mv88e6xxx_gpio_ops; struct mv88e6xxx_avb_ops; +struct mv88e6xxx_ptp_ops; struct mv88e6xxx_irq { u16 masked; @@ -190,12 +191,16 @@ struct mv88e6xxx_port_hwtstamp { }; struct mv88e6xxx_port { + struct mv88e6xxx_chip *chip; + int port; u64 serdes_stats[2]; u64 atu_member_violation; u64 atu_miss_violation; u64 atu_full_violation; u64 vtu_member_violation; u64 vtu_miss_violation; + u8 cmode; + int serdes_irq; }; struct mv88e6xxx_chip { @@ -273,6 +278,7 @@ struct mv88e6xxx_chip { struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO]; u16 trig_config; u16 evcap_config; + u16 enable_count; /* Per-port timestamping resources. */ struct mv88e6xxx_port_hwtstamp port_hwtstamp[DSA_MAX_PORTS]; @@ -349,6 +355,13 @@ struct mv88e6xxx_ops { */ int (*port_set_duplex)(struct mv88e6xxx_chip *chip, int port, int dup); +#define PAUSE_ON 1 +#define PAUSE_OFF 0 + + /* Enable/disable sending Pause */ + int (*port_set_pause)(struct mv88e6xxx_chip *chip, int port, + int pause); + #define SPEED_MAX INT_MAX #define SPEED_UNFORCED -2 @@ -381,12 +394,16 @@ struct mv88e6xxx_ops { */ int (*port_set_cmode)(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); + int (*port_get_cmode)(struct mv88e6xxx_chip *chip, int port, u8 *cmode); /* Some devices have a per port register indicating what is * the upstream port this port should forward to. */ int (*port_set_upstream_port)(struct mv88e6xxx_chip *chip, int port, int upstream_port); + /* Return the port link state, as required by phylink */ + int (*port_link_state)(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); /* Snapshot the statistics for a port. The statistics can then * be read back a leisure but still with a consistent view. @@ -418,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, @@ -439,6 +460,14 @@ struct mv88e6xxx_ops { /* Remote Management Unit operations */ int (*rmu_disable)(struct mv88e6xxx_chip *chip); + + /* 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 { @@ -486,6 +515,24 @@ struct mv88e6xxx_avb_ops { int (*tai_write)(struct mv88e6xxx_chip *chip, int addr, u16 data); }; +struct mv88e6xxx_ptp_ops { + u64 (*clock_read)(const struct cyclecounter *cc); + int (*ptp_enable)(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on); + int (*ptp_verify)(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan); + void (*event_work)(struct work_struct *ugly); + int (*port_enable)(struct mv88e6xxx_chip *chip, int port); + int (*port_disable)(struct mv88e6xxx_chip *chip, int port); + int (*global_enable)(struct mv88e6xxx_chip *chip); + int (*global_disable)(struct mv88e6xxx_chip *chip); + int n_ext_ts; + int arr0_sts_reg; + int arr1_sts_reg; + int dep_sts_reg; + u32 rx_filters; +}; + #define STATS_TYPE_PORT BIT(0) #define STATS_TYPE_BANK0 BIT(1) #define STATS_TYPE_BANK1 BIT(2) diff --git a/drivers/net/dsa/mv88e6xxx/global2.h b/drivers/net/dsa/mv88e6xxx/global2.h index 37e8ce2c72a0..194660d8c783 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.h +++ b/drivers/net/dsa/mv88e6xxx/global2.h @@ -160,6 +160,7 @@ #define MV88E6390_G2_AVB_CMD_OP_WRITE 0x6000 #define MV88E6352_G2_AVB_CMD_PORT_MASK 0x0f00 #define MV88E6352_G2_AVB_CMD_PORT_TAIGLOBAL 0xe +#define MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL 0xf #define MV88E6352_G2_AVB_CMD_PORT_PTPGLOBAL 0xf #define MV88E6390_G2_AVB_CMD_PORT_MASK 0x1f00 #define MV88E6390_G2_AVB_CMD_PORT_TAIGLOBAL 0x1e @@ -335,6 +336,7 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target, extern const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops; extern const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops; +extern const struct mv88e6xxx_avb_ops mv88e6165_avb_ops; extern const struct mv88e6xxx_avb_ops mv88e6352_avb_ops; extern const struct mv88e6xxx_avb_ops mv88e6390_avb_ops; @@ -484,6 +486,7 @@ static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip) static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {}; static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {}; +static const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {}; static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {}; static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {}; diff --git a/drivers/net/dsa/mv88e6xxx/global2_avb.c b/drivers/net/dsa/mv88e6xxx/global2_avb.c index 2e398ccb88ca..672b503a67e1 100644 --- a/drivers/net/dsa/mv88e6xxx/global2_avb.c +++ b/drivers/net/dsa/mv88e6xxx/global2_avb.c @@ -130,6 +130,31 @@ const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = { .tai_write = mv88e6352_g2_avb_tai_write, }; +static int mv88e6165_g2_avb_tai_read(struct mv88e6xxx_chip *chip, int addr, + u16 *data, int len) +{ + return mv88e6352_g2_avb_port_ptp_read(chip, + MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL, + addr, data, len); +} + +static int mv88e6165_g2_avb_tai_write(struct mv88e6xxx_chip *chip, int addr, + u16 data) +{ + return mv88e6352_g2_avb_port_ptp_write(chip, + MV88E6165_G2_AVB_CMD_PORT_PTPGLOBAL, + addr, data); +} + +const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = { + .port_ptp_read = mv88e6352_g2_avb_port_ptp_read, + .port_ptp_write = mv88e6352_g2_avb_port_ptp_write, + .ptp_read = mv88e6352_g2_avb_ptp_read, + .ptp_write = mv88e6352_g2_avb_ptp_write, + .tai_read = mv88e6165_g2_avb_tai_read, + .tai_write = mv88e6165_g2_avb_tai_write, +}; + static int mv88e6390_g2_avb_port_ptp_read(struct mv88e6xxx_chip *chip, int port, int addr, u16 *data, int len) diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.c b/drivers/net/dsa/mv88e6xxx/hwtstamp.c index a036c490b7ce..a17c16a2ab78 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.c +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.c @@ -51,17 +51,30 @@ static int mv88e6xxx_ptp_write(struct mv88e6xxx_chip *chip, int addr, return chip->info->ops->avb_ops->ptp_write(chip, addr, data); } +static int mv88e6xxx_ptp_read(struct mv88e6xxx_chip *chip, int addr, + u16 *data) +{ + if (!chip->info->ops->avb_ops->ptp_read) + return -EOPNOTSUPP; + + return chip->info->ops->avb_ops->ptp_read(chip, addr, data, 1); +} + /* TX_TSTAMP_TIMEOUT: This limits the time spent polling for a TX * timestamp. When working properly, hardware will produce a timestamp * within 1ms. Software may enounter delays due to MDIO contention, so * the timeout is set accordingly. */ -#define TX_TSTAMP_TIMEOUT msecs_to_jiffies(20) +#define TX_TSTAMP_TIMEOUT msecs_to_jiffies(40) int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, struct ethtool_ts_info *info) { - struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ptp_ops *ptp_ops; + struct mv88e6xxx_chip *chip; + + chip = ds->priv; + ptp_ops = chip->info->ops->ptp_ops; if (!chip->info->ptp_support) return -EOPNOTSUPP; @@ -74,17 +87,7 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, info->tx_types = (1 << HWTSTAMP_TX_OFF) | (1 << HWTSTAMP_TX_ON); - info->rx_filters = - (1 << HWTSTAMP_FILTER_NONE) | - (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | - (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | - (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | - (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | - (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | - (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ); + info->rx_filters = ptp_ops->rx_filters; return 0; } @@ -92,10 +95,9 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port, struct hwtstamp_config *config) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; bool tstamp_enable = false; - u16 port_config0; - int err; /* Prevent the TX/RX paths from trying to interact with the * timestamp hardware while we reconfigure it. @@ -120,6 +122,14 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port, /* The switch supports timestamping both L2 and L4; one cannot be * disabled independently of the other. */ + + if (!(BIT(config->rx_filter) & ptp_ops->rx_filters)) { + config->rx_filter = HWTSTAMP_FILTER_NONE; + dev_dbg(chip->dev, "Unsupported rx_filter %d\n", + config->rx_filter); + return -ERANGE; + } + switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: tstamp_enable = false; @@ -141,24 +151,22 @@ static int mv88e6xxx_set_hwtstamp_config(struct mv88e6xxx_chip *chip, int port, return -ERANGE; } + mutex_lock(&chip->reg_lock); if (tstamp_enable) { - /* Disable transportSpecific value matching, so that packets - * with either 1588 (0) and 802.1AS (1) will be timestamped. - */ - port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH; + chip->enable_count += 1; + if (chip->enable_count == 1 && ptp_ops->global_enable) + ptp_ops->global_enable(chip); + if (ptp_ops->port_enable) + ptp_ops->port_enable(chip, port); } else { - /* Disable PTP. This disables both RX and TX timestamping. */ - port_config0 = MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP; + if (ptp_ops->port_disable) + ptp_ops->port_disable(chip, port); + chip->enable_count -= 1; + if (chip->enable_count == 0 && ptp_ops->global_disable) + ptp_ops->global_disable(chip); } - - mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, - port_config0); mutex_unlock(&chip->reg_lock); - if (err < 0) - return err; - /* Once hardware has been configured, enable timestamp checks * in the RX/TX paths. */ @@ -338,17 +346,18 @@ static void mv88e6xxx_get_rxts(struct mv88e6xxx_chip *chip, static void mv88e6xxx_rxtstamp_work(struct mv88e6xxx_chip *chip, struct mv88e6xxx_port_hwtstamp *ps) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; struct sk_buff *skb; skb = skb_dequeue(&ps->rx_queue); if (skb) - mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR0_STS, + mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr0_sts_reg, &ps->rx_queue); skb = skb_dequeue(&ps->rx_queue2); if (skb) - mv88e6xxx_get_rxts(chip, ps, skb, MV88E6XXX_PORT_PTP_ARR1_STS, + mv88e6xxx_get_rxts(chip, ps, skb, ptp_ops->arr1_sts_reg, &ps->rx_queue2); } @@ -389,6 +398,7 @@ bool mv88e6xxx_port_rxtstamp(struct dsa_switch *ds, int port, static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip, struct mv88e6xxx_port_hwtstamp *ps) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; struct skb_shared_hwtstamps shhwtstamps; u16 departure_block[4], status; struct sk_buff *tmp_skb; @@ -401,7 +411,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip, mutex_lock(&chip->reg_lock); err = mv88e6xxx_port_ptp_read(chip, ps->port_id, - MV88E6XXX_PORT_PTP_DEP_STS, + ptp_ops->dep_sts_reg, departure_block, ARRAY_SIZE(departure_block)); mutex_unlock(&chip->reg_lock); @@ -425,8 +435,7 @@ static int mv88e6xxx_txtstamp_work(struct mv88e6xxx_chip *chip, /* We have the timestamp; go ahead and clear valid now */ mutex_lock(&chip->reg_lock); - mv88e6xxx_port_ptp_write(chip, ps->port_id, - MV88E6XXX_PORT_PTP_DEP_STS, 0); + mv88e6xxx_port_ptp_write(chip, ps->port_id, ptp_ops->dep_sts_reg, 0); mutex_unlock(&chip->reg_lock); status = departure_block[0] & MV88E6XXX_PTP_TS_STATUS_MASK; @@ -522,8 +531,48 @@ bool mv88e6xxx_port_txtstamp(struct dsa_switch *ds, int port, return true; } +int mv88e6165_global_disable(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_ptp_read(chip, MV88E6165_PTP_CFG, &val); + if (err) + return err; + val |= MV88E6165_PTP_CFG_DISABLE_PTP; + + return mv88e6xxx_ptp_write(chip, MV88E6165_PTP_CFG, val); +} + +int mv88e6165_global_enable(struct mv88e6xxx_chip *chip) +{ + u16 val; + int err; + + err = mv88e6xxx_ptp_read(chip, MV88E6165_PTP_CFG, &val); + if (err) + return err; + + val &= ~(MV88E6165_PTP_CFG_DISABLE_PTP | MV88E6165_PTP_CFG_TSPEC_MASK); + + return mv88e6xxx_ptp_write(chip, MV88E6165_PTP_CFG, val); +} + +int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, + MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP); +} + +int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port) +{ + return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, + MV88E6XXX_PORT_PTP_CFG0_DISABLE_TSPEC_MATCH); +} + static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; struct mv88e6xxx_port_hwtstamp *ps = &chip->port_hwtstamp[port]; ps->port_id = port; @@ -531,12 +580,15 @@ static int mv88e6xxx_hwtstamp_port_setup(struct mv88e6xxx_chip *chip, int port) skb_queue_head_init(&ps->rx_queue); skb_queue_head_init(&ps->rx_queue2); - return mv88e6xxx_port_ptp_write(chip, port, MV88E6XXX_PORT_PTP_CFG0, - MV88E6XXX_PORT_PTP_CFG0_DISABLE_PTP); + if (ptp_ops->port_disable) + return ptp_ops->port_disable(chip, port); + + return 0; } int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; int err; int i; @@ -547,6 +599,18 @@ int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip) return err; } + /* Disable PTP globally */ + if (ptp_ops->global_disable) { + err = ptp_ops->global_disable(chip); + if (err) + return err; + } + + /* Set the ethertype of L2 PTP messages */ + err = mv88e6xxx_ptp_write(chip, MV88E6XXX_PTP_GC_ETYPE, ETH_P_1588); + if (err) + return err; + /* MV88E6XXX_PTP_MSG_TYPE is a mask of PTP message types to * timestamp. This affects all ports that have timestamping enabled, * but the timestamp config is per-port; thus we configure all events diff --git a/drivers/net/dsa/mv88e6xxx/hwtstamp.h b/drivers/net/dsa/mv88e6xxx/hwtstamp.h index bc71c9212a08..b9a72661bcc4 100644 --- a/drivers/net/dsa/mv88e6xxx/hwtstamp.h +++ b/drivers/net/dsa/mv88e6xxx/hwtstamp.h @@ -19,7 +19,7 @@ #include "chip.h" -/* Global PTP registers */ +/* Global 6352 PTP registers */ /* Offset 0x00: PTP EtherType */ #define MV88E6XXX_PTP_ETHERTYPE 0x00 @@ -34,6 +34,12 @@ /* Offset 0x02: Timestamp Arrival Capture Pointers */ #define MV88E6XXX_PTP_TS_ARRIVAL_PTR 0x02 +/* Offset 0x05: PTP Global Configuration */ +#define MV88E6165_PTP_CFG 0x05 +#define MV88E6165_PTP_CFG_TSPEC_MASK 0xf000 +#define MV88E6165_PTP_CFG_DISABLE_TS_OVERWRITE BIT(1) +#define MV88E6165_PTP_CFG_DISABLE_PTP BIT(0) + /* Offset 0x07: PTP Global Configuration */ #define MV88E6341_PTP_CFG 0x07 #define MV88E6341_PTP_CFG_UPDATE 0x8000 @@ -46,7 +52,7 @@ /* Offset 0x08: PTP Interrupt Status */ #define MV88E6XXX_PTP_IRQ_STATUS 0x08 -/* Per-Port PTP Registers */ +/* Per-Port 6352 PTP Registers */ /* Offset 0x00: PTP Configuration 0 */ #define MV88E6XXX_PORT_PTP_CFG0 0x00 #define MV88E6XXX_PORT_PTP_CFG0_TSPEC_SHIFT 12 @@ -123,6 +129,10 @@ int mv88e6xxx_get_ts_info(struct dsa_switch *ds, int port, int mv88e6xxx_hwtstamp_setup(struct mv88e6xxx_chip *chip); void mv88e6xxx_hwtstamp_free(struct mv88e6xxx_chip *chip); +int mv88e6352_hwtstamp_port_enable(struct mv88e6xxx_chip *chip, int port); +int mv88e6352_hwtstamp_port_disable(struct mv88e6xxx_chip *chip, int port); +int mv88e6165_global_enable(struct mv88e6xxx_chip *chip); +int mv88e6165_global_disable(struct mv88e6xxx_chip *chip); #else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */ diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c index 429d0ebcd5b1..92945841c8e8 100644 --- a/drivers/net/dsa/mv88e6xxx/port.c +++ b/drivers/net/dsa/mv88e6xxx/port.c @@ -19,6 +19,7 @@ #include "chip.h" #include "port.h" +#include "serdes.h" int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, u16 *val) @@ -36,6 +37,29 @@ int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, return mv88e6xxx_write(chip, addr, reg, val); } +/* Offset 0x00: MAC (or PCS or Physical) Status Register + * + * For most devices, this is read only. However the 6185 has the MyPause + * bit read/write. + */ +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause) +{ + u16 reg; + int err; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + if (pause) + reg |= MV88E6XXX_PORT_STS_MY_PAUSE; + else + reg &= ~MV88E6XXX_PORT_STS_MY_PAUSE; + + return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); +} + /* Offset 0x01: MAC (or PCS or Physical) Control Register * * Link, Duplex and Flow Control have one force bit, one value bit. @@ -318,8 +342,9 @@ int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode) { - u16 reg; + int lane; u16 cmode; + u16 reg; int err; if (mode == PHY_INTERFACE_MODE_NA) @@ -349,6 +374,20 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, cmode = 0; } + lane = mv88e6390x_serdes_get_lane(chip, port); + if (lane < 0) + return lane; + + if (chip->ports[port].serdes_irq) { + err = mv88e6390_serdes_irq_disable(chip, port, lane); + if (err) + return err; + } + + err = mv88e6390_serdes_power(chip, port, false); + if (err) + return err; + if (cmode) { err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); if (err) @@ -360,12 +399,38 @@ int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_STS, reg); if (err) return err; + + err = mv88e6390_serdes_power(chip, port, true); + if (err) + return err; + + if (chip->ports[port].serdes_irq) { + err = mv88e6390_serdes_irq_enable(chip, port, lane); + if (err) + return err; + } } + chip->ports[port].cmode = cmode; + return 0; } -int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) +int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) +{ + int err; + u16 reg; + + err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, ®); + if (err) + return err; + + *cmode = reg & MV88E6185_PORT_STS_CMODE_MASK; + + return 0; +} + +int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) { int err; u16 reg; @@ -379,7 +444,7 @@ int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode) return 0; } -int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state) { int err; @@ -400,7 +465,7 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, state->speed = SPEED_1000; break; case MV88E6XXX_PORT_STS_SPEED_10000: - if ((reg &MV88E6XXX_PORT_STS_CMODE_MASK) == + if ((reg & MV88E6XXX_PORT_STS_CMODE_MASK) == MV88E6XXX_PORT_STS_CMODE_2500BASEX) state->speed = SPEED_2500; else @@ -417,6 +482,42 @@ int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, return 0; } +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state) +{ + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + u8 cmode = chip->ports[port].cmode; + + /* When a port is in "Cross-chip serdes" mode, it uses + * 1000Base-X full duplex mode, but there is no automatic + * link detection. Use the sync OK status for link (as it + * would do for 1000Base-X mode.) + */ + if (cmode == MV88E6185_PORT_STS_CMODE_SERDES) { + u16 mac; + int err; + + err = mv88e6xxx_port_read(chip, port, + MV88E6XXX_PORT_MAC_CTL, &mac); + if (err) + return err; + + state->link = !!(mac & MV88E6185_PORT_MAC_CTL_SYNC_OK); + state->an_enabled = 1; + state->an_complete = + !!(mac & MV88E6185_PORT_MAC_CTL_AN_DONE); + state->duplex = + state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN; + state->speed = + state->link ? SPEED_1000 : SPEED_UNKNOWN; + + return 0; + } + } + + return mv88e6352_port_link_state(chip, port, state); +} + /* Offset 0x02: Jamming Control * * Do not limit the period of time that this port can be paused for by diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h index 5e1db1b221ca..f32f56af8e35 100644 --- a/drivers/net/dsa/mv88e6xxx/port.h +++ b/drivers/net/dsa/mv88e6xxx/port.h @@ -42,14 +42,28 @@ #define MV88E6XXX_PORT_STS_CMODE_2500BASEX 0x000b #define MV88E6XXX_PORT_STS_CMODE_XAUI 0x000c #define MV88E6XXX_PORT_STS_CMODE_RXAUI 0x000d +#define MV88E6185_PORT_STS_CDUPLEX 0x0008 +#define MV88E6185_PORT_STS_CMODE_MASK 0x0007 +#define MV88E6185_PORT_STS_CMODE_GMII_FD 0x0000 +#define MV88E6185_PORT_STS_CMODE_MII_100_FD_PS 0x0001 +#define MV88E6185_PORT_STS_CMODE_MII_100 0x0002 +#define MV88E6185_PORT_STS_CMODE_MII_10 0x0003 +#define MV88E6185_PORT_STS_CMODE_SERDES 0x0004 +#define MV88E6185_PORT_STS_CMODE_1000BASE_X 0x0005 +#define MV88E6185_PORT_STS_CMODE_PHY 0x0006 +#define MV88E6185_PORT_STS_CMODE_DISABLED 0x0007 /* Offset 0x01: MAC (or PCS or Physical) Control Register */ #define MV88E6XXX_PORT_MAC_CTL 0x01 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_RXCLK 0x8000 #define MV88E6XXX_PORT_MAC_CTL_RGMII_DELAY_TXCLK 0x4000 +#define MV88E6185_PORT_MAC_CTL_SYNC_OK 0x4000 #define MV88E6390_PORT_MAC_CTL_FORCE_SPEED 0x2000 #define MV88E6390_PORT_MAC_CTL_ALTSPEED 0x1000 #define MV88E6352_PORT_MAC_CTL_200BASE 0x1000 +#define MV88E6185_PORT_MAC_CTL_AN_EN 0x0400 +#define MV88E6185_PORT_MAC_CTL_AN_RESTART 0x0200 +#define MV88E6185_PORT_MAC_CTL_AN_DONE 0x0100 #define MV88E6XXX_PORT_MAC_CTL_FC 0x0080 #define MV88E6XXX_PORT_MAC_CTL_FORCE_FC 0x0040 #define MV88E6XXX_PORT_MAC_CTL_LINK_UP 0x0020 @@ -242,6 +256,8 @@ int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg, int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg, u16 val); +int mv88e6185_port_set_pause(struct mv88e6xxx_chip *chip, int port, + int pause); int mv88e6352_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); int mv88e6390_port_set_rgmii_delay(struct mv88e6xxx_chip *chip, int port, @@ -295,8 +311,11 @@ int mv88e6390_port_pause_limit(struct mv88e6xxx_chip *chip, int port, u8 in, u8 out); int mv88e6390x_port_set_cmode(struct mv88e6xxx_chip *chip, int port, phy_interface_t mode); -int mv88e6xxx_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); -int mv88e6xxx_port_link_state(struct mv88e6xxx_chip *chip, int port, +int mv88e6185_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); +int mv88e6352_port_get_cmode(struct mv88e6xxx_chip *chip, int port, u8 *cmode); +int mv88e6185_port_link_state(struct mv88e6xxx_chip *chip, int port, + struct phylink_link_state *state); +int mv88e6352_port_link_state(struct mv88e6xxx_chip *chip, int port, struct phylink_link_state *state); int mv88e6xxx_port_set_map_da(struct mv88e6xxx_chip *chip, int port); int mv88e6095_port_set_upstream_port(struct mv88e6xxx_chip *chip, int port, diff --git a/drivers/net/dsa/mv88e6xxx/ptp.c b/drivers/net/dsa/mv88e6xxx/ptp.c index bd85e2c390e1..4b336d8d4c67 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.c +++ b/drivers/net/dsa/mv88e6xxx/ptp.c @@ -16,6 +16,7 @@ #include "chip.h" #include "global2.h" +#include "hwtstamp.h" #include "ptp.h" /* Raw timestamps are in units of 8-ns clock periods. */ @@ -50,7 +51,7 @@ static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data) } /* TODO: places where this are called should be using pinctrl */ -static int mv88e6xxx_set_gpio_func(struct mv88e6xxx_chip *chip, int pin, +static int mv88e6352_set_gpio_func(struct mv88e6xxx_chip *chip, int pin, int func, int input) { int err; @@ -65,7 +66,7 @@ static int mv88e6xxx_set_gpio_func(struct mv88e6xxx_chip *chip, int pin, return chip->info->ops->gpio_ops->set_pctl(chip, pin, func); } -static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc) +static u64 mv88e6352_ptp_clock_read(const struct cyclecounter *cc) { struct mv88e6xxx_chip *chip = cc_to_chip(cc); u16 phc_time[2]; @@ -79,13 +80,27 @@ static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc) return ((u32)phc_time[1] << 16) | phc_time[0]; } -/* mv88e6xxx_config_eventcap - configure TAI event capture +static u64 mv88e6165_ptp_clock_read(const struct cyclecounter *cc) +{ + struct mv88e6xxx_chip *chip = cc_to_chip(cc); + u16 phc_time[2]; + int err; + + err = mv88e6xxx_tai_read(chip, MV88E6XXX_PTP_GC_TIME_LO, phc_time, + ARRAY_SIZE(phc_time)); + if (err) + return 0; + else + return ((u32)phc_time[1] << 16) | phc_time[0]; +} + +/* mv88e6352_config_eventcap - configure TAI event capture * @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external) * @rising: zero for falling-edge trigger, else rising-edge trigger * * This will also reset the capture sequence counter. */ -static int mv88e6xxx_config_eventcap(struct mv88e6xxx_chip *chip, int event, +static int mv88e6352_config_eventcap(struct mv88e6xxx_chip *chip, int event, int rising) { u16 global_config; @@ -118,7 +133,7 @@ static int mv88e6xxx_config_eventcap(struct mv88e6xxx_chip *chip, int event, return err; } -static void mv88e6xxx_tai_event_work(struct work_struct *ugly) +static void mv88e6352_tai_event_work(struct work_struct *ugly) { struct delayed_work *dw = to_delayed_work(ugly); struct mv88e6xxx_chip *chip = dw_tai_event_to_chip(dw); @@ -232,7 +247,7 @@ static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp, return 0; } -static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip, +static int mv88e6352_ptp_enable_extts(struct mv88e6xxx_chip *chip, struct ptp_clock_request *rq, int on) { int rising = (rq->extts.flags & PTP_RISING_EDGE); @@ -250,18 +265,18 @@ static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip, if (on) { func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ; - err = mv88e6xxx_set_gpio_func(chip, pin, func, true); + err = mv88e6352_set_gpio_func(chip, pin, func, true); if (err) goto out; schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL); - err = mv88e6xxx_config_eventcap(chip, PTP_CLOCK_EXTTS, rising); + err = mv88e6352_config_eventcap(chip, PTP_CLOCK_EXTTS, rising); } else { func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO; - err = mv88e6xxx_set_gpio_func(chip, pin, func, true); + err = mv88e6352_set_gpio_func(chip, pin, func, true); cancel_delayed_work_sync(&chip->tai_event_work); } @@ -272,20 +287,20 @@ out: return err; } -static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp, +static int mv88e6352_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { struct mv88e6xxx_chip *chip = ptp_to_chip(ptp); switch (rq->type) { case PTP_CLK_REQ_EXTTS: - return mv88e6xxx_ptp_enable_extts(chip, rq, on); + return mv88e6352_ptp_enable_extts(chip, rq, on); default: return -EOPNOTSUPP; } } -static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, +static int mv88e6352_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, enum ptp_pin_function func, unsigned int chan) { switch (func) { @@ -299,6 +314,55 @@ static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, return 0; } +const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = { + .clock_read = mv88e6352_ptp_clock_read, + .ptp_enable = mv88e6352_ptp_enable, + .ptp_verify = mv88e6352_ptp_verify, + .event_work = mv88e6352_tai_event_work, + .port_enable = mv88e6352_hwtstamp_port_enable, + .port_disable = mv88e6352_hwtstamp_port_disable, + .n_ext_ts = 1, + .arr0_sts_reg = MV88E6XXX_PORT_PTP_ARR0_STS, + .arr1_sts_reg = MV88E6XXX_PORT_PTP_ARR1_STS, + .dep_sts_reg = MV88E6XXX_PORT_PTP_DEP_STS, + .rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), +}; + +const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = { + .clock_read = mv88e6165_ptp_clock_read, + .global_enable = mv88e6165_global_enable, + .global_disable = mv88e6165_global_disable, + .arr0_sts_reg = MV88E6165_PORT_PTP_ARR0_STS, + .arr1_sts_reg = MV88E6165_PORT_PTP_ARR1_STS, + .dep_sts_reg = MV88E6165_PORT_PTP_DEP_STS, + .rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ) | + (1 << HWTSTAMP_FILTER_PTP_V2_EVENT) | + (1 << HWTSTAMP_FILTER_PTP_V2_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V2_DELAY_REQ), +}; + +static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc) +{ + struct mv88e6xxx_chip *chip = cc_to_chip(cc); + + if (chip->info->ops->ptp_ops->clock_read) + return chip->info->ops->ptp_ops->clock_read(cc); + + return 0; +} + /* With a 125MHz input clock, the 32-bit timestamp counter overflows in ~34.3 * seconds; this task forces periodic reads so that we don't miss any. */ @@ -317,6 +381,7 @@ static void mv88e6xxx_ptp_overflow_check(struct work_struct *work) int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) { + const struct mv88e6xxx_ptp_ops *ptp_ops = chip->info->ops->ptp_ops; int i; /* Set up the cycle counter */ @@ -330,14 +395,15 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) ktime_to_ns(ktime_get_real())); INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check); - INIT_DELAYED_WORK(&chip->tai_event_work, mv88e6xxx_tai_event_work); + if (ptp_ops->event_work) + INIT_DELAYED_WORK(&chip->tai_event_work, ptp_ops->event_work); chip->ptp_clock_info.owner = THIS_MODULE; snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name), dev_name(chip->dev)); chip->ptp_clock_info.max_adj = 1000000; - chip->ptp_clock_info.n_ext_ts = 1; + chip->ptp_clock_info.n_ext_ts = ptp_ops->n_ext_ts; chip->ptp_clock_info.n_per_out = 0; chip->ptp_clock_info.n_pins = mv88e6xxx_num_gpio(chip); chip->ptp_clock_info.pps = 0; @@ -355,8 +421,8 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip) chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime; chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime; chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime; - chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable; - chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify; + chip->ptp_clock_info.enable = ptp_ops->ptp_enable; + chip->ptp_clock_info.verify = ptp_ops->ptp_verify; chip->ptp_clock_info.do_aux_work = mv88e6xxx_hwtstamp_work; chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev); @@ -373,7 +439,8 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) { if (chip->ptp_clock) { cancel_delayed_work_sync(&chip->overflow_work); - cancel_delayed_work_sync(&chip->tai_event_work); + if (chip->info->ops->ptp_ops->event_work) + cancel_delayed_work_sync(&chip->tai_event_work); ptp_clock_unregister(chip->ptp_clock); chip->ptp_clock = NULL; diff --git a/drivers/net/dsa/mv88e6xxx/ptp.h b/drivers/net/dsa/mv88e6xxx/ptp.h index 10f271ab650d..28a030840517 100644 --- a/drivers/net/dsa/mv88e6xxx/ptp.h +++ b/drivers/net/dsa/mv88e6xxx/ptp.h @@ -78,6 +78,71 @@ /* Offset 0x12: Lock Status */ #define MV88E6XXX_TAI_LOCK_STATUS 0x12 +/* Offset 0x00: Ether Type */ +#define MV88E6XXX_PTP_GC_ETYPE 0x00 + +/* 6165 Global Control Registers */ +/* Offset 0x00: Ether Type */ +#define MV88E6XXX_PTP_GC_ETYPE 0x00 + +/* Offset 0x01: Message ID */ +#define MV88E6XXX_PTP_GC_MESSAGE_ID 0x01 + +/* Offset 0x02: Time Stamp Arrive Time */ +#define MV88E6XXX_PTP_GC_TS_ARR_PTR 0x02 + +/* Offset 0x03: Port Arrival Interrupt Enable */ +#define MV88E6XXX_PTP_GC_PORT_ARR_INT_EN 0x03 + +/* Offset 0x04: Port Departure Interrupt Enable */ +#define MV88E6XXX_PTP_GC_PORT_DEP_INT_EN 0x04 + +/* Offset 0x05: Configuration */ +#define MV88E6XXX_PTP_GC_CONFIG 0x05 +#define MV88E6XXX_PTP_GC_CONFIG_DIS_OVERWRITE BIT(1) +#define MV88E6XXX_PTP_GC_CONFIG_DIS_TS BIT(0) + +/* Offset 0x8: Interrupt Status */ +#define MV88E6XXX_PTP_GC_INT_STATUS 0x08 + +/* Offset 0x9/0xa: Global Time */ +#define MV88E6XXX_PTP_GC_TIME_LO 0x09 +#define MV88E6XXX_PTP_GC_TIME_HI 0x0A + +/* 6165 Per Port Registers */ +/* Offset 0: Arrival Time 0 Status */ +#define MV88E6165_PORT_PTP_ARR0_STS 0x00 + +/* Offset 0x01/0x02: PTP Arrival 0 Time */ +#define MV88E6165_PORT_PTP_ARR0_TIME_LO 0x01 +#define MV88E6165_PORT_PTP_ARR0_TIME_HI 0x02 + +/* Offset 0x03: PTP Arrival 0 Sequence ID */ +#define MV88E6165_PORT_PTP_ARR0_SEQID 0x03 + +/* Offset 0x04: PTP Arrival 1 Status */ +#define MV88E6165_PORT_PTP_ARR1_STS 0x04 + +/* Offset 0x05/0x6E: PTP Arrival 1 Time */ +#define MV88E6165_PORT_PTP_ARR1_TIME_LO 0x05 +#define MV88E6165_PORT_PTP_ARR1_TIME_HI 0x06 + +/* Offset 0x07: PTP Arrival 1 Sequence ID */ +#define MV88E6165_PORT_PTP_ARR1_SEQID 0x07 + +/* Offset 0x08: PTP Departure Status */ +#define MV88E6165_PORT_PTP_DEP_STS 0x08 + +/* Offset 0x09/0x0a: PTP Deperture Time */ +#define MV88E6165_PORT_PTP_DEP_TIME_LO 0x09 +#define MV88E6165_PORT_PTP_DEP_TIME_HI 0x0a + +/* Offset 0x0b: PTP Departure Sequence ID */ +#define MV88E6165_PORT_PTP_DEP_SEQID 0x0b + +/* Offset 0x0d: Port Status */ +#define MV88E6164_PORT_STATUS 0x0d + #ifdef CONFIG_NET_DSA_MV88E6XXX_PTP long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp); @@ -87,6 +152,9 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip); #define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \ ptp_clock_info) +extern const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops; +extern const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops; + #else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */ static inline long mv88e6xxx_hwtstamp_work(struct ptp_clock_info *ptp) @@ -103,6 +171,9 @@ static inline void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip) { } +static const struct mv88e6xxx_ptp_ops mv88e6352_ptp_ops = {}; +static const struct mv88e6xxx_ptp_ops mv88e6165_ptp_ops = {}; + #endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */ #endif /* _MV88E6XXX_PTP_H */ diff --git a/drivers/net/dsa/mv88e6xxx/serdes.c b/drivers/net/dsa/mv88e6xxx/serdes.c index 880b2cf0a530..e82983975754 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 <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/mii.h> #include "chip.h" @@ -35,6 +37,22 @@ static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, reg, val); } +static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, + int lane, int device, int reg, u16 *val) +{ + int reg_c45 = MII_ADDR_C45 | device << 16 | reg; + + return mv88e6xxx_phy_read(chip, lane, reg_c45, val); +} + +static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, + int lane, int device, int reg, u16 val) +{ + int reg_c45 = MII_ADDR_C45 | device << 16 | reg; + + return mv88e6xxx_phy_write(chip, lane, reg_c45, val); +} + static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) { u16 val, new_val; @@ -57,14 +75,7 @@ static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) { - u8 cmode; - int err; - - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) { - dev_err(chip->dev, "failed to read cmode\n"); - return false; - } + u8 cmode = chip->ports[port].cmode; if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || @@ -174,16 +185,121 @@ int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, return ARRAY_SIZE(mv88e6352_serdes_hw_stats); } +/* Return the SERDES lane address a port is using. Only Ports 9 and 10 + * have SERDES lanes. Returns -ENODEV if a port does not have a lane. + */ +static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + u8 cmode = chip->ports[port].cmode; + + switch (port) { + case 9: + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + return MV88E6390_PORT9_LANE0; + return -ENODEV; + case 10: + if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + return MV88E6390_PORT10_LANE0; + return -ENODEV; + default: + return -ENODEV; + } +} + +/* Return the SERDES lane address a port is using. Ports 9 and 10 can + * use multiple lanes. If so, return the first lane the port uses. + * Returns -ENODEV if a port does not have a lane. + */ +int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) +{ + u8 cmode_port9, cmode_port10, cmode_port; + + cmode_port9 = chip->ports[9].cmode; + cmode_port10 = chip->ports[10].cmode; + cmode_port = chip->ports[port].cmode; + + switch (port) { + case 2: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE1; + return -ENODEV; + case 3: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE2; + return -ENODEV; + case 4: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT9_LANE3; + return -ENODEV; + case 5: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE1; + return -ENODEV; + case 6: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE2; + return -ENODEV; + case 7: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) + return MV88E6390_PORT10_LANE3; + return -ENODEV; + case 9: + if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || + cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + return MV88E6390_PORT9_LANE0; + return -ENODEV; + case 10: + if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || + cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) + return MV88E6390_PORT10_LANE0; + return -ENODEV; + default: + return -ENODEV; + } +} + /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) +static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; - int reg_c45; int err; - reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | - MV88E6390_PCS_CONTROL_1; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, &val); + if (err) return err; @@ -195,22 +311,21 @@ static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_PCS_CONTROL_1, new_val); return err; } -/* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ -static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, - bool on) +/* Set the power on/off for SGMII and 1000Base-X */ +static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, + bool on) { u16 val, new_val; - int reg_c45; int err; - reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | - MV88E6390_SGMII_CONTROL; - err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, &val); if (err) return err; @@ -222,127 +337,261 @@ static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; if (val != new_val) - err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); + err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_CONTROL, new_val); return err; } -static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, - int port_donor, int lane, bool rxaui, bool on) +static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, + int lane, bool on) { - int err; - u8 cmode_donor; - - err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); - if (err) - return err; + u8 cmode = chip->ports[port].cmode; - switch (cmode_donor) { - case MV88E6XXX_PORT_STS_CMODE_RXAUI: - if (!rxaui) - break; - /* Fall through */ - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || - cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) - return mv88e6390_serdes_sgmii(chip, lane, on); + return mv88e6390_serdes_power_sgmii(chip, lane, on); + case MV88E6XXX_PORT_STS_CMODE_XAUI: + case MV88E6XXX_PORT_STS_CMODE_RXAUI: + return mv88e6390_serdes_power_10g(chip, lane, on); + } + + return 0; +} + +int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + int lane; + + lane = mv88e6390_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + switch (port) { + case 9 ... 10: + return mv88e6390_serdes_power_lane(chip, port, lane, on); } + + return 0; +} + +int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + int lane; + + lane = mv88e6390x_serdes_get_lane(chip, port); + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + switch (port) { + case 2 ... 4: + case 5 ... 7: + case 9 ... 10: + return mv88e6390_serdes_power_lane(chip, port, lane, on); + } + return 0; } -static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) +static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, + int port, int lane) { + struct dsa_switch *ds = chip->ds; + u16 status; + bool up; + + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_STATUS, &status); + + /* Status must be read twice in order to give the current link + * status. Otherwise the change in link status since the last + * read of the register is returned. + */ + mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_STATUS, &status); + up = status & MV88E6390_SGMII_STATUS_LINK; + + dsa_port_phylink_mac_change(ds, port, up); +} + +static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, + int lane) +{ + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_ENABLE, + MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP); +} + +static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip, + int lane) +{ + return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_ENABLE, 0); +} + +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, + int lane) +{ + u8 cmode = chip->ports[port].cmode; + int err = 0; + switch (cmode) { - case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_SGMII: - return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); - case MV88E6XXX_PORT_STS_CMODE_XAUI: - case MV88E6XXX_PORT_STS_CMODE_RXAUI: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); + err = mv88e6390_serdes_irq_enable_sgmii(chip, lane); } - return 0; + return err; } -static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, - bool on) +int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, + int lane) { + u8 cmode = chip->ports[port].cmode; + int err = 0; + switch (cmode) { case MV88E6XXX_PORT_STS_CMODE_SGMII: - return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on); - case MV88E6XXX_PORT_STS_CMODE_XAUI: - case MV88E6XXX_PORT_STS_CMODE_RXAUI: case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: case MV88E6XXX_PORT_STS_CMODE_2500BASEX: - return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); + err = mv88e6390_serdes_irq_disable_sgmii(chip, lane); } - return 0; + return err; } -int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, + int lane, u16 *status) { - u8 cmode; int err; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) - return err; + err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, + MV88E6390_SGMII_INT_STATUS, status); - switch (port) { - case 2: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE1, - false, on); - case 3: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE2, - true, on); - case 4: - return mv88e6390_serdes_lower(chip, cmode, 9, - MV88E6390_PORT9_LANE3, - true, on); - case 5: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE1, - false, on); - case 6: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE2, - true, on); - case 7: - return mv88e6390_serdes_lower(chip, cmode, 10, - MV88E6390_PORT10_LANE3, - true, on); - case 9: - return mv88e6390_serdes_port9(chip, cmode, on); - case 10: - return mv88e6390_serdes_port10(chip, cmode, on); + return err; +} + +static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_port *port = dev_id; + struct mv88e6xxx_chip *chip = port->chip; + irqreturn_t ret = IRQ_NONE; + u8 cmode = port->cmode; + u16 status; + int lane; + int err; + + lane = mv88e6390x_serdes_get_lane(chip, port->port); + + mutex_lock(&chip->reg_lock); + + switch (cmode) { + case MV88E6XXX_PORT_STS_CMODE_SGMII: + case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: + case MV88E6XXX_PORT_STS_CMODE_2500BASEX: + err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); + if (err) + goto out; + if (status & (MV88E6390_SGMII_INT_LINK_DOWN | + MV88E6390_SGMII_INT_LINK_UP)) { + ret = IRQ_HANDLED; + mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane); + } } +out: + mutex_unlock(&chip->reg_lock); - return 0; + return ret; } -int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port) { + int lane; int err; - u8 cmode; - if (port != 5) + /* Only support ports 9 and 10 at the moment */ + if (port < 9) return 0; - err = mv88e6xxx_port_get_cmode(chip, port, &cmode); - if (err) + lane = mv88e6390x_serdes_get_lane(chip, port); + + if (lane == -ENODEV) + return 0; + + if (lane < 0) + return lane; + + chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain, + port); + if (chip->ports[port].serdes_irq < 0) { + dev_err(chip->dev, "Unable to map SERDES irq: %d\n", + chip->ports[port].serdes_irq); + return chip->ports[port].serdes_irq; + } + + /* Requesting the IRQ will trigger irq callbacks. So we cannot + * hold the reg_lock. + */ + mutex_unlock(&chip->reg_lock); + err = request_threaded_irq(chip->ports[port].serdes_irq, NULL, + mv88e6390_serdes_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-serdes", + &chip->ports[port]); + mutex_lock(&chip->reg_lock); + + if (err) { + dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n", + err); return err; + } + + return mv88e6390_serdes_irq_enable(chip, port, lane); +} + +void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port) +{ + int lane = mv88e6390x_serdes_get_lane(chip, port); + + if (port < 9) + return; + + if (lane < 0) + return; + + mv88e6390_serdes_irq_disable(chip, port, lane); + + /* Freeing the IRQ will trigger irq callbacks. So we cannot + * hold the reg_lock. + */ + mutex_unlock(&chip->reg_lock); + free_irq(chip->ports[port].serdes_irq, &chip->ports[port]); + mutex_lock(&chip->reg_lock); + + chip->ports[port].serdes_irq = 0; +} + +int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) +{ + u8 cmode = chip->ports[port].cmode; + + if (port != 5) + return 0; if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) - return mv88e6390_serdes_sgmii(chip, MV88E6341_ADDR_SERDES, on); + return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES, + on); return 0; } diff --git a/drivers/net/dsa/mv88e6xxx/serdes.h b/drivers/net/dsa/mv88e6xxx/serdes.h index b6e5fbd46b5e..b1496de9c6fe 100644 --- a/drivers/net/dsa/mv88e6xxx/serdes.h +++ b/drivers/net/dsa/mv88e6xxx/serdes.h @@ -29,7 +29,6 @@ #define MV88E6390_PORT10_LANE1 0x15 #define MV88E6390_PORT10_LANE2 0x16 #define MV88E6390_PORT10_LANE3 0x17 -#define MV88E6390_SERDES_DEVICE (4 << 16) /* 10GBASE-R and 10GBASE-X4/X2 */ #define MV88E6390_PCS_CONTROL_1 0x1000 @@ -43,13 +42,36 @@ #define MV88E6390_SGMII_CONTROL_RESET BIT(15) #define MV88E6390_SGMII_CONTROL_LOOPBACK BIT(14) #define MV88E6390_SGMII_CONTROL_PDOWN BIT(11) +#define MV88E6390_SGMII_STATUS 0x2001 +#define MV88E6390_SGMII_STATUS_AN_DONE BIT(5) +#define MV88E6390_SGMII_STATUS_REMOTE_FAULT BIT(4) +#define MV88E6390_SGMII_STATUS_LINK BIT(2) +#define MV88E6390_SGMII_INT_ENABLE 0xa001 +#define MV88E6390_SGMII_INT_SPEED_CHANGE BIT(14) +#define MV88E6390_SGMII_INT_DUPLEX_CHANGE BIT(13) +#define MV88E6390_SGMII_INT_PAGE_RX BIT(12) +#define MV88E6390_SGMII_INT_AN_COMPLETE BIT(11) +#define MV88E6390_SGMII_INT_LINK_DOWN BIT(10) +#define MV88E6390_SGMII_INT_LINK_UP BIT(9) +#define MV88E6390_SGMII_INT_SYMBOL_ERROR BIT(8) +#define MV88E6390_SGMII_INT_FALSE_CARRIER BIT(7) +#define MV88E6390_SGMII_INT_STATUS 0xa002 +int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port); int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); +int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on); +int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port); +void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port); int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, int port, uint8_t *data); int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, uint64_t *data); +int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, + int lane); +int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port, + int lane); + #endif |