net/i40e: support NIC reset
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index b423f5d..f12aefa 100644 (file)
@@ -250,6 +250,7 @@ static int i40e_dev_configure(struct rte_eth_dev *dev);
 static int i40e_dev_start(struct rte_eth_dev *dev);
 static void i40e_dev_stop(struct rte_eth_dev *dev);
 static void i40e_dev_close(struct rte_eth_dev *dev);
+static int  i40e_dev_reset(struct rte_eth_dev *dev);
 static void i40e_dev_promiscuous_enable(struct rte_eth_dev *dev);
 static void i40e_dev_promiscuous_disable(struct rte_eth_dev *dev);
 static void i40e_dev_allmulticast_enable(struct rte_eth_dev *dev);
@@ -449,6 +450,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .dev_start                    = i40e_dev_start,
        .dev_stop                     = i40e_dev_stop,
        .dev_close                    = i40e_dev_close,
+       .dev_reset                    = i40e_dev_reset,
        .promiscuous_enable           = i40e_dev_promiscuous_enable,
        .promiscuous_disable          = i40e_dev_promiscuous_disable,
        .allmulticast_enable          = i40e_dev_allmulticast_enable,
@@ -1814,11 +1816,15 @@ i40e_parse_link_speeds(uint16_t link_speeds)
 static int
 i40e_phy_conf_link(struct i40e_hw *hw,
                   uint8_t abilities,
-                  uint8_t force_speed)
+                  uint8_t force_speed,
+                  bool is_up)
 {
        enum i40e_status_code status;
        struct i40e_aq_get_phy_abilities_resp phy_ab;
        struct i40e_aq_set_phy_config phy_conf;
+       enum i40e_aq_phy_type cnt;
+       uint32_t phy_type_mask = 0;
+
        const uint8_t mask = I40E_AQ_PHY_FLAG_PAUSE_TX |
                        I40E_AQ_PHY_FLAG_PAUSE_RX |
                        I40E_AQ_PHY_FLAG_PAUSE_RX |
@@ -1836,6 +1842,10 @@ i40e_phy_conf_link(struct i40e_hw *hw,
        if (status)
                return ret;
 
+       /* If link already up, no need to set up again */
+       if (is_up && phy_ab.phy_type != 0)
+               return I40E_SUCCESS;
+
        memset(&phy_conf, 0, sizeof(phy_conf));
 
        /* bits 0-2 use the values from get_phy_abilities_resp */
@@ -1846,13 +1856,21 @@ i40e_phy_conf_link(struct i40e_hw *hw,
        if (abilities & I40E_AQ_PHY_AN_ENABLED)
                phy_conf.link_speed = advt;
        else
-               phy_conf.link_speed = force_speed;
+               phy_conf.link_speed = is_up ? force_speed : phy_ab.link_speed;
 
        phy_conf.abilities = abilities;
 
+
+
+       /* To enable link, phy_type mask needs to include each type */
+       for (cnt = I40E_PHY_TYPE_SGMII; cnt < I40E_PHY_TYPE_MAX; cnt++)
+               phy_type_mask |= 1 << cnt;
+
        /* use get_phy_abilities_resp value for the rest */
-       phy_conf.phy_type = phy_ab.phy_type;
-       phy_conf.phy_type_ext = phy_ab.phy_type_ext;
+       phy_conf.phy_type = is_up ? cpu_to_le32(phy_type_mask) : 0;
+       phy_conf.phy_type_ext = is_up ? (I40E_AQ_PHY_TYPE_EXT_25G_KR |
+               I40E_AQ_PHY_TYPE_EXT_25G_CR | I40E_AQ_PHY_TYPE_EXT_25G_SR |
+               I40E_AQ_PHY_TYPE_EXT_25G_LR) : 0;
        phy_conf.fec_config = phy_ab.fec_cfg_curr_mod_ext_info;
        phy_conf.eee_capability = phy_ab.eee_capability;
        phy_conf.eeer = phy_ab.eeer_val;
@@ -1884,13 +1902,7 @@ i40e_apply_link_speed(struct rte_eth_dev *dev)
                abilities |= I40E_AQ_PHY_AN_ENABLED;
        abilities |= I40E_AQ_PHY_LINK_ENABLED;
 
-       /* Skip changing speed on 40G interfaces, FW does not support */
-       if (I40E_PHY_TYPE_SUPPORT_40G(hw->phy.phy_types)) {
-               speed =  I40E_LINK_SPEED_UNKNOWN;
-               abilities |= I40E_AQ_PHY_AN_ENABLED;
-       }
-
-       return i40e_phy_conf_link(hw, abilities, speed);
+       return i40e_phy_conf_link(hw, abilities, speed, true);
 }
 
 static int
@@ -2016,7 +2028,7 @@ i40e_dev_start(struct rte_eth_dev *dev)
                if (dev->data->dev_conf.intr_conf.lsc != 0)
                        PMD_INIT_LOG(INFO,
                                "lsc won't enable because of no intr multiplex");
-       } else if (dev->data->dev_conf.intr_conf.lsc != 0) {
+       } else {
                ret = i40e_aq_set_phy_int_mask(hw,
                                               ~(I40E_AQ_EVENT_LINK_UPDOWN |
                                               I40E_AQ_EVENT_MODULE_QUAL_FAIL |
@@ -2024,7 +2036,7 @@ i40e_dev_start(struct rte_eth_dev *dev)
                if (ret != I40E_SUCCESS)
                        PMD_DRV_LOG(WARNING, "Fail to set phy mask");
 
-               /* Call get_link_info aq commond to enable LSE */
+               /* Call get_link_info aq commond to enable/disable LSE */
                i40e_dev_link_update(dev, 0);
        }
 
@@ -2033,6 +2045,11 @@ i40e_dev_start(struct rte_eth_dev *dev)
 
        i40e_filter_restore(pf);
 
+       if (pf->tm_conf.root && !pf->tm_conf.committed)
+               PMD_DRV_LOG(WARNING,
+                           "please call hierarchy_commit() "
+                           "before starting the port");
+
        return I40E_SUCCESS;
 
 err_up:
@@ -2046,12 +2063,15 @@ static void
 i40e_dev_stop(struct rte_eth_dev *dev)
 {
        struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
        struct i40e_vsi *main_vsi = pf->main_vsi;
        struct i40e_mirror_rule *p_mirror;
        struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
        struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
        int i;
 
+       if (hw->adapter_stopped == 1)
+               return;
        /* Disable all queues */
        i40e_dev_switch_queues(pf, FALSE);
 
@@ -2093,6 +2113,11 @@ i40e_dev_stop(struct rte_eth_dev *dev)
                rte_free(intr_handle->intr_vec);
                intr_handle->intr_vec = NULL;
        }
+
+       /* reset hierarchy commit */
+       pf->tm_conf.committed = false;
+
+       hw->adapter_stopped = 1;
 }
 
 static void
@@ -2108,7 +2133,6 @@ i40e_dev_close(struct rte_eth_dev *dev)
        PMD_INIT_FUNC_TRACE();
 
        i40e_dev_stop(dev);
-       hw->adapter_stopped = 1;
        i40e_dev_free_queues(dev);
 
        /* Disable interrupt */
@@ -2143,6 +2167,32 @@ i40e_dev_close(struct rte_eth_dev *dev)
        I40E_WRITE_FLUSH(hw);
 }
 
+/*
+ * Reset PF device only to re-initialize resources in PMD layer
+ */
+static int
+i40e_dev_reset(struct rte_eth_dev *dev)
+{
+       int ret;
+
+       /* When a DPDK PMD PF begin to reset PF port, it should notify all
+        * its VF to make them align with it. The detailed notification
+        * mechanism is PMD specific. As to i40e PF, it is rather complex.
+        * To avoid unexpected behavior in VF, currently reset of PF with
+        * SR-IOV activation is not supported. It might be supported later.
+        */
+       if (dev->data->sriov.active)
+               return -ENOTSUP;
+
+       ret = eth_i40e_dev_uninit(dev);
+       if (ret)
+               return ret;
+
+       ret = eth_i40e_dev_init(dev);
+
+       return ret;
+}
+
 static void
 i40e_dev_promiscuous_enable(struct rte_eth_dev *dev)
 {
@@ -2233,7 +2283,7 @@ i40e_dev_set_link_down(struct rte_eth_dev *dev)
        struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
 
        abilities = I40E_AQ_PHY_ENABLE_ATOMIC_LINK;
-       return i40e_phy_conf_link(hw, abilities, speed);
+       return i40e_phy_conf_link(hw, abilities, speed, false);
 }
 
 int
@@ -2360,9 +2410,6 @@ i40e_update_vsi_stats(struct i40e_vsi *vsi)
        i40e_stat_update_48(hw, I40E_GLV_BPTCH(idx), I40E_GLV_BPTCL(idx),
                            vsi->offset_loaded,  &oes->tx_broadcast,
                            &nes->tx_broadcast);
-       /* exclude CRC bytes */
-       nes->tx_bytes -= (nes->tx_unicast + nes->tx_multicast +
-               nes->tx_broadcast) * ETHER_CRC_LEN;
        /* GLV_TDPC not supported */
        i40e_stat_update_32(hw, I40E_GLV_TEPC(idx), vsi->offset_loaded,
                            &oes->tx_errors, &nes->tx_errors);
@@ -9218,8 +9265,9 @@ i40e_pctype_to_flowtype(enum i40e_filter_pctype pctype)
  */
 
 /* For both X710 and XL710 */
-#define I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE 0x10000200
-#define I40E_GL_SWR_PRI_JOIN_MAP_0       0x26CE00
+#define I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE_1     0x10000200
+#define I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE_2     0x20000200
+#define I40E_GL_SWR_PRI_JOIN_MAP_0             0x26CE00
 
 #define I40E_GL_SWR_PRI_JOIN_MAP_2_VALUE 0x011f0200
 #define I40E_GL_SWR_PRI_JOIN_MAP_2       0x26CE08
@@ -9240,16 +9288,22 @@ i40e_dev_sync_phy_type(struct i40e_hw *hw)
        enum i40e_status_code status;
        struct i40e_aq_get_phy_abilities_resp phy_ab;
        int ret = -ENOTSUP;
+       int retries = 0;
 
        status = i40e_aq_get_phy_capabilities(hw, false, true, &phy_ab,
                                              NULL);
 
-       if (status) {
+       while (status) {
                PMD_INIT_LOG(WARNING, "Failed to sync phy type: status=%d",
                        status);
-               return ret;
+               retries++;
+               rte_delay_us(100000);
+               if  (retries < 5)
+                       status = i40e_aq_get_phy_capabilities(hw, false,
+                                       true, &phy_ab, NULL);
+               else
+                       return ret;
        }
-
        return 0;
 }
 
@@ -9274,8 +9328,12 @@ i40e_configure_registers(struct i40e_hw *hw)
                                reg_table[i].val =
                                        I40E_X722_GL_SWR_PRI_JOIN_MAP_0_VALUE;
                        else /* For X710/XL710/XXV710 */
-                               reg_table[i].val =
-                                       I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE;
+                               if (hw->aq.fw_maj_ver < 6)
+                                       reg_table[i].val =
+                                            I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE_1;
+                               else
+                                       reg_table[i].val =
+                                            I40E_GL_SWR_PRI_JOIN_MAP_0_VALUE_2;
                }
 
                if (reg_table[i].addr == I40E_GL_SWR_PRI_JOIN_MAP_2) {