OSDN Git Service

Merge branch 'add-and-use-helper-for-pcs-negotiation-modes'
authorJakub Kicinski <kuba@kernel.org>
Fri, 23 Jun 2023 02:41:13 +0000 (19:41 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 23 Jun 2023 02:41:13 +0000 (19:41 -0700)
Russell King says:

====================
Add and use helper for PCS negotiation modes

Earlier this month, I proposed a helper for deciding whether a PCS
should use inband negotiation modes or not. There was some discussion
around this topic, and I believe there was no disagreement about
providing the helper.

The initial discussion can be found at:

https://lore.kernel.org/r/ZGIkGmyL8yL1q1zp@shell.armlinux.org.uk

Subsequently, I posted a RFC series back in May:

https://lore.kernel.org/r/ZGzhvePzPjJ0v2En@shell.armlinux.org.uk

that added a helper, phylink_pcs_neg_mode() which PCS drivers could use
to parse the state, and updated a bunch of drivers to use it. I got
a couple of bits of feedback to it, including some ACKs.

However, I've decided to take this slightly further and change the
"mode" parameter to both the pcs_config() and pcs_link_up() methods
when a PCS driver opts in to this (by setting "neg_mode" in the
phylink_pcs structure.) If this is not set, we default to the old
behaviour. That said, this series converts all the PCS implementations
I can find currently in net-next.

Doing this has the added benefit that the negotiation mode parameter
is also available to the pcs_link_up() function, which can now know
whether inband negotiation was in fact enabled or not at pcs_config()
time.

It has been posted as RFC at:

https://lore.kernel.org/r/ZIh/CLQ3z89g0Ua0@shell.armlinux.org.uk

and received one reply, thanks Elad, which is a similar amount of
interest to previous postings. Let's post it as non-RFC and see
whether we get more reaction.
====================

Link: https://lore.kernel.org/r/ZIxQIBfO9dH5xFlg@shell.armlinux.org.uk
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
20 files changed:
drivers/net/dsa/b53/b53_serdes.c
drivers/net/dsa/mt7530.c
drivers/net/dsa/qca/qca8k-8xxx.c
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/freescale/fman/fman_dtsec.c
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
drivers/net/ethernet/marvell/prestera/prestera_main.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.c
drivers/net/ethernet/microchip/lan966x/lan966x_phylink.c
drivers/net/ethernet/microchip/sparx5/sparx5_main.c
drivers/net/ethernet/microchip/sparx5/sparx5_phylink.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/pcs/pcs-lynx.c
drivers/net/pcs/pcs-mtk-lynxi.c
drivers/net/pcs/pcs-xpcs.c
drivers/net/phy/phylink.c
include/linux/pcs/pcs-xpcs.h
include/linux/phylink.h

index 0690210..b0ccebc 100644 (file)
@@ -65,7 +65,7 @@ static u16 b53_serdes_read(struct b53_device *dev, u8 lane,
        return b53_serdes_read_blk(dev, offset, block);
 }
 
-static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int mode,
+static int b53_serdes_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                             phy_interface_t interface,
                             const unsigned long *advertising,
                             bool permit_pause_to_mac)
@@ -239,6 +239,7 @@ int b53_serdes_init(struct b53_device *dev, int port)
        pcs->dev = dev;
        pcs->lane = lane;
        pcs->pcs.ops = &b53_pcs_ops;
+       pcs->pcs.neg_mode = true;
 
        return 0;
 }
index 7e773c4..38b3c6d 100644 (file)
@@ -3005,7 +3005,7 @@ static void mt7530_pcs_get_state(struct phylink_pcs *pcs,
                state->pause |= MLO_PAUSE_TX;
 }
 
-static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                             phy_interface_t interface,
                             const unsigned long *advertising,
                             bool permit_pause_to_mac)
@@ -3033,6 +3033,7 @@ mt753x_setup(struct dsa_switch *ds)
        /* Initialise the PCS devices */
        for (i = 0; i < priv->ds->num_ports; i++) {
                priv->pcs[i].pcs.ops = priv->info->pcs_ops;
+               priv->pcs[i].pcs.neg_mode = true;
                priv->pcs[i].priv = priv;
                priv->pcs[i].port = i;
        }
index dee7b65..f7d7cfb 100644 (file)
@@ -1493,7 +1493,7 @@ static void qca8k_pcs_get_state(struct phylink_pcs *pcs,
                state->pause |= MLO_PAUSE_TX;
 }
 
-static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                            phy_interface_t interface,
                            const unsigned long *advertising,
                            bool permit_pause_to_mac)
@@ -1520,14 +1520,12 @@ static int qca8k_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
        }
 
        /* Enable/disable SerDes auto-negotiation as necessary */
-       ret = qca8k_read(priv, QCA8K_REG_PWS, &val);
+       val = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED ?
+               0 : QCA8K_PWS_SERDES_AEN_DIS;
+
+       ret = qca8k_rmw(priv, QCA8K_REG_PWS, QCA8K_PWS_SERDES_AEN_DIS, val);
        if (ret)
                return ret;
-       if (phylink_autoneg_inband(mode))
-               val &= ~QCA8K_PWS_SERDES_AEN_DIS;
-       else
-               val |= QCA8K_PWS_SERDES_AEN_DIS;
-       qca8k_write(priv, QCA8K_REG_PWS, val);
 
        /* Configure the SGMII parameters */
        ret = qca8k_read(priv, QCA8K_REG_SGMII_CTRL, &val);
