i40e: support flow control
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index da6c0b5..497fee7 100644 (file)
@@ -55,6 +55,7 @@
 #include "base/i40e_prototype.h"
 #include "base/i40e_adminq_cmd.h"
 #include "base/i40e_type.h"
+#include "base/i40e_register.h"
 #include "i40e_ethdev.h"
 #include "i40e_rxtx.h"
 #include "i40e_pf.h"
 
 #define I40E_PRE_TX_Q_CFG_WAIT_US       10 /* 10 us */
 
+/* Flow control default timer */
+#define I40E_DEFAULT_PAUSE_TIME 0xFFFFU
+
+/* Flow control default high water */
+#define I40E_DEFAULT_HIGH_WATER (0x1C40/1024)
+
+/* Flow control default low water */
+#define I40E_DEFAULT_LOW_WATER  (0x1A40/1024)
+
+/* Flow control enable fwd bit */
+#define I40E_PRTMAC_FWD_CTRL   0x00000001
+
+/* Receive Packet Buffer size */
+#define I40E_RXPBSIZE (968 * 1024)
+
+/* Kilobytes shift */
+#define I40E_KILOSHIFT 10
+
+/* Receive Average Packet Size in Byte*/
+#define I40E_PACKET_AVERAGE_SIZE 128
+
 /* Mask of PF interrupt causes */
 #define I40E_PFINT_ICR0_ENA_MASK ( \
                I40E_PFINT_ICR0_ENA_ECC_ERR_MASK | \
        (1UL << RTE_ETH_FLOW_NONFRAG_IPV6_OTHER) | \
        (1UL << RTE_ETH_FLOW_L2_PAYLOAD))
 
+#define I40E_PTP_40GB_INCVAL  0x0199999999ULL
+#define I40E_PTP_10GB_INCVAL  0x0333333333ULL
+#define I40E_PTP_1GB_INCVAL   0x2000000000ULL
+#define I40E_PRTTSYN_TSYNENA  0x80000000
+#define I40E_PRTTSYN_TSYNTYPE 0x0e000000
+
 static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
+static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
 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);
@@ -137,6 +166,8 @@ static void i40e_vlan_strip_queue_set(struct rte_eth_dev *dev,
 static int i40e_vlan_pvid_set(struct rte_eth_dev *dev, uint16_t pvid, int on);
 static int i40e_dev_led_on(struct rte_eth_dev *dev);
 static int i40e_dev_led_off(struct rte_eth_dev *dev);
+static int i40e_flow_ctrl_get(struct rte_eth_dev *dev,
+                             struct rte_eth_fc_conf *fc_conf);
 static int i40e_flow_ctrl_set(struct rte_eth_dev *dev,
                              struct rte_eth_fc_conf *fc_conf);
 static int i40e_priority_flow_ctrl_set(struct rte_eth_dev *dev,
@@ -211,6 +242,19 @@ static int i40e_dev_filter_ctrl(struct rte_eth_dev *dev,
                                void *arg);
 static void i40e_configure_registers(struct i40e_hw *hw);
 static void i40e_hw_init(struct i40e_hw *hw);
+static int i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi);
+static int i40e_mirror_rule_set(struct rte_eth_dev *dev,
+                       struct rte_eth_mirror_conf *mirror_conf,
+                       uint8_t sw_id, uint8_t on);
+static int i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id);
+
+static int i40e_timesync_enable(struct rte_eth_dev *dev);
+static int i40e_timesync_disable(struct rte_eth_dev *dev);
+static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+                                          struct timespec *timestamp,
+                                          uint32_t flags);
+static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+                                          struct timespec *timestamp);
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
@@ -251,6 +295,7 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .tx_queue_release             = i40e_dev_tx_queue_release,
        .dev_led_on                   = i40e_dev_led_on,
        .dev_led_off                  = i40e_dev_led_off,
+       .flow_ctrl_get                = i40e_flow_ctrl_get,
        .flow_ctrl_set                = i40e_flow_ctrl_set,
        .priority_flow_ctrl_set       = i40e_priority_flow_ctrl_set,
        .mac_addr_add                 = i40e_macaddr_add,
@@ -262,26 +307,26 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .udp_tunnel_add               = i40e_dev_udp_tunnel_add,
        .udp_tunnel_del               = i40e_dev_udp_tunnel_del,
        .filter_ctrl                  = i40e_dev_filter_ctrl,
+       .mirror_rule_set              = i40e_mirror_rule_set,
+       .mirror_rule_reset            = i40e_mirror_rule_reset,
+       .timesync_enable              = i40e_timesync_enable,
+       .timesync_disable             = i40e_timesync_disable,
+       .timesync_read_rx_timestamp   = i40e_timesync_read_rx_timestamp,
+       .timesync_read_tx_timestamp   = i40e_timesync_read_tx_timestamp,
 };
 
 static struct eth_driver rte_i40e_pmd = {
-       {
+       .pci_drv = {
                .name = "rte_i40e_pmd",
                .id_table = pci_id_i40e_map,
-               .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC,
+               .drv_flags = RTE_PCI_DRV_NEED_MAPPING | RTE_PCI_DRV_INTR_LSC |
+                       RTE_PCI_DRV_DETACHABLE,
        },
        .eth_dev_init = eth_i40e_dev_init,
+       .eth_dev_uninit = eth_i40e_dev_uninit,
        .dev_private_size = sizeof(struct i40e_adapter),
 };
 
-static inline int
-i40e_align_floor(int n)
-{
-       if (n == 0)
-               return 0;
-       return (1 << (sizeof(n) * CHAR_BIT - 1 - __builtin_clz(n)));
-}
-
 static inline int
 rte_i40e_dev_atomic_read_link_status(struct rte_eth_dev *dev,
                                     struct rte_eth_link *link)
@@ -361,6 +406,30 @@ static inline void i40e_flex_payload_reg_init(struct i40e_hw *hw)
        I40E_WRITE_REG(hw, I40E_GLQF_PIT(17), 0x00007440);
 }
 
