New ntnic FPGA modules:
- Host Interface (HIF): Basic FPGA info such as prod ID and build time.
- Inter-Integrated Circuit Controller (IIC): Use the FPGA to access
        the other integrated circuits on the ntnic.
- PCI Express Gen3 (PCIE3): The FPGA part of PCIe3 initialization,
        speed tests, and configuration.

Signed-off-by: Serhii Iliushyk <sil-...@napatech.com>
---
v6
* Remove unnecessary comments

DVO: Remove unnecessary comments
---
 drivers/net/ntnic/meson.build                 |   1 +
 .../net/ntnic/nthw/core/include/nthw_core.h   |  16 +
 .../net/ntnic/nthw/core/include/nthw_hif.h    | 151 ++++
 .../net/ntnic/nthw/core/include/nthw_iic.h    | 100 +++
 .../net/ntnic/nthw/core/include/nthw_pcie3.h  |  96 +++
 drivers/net/ntnic/nthw/core/nthw_hif.c        | 312 +++++++
 drivers/net/ntnic/nthw/core/nthw_iic.c        | 527 ++++++++++++
 drivers/net/ntnic/nthw/core/nthw_pcie3.c      | 259 ++++++
 drivers/net/ntnic/nthw/nthw_drv.h             |   3 +-
 drivers/net/ntnic/nthw/nthw_rac.c             | 784 ++++++++++++++++++
 drivers/net/ntnic/nthw/nthw_rac.h             | 153 ++++
 11 files changed, 2400 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_core.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_hif.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_iic.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_pcie3.h
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_hif.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_iic.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_pcie3.c
 create mode 100644 drivers/net/ntnic/nthw/nthw_rac.c
 create mode 100644 drivers/net/ntnic/nthw/nthw_rac.h

diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index 9d78e54470..5dd1beada2 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -13,6 +13,7 @@ includes = [
     include_directories('include'),
     include_directories('ntlog'),
     include_directories('ntutil'),
+    include_directories('nthw/core/include'),
     include_directories('nthw'),
     include_directories('nthw/supported'),
 ]
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h 
b/drivers/net/ntnic/nthw/core/include/nthw_core.h
new file mode 100644
index 0000000000..c2602e396f
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_CORE_H__
+#define __NTHW_CORE_H__
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "nthw_platform_drv.h"
+
+
+#endif /* __NTHW_CORE_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_hif.h 
b/drivers/net/ntnic/nthw/core/include/nthw_hif.h
new file mode 100644
index 0000000000..c8f4669f83
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_hif.h
@@ -0,0 +1,151 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_HIF_H__
+#define __NTHW_HIF_H__
+
+#define NTHW_TG_CNT_SIZE (4ULL)
+
+struct nthw_hif {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_hif;
+       int mn_instance;
+
+       nthw_register_t *mp_reg_ctrl;
+       nthw_field_t *mp_fld_ctrl_fsr;
+
+       nthw_register_t *mp_reg_prod_id_lsb;
+       nthw_field_t *mp_fld_prod_id_lsb_rev_id;
+       nthw_field_t *mp_fld_prod_id_lsb_ver_id;
+       nthw_field_t *mp_fld_prod_id_lsb_group_id;
+
+       nthw_register_t *mp_reg_prod_id_msb;
+       nthw_field_t *mp_fld_prod_id_msb_type_id;
+       nthw_field_t *mp_fld_prod_id_msb_build_no;
+
+       nthw_register_t *mp_reg_build_time;
+       nthw_field_t *mp_fld_build_time;
+
+       nthw_register_t *mp_reg_build_seed;
+       nthw_field_t *mp_fld_build_seed;
+
+       nthw_register_t *mp_reg_core_speed;
+       nthw_field_t *mp_fld_core_speed;
+       nthw_field_t *mp_fld_ddr3_speed;
+
+       nthw_register_t *mp_reg_int_mask;
+       nthw_field_t *mp_fld_int_mask_timer;
+       nthw_field_t *mp_fld_int_mask_port;
+       nthw_field_t *mp_fld_int_mask_pps;
+
+       nthw_register_t *mp_reg_int_clr;
+       nthw_field_t *mp_fld_int_clr_timer;
+       nthw_field_t *mp_fld_int_clr_port;
+       nthw_field_t *mp_fld_int_clr_pps;
+
+       nthw_register_t *mp_reg_int_force;
+       nthw_field_t *mp_fld_int_force_timer;
+       nthw_field_t *mp_fld_int_force_port;
+       nthw_field_t *mp_fld_int_force_pps;
+
+       nthw_register_t *mp_reg_sample_time;
+       nthw_field_t *mp_fld_sample_time;
+
+       nthw_register_t *mp_reg_status;
+       nthw_field_t *mp_fld_status_tags_in_use;
+       nthw_field_t *mp_fld_status_wr_err;
+       nthw_field_t *mp_fld_status_rd_err;
+
+       nthw_register_t *mp_reg_stat_ctrl;
+       nthw_field_t *mp_fld_stat_ctrl_ena;
+       nthw_field_t *mp_fld_stat_ctrl_req;
+
+       nthw_register_t *mp_reg_stat_rx;
+       nthw_field_t *mp_fld_stat_rx_counter;
+
+       nthw_register_t *mp_reg_stat_tx;
+       nthw_field_t *mp_fld_stat_tx_counter;
+
+       nthw_register_t *mp_reg_stat_ref_clk;
+       nthw_field_t *mp_fld_stat_ref_clk_ref_clk;
+
+       nthw_register_t *mp_reg_pci_test0;
+       nthw_field_t *mp_fld_pci_test0;
+
+       nthw_register_t *mp_reg_pci_test1;
+       nthw_field_t *mp_fld_pci_test1;
+
+       nthw_register_t *mp_reg_pci_test2;
+       nthw_field_t *mp_fld_pci_test2;
+
+       nthw_register_t *mp_reg_pci_test3;
+       nthw_field_t *mp_fld_pci_test3;
+
+       nthw_register_t *mp_reg_config;
+       nthw_field_t *mp_fld_max_tlp;
+       nthw_field_t *mp_fld_max_read;
+       nthw_field_t *mp_fld_ext_tag;
+
+       int mn_fpga_id_item;
+       int mn_fpga_id_prod;
+       int mn_fpga_id_ver;
+       int mn_fpga_id_rev;
+       int mn_fpga_id_build_no;
+
+       int mn_fpga_param_hif_per_ps;
+       uint32_t mn_fpga_hif_ref_clk_freq;
+};
+
+typedef struct nthw_hif nthw_hif_t;
+
+struct nthw_hif_end_point_err_counters {
+       uint32_t n_err_correctable, n_err_non_fatal, n_err_fatal;
+};
+
+struct nthw_hif_end_point_counters {
+       int n_numa_node;
+
+       int n_tg_direction;
+       int n_tg_pkt_size;
+       int n_tg_num_pkts;
+       int n_tg_delay;
+
+       uint64_t cur_rx, cur_tx;
+       uint64_t cur_pci_nt_util, cur_pci_xil_util;
+       uint64_t n_ref_clk_cnt;
+
+       uint64_t n_tags_in_use;
+       uint64_t n_rd_err;
+       uint64_t n_wr_err;
+
+       struct nthw_hif_end_point_err_counters s_rc_ep_pre, s_rc_ep_post, 
s_rc_ep_delta;
+       struct nthw_hif_end_point_err_counters s_ep_rc_pre, s_ep_rc_post, 
s_ep_rc_delta;
+
+       int bo_error;
+};
+
+struct nthw_hif_end_points {
+       struct nthw_hif_end_point_counters pri, sla;
+};
+
+nthw_hif_t *nthw_hif_new(void);
+void nthw_hif_delete(nthw_hif_t *p);
+int nthw_hif_init(nthw_hif_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+int nthw_hif_trigger_sample_time(nthw_hif_t *p);
+
+int nthw_hif_stat_req_enable(nthw_hif_t *p);
+int nthw_hif_stat_req_disable(nthw_hif_t *p);
+
+int nthw_hif_get_stat(nthw_hif_t *p, uint32_t *p_rx_cnt, uint32_t *p_tx_cnt,
+       uint32_t *p_ref_clk_cnt, uint32_t *p_tg_unit_size, uint32_t 
*p_tg_ref_freq,
+       uint64_t *p_tags_in_use, uint64_t *p_rd_err, uint64_t *p_wr_err);
+int nthw_hif_get_stat_rate(nthw_hif_t *p, uint64_t *p_pci_rx_rate, uint64_t 
*p_pci_tx_rate,
+       uint64_t *p_ref_clk_cnt, uint64_t *p_tags_in_use,
+       uint64_t *p_rd_err_cnt, uint64_t *p_wr_err_cnt);
+
+int nthw_hif_end_point_counters_sample(nthw_hif_t *p, struct 
nthw_hif_end_point_counters *epc);
+
+#endif /* __NTHW_HIF_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_iic.h 
b/drivers/net/ntnic/nthw/core/include/nthw_iic.h
new file mode 100644
index 0000000000..41574f51f3
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_iic.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_IIC_H__
+#define __NTHW_IIC_H__
+
+#include "nthw_fpga_model.h"
+
+struct nthw_iic {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_iic;
+       int mn_iic_instance;
+
+       uint32_t mn_iic_cycle_time;
+       int mn_poll_delay;
+       int mn_bus_ready_retry;
+       int mn_data_ready_retry;
+       int mn_read_data_retry;
+       int mn_write_data_retry;
+
+       nthw_register_t *mp_reg_tsusta;
+       nthw_field_t *mp_fld_tsusta;
+
+       nthw_register_t *mp_reg_tsusto;
+       nthw_field_t *mp_fld_tsusto;
+
+       nthw_register_t *mp_reg_thdsta;
+       nthw_field_t *mp_fld_thdsta;
+
+       nthw_register_t *mp_reg_tsudat;
+       nthw_field_t *mp_fld_tsudat;
+
+       nthw_register_t *mp_reg_tbuf;
+       nthw_field_t *mp_fld_tbuf;
+
+       nthw_register_t *mp_reg_thigh;
+       nthw_field_t *mp_fld_thigh;
+
+       nthw_register_t *mp_reg_tlow;
+       nthw_field_t *mp_fld_tlow;
+
+       nthw_register_t *mp_reg_thddat;
+       nthw_field_t *mp_fld_thddat;
+
+       nthw_register_t *mp_reg_cr;
+       nthw_field_t *mp_fld_cr_en;
+       nthw_field_t *mp_fld_cr_msms;
+       nthw_field_t *mp_fld_cr_txfifo_reset;
+       nthw_field_t *mp_fld_cr_txak;
+
+       nthw_register_t *mp_reg_sr;
+       nthw_field_t *mp_fld_sr_bb;
+       nthw_field_t *mp_fld_sr_rxfifo_full;
+       nthw_field_t *mp_fld_sr_rxfifo_empty;
+       nthw_field_t *mp_fld_sr_txfifo_full;
+       nthw_field_t *mp_fld_sr_txfifo_empty;
+
+       nthw_register_t *mp_reg_tx_fifo;
+       nthw_field_t *mp_fld_tx_fifo_txdata;
+       nthw_field_t *mp_fld_tx_fifo_start;
+       nthw_field_t *mp_fld_tx_fifo_stop;
+
+       nthw_register_t *mp_reg_rx_fifo_pirq;
+       nthw_field_t *mp_fld_rx_fifo_pirq_cmp_val;
+
+       nthw_register_t *mp_reg_rx_fifo;
+       nthw_field_t *mp_fld_rx_fifo_rxdata;
+
+       nthw_register_t *mp_reg_softr;
+       nthw_field_t *mp_fld_softr_rkey;
+};
+
+typedef struct nthw_iic nthw_iic_t;
+
+nthw_iic_t *nthw_iic_new(void);
+int nthw_iic_init(nthw_iic_t *p, nthw_fpga_t *p_fpga, int n_iic_instance,
+       uint32_t n_iic_cycle_time);
+void nthw_iic_delete(nthw_iic_t *p);
+
+int nthw_iic_set_retry_params(nthw_iic_t *p, const int n_poll_delay, const int 
n_bus_ready_retry,
+       const int n_data_ready_retry, const int n_read_data_retry,
+       const int n_write_data_retry);
+
+int nthw_iic_read_data(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       void *p_void);
+int nthw_iic_readbyte(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       uint8_t *p_byte);
+int nthw_iic_write_data(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       void *p_void);
+int nthw_iic_writebyte(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       uint8_t *p_byte);
+bool nthw_iic_bus_ready(nthw_iic_t *p);
+bool nthw_iic_data_ready(nthw_iic_t *p);
+
+int nthw_iic_scan(nthw_iic_t *p);
+int nthw_iic_scan_dev_addr(nthw_iic_t *p, int n_dev_addr, int n_reg_addr);
+
+#endif /* __NTHW_IIC_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_pcie3.h 
b/drivers/net/ntnic/nthw/core/include/nthw_pcie3.h
new file mode 100644
index 0000000000..846c09b1b9
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_pcie3.h
@@ -0,0 +1,96 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_PCIE3_H__
+#define __NTHW_PCIE3_H__
+
+struct nthw_pcie3 {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_pcie3;
+       int mn_instance;
+
+       nthw_register_t *mp_reg_stat_ctrl;
+       nthw_field_t *mp_fld_stat_ctrl_req;
+       nthw_field_t *mp_fld_stat_ctrl_ena;
+
+       nthw_register_t *mp_reg_stat_rx;
+       nthw_field_t *mp_fld_stat_rx_counter;
+
+       nthw_register_t *mp_reg_stat_tx;
+       nthw_field_t *mp_fld_stat_tx_counter;
+
+       nthw_register_t *mp_reg_stat_rq_rdy;
+       nthw_field_t *mp_fld_stat_rq_rdy_counter;
+
+       nthw_register_t *mp_reg_stat_rq_vld;
+       nthw_field_t *mp_fld_stat_rq_vld_counter;
+
+       nthw_register_t *mp_reg_status0;
+       nthw_field_t *mp_fld_status0_tags_in_use;
+
+       nthw_register_t *mp_reg_stat_ref_clk;
+       nthw_field_t *mp_fld_stat_ref_clk_ref_clk;
+
+       nthw_register_t *mp_reg_rp_to_ep_err;
+       nthw_field_t *mp_fld_rp_to_ep_err_cor;
+       nthw_field_t *mp_fld_rp_to_ep_err_non_fatal;
+       nthw_field_t *mp_fld_rp_to_ep_err_fatal;
+
+       nthw_register_t *mp_reg_ep_to_rp_err;
+       nthw_field_t *mp_fld_ep_to_rp_err_cor;
+       nthw_field_t *mp_fld_ep_to_rp_err_non_fatal;
+       nthw_field_t *mp_fld_ep_to_rp_err_fatal;
+
+       nthw_register_t *mp_reg_sample_time;
+       nthw_field_t *mp_fld_sample_time;
+
+       nthw_register_t *mp_reg_pci_end_point;
+       nthw_field_t *mp_fld_pci_end_point_if_id;
+       nthw_field_t *mp_fld_pci_end_point_send_msg;
+       nthw_field_t *mp_fld_pci_end_point_get_msg;
+       nthw_field_t *mp_fld_pci_end_point_dmaep0_allow_mask;
+       nthw_field_t *mp_fld_pci_end_point_dmaep1_allow_mask;
+
+       nthw_register_t *mp_reg_pci_e3_mark_adr_lsb;
+       nthw_field_t *mp_fld_pci_e3_mark_adr_lsb_adr;
+
+       nthw_register_t *mp_reg_pci_e3_mark_adr_msb;
+       nthw_field_t *mp_fld_pci_e3_mark_adr_msb_adr;
+
+       nthw_register_t *mp_reg_pci_test0;
+       nthw_field_t *mp_fld_pci_test0;
+
+       nthw_register_t *mp_reg_pci_test1;
+       nthw_field_t *mp_fld_pci_test1;
+
+       nthw_register_t *mp_reg_pci_test2;
+       nthw_field_t *mp_fld_pci_test2;
+
+       nthw_register_t *mp_reg_pci_test3;
+       nthw_field_t *mp_fld_pci_test3;
+};
+
+typedef struct nthw_pcie3 nthw_pcie3_t;
+
+nthw_pcie3_t *nthw_pcie3_new(void);
+void nthw_pcie3_delete(nthw_pcie3_t *p);
+int nthw_pcie3_init(nthw_pcie3_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+int nthw_pcie3_trigger_sample_time(nthw_pcie3_t *p);
+
+int nthw_pcie3_stat_req_enable(nthw_pcie3_t *p);
+int nthw_pcie3_stat_req_disable(nthw_pcie3_t *p);
+
+int nthw_pcie3_get_stat(nthw_pcie3_t *p, uint32_t *p_rx_cnt, uint32_t 
*p_tx_cnt,
+       uint32_t *p_ref_clk_cnt, uint32_t *p_tg_unit_size, uint32_t 
*p_tg_ref_freq,
+       uint32_t *p_tag_use_cnt, uint32_t *p_rq_rdy_cnt, uint32_t 
*p_rq_vld_cnt);
+int nthw_pcie3_get_stat_rate(nthw_pcie3_t *p, uint64_t *p_pci_rx_rate, 
uint64_t *p_pci_tx_rate,
+       uint64_t *p_ref_clk_cnt, uint64_t *p_tag_use_cnt,
+       uint64_t *p_pci_nt_bus_util, uint64_t *p_pci_xil_bus_util);
+
+int nthw_pcie3_end_point_counters_sample_post(nthw_pcie3_t *p,
+       struct nthw_hif_end_point_counters *epc);
+
+#endif /* __NTHW_PCIE3_H__ */
diff --git a/drivers/net/ntnic/nthw/core/nthw_hif.c 
b/drivers/net/ntnic/nthw/core/nthw_hif.c
new file mode 100644
index 0000000000..f05e1a0c51
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_hif.c
@@ -0,0 +1,312 @@
+/*
+ * 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_hif.h"
+
+nthw_hif_t *nthw_hif_new(void)
+{
+       nthw_hif_t *p = malloc(sizeof(nthw_hif_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_hif_t));
+
+       return p;
+}
+
+void nthw_hif_delete(nthw_hif_t *p)
+{
+       if (p) {
+               memset(p, 0, sizeof(nthw_hif_t));
+               free(p);
+       }
+}
+
+int nthw_hif_init(nthw_hif_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       const char *const p_adapter_id_str = 
p_fpga->p_fpga_info->mp_adapter_id_str;
+       (void)p_adapter_id_str;
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_HIF, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: HIF %d: no such instance\n",
+                       p_fpga->p_fpga_info->mp_adapter_id_str, n_instance);
+               return -1;
+       }
+
+       p->mp_fpga = p_fpga;
+       p->mn_instance = n_instance;
+       p->mp_mod_hif = mod;
+
+       /* default for (Xilinx-based) products until august 2022: (1e6/4000 = 
250 MHz) */
+       p->mn_fpga_param_hif_per_ps = nthw_fpga_get_product_param(p->mp_fpga, 
NT_HIF_PER_PS, 4000);
+       p->mn_fpga_hif_ref_clk_freq =
+               (uint32_t)(1000000000000ULL / (unsigned 
int)p->mn_fpga_param_hif_per_ps);
+
+       p->mp_reg_prod_id_lsb = nthw_module_get_register(p->mp_mod_hif, 
HIF_PROD_ID_LSB);
+       p->mp_fld_prod_id_lsb_rev_id =
+               nthw_register_get_field(p->mp_reg_prod_id_lsb, 
HIF_PROD_ID_LSB_REV_ID);
+       p->mp_fld_prod_id_lsb_ver_id =
+               nthw_register_get_field(p->mp_reg_prod_id_lsb, 
HIF_PROD_ID_LSB_VER_ID);
+       p->mp_fld_prod_id_lsb_group_id =
+               nthw_register_get_field(p->mp_reg_prod_id_lsb, 
HIF_PROD_ID_LSB_GROUP_ID);
+
+       p->mp_reg_prod_id_msb = nthw_module_get_register(p->mp_mod_hif, 
HIF_PROD_ID_MSB);
+       p->mp_fld_prod_id_msb_type_id =
+               nthw_register_get_field(p->mp_reg_prod_id_msb, 
HIF_PROD_ID_MSB_TYPE_ID);
+       p->mp_fld_prod_id_msb_build_no =
+               nthw_register_get_field(p->mp_reg_prod_id_msb, 
HIF_PROD_ID_MSB_BUILD_NO);
+
+       p->mp_reg_build_time = nthw_module_get_register(p->mp_mod_hif, 
HIF_BUILD_TIME);
+       p->mp_fld_build_time = nthw_register_get_field(p->mp_reg_build_time, 
HIF_BUILD_TIME_TIME);
+
+       p->mn_fpga_id_prod = 
nthw_field_get_updated(p->mp_fld_prod_id_lsb_group_id);
+       p->mn_fpga_id_ver = 
nthw_field_get_updated(p->mp_fld_prod_id_lsb_ver_id);
+       p->mn_fpga_id_rev = 
nthw_field_get_updated(p->mp_fld_prod_id_lsb_rev_id);
+       p->mn_fpga_id_build_no = 
nthw_field_get_updated(p->mp_fld_prod_id_msb_build_no);
+       p->mn_fpga_id_item = 
nthw_field_get_updated(p->mp_fld_prod_id_msb_type_id);
+
+       NT_LOG(DBG, NTHW, "%s: HIF %d: %d-%d-%d-%d-%d\n", p_adapter_id_str, 
p->mn_instance,
+               p->mn_fpga_id_item, p->mn_fpga_id_prod, p->mn_fpga_id_ver,
+               p->mn_fpga_id_rev, p->mn_fpga_id_build_no);
+       NT_LOG(DBG, NTHW, "%s: HIF %d: HIF ref clock: %d Hz (%d ticks/ps)\n", 
p_adapter_id_str,
+               p->mn_instance, p->mn_fpga_hif_ref_clk_freq, 
p->mn_fpga_param_hif_per_ps);
+
+       p->mp_reg_build_seed = NULL;    /* Reg/Fld not present on HIF */
+
+       if (p->mp_reg_build_seed)
+               p->mp_fld_build_seed = NULL;    /* Reg/Fld not present on HIF */
+       else
+               p->mp_fld_build_seed = NULL;
+
+       p->mp_reg_core_speed = NULL;    /* Reg/Fld not present on HIF */
+
+       if (p->mp_reg_core_speed) {
+               p->mp_fld_core_speed = NULL;    /* Reg/Fld not present on HIF */
+               p->mp_fld_ddr3_speed = NULL;    /* Reg/Fld not present on HIF */
+
+       } else {
+               p->mp_reg_core_speed = NULL;
+               p->mp_fld_core_speed = NULL;
+               p->mp_fld_ddr3_speed = NULL;
+       }
+
+       /* Optional registers since: 2018-04-25 */
+       p->mp_reg_int_mask = NULL;      /* Reg/Fld not present on HIF */
+       p->mp_reg_int_clr = NULL;       /* Reg/Fld not present on HIF */
+       p->mp_reg_int_force = NULL;     /* Reg/Fld not present on HIF */
+
+       p->mp_fld_int_mask_timer = NULL;
+       p->mp_fld_int_clr_timer = NULL;
+       p->mp_fld_int_force_timer = NULL;
+
+       p->mp_fld_int_mask_port = NULL;
+       p->mp_fld_int_clr_port = NULL;
+       p->mp_fld_int_force_port = NULL;
+
+       p->mp_fld_int_mask_pps = NULL;
+       p->mp_fld_int_clr_pps = NULL;
+       p->mp_fld_int_force_pps = NULL;
+
+       p->mp_reg_ctrl = nthw_module_get_register(p->mp_mod_hif, HIF_CONTROL);
+       p->mp_fld_ctrl_fsr = nthw_register_query_field(p->mp_reg_ctrl, 
HIF_CONTROL_FSR);
+
+       p->mp_reg_stat_ctrl = nthw_module_get_register(p->mp_mod_hif, 
HIF_STAT_CTRL);
+       p->mp_fld_stat_ctrl_ena =
+               nthw_register_get_field(p->mp_reg_stat_ctrl, 
HIF_STAT_CTRL_STAT_ENA);
+       p->mp_fld_stat_ctrl_req =
+               nthw_register_get_field(p->mp_reg_stat_ctrl, 
HIF_STAT_CTRL_STAT_REQ);
+
+       p->mp_reg_stat_rx = nthw_module_get_register(p->mp_mod_hif, 
HIF_STAT_RX);
+       p->mp_fld_stat_rx_counter =
+               nthw_register_get_field(p->mp_reg_stat_rx, HIF_STAT_RX_COUNTER);
+
+       p->mp_reg_stat_tx = nthw_module_get_register(p->mp_mod_hif, 
HIF_STAT_TX);
+       p->mp_fld_stat_tx_counter =
+               nthw_register_get_field(p->mp_reg_stat_tx, HIF_STAT_TX_COUNTER);
+
+       p->mp_reg_stat_ref_clk = nthw_module_get_register(p->mp_mod_hif, 
HIF_STAT_REFCLK);
+       p->mp_fld_stat_ref_clk_ref_clk =
+               nthw_register_get_field(p->mp_reg_stat_ref_clk, 
HIF_STAT_REFCLK_REFCLK250);
+
+       p->mp_reg_status = nthw_module_query_register(p->mp_mod_hif, 
HIF_STATUS);
+
+       if (p->mp_reg_status) {
+               p->mp_fld_status_tags_in_use =
+                       nthw_register_query_field(p->mp_reg_status, 
HIF_STATUS_TAGS_IN_USE);
+               p->mp_fld_status_wr_err =
+                       nthw_register_query_field(p->mp_reg_status, 
HIF_STATUS_WR_ERR);
+               p->mp_fld_status_rd_err =
+                       nthw_register_query_field(p->mp_reg_status, 
HIF_STATUS_RD_ERR);
+
+       } else {
+               p->mp_reg_status = nthw_module_query_register(p->mp_mod_hif, 
HIF_STATUS);
+               p->mp_fld_status_tags_in_use =
+                       nthw_register_query_field(p->mp_reg_status, 
HIF_STATUS_TAGS_IN_USE);
+               p->mp_fld_status_wr_err = NULL;
+               p->mp_fld_status_rd_err = NULL;
+       }
+
+       p->mp_reg_pci_test0 = nthw_module_get_register(p->mp_mod_hif, 
HIF_TEST0);
+       p->mp_fld_pci_test0 = nthw_register_get_field(p->mp_reg_pci_test0, 
HIF_TEST0_DATA);
+
+       p->mp_reg_pci_test1 = nthw_module_get_register(p->mp_mod_hif, 
HIF_TEST1);
+       p->mp_fld_pci_test1 = nthw_register_get_field(p->mp_reg_pci_test1, 
HIF_TEST1_DATA);
+
+       /* Module::Version({2, 0})+ */
+       p->mp_reg_pci_test2 = nthw_module_query_register(p->mp_mod_hif, 
HIF_TEST2);
+
+       if (p->mp_reg_pci_test2)
+               p->mp_fld_pci_test2 = 
nthw_register_get_field(p->mp_reg_pci_test2, HIF_TEST2_DATA);
+
+       else
+               p->mp_fld_pci_test2 = NULL;
+
+       /* Module::Version({1, 2})+ */
+       p->mp_reg_pci_test3 = nthw_module_query_register(p->mp_mod_hif, 
HIF_TEST3);
+
+       if (p->mp_reg_pci_test3)
+               p->mp_fld_pci_test3 = 
nthw_register_get_field(p->mp_reg_pci_test3, HIF_TEST3_DATA);
+
+       else
+               p->mp_fld_pci_test3 = NULL;
+
+       /* Required to run TSM */
+       p->mp_reg_sample_time = nthw_module_get_register(p->mp_mod_hif, 
HIF_SAMPLE_TIME);
+
+       if (p->mp_reg_sample_time) {
+               p->mp_fld_sample_time = 
nthw_register_get_field(p->mp_reg_sample_time,
+                               HIF_SAMPLE_TIME_SAMPLE_TIME);
+
+       } else {
+               p->mp_fld_sample_time = NULL;
+       }
+
+       /* We need to optimize PCIe3 TLP-size read-request and extended tag 
usage */
+       {
+               p->mp_reg_config = nthw_module_query_register(p->mp_mod_hif, 
HIF_CONFIG);
+
+               if (p->mp_reg_config) {
+                       p->mp_fld_max_tlp =
+                               nthw_register_get_field(p->mp_reg_config, 
HIF_CONFIG_MAX_TLP);
+                       p->mp_fld_max_read =
+                               nthw_register_get_field(p->mp_reg_config, 
HIF_CONFIG_MAX_READ);
+                       p->mp_fld_ext_tag =
+                               nthw_register_get_field(p->mp_reg_config, 
HIF_CONFIG_EXT_TAG);
+
+               } else {
+                       p->mp_fld_max_tlp = NULL;
+                       p->mp_fld_max_read = NULL;
+                       p->mp_fld_ext_tag = NULL;
+               }
+       }
+
+       return 0;
+}
+
+int nthw_hif_trigger_sample_time(nthw_hif_t *p)
+{
+       nthw_field_set_val_flush32(p->mp_fld_sample_time, 0xfee1dead);
+
+       return 0;
+}
+
+int nthw_hif_get_stat(nthw_hif_t *p, uint32_t *p_rx_cnt, uint32_t *p_tx_cnt,
+       uint32_t *p_ref_clk_cnt, uint32_t *p_tg_unit_size, uint32_t 
*p_tg_ref_freq,
+       uint64_t *p_tags_in_use, uint64_t *p_rd_err, uint64_t *p_wr_err)
+{
+       *p_rx_cnt = nthw_field_get_updated(p->mp_fld_stat_rx_counter);
+       *p_tx_cnt = nthw_field_get_updated(p->mp_fld_stat_tx_counter);
+
+       *p_ref_clk_cnt = nthw_field_get_updated(p->mp_fld_stat_ref_clk_ref_clk);
+
+       *p_tg_unit_size = NTHW_TG_CNT_SIZE;
+       *p_tg_ref_freq = p->mn_fpga_hif_ref_clk_freq;
+
+       *p_tags_in_use = (p->mp_fld_status_tags_in_use
+                       ? nthw_field_get_updated(p->mp_fld_status_tags_in_use)
+                       : 0);
+
+       *p_rd_err =
+               (p->mp_fld_status_rd_err ? 
nthw_field_get_updated(p->mp_fld_status_rd_err) : 0);
+       *p_wr_err =
+               (p->mp_fld_status_wr_err ? 
nthw_field_get_updated(p->mp_fld_status_wr_err) : 0);
+
+       return 0;
+}
+
+int nthw_hif_get_stat_rate(nthw_hif_t *p, uint64_t *p_pci_rx_rate, uint64_t 
*p_pci_tx_rate,
+       uint64_t *p_ref_clk_cnt, uint64_t *p_tags_in_use,
+       uint64_t *p_rd_err_cnt, uint64_t *p_wr_err_cnt)
+{
+       uint32_t rx_cnt, tx_cnt, ref_clk_cnt, tg_unit_size, tg_ref_freq;
+       uint64_t n_tags_in_use, n_rd_err, n_wr_err;
+
+       nthw_hif_get_stat(p, &rx_cnt, &tx_cnt, &ref_clk_cnt, &tg_unit_size, 
&tg_ref_freq,
+               &n_tags_in_use, &n_rd_err, &n_wr_err);
+
+       *p_tags_in_use = n_tags_in_use;
+
+       if (n_rd_err)
+               (*p_rd_err_cnt)++;
+
+       if (n_wr_err)
+               (*p_wr_err_cnt)++;
+
+       if (ref_clk_cnt) {
+               uint64_t rx_rate;
+               uint64_t tx_rate;
+
+               *p_ref_clk_cnt = ref_clk_cnt;
+
+               rx_rate = ((uint64_t)rx_cnt * tg_unit_size * tg_ref_freq) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_rx_rate = rx_rate;
+
+               tx_rate = ((uint64_t)tx_cnt * tg_unit_size * tg_ref_freq) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_tx_rate = tx_rate;
+
+       } else {
+               *p_pci_rx_rate = 0;
+               *p_pci_tx_rate = 0;
+               *p_ref_clk_cnt = 0;
+       }
+
+       return 0;
+}
+
+int nthw_hif_stat_req_enable(nthw_hif_t *p)
+{
+       nthw_field_set_all(p->mp_fld_stat_ctrl_ena);
+       nthw_field_set_all(p->mp_fld_stat_ctrl_req);
+       nthw_field_flush_register(p->mp_fld_stat_ctrl_req);
+       return 0;
+}
+
+int nthw_hif_stat_req_disable(nthw_hif_t *p)
+{
+       nthw_field_clr_all(p->mp_fld_stat_ctrl_ena);
+       nthw_field_set_all(p->mp_fld_stat_ctrl_req);
+       nthw_field_flush_register(p->mp_fld_stat_ctrl_req);
+       return 0;
+}
+
+int nthw_hif_end_point_counters_sample(nthw_hif_t *p, struct 
nthw_hif_end_point_counters *epc)
+{
+       assert(epc);
+
+       /* Get stat rate and maintain rx/tx min/max */
+       nthw_hif_get_stat_rate(p, &epc->cur_tx, &epc->cur_rx, 
&epc->n_ref_clk_cnt,
+               &epc->n_tags_in_use, &epc->n_rd_err, &epc->n_wr_err);
+
+       return 0;
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_iic.c 
b/drivers/net/ntnic/nthw/core/nthw_iic.c
new file mode 100644
index 0000000000..7f324dec78
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_iic.c
@@ -0,0 +1,527 @@
+/*
+ * 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_iic.h"
+
+#define I2C_TRANSMIT_WR (0x00)
+#define I2C_TRANSMIT_RD (0x01)
+
+#define I2C_WAIT_US(x) nt_os_wait_usec(x)
+
+/*
+ * Minimum timing values for I2C for a Marvel 88E11111 Phy.
+ * This Phy is used in many Trispeed NIMs.
+ * In order to access this Phy, the I2C clock speed is needed to be set to 
100KHz.
+ */
+static const uint32_t SUSTA = 4700;    /* ns */
+static const uint32_t SUSTO = 4000;    /* ns */
+static const uint32_t HDSTA = 4000;    /* ns */
+static const uint32_t SUDAT = 250;     /* ns */
+static const uint32_t BUF = 4700;      /* ns */
+static const uint32_t HIGH = 4000;     /* ns */
+static const uint32_t LOW = 4700;      /* ns */
+static const uint32_t HDDAT = 300;     /* ns */
+
+static int nthw_iic_reg_control_txfifo_reset(nthw_iic_t *p)
+{
+       nthw_field_update_register(p->mp_fld_cr_txfifo_reset);
+
+       nthw_field_set_all(p->mp_fld_cr_txfifo_reset);
+       nthw_field_flush_register(p->mp_fld_cr_txfifo_reset);
+
+       nthw_field_clr_all(p->mp_fld_cr_txfifo_reset);
+       nthw_field_flush_register(p->mp_fld_cr_txfifo_reset);
+
+       return 0;
+}
+
+static int nthw_iic_reg_tx_fifo_write(nthw_iic_t *p, uint32_t data, bool 
start, bool stop)
+{
+       if (start)
+               nthw_field_set_all(p->mp_fld_tx_fifo_start);
+
+       else
+               nthw_field_clr_all(p->mp_fld_tx_fifo_start);
+
+       if (stop)
+               nthw_field_set_all(p->mp_fld_tx_fifo_stop);
+
+       else
+               nthw_field_clr_all(p->mp_fld_tx_fifo_stop);
+
+       nthw_field_set_val32(p->mp_fld_tx_fifo_txdata, data);
+
+       nthw_register_flush(p->mp_reg_tx_fifo, 1);
+
+       return 0;
+}
+
+static int nthw_iic_reg_read_i2c_rx_fifo(nthw_iic_t *p, uint8_t *p_data)
+{
+       assert(p_data);
+
+       *p_data = (uint8_t)nthw_field_get_updated(p->mp_fld_rx_fifo_rxdata);
+
+       return 0;
+}
+
+static int nthw_iic_reg_softr(nthw_iic_t *p)
+{
+       nthw_field_update_register(p->mp_fld_cr_en);
+       nthw_field_set_val_flush32(p->mp_fld_softr_rkey, 0x0A);
+
+       return 0;
+}
+
+static int nthw_iic_reg_enable(nthw_iic_t *p)
+{
+       nthw_field_update_register(p->mp_fld_cr_en);
+       nthw_field_set_flush(p->mp_fld_cr_en);
+
+       return 0;
+}
+
+static int nthw_iic_reg_busbusy(nthw_iic_t *p, bool *pb_flag)
+{
+       assert(pb_flag);
+
+       *pb_flag = nthw_field_get_updated(p->mp_fld_sr_bb) ? true : false;
+
+       return 0;
+}
+
+static int nthw_iic_reg_rxfifo_empty(nthw_iic_t *p, bool *pb_flag)
+{
+       assert(pb_flag);
+
+       *pb_flag = nthw_field_get_updated(p->mp_fld_sr_rxfifo_empty) ? true : 
false;
+
+       return 0;
+}
+
+/*
+ * n_iic_cycle_time is the I2C clock cycle time in ns ie 125MHz = 8ns
+ */
+static int nthw_iic_reg_set_timing(nthw_iic_t *p, uint32_t n_iic_cycle_time)
+{
+       uint32_t val;
+
+       val = SUSTA / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_tsusta, &val, 1);
+
+       val = SUSTO / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_tsusto, &val, 1);
+
+       val = HDSTA / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_thdsta, &val, 1);
+
+       val = SUDAT / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_tsudat, &val, 1);
+
+       val = BUF / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_tbuf, &val, 1);
+
+       val = HIGH / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_thigh, &val, 1);
+
+       val = LOW / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_tlow, &val, 1);
+
+       val = HDDAT / n_iic_cycle_time;
+       nthw_field_set_val_flush(p->mp_fld_thddat, &val, 1);
+
+       return 0;
+}
+
+nthw_iic_t *nthw_iic_new(void)
+{
+       nthw_iic_t *p = malloc(sizeof(nthw_iic_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_iic_t));
+
+       return p;
+}
+
+int nthw_iic_init(nthw_iic_t *p, nthw_fpga_t *p_fpga, int n_iic_instance,
+       uint32_t n_iic_cycle_time)
+{
+       const char *const p_adapter_id_str = 
p_fpga->p_fpga_info->mp_adapter_id_str;
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_IIC, 
n_iic_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: I2C %d: no such instance\n", 
p_adapter_id_str,
+                       n_iic_instance);
+               return -1;
+       }
+
+       p->mp_fpga = p_fpga;
+       p->mn_iic_instance = n_iic_instance;
+
+       p->mn_iic_cycle_time = n_iic_cycle_time;
+
+       nthw_iic_set_retry_params(p, -1, -1, -1, -1, -1);
+
+       p->mp_mod_iic = mod;
+
+       /* I2C is a primary communication channel - turn off debug by default */
+       nthw_module_set_debug_mode(p->mp_mod_iic, 0x00);
+
+       p->mp_reg_tsusta = nthw_module_get_register(p->mp_mod_iic, IIC_TSUSTA);
+       p->mp_fld_tsusta = nthw_register_get_field(p->mp_reg_tsusta, 
IIC_TSUSTA_TSUSTA_VAL);
+
+       p->mp_reg_tsusto = nthw_module_get_register(p->mp_mod_iic, IIC_TSUSTO);
+       p->mp_fld_tsusto = nthw_register_get_field(p->mp_reg_tsusto, 
IIC_TSUSTO_TSUSTO_VAL);
+
+       p->mp_reg_thdsta = nthw_module_get_register(p->mp_mod_iic, IIC_THDSTA);
+       p->mp_fld_thdsta = nthw_register_get_field(p->mp_reg_thdsta, 
IIC_THDSTA_THDSTA_VAL);
+
+       p->mp_reg_tsudat = nthw_module_get_register(p->mp_mod_iic, IIC_TSUDAT);
+       p->mp_fld_tsudat = nthw_register_get_field(p->mp_reg_tsudat, 
IIC_TSUDAT_TSUDAT_VAL);
+
+       p->mp_reg_tbuf = nthw_module_get_register(p->mp_mod_iic, IIC_TBUF);
+       p->mp_fld_tbuf = nthw_register_get_field(p->mp_reg_tbuf, 
IIC_TBUF_TBUF_VAL);
+
+       p->mp_reg_thigh = nthw_module_get_register(p->mp_mod_iic, IIC_THIGH);
+       p->mp_fld_thigh = nthw_register_get_field(p->mp_reg_thigh, 
IIC_THIGH_THIGH_VAL);
+
+       p->mp_reg_tlow = nthw_module_get_register(p->mp_mod_iic, IIC_TLOW);
+       p->mp_fld_tlow = nthw_register_get_field(p->mp_reg_tlow, 
IIC_TLOW_TLOW_VAL);
+
+       p->mp_reg_thddat = nthw_module_get_register(p->mp_mod_iic, IIC_THDDAT);
+       p->mp_fld_thddat = nthw_register_get_field(p->mp_reg_thddat, 
IIC_THDDAT_THDDAT_VAL);
+
+       p->mp_reg_cr = nthw_module_get_register(p->mp_mod_iic, IIC_CR);
+       p->mp_fld_cr_en = nthw_register_get_field(p->mp_reg_cr, IIC_CR_EN);
+       p->mp_fld_cr_msms = nthw_register_get_field(p->mp_reg_cr, IIC_CR_MSMS);
+       p->mp_fld_cr_txfifo_reset = nthw_register_get_field(p->mp_reg_cr, 
IIC_CR_TXFIFO_RESET);
+       p->mp_fld_cr_txak = nthw_register_get_field(p->mp_reg_cr, IIC_CR_TXAK);
+
+       p->mp_reg_sr = nthw_module_get_register(p->mp_mod_iic, IIC_SR);
+       p->mp_fld_sr_bb = nthw_register_get_field(p->mp_reg_sr, IIC_SR_BB);
+       p->mp_fld_sr_rxfifo_full = nthw_register_get_field(p->mp_reg_sr, 
IIC_SR_RXFIFO_FULL);
+       p->mp_fld_sr_rxfifo_empty = nthw_register_get_field(p->mp_reg_sr, 
IIC_SR_RXFIFO_EMPTY);
+       p->mp_fld_sr_txfifo_full = nthw_register_get_field(p->mp_reg_sr, 
IIC_SR_TXFIFO_FULL);
+       p->mp_fld_sr_txfifo_empty = nthw_register_get_field(p->mp_reg_sr, 
IIC_SR_TXFIFO_EMPTY);
+
+       p->mp_reg_tx_fifo = nthw_module_get_register(p->mp_mod_iic, 
IIC_TX_FIFO);
+       p->mp_fld_tx_fifo_txdata = nthw_register_get_field(p->mp_reg_tx_fifo, 
IIC_TX_FIFO_TXDATA);
+       p->mp_fld_tx_fifo_start = nthw_register_get_field(p->mp_reg_tx_fifo, 
IIC_TX_FIFO_START);
+       p->mp_fld_tx_fifo_stop = nthw_register_get_field(p->mp_reg_tx_fifo, 
IIC_TX_FIFO_STOP);
+
+       p->mp_reg_rx_fifo_pirq = nthw_module_get_register(p->mp_mod_iic, 
IIC_RX_FIFO_PIRQ);
+       p->mp_fld_rx_fifo_pirq_cmp_val =
+               nthw_register_get_field(p->mp_reg_rx_fifo_pirq, 
IIC_RX_FIFO_PIRQ_CMP_VAL);
+
+       p->mp_reg_rx_fifo = nthw_module_get_register(p->mp_mod_iic, 
IIC_RX_FIFO);
+       p->mp_fld_rx_fifo_rxdata = nthw_register_get_field(p->mp_reg_rx_fifo, 
IIC_RX_FIFO_RXDATA);
+
+       p->mp_reg_softr = nthw_module_get_register(p->mp_mod_iic, IIC_SOFTR);
+       p->mp_fld_softr_rkey = nthw_register_get_field(p->mp_reg_softr, 
IIC_SOFTR_RKEY);
+
+       /*
+        * Initialize I2C controller by applying soft reset and enable the 
controller
+        */
+       nthw_iic_reg_softr(p);
+       /* Enable the controller */
+       nthw_iic_reg_enable(p);
+
+       /* Setup controller timing */
+       if (p->mn_iic_cycle_time) {
+               NT_LOG(DBG, NTHW, "%s: I2C%d: cycletime=%d\n", p_adapter_id_str,
+                       p->mn_iic_instance, p->mn_iic_cycle_time);
+               nthw_iic_reg_set_timing(p, p->mn_iic_cycle_time);
+       }
+
+       /* Reset TX fifo - must be after enable */
+       nthw_iic_reg_control_txfifo_reset(p);
+       nthw_iic_reg_tx_fifo_write(p, 0, 0, 0);
+
+       return 0;
+}
+
+void nthw_iic_delete(nthw_iic_t *p)
+{
+       if (p) {
+               memset(p, 0, sizeof(nthw_iic_t));
+               free(p);
+       }
+}
+
+int nthw_iic_set_retry_params(nthw_iic_t *p, const int n_poll_delay, const int 
n_bus_ready_retry,
+       const int n_data_ready_retry, const int n_read_data_retry,
+       const int n_write_data_retry)
+{
+       p->mn_poll_delay = n_poll_delay >= 0 ? n_poll_delay : 10;
+
+       p->mn_bus_ready_retry = n_bus_ready_retry >= 0 ? n_bus_ready_retry : 
1000;
+       p->mn_data_ready_retry = n_data_ready_retry >= 0 ? n_data_ready_retry : 
1000;
+
+       p->mn_read_data_retry = n_read_data_retry >= 0 ? n_read_data_retry : 10;
+       p->mn_write_data_retry = n_write_data_retry >= 0 ? n_write_data_retry : 
10;
+
+       return 0;
+}
+
+int nthw_iic_read_data(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       void *p_void)
+{
+       const char *const p_adapter_id_str = 
p->mp_fpga->p_fpga_info->mp_adapter_id_str;
+       const int n_debug_mode = nthw_module_get_debug_mode(p->mp_mod_iic);
+
+       uint8_t *pb = (uint8_t *)p_void;
+       int retry = (p->mn_read_data_retry >= 0 ? p->mn_read_data_retry : 10);
+
+       if (n_debug_mode == 0xff) {
+               NT_LOG(DBG, NTHW, "%s: adr=0x%2.2x, reg=%d, len=%d\n", 
p_adapter_id_str, dev_addr,
+                       a_reg_addr, data_len);
+       }
+
+       while (nthw_iic_readbyte(p, dev_addr, a_reg_addr, data_len, pb) != 0) {
+               retry--;
+
+               if (retry <= 0) {
+                       NT_LOG(ERR, NTHW,
+                               "%s: I2C%d: Read retry exhausted (dev_addr=%d 
a_reg_addr=%d)\n",
+                               p_adapter_id_str, p->mn_iic_instance, dev_addr, 
a_reg_addr);
+                       return -1;
+
+               } else {
+                       NT_LOG(DBG, NTHW, "%s: I2C%d: Read retry=%d 
(dev_addr=%d a_reg_addr=%d)\n",
+                               p_adapter_id_str, p->mn_iic_instance, retry, 
dev_addr, a_reg_addr);
+               }
+       }
+
+       if (n_debug_mode == 0xff) {
+               NT_LOG(DBG, NTHW, "%s: adr=0x%2.2x, reg=%d, len=%d, retries 
remaining: %d\n",
+                       p_adapter_id_str, dev_addr, a_reg_addr, data_len, 
retry);
+       }
+
+       return 0;
+}
+
+int nthw_iic_readbyte(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       uint8_t *p_byte)
+{
+       const char *const p_adapter_id_str = 
p->mp_fpga->p_fpga_info->mp_adapter_id_str;
+
+       uint32_t value;
+       uint32_t i;
+
+       if (nthw_iic_bus_ready(p)) {
+               /* Reset TX fifo */
+               nthw_iic_reg_control_txfifo_reset(p);
+
+               /* Write device address to TX_FIFO and set start bit!! */
+               value = (dev_addr << 1) | I2C_TRANSMIT_WR;
+               nthw_iic_reg_tx_fifo_write(p, value, 1, 0);
+
+               /* Write a_reg_addr to TX FIFO */
+               nthw_iic_reg_tx_fifo_write(p, a_reg_addr, 0, 1);
+
+               if (!nthw_iic_bus_ready(p)) {
+                       NT_LOG_DBGX(ERR, NTHW, "%s: error:\n", 
p_adapter_id_str);
+                       return -1;
+               }
+
+               /* Write device address + RD bit to TX_FIFO and set start bit!! 
*/
+               value = (dev_addr << 1) | I2C_TRANSMIT_RD;
+               nthw_iic_reg_tx_fifo_write(p, value, 1, 0);
+
+               /* Write data_len to TX_FIFO and set stop bit!! */
+               nthw_iic_reg_tx_fifo_write(p, data_len, 0, 1);
+
+               for (i = 0; i < data_len; i++) {
+                       /* Wait for RX FIFO not empty */
+                       if (!nthw_iic_data_ready(p))
+                               return -1;
+
+                       /* Read data_len bytes from RX_FIFO */
+                       nthw_iic_reg_read_i2c_rx_fifo(p, p_byte);
+                       p_byte++;
+               }
+
+               return 0;
+
+       } else {
+               NT_LOG_DBGX(ERR, NTHW, "%s: error\n", p_adapter_id_str);
+               return -1;
+       }
+
+       return 0;
+}
+
+int nthw_iic_write_data(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       void *p_void)
+{
+       const char *const p_adapter_id_str = 
p->mp_fpga->p_fpga_info->mp_adapter_id_str;
+       int retry = (p->mn_write_data_retry >= 0 ? p->mn_write_data_retry : 10);
+       uint8_t *pb = (uint8_t *)p_void;
+
+       while (nthw_iic_writebyte(p, dev_addr, a_reg_addr, data_len, pb) != 0) {
+               retry--;
+
+               if (retry <= 0) {
+                       NT_LOG(ERR, NTHW,
+                               "%s: I2C%d: Write retry exhausted (dev_addr=%d 
a_reg_addr=%d)\n",
+                               p_adapter_id_str, p->mn_iic_instance, dev_addr, 
a_reg_addr);
+                       return -1;
+
+               } else {
+                       NT_LOG(DBG, NTHW,
+                               "%s: I2C%d: Write retry=%d (dev_addr=%d 
a_reg_addr=%d)\n",
+                               p_adapter_id_str, p->mn_iic_instance, retry, 
dev_addr, a_reg_addr);
+               }
+       }
+
+       return 0;
+}
+
+int nthw_iic_writebyte(nthw_iic_t *p, uint8_t dev_addr, uint8_t a_reg_addr, 
uint8_t data_len,
+       uint8_t *p_byte)
+{
+       const char *const p_adapter_id_str = 
p->mp_fpga->p_fpga_info->mp_adapter_id_str;
+       uint32_t value;
+       int count;
+       int i;
+
+       if (data_len == 0)
+               return -1;
+
+       count = data_len - 1;
+
+       if (nthw_iic_bus_ready(p)) {
+               /* Reset TX fifo */
+               nthw_iic_reg_control_txfifo_reset(p);
+
+               /* Write device address to TX_FIFO and set start bit!! */
+               value = (dev_addr << 1) | I2C_TRANSMIT_WR;
+               nthw_iic_reg_tx_fifo_write(p, value, 1, 0);
+
+               /* Write a_reg_addr to TX FIFO */
+               nthw_iic_reg_tx_fifo_write(p, a_reg_addr, 0, 0);
+
+               for (i = 0; i < count; i++) {
+                       /* Write data byte to TX fifo and set stop bit */
+                       nthw_iic_reg_tx_fifo_write(p, *p_byte, 0, 0);
+                       p_byte++;
+               }
+
+               /* Write data byte to TX fifo and set stop bit */
+               nthw_iic_reg_tx_fifo_write(p, *p_byte, 0, 1);
+
+               if (!nthw_iic_bus_ready(p)) {
+                       NT_LOG_DBGX(WARNING, NTHW, "%s: warn: !busReady\n", 
p_adapter_id_str);
+
+                       while (true)
+                               if (nthw_iic_bus_ready(p)) {
+                                       NT_LOG_DBGX(DEBUG, NTHW, "%s: info: 
busReady\n",
+                                       p_adapter_id_str);
+                                       break;
+                               }
+               }
+
+               return 0;
+
+       } else {
+               NT_LOG_DBGX(WARNING, NTHW, "%s\n", p_adapter_id_str);
+               return -1;
+       }
+}
+
+/*
+ * Support function for read/write functions below. Waits for bus ready.
+ */
+bool nthw_iic_bus_ready(nthw_iic_t *p)
+{
+       int count = (p->mn_bus_ready_retry >= 0 ? p->mn_bus_ready_retry : 1000);
+       bool b_bus_busy = true;
+
+       while (true) {
+               nthw_iic_reg_busbusy(p, &b_bus_busy);
+
+               if (!b_bus_busy)
+                       break;
+
+               count--;
+
+               if (count <= 0) /* Test for timeout */
+                       break;
+
+               if (p->mn_poll_delay != 0)
+                       I2C_WAIT_US(p->mn_poll_delay);
+       }
+
+       if (count == 0)
+               return false;
+
+       return true;
+}
+
+/*
+ * Support function for read function. Waits for data ready.
+ */
+bool nthw_iic_data_ready(nthw_iic_t *p)
+{
+       int count = (p->mn_data_ready_retry >= 0 ? p->mn_data_ready_retry : 
1000);
+       bool b_rx_fifo_empty = true;
+
+       while (true) {
+               nthw_iic_reg_rxfifo_empty(p, &b_rx_fifo_empty);
+
+               if (!b_rx_fifo_empty)
+                       break;
+
+               count--;
+
+               if (count <= 0) /* Test for timeout */
+                       break;
+
+               if (p->mn_poll_delay != 0)
+                       I2C_WAIT_US(p->mn_poll_delay);
+       }
+
+       if (count == 0)
+               return false;
+
+       return true;
+}
+
+int nthw_iic_scan_dev_addr(nthw_iic_t *p, int n_dev_addr, int n_reg_addr)
+{
+       const char *const p_adapter_id_str = 
p->mp_fpga->p_fpga_info->mp_adapter_id_str;
+       (void)p_adapter_id_str;
+       int res;
+       uint8_t data_val = -1;
+       res = nthw_iic_readbyte(p, (uint8_t)n_dev_addr, (uint8_t)n_reg_addr, 1, 
&data_val);
+
+       if (res == 0) {
+               NT_LOG(DBG, NTHW,
+                       "%s: I2C%d: devaddr=0x%02X (%03d) regaddr=%02X val=%02X 
(%03d) res=%d\n",
+                       p_adapter_id_str, p->mn_iic_instance, n_dev_addr, 
n_dev_addr, n_reg_addr,
+                       data_val, data_val, res);
+       }
+
+       return res;
+}
+
+int nthw_iic_scan(nthw_iic_t *p)
+{
+       int i;
+
+       for (i = 0; i < 128; i++)
+               (void)nthw_iic_scan_dev_addr(p, i, 0x00);
+
+       return 0;
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_pcie3.c 
b/drivers/net/ntnic/nthw/core/nthw_pcie3.c
new file mode 100644
index 0000000000..c6cb3ce8de
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_pcie3.c
@@ -0,0 +1,259 @@
+/*
+ * 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_pcie3.h"
+
+#define NTHW_TG_REF_FREQ (250000000ULL)
+
+nthw_pcie3_t *nthw_pcie3_new(void)
+{
+       nthw_pcie3_t *p = malloc(sizeof(nthw_pcie3_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_pcie3_t));
+
+       return p;
+}
+
+void nthw_pcie3_delete(nthw_pcie3_t *p)
+{
+       if (p) {
+               memset(p, 0, sizeof(nthw_pcie3_t));
+               free(p);
+       }
+}
+
+int nthw_pcie3_init(nthw_pcie3_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_PCIE3, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: PCIE3 %d: no such instance\n",
+                       p_fpga->p_fpga_info->mp_adapter_id_str, n_instance);
+               return -1;
+       }
+
+       p->mp_fpga = p_fpga;
+       p->mn_instance = n_instance;
+       p->mp_mod_pcie3 = mod;
+
+       /* PCIe3 */
+       p->mp_reg_stat_ctrl = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_CTRL);
+       p->mp_fld_stat_ctrl_ena =
+               nthw_register_get_field(p->mp_reg_stat_ctrl, 
PCIE3_STAT_CTRL_STAT_ENA);
+       p->mp_fld_stat_ctrl_req =
+               nthw_register_get_field(p->mp_reg_stat_ctrl, 
PCIE3_STAT_CTRL_STAT_REQ);
+
+       p->mp_reg_stat_rx = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_RX);
+       p->mp_fld_stat_rx_counter =
+               nthw_register_get_field(p->mp_reg_stat_rx, 
PCIE3_STAT_RX_COUNTER);
+
+       p->mp_reg_stat_tx = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_TX);
+       p->mp_fld_stat_tx_counter =
+               nthw_register_get_field(p->mp_reg_stat_tx, 
PCIE3_STAT_TX_COUNTER);
+
+       p->mp_reg_stat_ref_clk = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_REFCLK);
+       p->mp_fld_stat_ref_clk_ref_clk =
+               nthw_register_get_field(p->mp_reg_stat_ref_clk, 
PCIE3_STAT_REFCLK_REFCLK250);
+
+       p->mp_reg_stat_rq_rdy = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_RQ_RDY);
+       p->mp_fld_stat_rq_rdy_counter =
+               nthw_register_get_field(p->mp_reg_stat_rq_rdy, 
PCIE3_STAT_RQ_RDY_COUNTER);
+
+       p->mp_reg_stat_rq_vld = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STAT_RQ_VLD);
+       p->mp_fld_stat_rq_vld_counter =
+               nthw_register_get_field(p->mp_reg_stat_rq_vld, 
PCIE3_STAT_RQ_VLD_COUNTER);
+
+       p->mp_reg_status0 = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_STATUS0);
+       p->mp_fld_status0_tags_in_use =
+               nthw_register_get_field(p->mp_reg_status0, 
PCIE3_STATUS0_TAGS_IN_USE);
+
+       p->mp_reg_rp_to_ep_err = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_RP_TO_EP_ERR);
+       p->mp_fld_rp_to_ep_err_cor =
+               nthw_register_get_field(p->mp_reg_rp_to_ep_err, 
PCIE3_RP_TO_EP_ERR_ERR_COR);
+       p->mp_fld_rp_to_ep_err_non_fatal =
+               nthw_register_get_field(p->mp_reg_rp_to_ep_err, 
PCIE3_RP_TO_EP_ERR_ERR_NONFATAL);
+       p->mp_fld_rp_to_ep_err_fatal =
+               nthw_register_get_field(p->mp_reg_rp_to_ep_err, 
PCIE3_RP_TO_EP_ERR_ERR_FATAL);
+
+       p->mp_reg_ep_to_rp_err = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_EP_TO_RP_ERR);
+       p->mp_fld_ep_to_rp_err_cor =
+               nthw_register_get_field(p->mp_reg_ep_to_rp_err, 
PCIE3_EP_TO_RP_ERR_ERR_COR);
+       p->mp_fld_ep_to_rp_err_non_fatal =
+               nthw_register_get_field(p->mp_reg_ep_to_rp_err, 
PCIE3_EP_TO_RP_ERR_ERR_NONFATAL);
+       p->mp_fld_ep_to_rp_err_fatal =
+               nthw_register_get_field(p->mp_reg_ep_to_rp_err, 
PCIE3_EP_TO_RP_ERR_ERR_FATAL);
+
+       p->mp_reg_sample_time = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_SAMPLE_TIME);
+       p->mp_fld_sample_time =
+               nthw_register_get_field(p->mp_reg_sample_time, 
PCIE3_SAMPLE_TIME_SAMPLE_TIME);
+
+       p->mp_reg_pci_end_point = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_PCI_ENDPOINT);
+       p->mp_fld_pci_end_point_if_id =
+               nthw_register_get_field(p->mp_reg_pci_end_point, 
PCIE3_PCI_ENDPOINT_IF_ID);
+       p->mp_fld_pci_end_point_send_msg =
+               nthw_register_get_field(p->mp_reg_pci_end_point, 
PCIE3_PCI_ENDPOINT_SEND_MSG);
+       p->mp_fld_pci_end_point_get_msg =
+               nthw_register_get_field(p->mp_reg_pci_end_point, 
PCIE3_PCI_ENDPOINT_GET_MSG);
+       p->mp_fld_pci_end_point_dmaep0_allow_mask =
+               nthw_register_get_field(p->mp_reg_pci_end_point,
+                       PCIE3_PCI_ENDPOINT_DMA_EP0_ALLOW_MASK);
+       p->mp_fld_pci_end_point_dmaep1_allow_mask =
+               nthw_register_get_field(p->mp_reg_pci_end_point,
+                       PCIE3_PCI_ENDPOINT_DMA_EP1_ALLOW_MASK);
+
+       if (p->mp_reg_pci_end_point)
+               nthw_register_update(p->mp_reg_pci_end_point);
+
+       p->mp_reg_pci_test0 = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_PCI_TEST0);
+       p->mp_fld_pci_test0 = nthw_register_get_field(p->mp_reg_pci_test0, 
PCIE3_PCI_TEST0_DATA);
+
+       if (p->mp_reg_pci_test0)
+               nthw_register_update(p->mp_reg_pci_test0);
+
+       p->mp_reg_pci_test1 = nthw_module_get_register(p->mp_mod_pcie3, 
PCIE3_PCI_TEST1);
+       p->mp_fld_pci_test1 = nthw_register_get_field(p->mp_reg_pci_test1, 
PCIE3_PCI_TEST1_DATA);
+
+       if (p->mp_reg_pci_test1)
+               nthw_register_update(p->mp_reg_pci_test1);
+
+       p->mp_reg_pci_e3_mark_adr_lsb =
+               nthw_module_get_register(p->mp_mod_pcie3, PCIE3_MARKADR_LSB);
+       p->mp_fld_pci_e3_mark_adr_lsb_adr =
+               nthw_register_get_field(p->mp_reg_pci_e3_mark_adr_lsb, 
PCIE3_MARKADR_LSB_ADR);
+
+       if (p->mp_reg_pci_e3_mark_adr_lsb)
+               nthw_register_update(p->mp_reg_pci_e3_mark_adr_lsb);
+
+       p->mp_reg_pci_e3_mark_adr_msb =
+               nthw_module_get_register(p->mp_mod_pcie3, PCIE3_MARKADR_MSB);
+       p->mp_fld_pci_e3_mark_adr_msb_adr =
+               nthw_register_get_field(p->mp_reg_pci_e3_mark_adr_msb, 
PCIE3_MARKADR_MSB_ADR);
+
+       if (p->mp_reg_pci_e3_mark_adr_msb)
+               nthw_register_update(p->mp_reg_pci_e3_mark_adr_msb);
+
+       /* Initial setup - disable markerscheme and bifurcation */
+       if (p->mp_fld_pci_end_point_dmaep0_allow_mask)
+               nthw_field_clr_flush(p->mp_fld_pci_end_point_dmaep0_allow_mask);
+
+       if (p->mp_fld_pci_end_point_dmaep1_allow_mask)
+               nthw_field_clr_flush(p->mp_fld_pci_end_point_dmaep1_allow_mask);
+
+       if (p->mp_fld_pci_e3_mark_adr_lsb_adr)
+               nthw_field_set_val_flush32(p->mp_fld_pci_e3_mark_adr_lsb_adr, 
0UL);
+
+       if (p->mp_fld_pci_e3_mark_adr_msb_adr)
+               nthw_field_set_val_flush32(p->mp_fld_pci_e3_mark_adr_msb_adr, 
0UL);
+
+       if (p->mp_fld_pci_end_point_dmaep0_allow_mask)
+               nthw_field_set_flush(p->mp_fld_pci_end_point_dmaep0_allow_mask);
+
+       if (p->mp_fld_pci_end_point_dmaep1_allow_mask)
+               nthw_field_clr_flush(p->mp_fld_pci_end_point_dmaep1_allow_mask);
+
+       return 0;
+};
+
+int nthw_pcie3_trigger_sample_time(nthw_pcie3_t *p)
+{
+       nthw_field_set_val_flush32(p->mp_fld_sample_time, 0xfee1dead);
+
+       return 0;
+}
+
+int nthw_pcie3_stat_req_enable(nthw_pcie3_t *p)
+{
+       nthw_field_set_all(p->mp_fld_stat_ctrl_ena);
+       nthw_field_set_all(p->mp_fld_stat_ctrl_req);
+       nthw_field_flush_register(p->mp_fld_stat_ctrl_req);
+       return 0;
+}
+
+int nthw_pcie3_stat_req_disable(nthw_pcie3_t *p)
+{
+       nthw_field_clr_all(p->mp_fld_stat_ctrl_ena);
+       nthw_field_set_all(p->mp_fld_stat_ctrl_req);
+       nthw_field_flush_register(p->mp_fld_stat_ctrl_req);
+       return 0;
+}
+
+int nthw_pcie3_get_stat(nthw_pcie3_t *p, uint32_t *p_rx_cnt, uint32_t 
*p_tx_cnt,
+       uint32_t *p_ref_clk_cnt, uint32_t *p_tg_unit_size, uint32_t 
*p_tg_ref_freq,
+       uint32_t *p_tag_use_cnt, uint32_t *p_rq_rdy_cnt, uint32_t *p_rq_vld_cnt)
+{
+       *p_rx_cnt = nthw_field_get_updated(p->mp_fld_stat_rx_counter);
+       *p_tx_cnt = nthw_field_get_updated(p->mp_fld_stat_tx_counter);
+
+       *p_ref_clk_cnt = nthw_field_get_updated(p->mp_fld_stat_ref_clk_ref_clk);
+
+       *p_tg_unit_size = NTHW_TG_CNT_SIZE;
+       *p_tg_ref_freq = NTHW_TG_REF_FREQ;
+
+       *p_tag_use_cnt = nthw_field_get_updated(p->mp_fld_status0_tags_in_use);
+
+       *p_rq_rdy_cnt = nthw_field_get_updated(p->mp_fld_stat_rq_rdy_counter);
+       *p_rq_vld_cnt = nthw_field_get_updated(p->mp_fld_stat_rq_vld_counter);
+
+       return 0;
+}
+
+int nthw_pcie3_get_stat_rate(nthw_pcie3_t *p, uint64_t *p_pci_rx_rate, 
uint64_t *p_pci_tx_rate,
+       uint64_t *p_ref_clk_cnt, uint64_t *p_tag_use_cnt,
+       uint64_t *p_pci_nt_bus_util, uint64_t *p_pci_xil_bus_util)
+{
+       uint32_t rx_cnt, tx_cnt, ref_clk_cnt;
+       uint32_t tg_unit_size, tg_ref_freq;
+       uint32_t tag_use_cnt, rq_rdy_cnt, rq_vld_cnt;
+
+       nthw_pcie3_get_stat(p, &rx_cnt, &tx_cnt, &ref_clk_cnt, &tg_unit_size, 
&tg_ref_freq,
+               &tag_use_cnt, &rq_rdy_cnt, &rq_vld_cnt);
+
+       if (ref_clk_cnt) {
+               uint64_t nt_bus_util, xil_bus_util;
+               uint64_t rx_rate, tx_rate;
+
+               rx_rate = ((uint64_t)rx_cnt * tg_unit_size * tg_ref_freq) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_rx_rate = rx_rate;
+
+               tx_rate = ((uint64_t)tx_cnt * tg_unit_size * tg_ref_freq) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_tx_rate = tx_rate;
+
+               *p_ref_clk_cnt = ref_clk_cnt;
+
+               *p_tag_use_cnt = tag_use_cnt;
+
+               nt_bus_util = ((uint64_t)rq_vld_cnt * 1000000ULL) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_nt_bus_util = nt_bus_util;
+               xil_bus_util = ((uint64_t)rq_rdy_cnt * 1000000ULL) / 
(uint64_t)ref_clk_cnt;
+               *p_pci_xil_bus_util = xil_bus_util;
+
+       } else {
+               *p_ref_clk_cnt = 0;
+               *p_pci_nt_bus_util = 0;
+               *p_pci_xil_bus_util = 0;
+       }
+
+       return 0;
+}
+
+int nthw_pcie3_end_point_counters_sample_post(nthw_pcie3_t *p,
+       struct nthw_hif_end_point_counters *epc)
+{
+       NT_LOG_DBGX(DEBUG, NTHW);
+       assert(epc);
+       nthw_pcie3_get_stat_rate(p, &epc->cur_tx, &epc->cur_rx, 
&epc->n_ref_clk_cnt,
+               &epc->n_tags_in_use, &epc->cur_pci_nt_util,
+               &epc->cur_pci_xil_util);
+       return 0;
+}
diff --git a/drivers/net/ntnic/nthw/nthw_drv.h 
b/drivers/net/ntnic/nthw/nthw_drv.h
index 0b89a5c5a0..d7cd64bb1d 100644
--- a/drivers/net/ntnic/nthw/nthw_drv.h
+++ b/drivers/net/ntnic/nthw/nthw_drv.h
@@ -6,8 +6,7 @@
 #ifndef __NTHW_DRV_H__
 #define __NTHW_DRV_H__
 