@@ -1598,6 +1596,7 @@ static void qca8k_setup_pcs(struct qca8k_priv *priv, struct qca8k_pcs *qpcs,
                            int port)
 {
        qpcs->pcs.ops = &qca8k_pcs_ops;
+       qpcs->pcs.neg_mode = true;
 
        /* We don't have interrupts for link changes, so we need to poll */
        qpcs->pcs.poll = true;
index b70dcf3..a55a643 100644 (file)
@@ -2314,7 +2314,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
 
        for (i = 0; i < ds->num_ports; i++) {
                struct dw_xpcs *xpcs = priv->xpcs[i];
-               unsigned int mode;
+               unsigned int neg_mode;
 
                rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]);
                if (rc < 0)
@@ -2324,17 +2324,15 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
                        continue;
 
                if (bmcr[i] & BMCR_ANENABLE)
-                       mode = MLO_AN_INBAND;
-               else if (priv->fixed_link[i])
-                       mode = MLO_AN_FIXED;
+                       neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
                else
-                       mode = MLO_AN_PHY;
+                       neg_mode = PHYLINK_PCS_NEG_OUTBAND;
 
-               rc = xpcs_do_config(xpcs, priv->phy_mode[i], mode, NULL);
+               rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode);
                if (rc < 0)
                        goto out;
 
-               if (!phylink_autoneg_inband(mode)) {
+               if (neg_mode == PHYLINK_PCS_NEG_OUTBAND) {
                        int speed = SPEED_UNKNOWN;
 
                        if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX)
@@ -2346,7 +2344,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
                        else
                                speed = SPEED_10;
 
-                       xpcs_link_up(&xpcs->pcs, mode, priv->phy_mode[i],
+                       xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i],
                                     speed, DUPLEX_FULL);
                }
        }
index 2e35e20..f6a0f12 100644 (file)
@@ -563,7 +563,7 @@ static void macb_set_tx_clk(struct macb *bp, int speed)
                netdev_err(bp->dev, "adjusting tx_clk failed.\n");
 }
 
-static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+static void macb_usx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                                 phy_interface_t interface, int speed,
                                 int duplex)
 {
@@ -596,7 +596,7 @@ static void macb_usx_pcs_get_state(struct phylink_pcs *pcs,
 }
 
 static int macb_usx_pcs_config(struct phylink_pcs *pcs,
-                              unsigned int mode,
+                              unsigned int neg_mode,
                               phy_interface_t interface,
                               const unsigned long *advertising,
                               bool permit_pause_to_mac)
@@ -621,7 +621,7 @@ static void macb_pcs_an_restart(struct phylink_pcs *pcs)
 }
 
 static int macb_pcs_config(struct phylink_pcs *pcs,
-                          unsigned int mode,
+                          unsigned int neg_mode,
                           phy_interface_t interface,
                           const unsigned long *advertising,
                           bool permit_pause_to_mac)
@@ -862,7 +862,9 @@ static int macb_mii_probe(struct net_device *dev)
        struct macb *bp = netdev_priv(dev);
 
        bp->phylink_sgmii_pcs.ops = &macb_phylink_pcs_ops;
+       bp->phylink_sgmii_pcs.neg_mode = true;
        bp->phylink_usx_pcs.ops = &macb_phylink_usx_pcs_ops;
+       bp->phylink_usx_pcs.neg_mode = true;
 
        bp->phylink_config.dev = &dev->dev;
        bp->phylink_config.type = PHYLINK_NETDEV;
index d528ca6..3088da7 100644 (file)
@@ -763,15 +763,15 @@ static void dtsec_pcs_get_state(struct phylink_pcs *pcs,
        phylink_mii_c22_pcs_get_state(dtsec->tbidev, state);
 }
 
-static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int dtsec_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                            phy_interface_t interface,
                            const unsigned long *advertising,
                            bool permit_pause_to_mac)
 {
        struct fman_mac *dtsec = pcs_to_dtsec(pcs);
 
-       return phylink_mii_c22_pcs_config(dtsec->tbidev, mode, interface,
-                                         advertising);
+       return phylink_mii_c22_pcs_config(dtsec->tbidev, interface,
+                                         advertising, neg_mode);
 }
 
 static void dtsec_pcs_an_restart(struct phylink_pcs *pcs)
@@ -1447,6 +1447,7 @@ int dtsec_initialization(struct mac_device *mac_dev,
                goto _return_fm_mac_free;
        }
        dtsec->pcs.ops = &dtsec_pcs_ops;
+       dtsec->pcs.neg_mode = true;
        dtsec->pcs.poll = true;
 
        supported = mac_dev->phylink_config.supported_interfaces;
index e2abc00..ff5647b 100644 (file)
@@ -4002,8 +4002,8 @@ static void mvneta_pcs_get_state(struct phylink_pcs *pcs,
                state->pause |= MLO_PAUSE_TX;
 }
 