+#define I40E_FLOW_CONTROL_ETHERTYPE  0x8808
+
+/*
+ * Add a ethertype filter to drop all flow control frames transmitted
+ * from VSIs.
+*/
+static void
+i40e_add_tx_flow_control_drop_filter(struct i40e_pf *pf)
+{
+       struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+       uint16_t flags = I40E_AQC_ADD_CONTROL_PACKET_FLAGS_IGNORE_MAC |
+                       I40E_AQC_ADD_CONTROL_PACKET_FLAGS_DROP |
+                       I40E_AQC_ADD_CONTROL_PACKET_FLAGS_TX;
+       int ret;
+
+       ret = i40e_aq_add_rem_control_packet_filter(hw, NULL,
+                               I40E_FLOW_CONTROL_ETHERTYPE, flags,
+                               pf->main_vsi_seid, 0,
+                               TRUE, NULL, NULL);
+       if (ret)
+               PMD_INIT_LOG(ERR, "Failed to add filter to drop flow control "
+                                 " frames from VSIs.");
+}
+
 static int
 eth_i40e_dev_init(struct rte_eth_dev *dev)
 {
@@ -405,6 +474,7 @@ eth_i40e_dev_init(struct rte_eth_dev *dev)
        hw->subsystem_device_id = pci_dev->id.subsystem_device_id;
        hw->bus.device = pci_dev->addr.devid;
        hw->bus.func = pci_dev->addr.function;
+       hw->adapter_stopped = 0;
 
        /* Make sure all is clean before doing PF reset */
        i40e_clear_hw(hw);
@@ -562,6 +632,15 @@ eth_i40e_dev_init(struct rte_eth_dev *dev)
 
        /* enable uio intr after callback register */
        rte_intr_enable(&(pci_dev->intr_handle));
+       /*
+        * Add an ethertype filter to drop all flow control frames transmitted
+        * from VSIs. By doing so, we stop VF from sending out PAUSE or PFC
+        * frames to wire.
+        */
+       i40e_add_tx_flow_control_drop_filter(pf);
+
+       /* initialize mirror rule list */
+       TAILQ_INIT(&pf->mirror_list);
 
        return 0;
 
@@ -583,6 +662,65 @@ err_get_capabilities:
        return ret;
 }
 
