Merge branch '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue
[platform/kernel/linux-rpi.git] / drivers / net / ethernet / intel / i40e / i40e_ethtool.c
index 9e81f85..c70dec6 100644 (file)
@@ -3222,13 +3222,30 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
                fsp->m_u.usr_ip4_spec.proto = 0;
        }
 
-       /* Reverse the src and dest notion, since the HW views them from
-        * Tx perspective where as the user expects it from Rx filter view.
-        */
-       fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
-       fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
-       fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
-       fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
+       if (fsp->flow_type == IPV6_USER_FLOW ||
+           fsp->flow_type == UDP_V6_FLOW ||
+           fsp->flow_type == TCP_V6_FLOW ||
+           fsp->flow_type == SCTP_V6_FLOW) {
+               /* Reverse the src and dest notion, since the HW views them
+                * from Tx perspective where as the user expects it from
+                * Rx filter view.
+                */
+               fsp->h_u.tcp_ip6_spec.psrc = rule->dst_port;
+               fsp->h_u.tcp_ip6_spec.pdst = rule->src_port;
+               memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, rule->src_ip6,
+                      sizeof(__be32) * 4);
+               memcpy(fsp->h_u.tcp_ip6_spec.ip6src, rule->dst_ip6,
+                      sizeof(__be32) * 4);
+       } else {
+               /* Reverse the src and dest notion, since the HW views them
+                * from Tx perspective where as the user expects it from
+                * Rx filter view.
+                */
+               fsp->h_u.tcp_ip4_spec.psrc = rule->dst_port;
+               fsp->h_u.tcp_ip4_spec.pdst = rule->src_port;
+               fsp->h_u.tcp_ip4_spec.ip4src = rule->dst_ip;
+               fsp->h_u.tcp_ip4_spec.ip4dst = rule->src_ip;
+       }
 
        switch (rule->flow_type) {
        case SCTP_V4_FLOW:
@@ -3240,9 +3257,21 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
        case UDP_V4_FLOW:
                index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
                break;
+       case SCTP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
+               break;
+       case TCP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
+               break;
+       case UDP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
+               break;
        case IP_USER_FLOW:
                index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
                break;
+       case IPV6_USER_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
+               break;
        default:
                /* If we have stored a filter with a flow type not listed here
                 * it is almost certainly a driver bug. WARN(), and then
@@ -3258,6 +3287,20 @@ static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
        input_set = i40e_read_fd_input_set(pf, index);
 
 no_input_set:
+       if (input_set & I40E_L3_V6_SRC_MASK) {
+               fsp->m_u.tcp_ip6_spec.ip6src[0] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6src[1] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6src[2] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6src[3] = htonl(0xFFFFFFFF);
+       }
+
+       if (input_set & I40E_L3_V6_DST_MASK) {
+               fsp->m_u.tcp_ip6_spec.ip6dst[0] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6dst[1] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6dst[2] = htonl(0xFFFFFFFF);
+               fsp->m_u.tcp_ip6_spec.ip6dst[3] = htonl(0xFFFFFFFF);
+       }
+
        if (input_set & I40E_L3_SRC_MASK)
                fsp->m_u.tcp_ip4_spec.ip4src = htonl(0xFFFFFFFF);
 
@@ -3275,6 +3318,14 @@ no_input_set:
        else
                fsp->ring_cookie = rule->q_index;
 
+       if (rule->vlan_tag) {
+               fsp->h_ext.vlan_etype = rule->vlan_etype;
+               fsp->m_ext.vlan_etype = htons(0xFFFF);
+               fsp->h_ext.vlan_tci = rule->vlan_tag;
+               fsp->m_ext.vlan_tci = htons(0xFFFF);
+               fsp->flow_type |= FLOW_EXT;
+       }
+
        if (rule->dest_vsi != pf->vsi[pf->lan_vsi]->id) {
                struct i40e_vsi *vsi;
 
@@ -3921,6 +3972,14 @@ static const char *i40e_flow_str(struct ethtool_rx_flow_spec *fsp)
                return "sctp4";
        case IP_USER_FLOW:
                return "ip4";
+       case TCP_V6_FLOW:
+               return "tcp6";
+       case UDP_V6_FLOW:
+               return "udp6";
+       case SCTP_V6_FLOW:
+               return "sctp6";
+       case IPV6_USER_FLOW:
+               return "ip6";
        default:
                return "unknown";
        }
@@ -4056,9 +4115,14 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
                                     struct ethtool_rx_flow_spec *fsp,
                                     struct i40e_rx_flow_userdef *userdef)
 {
-       struct i40e_pf *pf = vsi->back;
+       static const __be32 ipv6_full_mask[4] = {cpu_to_be32(0xffffffff),
+               cpu_to_be32(0xffffffff), cpu_to_be32(0xffffffff),
+               cpu_to_be32(0xffffffff)};
+       struct ethtool_tcpip6_spec *tcp_ip6_spec;
+       struct ethtool_usrip6_spec *usr_ip6_spec;
        struct ethtool_tcpip4_spec *tcp_ip4_spec;
        struct ethtool_usrip4_spec *usr_ip4_spec;
+       struct i40e_pf *pf = vsi->back;
        u64 current_mask, new_mask;
        bool new_flex_offset = false;
        bool flex_l3 = false;
@@ -4080,11 +4144,28 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
                index = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
                fdir_filter_count = &pf->fd_udp4_filter_cnt;
                break;
+       case SCTP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_SCTP;
+               fdir_filter_count = &pf->fd_sctp6_filter_cnt;
+               break;
+       case TCP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_TCP;
+               fdir_filter_count = &pf->fd_tcp6_filter_cnt;
+               break;
+       case UDP_V6_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_UDP;
+               fdir_filter_count = &pf->fd_udp6_filter_cnt;
+               break;
        case IP_USER_FLOW:
                index = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
                fdir_filter_count = &pf->fd_ip4_filter_cnt;
                flex_l3 = true;
                break;
+       case IPV6_USER_FLOW:
+               index = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
+               fdir_filter_count = &pf->fd_ip6_filter_cnt;
+               flex_l3 = true;
+               break;
        default:
                return -EOPNOTSUPP;
        }
@@ -4147,6 +4228,53 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
                        return -EOPNOTSUPP;
 
                break;
+       case SCTP_V6_FLOW:
+               new_mask &= ~I40E_VERIFY_TAG_MASK;
+               fallthrough;
+       case TCP_V6_FLOW:
+       case UDP_V6_FLOW:
+               tcp_ip6_spec = &fsp->m_u.tcp_ip6_spec;
+
+               /* Check if user provided IPv6 source address. */
+               if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6src,
+                                   (struct in6_addr *)&ipv6_full_mask))
+                       new_mask |= I40E_L3_V6_SRC_MASK;
+               else if (ipv6_addr_any((struct in6_addr *)
+                                      &tcp_ip6_spec->ip6src))
+                       new_mask &= ~I40E_L3_V6_SRC_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               /* Check if user provided destination address. */
+               if (ipv6_addr_equal((struct in6_addr *)&tcp_ip6_spec->ip6dst,
+                                   (struct in6_addr *)&ipv6_full_mask))
+                       new_mask |= I40E_L3_V6_DST_MASK;
+               else if (ipv6_addr_any((struct in6_addr *)
+                                      &tcp_ip6_spec->ip6dst))
+                       new_mask &= ~I40E_L3_V6_DST_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               /* L4 source port */
+               if (tcp_ip6_spec->psrc == htons(0xFFFF))
+                       new_mask |= I40E_L4_SRC_MASK;
+               else if (!tcp_ip6_spec->psrc)
+                       new_mask &= ~I40E_L4_SRC_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               /* L4 destination port */
+               if (tcp_ip6_spec->pdst == htons(0xFFFF))
+                       new_mask |= I40E_L4_DST_MASK;
+               else if (!tcp_ip6_spec->pdst)
+                       new_mask &= ~I40E_L4_DST_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               /* Filtering on Traffic Classes is not supported. */
+               if (tcp_ip6_spec->tclass)
+                       return -EOPNOTSUPP;
+               break;
        case IP_USER_FLOW:
                usr_ip4_spec = &fsp->m_u.usr_ip4_spec;
 
