Add ntnic HW interfaces (PCIe, I2C) API.

Signed-off-by: Serhii Iliushyk <sil-...@napatech.com>
---
v2:
* Fixed WARNING:TYPO_SPELLING
v3:
* Fix meson file: list of source files
---
 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c   | 550 ++++++++++++++++++
 drivers/net/ntnic/adapter/nt4ga_tfg.c         |  69 +++
 drivers/net/ntnic/include/nt4ga_tfg.h         |   2 +
 drivers/net/ntnic/include/ntnic_nim.h         |   1 +
 drivers/net/ntnic/meson.build                 |   8 +
 .../net/ntnic/nthw/core/include/nthw_core.h   |   6 +
 .../net/ntnic/nthw/core/include/nthw_gfg.h    |  40 ++
 .../net/ntnic/nthw/core/include/nthw_gmf.h    |  75 +++
 .../net/ntnic/nthw/core/include/nthw_i2cm.h   |  51 ++
 .../ntnic/nthw/core/include/nthw_pci_rd_tg.h  |  50 ++
 .../net/ntnic/nthw/core/include/nthw_pci_ta.h |  40 ++
 .../ntnic/nthw/core/include/nthw_pci_wr_tg.h  |  55 ++
 drivers/net/ntnic/nthw/core/nthw_gfg.c        | 365 ++++++++++++
 drivers/net/ntnic/nthw/core/nthw_gmf.c        | 176 ++++++
 drivers/net/ntnic/nthw/core/nthw_i2cm.c       | 197 +++++++
 drivers/net/ntnic/nthw/core/nthw_pci_rd_tg.c  | 115 ++++
 drivers/net/ntnic/nthw/core/nthw_pci_ta.c     |  94 +++
 drivers/net/ntnic/nthw/core/nthw_pci_wr_tg.c  | 122 ++++
 18 files changed, 2016 insertions(+)
 create mode 100644 drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c
 create mode 100644 drivers/net/ntnic/adapter/nt4ga_tfg.c
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_gfg.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_gmf.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_i2cm.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_pci_rd_tg.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_pci_ta.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_pci_wr_tg.h
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_gfg.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_gmf.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_i2cm.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_pci_rd_tg.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_pci_ta.c
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_pci_wr_tg.c

