ixgbevf: fix statistic wraparound
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index 5eb7c2b..2dd9fdc 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"
        (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);
@@ -212,6 +220,18 @@ static int i40e_dev_filter_ctrl(struct rte_eth_dev *dev,
 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)},
@@ -263,26 +283,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)
@@ -406,6 +426,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);
@@ -564,6 +585,9 @@ eth_i40e_dev_init(struct rte_eth_dev *dev)
        /* enable uio intr after callback register */
        rte_intr_enable(&(pci_dev->intr_handle));
 
+       /* initialize mirror rule list */
+       TAILQ_INIT(&pf->mirror_list);
+
        return 0;
 
 err_mac_alloc:
@@ -584,6 +608,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)
 {
@@ -859,6 +942,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",
@@ -930,6 +1015,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 */
@@ -954,6 +1040,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
@@ -962,10 +1055,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);
@@ -978,6 +1074,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);
@@ -1543,6 +1647,8 @@ i40e_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
                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;
 
@@ -1993,10 +2099,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;
@@ -5749,3 +5856,445 @@ i40e_config_qinq(struct i40e_hw *hw, struct i40e_vsi *vsi)
 
        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;
+}