/* 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_flow.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)
			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,
	struct ipn3ke_tm_node *parent_node)
{
	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
		 */
		if (parent_node)
			IPN3KE_MASK_WRITE_REG(hw,
					IPN3KE_QOS_MAP_L2_X,
					n->node_index,
					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))
			;

		if (parent_node && parent_node->parent_node)
			IPN3KE_MASK_WRITE_REG(hw,
				IPN3KE_QM_UID_CONFIG_DATA,
				0,
				(1 << 8 | 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
		 */
		if (parent_node)
			IPN3KE_MASK_WRITE_REG(hw,
					IPN3KE_QOS_MAP_L1_X,
					n->node_index,
					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));
		}
		parent_node = n->parent_node;
		ipn3ke_hw_tm_node_wr(hw, n, parent_node);
	}

	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, parent_node);
	}

	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, parent_node);
	}

	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;
}