diff --git a/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c 
b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c
new file mode 100644
index 0000000000..b7a9ca2fbe
--- /dev/null
+++ b/drivers/net/ntnic/adapter/nt4ga_pci_ta_tg.c
@@ -0,0 +1,550 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nt_util.h"
+#include "nthw_drv.h"
+#include "nt4ga_adapter.h"
+#include "nt4ga_pci_ta_tg.h"
+#include "nthw_pci_ta.h"
+#include "nthw_pci_rd_tg.h"
+#include "nthw_pci_wr_tg.h"
+
+int nt4ga_pci_ta_tg_init(struct adapter_info_s *p_adapter_info)
+{
+       const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;
+       fpga_info_t *fpga_info = &p_adapter_info->fpga_info;
+       nthw_fpga_t *p_fpga = fpga_info->mp_fpga;
+       nt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg;
+       int res;
+       int n_err_cnt = 0;
+
+       if (p) {
+               memset(p, 0, sizeof(nt4ga_pci_ta_tg_t));
+
+       } else {
+               NT_LOG(ERR, NTHW, "%s: %s: null ptr\n", p_adapter_id_str, 
__func__);
+               return -1;
+       }
+
+       assert(p_fpga);
+
+       p->mp_nthw_pci_rd_tg = nthw_pci_rd_tg_new();
+       assert(p->mp_nthw_pci_rd_tg);
+       res = nthw_pci_rd_tg_init(p->mp_nthw_pci_rd_tg, p_fpga, 0);
+
+       if (res) {
+               n_err_cnt++;
+               NT_LOG(WRN, NTHW, "%s: module PCI_RD_TG not found\n", 
p_adapter_id_str);
+       }
+
+       p->mp_nthw_pci_wr_tg = nthw_pci_wr_tg_new();
+       assert(p->mp_nthw_pci_wr_tg);
+       res = nthw_pci_wr_tg_init(p->mp_nthw_pci_wr_tg, p_fpga, 0);
+
+       if (res) {
+               n_err_cnt++;
+               NT_LOG(WRN, NTHW, "%s: module PCI_WR_TG not found\n", 
p_adapter_id_str);
+       }
+
+       p->mp_nthw_pci_ta = nthw_pci_ta_new();
+       assert(p->mp_nthw_pci_ta);
+       res = nthw_pci_ta_init(p->mp_nthw_pci_ta, p_fpga, 0);
+
+       if (res) {
+               n_err_cnt++;
+               NT_LOG(WRN, NTHW, "%s: module PCI_TA not found\n", 
p_adapter_id_str);
+       }
+
+       return n_err_cnt;
+}
+
+static int nt4ga_pci_ta_tg_ta_write_control_enable(nt4ga_pci_ta_tg_t *p, 
uint32_t enable)
+{
+       nthw_pci_ta_set_control_enable(p->mp_nthw_pci_ta, enable);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_ta_read_length_error(nt4ga_pci_ta_tg_t *p, uint32_t 
*p_data)
+{
+       nthw_pci_ta_get_length_error(p->mp_nthw_pci_ta, p_data);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_ta_read_packet_bad(nt4ga_pci_ta_tg_t *p, uint32_t 
*p_data)
+{
+       nthw_pci_ta_get_packet_bad(p->mp_nthw_pci_ta, p_data);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_ta_read_packet_good(nt4ga_pci_ta_tg_t *p, uint32_t 
*p_data)
+{
+       nthw_pci_ta_get_packet_good(p->mp_nthw_pci_ta, p_data);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_ta_read_payload_error(nt4ga_pci_ta_tg_t *p, 
uint32_t *p_data)
+{
+       nthw_pci_ta_get_payload_error(p->mp_nthw_pci_ta, p_data);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_rd_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova, 
int slot_addr,
+       uint32_t req_size, bool wait, bool wrap)
+{
+       const uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * 
req_size));
+       nthw_pci_rd_tg_set_ram_addr(p->mp_nthw_pci_rd_tg, slot_addr);
+       nthw_pci_rd_tg_set_phys_addr(p->mp_nthw_pci_rd_tg, n_phys_addr);
+       nthw_pci_rd_tg_set_ram_data(p->mp_nthw_pci_rd_tg, req_size, wait, wrap);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_rd_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t 
num_iterations)
+{
+       nthw_pci_rd_tg_set_run(p->mp_nthw_pci_rd_tg, num_iterations);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_rd_tg_wait_ready(nt4ga_pci_ta_tg_t *p)
+{
+       int poll = 0;
+       uint32_t data = 0;
+
+       while (data == 0) {
+               /* NOTE: Deliberately start with a sleep - ensures that the 
FPGA pipe is empty */
+               nt_os_wait_usec(1000);
+               data = nthw_pci_rd_tg_get_ctrl_rdy(p->mp_nthw_pci_rd_tg);
+               poll++;
+
+               if (poll >= 1000) {
+                       NT_LOG(ERR, NTHW, "%s: FAILED waiting PCI RD TG ready: 
poll=%d\n",
+                               __func__, poll);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_wr_tg_setup(nt4ga_pci_ta_tg_t *p, uint64_t iova, 
int slot_addr,
+       uint32_t req_size, bool wait, bool wrap, bool inc)
+{
+       const uint64_t n_phys_addr = (iova + (unsigned long)(slot_addr * 
req_size));
+
+       nthw_pci_wr_tg_set_ram_addr(p->mp_nthw_pci_wr_tg, slot_addr);
+       nthw_pci_wr_tg_set_phys_addr(p->mp_nthw_pci_wr_tg, n_phys_addr);
+       nthw_pci_wr_tg_set_ram_data(p->mp_nthw_pci_wr_tg, req_size, wait, wrap, 
inc);
+
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_wr_tg_run(nt4ga_pci_ta_tg_t *p, uint32_t 
num_iterations)
+{
+       nthw_pci_wr_tg_set_run(p->mp_nthw_pci_wr_tg, num_iterations);
+       return 0;
+}
+
+static int nt4ga_pci_ta_tg_wr_tg_wait_ready(nt4ga_pci_ta_tg_t *p)
+{
+       int poll = 0;
+       uint32_t data = 0;
+
+       while (data == 0) {
+               /* NOTE: Deliberately start with a sleep - ensures that the 
FPGA pipe is empty */
+               nt_os_wait_usec(1000);
+               data = nthw_pci_wr_tg_get_ctrl_rdy(p->mp_nthw_pci_wr_tg);
+               poll++;
+
+               if (poll >= 1000) {
+                       NT_LOG(ERR, NTHW, "%s: FAILED waiting PCI WR TG ready: 
poll=%d\n",
+                               __func__, poll);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+int nt4ga_pci_ta_tg_measure_throughput_run(struct adapter_info_s 
*p_adapter_info,
+       struct nthw_hif_end_point_counters *pri,
+       struct nthw_hif_end_point_counters *sla)
+{
+       nt4ga_pci_ta_tg_t *p = &p_adapter_info->nt4ga_pci_ta_tg;
+
+       const int delay = pri->n_tg_delay;
+       const int pkt_size = pri->n_tg_pkt_size;
+       const int num_pkts = pri->n_tg_num_pkts;
+       const int n_direction = pri->n_tg_direction;
+       const uint8_t n_numa_node = (uint8_t)pri->n_numa_node;
+       const int dma_buf_size = (4 * 1024 * 1024);
+
+       const size_t align_size = nt_util_align_size(dma_buf_size);
+       uint32_t *mem_addr;
+       uint64_t iova;
+
+       int bo_error = 0;
+
+       nthw_hif *p_primary_instance = p_adapter_info->fpga_info.mp_nthw_hif;
+       nthw_hif *p_secondary_instance = NULL;
+
+       nthw_pcie3 *p_pci_primary = p_adapter_info->fpga_info.mp_nthw_pcie3;
+       nthw_pcie3 *p_pci_secondary = NULL;
+
+       assert(p_primary_instance || p_pci_primary);
+
+       struct nt_dma_s *p_dma;
+       /* FPGA needs a Page alignment (4K on Intel) */
+       p_dma = nt_dma_alloc(align_size, 0x1000, n_numa_node);
+
+       if (p_dma == NULL) {
+               NT_LOG(DBG, ETHDEV, "%s: vfio_dma_alloc failed\n", __func__);
+               return 0;
+       }
+
+       mem_addr = (uint32_t *)p_dma->addr;
+       iova = p_dma->iova;
+
+       NT_LOG(DBG, NTHW, "%s: Running HIF bandwidth measurements on NUMA node 
%d\n", __func__,
+               n_numa_node);
+
+       bo_error = 0;
+       {
+               int wrap;
+
+               /* Stop any existing running test */
+               bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);
+               bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);
+
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+               bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);
+
+               /* Prepare the HIF Traffic generator */
+               bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 1);
+               bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+               /*
+                * Ensure that the hostbuffer memory contain data that can be 
read -
+                * For this we will ask the FPGA to write data to it. The last 
wrap packet
+                * does not generate any data it only wraps (unlike the PCIe2 
TG)
+                */
+               {
+                       int pkt;
+
+                       for (pkt = 0; pkt < num_pkts; pkt++) {
+                               if (pkt >= (num_pkts - 1))
+                                       wrap = 1;
+
+                               else
+                                       wrap = 0;
+
+                               bo_error |= nt4ga_pci_ta_tg_wr_tg_setup(p, 
iova, pkt, pkt_size, 0,
+                                               wrap, 1);
+                               bo_error |= nt4ga_pci_ta_tg_rd_tg_setup(p, 
iova, pkt, pkt_size, 0,
+                                               wrap);
+                       }
+               }
+
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1);
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+               /* Start WR TG Write once */
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 1);
+               /* Wait until WR TG ready */
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+               /* Verify that we have a packet */
+               {
+                       int pkt;
+
+                       for (pkt = 0; pkt < num_pkts; pkt++) {
+                               uint32_t value = 0;
+                               int poll;
+
+                               for (poll = 8; poll < pkt_size; poll += 4, 
value++) {
+                                       if (*(uint32_t *)((uint8_t *)mem_addr + 
(pkt * pkt_size) +
+                                                       poll) != value) {
+                                               NT_LOG(ERR, NTHW,
+                                                       "HIF TG: Prepare 
failed. Data write failed: #%d.%d:  %016X:%08X\n",
+                                                       pkt, poll,
+                                                       *(uint32_t *)((uint8_t 
*)mem_addr +
+                                                               (pkt * 
pkt_size) + poll),
+                                                       value);
+
+                                               /*
+                                                * Break out of the 
verification loop on first
+                                                * compare error
+                                                */
+                                               bo_error |= 1;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               switch (n_direction) {
+               case 1: /* Read only test */
+                       nt4ga_pci_ta_tg_wr_tg_run(p, 0xffff);
+                       break;
+
+               case 2: /* Write only test */
+                       nt4ga_pci_ta_tg_rd_tg_run(p, 0xffff);
+                       break;
+
+               case 3: /* Combined read/write test */
+                       nt4ga_pci_ta_tg_wr_tg_run(p, 0xffff);
+                       nt4ga_pci_ta_tg_rd_tg_run(p, 0xffff);
+                       break;
+
+               default:/* stop tests */
+                       nt4ga_pci_ta_tg_wr_tg_run(p, 0);
+                       nt4ga_pci_ta_tg_rd_tg_run(p, 0);
+                       break;
+               }
+
+               do {
+                       /* prep */
+                       if (p_pci_primary)
+                               
nthw_pcie3_end_point_counters_sample_pre(p_pci_primary, pri);
+
+                       if (p_pci_secondary)
+                               
nthw_pcie3_end_point_counters_sample_pre(p_pci_secondary, sla);
+
+                       /* start measure */
+                       if (p_primary_instance)
+                               nthw_hif_stat_req_enable(p_primary_instance);
+
+                       if (p_pci_primary)
+                               nthw_pcie3_stat_req_enable(p_pci_primary);
+
+                       if (p_secondary_instance)
+                               nthw_hif_stat_req_enable(p_secondary_instance);
+
+                       if (p_pci_secondary)
+                               nthw_pcie3_stat_req_enable(p_pci_secondary);
+
+                       /* Wait */
+                       nt_os_wait_usec(delay);
+
+                       /* Stop measure */
+                       if (p_primary_instance)
+                               nthw_hif_stat_req_disable(p_primary_instance);
+
+                       if (p_pci_primary)
+                               nthw_pcie3_stat_req_disable(p_pci_primary);
+
+                       if (p_secondary_instance)
+                               nthw_hif_stat_req_disable(p_secondary_instance);
+
+                       if (p_pci_secondary)
+                               nthw_pcie3_stat_req_disable(p_pci_secondary);
+
+                       /* Post process primary */
+                       if (p_primary_instance)
+                               
nthw_hif_end_point_counters_sample(p_primary_instance, pri);
+
+                       if (p_pci_primary)
+                               
nthw_pcie3_end_point_counters_sample_post(p_pci_primary, pri);
+
+                       /* Post process secondary */
+                       if (p_secondary_instance)
+                               
nthw_hif_end_point_counters_sample(p_secondary_instance, sla);
+
+                       if (p_pci_secondary)
+                               
nthw_pcie3_end_point_counters_sample_post(p_pci_secondary, sla);
+
+                       {
+                               /* Check for TA transmit errors */
+                               uint32_t dw_good_pkts, dw_bad_pkts, 
dw_bad_length, dw_bad_payload;
+                               nt4ga_pci_ta_tg_ta_read_packet_good(p, 
&dw_good_pkts);
+                               nt4ga_pci_ta_tg_ta_read_packet_bad(p, 
&dw_bad_pkts);
+                               nt4ga_pci_ta_tg_ta_read_length_error(p, 
&dw_bad_length);
+                               nt4ga_pci_ta_tg_ta_read_payload_error(p, 
&dw_bad_payload);
+
+                               NT_LOG(DBG, NTHW,
+                                       "%s: NUMA node %u: HIF: TA: Good pkts, 
Bad pkts, Bad length, Bad payload\n",
+                                       __func__, n_numa_node);
+                               NT_LOG(DBG, NTHW,
+                                       "%s: NUMA node %u: HIF: TA: 0x%08x 
0x%08x 0x%08x 0x%08x\n",
+                                       __func__, n_numa_node, dw_good_pkts, 
dw_bad_pkts,
+                                       dw_bad_length, dw_bad_payload);
+
+                               if (dw_bad_pkts | dw_bad_length | 
dw_bad_payload) {
+                                       bo_error |= 1;
+                                       NT_LOG(ERR, NTHW,
+                                               "%s: NUMA node %u: HIF: TA: 
error detected\n",
+                                               __func__, n_numa_node);
+                                       NT_LOG(ERR, NTHW,
+                                               "%s: NUMA node %u: HIF: TA: 
Good packets received: %u\n",
+                                               __func__, n_numa_node, 
dw_good_pkts);
+                                       NT_LOG(ERR, NTHW,
+                                               "%s: NUMA node %u: HIF: TA: Bad 
packets received : %u\n",
+                                               __func__, n_numa_node, 
dw_bad_pkts);
+                                       NT_LOG(ERR, NTHW,
+                                               "%s: NUMA node %u: HIF: TA: Bad 
length received  : %u\n",
+                                               __func__, n_numa_node, 
dw_bad_length);
+                                       NT_LOG(ERR, NTHW,
+                                               "%s: NUMA node %u: HIF: TA: Bad 
payload received : %u\n",
+                                               __func__, n_numa_node, 
dw_bad_payload);
+                               }
+                       }
+
+                       if (bo_error != 0)
+                               break;
+
+                       break;  /* for now only loop once */
+
+                       /*
+                        * Only do "signalstop" looping if a specific numa node 
and direction is to
+                        * be tested.
+                        */
+               } while ((bo_error == 0) && (n_numa_node != UINT8_MAX) && 
(n_direction != -1));
+
+               /* Stop the test */
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);
+               bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+               bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);
+               bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);
+
+               bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);
+
+               /* PCIe3 sanity checks */
+               {
+#if defined(DEBUG)
+                       int do_loop = 1;
+#else
+                       int do_loop = 0;
+#endif
+
+                       while (do_loop) {
+                               do_loop = 0;
+
+                               if (p_primary_instance) {
+                                       
nthw_hif_stat_req_enable(p_primary_instance);
+                                       nt_os_wait_usec(100);
+                                       
nthw_hif_stat_req_disable(p_primary_instance);
+                               }
+
+                               if (do_loop == 0)
+                                       break;
+
+                               NT_LOG(DBG, NTHW, "%s: WARNING this is wrong - 
wait again\n",
+                                       __func__);
+                               nt_os_wait_usec(200 * 1000);
+                       }
+               }
+       }
+
+       /* Stop the test */
+
+       bo_error |= nt4ga_pci_ta_tg_wr_tg_run(p, 0);
+       bo_error |= nt4ga_pci_ta_tg_wr_tg_wait_ready(p);
+
+       bo_error |= nt4ga_pci_ta_tg_rd_tg_run(p, 0);
+       bo_error |= nt4ga_pci_ta_tg_rd_tg_wait_ready(p);
+
+       bo_error |= nt4ga_pci_ta_tg_ta_write_control_enable(p, 0);
+
+       nt_dma_free(p_dma);
+
+       return bo_error;
+}
+
+int nt4ga_pci_ta_tg_measure_throughput_main(struct adapter_info_s 
*p_adapter_info,
+       const uint8_t numa_node, const int direction,
+       const int n_pkt_size, const int n_batch_count,
+       const int n_delay)
+{
+       /* All numa nodes is indicated by UINT8_MAX */
+       const uint8_t numa_begin = (numa_node == UINT8_MAX ? 0 : numa_node);
+       const uint8_t numa_end = numa_begin;
+
+       /* sanity check direction param */
+       const int dir_begin = (direction <= 0 ? 1 : direction);
+       const int dir_end = (direction <= 0 ? 3 : direction);
+
+       int bo_error = 0;
+       struct nthw_hif_end_points eps;
+
+       if (n_delay == 0)
+               return -1;
+
+       NT_LOG(DBG, NTHW, "HIF adapter throughput:\n");
+
+       /* Only do "signalstop"-looping if a specific numa node is to be 
tested. */
+       {
+               uint8_t numa;
+
+               for (numa = numa_begin; numa <= numa_end; numa++) {
+                       int by_loop;
+
+                       for (by_loop = dir_begin; by_loop <= dir_end; 
by_loop++) {
+                               struct nthw_hif_end_point_counters *pri = 
&eps.pri;
+                               struct nthw_hif_end_point_counters *sla = 
&eps.sla;
+
+                               pri->n_numa_node = numa;
+                               pri->n_tg_direction = by_loop;
+                               pri->n_tg_pkt_size = (n_pkt_size > 0 ? 
n_pkt_size : TG_PKT_SIZE);
+                               pri->n_tg_num_pkts =
+                                       (n_batch_count > 0 ? n_batch_count : 
TG_NUM_PACKETS);
+                               pri->n_tg_delay = (n_delay > 0 ? n_delay : 
TG_DELAY);
+                               pri->cur_rx = 0;
+                               pri->cur_tx = 0;
+                               pri->n_ref_clk_cnt = -1;
+                               pri->bo_error = 0;
+
+                               sla->n_numa_node = numa;
+                               sla->n_tg_direction = by_loop;
+                               sla->n_tg_pkt_size = (n_pkt_size > 0 ? 
n_pkt_size : TG_PKT_SIZE);
+                               sla->n_tg_num_pkts =
+                                       (n_batch_count > 0 ? n_batch_count : 
TG_NUM_PACKETS);
+                               sla->n_tg_delay = (n_delay > 0 ? n_delay : 
TG_DELAY);
+                               sla->cur_rx = 0;
+                               sla->cur_tx = 0;
+                               pri->n_ref_clk_cnt = -1;
+                               sla->bo_error = 0;
+
+                               bo_error += 
nt4ga_pci_ta_tg_measure_throughput_run(p_adapter_info,
+                                               pri, sla);
+#if defined(DEBUG) && (1)
+                               {
+                                       NT_LOG(DBG, NTHW,
+                                               "%s: @ %d: %d %d %d %d: %016lX 
%016lX : %6ld Mbps %6ld Mbps\n",
+                                               __func__, pri->n_numa_node, 
pri->n_tg_direction,
+                                               pri->n_tg_num_pkts, 
pri->n_tg_pkt_size,
+                                               pri->n_tg_delay, pri->cur_rx, 
pri->cur_tx,
+                                               (pri->cur_rx * 8UL / 1000000UL),
+                                               (pri->cur_tx * 8UL / 
1000000UL));
+                               }
+                               {
+                                       NT_LOG(DBG, NTHW,
+                                               "%s: @ %d: %d %d %d %d: %016lX 
%016lX : %6ld Mbps %6ld Mbps\n",
+                                               __func__, sla->n_numa_node, 
sla->n_tg_direction,
+                                               sla->n_tg_num_pkts, 
sla->n_tg_pkt_size,
+                                               sla->n_tg_delay, sla->cur_rx, 
sla->cur_tx,
+                                               (sla->cur_rx * 8UL / 1000000UL),
+                                               (sla->cur_tx * 8UL / 
1000000UL));
+                               }
+#endif
+
+                               if (pri->bo_error != 0 || sla->bo_error != 0)
+                                       bo_error++;
+
+                               if (bo_error)
+                                       break;
+                       }
+               }
+       }
+
+       if (bo_error != 0)
+               NT_LOG(ERR, NTHW, "%s: error during bandwidth measurement\n", 
__func__);
+
+       NT_LOG(DBG, NTHW, "HIF adapter throughput: done %s\n", __func__);
+
+       return 0;
+}
diff --git a/drivers/net/ntnic/adapter/nt4ga_tfg.c 
b/drivers/net/ntnic/adapter/nt4ga_tfg.c
new file mode 100644
index 0000000000..be1b2a55ad
--- /dev/null
+++ b/drivers/net/ntnic/adapter/nt4ga_tfg.c
@@ -0,0 +1,69 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+#include "nthw_drv.h"
+#include "nt4ga_adapter.h"
+#include "nthw_fpga.h"
+#include "nt4ga_tfg.h"
+
+int nt4ga_tfg_init(struct adapter_info_s *p_adapter_info)
+{
+       const char *const p_adapter_id_str = p_adapter_info->mp_adapter_id_str;
+
+       fpga_info_t *fpga_info = &p_adapter_info->fpga_info;
+       nthw_fpga_t *p_fpga = fpga_info->mp_fpga;
+       nt4ga_tfg_t *p_nt4ga_tfg = &p_adapter_info->nt4ga_tfg;
+
+       nthw_gfg_t *p_nthw_gfg = nthw_gfg_new();
+
+       if (p_nthw_gfg) {
+               int res = nthw_gfg_init(p_nthw_gfg, p_fpga, 0);
+
+               if (res) {
+                       NT_LOG(WRN, ETHDEV, "%s: TFG/GFG capability is not 
available\n",
+                               p_adapter_id_str);
+                       free(p_nthw_gfg);
+                       p_nthw_gfg = NULL;
+               }
+       }
+
+       p_nt4ga_tfg->mp_nthw_gfg = p_nthw_gfg;
+
+       return p_nthw_gfg ? 0 : -1;
+}
+
+int nt4ga_tfg_setup(struct adapter_info_s *p_adapter_info, const int n_intf_no,
+       const int n_cmd_start_stop, const int n_frame_count, const int 
n_frame_size,
+       const int n_frame_fill_mode, const int n_frame_stream_id)
+{
+       fpga_info_t *fpga_info = &p_adapter_info->fpga_info;
+       nt4ga_tfg_t *p_nt4ga_tfg = &p_adapter_info->nt4ga_tfg;
+
+       nthw_gfg_t *p_nthw_gfg = p_nt4ga_tfg->mp_nthw_gfg;
+
+       if (p_nthw_gfg) {
+               nthw_fpga_t *p_fpga = fpga_info->mp_fpga;
+
+               /* Does FPGA have GMF module? */
+               if (nthw_gmf_init(NULL, p_fpga, n_intf_no) == 0) {
+                       /* Yes, FPGA has GMF module */
+                       nthw_gmf_t gmf;
+
+                       if (nthw_gmf_init(&gmf, p_fpga, n_intf_no) == 0)
+                               nthw_gmf_set_ifg_speed_percent(&gmf, 
n_cmd_start_stop);
+               }
+
+               if (n_cmd_start_stop) {
+                       nthw_gfg_start(p_nthw_gfg, n_intf_no, n_frame_count, 
n_frame_size,
+                               n_frame_fill_mode, n_frame_stream_id);
+
+               } else {
+                       nthw_gfg_stop(p_nthw_gfg, n_intf_no);
+               }
+       }
+
+       return 0;
+}
diff --git a/drivers/net/ntnic/include/nt4ga_tfg.h 
b/drivers/net/ntnic/include/nt4ga_tfg.h
index 9797403dec..db2f42b215 100644
--- a/drivers/net/ntnic/include/nt4ga_tfg.h
+++ b/drivers/net/ntnic/include/nt4ga_tfg.h
@@ -7,6 +7,8 @@
 #define NT4GA_TFG_H_
 
 typedef struct nt4ga_tfg_s {
+       nthw_gfg_t *mp_nthw_gfg;
+       nthw_gmf_t *mp_nthw_gmf;
        nthw_mac_tfg_t *mp_nthw_mac_tfg;
 } nt4ga_tfg_t;
 
diff --git a/drivers/net/ntnic/include/ntnic_nim.h 
b/drivers/net/ntnic/include/ntnic_nim.h
index 41457b7a07..fc9b2a2b23 100644
--- a/drivers/net/ntnic/include/ntnic_nim.h
+++ b/drivers/net/ntnic/include/ntnic_nim.h
@@ -93,6 +93,7 @@ typedef struct nim_i2c_ctx {
        union {
                nthw_iic_t hwiic;       /* depends on *Fpga_t, instance number, 
and cycle time */
                struct {
+                       nthw_i2cm_t *p_nt_i2cm;
                        int mux_channel;
                } hwagx;
        };
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index e21c075d74..84f8f47743 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -75,6 +75,8 @@ headers = files('rte_pmd_ntnic.h')
 # all sources
 sources = files(
     'dpdk_mod_reg.c',
+    'adapter/nt4ga_pci_ta_tg.c',
+    'adapter/nt4ga_tfg.c',
     'nthw/supported/nthw_fpga_9563_055_039_0000.c',
     'nthw/supported/nthw_fpga_instances.c',
     'nthw/supported/nthw_fpga_mod_str_map.c',
@@ -84,10 +86,16 @@ sources = files(
     'nthw/core/nt200a0x/reset/nthw_fpga_rst_nt200a0x.c',
     'nthw/core/nthw_fpga.c',
     'nthw/core/nthw_fpga_rst.c',
+    'nthw/core/nthw_gfg.c',
+    'nthw/core/nthw_gmf.c',
     'nthw/core/nthw_hif.c',
+    'nthw/core/nthw_i2cm.c',
     'nthw/core/nthw_iic.c',
     'nthw/core/nthw_mac_pcs_xxv.c',
     'nthw/core/nthw_pcie3.c',
+    'nthw/core/nthw_pci_rd_tg.c',
+    'nthw/core/nthw_pci_ta.c',
+    'nthw/core/nthw_pci_wr_tg.c',
     'nthw/core/nthw_sdc.c',
     'nthw/core/nthw_si5340.c',
     'nthw/core/nthw_spim.c',
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h 
b/drivers/net/ntnic/nthw/core/include/nthw_core.h
index f2d56a41f9..b72766cc5c 100644
--- a/drivers/net/ntnic/nthw/core/include/nthw_core.h
+++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h
@@ -13,8 +13,14 @@
 #include "nthw_fpga_model.h"
 #include "nthw_hif.h"
 #include "nthw_pcie3.h"
+#include "nthw_pci_rd_tg.h"
+#include "nthw_pci_wr_tg.h"
+#include "nthw_pci_ta.h"
 #include "nthw_iic.h"
+#include "nthw_i2cm.h"
 
+#include "nthw_gfg.h"
+#include "nthw_gmf.h"
 #include "nthw_mac_pcs_xxv.h"
 #include "nthw_mac_tfg.h"
 #include "nthw_sdc.h"
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gfg.h 
b/drivers/net/ntnic/nthw/core/include/nthw_gfg.h
new file mode 100644
index 0000000000..fd477775d5
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_gfg.h
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NTHW_GFG_H_
+#define NTHW_GFG_H_
+
+struct nthw_gfg {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_gfg;
+       int mn_instance;
+
+       int mn_param_gfg_present;
+
+       nthw_field_t *mpa_fld_ctrl_enable[8];
+       nthw_field_t *mpa_fld_ctrl_mode[8];
+       nthw_field_t *mpa_fld_ctrl_prbs_en[8];
+       nthw_field_t *mpa_fld_ctrl_size[8];
+       nthw_field_t *mpa_fld_stream_id_val[8];
+       nthw_field_t *mpa_fld_run_run[8];
+       nthw_field_t *mpa_fld_size_mask[8];
+       nthw_field_t *mpa_fld_burst_size_val[8];
+};
+
+typedef struct nthw_gfg nthw_gfg_t;
+typedef struct nthw_gfg nthw_gfg;
+
+nthw_gfg_t *nthw_gfg_new(void);
+int nthw_gfg_init(nthw_gfg_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+int nthw_gfg_start(nthw_gfg_t *p, const int n_intf_no, const int n_frame_count,
+       const int n_frame_size, const int n_frame_fill_mode,
+       const int n_frame_stream_id);
+int nthw_gfg_stop(nthw_gfg_t *p, const int n_intf_no);
+int nthw_gfg_setup(nthw_gfg_t *p, const size_t n_intf_no, const int n_enable,
+       const int n_frame_count, const int n_frame_size, const int 
n_frame_fill_mode,
+       const int n_frame_stream_id);
+
+#endif /* NTHW_GFG_H_ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gmf.h 
b/drivers/net/ntnic/nthw/core/include/nthw_gmf.h
new file mode 100644
index 0000000000..b9907a9285
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_gmf.h
@@ -0,0 +1,75 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_GMF_H__
+#define __NTHW_GMF_H__
+
+enum GMF_STATUS_MASK {
+       GMF_STATUS_MASK_DATA_UNDERFLOWED = 1,
+       GMF_STATUS_MASK_IFG_ADJUSTED
+};
+
+struct nthw_gmf {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_gmf;
+       int mn_instance;
+
+       nthw_register_t *mp_ctrl;
+       nthw_field_t *mp_ctrl_enable;
+       nthw_field_t *mp_ctrl_ifg_enable;
+       nthw_field_t *mp_ctrl_ifg_tx_now_always;
+       nthw_field_t *mp_ctrl_ifg_tx_on_ts_always;
+       nthw_field_t *mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock;
+       nthw_field_t *mp_ctrl_ifg_auto_adjust_enable;
+       nthw_field_t *mp_ctrl_ts_inject_always;
+       nthw_field_t *mp_ctrl_fcs_always;
+
+       nthw_register_t *mp_speed;
+       nthw_field_t *mp_speed_ifg_speed;
+
+       nthw_register_t *mp_ifg_clock_delta;
+       nthw_field_t *mp_ifg_clock_delta_delta;
+
+       nthw_register_t *mp_ifg_clock_delta_adjust;
+       nthw_field_t *mp_ifg_clock_delta_adjust_delta;
+
+       nthw_register_t *mp_ifg_max_adjust_slack;
+       nthw_field_t *mp_ifg_max_adjust_slack_slack;
+
+       nthw_register_t *mp_debug_lane_marker;
+       nthw_field_t *mp_debug_lane_marker_compensation;
+
+       nthw_register_t *mp_stat_sticky;
+       nthw_field_t *mp_stat_sticky_data_underflowed;
+       nthw_field_t *mp_stat_sticky_ifg_adjusted;
+
+       nthw_register_t *mp_stat_next_pkt;
+       nthw_field_t *mp_stat_next_pkt_ns;
+
+       nthw_register_t *mp_stat_max_delayed_pkt;
+       nthw_field_t *mp_stat_max_delayed_pkt_ns;
+
+       nthw_register_t *mp_ts_inject;
+       nthw_field_t *mp_ts_inject_offset;
+       nthw_field_t *mp_ts_inject_pos;
+       int mn_param_gmf_ifg_speed_mul;
+       int mn_param_gmf_ifg_speed_div;
+
+       bool m_administrative_block;    /* Used to enforce license expiry */
+};
+
+typedef struct nthw_gmf nthw_gmf_t;
+typedef struct nthw_gmf nthw_gmf;
+
+int nthw_gmf_init(nthw_gmf_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable);
+
+int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p);
+
+int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val);
+int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double 
f_rate_limit_percent);
+
+#endif /* __NTHW_GMF_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_i2cm.h 
b/drivers/net/ntnic/nthw/core/include/nthw_i2cm.h
new file mode 100644
index 0000000000..9d08da07ee
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_i2cm.h
@@ -0,0 +1,51 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_II2CM_H__
+#define __NTHW_II2CM_H__
+
+#include "nthw_fpga_model.h"
+#include "pthread.h"
+
+struct nt_i2cm {
+       nthw_fpga_t *mp_fpga;
+
+       nthw_module_t *m_mod_i2cm;
+
+       int mn_i2c_instance;
+
+       nthw_register_t *mp_reg_prer_low;
+       nthw_field_t *mp_fld_prer_low_prer_low;
+
+       nthw_register_t *mp_reg_prer_high;
+       nthw_field_t *mp_fld_prer_high_prer_high;
+
+       nthw_register_t *mp_reg_ctrl;
+       nthw_field_t *mp_fld_ctrl_ien;
+       nthw_field_t *mp_fld_ctrl_en;
+
+       nthw_register_t *mp_reg_data;
+       nthw_field_t *mp_fld_data_data;
+
+       nthw_register_t *mp_reg_cmd_status;
+       nthw_field_t *mp_fld_cmd_status_cmd_status;
+
+       nthw_register_t *mp_reg_select;
+       nthw_field_t *mp_fld_select_select;
+
+       nthw_register_t *mp_reg_io_exp;
+       nthw_field_t *mp_fld_io_exp_rst;
+       nthw_field_t *mp_fld_io_exp_int_b;
+
+       pthread_mutex_t i2cmmutex;
+};
+
+typedef struct nt_i2cm nthw_i2cm_t;
+typedef struct nt_i2cm nt_i2cm;
+
+int nthw_i2cm_read(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t 
*value);
+int nthw_i2cm_write(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, 
uint8_t value);
+
+#endif /* __NTHW_II2CM_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_pci_rd_tg.h 
b/drivers/net/ntnic/nthw/core/include/nthw_pci_rd_tg.h
new file mode 100644
index 0000000000..3cef7809ea
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_pci_rd_tg.h
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_PCI_RD_TG_H__
+#define __NTHW_PCI_RD_TG_H__
+
+struct nthw_pci_rd_tg {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_pci_rd_tg;
+       int mn_instance;
+
+       int mn_param_pci_ta_tg_present;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_data0;
+       nthw_field_t *mp_fld_pci_rd_tg_phys_addr_low;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_data1;
+       nthw_field_t *mp_fld_pci_rd_tg_phys_addr_high;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_data2;
+       nthw_field_t *mp_fld_pci_rd_tg_req_size;
+       nthw_field_t *mp_fld_pci_rd_tg_req_hid;
+       nthw_field_t *mp_fld_pci_rd_tg_wait;
+       nthw_field_t *mp_fld_pci_rd_tg_wrap;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_addr;
+       nthw_field_t *mp_fld_pci_rd_tg_ram_addr;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_run;
+       nthw_field_t *mp_fld_pci_rd_tg_run_iteration;
+
+       nthw_register_t *mp_reg_pci_rd_tg_rd_ctrl;
+       nthw_field_t *mp_fld_pci_rd_tg_ctrl_rdy;
+};
+
+typedef struct nthw_pci_rd_tg nthw_pci_rd_tg_t;
+typedef struct nthw_pci_rd_tg nthw_pci_rd_tg;
+
+nthw_pci_rd_tg_t *nthw_pci_rd_tg_new(void);
+int nthw_pci_rd_tg_init(nthw_pci_rd_tg_t *p, nthw_fpga_t *p_fpga, int 
n_instance);
+
+void nthw_pci_rd_tg_set_phys_addr(nthw_pci_rd_tg_t *p, uint64_t n_phys_addr);
+void nthw_pci_rd_tg_set_ram_addr(nthw_pci_rd_tg_t *p, int n_ram_addr);
+void nthw_pci_rd_tg_set_ram_data(nthw_pci_rd_tg_t *p, uint32_t req_size, bool 
wait, bool wrap);
+void nthw_pci_rd_tg_set_run(nthw_pci_rd_tg_t *p, int n_iterations);
+uint32_t nthw_pci_rd_tg_get_ctrl_rdy(nthw_pci_rd_tg_t *p);
+
+#endif /* __NTHW_PCI_RD_TG_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_pci_ta.h 
b/drivers/net/ntnic/nthw/core/include/nthw_pci_ta.h
new file mode 100644
index 0000000000..56892d25d2
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_pci_ta.h
@@ -0,0 +1,40 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_PCI_TA_H__
+#define __NTHW_PCI_TA_H__
+
+struct nthw_pci_ta {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_pci_ta;
+       int mn_instance;
+
+       int mn_param_pci_ta_tg_present;
+
+       nthw_register_t *mp_reg_pci_ta_ctrl;
+       nthw_field_t *mp_fld_pci_ta_ctrl_enable;
+       nthw_register_t *mp_reg_pci_ta_packet_good;
+       nthw_field_t *mp_fld_pci_ta_packet_good_amount;
+       nthw_register_t *mp_reg_pci_ta_packet_bad;
+       nthw_field_t *mp_fld_pci_ta_packet_bad_amount;
+       nthw_register_t *mp_reg_pci_ta_length_error;
+       nthw_field_t *mp_fld_pci_ta_length_error_amount;
+       nthw_register_t *mp_reg_pci_ta_payload_error;
+       nthw_field_t *mp_fld_pci_ta_payload_error_amount;
+};
+
+typedef struct nthw_pci_ta nthw_pci_ta_t;
+typedef struct nthw_pci_ta nthw_pci_ta;
+
+nthw_pci_ta_t *nthw_pci_ta_new(void);
+int nthw_pci_ta_init(nthw_pci_ta_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+void nthw_pci_ta_set_control_enable(nthw_pci_ta_t *p, uint32_t val);
+void nthw_pci_ta_get_packet_good(nthw_pci_ta_t *p, uint32_t *val);
+void nthw_pci_ta_get_packet_bad(nthw_pci_ta_t *p, uint32_t *val);
+void nthw_pci_ta_get_length_error(nthw_pci_ta_t *p, uint32_t *val);
+void nthw_pci_ta_get_payload_error(nthw_pci_ta_t *p, uint32_t *val);
+
+#endif /* __NTHW_PCI_TA_H__ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_pci_wr_tg.h 
b/drivers/net/ntnic/nthw/core/include/nthw_pci_wr_tg.h
new file mode 100644
index 0000000000..e0323d1d24
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_pci_wr_tg.h
@@ -0,0 +1,55 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef __NTHW_PCI_WR_TG_H__
+#define __NTHW_PCI_WR_TG_H__
+
+struct nthw_pci_wr_tg {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_pci_wr_tg;
+       int mn_instance;
+
+       int mn_param_pci_ta_tg_present;
+
+       nthw_register_t *mp_reg_pci_wr_tg_data0;
+       nthw_field_t *mp_fld_pci_wr_tg_phys_addr_low;
+
+       nthw_register_t *mp_reg_pci_wr_tg_data1;
+       nthw_field_t *mp_fld_pci_wr_tg_phys_addr_high;
+
+       nthw_register_t *mp_reg_pci_wr_tg_data2;
+       nthw_field_t *mp_fld_pci_wr_tg_req_size;
+       nthw_field_t *mp_fld_pci_wr_tg_req_hid;
+       nthw_field_t *mp_fld_pci_wr_tg_inc_mode;
+       nthw_field_t *mp_fld_pci_wr_tg_wait;
+       nthw_field_t *mp_fld_pci_wr_tg_wrap;
+
+       nthw_register_t *mp_reg_pci_wr_tg_addr;
+       nthw_field_t *mp_fld_pci_wr_tg_ram_addr;
+
+       nthw_register_t *mp_reg_pci_wr_tg_run;
+       nthw_field_t *mp_fld_pci_wr_tg_run_iteration;
+
+       nthw_register_t *mp_reg_pci_wr_tg_ctrl;
+       nthw_field_t *mp_fld_pci_wr_tg_ctrl_rdy;
+
+       nthw_register_t *mp_reg_pci_wr_tg_seq;
+       nthw_field_t *mp_fld_pci_wr_tg_seq_sequence;
+};
+
+typedef struct nthw_pci_wr_tg nthw_pci_wr_tg_t;
+typedef struct nthw_pci_wr_tg nthw_pci_wr_tg;
+
+nthw_pci_wr_tg_t *nthw_pci_wr_tg_new(void);
+int nthw_pci_wr_tg_init(nthw_pci_wr_tg_t *p, nthw_fpga_t *p_fpga, int 
n_instance);
+
+void nthw_pci_wr_tg_set_phys_addr(nthw_pci_wr_tg_t *p, uint64_t n_phys_addr);
+void nthw_pci_wr_tg_set_ram_addr(nthw_pci_wr_tg_t *p, int n_ram_addr);
+void nthw_pci_wr_tg_set_ram_data(nthw_pci_wr_tg_t *p, uint32_t req_size, bool 
wait, bool wrap,
+       bool inc);
+void nthw_pci_wr_tg_set_run(nthw_pci_wr_tg_t *p, int n_iterations);
+uint32_t nthw_pci_wr_tg_get_ctrl_rdy(nthw_pci_wr_tg_t *p);
+
+#endif /* __NTHW_PCI_WR_TG_H__ */
diff --git a/drivers/net/ntnic/nthw/core/nthw_gfg.c 
b/drivers/net/ntnic/nthw/core/nthw_gfg.c
new file mode 100644
index 0000000000..88f349eae3
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_gfg.c
@@ -0,0 +1,365 @@
+/*
+ * 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_gfg.h"
+
+nthw_gfg_t *nthw_gfg_new(void)
+{
+       nthw_gfg_t *p = malloc(sizeof(nthw_gfg_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_gfg_t));
+
+       return p;
+}
+
+int nthw_gfg_init(nthw_gfg_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_GFG, 
n_instance);
+       const char *const p_adapter_id_str = 
p_fpga->p_fpga_info->mp_adapter_id_str;
+       nthw_register_t *p_reg;
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: GFG %d: no such instance\n", 
p_adapter_id_str, n_instance);
+               return -1;
+       }
+
+       p->mp_fpga = p_fpga;
+       p->mn_instance = n_instance;
+       p->mp_mod_gfg = mod;
+
+       p->mn_param_gfg_present = nthw_fpga_get_product_param(p_fpga, 
NT_GFG_PRESENT, 0);
+
+       if (!p->mn_param_gfg_present) {
+               NT_LOG(ERR,
+                       NTHW,
+                       "%s: %s: GFG %d module found - but logically not 
present - failing",
+                       __func__,
+                       p_adapter_id_str,
+                       p->mn_instance);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL0);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[0] = nthw_register_query_field(p_reg, 
GFG_CTRL0_ENABLE);
+               p->mpa_fld_ctrl_mode[0] = nthw_register_query_field(p_reg, 
GFG_CTRL0_MODE);
+               p->mpa_fld_ctrl_prbs_en[0] = nthw_register_query_field(p_reg, 
GFG_CTRL0_PRBS_EN);
+               p->mpa_fld_ctrl_size[0] = nthw_register_query_field(p_reg, 
GFG_CTRL0_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL1);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[1] = nthw_register_query_field(p_reg, 
GFG_CTRL1_ENABLE);
+               p->mpa_fld_ctrl_mode[1] = nthw_register_query_field(p_reg, 
GFG_CTRL1_MODE);
+               p->mpa_fld_ctrl_prbs_en[1] = nthw_register_query_field(p_reg, 
GFG_CTRL1_PRBS_EN);
+               p->mpa_fld_ctrl_size[1] = nthw_register_query_field(p_reg, 
GFG_CTRL1_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL2);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[2] = nthw_register_query_field(p_reg, 
GFG_CTRL2_ENABLE);
+               p->mpa_fld_ctrl_mode[2] = nthw_register_query_field(p_reg, 
GFG_CTRL2_MODE);
+               p->mpa_fld_ctrl_prbs_en[2] = nthw_register_query_field(p_reg, 
GFG_CTRL2_PRBS_EN);
+               p->mpa_fld_ctrl_size[2] = nthw_register_query_field(p_reg, 
GFG_CTRL2_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL3);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[3] = nthw_register_query_field(p_reg, 
GFG_CTRL3_ENABLE);
+               p->mpa_fld_ctrl_mode[3] = nthw_register_query_field(p_reg, 
GFG_CTRL3_MODE);
+               p->mpa_fld_ctrl_prbs_en[3] = nthw_register_query_field(p_reg, 
GFG_CTRL3_PRBS_EN);
+               p->mpa_fld_ctrl_size[3] = nthw_register_query_field(p_reg, 
GFG_CTRL3_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL4);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[4] = nthw_register_query_field(p_reg, 
GFG_CTRL4_ENABLE);
+               p->mpa_fld_ctrl_mode[4] = nthw_register_query_field(p_reg, 
GFG_CTRL4_MODE);
+               p->mpa_fld_ctrl_prbs_en[4] = nthw_register_query_field(p_reg, 
GFG_CTRL4_PRBS_EN);
+               p->mpa_fld_ctrl_size[4] = nthw_register_query_field(p_reg, 
GFG_CTRL4_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL5);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[5] = nthw_register_query_field(p_reg, 
GFG_CTRL5_ENABLE);
+               p->mpa_fld_ctrl_mode[5] = nthw_register_query_field(p_reg, 
GFG_CTRL5_MODE);
+               p->mpa_fld_ctrl_prbs_en[5] = nthw_register_query_field(p_reg, 
GFG_CTRL5_PRBS_EN);
+               p->mpa_fld_ctrl_size[5] = nthw_register_query_field(p_reg, 
GFG_CTRL5_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL6);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[6] = nthw_register_query_field(p_reg, 
GFG_CTRL6_ENABLE);
+               p->mpa_fld_ctrl_mode[6] = nthw_register_query_field(p_reg, 
GFG_CTRL6_MODE);
+               p->mpa_fld_ctrl_prbs_en[6] = nthw_register_query_field(p_reg, 
GFG_CTRL6_PRBS_EN);
+               p->mpa_fld_ctrl_size[6] = nthw_register_query_field(p_reg, 
GFG_CTRL6_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_CTRL7);
+
+       if (p_reg) {
+               p->mpa_fld_ctrl_enable[7] = nthw_register_query_field(p_reg, 
GFG_CTRL7_ENABLE);
+               p->mpa_fld_ctrl_mode[7] = nthw_register_query_field(p_reg, 
GFG_CTRL7_MODE);
+               p->mpa_fld_ctrl_prbs_en[7] = nthw_register_query_field(p_reg, 
GFG_CTRL7_PRBS_EN);
+               p->mpa_fld_ctrl_size[7] = nthw_register_query_field(p_reg, 
GFG_CTRL7_SIZE);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN0);
+
+       if (p_reg)
+               p->mpa_fld_run_run[0] = nthw_register_query_field(p_reg, 
GFG_RUN0_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN1);
+
+       if (p_reg)
+               p->mpa_fld_run_run[1] = nthw_register_query_field(p_reg, 
GFG_RUN1_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN2);
+
+       if (p_reg)
+               p->mpa_fld_run_run[2] = nthw_register_query_field(p_reg, 
GFG_RUN2_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN3);
+
+       if (p_reg)
+               p->mpa_fld_run_run[3] = nthw_register_query_field(p_reg, 
GFG_RUN3_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN4);
+
+       if (p_reg)
+               p->mpa_fld_run_run[4] = nthw_register_query_field(p_reg, 
GFG_RUN4_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN5);
+
+       if (p_reg)
+               p->mpa_fld_run_run[5] = nthw_register_query_field(p_reg, 
GFG_RUN5_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN6);
+
+       if (p_reg)
+               p->mpa_fld_run_run[6] = nthw_register_query_field(p_reg, 
GFG_RUN6_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_RUN7);
+
+       if (p_reg)
+               p->mpa_fld_run_run[7] = nthw_register_query_field(p_reg, 
GFG_RUN7_RUN);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID0);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[0] = nthw_register_query_field(p_reg, 
GFG_STREAMID0_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID1);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[1] = nthw_register_query_field(p_reg, 
GFG_STREAMID1_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID2);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[2] = nthw_register_query_field(p_reg, 
GFG_STREAMID2_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID3);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[3] = nthw_register_query_field(p_reg, 
GFG_STREAMID3_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID4);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[4] = nthw_register_query_field(p_reg, 
GFG_STREAMID4_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID5);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[5] = nthw_register_query_field(p_reg, 
GFG_STREAMID5_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID6);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[6] = nthw_register_query_field(p_reg, 
GFG_STREAMID6_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_STREAMID7);
+
+       if (p_reg)
+               p->mpa_fld_stream_id_val[7] = nthw_register_query_field(p_reg, 
GFG_STREAMID7_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK0);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[0] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK0_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK1);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[1] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK1_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK2);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[2] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK2_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK3);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[3] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK3_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK4);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[4] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK4_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK5);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[5] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK5_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK6);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[6] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK6_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_SIZEMASK7);
+
+       if (p_reg)
+               p->mpa_fld_size_mask[7] = nthw_register_query_field(p_reg, 
GFG_SIZEMASK7_VAL);
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE0);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[0] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE0_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE1);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[1] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE1_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE2);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[2] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE2_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE3);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[3] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE3_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE4);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[4] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE4_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE5);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[5] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE5_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE6);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[6] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE6_VAL);
+       }
+
+       p_reg = nthw_module_query_register(p->mp_mod_gfg, GFG_BURSTSIZE7);
+
+       if (p_reg) {
+               p->mpa_fld_burst_size_val[7] =
+                       nthw_register_query_field(p_reg, GFG_BURSTSIZE7_VAL);
+       }
+
+       return 0;
+}
+
+int nthw_gfg_setup(nthw_gfg_t *p,
+       const size_t n_intf_no,
+       const int n_enable,
+       const int n_frame_count,
+       const int n_frame_size,
+       const int n_frame_fill_mode,
+       const int n_frame_stream_id)
+{
+       if ((size_t)n_intf_no >= ARRAY_SIZE(p->mpa_fld_ctrl_enable) ||
+               n_intf_no >= ARRAY_SIZE(p->mpa_fld_ctrl_mode) ||
+               (size_t)n_intf_no >= ARRAY_SIZE(p->mpa_fld_ctrl_size) ||
+               n_intf_no >= ARRAY_SIZE(p->mpa_fld_stream_id_val) ||
+               (size_t)n_intf_no >= ARRAY_SIZE(p->mpa_fld_burst_size_val)) {
+               assert(false);
+               return -1;
+       }
+
+       if (p->mpa_fld_ctrl_enable[n_intf_no] == NULL || 
p->mpa_fld_ctrl_mode[n_intf_no] == NULL ||
+               p->mpa_fld_ctrl_size[n_intf_no] == NULL ||
+               p->mpa_fld_stream_id_val[n_intf_no] == NULL ||
+               p->mpa_fld_burst_size_val[n_intf_no] == NULL) {
+               assert(false);
+               return -1;
+       }
+
+       nthw_field_set_val_flush32(p->mpa_fld_stream_id_val[n_intf_no], 
n_frame_stream_id);
+       nthw_field_set_val_flush32(p->mpa_fld_burst_size_val[n_intf_no], 
n_frame_count);
+
+       if (p->mpa_fld_size_mask[n_intf_no])
+               nthw_field_set_val_flush32(p->mpa_fld_size_mask[n_intf_no], 0);
+
+       nthw_field_set_val32(p->mpa_fld_ctrl_mode[n_intf_no], 
n_frame_fill_mode);
+       nthw_field_set_val32(p->mpa_fld_ctrl_size[n_intf_no], n_frame_size);
+
+       if (p->mpa_fld_ctrl_prbs_en[n_intf_no])
+               nthw_field_set_val32(p->mpa_fld_ctrl_prbs_en[n_intf_no], 0);
+
+       nthw_field_set_val_flush32(p->mpa_fld_ctrl_enable[n_intf_no],
+               n_enable);      /* write enable and flush */
+
+       return 0;
+}
+
+int nthw_gfg_start(nthw_gfg_t *p,
+       const int n_intf_no,
+       const int n_frame_count,
+       const int n_frame_size,
+       const int n_frame_fill_mode,
+       const int n_frame_stream_id)
+{
+       return nthw_gfg_setup(p,
+                       n_intf_no,
+                       1,
+                       n_frame_count,
+                       n_frame_size,
+                       n_frame_fill_mode,
+                       n_frame_stream_id);
+}
+
+int nthw_gfg_stop(nthw_gfg_t *p, const int n_intf_no)
+{
+       return nthw_gfg_setup(p, n_intf_no, 0, 1, 0x666, 0, n_intf_no);
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_gmf.c 
b/drivers/net/ntnic/nthw/core/nthw_gmf.c
new file mode 100644
index 0000000000..3a54138578
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_gmf.c
@@ -0,0 +1,176 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include <limits.h>
+#include <math.h>
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "nthw_gmf.h"
+
+int nthw_gmf_init(nthw_gmf_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_GMF, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: GMF %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_gmf = mod;
+
+       p->mp_ctrl = nthw_module_get_register(p->mp_mod_gmf, GMF_CTRL);
+       p->mp_ctrl_enable = nthw_register_get_field(p->mp_ctrl, 
GMF_CTRL_ENABLE);
+       p->mp_ctrl_ifg_enable = nthw_register_get_field(p->mp_ctrl, 
GMF_CTRL_IFG_ENABLE);
+       p->mp_ctrl_ifg_auto_adjust_enable =
+               nthw_register_get_field(p->mp_ctrl, 
GMF_CTRL_IFG_AUTO_ADJUST_ENABLE);
+       p->mp_ctrl_ts_inject_always =
+               nthw_register_query_field(p->mp_ctrl, 
GMF_CTRL_TS_INJECT_ALWAYS);
+       p->mp_ctrl_fcs_always = nthw_register_query_field(p->mp_ctrl, 
GMF_CTRL_FCS_ALWAYS);
+
+       p->mp_speed = nthw_module_get_register(p->mp_mod_gmf, GMF_SPEED);
+       p->mp_speed_ifg_speed = nthw_register_get_field(p->mp_speed, 
GMF_SPEED_IFG_SPEED);
+
+       p->mp_ifg_clock_delta = nthw_module_get_register(p->mp_mod_gmf, 
GMF_IFG_SET_CLOCK_DELTA);
+       p->mp_ifg_clock_delta_delta =
+               nthw_register_get_field(p->mp_ifg_clock_delta, 
GMF_IFG_SET_CLOCK_DELTA_DELTA);
+
+       p->mp_ifg_max_adjust_slack =
+               nthw_module_get_register(p->mp_mod_gmf, 
GMF_IFG_MAX_ADJUST_SLACK);
+       p->mp_ifg_max_adjust_slack_slack = 
nthw_register_get_field(p->mp_ifg_max_adjust_slack,
+                       GMF_IFG_MAX_ADJUST_SLACK_SLACK);
+
+       p->mp_debug_lane_marker = nthw_module_get_register(p->mp_mod_gmf, 
GMF_DEBUG_LANE_MARKER);
+       p->mp_debug_lane_marker_compensation =
+               nthw_register_get_field(p->mp_debug_lane_marker,
+                       GMF_DEBUG_LANE_MARKER_COMPENSATION);
+
+       p->mp_stat_sticky = nthw_module_get_register(p->mp_mod_gmf, 
GMF_STAT_STICKY);
+       p->mp_stat_sticky_data_underflowed =
+               nthw_register_get_field(p->mp_stat_sticky, 
GMF_STAT_STICKY_DATA_UNDERFLOWED);
+       p->mp_stat_sticky_ifg_adjusted =
+               nthw_register_get_field(p->mp_stat_sticky, 
GMF_STAT_STICKY_IFG_ADJUSTED);
+
+       p->mn_param_gmf_ifg_speed_mul =
+               nthw_fpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_MUL, 1);
+       p->mn_param_gmf_ifg_speed_div =
+               nthw_fpga_get_product_param(p_fpga, NT_GMF_IFG_SPEED_DIV, 1);
+
+       p->m_administrative_block = false;
+
+       p->mp_stat_next_pkt = nthw_module_query_register(p->mp_mod_gmf, 
GMF_STAT_NEXT_PKT);
+
+       if (p->mp_stat_next_pkt) {
+               p->mp_stat_next_pkt_ns =
+                       nthw_register_query_field(p->mp_stat_next_pkt, 
GMF_STAT_NEXT_PKT_NS);
+
+       } else {
+               p->mp_stat_next_pkt_ns = NULL;
+       }
+
+       p->mp_stat_max_delayed_pkt =
+               nthw_module_query_register(p->mp_mod_gmf, 
GMF_STAT_MAX_DELAYED_PKT);
+
+       if (p->mp_stat_max_delayed_pkt) {
+               p->mp_stat_max_delayed_pkt_ns =
+                       nthw_register_query_field(p->mp_stat_max_delayed_pkt,
+                               GMF_STAT_MAX_DELAYED_PKT_NS);
+
+       } else {
+               p->mp_stat_max_delayed_pkt_ns = NULL;
+       }
+
+       p->mp_ctrl_ifg_tx_now_always =
+               nthw_register_query_field(p->mp_ctrl, 
GMF_CTRL_IFG_TX_NOW_ALWAYS);
+       p->mp_ctrl_ifg_tx_on_ts_always =
+               nthw_register_query_field(p->mp_ctrl, 
GMF_CTRL_IFG_TX_ON_TS_ALWAYS);
+
+       p->mp_ctrl_ifg_tx_on_ts_adjust_on_set_clock =
+               nthw_register_query_field(p->mp_ctrl, 
GMF_CTRL_IFG_TX_ON_TS_ADJUST_ON_SET_CLOCK);
+
+       p->mp_ifg_clock_delta_adjust =
+               nthw_module_query_register(p->mp_mod_gmf, 
GMF_IFG_SET_CLOCK_DELTA_ADJUST);
+
+       if (p->mp_ifg_clock_delta_adjust) {
+               p->mp_ifg_clock_delta_adjust_delta =
+                       nthw_register_query_field(p->mp_ifg_clock_delta_adjust,
+                               GMF_IFG_SET_CLOCK_DELTA_ADJUST_DELTA);
+
+       } else {
+               p->mp_ifg_clock_delta_adjust_delta = NULL;
+       }
+
+       p->mp_ts_inject = nthw_module_query_register(p->mp_mod_gmf, 
GMF_TS_INJECT);
+
+       if (p->mp_ts_inject) {
+               p->mp_ts_inject_offset =
+                       nthw_register_query_field(p->mp_ts_inject, 
GMF_TS_INJECT_OFFSET);
+               p->mp_ts_inject_pos =
+                       nthw_register_query_field(p->mp_ts_inject, 
GMF_TS_INJECT_POS);
+
+       } else {
+               p->mp_ts_inject_offset = NULL;
+               p->mp_ts_inject_pos = NULL;
+       }
+
+       return 0;
+}
+
+void nthw_gmf_set_enable(nthw_gmf_t *p, bool enable)
+{
+       if (!p->m_administrative_block)
+               nthw_field_set_val_flush32(p->mp_ctrl_enable, enable ? 1 : 0);
+}
+
+int nthw_gmf_set_ifg_speed_raw(nthw_gmf_t *p, uint64_t n_speed_val)
+{
+       if (n_speed_val <= (1ULL << 
(nthw_field_get_bit_width(p->mp_speed_ifg_speed) - 1))) {
+               nthw_field_set_val(p->mp_speed_ifg_speed, (uint32_t 
*)&n_speed_val,
+                       (nthw_field_get_bit_width(p->mp_speed_ifg_speed) <= 32 
? 1
+                               : 2));
+               nthw_field_flush_register(p->mp_speed_ifg_speed);
+               return 0;
+       }
+
+       return -1;
+}
+
+int nthw_gmf_get_ifg_speed_bit_width(nthw_gmf_t *p)
+{
+       const int n_bit_width = nthw_field_get_bit_width(p->mp_speed_ifg_speed);
+       /* Sanity check: GMF ver 1.2 is bw 22 - GMF ver 1.3 is bw 64 */
+       assert(n_bit_width >= 22);
+       return n_bit_width;
+}
+
+int nthw_gmf_set_ifg_speed_percent(nthw_gmf_t *p, const double 
f_rate_limit_percent)
+{
+       uint64_t n_speed_val;
+
+       if (f_rate_limit_percent == 0.0 || f_rate_limit_percent == 100.0) {
+               n_speed_val = 0;
+
+       } else if (f_rate_limit_percent <= 99) {
+               const int n_bit_width = (nthw_gmf_get_ifg_speed_bit_width(p) / 
2);
+               const double f_adj_rate =
+                       ((double)(f_rate_limit_percent * 
(double)p->mn_param_gmf_ifg_speed_mul) /
+                               p->mn_param_gmf_ifg_speed_div / 100);
+               const double f_speed = ((1UL / f_adj_rate) - 1) * 
exp2(n_bit_width);
+               n_speed_val = (uint64_t)f_speed;
+
+       } else {
+               return -1;
+       }
+
+       return nthw_gmf_set_ifg_speed_raw(p, n_speed_val);
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_i2cm.c 
b/drivers/net/ntnic/nthw/core/nthw_i2cm.c
new file mode 100644
index 0000000000..f76fab5783
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_i2cm.c
@@ -0,0 +1,197 @@
+/*
+ * 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_i2cm.h"
+
+#define NT_I2C_CMD_START 0x80
+#define NT_I2C_CMD_STOP 0x40
+#define NT_I2C_CMD_RD 0x20
+#define NT_I2C_CMD_WR 0x10
+#define NT_I2C_CMD_NACK 0x08
+#define NT_I2C_CMD_IRQ_ACK 0x01
+
+#define NT_I2C_STATUS_NACK 0x80
+#define NT_I2C_STATUS_BUSY 0x40
+#define NT_I2C_STATUS_AL 0x20
+#define NT_I2C_STATUS_TIP 0x02
+#define NT_I2C_STATUS_IRQ 0x01
+
+#define NT_I2C_TRANSMIT_WR 0x00
+#define NT_I2C_TRANSMIT_RD 0x01
+
+#define NUM_RETRIES 50U
+#define SLEEP_USECS 100U/* 0.1 ms */
+
+#define I2C_TRANSMIT_WR (0x00)
+#define I2C_TRANSMIT_RD (0x01)
+
+static bool nthw_i2cm_ready(nthw_i2cm_t *p, bool wait_for_ack)
+{
+       uint32_t flags = NT_I2C_STATUS_TIP | (wait_for_ack ? NT_I2C_STATUS_NACK 
: 0U);
+
+       for (uint32_t i = 0U; i < NUM_RETRIES; i++) {
+               uint32_t status = 
nthw_field_get_updated(p->mp_fld_cmd_status_cmd_status);
+               uint32_t ready = (status & flags) == 0U;
+               /* MUST have a short break to avoid time-outs, even if ready == 
true */
+               nt_os_wait_usec(SLEEP_USECS);
+
+               if (ready)
+                       return true;
+       }
+
+       return false;
+}
+
+static int nthw_i2cm_write_internal(nthw_i2cm_t *p, uint8_t value)
+{
+       /* Write data to data register */
+       nthw_field_set_val_flush32(p->mp_fld_data_data, value);
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, true)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out writing data %u", 
__PRETTY_FUNCTION__, value);
+               return 1;
+       }
+
+       /* Generate stop condition and clear interrupt */
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, true)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", 
__PRETTY_FUNCTION__);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int nthw_i2cm_write_reg_addr_internal(nthw_i2cm_t *p, uint8_t dev_addr, 
uint8_t reg_addr,
+       bool send_stop)
+{
+       /* Write device address to data register */
+       nthw_field_set_val_flush32(p->mp_fld_data_data,
+               (uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_WR));
+
+       /* #Set start condition along with secondary I2C dev_addr */
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, true)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out writing device address %u, 
reg_addr=%u",
+                       __PRETTY_FUNCTION__, dev_addr, reg_addr);
+               return 1;
+       }
+
+       /* Writing I2C register address */
+       nthw_field_set_val_flush32(p->mp_fld_data_data, reg_addr);
+
+       if (send_stop) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK | NT_I2C_CMD_STOP);
+
+       } else {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+       }
+
+       if (!nthw_i2cm_ready(p, true)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out writing register address %u", 
__PRETTY_FUNCTION__,
+                       reg_addr);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int nthw_i2cm_read_internal(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t 
*value)
+{
+       /* Write I2C device address - with LSBit set to READ */
+
+       nthw_field_set_val_flush32(p->mp_fld_data_data,
+               (uint8_t)(dev_addr << 1 | NT_I2C_TRANSMIT_RD));
+       /* #Send START condition along with secondary I2C dev_addr */
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_START | NT_I2C_CMD_WR | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, true)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out rewriting device address %u", 
__PRETTY_FUNCTION__,
+                       dev_addr);
+               return 1;
+       }
+
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_RD | NT_I2C_CMD_NACK | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, false)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out during read operation", 
__PRETTY_FUNCTION__);
+               return 1;
+       }
+
+       *value = (uint8_t)nthw_field_get_updated(p->mp_fld_data_data);
+
+       /* Generate stop condition and clear interrupt */
+       nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+               NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+
+       if (!nthw_i2cm_ready(p, false)) {
+               nthw_field_set_val_flush32(p->mp_fld_cmd_status_cmd_status,
+                       NT_I2C_CMD_STOP | NT_I2C_CMD_IRQ_ACK);
+               NT_LOG(ERR, NTHW, "%s: Time-out sending stop condition", 
__PRETTY_FUNCTION__);
+               return 1;
+       }
+
+       return 0;
+}
+
+int nthw_i2cm_read(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, uint8_t 
*value)
+{
+       int status;
+       status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, 
false);
+
+       if (status != 0)
+               return status;
+
+       status = nthw_i2cm_read_internal(p, dev_addr, value);
+
+       if (status != 0)
+               return status;
+
+       return 0;
+}
+
+int nthw_i2cm_write(nthw_i2cm_t *p, uint8_t dev_addr, uint8_t reg_addr, 
uint8_t value)
+{
+       int status;
+       status = nthw_i2cm_write_reg_addr_internal(p, dev_addr, reg_addr, 
false);
+
+       if (status != 0)
+               return status;
+
+       status = nthw_i2cm_write_internal(p, value);
+
+       if (status != 0)
+               return status;
+
+       return 0;
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_pci_rd_tg.c 
b/drivers/net/ntnic/nthw/core/nthw_pci_rd_tg.c
new file mode 100644
index 0000000000..5648a09853
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_pci_rd_tg.c
@@ -0,0 +1,115 @@
+/*
+ * 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_pci_rd_tg.h"
+
+nthw_pci_rd_tg_t *nthw_pci_rd_tg_new(void)
+{
+       nthw_pci_rd_tg_t *p = malloc(sizeof(nthw_pci_rd_tg_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_pci_rd_tg_t));
+
+       return p;
+}
+
+int nthw_pci_rd_tg_init(nthw_pci_rd_tg_t *p, nthw_fpga_t *p_fpga, int 
n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_PCI_RD_TG, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: PCI_RD_TG %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_pci_rd_tg = mod;
+
+       p->mn_param_pci_ta_tg_present =
+               nthw_fpga_get_product_param(p_fpga, NT_PCI_TA_TG_PRESENT, 1);
+
+       p->mp_reg_pci_rd_tg_rd_data0 =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_RDDATA0);
+       p->mp_fld_pci_rd_tg_phys_addr_low =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_data0,
+                       PCI_RD_TG_TG_RDDATA0_PHYS_ADDR_LOW);
+
+       p->mp_reg_pci_rd_tg_rd_data1 =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_RDDATA1);
+       p->mp_fld_pci_rd_tg_phys_addr_high =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_data1,
+                       PCI_RD_TG_TG_RDDATA1_PHYS_ADDR_HIGH);
+
+       p->mp_reg_pci_rd_tg_rd_data2 =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_RDDATA2);
+       p->mp_fld_pci_rd_tg_req_size = 
nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_data2,
+                       PCI_RD_TG_TG_RDDATA2_REQ_SIZE);
+       p->mp_fld_pci_rd_tg_wait =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_data2, 
PCI_RD_TG_TG_RDDATA2_WAIT);
+       p->mp_fld_pci_rd_tg_wrap =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_data2, 
PCI_RD_TG_TG_RDDATA2_WRAP);
+       /* optional VF host id */
+       p->mp_fld_pci_rd_tg_req_hid = 
nthw_register_query_field(p->mp_reg_pci_rd_tg_rd_data2,
+                       PCI_RD_TG_TG_RDDATA2_REQ_HID);
+
+       p->mp_reg_pci_rd_tg_rd_addr =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_RDADDR);
+       p->mp_fld_pci_rd_tg_ram_addr =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_addr, 
PCI_RD_TG_TG_RDADDR_RAM_ADDR);
+
+       p->mp_reg_pci_rd_tg_rd_run =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_RD_RUN);
+       p->mp_fld_pci_rd_tg_run_iteration =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_run,
+                       PCI_RD_TG_TG_RD_RUN_RD_ITERATION);
+
+       p->mp_reg_pci_rd_tg_rd_ctrl =
+               nthw_module_get_register(p->mp_mod_pci_rd_tg, 
PCI_RD_TG_TG_CTRL);
+       p->mp_fld_pci_rd_tg_ctrl_rdy =
+               nthw_register_get_field(p->mp_reg_pci_rd_tg_rd_ctrl, 
PCI_RD_TG_TG_CTRL_TG_RD_RDY);
+
+       return 0;
+}
+
+void nthw_pci_rd_tg_set_phys_addr(nthw_pci_rd_tg_t *p, uint64_t n_phys_addr)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_rd_tg_phys_addr_low,
+               (uint32_t)(n_phys_addr & ((1UL << 32) - 1)));
+       nthw_field_set_val_flush32(p->mp_fld_pci_rd_tg_phys_addr_high,
+               (uint32_t)((n_phys_addr >> 32) & ((1UL << 32) - 1)));
+}
+
+void nthw_pci_rd_tg_set_ram_addr(nthw_pci_rd_tg_t *p, int n_ram_addr)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_rd_tg_ram_addr, n_ram_addr);
+}
+
+void nthw_pci_rd_tg_set_ram_data(nthw_pci_rd_tg_t *p, uint32_t req_size, bool 
wait, bool wrap)
+{
+       nthw_field_set_val32(p->mp_fld_pci_rd_tg_req_size, req_size);
+       nthw_field_set_val32(p->mp_fld_pci_rd_tg_wait, wait);
+       nthw_field_set_val32(p->mp_fld_pci_rd_tg_wrap, wrap);
+       nthw_field_flush_register(p->mp_fld_pci_rd_tg_wrap);
+}
+
+void nthw_pci_rd_tg_set_run(nthw_pci_rd_tg_t *p, int n_iterations)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_rd_tg_run_iteration, 
n_iterations);
+}
+
+uint32_t nthw_pci_rd_tg_get_ctrl_rdy(nthw_pci_rd_tg_t *p)
+{
+       return nthw_field_get_updated(p->mp_fld_pci_rd_tg_ctrl_rdy);
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_pci_ta.c 
b/drivers/net/ntnic/nthw/core/nthw_pci_ta.c
new file mode 100644
index 0000000000..e23d9b7c7b
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_pci_ta.c
@@ -0,0 +1,94 @@
+/*
+ * 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_pci_ta.h"
+
+nthw_pci_ta_t *nthw_pci_ta_new(void)
+{
+       nthw_pci_ta_t *p = malloc(sizeof(nthw_pci_ta_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_pci_ta_t));
+
+       return p;
+}
+
+int nthw_pci_ta_init(nthw_pci_ta_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_PCI_TA, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: PCI_TA %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_pci_ta = mod;
+
+       p->mn_param_pci_ta_tg_present =
+               nthw_fpga_get_product_param(p_fpga, NT_PCI_TA_TG_PRESENT, 1);
+
+       p->mp_reg_pci_ta_ctrl = nthw_module_get_register(p->mp_mod_pci_ta, 
PCI_TA_CONTROL);
+       p->mp_fld_pci_ta_ctrl_enable =
+               nthw_register_get_field(p->mp_reg_pci_ta_ctrl, 
PCI_TA_CONTROL_ENABLE);
+
+       p->mp_reg_pci_ta_packet_good =
+               nthw_module_get_register(p->mp_mod_pci_ta, PCI_TA_PACKET_GOOD);
+       p->mp_fld_pci_ta_packet_good_amount =
+               nthw_register_get_field(p->mp_reg_pci_ta_packet_good, 
PCI_TA_PACKET_GOOD_AMOUNT);
+
+       p->mp_reg_pci_ta_packet_bad =
+               nthw_module_get_register(p->mp_mod_pci_ta, PCI_TA_PACKET_BAD);
+       p->mp_fld_pci_ta_packet_bad_amount =
+               nthw_register_get_field(p->mp_reg_pci_ta_packet_bad, 
PCI_TA_PACKET_BAD_AMOUNT);
+
+       p->mp_reg_pci_ta_length_error =
+               nthw_module_get_register(p->mp_mod_pci_ta, PCI_TA_LENGTH_ERROR);
+       p->mp_fld_pci_ta_length_error_amount =
+               nthw_register_get_field(p->mp_reg_pci_ta_length_error, 
PCI_TA_LENGTH_ERROR_AMOUNT);
+
+       p->mp_reg_pci_ta_payload_error =
+               nthw_module_get_register(p->mp_mod_pci_ta, 
PCI_TA_PAYLOAD_ERROR);
+       p->mp_fld_pci_ta_payload_error_amount =
+               nthw_register_get_field(p->mp_reg_pci_ta_payload_error,
+                       PCI_TA_PAYLOAD_ERROR_AMOUNT);
+
+       return 0;
+}
+
+void nthw_pci_ta_set_control_enable(nthw_pci_ta_t *p, uint32_t val)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_ta_ctrl_enable, val);
+}
+
+void nthw_pci_ta_get_packet_good(nthw_pci_ta_t *p, uint32_t *val)
+{
+       *val = nthw_field_get_updated(p->mp_fld_pci_ta_packet_good_amount);
+}
+
+void nthw_pci_ta_get_packet_bad(nthw_pci_ta_t *p, uint32_t *val)
+{
+       *val = nthw_field_get_updated(p->mp_fld_pci_ta_packet_bad_amount);
+}
+
+void nthw_pci_ta_get_length_error(nthw_pci_ta_t *p, uint32_t *val)
+{
+       *val = nthw_field_get_updated(p->mp_fld_pci_ta_length_error_amount);
+}
+
+void nthw_pci_ta_get_payload_error(nthw_pci_ta_t *p, uint32_t *val)
+{
+       *val = nthw_field_get_updated(p->mp_fld_pci_ta_payload_error_amount);
+}
diff --git a/drivers/net/ntnic/nthw/core/nthw_pci_wr_tg.c 
b/drivers/net/ntnic/nthw/core/nthw_pci_wr_tg.c
new file mode 100644
index 0000000000..dfcfe7b1d2
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_pci_wr_tg.c
@@ -0,0 +1,122 @@
+/*
+ * 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_pci_wr_tg.h"
+
+nthw_pci_wr_tg_t *nthw_pci_wr_tg_new(void)
+{
+       nthw_pci_wr_tg_t *p = malloc(sizeof(nthw_pci_wr_tg_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_pci_wr_tg_t));
+
+       return p;
+}
+
+int nthw_pci_wr_tg_init(nthw_pci_wr_tg_t *p, nthw_fpga_t *p_fpga, int 
n_instance)
+{
+       nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_PCI_WR_TG, 
n_instance);
+
+       if (p == NULL)
+               return mod == NULL ? -1 : 0;
+
+       if (mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: PCI_WR_TG %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_pci_wr_tg = mod;
+
+       p->mn_param_pci_ta_tg_present =
+               nthw_fpga_get_product_param(p_fpga, NT_PCI_TA_TG_PRESENT, 1);
+
+       p->mp_reg_pci_wr_tg_data0 =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_WRDATA0);
+       p->mp_fld_pci_wr_tg_phys_addr_low =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data0,
+                       PCI_WR_TG_TG_WRDATA0_PHYS_ADDR_LOW);
+
+       p->mp_reg_pci_wr_tg_data1 =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_WRDATA1);
+       p->mp_fld_pci_wr_tg_phys_addr_high =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data1,
+                       PCI_WR_TG_TG_WRDATA1_PHYS_ADDR_HIGH);
+
+       p->mp_reg_pci_wr_tg_data2 =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_WRDATA2);
+       p->mp_fld_pci_wr_tg_req_size =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data2, 
PCI_WR_TG_TG_WRDATA2_REQ_SIZE);
+       p->mp_fld_pci_wr_tg_inc_mode =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data2, 
PCI_WR_TG_TG_WRDATA2_INC_MODE);
+       p->mp_fld_pci_wr_tg_wait =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data2, 
PCI_WR_TG_TG_WRDATA2_WAIT);
+       p->mp_fld_pci_wr_tg_wrap =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_data2, 
PCI_WR_TG_TG_WRDATA2_WRAP);
+       /* optional VF host id */
+       p->mp_fld_pci_wr_tg_req_hid =
+               nthw_register_query_field(p->mp_reg_pci_wr_tg_data2, 
PCI_WR_TG_TG_WRDATA2_REQ_HID);
+
+       p->mp_reg_pci_wr_tg_addr =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_WRADDR);
+       p->mp_fld_pci_wr_tg_ram_addr =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_addr, 
PCI_WR_TG_TG_WRADDR_RAM_ADDR);
+
+       p->mp_reg_pci_wr_tg_run =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_WR_RUN);
+       p->mp_fld_pci_wr_tg_run_iteration =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_run, 
PCI_WR_TG_TG_WR_RUN_WR_ITERATION);
+
+       p->mp_reg_pci_wr_tg_ctrl =
+               nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_CTRL);
+       p->mp_fld_pci_wr_tg_ctrl_rdy =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_ctrl, 
PCI_WR_TG_TG_CTRL_TG_WR_RDY);
+
+       p->mp_reg_pci_wr_tg_seq = nthw_module_get_register(p->mp_mod_pci_wr_tg, 
PCI_WR_TG_TG_SEQ);
+       p->mp_fld_pci_wr_tg_seq_sequence =
+               nthw_register_get_field(p->mp_reg_pci_wr_tg_seq, 
PCI_WR_TG_TG_SEQ_SEQUENCE);
+
+       return 0;
+}
+
+void nthw_pci_wr_tg_set_phys_addr(nthw_pci_wr_tg_t *p, uint64_t n_phys_addr)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_wr_tg_phys_addr_low,
+               (uint32_t)(n_phys_addr & ((1UL << 32) - 1)));
+       nthw_field_set_val_flush32(p->mp_fld_pci_wr_tg_phys_addr_high,
+               (uint32_t)((n_phys_addr >> 32) & ((1UL << 32) - 1)));
+}
+
+void nthw_pci_wr_tg_set_ram_addr(nthw_pci_wr_tg_t *p, int n_ram_addr)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_wr_tg_ram_addr, n_ram_addr);
+}
+
+void nthw_pci_wr_tg_set_ram_data(nthw_pci_wr_tg_t *p, uint32_t req_size, bool 
wait, bool wrap,
+       bool inc)
+{
+       nthw_field_set_val32(p->mp_fld_pci_wr_tg_req_size, req_size);
+       nthw_field_set_val32(p->mp_fld_pci_wr_tg_wait, wait);
+       nthw_field_set_val32(p->mp_fld_pci_wr_tg_wrap, wrap);
+       nthw_field_set_val32(p->mp_fld_pci_wr_tg_inc_mode, inc);
+       nthw_field_flush_register(p->mp_fld_pci_wr_tg_inc_mode);
+}
+
+void nthw_pci_wr_tg_set_run(nthw_pci_wr_tg_t *p, int n_iterations)
+{
+       nthw_field_set_val_flush32(p->mp_fld_pci_wr_tg_run_iteration, 
n_iterations);
+}
+
+uint32_t nthw_pci_wr_tg_get_ctrl_rdy(nthw_pci_wr_tg_t *p)
+{
+       return nthw_field_get_updated(p->mp_fld_pci_wr_tg_ctrl_rdy);
+}
-- 
2.45.0

Reply via email to