|
|
@@ -35,6 +35,7 @@
|
|
|
#include <linux/ethtool.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/mlx4/driver.h>
|
|
|
+#include <linux/mlx4/device.h>
|
|
|
#include <linux/in.h>
|
|
|
#include <net/ip.h>
|
|
|
|
|
|
@@ -374,7 +375,302 @@ static void mlx4_en_get_strings(struct net_device *dev,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
+static u32 mlx4_en_autoneg_get(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ u32 autoneg = AUTONEG_DISABLE;
|
|
|
+
|
|
|
+ if ((mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP) &&
|
|
|
+ (priv->port_state.flags & MLX4_EN_PORT_ANE))
|
|
|
+ autoneg = AUTONEG_ENABLE;
|
|
|
+
|
|
|
+ return autoneg;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 ptys_get_supported_port(struct mlx4_ptys_reg *ptys_reg)
|
|
|
+{
|
|
|
+ u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap);
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_T)
|
|
|
+ | MLX4_PROT_MASK(MLX4_100BASE_TX))) {
|
|
|
+ return SUPPORTED_TP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_10GBASE_SR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_56GBASE_SR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_CR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_SR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) {
|
|
|
+ return SUPPORTED_FIBRE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_KR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_20GBASE_KR2)
|
|
|
+ | MLX4_PROT_MASK(MLX4_10GBASE_KR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_10GBASE_KX4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_KX))) {
|
|
|
+ return SUPPORTED_Backplane;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 ptys_get_active_port(struct mlx4_ptys_reg *ptys_reg)
|
|
|
+{
|
|
|
+ u32 eth_proto = be32_to_cpu(ptys_reg->eth_proto_oper);
|
|
|
+
|
|
|
+ if (!eth_proto) /* link down */
|
|
|
+ eth_proto = be32_to_cpu(ptys_reg->eth_proto_cap);
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_T)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_T)
|
|
|
+ | MLX4_PROT_MASK(MLX4_100BASE_TX))) {
|
|
|
+ return PORT_TP;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_SR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_56GBASE_SR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_SR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_CX_SGMII))) {
|
|
|
+ return PORT_FIBRE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_10GBASE_CR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_56GBASE_CR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_CR4))) {
|
|
|
+ return PORT_DA;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (eth_proto & (MLX4_PROT_MASK(MLX4_56GBASE_KR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_40GBASE_KR4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_20GBASE_KR2)
|
|
|
+ | MLX4_PROT_MASK(MLX4_10GBASE_KR)
|
|
|
+ | MLX4_PROT_MASK(MLX4_10GBASE_KX4)
|
|
|
+ | MLX4_PROT_MASK(MLX4_1000BASE_KX))) {
|
|
|
+ return PORT_NONE;
|
|
|
+ }
|
|
|
+ return PORT_OTHER;
|
|
|
+}
|
|
|
+
|
|
|
+#define MLX4_LINK_MODES_SZ \
|
|
|
+ (FIELD_SIZEOF(struct mlx4_ptys_reg, eth_proto_cap) * 8)
|
|
|
+
|
|
|
+enum ethtool_report {
|
|
|
+ SUPPORTED = 0,
|
|
|
+ ADVERTISED = 1,
|
|
|
+ SPEED = 2
|
|
|
+};
|
|
|
+
|
|
|
+/* Translates mlx4 link mode to equivalent ethtool Link modes/speed */
|
|
|
+static u32 ptys2ethtool_map[MLX4_LINK_MODES_SZ][3] = {
|
|
|
+ [MLX4_100BASE_TX] = {
|
|
|
+ SUPPORTED_100baseT_Full,
|
|
|
+ ADVERTISED_100baseT_Full,
|
|
|
+ SPEED_100
|
|
|
+ },
|
|
|
+
|
|
|
+ [MLX4_1000BASE_T] = {
|
|
|
+ SUPPORTED_1000baseT_Full,
|
|
|
+ ADVERTISED_1000baseT_Full,
|
|
|
+ SPEED_1000
|
|
|
+ },
|
|
|
+ [MLX4_1000BASE_CX_SGMII] = {
|
|
|
+ SUPPORTED_1000baseKX_Full,
|
|
|
+ ADVERTISED_1000baseKX_Full,
|
|
|
+ SPEED_1000
|
|
|
+ },
|
|
|
+ [MLX4_1000BASE_KX] = {
|
|
|
+ SUPPORTED_1000baseKX_Full,
|
|
|
+ ADVERTISED_1000baseKX_Full,
|
|
|
+ SPEED_1000
|
|
|
+ },
|
|
|
+
|
|
|
+ [MLX4_10GBASE_T] = {
|
|
|
+ SUPPORTED_10000baseT_Full,
|
|
|
+ ADVERTISED_10000baseT_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+ [MLX4_10GBASE_CX4] = {
|
|
|
+ SUPPORTED_10000baseKX4_Full,
|
|
|
+ ADVERTISED_10000baseKX4_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+ [MLX4_10GBASE_KX4] = {
|
|
|
+ SUPPORTED_10000baseKX4_Full,
|
|
|
+ ADVERTISED_10000baseKX4_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+ [MLX4_10GBASE_KR] = {
|
|
|
+ SUPPORTED_10000baseKR_Full,
|
|
|
+ ADVERTISED_10000baseKR_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+ [MLX4_10GBASE_CR] = {
|
|
|
+ SUPPORTED_10000baseKR_Full,
|
|
|
+ ADVERTISED_10000baseKR_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+ [MLX4_10GBASE_SR] = {
|
|
|
+ SUPPORTED_10000baseKR_Full,
|
|
|
+ ADVERTISED_10000baseKR_Full,
|
|
|
+ SPEED_10000
|
|
|
+ },
|
|
|
+
|
|
|
+ [MLX4_20GBASE_KR2] = {
|
|
|
+ SUPPORTED_20000baseMLD2_Full | SUPPORTED_20000baseKR2_Full,
|
|
|
+ ADVERTISED_20000baseMLD2_Full | ADVERTISED_20000baseKR2_Full,
|
|
|
+ SPEED_20000
|
|
|
+ },
|
|
|
+
|
|
|
+ [MLX4_40GBASE_CR4] = {
|
|
|
+ SUPPORTED_40000baseCR4_Full,
|
|
|
+ ADVERTISED_40000baseCR4_Full,
|
|
|
+ SPEED_40000
|
|
|
+ },
|
|
|
+ [MLX4_40GBASE_KR4] = {
|
|
|
+ SUPPORTED_40000baseKR4_Full,
|
|
|
+ ADVERTISED_40000baseKR4_Full,
|
|
|
+ SPEED_40000
|
|
|
+ },
|
|
|
+ [MLX4_40GBASE_SR4] = {
|
|
|
+ SUPPORTED_40000baseSR4_Full,
|
|
|
+ ADVERTISED_40000baseSR4_Full,
|
|
|
+ SPEED_40000
|
|
|
+ },
|
|
|
+
|
|
|
+ [MLX4_56GBASE_KR4] = {
|
|
|
+ SUPPORTED_56000baseKR4_Full,
|
|
|
+ ADVERTISED_56000baseKR4_Full,
|
|
|
+ SPEED_56000
|
|
|
+ },
|
|
|
+ [MLX4_56GBASE_CR4] = {
|
|
|
+ SUPPORTED_56000baseCR4_Full,
|
|
|
+ ADVERTISED_56000baseCR4_Full,
|
|
|
+ SPEED_56000
|
|
|
+ },
|
|
|
+ [MLX4_56GBASE_SR4] = {
|
|
|
+ SUPPORTED_56000baseSR4_Full,
|
|
|
+ ADVERTISED_56000baseSR4_Full,
|
|
|
+ SPEED_56000
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static u32 ptys2ethtool_link_modes(u32 eth_proto, enum ethtool_report report)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ u32 link_modes = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
|
|
|
+ if (eth_proto & MLX4_PROT_MASK(i))
|
|
|
+ link_modes |= ptys2ethtool_map[i][report];
|
|
|
+ }
|
|
|
+ return link_modes;
|
|
|
+}
|
|
|
+
|
|
|
+static u32 ethtool2ptys_link_modes(u32 link_modes, enum ethtool_report report)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ u32 ptys_modes = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
|
|
|
+ if (ptys2ethtool_map[i][report] & link_modes)
|
|
|
+ ptys_modes |= 1 << i;
|
|
|
+ }
|
|
|
+ return ptys_modes;
|
|
|
+}
|
|
|
+
|
|
|
+/* Convert actual speed (SPEED_XXX) to ptys link modes */
|
|
|
+static u32 speed2ptys_link_modes(u32 speed)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ u32 ptys_modes = 0;
|
|
|
+
|
|
|
+ for (i = 0; i < MLX4_LINK_MODES_SZ; i++) {
|
|
|
+ if (ptys2ethtool_map[i][SPEED] == speed)
|
|
|
+ ptys_modes |= 1 << i;
|
|
|
+ }
|
|
|
+ return ptys_modes;
|
|
|
+}
|
|
|
+
|
|
|
+static int ethtool_get_ptys_settings(struct net_device *dev,
|
|
|
+ struct ethtool_cmd *cmd)
|
|
|
+{
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ struct mlx4_ptys_reg ptys_reg;
|
|
|
+ u32 eth_proto;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ memset(&ptys_reg, 0, sizeof(ptys_reg));
|
|
|
+ ptys_reg.local_port = priv->port;
|
|
|
+ ptys_reg.proto_mask = MLX4_PTYS_EN;
|
|
|
+ ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev,
|
|
|
+ MLX4_ACCESS_REG_QUERY, &ptys_reg);
|
|
|
+ if (ret) {
|
|
|
+ en_warn(priv, "Failed to run mlx4_ACCESS_PTYS_REG status(%x)",
|
|
|
+ ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ en_dbg(DRV, priv, "ptys_reg.proto_mask %x\n",
|
|
|
+ ptys_reg.proto_mask);
|
|
|
+ en_dbg(DRV, priv, "ptys_reg.eth_proto_cap %x\n",
|
|
|
+ be32_to_cpu(ptys_reg.eth_proto_cap));
|
|
|
+ en_dbg(DRV, priv, "ptys_reg.eth_proto_admin %x\n",
|
|
|
+ be32_to_cpu(ptys_reg.eth_proto_admin));
|
|
|
+ en_dbg(DRV, priv, "ptys_reg.eth_proto_oper %x\n",
|
|
|
+ be32_to_cpu(ptys_reg.eth_proto_oper));
|
|
|
+ en_dbg(DRV, priv, "ptys_reg.eth_proto_lp_adv %x\n",
|
|
|
+ be32_to_cpu(ptys_reg.eth_proto_lp_adv));
|
|
|
+
|
|
|
+ cmd->supported = 0;
|
|
|
+ cmd->advertising = 0;
|
|
|
+
|
|
|
+ cmd->supported |= ptys_get_supported_port(&ptys_reg);
|
|
|
+
|
|
|
+ eth_proto = be32_to_cpu(ptys_reg.eth_proto_cap);
|
|
|
+ cmd->supported |= ptys2ethtool_link_modes(eth_proto, SUPPORTED);
|
|
|
+
|
|
|
+ eth_proto = be32_to_cpu(ptys_reg.eth_proto_admin);
|
|
|
+ cmd->advertising |= ptys2ethtool_link_modes(eth_proto, ADVERTISED);
|
|
|
+
|
|
|
+ cmd->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
|
|
|
+ cmd->advertising |= (priv->prof->tx_pause) ? ADVERTISED_Pause : 0;
|
|
|
+
|
|
|
+ cmd->advertising |= (priv->prof->tx_pause ^ priv->prof->rx_pause) ?
|
|
|
+ ADVERTISED_Asym_Pause : 0;
|
|
|
+
|
|
|
+ cmd->port = ptys_get_active_port(&ptys_reg);
|
|
|
+ cmd->transceiver = (SUPPORTED_TP & cmd->supported) ?
|
|
|
+ XCVR_EXTERNAL : XCVR_INTERNAL;
|
|
|
+
|
|
|
+ if (mlx4_en_autoneg_get(dev)) {
|
|
|
+ cmd->supported |= SUPPORTED_Autoneg;
|
|
|
+ cmd->advertising |= ADVERTISED_Autoneg;
|
|
|
+ }
|
|
|
+
|
|
|
+ cmd->autoneg = (priv->port_state.flags & MLX4_EN_PORT_ANC) ?
|
|
|
+ AUTONEG_ENABLE : AUTONEG_DISABLE;
|
|
|
+
|
|
|
+ eth_proto = be32_to_cpu(ptys_reg.eth_proto_lp_adv);
|
|
|
+ cmd->lp_advertising = ptys2ethtool_link_modes(eth_proto, ADVERTISED);
|
|
|
+
|
|
|
+ cmd->lp_advertising |= (priv->port_state.flags & MLX4_EN_PORT_ANC) ?
|
|
|
+ ADVERTISED_Autoneg : 0;
|
|
|
+
|
|
|
+ cmd->phy_address = 0;
|
|
|
+ cmd->mdio_support = 0;
|
|
|
+ cmd->maxtxpkt = 0;
|
|
|
+ cmd->maxrxpkt = 0;
|
|
|
+ cmd->eth_tp_mdix = ETH_TP_MDI_INVALID;
|
|
|
+ cmd->eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static void ethtool_get_default_settings(struct net_device *dev,
|
|
|
+ struct ethtool_cmd *cmd)
|
|
|
{
|
|
|
struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
int trans_type;
|
|
|
@@ -382,18 +678,7 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
cmd->autoneg = AUTONEG_DISABLE;
|
|
|
cmd->supported = SUPPORTED_10000baseT_Full;
|
|
|
cmd->advertising = ADVERTISED_10000baseT_Full;
|
|
|
-
|
|
|
- if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- trans_type = priv->port_state.transciver;
|
|
|
- if (netif_carrier_ok(dev)) {
|
|
|
- ethtool_cmd_speed_set(cmd, priv->port_state.link_speed);
|
|
|
- cmd->duplex = DUPLEX_FULL;
|
|
|
- } else {
|
|
|
- ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
|
|
|
- cmd->duplex = DUPLEX_UNKNOWN;
|
|
|
- }
|
|
|
+ trans_type = priv->port_state.transceiver;
|
|
|
|
|
|
if (trans_type > 0 && trans_type <= 0xC) {
|
|
|
cmd->port = PORT_FIBRE;
|
|
|
@@ -409,17 +694,118 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
cmd->port = -1;
|
|
|
cmd->transceiver = -1;
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
+{
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ int ret = -EINVAL;
|
|
|
+
|
|
|
+ if (mlx4_en_QUERY_PORT(priv->mdev, priv->port))
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ en_dbg(DRV, priv, "query port state.flags ANC(%x) ANE(%x)\n",
|
|
|
+ priv->port_state.flags & MLX4_EN_PORT_ANC,
|
|
|
+ priv->port_state.flags & MLX4_EN_PORT_ANE);
|
|
|
+
|
|
|
+ if (priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL)
|
|
|
+ ret = ethtool_get_ptys_settings(dev, cmd);
|
|
|
+ if (ret) /* ETH PROT CRTL is not supported or PTYS CMD failed */
|
|
|
+ ethtool_get_default_settings(dev, cmd);
|
|
|
+
|
|
|
+ if (netif_carrier_ok(dev)) {
|
|
|
+ ethtool_cmd_speed_set(cmd, priv->port_state.link_speed);
|
|
|
+ cmd->duplex = DUPLEX_FULL;
|
|
|
+ } else {
|
|
|
+ ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
|
|
|
+ cmd->duplex = DUPLEX_UNKNOWN;
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* Calculate PTYS admin according ethtool speed (SPEED_XXX) */
|
|
|
+static __be32 speed_set_ptys_admin(struct mlx4_en_priv *priv, u32 speed,
|
|
|
+ __be32 proto_cap)
|
|
|
+{
|
|
|
+ __be32 proto_admin = 0;
|
|
|
+
|
|
|
+ if (!speed) { /* Speed = 0 ==> Reset Link modes */
|
|
|
+ proto_admin = proto_cap;
|
|
|
+ en_info(priv, "Speed was set to 0, Reset advertised Link Modes to default (%x)\n",
|
|
|
+ be32_to_cpu(proto_cap));
|
|
|
+ } else {
|
|
|
+ u32 ptys_link_modes = speed2ptys_link_modes(speed);
|
|
|
+
|
|
|
+ proto_admin = cpu_to_be32(ptys_link_modes) & proto_cap;
|
|
|
+ en_info(priv, "Setting Speed to %d\n", speed);
|
|
|
+ }
|
|
|
+ return proto_admin;
|
|
|
+}
|
|
|
+
|
|
|
static int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
|
|
|
{
|
|
|
- if ((cmd->autoneg == AUTONEG_ENABLE) ||
|
|
|
- (ethtool_cmd_speed(cmd) != SPEED_10000) ||
|
|
|
- (cmd->duplex != DUPLEX_FULL))
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ struct mlx4_ptys_reg ptys_reg;
|
|
|
+ __be32 proto_admin;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ u32 ptys_adv = ethtool2ptys_link_modes(cmd->advertising, ADVERTISED);
|
|
|
+ int speed = ethtool_cmd_speed(cmd);
|
|
|
+
|
|
|
+ en_dbg(DRV, priv, "Set Speed=%d adv=0x%x autoneg=%d duplex=%d\n",
|
|
|
+ speed, cmd->advertising, cmd->autoneg, cmd->duplex);
|
|
|
+
|
|
|
+ if (!(priv->mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_ETH_PROT_CTRL) ||
|
|
|
+ (cmd->autoneg == AUTONEG_ENABLE) || (cmd->duplex == DUPLEX_HALF))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /* Nothing to change */
|
|
|
+ memset(&ptys_reg, 0, sizeof(ptys_reg));
|
|
|
+ ptys_reg.local_port = priv->port;
|
|
|
+ ptys_reg.proto_mask = MLX4_PTYS_EN;
|
|
|
+ ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev,
|
|
|
+ MLX4_ACCESS_REG_QUERY, &ptys_reg);
|
|
|
+ if (ret) {
|
|
|
+ en_warn(priv, "Failed to QUERY mlx4_ACCESS_PTYS_REG status(%x)\n",
|
|
|
+ ret);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ proto_admin = cpu_to_be32(ptys_adv);
|
|
|
+ if (speed >= 0 && speed != priv->port_state.link_speed)
|
|
|
+ /* If speed was set then speed decides :-) */
|
|
|
+ proto_admin = speed_set_ptys_admin(priv, speed,
|
|
|
+ ptys_reg.eth_proto_cap);
|
|
|
+
|
|
|
+ proto_admin &= ptys_reg.eth_proto_cap;
|
|
|
+
|
|
|
+ if (proto_admin == ptys_reg.eth_proto_admin)
|
|
|
+ return 0; /* Nothing to change */
|
|
|
+
|
|
|
+ if (!proto_admin) {
|
|
|
+ en_warn(priv, "Not supported link mode(s) requested, check supported link modes.\n");
|
|
|
+ return -EINVAL; /* nothing to change due to bad input */
|
|
|
+ }
|
|
|
+
|
|
|
+ en_dbg(DRV, priv, "mlx4_ACCESS_PTYS_REG SET: ptys_reg.eth_proto_admin = 0x%x\n",
|
|
|
+ be32_to_cpu(proto_admin));
|
|
|
+
|
|
|
+ ptys_reg.eth_proto_admin = proto_admin;
|
|
|
+ ret = mlx4_ACCESS_PTYS_REG(priv->mdev->dev, MLX4_ACCESS_REG_WRITE,
|
|
|
+ &ptys_reg);
|
|
|
+ if (ret) {
|
|
|
+ en_warn(priv, "Failed to write mlx4_ACCESS_PTYS_REG eth_proto_admin(0x%x) status(0x%x)",
|
|
|
+ be32_to_cpu(ptys_reg.eth_proto_admin), ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ en_warn(priv, "Port link mode changed, restarting port...\n");
|
|
|
+ mutex_lock(&priv->mdev->state_lock);
|
|
|
+ if (priv->port_up) {
|
|
|
+ mlx4_en_stop_port(dev, 1);
|
|
|
+ if (mlx4_en_start_port(dev))
|
|
|
+ en_err(priv, "Failed restarting port %d\n", priv->port);
|
|
|
+ }
|
|
|
+ mutex_unlock(&priv->mdev->state_lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
@@ -596,6 +982,7 @@ static int mlx4_en_get_rxfh(struct net_device *dev, u32 *ring_index, u8 *key)
|
|
|
int err = 0;
|
|
|
|
|
|
rss_rings = priv->prof->rss_rings ?: priv->rx_ring_num;
|
|
|
+ rss_rings = 1 << ilog2(rss_rings);
|
|
|
|
|
|
while (n--) {
|
|
|
ring_index[n] = rss_map->qps[n % rss_rings].qpn -
|
|
|
@@ -1309,6 +1696,86 @@ static int mlx4_en_set_tunable(struct net_device *dev,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int mlx4_en_get_module_info(struct net_device *dev,
|
|
|
+ struct ethtool_modinfo *modinfo)
|
|
|
+{
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ int ret;
|
|
|
+ u8 data[4];
|
|
|
+
|
|
|
+ /* Read first 2 bytes to get Module & REV ID */
|
|
|
+ ret = mlx4_get_module_info(mdev->dev, priv->port,
|
|
|
+ 0/*offset*/, 2/*size*/, data);
|
|
|
+ if (ret < 2)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ switch (data[0] /* identifier */) {
|
|
|
+ case MLX4_MODULE_ID_QSFP:
|
|
|
+ modinfo->type = ETH_MODULE_SFF_8436;
|
|
|
+ modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
|
|
+ break;
|
|
|
+ case MLX4_MODULE_ID_QSFP_PLUS:
|
|
|
+ if (data[1] >= 0x3) { /* revision id */
|
|
|
+ modinfo->type = ETH_MODULE_SFF_8636;
|
|
|
+ modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
|
|
|
+ } else {
|
|
|
+ modinfo->type = ETH_MODULE_SFF_8436;
|
|
|
+ modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case MLX4_MODULE_ID_QSFP28:
|
|
|
+ modinfo->type = ETH_MODULE_SFF_8636;
|
|
|
+ modinfo->eeprom_len = ETH_MODULE_SFF_8636_LEN;
|
|
|
+ break;
|
|
|
+ case MLX4_MODULE_ID_SFP:
|
|
|
+ modinfo->type = ETH_MODULE_SFF_8472;
|
|
|
+ modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -ENOSYS;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mlx4_en_get_module_eeprom(struct net_device *dev,
|
|
|
+ struct ethtool_eeprom *ee,
|
|
|
+ u8 *data)
|
|
|
+{
|
|
|
+ struct mlx4_en_priv *priv = netdev_priv(dev);
|
|
|
+ struct mlx4_en_dev *mdev = priv->mdev;
|
|
|
+ int offset = ee->offset;
|
|
|
+ int i = 0, ret;
|
|
|
+
|
|
|
+ if (ee->len == 0)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ memset(data, 0, ee->len);
|
|
|
+
|
|
|
+ while (i < ee->len) {
|
|
|
+ en_dbg(DRV, priv,
|
|
|
+ "mlx4_get_module_info i(%d) offset(%d) len(%d)\n",
|
|
|
+ i, offset, ee->len - i);
|
|
|
+
|
|
|
+ ret = mlx4_get_module_info(mdev->dev, priv->port,
|
|
|
+ offset, ee->len - i, data + i);
|
|
|
+
|
|
|
+ if (!ret) /* Done reading */
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (ret < 0) {
|
|
|
+ en_err(priv,
|
|
|
+ "mlx4_get_module_info i(%d) offset(%d) bytes_to_read(%d) - FAILED (0x%x)\n",
|
|
|
+ i, offset, ee->len - i, ret);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ i += ret;
|
|
|
+ offset += ret;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
const struct ethtool_ops mlx4_en_ethtool_ops = {
|
|
|
.get_drvinfo = mlx4_en_get_drvinfo,
|
|
|
@@ -1341,6 +1808,8 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
|
|
|
.get_priv_flags = mlx4_en_get_priv_flags,
|
|
|
.get_tunable = mlx4_en_get_tunable,
|
|
|
.set_tunable = mlx4_en_set_tunable,
|
|
|
+ .get_module_info = mlx4_en_get_module_info,
|
|
|
+ .get_module_eeprom = mlx4_en_get_module_eeprom
|
|
|
};
|
|
|
|
|
|
|