]> git.baikalelectronics.ru Git - kernel.git/commitdiff
nfp: implement ethtool FEC mode settings
authorDirk van der Merwe <dirk.vandermerwe@netronome.com>
Sat, 4 Nov 2017 15:49:00 +0000 (16:49 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 5 Nov 2017 14:23:27 +0000 (23:23 +0900)
Add support in the driver ethtool ops to modify the NFP FEC modes.

The FEC modes can be set for vNIC associated with physical ports or
for MAC representor netdevs.

Signed-off-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c

index d0028894667c3ef6af0719631af5c3d9d6959012..60c8d733a37daa47216ccda526ccc7d9ca3448b0 100644 (file)
@@ -244,6 +244,30 @@ nfp_app_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
        nfp_get_drvinfo(app, app->pdev, "*", drvinfo);
 }
 
+static void
+nfp_net_set_fec_link_mode(struct nfp_eth_table_port *eth_port,
+                         struct ethtool_link_ksettings *c)
+{
+       unsigned int modes;
+
+       ethtool_link_ksettings_add_link_mode(c, supported, FEC_NONE);
+       if (!nfp_eth_can_support_fec(eth_port)) {
+               ethtool_link_ksettings_add_link_mode(c, advertising, FEC_NONE);
+               return;
+       }
+
+       modes = nfp_eth_supported_fec_modes(eth_port);
+       if (modes & NFP_FEC_BASER) {
+               ethtool_link_ksettings_add_link_mode(c, supported, FEC_BASER);
+               ethtool_link_ksettings_add_link_mode(c, advertising, FEC_BASER);
+       }
+
+       if (modes & NFP_FEC_REED_SOLOMON) {
+               ethtool_link_ksettings_add_link_mode(c, supported, FEC_RS);
+               ethtool_link_ksettings_add_link_mode(c, advertising, FEC_RS);
+       }
+}
+
 /**
  * nfp_net_get_link_ksettings - Get Link Speed settings
  * @netdev:    network interface device structure
@@ -278,9 +302,11 @@ nfp_net_get_link_ksettings(struct net_device *netdev,
 
        port = nfp_port_from_netdev(netdev);
        eth_port = nfp_port_get_eth_port(port);
-       if (eth_port)
+       if (eth_port) {
                cmd->base.autoneg = eth_port->aneg != NFP_ANEG_DISABLED ?
                        AUTONEG_ENABLE : AUTONEG_DISABLE;
+               nfp_net_set_fec_link_mode(eth_port, cmd);
+       }
 
        if (!netif_carrier_ok(netdev))
                return 0;
@@ -686,6 +712,91 @@ static int nfp_port_get_sset_count(struct net_device *netdev, int sset)
        }
 }
 
+static int nfp_port_fec_ethtool_to_nsp(u32 fec)
+{
+       switch (fec) {
+       case ETHTOOL_FEC_AUTO:
+               return NFP_FEC_AUTO_BIT;
+       case ETHTOOL_FEC_OFF:
+               return NFP_FEC_DISABLED_BIT;
+       case ETHTOOL_FEC_RS:
+               return NFP_FEC_REED_SOLOMON_BIT;
+       case ETHTOOL_FEC_BASER:
+               return NFP_FEC_BASER_BIT;
+       default:
+               /* NSP only supports a single mode at a time */
+               return -EOPNOTSUPP;
+       }
+}
+
+static u32 nfp_port_fec_nsp_to_ethtool(u32 fec)
+{
+       u32 result = 0;
+
+       if (fec & NFP_FEC_AUTO)
+               result |= ETHTOOL_FEC_AUTO;
+       if (fec & NFP_FEC_BASER)
+               result |= ETHTOOL_FEC_BASER;
+       if (fec & NFP_FEC_REED_SOLOMON)
+               result |= ETHTOOL_FEC_RS;
+       if (fec & NFP_FEC_DISABLED)
+               result |= ETHTOOL_FEC_OFF;
+
+       return result ?: ETHTOOL_FEC_NONE;
+}
+
+static int
+nfp_port_get_fecparam(struct net_device *netdev,
+                     struct ethtool_fecparam *param)
+{
+       struct nfp_eth_table_port *eth_port;
+       struct nfp_port *port;
+
+       param->active_fec = ETHTOOL_FEC_NONE_BIT;
+       param->fec = ETHTOOL_FEC_NONE_BIT;
+
+       port = nfp_port_from_netdev(netdev);
+       eth_port = nfp_port_get_eth_port(port);
+       if (!eth_port)
+               return -EOPNOTSUPP;
+
+       if (!nfp_eth_can_support_fec(eth_port))
+               return 0;
+
+       param->fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec_modes_supported);
+       param->active_fec = nfp_port_fec_nsp_to_ethtool(eth_port->fec);
+
+       return 0;
+}
+
+static int
+nfp_port_set_fecparam(struct net_device *netdev,
+                     struct ethtool_fecparam *param)
+{
+       struct nfp_eth_table_port *eth_port;
+       struct nfp_port *port;
+       int err, fec;
+
+       port = nfp_port_from_netdev(netdev);
+       eth_port = nfp_port_get_eth_port(port);
+       if (!eth_port)
+               return -EOPNOTSUPP;
+
+       if (!nfp_eth_can_support_fec(eth_port))
+               return -EOPNOTSUPP;
+
+       fec = nfp_port_fec_ethtool_to_nsp(param->fec);
+       if (fec < 0)
+               return fec;
+
+       err = nfp_eth_set_fec(port->app->cpp, eth_port->index, fec);
+       if (!err)
+               /* Only refresh if we did something */
+               nfp_net_refresh_port_table(port);
+
+       return err < 0 ? err : 0;
+}
+
 /* RX network flow classification (RSS, filters, etc)
  */
 static u32 ethtool_flow_to_nfp_flag(u32 flow_type)
@@ -1144,6 +1255,8 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
        .set_channels           = nfp_net_set_channels,
        .get_link_ksettings     = nfp_net_get_link_ksettings,
        .set_link_ksettings     = nfp_net_set_link_ksettings,
+       .get_fecparam           = nfp_port_get_fecparam,
+       .set_fecparam           = nfp_port_set_fecparam,
 };
 
 const struct ethtool_ops nfp_port_ethtool_ops = {
@@ -1157,6 +1270,8 @@ const struct ethtool_ops nfp_port_ethtool_ops = {
        .get_dump_data          = nfp_app_get_dump_data,
        .get_link_ksettings     = nfp_net_get_link_ksettings,
        .set_link_ksettings     = nfp_net_set_link_ksettings,
+       .get_fecparam           = nfp_port_get_fecparam,
+       .set_fecparam           = nfp_port_set_fecparam,
 };
 
 void nfp_net_set_ethtool_ops(struct net_device *netdev)