diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index a8bf67af10..96d8c7b49d 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -18,6 +18,7 @@ includes = [
     include_directories('nthw'),
     include_directories('nthw/core'),
     include_directories('nthw/supported'),
+    include_directories('nthw/flow_filter'),
     include_directories('sensors'),
     include_directories('sensors/avr_sensors'),
     include_directories('sensors/board_sensors'),
@@ -59,6 +60,26 @@ sources = files(
     'nthw/core/nthw_spim.c',
     'nthw/core/nthw_spis.c',
     'nthw/core/nthw_tsm.c',
+    'nthw/flow_filter/flow_nthw_cat.c',
+    'nthw/flow_filter/flow_nthw_csu.c',
+    'nthw/flow_filter/flow_nthw_flm.c',
+    'nthw/flow_filter/flow_nthw_hfu.c',
+    'nthw/flow_filter/flow_nthw_hsh.c',
+    'nthw/flow_filter/flow_nthw_hst.c',
+    'nthw/flow_filter/flow_nthw_ifr.c',
+    'nthw/flow_filter/flow_nthw_info.c',
+    'nthw/flow_filter/flow_nthw_ioa.c',
+    'nthw/flow_filter/flow_nthw_km.c',
+    'nthw/flow_filter/flow_nthw_pdb.c',
+    'nthw/flow_filter/flow_nthw_qsl.c',
+    'nthw/flow_filter/flow_nthw_rmc.c',
+    'nthw/flow_filter/flow_nthw_roa.c',
+    'nthw/flow_filter/flow_nthw_rpp_lr.c',
+    'nthw/flow_filter/flow_nthw_slc.c',
+    'nthw/flow_filter/flow_nthw_slc_lr.c',
+    'nthw/flow_filter/flow_nthw_tx_cpy.c',
+    'nthw/flow_filter/flow_nthw_tx_ins.c',
+    'nthw/flow_filter/flow_nthw_tx_rpl.c',
     'nthw/nthw_fpga_model.c',
     'nthw/nthw_dbs.c',
     'nthw/nthw_epp.c',
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.c
new file mode 100644
index 0000000000..91376363c1
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.c
@@ -0,0 +1,1107 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_cat.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+struct cat_nthw *cat_nthw_new(void)
+{
+	struct cat_nthw *p = malloc(sizeof(struct cat_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void cat_nthw_delete(struct cat_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+void cat_nthw_set_debug_mode(struct cat_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_cat, n_debug_mode);
+}
+
+int cat_nthw_init(struct cat_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_CAT, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Cat %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_cat = p_mod;
+
+	p->m_km_if_cnt = fpga_get_product_param(p->mp_fpga, NT_CAT_KM_IF_CNT, -1);
+
+	/* CFN */
+	p->mp_cfn_ctrl = module_get_register(p->m_cat, CAT_CFN_CTRL);
+	p->mp_cfn_addr = register_get_field(p->mp_cfn_ctrl, CAT_CFN_CTRL_ADR);
+	p->mp_cfn_cnt = register_get_field(p->mp_cfn_ctrl, CAT_CFN_CTRL_CNT);
+	p->mp_cfn_data = module_get_register(p->m_cat, CAT_CFN_DATA);
+	p->mp_cfn_data_enable =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_ENABLE);
+	p->mp_cfn_data_inv = register_get_field(p->mp_cfn_data, CAT_CFN_DATA_INV);
+	p->mp_cfn_data_ptc_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_INV);
+	p->mp_cfn_data_ptc_isl =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_ISL);
+	p->mp_cfn_data_ptc_mac =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_MAC);
+	p->mp_cfn_data_ptc_l2 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_L2);
+	p->mp_cfn_data_ptc_vn_tag =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_VNTAG);
+	p->mp_cfn_data_ptc_vlan =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_VLAN);
+	p->mp_cfn_data_ptc_mpls =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_MPLS);
+	p->mp_cfn_data_ptc_l3 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_L3);
+	p->mp_cfn_data_ptc_frag =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_FRAG);
+	p->mp_cfn_data_ptc_ip_prot =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_IP_PROT);
+	p->mp_cfn_data_ptc_l4 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_L4);
+	p->mp_cfn_data_ptc_tunnel =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TUNNEL);
+	p->mp_cfn_data_ptc_tnl_l2 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_L2);
+	p->mp_cfn_data_ptc_tnl_vlan =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_VLAN);
+	p->mp_cfn_data_ptc_tnl_mpls =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_MPLS);
+	p->mp_cfn_data_ptc_tnl_l3 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_L3);
+	p->mp_cfn_data_ptc_tnl_frag =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_FRAG);
+	p->mp_cfn_data_ptc_tnl_ip_prot =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_IP_PROT);
+	p->mp_cfn_data_ptc_tnl_l4 =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_TNL_L4);
+	p->mp_cfn_data_err_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_INV);
+	p->mp_cfn_data_err_cv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_CV);
+	p->mp_cfn_data_err_fcs =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_FCS);
+	p->mp_cfn_data_err_trunc =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_TRUNC);
+	p->mp_cfn_data_mac_port =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_MAC_PORT);
+	p->mp_cfn_data_pm_cmp =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_CMP);
+	p->mp_cfn_data_pm_dct =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_DCT);
+	p->mp_cfn_data_pm_ext_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_EXT_INV);
+	p->mp_cfn_data_pm_cmb =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_CMB);
+	p->mp_cfn_data_pm_and_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_AND_INV);
+	p->mp_cfn_data_pm_or_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_OR_INV);
+	p->mp_cfn_data_pm_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_PM_INV);
+	p->mp_cfn_data_lc = register_get_field(p->mp_cfn_data, CAT_CFN_DATA_LC);
+	p->mp_cfn_data_lc_inv =
+		register_get_field(p->mp_cfn_data, CAT_CFN_DATA_LC_INV);
+
+	if (p->m_km_if_cnt == -1) {
+		p->mp_cfn_data_km0_or =
+			register_get_field(p->mp_cfn_data, CAT_CFN_DATA_KM_OR);
+	} else {
+		p->mp_cfn_data_km0_or =
+			register_get_field(p->mp_cfn_data, CAT_CFN_DATA_KM0_OR);
+		p->mp_cfn_data_km1_or =
+			register_query_field(p->mp_cfn_data, CAT_CFN_DATA_KM1_OR);
+	}
+
+	if (p->m_km_if_cnt < 0) {
+		/* KCE */
+		p->mp_kce_ctrl[0] = module_get_register(p->m_cat, CAT_KCE_CTRL);
+		p->mp_kce_addr[0] =
+			register_get_field(p->mp_kce_ctrl[0], CAT_KCE_CTRL_ADR);
+		p->mp_kce_cnt[0] =
+			register_get_field(p->mp_kce_ctrl[0], CAT_KCE_CTRL_CNT);
+		p->mp_kce_data[0] = module_get_register(p->m_cat, CAT_KCE_DATA);
+		p->mp_kce_data_enable[0] =
+			register_get_field(p->mp_kce_data[0], CAT_KCE_DATA_ENABLE);
+		/* KCS */
+		p->mp_kcs_ctrl[0] = module_get_register(p->m_cat, CAT_KCS_CTRL);
+		p->mp_kcs_addr[0] =
+			register_get_field(p->mp_kcs_ctrl[0], CAT_KCS_CTRL_ADR);
+		p->mp_kcs_cnt[0] =
+			register_get_field(p->mp_kcs_ctrl[0], CAT_KCS_CTRL_CNT);
+		p->mp_kcs_data[0] = module_get_register(p->m_cat, CAT_KCS_DATA);
+		p->mp_kcs_data_category[0] =
+			register_get_field(p->mp_kcs_data[0], CAT_KCS_DATA_CATEGORY);
+		/* FTE */
+		p->mp_fte_ctrl[0] = module_get_register(p->m_cat, CAT_FTE_CTRL);
+		p->mp_fte_addr[0] =
+			register_get_field(p->mp_fte_ctrl[0], CAT_FTE_CTRL_ADR);
+		p->mp_fte_cnt[0] =
+			register_get_field(p->mp_fte_ctrl[0], CAT_FTE_CTRL_CNT);
+		p->mp_fte_data[0] = module_get_register(p->m_cat, CAT_FTE_DATA);
+		p->mp_fte_data_enable[0] =
+			register_get_field(p->mp_fte_data[0], CAT_FTE_DATA_ENABLE);
+	} else {
+		/* KCE 0 */
+		p->mp_kce_ctrl[0] = module_get_register(p->m_cat, CAT_KCE0_CTRL);
+		p->mp_kce_addr[0] =
+			register_get_field(p->mp_kce_ctrl[0], CAT_KCE0_CTRL_ADR);
+		p->mp_kce_cnt[0] =
+			register_get_field(p->mp_kce_ctrl[0], CAT_KCE0_CTRL_CNT);
+		p->mp_kce_data[0] = module_get_register(p->m_cat, CAT_KCE0_DATA);
+		p->mp_kce_data_enable[0] = register_get_field(p->mp_kce_data[0],
+					CAT_KCE0_DATA_ENABLE);
+		/* KCS 0 */
+		p->mp_kcs_ctrl[0] = module_get_register(p->m_cat, CAT_KCS0_CTRL);
+		p->mp_kcs_addr[0] =
+			register_get_field(p->mp_kcs_ctrl[0], CAT_KCS0_CTRL_ADR);
+		p->mp_kcs_cnt[0] =
+			register_get_field(p->mp_kcs_ctrl[0], CAT_KCS0_CTRL_CNT);
+		p->mp_kcs_data[0] = module_get_register(p->m_cat, CAT_KCS0_DATA);
+		p->mp_kcs_data_category[0] =
+			register_get_field(p->mp_kcs_data[0], CAT_KCS0_DATA_CATEGORY);
+		/* FTE 0 */
+		p->mp_fte_ctrl[0] = module_get_register(p->m_cat, CAT_FTE0_CTRL);
+		p->mp_fte_addr[0] =
+			register_get_field(p->mp_fte_ctrl[0], CAT_FTE0_CTRL_ADR);
+		p->mp_fte_cnt[0] =
+			register_get_field(p->mp_fte_ctrl[0], CAT_FTE0_CTRL_CNT);
+		p->mp_fte_data[0] = module_get_register(p->m_cat, CAT_FTE0_DATA);
+		p->mp_fte_data_enable[0] = register_get_field(p->mp_fte_data[0],
+					CAT_FTE0_DATA_ENABLE);
+		/* KCE 1 */
+		p->mp_kce_ctrl[1] = module_get_register(p->m_cat, CAT_KCE1_CTRL);
+		p->mp_kce_addr[1] =
+			register_get_field(p->mp_kce_ctrl[1], CAT_KCE1_CTRL_ADR);
+		p->mp_kce_cnt[1] =
+			register_get_field(p->mp_kce_ctrl[1], CAT_KCE1_CTRL_CNT);
+		p->mp_kce_data[1] = module_get_register(p->m_cat, CAT_KCE1_DATA);
+		p->mp_kce_data_enable[1] = register_get_field(p->mp_kce_data[1],
+					CAT_KCE1_DATA_ENABLE);
+		/* KCS 1 */
+		p->mp_kcs_ctrl[1] = module_get_register(p->m_cat, CAT_KCS1_CTRL);
+		p->mp_kcs_addr[1] =
+			register_get_field(p->mp_kcs_ctrl[1], CAT_KCS1_CTRL_ADR);
+		p->mp_kcs_cnt[1] =
+			register_get_field(p->mp_kcs_ctrl[1], CAT_KCS1_CTRL_CNT);
+		p->mp_kcs_data[1] = module_get_register(p->m_cat, CAT_KCS1_DATA);
+		p->mp_kcs_data_category[1] =
+			register_get_field(p->mp_kcs_data[1], CAT_KCS1_DATA_CATEGORY);
+		/* FTE 1 */
+		p->mp_fte_ctrl[1] = module_get_register(p->m_cat, CAT_FTE1_CTRL);
+		p->mp_fte_addr[1] =
+			register_get_field(p->mp_fte_ctrl[1], CAT_FTE1_CTRL_ADR);
+		p->mp_fte_cnt[1] =
+			register_get_field(p->mp_fte_ctrl[1], CAT_FTE1_CTRL_CNT);
+		p->mp_fte_data[1] = module_get_register(p->m_cat, CAT_FTE1_DATA);
+		p->mp_fte_data_enable[1] = register_get_field(p->mp_fte_data[1],
+					CAT_FTE1_DATA_ENABLE);
+	}
+
+	/* CTE */
+	p->mp_cte_ctrl = module_get_register(p->m_cat, CAT_CTE_CTRL);
+	p->mp_cte_addr = register_get_field(p->mp_cte_ctrl, CAT_CTE_CTRL_ADR);
+	p->mp_cte_cnt = register_get_field(p->mp_cte_ctrl, CAT_CTE_CTRL_CNT);
+	p->mp_cte_data = module_get_register(p->m_cat, CAT_CTE_DATA);
+	p->mp_cte_data_col =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_COL_ENABLE);
+	p->mp_cte_data_cor =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_COR_ENABLE);
+	p->mp_cte_data_hsh =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_HSH_ENABLE);
+	p->mp_cte_data_qsl =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_QSL_ENABLE);
+	p->mp_cte_data_ipf =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_IPF_ENABLE);
+	p->mp_cte_data_slc =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_SLC_ENABLE);
+	p->mp_cte_data_pdb =
+		register_get_field(p->mp_cte_data, CAT_CTE_DATA_PDB_ENABLE);
+	p->mp_cte_data_msk =
+		register_query_field(p->mp_cte_data, CAT_CTE_DATA_MSK_ENABLE);
+	p->mp_cte_data_hst =
+		register_query_field(p->mp_cte_data, CAT_CTE_DATA_HST_ENABLE);
+	p->mp_cte_data_epp =
+		register_query_field(p->mp_cte_data, CAT_CTE_DATA_EPP_ENABLE);
+	p->mp_cte_data_tpe =
+		register_query_field(p->mp_cte_data, CAT_CTE_DATA_TPE_ENABLE);
+	p->mp_cte_data_rrb =
+		register_query_field(p->mp_cte_data, CAT_CTE_DATA_RRB_ENABLE);
+	/* CTS */
+	p->mp_cts_ctrl = module_get_register(p->m_cat, CAT_CTS_CTRL);
+	p->mp_cts_addr = register_get_field(p->mp_cts_ctrl, CAT_CTS_CTRL_ADR);
+	p->mp_cts_cnt = register_get_field(p->mp_cts_ctrl, CAT_CTS_CTRL_CNT);
+	p->mp_cts_data = module_get_register(p->m_cat, CAT_CTS_DATA);
+	p->mp_cts_data_cat_a = register_get_field(p->mp_cts_data, CAT_CTS_DATA_CAT_A);
+	p->mp_cts_data_cat_b = register_get_field(p->mp_cts_data, CAT_CTS_DATA_CAT_B);
+	/* COT */
+	p->mp_cot_ctrl = module_get_register(p->m_cat, CAT_COT_CTRL);
+	p->mp_cot_addr = register_get_field(p->mp_cot_ctrl, CAT_COT_CTRL_ADR);
+	p->mp_cot_cnt = register_get_field(p->mp_cot_ctrl, CAT_COT_CTRL_CNT);
+	p->mp_cot_data = module_get_register(p->m_cat, CAT_COT_DATA);
+	p->mp_cot_data_color = register_get_field(p->mp_cot_data, CAT_COT_DATA_COLOR);
+	p->mp_cot_data_km = register_get_field(p->mp_cot_data, CAT_COT_DATA_KM);
+	p->mp_cot_data_nfv_sb =
+		register_query_field(p->mp_cot_data, CAT_COT_DATA_NFV_SB);
+	/* CCT */
+	p->mp_cct_ctrl = module_get_register(p->m_cat, CAT_CCT_CTRL);
+	p->mp_cct_addr = register_get_field(p->mp_cct_ctrl, CAT_CCT_CTRL_ADR);
+	p->mp_cct_cnt = register_get_field(p->mp_cct_ctrl, CAT_CCT_CTRL_CNT);
+	p->mp_cct_data = module_get_register(p->m_cat, CAT_CCT_DATA);
+	p->mp_cct_data_color = register_get_field(p->mp_cct_data, CAT_CCT_DATA_COLOR);
+	p->mp_cct_data_km = register_get_field(p->mp_cct_data, CAT_CCT_DATA_KM);
+	/* EXO */
+	p->mp_exo_ctrl = module_get_register(p->m_cat, CAT_EXO_CTRL);
+	p->mp_exo_addr = register_get_field(p->mp_exo_ctrl, CAT_EXO_CTRL_ADR);
+	p->mp_exo_cnt = register_get_field(p->mp_exo_ctrl, CAT_EXO_CTRL_CNT);
+	p->mp_exo_data = module_get_register(p->m_cat, CAT_EXO_DATA);
+	p->mp_exo_data_dyn = register_get_field(p->mp_exo_data, CAT_EXO_DATA_DYN);
+	p->mp_exo_data_ofs = register_get_field(p->mp_exo_data, CAT_EXO_DATA_OFS);
+	/* RCK */
+	p->mp_rck_ctrl = module_get_register(p->m_cat, CAT_RCK_CTRL);
+	p->mp_rck_addr = register_get_field(p->mp_rck_ctrl, CAT_RCK_CTRL_ADR);
+	p->mp_rck_cnt = register_get_field(p->mp_rck_ctrl, CAT_RCK_CTRL_CNT);
+	p->mp_rck_data = module_get_register(p->m_cat, CAT_RCK_DATA);
+	/* LEN */
+	p->mp_len_ctrl = module_get_register(p->m_cat, CAT_LEN_CTRL);
+	p->mp_len_addr = register_get_field(p->mp_len_ctrl, CAT_LEN_CTRL_ADR);
+	p->mp_len_cnt = register_get_field(p->mp_len_ctrl, CAT_LEN_CTRL_CNT);
+	p->mp_len_data = module_get_register(p->m_cat, CAT_LEN_DATA);
+	p->mp_len_data_lower = register_get_field(p->mp_len_data, CAT_LEN_DATA_LOWER);
+	p->mp_len_data_upper = register_get_field(p->mp_len_data, CAT_LEN_DATA_UPPER);
+	p->mp_len_data_dyn1 = register_get_field(p->mp_len_data, CAT_LEN_DATA_DYN1);
+	p->mp_len_data_dyn2 = register_get_field(p->mp_len_data, CAT_LEN_DATA_DYN2);
+	p->mp_len_data_inv = register_get_field(p->mp_len_data, CAT_LEN_DATA_INV);
+
+	p->mp_cfn_data_ptc_cfp =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_PTC_CFP);
+	p->mp_cfn_data_err_l3_cs =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_L3_CS);
+	p->mp_cfn_data_err_l4_cs =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_L4_CS);
+	p->mp_cfn_data_err_tnl_l3_cs =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_TNL_L3_CS);
+	p->mp_cfn_data_err_tnl_l4_cs =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_TNL_L4_CS);
+	p->mp_cfn_data_err_ttl_exp =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_TTL_EXP);
+	p->mp_cfn_data_err_tnl_ttl_exp =
+		register_query_field(p->mp_cfn_data, CAT_CFN_DATA_ERR_TNL_TTL_EXP);
+
+	p->mp_kcc_ctrl = module_query_register(p->m_cat, CAT_KCC_CTRL);
+	if (p->mp_kcc_ctrl != NULL) {
+		p->mp_kcc_addr =
+			register_query_field(p->mp_kcc_ctrl, CAT_KCC_CTRL_ADR);
+		p->mp_kcc_cnt =
+			register_query_field(p->mp_kcc_ctrl, CAT_KCC_CTRL_CNT);
+	}
+	p->mp_kcc_data = module_query_register(p->m_cat, CAT_KCC_DATA);
+	if (p->mp_kcc_data != NULL) {
+		p->mp_kcc_data_key =
+			register_query_field(p->mp_kcc_data, CAT_KCC_DATA_KEY);
+		p->mp_kcc_data_category =
+			register_query_field(p->mp_kcc_data, CAT_KCC_DATA_CATEGORY);
+		p->mp_kcc_data_id =
+			register_query_field(p->mp_kcc_data, CAT_KCC_DATA_ID);
+	}
+
+	p->mp_cce_ctrl = module_query_register(p->m_cat, CAT_CCE_CTRL);
+	if (p->mp_cce_ctrl != NULL) {
+		p->mp_cce_addr =
+			register_query_field(p->mp_cce_ctrl, CAT_CCE_CTRL_ADR);
+		p->mp_cce_cnt =
+			register_query_field(p->mp_cce_ctrl, CAT_CCE_CTRL_CNT);
+	}
+	p->mp_cce_data = module_query_register(p->m_cat, CAT_CCE_DATA);
+	if (p->mp_cce_data != NULL) {
+		p->mp_cce_data_imm =
+			register_query_field(p->mp_cce_data, CAT_CCE_DATA_IMM);
+		p->mp_cce_data_ind =
+			register_query_field(p->mp_cce_data, CAT_CCE_DATA_IND);
+	}
+
+	p->mp_ccs_ctrl = module_query_register(p->m_cat, CAT_CCS_CTRL);
+	if (p->mp_ccs_ctrl != NULL) {
+		p->mp_ccs_addr =
+			register_query_field(p->mp_ccs_ctrl, CAT_CCS_CTRL_ADR);
+		p->mp_ccs_cnt =
+			register_query_field(p->mp_ccs_ctrl, CAT_CCS_CTRL_CNT);
+	}
+	p->mp_ccs_data = module_query_register(p->m_cat, CAT_CCS_DATA);
+	if (p->mp_ccs_data != NULL) {
+		p->mp_ccs_data_cor_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_COR_EN);
+		p->mp_ccs_data_cor =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_COR);
+		p->mp_ccs_data_hsh_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_HSH_EN);
+		p->mp_ccs_data_hsh =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_HSH);
+		p->mp_ccs_data_qsl_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_QSL_EN);
+		p->mp_ccs_data_qsl =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_QSL);
+		p->mp_ccs_data_ipf_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_IPF_EN);
+		p->mp_ccs_data_ipf =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_IPF);
+		p->mp_ccs_data_slc_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SLC_EN);
+		p->mp_ccs_data_slc =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SLC);
+		p->mp_ccs_data_pdb_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_PDB_EN);
+		p->mp_ccs_data_pdb =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_PDB);
+		p->mp_ccs_data_msk_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_MSK_EN);
+		p->mp_ccs_data_msk =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_MSK);
+		p->mp_ccs_data_hst_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_HST_EN);
+		p->mp_ccs_data_hst =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_HST);
+		p->mp_ccs_data_epp_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_EPP_EN);
+		p->mp_ccs_data_epp =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_EPP);
+		p->mp_ccs_data_tpe_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_TPE_EN);
+		p->mp_ccs_data_tpe =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_TPE);
+		p->mp_ccs_data_rrb_en =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_RRB_EN);
+		p->mp_ccs_data_rrb =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_RRB);
+		p->mp_ccs_data_sb0_type =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB0_TYPE);
+		p->mp_ccs_data_sb0_data =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB0_DATA);
+		p->mp_ccs_data_sb1_type =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB1_TYPE);
+		p->mp_ccs_data_sb1_data =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB1_DATA);
+		p->mp_ccs_data_sb2_type =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB2_TYPE);
+		p->mp_ccs_data_sb2_data =
+			register_query_field(p->mp_ccs_data, CAT_CCS_DATA_SB2_DATA);
+	}
+
+	return 0;
+}
+
+/* CFN */
+void cat_nthw_cfn_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_addr, val);
+}
+
+void r(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_cnt, val);
+}
+
+void cat_nthw_cfn_enable(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_enable, val);
+}
+
+void cat_nthw_cfn_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_inv, val);
+}
+
+void cat_nthw_cfn_ptc_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_inv, val);
+}
+
+void cat_nthw_cfn_ptc_isl(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_isl, val);
+}
+
+void cat_nthw_cfn_ptc_mac(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_mac, val);
+}
+
+void cat_nthw_cfn_ptc_l2(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_l2, val);
+}
+
+void cat_nthw_cfn_ptc_vn_tag(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_vn_tag, val);
+}
+
+void cat_nthw_cfn_ptc_vlan(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_vlan, val);
+}
+
+void cat_nthw_cfn_ptc_mpls(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_mpls, val);
+}
+
+void cat_nthw_cfn_ptc_l3(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_l3, val);
+}
+
+void cat_nthw_cfn_ptc_frag(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_frag, val);
+}
+
+void cat_nthw_cfn_ptc_ip_prot(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_ip_prot, val);
+}
+
+void cat_nthw_cfn_ptc_l4(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_l4, val);
+}
+
+void cat_nthw_cfn_ptc_tunnel(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tunnel, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_l2(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_l2, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_vlan(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_vlan, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_mpls(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_mpls, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_l3(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_l3, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_frag(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_frag, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_ip_prot(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_ip_prot, val);
+}
+
+void cat_nthw_cfn_ptc_tnl_l4(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_ptc_tnl_l4, val);
+}
+
+void cat_nthw_cfn_ptc_cfp(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_ptc_cfp);
+	field_set_val32(p->mp_cfn_data_ptc_cfp, val);
+}
+
+void cat_nthw_cfn_err_l3_cs(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_l3_cs);
+	field_set_val32(p->mp_cfn_data_err_l3_cs, val);
+}
+
+void cat_nthw_cfn_err_l4_cs(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_l4_cs);
+	field_set_val32(p->mp_cfn_data_err_l4_cs, val);
+}
+
+void cat_nthw_cfn_err_tnl_l3_cs(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_tnl_l3_cs);
+	field_set_val32(p->mp_cfn_data_err_tnl_l3_cs, val);
+}
+
+void cat_nthw_cfn_err_tnl_l4_cs(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_tnl_l4_cs);
+	field_set_val32(p->mp_cfn_data_err_tnl_l4_cs, val);
+}
+
+void cat_nthw_cfn_err_ttl_exp(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_ttl_exp);
+	field_set_val32(p->mp_cfn_data_err_ttl_exp, val);
+}
+
+void cat_nthw_cfn_err_tnl_ttl_exp(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_err_tnl_ttl_exp);
+	field_set_val32(p->mp_cfn_data_err_tnl_ttl_exp, val);
+}
+
+void cat_nthw_cfn_err_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_err_inv, val);
+}
+
+void cat_nthw_cfn_err_cv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_err_cv, val);
+}
+
+void cat_nthw_cfn_err_fcs(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_err_fcs, val);
+}
+
+void cat_nthw_cfn_err_trunc(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_err_trunc, val);
+}
+
+void cat_nthw_cfn_mac_port(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_mac_port, val);
+}
+
+void cat_nthw_cfn_pm_cmp(const struct cat_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_cfn_data_pm_cmp, val, p->mp_cfn_data_pm_cmp->mn_words);
+}
+
+void cat_nthw_cfn_pm_dct(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_dct, val);
+}
+
+void cat_nthw_cfn_pm_ext_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_ext_inv, val);
+}
+
+void cat_nthw_cfn_pm_cmb(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_cmb, val);
+}
+
+void cat_nthw_cfn_pm_and_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_and_inv, val);
+}
+
+void cat_nthw_cfn_pm_or_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_or_inv, val);
+}
+
+void cat_nthw_cfn_pm_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_pm_inv, val);
+}
+
+void cat_nthw_cfn_lc(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_lc, val);
+}
+
+void cat_nthw_cfn_lc_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_lc_inv, val);
+}
+
+void cat_nthw_cfn_km0_or(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cfn_data_km0_or, val);
+}
+
+void cat_nthw_cfn_km1_or(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cfn_data_km1_or);
+	field_set_val32(p->mp_cfn_data_km1_or, val);
+}
+
+void cat_nthw_cfn_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_cfn_ctrl, 1);
+	register_flush(p->mp_cfn_data, 1);
+}
+
+void cat_nthw_kce_select(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kce_addr[index], val);
+}
+
+void cat_nthw_kce_cnt(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kce_cnt[index], val);
+}
+
+void cat_nthw_kce_enable(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kce_data_enable[index], val);
+}
+
+void cat_nthw_kce_flush(const struct cat_nthw *p, int index)
+{
+	register_flush(p->mp_kce_ctrl[index], 1);
+	register_flush(p->mp_kce_data[index], 1);
+}
+
+void cat_nthw_kcs_select(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kcs_addr[index], val);
+}
+
+void cat_nthw_kcs_cnt(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kcs_cnt[index], val);
+}
+
+void cat_nthw_kcs_category(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_kcs_data_category[index], val);
+}
+
+void cat_nthw_kcs_flush(const struct cat_nthw *p, int index)
+{
+	register_flush(p->mp_kcs_ctrl[index], 1);
+	register_flush(p->mp_kcs_data[index], 1);
+}
+
+void cat_nthw_fte_select(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_fte_addr[index], val);
+}
+
+void cat_nthw_fte_cnt(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_fte_cnt[index], val);
+}
+
+void cat_nthw_fte_enable(const struct cat_nthw *p, int index, uint32_t val)
+{
+	field_set_val32(p->mp_fte_data_enable[index], val);
+}
+
+void cat_nthw_fte_flush(const struct cat_nthw *p, int index)
+{
+	register_flush(p->mp_fte_ctrl[index], 1);
+	register_flush(p->mp_fte_data[index], 1);
+}
+
+void cat_nthw_cte_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_addr, val);
+}
+
+void cat_nthw_cte_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_cnt, val);
+}
+
+void cat_nthw_cte_enable_col(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_col, val);
+}
+
+void cat_nthw_cte_enable_cor(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_cor, val);
+}
+
+void cat_nthw_cte_enable_hsh(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_hsh, val);
+}
+
+void cat_nthw_cte_enable_qsl(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_qsl, val);
+}
+
+void cat_nthw_cte_enable_ipf(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_ipf, val);
+}
+
+void cat_nthw_cte_enable_slc(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_slc, val);
+}
+
+void cat_nthw_cte_enable_pdb(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cte_data_pdb, val);
+}
+
+void cat_nthw_cte_enable_msk(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cte_data_msk);
+	field_set_val32(p->mp_cte_data_msk, val);
+}
+
+void cat_nthw_cte_enable_hst(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cte_data_hst);
+	field_set_val32(p->mp_cte_data_hst, val);
+}
+
+void cat_nthw_cte_enable_epp(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cte_data_epp);
+	field_set_val32(p->mp_cte_data_epp, val);
+}
+
+void cat_nthw_cte_enable_tpe(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cte_data_tpe);
+	field_set_val32(p->mp_cte_data_tpe, val);
+}
+
+void cat_nthw_cte_enable_rrb(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cte_data_rrb);
+	field_set_val32(p->mp_cte_data_rrb, val);
+}
+
+void cat_nthw_cte_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_cte_ctrl, 1);
+	register_flush(p->mp_cte_data, 1);
+}
+
+void cat_nthw_cts_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cts_addr, val);
+}
+
+void cat_nthw_cts_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cts_cnt, val);
+}
+
+void cat_nthw_cts_cat_a(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cts_data_cat_a, val);
+}
+
+void cat_nthw_cts_cat_b(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cts_data_cat_b, val);
+}
+
+void cat_nthw_cts_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_cts_ctrl, 1);
+	register_flush(p->mp_cts_data, 1);
+}
+
+void cat_nthw_cot_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cot_addr, val);
+}
+
+void cat_nthw_cot_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cot_cnt, val);
+}
+
+void cat_nthw_cot_color(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cot_data_color, val);
+}
+
+void cat_nthw_cot_km(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cot_data_km, val);
+}
+
+void cat_nthw_cot_nfv_sb(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cot_data_nfv_sb);
+	field_set_val32(p->mp_cot_data_nfv_sb, val);
+}
+
+void cat_nthw_cot_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_cot_ctrl, 1);
+	register_flush(p->mp_cot_data, 1);
+}
+
+void cat_nthw_cct_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cct_addr, val);
+}
+
+void cat_nthw_cct_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cct_cnt, val);
+}
+
+void cat_nthw_cct_color(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cct_data_color, val);
+}
+
+void cat_nthw_cct_km(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cct_data_km, val);
+}
+
+void cat_nthw_cct_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_cct_ctrl, 1);
+	register_flush(p->mp_cct_data, 1);
+}
+
+void cat_nthw_exo_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_exo_addr, val);
+}
+
+void cat_nthw_exo_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_exo_cnt, val);
+}
+
+void cat_nthw_exo_dyn(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_exo_data_dyn, val);
+}
+
+void cat_nthw_exo_ofs(const struct cat_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_exo_data_ofs, val);
+}
+
+void cat_nthw_exo_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_exo_ctrl, 1);
+	register_flush(p->mp_exo_data, 1);
+}
+
+void cat_nthw_rck_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rck_addr, val);
+}
+
+void cat_nthw_rck_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rck_cnt, val);
+}
+
+void cat_nthw_rck_data(const struct cat_nthw *p, uint32_t val)
+{
+	register_set_val(p->mp_rck_data, &val, 1);
+	register_make_dirty(p->mp_rck_data);
+}
+
+void cat_nthw_rck_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_rck_ctrl, 1);
+	register_flush(p->mp_rck_data, 1);
+}
+
+void cat_nthw_len_select(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_addr, val);
+}
+
+void cat_nthw_len_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_cnt, val);
+}
+
+void cat_nthw_len_lower(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_data_lower, val);
+}
+
+void cat_nthw_len_upper(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_data_upper, val);
+}
+
+void cat_nthw_len_dyn1(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_data_dyn1, val);
+}
+
+void cat_nthw_len_dyn2(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_data_dyn2, val);
+}
+
+void cat_nthw_len_inv(const struct cat_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_len_data_inv, val);
+}
+
+void cat_nthw_len_flush(const struct cat_nthw *p)
+{
+	register_flush(p->mp_len_ctrl, 1);
+	register_flush(p->mp_len_data, 1);
+}
+
+void cat_nthw_kcc_select(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_kcc_addr);
+	field_set_val32(p->mp_kcc_addr, val);
+}
+
+void cat_nthw_kcc_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_kcc_cnt);
+	field_set_val32(p->mp_kcc_cnt, val);
+}
+
+void cat_nthw_kcc_key(const struct cat_nthw *p, uint32_t *val)
+{
+	assert(p->mp_kcc_data_key);
+	field_set_val(p->mp_kcc_data_key, val, 2);
+}
+
+void cat_nthw_kcc_category(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_kcc_data_category);
+	field_set_val32(p->mp_kcc_data_category, val);
+}
+
+void cat_nthw_kcc_id(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_kcc_data_id);
+	field_set_val32(p->mp_kcc_data_id, val);
+}
+
+void cat_nthw_kcc_flush(const struct cat_nthw *p)
+{
+	assert(p->mp_kcc_ctrl);
+	assert(p->mp_kcc_data);
+	register_flush(p->mp_kcc_ctrl, 1);
+	register_flush(p->mp_kcc_data, 1);
+}
+
+void cat_nthw_cce_select(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cce_addr);
+	field_set_val32(p->mp_cce_addr, val);
+}
+
+void cat_nthw_cce_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cce_cnt);
+	field_set_val32(p->mp_cce_cnt, val);
+}
+
+void cat_nthw_cce_data_imm(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cce_data_imm);
+	field_set_val32(p->mp_cce_data_imm, val);
+}
+
+void cat_nthw_cce_data_ind(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_cce_data_ind);
+	field_set_val32(p->mp_cce_data_ind, val);
+}
+
+void cat_nthw_cce_flush(const struct cat_nthw *p)
+{
+	assert(p->mp_cce_ctrl);
+	assert(p->mp_cce_data);
+	register_flush(p->mp_cce_ctrl, 1);
+	register_flush(p->mp_cce_data, 1);
+}
+
+void cat_nthw_ccs_select(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_ccs_addr);
+	field_set_val32(p->mp_ccs_addr, val);
+}
+
+void cat_nthw_ccs_cnt(const struct cat_nthw *p, uint32_t val)
+{
+	assert(p->mp_ccs_cnt);
+	field_set_val32(p->mp_ccs_cnt, val);
+}
+
+#define CATNTHW_CCS_SET(name)                                             \
+	void cat_nthw_ccs_data_##name(const struct cat_nthw *p, uint32_t val) \
+	{                                                                 \
+		assert(p->mp_ccs_data_##name);                               \
+		field_set_val32(p->mp_ccs_data_##name, val);                  \
+	}
+
+CATNTHW_CCS_SET(cor_en);
+CATNTHW_CCS_SET(cor);
+CATNTHW_CCS_SET(hsh_en);
+CATNTHW_CCS_SET(hsh);
+CATNTHW_CCS_SET(qsl_en);
+CATNTHW_CCS_SET(qsl);
+CATNTHW_CCS_SET(ipf_en);
+CATNTHW_CCS_SET(ipf);
+CATNTHW_CCS_SET(slc_en);
+CATNTHW_CCS_SET(slc);
+CATNTHW_CCS_SET(pdb_en);
+CATNTHW_CCS_SET(pdb);
+CATNTHW_CCS_SET(msk_en);
+CATNTHW_CCS_SET(msk);
+CATNTHW_CCS_SET(hst_en);
+CATNTHW_CCS_SET(hst);
+CATNTHW_CCS_SET(epp_en);
+CATNTHW_CCS_SET(epp);
+CATNTHW_CCS_SET(tpe_en);
+CATNTHW_CCS_SET(tpe);
+CATNTHW_CCS_SET(rrb_en);
+CATNTHW_CCS_SET(rrb);
+CATNTHW_CCS_SET(sb0_type);
+CATNTHW_CCS_SET(sb0_data);
+CATNTHW_CCS_SET(sb1_type);
+CATNTHW_CCS_SET(sb1_data);
+CATNTHW_CCS_SET(sb2_type);
+CATNTHW_CCS_SET(sb2_data);
+
+void cat_nthw_ccs_flush(const struct cat_nthw *p)
+{
+	assert(p->mp_ccs_ctrl);
+	assert(p->mp_ccs_data);
+	register_flush(p->mp_ccs_ctrl, 1);
+	register_flush(p->mp_ccs_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.h
new file mode 100644
index 0000000000..41ac891a93
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_cat.h
@@ -0,0 +1,372 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_CAT_H__
+#define __FLOW_NTHW_CAT_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct cat_nthw;
+
+typedef struct cat_nthw cat_nthw_t;
+
+struct cat_nthw *cat_nthw_new(void);
+void cat_nthw_delete(struct cat_nthw *p);
+int cat_nthw_init(struct cat_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int cat_nthw_setup(struct cat_nthw *p, int n_idx, int n_idx_cnt);
+void cat_nthw_set_debug_mode(struct cat_nthw *p, unsigned int n_debug_mode);
+
+/* CFN */
+void cat_nthw_cfn_select(const struct cat_nthw *p, uint32_t val);
+void r(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_enable(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_isl(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_cfp(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_mac(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_l2(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_vn_tag(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_vlan(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_mpls(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_l3(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_frag(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_ip_prot(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_l4(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tunnel(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_l2(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_vlan(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_mpls(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_l3(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_frag(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_ip_prot(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_ptc_tnl_l4(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_cv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_fcs(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_trunc(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_l3_cs(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_l4_cs(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_tnl_l3_cs(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_tnl_l4_cs(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_ttl_exp(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_err_tnl_ttl_exp(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_mac_port(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_cmp(const struct cat_nthw *p, const uint32_t *val);
+void cat_nthw_cfn_pm_dct(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_ext_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_cmb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_and_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_or_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_pm_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_lc(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_lc_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_km0_or(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_km1_or(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cfn_flush(const struct cat_nthw *p);
+/* KCE 0/1 */
+void cat_nthw_kce_select(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kce_cnt(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kce_enable(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kce_flush(const struct cat_nthw *p, int index);
+/* KCS 0/1 */
+void cat_nthw_kcs_select(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kcs_cnt(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kcs_category(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_kcs_flush(const struct cat_nthw *p, int index);
+/* FTE 0/1 */
+void cat_nthw_fte_select(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_fte_cnt(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_fte_enable(const struct cat_nthw *p, int index, uint32_t val);
+void cat_nthw_fte_flush(const struct cat_nthw *p, int index);
+/* CTE */
+void cat_nthw_cte_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_col(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_cor(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_hsh(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_qsl(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_ipf(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_slc(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_pdb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_msk(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_hst(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_epp(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_tpe(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_enable_rrb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cte_flush(const struct cat_nthw *p);
+/* CTS */
+void cat_nthw_cts_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cts_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cts_flush(const struct cat_nthw *p);
+void cat_nthw_cts_cat_a(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cts_cat_b(const struct cat_nthw *p, uint32_t val);
+/* COT */
+void cat_nthw_cot_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cot_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cot_color(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cot_km(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cot_nfv_sb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cot_flush(const struct cat_nthw *p);
+/* CCT */
+void cat_nthw_cct_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cct_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cct_color(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cct_km(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cct_flush(const struct cat_nthw *p);
+/* EXO */
+void cat_nthw_exo_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_exo_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_exo_dyn(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_exo_ofs(const struct cat_nthw *p, int32_t val);
+void cat_nthw_exo_flush(const struct cat_nthw *p);
+/* RCK */
+void cat_nthw_rck_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_rck_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_rck_data(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_rck_flush(const struct cat_nthw *p);
+/* LEN */
+void cat_nthw_len_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_lower(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_upper(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_dyn1(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_dyn2(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_inv(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_len_flush(const struct cat_nthw *p);
+/* KCC */
+void cat_nthw_kcc_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_kcc_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_kcc_key(const struct cat_nthw *p, uint32_t *val);
+void cat_nthw_kcc_category(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_kcc_id(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_kcc_flush(const struct cat_nthw *p);
+/* CCE */
+void cat_nthw_cce_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cce_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cce_data_imm(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cce_data_ind(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_cce_flush(const struct cat_nthw *p);
+/* CCS */
+void cat_nthw_ccs_select(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_cnt(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_cor_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_cor(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_hsh_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_hsh(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_qsl_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_qsl(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_ipf_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_ipf(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_slc_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_slc(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_pdb_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_pdb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_msk_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_msk(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_hst_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_hst(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_epp_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_epp(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_tpe_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_tpe(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_rrb_en(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_rrb(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb0_type(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb0_data(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb1_type(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb1_data(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb2_type(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_data_sb2_data(const struct cat_nthw *p, uint32_t val);
+void cat_nthw_ccs_flush(const struct cat_nthw *p);
+
+struct cat_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+	nt_module_t *m_cat;
+	int m_km_if_cnt;
+
+	nt_register_t *mp_cfn_ctrl;
+	nt_field_t *mp_cfn_addr;
+	nt_field_t *mp_cfn_cnt;
+	nt_register_t *mp_cfn_data;
+	nt_field_t *mp_cfn_data_enable;
+	nt_field_t *mp_cfn_data_inv;
+	nt_field_t *mp_cfn_data_ptc_inv;
+	nt_field_t *mp_cfn_data_ptc_isl;
+	nt_field_t *mp_cfn_data_ptc_cfp;
+	nt_field_t *mp_cfn_data_ptc_mac;
+	nt_field_t *mp_cfn_data_ptc_l2;
+	nt_field_t *mp_cfn_data_ptc_vn_tag;
+	nt_field_t *mp_cfn_data_ptc_vlan;
+	nt_field_t *mp_cfn_data_ptc_mpls;
+	nt_field_t *mp_cfn_data_ptc_l3;
+	nt_field_t *mp_cfn_data_ptc_frag;
+	nt_field_t *mp_cfn_data_ptc_ip_prot;
+	nt_field_t *mp_cfn_data_ptc_l4;
+	nt_field_t *mp_cfn_data_ptc_tunnel;
+	nt_field_t *mp_cfn_data_ptc_tnl_l2;
+	nt_field_t *mp_cfn_data_ptc_tnl_vlan;
+	nt_field_t *mp_cfn_data_ptc_tnl_mpls;
+	nt_field_t *mp_cfn_data_ptc_tnl_l3;
+	nt_field_t *mp_cfn_data_ptc_tnl_frag;
+	nt_field_t *mp_cfn_data_ptc_tnl_ip_prot;
+	nt_field_t *mp_cfn_data_ptc_tnl_l4;
+	nt_field_t *mp_cfn_data_err_inv;
+	nt_field_t *mp_cfn_data_err_cv;
+	nt_field_t *mp_cfn_data_err_fcs;
+	nt_field_t *mp_cfn_data_err_trunc;
+	nt_field_t *mp_cfn_data_err_l3_cs;
+	nt_field_t *mp_cfn_data_err_l4_cs;
+	nt_field_t *mp_cfn_data_err_tnl_l3_cs;
+	nt_field_t *mp_cfn_data_err_tnl_l4_cs;
+	nt_field_t *mp_cfn_data_err_ttl_exp;
+	nt_field_t *mp_cfn_data_err_tnl_ttl_exp;
+	nt_field_t *mp_cfn_data_mac_port;
+	nt_field_t *mp_cfn_data_pm_cmp;
+	nt_field_t *mp_cfn_data_pm_dct;
+	nt_field_t *mp_cfn_data_pm_ext_inv;
+	nt_field_t *mp_cfn_data_pm_cmb;
+	nt_field_t *mp_cfn_data_pm_and_inv;
+	nt_field_t *mp_cfn_data_pm_or_inv;
+	nt_field_t *mp_cfn_data_pm_inv;
+	nt_field_t *mp_cfn_data_lc;
+	nt_field_t *mp_cfn_data_lc_inv;
+	nt_field_t *mp_cfn_data_km0_or;
+	nt_field_t *mp_cfn_data_km1_or;
+
+	nt_register_t *mp_kce_ctrl[2];
+	nt_field_t *mp_kce_addr[2];
+	nt_field_t *mp_kce_cnt[2];
+	nt_register_t *mp_kce_data[2];
+	nt_field_t *mp_kce_data_enable[2];
+
+	nt_register_t *mp_kcs_ctrl[2];
+	nt_field_t *mp_kcs_addr[2];
+	nt_field_t *mp_kcs_cnt[2];
+	nt_register_t *mp_kcs_data[2];
+	nt_field_t *mp_kcs_data_category[2];
+
+	nt_register_t *mp_fte_ctrl[2];
+	nt_field_t *mp_fte_addr[2];
+	nt_field_t *mp_fte_cnt[2];
+	nt_register_t *mp_fte_data[2];
+	nt_field_t *mp_fte_data_enable[2];
+
+	nt_register_t *mp_cte_ctrl;
+	nt_field_t *mp_cte_addr;
+	nt_field_t *mp_cte_cnt;
+	nt_register_t *mp_cte_data;
+	nt_field_t *mp_cte_data_col;
+	nt_field_t *mp_cte_data_cor;
+	nt_field_t *mp_cte_data_hsh;
+	nt_field_t *mp_cte_data_qsl;
+	nt_field_t *mp_cte_data_ipf;
+	nt_field_t *mp_cte_data_slc;
+	nt_field_t *mp_cte_data_pdb;
+	nt_field_t *mp_cte_data_msk;
+	nt_field_t *mp_cte_data_hst;
+	nt_field_t *mp_cte_data_epp;
+	nt_field_t *mp_cte_data_tpe;
+	nt_field_t *mp_cte_data_rrb;
+
+	nt_register_t *mp_cts_ctrl;
+	nt_field_t *mp_cts_addr;
+	nt_field_t *mp_cts_cnt;
+	nt_register_t *mp_cts_data;
+	nt_field_t *mp_cts_data_cat_a;
+	nt_field_t *mp_cts_data_cat_b;
+
+	nt_register_t *mp_cot_ctrl;
+	nt_field_t *mp_cot_addr;
+	nt_field_t *mp_cot_cnt;
+	nt_register_t *mp_cot_data;
+	nt_field_t *mp_cot_data_color;
+	nt_field_t *mp_cot_data_km;
+	nt_field_t *mp_cot_data_nfv_sb;
+
+	nt_register_t *mp_cct_ctrl;
+	nt_field_t *mp_cct_addr;
+	nt_field_t *mp_cct_cnt;
+	nt_register_t *mp_cct_data;
+	nt_field_t *mp_cct_data_color;
+	nt_field_t *mp_cct_data_km;
+
+	nt_register_t *mp_exo_ctrl;
+	nt_field_t *mp_exo_addr;
+	nt_field_t *mp_exo_cnt;
+	nt_register_t *mp_exo_data;
+	nt_field_t *mp_exo_data_dyn;
+	nt_field_t *mp_exo_data_ofs;
+
+	nt_register_t *mp_rck_ctrl;
+	nt_field_t *mp_rck_addr;
+	nt_field_t *mp_rck_cnt;
+	nt_register_t *mp_rck_data;
+
+	nt_register_t *mp_len_ctrl;
+	nt_field_t *mp_len_addr;
+	nt_field_t *mp_len_cnt;
+	nt_register_t *mp_len_data;
+	nt_field_t *mp_len_data_lower;
+	nt_field_t *mp_len_data_upper;
+	nt_field_t *mp_len_data_dyn1;
+	nt_field_t *mp_len_data_dyn2;
+	nt_field_t *mp_len_data_inv;
+	nt_register_t *mp_kcc_ctrl;
+	nt_field_t *mp_kcc_addr;
+	nt_field_t *mp_kcc_cnt;
+
+	nt_register_t *mp_kcc_data;
+	nt_field_t *mp_kcc_data_key;
+	nt_field_t *mp_kcc_data_category;
+	nt_field_t *mp_kcc_data_id;
+
+	nt_register_t *mp_cce_ctrl;
+	nt_field_t *mp_cce_addr;
+	nt_field_t *mp_cce_cnt;
+
+	nt_register_t *mp_cce_data;
+	nt_field_t *mp_cce_data_imm;
+	nt_field_t *mp_cce_data_ind;
+
+	nt_register_t *mp_ccs_ctrl;
+	nt_field_t *mp_ccs_addr;
+	nt_field_t *mp_ccs_cnt;
+
+	nt_register_t *mp_ccs_data;
+	nt_field_t *mp_ccs_data_cor_en;
+	nt_field_t *mp_ccs_data_cor;
+
+	nt_field_t *mp_ccs_data_hsh_en;
+	nt_field_t *mp_ccs_data_hsh;
+	nt_field_t *mp_ccs_data_qsl_en;
+	nt_field_t *mp_ccs_data_qsl;
+	nt_field_t *mp_ccs_data_ipf_en;
+	nt_field_t *mp_ccs_data_ipf;
+	nt_field_t *mp_ccs_data_slc_en;
+	nt_field_t *mp_ccs_data_slc;
+	nt_field_t *mp_ccs_data_pdb_en;
+	nt_field_t *mp_ccs_data_pdb;
+	nt_field_t *mp_ccs_data_msk_en;
+	nt_field_t *mp_ccs_data_msk;
+	nt_field_t *mp_ccs_data_hst_en;
+	nt_field_t *mp_ccs_data_hst;
+	nt_field_t *mp_ccs_data_epp_en;
+	nt_field_t *mp_ccs_data_epp;
+	nt_field_t *mp_ccs_data_tpe_en;
+	nt_field_t *mp_ccs_data_tpe;
+	nt_field_t *mp_ccs_data_rrb_en;
+	nt_field_t *mp_ccs_data_rrb;
+	nt_field_t *mp_ccs_data_sb0_type;
+	nt_field_t *mp_ccs_data_sb0_data;
+	nt_field_t *mp_ccs_data_sb1_type;
+	nt_field_t *mp_ccs_data_sb1_data;
+	nt_field_t *mp_ccs_data_sb2_type;
+	nt_field_t *mp_ccs_data_sb2_data;
+};
+
+#endif /* __FLOW_NTHW_CAT_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.c
new file mode 100644
index 0000000000..5a7f90ad69
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.c
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_csu.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void csu_nthw_set_debug_mode(struct csu_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_csu, n_debug_mode);
+}
+
+struct csu_nthw *csu_nthw_new(void)
+{
+	struct csu_nthw *p = malloc(sizeof(struct csu_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void csu_nthw_delete(struct csu_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int csu_nthw_init(struct csu_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_CSU, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Csu %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_csu = p_mod;
+
+	p->mp_rcp_ctrl = module_get_register(p->m_csu, CSU_RCP_CTRL);
+	p->mp_rcp_ctrl_adr = register_get_field(p->mp_rcp_ctrl, CSU_RCP_CTRL_ADR);
+	p->mp_rcp_ctrl_cnt = register_get_field(p->mp_rcp_ctrl, CSU_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_csu, CSU_RCP_DATA);
+	p->mp_rcp_data_ol3_cmd =
+		register_get_field(p->mp_rcp_data, CSU_RCP_DATA_OL3_CMD);
+	p->mp_rcp_data_ol4_cmd =
+		register_get_field(p->mp_rcp_data, CSU_RCP_DATA_OL4_CMD);
+	p->mp_rcp_data_il3_cmd =
+		register_get_field(p->mp_rcp_data, CSU_RCP_DATA_IL3_CMD);
+	p->mp_rcp_data_il4_cmd =
+		register_get_field(p->mp_rcp_data, CSU_RCP_DATA_IL4_CMD);
+
+	return 0;
+}
+
+void csu_nthw_rcp_select(const struct csu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_adr, val);
+}
+
+void csu_nthw_rcp_cnt(const struct csu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_cnt, val);
+}
+
+void csu_nthw_rcp_outer_l3_cmd(const struct csu_nthw *p, uint32_t val)
+{
+	/*
+	 * Select L3 calc method for outer layer3.
+	 * 0: Do not touch checksum field.
+	 * 1: Check, but do not touch checksum field.
+	 * 2: Insert checksum header value for BAD checksum.
+	 * 3: Insert checksum header value for GOOD checksum.
+	 */
+	field_set_val32(p->mp_rcp_data_ol3_cmd, val);
+}
+
+void csu_nthw_rcp_outer_l4_cmd(const struct csu_nthw *p, uint32_t val)
+{
+	/*
+	 * Select L4 calc method for outer layer4.
+	 * 0: Do not touch checksum field.
+	 * 1: Check, but do not touch checksum field.
+	 * 2: Insert checksum header value for BAD checksum.
+	 * 3: Insert checksum header value for GOOD checksum.
+	 * 4: Set UDP checksum value of ZERO for both IPv4/IPv6, set good checksum for TCP.
+	 * 5: Set UDP checksum value of ZERO for IPv4, set good checksum for TCP.
+	 * 6: Set UDP checksum value of ZERO for outer tunnel when tunnel is IPv4/IPv6 and UDP,
+	 *    otherwise GOOD checksum.
+	 * 7: Set UDP checksum value of ZERO for outer tunnel when tunnel is IPv4 and UDP, otherwise
+	 *    GOOD checksum.
+	 */
+	field_set_val32(p->mp_rcp_data_ol4_cmd, val);
+}
+
+void csu_nthw_rcp_inner_l3_cmd(const struct csu_nthw *p, uint32_t val)
+{
+	/*
+	 * Select L3 calc method for inner layer3 (tunneled).
+	 * 0: Do not touch checksum field.
+	 * 1: Check, but do not touch checksum field.
+	 * 2: Insert checksum header value for BAD checksum.
+	 * 3: Insert checksum header value for GOOD checksum.
+	 */
+	field_set_val32(p->mp_rcp_data_il3_cmd, val);
+}
+
+void csu_nthw_rcp_inner_l4_cmd(const struct csu_nthw *p, uint32_t val)
+{
+	/*
+	 * Select L4 calc method for inner layer4 (tunneled).
+	 * 0: Do not touch checksum field.
+	 * 1: Check, but do not touch checksum field.
+	 * 2: Insert checksum header value for BAD checksum.
+	 * 3: Insert checksum header value for GOOD checksum.
+	 * 4: Set UDP checksum value of ZERO for both IPv4/IPv6, set good checksum for TCP.
+	 * 5: Set UDP checksum value of ZERO for IPv4, set good checksum for TCP.
+	 * 6: Set UDP checksum value of ZERO for outer tunnel when tunnel is IPv4/IPv6 and UDP,
+	 *    otherwise GOOD checksum.
+	 * 7: Set UDP checksum value of ZERO for outer tunnel when tunnel is IPv4 and UDP, otherwise
+	 *    GOOD checksum.
+	 */
+	field_set_val32(p->mp_rcp_data_il4_cmd, val);
+}
+
+void csu_nthw_rcp_flush(const struct csu_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.h
new file mode 100644
index 0000000000..6cb0e1f781
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_csu.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef _FLOW_NTHW_CSU_H_
+#define _FLOW_NTHW_CSU_H_
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct csu_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_csu;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_ctrl_adr;
+	nt_field_t *mp_rcp_ctrl_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_ol3_cmd;
+	nt_field_t *mp_rcp_data_ol4_cmd;
+	nt_field_t *mp_rcp_data_il3_cmd;
+	nt_field_t *mp_rcp_data_il4_cmd;
+};
+
+struct csu_nthw *csu_nthw_new(void);
+void csu_nthw_delete(struct csu_nthw *p);
+int csu_nthw_init(struct csu_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int csu_nthw_setup(struct csu_nthw *p, int n_idx, int n_idx_cnt);
+void csu_nthw_set_debug_mode(struct csu_nthw *p, unsigned int n_debug_mode);
+
+void csu_nthw_rcp_select(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_cnt(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_outer_l3_cmd(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_outer_l4_cmd(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_inner_l3_cmd(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_inner_l4_cmd(const struct csu_nthw *p, uint32_t val);
+void csu_nthw_rcp_flush(const struct csu_nthw *p);
+
+#endif /* _FLOW_NTHW_CSU_H_ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.c
new file mode 100644
index 0000000000..4549898cc1
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.c
@@ -0,0 +1,1140 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+#include "nthw_rac.h"
+
+#include "flow_nthw_flm.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+struct flm_nthw *flm_nthw_new(void)
+{
+	struct flm_nthw *p = malloc(sizeof(struct flm_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void flm_nthw_delete(struct flm_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+void flm_nthw_set_debug_mode(struct flm_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_flm, n_debug_mode);
+}
+
+int flm_nthw_init(struct flm_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_FLM, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Flm %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_rac = p_fpga->p_fpga_info->mp_nthw_rac;
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_flm = p_mod;
+
+	p->mp_control = module_get_register(p->m_flm, FLM_CONTROL);
+	p->mp_control_enable =
+		register_get_field(p->mp_control, FLM_CONTROL_ENABLE);
+	p->mp_control_init = register_get_field(p->mp_control, FLM_CONTROL_INIT);
+	p->mp_control_lds = register_get_field(p->mp_control, FLM_CONTROL_LDS);
+	p->mp_control_lfs = register_get_field(p->mp_control, FLM_CONTROL_LFS);
+	p->mp_control_lis = register_get_field(p->mp_control, FLM_CONTROL_LIS);
+	p->mp_control_uds = register_get_field(p->mp_control, FLM_CONTROL_UDS);
+	p->mp_control_uis = register_get_field(p->mp_control, FLM_CONTROL_UIS);
+	p->mp_control_rds = register_get_field(p->mp_control, FLM_CONTROL_RDS);
+	p->mp_control_ris = register_get_field(p->mp_control, FLM_CONTROL_RIS);
+	p->mp_control_pds = register_query_field(p->mp_control, FLM_CONTROL_PDS);
+	p->mp_control_pis = register_query_field(p->mp_control, FLM_CONTROL_PIS);
+	p->mp_control_crcwr = register_get_field(p->mp_control, FLM_CONTROL_CRCWR);
+	p->mp_control_crcrd = register_get_field(p->mp_control, FLM_CONTROL_CRCRD);
+	p->mp_control_rbl = register_get_field(p->mp_control, FLM_CONTROL_RBL);
+	p->mp_control_eab = register_get_field(p->mp_control, FLM_CONTROL_EAB);
+	p->mp_control_split_sdram_usage =
+		register_get_field(p->mp_control, FLM_CONTROL_SPLIT_SDRAM_USAGE);
+
+	p->mp_status = module_get_register(p->m_flm, FLM_STATUS);
+	p->mp_status_calibdone =
+		register_get_field(p->mp_status, FLM_STATUS_CALIBDONE);
+	p->mp_status_initdone =
+		register_get_field(p->mp_status, FLM_STATUS_INITDONE);
+	p->mp_status_idle = register_get_field(p->mp_status, FLM_STATUS_IDLE);
+	p->mp_status_critical =
+		register_get_field(p->mp_status, FLM_STATUS_CRITICAL);
+	p->mp_status_panic = register_get_field(p->mp_status, FLM_STATUS_PANIC);
+	p->mp_status_crcerr = register_get_field(p->mp_status, FLM_STATUS_CRCERR);
+	p->mp_status_eft_bp = register_get_field(p->mp_status, FLM_STATUS_EFT_BP);
+
+	p->mp_timeout = module_get_register(p->m_flm, FLM_TIMEOUT);
+	p->mp_timeout_t = register_get_field(p->mp_timeout, FLM_TIMEOUT_T);
+
+	p->mp_scrub = module_get_register(p->m_flm, FLM_SCRUB);
+	p->mp_scrub_i = register_get_field(p->mp_scrub, FLM_SCRUB_I);
+
+	p->mp_load_bin = module_get_register(p->m_flm, FLM_LOAD_BIN);
+	p->mp_load_bin_bin = register_get_field(p->mp_load_bin, FLM_LOAD_BIN_BIN);
+
+	p->mp_load_pps = module_get_register(p->m_flm, FLM_LOAD_PPS);
+	p->mp_load_pps_pps = register_get_field(p->mp_load_pps, FLM_LOAD_PPS_PPS);
+
+	p->mp_load_lps = module_get_register(p->m_flm, FLM_LOAD_LPS);
+	p->mp_load_lps_lps = register_get_field(p->mp_load_lps, FLM_LOAD_LPS_LPS);
+
+	p->mp_load_aps = module_get_register(p->m_flm, FLM_LOAD_APS);
+	p->mp_load_aps_aps = register_get_field(p->mp_load_aps, FLM_LOAD_APS_APS);
+
+	p->mp_prio = module_get_register(p->m_flm, FLM_PRIO);
+	p->mp_prio_limit0 = register_get_field(p->mp_prio, FLM_PRIO_LIMIT0);
+	p->mp_prio_ft0 = register_get_field(p->mp_prio, FLM_PRIO_FT0);
+	p->mp_prio_limit1 = register_get_field(p->mp_prio, FLM_PRIO_LIMIT1);
+	p->mp_prio_ft1 = register_get_field(p->mp_prio, FLM_PRIO_FT1);
+	p->mp_prio_limit2 = register_get_field(p->mp_prio, FLM_PRIO_LIMIT2);
+	p->mp_prio_ft2 = register_get_field(p->mp_prio, FLM_PRIO_FT2);
+	p->mp_prio_limit3 = register_get_field(p->mp_prio, FLM_PRIO_LIMIT3);
+	p->mp_prio_ft3 = register_get_field(p->mp_prio, FLM_PRIO_FT3);
+
+	p->mp_pst_ctrl = module_get_register(p->m_flm, FLM_PST_CTRL);
+	p->mp_pst_ctrl_adr = register_get_field(p->mp_pst_ctrl, FLM_PST_CTRL_ADR);
+	p->mp_pst_ctrl_cnt = register_get_field(p->mp_pst_ctrl, FLM_PST_CTRL_CNT);
+	p->mp_pst_data = module_get_register(p->m_flm, FLM_PST_DATA);
+	p->mp_pst_data_bp = register_get_field(p->mp_pst_data, FLM_PST_DATA_BP);
+	p->mp_pst_data_pp = register_get_field(p->mp_pst_data, FLM_PST_DATA_PP);
+	p->mp_pst_data_tp = register_get_field(p->mp_pst_data, FLM_PST_DATA_TP);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_flm, FLM_RCP_CTRL);
+	p->mp_rcp_ctrl_adr = register_get_field(p->mp_rcp_ctrl, FLM_RCP_CTRL_ADR);
+	p->mp_rcp_ctrl_cnt = register_get_field(p->mp_rcp_ctrl, FLM_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_flm, FLM_RCP_DATA);
+	p->mp_rcp_data_lookup =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_LOOKUP);
+	p->mp_rcp_data_qw0_dyn =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_QW0_DYN);
+	p->mp_rcp_data_qw0_ofs =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_QW0_OFS);
+	p->mp_rcp_data_qw0_sel =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_QW0_SEL);
+	p->mp_rcp_data_qw4_dyn =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_QW4_DYN);
+	p->mp_rcp_data_qw4_ofs =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_QW4_OFS);
+	p->mp_rcp_data_sw8_dyn =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_SW8_DYN);
+	p->mp_rcp_data_sw8_ofs =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_SW8_OFS);
+	p->mp_rcp_data_sw8_sel =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_SW8_SEL);
+	p->mp_rcp_data_sw9_dyn =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_SW9_DYN);
+	p->mp_rcp_data_sw9_ofs =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_SW9_OFS);
+	p->mp_rcp_data_mask = register_get_field(p->mp_rcp_data, FLM_RCP_DATA_MASK);
+	p->mp_rcp_data_kid = register_get_field(p->mp_rcp_data, FLM_RCP_DATA_KID);
+	p->mp_rcp_data_opn = register_get_field(p->mp_rcp_data, FLM_RCP_DATA_OPN);
+	p->mp_rcp_data_ipn = register_get_field(p->mp_rcp_data, FLM_RCP_DATA_IPN);
+	p->mp_rcp_data_byt_dyn =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_BYT_DYN);
+	p->mp_rcp_data_byt_ofs =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_BYT_OFS);
+	p->mp_rcp_data_txplm = register_get_field(p->mp_rcp_data, FLM_RCP_DATA_TXPLM);
+	p->mp_rcp_data_auto_ipv4_mask =
+		register_get_field(p->mp_rcp_data, FLM_RCP_DATA_AUTO_IPV4_MASK);
+
+	p->mp_buf_ctrl = module_get_register(p->m_flm, FLM_BUF_CTRL);
+
+	p->mp_lrn_data = module_get_register(p->m_flm, FLM_LRN_DATA);
+	p->mp_inf_data = module_get_register(p->m_flm, FLM_INF_DATA);
+	p->mp_sta_data = module_get_register(p->m_flm, FLM_STA_DATA);
+
+	p->mp_stat_lrn_done = module_get_register(p->m_flm, FLM_STAT_LRN_DONE);
+	p->mp_stat_lrn_done_cnt =
+		register_get_field(p->mp_stat_lrn_done, FLM_STAT_LRN_DONE_CNT);
+
+	p->mp_stat_lrn_ignore = module_get_register(p->m_flm, FLM_STAT_LRN_IGNORE);
+	p->mp_stat_lrn_ignore_cnt =
+		register_get_field(p->mp_stat_lrn_ignore, FLM_STAT_LRN_IGNORE_CNT);
+
+	p->mp_stat_lrn_fail = module_get_register(p->m_flm, FLM_STAT_LRN_FAIL);
+	p->mp_stat_lrn_fail_cnt =
+		register_get_field(p->mp_stat_lrn_fail, FLM_STAT_LRN_FAIL_CNT);
+
+	p->mp_stat_unl_done = module_get_register(p->m_flm, FLM_STAT_UNL_DONE);
+	p->mp_stat_unl_done_cnt =
+		register_get_field(p->mp_stat_unl_done, FLM_STAT_UNL_DONE_CNT);
+
+	p->mp_stat_unl_ignore = module_get_register(p->m_flm, FLM_STAT_UNL_IGNORE);
+	p->mp_stat_unl_ignore_cnt =
+		register_get_field(p->mp_stat_unl_ignore, FLM_STAT_UNL_IGNORE_CNT);
+
+	p->mp_stat_prb_done = module_query_register(p->m_flm, FLM_STAT_PRB_DONE);
+	p->mp_stat_prb_done_cnt =
+		register_query_field(p->mp_stat_prb_done, FLM_STAT_PRB_DONE_CNT);
+
+	p->mp_stat_prb_ignore = module_query_register(p->m_flm, FLM_STAT_PRB_IGNORE);
+	p->mp_stat_prb_ignore_cnt = register_query_field(p->mp_stat_prb_ignore,
+				FLM_STAT_PRB_IGNORE_CNT);
+
+	p->mp_stat_rel_done = module_get_register(p->m_flm, FLM_STAT_REL_DONE);
+	p->mp_stat_rel_done_cnt =
+		register_get_field(p->mp_stat_rel_done, FLM_STAT_REL_DONE_CNT);
+
+	p->mp_stat_rel_ignore = module_get_register(p->m_flm, FLM_STAT_REL_IGNORE);
+	p->mp_stat_rel_ignore_cnt =
+		register_get_field(p->mp_stat_rel_ignore, FLM_STAT_REL_IGNORE_CNT);
+
+	p->mp_stat_aul_done = module_get_register(p->m_flm, FLM_STAT_AUL_DONE);
+	p->mp_stat_aul_done_cnt =
+		register_get_field(p->mp_stat_aul_done, FLM_STAT_AUL_DONE_CNT);
+
+	p->mp_stat_aul_ignore = module_get_register(p->m_flm, FLM_STAT_AUL_IGNORE);
+	p->mp_stat_aul_ignore_cnt =
+		register_get_field(p->mp_stat_aul_ignore, FLM_STAT_AUL_IGNORE_CNT);
+
+	p->mp_stat_aul_fail = module_get_register(p->m_flm, FLM_STAT_AUL_FAIL);
+	p->mp_stat_aul_fail_cnt =
+		register_get_field(p->mp_stat_aul_fail, FLM_STAT_AUL_FAIL_CNT);
+
+	p->mp_stat_tul_done = module_get_register(p->m_flm, FLM_STAT_TUL_DONE);
+	p->mp_stat_tul_done_cnt =
+		register_get_field(p->mp_stat_tul_done, FLM_STAT_TUL_DONE_CNT);
+
+	p->mp_stat_flows = module_get_register(p->m_flm, FLM_STAT_FLOWS);
+	p->mp_stat_flows_cnt =
+		register_get_field(p->mp_stat_flows, FLM_STAT_FLOWS_CNT);
+
+	p->mp_stat_sta_done = module_query_register(p->m_flm, FLM_STAT_STA_DONE);
+	p->mp_stat_sta_done_cnt =
+		register_query_field(p->mp_stat_sta_done, FLM_STAT_STA_DONE_CNT);
+
+	p->mp_stat_inf_done = module_query_register(p->m_flm, FLM_STAT_INF_DONE);
+	p->mp_stat_inf_done_cnt =
+		register_query_field(p->mp_stat_inf_done, FLM_STAT_INF_DONE_CNT);
+
+	p->mp_stat_inf_skip = module_query_register(p->m_flm, FLM_STAT_INF_SKIP);
+	p->mp_stat_inf_skip_cnt =
+		register_query_field(p->mp_stat_inf_skip, FLM_STAT_INF_SKIP_CNT);
+
+	p->mp_stat_pck_hit = module_query_register(p->m_flm, FLM_STAT_PCK_HIT);
+	p->mp_stat_pck_hit_cnt =
+		register_query_field(p->mp_stat_pck_hit, FLM_STAT_PCK_HIT_CNT);
+
+	p->mp_stat_pck_miss = module_query_register(p->m_flm, FLM_STAT_PCK_MISS);
+	p->mp_stat_pck_miss_cnt =
+		register_query_field(p->mp_stat_pck_miss, FLM_STAT_PCK_MISS_CNT);
+
+	p->mp_stat_pck_unh = module_query_register(p->m_flm, FLM_STAT_PCK_UNH);
+	p->mp_stat_pck_unh_cnt =
+		register_query_field(p->mp_stat_pck_unh, FLM_STAT_PCK_UNH_CNT);
+
+	p->mp_stat_pck_dis = module_query_register(p->m_flm, FLM_STAT_PCK_DIS);
+	p->mp_stat_pck_dis_cnt =
+		register_query_field(p->mp_stat_pck_dis, FLM_STAT_PCK_DIS_CNT);
+
+	p->mp_stat_csh_hit = module_query_register(p->m_flm, FLM_STAT_CSH_HIT);
+	p->mp_stat_csh_hit_cnt =
+		register_query_field(p->mp_stat_csh_hit, FLM_STAT_CSH_HIT_CNT);
+
+	p->mp_stat_csh_miss = module_query_register(p->m_flm, FLM_STAT_CSH_MISS);
+	p->mp_stat_csh_miss_cnt =
+		register_query_field(p->mp_stat_csh_miss, FLM_STAT_CSH_MISS_CNT);
+
+	p->mp_stat_csh_unh = module_query_register(p->m_flm, FLM_STAT_CSH_UNH);
+	p->mp_stat_csh_unh_cnt =
+		register_query_field(p->mp_stat_csh_unh, FLM_STAT_CSH_UNH_CNT);
+
+	p->mp_stat_cuc_start = module_query_register(p->m_flm, FLM_STAT_CUC_START);
+	p->mp_stat_cuc_start_cnt =
+		register_query_field(p->mp_stat_cuc_start, FLM_STAT_CUC_START_CNT);
+
+	p->mp_stat_cuc_move = module_query_register(p->m_flm, FLM_STAT_CUC_MOVE);
+	p->mp_stat_cuc_move_cnt =
+		register_query_field(p->mp_stat_cuc_move, FLM_STAT_CUC_MOVE_CNT);
+
+	return 0;
+}
+
+void flm_nthw_control_enable(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_enable, val);
+}
+
+void flm_nthw_control_init(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_init, val);
+}
+
+void flm_nthw_control_lds(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_lds, val);
+}
+
+void flm_nthw_control_lfs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_lfs, val);
+}
+
+void flm_nthw_control_lis(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_lis, val);
+}
+
+void flm_nthw_control_uds(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_uds, val);
+}
+
+void flm_nthw_control_uis(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_uis, val);
+}
+
+void flm_nthw_control_rds(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_rds, val);
+}
+
+void flm_nthw_control_ris(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_ris, val);
+}
+
+void flm_nthw_control_pds(const struct flm_nthw *p, uint32_t val)
+{
+	assert(p->mp_control_pds);
+	field_set_val32(p->mp_control_pds, val);
+}
+
+void flm_nthw_control_pis(const struct flm_nthw *p, uint32_t val)
+{
+	assert(p->mp_control_pis);
+	field_set_val32(p->mp_control_pis, val);
+}
+
+void flm_nthw_control_crcwr(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_crcwr, val);
+}
+
+void flm_nthw_control_crcrd(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_crcrd, val);
+}
+
+void flm_nthw_control_rbl(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_rbl, val);
+}
+
+void flm_nthw_control_eab(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_eab, val);
+}
+
+void flm_nthw_control_split_sdram_usage(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_control_split_sdram_usage, val);
+}
+
+void flm_nthw_control_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_control, 1);
+}
+
+void flm_nthw_status_calibdone(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_calibdone);
+}
+
+void flm_nthw_status_initdone(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_initdone);
+}
+
+void flm_nthw_status_idle(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_idle);
+}
+
+void flm_nthw_status_critical(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_critical);
+
+	else
+		field_set_val32(p->mp_status_critical, *val);
+}
+
+void flm_nthw_status_panic(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_panic);
+
+	else
+		field_set_val32(p->mp_status_panic, *val);
+}
+
+void flm_nthw_status_crcerr(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_crcerr);
+
+	else
+		field_set_val32(p->mp_status_crcerr, *val);
+}
+
+void flm_nthw_status_eft_bp(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_status_eft_bp);
+}
+
+void flm_nthw_status_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_status, 1);
+}
+
+void flm_nthw_status_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_status);
+}
+
+void flm_nthw_timeout_t(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_timeout_t, val);
+}
+
+void flm_nthw_timeout_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_timeout, 1);
+}
+
+void flm_nthw_scrub_i(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_scrub_i, val);
+}
+
+void flm_nthw_scrub_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_scrub, 1);
+}
+
+void flm_nthw_load_bin(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_load_bin_bin, val);
+}
+
+void flm_nthw_load_bin_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_load_bin, 1);
+}
+
+void flm_nthw_load_pps(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_load_pps_pps, val);
+}
+
+void flm_nthw_load_pps_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_load_pps, 1);
+}
+
+void flm_nthw_load_lps(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_load_lps_lps, val);
+}
+
+void flm_nthw_load_lps_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_load_lps, 1);
+}
+
+void flm_nthw_load_aps(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_load_aps_aps, val);
+}
+
+void flm_nthw_load_aps_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_load_aps, 1);
+}
+
+void flm_nthw_prio_limit0(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_limit0, val);
+}
+
+void flm_nthw_prio_ft0(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_ft0, val);
+}
+
+void flm_nthw_prio_limit1(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_limit1, val);
+}
+
+void flm_nthw_prio_ft1(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_ft1, val);
+}
+
+void flm_nthw_prio_limit2(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_limit2, val);
+}
+
+void flm_nthw_prio_ft2(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_ft2, val);
+}
+
+void flm_nthw_prio_limit3(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_limit3, val);
+}
+
+void flm_nthw_prio_ft3(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_prio_ft3, val);
+}
+
+void flm_nthw_prio_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_prio, 1);
+}
+
+void flm_nthw_pst_select(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_pst_ctrl_adr, val);
+}
+
+void flm_nthw_pst_cnt(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_pst_ctrl_cnt, val);
+}
+
+void flm_nthw_pst_bp(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_pst_data_bp, val);
+}
+
+void flm_nthw_pst_pp(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_pst_data_pp, val);
+}
+
+void flm_nthw_pst_tp(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_pst_data_tp, val);
+}
+
+void flm_nthw_pst_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_pst_ctrl, 1);
+	register_flush(p->mp_pst_data, 1);
+}
+
+void flm_nthw_rcp_select(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_adr, val);
+}
+
+void flm_nthw_rcp_cnt(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_cnt, val);
+}
+
+void flm_nthw_rcp_lookup(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_lookup, val);
+}
+
+void flm_nthw_rcp_qw0_dyn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_dyn, val);
+}
+
+void flm_nthw_rcp_qw0_ofs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_ofs, val);
+}
+
+void flm_nthw_rcp_qw0_sel(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_sel, val);
+}
+
+void flm_nthw_rcp_qw4_dyn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_dyn, val);
+}
+
+void flm_nthw_rcp_qw4_ofs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_ofs, val);
+}
+
+void flm_nthw_rcp_sw8_dyn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw8_dyn, val);
+}
+
+void flm_nthw_rcp_sw8_ofs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw8_ofs, val);
+}
+
+void flm_nthw_rcp_sw8_sel(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw8_sel, val);
+}
+
+void flm_nthw_rcp_sw9_dyn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw9_dyn, val);
+}
+
+void flm_nthw_rcp_sw9_ofs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw9_ofs, val);
+}
+
+void flm_nthw_rcp_mask(const struct flm_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_mask, val, 10);
+}
+
+void flm_nthw_rcp_kid(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_kid, val);
+}
+
+void flm_nthw_rcp_opn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_opn, val);
+}
+
+void flm_nthw_rcp_ipn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ipn, val);
+}
+
+void flm_nthw_rcp_byt_dyn(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_byt_dyn, val);
+}
+
+void flm_nthw_rcp_byt_ofs(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_byt_ofs, val);
+}
+
+void flm_nthw_rcp_txplm(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_txplm, val);
+}
+
+void flm_nthw_rcp_auto_ipv4_mask(const struct flm_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_auto_ipv4_mask, val);
+}
+
+void flm_nthw_rcp_flush(const struct flm_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+int flm_nthw_buf_ctrl_update(const struct flm_nthw *p, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail)
+{
+	int ret = -1;
+
+	struct nthw_rac *rac = (struct nthw_rac *)p->mp_rac;
+	uint32_t address_bufctrl = register_get_address(p->mp_buf_ctrl);
+	rab_bus_id_t bus_id = 1;
+	struct dma_buf_ptr bc_buf;
+
+	ret = nthw_rac_rab_dma_begin(rac);
+	if (ret == 0) {
+		nthw_rac_rab_read32_dma(rac, address_bufctrl, bus_id, 2, &bc_buf);
+		ret = nthw_rac_rab_dma_commit(rac);
+		if (ret != 0)
+			return ret;
+
+		uint32_t bc_mask = bc_buf.size - 1;
+		uint32_t bc_index = bc_buf.index;
+		*lrn_free = bc_buf.base[bc_index & bc_mask] & 0xffff;
+		*inf_avail = (bc_buf.base[bc_index & bc_mask] >> 16) & 0xffff;
+		*sta_avail = bc_buf.base[(bc_index + 1) & bc_mask] & 0xffff;
+	}
+
+	return ret;
+}
+
+int flm_nthw_lrn_data_flush(const struct flm_nthw *p, const uint32_t *data,
+			 uint32_t word_count, uint32_t *lrn_free,
+			 uint32_t *inf_avail, uint32_t *sta_avail)
+{
+	int ret = -1;
+
+	struct nthw_rac *rac = (struct nthw_rac *)p->mp_rac;
+	uint32_t address = register_get_address(p->mp_lrn_data);
+	uint32_t address_bufctrl = register_get_address(p->mp_buf_ctrl);
+	rab_bus_id_t bus_id = 1;
+	struct dma_buf_ptr bc_buf;
+
+	if (nthw_rac_rab_dma_begin(rac) == 0) {
+		/* Announce the number of words to write to LRN_DATA */
+		uint32_t bufctrl_data[2];
+
+		bufctrl_data[0] = word_count;
+		bufctrl_data[1] = 0;
+		nthw_rac_rab_write32_dma(rac, address_bufctrl, bus_id, 2,
+					bufctrl_data);
+		nthw_rac_rab_write32_dma(rac, address, bus_id, word_count, data);
+		nthw_rac_rab_read32_dma(rac, address_bufctrl, bus_id, 2, &bc_buf);
+		ret = nthw_rac_rab_dma_commit(rac);
+		if (ret != 0)
+			return ret;
+
+		uint32_t bc_mask = bc_buf.size - 1;
+		uint32_t bc_index = bc_buf.index;
+		*lrn_free = bc_buf.base[bc_index & bc_mask] & 0xffff;
+		*inf_avail = (bc_buf.base[bc_index & bc_mask] >> 16) & 0xffff;
+		*sta_avail = bc_buf.base[(bc_index + 1) & bc_mask] & 0xffff;
+	}
+
+	return ret;
+}
+
+int flm_nthw_inf_data_update(const struct flm_nthw *p, uint32_t *data,
+			  uint32_t word_count, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail)
+{
+	int ret = -1;
+
+	struct nthw_rac *rac = (struct nthw_rac *)p->mp_rac;
+	uint32_t address_infdata = register_get_address(p->mp_inf_data);
+	uint32_t address_bufctrl = register_get_address(p->mp_buf_ctrl);
+	rab_bus_id_t bus_id = 1;
+	struct dma_buf_ptr buf;
+	struct dma_buf_ptr bc_buf;
+
+	ret = nthw_rac_rab_dma_begin(rac);
+	if (ret == 0) {
+		/* Announce the number of words to read from INF_DATA */
+		uint32_t bufctrl_data[2];
+
+		bufctrl_data[0] = word_count << 16;
+		bufctrl_data[1] = 0;
+		nthw_rac_rab_write32_dma(rac, address_bufctrl, bus_id, 2,
+					bufctrl_data);
+		nthw_rac_rab_read32_dma(rac, address_infdata, bus_id, word_count,
+				       &buf);
+		nthw_rac_rab_read32_dma(rac, address_bufctrl, bus_id, 2, &bc_buf);
+		ret = nthw_rac_rab_dma_commit(rac);
+		if (ret != 0)
+			return ret;
+
+		uint32_t mask = buf.size - 1;
+		uint32_t index = buf.index;
+
+		for (uint32_t i = 0; i < word_count; ++index, ++i)
+			data[i] = buf.base[index & mask];
+
+		uint32_t bc_mask = bc_buf.size - 1;
+		uint32_t bc_index = bc_buf.index;
+		*lrn_free = bc_buf.base[bc_index & bc_mask] & 0xffff;
+		*inf_avail = (bc_buf.base[bc_index & bc_mask] >> 16) & 0xffff;
+		*sta_avail = bc_buf.base[(bc_index + 1) & bc_mask] & 0xffff;
+	}
+
+	return ret;
+}
+
+int flm_nthw_sta_data_update(const struct flm_nthw *p, uint32_t *data,
+			  uint32_t word_count, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail)
+{
+	int ret = -1;
+
+	struct nthw_rac *rac = (struct nthw_rac *)p->mp_rac;
+	uint32_t address_stadata = register_get_address(p->mp_sta_data);
+	uint32_t address_bufctrl = register_get_address(p->mp_buf_ctrl);
+	rab_bus_id_t bus_id = 1;
+	struct dma_buf_ptr buf;
+	struct dma_buf_ptr bc_buf;
+
+	ret = nthw_rac_rab_dma_begin(rac);
+	if (ret == 0) {
+		/* Announce the number of words to read from STA_DATA */
+		uint32_t bufctrl_data[2];
+
+		bufctrl_data[0] = 0;
+		bufctrl_data[1] = word_count;
+		nthw_rac_rab_write32_dma(rac, address_bufctrl, bus_id, 2,
+					bufctrl_data);
+		nthw_rac_rab_read32_dma(rac, address_stadata, bus_id, word_count,
+				       &buf);
+		nthw_rac_rab_read32_dma(rac, address_bufctrl, bus_id, 2, &bc_buf);
+		ret = nthw_rac_rab_dma_commit(rac);
+		if (ret != 0)
+			return ret;
+
+		uint32_t mask = buf.size - 1;
+		uint32_t index = buf.index;
+
+		for (uint32_t i = 0; i < word_count; ++index, ++i)
+			data[i] = buf.base[index & mask];
+
+		uint32_t bc_mask = bc_buf.size - 1;
+		uint32_t bc_index = bc_buf.index;
+		*lrn_free = bc_buf.base[bc_index & bc_mask] & 0xffff;
+		*inf_avail = (bc_buf.base[bc_index & bc_mask] >> 16) & 0xffff;
+		*sta_avail = bc_buf.base[(bc_index + 1) & bc_mask] & 0xffff;
+	}
+
+	return ret;
+}
+
+void flm_nthw_stat_lrn_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_lrn_done_cnt);
+}
+
+void flm_nthw_stat_lrn_done_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_lrn_done);
+}
+
+void flm_nthw_stat_lrn_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_lrn_ignore_cnt);
+}
+
+void flm_nthw_stat_lrn_ignore_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_lrn_ignore);
+}
+
+void flm_nthw_stat_lrn_fail_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_lrn_fail_cnt);
+}
+
+void flm_nthw_stat_lrn_fail_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_lrn_fail);
+}
+
+void flm_nthw_stat_unl_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_unl_done_cnt);
+}
+
+void flm_nthw_stat_unl_done_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_unl_done);
+}
+
+void flm_nthw_stat_unl_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_unl_ignore_cnt);
+}
+
+void flm_nthw_stat_unl_ignore_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_unl_ignore);
+}
+
+void flm_nthw_stat_prb_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_prb_done_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_prb_done_cnt);
+}
+
+void flm_nthw_stat_prb_done_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_prb_done);
+	register_update(p->mp_stat_prb_done);
+}
+
+void flm_nthw_stat_prb_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_prb_ignore_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_prb_ignore_cnt);
+}
+
+void flm_nthw_stat_prb_ignore_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_prb_ignore);
+	register_update(p->mp_stat_prb_ignore);
+}
+
+void flm_nthw_stat_rel_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_rel_done_cnt);
+}
+
+void flm_nthw_stat_rel_done_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_rel_done);
+}
+
+void flm_nthw_stat_rel_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_rel_ignore_cnt);
+}
+
+void flm_nthw_stat_rel_ignore_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_rel_ignore);
+}
+
+void flm_nthw_stat_aul_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_aul_done_cnt);
+}
+
+void flm_nthw_stat_aul_done_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_aul_done);
+}
+
+void flm_nthw_stat_aul_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_aul_ignore_cnt);
+}
+
+void flm_nthw_stat_aul_ignore_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_aul_ignore);
+}
+
+void flm_nthw_stat_aul_fail_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_aul_fail_cnt);
+}
+
+void flm_nthw_stat_aul_fail_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_aul_fail);
+}
+
+void flm_nthw_stat_tul_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_tul_done_cnt);
+}
+
+void flm_nthw_stat_tul_done_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_tul_done);
+}
+
+void flm_nthw_stat_flows_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	if (get)
+		*val = field_get_val32(p->mp_stat_flows_cnt);
+}
+
+void flm_nthw_stat_flows_update(const struct flm_nthw *p)
+{
+	register_update(p->mp_stat_flows);
+}
+
+void flm_nthw_stat_sta_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_sta_done_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_sta_done_cnt);
+}
+
+void flm_nthw_stat_sta_done_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_sta_done);
+	register_update(p->mp_stat_sta_done);
+}
+
+void flm_nthw_stat_inf_done_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_inf_done_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_inf_done_cnt);
+}
+
+void flm_nthw_stat_inf_done_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_inf_done);
+	register_update(p->mp_stat_inf_done);
+}
+
+void flm_nthw_stat_inf_skip_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_inf_skip_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_inf_skip_cnt);
+}
+
+void flm_nthw_stat_inf_skip_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_inf_skip);
+	register_update(p->mp_stat_inf_skip);
+}
+
+void flm_nthw_stat_pck_hit_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_pck_hit_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_pck_hit_cnt);
+}
+
+void flm_nthw_stat_pck_hit_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_pck_hit);
+	register_update(p->mp_stat_pck_hit);
+}
+
+void flm_nthw_stat_pck_miss_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_pck_miss_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_pck_miss_cnt);
+}
+
+void flm_nthw_stat_pck_miss_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_pck_miss);
+	register_update(p->mp_stat_pck_miss);
+}
+
+void flm_nthw_stat_pck_unh_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_pck_unh_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_pck_unh_cnt);
+}
+
+void flm_nthw_stat_pck_unh_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_pck_unh);
+	register_update(p->mp_stat_pck_unh);
+}
+
+void flm_nthw_stat_pck_dis_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_pck_dis_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_pck_dis_cnt);
+}
+
+void flm_nthw_stat_pck_dis_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_pck_dis);
+	register_update(p->mp_stat_pck_dis);
+}
+
+void flm_nthw_stat_csh_hit_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_csh_hit_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_csh_hit_cnt);
+}
+
+void flm_nthw_stat_csh_hit_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_csh_hit);
+	register_update(p->mp_stat_csh_hit);
+}
+
+void flm_nthw_stat_csh_miss_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_csh_miss_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_csh_miss_cnt);
+}
+
+void flm_nthw_stat_csh_miss_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_csh_miss);
+	register_update(p->mp_stat_csh_miss);
+}
+
+void flm_nthw_stat_csh_unh_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_csh_unh_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_csh_unh_cnt);
+}
+
+void flm_nthw_stat_csh_unh_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_csh_unh);
+	register_update(p->mp_stat_csh_unh);
+}
+
+void flm_nthw_stat_cuc_start_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_cuc_start_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_cuc_start_cnt);
+}
+
+void flm_nthw_stat_cuc_start_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_cuc_start);
+	register_update(p->mp_stat_cuc_start);
+}
+
+void flm_nthw_stat_cuc_move_cnt(const struct flm_nthw *p, uint32_t *val, int get)
+{
+	assert(p->mp_stat_cuc_move_cnt);
+	if (get)
+		*val = field_get_val32(p->mp_stat_cuc_move_cnt);
+}
+
+void flm_nthw_stat_cuc_move_update(const struct flm_nthw *p)
+{
+	assert(p->mp_stat_cuc_move);
+	register_update(p->mp_stat_cuc_move);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.h
new file mode 100644
index 0000000000..4796d43940
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_flm.h
@@ -0,0 +1,422 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_FLM_H__
+#define __FLOW_NTHW_FLM_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct flm_nthw;
+
+typedef struct flm_nthw flm_nthw_t;
+
+struct flm_nthw *flm_nthw_new(void);
+void flm_nthw_delete(struct flm_nthw *p);
+int flm_nthw_init(struct flm_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+void flm_nthw_set_debug_mode(struct flm_nthw *p, unsigned int n_debug_mode);
+
+/* Control */
+void flm_nthw_control_enable(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_init(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_lds(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_lfs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_lis(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_uds(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_uis(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_rds(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_ris(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_pds(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_pis(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_crcwr(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_crcrd(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_rbl(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_eab(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_split_sdram_usage(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_control_flush(const struct flm_nthw *p);
+
+/* Status */
+void flm_nthw_status_calibdone(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_initdone(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_idle(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_critical(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_panic(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_crcerr(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_eft_bp(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_status_flush(const struct flm_nthw *p);
+void flm_nthw_status_update(const struct flm_nthw *p);
+
+/* Timeout */
+void flm_nthw_timeout_t(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_timeout_flush(const struct flm_nthw *p);
+
+/* Scrub */
+void flm_nthw_scrub_i(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_scrub_flush(const struct flm_nthw *p);
+
+/* Load BIN */
+void flm_nthw_load_bin(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_load_bin_flush(const struct flm_nthw *p);
+
+/* Load PPS */
+void flm_nthw_load_pps(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_load_pps_flush(const struct flm_nthw *p);
+
+/* Load LPS */
+void flm_nthw_load_lps(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_load_lps_flush(const struct flm_nthw *p);
+
+/* Load APS */
+void flm_nthw_load_aps(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_load_aps_flush(const struct flm_nthw *p);
+
+/* Prio */
+void flm_nthw_prio_limit0(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_ft0(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_limit1(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_ft1(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_limit2(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_ft2(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_limit3(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_ft3(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_prio_flush(const struct flm_nthw *p);
+
+/* PST */
+void flm_nthw_pst_select(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_pst_cnt(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_pst_bp(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_pst_pp(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_pst_tp(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_pst_flush(const struct flm_nthw *p);
+
+/* RCP */
+void flm_nthw_rcp_select(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_cnt(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_lookup(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_qw0_dyn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_qw0_ofs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_qw0_sel(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_qw4_dyn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_qw4_ofs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_sw8_dyn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_sw8_ofs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_sw8_sel(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_sw9_dyn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_sw9_ofs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_mask(const struct flm_nthw *p, const uint32_t *val);
+void flm_nthw_rcp_kid(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_opn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_ipn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_byt_dyn(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_byt_ofs(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_txplm(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_auto_ipv4_mask(const struct flm_nthw *p, uint32_t val);
+void flm_nthw_rcp_flush(const struct flm_nthw *p);
+
+/* Buf Ctrl */
+int flm_nthw_buf_ctrl_update(const struct flm_nthw *p, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail);
+
+/* Lrn Data */
+int flm_nthw_lrn_data_flush(const struct flm_nthw *p, const uint32_t *data,
+			 uint32_t word_count, uint32_t *lrn_free,
+			 uint32_t *inf_avail, uint32_t *sta_avail);
+
+/* Inf Data */
+int flm_nthw_inf_data_update(const struct flm_nthw *p, uint32_t *data,
+			  uint32_t word_count, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail);
+
+/* Sta Data */
+int flm_nthw_sta_data_update(const struct flm_nthw *p, uint32_t *data,
+			  uint32_t word_count, uint32_t *lrn_free,
+			  uint32_t *inf_avail, uint32_t *sta_avail);
+
+/* Stat Lrn _done */
+void flm_nthw_stat_lrn_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_lrn_done_update(const struct flm_nthw *p);
+
+/* Stat Lrn Ignore */
+void flm_nthw_stat_lrn_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_lrn_ignore_update(const struct flm_nthw *p);
+
+/* Stat Lrn Fail */
+void flm_nthw_stat_lrn_fail_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_lrn_fail_update(const struct flm_nthw *p);
+
+/* Stat Unl _done */
+void flm_nthw_stat_unl_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_unl_done_update(const struct flm_nthw *p);
+
+/* Stat Unl Ignore */
+void flm_nthw_stat_unl_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_unl_ignore_update(const struct flm_nthw *p);
+
+/* Stat Prb _done */
+void flm_nthw_stat_prb_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_prb_done_update(const struct flm_nthw *p);
+
+/* Stat Prb Ignore */
+void flm_nthw_stat_prb_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_prb_ignore_update(const struct flm_nthw *p);
+
+/* Stat Rel _done */
+void flm_nthw_stat_rel_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_rel_done_update(const struct flm_nthw *p);
+
+/* Stat Rel Ignore */
+void flm_nthw_stat_rel_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_rel_ignore_update(const struct flm_nthw *p);
+
+/* Stat Aul _done */
+void flm_nthw_stat_aul_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_aul_done_update(const struct flm_nthw *p);
+
+/* Stat Aul Ignore */
+void flm_nthw_stat_aul_ignore_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_aul_ignore_update(const struct flm_nthw *p);
+
+/* Stat Aul Fail */
+void flm_nthw_stat_aul_fail_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_aul_fail_update(const struct flm_nthw *p);
+
+/* Stat Tul _done */
+void flm_nthw_stat_tul_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_tul_done_update(const struct flm_nthw *p);
+
+/* Stat Flows */
+void flm_nthw_stat_flows_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_flows_update(const struct flm_nthw *p);
+
+/* Stat Sta _done */
+void flm_nthw_stat_sta_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_sta_done_update(const struct flm_nthw *p);
+
+/* Stat Inf _done */
+void flm_nthw_stat_inf_done_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_inf_done_update(const struct flm_nthw *p);
+
+/* Stat Inf Skip */
+void flm_nthw_stat_inf_skip_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_inf_skip_update(const struct flm_nthw *p);
+
+/* Stat Pck Hit */
+void flm_nthw_stat_pck_hit_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_pck_hit_update(const struct flm_nthw *p);
+
+/* Stat Pck Miss */
+void flm_nthw_stat_pck_miss_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_pck_miss_update(const struct flm_nthw *p);
+
+/* Stat Pck Unh */
+void flm_nthw_stat_pck_unh_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_pck_unh_update(const struct flm_nthw *p);
+
+/* Stat Pck Dis */
+void flm_nthw_stat_pck_dis_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_pck_dis_update(const struct flm_nthw *p);
+
+/* Stat Csh Hit */
+void flm_nthw_stat_csh_hit_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_csh_hit_update(const struct flm_nthw *p);
+
+/* Stat Csh Miss */
+void flm_nthw_stat_csh_miss_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_csh_miss_update(const struct flm_nthw *p);
+
+/* Stat Csh Unh */
+void flm_nthw_stat_csh_unh_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_csh_unh_update(const struct flm_nthw *p);
+
+/* Stat Cuc Start */
+void flm_nthw_stat_cuc_start_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_cuc_start_update(const struct flm_nthw *p);
+
+/* Stat Cuc Move */
+void flm_nthw_stat_cuc_move_cnt(const struct flm_nthw *p, uint32_t *val, int get);
+void flm_nthw_stat_cuc_move_update(const struct flm_nthw *p);
+
+struct flm_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+	void *mp_rac;
+
+	nt_module_t *m_flm;
+
+	nt_register_t *mp_control;
+	nt_field_t *mp_control_enable;
+	nt_field_t *mp_control_init;
+	nt_field_t *mp_control_lds;
+	nt_field_t *mp_control_lfs;
+	nt_field_t *mp_control_lis;
+	nt_field_t *mp_control_uds;
+	nt_field_t *mp_control_uis;
+	nt_field_t *mp_control_rds;
+	nt_field_t *mp_control_ris;
+	nt_field_t *mp_control_pds;
+	nt_field_t *mp_control_pis;
+	nt_field_t *mp_control_crcwr;
+	nt_field_t *mp_control_crcrd;
+	nt_field_t *mp_control_rbl;
+	nt_field_t *mp_control_eab;
+	nt_field_t *mp_control_split_sdram_usage;
+
+	nt_register_t *mp_status;
+	nt_field_t *mp_status_calibdone;
+	nt_field_t *mp_status_initdone;
+	nt_field_t *mp_status_idle;
+	nt_field_t *mp_status_critical;
+	nt_field_t *mp_status_panic;
+	nt_field_t *mp_status_crcerr;
+	nt_field_t *mp_status_eft_bp;
+
+	nt_register_t *mp_timeout;
+	nt_field_t *mp_timeout_t;
+
+	nt_register_t *mp_scrub;
+	nt_field_t *mp_scrub_i;
+
+	nt_register_t *mp_load_bin;
+	nt_field_t *mp_load_bin_bin;
+
+	nt_register_t *mp_load_pps;
+	nt_field_t *mp_load_pps_pps;
+
+	nt_register_t *mp_load_lps;
+	nt_field_t *mp_load_lps_lps;
+
+	nt_register_t *mp_load_aps;
+	nt_field_t *mp_load_aps_aps;
+
+	nt_register_t *mp_prio;
+	nt_field_t *mp_prio_limit0;
+	nt_field_t *mp_prio_ft0;
+	nt_field_t *mp_prio_limit1;
+	nt_field_t *mp_prio_ft1;
+	nt_field_t *mp_prio_limit2;
+	nt_field_t *mp_prio_ft2;
+	nt_field_t *mp_prio_limit3;
+	nt_field_t *mp_prio_ft3;
+
+	nt_register_t *mp_pst_ctrl;
+	nt_field_t *mp_pst_ctrl_adr;
+	nt_field_t *mp_pst_ctrl_cnt;
+	nt_register_t *mp_pst_data;
+	nt_field_t *mp_pst_data_bp;
+	nt_field_t *mp_pst_data_pp;
+	nt_field_t *mp_pst_data_tp;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_ctrl_adr;
+	nt_field_t *mp_rcp_ctrl_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_lookup;
+	nt_field_t *mp_rcp_data_qw0_dyn;
+	nt_field_t *mp_rcp_data_qw0_ofs;
+	nt_field_t *mp_rcp_data_qw0_sel;
+	nt_field_t *mp_rcp_data_qw4_dyn;
+	nt_field_t *mp_rcp_data_qw4_ofs;
+	nt_field_t *mp_rcp_data_sw8_dyn;
+	nt_field_t *mp_rcp_data_sw8_ofs;
+	nt_field_t *mp_rcp_data_sw8_sel;
+	nt_field_t *mp_rcp_data_sw9_dyn;
+	nt_field_t *mp_rcp_data_sw9_ofs;
+	nt_field_t *mp_rcp_data_mask;
+	nt_field_t *mp_rcp_data_kid;
+	nt_field_t *mp_rcp_data_opn;
+	nt_field_t *mp_rcp_data_ipn;
+	nt_field_t *mp_rcp_data_byt_dyn;
+	nt_field_t *mp_rcp_data_byt_ofs;
+	nt_field_t *mp_rcp_data_txplm;
+	nt_field_t *mp_rcp_data_auto_ipv4_mask;
+
+	nt_register_t *mp_buf_ctrl;
+	nt_field_t *mp_buf_ctrl_lrn_free;
+	nt_field_t *mp_buf_ctrl_inf_avail;
+	nt_field_t *mp_buf_ctrl_sta_avail;
+
+	nt_register_t *mp_lrn_data;
+	nt_register_t *mp_inf_data;
+	nt_register_t *mp_sta_data;
+
+	nt_register_t *mp_stat_lrn_done;
+	nt_field_t *mp_stat_lrn_done_cnt;
+
+	nt_register_t *mp_stat_lrn_ignore;
+	nt_field_t *mp_stat_lrn_ignore_cnt;
+
+	nt_register_t *mp_stat_lrn_fail;
+	nt_field_t *mp_stat_lrn_fail_cnt;
+
+	nt_register_t *mp_stat_unl_done;
+	nt_field_t *mp_stat_unl_done_cnt;
+
+	nt_register_t *mp_stat_unl_ignore;
+	nt_field_t *mp_stat_unl_ignore_cnt;
+
+	nt_register_t *mp_stat_prb_done;
+	nt_field_t *mp_stat_prb_done_cnt;
+
+	nt_register_t *mp_stat_prb_ignore;
+	nt_field_t *mp_stat_prb_ignore_cnt;
+
+	nt_register_t *mp_stat_rel_done;
+	nt_field_t *mp_stat_rel_done_cnt;
+
+	nt_register_t *mp_stat_rel_ignore;
+	nt_field_t *mp_stat_rel_ignore_cnt;
+
+	nt_register_t *mp_stat_aul_done;
+	nt_field_t *mp_stat_aul_done_cnt;
+
+	nt_register_t *mp_stat_aul_ignore;
+	nt_field_t *mp_stat_aul_ignore_cnt;
+
+	nt_register_t *mp_stat_aul_fail;
+	nt_field_t *mp_stat_aul_fail_cnt;
+
+	nt_register_t *mp_stat_tul_done;
+	nt_field_t *mp_stat_tul_done_cnt;
+
+	nt_register_t *mp_stat_flows;
+	nt_field_t *mp_stat_flows_cnt;
+
+	nt_register_t *mp_stat_sta_done;
+	nt_field_t *mp_stat_sta_done_cnt;
+
+	nt_register_t *mp_stat_inf_done;
+	nt_field_t *mp_stat_inf_done_cnt;
+
+	nt_register_t *mp_stat_inf_skip;
+	nt_field_t *mp_stat_inf_skip_cnt;
+
+	nt_register_t *mp_stat_pck_hit;
+	nt_field_t *mp_stat_pck_hit_cnt;
+
+	nt_register_t *mp_stat_pck_miss;
+	nt_field_t *mp_stat_pck_miss_cnt;
+
+	nt_register_t *mp_stat_pck_unh;
+	nt_field_t *mp_stat_pck_unh_cnt;
+
+	nt_register_t *mp_stat_pck_dis;
+	nt_field_t *mp_stat_pck_dis_cnt;
+
+	nt_register_t *mp_stat_csh_hit;
+	nt_field_t *mp_stat_csh_hit_cnt;
+
+	nt_register_t *mp_stat_csh_miss;
+	nt_field_t *mp_stat_csh_miss_cnt;
+
+	nt_register_t *mp_stat_csh_unh;
+	nt_field_t *mp_stat_csh_unh_cnt;
+
+	nt_register_t *mp_stat_cuc_start;
+	nt_field_t *mp_stat_cuc_start_cnt;
+
+	nt_register_t *mp_stat_cuc_move;
+	nt_field_t *mp_stat_cuc_move_cnt;
+};
+
+#endif /* __FLOW_NTHW_FLM_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.c
new file mode 100644
index 0000000000..b7fe7c5863
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.c
@@ -0,0 +1,293 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_hfu.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void hfu_nthw_set_debug_mode(struct hfu_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_hfu, n_debug_mode);
+}
+
+struct hfu_nthw *hfu_nthw_new(void)
+{
+	struct hfu_nthw *p = malloc(sizeof(struct hfu_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void hfu_nthw_delete(struct hfu_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int hfu_nthw_init(struct hfu_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_HFU, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Hfu %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_hfu = fpga_query_module(p_fpga, MOD_HFU, n_instance);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_hfu, HFU_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, HFU_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, HFU_RCP_CTRL_CNT);
+
+	p->mp_rcp_data = module_get_register(p->m_hfu, HFU_RCP_DATA);
+	p->mp_rcp_data_len_a_wr =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_WR);
+	p->mp_rcp_data_len_a_ol4len =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_OL4LEN);
+	p->mp_rcp_data_len_a_pos_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_POS_DYN);
+	p->mp_rcp_data_len_a_pos_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_POS_OFS);
+	p->mp_rcp_data_len_a_add_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_ADD_DYN);
+	p->mp_rcp_data_len_a_add_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_ADD_OFS);
+	p->mp_rcp_data_len_a_sub_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_A_SUB_DYN);
+	p->mp_rcp_data_len_b_wr =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_WR);
+	p->mp_rcp_data_len_b_pos_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_POS_DYN);
+	p->mp_rcp_data_len_b_pos_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_POS_OFS);
+	p->mp_rcp_data_len_b_add_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_ADD_DYN);
+	p->mp_rcp_data_len_b_add_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_ADD_OFS);
+	p->mp_rcp_data_len_b_sub_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_B_SUB_DYN);
+	p->mp_rcp_data_len_c_wr =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_WR);
+	p->mp_rcp_data_len_c_pos_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_POS_DYN);
+	p->mp_rcp_data_len_c_pos_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_POS_OFS);
+	p->mp_rcp_data_len_c_add_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_ADD_DYN);
+	p->mp_rcp_data_len_c_add_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_ADD_OFS);
+	p->mp_rcp_data_len_c_sub_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_LEN_C_SUB_DYN);
+	p->mp_rcp_data_ttl_wr =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_TTL_WR);
+	p->mp_rcp_data_ttl_pos_dyn =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_TTL_POS_DYN);
+	p->mp_rcp_data_ttl_pos_ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_TTL_POS_OFS);
+	p->mp_rcp_data_csinf = register_get_field(p->mp_rcp_data, HFU_RCP_DATA_CSINF);
+	p->mp_rcp_data_l3prt = register_get_field(p->mp_rcp_data, HFU_RCP_DATA_L3PRT);
+	p->mp_rcp_data_l3frag =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_L3FRAG);
+	p->mp_rcp_data_tunnel =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_TUNNEL);
+	p->mp_rcp_data_l4prt = register_get_field(p->mp_rcp_data, HFU_RCP_DATA_L4PRT);
+	p->mp_rcp_data_ol3ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_OL3OFS);
+	p->mp_rcp_data_ol4ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_OL4OFS);
+	p->mp_rcp_data_il3ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_IL3OFS);
+	p->mp_rcp_data_il4ofs =
+		register_get_field(p->mp_rcp_data, HFU_RCP_DATA_IL4OFS);
+
+	return 0;
+}
+
+void hfu_nthw_rcp_select(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void hfu_nthw_rcp_cnt(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void hfu_nthw_rcp_len_a_wr(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_wr, val);
+}
+
+void hfu_nthw_rcp_len_a_ol4len(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_ol4len, val);
+}
+
+void hfu_nthw_rcp_len_a_pos_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_pos_dyn, val);
+}
+
+void hfu_nthw_rcp_len_a_pos_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_pos_ofs, val);
+}
+
+void hfu_nthw_rcp_len_a_add_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_add_dyn, val);
+}
+
+void hfu_nthw_rcp_len_a_add_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_add_ofs, val);
+}
+
+void hfu_nthw_rcp_len_a_sub_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_a_sub_dyn, val);
+}
+
+void hfu_nthw_rcp_len_b_wr(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_wr, val);
+}
+
+void hfu_nthw_rcp_len_b_pos_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_pos_dyn, val);
+}
+
+void hfu_nthw_rcp_len_b_pos_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_pos_ofs, val);
+}
+
+void hfu_nthw_rcp_len_b_add_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_add_dyn, val);
+}
+
+void hfu_nthw_rcp_len_b_add_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_add_ofs, val);
+}
+
+void hfu_nthw_rcp_len_b_sub_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_b_sub_dyn, val);
+}
+
+void hfu_nthw_rcp_len_c_wr(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_wr, val);
+}
+
+void hfu_nthw_rcp_len_c_pos_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_pos_dyn, val);
+}
+
+void hfu_nthw_rcp_len_c_pos_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_pos_ofs, val);
+}
+
+void hfu_nthw_rcp_len_c_add_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_add_dyn, val);
+}
+
+void hfu_nthw_rcp_len_c_add_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_add_ofs, val);
+}
+
+void hfu_nthw_rcp_len_c_sub_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len_c_sub_dyn, val);
+}
+
+void hfu_nthw_rcp_ttl_wr(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ttl_wr, val);
+}
+
+void hfu_nthw_rcp_ttl_pos_dyn(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ttl_pos_dyn, val);
+}
+
+void hfu_nthw_rcp_ttl_pos_ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ttl_pos_ofs, val);
+}
+
+void hfu_nthw_rcp_csinf(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_csinf, val);
+}
+
+void hfu_nthw_rcp_l3prt(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_l3prt, val);
+}
+
+void hfu_nthw_rcp_l3frag(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_l3frag, val);
+}
+
+void hfu_nthw_rcp_tunnel(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tunnel, val);
+}
+
+void hfu_nthw_rcp_l4prt(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_l4prt, val);
+}
+
+void hfu_nthw_rcp_ol3ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ol3ofs, val);
+}
+
+void hfu_nthw_rcp_ol4ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ol4ofs, val);
+}
+
+void hfu_nthw_rcp_il3ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_il3ofs, val);
+}
+
+void hfu_nthw_rcp_il4ofs(const struct hfu_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_il4ofs, val);
+}
+
+void hfu_nthw_rcp_flush(const struct hfu_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.h
new file mode 100644
index 0000000000..ecba1a8822
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hfu.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_HFU_H__
+#define __FLOW_NTHW_HFU_H__
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct hfu_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_hfu;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_len_a_wr;
+	nt_field_t *mp_rcp_data_len_a_ol4len;
+	nt_field_t *mp_rcp_data_len_a_pos_dyn;
+	nt_field_t *mp_rcp_data_len_a_pos_ofs;
+	nt_field_t *mp_rcp_data_len_a_add_dyn;
+	nt_field_t *mp_rcp_data_len_a_add_ofs;
+	nt_field_t *mp_rcp_data_len_a_sub_dyn;
+	nt_field_t *mp_rcp_data_len_b_wr;
+	nt_field_t *mp_rcp_data_len_b_pos_dyn;
+	nt_field_t *mp_rcp_data_len_b_pos_ofs;
+	nt_field_t *mp_rcp_data_len_b_add_dyn;
+	nt_field_t *mp_rcp_data_len_b_add_ofs;
+	nt_field_t *mp_rcp_data_len_b_sub_dyn;
+	nt_field_t *mp_rcp_data_len_c_wr;
+	nt_field_t *mp_rcp_data_len_c_pos_dyn;
+	nt_field_t *mp_rcp_data_len_c_pos_ofs;
+	nt_field_t *mp_rcp_data_len_c_add_dyn;
+	nt_field_t *mp_rcp_data_len_c_add_ofs;
+	nt_field_t *mp_rcp_data_len_c_sub_dyn;
+	nt_field_t *mp_rcp_data_ttl_wr;
+	nt_field_t *mp_rcp_data_ttl_pos_dyn;
+	nt_field_t *mp_rcp_data_ttl_pos_ofs;
+	nt_field_t *mp_rcp_data_csinf;
+	nt_field_t *mp_rcp_data_l3prt;
+	nt_field_t *mp_rcp_data_l3frag;
+	nt_field_t *mp_rcp_data_tunnel;
+	nt_field_t *mp_rcp_data_l4prt;
+	nt_field_t *mp_rcp_data_ol3ofs;
+	nt_field_t *mp_rcp_data_ol4ofs;
+	nt_field_t *mp_rcp_data_il3ofs;
+	nt_field_t *mp_rcp_data_il4ofs;
+};
+
+struct hfu_nthw *hfu_nthw_new(void);
+void hfu_nthw_delete(struct hfu_nthw *p);
+int hfu_nthw_init(struct hfu_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int hfu_nthw_setup(struct hfu_nthw *p, int n_idx, int n_idx_cnt);
+void hfu_nthw_set_debug_mode(struct hfu_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void hfu_nthw_rcp_select(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_cnt(const struct hfu_nthw *p, uint32_t val);
+
+void hfu_nthw_rcp_len_a_wr(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_ol4len(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_pos_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_pos_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_add_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_add_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_a_sub_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_wr(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_pos_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_pos_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_add_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_add_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_b_sub_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_wr(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_pos_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_pos_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_add_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_add_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_len_c_sub_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_ttl_wr(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_ttl_pos_dyn(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_ttl_pos_ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_csinf(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_l3prt(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_l3frag(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_tunnel(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_l4prt(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_ol3ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_ol4ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_il3ofs(const struct hfu_nthw *p, uint32_t val);
+void hfu_nthw_rcp_il4ofs(const struct hfu_nthw *p, uint32_t val);
+
+void hfu_nthw_rcp_flush(const struct hfu_nthw *p);
+
+#endif /* __FLOW_NTHW_HFU_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.c
new file mode 100644
index 0000000000..0dc6434e88
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.c
@@ -0,0 +1,254 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_hsh.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void hsh_nthw_set_debug_mode(struct hsh_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_hsh, n_debug_mode);
+}
+
+struct hsh_nthw *hsh_nthw_new(void)
+{
+	struct hsh_nthw *p = malloc(sizeof(struct hsh_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void hsh_nthw_delete(struct hsh_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int hsh_nthw_init(struct hsh_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_HSH, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Hsh %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_hsh = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_hsh, HSH_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, HSH_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, HSH_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_hsh, HSH_RCP_DATA);
+	p->mp_rcp_data_load_dist_type =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_LOAD_DIST_TYPE);
+	p->mp_rcp_data_mac_port_mask =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_MAC_PORT_MASK);
+	p->mp_rcp_data_sort = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_SORT);
+	p->mp_rcp_data_qw0_pe =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_QW0_PE);
+	p->mp_rcp_data_qw0_ofs =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_QW0_OFS);
+	p->mp_rcp_data_qw4_pe =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_QW4_PE);
+	p->mp_rcp_data_qw4_ofs =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_QW4_OFS);
+	p->mp_rcp_data_w8_pe = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W8_PE);
+	p->mp_rcp_data_w8_ofs =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W8_OFS);
+	p->mp_rcp_data_w8_sort =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W8_SORT);
+	p->mp_rcp_data_w9_pe = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W9_PE);
+	p->mp_rcp_data_w9_ofs =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W9_OFS);
+	p->mp_rcp_data_w9_sort =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W9_SORT);
+	p->mp_rcp_data_w9_p = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_W9_P);
+	p->mp_rcp_data_p_mask =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_P_MASK);
+	p->mp_rcp_data_word_mask =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_WORD_MASK);
+	p->mp_rcp_data_seed = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_SEED);
+	p->mp_rcp_data_tnl_p = register_get_field(p->mp_rcp_data, HSH_RCP_DATA_TNL_P);
+	p->mp_rcp_data_hsh_valid =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_HSH_VALID);
+	p->mp_rcp_data_hsh_type =
+		register_get_field(p->mp_rcp_data, HSH_RCP_DATA_HSH_TYPE);
+	p->mp_rcp_data_auto_ipv4_mask =
+		register_query_field(p->mp_rcp_data, HSH_RCP_DATA_AUTO_IPV4_MASK);
+
+	/* Init */
+	uint32_t val[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	field_set_val32(p->mp_rcp_addr, 0);
+	field_set_val32(p->mp_rcp_cnt, 1);
+
+	field_set_val32(p->mp_rcp_data_load_dist_type, 0);
+	field_set_val(p->mp_rcp_data_mac_port_mask, val,
+		     p->mp_rcp_data_mac_port_mask->mn_words);
+	field_set_val32(p->mp_rcp_data_sort, 0);
+	field_set_val32(p->mp_rcp_data_qw0_pe, 0);
+	field_set_val32(p->mp_rcp_data_qw0_ofs, 0);
+	field_set_val32(p->mp_rcp_data_qw4_pe, 0);
+	field_set_val32(p->mp_rcp_data_qw4_ofs, 0);
+	field_set_val32(p->mp_rcp_data_w8_pe, 0);
+	field_set_val32(p->mp_rcp_data_w8_ofs, 0);
+	field_set_val32(p->mp_rcp_data_w8_sort, 0);
+	field_set_val32(p->mp_rcp_data_w9_pe, 0);
+	field_set_val32(p->mp_rcp_data_w9_ofs, 0);
+	field_set_val32(p->mp_rcp_data_w9_sort, 0);
+	field_set_val32(p->mp_rcp_data_w9_p, 0);
+	field_set_val(p->mp_rcp_data_word_mask, val, 10);
+	field_set_val32(p->mp_rcp_data_seed, 0);
+	field_set_val32(p->mp_rcp_data_tnl_p, 0);
+	field_set_val32(p->mp_rcp_data_hsh_valid, 0);
+	field_set_val32(p->mp_rcp_data_hsh_type, 31);
+
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+
+	return 0;
+}
+
+void hsh_nthw_rcp_select(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void hsh_nthw_rcp_cnt(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void hsh_nthw_rcp_load_dist_type(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_load_dist_type, val);
+}
+
+void hsh_nthw_rcp_mac_port_mask(const struct hsh_nthw *p, uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_mac_port_mask, val,
+		     p->mp_rcp_data_mac_port_mask->mn_words);
+}
+
+void hsh_nthw_rcp_sort(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sort, val);
+}
+
+void hsh_nthw_rcp_qw0_pe(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_pe, val);
+}
+
+void hsh_nthw_rcp_qw0_ofs(const struct hsh_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_ofs, val);
+}
+
+void hsh_nthw_rcp_qw4_pe(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_pe, val);
+}
+
+void hsh_nthw_rcp_qw4_ofs(const struct hsh_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_ofs, val);
+}
+
+void hsh_nthw_rcp_w8_pe(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w8_pe, val);
+}
+
+void hsh_nthw_rcp_w8_ofs(const struct hsh_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w8_ofs, val);
+}
+
+void hsh_nthw_rcp_w8_sort(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w8_sort, val);
+}
+
+void hsh_nthw_rcp_w9_pe(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w9_pe, val);
+}
+
+void hsh_nthw_rcp_w9_ofs(const struct hsh_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w9_ofs, val);
+}
+
+void hsh_nthw_rcp_w9_sort(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w9_sort, val);
+}
+
+void hsh_nthw_rcp_w9_p(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_w9_p, val);
+}
+
+void hsh_nthw_rcp_p_mask(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_p_mask, val);
+}
+
+void hsh_nthw_rcp_word_mask(const struct hsh_nthw *p, uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_word_mask, val, 10);
+}
+
+void hsh_nthw_rcp_seed(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_seed, val);
+}
+
+void hsh_nthw_rcp_tnl_p(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tnl_p, val);
+}
+
+void hsh_nthw_rcp_hsh_valid(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_hsh_valid, val);
+}
+
+void hsh_nthw_rcp_hsh_type(const struct hsh_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_hsh_type, val);
+}
+
+void hsh_nthw_rcp_auto_ipv4_mask(const struct hsh_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_auto_ipv4_mask)
+		field_set_val32(p->mp_rcp_data_auto_ipv4_mask, val);
+}
+
+void hsh_nthw_rcp_flush(const struct hsh_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.h
new file mode 100644
index 0000000000..7cb7dbb743
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hsh.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_HSH_H__
+#define __FLOW_NTHW_HSH_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct hsh_nthw;
+
+typedef struct hsh_nthw hsh_nthw_t;
+
+struct hsh_nthw *hsh_nthw_new(void);
+void hsh_nthw_delete(struct hsh_nthw *p);
+int hsh_nthw_init(struct hsh_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int hsh_nthw_setup(struct hsh_nthw *p, int n_idx, int n_idx_cnt);
+void hsh_nthw_set_debug_mode(struct hsh_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void hsh_nthw_rcp_select(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_cnt(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_load_dist_type(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_mac_port_mask(const struct hsh_nthw *p, uint32_t *val);
+void hsh_nthw_rcp_sort(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_qw0_pe(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_qw0_ofs(const struct hsh_nthw *p, int32_t val);
+void hsh_nthw_rcp_qw4_pe(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_qw4_ofs(const struct hsh_nthw *p, int32_t val);
+void hsh_nthw_rcp_w8_pe(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_w8_ofs(const struct hsh_nthw *p, int32_t val);
+void hsh_nthw_rcp_w8_sort(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_w9_pe(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_w9_ofs(const struct hsh_nthw *p, int32_t val);
+void hsh_nthw_rcp_w9_sort(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_w9_p(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_p_mask(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_word_mask(const struct hsh_nthw *p, uint32_t *val);
+void hsh_nthw_rcp_seed(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_tnl_p(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_hsh_valid(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_hsh_type(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_auto_ipv4_mask(const struct hsh_nthw *p, uint32_t val);
+void hsh_nthw_rcp_flush(const struct hsh_nthw *p);
+
+struct hsh_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_hsh;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_load_dist_type;
+	nt_field_t *mp_rcp_data_mac_port_mask;
+	nt_field_t *mp_rcp_data_sort;
+	nt_field_t *mp_rcp_data_qw0_pe;
+	nt_field_t *mp_rcp_data_qw0_ofs;
+	nt_field_t *mp_rcp_data_qw4_pe;
+	nt_field_t *mp_rcp_data_qw4_ofs;
+	nt_field_t *mp_rcp_data_w8_pe;
+	nt_field_t *mp_rcp_data_w8_ofs;
+	nt_field_t *mp_rcp_data_w8_sort;
+	nt_field_t *mp_rcp_data_w9_pe;
+	nt_field_t *mp_rcp_data_w9_ofs;
+	nt_field_t *mp_rcp_data_w9_sort;
+	nt_field_t *mp_rcp_data_w9_p;
+	nt_field_t *mp_rcp_data_p_mask;
+	nt_field_t *mp_rcp_data_word_mask;
+	nt_field_t *mp_rcp_data_seed;
+	nt_field_t *mp_rcp_data_tnl_p;
+	nt_field_t *mp_rcp_data_hsh_valid;
+	nt_field_t *mp_rcp_data_hsh_type;
+	nt_field_t *mp_rcp_data_auto_ipv4_mask;
+};
+
+#endif /* __FLOW_NTHW_HSH_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.c
new file mode 100644
index 0000000000..fc3dc443a2
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.c
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_hst.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void hst_nthw_set_debug_mode(struct hst_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_hst, n_debug_mode);
+}
+
+struct hst_nthw *hst_nthw_new(void)
+{
+	struct hst_nthw *p = malloc(sizeof(struct hst_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void hst_nthw_delete(struct hst_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int hst_nthw_init(struct hst_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_HST, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Hst %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_hst = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_hst, HST_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, HST_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, HST_RCP_CTRL_CNT);
+
+	p->mp_rcp_data = module_get_register(p->m_hst, HST_RCP_DATA);
+	p->mp_rcp_data_strip_mode =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_STRIP_MODE);
+	p->mp_rcp_data_start_dyn =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_START_DYN);
+	p->mp_rcp_data_start_ofs =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_START_OFS);
+	p->mp_rcp_data_end_dyn =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_END_DYN);
+	p->mp_rcp_data_end_ofs =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_END_OFS);
+	p->mp_rcp_data_modif0_cmd =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF0_CMD);
+	p->mp_rcp_data_modif0_dyn =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF0_DYN);
+	p->mp_rcp_data_modif0_ofs =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF0_OFS);
+	p->mp_rcp_data_modif0_value =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF0_VALUE);
+	p->mp_rcp_data_modif1_cmd =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF1_CMD);
+	p->mp_rcp_data_modif1_dyn =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF1_DYN);
+	p->mp_rcp_data_modif1_ofs =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF1_OFS);
+	p->mp_rcp_data_modif1_value =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF1_VALUE);
+	p->mp_rcp_data_modif2_cmd =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF2_CMD);
+	p->mp_rcp_data_modif2_dyn =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF2_DYN);
+	p->mp_rcp_data_modif2_ofs =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF2_OFS);
+	p->mp_rcp_data_modif2_value =
+		register_get_field(p->mp_rcp_data, HST_RCP_DATA_MODIF2_VALUE);
+
+	return 0;
+}
+
+/* RCP */
+void hst_nthw_rcp_select(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void hst_nthw_rcp_cnt(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void hst_nthw_rcp_strip_mode(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_strip_mode, val);
+}
+
+void hst_nthw_rcp_start_dyn(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_start_dyn, val);
+}
+
+void hst_nthw_rcp_start_ofs(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_start_ofs, val);
+}
+
+void hst_nthw_rcp_end_dyn(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_end_dyn, val);
+}
+
+void hst_nthw_rcp_end_ofs(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_end_ofs, val);
+}
+
+void hst_nthw_rcp_modif0_cmd(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif0_cmd, val);
+}
+
+void hst_nthw_rcp_modif0_dyn(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif0_dyn, val);
+}
+
+void hst_nthw_rcp_modif0_ofs(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif0_ofs, val);
+}
+
+void hst_nthw_rcp_modif0_value(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif0_value, val);
+}
+
+void hst_nthw_rcp_modif1_cmd(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif1_cmd, val);
+}
+
+void hst_nthw_rcp_modif1_dyn(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif1_dyn, val);
+}
+
+void hst_nthw_rcp_modif1_ofs(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif1_ofs, val);
+}
+
+void hst_nthw_rcp_modif1_value(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif1_value, val);
+}
+
+void hst_nthw_rcp_modif2_cmd(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif2_cmd, val);
+}
+
+void hst_nthw_rcp_modif2_dyn(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif2_dyn, val);
+}
+
+void hst_nthw_rcp_modif2_ofs(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif2_ofs, val);
+}
+
+void hst_nthw_rcp_modif2_value(const struct hst_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_modif2_value, val);
+}
+
+void hst_nthw_rcp_flush(const struct hst_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.h
new file mode 100644
index 0000000000..5bc7eb6e55
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_hst.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_HST_H__
+#define __FLOW_NTHW_HST_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct hst_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_hst;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_strip_mode;
+	nt_field_t *mp_rcp_data_start_dyn;
+	nt_field_t *mp_rcp_data_start_ofs;
+	nt_field_t *mp_rcp_data_end_dyn;
+	nt_field_t *mp_rcp_data_end_ofs;
+	nt_field_t *mp_rcp_data_modif0_cmd;
+	nt_field_t *mp_rcp_data_modif0_dyn;
+	nt_field_t *mp_rcp_data_modif0_ofs;
+	nt_field_t *mp_rcp_data_modif0_value;
+	nt_field_t *mp_rcp_data_modif1_cmd;
+	nt_field_t *mp_rcp_data_modif1_dyn;
+	nt_field_t *mp_rcp_data_modif1_ofs;
+	nt_field_t *mp_rcp_data_modif1_value;
+	nt_field_t *mp_rcp_data_modif2_cmd;
+	nt_field_t *mp_rcp_data_modif2_dyn;
+	nt_field_t *mp_rcp_data_modif2_ofs;
+	nt_field_t *mp_rcp_data_modif2_value;
+};
+
+typedef struct hst_nthw hst_nthw_t;
+
+struct hst_nthw *hst_nthw_new(void);
+void hst_nthw_delete(struct hst_nthw *p);
+int hst_nthw_init(struct hst_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int hst_nthw_setup(struct hst_nthw *p, int n_idx, int n_idx_cnt);
+void hst_nthw_set_debug_mode(struct hst_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void hst_nthw_rcp_select(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_cnt(const struct hst_nthw *p, uint32_t val);
+
+void hst_nthw_rcp_strip_mode(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_start_dyn(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_start_ofs(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_end_dyn(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_end_ofs(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif0_cmd(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif0_dyn(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif0_ofs(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif0_value(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif1_cmd(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif1_dyn(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif1_ofs(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif1_value(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif2_cmd(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif2_dyn(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif2_ofs(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_modif2_value(const struct hst_nthw *p, uint32_t val);
+void hst_nthw_rcp_flush(const struct hst_nthw *p);
+
+#endif /* __FLOW_NTHW_HST_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.c
new file mode 100644
index 0000000000..0f51a36e57
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.c
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_ifr.h"
+
+void ifr_nthw_set_debug_mode(struct ifr_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_ifr, n_debug_mode);
+}
+
+struct ifr_nthw *ifr_nthw_new(void)
+{
+	struct ifr_nthw *p = malloc(sizeof(struct ifr_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void ifr_nthw_delete(struct ifr_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int ifr_nthw_init(struct ifr_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_IFR, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Ifr %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_ifr = fpga_query_module(p_fpga, MOD_IFR, n_instance);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_ifr, IFR_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, IFR_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, IFR_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_ifr, IFR_RCP_DATA);
+	p->mp_rcp_data_en = register_get_field(p->mp_rcp_data, IFR_RCP_DATA_EN);
+	p->mp_rcp_data_mtu = register_get_field(p->mp_rcp_data, IFR_RCP_DATA_MTU);
+
+	return 0;
+}
+
+void ifr_nthw_rcp_select(const struct ifr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_addr);
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void ifr_nthw_rcp_cnt(const struct ifr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_cnt);
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void ifr_nthw_rcp_en(const struct ifr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_data_en);
+	field_set_val32(p->mp_rcp_data_en, val);
+}
+
+void ifr_nthw_rcp_mtu(const struct ifr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_data_en);
+	field_set_val32(p->mp_rcp_data_mtu, val);
+}
+
+void ifr_nthw_rcp_flush(const struct ifr_nthw *p)
+{
+	assert(p->mp_rcp_ctrl);
+	assert(p->mp_rcp_data);
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.h
new file mode 100644
index 0000000000..626ca3d193
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ifr.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_IFR_H__
+#define __FLOW_NTHW_IFR_H__
+
+#include "nthw_fpga_model.h"
+
+struct ifr_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_ifr;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_en;
+	nt_field_t *mp_rcp_data_mtu;
+};
+
+struct ifr_nthw *ifr_nthw_new(void);
+void ifr_nthw_delete(struct ifr_nthw *p);
+int ifr_nthw_init(struct ifr_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int ifr_nthw_setup(struct ifr_nthw *p, int n_idx, int n_idx_cnt);
+void ifr_nthw_set_debug_mode(struct ifr_nthw *p, unsigned int n_debug_mode);
+
+/* IFR */
+void ifr_nthw_rcp_select(const struct ifr_nthw *p, uint32_t val);
+void ifr_nthw_rcp_cnt(const struct ifr_nthw *p, uint32_t val);
+void ifr_nthw_rcp_en(const struct ifr_nthw *p, uint32_t val);
+void ifr_nthw_rcp_mtu(const struct ifr_nthw *p, uint32_t val);
+void ifr_nthw_rcp_flush(const struct ifr_nthw *p);
+
+#endif /* __FLOW_NTHW_IFR_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.c
new file mode 100644
index 0000000000..27b55e3b7c
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.c
@@ -0,0 +1,341 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "nt_util.h"
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+#include "nthw_fpga_model.h"
+
+#include "flow_nthw_info.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+static inline unsigned int clamp_one(unsigned int val)
+{
+	return val > 1 ? 1 : val;
+}
+
+struct info_nthw *info_nthw_new(void)
+{
+	struct info_nthw *p = malloc(sizeof(struct info_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void info_nthw_delete(struct info_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int info_nthw_init(struct info_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	assert(n_instance >= 0 && n_instance < 256);
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+
+	unsigned int km_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_KM_PRESENT, 0));
+	unsigned int kcc_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_CAT_KCC_PRESENT, 0));
+	unsigned int ioa_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_IOA_PRESENT, 0));
+	unsigned int roa_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_ROA_PRESENT, 0));
+	unsigned int dbs_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_DBS_PRESENT, 0));
+	unsigned int flm_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_FLM_PRESENT, 0));
+	unsigned int hst_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_HST_PRESENT, 0));
+
+	/* Modules for Tx Packet Edit function */
+	unsigned int hfu_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_HFU_PRESENT, 0));
+	unsigned int tx_cpy_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_TX_CPY_PRESENT, 0));
+	unsigned int tx_ins_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_TX_INS_PRESENT, 0));
+	unsigned int tx_rpl_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_TX_RPL_PRESENT, 0));
+	unsigned int csu_present =
+		clamp_one(fpga_get_product_param(p_fpga, NT_CSU_PRESENT, 0));
+	unsigned int tpe_present = (hfu_present && tx_cpy_present && tx_ins_present &&
+				   tx_rpl_present && csu_present) ?
+				  1 :
+				  0;
+
+	p->n_phy_ports = fpga_get_product_param(p_fpga, NT_PHY_PORTS, 0);
+	p->n_rx_ports = fpga_get_product_param(p_fpga, NT_RX_PORTS, 0);
+	p->n_ltx_avail = fpga_get_product_param(p_fpga, NT_LR_PRESENT, 0);
+	p->nb_cat_func = fpga_get_product_param(p_fpga, NT_CAT_FUNCS, 0);
+	p->nb_categories = fpga_get_product_param(p_fpga, NT_CATEGORIES, 0);
+	p->nb_queues = fpga_get_product_param(p_fpga, NT_QUEUES, 0);
+	p->nb_flow_types = fpga_get_product_param(p_fpga, NT_KM_FLOW_TYPES, 0) *
+			 clamp_one(km_present + flm_present);
+	p->nb_pm_ext = fpga_get_product_param(p_fpga, NT_CAT_N_EXT, 0);
+	p->nb_len = fpga_get_product_param(p_fpga, NT_CAT_N_LEN, 0);
+	p->nb_kcc_size =
+		fpga_get_product_param(p_fpga, NT_CAT_KCC_SIZE, 0) * kcc_present;
+	p->nb_kcc_banks =
+		fpga_get_product_param(p_fpga, NT_CAT_KCC_BANKS, 0) * kcc_present;
+	p->nb_km_categories =
+		fpga_get_product_param(p_fpga, NT_KM_CATEGORIES, 0) * km_present;
+	p->nb_km_cam_banks =
+		fpga_get_product_param(p_fpga, NT_KM_CAM_BANKS, 0) * km_present;
+	p->nb_km_cam_record_words =
+		fpga_get_product_param(p_fpga, NT_KM_CAM_REC_WORDS, 0) * km_present;
+	p->nb_km_cam_records =
+		fpga_get_product_param(p_fpga, NT_KM_CAM_RECORDS, 0) * km_present;
+	p->nb_km_tcam_banks =
+		fpga_get_product_param(p_fpga, NT_KM_TCAM_BANKS, 0) * km_present;
+	p->nb_km_tcam_bank_width =
+		fpga_get_product_param(p_fpga, NT_KM_TCAM_BANK_WIDTH, 0) *
+		km_present;
+	p->nb_flm_categories =
+		fpga_get_product_param(p_fpga, NT_FLM_CATEGORIES, 0) * flm_present;
+	p->nb_flm_size_mb = fpga_get_product_param(p_fpga, NT_FLM_SIZE_MB, 0);
+	p->nb_flm_entry_size = fpga_get_product_param(p_fpga, NT_FLM_ENTRY_SIZE, 0);
+	p->nb_flm_variant = fpga_get_product_param(p_fpga, NT_FLM_VARIANT, 0);
+	p->nb_flm_prios =
+		fpga_get_product_param(p_fpga, NT_FLM_PRIOS, 0) * flm_present;
+	p->nb_flm_pst_profiles =
+		fpga_get_product_param(p_fpga, NT_FLM_PST_PROFILES, 0) *
+		flm_present;
+	p->nb_hst_categories =
+		fpga_get_product_param(p_fpga, NT_HST_CATEGORIES, 0) * hst_present;
+	p->nb_qsl_categories = fpga_get_product_param(p_fpga, NT_QSL_CATEGORIES, 0);
+	p->nb_qsl_qst_entries = fpga_get_product_param(p_fpga, NT_QSL_QST_SIZE, 0);
+	p->nb_pdb_categories = fpga_get_product_param(p_fpga, NT_PDB_CATEGORIES, 0);
+	p->nb_ioa_categories =
+		fpga_get_product_param(p_fpga, NT_IOA_CATEGORIES, 0) * ioa_present;
+	p->nb_roa_categories =
+		fpga_get_product_param(p_fpga, NT_ROA_CATEGORIES, 0) * roa_present;
+	p->nb_dbs_categories =
+		RTE_MIN(fpga_get_product_param(p_fpga, NT_DBS_RX_QUEUES, 0),
+		    fpga_get_product_param(p_fpga, NT_DBS_TX_QUEUES, 0)) *
+		dbs_present;
+	p->nb_cat_km_if_cnt = fpga_get_product_param(p_fpga, NT_CAT_KM_IF_CNT,
+					       km_present + flm_present);
+	p->m_cat_km_if_m0 = fpga_get_product_param(p_fpga, NT_CAT_KM_IF_M0, -1);
+	p->m_cat_km_if_m1 = fpga_get_product_param(p_fpga, NT_CAT_KM_IF_M1, -1);
+	p->nb_tpe_categories =
+		fpga_get_product_param(p_fpga, NT_TPE_CATEGORIES, 0) * tpe_present;
+	p->nb_tx_cpy_writers =
+		fpga_get_product_param(p_fpga, NT_TX_CPY_WRITERS, 0) * tpe_present;
+	p->nb_tx_cpy_mask_mem =
+		fpga_get_product_param(p_fpga, NT_CPY_MASK_MEM, 0) * tpe_present;
+	p->nb_tx_rpl_depth =
+		fpga_get_product_param(p_fpga, NT_TX_RPL_DEPTH, 0) * tpe_present;
+	p->nb_tx_rpl_ext_categories =
+		fpga_get_product_param(p_fpga, NT_TX_RPL_EXT_CATEGORIES, 0) *
+		tpe_present;
+	p->nb_tpe_ifr_categories =
+		fpga_get_product_param(p_fpga, NT_TX_MTU_PROFILE_IFR, 0);
+	return 0;
+}
+
+unsigned int info_nthw_get_nb_phy_ports(const struct info_nthw *p)
+{
+	return p->n_phy_ports;
+}
+
+unsigned int info_nthw_get_nb_rx_ports(const struct info_nthw *p)
+{
+	return p->n_rx_ports;
+}
+
+unsigned int info_nthw_get_ltx_avail(const struct info_nthw *p)
+{
+	return p->n_ltx_avail;
+}
+
+unsigned int info_nthw_get_nb_categories(const struct info_nthw *p)
+{
+	return p->nb_categories;
+}
+
+unsigned int info_nthw_get_kcc_size(const struct info_nthw *p)
+{
+	return p->nb_kcc_size;
+}
+
+unsigned int info_nthw_get_kcc_banks(const struct info_nthw *p)
+{
+	return p->nb_kcc_banks;
+}
+
+unsigned int info_nthw_get_nb_queues(const struct info_nthw *p)
+{
+	return p->nb_queues;
+}
+
+unsigned int info_nthw_get_nb_cat_funcs(const struct info_nthw *p)
+{
+	return p->nb_cat_func;
+}
+
+unsigned int info_nthw_get_nb_km_flow_types(const struct info_nthw *p)
+{
+	return p->nb_flow_types;
+}
+
+unsigned int info_nthw_get_nb_pm_ext(const struct info_nthw *p)
+{
+	return p->nb_pm_ext;
+}
+
+unsigned int info_nthw_get_nb_len(const struct info_nthw *p)
+{
+	return p->nb_len;
+}
+
+unsigned int info_nthw_get_nb_km_categories(const struct info_nthw *p)
+{
+	return p->nb_km_categories;
+}
+
+unsigned int info_nthw_get_nb_km_cam_banks(const struct info_nthw *p)
+{
+	return p->nb_km_cam_banks;
+}
+
+unsigned int info_nthw_get_nb_km_cam_record_words(const struct info_nthw *p)
+{
+	return p->nb_km_cam_record_words;
+}
+
+unsigned int info_nthw_get_nb_km_cam_records(const struct info_nthw *p)
+{
+	return p->nb_km_cam_records;
+}
+
+unsigned int info_nthw_get_nb_km_tcam_banks(const struct info_nthw *p)
+{
+	return p->nb_km_tcam_banks;
+}
+
+unsigned int info_nthw_get_nb_km_tcam_bank_width(const struct info_nthw *p)
+{
+	return p->nb_km_tcam_bank_width;
+}
+
+unsigned int info_nthw_get_nb_flm_categories(const struct info_nthw *p)
+{
+	return p->nb_flm_categories;
+}
+
+unsigned int info_nthw_get_nb_flm_size_mb(const struct info_nthw *p)
+{
+	return p->nb_flm_size_mb;
+}
+
+unsigned int info_nthw_get_nb_flm_entry_size(const struct info_nthw *p)
+{
+	return p->nb_flm_entry_size;
+}
+
+unsigned int info_nthw_get_nb_flm_variant(const struct info_nthw *p)
+{
+	return p->nb_flm_variant;
+}
+
+unsigned int info_nthw_get_nb_flm_prios(const struct info_nthw *p)
+{
+	return p->nb_flm_prios;
+}
+
+unsigned int info_nthw_get_nb_flm_pst_profiles(const struct info_nthw *p)
+{
+	return p->nb_flm_pst_profiles;
+}
+
+unsigned int info_nthw_get_nb_hst_categories(const struct info_nthw *p)
+{
+	return p->nb_hst_categories;
+}
+
+unsigned int info_nthw_get_nb_qsl_categories(const struct info_nthw *p)
+{
+	return p->nb_qsl_categories;
+}
+
+unsigned int info_nthw_get_nb_qsl_qst_entries(const struct info_nthw *p)
+{
+	return p->nb_qsl_qst_entries;
+}
+
+unsigned int info_nthw_get_nb_pdb_categories(const struct info_nthw *p)
+{
+	return p->nb_pdb_categories;
+}
+
+unsigned int info_nthw_get_nb_ioa_categories(const struct info_nthw *p)
+{
+	return p->nb_ioa_categories;
+}
+
+unsigned int info_nthw_get_nb_roa_categories(const struct info_nthw *p)
+{
+	return p->nb_roa_categories;
+}
+
+unsigned int info_nthw_get_nb_dbs_categories(const struct info_nthw *p)
+{
+	return p->nb_dbs_categories;
+}
+
+unsigned int info_nthw_get_nb_cat_km_if_cnt(const struct info_nthw *p)
+{
+	return p->nb_cat_km_if_cnt;
+}
+
+unsigned int info_nthw_get_nb_cat_km_if_m0(const struct info_nthw *p)
+{
+	return p->m_cat_km_if_m0;
+}
+
+unsigned int info_nthw_get_nb_cat_km_if_m1(const struct info_nthw *p)
+{
+	return p->m_cat_km_if_m1;
+}
+
+unsigned int info_nthw_get_nb_tpe_categories(const struct info_nthw *p)
+{
+	return p->nb_tpe_categories;
+}
+
+unsigned int info_nthw_get_nb_tx_cpy_writers(const struct info_nthw *p)
+{
+	return p->nb_tx_cpy_writers;
+}
+
+unsigned int info_nthw_get_nb_tx_cpy_mask_mem(const struct info_nthw *p)
+{
+	return p->nb_tx_cpy_mask_mem;
+}
+
+unsigned int info_nthw_get_nb_tx_rpl_depth(const struct info_nthw *p)
+{
+	return p->nb_tx_rpl_depth;
+}
+
+unsigned int info_nthw_get_nb_tx_rpl_ext_categories(const struct info_nthw *p)
+{
+	return p->nb_tx_rpl_ext_categories;
+}
+
+unsigned int info_nthw_get_nb_tpe_ifr_categories(const struct info_nthw *p)
+{
+	return p->nb_tpe_ifr_categories;
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.h
new file mode 100644
index 0000000000..c697ba84e9
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_info.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_INFO_H__
+#define __FLOW_NTHW_INFO_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct info_nthw;
+
+struct info_nthw *info_nthw_new(void);
+void info_nthw_delete(struct info_nthw *p);
+int info_nthw_init(struct info_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int info_nthw_setup(struct info_nthw *p, int n_idx, int n_idx_cnt);
+
+unsigned int info_nthw_get_nb_phy_ports(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_rx_ports(const struct info_nthw *p);
+unsigned int info_nthw_get_ltx_avail(const struct info_nthw *p);
+
+unsigned int info_nthw_get_nb_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_queues(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_cat_funcs(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_flow_types(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_pm_ext(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_len(const struct info_nthw *p);
+unsigned int info_nthw_get_kcc_size(const struct info_nthw *p);
+unsigned int info_nthw_get_kcc_banks(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_cam_banks(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_cam_record_words(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_cam_records(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_tcam_banks(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_km_tcam_bank_width(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_size_mb(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_entry_size(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_variant(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_prios(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_flm_pst_profiles(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_hst_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_qsl_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_qsl_qst_entries(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_pdb_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_ioa_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_roa_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_dbs_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_cat_km_if_cnt(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_cat_km_if_m0(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_cat_km_if_m1(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tpe_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tx_cpy_writers(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tx_cpy_mask_mem(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tx_rpl_depth(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tx_rpl_ext_categories(const struct info_nthw *p);
+unsigned int info_nthw_get_nb_tpe_ifr_categories(const struct info_nthw *p);
+
+struct info_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+	unsigned int n_phy_ports;
+	unsigned int n_rx_ports;
+	unsigned int n_ltx_avail;
+	unsigned int nb_cat_func;
+	unsigned int nb_categories;
+	unsigned int nb_queues;
+	unsigned int nb_flow_types;
+	unsigned int nb_pm_ext;
+	unsigned int nb_len;
+	unsigned int nb_kcc_size;
+	unsigned int nb_kcc_banks;
+	unsigned int nb_km_categories;
+	unsigned int nb_km_cam_banks;
+	unsigned int nb_km_cam_record_words;
+	unsigned int nb_km_cam_records;
+	unsigned int nb_km_tcam_banks;
+	unsigned int nb_km_tcam_bank_width;
+	unsigned int nb_flm_categories;
+	unsigned int nb_flm_size_mb;
+	unsigned int nb_flm_entry_size;
+	unsigned int nb_flm_variant;
+	unsigned int nb_flm_prios;
+	unsigned int nb_flm_pst_profiles;
+	unsigned int nb_hst_categories;
+	unsigned int nb_qsl_categories;
+	unsigned int nb_qsl_qst_entries;
+	unsigned int nb_pdb_categories;
+	unsigned int nb_ioa_categories;
+	unsigned int nb_roa_categories;
+	unsigned int nb_dbs_categories;
+	unsigned int nb_cat_km_if_cnt;
+	unsigned int m_cat_km_if_m0;
+	unsigned int m_cat_km_if_m1;
+	unsigned int nb_tpe_categories;
+	unsigned int nb_tx_cpy_writers;
+	unsigned int nb_tx_cpy_mask_mem;
+	unsigned int nb_tx_rpl_depth;
+	unsigned int nb_tx_rpl_ext_categories;
+	unsigned int nb_tpe_ifr_categories;
+};
+
+#endif /* __FLOW_NTHW_INFO_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.c
new file mode 100644
index 0000000000..a83d443f6f
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.c
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_ioa.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void ioa_nthw_set_debug_mode(struct ioa_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_ioa, n_debug_mode);
+}
+
+struct ioa_nthw *ioa_nthw_new(void)
+{
+	struct ioa_nthw *p = malloc(sizeof(struct ioa_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void ioa_nthw_delete(struct ioa_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int ioa_nthw_init(struct ioa_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_IOA, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Ioa %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_ioa = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_ioa, IOA_RECIPE_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, IOA_RECIPE_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, IOA_RECIPE_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_ioa, IOA_RECIPE_DATA);
+	p->mp_rcp_data_tunnel_pop =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_TUNNEL_POP);
+	p->mp_rcp_data_vlan_pop =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_POP);
+	p->mp_rcp_data_vlan_push =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_PUSH);
+	p->mp_rcp_data_vlan_vid =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_VID);
+	p->mp_rcp_data_vlan_dei =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_DEI);
+	p->mp_rcp_data_vlan_pcp =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_PCP);
+	p->mp_rcp_data_vlan_tpid_sel =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_VLAN_TPID_SEL);
+	p->mp_rcp_data_queue_override_en =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_QUEUE_OVERRIDE_EN);
+	p->mp_rcp_data_queue_id =
+		register_get_field(p->mp_rcp_data, IOA_RECIPE_DATA_QUEUE_ID);
+
+	/* Special Vlan Tpid */
+	p->mp_special = module_get_register(p->m_ioa, IOA_VLAN_TPID_SPECIAL);
+	p->mp_special_vlan_tpid_cust_tpid0 =
+		register_get_field(p->mp_special, IOA_VLAN_TPID_SPECIAL_CUSTTPID0);
+	p->mp_special_vlan_tpid_cust_tpid1 =
+		register_get_field(p->mp_special, IOA_VLAN_TPID_SPECIAL_CUSTTPID1);
+	{
+		/*
+		 * This extension in IOA is a messy way FPGA have chosen to
+		 * put control bits for EPP module in IOA. It is accepted as
+		 * we are going towards exchange IOA and ROA modules later
+		 * to get higher scalability in future.
+		 */
+		p->mp_roa_epp_ctrl =
+			module_query_register(p->m_ioa, IOA_ROA_EPP_CTRL);
+		if (p->mp_roa_epp_ctrl) {
+			p->mp_roa_epp_addr =
+				register_get_field(p->mp_roa_epp_ctrl,
+						   IOA_ROA_EPP_CTRL_ADR);
+			p->mp_roa_epp_cnt =
+				register_get_field(p->mp_roa_epp_ctrl,
+						   IOA_ROA_EPP_CTRL_CNT);
+		} else {
+			p->mp_roa_epp_addr = NULL;
+			p->mp_roa_epp_cnt = NULL;
+		}
+
+		p->mp_roa_epp_data =
+			module_query_register(p->m_ioa, IOA_ROA_EPP_DATA);
+		if (p->mp_roa_epp_data) {
+			p->mp_roa_epp_data_push_tunnel =
+				register_get_field(p->mp_roa_epp_data,
+						   IOA_ROA_EPP_DATA_PUSH_TUNNEL);
+			p->mp_roa_epp_data_tx_port =
+				register_get_field(p->mp_roa_epp_data,
+						   IOA_ROA_EPP_DATA_TX_PORT);
+		} else {
+			p->mp_roa_epp_data_push_tunnel = NULL;
+			p->mp_roa_epp_data_tx_port = NULL;
+		}
+	}
+	return 0;
+}
+
+/* RCP */
+void ioa_nthw_rcp_select(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void ioa_nthw_rcp_cnt(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void ioa_nthw_rcp_tunnel_pop(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tunnel_pop, val);
+}
+
+void ioa_nthw_rcp_vlan_pop(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_pop, val);
+}
+
+void ioa_nthw_rcp_vlan_push(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_push, val);
+}
+
+void ioa_nthw_rcp_vlan_vid(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_vid, val);
+}
+
+void ioa_nthw_rcp_vlan_dei(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_dei, val);
+}
+
+void ioa_nthw_rcp_vlan_pcp(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_pcp, val);
+}
+
+void ioa_nthw_rcp_vlan_tpid_sel(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_vlan_tpid_sel, val);
+}
+
+void ioa_nthw_rcp_queue_override_en(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_queue_override_en, val);
+}
+
+void ioa_nthw_rcp_queue_id(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_queue_id, val);
+}
+
+void ioa_nthw_rcp_flush(const struct ioa_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+/* Vlan Tpid Special */
+void ioa_nthw_special_vlan_tpid_cust_tpid0(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_special_vlan_tpid_cust_tpid0, val);
+}
+
+void ioa_nthw_special_vlan_tpid_cust_tpid1(const struct ioa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_special_vlan_tpid_cust_tpid1, val);
+}
+
+void ioa_nthw_special_vlan_tpid_flush(const struct ioa_nthw *p)
+{
+	register_flush(p->mp_special, 1);
+}
+
+void ioa_nthw_roa_epp_select(const struct ioa_nthw *p, uint32_t val)
+{
+	if (p->mp_roa_epp_addr)
+		field_set_val32(p->mp_roa_epp_addr, val);
+}
+
+void ioa_nthw_roa_epp_cnt(const struct ioa_nthw *p, uint32_t val)
+{
+	if (p->mp_roa_epp_cnt)
+		field_set_val32(p->mp_roa_epp_cnt, val);
+}
+
+void ioa_nthw_roa_epp_push_tunnel(const struct ioa_nthw *p, uint32_t val)
+{
+	if (p->mp_roa_epp_data_push_tunnel)
+		field_set_val32(p->mp_roa_epp_data_push_tunnel, val);
+}
+
+void ioa_nthw_roa_epp_tx_port(const struct ioa_nthw *p, uint32_t val)
+{
+	if (p->mp_roa_epp_data_tx_port)
+		field_set_val32(p->mp_roa_epp_data_tx_port, val);
+}
+
+void ioa_nthw_roa_epp_flush(const struct ioa_nthw *p)
+{
+	if (p->mp_roa_epp_ctrl)
+		register_flush(p->mp_roa_epp_ctrl, 1);
+	if (p->mp_roa_epp_data)
+		register_flush(p->mp_roa_epp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.h
new file mode 100644
index 0000000000..8ab30d2d28
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_ioa.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_IOA_H__
+#define __FLOW_NTHW_IOA_H__
+
+#include "nthw_fpga_model.h"
+
+#include <stdint.h> /* uint32_t */
+
+struct ioa_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_ioa;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+
+	nt_field_t *mp_rcp_data_tunnel_pop;
+	nt_field_t *mp_rcp_data_vlan_pop;
+	nt_field_t *mp_rcp_data_vlan_push;
+	nt_field_t *mp_rcp_data_vlan_vid;
+	nt_field_t *mp_rcp_data_vlan_dei;
+	nt_field_t *mp_rcp_data_vlan_pcp;
+	nt_field_t *mp_rcp_data_vlan_tpid_sel;
+	nt_field_t *mp_rcp_data_queue_override_en;
+	nt_field_t *mp_rcp_data_queue_id;
+
+	nt_register_t *mp_special;
+	nt_field_t *mp_special_vlan_tpid_cust_tpid0;
+	nt_field_t *mp_special_vlan_tpid_cust_tpid1;
+
+	nt_register_t *mp_roa_epp_ctrl;
+	nt_field_t *mp_roa_epp_addr;
+	nt_field_t *mp_roa_epp_cnt;
+	nt_register_t *mp_roa_epp_data;
+	nt_field_t *mp_roa_epp_data_push_tunnel;
+	nt_field_t *mp_roa_epp_data_tx_port;
+};
+
+typedef struct ioa_nthw ioa_nthw_t;
+
+struct ioa_nthw *ioa_nthw_new(void);
+void ioa_nthw_delete(struct ioa_nthw *p);
+int ioa_nthw_init(struct ioa_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int ioa_nthw_setup(struct ioa_nthw *p, int n_idx, int n_idx_cnt);
+void ioa_nthw_set_debug_mode(struct ioa_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void ioa_nthw_rcp_select(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_cnt(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_tunnel_pop(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_pop(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_push(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_vid(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_dei(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_pcp(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_vlan_tpid_sel(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_queue_override_en(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_queue_id(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_rcp_flush(const struct ioa_nthw *p);
+
+/* Vlan Tpid Special */
+void ioa_nthw_special_vlan_tpid_cust_tpid0(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_special_vlan_tpid_cust_tpid1(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_special_vlan_tpid_flush(const struct ioa_nthw *p);
+
+/* EPP module */
+void ioa_nthw_roa_epp_select(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_roa_epp_cnt(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_roa_epp_push_tunnel(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_roa_epp_tx_port(const struct ioa_nthw *p, uint32_t val);
+void ioa_nthw_roa_epp_flush(const struct ioa_nthw *p);
+
+#endif /* __FLOW_NTHW_IOA_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.c
new file mode 100644
index 0000000000..af54e14940
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.c
@@ -0,0 +1,685 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_km.h"
+
+#include <stdint.h>
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+#define CHECK_AND_SET_VALUE(_a, val)             \
+	do {                                    \
+		__typeof__(_a) (a) = (_a); \
+		if (a) {                        \
+			field_set_val32(a, val); \
+		}                               \
+	} while (0)
+
+void km_nthw_set_debug_mode(struct km_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_km, n_debug_mode);
+}
+
+struct km_nthw *km_nthw_new(void)
+{
+	struct km_nthw *p = malloc(sizeof(struct km_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void km_nthw_delete(struct km_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int km_nthw_init(struct km_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_KM, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Km %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_km = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_km, KM_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, KM_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, KM_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_km, KM_RCP_DATA);
+	p->mp_rcp_data_qw0_dyn =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW0_DYN);
+	p->mp_rcp_data_qw0_ofs =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW0_OFS);
+	p->mp_rcp_data_qw0_sel_a =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW0_SEL_A);
+	p->mp_rcp_data_qw0_sel_b =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW0_SEL_B);
+	p->mp_rcp_data_qw4_dyn =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW4_DYN);
+	p->mp_rcp_data_qw4_ofs =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW4_OFS);
+	p->mp_rcp_data_qw4_sel_a =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW4_SEL_A);
+	p->mp_rcp_data_qw4_sel_b =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_QW4_SEL_B);
+
+	p->mp_rcp_data_sw8_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_DYN);
+	p->mp_rcp_data_dw8_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW8_DYN);
+
+	p->mp_rcp_data_swx_ovs_sb =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SWX_OVS_SB);
+	p->mp_rcp_data_swx_cch =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SWX_CCH);
+	p->mp_rcp_data_swx_sel_a =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_SWX_SEL_A);
+	p->mp_rcp_data_swx_sel_b =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_SWX_SEL_B);
+	p->mp_rcp_data_mask_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_MASK_A);
+	p->mp_rcp_data_mask_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_MASK_B);
+	p->mp_rcp_data_dual = register_get_field(p->mp_rcp_data, KM_RCP_DATA_DUAL);
+	p->mp_rcp_data_paired =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_PAIRED);
+	p->mp_rcp_data_el_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_EL_A);
+	p->mp_rcp_data_el_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_EL_B);
+	p->mp_rcp_data_info_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_INFO_A);
+	p->mp_rcp_data_info_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_INFO_B);
+	p->mp_rcp_data_ftm_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_FTM_A);
+	p->mp_rcp_data_ftm_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_FTM_B);
+	p->mp_rcp_data_bank_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_BANK_A);
+	p->mp_rcp_data_bank_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_BANK_B);
+	p->mp_rcp_data_kl_a = register_get_field(p->mp_rcp_data, KM_RCP_DATA_KL_A);
+	p->mp_rcp_data_kl_b = register_get_field(p->mp_rcp_data, KM_RCP_DATA_KL_B);
+	p->mp_rcp_data_flow_set =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_FLOW_SET);
+	p->mp_rcp_data_keyway_a =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_KEYWAY_A);
+	p->mp_rcp_data_keyway_b =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_KEYWAY_B);
+	p->mp_rcp_data_synergy_mode =
+		register_get_field(p->mp_rcp_data, KM_RCP_DATA_SYNERGY_MODE);
+
+	/* CAM */
+	p->mp_cam_ctrl = module_get_register(p->m_km, KM_CAM_CTRL);
+	p->mp_cam_addr = register_get_field(p->mp_cam_ctrl, KM_CAM_CTRL_ADR);
+	p->mp_cam_cnt = register_get_field(p->mp_cam_ctrl, KM_CAM_CTRL_CNT);
+	p->mp_cam_data = module_get_register(p->m_km, KM_CAM_DATA);
+	p->mp_cam_data_w0 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W0);
+	p->mp_cam_data_w1 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W1);
+	p->mp_cam_data_w2 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W2);
+	p->mp_cam_data_w3 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W3);
+	p->mp_cam_data_w4 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W4);
+	p->mp_cam_data_w5 = register_get_field(p->mp_cam_data, KM_CAM_DATA_W5);
+	p->mp_cam_data_ft0 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT0);
+	p->mp_cam_data_ft1 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT1);
+	p->mp_cam_data_ft2 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT2);
+	p->mp_cam_data_ft3 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT3);
+	p->mp_cam_data_ft4 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT4);
+	p->mp_cam_data_ft5 = register_get_field(p->mp_cam_data, KM_CAM_DATA_FT5);
+	/* TCAM */
+	p->mp_tcam_ctrl = module_get_register(p->m_km, KM_TCAM_CTRL);
+	p->mp_tcam_addr = register_get_field(p->mp_tcam_ctrl, KM_TCAM_CTRL_ADR);
+	p->mp_tcam_cnt = register_get_field(p->mp_tcam_ctrl, KM_TCAM_CTRL_CNT);
+	p->mp_tcam_data = module_get_register(p->m_km, KM_TCAM_DATA);
+	p->mp_tcam_data_t = register_get_field(p->mp_tcam_data, KM_TCAM_DATA_T);
+	/* TCI */
+	p->mp_tci_ctrl = module_get_register(p->m_km, KM_TCI_CTRL);
+	p->mp_tci_addr = register_get_field(p->mp_tci_ctrl, KM_TCI_CTRL_ADR);
+	p->mp_tci_cnt = register_get_field(p->mp_tci_ctrl, KM_TCI_CTRL_CNT);
+	p->mp_tci_data = module_get_register(p->m_km, KM_TCI_DATA);
+	p->mp_tci_data_color = register_get_field(p->mp_tci_data, KM_TCI_DATA_COLOR);
+	p->mp_tci_data_ft = register_get_field(p->mp_tci_data, KM_TCI_DATA_FT);
+	/* TCQ */
+	p->mp_tcq_ctrl = module_get_register(p->m_km, KM_TCQ_CTRL);
+	p->mp_tcq_addr = register_get_field(p->mp_tcq_ctrl, KM_TCQ_CTRL_ADR);
+	p->mp_tcq_cnt = register_get_field(p->mp_tcq_ctrl, KM_TCQ_CTRL_CNT);
+	p->mp_tcq_data = module_get_register(p->m_km, KM_TCQ_DATA);
+	p->mp_tcq_data_bank_mask =
+		register_query_field(p->mp_tcq_data, KM_TCQ_DATA_BANK_MASK);
+	p->mp_tcq_data_qual = register_get_field(p->mp_tcq_data, KM_TCQ_DATA_QUAL);
+
+	p->mp_rcp_data_dw0_b_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW0_B_DYN);
+	p->mp_rcp_data_dw0_b_ofs =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW0_B_OFS);
+	p->mp_rcp_data_dw2_b_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW2_B_DYN);
+	p->mp_rcp_data_dw2_b_ofs =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW2_B_OFS);
+	p->mp_rcp_data_sw4_b_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW4_B_DYN);
+	p->mp_rcp_data_sw4_b_ofs =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW4_B_OFS);
+	p->mp_rcp_data_sw5_b_dyn =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW5_B_DYN);
+	p->mp_rcp_data_sw5_b_ofs =
+		register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW5_B_OFS);
+	if (!p->mp_rcp_data_dw0_b_dyn) {
+		/* old field defines */
+		p->mp_rcp_data_dw0_b_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_QW0_B_DYN);
+		p->mp_rcp_data_dw0_b_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_QW0_B_OFS);
+		p->mp_rcp_data_dw2_b_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_QW4_B_DYN);
+		p->mp_rcp_data_dw2_b_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_QW4_B_OFS);
+		p->mp_rcp_data_sw4_b_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_B_DYN);
+		p->mp_rcp_data_sw4_b_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_B_OFS);
+		p->mp_rcp_data_sw5_b_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_B_DYN);
+		p->mp_rcp_data_sw5_b_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_B_OFS);
+	}
+
+	/* v0.6+ */
+	if (p->mp_rcp_data_dw8_dyn) {
+		p->mp_rcp_data_dw8_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW8_OFS);
+		p->mp_rcp_data_dw8_sel_a =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW8_SEL_A);
+		p->mp_rcp_data_dw8_sel_b =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW8_SEL_B);
+		p->mp_rcp_data_dw10_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW10_DYN);
+		p->mp_rcp_data_dw10_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW10_OFS);
+		p->mp_rcp_data_dw10_sel_a =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW10_SEL_A);
+		p->mp_rcp_data_dw10_sel_b =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_DW10_SEL_B);
+	} else if (p->mp_rcp_data_sw8_dyn) {
+		p->mp_rcp_data_sw8_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_OFS);
+		p->mp_rcp_data_sw8_sel_a =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_SEL_A);
+		p->mp_rcp_data_sw8_sel_b =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW8_SEL_B);
+		p->mp_rcp_data_sw9_dyn =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_DYN);
+		p->mp_rcp_data_sw9_ofs =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_OFS);
+		p->mp_rcp_data_sw9_sel_a =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_SEL_A);
+		p->mp_rcp_data_sw9_sel_b =
+			register_query_field(p->mp_rcp_data, KM_RCP_DATA_SW9_SEL_B);
+	}
+
+	return 0;
+}
+
+/* RCP */
+void km_nthw_rcp_select(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+};
+
+void km_nthw_rcp_cnt(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+};
+
+void km_nthw_rcp_qw0_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_dyn, val);
+};
+
+void km_nthw_rcp_qw0_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_ofs, val);
+};
+
+void km_nthw_rcp_qw0_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_sel_a, val);
+};
+
+void km_nthw_rcp_qw0_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw0_sel_b, val);
+};
+
+void km_nthw_rcp_qw4_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_dyn, val);
+};
+
+void km_nthw_rcp_qw4_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_ofs, val);
+};
+
+void km_nthw_rcp_qw4_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_sel_a, val);
+};
+
+void km_nthw_rcp_qw4_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_qw4_sel_b, val);
+};
+
+void km_nthw_rcp_dw8_dyn(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw8_dyn, val);
+};
+
+void km_nthw_rcp_sw8_dyn(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw8_dyn, val);
+};
+
+void km_nthw_rcp_sw8_ofs(const struct km_nthw *p, int32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw8_ofs, val);
+};
+
+void km_nthw_rcp_sw8_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw8_sel_a, val);
+};
+
+void km_nthw_rcp_sw8_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw8_sel_b, val);
+};
+
+void km_nthw_rcp_sw9_dyn(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw9_dyn, val);
+};
+
+void km_nthw_rcp_sw9_ofs(const struct km_nthw *p, int32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw9_ofs, val);
+};
+
+void km_nthw_rcp_sw9_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw9_sel_a, val);
+};
+
+void km_nthw_rcp_sw9_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_sw9_sel_b, val);
+};
+
+void km_nthw_rcp_swx_ovs_sb(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_swx_ovs_sb, val);
+};
+
+void km_nthw_rcp_swx_cch(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_swx_cch, val);
+};
+
+void km_nthw_rcp_dw8_ofs(const struct km_nthw *p, int32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw8_ofs, val);
+};
+
+void km_nthw_rcp_dw8_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw8_sel_a, val);
+};
+
+void km_nthw_rcp_dw8_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw8_sel_b, val);
+};
+
+void km_nthw_rcp_dw10_dyn(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw10_dyn, val);
+};
+
+void km_nthw_rcp_dw10_ofs(const struct km_nthw *p, int32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw10_ofs, val);
+};
+
+void km_nthw_rcp_dw10_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw10_sel_a, val);
+};
+
+void km_nthw_rcp_dw10_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_dw10_sel_b, val);
+};
+
+void km_nthw_rcp_swx_sel_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_swx_sel_a, val);
+};
+
+void km_nthw_rcp_swx_sel_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_swx_sel_b, val);
+};
+
+void km_nthw_rcp_mask_a(const struct km_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_mask_a, val, p->mp_rcp_data_mask_a->mn_words);
+};
+
+void km_nthw_rcp_mask_b(const struct km_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_mask_b, val, p->mp_rcp_data_mask_b->mn_words);
+};
+
+void km_nthw_rcp_mask_d_a(const struct km_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_rcp_data_mask_a, val, p->mp_rcp_data_mask_a->mn_words);
+}; /* for DW8/DW10 from v6+ */
+
+void km_nthw_rcp_dual(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dual, val);
+};
+
+void km_nthw_rcp_paired(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_paired, val);
+};
+
+void km_nthw_rcp_el_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_el_a, val);
+};
+
+void km_nthw_rcp_el_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_el_b, val);
+};
+
+void km_nthw_rcp_info_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_info_a, val);
+};
+
+void km_nthw_rcp_info_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_info_b, val);
+};
+
+void km_nthw_rcp_ftm_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ftm_a, val);
+};
+
+void km_nthw_rcp_ftm_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ftm_b, val);
+};
+
+void km_nthw_rcp_bank_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_bank_a, val);
+};
+
+void km_nthw_rcp_bank_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_bank_b, val);
+};
+
+void km_nthw_rcp_kl_a(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_kl_a, val);
+};
+
+void km_nthw_rcp_kl_b(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_kl_b, val);
+};
+
+void km_nthw_rcp_flow_set(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_flow_set, val);
+};
+
+void km_nthw_rcp_keyway_a(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_keyway_a, val);
+};
+
+void km_nthw_rcp_keyway_b(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_rcp_data_keyway_b, val);
+};
+
+void km_nthw_rcp_synergy_mode(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_synergy_mode, val);
+};
+
+void km_nthw_rcp_dw0_b_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dw0_b_dyn, val);
+};
+
+void km_nthw_rcp_dw0_b_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dw0_b_ofs, val);
+};
+
+void km_nthw_rcp_dw2_b_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dw2_b_dyn, val);
+};
+
+void km_nthw_rcp_dw2_b_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dw2_b_ofs, val);
+};
+
+void km_nthw_rcp_sw4_b_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw4_b_dyn, val);
+};
+
+void km_nthw_rcp_sw4_b_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw4_b_ofs, val);
+};
+
+void km_nthw_rcp_sw5_b_dyn(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw5_b_dyn, val);
+};
+
+void km_nthw_rcp_sw5_b_ofs(const struct km_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_sw5_b_ofs, val);
+};
+
+void km_nthw_rcp_flush(const struct km_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+};
+
+/* CAM */
+void km_nthw_cam_select(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_addr, val);
+};
+
+void km_nthw_cam_cnt(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_cnt, val);
+};
+
+void km_nthw_cam_w0(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w0, val);
+};
+
+void km_nthw_cam_w1(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w1, val);
+};
+
+void km_nthw_cam_w2(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w2, val);
+};
+
+void km_nthw_cam_w3(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w3, val);
+};
+
+void km_nthw_cam_w4(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w4, val);
+};
+
+void km_nthw_cam_w5(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_w5, val);
+};
+
+void km_nthw_cam_ft0(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft0, val);
+};
+
+void km_nthw_cam_ft1(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft1, val);
+};
+
+void km_nthw_cam_ft2(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft2, val);
+};
+
+void km_nthw_cam_ft3(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft3, val);
+};
+
+void km_nthw_cam_ft4(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft4, val);
+};
+
+void km_nthw_cam_ft5(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_cam_data_ft5, val);
+};
+
+void km_nthw_cam_flush(const struct km_nthw *p)
+{
+	register_flush(p->mp_cam_ctrl, 1);
+	register_flush(p->mp_cam_data, 1);
+};
+
+/* TCAM */
+void km_nthw_tcam_select(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tcam_addr, val);
+};
+
+void km_nthw_tcam_cnt(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tcam_cnt, val);
+};
+
+void km_nthw_tcam_t(const struct km_nthw *p, uint32_t *val)
+{
+	field_set_val(p->mp_tcam_data_t, val, 3);
+};
+
+void km_nthw_tcam_flush(const struct km_nthw *p)
+{
+	register_flush(p->mp_tcam_ctrl, 1);
+	register_flush(p->mp_tcam_data, 1);
+};
+
+/* TCI */
+void km_nthw_tci_select(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tci_addr, val);
+};
+
+void km_nthw_tci_cnt(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tci_cnt, val);
+};
+
+void km_nthw_tci_color(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tci_data_color, val);
+};
+
+void km_nthw_tci_ft(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tci_data_ft, val);
+};
+
+void km_nthw_tci_flush(const struct km_nthw *p)
+{
+	register_flush(p->mp_tci_ctrl, 1);
+	register_flush(p->mp_tci_data, 1);
+};
+
+/* TCQ */
+void km_nthw_tcq_select(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tcq_addr, val);
+};
+
+void km_nthw_tcq_cnt(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tcq_cnt, val);
+};
+
+void km_nthw_tcq_bank_mask(const struct km_nthw *p, uint32_t val)
+{
+	CHECK_AND_SET_VALUE(p->mp_tcq_data_bank_mask, val);
+};
+
+void km_nthw_tcq_qual(const struct km_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tcq_data_qual, val);
+};
+
+void km_nthw_tcq_qual72(const struct km_nthw *p, uint32_t *val)
+{
+	field_set_val(p->mp_tcq_data_qual, val, 3);
+}; /* to use in v4 */
+
+void km_nthw_tcq_flush(const struct km_nthw *p)
+{
+	register_flush(p->mp_tcq_ctrl, 1);
+	register_flush(p->mp_tcq_data, 1);
+};
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.h
new file mode 100644
index 0000000000..61f9ed2ae4
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_km.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_KM_H__
+#define __FLOW_NTHW_KM_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct km_nthw;
+
+typedef struct km_nthw km_nthw_t;
+
+struct km_nthw *km_nthw_new(void);
+void km_nthw_delete(struct km_nthw *p);
+int km_nthw_init(struct km_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int km_nthw_setup(struct km_nthw *p, int n_idx, int n_idx_cnt);
+void km_nthw_set_debug_mode(struct km_nthw *p, unsigned int n_debug_mode);
+
+/* RCP initial v3 */
+void km_nthw_rcp_select(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_cnt(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw0_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw0_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_qw0_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw0_sel_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw4_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw4_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_qw4_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_qw4_sel_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw8_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw8_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_sw8_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw8_sel_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw9_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw9_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_sw9_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw9_sel_b(const struct km_nthw *p, uint32_t val);
+/* subst in v6 */
+void km_nthw_rcp_dw8_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw8_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_dw8_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw8_sel_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw10_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw10_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_dw10_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw10_sel_b(const struct km_nthw *p, uint32_t val);
+
+void km_nthw_rcp_swx_ovs_sb(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_swx_cch(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_swx_sel_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_swx_sel_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_mask_a(const struct km_nthw *p, const uint32_t *val);
+void km_nthw_rcp_mask_d_a(const struct km_nthw *p, const uint32_t *val);
+void km_nthw_rcp_mask_b(const struct km_nthw *p, const uint32_t *val);
+void km_nthw_rcp_dual(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_paired(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_el_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_el_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_info_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_info_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_ftm_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_ftm_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_bank_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_bank_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_kl_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_kl_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_flow_set(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_keyway_a(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_keyway_b(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_synergy_mode(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw0_b_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw0_b_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_dw2_b_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_dw2_b_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_sw4_b_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw4_b_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_sw5_b_dyn(const struct km_nthw *p, uint32_t val);
+void km_nthw_rcp_sw5_b_ofs(const struct km_nthw *p, int32_t val);
+void km_nthw_rcp_flush(const struct km_nthw *p);
+/* CAM */
+void km_nthw_cam_select(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_cnt(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w0(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w1(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w2(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w3(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w4(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_w5(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft0(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft1(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft2(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft3(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft4(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_ft5(const struct km_nthw *p, uint32_t val);
+void km_nthw_cam_flush(const struct km_nthw *p);
+/* TCAM */
+void km_nthw_tcam_select(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcam_cnt(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcam_t(const struct km_nthw *p, uint32_t *val);
+void km_nthw_tcam_flush(const struct km_nthw *p);
+/* TCI */
+void km_nthw_tci_select(const struct km_nthw *p, uint32_t val);
+void km_nthw_tci_cnt(const struct km_nthw *p, uint32_t val);
+void km_nthw_tci_color(const struct km_nthw *p, uint32_t val);
+void km_nthw_tci_ft(const struct km_nthw *p, uint32_t val);
+void km_nthw_tci_flush(const struct km_nthw *p);
+/* TCQ */
+void km_nthw_tcq_select(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcq_cnt(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcq_bank_mask(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcq_qual(const struct km_nthw *p, uint32_t val);
+void km_nthw_tcq_qual72(const struct km_nthw *p, uint32_t *val);
+
+void km_nthw_tcq_flush(const struct km_nthw *p);
+
+struct km_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_km;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_qw0_dyn;
+	nt_field_t *mp_rcp_data_qw0_ofs;
+	nt_field_t *mp_rcp_data_qw0_sel_a;
+	nt_field_t *mp_rcp_data_qw0_sel_b;
+	nt_field_t *mp_rcp_data_qw4_dyn;
+	nt_field_t *mp_rcp_data_qw4_ofs;
+	nt_field_t *mp_rcp_data_qw4_sel_a;
+	nt_field_t *mp_rcp_data_qw4_sel_b;
+	nt_field_t *mp_rcp_data_sw8_dyn;
+	nt_field_t *mp_rcp_data_sw8_ofs;
+	nt_field_t *mp_rcp_data_sw8_sel_a;
+	nt_field_t *mp_rcp_data_sw8_sel_b;
+	nt_field_t *mp_rcp_data_sw9_dyn;
+	nt_field_t *mp_rcp_data_sw9_ofs;
+	nt_field_t *mp_rcp_data_sw9_sel_a;
+	nt_field_t *mp_rcp_data_sw9_sel_b;
+
+	nt_field_t *mp_rcp_data_dw8_dyn; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw8_ofs; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw8_sel_a; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw8_sel_b; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw10_dyn; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw10_ofs; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw10_sel_a; /* substituted Sw<x> from v6+ */
+	nt_field_t *mp_rcp_data_dw10_sel_b; /* substituted Sw<x> from v6+ */
+
+	nt_field_t *mp_rcp_data_swx_ovs_sb;
+	nt_field_t *mp_rcp_data_swx_cch;
+	nt_field_t *mp_rcp_data_swx_sel_a;
+	nt_field_t *mp_rcp_data_swx_sel_b;
+	nt_field_t *mp_rcp_data_mask_a;
+	nt_field_t *mp_rcp_data_mask_b;
+	nt_field_t *mp_rcp_data_dual;
+	nt_field_t *mp_rcp_data_paired;
+	nt_field_t *mp_rcp_data_el_a;
+	nt_field_t *mp_rcp_data_el_b;
+	nt_field_t *mp_rcp_data_info_a;
+	nt_field_t *mp_rcp_data_info_b;
+	nt_field_t *mp_rcp_data_ftm_a;
+	nt_field_t *mp_rcp_data_ftm_b;
+	nt_field_t *mp_rcp_data_bank_a;
+	nt_field_t *mp_rcp_data_bank_b;
+	nt_field_t *mp_rcp_data_kl_a;
+	nt_field_t *mp_rcp_data_kl_b;
+	nt_field_t *mp_rcp_data_flow_set;
+	nt_field_t *mp_rcp_data_keyway_a;
+	nt_field_t *mp_rcp_data_keyway_b;
+	nt_field_t *mp_rcp_data_synergy_mode;
+	nt_field_t *mp_rcp_data_dw0_b_dyn;
+	nt_field_t *mp_rcp_data_dw0_b_ofs;
+	nt_field_t *mp_rcp_data_dw2_b_dyn;
+	nt_field_t *mp_rcp_data_dw2_b_ofs;
+	nt_field_t *mp_rcp_data_sw4_b_dyn;
+	nt_field_t *mp_rcp_data_sw4_b_ofs;
+	nt_field_t *mp_rcp_data_sw5_b_dyn;
+	nt_field_t *mp_rcp_data_sw5_b_ofs;
+
+	nt_register_t *mp_cam_ctrl;
+	nt_field_t *mp_cam_addr;
+	nt_field_t *mp_cam_cnt;
+	nt_register_t *mp_cam_data;
+	nt_field_t *mp_cam_data_w0;
+	nt_field_t *mp_cam_data_w1;
+	nt_field_t *mp_cam_data_w2;
+	nt_field_t *mp_cam_data_w3;
+	nt_field_t *mp_cam_data_w4;
+	nt_field_t *mp_cam_data_w5;
+	nt_field_t *mp_cam_data_ft0;
+	nt_field_t *mp_cam_data_ft1;
+	nt_field_t *mp_cam_data_ft2;
+	nt_field_t *mp_cam_data_ft3;
+	nt_field_t *mp_cam_data_ft4;
+	nt_field_t *mp_cam_data_ft5;
+
+	nt_register_t *mp_tcam_ctrl;
+	nt_field_t *mp_tcam_addr;
+	nt_field_t *mp_tcam_cnt;
+	nt_register_t *mp_tcam_data;
+	nt_field_t *mp_tcam_data_t;
+
+	nt_register_t *mp_tci_ctrl;
+	nt_field_t *mp_tci_addr;
+	nt_field_t *mp_tci_cnt;
+	nt_register_t *mp_tci_data;
+	nt_field_t *mp_tci_data_color;
+	nt_field_t *mp_tci_data_ft;
+
+	nt_register_t *mp_tcq_ctrl;
+	nt_field_t *mp_tcq_addr;
+	nt_field_t *mp_tcq_cnt;
+	nt_register_t *mp_tcq_data;
+	nt_field_t *mp_tcq_data_bank_mask;
+	nt_field_t *mp_tcq_data_qual;
+};
+
+#endif /* __FLOW_NTHW_KM_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.c
new file mode 100644
index 0000000000..e823a527bb
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.c
@@ -0,0 +1,230 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_pdb.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void pdb_nthw_set_debug_mode(struct pdb_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_pdb, n_debug_mode);
+}
+
+struct pdb_nthw *pdb_nthw_new(void)
+{
+	struct pdb_nthw *p = malloc(sizeof(struct pdb_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void pdb_nthw_delete(struct pdb_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int pdb_nthw_init(struct pdb_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_PDB, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Pdb %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_pdb = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_pdb, PDB_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, PDB_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, PDB_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_pdb, PDB_RCP_DATA);
+	p->mp_rcp_data_descriptor =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_DESCRIPTOR);
+	p->mp_rcp_data_desc_len =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_DESC_LEN);
+	p->mp_rcp_data_tx_port =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_TX_PORT);
+	p->mp_rcp_data_tx_ignore =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_TX_IGNORE);
+	p->mp_rcp_data_tx_now =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_TX_NOW);
+	p->mp_rcp_data_crc_overwrite =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_CRC_OVERWRITE);
+	p->mp_rcp_data_align = register_get_field(p->mp_rcp_data, PDB_RCP_DATA_ALIGN);
+	p->mp_rcp_data_ofs0_dyn =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS0_DYN);
+	p->mp_rcp_data_ofs0_rel =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS0_REL);
+	p->mp_rcp_data_ofs1_dyn =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS1_DYN);
+	p->mp_rcp_data_ofs1_rel =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS1_REL);
+	p->mp_rcp_data_ofs2_dyn =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS2_DYN);
+	p->mp_rcp_data_ofs2_rel =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_OFS2_REL);
+	p->mp_rcp_data_ip_prot_tnl =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_IP_PROT_TNL);
+	p->mp_rcp_data_ppc_hsh =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_PPC_HSH);
+	p->mp_rcp_data_duplicate_en =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_DUPLICATE_EN);
+	p->mp_rcp_data_duplicate_bit =
+		register_get_field(p->mp_rcp_data, PDB_RCP_DATA_DUPLICATE_BIT);
+	p->mp_rcp_data_pcap_keep_fcs =
+		register_query_field(p->mp_rcp_data, PDB_RCP_DATA_PCAP_KEEP_FCS);
+	/* CONFIG */
+	p->mp_config = module_get_register(p->m_pdb, PDB_CONFIG);
+	p->mp_config_ts_format =
+		register_get_field(p->mp_config, PDB_CONFIG_TS_FORMAT);
+	p->mp_config_port_ofs =
+		register_get_field(p->mp_config, PDB_CONFIG_PORT_OFS);
+
+	return 0;
+}
+
+/* RCP */
+void pdb_nthw_rcp_select(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void pdb_nthw_rcp_cnt(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void pdb_nthw_rcp_descriptor(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_descriptor, val);
+}
+
+void pdb_nthw_rcp_desc_len(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_desc_len, val);
+}
+
+void pdb_nthw_rcp_tx_port(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tx_port, val);
+}
+
+void pdb_nthw_rcp_tx_ignore(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tx_ignore, val);
+}
+
+void pdb_nthw_rcp_tx_now(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tx_now, val);
+}
+
+void pdb_nthw_rcp_crc_overwrite(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_crc_overwrite, val);
+}
+
+void pdb_nthw_rcp_align(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_align, val);
+}
+
+void pdb_nthw_rcp_ofs0_dyn(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs0_dyn, val);
+}
+
+void pdb_nthw_rcp_ofs0_rel(const struct pdb_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs0_rel, val);
+}
+
+void pdb_nthw_rcp_ofs1_dyn(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs1_dyn, val);
+}
+
+void pdb_nthw_rcp_ofs1_rel(const struct pdb_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs1_rel, val);
+}
+
+void pdb_nthw_rcp_ofs2_dyn(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs2_dyn, val);
+}
+
+void pdb_nthw_rcp_ofs2_rel(const struct pdb_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs2_rel, val);
+}
+
+void pdb_nthw_rcp_ip_prot_tnl(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ip_prot_tnl, val);
+}
+
+void pdb_nthw_rcp_ppc_hsh(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ppc_hsh, val);
+}
+
+void pdb_nthw_rcp_duplicate_en(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_duplicate_en, val);
+}
+
+void pdb_nthw_rcp_duplicate_bit(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_duplicate_bit, val);
+}
+
+void pdb_nthw_rcp_data_pcap_keep_fcs(const struct pdb_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_pcap_keep_fcs)
+		field_set_val32(p->mp_rcp_data_pcap_keep_fcs, val);
+}
+
+void pdb_nthw_rcp_flush(const struct pdb_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+/* CONFIG */
+void pdb_nthw_config_ts_format(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_ts_format, val);
+}
+
+void pdb_nthw_config_port_ofs(const struct pdb_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_port_ofs, val);
+}
+
+void pdb_nthw_config_flush(const struct pdb_nthw *p)
+{
+	register_flush(p->mp_config, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.h
new file mode 100644
index 0000000000..aed050eca5
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_pdb.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_PDB_H__
+#define __FLOW_NTHW_PDB_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct pdb_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_pdb;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_descriptor;
+	nt_field_t *mp_rcp_data_desc_len;
+	nt_field_t *mp_rcp_data_tx_port;
+	nt_field_t *mp_rcp_data_tx_ignore;
+	nt_field_t *mp_rcp_data_tx_now;
+	nt_field_t *mp_rcp_data_crc_overwrite;
+	nt_field_t *mp_rcp_data_align;
+	nt_field_t *mp_rcp_data_ofs0_dyn;
+	nt_field_t *mp_rcp_data_ofs0_rel;
+	nt_field_t *mp_rcp_data_ofs1_dyn;
+	nt_field_t *mp_rcp_data_ofs1_rel;
+	nt_field_t *mp_rcp_data_ofs2_dyn;
+	nt_field_t *mp_rcp_data_ofs2_rel;
+	nt_field_t *mp_rcp_data_ip_prot_tnl;
+	nt_field_t *mp_rcp_data_ppc_hsh;
+	nt_field_t *mp_rcp_data_duplicate_en;
+	nt_field_t *mp_rcp_data_duplicate_bit;
+	nt_field_t *mp_rcp_data_pcap_keep_fcs;
+
+	nt_register_t *mp_config;
+	nt_field_t *mp_config_ts_format;
+	nt_field_t *mp_config_port_ofs;
+};
+
+typedef struct pdb_nthw pdb_nthw_t;
+
+struct pdb_nthw *pdb_nthw_new(void);
+void pdb_nthw_delete(struct pdb_nthw *p);
+int pdb_nthw_init(struct pdb_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int pdb_nthw_setup(struct pdb_nthw *p, int n_idx, int n_idx_cnt);
+void pdb_nthw_set_debug_mode(struct pdb_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void pdb_nthw_rcp_select(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_cnt(const struct pdb_nthw *p, uint32_t val);
+
+void pdb_nthw_rcp_descriptor(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_desc_len(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_tx_port(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_tx_ignore(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_tx_now(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_crc_overwrite(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_align(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_ofs0_dyn(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_ofs0_rel(const struct pdb_nthw *p, int32_t val);
+void pdb_nthw_rcp_ofs1_dyn(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_ofs1_rel(const struct pdb_nthw *p, int32_t val);
+void pdb_nthw_rcp_ofs2_dyn(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_ofs2_rel(const struct pdb_nthw *p, int32_t val);
+void pdb_nthw_rcp_ip_prot_tnl(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_ppc_hsh(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_duplicate_en(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_duplicate_bit(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_data_pcap_keep_fcs(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_rcp_flush(const struct pdb_nthw *p);
+
+/* CONFIG */
+void pdb_nthw_config_ts_format(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_config_port_ofs(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_config_port_ofs(const struct pdb_nthw *p, uint32_t val);
+void pdb_nthw_config_flush(const struct pdb_nthw *p);
+
+#endif /* __FLOW_NTHW_PDB_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.c
new file mode 100644
index 0000000000..6c13824df6
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.c
@@ -0,0 +1,355 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_qsl.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void qsl_nthw_set_debug_mode(struct qsl_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_qsl, n_debug_mode);
+}
+
+struct qsl_nthw *qsl_nthw_new(void)
+{
+	struct qsl_nthw *p = malloc(sizeof(struct qsl_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void qsl_nthw_delete(struct qsl_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int qsl_nthw_init(struct qsl_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_QSL, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: QSL %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_qsl = p_mod;
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_qsl, QSL_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, QSL_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, QSL_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_qsl, QSL_RCP_DATA);
+	p->mp_rcp_data_discard =
+		register_get_field(p->mp_rcp_data, QSL_RCP_DATA_DISCARD);
+	p->mp_rcp_data_drop = register_get_field(p->mp_rcp_data, QSL_RCP_DATA_DROP);
+	p->mp_rcp_data_tbl_lo =
+		register_get_field(p->mp_rcp_data, QSL_RCP_DATA_TBL_LO);
+	p->mp_rcp_data_tbl_hi =
+		register_get_field(p->mp_rcp_data, QSL_RCP_DATA_TBL_HI);
+	p->mp_rcp_data_tbl_idx =
+		register_get_field(p->mp_rcp_data, QSL_RCP_DATA_TBL_IDX);
+	p->mp_rcp_data_tbl_msk =
+		register_get_field(p->mp_rcp_data, QSL_RCP_DATA_TBL_MSK);
+	p->mp_rcp_data_cao = register_query_field(p->mp_rcp_data, QSL_RCP_DATA_CAO);
+	p->mp_rcp_data_lr = register_query_field(p->mp_rcp_data, QSL_RCP_DATA_LR);
+	p->mp_rcp_data_tsa = register_query_field(p->mp_rcp_data, QSL_RCP_DATA_TSA);
+	p->mp_rcp_data_vli = register_query_field(p->mp_rcp_data, QSL_RCP_DATA_VLI);
+
+	/* QST */
+	p->mp_qst_ctrl = module_get_register(p->m_qsl, QSL_QST_CTRL);
+	p->mp_qst_addr = register_get_field(p->mp_qst_ctrl, QSL_QST_CTRL_ADR);
+	p->mp_qst_cnt = register_get_field(p->mp_qst_ctrl, QSL_QST_CTRL_CNT);
+	p->mp_qst_data = module_get_register(p->m_qsl, QSL_QST_DATA);
+	p->mp_qst_data_queue = register_get_field(p->mp_qst_data, QSL_QST_DATA_QUEUE);
+	p->mp_qst_data_en = register_query_field(p->mp_qst_data, QSL_QST_DATA_EN);
+	p->mp_qst_data_tx_port =
+		register_query_field(p->mp_qst_data, QSL_QST_DATA_TX_PORT);
+	p->mp_qst_data_lre = register_query_field(p->mp_qst_data, QSL_QST_DATA_LRE);
+	p->mp_qst_data_tci = register_query_field(p->mp_qst_data, QSL_QST_DATA_TCI);
+	p->mp_qst_data_ven = register_query_field(p->mp_qst_data, QSL_QST_DATA_VEN);
+	/* QEN */
+	p->mp_qen_ctrl = module_get_register(p->m_qsl, QSL_QEN_CTRL);
+	p->mp_qen_addr = register_get_field(p->mp_qen_ctrl, QSL_QEN_CTRL_ADR);
+	p->mp_qen_cnt = register_get_field(p->mp_qen_ctrl, QSL_QEN_CTRL_CNT);
+	p->mp_qen_data = module_get_register(p->m_qsl, QSL_QEN_DATA);
+	p->mp_qen_data_en = register_get_field(p->mp_qen_data, QSL_QEN_DATA_EN);
+	/* UNMQ */
+	p->mp_unmq_ctrl = module_get_register(p->m_qsl, QSL_UNMQ_CTRL);
+	p->mp_unmq_addr = register_get_field(p->mp_unmq_ctrl, QSL_UNMQ_CTRL_ADR);
+	p->mp_unmq_cnt = register_get_field(p->mp_unmq_ctrl, QSL_UNMQ_CTRL_CNT);
+	p->mp_unmq_data = module_get_register(p->m_qsl, QSL_UNMQ_DATA);
+	p->mp_unmq_data_dest_queue =
+		register_get_field(p->mp_unmq_data, QSL_UNMQ_DATA_DEST_QUEUE);
+	p->mp_unmq_data_en = register_get_field(p->mp_unmq_data, QSL_UNMQ_DATA_EN);
+
+	if (!p->mp_qst_data_en) {
+		/* changed name from EN to QEN in v0.7 */
+		p->mp_qst_data_en =
+			register_get_field(p->mp_qst_data, QSL_QST_DATA_QEN);
+	}
+
+	/* LTX - not there anymore from v0.7+ */
+	p->mp_ltx_ctrl = module_query_register(p->m_qsl, QSL_LTX_CTRL);
+	if (p->mp_ltx_ctrl) {
+		p->mp_ltx_addr =
+			register_get_field(p->mp_ltx_ctrl, QSL_LTX_CTRL_ADR);
+		p->mp_ltx_cnt = register_get_field(p->mp_ltx_ctrl, QSL_LTX_CTRL_CNT);
+	} else {
+		p->mp_ltx_addr = NULL;
+		p->mp_ltx_cnt = NULL;
+	}
+	p->mp_ltx_data = module_query_register(p->m_qsl, QSL_LTX_DATA);
+	if (p->mp_ltx_data) {
+		p->mp_ltx_data_lr =
+			register_get_field(p->mp_ltx_data, QSL_LTX_DATA_LR);
+		p->mp_ltx_data_tx_port =
+			register_get_field(p->mp_ltx_data, QSL_LTX_DATA_TX_PORT);
+		p->mp_ltx_data_tsa =
+			register_get_field(p->mp_ltx_data, QSL_LTX_DATA_TSA);
+	} else {
+		p->mp_ltx_data_lr = NULL;
+		p->mp_ltx_data_tx_port = NULL;
+		p->mp_ltx_data_tsa = NULL;
+	}
+	return 0;
+}
+
+int qsl_nthw_setup(struct qsl_nthw *p, int n_idx, int n_idx_cnt)
+{
+	(void)p;
+	(void)n_idx;
+	(void)n_idx_cnt;
+
+	return 0;
+}
+
+/* RCP */
+void qsl_nthw_rcp_select(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+};
+
+void qsl_nthw_rcp_cnt(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void qsl_nthw_rcp_discard(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_discard, val);
+}
+
+void qsl_nthw_rcp_drop(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_drop, val);
+}
+
+void qsl_nthw_rcp_tbl_lo(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tbl_lo, val);
+}
+
+void qsl_nthw_rcp_tbl_hi(const struct qsl_nthw *p, uint32_t val)
+
+{
+	field_set_val32(p->mp_rcp_data_tbl_hi, val);
+}
+
+void qsl_nthw_rcp_tbl_idx(const struct qsl_nthw *p, uint32_t val)
+
+{
+	field_set_val32(p->mp_rcp_data_tbl_idx, val);
+}
+
+void qsl_nthw_rcp_tbl_msk(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tbl_msk, val);
+}
+
+void qsl_nthw_rcp_cao(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_cao)
+		field_set_val32(p->mp_rcp_data_cao, val);
+}
+
+void qsl_nthw_rcp_lr(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_lr)
+		field_set_val32(p->mp_rcp_data_lr, val);
+}
+
+void qsl_nthw_rcp_tsa(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_tsa)
+		field_set_val32(p->mp_rcp_data_tsa, val);
+}
+
+void qsl_nthw_rcp_vli(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_rcp_data_vli)
+		field_set_val32(p->mp_rcp_data_vli, val);
+}
+
+void qsl_nthw_rcp_flush(const struct qsl_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+/* LTX */
+void qsl_nthw_ltx_select(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_ltx_addr)
+		field_set_val32(p->mp_ltx_addr, val);
+}
+
+void qsl_nthw_ltx_cnt(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_ltx_addr)
+		field_set_val32(p->mp_ltx_cnt, val);
+}
+
+void qsl_nthw_ltx_lr(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_ltx_data_lr)
+		field_set_val32(p->mp_ltx_data_lr, val);
+}
+
+void qsl_nthw_ltx_tx_port(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_ltx_data_tx_port)
+		field_set_val32(p->mp_ltx_data_tx_port, val);
+}
+
+void qsl_nthw_ltx_tsa(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_ltx_data_tsa)
+		field_set_val32(p->mp_ltx_data_tsa, val);
+};
+
+void qsl_nthw_ltx_flush(const struct qsl_nthw *p)
+{
+	register_flush(p->mp_ltx_ctrl, 1);
+	register_flush(p->mp_ltx_data, 1);
+}
+
+/* QST */
+void qsl_nthw_qst_select(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qst_addr, val);
+}
+
+void qsl_nthw_qst_cnt(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qst_cnt, val);
+}
+
+void qsl_nthw_qst_queue(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qst_data_queue, val);
+}
+
+void qsl_nthw_qst_en(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qst_data_en, val);
+}
+
+void qsl_nthw_qst_tx_port(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_qst_data_tx_port)
+		field_set_val32(p->mp_qst_data_tx_port, val);
+}
+
+void qsl_nthw_qst_lre(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_qst_data_lre)
+		field_set_val32(p->mp_qst_data_lre, val);
+}
+
+void qsl_nthw_qst_tci(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_qst_data_tci)
+		field_set_val32(p->mp_qst_data_tci, val);
+}
+
+void qsl_nthw_qst_ven(const struct qsl_nthw *p, uint32_t val)
+{
+	if (p->mp_qst_data_ven)
+		field_set_val32(p->mp_qst_data_ven, val);
+}
+
+void qsl_nthw_qst_flush(const struct qsl_nthw *p)
+{
+	register_flush(p->mp_qst_ctrl, 1);
+	register_flush(p->mp_qst_data, 1);
+}
+
+/* QEN */
+void qsl_nthw_qen_select(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qen_addr, val);
+}
+
+void qsl_nthw_qen_cnt(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qen_cnt, val);
+}
+
+void qsl_nthw_qen_en(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_qen_data_en, val);
+}
+
+void qsl_nthw_qen_flush(const struct qsl_nthw *p)
+{
+	register_flush(p->mp_qen_ctrl, 1);
+	register_flush(p->mp_qen_data, 1);
+}
+
+/* UNMQ */
+void qsl_nthw_unmq_select(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_unmq_addr, val);
+}
+
+void qsl_nthw_unmq_cnt(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_unmq_cnt, val);
+}
+
+void qsl_nthw_unmq_dest_queue(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_unmq_data_dest_queue, val);
+}
+
+void qsl_nthw_unmq_en(const struct qsl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_unmq_data_en, val);
+}
+
+void qsl_nthw_unmq_flush(const struct qsl_nthw *p)
+{
+	register_flush(p->mp_unmq_ctrl, 1);
+	register_flush(p->mp_unmq_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.h
new file mode 100644
index 0000000000..eeebbcf1c4
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_qsl.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_QSL_H__
+#define __FLOW_NTHW_QSL_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct qsl_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_qsl;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_discard;
+	nt_field_t *mp_rcp_data_drop;
+	nt_field_t *mp_rcp_data_tbl_lo;
+	nt_field_t *mp_rcp_data_tbl_hi;
+	nt_field_t *mp_rcp_data_tbl_idx;
+	nt_field_t *mp_rcp_data_tbl_msk;
+	nt_field_t *mp_rcp_data_cao;
+	nt_field_t *mp_rcp_data_lr;
+	nt_field_t *mp_rcp_data_tsa;
+	nt_field_t *mp_rcp_data_vli;
+
+	nt_register_t *mp_ltx_ctrl;
+	nt_field_t *mp_ltx_addr;
+	nt_field_t *mp_ltx_cnt;
+	nt_register_t *mp_ltx_data;
+	nt_field_t *mp_ltx_data_lr;
+	nt_field_t *mp_ltx_data_tx_port;
+	nt_field_t *mp_ltx_data_tsa;
+
+	nt_register_t *mp_qst_ctrl;
+	nt_field_t *mp_qst_addr;
+	nt_field_t *mp_qst_cnt;
+	nt_register_t *mp_qst_data;
+	nt_field_t *mp_qst_data_queue;
+	nt_field_t *mp_qst_data_en;
+	nt_field_t *mp_qst_data_tx_port;
+	nt_field_t *mp_qst_data_lre;
+	nt_field_t *mp_qst_data_tci;
+	nt_field_t *mp_qst_data_ven;
+
+	nt_register_t *mp_qen_ctrl;
+	nt_field_t *mp_qen_addr;
+	nt_field_t *mp_qen_cnt;
+	nt_register_t *mp_qen_data;
+	nt_field_t *mp_qen_data_en;
+
+	nt_register_t *mp_unmq_ctrl;
+	nt_field_t *mp_unmq_addr;
+	nt_field_t *mp_unmq_cnt;
+	nt_register_t *mp_unmq_data;
+	nt_field_t *mp_unmq_data_dest_queue;
+	nt_field_t *mp_unmq_data_en;
+};
+
+typedef struct qsl_nthw qsl_nthw_t;
+
+struct qsl_nthw *qsl_nthw_new(void);
+void qsl_nthw_delete(struct qsl_nthw *p);
+int qsl_nthw_init(struct qsl_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int qsl_nthw_setup(struct qsl_nthw *p, int n_idx, int n_idx_cnt);
+void qsl_nthw_set_debug_mode(struct qsl_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void qsl_nthw_rcp_select(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_cnt(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_discard(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_drop(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_tbl_lo(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_tbl_hi(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_tbl_idx(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_tbl_msk(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_cao(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_lr(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_tsa(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_vli(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_rcp_flush(const struct qsl_nthw *p);
+
+/* LTX */
+void qsl_nthw_ltx_select(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_ltx_cnt(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_ltx_lr(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_ltx_tx_port(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_ltx_tsa(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_ltx_flush(const struct qsl_nthw *p);
+
+/* QST */
+void qsl_nthw_qst_select(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_cnt(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_queue(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_en(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_tx_port(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_lre(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_tci(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_ven(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qst_flush(const struct qsl_nthw *p);
+
+/* QEN */
+void qsl_nthw_qen_select(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qen_cnt(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qen_en(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_qen_flush(const struct qsl_nthw *p);
+
+/* UNMQ */
+void qsl_nthw_unmq_select(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_unmq_cnt(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_unmq_dest_queue(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_unmq_en(const struct qsl_nthw *p, uint32_t val);
+void qsl_nthw_unmq_flush(const struct qsl_nthw *p);
+
+#endif /* __FLOW_NTHW_QSL_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.c
new file mode 100644
index 0000000000..8f519b7728
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.c
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_rmc.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void rmc_nthw_set_debug_mode(struct rmc_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_rmc, n_debug_mode);
+}
+
+struct rmc_nthw *rmc_nthw_new(void)
+{
+	struct rmc_nthw *p = malloc(sizeof(struct rmc_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void rmc_nthw_delete(struct rmc_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int rmc_nthw_init(struct rmc_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_RMC, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: RMC %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_rmc = p_mod;
+
+	/* CTRL */
+	p->mp_ctrl = module_get_register(p->m_rmc, RMC_CTRL);
+	p->mp_ctrl_block_statt =
+		register_get_field(p->mp_ctrl, RMC_CTRL_BLOCK_STATT);
+	p->mp_ctrl_block_keep_a =
+		register_get_field(p->mp_ctrl, RMC_CTRL_BLOCK_KEEPA);
+	p->mp_ctrl_block_rpp_slice =
+		register_query_field(p->mp_ctrl, RMC_CTRL_BLOCK_RPP_SLICE);
+	p->mp_ctrl_block_mac_port =
+		register_get_field(p->mp_ctrl, RMC_CTRL_BLOCK_MAC_PORT);
+	p->mp_ctrl_lag_phy_odd_even =
+		register_get_field(p->mp_ctrl, RMC_CTRL_LAG_PHY_ODD_EVEN);
+	return 0;
+}
+
+int rmc_nthw_setup(struct rmc_nthw *p, int n_idx, int n_idx_cnt)
+{
+	(void)p;
+	(void)n_idx;
+	(void)n_idx_cnt;
+
+	return 0;
+}
+
+/* CTRL */
+void rmc_nthw_ctrl_block_statt(const struct rmc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ctrl_block_statt, val);
+}
+
+void rmc_nthw_ctrl_block_keep_a(const struct rmc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ctrl_block_keep_a, val);
+}
+
+void rmc_nthw_ctrl_block_rpp_slice(const struct rmc_nthw *p, uint32_t val)
+{
+	if (p->mp_ctrl_block_rpp_slice)
+		field_set_val32(p->mp_ctrl_block_rpp_slice, val);
+}
+
+void rmc_nthw_ctrl_block_mac_port(const struct rmc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ctrl_block_mac_port, val);
+}
+
+void rmc_nthw_ctrl_lag_phy_odd_even(const struct rmc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ctrl_lag_phy_odd_even, val);
+}
+
+void rmc_nthw_ctrl_flush(const struct rmc_nthw *p)
+{
+	register_flush(p->mp_ctrl, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.h
new file mode 100644
index 0000000000..57d5776002
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rmc.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_RMC_H__
+#define __FLOW_NTHW_RMC_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct rmc_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_rmc;
+
+	nt_register_t *mp_ctrl;
+	nt_field_t *mp_ctrl_block_statt;
+	nt_field_t *mp_ctrl_block_keep_a;
+	nt_field_t *mp_ctrl_block_rpp_slice;
+	nt_field_t *mp_ctrl_block_mac_port;
+	nt_field_t *mp_ctrl_lag_phy_odd_even;
+};
+
+struct rmc_nthw *rmc_nthw_new(void);
+void rmc_nthw_delete(struct rmc_nthw *p);
+int rmc_nthw_init(struct rmc_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int rmc_nthw_setup(struct rmc_nthw *p, int n_idx, int n_idx_cnt);
+void rmc_nthw_set_debug_mode(struct rmc_nthw *p, unsigned int n_debug_mode);
+
+/* CTRL */
+void rmc_nthw_ctrl_block_statt(const struct rmc_nthw *p, uint32_t val);
+void rmc_nthw_ctrl_block_keep_a(const struct rmc_nthw *p, uint32_t val);
+void rmc_nthw_ctrl_block_rpp_slice(const struct rmc_nthw *p, uint32_t val);
+void rmc_nthw_ctrl_block_mac_port(const struct rmc_nthw *p, uint32_t val);
+void rmc_nthw_ctrl_lag_phy_odd_even(const struct rmc_nthw *p, uint32_t val);
+void rmc_nthw_ctrl_flush(const struct rmc_nthw *p);
+
+#endif /* __FLOW_NTHW_RMC_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.c
new file mode 100644
index 0000000000..934778f426
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.c
@@ -0,0 +1,294 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_roa.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void roa_nthw_set_debug_mode(struct roa_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_roa, n_debug_mode);
+}
+
+struct roa_nthw *roa_nthw_new(void)
+{
+	struct roa_nthw *p = malloc(sizeof(struct roa_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void roa_nthw_delete(struct roa_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int roa_nthw_init(struct roa_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_ROA, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: ROA %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_roa = p_mod;
+
+	/* TUN HDR */
+	p->mp_tun_hdr_ctrl = module_get_register(p->m_roa, ROA_TUNHDR_CTRL);
+	p->mp_tun_hdr_addr =
+		register_get_field(p->mp_tun_hdr_ctrl, ROA_TUNHDR_CTRL_ADR);
+	p->mp_tun_hdr_cnt =
+		register_get_field(p->mp_tun_hdr_ctrl, ROA_TUNHDR_CTRL_CNT);
+	p->mp_tun_hdr_data = module_get_register(p->m_roa, ROA_TUNHDR_DATA);
+	p->mp_tun_hdr_data_tunnel_hdr =
+		register_get_field(p->mp_tun_hdr_data, ROA_TUNHDR_DATA_TUNNEL_HDR);
+	/* TUN CFG */
+	p->mp_tun_cfg_ctrl = module_get_register(p->m_roa, ROA_TUNCFG_CTRL);
+	p->mp_tun_cfg_addr =
+		register_get_field(p->mp_tun_cfg_ctrl, ROA_TUNCFG_CTRL_ADR);
+	p->mp_tun_cfg_cnt =
+		register_get_field(p->mp_tun_cfg_ctrl, ROA_TUNCFG_CTRL_CNT);
+	p->mp_tun_cfg_data = module_get_register(p->m_roa, ROA_TUNCFG_DATA);
+	p->mp_tun_cfg_data_tun_len =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_LEN);
+	p->mp_tun_cfg_data_tun_type =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_TYPE);
+	p->mp_tun_cfg_data_tun_vlan =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_VLAN);
+	p->mp_tun_cfg_data_ip_type =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_IP_TYPE);
+	p->mp_tun_cfg_data_ipcs_upd =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_IPCS_UPD);
+	p->mp_tun_cfg_data_ipcs_precalc =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_IPCS_PRECALC);
+	p->mp_tun_cfg_data_iptl_upd =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_IPTL_UPD);
+	p->mp_tun_cfg_data_iptl_precalc =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_IPTL_PRECALC);
+	p->mp_tun_cfg_data_vxlan_udp_len_upd =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TUN_VXLAN_UDP_LEN_UPD);
+	p->mp_tun_cfg_data_tx_lag_ix =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_TX_LAG_IX);
+	p->mp_tun_cfg_data_recirculate =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_RECIRCULATE);
+	p->mp_tun_cfg_data_push_tunnel =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_PUSH_TUNNEL);
+	p->mp_tun_cfg_data_recirc_port =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_RECIRC_PORT);
+	p->mp_tun_cfg_data_recirc_bypass =
+		register_get_field(p->mp_tun_cfg_data, ROA_TUNCFG_DATA_RECIRC_BYPASS);
+	/* CONFIG */
+	p->mp_config = module_get_register(p->m_roa, ROA_CONFIG);
+	p->mp_config_fwd_recirculate =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_RECIRCULATE);
+	p->mp_config_fwd_normal_pcks =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_NORMAL_PCKS);
+	p->mp_config_fwd_tx_port0 =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_TXPORT0);
+	p->mp_config_fwd_tx_port1 =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_TXPORT1);
+	p->mp_config_fwd_cell_builder_pcks =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_CELLBUILDER_PCKS);
+	p->mp_config_fwd_non_normal_pcks =
+		register_get_field(p->mp_config, ROA_CONFIG_FWD_NON_NORMAL_PCKS);
+	/* LAG */
+	p->mp_lag_cfg_ctrl = module_get_register(p->m_roa, ROA_LAGCFG_CTRL);
+	p->mp_lag_cfg_addr =
+		register_get_field(p->mp_lag_cfg_ctrl, ROA_LAGCFG_CTRL_ADR);
+	p->mp_lag_cfg_cnt =
+		register_get_field(p->mp_lag_cfg_ctrl, ROA_LAGCFG_CTRL_CNT);
+	p->mp_lag_cfg_data = module_get_register(p->m_roa, ROA_LAGCFG_DATA);
+	p->mp_lag_cfg_data_tx_phy_port =
+		register_get_field(p->mp_lag_cfg_data, ROA_LAGCFG_DATA_TXPHY_PORT);
+
+	return 0;
+}
+
+/* TUN HDR */
+void roa_nthw_tun_hdr_select(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_hdr_addr, val);
+}
+
+void roa_nthw_tun_hdr_cnt(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_hdr_cnt, val);
+}
+
+void roa_nthw_tun_hdr_tunnel_hdr(const struct roa_nthw *p, uint32_t *val)
+{
+	field_set_val(p->mp_tun_hdr_data_tunnel_hdr, val, 4);
+}
+
+void roa_nthw_tun_hdr_flush(const struct roa_nthw *p)
+{
+	register_flush(p->mp_tun_hdr_ctrl, 1);
+	register_flush(p->mp_tun_hdr_data, 1);
+}
+
+/* TUN CFG */
+void roa_nthw_tun_cfg_select(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_addr, val);
+}
+
+void roa_nthw_tun_cfg_cnt(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_cnt, val);
+}
+
+void roa_nthw_tun_cfg_tun_len(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_tun_len, val);
+}
+
+void roa_nthw_tun_cfg_tun_type(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_tun_type, val);
+}
+
+void roa_nthw_tun_cfg_tun_vlan(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_tun_vlan, val);
+}
+
+void roa_nthw_tun_cfg_ip_type(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_ip_type, val);
+}
+
+void roa_nthw_tun_cfg_ipcs_upd(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_ipcs_upd, val);
+}
+
+void roa_nthw_tun_cfg_ipcs_precalc(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_ipcs_precalc, val);
+}
+
+void roa_nthw_tun_cfg_iptl_upd(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_iptl_upd, val);
+}
+
+void roa_nthw_tun_cfg_iptl_precalc(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_iptl_precalc, val);
+}
+
+void roa_nthw_tun_cfg_vxlan_udp_len_upd(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_vxlan_udp_len_upd, val);
+}
+
+void roa_nthw_tun_cfg_tx_lag_ix(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_tx_lag_ix, val);
+};
+
+void roa_nthw_tun_cfg_recirculate(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_recirculate, val);
+}
+
+void roa_nthw_tun_cfg_push_tunnel(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_push_tunnel, val);
+}
+
+void roa_nthw_tun_cfg_recirc_port(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_recirc_port, val);
+}
+
+void roa_nthw_tun_cfg_recirc_bypass(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_tun_cfg_data_recirc_bypass, val);
+}
+
+void roa_nthw_tun_cfg_flush(const struct roa_nthw *p)
+{
+	register_flush(p->mp_tun_cfg_ctrl, 1);
+	register_flush(p->mp_tun_cfg_data, 1);
+}
+
+/* ROA CONFIG */
+void roa_nthw_config_fwd_recirculate(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_recirculate, val);
+}
+
+void roa_nthw_config_fwd_normal_pcks(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_normal_pcks, val);
+}
+
+void roa_nthw_config_fwd_tx_port0(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_tx_port0, val);
+}
+
+void roa_nthw_config_fwd_tx_port1(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_tx_port1, val);
+}
+
+void roa_nthw_config_fwd_cell_builder_pcks(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_cell_builder_pcks, val);
+}
+
+void roa_nthw_config_fwd_non_normal_pcks(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_config_fwd_non_normal_pcks, val);
+}
+
+void roa_nthw_config_flush(const struct roa_nthw *p)
+{
+	register_flush(p->mp_config, 1);
+}
+
+/* LAG */
+void roa_nthw_lag_cfg_select(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_lag_cfg_addr, val);
+}
+
+void roa_nthw_lag_cfg_cnt(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_lag_cfg_cnt, val);
+}
+
+void roa_nthw_lag_cfg_tx_phy_port(const struct roa_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_lag_cfg_data_tx_phy_port, val);
+}
+
+void roa_nthw_lag_cfg_flush(const struct roa_nthw *p)
+{
+	register_flush(p->mp_lag_cfg_ctrl, 1);
+	register_flush(p->mp_lag_cfg_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.h
new file mode 100644
index 0000000000..9398ef5ae9
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_roa.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_ROA_H__
+#define __FLOW_NTHW_ROA_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct roa_nthw;
+
+typedef struct roa_nthw roa_nthw_t;
+
+struct roa_nthw *roa_nthw_new(void);
+void roa_nthw_delete(struct roa_nthw *p);
+int roa_nthw_init(struct roa_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int roa_nthw_setup(struct roa_nthw *p, int n_idx, int n_idx_cnt);
+void roa_nthw_set_debug_mode(struct roa_nthw *p, unsigned int n_debug_mode);
+
+/* TUN HDR */
+void roa_nthw_tun_hdr_select(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_hdr_cnt(const struct roa_nthw *p, uint32_t val);
+
+void roa_nthw_tun_hdr_tunnel_hdr(const struct roa_nthw *p, uint32_t *val);
+void roa_nthw_tun_hdr_flush(const struct roa_nthw *p);
+
+/* TUN CFG */
+void roa_nthw_tun_cfg_select(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_cnt(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_tun_len(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_tun_type(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_tun_vlan(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_ip_type(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_ipcs_upd(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_ipcs_precalc(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_iptl_upd(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_iptl_precalc(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_vxlan_udp_len_upd(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_tx_lag_ix(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_recirculate(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_push_tunnel(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_recirc_port(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_recirc_bypass(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_tun_cfg_flush(const struct roa_nthw *p);
+
+/* ROA CONFIG */
+void roa_nthw_config_fwd_recirculate(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_fwd_normal_pcks(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_fwd_tx_port0(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_fwd_tx_port1(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_fwd_cell_builder_pcks(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_fwd_non_normal_pcks(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_config_flush(const struct roa_nthw *p);
+
+/* LAG */
+void roa_nthw_lag_cfg_select(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_lag_cfg_cnt(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_lag_cfg_tx_phy_port(const struct roa_nthw *p, uint32_t val);
+void roa_nthw_lag_cfg_flush(const struct roa_nthw *p);
+
+struct roa_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_roa;
+
+	nt_register_t *mp_tun_hdr_ctrl;
+	nt_field_t *mp_tun_hdr_addr;
+	nt_field_t *mp_tun_hdr_cnt;
+	nt_register_t *mp_tun_hdr_data;
+	nt_field_t *mp_tun_hdr_data_tunnel_hdr;
+
+	nt_register_t *mp_tun_cfg_ctrl;
+	nt_field_t *mp_tun_cfg_addr;
+	nt_field_t *mp_tun_cfg_cnt;
+	nt_register_t *mp_tun_cfg_data;
+	nt_field_t *mp_tun_cfg_data_tun_len;
+	nt_field_t *mp_tun_cfg_data_tun_type;
+	nt_field_t *mp_tun_cfg_data_tun_vlan;
+	nt_field_t *mp_tun_cfg_data_ip_type;
+	nt_field_t *mp_tun_cfg_data_ipcs_upd;
+	nt_field_t *mp_tun_cfg_data_ipcs_precalc;
+	nt_field_t *mp_tun_cfg_data_iptl_upd;
+	nt_field_t *mp_tun_cfg_data_iptl_precalc;
+	nt_field_t *mp_tun_cfg_data_vxlan_udp_len_upd;
+	nt_field_t *mp_tun_cfg_data_tx_lag_ix;
+	nt_field_t *mp_tun_cfg_data_recirculate;
+	nt_field_t *mp_tun_cfg_data_push_tunnel;
+	nt_field_t *mp_tun_cfg_data_recirc_port;
+	nt_field_t *mp_tun_cfg_data_recirc_bypass;
+
+	nt_register_t *mp_config;
+	nt_field_t *mp_config_fwd_recirculate;
+	nt_field_t *mp_config_fwd_normal_pcks;
+	nt_field_t *mp_config_fwd_tx_port0;
+	nt_field_t *mp_config_fwd_tx_port1;
+	nt_field_t *mp_config_fwd_cell_builder_pcks;
+	nt_field_t *mp_config_fwd_non_normal_pcks;
+
+	nt_register_t *mp_lag_cfg_ctrl;
+	nt_field_t *mp_lag_cfg_addr;
+	nt_field_t *mp_lag_cfg_cnt;
+	nt_register_t *mp_lag_cfg_data;
+	nt_field_t *mp_lag_cfg_data_tx_phy_port;
+};
+
+#endif /* __FLOW_NTHW_ROA_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.c
new file mode 100644
index 0000000000..2ce3ce6cf8
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.c
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_rpp_lr.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void rpp_lr_nthw_set_debug_mode(struct rpp_lr_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_rpp_lr, n_debug_mode);
+}
+
+struct rpp_lr_nthw *rpp_lr_nthw_new(void)
+{
+	struct rpp_lr_nthw *p = malloc(sizeof(struct rpp_lr_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void rpp_lr_nthw_delete(struct rpp_lr_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int rpp_lr_nthw_init(struct rpp_lr_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_RPP_LR, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: RppLr %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_rpp_lr = fpga_query_module(p_fpga, MOD_RPP_LR, n_instance);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_rpp_lr, RPP_LR_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, RPP_LR_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, RPP_LR_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_rpp_lr, RPP_LR_RCP_DATA);
+	p->mp_rcp_data_exp = register_get_field(p->mp_rcp_data, RPP_LR_RCP_DATA_EXP);
+
+	p->mp_ifr_rcp_ctrl = module_query_register(p->m_rpp_lr, RPP_LR_IFR_RCP_CTRL);
+	p->mp_ifr_rcp_addr =
+		register_query_field(p->mp_ifr_rcp_ctrl, RPP_LR_IFR_RCP_CTRL_ADR);
+	p->mp_ifr_rcp_cnt =
+		register_query_field(p->mp_ifr_rcp_ctrl, RPP_LR_IFR_RCP_CTRL_CNT);
+	p->mp_ifr_rcp_data = module_query_register(p->m_rpp_lr, RPP_LR_IFR_RCP_DATA);
+	p->mp_ifr_rcp_data_en =
+		register_query_field(p->mp_ifr_rcp_data, RPP_LR_IFR_RCP_DATA_EN);
+	p->mp_ifr_rcp_data_mtu =
+		register_query_field(p->mp_ifr_rcp_data, RPP_LR_IFR_RCP_DATA_MTU);
+
+	return 0;
+}
+
+void rpp_lr_nthw_rcp_select(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_addr);
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void rpp_lr_nthw_rcp_cnt(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_cnt);
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void rpp_lr_nthw_rcp_exp(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_rcp_data_exp);
+	field_set_val32(p->mp_rcp_data_exp, val);
+}
+
+void rpp_lr_nthw_rcp_flush(const struct rpp_lr_nthw *p)
+{
+	assert(p->mp_rcp_ctrl);
+	assert(p->mp_rcp_data);
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+void rpp_lr_nthw_ifr_rcp_select(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_ifr_rcp_addr);
+	field_set_val32(p->mp_ifr_rcp_addr, val);
+}
+
+void rpp_lr_nthw_ifr_rcp_cnt(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_ifr_rcp_cnt);
+	field_set_val32(p->mp_ifr_rcp_cnt, val);
+}
+
+void rpp_lr_nthw_ifr_rcp_en(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_ifr_rcp_data_en);
+	field_set_val32(p->mp_ifr_rcp_data_en, val);
+}
+
+void rpp_lr_nthw_ifr_rcp_mtu(const struct rpp_lr_nthw *p, uint32_t val)
+{
+	assert(p->mp_ifr_rcp_data_mtu);
+	field_set_val32(p->mp_ifr_rcp_data_mtu, val);
+}
+
+void rpp_lr_nthw_ifr_rcp_flush(const struct rpp_lr_nthw *p)
+{
+	assert(p->mp_ifr_rcp_ctrl);
+	assert(p->mp_ifr_rcp_data);
+	register_flush(p->mp_ifr_rcp_ctrl, 1);
+	register_flush(p->mp_ifr_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.h
new file mode 100644
index 0000000000..e442c9d8d2
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_rpp_lr.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_RPP_LR_H__
+#define __FLOW_NTHW_RPP_LR_H__
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct rpp_lr_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_rpp_lr;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_exp;
+
+	nt_register_t *mp_ifr_rcp_ctrl;
+	nt_field_t *mp_ifr_rcp_addr;
+	nt_field_t *mp_ifr_rcp_cnt;
+
+	nt_register_t *mp_ifr_rcp_data;
+	nt_field_t *mp_ifr_rcp_data_en;
+	nt_field_t *mp_ifr_rcp_data_mtu;
+};
+
+struct rpp_lr_nthw *rpp_lr_nthw_new(void);
+void rpp_lr_nthw_delete(struct rpp_lr_nthw *p);
+int rpp_lr_nthw_init(struct rpp_lr_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int rpp_lr_nthw_setup(struct rpp_lr_nthw *p, int n_idx, int n_idx_cnt);
+void rpp_lr_nthw_set_debug_mode(struct rpp_lr_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void rpp_lr_nthw_rcp_select(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_rcp_cnt(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_rcp_exp(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_rcp_flush(const struct rpp_lr_nthw *p);
+
+/* RCP IFR */
+void rpp_lr_nthw_ifr_rcp_select(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_ifr_rcp_cnt(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_ifr_rcp_en(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_ifr_rcp_mtu(const struct rpp_lr_nthw *p, uint32_t val);
+void rpp_lr_nthw_ifr_rcp_flush(const struct rpp_lr_nthw *p);
+
+#endif /* __FLOW_NTHW_RPP_LR_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.c
new file mode 100644
index 0000000000..a409e68869
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_slc.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void slc_nthw_set_debug_mode(struct slc_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_slc, n_debug_mode);
+}
+
+struct slc_nthw *slc_nthw_new(void)
+{
+	struct slc_nthw *p = malloc(sizeof(struct slc_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void slc_nthw_delete(struct slc_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int slc_nthw_init(struct slc_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_SLC, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Slc %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_slc = fpga_query_module(p_fpga, MOD_SLC, n_instance);
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_slc, SLC_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, SLC_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, SLC_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_slc, SLC_RCP_DATA);
+	p->mp_rcp_data_tail_slc_en =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_SLC_EN);
+	p->mp_rcp_data_tail_dyn =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_DYN);
+	p->mp_rcp_data_tail_ofs =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_OFS);
+	p->mp_rcp_data_pcap = register_get_field(p->mp_rcp_data, SLC_RCP_DATA_PCAP);
+
+	return 0;
+}
+
+/* RCP */
+void slc_nthw_rcp_select(const struct slc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void slc_nthw_rcp_cnt(const struct slc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void slc_nthw_rcp_tail_slc_en(const struct slc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_slc_en, val);
+}
+
+void slc_nthw_rcp_tail_dyn(const struct slc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_dyn, val);
+}
+
+void slc_nthw_rcp_tail_ofs(const struct slc_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_ofs, val);
+}
+
+void slc_nthw_rcp_pcap(const struct slc_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_pcap, val);
+}
+
+void slc_nthw_rcp_flush(const struct slc_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.h
new file mode 100644
index 0000000000..e0f58e27e4
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_SLC_H__
+#define __FLOW_NTHW_SLC_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct slc_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_slc;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+
+	nt_field_t *mp_rcp_data_tail_slc_en;
+	nt_field_t *mp_rcp_data_tail_dyn;
+	nt_field_t *mp_rcp_data_tail_ofs;
+	nt_field_t *mp_rcp_data_pcap;
+};
+
+typedef struct slc_nthw slc_nthw_t;
+
+struct slc_nthw *slc_nthw_new(void);
+void slc_nthw_delete(struct slc_nthw *p);
+int slc_nthw_init(struct slc_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int slc_nthw_setup(struct slc_nthw *p, int n_idx, int n_idx_cnt);
+void slc_nthw_set_debug_mode(struct slc_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void slc_nthw_rcp_select(const struct slc_nthw *p, uint32_t val);
+void slc_nthw_rcp_cnt(const struct slc_nthw *p, uint32_t val);
+void slc_nthw_rcp_tail_slc_en(const struct slc_nthw *p, uint32_t val);
+void slc_nthw_rcp_tail_dyn(const struct slc_nthw *p, uint32_t val);
+void slc_nthw_rcp_tail_ofs(const struct slc_nthw *p, int32_t val);
+void slc_nthw_rcp_pcap(const struct slc_nthw *p, uint32_t val);
+void slc_nthw_rcp_flush(const struct slc_nthw *p);
+
+#endif /* __FLOW_NTHW_SLC_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.c
new file mode 100644
index 0000000000..f106974bdd
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.c
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_slc_lr.h"
+
+#include <stdlib.h> /* malloc */
+#include <string.h> /* memset */
+
+void slc_lr_nthw_set_debug_mode(struct slc_lr_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_slc_lr, n_debug_mode);
+}
+
+struct slc_lr_nthw *slc_lr_nthw_new(void)
+{
+	struct slc_lr_nthw *p = malloc(sizeof(struct slc_lr_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+
+	return p;
+}
+
+void slc_lr_nthw_delete(struct slc_lr_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int slc_lr_nthw_init(struct slc_lr_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_SLC_LR, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: Slc %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_slc_lr = fpga_query_module(p_fpga, MOD_SLC_LR, n_instance);
+
+	/* RCP */
+	p->mp_rcp_ctrl = module_get_register(p->m_slc_lr, SLC_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, SLC_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, SLC_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_slc_lr, SLC_RCP_DATA);
+	p->mp_rcp_data_tail_slc_en =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_SLC_EN);
+	p->mp_rcp_data_tail_dyn =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_DYN);
+	p->mp_rcp_data_tail_ofs =
+		register_get_field(p->mp_rcp_data, SLC_RCP_DATA_TAIL_OFS);
+	p->mp_rcp_data_pcap = register_get_field(p->mp_rcp_data, SLC_RCP_DATA_PCAP);
+
+	return 0;
+}
+
+/* RCP */
+void slc_lr_nthw_rcp_select(const struct slc_lr_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void slc_lr_nthw_rcp_cnt(const struct slc_lr_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void slc_lr_nthw_rcp_tail_slc_en(const struct slc_lr_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_slc_en, val);
+}
+
+void slc_lr_nthw_rcp_tail_dyn(const struct slc_lr_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_dyn, val);
+}
+
+void slc_lr_nthw_rcp_tail_ofs(const struct slc_lr_nthw *p, int32_t val)
+{
+	field_set_val32(p->mp_rcp_data_tail_ofs, val);
+}
+
+void slc_lr_nthw_rcp_pcap(const struct slc_lr_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_pcap, val);
+}
+
+void slc_lr_nthw_rcp_flush(const struct slc_lr_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.h
new file mode 100644
index 0000000000..533f2efbeb
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_slc_lr.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_SLC_LR_H__
+#define __FLOW_NTHW_SLC_LR_H__
+
+#include <stdint.h> /* uint32_t */
+#include "nthw_fpga_model.h"
+
+struct slc_lr_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_slc_lr;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+	nt_register_t *mp_rcp_data;
+
+	nt_field_t *mp_rcp_data_tail_slc_en;
+	nt_field_t *mp_rcp_data_tail_dyn;
+	nt_field_t *mp_rcp_data_tail_ofs;
+	nt_field_t *mp_rcp_data_pcap;
+};
+
+typedef struct slc_lr_nthw slc_lr_nthw_t;
+
+struct slc_lr_nthw *slc_lr_nthw_new(void);
+void slc_lr_nthw_delete(struct slc_lr_nthw *p);
+int slc_lr_nthw_init(struct slc_lr_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int slc_lr_nthw_setup(struct slc_lr_nthw *p, int n_idx, int n_idx_cnt);
+void slc_lr_nthw_set_debug_mode(struct slc_lr_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void slc_lr_nthw_rcp_select(const struct slc_lr_nthw *p, uint32_t val);
+void slc_lr_nthw_rcp_cnt(const struct slc_lr_nthw *p, uint32_t val);
+void slc_lr_nthw_rcp_tail_slc_en(const struct slc_lr_nthw *p, uint32_t val);
+void slc_lr_nthw_rcp_tail_dyn(const struct slc_lr_nthw *p, uint32_t val);
+void slc_lr_nthw_rcp_tail_ofs(const struct slc_lr_nthw *p, int32_t val);
+void slc_lr_nthw_rcp_pcap(const struct slc_lr_nthw *p, uint32_t val);
+void slc_lr_nthw_rcp_flush(const struct slc_lr_nthw *p);
+
+#endif /* __FLOW_NTHW_SLC_LR_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.c
new file mode 100644
index 0000000000..4d28d8cc3d
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.c
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_tx_cpy.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void tx_cpy_nthw_set_debug_mode(struct tx_cpy_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_tx_cpy, n_debug_mode);
+}
+
+struct tx_cpy_nthw *tx_cpy_nthw_new(void)
+{
+	struct tx_cpy_nthw *p = malloc(sizeof(struct tx_cpy_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void tx_cpy_nthw_delete(struct tx_cpy_nthw *p)
+{
+	if (p) {
+		free(p->m_writers);
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int tx_cpy_nthw_init(struct tx_cpy_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_TX_CPY, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: TxCpy %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_tx_cpy = fpga_query_module(p_fpga, MOD_TX_CPY, n_instance);
+
+	const int writers_cnt =
+		fpga_get_product_param(p->mp_fpga, NT_TX_CPY_WRITERS, 0);
+	if (writers_cnt < 1)
+		return -1;
+
+	p->m_writers_cnt = (unsigned int)writers_cnt;
+	p->m_writers = calloc(p->m_writers_cnt, sizeof(struct tx_cpy_writers_s));
+	if (p->m_writers == NULL)
+		return -1;
+
+	const int variant =
+		fpga_get_product_param(p->mp_fpga, NT_TX_CPY_VARIANT, 0);
+
+	switch (p->m_writers_cnt) {
+	default:
+	case 6:
+		p->m_writers[5].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER5_CTRL);
+		p->m_writers[5].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[5].mp_writer_ctrl,
+					   CPY_WRITER5_CTRL_ADR);
+		p->m_writers[5].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[5].mp_writer_ctrl,
+					   CPY_WRITER5_CTRL_CNT);
+		p->m_writers[5].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER5_DATA);
+		p->m_writers[5].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[5].mp_writer_data,
+					  CPY_WRITER5_DATA_READER_SELECT);
+		p->m_writers[5].mp_writer_data_dyn =
+			register_get_field(p->m_writers[5].mp_writer_data,
+					   CPY_WRITER5_DATA_DYN);
+		p->m_writers[5].mp_writer_data_ofs =
+			register_get_field(p->m_writers[5].mp_writer_data,
+					   CPY_WRITER5_DATA_OFS);
+		p->m_writers[5].mp_writer_data_len =
+			register_get_field(p->m_writers[5].mp_writer_data,
+					   CPY_WRITER5_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[5].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[5].mp_writer_data,
+						   CPY_WRITER5_DATA_MASK_POINTER);
+			p->m_writers[5].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER5_MASK_CTRL);
+			p->m_writers[5].mp_writer_mask_ctrl_addr =
+				register_get_field(p->m_writers[5].mp_writer_mask_ctrl,
+						   CPY_WRITER5_MASK_CTRL_ADR);
+			p->m_writers[5].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[5].mp_writer_mask_ctrl,
+						   CPY_WRITER5_MASK_CTRL_CNT);
+			p->m_writers[5].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER5_MASK_DATA);
+			p->m_writers[5].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[5].mp_writer_mask_data,
+						   CPY_WRITER5_MASK_DATA_BYTE_MASK);
+		}
+	/* Fallthrough */
+	case 5:
+		p->m_writers[4].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER4_CTRL);
+		p->m_writers[4].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[4].mp_writer_ctrl, CPY_WRITER4_CTRL_ADR);
+		p->m_writers[4].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[4].mp_writer_ctrl, CPY_WRITER4_CTRL_CNT);
+		p->m_writers[4].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER4_DATA);
+		p->m_writers[4].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[4].mp_writer_data,
+					   CPY_WRITER4_DATA_READER_SELECT);
+		p->m_writers[4].mp_writer_data_dyn =
+			register_get_field(p->m_writers[4].mp_writer_data, CPY_WRITER4_DATA_DYN);
+		p->m_writers[4].mp_writer_data_ofs =
+			register_get_field(p->m_writers[4].mp_writer_data, CPY_WRITER4_DATA_OFS);
+		p->m_writers[4].mp_writer_data_len =
+			register_get_field(p->m_writers[4].mp_writer_data, CPY_WRITER4_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[4].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[4].mp_writer_data,
+						   CPY_WRITER4_DATA_MASK_POINTER);
+			p->m_writers[4].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER4_MASK_CTRL);
+			p->m_writers[4].mp_writer_mask_ctrl_addr =
+				register_get_field(p->m_writers[4].mp_writer_mask_ctrl,
+						   CPY_WRITER4_MASK_CTRL_ADR);
+			p->m_writers[4].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[4].mp_writer_mask_ctrl,
+						   CPY_WRITER4_MASK_CTRL_CNT);
+			p->m_writers[4].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER4_MASK_DATA);
+			p->m_writers[4].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[4].mp_writer_mask_data,
+						   CPY_WRITER4_MASK_DATA_BYTE_MASK);
+		}
+	/* Fallthrough */
+	case 4:
+		p->m_writers[3].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER3_CTRL);
+		p->m_writers[3].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[3].mp_writer_ctrl, CPY_WRITER3_CTRL_ADR);
+		p->m_writers[3].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[3].mp_writer_ctrl, CPY_WRITER3_CTRL_CNT);
+		p->m_writers[3].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER3_DATA);
+		p->m_writers[3].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[3].mp_writer_data,
+					   CPY_WRITER3_DATA_READER_SELECT);
+		p->m_writers[3].mp_writer_data_dyn =
+			register_get_field(p->m_writers[3].mp_writer_data, CPY_WRITER3_DATA_DYN);
+		p->m_writers[3].mp_writer_data_ofs =
+			register_get_field(p->m_writers[3].mp_writer_data, CPY_WRITER3_DATA_OFS);
+		p->m_writers[3].mp_writer_data_len =
+			register_get_field(p->m_writers[3].mp_writer_data, CPY_WRITER3_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[3].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[3].mp_writer_data,
+						   CPY_WRITER3_DATA_MASK_POINTER);
+			p->m_writers[3].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER3_MASK_CTRL);
+			p->m_writers[3].mp_writer_mask_ctrl_addr =
+				register_get_field(p->m_writers[3].mp_writer_mask_ctrl,
+						   CPY_WRITER3_MASK_CTRL_ADR);
+			p->m_writers[3].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[3].mp_writer_mask_ctrl,
+						   CPY_WRITER3_MASK_CTRL_CNT);
+			p->m_writers[3].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER3_MASK_DATA);
+			p->m_writers[3].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[3].mp_writer_mask_data,
+						   CPY_WRITER3_MASK_DATA_BYTE_MASK);
+		}
+	/* Fallthrough */
+	case 3:
+		p->m_writers[2].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER2_CTRL);
+		p->m_writers[2].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[2].mp_writer_ctrl, CPY_WRITER2_CTRL_ADR);
+		p->m_writers[2].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[2].mp_writer_ctrl, CPY_WRITER2_CTRL_CNT);
+		p->m_writers[2].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER2_DATA);
+		p->m_writers[2].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[2].mp_writer_data,
+					   CPY_WRITER2_DATA_READER_SELECT);
+		p->m_writers[2].mp_writer_data_dyn =
+			register_get_field(p->m_writers[2].mp_writer_data, CPY_WRITER2_DATA_DYN);
+		p->m_writers[2].mp_writer_data_ofs =
+			register_get_field(p->m_writers[2].mp_writer_data, CPY_WRITER2_DATA_OFS);
+		p->m_writers[2].mp_writer_data_len =
+			register_get_field(p->m_writers[2].mp_writer_data, CPY_WRITER2_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[2].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[2].mp_writer_data,
+						   CPY_WRITER2_DATA_MASK_POINTER);
+			p->m_writers[2].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER2_MASK_CTRL);
+			p->m_writers[2].mp_writer_mask_ctrl_addr =
+				register_get_field(p->m_writers[2].mp_writer_mask_ctrl,
+						   CPY_WRITER2_MASK_CTRL_ADR);
+			p->m_writers[2].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[2].mp_writer_mask_ctrl,
+						   CPY_WRITER2_MASK_CTRL_CNT);
+			p->m_writers[2].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER2_MASK_DATA);
+			p->m_writers[2].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[2].mp_writer_mask_data,
+						   CPY_WRITER2_MASK_DATA_BYTE_MASK);
+		}
+	/* Fallthrough */
+	case 2:
+		p->m_writers[1].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER1_CTRL);
+		p->m_writers[1].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[1].mp_writer_ctrl, CPY_WRITER1_CTRL_ADR);
+		p->m_writers[1].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[1].mp_writer_ctrl, CPY_WRITER1_CTRL_CNT);
+		p->m_writers[1].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER1_DATA);
+		p->m_writers[1].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[1].mp_writer_data,
+					   CPY_WRITER1_DATA_READER_SELECT);
+		p->m_writers[1].mp_writer_data_dyn =
+			register_get_field(p->m_writers[1].mp_writer_data, CPY_WRITER1_DATA_DYN);
+		p->m_writers[1].mp_writer_data_ofs =
+			register_get_field(p->m_writers[1].mp_writer_data, CPY_WRITER1_DATA_OFS);
+		p->m_writers[1].mp_writer_data_len =
+			register_get_field(p->m_writers[1].mp_writer_data, CPY_WRITER1_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[1].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[1].mp_writer_data,
+						   CPY_WRITER1_DATA_MASK_POINTER);
+			p->m_writers[1].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER1_MASK_CTRL);
+			p->m_writers[1].mp_writer_mask_ctrl_addr =
+				register_get_field(p->m_writers[1].mp_writer_mask_ctrl,
+						   CPY_WRITER1_MASK_CTRL_ADR);
+			p->m_writers[1].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[1].mp_writer_mask_ctrl,
+						   CPY_WRITER1_MASK_CTRL_CNT);
+			p->m_writers[1].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER1_MASK_DATA);
+			p->m_writers[1].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[1].mp_writer_mask_data,
+						   CPY_WRITER1_MASK_DATA_BYTE_MASK);
+		}
+	/* Fallthrough */
+	case 1:
+		p->m_writers[0].mp_writer_ctrl =
+			module_get_register(p->m_tx_cpy, CPY_WRITER0_CTRL);
+		p->m_writers[0].mp_writer_ctrl_addr =
+			register_get_field(p->m_writers[0].mp_writer_ctrl, CPY_WRITER0_CTRL_ADR);
+		p->m_writers[0].mp_writer_ctrl_cnt =
+			register_get_field(p->m_writers[0].mp_writer_ctrl, CPY_WRITER0_CTRL_CNT);
+		p->m_writers[0].mp_writer_data =
+			module_get_register(p->m_tx_cpy, CPY_WRITER0_DATA);
+		p->m_writers[0].mp_writer_data_reader_select =
+			register_get_field(p->m_writers[0].mp_writer_data,
+					   CPY_WRITER0_DATA_READER_SELECT);
+		p->m_writers[0].mp_writer_data_dyn =
+			register_get_field(p->m_writers[0].mp_writer_data, CPY_WRITER0_DATA_DYN);
+		p->m_writers[0].mp_writer_data_ofs =
+			register_get_field(p->m_writers[0].mp_writer_data, CPY_WRITER0_DATA_OFS);
+		p->m_writers[0].mp_writer_data_len =
+			register_get_field(p->m_writers[0].mp_writer_data, CPY_WRITER0_DATA_LEN);
+		if (variant != 0) {
+			p->m_writers[0].mp_writer_data_mask_pointer =
+				register_get_field(p->m_writers[0].mp_writer_data,
+						   CPY_WRITER0_DATA_MASK_POINTER);
+			p->m_writers[0].mp_writer_mask_ctrl =
+				module_get_register(p->m_tx_cpy, CPY_WRITER0_MASK_CTRL);
+			p->m_writers[0].mp_writer_mask_ctrl_addr =
+			 register_get_field(p->m_writers[0].mp_writer_mask_ctrl,
+					    CPY_WRITER0_MASK_CTRL_ADR);
+			p->m_writers[0].mp_writer_mask_ctrl_cnt =
+				register_get_field(p->m_writers[0].mp_writer_mask_ctrl,
+						   CPY_WRITER0_MASK_CTRL_CNT);
+			p->m_writers[0].mp_writer_mask_data =
+				module_get_register(p->m_tx_cpy, CPY_WRITER0_MASK_DATA);
+			p->m_writers[0].mp_writer_mask_data_byte_mask =
+				register_get_field(p->m_writers[0].mp_writer_mask_data,
+						   CPY_WRITER0_MASK_DATA_BYTE_MASK);
+		}
+		break;
+	case 0:
+		return -1;
+	}
+
+	return 0;
+}
+
+void tx_cpy_nthw_writer_select(const struct tx_cpy_nthw *p, unsigned int index,
+			    uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_ctrl_addr, val);
+}
+
+void tx_cpy_nthw_writer_cnt(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_ctrl_cnt, val);
+}
+
+void tx_cpy_nthw_writer_reader_select(const struct tx_cpy_nthw *p, unsigned int index,
+				  uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_data_reader_select, val);
+}
+
+void tx_cpy_nthw_writer_dyn(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_data_dyn, val);
+}
+
+void tx_cpy_nthw_writer_ofs(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_data_ofs, val);
+}
+
+void tx_cpy_nthw_writer_len(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_data_len, val);
+}
+
+void tx_cpy_nthw_writer_mask_pointer(const struct tx_cpy_nthw *p, unsigned int index,
+				 uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	assert(p->m_writers[index].mp_writer_data_mask_pointer);
+	field_set_val32(p->m_writers[index].mp_writer_data_mask_pointer, val);
+}
+
+void tx_cpy_nthw_writer_flush(const struct tx_cpy_nthw *p, unsigned int index)
+{
+	assert(index < p->m_writers_cnt);
+	register_flush(p->m_writers[index].mp_writer_ctrl, 1);
+	register_flush(p->m_writers[index].mp_writer_data, 1);
+}
+
+void tx_cpy_nthw_writer_mask_select(const struct tx_cpy_nthw *p, unsigned int index,
+				uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	assert(p->m_writers[index].mp_writer_mask_ctrl_addr);
+	field_set_val32(p->m_writers[index].mp_writer_mask_ctrl_addr, val);
+}
+
+void tx_cpy_nthw_writer_mask_cnt(const struct tx_cpy_nthw *p, unsigned int index,
+			     uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	assert(p->m_writers[index].mp_writer_mask_ctrl_cnt);
+	field_set_val32(p->m_writers[index].mp_writer_mask_ctrl_cnt, val);
+}
+
+void tx_cpy_nthw_writer_mask(const struct tx_cpy_nthw *p, unsigned int index,
+			  uint32_t val)
+{
+	assert(index < p->m_writers_cnt);
+	assert(p->m_writers[index].mp_writer_mask_data_byte_mask);
+	field_set_val32(p->m_writers[index].mp_writer_mask_data_byte_mask, val);
+}
+
+void tx_cpy_nthw_writer_mask_flush(const struct tx_cpy_nthw *p, unsigned int index)
+{
+	assert(index < p->m_writers_cnt);
+	assert(p->m_writers[index].mp_writer_mask_ctrl);
+	assert(p->m_writers[index].mp_writer_mask_data);
+	register_flush(p->m_writers[index].mp_writer_mask_ctrl, 1);
+	register_flush(p->m_writers[index].mp_writer_mask_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.h
new file mode 100644
index 0000000000..f97983b29a
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_cpy.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_TX_CPY_H__
+#define __FLOW_NTHW_TX_CPY_H__
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct tx_cpy_writers_s {
+	nt_register_t *mp_writer_ctrl;
+	nt_field_t *mp_writer_ctrl_addr;
+	nt_field_t *mp_writer_ctrl_cnt;
+
+	nt_register_t *mp_writer_data;
+	nt_field_t *mp_writer_data_reader_select;
+	nt_field_t *mp_writer_data_dyn;
+	nt_field_t *mp_writer_data_ofs;
+	nt_field_t *mp_writer_data_len;
+	nt_field_t *mp_writer_data_mask_pointer;
+
+	nt_register_t *mp_writer_mask_ctrl;
+	nt_field_t *mp_writer_mask_ctrl_addr;
+	nt_field_t *mp_writer_mask_ctrl_cnt;
+
+	nt_register_t *mp_writer_mask_data;
+	nt_field_t *mp_writer_mask_data_byte_mask;
+};
+
+struct tx_cpy_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_tx_cpy;
+
+	unsigned int m_writers_cnt;
+	struct tx_cpy_writers_s *m_writers;
+};
+
+struct tx_cpy_nthw *tx_cpy_nthw_new(void);
+void tx_cpy_nthw_delete(struct tx_cpy_nthw *p);
+int tx_cpy_nthw_init(struct tx_cpy_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int tx_cpy_nthw_setup(struct tx_cpy_nthw *p, int n_idx, int n_idx_cnt);
+void tx_cpy_nthw_set_debug_mode(struct tx_cpy_nthw *p, unsigned int n_debug_mode);
+
+void tx_cpy_nthw_writer_select(const struct tx_cpy_nthw *p, unsigned int index,
+			    uint32_t val);
+void tx_cpy_nthw_writer_cnt(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val);
+void tx_cpy_nthw_writer_reader_select(const struct tx_cpy_nthw *p, unsigned int index,
+				  uint32_t val);
+void tx_cpy_nthw_writer_dyn(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val);
+void tx_cpy_nthw_writer_ofs(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val);
+void tx_cpy_nthw_writer_len(const struct tx_cpy_nthw *p, unsigned int index,
+			 uint32_t val);
+void tx_cpy_nthw_writer_mask_pointer(const struct tx_cpy_nthw *p, unsigned int index,
+				 uint32_t val);
+void tx_cpy_nthw_writer_flush(const struct tx_cpy_nthw *p, unsigned int index);
+
+void tx_cpy_nthw_writer_mask_select(const struct tx_cpy_nthw *p, unsigned int index,
+				uint32_t val);
+void tx_cpy_nthw_writer_mask_cnt(const struct tx_cpy_nthw *p, unsigned int index,
+			     uint32_t val);
+void tx_cpy_nthw_writer_mask(const struct tx_cpy_nthw *p, unsigned int index,
+			  uint32_t val);
+void tx_cpy_nthw_writer_mask_flush(const struct tx_cpy_nthw *p, unsigned int index);
+
+#endif /* __FLOW_NTHW_TX_CPY_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.c
new file mode 100644
index 0000000000..998c3613ee
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.c
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_tx_ins.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void tx_ins_nthw_set_debug_mode(struct tx_ins_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_tx_ins, n_debug_mode);
+}
+
+struct tx_ins_nthw *tx_ins_nthw_new(void)
+{
+	struct tx_ins_nthw *p = malloc(sizeof(struct tx_ins_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void tx_ins_nthw_delete(struct tx_ins_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int tx_ins_nthw_init(struct tx_ins_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_TX_INS, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: TxIns %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_tx_ins = fpga_query_module(p_fpga, MOD_TX_INS, n_instance);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_tx_ins, INS_RCP_CTRL);
+	p->mp_rcp_addr = register_get_field(p->mp_rcp_ctrl, INS_RCP_CTRL_ADR);
+	p->mp_rcp_cnt = register_get_field(p->mp_rcp_ctrl, INS_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_tx_ins, INS_RCP_DATA);
+	p->mp_rcp_data_dyn = register_get_field(p->mp_rcp_data, INS_RCP_DATA_DYN);
+	p->mp_rcp_data_ofs = register_get_field(p->mp_rcp_data, INS_RCP_DATA_OFS);
+	p->mp_rcp_data_len = register_get_field(p->mp_rcp_data, INS_RCP_DATA_LEN);
+
+	return 0;
+}
+
+void tx_ins_nthw_rcp_select(const struct tx_ins_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_addr, val);
+}
+
+void tx_ins_nthw_rcp_cnt(const struct tx_ins_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_cnt, val);
+}
+
+void tx_ins_nthw_rcp_dyn(const struct tx_ins_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dyn, val);
+}
+
+void tx_ins_nthw_rcp_ofs(const struct tx_ins_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs, val);
+}
+
+void tx_ins_nthw_rcp_len(const struct tx_ins_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len, val);
+}
+
+void tx_ins_nthw_rcp_flush(const struct tx_ins_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.h
new file mode 100644
index 0000000000..813bd30c62
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_ins.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_TX_INS_H__
+#define __FLOW_NTHW_TX_INS_H__
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct tx_ins_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_tx_ins;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_addr;
+	nt_field_t *mp_rcp_cnt;
+
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_dyn;
+	nt_field_t *mp_rcp_data_ofs;
+	nt_field_t *mp_rcp_data_len;
+};
+
+struct tx_ins_nthw *tx_ins_nthw_new(void);
+void tx_ins_nthw_delete(struct tx_ins_nthw *p);
+int tx_ins_nthw_init(struct tx_ins_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int tx_ins_nthw_setup(struct tx_ins_nthw *p, int n_idx, int n_idx_cnt);
+void tx_ins_nthw_set_debug_mode(struct tx_ins_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void tx_ins_nthw_rcp_select(const struct tx_ins_nthw *p, uint32_t val);
+void tx_ins_nthw_rcp_cnt(const struct tx_ins_nthw *p, uint32_t val);
+void tx_ins_nthw_rcp_dyn(const struct tx_ins_nthw *p, uint32_t val);
+void tx_ins_nthw_rcp_ofs(const struct tx_ins_nthw *p, uint32_t val);
+void tx_ins_nthw_rcp_len(const struct tx_ins_nthw *p, uint32_t val);
+void tx_ins_nthw_rcp_flush(const struct tx_ins_nthw *p);
+
+#endif /* __FLOW_NTHW_TX_INS_H__ */
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.c b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.c
new file mode 100644
index 0000000000..5e7e26f74d
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "flow_nthw_tx_rpl.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+void tx_rpl_nthw_set_debug_mode(struct tx_rpl_nthw *p, unsigned int n_debug_mode)
+{
+	module_set_debug_mode(p->m_tx_rpl, n_debug_mode);
+}
+
+struct tx_rpl_nthw *tx_rpl_nthw_new(void)
+{
+	struct tx_rpl_nthw *p = malloc(sizeof(struct tx_rpl_nthw));
+
+	if (p)
+		(void)memset(p, 0, sizeof(*p));
+	return p;
+}
+
+void tx_rpl_nthw_delete(struct tx_rpl_nthw *p)
+{
+	if (p) {
+		(void)memset(p, 0, sizeof(*p));
+		free(p);
+	}
+}
+
+int tx_rpl_nthw_init(struct tx_rpl_nthw *p, nt_fpga_t *p_fpga, int n_instance)
+{
+	const char *const p_adapter_id_str = p_fpga->p_fpga_info->mp_adapter_id_str;
+	nt_module_t *p_mod = fpga_query_module(p_fpga, MOD_TX_RPL, n_instance);
+
+	assert(n_instance >= 0 && n_instance < 256);
+
+	if (p == NULL)
+		return p_mod == NULL ? -1 : 0;
+
+	if (p_mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: TxRpl %d: no such instance\n",
+		       p_adapter_id_str, n_instance);
+		return -1;
+	}
+
+	p->mp_fpga = p_fpga;
+	p->m_physical_adapter_no = (uint8_t)n_instance;
+	p->m_tx_rpl = fpga_query_module(p_fpga, MOD_TX_RPL, n_instance);
+
+	p->mp_rcp_ctrl = module_get_register(p->m_tx_rpl, RPL_RCP_CTRL);
+	p->mp_rcp_ctrl_addr = register_get_field(p->mp_rcp_ctrl, RPL_RCP_CTRL_ADR);
+	p->mp_rcp_ctrl_cnt = register_get_field(p->mp_rcp_ctrl, RPL_RCP_CTRL_CNT);
+	p->mp_rcp_data = module_get_register(p->m_tx_rpl, RPL_RCP_DATA);
+	p->mp_rcp_data_dyn = register_get_field(p->mp_rcp_data, RPL_RCP_DATA_DYN);
+	p->mp_rcp_data_ofs = register_get_field(p->mp_rcp_data, RPL_RCP_DATA_OFS);
+	p->mp_rcp_data_len = register_get_field(p->mp_rcp_data, RPL_RCP_DATA_LEN);
+	p->mp_rcp_data_rpl_ptr =
+		register_get_field(p->mp_rcp_data, RPL_RCP_DATA_RPL_PTR);
+	p->mp_rcp_data_ext_prio =
+		register_get_field(p->mp_rcp_data, RPL_RCP_DATA_EXT_PRIO);
+
+	p->mp_ext_ctrl = module_get_register(p->m_tx_rpl, RPL_EXT_CTRL);
+	p->mp_ext_ctrl_addr = register_get_field(p->mp_ext_ctrl, RPL_EXT_CTRL_ADR);
+	p->mp_ext_ctrl_cnt = register_get_field(p->mp_ext_ctrl, RPL_EXT_CTRL_CNT);
+	p->mp_ext_data = module_get_register(p->m_tx_rpl, RPL_EXT_DATA);
+	p->mp_ext_data_rpl_ptr =
+		register_get_field(p->mp_ext_data, RPL_EXT_DATA_RPL_PTR);
+
+	p->mp_rpl_ctrl = module_get_register(p->m_tx_rpl, RPL_RPL_CTRL);
+	p->mp_rpl_ctrl_addr = register_get_field(p->mp_rpl_ctrl, RPL_RPL_CTRL_ADR);
+	p->mp_rpl_ctrl_cnt = register_get_field(p->mp_rpl_ctrl, RPL_RPL_CTRL_CNT);
+	p->mp_rpl_data = module_get_register(p->m_tx_rpl, RPL_RPL_DATA);
+	p->mp_rpl_data_value = register_get_field(p->mp_rpl_data, RPL_RPL_DATA_VALUE);
+
+	return 0;
+}
+
+void tx_rpl_nthw_rcp_select(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_addr, val);
+}
+
+void tx_rpl_nthw_rcp_cnt(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_ctrl_cnt, val);
+}
+
+void tx_rpl_nthw_rcp_dyn(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_dyn, val);
+}
+
+void tx_rpl_nthw_rcp_ofs(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ofs, val);
+}
+
+void tx_rpl_nthw_rcp_len(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_len, val);
+}
+
+void tx_rpl_nthw_rcp_rpl_ptr(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_rpl_ptr, val);
+}
+
+void tx_rpl_nthw_rcp_ext_prio(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rcp_data_ext_prio, val);
+}
+
+void tx_rpl_nthw_rcp_flush(const struct tx_rpl_nthw *p)
+{
+	register_flush(p->mp_rcp_ctrl, 1);
+	register_flush(p->mp_rcp_data, 1);
+}
+
+void tx_rpl_nthw_ext_select(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ext_ctrl_addr, val);
+}
+
+void tx_rpl_nthw_ext_cnt(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ext_ctrl_cnt, val);
+}
+
+void tx_rpl_nthw_ext_rpl_ptr(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_ext_data_rpl_ptr, val);
+}
+
+void tx_rpl_nthw_ext_flush(const struct tx_rpl_nthw *p)
+{
+	register_flush(p->mp_ext_ctrl, 1);
+	register_flush(p->mp_ext_data, 1);
+}
+
+void tx_rpl_nthw_rpl_select(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rpl_ctrl_addr, val);
+}
+
+void tx_rpl_nthw_rpl_cnt(const struct tx_rpl_nthw *p, uint32_t val)
+{
+	field_set_val32(p->mp_rpl_ctrl_cnt, val);
+}
+
+void tx_rpl_nthw_rpl_value(const struct tx_rpl_nthw *p, const uint32_t *val)
+{
+	field_set_val(p->mp_rpl_data_value, val, 4);
+}
+
+void tx_rpl_nthw_rpl_flush(const struct tx_rpl_nthw *p)
+{
+	register_flush(p->mp_rpl_ctrl, 1);
+	register_flush(p->mp_rpl_data, 1);
+}
diff --git a/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.h b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.h
new file mode 100644
index 0000000000..e5f724361b
--- /dev/null
+++ b/drivers/net/ntnic/nthw/flow_filter/flow_nthw_tx_rpl.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __FLOW_NTHW_TX_RPL_H__
+#define __FLOW_NTHW_TX_RPL_H__
+
+#include <stdint.h>
+#include "nthw_fpga_model.h"
+
+struct tx_rpl_nthw {
+	uint8_t m_physical_adapter_no;
+	nt_fpga_t *mp_fpga;
+
+	nt_module_t *m_tx_rpl;
+
+	nt_register_t *mp_rcp_ctrl;
+	nt_field_t *mp_rcp_ctrl_addr;
+	nt_field_t *mp_rcp_ctrl_cnt;
+
+	nt_register_t *mp_rcp_data;
+	nt_field_t *mp_rcp_data_dyn;
+	nt_field_t *mp_rcp_data_ofs;
+	nt_field_t *mp_rcp_data_len;
+	nt_field_t *mp_rcp_data_rpl_ptr;
+	nt_field_t *mp_rcp_data_ext_prio;
+
+	nt_register_t *mp_ext_ctrl;
+	nt_field_t *mp_ext_ctrl_addr;
+	nt_field_t *mp_ext_ctrl_cnt;
+
+	nt_register_t *mp_ext_data;
+	nt_field_t *mp_ext_data_rpl_ptr;
+
+	nt_register_t *mp_rpl_ctrl;
+	nt_field_t *mp_rpl_ctrl_addr;
+	nt_field_t *mp_rpl_ctrl_cnt;
+
+	nt_register_t *mp_rpl_data;
+	nt_field_t *mp_rpl_data_value;
+};
+
+struct tx_rpl_nthw *tx_rpl_nthw_new(void);
+void tx_rpl_nthw_delete(struct tx_rpl_nthw *p);
+int tx_rpl_nthw_init(struct tx_rpl_nthw *p, nt_fpga_t *p_fpga, int n_instance);
+
+int tx_rpl_nthw_setup(struct tx_rpl_nthw *p, int n_idx, int n_idx_cnt);
+void tx_rpl_nthw_set_debug_mode(struct tx_rpl_nthw *p, unsigned int n_debug_mode);
+
+/* RCP */
+void tx_rpl_nthw_rcp_select(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_cnt(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_dyn(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_ofs(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_len(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_rpl_ptr(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_ext_prio(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rcp_flush(const struct tx_rpl_nthw *p);
+
+void tx_rpl_nthw_ext_select(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_ext_cnt(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_ext_rpl_ptr(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_ext_flush(const struct tx_rpl_nthw *p);
+
+void tx_rpl_nthw_rpl_select(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rpl_cnt(const struct tx_rpl_nthw *p, uint32_t val);
+void tx_rpl_nthw_rpl_value(const struct tx_rpl_nthw *p, const uint32_t *val);
+void tx_rpl_nthw_rpl_flush(const struct tx_rpl_nthw *p);
+
+#endif /* __FLOW_NTHW_TX_RPL_H__ */
