[v3,3/5] net/fm10k: add ffu and statistics and config file functions
diff mbox series

Message ID 1584687523-19362-4-git-send-email-xiaojun.liu@silicom.co.il
State Superseded, archived
Delegated to: xiaolong ye
Headers show
Series
  • support fm10k switch management
Related show

Checks

Context Check Description
ci/Intel-compilation success Compilation OK
ci/checkpatch success coding style OK

Commit Message

Xiaojun Liu March 20, 2020, 6:58 a.m. UTC
Add ffu to support offload flow into HW.
It supports forward, mirror, push VLAN, pop VLAN.
It also supports flowset for a group flow definition.
The config file can configure debug log, port speed,
epl port mapping dpdk port, flowset. All these configuration
will be used by switch management.
Statistics includes epl port, ffu rule, dpdk port, and error.
All these statistics data are read from HW.
Modify switch header file to support getting logical port
and glort and device info.

Signed-off-by: Xiaojun Liu <xiaojun.liu@silicom.co.il>
---
 drivers/net/fm10k/Makefile              |    3 +
 drivers/net/fm10k/switch/fm10k_config.c |  857 +++++++++++++++++++++
 drivers/net/fm10k/switch/fm10k_config.h |  178 +++++
 drivers/net/fm10k/switch/fm10k_ffu.c    | 1250 +++++++++++++++++++++++++++++++
 drivers/net/fm10k/switch/fm10k_ffu.h    |   31 +
 drivers/net/fm10k/switch/fm10k_stats.c  | 1105 +++++++++++++++++++++++++++
 drivers/net/fm10k/switch/fm10k_stats.h  |  257 +++++++
 drivers/net/fm10k/switch/fm10k_switch.h |  158 +++-
 8 files changed, 3830 insertions(+), 9 deletions(-)
 create mode 100644 drivers/net/fm10k/switch/fm10k_config.c
 create mode 100644 drivers/net/fm10k/switch/fm10k_config.h
 create mode 100644 drivers/net/fm10k/switch/fm10k_ffu.c
 create mode 100644 drivers/net/fm10k/switch/fm10k_ffu.h
 create mode 100644 drivers/net/fm10k/switch/fm10k_stats.c
 create mode 100644 drivers/net/fm10k/switch/fm10k_stats.h

Patch
diff mbox series

diff --git a/drivers/net/fm10k/Makefile b/drivers/net/fm10k/Makefile
index 6f5ae60..68a72d5 100644
--- a/drivers/net/fm10k/Makefile
+++ b/drivers/net/fm10k/Makefile
@@ -85,6 +85,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..6fb9bab
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_config.c
@@ -0,0 +1,857 @@ 
+/* 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..37be806
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_ffu.c
@@ -0,0 +1,1250 @@ 
+/* 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_MulticastIndex	59: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..451813f
--- /dev/null
+++ b/drivers/net/fm10k/switch/fm10k_stats.c
@@ -0,0 +1,1105 @@ 
+/* 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);