@@ -4187,10 +4315,62 @@ static int i40e_check_fdir_input_set(struct i40e_vsi *vsi,
                        return -EINVAL;
 
                break;
+       case IPV6_USER_FLOW:
+               usr_ip6_spec = &fsp->m_u.usr_ip6_spec;
+
+               /* Check if user provided IPv6 source address. */
+               if (ipv6_addr_equal((struct in6_addr *)&usr_ip6_spec->ip6src,
+                                   (struct in6_addr *)&ipv6_full_mask))
+                       new_mask |= I40E_L3_V6_SRC_MASK;
+               else if (ipv6_addr_any((struct in6_addr *)
+                                      &usr_ip6_spec->ip6src))
+                       new_mask &= ~I40E_L3_V6_SRC_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               /* Check if user provided destination address. */
+               if (ipv6_addr_equal((struct in6_addr *)&usr_ip6_spec->ip6dst,
+                                   (struct in6_addr *)&ipv6_full_mask))
+                       new_mask |= I40E_L3_V6_DST_MASK;
+               else if (ipv6_addr_any((struct in6_addr *)
+                                      &usr_ip6_spec->ip6src))
+                       new_mask &= ~I40E_L3_V6_DST_MASK;
+               else
+                       return -EOPNOTSUPP;
+
+               if (usr_ip6_spec->l4_4_bytes == htonl(0xFFFFFFFF))
+                       new_mask |= I40E_L4_SRC_MASK | I40E_L4_DST_MASK;
+               else if (!usr_ip6_spec->l4_4_bytes)
+                       new_mask &= ~(I40E_L4_SRC_MASK | I40E_L4_DST_MASK);
+               else
+                       return -EOPNOTSUPP;
+
+               /* Filtering on Traffic class is not supported. */
+               if (usr_ip6_spec->tclass)
+                       return -EOPNOTSUPP;
+
+               /* Filtering on L4 protocol is not supported */
+               if (usr_ip6_spec->l4_proto)
+                       return -EINVAL;
+
+               break;
        default:
                return -EOPNOTSUPP;
        }
 