+static int
+eth_i40e_dev_uninit(struct rte_eth_dev *dev)
+{
+       struct rte_pci_device *pci_dev;
+       struct i40e_hw *hw;
+       struct i40e_filter_control_settings settings;
+       int ret;
+       uint8_t aq_fail = 0;
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (rte_eal_process_type() != RTE_PROC_PRIMARY)
+               return 0;
+
+       hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       pci_dev = dev->pci_dev;
+
+       if (hw->adapter_stopped == 0)
+               i40e_dev_close(dev);
+
+       dev->dev_ops = NULL;
+       dev->rx_pkt_burst = NULL;
+       dev->tx_pkt_burst = NULL;
+
+       /* Disable LLDP */
+       ret = i40e_aq_stop_lldp(hw, true, NULL);
+       if (ret != I40E_SUCCESS) /* Its failure can be ignored */
+               PMD_INIT_LOG(INFO, "Failed to stop lldp");
+
+       /* Clear PXE mode */
+       i40e_clear_pxe_mode(hw);
+
+       /* Unconfigure filter control */
+       memset(&settings, 0, sizeof(settings));
+       ret = i40e_set_filter_control(hw, &settings);
+       if (ret)
+               PMD_INIT_LOG(WARNING, "setup_pf_filter_control failed: %d",
+                                       ret);
+
+       /* Disable flow control */
+       hw->fc.requested_mode = I40E_FC_NONE;
+       i40e_set_fc(hw, &aq_fail, TRUE);
+
+       /* uninitialize pf host driver */
+       i40e_pf_host_uninit(dev);
+
+       rte_free(dev->data->mac_addrs);
+       dev->data->mac_addrs = NULL;
+
+       /* disable uio intr before callback unregister */
+       rte_intr_disable(&(pci_dev->intr_handle));
+
+       /* register callback func to eal lib */
+       rte_intr_callback_unregister(&(pci_dev->intr_handle),
+               i40e_dev_interrupt_handler, (void *)dev);
+
+       return 0;
+}
+
 static int
 i40e_dev_configure(struct rte_eth_dev *dev)
 {
@@ -858,6 +996,8 @@ i40e_dev_start(struct rte_eth_dev *dev)
        struct i40e_vsi *main_vsi = pf->main_vsi;
        int ret, i;
 
+       hw->adapter_stopped = 0;
+
        if ((dev->data->dev_conf.link_duplex != ETH_LINK_AUTONEG_DUPLEX) &&
                (dev->data->dev_conf.link_duplex != ETH_LINK_FULL_DUPLEX)) {
                PMD_INIT_LOG(ERR, "Invalid link_duplex (%hu) for port %hhu",
@@ -929,6 +1069,7 @@ i40e_dev_stop(struct rte_eth_dev *dev)
 {
        struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
        struct i40e_vsi *main_vsi = pf->main_vsi;
+       struct i40e_mirror_rule *p_mirror;
        int i;
 
        /* Disable all queues */
@@ -953,6 +1094,13 @@ i40e_dev_stop(struct rte_eth_dev *dev)
        /* Set link down */
        i40e_dev_set_link_down(dev);
 
+       /* Remove all mirror rules */
+       while ((p_mirror = TAILQ_FIRST(&pf->mirror_list))) {
+               TAILQ_REMOVE(&pf->mirror_list, p_mirror, rules);
+               rte_free(p_mirror);
+       }
+       pf->nb_mirror_rule = 0;
+
 }
 
 static void
@@ -961,10 +1109,13 @@ i40e_dev_close(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);
        uint32_t reg;
+       int i;
 
        PMD_INIT_FUNC_TRACE();
 
        i40e_dev_stop(dev);
+       hw->adapter_stopped = 1;
+       i40e_dev_free_queues(dev);
 
        /* Disable interrupt */
        i40e_pf_disable_irq0(hw);
@@ -977,6 +1128,14 @@ i40e_dev_close(struct rte_eth_dev *dev)
        i40e_fdir_teardown(pf);
        i40e_vsi_release(pf->main_vsi);
 
+       for (i = 0; i < pf->nb_cfg_vmdq_vsi; i++) {
+               i40e_vsi_release(pf->vmdq[i].vsi);
+               pf->vmdq[i].vsi = NULL;
+       }
+
+       rte_free(pf->vmdq);
+       pf->vmdq = NULL;
+
        /* shutdown the adminq */
        i40e_aq_queue_shutdown(hw, true);
        i40e_shutdown_adminq(hw);
@@ -1529,17 +1688,21 @@ i40e_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
        dev_info->max_vfs = dev->pci_dev->max_vfs;
        dev_info->rx_offload_capa =
                DEV_RX_OFFLOAD_VLAN_STRIP |
+               DEV_RX_OFFLOAD_QINQ_STRIP |
                DEV_RX_OFFLOAD_IPV4_CKSUM |
                DEV_RX_OFFLOAD_UDP_CKSUM |
                DEV_RX_OFFLOAD_TCP_CKSUM;
        dev_info->tx_offload_capa =
                DEV_TX_OFFLOAD_VLAN_INSERT |
+               DEV_TX_OFFLOAD_QINQ_INSERT |
                DEV_TX_OFFLOAD_IPV4_CKSUM |
                DEV_TX_OFFLOAD_UDP_CKSUM |
                DEV_TX_OFFLOAD_TCP_CKSUM |
                DEV_TX_OFFLOAD_SCTP_CKSUM |
                DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM |
                DEV_TX_OFFLOAD_TCP_TSO;
+       dev_info->hash_key_size = (I40E_PFQF_HKEY_MAX_INDEX + 1) *
+                                               sizeof(uint32_t);
        dev_info->reta_size = pf->hash_lut_size;
        dev_info->flow_type_rss_offloads = I40E_RSS_OFFLOAD_ALL;
 
@@ -1673,12 +1836,148 @@ i40e_dev_led_off(struct rte_eth_dev *dev)
 }
 
 static int
-i40e_flow_ctrl_set(__rte_unused struct rte_eth_dev *dev,
-                  __rte_unused struct rte_eth_fc_conf *fc_conf)
+i40e_flow_ctrl_get(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
+{
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+
+       fc_conf->pause_time = pf->fc_conf.pause_time;
+       fc_conf->high_water =  pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS];
+       fc_conf->low_water = pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS];
+
+        /* Return current mode according to actual setting*/
+       switch (hw->fc.current_mode) {
+       case I40E_FC_FULL:
+               fc_conf->mode = RTE_FC_FULL;
+               break;
+       case I40E_FC_TX_PAUSE:
+               fc_conf->mode = RTE_FC_TX_PAUSE;
+               break;
+       case I40E_FC_RX_PAUSE:
+               fc_conf->mode = RTE_FC_RX_PAUSE;
+               break;
+       case I40E_FC_NONE:
+       default:
+               fc_conf->mode = RTE_FC_NONE;
+       };
+
+       return 0;
+}
+
+static int
+i40e_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
 {
+       uint32_t mflcn_reg, fctrl_reg, reg;
+       uint32_t max_high_water;
+       uint8_t i, aq_failure;
+       int err;
+       struct i40e_hw *hw;
+       struct i40e_pf *pf;
+       enum i40e_fc_mode rte_fcmode_2_i40e_fcmode[] = {
+               [RTE_FC_NONE] = I40E_FC_NONE,
+               [RTE_FC_RX_PAUSE] = I40E_FC_RX_PAUSE,
+               [RTE_FC_TX_PAUSE] = I40E_FC_TX_PAUSE,
+               [RTE_FC_FULL] = I40E_FC_FULL
+       };
+
+       /* high_water field in the rte_eth_fc_conf using the kilobytes unit */
+
+       max_high_water = I40E_RXPBSIZE >> I40E_KILOSHIFT;
+       if ((fc_conf->high_water > max_high_water) ||
+                       (fc_conf->high_water < fc_conf->low_water)) {
+               PMD_INIT_LOG(ERR, "Invalid high/low water setup value in KB, "
+                       "High_water must <= %d.", max_high_water);
+               return -EINVAL;
+       }
+
+       hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
+       hw->fc.requested_mode = rte_fcmode_2_i40e_fcmode[fc_conf->mode];
+
+       pf->fc_conf.pause_time = fc_conf->pause_time;
+       pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS] = fc_conf->high_water;
+       pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS] = fc_conf->low_water;
+
        PMD_INIT_FUNC_TRACE();
 