-static int mvneta_pcs_config(struct phylink_pcs *pcs,
-                            unsigned int mode, phy_interface_t interface,
+static int mvneta_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+                            phy_interface_t interface,
                             const unsigned long *advertising,
                             bool permit_pause_to_mac)
 {
@@ -4016,7 +4016,7 @@ static int mvneta_pcs_config(struct phylink_pcs *pcs,
               MVNETA_GMAC_AN_FLOW_CTRL_EN |
               MVNETA_GMAC_AN_DUPLEX_EN;
 
-       if (phylink_autoneg_inband(mode)) {
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                mask |= MVNETA_GMAC_CONFIG_MII_SPEED |
                        MVNETA_GMAC_CONFIG_GMII_SPEED |
                        MVNETA_GMAC_CONFIG_FULL_DUPLEX;
@@ -5518,6 +5518,7 @@ static int mvneta_probe(struct platform_device *pdev)
                clk_prepare_enable(pp->clk_bus);
 
        pp->phylink_pcs.ops = &mvneta_phylink_pcs_ops;
+       pp->phylink_pcs.neg_mode = true;
 
        pp->phylink_config.dev = &dev->dev;
        pp->phylink_config.type = PHYLINK_NETDEV;
index adc9536..1fec84b 100644 (file)
@@ -6168,8 +6168,7 @@ static void mvpp2_xlg_pcs_get_state(struct phylink_pcs *pcs,
                state->pause |= MLO_PAUSE_RX;
 }
 
-static int mvpp2_xlg_pcs_config(struct phylink_pcs *pcs,
-                               unsigned int mode,
+static int mvpp2_xlg_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                                phy_interface_t interface,
                                const unsigned long *advertising,
                                bool permit_pause_to_mac)
@@ -6232,7 +6231,7 @@ static void mvpp2_gmac_pcs_get_state(struct phylink_pcs *pcs,
                state->pause |= MLO_PAUSE_TX;
 }
 
-static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                                 phy_interface_t interface,
                                 const unsigned long *advertising,
                                 bool permit_pause_to_mac)
@@ -6246,7 +6245,7 @@ static int mvpp2_gmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
               MVPP2_GMAC_FLOW_CTRL_AUTONEG |
               MVPP2_GMAC_AN_DUPLEX_EN;
 
-       if (phylink_autoneg_inband(mode)) {
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                mask |= MVPP2_GMAC_CONFIG_MII_SPEED |
                        MVPP2_GMAC_CONFIG_GMII_SPEED |
                        MVPP2_GMAC_CONFIG_FULL_DUPLEX;
@@ -6649,8 +6648,9 @@ static void mvpp2_acpi_start(struct mvpp2_port *port)
        mvpp2_mac_prepare(&port->phylink_config, MLO_AN_INBAND,
                          port->phy_interface);
        mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state);
-       pcs->ops->pcs_config(pcs, MLO_AN_INBAND, port->phy_interface,
-                            state.advertising, false);
+       pcs->ops->pcs_config(pcs, PHYLINK_PCS_NEG_INBAND_ENABLED,
+                            port->phy_interface, state.advertising,
+                            false);
        mvpp2_mac_finish(&port->phylink_config, MLO_AN_INBAND,
                         port->phy_interface);
        mvpp2_mac_link_up(&port->phylink_config, NULL,
@@ -6896,7 +6896,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        dev->dev.of_node = port_node;
 
        port->pcs_gmac.ops = &mvpp2_phylink_gmac_pcs_ops;
+       port->pcs_gmac.neg_mode = true;
        port->pcs_xlg.ops = &mvpp2_phylink_xlg_pcs_ops;
+       port->pcs_xlg.neg_mode = true;
 
        if (!mvpp2_use_acpi_compat_mode(port_fwnode)) {
                port->phylink_config.dev = &dev->dev;
index 9d50414..4fb886c 100644 (file)
@@ -300,8 +300,7 @@ static void prestera_pcs_get_state(struct phylink_pcs *pcs,
        }
 }
 
-static int prestera_pcs_config(struct phylink_pcs *pcs,
-                              unsigned int mode,
+static int prestera_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                               phy_interface_t interface,
                               const unsigned long *advertising,
                               bool permit_pause_to_mac)
@@ -316,30 +315,25 @@ static int prestera_pcs_config(struct phylink_pcs *pcs,
 
        cfg_mac.admin = true;
        cfg_mac.fec = PRESTERA_PORT_FEC_OFF;
+       cfg_mac.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
 
        switch (interface) {
        case PHY_INTERFACE_MODE_10GBASER:
                cfg_mac.speed = SPEED_10000;
-               cfg_mac.inband = 0;
                cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR;
                break;
        case PHY_INTERFACE_MODE_2500BASEX:
                cfg_mac.speed = SPEED_2500;
                cfg_mac.duplex = DUPLEX_FULL;
-               cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-                                         advertising);
                cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
                break;
        case PHY_INTERFACE_MODE_SGMII:
-               cfg_mac.inband = 1;
                cfg_mac.mode = PRESTERA_MAC_MODE_SGMII;
                break;
        case PHY_INTERFACE_MODE_1000BASEX:
        default:
                cfg_mac.speed = SPEED_1000;
                cfg_mac.duplex = DUPLEX_FULL;
-               cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-                                         advertising);
                cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X;
                break;
        }
@@ -401,6 +395,7 @@ static int prestera_port_sfp_bind(struct prestera_port *port)
                        continue;
 
                port->phylink_pcs.ops = &prestera_pcs_ops;
+               port->phylink_pcs.neg_mode = true;
 
                port->phy_config.dev = &port->dev->dev;
                port->phy_config.type = PHYLINK_NETDEV;
index f6931df..fbb0bb4 100644 (file)
@@ -818,6 +818,7 @@ static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
        port->phylink_config.type = PHYLINK_NETDEV;
        port->phylink_pcs.poll = true;
        port->phylink_pcs.ops = &lan966x_phylink_pcs_ops;
+       port->phylink_pcs.neg_mode = true;
 
        port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
                MAC_10 | MAC_100 | MAC_1000FD | MAC_2500FD;
index c5f9803..1d63903 100644 (file)
@@ -95,8 +95,7 @@ static void lan966x_pcs_get_state(struct phylink_pcs *pcs,
        lan966x_port_status_get(port, state);
 }
 
-static int lan966x_pcs_config(struct phylink_pcs *pcs,
-                             unsigned int mode,
+static int lan966x_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                              phy_interface_t interface,
                              const unsigned long *advertising,
                              bool permit_pause_to_mac)
@@ -107,8 +106,8 @@ static int lan966x_pcs_config(struct phylink_pcs *pcs,
 
        config = port->config;
        config.portmode = interface;
-       config.inband = phylink_autoneg_inband(mode);
-       config.autoneg = phylink_test(advertising, Autoneg);
+       config.inband = neg_mode & PHYLINK_PCS_NEG_INBAND;
+       config.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
        config.advertising = advertising;
 
        ret = lan966x_port_pcs_set(port, &config);
index a7edf52..dc9af48 100644 (file)
@@ -281,6 +281,7 @@ static int sparx5_create_port(struct sparx5 *sparx5,
        spx5_port->custom_etype = 0x8880; /* Vitesse */
        spx5_port->phylink_pcs.poll = true;
        spx5_port->phylink_pcs.ops = &sparx5_phylink_pcs_ops;
+       spx5_port->phylink_pcs.neg_mode = true;
        spx5_port->is_mrouter = false;
        INIT_LIST_HEAD(&spx5_port->tc_templates);
        sparx5->ports[config->portno] = spx5_port;
index bb97d27..f8562c1 100644 (file)
@@ -91,8 +91,7 @@ static void sparx5_pcs_get_state(struct phylink_pcs *pcs,
        state->pause = status.pause;
 }
 
-static int sparx5_pcs_config(struct phylink_pcs *pcs,
-                            unsigned int mode,
+static int sparx5_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                             phy_interface_t interface,
                             const unsigned long *advertising,
                             bool permit_pause_to_mac)
@@ -104,8 +103,9 @@ static int sparx5_pcs_config(struct phylink_pcs *pcs,
        conf = port->conf;
        conf.power_down = false;
        conf.portmode = interface;
-       conf.inband = phylink_autoneg_inband(mode);
-       conf.autoneg = phylink_test(advertising, Autoneg);
+       conf.inband = neg_mode == PHYLINK_PCS_NEG_INBAND_DISABLED ||
+                     neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
+       conf.autoneg = neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED;
        conf.pause_adv = 0;
        if (phylink_test(advertising, Pause))
                conf.pause_adv |= ADVERTISE_1000XPAUSE;
index 3e310b5..ae7b9af 100644 (file)
@@ -1631,7 +1631,7 @@ static void axienet_pcs_an_restart(struct phylink_pcs *pcs)
        phylink_mii_c22_pcs_an_restart(pcs_phy);
 }
 
-static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                              phy_interface_t interface,
                              const unsigned long *advertising,
                              bool permit_pause_to_mac)
@@ -1653,7 +1653,8 @@ static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
                }
        }
 
-       ret = phylink_mii_c22_pcs_config(pcs_phy, mode, interface, advertising);
+       ret = phylink_mii_c22_pcs_config(pcs_phy, interface, advertising,
+                                        neg_mode);
        if (ret < 0)
                netdev_warn(ndev, "Failed to configure PCS: %d\n", ret);
 
@@ -2129,6 +2130,7 @@ static int axienet_probe(struct platform_device *pdev)
                }
                of_node_put(np);
                lp->pcs.ops = &axienet_pcs_ops;
