diff options
23 files changed, 358 insertions, 180 deletions
diff --git a/Documentation/networking/sfp-phylink.rst b/Documentation/networking/sfp-phylink.rst index d753a309f9d1..8d7af28cd835 100644 --- a/Documentation/networking/sfp-phylink.rst +++ b/Documentation/networking/sfp-phylink.rst @@ -74,10 +74,13 @@ phylib to the sfp/phylink support. Please send patches to improve this documentation. 1. Optionally split the network driver's phylib update function into - three parts dealing with link-down, link-up and reconfiguring the - MAC settings. This can be done as a separate preparation commit. + two parts dealing with link-down and link-up. This can be done as + a separate preparation commit. - An example of this preparation can be found in git commit fc548b991fb0. + An older example of this preparation can be found in git commit + fc548b991fb0, although this was splitting into three parts; the + link-up part now includes configuring the MAC for the link settings. + Please see :c:func:`mac_link_up` for more information on this. 2. Replace:: @@ -207,6 +210,14 @@ this documentation. using. This is particularly important for in-band negotiation methods such as 1000base-X and SGMII. + The :c:func:`mac_link_up` method is used to inform the MAC that the + link has come up. The call includes the negotiation mode and interface + for reference only. The finalised link parameters are also supplied + (speed, duplex and flow control/pause enablement settings) which + should be used to configure the MAC when the MAC and PCS are not + tightly integrated, or when the settings are not coming from in-band + negotiation. + The :c:func:`mac_config` method is used to update the MAC with the requested state, and must avoid unnecessarily taking the link down when making changes to the MAC configuration. This means the diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 1a69286daa8d..ceafce446317 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1289,7 +1289,9 @@ EXPORT_SYMBOL(b53_phylink_mac_link_down); void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct b53_device *dev = ds->priv; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 3c30f3a7eb29..3d42318bc3f1 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -338,7 +338,9 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port, void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev); + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause); int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering); int b53_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d1955543acd1..2daca9f0b8ca 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -649,7 +649,9 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->dev->ports[port].eee; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 0369c22fe3e1..cf6fa8fede33 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1517,7 +1517,9 @@ static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port, static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct gswip_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 022466ca1c19..86818ab3bb07 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1482,7 +1482,9 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port, static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct mt7530_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 705c118f6fdd..483db9d133c3 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -632,33 +632,78 @@ static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port, dev_err(ds->dev, "p%d: failed to configure MAC\n", port); } -static void mv88e6xxx_mac_link_force(struct dsa_switch *ds, int port, int link) +static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface) { struct mv88e6xxx_chip *chip = ds->priv; - int err; + const struct mv88e6xxx_ops *ops; + int err = 0; - mv88e6xxx_reg_lock(chip); - err = chip->info->ops->port_set_link(chip, port, link); - mv88e6xxx_reg_unlock(chip); + ops = chip->info->ops; - if (err) - dev_err(chip->dev, "p%d: failed to force MAC link\n", port); -} + /* Internal PHYs propagate their configuration directly to the MAC. + * External PHYs depend on whether the PPU is enabled for this port. + * FIXME: we should be using the PPU enable state here. What about + * an automedia port? + */ + if (!mv88e6xxx_phy_is_internal(ds, port) && ops->port_set_link) { + mv88e6xxx_reg_lock(chip); + err = ops->port_set_link(chip, port, LINK_FORCED_DOWN); + mv88e6xxx_reg_unlock(chip); -static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, - unsigned int mode, - phy_interface_t interface) -{ - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_DOWN); + if (err) + dev_err(chip->dev, + "p%d: failed to force MAC link down\n", port); + } } static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - if (mode == MLO_AN_FIXED) - mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP); + struct mv88e6xxx_chip *chip = ds->priv; + const struct mv88e6xxx_ops *ops; + int err = 0; + + ops = chip->info->ops; + + /* Internal PHYs propagate their configuration directly to the MAC. + * External PHYs depend on whether the PPU is enabled for this port. + * FIXME: we should be using the PPU enable state here. What about + * an automedia port? + */ + if (!mv88e6xxx_phy_is_internal(ds, port)) { + mv88e6xxx_reg_lock(chip); + /* FIXME: for an automedia port, should we force the link + * down here - what if the link comes up due to "other" media + * while we're bringing the port up, how is the exclusivity + * handled in the Marvell hardware? E.g. port 4 on 88E6532 + * shared between internal PHY and Serdes. + */ + if (ops->port_set_speed) { + err = ops->port_set_speed(chip, port, speed); + if (err && err != -EOPNOTSUPP) + goto error; + } + + if (ops->port_set_duplex) { + err = ops->port_set_duplex(chip, port, duplex); + if (err && err != -EOPNOTSUPP) + goto error; + } + + if (ops->port_set_link) + err = ops->port_set_link(chip, port, LINK_FORCED_UP); +error: + mv88e6xxx_reg_unlock(chip); + + if (err && err != -EOPNOTSUPP) + dev_err(ds->dev, + "p%d: failed to configure MAC link up\n", port); + } } static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 35124ef7e75b..7e66821b05b4 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -263,7 +263,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int link_an_mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct ocelot *ocelot = ds->priv; struct ocelot_port *ocelot_port = ocelot->ports[port]; diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index de25f99e995a..7c86056b9401 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -458,7 +458,9 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 03ba6d25f7fe..c27cc7b37440 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -786,7 +786,9 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port, static void sja1105_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { sja1105_inhibit_tx(ds->priv, BIT(port), false); } diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index a3f0f27fc79a..ab827fb4b6b9 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1200,7 +1200,6 @@ struct macb { unsigned int dma_burst_length; phy_interface_t phy_interface; - int speed; /* AT91RM9200 transmit */ struct sk_buff *skb; /* holds skb until xmit interrupt completes */ diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2c28da1737fe..3a7c26b08607 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -571,37 +571,20 @@ static void macb_mac_config(struct phylink_config *config, unsigned int mode, old_ctrl = ctrl = macb_or_gem_readl(bp, NCFGR); - /* Clear all the bits we might set later */ - ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD) | MACB_BIT(PAE)); - if (bp->caps & MACB_CAPS_MACB_IS_EMAC) { if (state->interface == PHY_INTERFACE_MODE_RMII) ctrl |= MACB_BIT(RM9200_RMII); } else { - ctrl &= ~(GEM_BIT(GBE) | GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL)); - - /* We do not support MLO_PAUSE_RX yet */ - if (state->pause & MLO_PAUSE_TX) - ctrl |= MACB_BIT(PAE); + ctrl &= ~(GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL)); if (state->interface == PHY_INTERFACE_MODE_SGMII) ctrl |= GEM_BIT(SGMIIEN) | GEM_BIT(PCSSEL); } - if (state->speed == SPEED_1000) - ctrl |= GEM_BIT(GBE); - else if (state->speed == SPEED_100) - ctrl |= MACB_BIT(SPD); - - if (state->duplex) - ctrl |= MACB_BIT(FD); - /* Apply the new configuration, if any */ if (old_ctrl ^ ctrl) macb_or_gem_writel(bp, NCFGR, ctrl); - bp->speed = state->speed; - spin_unlock_irqrestore(&bp->lock, flags); } @@ -626,16 +609,42 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, netif_tx_stop_all_queues(ndev); } -static void macb_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void macb_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *ndev = to_net_dev(config->dev); struct macb *bp = netdev_priv(ndev); struct macb_queue *queue; + unsigned long flags; unsigned int q; + u32 ctrl; + + spin_lock_irqsave(&bp->lock, flags); + + ctrl = macb_or_gem_readl(bp, NCFGR); + + ctrl &= ~(MACB_BIT(SPD) | MACB_BIT(FD)); + + if (speed == SPEED_100) + ctrl |= MACB_BIT(SPD); + + if (duplex) + ctrl |= MACB_BIT(FD); if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) { - macb_set_tx_clk(bp->tx_clk, bp->speed, ndev); + ctrl &= ~(GEM_BIT(GBE) | MACB_BIT(PAE)); + + if (speed == SPEED_1000) + ctrl |= GEM_BIT(GBE); + + /* We do not support MLO_PAUSE_RX yet */ + if (tx_pause) + ctrl |= MACB_BIT(PAE); + + macb_set_tx_clk(bp->tx_clk, speed, ndev); /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. @@ -648,6 +657,10 @@ static void macb_mac_link_up(struct phylink_config *config, unsigned int mode, bp->rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT(HRESP)); } + macb_or_gem_writel(bp, NCFGR, ctrl); + + spin_unlock_irqrestore(&bp->lock, flags); + /* Enable Rx and Tx */ macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(RE) | MACB_BIT(TE)); @@ -4429,8 +4442,6 @@ static int macb_probe(struct platform_device *pdev) else bp->phy_interface = interface; - bp->speed = SPEED_UNKNOWN; - /* IP specific init */ err = init(pdev); if (err) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 84233e467ed1..3ee236c5fc37 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -123,49 +123,60 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, struct dpmac_link_state *dpmac_state = &mac->state; int err; - if (state->speed != SPEED_UNKNOWN) - dpmac_state->rate = state->speed; - - if (state->duplex != DUPLEX_UNKNOWN) { - if (!state->duplex) - dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; - } - if (state->an_enabled) dpmac_state->options |= DPMAC_LINK_OPT_AUTONEG; else dpmac_state->options &= ~DPMAC_LINK_OPT_AUTONEG; - if (state->pause & MLO_PAUSE_RX) - dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; - - if (!!(state->pause & MLO_PAUSE_RX) ^ !!(state->pause & MLO_PAUSE_TX)) - dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; - else - dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; - err = dpmac_set_link_state(mac->mc_io, 0, mac->mc_dev->mc_handle, dpmac_state); if (err) - netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err); + netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", + __func__, err); } -static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void dpaa2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); struct dpmac_link_state *dpmac_state = &mac->state; int err; dpmac_state->up = 1; + + if (mac->if_link_type == DPMAC_LINK_TYPE_PHY) { + /* If the DPMAC is configured for PHY mode, we need + * to pass the link parameters to the MC firmware. + */ + dpmac_state->rate = speed; + + if (duplex == DUPLEX_HALF) + dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX; + else if (duplex == DUPLEX_FULL) + dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX; + + /* This is lossy; the firmware really should take the pause + * enablement status rather than pause/asym pause status. + */ + if (rx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE; + + if (rx_pause ^ tx_pause) + dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE; + else + dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE; + } + err = dpmac_set_link_state(mac->mc_io, 0, mac->mc_dev->mc_handle, dpmac_state); if (err) - netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err); + netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n", + __func__, err); } static void dpaa2_mac_link_down(struct phylink_config *config, @@ -238,6 +249,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) goto err_close_dpmac; } + mac->if_link_type = attr.link_type; + dpmac_node = dpaa2_mac_get_node(attr.id); if (!dpmac_node) { netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index 4da8079b9155..2130d9c7d40e 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -20,6 +20,7 @@ struct dpaa2_mac { struct phylink_config phylink_config; struct phylink *phylink; phy_interface_t if_mode; + enum dpmac_link_type if_link_type; }; bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev, diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 1c391f63a26f..b22eeb5f8700 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3830,13 +3830,9 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_INBAND_RESTART_AN | - MVNETA_GMAC_CONFIG_MII_SPEED | - MVNETA_GMAC_CONFIG_GMII_SPEED | MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | - MVNETA_GMAC_CONFIG_FLOW_CTRL | MVNETA_GMAC_AN_FLOW_CTRL_EN | - MVNETA_GMAC_CONFIG_FULL_DUPLEX | MVNETA_GMAC_AN_DUPLEX_EN); /* Even though it might look weird, when we're configured in @@ -3851,24 +3847,20 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, if (phylink_test(state->advertising, Pause)) new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; - if (state->pause & MLO_PAUSE_TXRX_MASK) - new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL; if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed */ - if (state->duplex) - new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; - - if (state->speed == SPEED_1000 || state->speed == SPEED_2500) - new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED; - else if (state->speed == SPEED_100) - new_an |= MVNETA_GMAC_CONFIG_MII_SPEED; + /* Phy or fixed speed - nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII mode receives the state from the PHY */ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS)) | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FULL_DUPLEX)) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_AN_SPEED_EN | MVNETA_GMAC_AN_DUPLEX_EN; @@ -3877,7 +3869,8 @@ static void mvneta_mac_config(struct phylink_config *config, unsigned int mode, new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | - MVNETA_GMAC_FORCE_LINK_PASS)) | + MVNETA_GMAC_FORCE_LINK_PASS | + MVNETA_GMAC_CONFIG_MII_SPEED)) | MVNETA_GMAC_INBAND_AN_ENABLE | MVNETA_GMAC_CONFIG_GMII_SPEED | /* The MAC only supports FD mode */ @@ -3965,9 +3958,11 @@ static void mvneta_mac_link_down(struct phylink_config *config, mvneta_set_eee(pp, false); } -static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) +static void mvneta_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); @@ -3975,8 +3970,36 @@ static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode, if (!phylink_autoneg_inband(mode)) { val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); - val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; + val &= ~(MVNETA_GMAC_FORCE_LINK_DOWN | + MVNETA_GMAC_CONFIG_MII_SPEED | + MVNETA_GMAC_CONFIG_GMII_SPEED | + MVNETA_GMAC_CONFIG_FLOW_CTRL | + MVNETA_GMAC_CONFIG_FULL_DUPLEX); val |= MVNETA_GMAC_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + val |= MVNETA_GMAC_CONFIG_GMII_SPEED; + else if (speed == SPEED_100) + val |= MVNETA_GMAC_CONFIG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; + + if (tx_pause || rx_pause) + val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; + + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } else { + /* When inband doesn't cover flow control or flow control is + * disabled, we need to manually configure it. This bit will + * only have effect if MVNETA_GMAC_AN_FLOW_CTRL_EN is unset. + */ + val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); + val &= ~MVNETA_GMAC_CONFIG_FLOW_CTRL; + + if (tx_pause || rx_pause) + val |= MVNETA_GMAC_CONFIG_FLOW_CTRL; + mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); } diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 72133cbe55d4..6b9c7ed2547e 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -58,8 +58,11 @@ static struct { */ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state); -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy); +static void mvpp2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause); /* Queue modes */ #define MVPP2_QDIST_SINGLE_MODE 0 @@ -3473,8 +3476,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port) .interface = port->phy_interface, }; mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); - mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND, - port->phy_interface, NULL); + mvpp2_mac_link_up(&port->phylink_config, NULL, + MLO_AN_INBAND, port->phy_interface, + SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false); } netif_tx_start_all_queues(port->dev); @@ -4972,15 +4976,13 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, old_ctrl2 = ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG); old_ctrl4 = ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG); - an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED | - MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN | + an &= ~(MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN | MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | - MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN | - MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS); + MVPP2_GMAC_AN_DUPLEX_EN | MVPP2_GMAC_IN_BAND_AUTONEG | + MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS); ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK; ctrl2 &= ~(MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK); - ctrl4 &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN); /* Configure port type */ if (phy_interface_mode_is_8023z(state->interface)) { @@ -5010,31 +5012,20 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, /* Configure negotiation style */ if (!phylink_autoneg_inband(mode)) { - /* Phy or fixed speed - no in-band AN */ - if (state->duplex) - an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; - - if (state->speed == SPEED_1000 || state->speed == SPEED_2500) - an |= MVPP2_GMAC_CONFIG_GMII_SPEED; - else if (state->speed == SPEED_100) - an |= MVPP2_GMAC_CONFIG_MII_SPEED; - - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; + /* Phy or fixed speed - no in-band AN, nothing to do, leave the + * configured speed, duplex and flow control as-is. + */ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) { /* SGMII in-band mode receives the speed and duplex from * the PHY. Flow control information is not received. */ - an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS); + an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); an |= MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_AN_DUPLEX_EN; - - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; } else if (phy_interface_mode_is_8023z(state->interface)) { /* 1000BaseX and 2500BaseX ports cannot negotiate speed nor can * they negotiate duplex: they are always operating with a fixed @@ -5042,19 +5033,17 @@ static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode, * speed and full duplex here. */ ctrl0 |= MVPP2_GMAC_PORT_TYPE_MASK; - an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | MVPP2_GMAC_FORCE_LINK_PASS); + an &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); an |= MVPP2_GMAC_IN_BAND_AUTONEG | MVPP2_GMAC_CONFIG_GMII_SPEED | MVPP2_GMAC_CONFIG_FULL_DUPLEX; - if (state->pause & MLO_PAUSE_AN && state->an_enabled) { + if (state->pause & MLO_PAUSE_AN && state->an_enabled) an |= MVPP2_GMAC_FLOW_CTRL_AUTONEG; - } else { - if (state->pause & MLO_PAUSE_TX) - ctrl4 |= MVPP22_CTRL4_TX_FC_EN; - if (state->pause & MLO_PAUSE_RX) - ctrl4 |= MVPP22_CTRL4_RX_FC_EN; - } } /* Some fields of the auto-negotiation register require the port to be down when @@ -5141,25 +5130,54 @@ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, mvpp2_port_enable(port); } -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void mvpp2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); u32 val; - if (!phylink_autoneg_inband(mode)) { - if (mvpp2_is_xlg(interface)) { + if (mvpp2_is_xlg(interface)) { + if (!phylink_autoneg_inband(mode)) { val = readl(port->base + MVPP22_XLG_CTRL0_REG); val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_DOWN; val |= MVPP22_XLG_CTRL0_FORCE_LINK_PASS; writel(val, port->base + MVPP22_XLG_CTRL0_REG); - } else { + } + } else { + if (!phylink_autoneg_inband(mode)) { val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val &= ~MVPP2_GMAC_FORCE_LINK_DOWN; + val &= ~(MVPP2_GMAC_FORCE_LINK_DOWN | + MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX); val |= MVPP2_GMAC_FORCE_LINK_PASS; + + if (speed == SPEED_1000 || speed == SPEED_2500) + val |= MVPP2_GMAC_CONFIG_GMII_SPEED; + else if (speed == SPEED_100) + val |= MVPP2_GMAC_CONFIG_MII_SPEED; + + if (duplex == DUPLEX_FULL) + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); } + + /* We can always update the flow control enable bits; + * these will only be effective if flow control AN + * (MVPP2_GMAC_FLOW_CTRL_AUTONEG) is disabled. + */ + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val &= ~(MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN); + if (tx_pause) + val |= MVPP22_CTRL4_TX_FC_EN; + if (rx_pause) + val |= MVPP22_CTRL4_RX_FC_EN; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); } mvpp2_port_enable(port); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8c6cfd15481c..8d28f90acfe7 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/ |