-       return -ENOSYS;
+       /* All the link flow control related enable/disable register
+        * configuration is handle by the F/W
+        */
+       err = i40e_set_fc(hw, &aq_failure, true);
+       if (err < 0)
+               return -ENOSYS;
+
+       if (i40e_is_40G_device(hw->device_id)) {
+               /* Configure flow control refresh threshold,
+                * the value for stat_tx_pause_refresh_timer[8]
+                * is used for global pause operation.
+                */
+
+               I40E_WRITE_REG(hw,
+                              I40E_PRTMAC_HSEC_CTL_TX_PAUSE_REFRESH_TIMER(8),
+                              pf->fc_conf.pause_time);
+
+               /* configure the timer value included in transmitted pause
+                * frame,
+                * the value for stat_tx_pause_quanta[8] is used for global
+                * pause operation
+                */
+               I40E_WRITE_REG(hw, I40E_PRTMAC_HSEC_CTL_TX_PAUSE_QUANTA(8),
+                              pf->fc_conf.pause_time);
+
+               fctrl_reg = I40E_READ_REG(hw,
+                                         I40E_PRTMAC_HSEC_CTL_RX_FORWARD_CONTROL);
+
+               if (fc_conf->mac_ctrl_frame_fwd != 0)
+                       fctrl_reg |= I40E_PRTMAC_FWD_CTRL;
+               else
+                       fctrl_reg &= ~I40E_PRTMAC_FWD_CTRL;
+
+               I40E_WRITE_REG(hw, I40E_PRTMAC_HSEC_CTL_RX_FORWARD_CONTROL,
+                              fctrl_reg);
+       } else {
+               /* Configure pause time (2 TCs per register) */
+               reg = (uint32_t)pf->fc_conf.pause_time * (uint32_t)0x00010001;
+               for (i = 0; i < I40E_MAX_TRAFFIC_CLASS / 2; i++)
+                       I40E_WRITE_REG(hw, I40E_PRTDCB_FCTTVN(i), reg);
+
+               /* Configure flow control refresh threshold value */
+               I40E_WRITE_REG(hw, I40E_PRTDCB_FCRTV,
+                              pf->fc_conf.pause_time / 2);
+
+               mflcn_reg = I40E_READ_REG(hw, I40E_PRTDCB_MFLCN);
+
+               /* set or clear MFLCN.PMCF & MFLCN.DPF bits
+                *depending on configuration
+                */
+               if (fc_conf->mac_ctrl_frame_fwd != 0) {
+                       mflcn_reg |= I40E_PRTDCB_MFLCN_PMCF_MASK;
+                       mflcn_reg &= ~I40E_PRTDCB_MFLCN_DPF_MASK;
+               } else {
+                       mflcn_reg &= ~I40E_PRTDCB_MFLCN_PMCF_MASK;
+                       mflcn_reg |= I40E_PRTDCB_MFLCN_DPF_MASK;
+               }
+
+               I40E_WRITE_REG(hw, I40E_PRTDCB_MFLCN, mflcn_reg);
+       }
+
+       /* config the water marker both based on the packets and bytes */
+       I40E_WRITE_REG(hw, I40E_GLRPB_PHW,
+                      (pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS]
+                      << I40E_KILOSHIFT) / I40E_PACKET_AVERAGE_SIZE);
+       I40E_WRITE_REG(hw, I40E_GLRPB_PLW,
+                      (pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS]
+                      << I40E_KILOSHIFT) / I40E_PACKET_AVERAGE_SIZE);
+       I40E_WRITE_REG(hw, I40E_GLRPB_GHW,
+                      pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS]
+                      << I40E_KILOSHIFT);
+       I40E_WRITE_REG(hw, I40E_GLRPB_GLW,
+                      pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS]
+                      << I40E_KILOSHIFT);
+
+       I40E_WRITE_FLUSH(hw);
+
+       return 0;
 }
 
 static int
@@ -1990,10 +2289,11 @@ i40e_allocate_dma_mem_d(__attribute__((unused)) struct i40e_hw *hw,
        id++;
        snprintf(z_name, sizeof(z_name), "i40e_dma_%"PRIu64, id);
 #ifdef RTE_LIBRTE_XEN_DOM0
-       mz = rte_memzone_reserve_bounded(z_name, size, 0, 0, alignment,
-                                                       RTE_PGSIZE_2M);
+       mz = rte_memzone_reserve_bounded(z_name, size, SOCKET_ID_ANY, 0,
+                                        alignment, RTE_PGSIZE_2M);
 #else
-       mz = rte_memzone_reserve_aligned(z_name, size, 0, 0, alignment);
+       mz = rte_memzone_reserve_aligned(z_name, size, SOCKET_ID_ANY, 0,
+                                        alignment);
 #endif
        if (!mz)
                return I40E_ERR_NO_MEMORY;
@@ -2137,6 +2437,10 @@ i40e_pf_parameter_init(struct rte_eth_dev *dev)
                PMD_INIT_LOG(ERR, "HW configuration doesn't support SRIOV");
                return -EINVAL;
        }