+               lp->pcs.neg_mode = true;
                lp->pcs.poll = true;
        }
 
index fca48eb..9021b96 100644 (file)
@@ -112,9 +112,10 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs,
                state->link, state->an_complete);
 }
 
-static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
+static int lynx_pcs_config_giga(struct mdio_device *pcs,
                                phy_interface_t interface,
-                               const unsigned long *advertising)
+                               const unsigned long *advertising,
+                               unsigned int neg_mode)
 {
        int link_timer_ns;
        u32 link_timer;
@@ -132,8 +133,9 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
        if (interface == PHY_INTERFACE_MODE_1000BASEX) {
                if_mode = 0;
        } else {
+               /* SGMII and QSGMII */
                if_mode = IF_MODE_SGMII_EN;
-               if (mode == MLO_AN_INBAND)
+               if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                        if_mode |= IF_MODE_USE_SGMII_AN;
        }
 
@@ -143,16 +145,18 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
        if (err)
                return err;
 
-       return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising);
+       return phylink_mii_c22_pcs_config(pcs, interface, advertising,
+                                         neg_mode);
 }
 
-static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
-                                  const unsigned long *advertising)
+static int lynx_pcs_config_usxgmii(struct mdio_device *pcs,
+                                  const unsigned long *advertising,
+                                  unsigned int neg_mode)
 {
        struct mii_bus *bus = pcs->bus;
        int addr = pcs->addr;
 
-       if (!phylink_autoneg_inband(mode)) {
+       if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
                dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n");
                return -EOPNOTSUPP;
        }
@@ -164,10 +168,9 @@ static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
                                 ADVERTISE_SGMII | ADVERTISE_LPACK);
 }
 
-static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                           phy_interface_t ifmode,
-                          const unsigned long *advertising,
-                          bool permit)
+                          const unsigned long *advertising, bool permit)
 {
        struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
 
@@ -175,17 +178,18 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
        case PHY_INTERFACE_MODE_1000BASEX:
        case PHY_INTERFACE_MODE_SGMII:
        case PHY_INTERFACE_MODE_QSGMII:
-               return lynx_pcs_config_giga(lynx->mdio, mode, ifmode,
-                                           advertising);
+               return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
+                                           neg_mode);
        case PHY_INTERFACE_MODE_2500BASEX:
-               if (phylink_autoneg_inband(mode)) {
+               if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                        dev_err(&lynx->mdio->dev,
                                "AN not supported on 3.125GHz SerDes lane\n");
                        return -EOPNOTSUPP;
                }
                break;
        case PHY_INTERFACE_MODE_USXGMII:
-               return lynx_pcs_config_usxgmii(lynx->mdio, mode, advertising);
+               return lynx_pcs_config_usxgmii(lynx->mdio, advertising,
+                                              neg_mode);
        case PHY_INTERFACE_MODE_10GBASER:
                /* Nothing to do here for 10GBASER */
                break;
@@ -203,7 +207,8 @@ static void lynx_pcs_an_restart(struct phylink_pcs *pcs)
        phylink_mii_c22_pcs_an_restart(lynx->mdio);
 }
 
