nfp: add support for link auto negotiation
authorYinjun Zhang <yinjun.zhang@corigine.com>
Thu, 29 Sep 2022 08:58:31 +0000 (10:58 +0200)
committerJakub Kicinski <kuba@kernel.org>
Sat, 1 Oct 2022 01:47:53 +0000 (18:47 -0700)
Report the auto negotiation capability if it's supported
in management firmware, and advertise it if it's enabled.
Changing port speed is not allowed when autoneg is enabled.

The ethtool <intf> command displays the auto-neg capability:

  # ethtool enp1s0np0
  Settings for enp1s0np0:
          Supported ports: [ FIBRE ]
          Supported link modes:   Not reported
          Supported pause frame use: Symmetric
          Supports auto-negotiation: Yes
          Supported FEC modes: None        RS      BASER
          Advertised link modes:  Not reported
          Advertised pause frame use: Symmetric
          Advertised auto-negotiation: Yes
          Advertised FEC modes: None       RS      BASER
          Speed: 25000Mb/s
          Duplex: Full
          Auto-negotiation: on
          Port: FIBRE
          PHYAD: 0
          Transceiver: internal
          Link detected: yes

Signed-off-by: Yinjun Zhang <yinjun.zhang@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/netronome/nfp/nfp_main.c
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h
drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c

index 91063f1..e66e548 100644 (file)
@@ -729,8 +729,15 @@ static int nfp_pf_cfg_hwinfo(struct nfp_pf *pf, bool sp_indiff)
        snprintf(hwinfo, sizeof(hwinfo), "sp_indiff=%d", sp_indiff);
        err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
        /* Not a fatal error, no need to return error to stop driver from loading */
-       if (err)
+       if (err) {
                nfp_warn(pf->cpp, "HWinfo(sp_indiff=%d) set failed: %d\n", sp_indiff, err);
+       } else {
+               /* Need reinit eth_tbl since the eth table state may change
+                * after sp_indiff is configured.
+                */
+               kfree(pf->eth_tbl);
+               pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
+       }
 
        nfp_nsp_close(nsp);
        return 0;
index d50af23..678cea0 100644 (file)
@@ -290,8 +290,13 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
        if (eth_port) {
                ethtool_link_ksettings_add_link_mode(cmd, supported, Pause);
                ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause);
-               cmd->base.autoneg = eth_port->aneg != NFP_ANEG_DISABLED ?
-                       AUTONEG_ENABLE : AUTONEG_DISABLE;
+               if (eth_port->supp_aneg) {
+                       ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg);
+                       if (eth_port->aneg == NFP_ANEG_AUTO) {
+                               ethtool_link_ksettings_add_link_mode(cmd, advertising, Autoneg);
+                               cmd->base.autoneg = AUTONEG_ENABLE;
+                       }
+               }
                nfp_net_set_fec_link_mode(eth_port, cmd);
        }
 
@@ -327,6 +332,7 @@ static int
 nfp_net_set_link_ksettings(struct net_device *netdev,
                           const struct ethtool_link_ksettings *cmd)
 {
+       bool req_aneg = (cmd->base.autoneg == AUTONEG_ENABLE);
        struct nfp_eth_table_port *eth_port;
        struct nfp_port *port;
        struct nfp_nsp *nsp;
@@ -346,13 +352,25 @@ nfp_net_set_link_ksettings(struct net_device *netdev,
        if (IS_ERR(nsp))
                return PTR_ERR(nsp);
 
-       err = __nfp_eth_set_aneg(nsp, cmd->base.autoneg == AUTONEG_ENABLE ?
-                                NFP_ANEG_AUTO : NFP_ANEG_DISABLED);
+       if (req_aneg && !eth_port->supp_aneg) {
+               netdev_warn(netdev, "Autoneg is not supported.\n");
+               err = -EOPNOTSUPP;
+               goto err_bad_set;
+       }
+
+       err = __nfp_eth_set_aneg(nsp, req_aneg ? NFP_ANEG_AUTO : NFP_ANEG_DISABLED);
        if (err)
                goto err_bad_set;
+
        if (cmd->base.speed != SPEED_UNKNOWN) {
                u32 speed = cmd->base.speed / eth_port->lanes;
 
+               if (req_aneg) {
+                       netdev_err(netdev, "Speed changing is not allowed when working on autoneg mode.\n");
+                       err = -EINVAL;
+                       goto err_bad_set;
+               }
+
                err = __nfp_eth_set_speed(nsp, speed);
                if (err)
                        goto err_bad_set;
index 5246567..992d72a 100644 (file)
@@ -174,6 +174,7 @@ struct nfp_eth_table {
                bool enabled;
                bool tx_enabled;
                bool rx_enabled;
+               bool supp_aneg;
 
                bool override_changed;
 
index 18ba762..bb64efe 100644 (file)
@@ -27,6 +27,7 @@
 #define NSP_ETH_PORT_PHYLABEL          GENMASK_ULL(59, 54)
 #define NSP_ETH_PORT_FEC_SUPP_BASER    BIT_ULL(60)
 #define NSP_ETH_PORT_FEC_SUPP_RS       BIT_ULL(61)
+#define NSP_ETH_PORT_SUPP_ANEG         BIT_ULL(63)
 
 #define NSP_ETH_PORT_LANES_MASK                cpu_to_le64(NSP_ETH_PORT_LANES)
 
@@ -178,6 +179,7 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src,
                return;
 
        dst->act_fec = FIELD_GET(NSP_ETH_STATE_ACT_FEC, state);
+       dst->supp_aneg = FIELD_GET(NSP_ETH_PORT_SUPP_ANEG, port);
 }
 
 static void