+       /* Add the parameter init for LFC */
+       pf->fc_conf.pause_time = I40E_DEFAULT_PAUSE_TIME;
+       pf->fc_conf.high_water[I40E_MAX_TRAFFIC_CLASS] = I40E_DEFAULT_HIGH_WATER;
+       pf->fc_conf.low_water[I40E_MAX_TRAFFIC_CLASS] = I40E_DEFAULT_LOW_WATER;
 
        pf->flags = I40E_FLAG_HEADER_SPLIT_DISABLED;
        pf->max_num_vsi = RTE_MIN(hw->func_caps.num_vsis, I40E_MAX_NUM_VSIS);
@@ -3056,6 +3360,7 @@ i40e_vsi_setup(struct i40e_pf *pf,
                 * macvlan filter which is expected and cannot be removed.
                 */
                i40e_update_default_filter_setting(vsi);
+               i40e_config_qinq(hw, vsi);
        } else if (type == I40E_VSI_SRIOV) {
                memset(&ctxt, 0, sizeof(ctxt));
                /**
@@ -3096,6 +3401,8 @@ i40e_vsi_setup(struct i40e_pf *pf,
                 * Since VSI is not created yet, only configure parameter,
                 * will add vsi below.
                 */
+
+               i40e_config_qinq(hw, vsi);
        } else if (type == I40E_VSI_VMDQ2) {
                memset(&ctxt, 0, sizeof(ctxt));
                /*
@@ -5697,3 +6004,491 @@ i40e_configure_registers(struct i40e_hw *hw)
                        "0x%"PRIx32, reg_table[i].val, reg_table[i].addr);
        }
 }
+
+#define I40E_VSI_TSR(_i)            (0x00050800 + ((_i) * 4))
+#define I40E_VSI_TSR_QINQ_CONFIG    0xc030
+#define I40E_VSI_L2TAGSTXVALID(_i)  (0x00042800 + ((_i) * 4))
+#define I40E_VSI_L2TAGSTXVALID_QINQ 0xab
+static int
+i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi)
+{
+       uint32_t reg;
+       int ret;
+
+       if (vsi->vsi_id >= I40E_MAX_NUM_VSIS) {
+               PMD_DRV_LOG(ERR, "VSI ID exceeds the maximum");
+               return -EINVAL;
+       }
+
+       /* Configure for double VLAN RX stripping */
+       reg = I40E_READ_REG(hw, I40E_VSI_TSR(vsi->vsi_id));
+       if ((reg & I40E_VSI_TSR_QINQ_CONFIG) != I40E_VSI_TSR_QINQ_CONFIG) {
+               reg |= I40E_VSI_TSR_QINQ_CONFIG;
+               ret = i40e_aq_debug_write_register(hw,
+                                                  I40E_VSI_TSR(vsi->vsi_id),
+                                                  reg, NULL);
+               if (ret < 0) {
+                       PMD_DRV_LOG(ERR, "Failed to update VSI_TSR[%d]",
+                                   vsi->vsi_id);
+                       return I40E_ERR_CONFIG;
+               }
+       }
+
+       /* Configure for double VLAN TX insertion */
+       reg = I40E_READ_REG(hw, I40E_VSI_L2TAGSTXVALID(vsi->vsi_id));
+       if ((reg & 0xff) != I40E_VSI_L2TAGSTXVALID_QINQ) {
+               reg = I40E_VSI_L2TAGSTXVALID_QINQ;
+               ret = i40e_aq_debug_write_register(hw,
+                                                  I40E_VSI_L2TAGSTXVALID(
+                                                  vsi->vsi_id), reg, NULL);
+               if (ret < 0) {
+                       PMD_DRV_LOG(ERR, "Failed to update "
+                               "VSI_L2TAGSTXVALID[%d]", vsi->vsi_id);
+                       return I40E_ERR_CONFIG;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * i40e_aq_add_mirror_rule
+ * @hw: pointer to the hardware structure
+ * @seid: VEB seid to add mirror rule to
+ * @dst_id: destination vsi seid
+ * @entries: Buffer which contains the entities to be mirrored
+ * @count: number of entities contained in the buffer
+ * @rule_id:the rule_id of the rule to be added
+ *
+ * Add a mirror rule for a given veb.
+ *
+ **/
+static enum i40e_status_code
+i40e_aq_add_mirror_rule(struct i40e_hw *hw,
+                       uint16_t seid, uint16_t dst_id,
+                       uint16_t rule_type, uint16_t *entries,
+                       uint16_t count, uint16_t *rule_id)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_add_delete_mirror_rule cmd;
+       struct i40e_aqc_add_delete_mirror_rule_completion *resp =
+               (struct i40e_aqc_add_delete_mirror_rule_completion *)
+               &desc.params.raw;
+       uint16_t buff_len;
+       enum i40e_status_code status;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_add_mirror_rule);
+       memset(&cmd, 0, sizeof(cmd));
+
+       buff_len = sizeof(uint16_t) * count;
+       desc.datalen = rte_cpu_to_le_16(buff_len);
+       if (buff_len > 0)
+               desc.flags |= rte_cpu_to_le_16(
+                       (uint16_t)(I40E_AQ_FLAG_BUF | I40E_AQ_FLAG_RD));
+       cmd.rule_type = rte_cpu_to_le_16(rule_type <<
+                               I40E_AQC_MIRROR_RULE_TYPE_SHIFT);
+       cmd.num_entries = rte_cpu_to_le_16(count);
+       cmd.seid = rte_cpu_to_le_16(seid);
+       cmd.destination = rte_cpu_to_le_16(dst_id);
+
+       rte_memcpy(&desc.params.raw, &cmd, sizeof(cmd));
+       status = i40e_asq_send_command(hw, &desc, entries, buff_len, NULL);
+       PMD_DRV_LOG(INFO, "i40e_aq_add_mirror_rule, aq_status %d,"
+                        "rule_id = %u"
+                        " mirror_rules_used = %u, mirror_rules_free = %u,",
+                        hw->aq.asq_last_status, resp->rule_id,
+                        resp->mirror_rules_used, resp->mirror_rules_free);
+       *rule_id = rte_le_to_cpu_16(resp->rule_id);
+
+       return status;
+}
+
+/**
+ * i40e_aq_del_mirror_rule
+ * @hw: pointer to the hardware structure
+ * @seid: VEB seid to add mirror rule to
+ * @entries: Buffer which contains the entities to be mirrored
+ * @count: number of entities contained in the buffer
+ * @rule_id:the rule_id of the rule to be delete
+ *
+ * Delete a mirror rule for a given veb.
+ *
+ **/
+static enum i40e_status_code
+i40e_aq_del_mirror_rule(struct i40e_hw *hw,
+               uint16_t seid, uint16_t rule_type, uint16_t *entries,
+               uint16_t count, uint16_t rule_id)
+{
+       struct i40e_aq_desc desc;
+       struct i40e_aqc_add_delete_mirror_rule cmd;
+       uint16_t buff_len = 0;
+       enum i40e_status_code status;
+       void *buff = NULL;
+
+       i40e_fill_default_direct_cmd_desc(&desc,
+                                         i40e_aqc_opc_delete_mirror_rule);
+       memset(&cmd, 0, sizeof(cmd));
+       if (rule_type == I40E_AQC_MIRROR_RULE_TYPE_VLAN) {
+               desc.flags |= rte_cpu_to_le_16((uint16_t)(I40E_AQ_FLAG_BUF |
+                                                         I40E_AQ_FLAG_RD));
+               cmd.num_entries = count;
+               buff_len = sizeof(uint16_t) * count;
+               desc.datalen = rte_cpu_to_le_16(buff_len);
+               buff = (void *)entries;
+       } else
+               /* rule id is filled in destination field for deleting mirror rule */
+               cmd.destination = rte_cpu_to_le_16(rule_id);
+
+       cmd.rule_type = rte_cpu_to_le_16(rule_type <<
+                               I40E_AQC_MIRROR_RULE_TYPE_SHIFT);
+       cmd.seid = rte_cpu_to_le_16(seid);
+
+       rte_memcpy(&desc.params.raw, &cmd, sizeof(cmd));
+       status = i40e_asq_send_command(hw, &desc, buff, buff_len, NULL);
+
+       return status;
+}
+
+/**
+ * i40e_mirror_rule_set
+ * @dev: pointer to the hardware structure
+ * @mirror_conf: mirror rule info
+ * @sw_id: mirror rule's sw_id
+ * @on: enable/disable
+ *
+ * set a mirror rule.
+ *
+ **/
+static int
+i40e_mirror_rule_set(struct rte_eth_dev *dev,
+                       struct rte_eth_mirror_conf *mirror_conf,
+                       uint8_t sw_id, uint8_t on)
+{
+       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_mirror_rule *it, *mirr_rule = NULL;
+       struct i40e_mirror_rule *parent = NULL;
+       uint16_t seid, dst_seid, rule_id;
+       uint16_t i, j = 0;
+       int ret;
+
+       PMD_DRV_LOG(DEBUG, "i40e_mirror_rule_set: sw_id = %d.", sw_id);
+
+       if (pf->main_vsi->veb == NULL || pf->vfs == NULL) {
+               PMD_DRV_LOG(ERR, "mirror rule can not be configured"
+                       " without veb or vfs.");
+               return -ENOSYS;
+       }
+       if (pf->nb_mirror_rule > I40E_MAX_MIRROR_RULES) {
+               PMD_DRV_LOG(ERR, "mirror table is full.");
+               return -ENOSPC;
+       }
+       if (mirror_conf->dst_pool > pf->vf_num) {
+               PMD_DRV_LOG(ERR, "invalid destination pool %u.",
+                                mirror_conf->dst_pool);
+               return -EINVAL;
+       }
+
+       seid = pf->main_vsi->veb->seid;
+
+       TAILQ_FOREACH(it, &pf->mirror_list, rules) {
+               if (sw_id <= it->index) {
+                       mirr_rule = it;
+                       break;
+               }
+               parent = it;
+       }
+       if (mirr_rule && sw_id == mirr_rule->index) {
+               if (on) {
+                       PMD_DRV_LOG(ERR, "mirror rule exists.");
+                       return -EEXIST;
+               } else {
+                       ret = i40e_aq_del_mirror_rule(hw, seid,
+                                       mirr_rule->rule_type,
+                                       mirr_rule->entries,
+                                       mirr_rule->num_entries, mirr_rule->id);
+                       if (ret < 0) {
+                               PMD_DRV_LOG(ERR, "failed to remove mirror rule:"
+                                                  " ret = %d, aq_err = %d.",
+                                                  ret, hw->aq.asq_last_status);
+                               return -ENOSYS;
+                       }
+                       TAILQ_REMOVE(&pf->mirror_list, mirr_rule, rules);
+                       rte_free(mirr_rule);
+                       pf->nb_mirror_rule--;
+                       return 0;
+               }
+       } else if (!on) {
+               PMD_DRV_LOG(ERR, "mirror rule doesn't exist.");
+               return -ENOENT;
+       }
+
+       mirr_rule = rte_zmalloc("i40e_mirror_rule",
+                               sizeof(struct i40e_mirror_rule) , 0);
+       if (!mirr_rule) {
+               PMD_DRV_LOG(ERR, "failed to allocate memory");
+               return I40E_ERR_NO_MEMORY;
+       }
+       switch (mirror_conf->rule_type) {
+       case ETH_MIRROR_VLAN:
+               for (i = 0, j = 0; i < ETH_MIRROR_MAX_VLANS; i++) {
+                       if (mirror_conf->vlan.vlan_mask & (1ULL << i)) {
+                               mirr_rule->entries[j] =
+                                       mirror_conf->vlan.vlan_id[i];
+                               j++;
+                       }
+               }
+               if (j == 0) {
+                       PMD_DRV_LOG(ERR, "vlan is not specified.");
+                       rte_free(mirr_rule);
+                       return -EINVAL;
+               }
+               mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_VLAN;
+               break;
+       case ETH_MIRROR_VIRTUAL_POOL_UP:
+       case ETH_MIRROR_VIRTUAL_POOL_DOWN:
+               /* check if the specified pool bit is out of range */
+               if (mirror_conf->pool_mask > (uint64_t)(1ULL << (pf->vf_num + 1))) {
+                       PMD_DRV_LOG(ERR, "pool mask is out of range.");
+                       rte_free(mirr_rule);
+                       return -EINVAL;
+               }
+               for (i = 0, j = 0; i < pf->vf_num; i++) {
+                       if (mirror_conf->pool_mask & (1ULL << i)) {
+                               mirr_rule->entries[j] = pf->vfs[i].vsi->seid;
+                               j++;
+                       }
+               }
+               if (mirror_conf->pool_mask & (1ULL << pf->vf_num)) {
+                       /* add pf vsi to entries */
+                       mirr_rule->entries[j] = pf->main_vsi_seid;
+                       j++;
+               }
+               if (j == 0) {
+                       PMD_DRV_LOG(ERR, "pool is not specified.");
+                       rte_free(mirr_rule);
+                       return -EINVAL;
+               }
+               /* egress and ingress in aq commands means from switch but not port */
+               mirr_rule->rule_type =
+                       (mirror_conf->rule_type == ETH_MIRROR_VIRTUAL_POOL_UP) ?
+                       I40E_AQC_MIRROR_RULE_TYPE_VPORT_EGRESS :
+                       I40E_AQC_MIRROR_RULE_TYPE_VPORT_INGRESS;
+               break;
+       case ETH_MIRROR_UPLINK_PORT:
+               /* egress and ingress in aq commands means from switch but not port*/
+               mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_EGRESS;
+               break;
+       case ETH_MIRROR_DOWNLINK_PORT:
+               mirr_rule->rule_type = I40E_AQC_MIRROR_RULE_TYPE_ALL_INGRESS;
+               break;
+       default:
+               PMD_DRV_LOG(ERR, "unsupported mirror type %d.",
+                       mirror_conf->rule_type);
+               rte_free(mirr_rule);
+               return -EINVAL;
+       }
+
+       /* If the dst_pool is equal to vf_num, consider it as PF */
+       if (mirror_conf->dst_pool == pf->vf_num)
+               dst_seid = pf->main_vsi_seid;
+       else
+               dst_seid = pf->vfs[mirror_conf->dst_pool].vsi->seid;
+
+       ret = i40e_aq_add_mirror_rule(hw, seid, dst_seid,
+                                     mirr_rule->rule_type, mirr_rule->entries,
+                                     j, &rule_id);
+       if (ret < 0) {
+               PMD_DRV_LOG(ERR, "failed to add mirror rule:"
+                                  " ret = %d, aq_err = %d.",
+                                  ret, hw->aq.asq_last_status);
+               rte_free(mirr_rule);
+               return -ENOSYS;
+       }
+
+       mirr_rule->index = sw_id;
+       mirr_rule->num_entries = j;
+       mirr_rule->id = rule_id;
+       mirr_rule->dst_vsi_seid = dst_seid;
+
+       if (parent)
+               TAILQ_INSERT_AFTER(&pf->mirror_list, parent, mirr_rule, rules);
+       else
+               TAILQ_INSERT_HEAD(&pf->mirror_list, mirr_rule, rules);
+
+       pf->nb_mirror_rule++;
+       return 0;
+}
+
+/**
+ * i40e_mirror_rule_reset
+ * @dev: pointer to the device
+ * @sw_id: mirror rule's sw_id
+ *
+ * reset a mirror rule.
+ *
+ **/
+static int
+i40e_mirror_rule_reset(struct rte_eth_dev *dev, uint8_t sw_id)
+{
+       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_mirror_rule *it, *mirr_rule = NULL;
+       uint16_t seid;
+       int ret;
+
+       PMD_DRV_LOG(DEBUG, "i40e_mirror_rule_reset: sw_id = %d.", sw_id);
+
+       seid = pf->main_vsi->veb->seid;
+
+       TAILQ_FOREACH(it, &pf->mirror_list, rules) {
+               if (sw_id == it->index) {
+                       mirr_rule = it;
+                       break;
+               }
+       }
+       if (mirr_rule) {
+               ret = i40e_aq_del_mirror_rule(hw, seid,
+                               mirr_rule->rule_type,
+                               mirr_rule->entries,
+                               mirr_rule->num_entries, mirr_rule->id);
+               if (ret < 0) {
+                       PMD_DRV_LOG(ERR, "failed to remove mirror rule:"
+                                          " status = %d, aq_err = %d.",
+                                          ret, hw->aq.asq_last_status);
+                       return -ENOSYS;
+               }
+               TAILQ_REMOVE(&pf->mirror_list, mirr_rule, rules);
+               rte_free(mirr_rule);
+               pf->nb_mirror_rule--;
+       } else {
+               PMD_DRV_LOG(ERR, "mirror rule doesn't exist.");
+               return -ENOENT;
+       }
+       return 0;
+}
+
+static int
+i40e_timesync_enable(struct rte_eth_dev *dev)
+{
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       struct rte_eth_link *link = &dev->data->dev_link;
+       uint32_t tsync_ctl_l;
+       uint32_t tsync_ctl_h;
+       uint32_t tsync_inc_l;
+       uint32_t tsync_inc_h;
+
+       switch (link->link_speed) {
+       case ETH_LINK_SPEED_40G:
+               tsync_inc_l = I40E_PTP_40GB_INCVAL & 0xFFFFFFFF;
+               tsync_inc_h = I40E_PTP_40GB_INCVAL >> 32;
+               break;
+       case ETH_LINK_SPEED_10G:
+               tsync_inc_l = I40E_PTP_10GB_INCVAL & 0xFFFFFFFF;
+               tsync_inc_h = I40E_PTP_10GB_INCVAL >> 32;
+               break;
+       case ETH_LINK_SPEED_1000:
+               tsync_inc_l = I40E_PTP_1GB_INCVAL & 0xFFFFFFFF;
+               tsync_inc_h = I40E_PTP_1GB_INCVAL >> 32;
+               break;
+       default:
+               tsync_inc_l = 0x0;
+               tsync_inc_h = 0x0;
+       }
+
+       /* Clear timesync registers. */
+       I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
+       I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+       I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(0));
+       I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(1));
+       I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(2));
+       I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(3));
+       I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+
+       /* Set the timesync increment value. */
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, tsync_inc_l);
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, tsync_inc_h);
+
+       /* Enable timestamping of PTP packets. */
+       tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
+       tsync_ctl_l |= I40E_PRTTSYN_TSYNENA;
+
+       tsync_ctl_h = I40E_READ_REG(hw, I40E_PRTTSYN_CTL1);
+       tsync_ctl_h |= I40E_PRTTSYN_TSYNENA;
+       tsync_ctl_h |= I40E_PRTTSYN_TSYNTYPE;
+
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
+
+       return 0;
+}
+
+static int
+i40e_timesync_disable(struct rte_eth_dev *dev)
+{
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       uint32_t tsync_ctl_l;
+       uint32_t tsync_ctl_h;
+
+       /* Disable timestamping of transmitted PTP packets. */
+       tsync_ctl_l = I40E_READ_REG(hw, I40E_PRTTSYN_CTL0);
+       tsync_ctl_l &= ~I40E_PRTTSYN_TSYNENA;
+
+       tsync_ctl_h = I40E_READ_REG(hw, I40E_PRTTSYN_CTL1);
+       tsync_ctl_h &= ~I40E_PRTTSYN_TSYNENA;
+
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL0, tsync_ctl_l);
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_CTL1, tsync_ctl_h);
+
+       /* Set the timesync increment value. */
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_L, 0x0);
+       I40E_WRITE_REG(hw, I40E_PRTTSYN_INC_H, 0x0);
+
+       return 0;
+}
+
+static int
+i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
+                               struct timespec *timestamp, uint32_t flags)
+{
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       uint32_t sync_status;
+       uint32_t rx_stmpl;
+       uint32_t rx_stmph;
+       uint32_t index = flags & 0x03;
+
+       sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_1);
+       if ((sync_status & (1 << index)) == 0)
+               return -EINVAL;
+
+       rx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_L(index));
+       rx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_RXTIME_H(index));
+
+       timestamp->tv_sec = (uint64_t)(((uint64_t)rx_stmph << 32) | rx_stmpl);
+       timestamp->tv_nsec = 0;
+
+       return  0;
+}
+
+static int
+i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
+                               struct timespec *timestamp)
+{
+       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       uint32_t sync_status;
+       uint32_t tx_stmpl;
+       uint32_t tx_stmph;
+
+       sync_status = I40E_READ_REG(hw, I40E_PRTTSYN_STAT_0);
+       if ((sync_status & I40E_PRTTSYN_STAT_0_TXTIME_MASK) == 0)
+               return -EINVAL;
+
+       tx_stmpl = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_L);
+       tx_stmph = I40E_READ_REG(hw, I40E_PRTTSYN_TXTIME_H);
+
+       timestamp->tv_sec = (uint64_t)(((uint64_t)tx_stmph << 32) | tx_stmpl);
+       timestamp->tv_nsec = 0;
+
+       return  0;
+}