net/ipn3ke: support TM
authorRosen Xu <rosen.xu@intel.com>
Tue, 16 Apr 2019 03:17:42 +0000 (11:17 +0800)
committerFerruh Yigit <ferruh.yigit@intel.com>
Fri, 19 Apr 2019 12:51:54 +0000 (14:51 +0200)
Add Intel FPGA Acceleration NIC IPN3KE TM of PMD driver.

Signed-off-by: Rosen Xu <rosen.xu@intel.com>
Signed-off-by: Andy Pei <andy.pei@intel.com>
Signed-off-by: Dan Wei <dan.wei@intel.com>
drivers/net/ipn3ke/Makefile
drivers/net/ipn3ke/ipn3ke_ethdev.c
drivers/net/ipn3ke/ipn3ke_ethdev.h
drivers/net/ipn3ke/ipn3ke_representor.c
drivers/net/ipn3ke/ipn3ke_tm.c [new file with mode: 0644]
drivers/net/ipn3ke/meson.build

index 221567d..38d9384 100644 (file)
@@ -34,5 +34,6 @@ LIBABIVER := 1
 #
 SRCS-$(CONFIG_RTE_LIBRTE_IPN3KE_PMD) += ipn3ke_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_IPN3KE_PMD) += ipn3ke_representor.c
