diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 0d3b688..d4d2037 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -88,6 +88,9 @@ SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_ext_port.c
 SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_serdes.c
 SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_sm.c
 SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_spico_code.c
+SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_config.c
+SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_ffu.c
+SRCS-$(CONFIG_RTE_LIBRTE_FM10K_PMD) += fm10k_stats.c
 endif
 ifeq ($(CONFIG_RTE_ARCH_X86), y)
 SRCS-$(CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR) += fm10k_rxtx_vec.c
diff --git a/drivers/net/fm10k/switch/fm10k_config.c b/drivers/net/fm10k/switch/fm10k_config.c
new file mode 100644
index 0000000..e958663
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_config.c
@@ -0,0 +1,863 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019   Silicom Ltd. Connectivity Solutions
+ */
+
+#include <unistd.h>
+
+#include <rte_time.h>
+#include <rte_kvargs.h>
+#include <rte_hash.h>
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+#include <rte_tm_driver.h>
+
+#include "../base/fm10k_type.h"
+#include "../base/fm10k_osdep.h"
+
+#include "../fm10k.h"
+#include "../fm10k_logs.h"
+#include "fm10k_debug.h"
+#include "fm10k_regs.h"
+#include "fm10k_switch.h"
+#include "fm10k_config.h"
+
+
+#define FM10K_CONFIG_WORD_MAX			10
+#define FM10K_CONFIG_WORD_LEN			20
+#define FM10K_CONFIG_STR_MAX			100
+
+/* used for store the key strings */
+struct fm10k_cfg_key_item {
+	const char *key_str[FM10K_CONFIG_WORD_MAX];
+	uint8_t type;
+};
+
+/* configuration file path */
+static const char *fm10k_config_dpdk_conf_file = "/etc/dpdk_fm10k.conf";
+/* configuration structure */
+static struct fm10k_dpdk_cfg fm10k_config_dpdk_cfg;
+
+/* configuration key strings */
+static struct fm10k_cfg_key_item fm10k_config_key_items[] = {
+		/* enable debug print */
+		{ {"debug", "print", "enable"},
+			FM10K_CONFIG_DEBUG_ENABLE},
+		{ {"debug", "print", "config"},
+			FM10K_CONFIG_DEBUG_CONFIG},
+		{ {"debug", "print", "ffu", "init"},
+			FM10K_CONFIG_DEBUG_FFU_INIT},
+		{ {"debug", "print", "ffu", "register"},
+			FM10K_CONFIG_DEBUG_FFU_REG},
+		{ {"debug", "print", "ffu", "rule"},
+			FM10K_CONFIG_DEBUG_FFU_RULE},
+		{ {"debug", "print", "stats", "port"},
+			FM10K_CONFIG_DEBUG_STATS_PORT},
+		{ {"debug", "print", "stats", "queue"},
+			FM10K_CONFIG_DEBUG_STATS_QUEUE},
+		{ {"debug", "print", "stats", "rule"},
+			FM10K_CONFIG_DEBUG_STATS_FFU},
+		{ {"debug", "print", "stats", "detail"},
+			FM10K_CONFIG_DEBUG_STATS_MORE},
+		{ {"debug", "print", "stats", "interval"},
+			FM10K_CONFIG_DEBUG_STATS_INTERVAL},
+		/* general configuration */
+		{ {"dpdk", "bind", "pf", "number"},
+			FM10K_CONFIG_BIND_PF_NUMBER},
+		{ {"extern", "port", "speed"},
+			FM10K_CONFIG_EXT_PORT_SPEED},
+		/* internal redirect configuration */
+		{ {"dpdk", "port", "*", "map", "pf"},
+			FM10K_CONFIG_DPDK_PORT_MAP_PF},
+		{ {"extern", "port", "*", "map", "pf"},
+			FM10K_CONFIG_EXT_PORT_MAP_PF},
+		/* external redirect configuration */
+		{ {"flowset", "start"},
+			FM10K_CONFIG_FLOWSET_START},
+		{ {"flowset", "stop"},
+			FM10K_CONFIG_FLOWSET_STOP},
+		{ {"flowset", "enable"},
+			FM10K_CONFIG_FLOWSET_ENABLE},
+		{ {"flow", "*", "condition", "source", "extern", "port"},
+			FM10K_CONFIG_FLOW_COND_SRC_EXT_PORT},
+		{ {"flow", "*", "condition", "source", "dpdk", "port"},
+			FM10K_CONFIG_FLOW_COND_SRC_DPDK_PORT},
+		{ {"flow", "*", "condition", "vlan"},
+			FM10K_CONFIG_FLOW_COND_VLAN},
+		{ {"flow", "*", "action", "forward", "extern", "port"},
+			FM10K_CONFIG_FLOW_ACT_FW_EXT_PORT},
+		{ {"flow", "*", "action", "forward", "dpdk", "port"},
+			FM10K_CONFIG_FLOW_ACT_FW_DPDK_PORT},
+		{ {"flow", "*", "action", "forward", "vlan"},
+			FM10K_CONFIG_FLOW_ACT_FW_VALN},
+};
+
+static char default_flowset[10] = "default";
+
+static struct fm10k_cfg_config_item fm10k_silc_nic_2ext_2pep[] = {
+	{   FM10K_CONFIG_BIND_PF_NUMBER, FM10K_CONFIG_VALUE_INT, 0,
+		"# Tell how many PF ports are bound in DPDK.\n"
+		"# Driver will check the number when initializes.",
+		.val.int64 = 2
+	},
+	{   FM10K_CONFIG_EXT_PORT_SPEED, FM10K_CONFIG_VALUE_INT, 0,
+		"# Set external port speed, 40 means 40G, 100 means 100G.",
+		.val.int64 = 100
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 0,
+		"# Map 1 or 2 PF ports to one DPDK port.\n"
+		"# If mapped PF number is 2, traffic will be\n"
+		"# load balance between the 2 PF.\n"
+		"# And the DPDK port queue number will be configured\n"
+		"# more than 2(each PF need at least 1 DPDK port queue).",
+		.val.int64 = 0
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"",
+		.val.int64 = 1
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"# Map 1 or 2 PF to one external port. If mapped PF number is 2,\n"
+		"# traffic will be load balance between the 2 PF. ",
+		.val.int64 = 0
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 2,
+		"",
+		.val.int64 = 1
+	},
+	{   FM10K_CONFIG_FLOWSET_START, FM10K_CONFIG_VALUE_STR, 0,
+		"# Define flow rule",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_FLOWSET_STOP, FM10K_CONFIG_VALUE_STR, 0,
+		"",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_FLOWSET_ENABLE, FM10K_CONFIG_VALUE_STR, 0,
+		"",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_TYPE_NULL, 0, 0,
+		"",
+		.val.int64 = 0
+	},
+};
+
+static struct fm10k_cfg_config_item fm10k_silc_nic_2ext_4pep[] = {
+	{   FM10K_CONFIG_BIND_PF_NUMBER, FM10K_CONFIG_VALUE_INT, 0,
+		"# Tell how many PF ports are bound in DPDK.\n"
+		"# Driver will check the  number when initializes.",
+		.val.int64 = 4
+	},
+
+	{   FM10K_CONFIG_EXT_PORT_SPEED, FM10K_CONFIG_VALUE_INT, 0,
+		"# Set external port speed, 40 means 40G, 100 means 100G.",
+		.val.int64 = 100
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 0,
+		"# Map 1 or 2 PF ports to one DPDK port.\n"
+		"# If mapped PF number is 2, traffic will be\n"
+		"# load balance between the 2 PFs.\n"
+		"# And the DPDK port queue number will be configured\n"
+		"# more than 2(each PF need at least 1 DPDK port queue).",
+		.val.int64 = 0
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 0,
+		"",
+		.val.int64 = 2
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"",
+		.val.int64 = 1
+	},
+	{   FM10K_CONFIG_DPDK_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"",
+		.val.int64 = 3
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"# Map 1 or 2 PF to one external port. If mapped PF number is 2,\n"
+		"# traffic will be load balance between the 2 PF.",
+		.val.int64 = 0
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 1,
+		"",
+		.val.int64 = 2
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 2,
+		"",
+		.val.int64 = 1
+	},
+	{   FM10K_CONFIG_EXT_PORT_MAP_PF, FM10K_CONFIG_VALUE_INT, 2,
+		"",
+		.val.int64 = 3
+	},
+	{   FM10K_CONFIG_FLOWSET_START, FM10K_CONFIG_VALUE_STR, 0,
+		"# Define flow rule",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_FLOWSET_STOP, FM10K_CONFIG_VALUE_STR, 0,
+		"",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_FLOWSET_ENABLE, FM10K_CONFIG_VALUE_STR, 0,
+		"",
+		.val.str = default_flowset
+	},
+	{   FM10K_CONFIG_TYPE_NULL, 0, 0,
+		"",
+		.val.int64 = 0
+	},
+};
+
+
+static int
+fm10k_config_conf_line_parser(char *buff, struct fm10k_cfg_config_item *item)
+{
+	char *p;
+	char *endptr;
+	const char *cfg_delims = { " " };
+	const char *key_delims = { "." };
+	struct fm10k_cfg_key_item *key;
+	char cfgs[3][FM10K_CONFIG_STR_MAX];
+	char cmp_keys[FM10K_CONFIG_WORD_MAX][FM10K_CONFIG_WORD_LEN];
+	long key_param = 0;
+	uint16_t i, j;
+	uint8_t val_type = FM10K_CONFIG_VALUE_NULL;
+
+	for (i = 0; i < 3; i++) {
+		if (i == 0)
+			p = strtok(buff, cfg_delims);
+		else
+			p = strtok(NULL, cfg_delims);
+		if (p == NULL)
+			return -1;
+		strncpy(cfgs[i], p, FM10K_CONFIG_STR_MAX);
+	}
+
+	p = strtok(NULL, cfg_delims);
+	if (p)
+		return -1;
+
+	memset(cmp_keys, 0, sizeof(cmp_keys));
+	for (i = 0; i < FM10K_CONFIG_WORD_MAX; i++) {
+		if (i == 0)
+			p = strtok(cfgs[0], key_delims);
+		else
+			p = strtok(NULL, key_delims);
+		if (p == NULL)
+			break;
+		strncpy(cmp_keys[i], p, FM10K_CONFIG_WORD_LEN);
+	}
+
+	if (strcmp(cfgs[1], "int") == 0)
+		val_type = FM10K_CONFIG_VALUE_INT;
+	else if (strcmp(cfgs[1], "string") == 0)
+		val_type = FM10K_CONFIG_VALUE_STR;
+	else
+		return -1;
+
+	for (i = 0;
+			i < sizeof(fm10k_config_key_items) /
+				sizeof(fm10k_config_key_items[0]);
+			i++) {
+		key = &fm10k_config_key_items[i];
+		for (j = 0; j < FM10K_CONFIG_WORD_MAX; j++)	{
+			if (key->key_str[j] &&
+					strlen(cmp_keys[j]) > 0) {
+				if (strcmp(key->key_str[j], "*") == 0) {
+					key_param =
+					strtol(cmp_keys[j], &endptr, 10);
+					if ((key_param == 0 &&
+						endptr == cmp_keys[j]) ||
+						(endptr != cmp_keys[j] &&
+						strlen(endptr) != 0))
+						break;
+				} else if (strcmp(key->key_str[j],
+							cmp_keys[j]) != 0) {
+					break;
+				}
+			} else if (key->key_str[j] == 0 &&
+					strlen(cmp_keys[j]) == 0) {
+				if (val_type == FM10K_CONFIG_VALUE_STR) {
+					item->val.str =
+					malloc(strlen(cfgs[2]) + 1);
+					if (item->val.str == NULL)
+						return -1;
+					strcpy(item->val.str, cfgs[2]);
+				} else {
+					item->val.int64 =
+					strtol(cfgs[2], NULL, 10);
+				}
+				item->type = key->type;
+				item->key_param = key_param;
+				item->val_type = val_type;
+				return 0;
+			}
+		}
+	}
+	return -1;
+}
+
+static bool
+fm10k_config_blank_line_check(char *line)
+{
+	uint16_t i;
+
+	for (i = 0; i < strlen(line); i++) {
+		if (line[i] == ' ' ||
+				line[i] == '\n' ||
+				line[i] == '\t' || line[i] == 0x0d) /* 0d: CR */
+			continue;
+		else
+			return false;
+	}
+	return true;
+}
+
+static int
+fm10k_config_conf_file_load(void)
+{
+	int i = 0;
+	FILE *fp;
+	char buff[255];
+	struct fm10k_cfg_config_item *item;
+
+	fp = fopen(fm10k_config_dpdk_conf_file, "r");
+	if (fp == NULL)
+		return -1;
+
+	fm10k_config_dpdk_cfg.config_list =	malloc
+		(sizeof(struct fm10k_cfg_config_item) * FM10K_SW_CONFIG_MAX);
+	if (fm10k_config_dpdk_cfg.config_list == NULL)
+		return -1;
+	memset(fm10k_config_dpdk_cfg.config_list, 0,
+		sizeof(struct fm10k_cfg_config_item) * FM10K_SW_CONFIG_MAX);
+
+	while (fgets(buff, sizeof(buff), fp)) {
+		if (buff[0] == '#' || buff[0] == 0)
+			continue;
+
+		if (fm10k_config_blank_line_check(buff))
+			continue;
+
+		item = &fm10k_config_dpdk_cfg.config_list[i++];
+		if (fm10k_config_conf_line_parser(buff, item) < 0) {
+			FM10K_SW_ERR("Unknown configuration: %s", buff);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+struct fm10k_hw*
+fm10k_config_hw_get(int port_no)
+{
+	int hw_port;
+
+	hw_port = fm10k_config_dpdk_cfg.dpdk_port_map[port_no].map_no[0];
+	return fm10k_config_dpdk_cfg.pf_hw[hw_port];
+}
+
+struct fm10k_cfg_flowset *
+fm10k_config_flowset_current_get(void)
+{
+	return fm10k_config_dpdk_cfg.current;
+}
+
+void
+fm10k_config_flowset_current_set(struct fm10k_cfg_flowset *new)
+{
+	fm10k_config_dpdk_cfg.current = new;
+}
+
+bool
+fm10k_config_flow_list_end(struct fm10k_cfg_flow *list,
+		struct fm10k_cfg_flow *flow)
+{
+	return (list == flow);
+}
+
+static void
+fm10k_config_flow_list_init(struct fm10k_cfg_flow *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+static void
+fm10k_config_flow_list_add(struct fm10k_cfg_flow *new,
+		struct fm10k_cfg_flow *list)
+{
+	struct fm10k_cfg_flow *next = list->next;
+	next->prev = new;
+	new->next = next;
+	new->prev = list;
+	list->next = new;
+}
+
+void
+fm10k_config_flow_list_add_tail(struct fm10k_cfg_flowset *flowset,
+		struct fm10k_cfg_flow *flow)
+{
+	fm10k_config_flow_list_add(flow, flowset->flow_head.prev);
+}
+
+void
+fm10k_config_flow_list_delete(struct fm10k_cfg_flow *flow)
+{
+	flow->prev->next = flow->next;
+	flow->next->prev = flow->prev;
+	flow->prev = NULL;
+	flow->next = NULL;
+}
+
+struct fm10k_cfg_flowset *
+fm10k_config_flowset_get(const char *name)
+{
+	struct fm10k_cfg_flowset *flowset;
+
+	flowset = fm10k_config_dpdk_cfg.flowset_head.next;
+	while (flowset) {
+		if (strcmp(flowset->name, name) == 0)
+			break;
+		flowset = flowset->next;
+	}
+
+	if (flowset == NULL) {
+		flowset = malloc(sizeof(struct fm10k_cfg_flowset));
+		if (flowset == NULL)
+			return NULL;
+		strcpy(flowset->name, name);
+		fm10k_config_flow_list_init(&flowset->flow_head);
+		flowset->next = fm10k_config_dpdk_cfg.flowset_head.next;
+		fm10k_config_dpdk_cfg.flowset_head.next = flowset;
+	}
+	return flowset;
+}
+
+
+static int
+fm10k_cfg_flow_item_set(struct fm10k_cfg_flowset *flowset,
+		uint8_t flow_no, uint8_t type, int64_t val)
+{
+	struct fm10k_cfg_flow *tmp;
+	struct fm10k_cfg_flow *flow = NULL;
+
+	if (flowset == NULL)
+		return -1;
+
+	tmp = flowset->flow_head.next;
+	while (!fm10k_config_flow_list_end(&flowset->flow_head, tmp)) {
+		if (tmp->flow_no == flow_no) {
+			flow = tmp;
+			break;
+		}
+		tmp = tmp->next;
+	}
+	if (flow == NULL) {
+		flow = malloc(sizeof(struct fm10k_cfg_flow));
+		memset(flow, 0, sizeof(struct fm10k_cfg_flow));
+		flow->flow_no = flow_no;
+		fm10k_config_flow_list_add_tail(flowset, flow);
+	}
+	if (flow == NULL)
+		return -1;
+
+	switch (type) {
+	case FM10K_CONFIG_FLOW_COND_SRC_EXT_PORT:
+		flow->src_port.port_type = FM10K_CONFIG_FLOW_EXT_PORT;
+		flow->src_port.port_no = val;
+		break;
+	case FM10K_CONFIG_FLOW_COND_SRC_DPDK_PORT:
+		flow->src_port.port_type = FM10K_CONFIG_FLOW_DPDK_PORT;
+		flow->src_port.port_no = val;
+		break;
+	case FM10K_CONFIG_FLOW_COND_VLAN:
+		flow->src_port.vlan_id = val;
+		break;
+	case FM10K_CONFIG_FLOW_ACT_FW_EXT_PORT:
+		if (flow->fw_port[0].port_type == FM10K_CONFIG_FLOW_NONE_PORT) {
+			flow->fw_port[0].port_type = FM10K_CONFIG_FLOW_EXT_PORT;
+			flow->fw_port[0].port_no = val;
+		} else {
+			flow->fw_port[1].port_type = FM10K_CONFIG_FLOW_EXT_PORT;
+			flow->fw_port[1].port_no = val;
+		}
+		break;
+	case FM10K_CONFIG_FLOW_ACT_FW_DPDK_PORT:
+		if (flow->fw_port[0].port_type == FM10K_CONFIG_FLOW_NONE_PORT) {
+			flow->fw_port[0].port_type =
+					FM10K_CONFIG_FLOW_DPDK_PORT;
+			flow->fw_port[0].port_no = val;
+		} else {
+			flow->fw_port[1].port_type =
+					FM10K_CONFIG_FLOW_DPDK_PORT;
+			flow->fw_port[1].port_no = val;
+		}
+		break;
+	case FM10K_CONFIG_FLOW_ACT_FW_VALN:
+		if (flow->fw_port[0].vlan_id == 0)
+			flow->fw_port[0].vlan_id = val;
+		else
+			flow->fw_port[1].vlan_id = val;
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+fm10k_config_conf_file_transfer(void)
+{
+	int i;
+	int offset;
+	uint32_t tmp, data;
+	struct fm10k_cfg_config_item *item;
+	struct fm10k_cfg_flowset *flowset = NULL;
+	struct fm10k_cfg_port_pf_map *map;
+
+	for (i = 0; i < FM10K_SW_CONFIG_MAX; i++) {
+		item = &fm10k_config_dpdk_cfg.config_list[i];
+		if (item->type == FM10K_CONFIG_TYPE_NULL)
+			break;
+
+		switch (item->type)	{
+		case FM10K_CONFIG_BIND_PF_NUMBER:
+			fm10k_config_dpdk_cfg.pf_num = item->val.int64;
+			break;
+		case FM10K_CONFIG_EXT_PORT_SPEED:
+			fm10k_config_dpdk_cfg.ext_port_speed = item->val.int64;
+			break;
+		case FM10K_CONFIG_DPDK_PORT_MAP_PF:
+			map =
+			&fm10k_config_dpdk_cfg.dpdk_port_map[item->key_param];
+			if (map->type == FM10K_CONFIG_PORT_MAP_PF) {
+				map->type = FM10K_CONFIG_PORT_MAP_PFS;
+				map->map_no[1] = item->val.int64;
+			} else {
+				map->type = FM10K_CONFIG_PORT_MAP_PF;
+				map->map_no[0] = item->val.int64;
+			}
+			break;
+		case FM10K_CONFIG_EXT_PORT_MAP_PF:
+			map =
+				&fm10k_config_dpdk_cfg.ext_port_map
+				[item->key_param - 1];
+			if (map->type == FM10K_CONFIG_PORT_MAP_PF) {
+				map->type = FM10K_CONFIG_PORT_MAP_PFS;
+				map->map_no[1] = item->val.int64;
+			} else {
+				map->type = FM10K_CONFIG_PORT_MAP_PF;
+				map->map_no[0] = item->val.int64;
+			}
+			break;
+
+		case FM10K_CONFIG_DEBUG_ENABLE:
+		case FM10K_CONFIG_DEBUG_CONFIG:
+		case FM10K_CONFIG_DEBUG_FFU_INIT:
+		case FM10K_CONFIG_DEBUG_FFU_REG:
+		case FM10K_CONFIG_DEBUG_FFU_RULE:
+		case FM10K_CONFIG_DEBUG_STATS_PORT:
+		case FM10K_CONFIG_DEBUG_STATS_QUEUE:
+		case FM10K_CONFIG_DEBUG_STATS_FFU:
+		case FM10K_CONFIG_DEBUG_STATS_MORE:
+			offset = item->type - FM10K_CONFIG_DEBUG_START;
+			tmp = fm10k_config_dpdk_cfg.debug_cfg;
+			data = 1;
+			if (item->val.int64 != 1)
+				fm10k_config_dpdk_cfg.debug_cfg =
+						tmp & ~(data << offset);
+			else
+				fm10k_config_dpdk_cfg.debug_cfg |=
+						(data << offset);
+			break;
+		case FM10K_CONFIG_DEBUG_STATS_INTERVAL:
+			fm10k_config_dpdk_cfg.stats_interval = item->val.int64;
+			break;
+
+		case FM10K_CONFIG_FLOWSET_START:
+			/* skip /n */
+			item->val.str[strlen(item->val.str) - 1] = 0;
+			flowset = fm10k_config_flowset_get(item->val.str);
+			if (flowset == NULL)
+				return -1;
+			break;
+		case FM10K_CONFIG_FLOWSET_STOP:
+			flowset = NULL;
+			break;
+		case FM10K_CONFIG_FLOWSET_ENABLE:
+			/* skip /n */
+			item->val.str[strlen(item->val.str) - 1] = 0;
+			fm10k_config_dpdk_cfg.current =
+					fm10k_config_flowset_get(item->val.str);
+			break;
+		case FM10K_CONFIG_FLOW_COND_SRC_EXT_PORT:
+		case FM10K_CONFIG_FLOW_COND_SRC_DPDK_PORT:
+		case FM10K_CONFIG_FLOW_COND_VLAN:
+		case FM10K_CONFIG_FLOW_ACT_FW_EXT_PORT:
+		case FM10K_CONFIG_FLOW_ACT_FW_DPDK_PORT:
+		case FM10K_CONFIG_FLOW_ACT_FW_VALN:
+			if (flowset == NULL ||
+					fm10k_cfg_flow_item_set(flowset,
+					item->key_param, item->type,
+					item->val.int64) != 0)
+				return -1;
+			break;
+		default:
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static bool
+fm10k_config_conf_file_exist(void)
+{
+	if (access(fm10k_config_dpdk_conf_file, 0) == 0)
+		return true;
+	return false;
+}
+
+static int
+fm10k_config_conf_file_item_create(FILE *fp, char *buff,
+		struct fm10k_cfg_key_item *key,
+		struct fm10k_cfg_config_item *item)
+{
+	int i;
+
+	if (item->type != key->type)
+		return 0;
+
+	for (i = 0; i < FM10K_CONFIG_WORD_MAX; i++) {
+		if (key->key_str[i] == 0)
+			break;
+		if (i == 0) {
+			sprintf(buff + strlen(buff),
+				"%s", key->key_str[i]);
+		} else {
+			if (strcmp(key->key_str[i], "*") == 0)
+				sprintf(buff + strlen(buff),
+						".%u", item->key_param);
+			else
+				sprintf(buff + strlen(buff),
+						".%s", key->key_str[i]);
+		}
+	}
+	if (item->val_type == FM10K_CONFIG_VALUE_INT)
+		sprintf(buff + strlen(buff),
+				" int %lld\n", (long long)item->val.int64);
+	else if (item->val_type == FM10K_CONFIG_VALUE_STR)
+		sprintf(buff + strlen(buff), " string %s\n", item->val.str);
+	else
+		return -1;
+	fwrite(buff, strlen(buff), 1, fp);
+	FM10K_SW_TRACE("[write] %s", buff);
+
+	return 0;
+}
+
+static int
+fm10k_config_conf_file_create(void)
+{
+	uint16_t i, j;
+	struct fm10k_cfg_key_item *key;
+	struct fm10k_cfg_config_item *item;
+	FILE *fp;
+	char buff[255] = "";
+
+	fp = fopen(fm10k_config_dpdk_conf_file, "w");
+	if (fp == NULL)
+		return -1;
+
+	for (i = 0; i < FM10K_SW_CONFIG_MAX; i++) {
+		item = &fm10k_config_dpdk_cfg.config_list[i];
+		if (item->type == FM10K_CONFIG_TYPE_NULL)
+			break;
+		buff[0] = 0;
+		if (strlen(item->describe) > 0)
+			sprintf(buff, "\n\n%s\n", item->describe);
+		for (j = 0;
+				j < sizeof(fm10k_config_key_items) /
+					sizeof(fm10k_config_key_items[0]);
+				j++) {
+			key = &fm10k_config_key_items[j];
+			if (fm10k_config_conf_file_item_create
+					(fp, buff, key, item) != 0) {
+				fclose(fp);
+				return -1;
+			}
+		}
+	}
+	fclose(fp);
+	return 0;
+}
+
+void
+fm10k_config_cfg_flowset_show(void)
+{
+	struct fm10k_cfg_flowset *flowset;
+
+	FM10K_SW_INFO("  FLOWSET ENABLE: %s\n",
+			fm10k_config_dpdk_cfg.current->name);
+
+	flowset = fm10k_config_dpdk_cfg.flowset_head.next;
+	while (flowset) {
+		FM10K_SW_INFO("\n  FLOWSET  : %s\n", flowset->name);
+		struct fm10k_cfg_flow *flow = flowset->flow_head.next;
+		while (!fm10k_config_flow_list_end(&flowset->flow_head, flow)) {
+			const char *port_type[3] = { "NON", "EXT", "DPDK" };
+			if (flow->fw_port[1].port_type !=
+					FM10K_CONFIG_FLOW_NONE_PORT) {
+				FM10K_SW_INFO("  FLOW %d : %4s PORT %d VLAN %4d --> "
+					"%4s PORT %d VLAN %4d & %4s PORT %d VLAN %4d\n",
+					flow->flow_no,
+					port_type[flow->src_port.port_type],
+					flow->src_port.port_no,
+					flow->src_port.vlan_id,
+					port_type[flow->fw_port[0].port_type],
+					flow->fw_port[0].port_no,
+					flow->fw_port[0].vlan_id,
+					port_type[flow->fw_port[1].port_type],
+					flow->fw_port[1].port_no,
+					flow->fw_port[1].vlan_id);
+			} else {
+				FM10K_SW_INFO("  FLOW %d : %4s PORT %d VLAN %4d --> "
+					"%4s PORT %d VLAN %4d\n",
+					flow->flow_no,
+					port_type[flow->src_port.port_type],
+					flow->src_port.port_no,
+					flow->src_port.vlan_id,
+					port_type[flow->fw_port[0].port_type],
+					flow->fw_port[0].port_no,
+					flow->fw_port[0].vlan_id);
+			}
+			flow = flow->next;
+		}
+		flowset = flowset->next;
+	}
+}
+
+static void
+fm10k_config_cfg_describe(struct fm10k_switch *sw,
+		struct fm10k_device_info *info)
+{
+	uint16_t i;
+	struct fm10k_cfg_flowset *flowset;
+
+	if (!fm10k_config_check_debug(sw->dpdk_cfg, FM10K_CONFIG_DEBUG_CONFIG))
+		return;
+
+	FM10K_SW_INFO("--- FM10K STATIC CONFIG ---\n");
+	FM10K_SW_INFO("  Card Type: %s\n", info->desc);
+	FM10K_SW_INFO("  PF Max   : %d\n", fm10k_config_dpdk_cfg.pf_max);
+	FM10K_SW_INFO("  PF Bind  : %d\n", fm10k_config_dpdk_cfg.pf_num);
+	FM10K_SW_INFO("  DEBUG    : %#x\n", fm10k_config_dpdk_cfg.debug_cfg);
+	FM10K_SW_INFO("  STATS GAP: %d sec\n",
+			fm10k_config_dpdk_cfg.stats_interval);
+	FM10K_SW_INFO("  EXT PORT speed: %d Gbps\n",
+			fm10k_config_dpdk_cfg.ext_port_speed);
+	FM10K_SW_INFO("  FLOWSET ENABLE: %s\n",
+			fm10k_config_dpdk_cfg.current->name);
+
+	for (i = 0; i < fm10k_config_dpdk_cfg.ext_port_num; i++) {
+		if (fm10k_config_dpdk_cfg.ext_port_map[i].type ==
+				FM10K_CONFIG_PORT_MAP_NULL)
+			continue;
+		if (fm10k_config_dpdk_cfg.ext_port_map[i].type ==
+				FM10K_CONFIG_PORT_MAP_PF)
+			FM10K_SW_INFO("  EXT PORT[%d] MAP: PF%d\n", i + 1,
+			fm10k_config_dpdk_cfg.ext_port_map[i].map_no[0]);
+		else
+			FM10K_SW_INFO("  EXT PORT[%d] MAP: PF%d PF%d\n", i + 1,
+			fm10k_config_dpdk_cfg.ext_port_map[i].map_no[0],
+			fm10k_config_dpdk_cfg.ext_port_map[i].map_no[1]);
+	}
+
+	for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++) {
+		if (fm10k_config_dpdk_cfg.dpdk_port_map[i].type ==
+				FM10K_CONFIG_PORT_MAP_NULL)
+			continue;
+		if (fm10k_config_dpdk_cfg.ext_port_map[i].type ==
+				FM10K_CONFIG_PORT_MAP_PF)
+			FM10K_SW_INFO("  DPDK PORT[%d] MAP: PF%d\n", i,
+			fm10k_config_dpdk_cfg.dpdk_port_map[i].map_no[0]);
+		else
+			FM10K_SW_INFO("  DPDK PORT[%d] MAP: PF%d PF%d\n", i,
+			fm10k_config_dpdk_cfg.dpdk_port_map[i].map_no[0],
+			fm10k_config_dpdk_cfg.dpdk_port_map[i].map_no[1]);
+	}
+
+	flowset = fm10k_config_dpdk_cfg.flowset_head.next;
+	while (flowset) {
+		FM10K_SW_INFO("\n  FLOWSET  : %s\n", flowset->name);
+		struct fm10k_cfg_flow *flow = flowset->flow_head.next;
+		while (!fm10k_config_flow_list_end(&flowset->flow_head, flow)) {
+			const char *port_type[3] = { "NON", "EXT", "DPDK" };
+			if (flow->fw_port[1].port_type !=
+					FM10K_CONFIG_FLOW_NONE_PORT) {
+				FM10K_SW_INFO("  FLOW %d : %4s PORT %d VLAN %4d"
+					" --> %4s PORT %d VLAN %4d & %4s PORT %d"
+					" VLAN %4d\n",
+					flow->flow_no,
+					port_type[flow->src_port.port_type],
+					flow->src_port.port_no,
+					flow->src_port.vlan_id,
+					port_type[flow->fw_port[0].port_type],
+					flow->fw_port[0].port_no,
+					flow->fw_port[0].vlan_id,
+					port_type[flow->fw_port[1].port_type],
+					flow->fw_port[1].port_no,
+					flow->fw_port[1].vlan_id);
+			} else {
+				FM10K_SW_INFO("  FLOW %d : %4s PORT %d VLAN %4d"
+					" --> %4s PORT %d VLAN %4d\n",
+					flow->flow_no,
+					port_type[flow->src_port.port_type],
+					flow->src_port.port_no,
+					flow->src_port.vlan_id,
+					port_type[flow->fw_port[0].port_type],
+					flow->fw_port[0].port_no,
+					flow->fw_port[0].vlan_id);
+			}
+			flow = flow->next;
+		}
+		flowset = flowset->next;
+	}
+	FM10K_SW_INFO("\n");
+}
+
+int
+fm10k_config_init(struct fm10k_switch *sw, struct fm10k_hw *hw)
+{
+	struct fm10k_device_info *info = fm10k_get_device_info(hw);
+
+	fm10k_config_dpdk_cfg.stats_interval = 2;
+	fm10k_config_dpdk_cfg.pf_max = info->num_peps;
+	fm10k_config_dpdk_cfg.ext_port_num = info->num_ext_ports;
+
+	if (!fm10k_config_conf_file_exist()) {
+		if (info->num_epls == 2 && info->num_peps == 2)
+			fm10k_config_dpdk_cfg.config_list =
+					fm10k_silc_nic_2ext_2pep;
+		else if (info->num_epls == 2 && info->num_peps == 4)
+			fm10k_config_dpdk_cfg.config_list =
+					fm10k_silc_nic_2ext_4pep;
+		else
+			return -1;
+
+		fm10k_config_conf_file_create();
+	} else {
+		if (fm10k_config_conf_file_load() < 0)
+			return -1;
+	}
+
+	if (fm10k_config_conf_file_transfer() < 0)
+		return -1;
+	sw->dpdk_cfg = &fm10k_config_dpdk_cfg;
+
+	fm10k_config_cfg_describe(sw, info);
+
+	return 0;
+}
diff --git a/drivers/net/fm10k/switch/fm10k_config.h b/drivers/net/fm10k/switch/fm10k_config.h
new file mode 100644
index 0000000..f1c6ee4
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_config.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019   Silicom Ltd. Connectivity Solutions
+ */
+
+#ifndef _FM10K_CONFIG_H_
+#define _FM10K_CONFIG_H_
+
+
+#include <stdint.h>
+#include "fm10k_switch.h"
+
+/* General configuration */
+#define FM10K_CONFIG_TYPE_NULL			0
+#define FM10K_CONFIG_BIND_PF_NUMBER		1
+#define FM10K_CONFIG_EXT_PORT_SPEED		2
+
+/* Internal redirect configuration */
+#define FM10K_CONFIG_DPDK_PORT_MAP_PF		10
+#define FM10K_CONFIG_EXT_PORT_MAP_PF		11
+
+/* Debug configuration */
+#define FM10K_CONFIG_DEBUG_START		50
+#define FM10K_CONFIG_DEBUG_ENABLE		(FM10K_CONFIG_DEBUG_START + 0)
+#define FM10K_CONFIG_DEBUG_CONFIG		(FM10K_CONFIG_DEBUG_START + 1)
+#define FM10K_CONFIG_DEBUG_FFU_INIT		(FM10K_CONFIG_DEBUG_START + 2)
+#define FM10K_CONFIG_DEBUG_FFU_REG		(FM10K_CONFIG_DEBUG_START + 3)
+#define FM10K_CONFIG_DEBUG_FFU_RULE		(FM10K_CONFIG_DEBUG_START + 4)
+#define FM10K_CONFIG_DEBUG_STATS_PORT		(FM10K_CONFIG_DEBUG_START + 5)
+#define FM10K_CONFIG_DEBUG_STATS_QUEUE		(FM10K_CONFIG_DEBUG_START + 6)
+#define FM10K_CONFIG_DEBUG_STATS_FFU		(FM10K_CONFIG_DEBUG_START + 7)
+#define FM10K_CONFIG_DEBUG_STATS_MORE		(FM10K_CONFIG_DEBUG_START + 8)
+#define FM10K_CONFIG_DEBUG_STATS_INTERVAL	(FM10K_CONFIG_DEBUG_START + 9)
+
+/* external redirect configuration */
+#define FM10K_CONFIG_FLOWSET_START		80
+#define FM10K_CONFIG_FLOWSET_STOP		81
+#define FM10K_CONFIG_FLOWSET_ENABLE		82
+
+#define FM10K_CONFIG_FLOW_COND_START		100
+#define FM10K_CONFIG_FLOW_COND_SRC_EXT_PORT	\
+		(FM10K_CONFIG_FLOW_COND_START + 0)
+#define FM10K_CONFIG_FLOW_COND_SRC_DPDK_PORT	\
+		(FM10K_CONFIG_FLOW_COND_START + 1)
+#define FM10K_CONFIG_FLOW_COND_VLAN		\
+		(FM10K_CONFIG_FLOW_COND_START + 2)
+#define FM10K_CONFIG_FLOW_COND_END		\
+		(FM10K_CONFIG_FLOW_COND_START + 2)
+
+#define FM10K_CONFIG_FLOW_ACT_START		200
+#define FM10K_CONFIG_FLOW_ACT_FW_EXT_PORT	\
+		(FM10K_CONFIG_FLOW_ACT_START + 0)
+#define FM10K_CONFIG_FLOW_ACT_FW_DPDK_PORT	\
+		(FM10K_CONFIG_FLOW_ACT_START + 1)
+#define FM10K_CONFIG_FLOW_ACT_FW_VALN		\
+		(FM10K_CONFIG_FLOW_ACT_START + 2)
+#define FM10K_CONFIG_FLOW_ACT_END		\
+		(FM10K_CONFIG_FLOW_ACT_START + 2)
+
+#define FM10K_CONFIG_FLOW_NONE_PORT		0
+#define FM10K_CONFIG_FLOW_EXT_PORT		1
+#define FM10K_CONFIG_FLOW_DPDK_PORT		2
+
+#define FM10K_CONFIG_VALUE_NULL			0
+#define FM10K_CONFIG_VALUE_INT			1
+#define FM10K_CONFIG_VALUE_STR			2
+
+/* SWITCH config */
+#define FM10K_CONFIG_PORT_MAP_NULL		0
+#define FM10K_CONFIG_PORT_MAP_PF		1
+#define FM10K_CONFIG_PORT_MAP_PFS		2
+#define FM10K_CONFIG_PORT_MAP_PFSS		3
+
+/* DPDK port */
+#define	FM10K_CONFIG_DPDK_NULL			0
+#define FM10K_CONFIG_DPDK_PF			1
+#define FM10K_CONFIG_DPDK_VF			2
+#define FM10K_CONFIG_DPDK_MAX			3
+
+struct fm10k_cfg_port_pf_map {
+	uint8_t type;
+	uint8_t map_no[2];
+};
+
+/* Configuration read from file */
+struct fm10k_cfg_config_item {
+	uint8_t  type;
+	uint8_t  val_type;
+	uint16_t key_param;
+	const char *describe;
+	union {
+		int64_t int64;
+		char *str;
+	} val;
+};
+
+struct fm10k_hw;
+
+struct fm10k_dpdk_port {
+	uint8_t type;
+	struct fm10k_hw *hw;
+	void *rte_dev;
+	void *flow_list;
+	uint16_t pf_no;
+	uint8_t tx_queue_num;
+	uint8_t rx_queue_num;
+};
+
+
+/* Flow configuration */
+struct fm10k_cfg_port {
+	uint8_t port_type;
+	uint8_t port_no;
+	uint16_t vlan_id;
+};
+
+struct fm10k_cfg_flow {
+	/* set by configuration */
+	struct fm10k_cfg_flow *prev;
+	struct fm10k_cfg_flow *next;
+	uint8_t flow_no; /* only configured flow has this NO. */
+	struct fm10k_cfg_port src_port;
+	struct fm10k_cfg_port fw_port[2];
+	/* set by ffu rule add */
+	uint16_t rule_id;
+};
+
+#define FM10K_CONFIG_FLOWSET_NAME_MAX	256
+struct fm10k_cfg_flowset {
+	char name[FM10K_CONFIG_FLOWSET_NAME_MAX];
+	struct fm10k_cfg_flow flow_head;
+	struct fm10k_cfg_flowset *next;
+};
+
+/* Configuration */
+struct fm10k_dpdk_cfg {
+	uint8_t pf_num;		/* configure by conf */
+	uint8_t pf_bind;	/* initialize by dpdk */
+	uint8_t pf_max;		/* set by card type */
+	uint8_t ext_port_num;	/* configure by conf */
+	uint8_t ext_port_speed;	/* configure by conf */
+	uint32_t debug_cfg;	/* configure by conf */
+	uint32_t stats_interval;/* configure by conf */
+
+	struct fm10k_hw *master_hw;	/* initialize by dpdk */
+	struct fm10k_hw *pf_hw[FM10K_SW_PEP_PORTS_MAX];	/* initialize by dpdk */
+
+	/* initialize by dpdk */
+	struct fm10k_dpdk_port ports[FM10K_SW_LOGICAL_PORTS_MAX];
+	/* configure by conf or default*/
+	struct fm10k_cfg_port_pf_map dpdk_port_map[FM10K_SW_LOGICAL_PORTS_MAX];
+	/* configure by conf */
+	struct fm10k_cfg_port_pf_map ext_port_map[FM10K_SW_EXT_PORTS_MAX];
+	struct fm10k_cfg_config_item *config_list;	/* configure by conf */
+	struct fm10k_cfg_flowset flowset_head;	/* transfer from conf file */
+	struct fm10k_cfg_flowset *current;
+};
+
+static inline bool
+fm10k_config_check_debug(struct fm10k_dpdk_cfg *cfg, uint16_t dbg)
+{
+	return cfg->debug_cfg & 1 << (dbg - FM10K_CONFIG_DEBUG_START) &&
+		   cfg->debug_cfg & 1;
+}
+
+struct fm10k_cfg_flowset *fm10k_config_flowset_get(const char *name);
+struct fm10k_cfg_flowset *fm10k_config_flowset_current_get(void);
+void fm10k_config_flowset_current_set(struct fm10k_cfg_flowset *new);
+void fm10k_config_flow_list_add_tail(struct fm10k_cfg_flowset *flowset,
+				struct fm10k_cfg_flow *flow);
+void fm10k_config_flow_list_delete(struct fm10k_cfg_flow *flow);
+bool fm10k_config_flow_list_end(struct fm10k_cfg_flow *list,
+				struct fm10k_cfg_flow *flow);
+
+void fm10k_config_cfg_flowset_show(void);
+struct fm10k_hw *fm10k_config_hw_get(int port_no);
+int fm10k_config_init(struct fm10k_switch *sw, struct fm10k_hw *hw);
+
+#endif /* _FM10K_CONFIG_H */
diff --git a/drivers/net/fm10k/switch/fm10k_ffu.c b/drivers/net/fm10k/switch/fm10k_ffu.c
new file mode 100644
index 0000000..a9b23aa
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_ffu.c
@@ -0,0 +1,1253 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019   Silicom Ltd. Connectivity Solutions
+ */
+
+#include <rte_time.h>
+#include <rte_kvargs.h>
+#include <rte_hash.h>
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+#include <rte_tm_driver.h>
+
+#include "../base/fm10k_type.h"
+#include "../base/fm10k_osdep.h"
+
+#include "../fm10k.h"
+#include "../fm10k_logs.h"
+#include "fm10k_debug.h"
+#include "fm10k_regs.h"
+#include "fm10k_switch.h"
+#include "fm10k_config.h"
+#include "fm10k_ffu.h"
+#include "fm10k_stats.h"
+
+/*
+ * one SLICE for 40bits SEL
+ * SLICE 28   FOR SEL SGLORT(16bits) and VLAN(16bits)
+ *            FOR ACT ROUTE
+ * SLICE 29   FOR SEL ETHER_TYPE(16bits)
+ *			  FOR ACT MIRROR | SET_VLAN
+ * SLICE 30   FOR SEL MPLS_LABEL0(32bits)
+ * SLICE 31   FOR SEL MPLS_LABEL1(32bits)
+ */
+#define FM10K_FFU_SLICE_START			28
+#define FM10K_FFU_SLICE_SGLORT_VID		28
+#define FM10K_FFU_SLICE_ETYPE_VID2		29
+#define FM10K_FFU_SLICE_MPLS_LABEL0		30
+#define FM10K_FFU_SLICE_MPLS_LABEL1		31
+
+#define FM10K_FFU_SLICE_SGLORT_VLAN_CFG		\
+		(FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_0, \
+				FM10K_SW_FFU_MUX_SEL_SGLORT) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_1, \
+				FM10K_SW_FFU_MUX_SEL_SGLORT) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_2, \
+				FM10K_SW_FFU_MUX_SEL_VPRI_VID) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_3, \
+				FM10K_SW_FFU_MUX_SEL_VPRI_VID) | \
+		FM10K_SW_FFU_SLICE_CFG_START_ACTION | \
+		FM10K_SW_FFU_SLICE_CFG_START_COMPARE | \
+		FM10K_SW_FFU_SLICE_CFG_VALID_LOW)
+
+#define FM10K_FFU_SLICE_ETHER_TYPE_CFG		\
+		(FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_0, \
+				FM10K_SW_FFU_MUX_SEL_L2_TYPE) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_1, \
+				FM10K_SW_FFU_MUX_SEL_L2_TYPE) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_2, \
+				FM10K_SW_FFU_MUX_SEL_VPRI2_VID2) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_3, \
+				FM10K_SW_FFU_MUX_SEL_VPRI2_VID2) | \
+		FM10K_SW_FFU_SLICE_CFG_VALID_LOW)
+
+#define FM10K_FFU_SLICE_MPLS_LABEL0_CFG		\
+		(FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_0, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_127_96) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_1, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_127_96) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_2, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_127_96) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_3, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_127_96) | \
+		FM10K_SW_FFU_SLICE_CFG_START_COMPARE | \
+		FM10K_SW_FFU_SLICE_CFG_VALID_LOW)
+
+#define FM10K_FFU_SLICE_MPLS_LABEL1_CFG		\
+		(FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_0, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_95_64) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_1, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_95_64) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_2, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_95_64) | \
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_CFG_SELECT_3, \
+				FM10K_SW_FFU_MUX_SEL_L3_SIP_95_64) | \
+		FM10K_SW_FFU_SLICE_CFG_START_COMPARE | \
+		FM10K_SW_FFU_SLICE_CFG_VALID_LOW)
+
+static uint64_t fm10k_ffu_slice_cfgs[4] = {
+		FM10K_FFU_SLICE_SGLORT_VLAN_CFG,
+		FM10K_FFU_SLICE_ETHER_TYPE_CFG,
+		FM10K_FFU_SLICE_MPLS_LABEL0_CFG,
+		FM10K_FFU_SLICE_MPLS_LABEL1_CFG
+};
+
+#define FM10K_FFU_INIT_PRINT(cfg, ...)		\
+do { \
+	if (fm10k_config_check_debug(cfg, FM10K_CONFIG_DEBUG_FFU_INIT)) \
+		FM10K_SW_INFO(__VA_ARGS__); \
+} while (0)
+
+#define FM10K_FFU_RULE_PRINT(cfg, ...)		\
+do { \
+	if (fm10k_config_check_debug(cfg, FM10K_CONFIG_DEBUG_FFU_RULE)) \
+		FM10K_SW_INFO(__VA_ARGS__); \
+} while (0)
+
+#define FM10K_FFU_CNT_INDEX(x)		(FM10K_SW_FFU_CNT_START + (x))
+#define FM10K_FFU_FLOW_START		10
+#define FM10K_FFU_MIRROR_PROFILE	1
+#define FM10K_FFU_SGLORT_TYPE_NULL	0
+#define FM10K_FFU_SGLORT_TYPE_EPL	1
+#define FM10K_FFU_SGLORT_TYPE_PF	2
+#define FM10K_FFU_SGLORT_TYPE_PFS	3
+#define FM10K_FFU_SGLORT_TYPE_DPDK	4
+
+struct fm10k_ffu_rule_data {
+	uint16_t sglort_type;
+	uint16_t cond_sglort;
+	uint16_t cond_vlan;
+	uint16_t act_dglort;
+	uint16_t act_vlan;
+	uint16_t bypass_dglort;
+	uint16_t bypass_vlan;
+};
+
+
+static uint8_t fm10k_ffu_bitmap[FM10K_FFU_RULE_MAX / 8];
+static uint8_t
+fm10k_ffu_rule_get_bit(int id)
+{
+	int num = id / 8;
+	int offset = id % 8;
+
+	return (fm10k_ffu_bitmap[num] >> offset) & 0x1;
+}
+
+static void
+fm10k_ffu_rule_set_bit(int id, uint8_t bit)
+{
+	int num = id / 8;
+	int offset = id % 8;
+	uint8_t tmp = fm10k_ffu_bitmap[num];
+	uint8_t data = 1;
+
+	if (bit == 0)
+		fm10k_ffu_bitmap[num] = tmp & ~(data << offset);
+	else
+		fm10k_ffu_bitmap[num] |= (data << offset);
+}
+
+static int
+fm10k_ffu_rule_alloc(void)
+{
+	int i;
+
+	for (i = FM10K_FFU_FLOW_START; i < FM10K_FFU_RULE_MAX; i++)	{
+		if (fm10k_ffu_rule_get_bit(i) != 0)
+			continue;
+		fm10k_ffu_rule_set_bit(i, 1);
+		return i;
+	}
+	return -1;
+}
+
+static void
+fm10k_ffu_rule_free(int id)
+{
+	fm10k_ffu_rule_set_bit(id, 0);
+}
+
+static void
+fm10k_ffu_always_mismatch(struct fm10k_switch *sw,
+		unsigned int slice, unsigned int idx)
+{
+	uint64_t temp64;
+
+	temp64 =
+	    FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_TCAM_KEY, ~0ULL) |
+	    FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_TCAM_KEY_TOP, ~0ULL);
+	fm10k_write_switch_reg128(sw, FM10K_SW_FFU_SLICE_TCAM(slice, idx),
+	    temp64, temp64);
+}
+
+static void
+fm10k_ffu_route_dglort(struct fm10k_switch *sw, unsigned int slice,
+			unsigned int idx, uint16_t sglort, uint16_t dglort)
+{
+	uint64_t temp64, temp64_2;
+
+	/*
+	 * Set the key to exact match on the 16 SGLORT bits and
+	 * always match everywhere else.
+	 */
+	temp64 = FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_TCAM_KEY, sglort);
+	temp64_2 =
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_TCAM_KEY, ~sglort & 0xffff);
+	fm10k_write_switch_reg128(sw,
+			FM10K_SW_FFU_SLICE_TCAM(slice, idx),
+			temp64_2, temp64);
+
+	/*
+	 * Set the corresponding SRAM entry to ROUTE_GLORT to the
+	 * corresponding DGLORT.
+	 */
+	temp64 = 0x40;
+	temp64 = temp64 << 32 |
+	    FM10K_SW_MAKE_REG_FIELD(FFU_SLICE_SRAM_ROUTE_GLORT_DGLORT, dglort);
+	temp64 |=
+		FM10K_SW_MAKE_REG_FIELD64(FFU_SLICE_SRAM_COMMAND,
+	    FM10K_SW_FFU_SLICE_SRAM_COMMAND_ROUTE_GLORT);
+	/*
+	 * 11.5.4.2 FFU_SLICE_SRAM[0..31][0..1023]
+	 */
+	temp64_2 =
+		FM10K_SW_FFU_CNT_BANK << (35 - 23) |
+		(FM10K_SW_FFU_CNT_START + idx);
+	temp64 |= temp64_2 << 23;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(slice, idx), temp64);
+}
+
+
+static void
+fm10k_ffu_set_dest_glort_mask(struct fm10k_switch *sw,
+		unsigned int idx, uint16_t dglort, uint64_t dport_mask)
+{
+	uint64_t temp64;
+	unsigned int multiple_dports;
+	unsigned int num_dports;
+	unsigned int amplification_factor;
+	unsigned int hashed_entries;
+	unsigned int i, j;
+
+	multiple_dports = (dport_mask & (dport_mask - 1));
+
+	FM10K_FFU_INIT_PRINT(sw->dpdk_cfg,
+			"%s set glort %#x to port ", __func__, dglort);
+	/*
+	 * Create an exact-match key for the given DGLORT in the DGLORT CAM.
+	 */
+	fm10k_write_switch_reg(sw, FM10K_SW_GLORT_CAM(idx),
+	    FM10K_SW_MAKE_REG_FIELD(GLORT_CAM_KEY, dglort) |
+	    FM10K_SW_MAKE_REG_FIELD(GLORT_CAM_KEY_INVERT, ~dglort));
+
+	if (multiple_dports) {
+		/*
+		 * Create a pair of entries and use the hash value to select
+		 * among them.
+		 */
+		num_dports = 0;
+		for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++)
+			if (dport_mask & (1ULL << i)) {
+				num_dports++;
+				FM10K_FFU_INIT_PRINT(sw->dpdk_cfg, " %d", i);
+			}
+		FM10K_FFU_INIT_PRINT(sw->dpdk_cfg, "\n");
+
+		/*
+		 * Create multiple entries for each dport to increase the
+		 * hash modulus and capture more hash entropy.  The maximum
+		 * number of hashed entries is 16.
+		 */
+		amplification_factor = 16 / num_dports;
+		hashed_entries = num_dports * amplification_factor;
+		temp64 =
+			FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_STRICT,
+		    FM10K_SW_GLORT_RAM_STRICT_HASHED);
+		temp64 |=
+			FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_DEST_INDEX,
+			sw->glort_dest_table_idx);
+		temp64 |=
+			FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_DEST_COUNT,
+			hashed_entries);
+		fm10k_write_switch_reg64(sw, FM10K_SW_GLORT_RAM(idx), temp64);
+
+		for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++)
+			if (dport_mask & (1ULL << i))
+				for (j = 0; j < amplification_factor; j++) {
+					fm10k_write_switch_reg64(sw,
+					    FM10K_SW_GLORT_DEST_TABLE
+						(sw->glort_dest_table_idx),
+					    FM10K_SW_MAKE_REG_FIELD64
+						(GLORT_DEST_TABLE_MASK,
+						(1ULL << i)));
+					sw->glort_dest_table_idx++;
+				}
+	} else {
+		/*
+		 * Set the corresponding entry in the DGLORT map RAM to use
+		 * strict indexing straight into the DEST_TABLE, then write
+		 * the corresponding destination port in the DEST_TABLE.
+		 */
+
+		temp64 =
+			FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_STRICT,
+		    FM10K_SW_GLORT_RAM_STRICT_STRICT);
+		temp64 |=
+			FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_DEST_INDEX,
+			sw->glort_dest_table_idx);
+		fm10k_write_switch_reg64(sw,
+			FM10K_SW_GLORT_RAM(idx), temp64);
+
+		fm10k_write_switch_reg64(sw,
+			FM10K_SW_GLORT_DEST_TABLE(sw->glort_dest_table_idx),
+		    FM10K_SW_MAKE_REG_FIELD64
+			(GLORT_DEST_TABLE_MASK,
+			dport_mask));
+		sw->glort_dest_table_idx++;
+		for (i = 0; i < FM10K_SW_LOGICAL_PORTS_MAX; i++)
+			if (dport_mask & (1ULL << i))
+				FM10K_FFU_INIT_PRINT(sw->dpdk_cfg, " %d\n", i);
+	}
+}
+
+static struct fm10k_multi_glort {
+	uint8_t lport1;
+	uint8_t lport2;
+	uint16_t vlan1;
+	uint16_t vlan2;
+} fm10k_multi_glorts[FM10K_SW_FFU_RULE_MAX];
+
+static uint32_t
+fm10k_ffu_multi_glort_get(uint8_t lport1, uint8_t lport2,
+		uint16_t vlan1, uint16_t vlan2, bool *p_new)
+{
+	int i;
+
+	for (i = 0; i < FM10K_SW_FFU_RULE_MAX; i++) {
+		if (lport1 == fm10k_multi_glorts[i].lport1 &&
+		   lport2 == fm10k_multi_glorts[i].lport2 &&
+		   vlan1 == fm10k_multi_glorts[i].vlan1 &&
+		   vlan2 == fm10k_multi_glorts[i].vlan2) {
+			if (p_new != NULL)
+				*p_new = false;
+			return FM10K_SW_MULTI_GLORT_START + i;
+		}
+	}
+
+	for (i = 0; i < FM10K_SW_FFU_RULE_MAX; i++) {
+		if (fm10k_multi_glorts[i].lport1 == 0 &&
+		   fm10k_multi_glorts[i].lport2 == 0 &&
+		   fm10k_multi_glorts[i].vlan1 == 0 &&
+		   fm10k_multi_glorts[i].vlan2 == 0) {
+			fm10k_multi_glorts[i].lport1 = lport1;
+			fm10k_multi_glorts[i].lport2 = lport2;
+			fm10k_multi_glorts[i].vlan1 = vlan1;
+			fm10k_multi_glorts[i].vlan2 = vlan2;
+			if (p_new != NULL)
+				*p_new = true;
+			return FM10K_SW_MULTI_GLORT_START + i;
+		}
+	}
+
+	return 0;
+}
+
+static uint32_t
+fm10k_ffu_set_dest_glort_multi_cast(struct fm10k_switch *sw,
+		uint8_t lport1, uint8_t lport2,
+		uint16_t vlan1, uint16_t vlan2)
+{
+	uint32_t dglort;
+	bool is_new = false;
+	uint32_t idx;
+	uint64_t temp64;
+
+	dglort =
+		fm10k_ffu_multi_glort_get(lport1, lport2,
+				vlan1, vlan2, &is_new);
+
+	if (!is_new)
+		return dglort;
+
+	FM10K_FFU_INIT_PRINT(sw->dpdk_cfg,
+			"%s set glort %#x to (%d:%d) (%d:%d)\n",
+			__func__, dglort, lport1, vlan1, lport2, vlan2);
+	/*
+	 * Create an exact-match key for the given DGLORT in the DGLORT CAM.
+	 */
+	idx = sw->glort_cam_ram_idx++;
+	fm10k_write_switch_reg(sw, FM10K_SW_GLORT_CAM(idx),
+	    FM10K_SW_MAKE_REG_FIELD(GLORT_CAM_KEY, dglort) |
+	    FM10K_SW_MAKE_REG_FIELD(GLORT_CAM_KEY_INVERT, ~dglort));
+
+	/*
+	 * Create multiple entries for each dport to increase the
+	 * hash modulus and capture more hash entropy.  The maximum
+	 * number of hashed entries is 16.
+	 */
+	temp64 =
+		FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_STRICT,
+	    FM10K_SW_GLORT_RAM_STRICT_STRICT);
+	temp64 |=
+		FM10K_SW_MAKE_REG_FIELD64(GLORT_RAM_DEST_INDEX,
+		sw->glort_dest_table_idx);
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_GLORT_RAM(idx), temp64);
+
+	/* GLORT_DEST_TABLE
+	 * Field Name		Bit(s)	Type	Default
+	 * DestMask			47:0	RW		0x0
+	 * IP_MulticastIndex59:48	RW		0x0
+	 * Reserved			63:60	RSV		0x0
+	 */
+	temp64 = sw->mcast_dest_table_idx;
+	temp64 = temp64 << 48 | 1 << lport1 | 1 << lport2;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_GLORT_DEST_TABLE
+			(sw->glort_dest_table_idx), temp64);
+	sw->glort_dest_table_idx++;
+
+	/* MCAST_DEST_TABLE
+	 * Field Name		Bit(s)	Type	Default
+	 * PortMask			47:0	RW		0x0
+	 * LenTableIdx		61:48	RW		0x0
+	 * Reserved			63:62	RSV		00b
+	 */
+	temp64 = sw->mcast_len_table_idx;
+	temp64 = 1 << lport1 | 1 << lport2 | temp64 << 48;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_SCHED_MCAST_DEST_TABLE
+			(sw->mcast_dest_table_idx++), temp64);
+
+	/* MCAST_LEN_TABLE
+	 * Field Name		Bit(s)	Type	Default
+	 * L3_McastIdx		14:0	RW		0x0
+	 * L3_Repcnt		26:15	RW		0x0
+	 * Reserved			31:27	RSV		0x0
+	 */
+	temp64 =
+		sw->mcast_vlan_table_idx | 1 << 15;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_SCHED_MCAST_LEN_TABLE
+			(sw->mcast_len_table_idx++), temp64);
+
+	/* MCAST_VLAN_TABLE
+	 * Field Name		Bit(s)	Type	Default
+	 * VID				11:0	RW		0x0
+	 * DGLORT			27:12	RW		0x0
+	 * ReplaceVID		28		RW		0b
+	 * ReplaceDGLORT	29		RW		0b
+	 * Reserved			31:30	RSV		00b
+	 */
+	temp64 = vlan1 |
+			fm10k_switch_pf_glort_get
+			(lport1) << 12 | 1 << 28 | 1 << 29;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_MOD_MCAST_VLAN_TABLE
+			(sw->mcast_vlan_table_idx++), temp64);
+	temp64 = vlan2 |
+			fm10k_switch_pf_glort_get
+			(lport2) << 12 | 1 << 28 | 1 << 29;
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_MOD_MCAST_VLAN_TABLE
+			(sw->mcast_vlan_table_idx++), temp64);
+
+	return dglort;
+}
+
+static uint64_t
+fm10k_data64_field64_get(uint64_t data, int start, int end)
+{
+	uint64_t tmp64 = data;
+
+	if (start == end) {
+		tmp64 = (data >> start) & 1;
+	} else {
+		tmp64 = tmp64 << (64 - end);
+		tmp64 = tmp64 >> (64 - end + start);
+	}
+	return tmp64;
+}
+
+static uint32_t
+fm10k_data64_field32_get(uint64_t data, int start, int end)
+{
+	uint64_t tmp64 = data;
+	uint32_t tmp32;
+
+	if (start == end) {
+		tmp32 = (data >> start) & 1;
+	} else {
+		tmp64 = tmp64 << (64 - end);
+		tmp32 = tmp64 >> (64 - end + start);
+	}
+	return tmp32;
+}
+
+static uint32_t
+fm10k_data32_field_get(uint32_t data, int start, int end)
+{
+	uint32_t tmp32 = data;
+
+	if (start == end) {
+		tmp32 = (data >> start) & 1;
+	} else {
+		tmp32 = tmp32 << (64 - end);
+		tmp32 = tmp32 >> (64 - end + start);
+	}
+	return tmp32;
+}
+
+
+static void
+fm10k_glort_register_dump(struct fm10k_switch *sw)
+{
+	uint32_t i;
+	uint64_t data64;
+	uint32_t data32;
+	uint32_t tmp32;
+
+	if (!fm10k_config_check_debug
+		(sw->dpdk_cfg, FM10K_CONFIG_DEBUG_FFU_REG))
+		return;
+
+	FM10K_SW_INFO("----- GLORT -----\n");
+
+	for (i = 0; i < sw->glort_cam_ram_idx; i++)	{
+		data32 = fm10k_read_switch_reg(sw, FM10K_SW_GLORT_CAM(i));
+		FM10K_SW_INFO("[%02u]GLORT_CAM %#x Key %#x\n", i, data32,
+				fm10k_data32_field_get(data32, 0, 15));
+		data64 = fm10k_read_switch_reg64(sw, FM10K_SW_GLORT_RAM(i));
+		FM10K_SW_INFO("    GLORT_RAM %#llx Strict %u DestIndex %u\n",
+				(unsigned long long)data64,
+				fm10k_data64_field32_get(data64, 0, 1),
+				fm10k_data64_field32_get(data64, 2, 13));
+		tmp32 = fm10k_data64_field32_get(data64, 2, 13);
+		data64 =
+			fm10k_read_switch_reg64(sw,
+				FM10K_SW_GLORT_DEST_TABLE(tmp32));
+		FM10K_SW_INFO("    GLORT_DEST_TABLE[%u] %#llx "
+				"IP_MulticastIndex %u\n",
+				tmp32, (unsigned long long)data64,
+				fm10k_data64_field32_get(data64, 48, 59));
+		tmp32 = fm10k_data64_field32_get(data64, 48, 59);
+		if (tmp32 == 0)
+			continue;
+		data64 = fm10k_read_switch_reg64(sw,
+				FM10K_SW_SCHED_MCAST_DEST_TABLE(tmp32));
+		FM10K_SW_INFO("    SCHED_MCAST_DEST_TABLE[%u] "
+				"%#llx PortMask %#llx"
+				" LenTableIdx %u\n", tmp32,
+				(unsigned long long)data64,
+				(unsigned long long)
+				fm10k_data64_field64_get(data64, 0, 47),
+				fm10k_data64_field32_get(data64, 48, 61));
+		tmp32 = fm10k_data64_field32_get(data64, 48, 61);
+		data32 = fm10k_read_switch_reg(sw,
+				FM10K_SW_SCHED_MCAST_LEN_TABLE(tmp32));
+		FM10K_SW_INFO("    SCHED_MCAST_LEN_TABLE[%u] %#x "
+				"L3_McastIdx %u L3_Repcnt %u\n",
+				tmp32, data32,
+				fm10k_data32_field_get(data32, 0, 14),
+				fm10k_data32_field_get(data32, 15, 26));
+		tmp32 = fm10k_data32_field_get(data32, 0, 14);
+		data32 = fm10k_read_switch_reg(sw,
+				FM10K_SW_MOD_MCAST_VLAN_TABLE(tmp32));
+		FM10K_SW_INFO("    MOD_MCAST_VLAN_TABLE[%u] %#x VID %u "
+				"DGLORT %#x ReplaceVID %u ReplaceDGLORT %u\n",
+				tmp32, data32,
+				fm10k_data32_field_get(data32, 0, 11),
+				fm10k_data32_field_get(data32, 12, 27),
+				fm10k_data32_field_get(data32, 28, 28),
+				fm10k_data32_field_get(data32, 29, 29));
+		data32 = fm10k_read_switch_reg(sw,
+				FM10K_SW_MOD_MCAST_VLAN_TABLE(tmp32 + 1));
+		FM10K_SW_INFO("    MOD_MCAST_VLAN_TABLE[%u] %#x VID %u "
+				"DGLORT %#x ReplaceVID %u ReplaceDGLORT %u\n",
+				tmp32 + 1, data32,
+				fm10k_data32_field_get(data32, 0, 11),
+				fm10k_data32_field_get(data32, 12, 27),
+				fm10k_data32_field_get(data32, 28, 28),
+				fm10k_data32_field_get(data32, 29, 29));
+	}
+}
+
+static void
+fm10k_ffu_register_dump(struct fm10k_switch *sw)
+{
+	int i, j;
+	uint32_t data[8];
+	uint32_t ffu_valid;
+
+	if (!fm10k_config_check_debug(sw->dpdk_cfg, FM10K_CONFIG_DEBUG_FFU_REG))
+		return;
+
+	FM10K_SW_INFO("--------------- FFU REGISTERS DUMP -----------------\n");
+
+	fm10k_read_switch_array(sw, FM10K_SW_FFU_MASTER_VALID, data, 2);
+	FM10K_SW_INFO("FFU_MASTER_VALID: %#x %#x\n", data[0], data[1]);
+	ffu_valid = data[0];
+
+	for (i = 0; i < 32; i++) {
+		if ((ffu_valid & (1 << i)) == 0)
+			continue;
+
+		FM10K_SW_INFO("------ SLICE%d ------\n", i);
+		fm10k_read_switch_array(sw,
+				FM10K_SW_FFU_SLICE_VALID(i), data, 2);
+		if (data[0] != 0 || data[1] != 0)
+			FM10K_SW_INFO("FFU_SLICE_VALID[%d]: %#x %#x\n",
+				i, data[0], data[1]);
+
+		fm10k_read_switch_array(sw,
+				FM10K_SW_FFU_SLICE_CASCADE_ACTION(i), data, 2);
+		if (data[0] != 0 || data[1] != 0)
+			FM10K_SW_INFO("FFU_SLICE_CASCADE_ACTION[%d]: %#x %#x\n",
+				i, data[0], data[1]);
+
+		for (j = 0; j < 1; j++) {
+			fm10k_read_switch_array(sw,
+					FM10K_SW_FFU_SLICE_CFG(i, j), data, 2);
+			if (data[0] != 0 || data[1] != 0)
+				FM10K_SW_INFO("FFU_SLICE_CFG[%d][%d]: %#x %#x\n",
+						i, j, data[0], data[1]);
+		}
+		for (j = 0; j < 32; j++) {
+			fm10k_read_switch_array(sw,
+					FM10K_SW_FFU_SLICE_TCAM(i, j), data, 4);
+			if ((data[0] != 0 || data[1] != 0 ||
+					data[2] != 0 || data[3] != 0))
+				FM10K_SW_INFO("FFU_SLICE_TCAM[%d][%d]: "
+					"%#x %#x %#x %#x\n",
+					i, j, data[0], data[1],
+					data[2], data[3]);
+
+			fm10k_read_switch_array(sw,
+					FM10K_SW_FFU_SLICE_SRAM(i, j), data, 2);
+			if (data[0] != 0 || data[1] != 0)
+				FM10K_SW_INFO("FFU_SLICE_SRAM[%d][%d]: %#x %#x\n",
+					i, j, data[0], data[1]);
+		}
+	}
+}
+
+
+static void
+fm10k_ffu_mirror_set_action(struct fm10k_switch *sw,
+		uint16_t ffu_slice, uint16_t table_idx)
+{
+	uint64_t data64;
+
+	/* blank the next ffu slice */
+	fm10k_write_switch_reg128(sw,
+		FM10K_SW_FFU_SLICE_TCAM
+		(ffu_slice, table_idx), 0xffffffffff, 0x1);
+	/* SET FFU RX_MIRROR */
+	data64 = 0x60; /* higher than route, not necessary */
+	data64 = data64 << 32 | 0x2 << 21 | 1 << (8 + 4) | 1 << 4;
+	fm10k_write_switch_reg64(sw,
+		FM10K_SW_FFU_SLICE_SRAM(ffu_slice, table_idx), data64);
+}
+
+static void
+fm10k_ffu_mirror_set_forward(struct fm10k_switch *sw,
+		int profile,
+		uint16_t vlan,
+		uint16_t dest_lport,
+		uint32_t dest_sglort)
+{
+	uint64_t data64;
+
+	/* RX_MIRROR_CFG */
+	fm10k_write_switch_reg(sw, 0xd50000 + 0xd, profile);
+
+	/* FH_MIRROR_PROFILE_TABLE */
+	fm10k_write_switch_reg(sw,
+			0xd50000 + 0x1 * profile + 0x40, dest_lport);
+
+	/* MOD_MIRROR_PROFILE_TABLE don't work */
+	data64 = vlan;
+	data64 = (dest_sglort & 0xffff) | data64 << 17;
+	fm10k_write_switch_reg64(sw,
+			0xe80000 + 0x2 * profile + 0x24000, data64);
+}
+
+
+int
+fm10k_ffu_mirror_set(struct fm10k_switch *sw,
+		uint16_t src_ext_port,
+		uint16_t dest_ext_port,
+		uint16_t vlan)
+{
+	uint16_t table_idx;
+	uint16_t ffu_slice = FM10K_FFU_SLICE_SGLORT_VID + 1;
+	uint16_t mirror_profile = FM10K_FFU_MIRROR_PROFILE;
+
+	table_idx = FM10K_FFU_EXT_PORT_RULE_INGRESS(src_ext_port - 1);
+	fm10k_ffu_mirror_set_action(sw, ffu_slice, table_idx);
+
+	fm10k_ffu_mirror_set_forward(sw, mirror_profile,
+			vlan, sw->epl_map[dest_ext_port - 1].logical_port,
+			sw->epl_map[dest_ext_port - 1].glort);
+	return 0;
+}
+
+int
+fm10k_ffu_mirror_reset(struct fm10k_switch *sw, int src_ext_port)
+{
+	uint16_t table_idx;
+	uint16_t ffu_slice = FM10K_FFU_SLICE_SGLORT_VID + 1;
+
+	table_idx = FM10K_FFU_EXT_PORT_RULE_INGRESS(src_ext_port - 1);
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(ffu_slice, table_idx), 0);
+	return 0;
+}
+
+static void
+fm10k_ffu_logical_port_vlan_set(struct fm10k_switch *sw,
+		uint16_t lport, uint16_t vlan_id)
+{
+	uint64_t data;
+
+	/* 11.21.3.9 INGRESS_VID_TABLE[0..4095] */
+	data = fm10k_read_switch_reg64(sw,
+			0xE80000 + 0x2 * vlan_id + 0x20000);
+	data |= 1 << lport;
+	fm10k_write_switch_reg64(sw,
+			0xE80000 + 0x2 * vlan_id + 0x20000, data);
+
+	FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"%s lport:%d, vlan_id:%d, reg:%#llx\n",
+			__func__, lport, vlan_id, (unsigned long long)data);
+}
+
+static void
+fm10k_ffu_glort_port_vlan_set(struct fm10k_switch *sw,
+		uint16_t glort, uint16_t vlan_id)
+{
+	int i;
+
+	for (i = 0; i < FM10K_SW_PEPS_SUPPORTED; i++) {
+		if (sw->pep_map[i].glort == glort) {
+			fm10k_ffu_logical_port_vlan_set(sw,
+					sw->pep_map[i].logical_port, vlan_id);
+			break;
+		}
+	}
+	for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++) {
+		if (sw->epl_map[i].glort == glort) {
+			fm10k_ffu_logical_port_vlan_set(sw,
+					sw->epl_map[i].logical_port, vlan_id);
+			break;
+		}
+	}
+}
+
+
+static uint16_t
+fm10k_ffu_dpdk_port_glort_get(struct fm10k_switch *sw, int port)
+{
+	int pf;
+	uint16_t glort = 0;
+
+	if (sw->dpdk_cfg->dpdk_port_map[port].type ==
+			FM10K_CONFIG_PORT_MAP_PFS ||
+		sw->dpdk_cfg->dpdk_port_map[port].type ==
+			FM10K_CONFIG_PORT_MAP_PFSS) {
+		glort = fm10k_switch_pfs_glort_get
+				(sw->dpdk_cfg->dpdk_port_map[port].map_no[0],
+				sw->dpdk_cfg->dpdk_port_map[port].map_no[1]);
+	} else if (sw->dpdk_cfg->dpdk_port_map[port].type ==
+			FM10K_CONFIG_PORT_MAP_PF) {
+		pf = sw->dpdk_cfg->dpdk_port_map[port].map_no[0];
+		glort = fm10k_switch_pf_glort_get(pf);
+	}
+	return glort;
+}
+
+static void
+fm10k_ffu_rule_enable_single_cast(struct fm10k_switch *sw,
+		int rule_id,
+		uint16_t sglort,
+		uint16_t svlan,
+		uint16_t dglort,
+		uint16_t dvlan)
+{
+	uint64_t temp64;
+	uint64_t sglort_vid_tcam = 0, sglort_vid_tcam_mask = 0;
+	uint64_t sram[4] = { 0, 0, 0, 0 };
+	uint16_t sram_idx = 0, tcam_slice, sram_slice, i;
+
+	sglort_vid_tcam |= sglort;
+	sglort_vid_tcam_mask |= 0xffff;
+	if (svlan) {
+		temp64 = svlan;
+		sglort_vid_tcam |= temp64 << 16;
+		sglort_vid_tcam_mask |= 0xfff0000;
+		fm10k_ffu_glort_port_vlan_set(sw, sglort, svlan);
+	}
+
+	/* set counter */
+	sram[sram_idx] |=
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COUNTER_BANK,
+			FM10K_SW_FFU_CNT_BANK) |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COUNTER_INDEX,
+			FM10K_FFU_CNT_INDEX(rule_id));
+
+	sram[sram_idx] |=
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_PRECEDENCE, 3) |
+		    FM10K_SW_MAKE_REG_FIELD
+			(FFU_SLICE_SRAM_ROUTE_GLORT_DGLORT, dglort) |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COMMAND,
+			FM10K_SW_FFU_SLICE_SRAM_COMMAND_ROUTE_GLORT);
+	sram_idx++;
+
+	if (dvlan) {
+		/* Force updating VLAN tag if present.
+		 * Add if absent. 11.5.3.4
+		 */
+		temp64 = 3;
+		sram[sram_idx] =
+			temp64 << 16 | dvlan |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_PRECEDENCE, 2) |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COMMAND,
+			FM10K_SW_FFU_SLICE_SRAM_COMMAND_FIELD_SET);
+		fm10k_ffu_glort_port_vlan_set(sw, dglort, dvlan);
+		sram_idx++;
+	}
+
+	if (sglort_vid_tcam) {
+		tcam_slice = FM10K_FFU_SLICE_SGLORT_VID;
+		temp64 =
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_TCAM_KEY,
+			~sglort_vid_tcam & sglort_vid_tcam_mask);
+		fm10k_write_switch_reg128(sw,
+			FM10K_SW_FFU_SLICE_TCAM(tcam_slice, rule_id),
+			temp64, sglort_vid_tcam);
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"TCAM slice:%d, rule:%d, data0:%#llx, data1:%#llx\n",
+			tcam_slice, rule_id,
+			(unsigned long long)sglort_vid_tcam,
+			(unsigned long long)temp64);
+	}
+
+	/* blank the next SLICE TCAM */
+	fm10k_ffu_always_mismatch(sw, tcam_slice + 1, rule_id);
+
+	for (i = 0; i < sram_idx; i++) {
+		sram_slice = FM10K_FFU_SLICE_START + i;
+		fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(sram_slice, rule_id), sram[i]);
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"SRAM slice:%d, rule:%d, data:%#llx\n",
+			sram_slice, rule_id, (unsigned long long)sram[i]);
+	}
+	/* disable the next SLICE SRAM */
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(sram_slice + 1, rule_id), 0);
+
+	fm10k_stats_rule_count_reg(rule_id);
+}
+
+static void
+fm10k_ffu_rule_enable_multi_cast(struct fm10k_switch *sw,
+		int rule_id,
+		uint16_t sglort,
+		uint16_t svlan,
+		uint8_t lport1,
+		uint8_t lport2,
+		uint16_t vlan1,
+		uint16_t vlan2)
+{
+	uint64_t temp64;
+	uint32_t dglort;
+	uint64_t sglort_vid_tcam = 0, sglort_vid_tcam_mask = 0;
+	uint64_t sram[4] = { 0, 0, 0, 0 };
+	uint16_t sram_idx = 0, tcam_slice, sram_slice, i;
+
+	dglort =
+		fm10k_ffu_set_dest_glort_multi_cast(sw,
+				lport1, lport2, vlan1, vlan2);
+
+	sglort_vid_tcam |= sglort;
+	sglort_vid_tcam_mask |= 0xffff;
+	if (svlan) {
+		temp64 = svlan;
+		sglort_vid_tcam |= temp64 << 16;
+		sglort_vid_tcam_mask |= 0xfff0000;
+		fm10k_ffu_glort_port_vlan_set(sw, sglort, svlan);
+	}
+
+	fm10k_ffu_logical_port_vlan_set(sw, lport1, vlan1);
+	fm10k_ffu_logical_port_vlan_set(sw, lport2, vlan2);
+
+	/* set counter */
+	sram[sram_idx] |=
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COUNTER_BANK,
+			FM10K_SW_FFU_CNT_BANK) |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COUNTER_INDEX,
+			FM10K_FFU_CNT_INDEX(rule_id));
+
+	sram[sram_idx] |=
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_PRECEDENCE, 3) |
+		    FM10K_SW_MAKE_REG_FIELD
+			(FFU_SLICE_SRAM_ROUTE_GLORT_DGLORT, dglort) |
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_SRAM_COMMAND,
+			FM10K_SW_FFU_SLICE_SRAM_COMMAND_ROUTE_GLORT);
+	sram_idx++;
+
+	if (sglort_vid_tcam) {
+		tcam_slice = FM10K_FFU_SLICE_SGLORT_VID;
+		temp64 =
+			FM10K_SW_MAKE_REG_FIELD64
+			(FFU_SLICE_TCAM_KEY,
+			~sglort_vid_tcam & sglort_vid_tcam_mask);
+		fm10k_write_switch_reg128(sw,
+			FM10K_SW_FFU_SLICE_TCAM(tcam_slice, rule_id),
+			temp64, sglort_vid_tcam);
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"TCAM slice:%d, rule:%d, data0:%#llx, data1:%#llx\n",
+			tcam_slice, rule_id,
+			(unsigned long long)sglort_vid_tcam,
+			(unsigned long long)temp64);
+	}
+
+	/* blank the next SLICE TCAM */
+	fm10k_ffu_always_mismatch(sw, tcam_slice + 1, rule_id);
+
+	for (i = 0; i < sram_idx; i++) {
+		sram_slice = FM10K_FFU_SLICE_START + i;
+		fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(sram_slice, rule_id), sram[i]);
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"SRAM slice:%d, rule:%d, data:%#llx\n",
+			sram_slice, rule_id, (unsigned long long)sram[i]);
+	}
+	/* disable the next SLICE SRAM */
+	fm10k_write_switch_reg64(sw,
+			FM10K_SW_FFU_SLICE_SRAM(sram_slice + 1, rule_id), 0);
+
+	fm10k_stats_rule_count_reg(rule_id);
+}
+
+
+void
+fm10k_ffu_flow_enable(struct fm10k_switch *sw, struct fm10k_cfg_flow *flow)
+{
+	uint16_t sglort = 0;
+	uint16_t svlan = 0;
+	uint16_t dglort = 0;
+	uint16_t dvlan = 0;
+	uint16_t lport1 = 0, lport2 = 0;
+	uint16_t dvlan2 = 0;
+
+	switch (flow->src_port.port_type) {
+	case FM10K_CONFIG_FLOW_EXT_PORT:
+		sglort = fm10k_switch_epl_glort_get(flow->src_port.port_no - 1);
+		break;
+	case FM10K_CONFIG_FLOW_DPDK_PORT:
+		sglort =
+			fm10k_ffu_dpdk_port_glort_get(sw,
+			flow->src_port.port_no);
+		break;
+	}
+	switch (flow->fw_port[0].port_type) {
+	case FM10K_CONFIG_FLOW_EXT_PORT:
+		dglort =
+			fm10k_switch_epl_glort_get
+			(flow->fw_port[0].port_no - 1);
+		lport1 =
+			fm10k_switch_epl_logical_get
+			(flow->fw_port[0].port_no - 1);
+		break;
+	case FM10K_CONFIG_FLOW_DPDK_PORT:
+		dglort =
+			fm10k_ffu_dpdk_port_glort_get
+			(sw, flow->fw_port[0].port_no);
+		lport1 =
+			fm10k_switch_pf_logical_get
+			(flow->fw_port[0].port_no);
+		break;
+	}
+	svlan = flow->src_port.vlan_id;
+	dvlan = flow->fw_port[0].vlan_id;
+
+	flow->rule_id = fm10k_ffu_rule_alloc();
+
+	FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+		"Set rule cond sglort:%#x, vlan:%d ==> "
+		"act dglort:%#x, vlan:%d",
+		sglort, svlan, dglort, dvlan);
+
+	if (flow->fw_port[1].port_type)	{
+		switch (flow->fw_port[1].port_type)	{
+		case FM10K_CONFIG_FLOW_EXT_PORT:
+			dglort =
+				fm10k_switch_epl_glort_get
+				(flow->fw_port[1].port_no - 1);
+			lport2 =
+				fm10k_switch_epl_logical_get
+				(flow->fw_port[1].port_no - 1);
+			break;
+		case FM10K_CONFIG_FLOW_DPDK_PORT:
+			dglort =
+				fm10k_ffu_dpdk_port_glort_get
+				(sw, flow->fw_port[1].port_no);
+			lport2 =
+				fm10k_switch_pf_logical_get
+				(flow->fw_port[1].port_no);
+			break;
+		}
+		dvlan2 = flow->fw_port[1].vlan_id;
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			" (bypass glort:%#x, vlan:%d)\n",
+			dglort, dvlan2);
+
+		fm10k_ffu_rule_enable_multi_cast(sw,
+			flow->rule_id, sglort, svlan,
+			lport1, lport2, dvlan, dvlan2);
+	} else {
+		FM10K_FFU_RULE_PRINT(sw->dpdk_cfg, "\n");
+		fm10k_ffu_rule_enable_single_cast(sw,
+			flow->rule_id, sglort, svlan, dglort, dvlan);
+	}
+}
+
+
+void
+fm10k_ffu_flow_disable(struct fm10k_switch *sw, struct fm10k_cfg_flow *flow)
+{
+	int i;
+
+	if (flow->rule_id == 0)
+		return;
+
+	FM10K_FFU_RULE_PRINT(sw->dpdk_cfg,
+			"Remove flow %d rule %d\n",
+			flow->flow_no, flow->rule_id);
+
+	for (i = FM10K_FFU_SLICE_START; i < FM10K_SW_FFU_NUM_SLICES; i++) {
+		fm10k_ffu_always_mismatch(sw, i, flow->rule_id);
+		fm10k_write_switch_reg64(sw,
+				FM10K_SW_FFU_SLICE_SRAM(i, flow->rule_id), 0);
+	}
+	fm10k_ffu_rule_free(flow->rule_id);
+}
+
+
+static int
+fm10k_ffu_configured_flowset_enable(struct fm10k_switch *sw)
+{
+	struct fm10k_cfg_flowset *flowset;
+	struct fm10k_cfg_flow *flow;
+
+	flowset = fm10k_config_flowset_current_get();
+	flow = flowset->flow_head.next;
+	while (!fm10k_config_flow_list_end(&flowset->flow_head, flow)) {
+		fm10k_ffu_flow_enable(sw, flow);
+		flow = flow->next;
+	}
+
+	return 0;
+}
+
+
+void
+fm10k_ffu_flowset_switch(struct fm10k_switch *sw, const char *new_name)
+{
+	struct fm10k_cfg_flowset *flowset;
+	struct fm10k_cfg_flowset *cur_fs;
+	struct fm10k_cfg_flow *flow;
+
+	cur_fs = fm10k_config_flowset_current_get();
+	if (strcmp(cur_fs->name, new_name) == 0)
+		return;
+
+	flowset = fm10k_config_flowset_get(new_name);
+	if (flowset == NULL) {
+		FM10K_SW_ERR("Can not find flowset %s!!\n", new_name);
+		return;
+	}
+
+	/* disable current flowset */
+	flow = cur_fs->flow_head.next;
+	while (!fm10k_config_flow_list_end(&cur_fs->flow_head, flow)) {
+		fm10k_ffu_flow_disable(sw, flow);
+		flow = flow->next;
+	}
+
+	/* enable new flowset */
+	fm10k_config_flowset_current_set(flowset);
+	flow = flowset->flow_head.next;
+	while (!fm10k_config_flow_list_end(&flowset->flow_head, flow)) {
+		fm10k_ffu_flow_enable(sw, flow);
+		flow = flow->next;
+	}
+}
+
+
+int
+fm10k_ffu_init(struct fm10k_switch *sw, struct fm10k_dpdk_cfg *cfg)
+{
+	int ret = 0;
+	uint64_t data64;
+	uint16_t i, j;
+	uint32_t sglort = 0, dglort = 0;
+	uint16_t table_idx = 0;
+	uint16_t ffu_slice = FM10K_FFU_SLICE_START;
+
+	sw->mcast_dest_table_idx = 1;
+	sw->mcast_len_table_idx = 1;
+	sw->mcast_vlan_table_idx = 1;
+
+	for (i = 0;
+		 i < sizeof(fm10k_ffu_slice_cfgs) / sizeof(uint64_t);
+		 i++) {
+		for (j = 0; j < FM10K_SW_FFU_NUM_SCENARIOS; j++) {
+			data64 = fm10k_ffu_slice_cfgs[i];
+			fm10k_write_switch_reg64(sw,
+					FM10K_SW_FFU_SLICE_CFG
+					(FM10K_FFU_SLICE_START + i, j),
+					data64);
+		}
+		FM10K_FFU_INIT_PRINT(cfg, "SET slice %d cfg = %#llx\n",
+				FM10K_FFU_SLICE_START + i,
+				(unsigned long long)data64);
+	}
+
+	for (table_idx = 0;
+		 table_idx < FM10K_SW_FFU_SLICE_TCAM_ENTRIES / 2;
+		 table_idx++)
+		for (i = ffu_slice; i < FM10K_SW_FFU_NUM_SLICES; i++)
+			fm10k_ffu_always_mismatch(sw, i, table_idx);
+
+	/*
+	 * Create a TCAM entry to match each SGLORT that might be used, and
+	 * set the corresponding SRAM action entries to ROUTE_GLORT to the
+	 * corresponding DGLORT (see above table).
+	 * SGLORT ROUTE is always the first slice
+	 */
+	for (i = 0; i < FM10K_SW_EXT_PORTS_MAX; i++) {
+		if (cfg->ext_port_map[i].type == FM10K_CONFIG_PORT_MAP_NULL)
+			continue;
+
+		if (cfg->ext_port_map[i].type == FM10K_CONFIG_PORT_MAP_PF) {
+			sglort = fm10k_switch_epl_glort_get(i);
+			dglort =
+				fm10k_switch_pf_glort_get
+				(cfg->ext_port_map[i].map_no[0]);
+		} else if (cfg->ext_port_map[i].type ==
+				   FM10K_CONFIG_PORT_MAP_PFS) {
+			sglort = fm10k_switch_epl_glort_get(i);
+			dglort =
+				fm10k_switch_pfs_glort_get
+				(cfg->ext_port_map[i].map_no[0],
+				cfg->ext_port_map[i].map_no[1]);
+		}
+
+		table_idx = FM10K_FFU_EXT_PORT_RULE_INGRESS(i);
+		fm10k_ffu_route_dglort(sw,
+				ffu_slice, table_idx, sglort, dglort);
+
+		/* blank the next ffu slice */
+		fm10k_write_switch_reg128(sw,
+				FM10K_SW_FFU_SLICE_TCAM
+				(ffu_slice + 1, table_idx),
+			    0xffffffffff, 0x1);
+		table_idx = FM10K_FFU_EXT_PORT_RULE_EGRESS(i);
+		fm10k_ffu_route_dglort(sw,
+				ffu_slice, table_idx, dglort, sglort);
+
+		/* blank the next ffu slice */
+		fm10k_write_switch_reg128(sw,
+				FM10K_SW_FFU_SLICE_TCAM
+				(ffu_slice + 1, table_idx),
+			    0xffffffffff, 0x1);
+	}
+
+	for (i = ffu_slice; i < FM10K_SW_FFU_NUM_SLICES; i++) {
+		fm10k_write_switch_reg64(sw,
+				FM10K_SW_FFU_SLICE_CASCADE_ACTION(i),
+				0xf0f000f);
+		/* Set slice 0 to valid for all scenarios */
+		fm10k_write_switch_reg64(sw,
+				FM10K_SW_FFU_SLICE_VALID(i),
+				FM10K_SW_FFU_SLICE_VALID_ALL_SCENARIOS);
+	}
+
+	/* Mark slice 0 as valid and all chunks as invalid */
+	data64 = 0;
+	for (i = ffu_slice; i < FM10K_SW_FFU_NUM_SLICES; i++)
+		data64 |= FM10K_SW_FFU_MASTER_VALID_SLICE_VALID(i);
+	fm10k_write_switch_reg64(sw, FM10K_SW_FFU_MASTER_VALID, data64);
+
+	/*
+	 * Set up the DGLORT map according to the desired
+	 * SGLORT -> { DGLORT, logical_port } map.
+	 */
+	for (i = 0; i < sw->info->num_peps; i++) {
+		if (sw->pep_map[i].glort == 0)
+			continue;
+		fm10k_ffu_set_dest_glort_mask(sw,
+				sw->glort_cam_ram_idx++,
+				sw->pep_map[i].glort,
+				FM10K_SW_DPORT_MASK
+				(sw->pep_map[i].logical_port));
+	}
+
+	for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++) {
+		if (sw->epl_map[i].glort == 0)
+			continue;
+		fm10k_ffu_set_dest_glort_mask(sw,
+				sw->glort_cam_ram_idx++,
+				sw->epl_map[i].glort,
+				FM10K_SW_DPORT_MASK
+				(sw->epl_map[i].logical_port));
+	}
+
+	for (i = 0; i < FM10K_SW_EXT_PORTS_MAX; i++) {
+		uint64_t dport_mask = 0;
+
+		if (cfg->ext_port_map[i].type ==
+				FM10K_CONFIG_PORT_MAP_PFS) {
+			dglort =
+				fm10k_switch_pfs_glort_get
+				(cfg->ext_port_map[i].map_no[0],
+						cfg->ext_port_map[i].map_no[1]);
+			dport_mask =
+				FM10K_SW_DPORT_MASK
+		(sw->pep_map[cfg->ext_port_map[i].map_no[0]].logical_port) |
+				FM10K_SW_DPORT_MASK
+		(sw->pep_map[cfg->ext_port_map[i].map_no[1]].logical_port);
+			fm10k_ffu_set_dest_glort_mask(sw,
+				sw->glort_cam_ram_idx++, dglort, dport_mask);
+		}
+	}
+
+	/* Ensure the rest of the DGLORT TCAM won't match */
+	for (table_idx = sw->glort_cam_ram_idx;
+		 table_idx < FM10K_SW_GLORT_CAM_ENTRIES;
+		 table_idx++)
+		fm10k_write_switch_reg(sw,
+				FM10K_SW_GLORT_CAM(table_idx),
+				FM10K_SW_GLORT_CAM_MATCH_NONE);
+
+	ret = fm10k_ffu_configured_flowset_enable(sw);
+
+	fm10k_ffu_register_dump(sw);
+	fm10k_glort_register_dump(sw);
+	return ret;
+}
diff --git a/drivers/net/fm10k/switch/fm10k_ffu.h b/drivers/net/fm10k/switch/fm10k_ffu.h
new file mode 100644
index 0000000..bcd9329
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_ffu.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2019   Silicom Ltd. Connectivity Solutions
+ */
+
+#ifndef _FM10K_FFU_H_
+#define _FM10K_FFU_H_
+
+
+#include <stdint.h>
+
+#define FM10K_FFU_EXT_PORT_RULE_INGRESS(i)	((i) * 2)
+#define FM10K_FFU_EXT_PORT_RULE_EGRESS(i)	((i) * 2 + 1)
+#define FM10K_FFU_RULE_MAX			FM10K_SW_FFU_RULE_MAX
+
+struct fm10k_switch;
+struct fm10k_dpdk_cfg;
+struct fm10k_cfg_flow;
+
+int fm10k_ffu_init(struct fm10k_switch *sw, struct fm10k_dpdk_cfg *cfg);
+
+int fm10k_ffu_mirror_set(struct fm10k_switch *sw,
+		uint16_t src_dpdk_port, u16 dest_dpdk_port, uint16_t vlan);
+int fm10k_ffu_mirror_reset(struct fm10k_switch *sw, int src_dpdk_port);
+
+void fm10k_ffu_flow_enable(struct fm10k_switch *sw,
+		struct fm10k_cfg_flow *flow);
+void fm10k_ffu_flow_disable(struct fm10k_switch *sw,
+		struct fm10k_cfg_flow *flow);
+void fm10k_ffu_flowset_switch(struct fm10k_switch *sw, const char *new_name);
+
+#endif /* _FM10K_FFU_H */
diff --git a/drivers/net/fm10k/switch/fm10k_stats.c b/drivers/net/fm10k/switch/fm10k_stats.c
new file mode 100644
index 0000000..563f9ac
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_stats.c
@@ -0,0 +1,1114 @@
+/* spdx-license-identifier: bsd-3-clause
+ * copyright 2019   silicom ltd. connectivity solutions
+ */
+
+#include <rte_time.h>
+#include <rte_kvargs.h>
+#include <rte_hash.h>
+#include <rte_flow.h>
+#include <rte_flow_driver.h>
+#include <rte_tm_driver.h>
+
+#include "../base/fm10k_type.h"
+#include "../base/fm10k_osdep.h"
+
+#include "../fm10k.h"
+#include "../fm10k_logs.h"
+#include "fm10k_debug.h"
+#include "fm10k_regs.h"
+#include "fm10k_ext_port.h"
+#include "fm10k_stats.h"
+#include "fm10k_switch.h"
+#include "fm10k_config.h"
+#include "fm10k_ffu.h"
+
+
+#define FM10K_MAX_VLAN_COUNTER			63
+#define FM10K_NB_RX_STATS_BANKS			6
+#define FM10K_BINS_PER_RX_STATS_BANK		16
+#define FM10K_WORDS_PER_RX_STATS_COUNTER	4
+
+/* Max expected entries in read stats scatter gather list */
+#define MAX_STATS_SGLIST 128
+#define FM10K_RX_STATS_BASE			(0xE00000)
+#define FM10K_MOD_BASE				(0xE80000)
+#define FM10K_CM_APPLY_BASE			(0xD40000)
+#define FM10K_EPL_BASE				(0x0E0000)
+
+#define FM10K_RX_STATS_BANK(index1, index0, word)		\
+			((0x001000) * ((index1) - 0) + \
+			(0x000004) * ((index0) - 0) + \
+			(word) + (0x000000) + \
+			(FM10K_RX_STATS_BASE))
+#define FM10K_MOD_STATS_BANK_FRAME(index1, index0, word)	\
+			((0x000800) * ((index1) - 0) + \
+			(0x000002) * ((index0) - 0) + \
+			(word) + (0x025000) + \
+			(FM10K_MOD_BASE))
+#define FM10K_MOD_STATS_BANK_BYTE(index1, index0, word)		\
+			((0x000800) * ((index1) - 0) + \
+			(0x000002) * ((index0) - 0) + \
+			(word) + (0x026000) + \
+			(FM10K_MOD_BASE))
+#define FM10K_CM_APPLY_DROP_COUNT(index, word)			\
+			((0x000002) * ((index) - 0) + \
+			(word) + (0x000880) + \
+			(FM10K_CM_APPLY_BASE))
+#define FM10K_MAC_OVERSIZE_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000021) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_JABBER_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000022) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_UNDERSIZE_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000023) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_RUNT_COUNTER(index1, index0)			\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000024) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_OVERRUN_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000025) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_UNDERRUN_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000026) + \
+			(FM10K_EPL_BASE))
+#define FM10K_MAC_CODE_ERROR_COUNTER(index1, index0)		\
+			((0x000400) * ((index1) - 0) + \
+			(0x000080) * ((index0) - 0) + \
+			(0x000027) + \
+			(FM10K_EPL_BASE))
+
+/* Rx Bank Definitions */
+#define FM10K_RX_STAT_BANK_TYPE                       0
+#define FM10K_RX_STAT_BANK_SIZE                       1
+#define FM10K_RX_STAT_BANK_PRI                        2
+#define FM10K_RX_STAT_BANK_FWD_1                      3
+#define FM10K_RX_STAT_BANK_FWD_2                      4
+#define FM10K_RX_STAT_BANK_VLAN                       5
+
+/* FM10K_RX_STAT_BANK_TYPE Bin Definitions */
+#define FM10K_RX_STAT_NONIP_L2UCAST                   0
+#define FM10K_RX_STAT_NONIP_L2MCAST                   1
+#define FM10K_RX_STAT_NONIP_L2BCAST                   2
+#define FM10K_RX_STAT_IPV4_L2UCAST                    3
+#define FM10K_RX_STAT_IPV4_L2MCAST                    4
+#define FM10K_RX_STAT_IPV4_L2BCAST                    5
+#define FM10K_RX_STAT_IPV6_L2UCAST                    6
+#define FM10K_RX_STAT_IPV6_L2MCAST                    7
+#define FM10K_RX_STAT_IPV6_L2BCAST                    8
+#define FM10K_RX_STAT_IEEE802_3_PAUSE                 9
+#define FM10K_RX_STAT_CLASS_BASED_PAUSE               10
+#define FM10K_RX_STAT_FRAMING_ERR                     11
+#define FM10K_RX_STAT_FCS_ERR                         12
+
+/* FM10K_RX_STAT_BANK_SIZE Bin Definitions */
+#define FM10K_RX_STAT_LEN_LT_64                       0
+#define FM10K_RX_STAT_LEN_EQ_64                       1
+#define FM10K_RX_STAT_LEN_65_127                      2
+#define FM10K_RX_STAT_LEN_128_255                     3
+#define FM10K_RX_STAT_LEN_256_511                     4
+#define FM10K_RX_STAT_LEN_512_1023                    5
+#define FM10K_RX_STAT_LEN_1024_1522                   6
+#define FM10K_RX_STAT_LEN_1523_2047                   7
+#define FM10K_RX_STAT_LEN_2048_4095                   8
+#define FM10K_RX_STAT_LEN_4096_8191                   9
+#define FM10K_RX_STAT_LEN_8192_10239                  10
+#define FM10K_RX_STAT_LEN_GE_10240                    11
+
+/* FM10K_RX_STAT_BANK_PRI Bin Definitions */
+#define FM10K_RX_STAT_PRI_0                           0
+#define FM10K_RX_STAT_PRI_1                           1
+#define FM10K_RX_STAT_PRI_2                           2
+#define FM10K_RX_STAT_PRI_3                           3
+#define FM10K_RX_STAT_PRI_4                           4
+#define FM10K_RX_STAT_PRI_5                           5
+#define FM10K_RX_STAT_PRI_6                           6
+#define FM10K_RX_STAT_PRI_7                           7
+#define FM10K_RX_STAT_PRI_8                           8
+#define FM10K_RX_STAT_PRI_9                           9
+#define FM10K_RX_STAT_PRI_10                          10
+#define FM10K_RX_STAT_PRI_11                          11
+#define FM10K_RX_STAT_PRI_12                          12
+#define FM10K_RX_STAT_PRI_13                          13
+#define FM10K_RX_STAT_PRI_14                          14
+#define FM10K_RX_STAT_PRI_15                          15
+
+/* FM10K_RX_STAT_BANK_FWD_1 Bin Definitions */
+#define FM10K_RX_STAT_FID_FORWARDED                   0
+#define FM10K_RX_STAT_FLOOD_FORWARDED                 1
+#define FM10K_RX_STAT_SPECIALLY_HANDLED               2
+#define FM10K_RX_STAT_PARSER_ERROR_DROP               3
+#define FM10K_RX_STAT_ECC_ERROR_DROP                  4
+#define FM10K_RX_STAT_TRAPPED                         5
+#define FM10K_RX_STAT_PAUSE_DROPS                     6
+#define FM10K_RX_STAT_STP_DROPS                       7
+#define FM10K_RX_STAT_SECURITY_VIOLATIONS             8
+#define FM10K_RX_STAT_VLAN_TAG_DROPS                  9
+#define FM10K_RX_STAT_VLAN_INGRESS_DROPS              10
+#define FM10K_RX_STAT_VLAN_EGRESS_DROPS               11
+#define FM10K_RX_STAT_GLORT_MISS_DROPS                12
+#define FM10K_RX_STAT_FFU_DROPS                       13
+#define FM10K_RX_STAT_TRIGGER_DROPS                   14
+#define FM10K_RX_STAT_OVERFLOW4                       15
+
+/* FM10K_RX_STAT_BANK_FWD_2 Bin Definitions */
+#define FM10K_RX_STAT_POLICER_DROPS                   0
+#define FM10K_RX_STAT_TTL_DROPS                       1
+#define FM10K_RX_STAT_CM_PRIV_DROPS                   2
+#define FM10K_RX_STAT_CM_SMP0_DROPS                   3
+#define FM10K_RX_STAT_CM_SMP1_DROPS                   4
+#define FM10K_RX_STAT_CM_RX_HOG_0_DROPS               5
+#define FM10K_RX_STAT_CM_RX_HOG_1_DROPS               6
+#define FM10K_RX_STAT_CM_TX_HOG_0_DROPS               7
+#define FM10K_RX_STAT_CM_TX_HOG_1_DROPS               8
+/* Bin 9 reserved */
+#define FM10K_RX_STAT_TRIGGER_REDIRECTS               10
+#define FM10K_RX_STAT_FLOOD_CONTROL_DROPS             11
+#define FM10K_RX_STAT_GLORT_FORWARDED                 12
+#define FM10K_RX_STAT_LOOPBACK_SUPPRESS               13
+#define FM10K_RX_STAT_OTHERS                          14
+#define FM10K_RX_STAT_OVERFLOW5                       15
+
+/* FM10K_RX_STAT_BANK_VLAN Bin definitions */
+#define FM10K_RX_STAT_VLAN_UCAST                      0
+#define FM10K_RX_STAT_VLAN_MCAST                      1
+#define FM10K_RX_STAT_VLAN_BCAST                      2
+
+/* Tx Bank Definitions */
+#define FM10K_TX_STAT_BANK_TYPE                       0
+#define FM10K_TX_STAT_BANK_SIZE                       1
+
+/* FM10K_TX_STAT_BANK_TYPE Bin Definitions */
+#define FM10K_TX_STAT_L2UCAST                         0
+#define FM10K_TX_STAT_L2MCAST                         1
+#define FM10K_TX_STAT_L2BCAST                         2
+#define FM10K_TX_STAT_ERR_SENT                        3
+#define FM10K_TX_STAT_TIMEOUT_DROP                    4
+#define FM10K_TX_STAT_ERR_DROP                        5
+#define FM10K_TX_STAT_ECC_DROP                        6
+#define FM10K_TX_STAT_LOOPBACK_DROP                   7
+#define FM10K_TX_STAT_TTL1_DROP                       8
+#define FM10K_TX_STAT_IEEE802_3_PAUSE                 9
+#define FM10K_TX_STAT_CLASS_BASED_PAUSE               10
+
+/* FM10K_TX_STAT_BANK_SIZE Bin Definitions */
+#define FM10K_TX_STAT_LEN_LT_64                       0
+#define FM10K_TX_STAT_LEN_EQ_64                       1
+#define FM10K_TX_STAT_LEN_65_127                      2
+#define FM10K_TX_STAT_LEN_128_255                     3
+#define FM10K_TX_STAT_LEN_256_511                     4
+#define FM10K_TX_STAT_LEN_512_1023                    5
+#define FM10K_TX_STAT_LEN_1024_1522                   6
+#define FM10K_TX_STAT_LEN_1523_2047                   7
+#define FM10K_TX_STAT_LEN_2048_4095                   8
+#define FM10K_TX_STAT_LEN_4096_8191                   9
+#define FM10K_TX_STAT_LEN_8192_10239                  10
+#define FM10K_TX_STAT_LEN_GE_10240                    11
+
+
+/* This structure is used to build a table of all rx / tx counters */
+struct fm10k_port_count_mapping {
+	/* Bank in which the counter is located */
+	uint32_t bank;
+
+	/* Bin  in which the counter is located */
+	uint32_t bin;
+
+	/*
+	 * Offset (in bytes) of a the frame counter in
+	 * the fm10k_counters structure
+	 */
+	uint32_t frame_offset;
+
+	/*
+	 * Offset (in bytes) of a the byte counter in
+	 * the fm10k_counters structure
+	 */
+	uint32_t byte_offset;
+};
+
+
+/*
+ * Table of all RX port counters in the RX banks
+ * This table is used for retrieving counters as
+ * well as resetting them
+ */
+static struct fm10k_port_count_mapping rx_port_cnt_map_table[] = {
+    /* Bank                        Bin
+     * --------------------------  ------------------------------
+     * frame_offset
+     * ---------------------------------------------
+     * byte_offset
+     * ---------------------------------------------------
+     */
+    /* Type Bank */
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_NONIP_L2UCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_pkts_nonip),
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_octets_nonip)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_NONIP_L2MCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_pkts_nonip),
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_octets_nonip)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_NONIP_L2BCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_pkts_nonip),
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_octets_nonip)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV4_L2UCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_pkts_ipv4),
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_octets_ipv4)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV4_L2MCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_pkts_ipv4),
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_octets_ipv4)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV4_L2BCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_pkts_ipv4),
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_octets_ipv4)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV6_L2UCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_pkts_ipv6),
+	offsetof(struct fm10k_port_counters, cnt_rx_ucst_octets_ipv6)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV6_L2MCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_pkts_ipv6),
+	offsetof(struct fm10k_port_counters, cnt_rx_mcst_octets_ipv6)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IPV6_L2BCAST,
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_pkts_ipv6),
+	offsetof(struct fm10k_port_counters, cnt_rx_bcst_octets_ipv6)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_IEEE802_3_PAUSE,
+	offsetof(struct fm10k_port_counters, cnt_rx_pause_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_pause_octets)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_CLASS_BASED_PAUSE,
+	offsetof(struct fm10k_port_counters, cnt_rx_cbpause_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_cbpause_octets)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_FRAMING_ERR,
+	offsetof(struct fm10k_port_counters, cnt_rx_framing_error_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_framing_error_octets)},
+{	FM10K_RX_STAT_BANK_TYPE,  FM10K_RX_STAT_FCS_ERR,
+	offsetof(struct fm10k_port_counters, cnt_rx_fcs_errors),
+	offsetof(struct fm10k_port_counters, cnt_rx_fcs_errors_octets)},
+
+    /* Size Bank */
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_LT_64,
+	offsetof(struct fm10k_port_counters, cnt_rx_minto63_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_minto63_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_EQ_64,
+	offsetof(struct fm10k_port_counters, cnt_rx_64_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_64_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_65_127,
+	offsetof(struct fm10k_port_counters, cnt_rx_65to127_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_65to127_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_128_255,
+	offsetof(struct fm10k_port_counters, cnt_rx_128to255_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_128to255_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_256_511,
+	offsetof(struct fm10k_port_counters, cnt_rx_256to511_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_256to511_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_512_1023,
+	offsetof(struct fm10k_port_counters, cnt_rx_512to1023_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_512to1023_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_1024_1522,
+	offsetof(struct fm10k_port_counters, cnt_rx_1024to1522_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_1024to1522_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_1523_2047,
+	offsetof(struct fm10k_port_counters, cnt_rx_1523to2047_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_1523to2047_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_2048_4095,
+	offsetof(struct fm10k_port_counters, cnt_rx_2048to4095_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_2048to4095_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_4096_8191,
+	offsetof(struct fm10k_port_counters, cnt_rx_4096to8191_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_4096to8191_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_8192_10239,
+	offsetof(struct fm10k_port_counters, cnt_rx_8192to10239_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_8192to10239_octets)},
+{	FM10K_RX_STAT_BANK_SIZE,  FM10K_RX_STAT_LEN_GE_10240,
+	offsetof(struct fm10k_port_counters, cnt_rx_10240tomax_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_10240tomax_octets)},
+
+    /* priority Bank */
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_0,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[0]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[0])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_1,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[1]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[1])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_2,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[2]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[2])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_3,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[3]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[3])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_4,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[4]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[4])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_5,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[5]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[5])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_6,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[6]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[6])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_7,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[7]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[7])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_8,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[8]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[8])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_9,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[9]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[9])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_10,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[10]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[10])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_11,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[11]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[11])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_12,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[12]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[12])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_13,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[13]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[13])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_14,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[14]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[14])},
+{	FM10K_RX_STAT_BANK_PRI,   FM10K_RX_STAT_PRI_15,
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_pkts[15]),
+	offsetof(struct fm10k_port_counters, cnt_rx_priority_octets[15])},
+
+    /* Forwarding Bank */
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_FID_FORWARDED,
+	offsetof(struct fm10k_port_counters, cnt_fid_forwarded_pkts),
+	offsetof(struct fm10k_port_counters, cnt_fid_forwarded_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_FLOOD_FORWARDED,
+	offsetof(struct fm10k_port_counters, cnt_flood_forwarded_pkts),
+	offsetof(struct fm10k_port_counters, cnt_flood_forwarded_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_SPECIALLY_HANDLED,
+	offsetof(struct fm10k_port_counters, cnt_specially_handled_pkts),
+	offsetof(struct fm10k_port_counters, cnt_specially_handled_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_PARSER_ERROR_DROP,
+	offsetof(struct fm10k_port_counters, cnt_parse_err_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_parse_err_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_ECC_ERROR_DROP,
+	offsetof(struct fm10k_port_counters, cnt_parity_error_pkts),
+	offsetof(struct fm10k_port_counters, cnt_parity_error_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_TRAPPED,
+	offsetof(struct fm10k_port_counters, cnt_trapped_pkts),
+	offsetof(struct fm10k_port_counters, cnt_trapped_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_PAUSE_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_pause_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_pause_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_STP_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_stp_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_stp_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_SECURITY_VIOLATIONS,
+	offsetof(struct fm10k_port_counters, cnt_security_violation_pkts),
+	offsetof(struct fm10k_port_counters, cnt_security_violation_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_VLAN_TAG_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_vlan_tag_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_vlan_tag_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_VLAN_INGRESS_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_vlan_ingressbv_pkts),
+	offsetof(struct fm10k_port_counters, cnt_vlan_ingressbv_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_VLAN_EGRESS_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_vlan_egressbv_pkts),
+	offsetof(struct fm10k_port_counters, cnt_vlan_egressbv_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_GLORT_MISS_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_glort_miss_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_glort_miss_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_FFU_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_ffu_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_ffu_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_1, FM10K_RX_STAT_TRIGGER_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_trigger_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_trigger_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_POLICER_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_policer_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_policer_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_TTL_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_ttl_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_ttl_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_PRIV_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_cmpriv_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_cmpriv_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_SMP0_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_smp0_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_smp0_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_SMP1_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_smp1_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_smp1_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_RX_HOG_0_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_rx_hog0_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_hog0_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_RX_HOG_1_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_rx_hog1_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_rx_hog1_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_TX_HOG_0_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_tx_hog0_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_hog0_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_CM_TX_HOG_1_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_tx_hog1_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_hog1_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_TRIGGER_REDIRECTS,
+	offsetof(struct fm10k_port_counters, cnt_trigger_redir_pkts),
+	offsetof(struct fm10k_port_counters, cnt_trigger_redir_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_FLOOD_CONTROL_DROPS,
+	offsetof(struct fm10k_port_counters, cnt_flood_control_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_flood_control_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_GLORT_FORWARDED,
+	offsetof(struct fm10k_port_counters, cnt_glort_forwarded_pkts),
+	offsetof(struct fm10k_port_counters, cnt_glort_forwarded_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_LOOPBACK_SUPPRESS,
+	offsetof(struct fm10k_port_counters, cnt_loopback_drops_pkts),
+	offsetof(struct fm10k_port_counters, cnt_loopback_drop_octets)},
+{	FM10K_RX_STAT_BANK_FWD_2, FM10K_RX_STAT_OTHERS,
+	offsetof(struct fm10k_port_counters, cnt_other_pkts),
+	offsetof(struct fm10k_port_counters, cnt_other_octets)},
+
+}; /* end rxPortCntMapTable */
+
+
+/*
+ * Table of all TX port counters in the TX banks
+ * This table is used for retrieving counters as
+ * well as resetting them
+ */
+static struct fm10k_port_count_mapping tx_port_cnt_map_table[] = {
+    /* Bank                        Bin
+     * --------------------------  ------------------------------
+     * frame_offset
+     * ---------------------------------------------
+     * byte_offset
+     * ---------------------------------------------------
+     */
+    /* Type Bank */
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_L2UCAST,
+	offsetof(struct fm10k_port_counters, cnt_tx_ucst_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_ucst_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_L2MCAST,
+	offsetof(struct fm10k_port_counters, cnt_tx_mcst_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_mcst_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_L2BCAST,
+	offsetof(struct fm10k_port_counters, cnt_tx_bcst_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_bcst_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_ERR_SENT,
+	offsetof(struct fm10k_port_counters, cnt_tx_error_sent_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_error_sent_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_TIMEOUT_DROP,
+	offsetof(struct fm10k_port_counters, cnt_tx_timeout_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_timeout_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_ERR_DROP,
+	offsetof(struct fm10k_port_counters, cnt_tx_error_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_error_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_ECC_DROP,
+	offsetof(struct fm10k_port_counters, cnt_tx_unrepair_ecc_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_unrepair_ecc_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_LOOPBACK_DROP,
+	offsetof(struct fm10k_port_counters, cnt_tx_loopback_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_loopback_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_TTL1_DROP,
+	offsetof(struct fm10k_port_counters, cnt_tx_ttl_drop_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_ttl_drop_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_IEEE802_3_PAUSE,
+	offsetof(struct fm10k_port_counters, cnt_tx_pause_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_pause_octets)},
+{	FM10K_TX_STAT_BANK_TYPE, FM10K_TX_STAT_CLASS_BASED_PAUSE,
+	offsetof(struct fm10k_port_counters, cnt_tx_cbpause_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_cbpause_octets)},
+
+    /* Size Bank */
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_LT_64,
+	offsetof(struct fm10k_port_counters, cnt_tx_minto63_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_minto63_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_EQ_64,
+	offsetof(struct fm10k_port_counters, cnt_tx_64_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_64_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_65_127,
+	offsetof(struct fm10k_port_counters, cnt_tx_65to127_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_65to127_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_128_255,
+	offsetof(struct fm10k_port_counters, cnt_tx_128to255_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_128to255_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_256_511,
+	offsetof(struct fm10k_port_counters, cnt_tx_256to511_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_256to511_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_512_1023,
+	offsetof(struct fm10k_port_counters, cnt_tx_512to1023_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_512to1023_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_1024_1522,
+	offsetof(struct fm10k_port_counters, cnt_tx_1024to1522_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_1024to1522_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_1523_2047,
+	offsetof(struct fm10k_port_counters, cnt_tx_1523to2047_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_1523to2047_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_2048_4095,
+	offsetof(struct fm10k_port_counters, cnt_tx_2048to4095_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_2048to4095_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_4096_8191,
+	offsetof(struct fm10k_port_counters, cnt_tx_4096to8191_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_4096to8191_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_8192_10239,
+	offsetof(struct fm10k_port_counters, cnt_tx_8192to10239_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_8192to10239_octets)},
+{	FM10K_TX_STAT_BANK_SIZE, FM10K_TX_STAT_LEN_GE_10240,
+	offsetof(struct fm10k_port_counters, cnt_tx_10240tomax_pkts),
+	offsetof(struct fm10k_port_counters, cnt_tx_10240tomax_octets)},
+
+};  /* end tx_port_cnt_map_table */
+
+
+/*
+ * Get a 32bit EPL counter by adding it to the scatter gather
+ * list.
+ * Depends on the existence of the variables:
+ * sgList [out] - scatter gather array to hold the reads
+ * sgListCnt [out] - number of entry in the sgList
+ */
+static inline void
+fm10k_get_epl_port_stat32(struct fm10k_scatter_gather_entry *sg_list,
+		struct fm10k_port_counters *counters,
+		int *sg_list_cnt, int epl, int lane)
+{
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_OVERSIZE_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_rx_oversized_pkts;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_JABBER_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_rx_jabber_pkts;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_UNDERSIZE_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_rx_undersized_pkts;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_RUNT_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_rx_fragment_pkts;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_OVERRUN_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_over_run_pkts;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+
+	sg_list[*sg_list_cnt].addr = FM10K_MAC_CODE_ERROR_COUNTER(epl, lane);
+	sg_list[*sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_code_errors;
+	sg_list[*sg_list_cnt].count = 1;
+	(*sg_list_cnt)++;
+}
+
+
+static void
+fm10k_read_scatter_gather(struct fm10k_switch *sw,
+		int n_entries,
+		struct fm10k_scatter_gather_entry *sg_list)
+{
+	int i;
+
+	for (i = 0; i < n_entries; i++) {
+		uint32_t addr  = sg_list[i].addr;
+		uint32_t count = sg_list[i].count;
+		uint32_t *data = sg_list[i].data;
+
+		fm10k_read_switch_array(sw, addr, data, count);
+	}
+}
+
+
+static int
+fm10k_get_port_counters(struct fm10k_switch *sw,
+		struct fm10k_ext_port *ext_port,
+		struct fm10k_port_counters *counters)
+{
+	int phys_port;
+	int epl;
+	int lane;
+	struct timeval ts;
+	bool valid_ip_stats = true;
+	uint32_t i;
+	struct fm10k_scatter_gather_entry sg_list[MAX_STATS_SGLIST];
+	int sg_list_cnt = 0;
+	/* Temporary bin array to retrieve 128bit
+	 * port counters (frame + bytes).
+	 */
+	uint32_t cnt_rx_port_stats_bank
+					[FM10K_NB_RX_STATS_BANKS]
+					[FM10K_BINS_PER_RX_STATS_BANK]
+					[FM10K_WORDS_PER_RX_STATS_COUNTER];
+	/*
+	 * Fill the counters structure with 0 to assure
+	 * all fields not explicitly set below, which will
+	 * be the fields not supported by the FM10000,
+	 * will be 0 on return.
+	 */
+	memset(counters, 0, sizeof(*counters));
+
+	phys_port = ext_port->portno;
+	epl = ext_port->eplno;
+	lane = ext_port->first_lane;
+
+	counters->cnt_version = FM10K_STATS_VERSION;
+
+	/*
+	 * Reading counters for each RX bank
+	 *
+	 * Because the RX_STATS_BANK register contains
+	 * both, frame count and byte count in a 128bit
+	 * register, we first store the data in a temporary
+	 * array.
+	 * 1. Fill Scatter Gather to retrieve each bin
+	 *    counter from the rx bank and store in a temp
+	 *    array.
+	 */
+	for (i = 0;
+			i < sizeof(rx_port_cnt_map_table) /
+				sizeof(rx_port_cnt_map_table[0]);
+			i++) {
+		/*
+		 * Add a 128bit read of an rx counter to the scatter gather
+		 * list.
+		 * NOTE: Read values will be stored in the temporary
+		 *       array switchExt->cnt_rx_port_stats_bank;
+		 * Depends on the existence of the variables:
+		 * switchExt->cnt_rx_PortStatsBank [out] - Array to store the
+		 * 128bit values sgList
+		 * [out] - scatter gather array to hold the reads sgListCnt
+		 * [out] - number of entry in the sgList
+		 */
+		uint32_t bank = rx_port_cnt_map_table[i].bank;
+		uint32_t bin = rx_port_cnt_map_table[i].bin;
+
+		sg_list[sg_list_cnt].addr =
+			FM10K_RX_STATS_BANK(bank, (phys_port << 4 | (bin)), 0);
+		sg_list[sg_list_cnt].data = cnt_rx_port_stats_bank[bank][bin];
+		sg_list[sg_list_cnt].count = 4;
+		sg_list_cnt++;
+}
+
+	/*
+	 * 2. Fill in the scatter gather for tx bank
+	 *    counters. They will directly be stored
+	 *    in the fm10k_counters structure upon
+	 *    scatter gather read.
+	 */
+	for (i = 0;
+			i < sizeof(tx_port_cnt_map_table) /
+				sizeof(tx_port_cnt_map_table[0]);
+			i++) {
+		/*
+		 * Add a 64bit read of a tx counter to the scatter gather list.
+		 * Depends on the existence of the variables:
+		 * sgList [out] - scatter gather array to hold the reads
+		 * sgListCnt [out] - number of entry in the sgList
+		 */
+		uint32_t bank = tx_port_cnt_map_table[i].bank;
+		uint32_t bin = tx_port_cnt_map_table[i].bin;
+		uint32_t frame_offset = tx_port_cnt_map_table[i].frame_offset;
+
+		sg_list[sg_list_cnt].addr =
+			FM10K_MOD_STATS_BANK_FRAME(bank,
+					(phys_port << 4 | (bin)), 0);
+		sg_list[sg_list_cnt].data =
+			(uint32_t *)(((uint8_t *)counters) + (frame_offset));
+		sg_list[sg_list_cnt].count = 2;
+		sg_list_cnt++;
+	}
+
+	/*
+	 * 3. Fill in the scatter gather for TX CM DROP
+	 *    and for EPL counters. They will directly be
+	 *    stored in the fm10k_counters structure upon
+	 *    scatter gather read.
+	 */
+	sg_list[sg_list_cnt].addr =
+			FM10K_CM_APPLY_DROP_COUNT(phys_port, 0);
+	sg_list[sg_list_cnt].data =
+			(uint32_t *)&counters->cnt_rx_cm_drop_pkts;
+	sg_list[sg_list_cnt].count = 2;
+	sg_list_cnt++;
+	/* EPL Counters */
+	fm10k_get_epl_port_stat32(sg_list, counters, &sg_list_cnt, epl, lane);
+
+	/*
+	 * 4. Execute scatter gather read.
+	 */
+	if (sg_list_cnt >= MAX_STATS_SGLIST) {
+		/*
+		 * Pretty static. Mainly to warn if something new added,
+		 * but the array size is not adjust accordingly
+		 */
+		FM10K_SW_ERR("Scatter list array %d for port %d overflow.\n",
+				sg_list_cnt, phys_port);
+		return -1;
+	}
+
+	/*
+	 * Taking lock to protect temporary structures used to
+	 * store 128b counters
+	 */
+	FM10K_SW_SWITCH_LOCK(sw);
+
+	/* now get the stats in one shot, optimized for fibm */
+	fm10k_read_scatter_gather(sw, sg_list_cnt, sg_list);
+	FM10K_SW_SWITCH_UNLOCK(sw);
+
+	counters->timestamp = ts.tv_sec * 1000000 + ts.tv_usec;
+
+	/*
+	 * 5. Retrieve the frame/byte counts from 128bit
+	 *    registers stored in temporary array and set
+	 *    the proper fm_portCounter structure members.
+	 */
+	for (i = 0;
+			i < sizeof(rx_port_cnt_map_table) /
+				sizeof(rx_port_cnt_map_table[0]);
+			i++) {
+		/*
+		 * Update the counter variable related to a 128bit HW counter
+		 * NOTE: Read values will be retrieved from the temporary
+		 *       array switchExt->cnt_rx_PortStatsBank;
+		 * Depends on the existence of the variables:
+		 * switchExt->cnt_rx_PortStatsBank [in] - Array to read the
+		 * 128bit values from
+		 */
+		uint32_t bank = rx_port_cnt_map_table[i].bank;
+		uint32_t bin = rx_port_cnt_map_table[i].bin;
+		uint32_t frame_offset = rx_port_cnt_map_table[i].frame_offset;
+		uint32_t byte_offset = rx_port_cnt_map_table[i].byte_offset;
+
+		*((uint64_t *)(((uint8_t *)counters) + frame_offset)) =
+		(((uint64_t)(cnt_rx_port_stats_bank[bank][bin][1]) << 32) |
+		((uint64_t)(cnt_rx_port_stats_bank[bank][bin][0])));
+
+		*((uint64_t *)(((uint8_t *)counters) + byte_offset)) =
+		(((uint64_t)(cnt_rx_port_stats_bank[bank][bin][3]) << 32) |
+		((uint64_t)(cnt_rx_port_stats_bank[bank][bin][2])));
+	}
+
+	/*
+	 * 6. Set some counters that are not available
+	 *    in HW but can be computed from two or more
+	 *    HW counters.
+	 */
+	/*
+	 * If IP parsing is enabled then the IP stats will be read and will be
+	 * valid. Else all traffic, whether IP or not, is counted as _nonip.
+	 */
+	if (valid_ip_stats) {
+		/*
+		 * RX counters.
+		 */
+		counters->cnt_rx_ucst_pkts =
+				counters->cnt_rx_ucst_pkts_nonip +
+				counters->cnt_rx_ucst_pkts_ipv4 +
+				counters->cnt_rx_ucst_pkts_ipv6;
+
+		counters->cnt_rx_mcst_pkts =
+				counters->cnt_rx_mcst_pkts_nonip +
+				counters->cnt_rx_mcst_pkts_ipv4 +
+				counters->cnt_rx_mcst_pkts_ipv6;
+
+		counters->cnt_rx_bcst_pkts =
+				counters->cnt_rx_bcst_pkts_nonip +
+				counters->cnt_rx_bcst_pkts_ipv4 +
+				counters->cnt_rx_bcst_pkts_ipv6;
+
+		/*
+		 * Misc. counters.
+		 */
+		counters->cnt_rx_octets_nonip  =
+				counters->cnt_rx_ucst_octets_nonip +
+				counters->cnt_rx_mcst_octets_nonip +
+				counters->cnt_rx_bcst_octets_nonip;
+
+		counters->cnt_rx_octets_ipv4  =
+				counters->cnt_rx_ucst_octets_ipv4 +
+				counters->cnt_rx_mcst_octets_ipv4 +
+				counters->cnt_rx_bcst_octets_ipv4;
+
+		counters->cnt_rx_octets_ipv6  =
+				counters->cnt_rx_ucst_octets_ipv6 +
+				counters->cnt_rx_mcst_octets_ipv6 +
+				counters->cnt_rx_bcst_octets_ipv6;
+
+		counters->cnt_rx_good_octets =
+				counters->cnt_rx_octets_nonip +
+				counters->cnt_rx_octets_ipv4 +
+				counters->cnt_rx_octets_ipv6;
+	} else {
+		/*
+		 * RX counters.
+		 */
+		counters->cnt_rx_ucst_pkts =
+				counters->cnt_rx_ucst_pkts_nonip;
+		counters->cnt_rx_mcst_pkts =
+				counters->cnt_rx_mcst_pkts_nonip;
+		counters->cnt_rx_bcst_pkts =
+				counters->cnt_rx_bcst_pkts_nonip;
+
+		/*
+		 * Misc. counters.
+		 */
+		counters->cnt_rx_octets_nonip  =
+				counters->cnt_rx_ucst_octets_nonip +
+				counters->cnt_rx_mcst_octets_nonip +
+				counters->cnt_rx_bcst_octets_nonip;
+
+		counters->cnt_rx_good_octets =
+				counters->cnt_rx_octets_nonip;
+	}
+
+	counters->cnt_trigger_drop_redir_pkts =
+			counters->cnt_trigger_redir_pkts +
+			counters->cnt_trigger_drop_pkts;
+
+	/* Emulate Tx _octets counter using the sum of all size bins. */
+	counters->cnt_tx_octets += counters->cnt_tx_minto63_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_64_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_65to127_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_128to255_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_256to511_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_512to1023_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_1024to1522_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_1523to2047_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_2048to4095_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_4096to8191_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_8192to10239_octets;
+	counters->cnt_tx_octets += counters->cnt_tx_10240tomax_octets;
+
+	return 0;
+}   /* end fm10k_get_port_counters */
+
+
+struct fm10k_stats_data {
+	uint64_t rx_pkts;
+	uint64_t tx_pkts;
+	uint64_t rx_drop;
+};
+
+
+static uint64_t
+fm10k_stats_ffu_count_get(struct fm10k_switch *sw, int table_id)
+{
+	uint64_t data;
+
+	if (FM10K_SW_FFU_CNT_BANK < 2) {
+		data = fm10k_read_switch_reg64(sw,
+				0xE40000 + 0x4000 * FM10K_SW_FFU_CNT_BANK +
+				0x4 * (table_id + FM10K_SW_FFU_CNT_START) +
+				0x8000);
+	} else {
+		data = fm10k_read_switch_reg64(sw,
+				0xE40000 + 0x800 * (FM10K_SW_FFU_CNT_BANK - 2) +
+				0x4 * (table_id + FM10K_SW_FFU_CNT_START) +
+				0x10000);
+	}
+	return data;
+}
+
+static void
+fm10k_stats_epl_ffu_print(struct fm10k_switch *sw, int epl)
+{
+	int table_id;
+	uint64_t data;
+
+	table_id = FM10K_FFU_EXT_PORT_RULE_INGRESS(epl);
+	data = fm10k_stats_ffu_count_get(sw, table_id);
+	FM10K_SW_INFO("             FFU[%d] ingress        %-12llu\n",
+			table_id, (unsigned long long)(data));
+
+	table_id = FM10K_FFU_EXT_PORT_RULE_EGRESS(epl);
+	data = fm10k_stats_ffu_count_get(sw, table_id);
+	FM10K_SW_INFO("             FFU[%d]  egress        %-12llu\n",
+			table_id, (unsigned long long)(data));
+}
+
+#define FM10K_STATS_RULE_NUM_MAX	100
+static uint16_t fm10k_stats_rule_list[FM10K_STATS_RULE_NUM_MAX];
+void
+fm10k_stats_rule_count_reg(uint16_t rule_id)
+{
+	int i;
+
+	for (i = 0; i < FM10K_STATS_RULE_NUM_MAX; i++) {
+		if (fm10k_stats_rule_list[i] == 0) {
+			fm10k_stats_rule_list[i] = rule_id;
+			break;
+		}
+	}
+}
+
+void
+fm10k_stats_ffu_count_print(struct fm10k_switch *sw)
+{
+	int i, rule_id;
+	uint64_t data;
+	static uint64_t ffu_count[FM10K_STATS_RULE_NUM_MAX];
+
+	for (i = 0; i < FM10K_STATS_RULE_NUM_MAX; i++) {
+		if (fm10k_stats_rule_list[i] == 0)
+			continue;
+
+		rule_id = fm10k_stats_rule_list[i];
+		data = fm10k_stats_ffu_count_get(sw, rule_id);
+		FM10K_SW_INFO("FFU[%d] count         %-12llu\n",
+				rule_id,
+				(unsigned long long)
+				(data - ffu_count[rule_id]));
+		ffu_count[rule_id] = data;
+	}
+}
+
+void
+fm10k_stats_epl_port_print(struct fm10k_switch *sw)
+{
+	int i, lport;
+	struct fm10k_port_counters counters;
+	struct fm10k_ext_port ext_port;
+	static struct fm10k_stats_data last_data[FM10K_SW_EXT_PORTS_MAX];
+	struct fm10k_stats_data data;
+
+	for (i = 0; i < FM10K_SW_EPLS_SUPPORTED; i++) {
+		lport = sw->epl_map[i].logical_port;
+		ext_port.portno = lport;
+		fm10k_get_port_counters(sw, &ext_port, &counters);
+
+		data.rx_pkts =
+			counters.cnt_rx_bcst_pkts +
+			counters.cnt_rx_ucst_pkts;
+		data.tx_pkts =
+			counters.cnt_tx_bcst_pkts +
+			counters.cnt_tx_ucst_pkts;
+		data.rx_drop = counters.cnt_cmpriv_drop_pkts;
+		FM10K_SW_INFO("EPL  port %-2d lport %-5d tx_pkt %-12llu "
+				"rx_pkt %-12llu drop_pkt %-12llu\n",
+				i, lport,
+				(unsigned long long)
+				(data.tx_pkts - last_data[i].tx_pkts),
+				(unsigned long long)
+				(data.rx_pkts - last_data[i].rx_pkts),
+				(unsigned long long)
+				(data.rx_drop - last_data[i].rx_drop));
+		last_data[i] = data;
+
+		if (fm10k_config_check_debug(sw->dpdk_cfg,
+				FM10K_CONFIG_DEBUG_STATS_FFU))
+			fm10k_stats_epl_ffu_print(sw, i);
+	}
+}
+
+
+static void
+fm10k_stats_port_counter_print(struct fm10k_switch *sw, int lport)
+{
+	unsigned int i;
+	struct fm10k_port_counters counters;
+	struct fm10k_ext_port ext_port;
+	uint64_t *pdata;
+
+	memset(&ext_port, 0, sizeof(ext_port));
+	ext_port.portno = lport;
+	fm10k_get_port_counters(sw, &ext_port, &counters);
+
+	for (i = 0;
+			i < sizeof(rx_port_cnt_map_table) /
+				sizeof(rx_port_cnt_map_table[0]);
+			i++) {
+		pdata =
+			(uint64_t *)((uint8_t *)(&counters) +
+			rx_port_cnt_map_table[i].frame_offset);
+		if (*pdata != 0) {
+			FM10K_SW_INFO("port %d rx bank %d idx %d pkt %llu\n",
+					lport,
+					rx_port_cnt_map_table[i].bank,
+					rx_port_cnt_map_table[i].bin,
+					(unsigned long long)*pdata);
+		}
+	}
+	for (i = 0;
+			i < sizeof(tx_port_cnt_map_table) /
+				sizeof(tx_port_cnt_map_table[0]);
+			i++) {
+		pdata =
+			(uint64_t *)((uint8_t *)(&counters) +
+			tx_port_cnt_map_table[i].frame_offset);
+		if (*pdata != 0) {
+			FM10K_SW_INFO("port %d tx bank %d idx %d pkt %llu\n",
+					lport,
+					tx_port_cnt_map_table[i].bank,
+					tx_port_cnt_map_table[i].bin,
+					(unsigned long long)*pdata);
+		}
+	}
+}
+
+void
+fm10k_stats_port_bank_print(struct fm10k_switch *sw)
+{
+	int i;
+
+	for (i = 0;
+		 i < FM10K_SW_EPLS_SUPPORTED;
+		 i++) {
+		fm10k_stats_port_counter_print(sw,
+				sw->epl_map[i].logical_port);
+	}
+	for (i = 0;
+		 i < FM10K_SW_PEPS_SUPPORTED;
+		 i++) {
+		fm10k_stats_port_counter_print(sw,
+				sw->pep_map[i].logical_port);
+	}
+}
+
+
+void *
+fm10k_switch_process_stats(void *ctx)
+{
+	struct fm10k_switch *sw = ctx;
+
+	usec_delay(5000000);
+
+	if (!fm10k_config_check_debug(sw->dpdk_cfg,
+			FM10K_CONFIG_DEBUG_ENABLE))
+		return NULL;
+
+	if (!sw->dpdk_cfg->stats_interval)
+		return NULL;
+
+	while (sw->detaching == 0) {
+		if (fm10k_config_check_debug(sw->dpdk_cfg,
+				FM10K_CONFIG_DEBUG_STATS_PORT)) {
+			FM10K_SW_INFO("--- port statistic ---\n");
+			fm10k_stats_epl_port_print(sw);
+		}
+		if (fm10k_config_check_debug(sw->dpdk_cfg,
+				FM10K_CONFIG_DEBUG_STATS_FFU)) {
+			FM10K_SW_INFO("--- ffu statistic ---\n");
+			fm10k_stats_ffu_count_print(sw);
+		}
+		if (fm10k_config_check_debug(sw->dpdk_cfg,
+				FM10K_CONFIG_DEBUG_STATS_MORE)) {
+			FM10K_SW_INFO("--- detail statistic ---\n");
+			fm10k_stats_port_bank_print(sw);
+		}
+		usec_delay(1000000 * sw->dpdk_cfg->stats_interval);
+	}
+	return NULL;
+}
+
diff --git a/drivers/net/fm10k/switch/fm10k_stats.h b/drivers/net/fm10k/switch/fm10k_stats.h
new file mode 100644
index 0000000..0bc84e4
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_stats.h
@@ -0,0 +1,257 @@
+/* spdx-license-identifier: bsd-3-clause
+ * copyright 2019   silicom ltd. connectivity solutions
+ */
+
+#ifndef _fm10k_stats_h_
+#define _fm10k_stats_h_
+
+
+#include <stdint.h>
+
+
+#define FM10K_STATS_VERSION		(1 << 4)
+
+
+struct fm10k_scatter_gather_entry {
+	uint32_t  addr;
+	uint32_t  count;
+	uint32_t *data;
+
+};
+
+
+struct fm10k_port_counters {
+	uint64_t cnt_version;
+	uint64_t cnt_rx_ucst_pkts;
+	uint64_t cnt_rx_ucst_pkts_nonip;
+	uint64_t cnt_rx_ucst_pkts_ipv4;
+	uint64_t cnt_rx_ucst_pkts_ipv6;
+	uint64_t cnt_rx_bcst_pkts;
+	uint64_t cnt_rx_bcst_pkts_nonip;
+	uint64_t cnt_rx_bcst_pkts_ipv4;
+	uint64_t cnt_rx_bcst_pkts_ipv6;
+	uint64_t cnt_rx_mcst_pkts;
+	uint64_t cnt_rx_mcst_pkts_nonip;
+	uint64_t cnt_rx_mcst_pkts_ipv4;
+	uint64_t cnt_rx_mcst_pkts_ipv6;
+	uint64_t cnt_rx_pause_pkts;
+	uint64_t cnt_rx_cbpause_pkts;
+	uint64_t cnt_rx_fcs_errors;
+	uint64_t cnt_rx_symbol_errors;
+	uint64_t cnt_rx_framesize_errors;
+	uint64_t cnt_rx_framing_error_pkts;
+	uint64_t cnt_rx_minto63_pkts;
+	uint64_t cnt_rx_64_pkts;
+	uint64_t cnt_rx_65to127_pkts;
+	uint64_t cnt_rx_128to255_pkts;
+	uint64_t cnt_rx_256to511_pkts;
+	uint64_t cnt_rx_512to1023_pkts;
+	uint64_t cnt_rx_1024to1522_pkts;
+	uint64_t cnt_rx_1523to2047_pkts;
+	uint64_t cnt_rx_2048to4095_pkts;
+	uint64_t cnt_rx_4096to8191_pkts;
+	uint64_t cnt_rx_8192to10239_pkts;
+	uint64_t cnt_rx_10240tomax_pkts;
+	uint64_t cnt_rx_minto63_octets;
+	uint64_t cnt_rx_64_octets;
+	uint64_t cnt_rx_65to127_octets;
+	uint64_t cnt_rx_128to255_octets;
+	uint64_t cnt_rx_256to511_octets;
+	uint64_t cnt_rx_512to1023_octets;
+	uint64_t cnt_rx_1024to1522_octets;
+	uint64_t cnt_rx_1523to2047_octets;
+	uint64_t cnt_rx_2048to4095_octets;
+	uint64_t cnt_rx_4096to8191_octets;
+	uint64_t cnt_rx_8192to10239_octets;
+	uint64_t cnt_rx_10240tomax_octets;
+	uint64_t cnt_rx_octets_nonip;
+	uint64_t cnt_rx_octets_ipv4;
+	uint64_t cnt_rx_octets_ipv6;
+	uint64_t cnt_rx_ucst_octets_nonip;
+	uint64_t cnt_rx_ucst_octets_ipv4;
+	uint64_t cnt_rx_ucst_octets_ipv6;
+	uint64_t cnt_rx_bcst_octets_nonip;
+	uint64_t cnt_rx_bcst_octets_ipv4;
+	uint64_t cnt_rx_bcst_octets_ipv6;
+	uint64_t cnt_rx_mcst_octets_nonip;
+	uint64_t cnt_rx_mcst_octets_ipv4;
+	uint64_t cnt_rx_mcst_octets_ipv6;
+	uint64_t cnt_rx_pause_octets;
+	uint64_t cnt_rx_cbpause_octets;
+	uint64_t cnt_rx_fcs_errors_octets;
+	uint64_t cnt_rx_framing_error_octets;
+	uint64_t cnt_rx_good_octets;
+	uint64_t cnt_rx_bad_octets;
+	uint64_t cnt_rx_priority_pkts[16];
+	uint64_t cnt_rx_invalid_priority_pkts;
+	uint64_t cnt_rx_priority_octets[16];
+	uint64_t cnt_rx_invalid_priority_octets;
+	uint64_t cnt_fid_forwarded_pkts;
+	uint64_t cnt_flood_forwarded_pkts;
+	uint64_t cnt_glort_switched_pkts;
+	uint64_t cnt_glort_routed_pkts;
+	uint64_t cnt_specially_handled_pkts;
+	uint64_t cnt_parse_err_drop_pkts;
+	uint64_t cnt_parity_error_pkts;
+	uint64_t cnt_trapped_pkts;
+	uint64_t cnt_pause_drop_pkts;
+	uint64_t cnt_stp_drop_pkts;
+	uint64_t cnt_stp_ingress_drops_pkts;
+	uint64_t cnt_stp_egress_drops_pkts;
+	uint64_t cnt_reserved_trap_pkts;
+	uint64_t cnt_security_violation_pkts;
+	uint64_t cnt_vlan_tag_drop_pkts;
+	uint64_t cnt_vlan_ingressbv_pkts;
+	uint64_t cnt_vlan_egressbv_pkts;
+	uint64_t cnt_loopback_drops_pkts;
+	uint64_t cnt_glort_miss_drop_pkts;
+	uint64_t cnt_ffu_drop_pkts;
+	uint64_t cnt_invalid_drop_pkts;
+	uint64_t cnt_policer_drop_pkts;
+	uint64_t cnt_ttl_drop_pkts;
+	uint64_t cnt_global_wmdrop_pkts;
+	uint64_t cnt_rx_mpdrop_pkts;
+	uint64_t cnt_rx_hogdrop_pkts;
+	uint64_t cnt_tx_hogdrop_pkts;
+	uint64_t cnt_other_pkts;
+	uint64_t cnt_flood_control_drop_pkts;
+	uint64_t cnt_cmpriv_drop_pkts;
+	uint64_t cnt_smp0_drop_pkts;
+	uint64_t cnt_smp1_drop_pkts;
+	uint64_t cnt_rx_hog0_drop_pkts;
+	uint64_t cnt_rx_hog1_drop_pkts;
+	uint64_t cnt_tx_hog0_drop_pkts;
+	uint64_t cnt_tx_hog1_drop_pkts;
+	uint64_t cnt_rate_limit0_drop_pkts;
+	uint64_t cnt_rate_limit1_drop_pkts;
+	uint64_t cnt_bad_smp_drop_pkts;
+	uint64_t cnt_trigger_drop_redir_pkts;
+	uint64_t cnt_trigger_drop_pkts;
+	uint64_t cnt_trigger_redir_pkts;
+	uint64_t cnt_glort_forwarded_pkts;
+	uint64_t cnt_trigger_mirrored_pkts;
+	uint64_t cnt_broadcast_drop_pkts;
+	uint64_t cnt_dlf_drop_pkts;
+	uint64_t cnt_rx_cm_drop_pkts;
+	uint64_t cnt_fid_forwarded_octets;
+	uint64_t cnt_flood_forwarded_octets;
+	uint64_t cnt_specially_handled_octets;
+	uint64_t cnt_parse_err_drop_octets;
+	uint64_t cnt_parity_error_octets;
+	uint64_t cnt_trapped_octets;
+	uint64_t cnt_pause_drop_octets;
+	uint64_t cnt_stp_drop_octets;
+	uint64_t cnt_security_violation_octets;
+	uint64_t cnt_vlan_tag_drop_octets;
+	uint64_t cnt_vlan_ingressbv_octets;
+	uint64_t cnt_vlan_egressbv_octets;
+	uint64_t cnt_loopback_drop_octets;
+	uint64_t cnt_glort_miss_drop_octets;
+	uint64_t cnt_ffu_drop_octets;
+	uint64_t cnt_policer_drop_octets;
+	uint64_t cnt_ttl_drop_octets;
+	uint64_t cnt_other_octets;
+	uint64_t cnt_flood_control_drop_octets;
+	uint64_t cnt_cmpriv_drop_octets;
+	uint64_t cnt_smp0_drop_octets;
+	uint64_t cnt_smp1_drop_octets;
+	uint64_t cnt_rx_hog0_drop_octets;
+	uint64_t cnt_rx_hog1_drop_octets;
+	uint64_t cnt_tx_hog0_drop_octets;
+	uint64_t cnt_tx_hog1_drop_octets;
+	uint64_t cnt_trigger_drop_octets;
+	uint64_t cnt_trigger_redir_octets;
+	uint64_t cnt_glort_forwarded_octets;
+	uint64_t cnt_tx_ucst_pkts;
+	uint64_t cnt_tx_bcst_pkts;
+	uint64_t cnt_tx_mcst_pkts;
+	uint64_t cnt_tx_ucst_pkts_nonip;
+	uint64_t cnt_tx_bcst_pkts_nonip;
+	uint64_t cnt_tx_mcst_pkts_nonip;
+	uint64_t cnt_tx_ucst_pkts_ip;
+	uint64_t cnt_tx_bcst_pkts_ip;
+	uint64_t cnt_tx_mcst_pkts_ip;
+	uint64_t cnt_tx_pause_pkts;
+	uint64_t cnt_tx_cbpause_pkts;
+	uint64_t cnt_tx_fcs_err_drop_pkts;
+	uint64_t cnt_tx_framing_error_pkts;
+	uint64_t cnt_tx_error_sent_pkts;
+	uint64_t cnt_tx_error_drop_pkts;
+	uint64_t cnt_tx_timeout_pkts;
+	uint64_t cnt_tx_outofmem_err_pkts;
+	uint64_t cnt_tx_unrepair_ecc_pkts;
+	uint64_t cnt_tx_loopback_pkts;
+	uint64_t cnt_tx_ttl_drop_pkts;
+	uint64_t cnt_tx_minto63_pkts;
+	uint64_t cnt_tx_64_pkts;
+	uint64_t cnt_tx_65to127_pkts;
+	uint64_t cnt_tx_128to255_pkts;
+	uint64_t cnt_tx_256to511_pkts;
+	uint64_t cnt_tx_512to1023_pkts;
+	uint64_t cnt_tx_1024to1522_pkts;
+	uint64_t cnt_tx_1523to2047_pkts;
+	uint64_t cnt_tx_2048to4095_pkts;
+	uint64_t cnt_tx_4096to8191_pkts;
+	uint64_t cnt_tx_8192to10239_pkts;
+	uint64_t cnt_tx_10240tomax_pkts;
+	uint64_t cnt_tx_minto63_octets;
+	uint64_t cnt_tx_64_octets;
+	uint64_t cnt_tx_65to127_octets;
+	uint64_t cnt_tx_128to255_octets;
+	uint64_t cnt_tx_256to511_octets;
+	uint64_t cnt_tx_512to1023_octets;
+	uint64_t cnt_tx_1024to1522_octets;
+	uint64_t cnt_tx_1523to2047_octets;
+	uint64_t cnt_tx_2048to4095_octets;
+	uint64_t cnt_tx_4096to8191_octets;
+	uint64_t cnt_tx_8192to10239_octets;
+	uint64_t cnt_tx_10240tomax_octets;
+	uint64_t cnt_tx_ucst_octets_nonip;
+	uint64_t cnt_tx_bcst_octets_nonip;
+	uint64_t cnt_tx_mcst_octets_nonip;
+	uint64_t cnt_tx_ucst_octets_ip;
+	uint64_t cnt_tx_bcst_octets_ip;
+	uint64_t cnt_tx_mcst_octets_ip;
+	uint64_t cnt_tx_ucst_octets;
+	uint64_t cnt_tx_mcst_octets;
+	uint64_t cnt_tx_bcst_octets;
+	uint64_t cnt_tx_fcs_err_drop_octets;
+	uint64_t cnt_tx_octets;
+	uint64_t cnt_tx_error_octets;
+	uint64_t cnt_tx_framing_error_octets;
+	uint64_t cnt_tx_pause_octets;
+	uint64_t cnt_tx_cbpause_octets;
+	uint64_t cnt_tx_fcs_errored_octets;
+	uint64_t cnt_tx_error_sent_octets;
+	uint64_t cnt_tx_timeout_octets;
+	uint64_t cnt_tx_outofmem_err_octets;
+	uint64_t cnt_tx_unrepair_ecc_octets;
+	uint64_t cnt_tx_loopback_octets;
+	uint64_t cnt_tx_ttl_drop_octets;
+	uint64_t cnt_tx_priority_octets[16];
+	uint64_t cnt_under_run_pkts;
+	uint64_t cnt_over_run_pkts;
+	uint64_t cnt_rx_fragment_pkts;
+	uint64_t cnt_rx_undersized_pkts;
+	uint64_t cnt_rx_jabber_pkts;
+	uint64_t cnt_corrupted_pkts;
+	uint64_t cnt_code_errors;
+	uint64_t cnt_rx_oversized_pkts;
+	uint64_t cnt_tx_fcs_errored_pkts;
+	uint64_t cnt_stats_drop_count_tx;
+	uint64_t cnt_stats_drop_count_rx;
+	uint64_t cnt_tx_mirror_pkts;
+	uint64_t cnt_tx_mirror_octets;
+	uint64_t cnt_tx_cmdrop_pkts;
+	uint64_t timestamp;
+};
+
+void fm10k_stats_rule_count_reg(uint16_t rule_id);
+void *fm10k_switch_process_stats(void *ctx_);
+
+void fm10k_stats_epl_port_print(struct fm10k_switch *sw);
+void fm10k_stats_dpdk_port_print(struct fm10k_switch *sw);
+void fm10k_stats_ffu_count_print(struct fm10k_switch *sw);
+void fm10k_stats_port_bank_print(struct fm10k_switch *sw);
+
+#endif /* _fm10k_stats_h */
diff --git a/drivers/net/fm10k/switch/fm10k_switch.h b/drivers/net/fm10k/switch/fm10k_switch.h
index 2423dbf..b4b363c 100644
--- a/drivers/net/fm10k/switch/fm10k_switch.h
+++ b/drivers/net/fm10k/switch/fm10k_switch.h
@@ -76,6 +76,37 @@
 #define FM10K_SW_MEM_POOL_SEGS_MAX	24576
 #define FM10K_SW_MEM_POOL_SEGS_RSVD	256
 
+/*
+ * GLORT MAP
+ */
+#define FM10K_SW_EPLA_GLORT		0x10
+#define FM10K_SW_EPLB_GLORT		0x20
+#define FM10K_SW_PEP01_GLORT		0x30
+#define FM10K_SW_PEP23_GLORT		0x40
+#define FM10K_SW_PEP45_GLORT		0x50
+#define FM10K_SW_PEP67_GLORT		0x60
+
+/*
+ * logical port number
+ */
+#define FM10K_SW_EPLA_LOGICAL_PORT	1
+#define FM10K_SW_EPLB_LOGICAL_PORT	2
+
+#define FM10K_SW_PEP01_LOGICAL_PORT	3
+#define FM10K_SW_PEP23_LOGICAL_PORT	4
+#define FM10K_SW_PEP45_LOGICAL_PORT	5
+#define FM10K_SW_PEP67_LOGICAL_PORT	6
+
+/*
+ * physical port number
+ */
+#define FM10K_SW_EPLA_PHYSICAL_PORT	0
+#define FM10K_SW_EPLB_PHYSICAL_PORT	4
+
+#define FM10K_SW_PEP01_PHYSICAL_PORT	36
+#define FM10K_SW_PEP23_PHYSICAL_PORT	40
+#define FM10K_SW_PEP45_PHYSICAL_PORT	44
+#define FM10K_SW_PEP67_PHYSICAL_PORT	48
 
 
 #define FM10K_SW_CARD_ID(v_, d_)	(((v_) << 16) | (d_))
@@ -111,6 +142,7 @@
 #define FM10K_SW_DEV_ID_PE3100G2DQIRM_QXSL4		0x01C2
 #define FM10K_SW_DEV_ID_PE325G2DSIR			0x01C8
 
+
 /*
  * SWITCH
  */
@@ -125,6 +157,15 @@ struct fm10k_device_info {
 	uint8_t num_peps;
 };
 
+static struct fm10k_device_info fm10k_device_table[] = {
+	{   FM10K_SW_VENDOR_ID_SILICOM,	FM10K_SW_DEV_ID_PE3100G2DQIR_QXSL4,
+		"Silicom PE3100G2DQiR-QX4/QS4/QL4",	2, 100, 2, 2 },
+	{   FM10K_SW_VENDOR_ID_SILICOM,	FM10K_SW_DEV_ID_PE3100G2DQIRL_QXSL4,
+		"Silicom PE3100G2DQiRL-QX4/QS4/QL4",	2, 100, 2, 2 },
+	{   FM10K_SW_VENDOR_ID_SILICOM,	FM10K_SW_DEV_ID_PE3100G2DQIRM_QXSL4,
+		"Silicom PE3100G2DQiRM-QX4/QS4/QL4",	2, 100, 2, 4 },
+};
+
 
 struct fm10k_i2c;
 struct fm10k_sbus;
@@ -287,6 +328,114 @@ struct fm10k_switch {
 	fm10k_write_switch_reg(sw, FM10K_SW_GPIO_DATA, data);
 }
 
+
+static struct fm10k_sw_port_map fm10k_pep_port_map[FM10K_SW_PEPS_SUPPORTED] = {
+	{
+		FM10K_SW_PEP01_GLORT,
+		FM10K_SW_PEP01_LOGICAL_PORT,
+		FM10K_SW_PEP01_PHYSICAL_PORT
+	},
+	{
+		FM10K_SW_PEP23_GLORT,
+		FM10K_SW_PEP23_LOGICAL_PORT,
+		FM10K_SW_PEP23_PHYSICAL_PORT
+	},
+	{
+		FM10K_SW_PEP45_GLORT,
+		FM10K_SW_PEP45_LOGICAL_PORT,
+		FM10K_SW_PEP45_PHYSICAL_PORT
+	},
+	{
+		FM10K_SW_PEP67_GLORT,
+		FM10K_SW_PEP67_LOGICAL_PORT,
+		FM10K_SW_PEP67_PHYSICAL_PORT
+	},
+};
+
+static struct fm10k_sw_port_map fm10k_epl_port_map[FM10K_SW_EPLS_SUPPORTED] = {
+	{
+		FM10K_SW_EPLA_GLORT,
+		FM10K_SW_EPLA_LOGICAL_PORT,
+		FM10K_SW_EPLA_PHYSICAL_PORT
+	},
+	{
+		FM10K_SW_EPLB_GLORT,
+		FM10K_SW_EPLB_LOGICAL_PORT,
+		FM10K_SW_EPLB_PHYSICAL_PORT
+	},
+};
+
+static inline uint32_t
+fm10k_switch_pf_logical_get(uint8_t pf_no)
+{
+	return fm10k_pep_port_map[pf_no].logical_port;
+}
+
+static inline uint32_t
+fm10k_switch_epl_logical_get(uint8_t epl_no)
+{
+	return fm10k_epl_port_map[epl_no].logical_port;
+}
+
+static inline uint32_t
+fm10k_switch_vf_glort_get(uint8_t vf_no)
+{
+	return FM10K_SW_VF_GLORT_START + vf_no;
+}
+
+static inline uint32_t
+fm10k_switch_pf_glort_get(uint8_t pf_no)
+{
+	return fm10k_pep_port_map[pf_no].glort;
+}
+
+static inline uint32_t
+fm10k_switch_epl_glort_get(uint8_t epl_no)
+{
+	return fm10k_epl_port_map[epl_no].glort;
+}
+
+static inline uint32_t
+fm10k_switch_pfs_glort_get(uint8_t pf1, uint8_t pf2)
+{
+	uint8_t idx;
+	if (pf1 > pf2)
+		idx = (pf2 & 0xf) | (pf1 << 4 & 0xf0);
+	else
+		idx = (pf1 & 0xf) | (pf2 << 4 & 0xf0);
+	return FM10K_SW_PFS_GLORT_START + idx;
+}
+
+
+static inline struct fm10k_device_info*
+fm10k_get_device_info(struct fm10k_hw *hw)
+{
+	unsigned int i;
+	struct fm10k_device_info *info;
+	uint16_t pci_vendor = hw->vendor_id;
+	uint16_t pci_device = hw->device_id;
+	uint16_t pci_subvendor = hw->subsystem_vendor_id;
+	uint16_t pci_subdevice = hw->subsystem_device_id;
+
+	if (pci_vendor != FM10K_SW_VENDOR_ID_INTEL ||
+	    pci_device != FM10K_SW_DEV_ID_FM10K)
+		return (NULL);
+
+	for (i = 0;
+			i < sizeof(fm10k_device_table) /
+				sizeof(fm10k_device_table[0]);
+			i++) {
+		info = &fm10k_device_table[i];
+		if (pci_subvendor == info->subvendor &&
+		    pci_subdevice == info->subdevice) {
+			return info;
+		}
+	}
+
+	return NULL;
+}
+
+
 #define fm10k_udelay	usec_delay
 typedef int eth_fm10k_dev_init_half_func(struct fm10k_hw *hw);
 
@@ -312,15 +461,6 @@ struct fm10k_flow_list *
 void fm10k_switch_dpdk_tx_queue_num_set(struct fm10k_hw *hw, uint8_t num);
 void fm10k_switch_dpdk_rx_queue_num_set(struct fm10k_hw *hw, uint8_t num);
 
-uint32_t fm10k_switch_pf_logical_get(uint8_t pf_no);
-uint32_t fm10k_switch_epl_logical_get(uint8_t epl_no);
-uint32_t fm10k_switch_vf_glort_get(uint8_t vf_no);
-uint32_t fm10k_switch_pf_glort_get(uint8_t pf_no);
-uint32_t fm10k_switch_pfs_glort_get(uint8_t pf1, uint8_t pf2);
-uint32_t fm10k_switch_epl_glort_get(uint8_t epl_no);
-uint32_t fm10k_switch_multi_glort_get(uint8_t pf1, uint8_t pf2,
-		uint16_t vlan1, uint16_t vlan2, bool *p_new);
-
 int fm10k_switch_mirror_set(struct fm10k_hw *hw, u16 dest_port, u16 vlan);
 int fm10k_switch_mirror_reset(struct fm10k_hw *hw);
 