-static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
+static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs,
+                                  unsigned int neg_mode,
                                   int speed, int duplex)
 {
        u16 if_mode = 0, sgmii_speed;
@@ -211,7 +216,7 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
        /* The PCS needs to be configured manually only
         * when not operating on in-band mode
         */
-       if (mode == MLO_AN_INBAND)
+       if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED)
                return;
 
        if (duplex == DUPLEX_HALF)
@@ -258,12 +263,12 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
  * 2500 Mbps and we do rate adaptation through pause frames.
  */
 static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
-                                      unsigned int mode,
+                                      unsigned int neg_mode,
                                       int speed, int duplex)
 {
        u16 if_mode = 0;
 
-       if (mode == MLO_AN_INBAND) {
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                dev_err(&pcs->dev, "AN not supported for 2500BaseX\n");
                return;
        }
@@ -277,7 +282,7 @@ static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
                       if_mode);
 }
 
-static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                             phy_interface_t interface,
                             int speed, int duplex)
 {
@@ -286,10 +291,10 @@ static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
        switch (interface) {
        case PHY_INTERFACE_MODE_SGMII:
        case PHY_INTERFACE_MODE_QSGMII:
-               lynx_pcs_link_up_sgmii(lynx->mdio, mode, speed, duplex);
+               lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex);
                break;
        case PHY_INTERFACE_MODE_2500BASEX:
-               lynx_pcs_link_up_2500basex(lynx->mdio, mode, speed, duplex);
+               lynx_pcs_link_up_2500basex(lynx->mdio, neg_mode, speed, duplex);
                break;
        case PHY_INTERFACE_MODE_USXGMII:
                /* At the moment, only in-band AN is supported for USXGMII
@@ -319,6 +324,7 @@ static struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
        mdio_device_get(mdio);
        lynx->mdio = mdio;
        lynx->pcs.ops = &lynx_pcs_phylink_ops;
+       lynx->pcs.neg_mode = true;
        lynx->pcs.poll = true;
 
        return lynx_to_phylink_pcs(lynx);
index 8884523..b0f3ede 100644 (file)
@@ -102,13 +102,13 @@ static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
                                         FIELD_GET(SGMII_LPA, adv));
 }
 
-static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
+static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                                phy_interface_t interface,
                                const unsigned long *advertising,
                                bool permit_pause_to_mac)
 {
        struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
-       bool mode_changed = false, changed, use_an;
+       bool mode_changed = false, changed;
        unsigned int rgc3, sgm_mode, bmcr;
        int advertise, link_timer;
 
@@ -121,30 +121,21 @@ static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
         * we assume that fixes it's speed at bitrate = line rate (in
         * other words, 1000Mbps or 2500Mbps).
         */
-       if (interface == PHY_INTERFACE_MODE_SGMII) {
+       if (interface == PHY_INTERFACE_MODE_SGMII)
                sgm_mode = SGMII_IF_MODE_SGMII;
-               if (phylink_autoneg_inband(mode)) {
-                       sgm_mode |= SGMII_REMOTE_FAULT_DIS |
-                                   SGMII_SPEED_DUPLEX_AN;
-                       use_an = true;
-               } else {
-                       use_an = false;
-               }
-       } else if (phylink_autoneg_inband(mode)) {
-               /* 1000base-X or 2500base-X autoneg */
-               sgm_mode = SGMII_REMOTE_FAULT_DIS;
-               use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
-                                          advertising);
-       } else {
-               /* 1000base-X or 2500base-X without autoneg */
+       else
                sgm_mode = 0;
-               use_an = false;
-       }
 
-       if (use_an)
+       if (neg_mode & PHYLINK_PCS_NEG_INBAND)
+               sgm_mode |= SGMII_REMOTE_FAULT_DIS;
+
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
+               if (interface == PHY_INTERFACE_MODE_SGMII)
+                       sgm_mode |= SGMII_SPEED_DUPLEX_AN;
                bmcr = BMCR_ANENABLE;
-       else
+       } else {
                bmcr = 0;
+       }
 
        if (mpcs->interface != interface) {
                link_timer = phylink_get_link_timer_ns(interface);
@@ -216,14 +207,15 @@ static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
        regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
 }
 
-static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
+static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs,
+                                 unsigned int neg_mode,
                                  phy_interface_t interface, int speed,
                                  int duplex)
 {
        struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
        unsigned int sgm_mode;
 
-       if (!phylink_autoneg_inband(mode)) {
+       if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) {
                /* Force the speed and duplex setting */
                if (speed == SPEED_10)
                        sgm_mode = SGMII_SPEED_10;
@@ -286,6 +278,7 @@ struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
        mpcs->regmap = regmap;
        mpcs->flags = flags;
        mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
+       mpcs->pcs.neg_mode = true;
        mpcs->pcs.poll = true;
        mpcs->interface = PHY_INTERFACE_MODE_NA;
 
index e4e59aa..44b0376 100644 (file)
@@ -657,7 +657,8 @@ int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns, int enable)
 }
 EXPORT_SYMBOL_GPL(xpcs_config_eee);
 
-static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
+static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs,
+                                     unsigned int neg_mode)
 {
        int ret, mdio_ctrl;
 
@@ -707,7 +708,7 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
        if (ret < 0)
                return ret;
 
-       if (phylink_autoneg_inband(mode))
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                ret |= DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
        else
                ret &= ~DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW;
@@ -716,14 +717,15 @@ static int xpcs_config_aneg_c37_sgmii(struct dw_xpcs *xpcs, unsigned int mode)
        if (ret < 0)
                return ret;
 
-       if (phylink_autoneg_inband(mode))
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
                                 mdio_ctrl | AN_CL37_EN);
 
        return ret;
 }
 