+SRCS-$(CONFIG_RTE_LIBRTE_IPN3KE_PMD) += ipn3ke_tm.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
index 58c8ce4..508ea01 100644 (file)
@@ -262,6 +262,9 @@ ipn3ke_hw_init(struct rte_afu_device *afu_dev,
        hw->flow_hw_enable = 0;
        if (afu_dev->id.uuid.uuid_low == IPN3KE_UUID_VBNG_LOW &&
                afu_dev->id.uuid.uuid_high == IPN3KE_UUID_VBNG_HIGH) {
+               ret = ipn3ke_hw_tm_init(hw);
+               if (ret)
+                       return ret;
                hw->tm_hw_enable = 1;
                hw->flow_hw_enable = 1;
        }
index d2c73e5..36ff2f8 100644 (file)
@@ -552,6 +552,13 @@ int
 ipn3ke_rpst_init(struct rte_eth_dev *ethdev, void *init_params);
 int
 ipn3ke_rpst_uninit(struct rte_eth_dev *ethdev);
+int
+ipn3ke_hw_tm_init(struct ipn3ke_hw *hw);
+void
+ipn3ke_tm_init(struct ipn3ke_rpst *rpst);
+int
+ipn3ke_tm_ops_get(struct rte_eth_dev *ethdev,
+               void *arg);
 
 
 /* IPN3KE_MASK is a macro used on 32 bit registers */
index 3831982..63098bf 100644 (file)
@@ -801,6 +801,8 @@ static const struct eth_dev_ops ipn3ke_rpst_dev_ops = {
        .allmulticast_disable = ipn3ke_rpst_allmulticast_disable,
        .mac_addr_set         = ipn3ke_rpst_mac_addr_set,
        .mtu_set              = ipn3ke_rpst_mtu_set,
+
+       .tm_ops_get           = ipn3ke_tm_ops_get,
 };
 
 static uint16_t ipn3ke_rpst_recv_pkts(__rte_unused void *rx_q,
@@ -840,6 +842,9 @@ ipn3ke_rpst_init(struct rte_eth_dev *ethdev, void *init_params)
                return -ENODEV;
        }
 
+       if (rpst->hw->tm_hw_enable)
+               ipn3ke_tm_init(rpst);
+
        /* Set representor device ops */
        ethdev->dev_ops = &ipn3ke_rpst_dev_ops;
 
diff --git a/drivers/net/ipn3ke/ipn3ke_tm.c b/drivers/net/ipn3ke/ipn3ke_tm.c
new file mode 100644 (file)
index 0000000..8baa2fb
--- /dev/null
@@ -0,0 +1,2068 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2019 Intel Corporation
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rte_bus_pci.h>
+#include <rte_ethdev.h>
+#include <rte_pci.h>
+#include <rte_malloc.h>
+#include <rte_tm_driver.h>
+
+#include <rte_mbuf.h>
+#include <rte_sched.h>
+#include <rte_ethdev_driver.h>
+
+#include <rte_io.h>
+#include <rte_rawdev.h>
+#include <rte_rawdev_pmd.h>
+#include <rte_bus_ifpga.h>
+#include <ifpga_logs.h>
+
+#include "ipn3ke_rawdev_api.h"
+#include "ipn3ke_logs.h"
+#include "ipn3ke_ethdev.h"
+
+#define BYTES_IN_MBPS     (1000 * 1000 / 8)
+#define SUBPORT_TC_PERIOD 10
+#define PIPE_TC_PERIOD    40
+
+struct ipn3ke_tm_shaper_params_range_type {
+       uint32_t m1;
+       uint32_t m2;
+       uint32_t exp;
+       uint32_t exp2;
+       uint32_t low;
+       uint32_t high;
+};
+struct ipn3ke_tm_shaper_params_range_type ipn3ke_tm_shaper_params_rang[] = {
+       {  0,       1,     0,        1,           0,            4},
+       {  2,       3,     0,        1,           8,           12},
+       {  4,       7,     0,        1,          16,           28},
+       {  8,      15,     0,        1,          32,           60},
+       { 16,      31,     0,        1,          64,          124},
+       { 32,      63,     0,        1,         128,          252},
+       { 64,     127,     0,        1,         256,          508},
+       {128,     255,     0,        1,         512,         1020},
+       {256,     511,     0,        1,        1024,         2044},
+       {512,    1023,     0,        1,        2048,         4092},
+       {512,    1023,     1,        2,        4096,         8184},
+       {512,    1023,     2,        4,        8192,        16368},
+       {512,    1023,     3,        8,       16384,        32736},
+       {512,    1023,     4,       16,       32768,        65472},
+       {512,    1023,     5,       32,       65536,       130944},
+       {512,    1023,     6,       64,      131072,       261888},
+       {512,    1023,     7,      128,      262144,       523776},
+       {512,    1023,     8,      256,      524288,      1047552},
+       {512,    1023,     9,      512,     1048576,      2095104},
+       {512,    1023,    10,     1024,     2097152,      4190208},
+       {512,    1023,    11,     2048,     4194304,      8380416},
+       {512,    1023,    12,     4096,     8388608,     16760832},
+       {512,    1023,    13,     8192,    16777216,     33521664},
+       {512,    1023,    14,    16384,    33554432,     67043328},
+       {512,    1023,    15,    32768,    67108864,    134086656},
+};
+
+#define IPN3KE_TM_SHAPER_RANGE_NUM (sizeof(ipn3ke_tm_shaper_params_rang) / \
+       sizeof(struct ipn3ke_tm_shaper_params_range_type))
+
+#define IPN3KE_TM_SHAPER_COMMITTED_RATE_MAX \
+       (ipn3ke_tm_shaper_params_rang[IPN3KE_TM_SHAPER_RANGE_NUM - 1].high)
+
+#define IPN3KE_TM_SHAPER_PEAK_RATE_MAX \
+       (ipn3ke_tm_shaper_params_rang[IPN3KE_TM_SHAPER_RANGE_NUM - 1].high)
+
+int
+ipn3ke_hw_tm_init(struct ipn3ke_hw *hw)
+{
+#define SCRATCH_DATA 0xABCDEF
+       struct ipn3ke_tm_node *nodes;
+       struct ipn3ke_tm_tdrop_profile *tdrop_profile;
+       int node_num;
+       int i;
+
+       if (hw == NULL)
+               return -EINVAL;
+#if IPN3KE_TM_SCRATCH_RW
+       uint32_t scratch_data;
+       IPN3KE_MASK_WRITE_REG(hw,
+                                       IPN3KE_TM_SCRATCH,
+                                       0,
+                                       SCRATCH_DATA,
+                                       0xFFFFFFFF);
+       scratch_data = IPN3KE_MASK_READ_REG(hw,
+                                       IPN3KE_TM_SCRATCH,
+                                       0,
+                                       0xFFFFFFFF);
+       if (scratch_data != SCRATCH_DATA)
+               return -EINVAL;
+#endif
+       /* alloc memory for all hierarchy nodes */
+       node_num = hw->port_num +
+               IPN3KE_TM_VT_NODE_NUM +
+               IPN3KE_TM_COS_NODE_NUM;
+
+       nodes = rte_zmalloc("ipn3ke_tm_nodes",
+                       sizeof(struct ipn3ke_tm_node) * node_num,
+                       0);
+       if (!nodes)
+               return -ENOMEM;
+
+       /* alloc memory for Tail Drop Profile */
+       tdrop_profile = rte_zmalloc("ipn3ke_tm_tdrop_profile",
+                               sizeof(struct ipn3ke_tm_tdrop_profile) *
+                               IPN3KE_TM_TDROP_PROFILE_NUM,
+                               0);
+       if (!tdrop_profile) {
+               rte_free(nodes);
+               return -ENOMEM;
+       }
+
+       hw->nodes = nodes;
+       hw->port_nodes = nodes;
+       hw->vt_nodes = hw->port_nodes + hw->port_num;
+       hw->cos_nodes = hw->vt_nodes + IPN3KE_TM_VT_NODE_NUM;
+       hw->tdrop_profile = tdrop_profile;
+       hw->tdrop_profile_num = IPN3KE_TM_TDROP_PROFILE_NUM;
+
+       for (i = 0, nodes = hw->port_nodes;
+               i < hw->port_num;
+               i++, nodes++) {
+               nodes->node_index = i;
+               nodes->level = IPN3KE_TM_NODE_LEVEL_PORT;
+               nodes->tm_id = RTE_TM_NODE_ID_NULL;
+               nodes->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               nodes->parent_node_id = RTE_TM_NODE_ID_NULL;
+               nodes->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               nodes->weight = 0;
+               nodes->parent_node = NULL;
+               nodes->shaper_profile.valid = 0;
+               nodes->tdrop_profile = NULL;
+               nodes->n_children = 0;
+               TAILQ_INIT(&nodes->children_node_list);
+       }
+
+       for (i = 0, nodes = hw->vt_nodes;
+               i < IPN3KE_TM_VT_NODE_NUM;
+               i++, nodes++) {
+               nodes->node_index = i;
+               nodes->level = IPN3KE_TM_NODE_LEVEL_VT;
+               nodes->tm_id = RTE_TM_NODE_ID_NULL;
+               nodes->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               nodes->parent_node_id = RTE_TM_NODE_ID_NULL;
+               nodes->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               nodes->weight = 0;
+               nodes->parent_node = NULL;
+               nodes->shaper_profile.valid = 0;
+               nodes->tdrop_profile = NULL;
+               nodes->n_children = 0;
+               TAILQ_INIT(&nodes->children_node_list);
+       }
+
+       for (i = 0, nodes = hw->cos_nodes;
+               i < IPN3KE_TM_COS_NODE_NUM;
+               i++, nodes++) {
+               nodes->node_index = i;
+               nodes->level = IPN3KE_TM_NODE_LEVEL_COS;
+               nodes->tm_id = RTE_TM_NODE_ID_NULL;
+               nodes->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               nodes->parent_node_id = RTE_TM_NODE_ID_NULL;
+               nodes->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               nodes->weight = 0;
+               nodes->parent_node = NULL;
+               nodes->shaper_profile.valid = 0;
+               nodes->tdrop_profile = NULL;
+               nodes->n_children = 0;
+               TAILQ_INIT(&nodes->children_node_list);
+       }
+
+       for (i = 0, tdrop_profile = hw->tdrop_profile;
+               i < IPN3KE_TM_TDROP_PROFILE_NUM;
+               i++, tdrop_profile++) {
+               tdrop_profile->tdrop_profile_id = i;
+               tdrop_profile->n_users = 0;
+               tdrop_profile->valid = 0;
+       }
+
+       return 0;
+}
+
+void
+ipn3ke_tm_init(struct ipn3ke_rpst *rpst)
+{
+       struct ipn3ke_tm_internals *tm;
+       struct ipn3ke_tm_node *port_node;
+
+       tm = &rpst->tm;
+
+       port_node = &rpst->hw->port_nodes[rpst->port_id];
+       tm->h.port_node = port_node;
+
+       tm->h.n_shaper_profiles = 0;
+       tm->h.n_tdrop_profiles = 0;
+       tm->h.n_vt_nodes = 0;
+       tm->h.n_cos_nodes = 0;
+
+       tm->h.port_commit_node = NULL;
+       TAILQ_INIT(&tm->h.vt_commit_node_list);
+       TAILQ_INIT(&tm->h.cos_commit_node_list);
+
+       tm->hierarchy_frozen = 0;
+       tm->tm_started = 1;
+       tm->tm_id = rpst->port_id;
+}
+
+static struct ipn3ke_tm_shaper_profile *
+ipn3ke_hw_tm_shaper_profile_search(struct ipn3ke_hw *hw,
+       uint32_t shaper_profile_id, struct rte_tm_error *error)
+{
+       struct ipn3ke_tm_shaper_profile *sp = NULL;
+       uint32_t level_of_node_id;
+       uint32_t node_index;
+
+       /* Shaper profile ID must not be NONE. */
+       if (shaper_profile_id == RTE_TM_SHAPER_PROFILE_ID_NONE) {
+               rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                               NULL,
+                               rte_strerror(EINVAL));
+
+               return NULL;
+       }
+
+       level_of_node_id = shaper_profile_id / IPN3KE_TM_NODE_LEVEL_MOD;
+       node_index = shaper_profile_id % IPN3KE_TM_NODE_LEVEL_MOD;
+
+       switch (level_of_node_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               if (node_index >= hw->port_num)
+                       rte_tm_error_set(error,
+                                       EEXIST,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EEXIST));
+               else
+                       sp = &hw->port_nodes[node_index].shaper_profile;
+
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_index >= IPN3KE_TM_VT_NODE_NUM)
+                       rte_tm_error_set(error,
+                                       EEXIST,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EEXIST));
+               else
+                       sp = &hw->vt_nodes[node_index].shaper_profile;
+
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_index >= IPN3KE_TM_COS_NODE_NUM)
+                       rte_tm_error_set(error,
+                                       EEXIST,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EEXIST));
+               else
+                       sp = &hw->cos_nodes[node_index].shaper_profile;
+
+               break;
+       default:
+               rte_tm_error_set(error,
+                               EEXIST,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                               NULL,
+                               rte_strerror(EEXIST));
+       }
+
+       return sp;
+}
+
+static struct ipn3ke_tm_tdrop_profile *
+ipn3ke_hw_tm_tdrop_profile_search(struct ipn3ke_hw *hw,
+       uint32_t tdrop_profile_id)
+{
+       struct ipn3ke_tm_tdrop_profile *tdrop_profile;
+
+       if (tdrop_profile_id >= hw->tdrop_profile_num)
+               return NULL;
+
+       tdrop_profile = &hw->tdrop_profile[tdrop_profile_id];
+       if (tdrop_profile->valid)
+               return tdrop_profile;
+
+       return NULL;
+}
+
+static struct ipn3ke_tm_node *
+ipn3ke_hw_tm_node_search(struct ipn3ke_hw *hw, uint32_t tm_id,
+       uint32_t node_id, uint32_t state_mask)
+{
+       uint32_t level_of_node_id;
+       uint32_t node_index;
+       struct ipn3ke_tm_node *n;
+
+       level_of_node_id = node_id / IPN3KE_TM_NODE_LEVEL_MOD;
+       node_index = node_id % IPN3KE_TM_NODE_LEVEL_MOD;
+
+       switch (level_of_node_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               if (node_index >= hw->port_num)
+                       return NULL;
+               n = &hw->port_nodes[node_index];
+
+               break;
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_index >= IPN3KE_TM_VT_NODE_NUM)
+                       return NULL;
+               n = &hw->vt_nodes[node_index];
+
+               break;
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_index >= IPN3KE_TM_COS_NODE_NUM)
+                       return NULL;
+               n = &hw->cos_nodes[node_index];
+
+               break;
+       default:
+               return NULL;
+       }
+
+       /* Check tm node status */
+       if (n->node_state == IPN3KE_TM_NODE_STATE_IDLE) {
+               if (n->tm_id != RTE_TM_NODE_ID_NULL ||
+               n->parent_node_id != RTE_TM_NODE_ID_NULL ||
+               n->parent_node != NULL ||
+               n->n_children > 0) {
+                       IPN3KE_AFU_PMD_ERR("tm node check error %d", 1);
+               }
+       } else if (n->node_state < IPN3KE_TM_NODE_STATE_MAX) {
+               if (n->tm_id == RTE_TM_NODE_ID_NULL ||
+               (level_of_node_id != IPN3KE_TM_NODE_LEVEL_PORT &&
+                       n->parent_node_id == RTE_TM_NODE_ID_NULL) ||
+               (level_of_node_id != IPN3KE_TM_NODE_LEVEL_PORT &&
+                       n->parent_node == NULL)) {
+                       IPN3KE_AFU_PMD_ERR("tm node check error %d", 1);
+               }
+       } else {
+               IPN3KE_AFU_PMD_ERR("tm node check error %d", 1);
+       }
+
+       if (IPN3KE_BIT_ISSET(state_mask, n->node_state)) {
+               if (n->node_state == IPN3KE_TM_NODE_STATE_IDLE)
+                       return n;
+               else if (n->tm_id == tm_id)
+                       return n;
+               else
+                       return NULL;
+       } else {
+               return NULL;
+       }
+}
+
+/* Traffic manager node type get */
+static int
+ipn3ke_pmd_tm_node_type_get(struct rte_eth_dev *dev,
+       uint32_t node_id, int *is_leaf, struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node *node;
+       uint32_t state_mask;
+
+       if (is_leaf == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       tm_id = tm->tm_id;
+
+       state_mask = 0;
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_COMMITTED);
+       node = ipn3ke_hw_tm_node_search(hw, tm_id, node_id, state_mask);
+       if (node_id == RTE_TM_NODE_ID_NULL ||
+               node == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       *is_leaf = (node->level == IPN3KE_TM_NODE_LEVEL_COS) ? 1 : 0;
+
+       return 0;
+}
+
+#define WRED_SUPPORTED    0
+
+#define STATS_MASK_DEFAULT \
+       (RTE_TM_STATS_N_PKTS | \
+       RTE_TM_STATS_N_BYTES | \
+       RTE_TM_STATS_N_PKTS_GREEN_DROPPED | \
+       RTE_TM_STATS_N_BYTES_GREEN_DROPPED)
+
+#define STATS_MASK_QUEUE \
+       (STATS_MASK_DEFAULT | RTE_TM_STATS_N_PKTS_QUEUED)
+
+/* Traffic manager capabilities get */
+static int
+ipn3ke_tm_capabilities_get(__rte_unused struct rte_eth_dev *dev,
+       struct rte_tm_capabilities *cap, struct rte_tm_error *error)
+{
+       if (cap == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_CAPABILITIES,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* set all the parameters to 0 first. */
+       memset(cap, 0, sizeof(*cap));
+
+       cap->n_nodes_max = 1 + IPN3KE_TM_COS_NODE_NUM + IPN3KE_TM_VT_NODE_NUM;
+       cap->n_levels_max = IPN3KE_TM_NODE_LEVEL_MAX;
+
+       cap->non_leaf_nodes_identical = 0;
+       cap->leaf_nodes_identical = 1;
+
+       cap->shaper_n_max = 1 + IPN3KE_TM_VT_NODE_NUM;
+       cap->shaper_private_n_max = 1 + IPN3KE_TM_VT_NODE_NUM;
+       cap->shaper_private_dual_rate_n_max = 0;
+       cap->shaper_private_rate_min = 1;
+       cap->shaper_private_rate_max = 1 + IPN3KE_TM_VT_NODE_NUM;
+
+       cap->shaper_shared_n_max = 0;
+       cap->shaper_shared_n_nodes_per_shaper_max = 0;
+       cap->shaper_shared_n_shapers_per_node_max = 0;
+       cap->shaper_shared_dual_rate_n_max = 0;
+       cap->shaper_shared_rate_min = 0;
+       cap->shaper_shared_rate_max = 0;
+
+       cap->shaper_pkt_length_adjust_min = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
+       cap->shaper_pkt_length_adjust_max = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
+
+       cap->sched_n_children_max = IPN3KE_TM_COS_NODE_NUM;
+       cap->sched_sp_n_priorities_max = 3;
+       cap->sched_wfq_n_children_per_group_max = UINT32_MAX;
+       cap->sched_wfq_n_groups_max = 1;
+       cap->sched_wfq_weight_max = UINT32_MAX;
+
+       cap->cman_wred_packet_mode_supported = 0;
+       cap->cman_wred_byte_mode_supported = 0;
+       cap->cman_head_drop_supported = 0;
+       cap->cman_wred_context_n_max = 0;
+       cap->cman_wred_context_private_n_max = 0;
+       cap->cman_wred_context_shared_n_max = 0;
+       cap->cman_wred_context_shared_n_nodes_per_context_max = 0;
+       cap->cman_wred_context_shared_n_contexts_per_node_max = 0;
+
+       /**
+        * cap->mark_vlan_dei_supported = {0, 0, 0};
+        * cap->mark_ip_ecn_tcp_supported = {0, 0, 0};
+        * cap->mark_ip_ecn_sctp_supported = {0, 0, 0};
+        * cap->mark_ip_dscp_supported = {0, 0, 0};
+        */
+
+       cap->dynamic_update_mask = 0;
+
+       cap->stats_mask = 0;
+
+       return 0;
+}
+
+/* Traffic manager level capabilities get */
+static int
+ipn3ke_tm_level_capabilities_get(struct rte_eth_dev *dev,
+       uint32_t level_id, struct rte_tm_level_capabilities *cap,
+       struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+
+       if (cap == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_CAPABILITIES,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       if (level_id >= IPN3KE_TM_NODE_LEVEL_MAX)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* set all the parameters to 0 first. */
+       memset(cap, 0, sizeof(*cap));
+
+       switch (level_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               cap->n_nodes_max = hw->port_num;
+               cap->n_nodes_nonleaf_max = IPN3KE_TM_VT_NODE_NUM;
+               cap->n_nodes_leaf_max = 0;
+               cap->non_leaf_nodes_identical = 0;
+               cap->leaf_nodes_identical = 0;
+
+               cap->nonleaf.shaper_private_supported = 0;
+               cap->nonleaf.shaper_private_dual_rate_supported = 0;
+               cap->nonleaf.shaper_private_rate_min = 1;
+               cap->nonleaf.shaper_private_rate_max = UINT32_MAX;
+               cap->nonleaf.shaper_shared_n_max = 0;
+
+               cap->nonleaf.sched_n_children_max = IPN3KE_TM_VT_NODE_NUM;
+               cap->nonleaf.sched_sp_n_priorities_max = 1;
+               cap->nonleaf.sched_wfq_n_children_per_group_max = 0;
+               cap->nonleaf.sched_wfq_n_groups_max = 0;
+               cap->nonleaf.sched_wfq_weight_max = 0;
+
+               cap->nonleaf.stats_mask = STATS_MASK_DEFAULT;
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               cap->n_nodes_max = IPN3KE_TM_VT_NODE_NUM;
+               cap->n_nodes_nonleaf_max = IPN3KE_TM_COS_NODE_NUM;
+               cap->n_nodes_leaf_max = 0;
+               cap->non_leaf_nodes_identical = 0;
+               cap->leaf_nodes_identical = 0;
+
+               cap->nonleaf.shaper_private_supported = 0;
+               cap->nonleaf.shaper_private_dual_rate_supported = 0;
+               cap->nonleaf.shaper_private_rate_min = 1;
+               cap->nonleaf.shaper_private_rate_max = UINT32_MAX;
+               cap->nonleaf.shaper_shared_n_max = 0;
+
+               cap->nonleaf.sched_n_children_max = IPN3KE_TM_COS_NODE_NUM;
+               cap->nonleaf.sched_sp_n_priorities_max = 1;
+               cap->nonleaf.sched_wfq_n_children_per_group_max = 0;
+               cap->nonleaf.sched_wfq_n_groups_max = 0;
+               cap->nonleaf.sched_wfq_weight_max = 0;
+
+               cap->nonleaf.stats_mask = STATS_MASK_DEFAULT;
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               cap->n_nodes_max = IPN3KE_TM_COS_NODE_NUM;
+               cap->n_nodes_nonleaf_max = 0;
+               cap->n_nodes_leaf_max = IPN3KE_TM_COS_NODE_NUM;
+               cap->non_leaf_nodes_identical = 0;
+               cap->leaf_nodes_identical = 0;
+
+               cap->leaf.shaper_private_supported = 0;
+               cap->leaf.shaper_private_dual_rate_supported = 0;
+               cap->leaf.shaper_private_rate_min = 0;
+               cap->leaf.shaper_private_rate_max = 0;
+               cap->leaf.shaper_shared_n_max = 0;
+
+               cap->leaf.cman_head_drop_supported = 0;
+               cap->leaf.cman_wred_packet_mode_supported = WRED_SUPPORTED;
+               cap->leaf.cman_wred_byte_mode_supported = 0;
+               cap->leaf.cman_wred_context_private_supported = WRED_SUPPORTED;
+               cap->leaf.cman_wred_context_shared_n_max = 0;
+
+               cap->leaf.stats_mask = STATS_MASK_QUEUE;
+               break;
+
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+       }
+
+       return 0;
+}
+
+/* Traffic manager node capabilities get */
+static int
+ipn3ke_tm_node_capabilities_get(struct rte_eth_dev *dev,
+       uint32_t node_id, struct rte_tm_node_capabilities *cap,
+       struct rte_tm_error *error)
+{
+       struct ipn3ke_rpst *representor = IPN3KE_DEV_PRIVATE_TO_RPST(dev);
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node *tm_node;
+       uint32_t state_mask;
+
+       if (cap == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_CAPABILITIES,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       tm_id = tm->tm_id;
+
+       state_mask = 0;
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_COMMITTED);
+       tm_node = ipn3ke_hw_tm_node_search(hw, tm_id, node_id, state_mask);
+       if (tm_node == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       if (tm_node->tm_id != representor->port_id)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* set all the parameters to 0 first. */
+       memset(cap, 0, sizeof(*cap));
+
+       switch (tm_node->level) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               cap->shaper_private_supported = 1;
+               cap->shaper_private_dual_rate_supported = 0;
+               cap->shaper_private_rate_min = 1;
+               cap->shaper_private_rate_max = UINT32_MAX;
+               cap->shaper_shared_n_max = 0;
+
+               cap->nonleaf.sched_n_children_max = IPN3KE_TM_VT_NODE_NUM;
+               cap->nonleaf.sched_sp_n_priorities_max = 1;
+               cap->nonleaf.sched_wfq_n_children_per_group_max =
+                       IPN3KE_TM_VT_NODE_NUM;
+               cap->nonleaf.sched_wfq_n_groups_max = 1;
+               cap->nonleaf.sched_wfq_weight_max = 1;
+
+               cap->stats_mask = STATS_MASK_DEFAULT;
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               cap->shaper_private_supported = 1;
+               cap->shaper_private_dual_rate_supported = 0;
+               cap->shaper_private_rate_min = 1;
+               cap->shaper_private_rate_max = UINT32_MAX;
+               cap->shaper_shared_n_max = 0;
+
+               cap->nonleaf.sched_n_children_max = IPN3KE_TM_COS_NODE_NUM;
+               cap->nonleaf.sched_sp_n_priorities_max = 1;
+               cap->nonleaf.sched_wfq_n_children_per_group_max =
+                       IPN3KE_TM_COS_NODE_NUM;
+               cap->nonleaf.sched_wfq_n_groups_max = 1;
+               cap->nonleaf.sched_wfq_weight_max = 1;
+
+               cap->stats_mask = STATS_MASK_DEFAULT;
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               cap->shaper_private_supported = 0;
+               cap->shaper_private_dual_rate_supported = 0;
+               cap->shaper_private_rate_min = 0;
+               cap->shaper_private_rate_max = 0;
+               cap->shaper_shared_n_max = 0;
+
+               cap->leaf.cman_head_drop_supported = 0;
+               cap->leaf.cman_wred_packet_mode_supported = WRED_SUPPORTED;
+               cap->leaf.cman_wred_byte_mode_supported = 0;
+               cap->leaf.cman_wred_context_private_supported = WRED_SUPPORTED;
+               cap->leaf.cman_wred_context_shared_n_max = 0;
+
+               cap->stats_mask = STATS_MASK_QUEUE;
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_shaper_parame_trans(struct rte_tm_shaper_params *profile,
+       struct ipn3ke_tm_shaper_profile *local_profile,
+       const struct ipn3ke_tm_shaper_params_range_type *ref_data)
+{
+       uint32_t i;
+       const struct ipn3ke_tm_shaper_params_range_type *r;
+       uint64_t rate;
+
+       rate = profile->peak.rate;
+       for (i = 0, r = ref_data; i < IPN3KE_TM_SHAPER_RANGE_NUM; i++, r++) {
+               if (rate >= r->low &&
+               rate <= r->high) {
+                       local_profile->m = (rate / 4) / r->exp2;
+                       local_profile->e = r->exp;
+                       local_profile->rate = rate;
+
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int
+ipn3ke_tm_shaper_profile_add(struct rte_eth_dev *dev,
+       uint32_t shaper_profile_id, struct rte_tm_shaper_params *profile,
+       struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_shaper_profile *sp;
+
+       /* Shaper profile must not exist. */
+       sp = ipn3ke_hw_tm_shaper_profile_search(hw, shaper_profile_id, error);
+       if (!sp || (sp && sp->valid))
+               return -rte_tm_error_set(error,
+                                       EEXIST,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EEXIST));
+
+       /* Profile must not be NULL. */
+       if (profile == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* Peak rate: non-zero, 32-bit */
+       if (profile->peak.rate == 0 ||
+               profile->peak.rate > IPN3KE_TM_SHAPER_PEAK_RATE_MAX)
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_RATE,
+                               NULL,
+                               rte_strerror(EINVAL));
+
+       /* Peak size: non-zero, 32-bit */
+       if (profile->peak.size != 0)
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_SIZE,
+                               NULL,
+                               rte_strerror(EINVAL));
+
+       /* Dual-rate profiles are not supported. */
+       if (profile->committed.rate > IPN3KE_TM_SHAPER_COMMITTED_RATE_MAX)
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE,
+                               NULL,
+                               rte_strerror(EINVAL));
+
+       /* Packet length adjust: 24 bytes */
+       if (profile->pkt_length_adjust != 0)
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PKT_ADJUST_LEN,
+                               NULL,
+                               rte_strerror(EINVAL));
+
+       if (ipn3ke_tm_shaper_parame_trans(profile,
+                                       sp,
+                                       ipn3ke_tm_shaper_params_rang)) {
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_RATE,
+                               NULL,
+                               rte_strerror(EINVAL));
+       } else {
+               sp->valid = 1;
+               rte_memcpy(&sp->params, profile, sizeof(sp->params));
+       }
+
+       tm->h.n_shaper_profiles++;
+
+       return 0;
+}
+
+/* Traffic manager shaper profile delete */
+static int
+ipn3ke_tm_shaper_profile_delete(struct rte_eth_dev *dev,
+       uint32_t shaper_profile_id, struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_shaper_profile *sp;
+
+       /* Check existing */
+       sp = ipn3ke_hw_tm_shaper_profile_search(hw, shaper_profile_id, error);
+       if (!sp || (sp && !sp->valid))
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       sp->valid = 0;
+       tm->h.n_shaper_profiles--;
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_tdrop_profile_check(__rte_unused struct rte_eth_dev *dev,
+       uint32_t tdrop_profile_id, struct rte_tm_wred_params *profile,
+       struct rte_tm_error *error)
+{
+       enum rte_color color;
+
+       /* TDROP profile ID must not be NONE. */
+       if (tdrop_profile_id == RTE_TM_WRED_PROFILE_ID_NONE)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_WRED_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* Profile must not be NULL. */
+       if (profile == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_WRED_PROFILE,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* TDROP profile should be in packet mode */
+       if (profile->packet_mode != 0)
+               return -rte_tm_error_set(error,
+                                       ENOTSUP,
+                                       RTE_TM_ERROR_TYPE_WRED_PROFILE,
+                                       NULL,
+                                       rte_strerror(ENOTSUP));
+
+       /* min_th <= max_th, max_th > 0  */
+       for (color = RTE_COLOR_GREEN; color <= RTE_COLOR_GREEN; color++) {
+               uint64_t min_th = profile->red_params[color].min_th;
+               uint64_t max_th = profile->red_params[color].max_th;
+
+               if (((min_th >> IPN3KE_TDROP_TH1_SHIFT) >>
+                               IPN3KE_TDROP_TH1_SHIFT) ||
+                       max_th != 0)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_WRED_PROFILE,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_hw_tm_tdrop_wr(struct ipn3ke_hw *hw,
+                               struct ipn3ke_tm_tdrop_profile *tp)
+{
+       if (tp->valid) {
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_CCB_PROFILE_MS,
+                               0,
+                               tp->th2,
+                               IPN3KE_CCB_PROFILE_MS_MASK);
+
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_CCB_PROFILE_P,
+                               tp->tdrop_profile_id,
+                               tp->th1,
+                               IPN3KE_CCB_PROFILE_MASK);
+       } else {
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_CCB_PROFILE_MS,
+                               0,
+                               0,
+                               IPN3KE_CCB_PROFILE_MS_MASK);
+
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_CCB_PROFILE_P,
+                               tp->tdrop_profile_id,
+                               0,
+                               IPN3KE_CCB_PROFILE_MASK);
+       }
+
+       return 0;
+}
+
+/* Traffic manager TDROP profile add */
+static int
+ipn3ke_tm_tdrop_profile_add(struct rte_eth_dev *dev,
+       uint32_t tdrop_profile_id, struct rte_tm_wred_params *profile,
+       struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_tdrop_profile *tp;
+       int status;
+       uint64_t min_th;
+       uint32_t th1, th2;
+
+       /* Check input params */
+       status = ipn3ke_tm_tdrop_profile_check(dev,
+                                       tdrop_profile_id,
+                                       profile,
+                                       error);
+       if (status)
+               return status;
+
+       /* Memory allocation */
+       tp = &hw->tdrop_profile[tdrop_profile_id];
+
+       /* Fill in */
+       tp->valid = 1;
+       min_th = profile->red_params[RTE_COLOR_GREEN].min_th;
+       th1 = (uint32_t)(min_th & IPN3KE_TDROP_TH1_MASK);
+       th2 = (uint32_t)((min_th >> IPN3KE_TDROP_TH1_SHIFT) &
+                       IPN3KE_TDROP_TH2_MASK);
+       tp->th1 = th1;
+       tp->th2 = th2;
+       rte_memcpy(&tp->params, profile, sizeof(tp->params));
+
+       /* Add to list */
+       tm->h.n_tdrop_profiles++;
+
+       /* Write FPGA */
+       ipn3ke_hw_tm_tdrop_wr(hw, tp);
+
+       return 0;
+}
+
+/* Traffic manager TDROP profile delete */
+static int
+ipn3ke_tm_tdrop_profile_delete(struct rte_eth_dev *dev,
+       uint32_t tdrop_profile_id, struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_tdrop_profile *tp;
+
+       /* Check existing */
+       tp = ipn3ke_hw_tm_tdrop_profile_search(hw, tdrop_profile_id);
+       if (tp == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_WRED_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* Check unused */
+       if (tp->n_users)
+               return -rte_tm_error_set(error,
+                                       EBUSY,
+                                       RTE_TM_ERROR_TYPE_WRED_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EBUSY));
+
+       /* Set free */
+       tp->valid = 0;
+       tm->h.n_tdrop_profiles--;
+
+       /* Write FPGA */
+       ipn3ke_hw_tm_tdrop_wr(hw, tp);
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_node_add_check_parameter(uint32_t tm_id,
+       uint32_t node_id, uint32_t parent_node_id, uint32_t priority,
+       uint32_t weight, uint32_t level_id, struct rte_tm_node_params *params,
+       struct rte_tm_error *error)
+{
+       uint32_t level_of_node_id;
+       uint32_t node_index;
+       uint32_t parent_level_id;
+
+       if (node_id == RTE_TM_NODE_ID_NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* priority: must be 0, 1, 2, 3 */
+       if (priority > IPN3KE_TM_NODE_PRIORITY_HIGHEST)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PRIORITY,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* weight: must be 1 .. 255 */
+       if (weight > IPN3KE_TM_NODE_WEIGHT_MAX)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_WEIGHT,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* check node id and parent id*/
+       level_of_node_id = node_id / IPN3KE_TM_NODE_LEVEL_MOD;
+       if (level_of_node_id != level_id)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       node_index = node_id % IPN3KE_TM_NODE_LEVEL_MOD;
+       parent_level_id = parent_node_id / IPN3KE_TM_NODE_LEVEL_MOD;
+       switch (level_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               if (node_index != tm_id)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               if (parent_node_id != RTE_TM_NODE_ID_NULL)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_index >= IPN3KE_TM_VT_NODE_NUM)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               if (parent_level_id != IPN3KE_TM_NODE_LEVEL_PORT)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_index >= IPN3KE_TM_COS_NODE_NUM)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               if (parent_level_id != IPN3KE_TM_NODE_LEVEL_VT)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       /* params: must not be NULL */
+       if (params == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARAMS,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       /* No shared shapers */
+       if (params->n_shared_shapers != 0)
+               return -rte_tm_error_set(error,
+                               EINVAL,
+                               RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_SHAPERS,
+                               NULL,
+                               rte_strerror(EINVAL));
+       return 0;
+}
+
+static int
+ipn3ke_tm_node_add_check_mount(uint32_t tm_id,
+       uint32_t node_id, uint32_t parent_node_id, uint32_t level_id,
+       struct rte_tm_error *error)
+{
+       /*struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);*/
+       uint32_t node_index;
+       uint32_t parent_index;
+       uint32_t parent_index1;
+
+       node_index = node_id % IPN3KE_TM_NODE_LEVEL_MOD;
+       parent_index = parent_node_id % IPN3KE_TM_NODE_LEVEL_MOD;
+       parent_index1 = node_index / IPN3KE_TM_NODE_MOUNT_MAX;
+       switch (level_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (parent_index != tm_id)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (parent_index != parent_index1)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               break;
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       return 0;
+}
+
+/* Traffic manager node add */
+static int
+ipn3ke_tm_node_add(struct rte_eth_dev *dev,
+       uint32_t node_id, uint32_t parent_node_id, uint32_t priority,
+       uint32_t weight, uint32_t level_id, struct rte_tm_node_params *params,
+       struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node *n, *parent_node;
+       uint32_t node_state, state_mask;
+       int status;
+
+       /* Checks */
+       if (tm->hierarchy_frozen)
+               return -rte_tm_error_set(error,
+                                       EBUSY,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EBUSY));
+
+       tm_id = tm->tm_id;
+
+       status = ipn3ke_tm_node_add_check_parameter(tm_id,
+                                               node_id,
+                                               parent_node_id,
+                                               priority,
+                                               weight,
+                                               level_id,
+                                               params,
+                                               error);
+       if (status)
+               return status;
+
+       status = ipn3ke_tm_node_add_check_mount(tm_id,
+                                               node_id,
+                                               parent_node_id,
+                                               level_id,
+                                               error);
+       if (status)
+               return status;
+
+       /* Shaper profile ID must not be NONE. */
+       if (params->shaper_profile_id != RTE_TM_SHAPER_PROFILE_ID_NONE &&
+               params->shaper_profile_id != node_id)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* Memory allocation */
+       state_mask = 0;
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_IDLE);
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_CONFIGURED_DEL);
+       n = ipn3ke_hw_tm_node_search(hw, tm_id, node_id, state_mask);
+       if (!n)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       node_state = n->node_state;
+
+       /* Check parent node */
+       state_mask = 0;
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_CONFIGURED_ADD);
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_COMMITTED);
+       if (parent_node_id != RTE_TM_NODE_ID_NULL) {
+               parent_node = ipn3ke_hw_tm_node_search(hw,
+                                                       tm_id,
+                                                       parent_node_id,
+                                                       state_mask);
+               if (!parent_node)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       } else {
+               parent_node = NULL;
+       }
+
+       switch (level_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_ADD;
+               n->tm_id = tm_id;
+               tm->h.port_commit_node = n;
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_state == IPN3KE_TM_NODE_STATE_IDLE) {
+                       TAILQ_INSERT_TAIL(&tm->h.vt_commit_node_list, n, node);
+                       if (parent_node)
+                               parent_node->n_children++;
+                       tm->h.n_vt_nodes++;
+               } else if (node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       if (parent_node)
+                               parent_node->n_children++;
+                       tm->h.n_vt_nodes++;
+               }
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_ADD;
+               n->parent_node_id = parent_node_id;
+               n->tm_id = tm_id;
+               n->parent_node = parent_node;
+
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_state == IPN3KE_TM_NODE_STATE_IDLE) {
+                       TAILQ_INSERT_TAIL(&tm->h.cos_commit_node_list,
+                               n, node);
+                       if (parent_node)
+                               parent_node->n_children++;
+                       tm->h.n_cos_nodes++;
+               } else if (node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       if (parent_node)
+                               parent_node->n_children++;
+                       tm->h.n_cos_nodes++;
+               }
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_ADD;
+               n->parent_node_id = parent_node_id;
+               n->tm_id = tm_id;
+               n->parent_node = parent_node;
+
+               break;
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       /* Fill in */
+       n->priority = priority;
+       n->weight = weight;
+
+       if (n->level == IPN3KE_TM_NODE_LEVEL_COS &&
+               params->leaf.cman == RTE_TM_CMAN_TAIL_DROP)
+               n->tdrop_profile = ipn3ke_hw_tm_tdrop_profile_search(hw,
+                       params->leaf.wred.wred_profile_id);
+
+       rte_memcpy(&n->params, params, sizeof(n->params));
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_node_del_check_parameter(uint32_t tm_id,
+       uint32_t node_id, struct rte_tm_error *error)
+{
+       uint32_t level_of_node_id;
+       uint32_t node_index;
+
+       if (node_id == RTE_TM_NODE_ID_NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       /* check node id and parent id*/
+       level_of_node_id = node_id / IPN3KE_TM_NODE_LEVEL_MOD;
+       node_index = node_id % IPN3KE_TM_NODE_LEVEL_MOD;
+       switch (level_of_node_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               if (node_index != tm_id)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_index >= IPN3KE_TM_VT_NODE_NUM)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_index >= IPN3KE_TM_COS_NODE_NUM)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               break;
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       return 0;
+}
+
+/* Traffic manager node delete */
+static int
+ipn3ke_pmd_tm_node_delete(struct rte_eth_dev *dev,
+       uint32_t node_id, struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_node *n, *parent_node;
+       uint32_t tm_id;
+       int status;
+       uint32_t level_of_node_id;
+       uint32_t node_state;
+       uint32_t state_mask;
+
+       /* Check hierarchy changes are currently allowed */
+       if (tm->hierarchy_frozen)
+               return -rte_tm_error_set(error,
+                                       EBUSY,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EBUSY));
+
+       tm_id = tm->tm_id;
+
+       status = ipn3ke_tm_node_del_check_parameter(tm_id,
+                                               node_id,
+                                               error);
+       if (status)
+               return status;
+
+       /* Check existing */
+       state_mask = 0;
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_CONFIGURED_ADD);
+       IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_COMMITTED);
+       n = ipn3ke_hw_tm_node_search(hw, tm_id, node_id, state_mask);
+       if (n == NULL)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       if (n->n_children > 0)
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+
+       node_state = n->node_state;
+
+       level_of_node_id = node_id / IPN3KE_TM_NODE_LEVEL_MOD;
+
+       /* Check parent node */
+       if (n->parent_node_id != RTE_TM_NODE_ID_NULL) {
+               state_mask = 0;
+               IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_CONFIGURED_ADD);
+               IPN3KE_BIT_SET(state_mask, IPN3KE_TM_NODE_STATE_COMMITTED);
+               parent_node = ipn3ke_hw_tm_node_search(hw,
+                                               tm_id,
+                                               n->parent_node_id,
+                                               state_mask);
+               if (!parent_node)
+                       return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+               if (n->parent_node != parent_node)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+       } else {
+               parent_node = NULL;
+       }
+
+       switch (level_of_node_id) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               if (tm->h.port_node != n)
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_NODE_ID,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_DEL;
+               tm->h.port_commit_node = n;
+
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               if (node_state == IPN3KE_TM_NODE_STATE_COMMITTED) {
+                       if (parent_node)
+                               TAILQ_REMOVE(&parent_node->children_node_list,
+                                       n, node);
+                       TAILQ_INSERT_TAIL(&tm->h.vt_commit_node_list, n, node);
+                       if (parent_node)
+                               parent_node->n_children--;
+                       tm->h.n_vt_nodes--;
+               } else if (node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       if (parent_node)
+                               parent_node->n_children--;
+                       tm->h.n_vt_nodes--;
+               }
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_DEL;
+
+               break;
+
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               if (node_state == IPN3KE_TM_NODE_STATE_COMMITTED) {
+                       if (parent_node)
+                               TAILQ_REMOVE(&parent_node->children_node_list,
+                                       n, node);
+                       TAILQ_INSERT_TAIL(&tm->h.cos_commit_node_list,
+                               n, node);
+                       if (parent_node)
+                               parent_node->n_children--;
+                       tm->h.n_cos_nodes--;
+               } else if (node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       if (parent_node)
+                               parent_node->n_children--;
+                       tm->h.n_cos_nodes--;
+               }
+               n->node_state = IPN3KE_TM_NODE_STATE_CONFIGURED_DEL;
+
+               break;
+       default:
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_LEVEL_ID,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_hierarchy_commit_check(struct rte_eth_dev *dev,
+                                               struct rte_tm_error *error)
+{
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node_list *nl;
+       struct ipn3ke_tm_node *n, *parent_node;
+
+       tm_id = tm->tm_id;
+
+       nl = &tm->h.cos_commit_node_list;
+       TAILQ_FOREACH(n, nl, node) {
+               parent_node = n->parent_node;
+               if (n->node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       if (n->parent_node_id == RTE_TM_NODE_ID_NULL ||
+                               n->level != IPN3KE_TM_NODE_LEVEL_COS ||
+                               n->tm_id != tm_id ||
+                               parent_node == NULL ||
+                               (parent_node &&
+                                       parent_node->node_state ==
+                                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) ||
+                               (parent_node &&
+                                       parent_node->node_state ==
+                                               IPN3KE_TM_NODE_STATE_IDLE) ||
+                               n->shaper_profile.valid == 0) {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       }
+               } else if (n->node_state ==
+                               IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       if (n->level != IPN3KE_TM_NODE_LEVEL_COS ||
+                               n->n_children != 0) {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       } else {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       }
+               }
+       }
+
+       nl = &tm->h.vt_commit_node_list;
+       TAILQ_FOREACH(n, nl, node) {
+               parent_node = n->parent_node;
+               if (n->node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       if (n->parent_node_id == RTE_TM_NODE_ID_NULL ||
+                               n->level != IPN3KE_TM_NODE_LEVEL_VT ||
+                               n->tm_id != tm_id ||
+                               parent_node == NULL ||
+                               (parent_node &&
+                                       parent_node->node_state ==
+                                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) ||
+                               (parent_node &&
+                                       parent_node->node_state ==
+                                               IPN3KE_TM_NODE_STATE_IDLE) ||
+                               n->shaper_profile.valid == 0) {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       }
+               } else if (n->node_state ==
+                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       if (n->level != IPN3KE_TM_NODE_LEVEL_VT ||
+                               n->n_children != 0) {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       } else {
+                               return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+                       }
+               }
+       }
+
+       n = tm->h.port_commit_node;
+       if (n &&
+               (n->parent_node_id != RTE_TM_NODE_ID_NULL ||
+               n->level != IPN3KE_TM_NODE_LEVEL_PORT ||
+               n->tm_id != tm_id ||
+               n->parent_node != NULL ||
+               n->shaper_profile.valid == 0)) {
+               return -rte_tm_error_set(error,
+                                       EINVAL,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EINVAL));
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_hw_tm_node_wr(struct ipn3ke_hw *hw,
+                               struct ipn3ke_tm_node *n)
+{
+       uint32_t level;
+
+       level = n->level;
+
+       switch (level) {
+       case IPN3KE_TM_NODE_LEVEL_PORT:
+               /**
+                * Configure Type
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_TYPE_L3_X,
+                               n->node_index,
+                               n->priority,
+                               IPN3KE_QOS_TYPE_MASK);
+
+               /**
+                * Configure Sch_wt
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_SCH_WT_L3_X,
+                               n->node_index,
+                               n->weight,
+                               IPN3KE_QOS_SCH_WT_MASK);
+
+               /**
+                * Configure Shap_wt
+                */
+               if (n->shaper_profile.valid)
+                       IPN3KE_MASK_WRITE_REG(hw,
+                                       IPN3KE_QOS_SHAP_WT_L3_X,
+                                       n->node_index,
+                                       ((n->shaper_profile.e << 10) |
+                                               n->shaper_profile.m),
+                                       IPN3KE_QOS_SHAP_WT_MASK);
+
+               break;
+       case IPN3KE_TM_NODE_LEVEL_VT:
+               /**
+                * Configure Type
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_TYPE_L2_X,
+                               n->node_index,
+                               n->priority,
+                               IPN3KE_QOS_TYPE_MASK);
+
+               /**
+                * Configure Sch_wt
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_SCH_WT_L2_X,
+                               n->node_index,
+                               n->weight,
+                               IPN3KE_QOS_SCH_WT_MASK);
+
+               /**
+                * Configure Shap_wt
+                */
+               if (n->shaper_profile.valid)
+                       IPN3KE_MASK_WRITE_REG(hw,
+                                       IPN3KE_QOS_SHAP_WT_L2_X,
+                                       n->node_index,
+                                       ((n->shaper_profile.e << 10) |
+                                               n->shaper_profile.m),
+                                       IPN3KE_QOS_SHAP_WT_MASK);
+
+               /**
+                * Configure Map
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_MAP_L2_X,
+                               n->node_index,
+                               n->parent_node->node_index,
+                               IPN3KE_QOS_MAP_L2_MASK);
+
+               break;
+       case IPN3KE_TM_NODE_LEVEL_COS:
+               /**
+                * Configure Tail Drop mapping
+                */
+               if (n->tdrop_profile && n->tdrop_profile->valid) {
+                       IPN3KE_MASK_WRITE_REG(hw,
+                                       IPN3KE_CCB_QPROFILE_Q,
+                                       n->node_index,
+                                       n->tdrop_profile->tdrop_profile_id,
+                                       IPN3KE_CCB_QPROFILE_MASK);
+               }
+
+               /**
+                * Configure Type
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_TYPE_L1_X,
+                               n->node_index,
+                               n->priority,
+                               IPN3KE_QOS_TYPE_MASK);
+
+               /**
+                * Configure Sch_wt
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_SCH_WT_L1_X,
+                               n->node_index,
+                               n->weight,
+                               IPN3KE_QOS_SCH_WT_MASK);
+
+               /**
+                * Configure Shap_wt
+                */
+               if (n->shaper_profile.valid)
+                       IPN3KE_MASK_WRITE_REG(hw,
+                                       IPN3KE_QOS_SHAP_WT_L1_X,
+                                       n->node_index,
+                                       ((n->shaper_profile.e << 10) |
+                                               n->shaper_profile.m),
+                                       IPN3KE_QOS_SHAP_WT_MASK);
+
+               /**
+                * Configure COS queue to port
+                */
+               while (IPN3KE_MASK_READ_REG(hw,
+                                       IPN3KE_QM_UID_CONFIG_CTRL,
+                                       0,
+                                       0x80000000))
+                       ;
+
+               IPN3KE_MASK_WRITE_REG(hw,
+                       IPN3KE_QM_UID_CONFIG_DATA,
+                       0,
+                       (1 << 8 | n->parent_node->parent_node->node_index),
+                       0x1FF);
+
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QM_UID_CONFIG_CTRL,
+                               0,
+                               n->node_index,
+                               0xFFFFF);
+
+               while (IPN3KE_MASK_READ_REG(hw,
+                                       IPN3KE_QM_UID_CONFIG_CTRL,
+                                       0,
+                                       0x80000000))
+                       ;
+
+               /**
+                * Configure Map
+                */
+               IPN3KE_MASK_WRITE_REG(hw,
+                               IPN3KE_QOS_MAP_L1_X,
+                               n->node_index,
+                               n->parent_node->node_index,
+                               IPN3KE_QOS_MAP_L1_MASK);
+
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_hierarchy_hw_commit(struct rte_eth_dev *dev,
+                                       struct rte_tm_error *error)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(dev);
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_node_list *nl;
+       struct ipn3ke_tm_node *n, *nn, *parent_node;
+
+       n = tm->h.port_commit_node;
+       if (n) {
+               if (n->node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       tm->h.port_commit_node = NULL;
+
+                       n->node_state = IPN3KE_TM_NODE_STATE_COMMITTED;
+               } else if (n->node_state ==
+                                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       tm->h.port_commit_node = NULL;
+
+                       n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+                       n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+                       n->weight = 0;
+                       n->tm_id = RTE_TM_NODE_ID_NULL;
+               } else {
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               }
+               ipn3ke_hw_tm_node_wr(hw, n);
+       }
+
+       nl = &tm->h.vt_commit_node_list;
+       for (n = TAILQ_FIRST(nl); n != NULL; n = nn) {
+               nn = TAILQ_NEXT(n, node);
+               if (n->node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       n->node_state = IPN3KE_TM_NODE_STATE_COMMITTED;
+                       parent_node = n->parent_node;
+                       TAILQ_REMOVE(nl, n, node);
+                       TAILQ_INSERT_TAIL(&parent_node->children_node_list,
+                                               n, node);
+               } else if (n->node_state ==
+                                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       parent_node = n->parent_node;
+                       TAILQ_REMOVE(nl, n, node);
+
+                       n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+                       n->parent_node_id = RTE_TM_NODE_ID_NULL;
+                       n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+                       n->weight = 0;
+                       n->tm_id = RTE_TM_NODE_ID_NULL;
+                       n->parent_node = NULL;
+               } else {
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               }
+               ipn3ke_hw_tm_node_wr(hw, n);
+       }
+
+       nl = &tm->h.cos_commit_node_list;
+       for (n = TAILQ_FIRST(nl); n != NULL; n = nn) {
+               nn = TAILQ_NEXT(n, node);
+               if (n->node_state == IPN3KE_TM_NODE_STATE_CONFIGURED_ADD) {
+                       n->node_state = IPN3KE_TM_NODE_STATE_COMMITTED;
+                       parent_node = n->parent_node;
+                       TAILQ_REMOVE(nl, n, node);
+                       TAILQ_INSERT_TAIL(&parent_node->children_node_list,
+                                       n, node);
+               } else if (n->node_state ==
+                                       IPN3KE_TM_NODE_STATE_CONFIGURED_DEL) {
+                       n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+                       parent_node = n->parent_node;
+                       TAILQ_REMOVE(nl, n, node);
+
+                       n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+                       n->parent_node_id = RTE_TM_NODE_ID_NULL;
+                       n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+                       n->weight = 0;
+                       n->tm_id = RTE_TM_NODE_ID_NULL;
+                       n->parent_node = NULL;
+
+                       if (n->tdrop_profile)
+                               n->tdrop_profile->n_users--;
+               } else {
+                       return -rte_tm_error_set(error,
+                                               EINVAL,
+                                               RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                               NULL,
+                                               rte_strerror(EINVAL));
+               }
+               ipn3ke_hw_tm_node_wr(hw, n);
+       }
+
+       return 0;
+}
+
+static int
+ipn3ke_tm_hierarchy_commit_clear(struct rte_eth_dev *dev)
+{
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       struct ipn3ke_tm_node_list *nl;
+       struct ipn3ke_tm_node *n;
+       struct ipn3ke_tm_node *nn;
+
+       n = tm->h.port_commit_node;
+       if (n) {
+               n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               n->weight = 0;
+               n->tm_id = RTE_TM_NODE_ID_NULL;
+               n->n_children = 0;
+
+               tm->h.port_commit_node = NULL;
+       }
+
+       nl = &tm->h.vt_commit_node_list;
+       for (n = TAILQ_FIRST(nl); n != NULL; n = nn) {
+               nn = TAILQ_NEXT(n, node);
+
+               n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               n->parent_node_id = RTE_TM_NODE_ID_NULL;
+               n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               n->weight = 0;
+               n->tm_id = RTE_TM_NODE_ID_NULL;
+               n->parent_node = NULL;
+               n->n_children = 0;
+               tm->h.n_vt_nodes--;
+
+               TAILQ_REMOVE(nl, n, node);
+       }
+
+       nl = &tm->h.cos_commit_node_list;
+       for (n = TAILQ_FIRST(nl); n != NULL; n = nn) {
+               nn = TAILQ_NEXT(n, node);
+
+               n->node_state = IPN3KE_TM_NODE_STATE_IDLE;
+               n->parent_node_id = RTE_TM_NODE_ID_NULL;
+               n->priority = IPN3KE_TM_NODE_PRIORITY_NORMAL0;
+               n->weight = 0;
+               n->tm_id = RTE_TM_NODE_ID_NULL;
+               n->parent_node = NULL;
+               tm->h.n_cos_nodes--;
+
+               TAILQ_REMOVE(nl, n, node);
+       }
+
+       return 0;
+}
+
+static void
+ipn3ke_tm_show(struct rte_eth_dev *dev)
+{
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node_list *vt_nl, *cos_nl;
+       struct ipn3ke_tm_node *port_n, *vt_n, *cos_n;
+       const char *str_state[IPN3KE_TM_NODE_STATE_MAX] = {"Idle",
+                                               "CfgAdd",
+                                               "CfgDel",
+                                               "Committed"};
+
+       tm_id = tm->tm_id;
+
+       IPN3KE_AFU_PMD_DEBUG("***HQoS Tree(%d)***\n", tm_id);
+
+       port_n = tm->h.port_node;
+       IPN3KE_AFU_PMD_DEBUG("Port: (%d|%s)\n", port_n->node_index,
+                               str_state[port_n->node_state]);
+
+       vt_nl = &tm->h.port_node->children_node_list;
+       TAILQ_FOREACH(vt_n, vt_nl, node) {
+               cos_nl = &vt_n->children_node_list;
+               IPN3KE_AFU_PMD_DEBUG("    VT%d: ", vt_n->node_index);
+               TAILQ_FOREACH(cos_n, cos_nl, node) {
+                       if (cos_n->parent_node_id !=
+                               (vt_n->node_index + IPN3KE_TM_NODE_LEVEL_MOD))
+                               IPN3KE_AFU_PMD_ERR("(%d|%s), ",
+                                       cos_n->node_index,
+                                       str_state[cos_n->node_state]);
+               }
+               IPN3KE_AFU_PMD_DEBUG("\n");
+       }
+}
+
+static void
+ipn3ke_tm_show_commmit(struct rte_eth_dev *dev)
+{
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       uint32_t tm_id;
+       struct ipn3ke_tm_node_list *nl;
+       struct ipn3ke_tm_node *n;
+       const char *str_state[IPN3KE_TM_NODE_STATE_MAX] = {"Idle",
+                                               "CfgAdd",
+                                               "CfgDel",
+                                               "Committed"};
+
+       tm_id = tm->tm_id;
+
+       IPN3KE_AFU_PMD_DEBUG("***Commit Tree(%d)***\n", tm_id);
+       n = tm->h.port_commit_node;
+       IPN3KE_AFU_PMD_DEBUG("Port: ");
+       if (n)
+               IPN3KE_AFU_PMD_DEBUG("(%d|%s)",
+                       n->node_index,
+                       str_state[n->node_state]);
+       IPN3KE_AFU_PMD_DEBUG("\n");
+
+       nl = &tm->h.vt_commit_node_list;
+       IPN3KE_AFU_PMD_DEBUG("VT  : ");
+       TAILQ_FOREACH(n, nl, node) {
+               IPN3KE_AFU_PMD_DEBUG("(%d|%s), ",
+                               n->node_index,
+                               str_state[n->node_state]);
+       }
+       IPN3KE_AFU_PMD_DEBUG("\n");
+
+       nl = &tm->h.cos_commit_node_list;
+       IPN3KE_AFU_PMD_DEBUG("COS : ");
+       TAILQ_FOREACH(n, nl, node) {
+               IPN3KE_AFU_PMD_DEBUG("(%d|%s), ",
+                               n->node_index,
+                               str_state[n->node_state]);
+       }
+       IPN3KE_AFU_PMD_DEBUG("\n");
+}
+
+/* Traffic manager hierarchy commit */
+static int
+ipn3ke_tm_hierarchy_commit(struct rte_eth_dev *dev,
+       int clear_on_fail, struct rte_tm_error *error)
+{
+       struct ipn3ke_tm_internals *tm = IPN3KE_DEV_PRIVATE_TO_TM(dev);
+       int status;
+
+       /* Checks */
+       if (tm->hierarchy_frozen)
+               return -rte_tm_error_set(error,
+                                       EBUSY,
+                                       RTE_TM_ERROR_TYPE_UNSPECIFIED,
+                                       NULL,
+                                       rte_strerror(EBUSY));
+
+       ipn3ke_tm_show_commmit(dev);
+
+       status = ipn3ke_tm_hierarchy_commit_check(dev, error);
+       if (status) {
+               if (clear_on_fail)
+                       ipn3ke_tm_hierarchy_commit_clear(dev);
+               return status;
+       }
+
+       ipn3ke_tm_hierarchy_hw_commit(dev, error);
+       ipn3ke_tm_show(dev);
+
+       return 0;
+}
+
+const struct rte_tm_ops ipn3ke_tm_ops = {
+       .node_type_get = ipn3ke_pmd_tm_node_type_get,
+       .capabilities_get = ipn3ke_tm_capabilities_get,
+       .level_capabilities_get = ipn3ke_tm_level_capabilities_get,
+       .node_capabilities_get = ipn3ke_tm_node_capabilities_get,
+
+       .wred_profile_add = ipn3ke_tm_tdrop_profile_add,
+       .wred_profile_delete = ipn3ke_tm_tdrop_profile_delete,
+       .shared_wred_context_add_update = NULL,
+       .shared_wred_context_delete = NULL,
+
+       .shaper_profile_add = ipn3ke_tm_shaper_profile_add,
+       .shaper_profile_delete = ipn3ke_tm_shaper_profile_delete,
+       .shared_shaper_add_update = NULL,
+       .shared_shaper_delete = NULL,
+
+       .node_add = ipn3ke_tm_node_add,
+       .node_delete = ipn3ke_pmd_tm_node_delete,
+       .node_suspend = NULL,
+       .node_resume = NULL,
+       .hierarchy_commit = ipn3ke_tm_hierarchy_commit,
+
+       .node_parent_update = NULL,
+       .node_shaper_update = NULL,
+       .node_shared_shaper_update = NULL,
+       .node_stats_update = NULL,
+       .node_wfq_weight_mode_update = NULL,
+       .node_cman_update = NULL,
+       .node_wred_context_update = NULL,
+       .node_shared_wred_context_update = NULL,
+
+       .node_stats_read = NULL,
+};
+
+int
+ipn3ke_tm_ops_get(struct rte_eth_dev *ethdev,
+               void *arg)
+{
+       struct ipn3ke_hw *hw = IPN3KE_DEV_PRIVATE_TO_HW(ethdev);
+       struct ipn3ke_rpst *rpst = IPN3KE_DEV_PRIVATE_TO_RPST(ethdev);
+       struct rte_eth_dev *i40e_pf_eth;
+       const struct rte_tm_ops *ops;
+
+       if (!arg)
+               return -EINVAL;
+
+       if (hw->acc_tm) {
+               *(const void **)arg = &ipn3ke_tm_ops;
+       } else if (rpst->i40e_pf_eth) {
+               i40e_pf_eth = rpst->i40e_pf_eth;
+               if (i40e_pf_eth->dev_ops->tm_ops_get == NULL ||
+                       i40e_pf_eth->dev_ops->tm_ops_get(i40e_pf_eth,
+                       &ops) != 0 ||
+                       ops == NULL) {
+                       return -EINVAL;
+               }
+               *(const void **)arg = ops;
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
index ec77390..3a95efc 100644 (file)
@@ -11,5 +11,6 @@
 allow_experimental_apis = true
 
 sources += files('ipn3ke_ethdev.c',
-       'ipn3ke_representor.c')
+       'ipn3ke_representor.c',
+       'ipn3ke_tm.c')
 deps += ['bus_ifpga', 'sched']