+       if (fsp->flow_type & FLOW_EXT) {
+               /* Allow only 802.1Q and no etype defined, as
+                * later it's modified to 0x8100
+                */
+               if (fsp->h_ext.vlan_etype != htons(ETH_P_8021Q) &&
+                   fsp->h_ext.vlan_etype != 0)
+                       return -EOPNOTSUPP;
+               if (fsp->m_ext.vlan_tci == htons(0xFFFF))
+                       new_mask |= I40E_VLAN_SRC_MASK;
+               else
+                       new_mask &= ~I40E_VLAN_SRC_MASK;
+       }
+
        /* First, clear all flexible filter entries */
        new_mask &= ~I40E_FLEX_INPUT_MASK;
 
@@ -4370,7 +4550,9 @@ static bool i40e_match_fdir_filter(struct i40e_fdir_filter *a,
            a->dst_port != b->dst_port ||
            a->src_port != b->src_port ||
            a->flow_type != b->flow_type ||
-           a->ip4_proto != b->ip4_proto)
+           a->ipl4_proto != b->ipl4_proto ||
+           a->vlan_tag != b->vlan_tag ||
+           a->vlan_etype != b->vlan_etype)
                return false;
 
        return true;
@@ -4528,15 +4710,38 @@ static int i40e_add_fdir_ethtool(struct i40e_vsi *vsi,
        input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
        input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
        input->flow_type = fsp->flow_type & ~FLOW_EXT;
-       input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
 
-       /* Reverse the src and dest notion, since the HW expects them to be from
-        * Tx perspective where as the input from user is from Rx filter view.
-        */
-       input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
-       input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
-       input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
-       input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
+       input->vlan_etype = fsp->h_ext.vlan_etype;
+       if (!fsp->m_ext.vlan_etype && fsp->h_ext.vlan_tci)
+               input->vlan_etype = cpu_to_be16(ETH_P_8021Q);
+       if (fsp->m_ext.vlan_tci && input->vlan_etype)
+               input->vlan_tag = fsp->h_ext.vlan_tci;
+       if (input->flow_type == IPV6_USER_FLOW ||
+           input->flow_type == UDP_V6_FLOW ||
+           input->flow_type == TCP_V6_FLOW ||
+           input->flow_type == SCTP_V6_FLOW) {
+               /* Reverse the src and dest notion, since the HW expects them
+                * to be from Tx perspective where as the input from user is
+                * from Rx filter view.
+                */
+               input->ipl4_proto = fsp->h_u.usr_ip6_spec.l4_proto;
+               input->dst_port = fsp->h_u.tcp_ip6_spec.psrc;
+               input->src_port = fsp->h_u.tcp_ip6_spec.pdst;
+               memcpy(input->dst_ip6, fsp->h_u.ah_ip6_spec.ip6src,
+                      sizeof(__be32) * 4);
+               memcpy(input->src_ip6, fsp->h_u.ah_ip6_spec.ip6dst,
+                      sizeof(__be32) * 4);
+       } else {
+               /* Reverse the src and dest notion, since the HW expects them
+                * to be from Tx perspective where as the input from user is
+                * from Rx filter view.
+                */
+               input->ipl4_proto = fsp->h_u.usr_ip4_spec.proto;
+               input->dst_port = fsp->h_u.tcp_ip4_spec.psrc;
+               input->src_port = fsp->h_u.tcp_ip4_spec.pdst;
+               input->dst_ip = fsp->h_u.tcp_ip4_spec.ip4src;
+               input->src_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
+       }
 
        if (userdef.flex_filter) {
                input->flex_filter = true;
@@ -5035,23 +5240,13 @@ flags_complete:
 
        if (changed_flags & I40E_FLAG_DISABLE_FW_LLDP) {
                if (new_flags & I40E_FLAG_DISABLE_FW_LLDP) {
-                       struct i40e_dcbx_config *dcbcfg;
-
+#ifdef CONFIG_I40E_DCB
+                       i40e_dcb_sw_default_config(pf);
+#endif /* CONFIG_I40E_DCB */
+                       i40e_aq_cfg_lldp_mib_change_event(&pf->hw, false, NULL);
                        i40e_aq_stop_lldp(&pf->hw, true, false, NULL);
-                       i40e_aq_set_dcb_parameters(&pf->hw, true, NULL);
-                       /* reset local_dcbx_config to default */
-                       dcbcfg = &pf->hw.local_dcbx_config;
-                       dcbcfg->etscfg.willing = 1;
-                       dcbcfg->etscfg.maxtcs = 0;
-                       dcbcfg->etscfg.tcbwtable[0] = 100;
-                       for (i = 1; i < I40E_MAX_TRAFFIC_CLASS; i++)
-                               dcbcfg->etscfg.tcbwtable[i] = 0;
-                       for (i = 0; i < I40E_MAX_USER_PRIORITY; i++)
-                               dcbcfg->etscfg.prioritytable[i] = 0;
-                       dcbcfg->etscfg.tsatable[0] = I40E_IEEE_TSA_ETS;
-                       dcbcfg->pfc.willing = 1;
-                       dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS;
                } else {
+                       i40e_set_lldp_forwarding(pf, false);
                        status = i40e_aq_start_lldp(&pf->hw, false, NULL);
                        if (status) {
                                adq_err = pf->hw.aq.asq_last_status;
@@ -5254,12 +5449,131 @@ static int i40e_get_module_eeprom(struct net_device *netdev,
 
 static int i40e_get_eee(struct net_device *netdev, struct ethtool_eee *edata)
 {
-       return -EOPNOTSUPP;
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_aq_get_phy_abilities_resp phy_cfg;
+       enum i40e_status_code status = 0;
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_hw *hw = &pf->hw;
+
+       /* Get initial PHY capabilities */
+       status = i40e_aq_get_phy_capabilities(hw, false, true, &phy_cfg, NULL);
+       if (status)
+               return -EAGAIN;
+
+       /* Check whether NIC configuration is compatible with Energy Efficient
+        * Ethernet (EEE) mode.
+        */
+       if (phy_cfg.eee_capability == 0)
+               return -EOPNOTSUPP;
+
+       edata->supported = SUPPORTED_Autoneg;
+       edata->lp_advertised = edata->supported;
+
+       /* Get current configuration */
+       status = i40e_aq_get_phy_capabilities(hw, false, false, &phy_cfg, NULL);
+       if (status)
+               return -EAGAIN;
+
+       edata->advertised = phy_cfg.eee_capability ? SUPPORTED_Autoneg : 0U;
+       edata->eee_enabled = !!edata->advertised;
+       edata->tx_lpi_enabled = pf->stats.tx_lpi_status;
+
+       edata->eee_active = pf->stats.tx_lpi_status && pf->stats.rx_lpi_status;
+
+       return 0;
+}
+
+static int i40e_is_eee_param_supported(struct net_device *netdev,
+                                      struct ethtool_eee *edata)
+{
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_ethtool_not_used {
+               u32 value;
+               const char *name;
+       } param[] = {
+               {edata->advertised & ~SUPPORTED_Autoneg, "advertise"},
+               {edata->tx_lpi_timer, "tx-timer"},
+               {edata->tx_lpi_enabled != pf->stats.tx_lpi_status, "tx-lpi"}
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(param); i++) {
+               if (param[i].value) {
+                       netdev_info(netdev,
+                                   "EEE setting %s not supported\n",
+                                   param[i].name);
+                       return -EOPNOTSUPP;
+               }
+       }
+
+       return 0;
 }
 
 static int i40e_set_eee(struct net_device *netdev, struct ethtool_eee *edata)
 {
-       return -EOPNOTSUPP;
+       struct i40e_netdev_priv *np = netdev_priv(netdev);
+       struct i40e_aq_get_phy_abilities_resp abilities;
+       enum i40e_status_code status = I40E_SUCCESS;
+       struct i40e_aq_set_phy_config config;
+       struct i40e_vsi *vsi = np->vsi;
+       struct i40e_pf *pf = vsi->back;
+       struct i40e_hw *hw = &pf->hw;
+       __le16 eee_capability;
+
+       /* Deny parameters we don't support */
+       if (i40e_is_eee_param_supported(netdev, edata))
+               return -EOPNOTSUPP;
+
+       /* Get initial PHY capabilities */
+       status = i40e_aq_get_phy_capabilities(hw, false, true, &abilities,
+                                             NULL);
+       if (status)
+               return -EAGAIN;
+
+       /* Check whether NIC configuration is compatible with Energy Efficient
+        * Ethernet (EEE) mode.
+        */
+       if (abilities.eee_capability == 0)
+               return -EOPNOTSUPP;
+
+       /* Cache initial EEE capability */
+       eee_capability = abilities.eee_capability;
+
+       /* Get current PHY configuration */
+       status = i40e_aq_get_phy_capabilities(hw, false, false, &abilities,
+                                             NULL);
+       if (status)
+               return -EAGAIN;
+
+       /* Cache current PHY configuration */
+       config.phy_type = abilities.phy_type;
+       config.phy_type_ext = abilities.phy_type_ext;
+       config.link_speed = abilities.link_speed;
+       config.abilities = abilities.abilities |
+                          I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
+       config.eeer = abilities.eeer_val;
+       config.low_power_ctrl = abilities.d3_lpan;
+       config.fec_config = abilities.fec_cfg_curr_mod_ext_info &
+                           I40E_AQ_PHY_FEC_CONFIG_MASK;
+
+       /* Set desired EEE state */
+       if (edata->eee_enabled) {
+               config.eee_capability = eee_capability;
+               config.eeer |= cpu_to_le32(I40E_PRTPM_EEER_TX_LPI_EN_MASK);
+       } else {
+               config.eee_capability = 0;
+               config.eeer &= cpu_to_le32(~I40E_PRTPM_EEER_TX_LPI_EN_MASK);
+       }
+
+       /* Apply modified PHY configuration */
+       status = i40e_aq_set_phy_config(hw, &config, NULL);
+       if (status)
+               return -EAGAIN;
+
+       return 0;
 }
 
 static const struct ethtool_ops i40e_ethtool_recovery_mode_ops = {