-static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
+static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs,
+                                         unsigned int neg_mode,
                                          const unsigned long *advertising)
 {
        phy_interface_t interface = PHY_INTERFACE_MODE_1000BASEX;
@@ -774,8 +776,7 @@ static int xpcs_config_aneg_c37_1000basex(struct dw_xpcs *xpcs, unsigned int mod
        if (ret < 0)
                return ret;
 
-       if (phylink_autoneg_inband(mode) &&
-           linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) {
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                ret = xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_MMD_CTRL,
                                 mdio_ctrl | AN_CL37_EN);
                if (ret < 0)
@@ -808,7 +809,7 @@ static int xpcs_config_2500basex(struct dw_xpcs *xpcs)
 }
 
 int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
-                  unsigned int mode, const unsigned long *advertising)
+                  const unsigned long *advertising, unsigned int neg_mode)
 {
        const struct xpcs_compat *compat;
        int ret;
@@ -821,19 +822,19 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
        case DW_10GBASER:
                break;
        case DW_AN_C73:
-               if (test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)) {
+               if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
                        ret = xpcs_config_aneg_c73(xpcs, compat);
                        if (ret)
                                return ret;
                }
                break;
        case DW_AN_C37_SGMII:
-               ret = xpcs_config_aneg_c37_sgmii(xpcs, mode);
+               ret = xpcs_config_aneg_c37_sgmii(xpcs, neg_mode);
                if (ret)
                        return ret;
                break;
        case DW_AN_C37_1000BASEX:
-               ret = xpcs_config_aneg_c37_1000basex(xpcs, mode,
+               ret = xpcs_config_aneg_c37_1000basex(xpcs, neg_mode,
                                                     advertising);
                if (ret)
                        return ret;
@@ -857,14 +858,14 @@ int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
 }
 EXPORT_SYMBOL_GPL(xpcs_do_config);
 
-static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int xpcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                       phy_interface_t interface,
                       const unsigned long *advertising,
                       bool permit_pause_to_mac)
 {
        struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
 
-       return xpcs_do_config(xpcs, interface, mode, advertising);
+       return xpcs_do_config(xpcs, interface, advertising, neg_mode);
 }
 
 static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
@@ -898,7 +899,8 @@ static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
 
                state->link = 0;
 
-               return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND, NULL);
+               return xpcs_do_config(xpcs, state->interface, NULL,
+                                     PHYLINK_PCS_NEG_INBAND_ENABLED);
        }
 
        /* There is no point doing anything else if the link is down. */
@@ -1046,12 +1048,12 @@ static void xpcs_get_state(struct phylink_pcs *pcs,
        }
 }
 
-static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
+static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int neg_mode,
                               int speed, int duplex)
 {
        int val, ret;
 
-       if (phylink_autoneg_inband(mode))
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                return;
 
        val = mii_bmcr_encode_fixed(speed, duplex);
@@ -1060,12 +1062,12 @@ static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
                pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
 }
 
-static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
+static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int neg_mode,
                                   int speed, int duplex)
 {
        int val, ret;
 
-       if (phylink_autoneg_inband(mode))
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                return;
 
        switch (speed) {
@@ -1089,7 +1091,7 @@ static void xpcs_link_up_1000basex(struct dw_xpcs *xpcs, unsigned int mode,
                pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
 }
 
-void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                  phy_interface_t interface, int speed, int duplex)
 {
        struct dw_xpcs *xpcs = phylink_pcs_to_xpcs(pcs);
@@ -1097,9 +1099,9 @@ void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
        if (interface == PHY_INTERFACE_MODE_USXGMII)
                return xpcs_config_usxgmii(xpcs, speed);
        if (interface == PHY_INTERFACE_MODE_SGMII)
-               return xpcs_link_up_sgmii(xpcs, mode, speed, duplex);
+               return xpcs_link_up_sgmii(xpcs, neg_mode, speed, duplex);
        if (interface == PHY_INTERFACE_MODE_1000BASEX)
-               return xpcs_link_up_1000basex(xpcs, mode, speed, duplex);
+               return xpcs_link_up_1000basex(xpcs, neg_mode, speed, duplex);
 }
 EXPORT_SYMBOL_GPL(xpcs_link_up);
 
@@ -1283,6 +1285,7 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev,
                }
 
                xpcs->pcs.ops = &xpcs_phylink_ops;
+               xpcs->pcs.neg_mode = true;
                if (compat->an_mode == DW_10GBASER)
                        return xpcs;
 
index 97c15e1..d0aaa5c 100644 (file)
@@ -71,6 +71,7 @@ struct phylink {
        struct mutex state_mutex;
        struct phylink_link_state phy_state;
        struct work_struct resolve;
+       unsigned int pcs_neg_mode;
 
        bool mac_link_dropped;
        bool using_mac_select_pcs;
@@ -992,23 +993,23 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
        }
 }
 
-static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
                              const struct phylink_link_state *state,
                              bool permit_pause_to_mac)
 {
        if (!pcs)
                return 0;
 
-       return pcs->ops->pcs_config(pcs, mode, state->interface,
+       return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
                                    state->advertising, permit_pause_to_mac);
 }
 
-static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                                phy_interface_t interface, int speed,
                                int duplex)
 {
        if (pcs && pcs->ops->pcs_link_up)
-               pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
+               pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
 }
 
 static void phylink_pcs_poll_stop(struct phylink *pl)