-#include <stddef.h>
-#include "nthw_platform_drv.h"
+#include "nthw_core.h"
 
 typedef enum nt_meta_port_type_e {
        PORT_TYPE_PHYSICAL,
diff --git a/drivers/net/ntnic/nthw/nthw_rac.c 
b/drivers/net/ntnic/nthw/nthw_rac.c
new file mode 100644
index 0000000000..2aef0c148f
--- /dev/null
+++ b/drivers/net/ntnic/nthw/nthw_rac.c
@@ -0,0 +1,784 @@
+/*
+ * 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_rac.h"
+
+#include <pthread.h>
+
+
+#define RAB_READ (0x01)
+#define RAB_WRITE (0x02)
+#define RAB_ECHO (0x08)
+#define RAB_COMPLETION (0x0F)
+
+#define RAB_OPR_LO (28)
+
+#define RAB_CNT_LO (20)
+#define RAB_CNT_BW (8)
+
+#define RAB_BUSID_LO (16)
+#define RAB_BUSID_BW (4)
+
+#define RAB_ADDR_BW (16)
+
+nthw_rac_t *nthw_rac_new(void)
+{
+       nthw_rac_t *p = malloc(sizeof(nthw_rac_t));
+       memset(p, 0, sizeof(nthw_rac_t));
+       return p;
+}
+
+int nthw_rac_init(nthw_rac_t *p, nthw_fpga_t *p_fpga, struct fpga_info_s 
*p_fpga_info)
+{
+       assert(p_fpga_info);
+
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       nthw_module_t *p_mod = nthw_fpga_query_module(p_fpga, MOD_RAC, 0);
+
+       if (p == NULL)
+               return p_mod == NULL ? -1 : 0;
+
+       if (p_mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: RAC %d: no such instance\n", 
p_adapter_id_str, 0);
+               return -1;
+       }
+
+       p->mp_fpga = p_fpga;
+       p->mp_mod_rac = p_mod;
+
+       p->mn_param_rac_rab_interfaces =
+               nthw_fpga_get_product_param(p->mp_fpga, NT_RAC_RAB_INTERFACES, 
3);
+       NT_LOG(DBG, NTHW, "%s: NT_RAC_RAB_INTERFACES=%d\n", p_adapter_id_str,
+               p->mn_param_rac_rab_interfaces);
+
+       p->mn_param_rac_rab_ob_update =
+               nthw_fpga_get_product_param(p->mp_fpga, NT_RAC_RAB_OB_UPDATE, 
0);
+       NT_LOG(DBG, NTHW, "%s: NT_RAC_RAB_OB_UPDATE=%d\n", p_adapter_id_str,
+               p->mn_param_rac_rab_ob_update);
+
+       /* Optional dummy test registers */
+       p->mp_reg_dummy0 = nthw_module_query_register(p->mp_mod_rac, 
RAC_DUMMY0);
+       p->mp_reg_dummy1 = nthw_module_query_register(p->mp_mod_rac, 
RAC_DUMMY1);
+       p->mp_reg_dummy2 = nthw_module_query_register(p->mp_mod_rac, 
RAC_DUMMY2);
+
+       p->mp_reg_rab_init = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_INIT);
+       p->mp_fld_rab_init = nthw_register_get_field(p->mp_reg_rab_init, 
RAC_RAB_INIT_RAB);
+       p->mn_fld_rab_init_bw = nthw_field_get_bit_width(p->mp_fld_rab_init);
+       p->mn_fld_rab_init_mask = nthw_field_get_mask(p->mp_fld_rab_init);
+
+       /* RAC_RAB_INIT_RAB reg/field sanity checks: */
+       assert(p->mn_fld_rab_init_mask == ((1UL << p->mn_fld_rab_init_bw) - 1));
+       assert(p->mn_fld_rab_init_bw == p->mn_param_rac_rab_interfaces);
+
+       p->mp_reg_dbg_ctrl = nthw_module_query_register(p->mp_mod_rac, 
RAC_DBG_CTRL);
+
+       if (p->mp_reg_dbg_ctrl)
+               p->mp_fld_dbg_ctrl = 
nthw_register_query_field(p->mp_reg_dbg_ctrl, RAC_DBG_CTRL_C);
+
+       else
+               p->mp_fld_dbg_ctrl = NULL;
+
+       p->mp_reg_dbg_data = nthw_module_query_register(p->mp_mod_rac, 
RAC_DBG_DATA);
+
+       if (p->mp_reg_dbg_data)
+               p->mp_fld_dbg_data = 
nthw_register_query_field(p->mp_reg_dbg_data, RAC_DBG_DATA_D);
+
+       else
+               p->mp_reg_dbg_data = NULL;
+
+       p->mp_reg_rab_ib_data = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_IB_DATA);
+       p->mp_fld_rab_ib_data = nthw_register_get_field(p->mp_reg_rab_ib_data, 
RAC_RAB_IB_DATA_D);
+
+       p->mp_reg_rab_ob_data = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_OB_DATA);
+       p->mp_fld_rab_ob_data = nthw_register_get_field(p->mp_reg_rab_ob_data, 
RAC_RAB_OB_DATA_D);
+
+       p->mp_reg_rab_buf_free = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_BUF_FREE);
+       p->mp_fld_rab_buf_free_ib_free =
+               nthw_register_get_field(p->mp_reg_rab_buf_free, 
RAC_RAB_BUF_FREE_IB_FREE);
+       p->mp_fld_rab_buf_free_ib_ovf =
+               nthw_register_get_field(p->mp_reg_rab_buf_free, 
RAC_RAB_BUF_FREE_IB_OVF);
+       p->mp_fld_rab_buf_free_ob_free =
+               nthw_register_get_field(p->mp_reg_rab_buf_free, 
RAC_RAB_BUF_FREE_OB_FREE);
+       p->mp_fld_rab_buf_free_ob_ovf =
+               nthw_register_get_field(p->mp_reg_rab_buf_free, 
RAC_RAB_BUF_FREE_OB_OVF);
+       p->mp_fld_rab_buf_free_timeout =
+               nthw_register_get_field(p->mp_reg_rab_buf_free, 
RAC_RAB_BUF_FREE_TIMEOUT);
+
+       p->mp_reg_rab_buf_used = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_BUF_USED);
+       p->mp_fld_rab_buf_used_ib_used =
+               nthw_register_get_field(p->mp_reg_rab_buf_used, 
RAC_RAB_BUF_USED_IB_USED);
+       p->mp_fld_rab_buf_used_ob_used =
+               nthw_register_get_field(p->mp_reg_rab_buf_used, 
RAC_RAB_BUF_USED_OB_USED);
+       p->mp_fld_rab_buf_used_flush =
+               nthw_register_get_field(p->mp_reg_rab_buf_used, 
RAC_RAB_BUF_USED_FLUSH);
+
+       /*
+        * RAC_RAB_DMA regs are optional - only found in real
+        * NT4GA - not found in 9231/9232 and earlier
+        */
+       p->mp_reg_rab_dma_ib_lo = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_IB_LO);
+       p->mp_fld_rab_dma_ib_lo_phy_addr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ib_lo, 
RAC_RAB_DMA_IB_LO_PHYADDR);
+
+       p->mp_reg_rab_dma_ib_hi = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_IB_HI);
+       p->mp_fld_rab_dma_ib_hi_phy_addr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ib_hi, 
RAC_RAB_DMA_IB_HI_PHYADDR);
+
+       p->mp_reg_rab_dma_ob_lo = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_OB_LO);
+       p->mp_fld_rab_dma_ob_lo_phy_addr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ob_lo, 
RAC_RAB_DMA_OB_LO_PHYADDR);
+
+       p->mp_reg_rab_dma_ob_hi = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_OB_HI);
+       p->mp_fld_rab_dma_ob_hi_phy_addr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ob_hi, 
RAC_RAB_DMA_OB_HI_PHYADDR);
+
+       p->mp_reg_rab_dma_ib_wr = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_IB_WR);
+       p->mp_fld_rab_dma_ib_wr_ptr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ib_wr, 
RAC_RAB_DMA_IB_WR_PTR);
+
+       p->mp_reg_rab_dma_ib_rd = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_IB_RD);
+       p->mp_fld_rab_dma_ib_rd_ptr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ib_rd, 
RAC_RAB_DMA_IB_RD_PTR);
+
+       p->mp_reg_rab_dma_ob_wr = nthw_module_get_register(p->mp_mod_rac, 
RAC_RAB_DMA_OB_WR);
+       p->mp_fld_rab_dma_ob_wr_ptr =
+               nthw_register_get_field(p->mp_reg_rab_dma_ob_wr, 
RAC_RAB_DMA_OB_WR_PTR);
+
+       p->RAC_RAB_INIT_ADDR = nthw_register_get_address(p->mp_reg_rab_init);
+       p->RAC_RAB_IB_DATA_ADDR = 
nthw_register_get_address(p->mp_reg_rab_ib_data);
+       p->RAC_RAB_OB_DATA_ADDR = 
nthw_register_get_address(p->mp_reg_rab_ob_data);
+       p->RAC_RAB_BUF_FREE_ADDR = 
nthw_register_get_address(p->mp_reg_rab_buf_free);
+       p->RAC_RAB_BUF_USED_ADDR = 
nthw_register_get_address(p->mp_reg_rab_buf_used);
+
+       /*
+        * RAC_RAB_DMA regs are optional - only found in real NT4GA - not found 
in 9231/9232 and
+        * earlier
+        */
+
+       p->RAC_RAB_DMA_IB_LO_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ib_lo);
+       p->RAC_RAB_DMA_IB_HI_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ib_hi);
+       p->RAC_RAB_DMA_OB_LO_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ob_lo);
+       p->RAC_RAB_DMA_OB_HI_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ob_hi);
+       p->RAC_RAB_DMA_IB_RD_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ib_rd);
+       p->RAC_RAB_DMA_OB_WR_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ob_wr);
+       p->RAC_RAB_DMA_IB_WR_ADDR = 
nthw_register_get_address(p->mp_reg_rab_dma_ib_wr);
+
+       p->RAC_RAB_BUF_FREE_IB_FREE_MASK = 
nthw_field_get_mask(p->mp_fld_rab_buf_free_ib_free);
+       p->RAC_RAB_BUF_FREE_OB_FREE_MASK = 
nthw_field_get_mask(p->mp_fld_rab_buf_free_ob_free);
+       p->RAC_RAB_BUF_USED_IB_USED_MASK = 
nthw_field_get_mask(p->mp_fld_rab_buf_used_ib_used);
+       p->RAC_RAB_BUF_USED_OB_USED_MASK = 
nthw_field_get_mask(p->mp_fld_rab_buf_used_ob_used);
+
+       p->RAC_RAB_BUF_USED_FLUSH_MASK = 
nthw_field_get_mask(p->mp_fld_rab_buf_used_flush);
+
+       p->RAC_RAB_BUF_USED_OB_USED_LOW =
+               nthw_field_get_bit_pos_low(p->mp_fld_rab_buf_used_ob_used);
+
+       p->mp_reg_rab_nmb_rd = nthw_module_query_register(p->mp_mod_rac, 
RAC_NMB_RD_ADR);
+
+       if (p->mp_reg_rab_nmb_rd)
+               p->RAC_NMB_RD_ADR_ADDR = 
nthw_register_get_address(p->mp_reg_rab_nmb_rd);
+
+       p->mp_reg_rab_nmb_data = nthw_module_query_register(p->mp_mod_rac, 
RAC_NMB_DATA);
+
+       if (p->mp_reg_rab_nmb_data)
+               p->RAC_NMB_DATA_ADDR = 
nthw_register_get_address(p->mp_reg_rab_nmb_data);
+
+       p->mp_reg_rab_nmb_wr = nthw_module_query_register(p->mp_mod_rac, 
RAC_NMB_WR_ADR);
+
+       if (p->mp_reg_rab_nmb_wr)
+               p->RAC_NMB_WR_ADR_ADDR = 
nthw_register_get_address(p->mp_reg_rab_nmb_wr);
+
+       p->mp_reg_rab_nmb_status = nthw_module_query_register(p->mp_mod_rac, 
RAC_NMB_STATUS);
+
+       if (p->mp_reg_rab_nmb_status)
+               p->RAC_NMB_STATUS_ADDR = 
nthw_register_get_address(p->mp_reg_rab_nmb_status);
+
+       p->m_dma = NULL;
+
+       {
+               /*
+                * RAC is a primary communication channel - debug will be messy
+                * turn off debug by default - except for rac_rab_init
+                * NOTE: currently debug will not work - due to optimizations
+                */
+               const int n_debug_mode = 
nthw_module_get_debug_mode(p->mp_mod_rac);
+
+               if (n_debug_mode && n_debug_mode <= 0xff) {
+                       nthw_module_set_debug_mode(p->mp_mod_rac, 0);
+                       nthw_register_set_debug_mode(p->mp_reg_rab_init, 
n_debug_mode);
+               }
+       }
+
+       pthread_mutex_init(&p->m_mutex, NULL);
+
+       return 0;
+}
+
+static int nthw_rac_get_rab_interface_count(const nthw_rac_t *p)
+{
+       return p->mn_param_rac_rab_interfaces;
+}
+
+/* private function for internal RAC operations -
+ * improves log flexibility and prevents log flooding
+ */
+static void nthw_rac_reg_read32(const struct fpga_info_s *p_fpga_info, 
uint32_t reg_addr,
+       uint32_t *p_data)
+{
+       *p_data = *(volatile uint32_t *)((uint8_t *)p_fpga_info->bar0_addr + 
reg_addr);
+}
+
+/* private function for internal RAC operations -
+ * improves log flexibility and prevents log flooding
+ */
+static void nthw_rac_reg_write32(const struct fpga_info_s *p_fpga_info, 
uint32_t reg_addr,
+       uint32_t n_data)
+{
+       *(volatile uint32_t *)((uint8_t *)p_fpga_info->bar0_addr + reg_addr) = 
n_data;
+}
+
+static inline int _nthw_rac_wait_for_rab_done(const nthw_rac_t *p, uint32_t 
address,
+       uint32_t word_cnt)
+{
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       uint32_t used = 0;
+       uint32_t retry;
+
+       for (retry = 0; retry < 100000; retry++) {
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR, 
&used);
+               used = (used & p->RAC_RAB_BUF_USED_OB_USED_MASK) >>
+                       p->RAC_RAB_BUF_USED_OB_USED_LOW;
+
+               if (used >= word_cnt)
+                       break;
+       }
+
+       if (used < word_cnt) {
+               NT_LOG(ERR, NTHW, "%s: Fail rab bus r/w addr=0x%08X used=%x 
wordcount=%d\n",
+                       p_adapter_id_str, address, used, word_cnt);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * NT_PCI_REG_P9xyz_RAC_RAB_INIT
+ *
+ * Initializes (resets) the programmable registers on the Register Access 
Buses (RAB).
+ * This initialization must be performed by software as part of the driver 
load procedure.
+ *
+ * Bit n of this field initializes the programmable registers on RAB interface 
n.
+ * Software must write one to the bit and then clear the bit again.
+ *
+ * All RAB module registers will be reset to their defaults.
+ * This includes the product specific RESET module (eg RST9xyz)
+ * As a consequence of this behavior the official reset sequence
+ * must be excersised - as all RAB modules will be held in reset.
+ */
+int nthw_rac_rab_init(nthw_rac_t *p, uint32_t n_rab_intf_mask)
+{
+       /*
+        * Write rac_rab_init
+        * Perform operation twice - first to get trace of operation -
+        * second to get things done...
+        */
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       nthw_field_set_val_flush32(p->mp_fld_rab_init, n_rab_intf_mask);
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_INIT_ADDR, 
n_rab_intf_mask);
+       return 0;
+}
+
+int nthw_rac_rab_reset(nthw_rac_t *p)
+{
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       (void)p_adapter_id_str;
+
+       /* RAC RAB bus "flip/flip" reset */
+       const int n_rac_rab_bus_count = nthw_rac_get_rab_interface_count(p);
+       const int n_rac_rab_bus_mask = (1 << n_rac_rab_bus_count) - 1;
+
+       NT_LOG(DBG, NTHW, "%s: NT_RAC_RAB_INTERFACES=%d (0x%02X)\n", 
p_adapter_id_str,
+               n_rac_rab_bus_count, n_rac_rab_bus_mask);
+       assert(n_rac_rab_bus_count);
+       assert(n_rac_rab_bus_mask);
+
+       /* RAC RAB bus "flip/flip" reset first stage - new impl (ref RMT#37020) 
*/
+       nthw_rac_rab_init(p, 0);
+       nthw_rac_rab_init(p, n_rac_rab_bus_mask);
+       nthw_rac_rab_init(p, n_rac_rab_bus_mask & ~0x01);
+
+       return 0;
+}
+
+int nthw_rac_rab_setup(nthw_rac_t *p)
+{
+       int rc = 0;
+
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       uint32_t n_dma_buf_size = 2L * RAB_DMA_BUF_CNT * sizeof(uint32_t);
+       const size_t align_size = nt_util_align_size(n_dma_buf_size);
+       int numa_node = p_fpga_info->numa_node;
+       uint64_t dma_addr;
+       uint32_t buf;
+
+       if (!p->m_dma) {
+               struct nt_dma_s *vfio_dma;
+               /* FPGA needs Page alignment (4K) */
+               vfio_dma = nt_dma_alloc(align_size, 0x1000, numa_node);
+
+               if (vfio_dma == NULL) {
+                       NT_LOG(ERR, NTNIC, "nt_dma_alloc failed\n");
+                       return -1;
+               }
+
+               p->m_dma_in_buf = (uint32_t *)vfio_dma->addr;
+               p->m_dma_out_buf = p->m_dma_in_buf + RAB_DMA_BUF_CNT;
+               p->m_dma = vfio_dma;
+       }
+
+       /* Setup DMA on the adapter */
+       dma_addr = p->m_dma->iova;
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_DMA_IB_LO_ADDR, dma_addr & 
0xffffffff);
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_DMA_IB_HI_ADDR,
+               (uint32_t)(dma_addr >> 32) & 0xffffffff);
+       dma_addr += RAB_DMA_BUF_CNT * sizeof(uint32_t);
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_DMA_OB_LO_ADDR, dma_addr & 
0xffffffff);
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_DMA_OB_HI_ADDR,
+               (uint32_t)(dma_addr >> 32) & 0xffffffff);
+
+       /* Set initial value of internal pointers */
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_DMA_IB_RD_ADDR, &buf);
+       p->m_dma_in_ptr_wr = (uint16_t)(buf / sizeof(uint32_t));
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_DMA_OB_WR_ADDR, &buf);
+       p->m_dma_out_ptr_rd = (uint16_t)(buf / sizeof(uint32_t));
+       p->m_in_free = RAB_DMA_BUF_CNT;
+
+       return rc;
+}
+
+void nthw_rac_bar0_read32(const struct fpga_info_s *p_fpga_info, uint32_t 
reg_addr,
+       uint32_t word_cnt, uint32_t *p_data)
+{
+       volatile const uint32_t *const src_addr =
+               (uint32_t *)((uint8_t *)p_fpga_info->bar0_addr + reg_addr);
+
+       for (uint32_t i = 0; i < word_cnt; i++)
+               p_data[i] = src_addr[i];
+}
+
+void nthw_rac_bar0_write32(const struct fpga_info_s *p_fpga_info, uint32_t 
reg_addr,
+       uint32_t word_cnt, const uint32_t *p_data)
+{
+       volatile uint32_t *const dst_addr =
+               (uint32_t *)((uint8_t *)p_fpga_info->bar0_addr + reg_addr);
+
+       for (uint32_t i = 0; i < word_cnt; i++)
+               dst_addr[i] = p_data[i];
+}
+
+int nthw_rac_rab_write32(nthw_rac_t *p, bool trc, nthw_rab_bus_id_t bus_id, 
uint32_t address,
+       uint32_t word_cnt, const uint32_t *p_data)
+{
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       uint32_t buf_used;
+       uint32_t buf_free;
+       uint32_t in_buf_free;
+       uint32_t out_buf_free;
+       int res = 0;
+
+       if (address > (1 << RAB_ADDR_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal address: value too large %d 
- max %d\n",
+                       p_adapter_id_str, address, (1 << RAB_ADDR_BW));
+               return -1;
+       }
+
+       if (bus_id > (1 << RAB_BUSID_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal bus id: value too large %d 
- max %d\n",
+                       p_adapter_id_str, bus_id, (1 << RAB_BUSID_BW));
+               return -1;
+       }
+
+       if (word_cnt == 0) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal word count: value is zero 
(%d)\n",
+                       p_adapter_id_str, word_cnt);
+               return -1;
+       }
+
+       if (word_cnt > (1 << RAB_CNT_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal word count: value too large 
%d - max %d\n",
+                       p_adapter_id_str, word_cnt, (1 << RAB_CNT_BW));
+               return -1;
+       }
+
+       pthread_mutex_lock(&p->m_mutex);
+
+       if (p->m_dma_active) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal operation: DMA enabled\n", 
p_adapter_id_str);
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+       /* Read buffer free register */
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_FREE_ADDR, &buf_free);
+
+       in_buf_free = buf_free & p->RAC_RAB_BUF_FREE_IB_FREE_MASK;
+       out_buf_free = (buf_free & p->RAC_RAB_BUF_FREE_OB_FREE_MASK) >> 16;
+
+       /* Read buffer used register */
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR, &buf_used);
+
+       buf_used =
+               buf_used & (p->RAC_RAB_BUF_USED_IB_USED_MASK | 
p->RAC_RAB_BUF_USED_OB_USED_MASK);
+
+       /*
+        * Verify that output buffer can hold one completion word,
+        * input buffer can hold the number of words to be written +
+        * one write and one completion command
+        * and that the input and output "used" buffer is 0
+        */
+       if (out_buf_free >= 1 && in_buf_free >= word_cnt + 2 && buf_used == 0) {
+               const uint32_t rab_oper_cmpl = (RAB_COMPLETION << RAB_OPR_LO);
+               uint32_t rab_echo_oper_cmpl;
+               uint32_t word_cnt_expected = 1;
+               uint32_t rab_oper_wr;
+               uint32_t i;
+
+               rab_oper_wr = (RAB_WRITE << RAB_OPR_LO) |
+                       ((word_cnt & ((1 << RAB_CNT_BW) - 1)) << RAB_CNT_LO) |
+                       (bus_id << RAB_BUSID_LO) | address;
+
+               if (trc) {
+                       rab_oper_wr |= (RAB_ECHO << RAB_OPR_LO);
+                       word_cnt_expected += word_cnt + 1;
+               }
+
+               /* Write command */
+               nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_IB_DATA_ADDR, 
rab_oper_wr);
+
+               /* Write data to input buffer */
+               for (i = 0; i < word_cnt; i++)
+                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_IB_DATA_ADDR, p_data[i]);
+
+               /* Write completion command */
+               nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_IB_DATA_ADDR, 
rab_oper_cmpl);
+
+               /* Wait until done */
+               if (_nthw_rac_wait_for_rab_done(p, address, word_cnt_expected)) 
{
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               if (trc) {
+                       uint32_t rab_echo_oper_wr;
+                       nthw_rac_reg_read32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR,
+                               &rab_echo_oper_wr);
+
+                       if (p->mn_param_rac_rab_ob_update)
+                               nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR, 0);
+
+                       if (rab_oper_wr != rab_echo_oper_wr) {
+                               NT_LOG(ERR, NTHW,
+                                       "%s: expected rab read echo oper 
(0x%08X) - read (0x%08X)\n",
+                                       p_adapter_id_str, rab_oper_wr, 
rab_echo_oper_wr);
+                       }
+               }
+
+               {
+                       /* Read data from output buffer */
+                       uint32_t data;
+                       char *tmp_string;
+
+                       if (trc) {
+                               tmp_string = 
ntlog_helper_str_alloc("Register::write");
+                               ntlog_helper_str_add(tmp_string,
+                                       "(Dev: NA, Bus: RAB%u, Addr: 0x%08X, 
Cnt: %d, Data:",
+                                       bus_id, address, word_cnt);
+                       }
+
+                       for (i = 0; i < word_cnt; i++) {
+                               nthw_rac_reg_read32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR, &data);
+
+                               if (p->mn_param_rac_rab_ob_update) {
+                                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR,
+                                               0);
+                               }
+
+                               if (trc)
+                                       ntlog_helper_str_add(tmp_string, " 
0x%08X", data);
+                       }
+
+                       if (trc) {
+                               ntlog_helper_str_add(tmp_string, ")");
+                               NT_LOG(DBG, NTHW, "%s", tmp_string);
+                               ntlog_helper_str_free(tmp_string);
+                       }
+               }
+
+               /* Read completion from out buffer */
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_OB_DATA_ADDR, 
&rab_echo_oper_cmpl);
+
+               if (p->mn_param_rac_rab_ob_update)
+                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR, 0);
+
+               if (rab_echo_oper_cmpl != rab_oper_cmpl) {
+                       NT_LOG(ERR, NTHW,
+                               "%s: RAB: Unexpected value of completion 
(0x%08X)- inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 0x%08X\n",
+                               p_adapter_id_str, rab_echo_oper_cmpl, 
in_buf_free, out_buf_free,
+                               buf_used);
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               /* Read buffer free register */
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_FREE_ADDR, 
&buf_free);
+
+               if (buf_free & 0x80000000) {
+                       /* Clear Timeout and overflow bits */
+                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_BUF_FREE_ADDR, 0x0);
+                       NT_LOG(ERR, NTHW,
+                               "%s: RAB: timeout - Access outside register - 
bus: %d addr: 0x%08X - inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 
0x%08X\n",
+                               p_adapter_id_str, bus_id, address, in_buf_free, 
out_buf_free,
+                               buf_used);
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               res = 0;
+               goto exit_unlock_res;
+
+       } else {
+               NT_LOG(ERR, NTHW,
+                       "%s: RAB: Fail rab bus buffer check - bus: %d addr: 
0x%08X wordcount: %d - inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 
0x%08X\n",
+                       p_adapter_id_str, bus_id, address, word_cnt, 
in_buf_free, out_buf_free,
+                       buf_used);
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+exit_unlock_res:
+       pthread_mutex_unlock(&p->m_mutex);
+       return res;
+}
+
+int nthw_rac_rab_read32(nthw_rac_t *p, bool trc, nthw_rab_bus_id_t bus_id, 
uint32_t address,
+       uint32_t word_cnt, uint32_t *p_data)
+{
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       uint32_t buf_used;
+       uint32_t buf_free;
+       uint32_t in_buf_free;
+       uint32_t out_buf_free;
+       int res = 0;
+
+       pthread_mutex_lock(&p->m_mutex);
+
+       if (address > (1 << RAB_ADDR_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal address: value too large %d 
- max %d\n",
+                       p_adapter_id_str, address, (1 << RAB_ADDR_BW));
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+       if (bus_id > (1 << RAB_BUSID_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal bus id: value too large %d 
- max %d\n",
+                       p_adapter_id_str, bus_id, (1 << RAB_BUSID_BW));
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+       if (word_cnt == 0) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal word count: value is zero 
(%d)\n",
+                       p_adapter_id_str, word_cnt);
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+       if (word_cnt > (1 << RAB_CNT_BW)) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Illegal word count: value too large 
%d - max %d\n",
+                       p_adapter_id_str, word_cnt, (1 << RAB_CNT_BW));
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+       /* Read buffer free register */
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_FREE_ADDR, &buf_free);
+
+       in_buf_free = buf_free & p->RAC_RAB_BUF_FREE_IB_FREE_MASK;
+       out_buf_free = (buf_free & p->RAC_RAB_BUF_FREE_OB_FREE_MASK) >> 16;
+
+       /* Read buffer used register */
+       nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR, &buf_used);
+
+       buf_used =
+               buf_used & (p->RAC_RAB_BUF_USED_IB_USED_MASK | 
p->RAC_RAB_BUF_USED_OB_USED_MASK);
+
+       /*
+        * Verify that output buffer can hold the number of words to be read,
+        * input buffer can hold one read command
+        * and that the input and output "used" buffer is 0
+        */
+       if (out_buf_free >= word_cnt && in_buf_free >= 1 && buf_used == 0) {
+               const uint32_t rab_oper_cmpl = (RAB_COMPLETION << RAB_OPR_LO);
+               uint32_t rab_read_oper_cmpl;
+               uint32_t word_cnt_expected = word_cnt + 1;
+               uint32_t rab_oper_rd;
+
+               rab_oper_rd = (RAB_READ << RAB_OPR_LO) |
+                       ((word_cnt & ((1 << RAB_CNT_BW) - 1)) << RAB_CNT_LO) |
+                       (bus_id << RAB_BUSID_LO) | address;
+
+               if (trc) {
+                       rab_oper_rd |= (RAB_ECHO << RAB_OPR_LO);
+                       word_cnt_expected++;
+               }
+
+               /* Write command */
+               nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_IB_DATA_ADDR, 
rab_oper_rd);
+
+               /* Write completion command */
+               nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_IB_DATA_ADDR, 
rab_oper_cmpl);
+
+               /* Wait until done */
+               if (_nthw_rac_wait_for_rab_done(p, address, word_cnt_expected)) 
{
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               if (trc) {
+                       uint32_t rab_echo_oper_rd;
+                       nthw_rac_reg_read32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR,
+                               &rab_echo_oper_rd);
+
+                       if (p->mn_param_rac_rab_ob_update)
+                               nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR, 0);
+
+                       if (rab_oper_rd != rab_echo_oper_rd) {
+                               NT_LOG(ERR, NTHW,
+                                       "%s: RAB: expected rab read echo oper 
(0x%08X) - read (0x%08X)\n",
+                                       p_adapter_id_str, rab_oper_rd, 
rab_echo_oper_rd);
+                       }
+               }
+
+               {
+                       /* Read data from output buffer */
+                       uint32_t i;
+
+                       for (i = 0; i < word_cnt; i++) {
+                               nthw_rac_reg_read32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR,
+                                       &p_data[i]);
+
+                               if (p->mn_param_rac_rab_ob_update) {
+                                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR,
+                                               0);
+                               }
+                       }
+
+                       if (trc) {
+                               char *tmp_string = 
ntlog_helper_str_alloc("Register::read");
+                               ntlog_helper_str_add(tmp_string,
+                                       "(Dev: NA, Bus: RAB%u, Addr: 0x%08X, 
Cnt: %d, Data:",
+                                       bus_id, address, word_cnt);
+
+                               for (i = 0; i < word_cnt; i++)
+                                       ntlog_helper_str_add(tmp_string, " 
0x%08X", p_data[i]);
+
+                               ntlog_helper_str_add(tmp_string, ")");
+                               NT_LOG(DBG, NTHW, "%s", tmp_string);
+                               ntlog_helper_str_free(tmp_string);
+                       }
+               }
+
+               /* Read completion from out buffer */
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_OB_DATA_ADDR, 
&rab_read_oper_cmpl);
+
+               if (p->mn_param_rac_rab_ob_update)
+                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_OB_DATA_ADDR, 0);
+
+               if (rab_read_oper_cmpl != rab_oper_cmpl) {
+                       NT_LOG(ERR, NTHW,
+                               "%s: RAB: Unexpected value of completion 
(0x%08X)- inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 0x%08X\n",
+                               p_adapter_id_str, rab_read_oper_cmpl, 
in_buf_free, out_buf_free,
+                               buf_used);
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               /* Read buffer free register */
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_FREE_ADDR, 
&buf_free);
+
+               if (buf_free & 0x80000000) {
+                       /* Clear Timeout and overflow bits */
+                       nthw_rac_reg_write32(p_fpga_info, 
p->RAC_RAB_BUF_FREE_ADDR, 0x0);
+                       NT_LOG(ERR, NTHW,
+                               "%s: RAB: timeout - Access outside register - 
bus: %d addr: 0x%08X - inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 
0x%08X\n",
+                               p_adapter_id_str, bus_id, address, in_buf_free, 
out_buf_free,
+                               buf_used);
+                       res = -1;
+                       goto exit_unlock_res;
+               }
+
+               res = 0;
+               goto exit_unlock_res;
+
+       } else {
+               NT_LOG(ERR, NTHW,
+                       "%s: RAB: Fail rab bus buffer check - bus: %d addr: 
0x%08X wordcount: %d - inBufFree: 0x%08X, outBufFree: 0x%08X, bufUsed: 
0x%08X\n",
+                       p_adapter_id_str, bus_id, address, word_cnt, 
in_buf_free, out_buf_free,
+                       buf_used);
+               res = -1;
+               goto exit_unlock_res;
+       }
+
+exit_unlock_res:
+       pthread_mutex_unlock(&p->m_mutex);
+       return res;
+}
+
+int nthw_rac_rab_flush(nthw_rac_t *p)
+{
+       const struct fpga_info_s *const p_fpga_info = p->mp_fpga->p_fpga_info;
+       const char *const p_adapter_id_str = p_fpga_info->mp_adapter_id_str;
+       uint32_t data = 0;
+       uint32_t retry;
+       int res = 0;
+
+       pthread_mutex_lock(&p->m_mutex);
+
+       /* Set the flush bit */
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR,
+               p->RAC_RAB_BUF_USED_FLUSH_MASK);
+
+       /* Reset BUF FREE register */
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_BUF_FREE_ADDR, 0x0);
+
+       /* Wait until OB_USED and IB_USED are 0 */
+       for (retry = 0; retry < 100000; retry++) {
+               nthw_rac_reg_read32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR, 
&data);
+
+               if ((data & 0xFFFFFFFF) == p->RAC_RAB_BUF_USED_FLUSH_MASK)
+                       break;
+       }
+
+       if (data != p->RAC_RAB_BUF_USED_FLUSH_MASK) {
+               NT_LOG(ERR, NTHW, "%s: RAB: Rab bus flush error.\n", 
p_adapter_id_str);
+               res = -1;
+       }
+
+       /* Clear flush bit when done */
+       nthw_rac_reg_write32(p_fpga_info, p->RAC_RAB_BUF_USED_ADDR, 0x0);
+
+       pthread_mutex_unlock(&p->m_mutex);
+       return res;
+}
diff --git a/drivers/net/ntnic/nthw/nthw_rac.h 
b/drivers/net/ntnic/nthw/nthw_rac.h
new file mode 100644
index 0000000000..c16ff77189
--- /dev/null
+++ b/drivers/net/ntnic/nthw/nthw_rac.h
@@ -0,0 +1,153 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_RAC_H__
+#define __NTHW_RAC_H__
+
+#include "nt_util.h"
+
+#define RAB_DMA_BUF_CNT (0x4000)
+
+typedef uint8_t nthw_rab_bus_id_t;
+
+struct nthw_rac {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_rac;
+
+       pthread_mutex_t m_mutex;
+
+       int mn_param_rac_rab_interfaces;
+       int mn_param_rac_rab_ob_update;
+
+       nthw_register_t *mp_reg_dummy0;
+       nthw_register_t *mp_reg_dummy1;
+       nthw_register_t *mp_reg_dummy2;
+
+       nthw_register_t *mp_reg_rab_init;
+       nthw_field_t *mp_fld_rab_init;
+
+       int mn_fld_rab_init_bw;
+       uint32_t mn_fld_rab_init_mask;
+
+       nthw_register_t *mp_reg_dbg_ctrl;
+       nthw_field_t *mp_fld_dbg_ctrl;
+
+       nthw_register_t *mp_reg_dbg_data;
+       nthw_field_t *mp_fld_dbg_data;
+
+       nthw_register_t *mp_reg_rab_ib_data;
+       nthw_field_t *mp_fld_rab_ib_data;
+
+       nthw_register_t *mp_reg_rab_ob_data;
+       nthw_field_t *mp_fld_rab_ob_data;
+
+       nthw_register_t *mp_reg_rab_buf_free;
+       nthw_field_t *mp_fld_rab_buf_free_ib_free;
+       nthw_field_t *mp_fld_rab_buf_free_ib_ovf;
+       nthw_field_t *mp_fld_rab_buf_free_ob_free;
+       nthw_field_t *mp_fld_rab_buf_free_ob_ovf;
+       nthw_field_t *mp_fld_rab_buf_free_timeout;
+
+       nthw_register_t *mp_reg_rab_buf_used;
+       nthw_field_t *mp_fld_rab_buf_used_ib_used;
+       nthw_field_t *mp_fld_rab_buf_used_ob_used;
+       nthw_field_t *mp_fld_rab_buf_used_flush;
+
+       nthw_register_t *mp_reg_rab_dma_ib_lo;
+       nthw_field_t *mp_fld_rab_dma_ib_lo_phy_addr;
+
+       nthw_register_t *mp_reg_rab_dma_ib_hi;
+       nthw_field_t *mp_fld_rab_dma_ib_hi_phy_addr;
+
+       nthw_register_t *mp_reg_rab_dma_ob_hi;
+       nthw_field_t *mp_fld_rab_dma_ob_hi_phy_addr;
+
+       nthw_register_t *mp_reg_rab_dma_ob_lo;
+       nthw_field_t *mp_fld_rab_dma_ob_lo_phy_addr;
+
+       nthw_register_t *mp_reg_rab_dma_ib_wr;
+       nthw_field_t *mp_fld_rab_dma_ib_wr_ptr;
+
+       nthw_register_t *mp_reg_rab_dma_ib_rd;
+       nthw_field_t *mp_fld_rab_dma_ib_rd_ptr;
+
+       nthw_register_t *mp_reg_rab_dma_ob_wr;
+       nthw_field_t *mp_fld_rab_dma_ob_wr_ptr;
+
+       nthw_register_t *mp_reg_rab_nmb_rd;
+       nthw_register_t *mp_reg_rab_nmb_data;
+       nthw_register_t *mp_reg_rab_nmb_wr;
+       nthw_register_t *mp_reg_rab_nmb_status;
+
+       uint32_t RAC_RAB_INIT_ADDR;
+       uint32_t RAC_RAB_IB_DATA_ADDR;
+       uint32_t RAC_RAB_OB_DATA_ADDR;
+       uint32_t RAC_RAB_BUF_FREE_ADDR;
+       uint32_t RAC_RAB_BUF_USED_ADDR;
+
+       uint32_t RAC_RAB_DMA_IB_LO_ADDR;
+       uint32_t RAC_RAB_DMA_IB_HI_ADDR;
+       uint32_t RAC_RAB_DMA_OB_LO_ADDR;
+       uint32_t RAC_RAB_DMA_OB_HI_ADDR;
+       uint32_t RAC_RAB_DMA_IB_RD_ADDR;
+       uint32_t RAC_RAB_DMA_OB_WR_ADDR;
+       uint32_t RAC_RAB_DMA_IB_WR_ADDR;
+
+       uint32_t RAC_RAB_BUF_FREE_IB_FREE_MASK;
+       uint32_t RAC_RAB_BUF_FREE_OB_FREE_MASK;
+       uint32_t RAC_RAB_BUF_USED_IB_USED_MASK;
+       uint32_t RAC_RAB_BUF_USED_OB_USED_MASK;
+       uint32_t RAC_RAB_BUF_USED_FLUSH_MASK;
+
+       uint32_t RAC_RAB_BUF_USED_OB_USED_LOW;
+
+       uint32_t RAC_NMB_RD_ADR_ADDR;
+       uint32_t RAC_NMB_DATA_ADDR;
+       uint32_t RAC_NMB_WR_ADR_ADDR;
+       uint32_t RAC_NMB_STATUS_ADDR;
+
+       bool m_dma_active;
+
+       struct nt_dma_s *m_dma;
+
+       volatile uint32_t *m_dma_in_buf;
+       volatile uint32_t *m_dma_out_buf;
+
+       uint16_t m_dma_out_ptr_rd;
+       uint16_t m_dma_in_ptr_wr;
+       uint32_t m_in_free;
+};
+
+typedef struct nthw_rac nthw_rac_t;
+typedef struct nthw_rac nthw_rac;
+
+struct dma_buf_ptr {
+       uint32_t size;
+       uint32_t index;
+       volatile uint32_t *base;
+};
+
+nthw_rac_t *nthw_rac_new(void);
+int nthw_rac_init(nthw_rac_t *p, nthw_fpga_t *p_fpga, struct fpga_info_s 
*p_fpga_info);
+
+int nthw_rac_rab_init(nthw_rac_t *p, uint32_t n_rab_intf_mask);
+
+int nthw_rac_rab_setup(nthw_rac_t *p);
+
+int nthw_rac_rab_reset(nthw_rac_t *p);
+
+int nthw_rac_rab_write32(nthw_rac_t *p, bool trc, nthw_rab_bus_id_t bus_id, 
uint32_t address,
+       uint32_t word_cnt, const uint32_t *p_data);
+int nthw_rac_rab_read32(nthw_rac_t *p, bool trc, nthw_rab_bus_id_t bus_id, 
uint32_t address,
+       uint32_t word_cnt, uint32_t *p_data);
+
+int nthw_rac_rab_flush(nthw_rac_t *p);
+
+void nthw_rac_bar0_read32(const struct fpga_info_s *p_fpga_info, uint32_t 
reg_addr,
+       uint32_t word_cnt, uint32_t *p_data);
+void nthw_rac_bar0_write32(const struct fpga_info_s *p_fpga_info, uint32_t 
reg_addr,
+       uint32_t word_cnt, const uint32_t *p_data);
+
+#endif /* __NTHW_RAC_H__ */
-- 
2.45.0


Reply via email to