@@ -1058,10 +1059,15 @@ static void phylink_major_config(struct phylink *pl, bool restart,
        struct phylink_pcs *pcs = NULL;
        bool pcs_changed = false;
        unsigned int rate_kbd;
+       unsigned int neg_mode;
        int err;
 
        phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
 
+       pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
+                                               state->interface,
+                                               state->advertising);
+
        if (pl->using_mac_select_pcs) {
                pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
                if (IS_ERR(pcs)) {
@@ -1094,9 +1100,12 @@ static void phylink_major_config(struct phylink *pl, bool restart,
 
        phylink_mac_config(pl, state);
 
-       err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
-                                !!(pl->link_config.pause &
-                                   MLO_PAUSE_AN));
+       neg_mode = pl->cur_link_an_mode;
+       if (pl->pcs && pl->pcs->neg_mode)
+               neg_mode = pl->pcs_neg_mode;
+
+       err = phylink_pcs_config(pl->pcs, neg_mode, state,
+                                !!(pl->link_config.pause & MLO_PAUSE_AN));
        if (err < 0)
                phylink_err(pl, "pcs_config failed: %pe\n",
                            ERR_PTR(err));
@@ -1131,6 +1140,7 @@ static void phylink_major_config(struct phylink *pl, bool restart,
  */
 static int phylink_change_inband_advert(struct phylink *pl)
 {
+       unsigned int neg_mode;
        int ret;
 
        if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
@@ -1149,12 +1159,20 @@ static int phylink_change_inband_advert(struct phylink *pl)
                    __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
                    pl->link_config.pause);
 
+       /* Recompute the PCS neg mode */
+       pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
+                                       pl->link_config.interface,
+                                       pl->link_config.advertising);
+
+       neg_mode = pl->cur_link_an_mode;
+       if (pl->pcs->neg_mode)
+               neg_mode = pl->pcs_neg_mode;
+
        /* Modern PCS-based method; update the advert at the PCS, and
         * restart negotiation if the pcs_config() helper indicates that
         * the programmed advertisement has changed.
         */
-       ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
-                                &pl->link_config,
+       ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
                                 !!(pl->link_config.pause & MLO_PAUSE_AN));
        if (ret < 0)
                return ret;
@@ -1257,6 +1275,7 @@ static void phylink_link_up(struct phylink *pl,
                            struct phylink_link_state link_state)
 {
        struct net_device *ndev = pl->netdev;
+       unsigned int neg_mode;
        int speed, duplex;
        bool rx_pause;
 
@@ -1287,8 +1306,12 @@ static void phylink_link_up(struct phylink *pl,
 
        pl->cur_interface = link_state.interface;
 
-       phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
-                           speed, duplex);
+       neg_mode = pl->cur_link_an_mode;
+       if (pl->pcs && pl->pcs->neg_mode)
+               neg_mode = pl->pcs_neg_mode;
+
+       phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
+                           duplex);
 
        pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
                                 pl->cur_interface, speed, duplex,
@@ -3522,18 +3545,19 @@ EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement);
 /**
  * phylink_mii_c22_pcs_config() - configure clause 22 PCS
  * @pcs: a pointer to a &struct mdio_device.
- * @mode: link autonegotiation mode
  * @interface: the PHY interface mode being configured
  * @advertising: the ethtool advertisement mask
+ * @neg_mode: PCS negotiation mode
  *
  * Configure a Clause 22 PCS PHY with the appropriate negotiation
  * parameters for the @mode, @interface and @advertising parameters.
  * Returns negative error number on failure, zero if the advertisement
  * has not changed, or positive if there is a change.
  */
-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
                               phy_interface_t interface,
-                              const unsigned long *advertising)
+                              const unsigned long *advertising,
+                              unsigned int neg_mode)
 {
        bool changed = 0;
        u16 bmcr;
@@ -3548,15 +3572,12 @@ int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
                changed = ret;
        }
 
-       /* Ensure ISOLATE bit is disabled */
-       if (mode == MLO_AN_INBAND &&
-           (interface == PHY_INTERFACE_MODE_SGMII ||
-            interface == PHY_INTERFACE_MODE_QSGMII ||
-            linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, advertising)))
+       if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
                bmcr = BMCR_ANENABLE;
        else
                bmcr = 0;
 
+       /* Configure the inband state. Ensure ISOLATE bit is disabled */
        ret = mdiodev_modify(pcs, MII_BMCR, BMCR_ANENABLE | BMCR_ISOLATE, bmcr);
        if (ret < 0)
                return ret;
index ec8175b..ff99cf7 100644 (file)
@@ -29,10 +29,10 @@ struct dw_xpcs {
 };
 
 int xpcs_get_an_mode(struct dw_xpcs *xpcs, phy_interface_t interface);
-void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+void xpcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                  phy_interface_t interface, int speed, int duplex);
 int xpcs_do_config(struct dw_xpcs *xpcs, phy_interface_t interface,
-                  unsigned int mode, const unsigned long *advertising);
+                  const unsigned long *advertising, unsigned int neg_mode);
 void xpcs_get_interfaces(struct dw_xpcs *xpcs, unsigned long *interfaces);
 int xpcs_config_eee(struct dw_xpcs *xpcs, int mult_fact_100ns,
                    int enable);
index 0cf07d7..516240f 100644 (file)
@@ -21,6 +21,24 @@ enum {
        MLO_AN_FIXED,   /* Fixed-link mode */
        MLO_AN_INBAND,  /* In-band protocol */
 
+       /* PCS "negotiation" mode.
+        *  PHYLINK_PCS_NEG_NONE - protocol has no inband capability
+        *  PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
+        *  PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
+        *                                    1000base-X with autoneg off
+        *  PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
+        * Additionally, this can be tested using bitmasks:
+        *  PHYLINK_PCS_NEG_INBAND - inband mode selected
+        *  PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
+        */
+       PHYLINK_PCS_NEG_NONE = 0,
+       PHYLINK_PCS_NEG_ENABLED = BIT(4),
+       PHYLINK_PCS_NEG_OUTBAND = BIT(5),
+       PHYLINK_PCS_NEG_INBAND = BIT(6),
+       PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
+       PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
+                                        PHYLINK_PCS_NEG_ENABLED,
+
        /* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
         * autonegotiation advertisement. They correspond to the PAUSE and
         * ASM_DIR bits defined by 802.3, respectively.
@@ -80,6 +98,70 @@ static inline bool phylink_autoneg_inband(unsigned int mode)
 }
 
 /**
+ * phylink_pcs_neg_mode() - helper to determine PCS inband mode
+ * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
+ * @interface: interface mode to be used
+ * @advertising: adertisement ethtool link mode mask
+ *
+ * Determines the negotiation mode to be used by the PCS, and returns
+ * one of:
+ * %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
+ * %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
+ *   will be used.
+ * %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
+ * %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
+ *
+ * Note: this is for cases where the PCS itself is involved in negotiation
+ * (e.g. Clause 37, SGMII and similar) not Clause 73.
+ */
+static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
+                                               phy_interface_t interface,
+                                               const unsigned long *advertising)
+{
+       unsigned int neg_mode;
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_QSGMII:
+       case PHY_INTERFACE_MODE_QUSGMII:
+       case PHY_INTERFACE_MODE_USXGMII:
+               /* These protocols are designed for use with a PHY which
+                * communicates its negotiation result back to the MAC via
+                * inband communication. Note: there exist PHYs that run
+                * with SGMII but do not send the inband data.
+                */
+               if (!phylink_autoneg_inband(mode))
+                       neg_mode = PHYLINK_PCS_NEG_OUTBAND;
+               else
+                       neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+               break;
+
+       case PHY_INTERFACE_MODE_1000BASEX:
+       case PHY_INTERFACE_MODE_2500BASEX:
+               /* 1000base-X is designed for use media-side for Fibre
+                * connections, and thus the Autoneg bit needs to be
+                * taken into account. We also do this for 2500base-X
+                * as well, but drivers may not support this, so may
+                * need to override this.
+                */
+               if (!phylink_autoneg_inband(mode))
+                       neg_mode = PHYLINK_PCS_NEG_OUTBAND;
+               else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                          advertising))
+                       neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
+               else
+                       neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
+               break;
+
+       default:
+               neg_mode = PHYLINK_PCS_NEG_NONE;
+               break;
+       }
+
+       return neg_mode;
+}
+
+/**
  * struct phylink_link_state - link state structure
  * @advertising: ethtool bitmask containing advertised link modes
  * @lp_advertising: ethtool bitmask containing link partner advertised link
@@ -436,6 +518,7 @@ struct phylink_pcs_ops;
 /**
  * struct phylink_pcs - PHYLINK PCS instance
  * @ops: a pointer to the &struct phylink_pcs_ops structure
+ * @neg_mode: provide PCS neg mode via "mode" argument
  * @poll: poll the PCS for link changes
  *
  * This structure is designed to be embedded within the PCS private data,
@@ -443,6 +526,7 @@ struct phylink_pcs_ops;
  */
 struct phylink_pcs {
        const struct phylink_pcs_ops *ops;
+       bool neg_mode;
        bool poll;
 };
 
@@ -460,12 +544,12 @@ struct phylink_pcs_ops {
                            const struct phylink_link_state *state);
        void (*pcs_get_state)(struct phylink_pcs *pcs,
                              struct phylink_link_state *state);
-       int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
+       int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
                          phy_interface_t interface,
                          const unsigned long *advertising,
                          bool permit_pause_to_mac);
        void (*pcs_an_restart)(struct phylink_pcs *pcs);
-       void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
+       void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
                            phy_interface_t interface, int speed, int duplex);
 };
 
@@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *pcs,
 /**
  * pcs_config() - Configure the PCS mode and advertisement
  * @pcs: a pointer to a &struct phylink_pcs.
- * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
+ * @neg_mode: link negotiation mode (see below)
  * @interface: interface mode to be used
  * @advertising: adertisement ethtool link mode mask
  * @permit_pause_to_mac: permit forwarding pause resolution to MAC
@@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *pcs,
  * For 1000BASE-X, the advertisement should be programmed into the PCS.
  *
  * For most 10GBASE-R, there is no advertisement.
+ *
+ * The %neg_mode argument should be tested via the phylink_mode_*() family of
+ * functions, or for PCS that set pcs->neg_mode true, should be tested
+ * against the %PHYLINK_PCS_NEG_* definitions.
  */
-int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
+int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
               phy_interface_t interface, const unsigned long *advertising,
               bool permit_pause_to_mac);
 
@@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *pcs);
 /**
  * pcs_link_up() - program the PCS for the resolved link configuration
  * @pcs: a pointer to a &struct phylink_pcs.
- * @mode: link autonegotiation mode
+ * @neg_mode: link negotiation mode (see below)
  * @interface: link &typedef phy_interface_t mode
  * @speed: link speed
  * @duplex: link duplex
@@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *pcs);
  * the resolved link parameters. For example, a PCS operating in SGMII
  * mode without in-band AN needs to be manually configured for the link
  * and duplex setting. Otherwise, this should be a no-op.
+ *
+ * The %mode argument should be tested via the phylink_mode_*() family of
+ * functions, or for PCS that set pcs->neg_mode true, should be tested
+ * against the %PHYLINK_PCS_NEG_* definitions.
  */
-void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
+void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
                 phy_interface_t interface, int speed, int duplex);
 #endif
 
@@ -651,9 +743,10 @@ void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
                                   struct phylink_link_state *state);
 int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface,
                                             const unsigned long *advertising);
-int phylink_mii_c22_pcs_config(struct mdio_device *pcs, unsigned int mode,
+int phylink_mii_c22_pcs_config(struct mdio_device *pcs,
                               phy_interface_t interface,
-                              const unsigned long *advertising);
+                              const unsigned long *advertising,
+                              unsigned int neg_mode);
 void phylink_mii_c22_pcs_an_restart(struct mdio_device *pcs);
 
 void phylink_resolve_c73(struct phylink_link_state *state);