DPDK patches and discussions
 help / color / mirror / Atom feed
From: Serhii Iliushyk <sil-plv@napatech.com>
To: dev@dpdk.org
Cc: mko-plv@napatech.com, ckm@napatech.com,
	andrew.rybchenko@oktetlabs.ru, ferruh.yigit@amd.com
Subject: [PATCH v2 13/17] net/ntnic: add adapter initialization
Date: Fri, 31 May 2024 17:47:17 +0200	[thread overview]
Message-ID: <20240531154731.1856874-13-sil-plv@napatech.com> (raw)
In-Reply-To: <20240531154731.1856874-1-sil-plv@napatech.com>

Add ntnic HW interfaces (PCIe, I2C) API.

Signed-off-by: Serhii Iliushyk <sil-plv@napatech.com>
---
v2:
* Fixed WARNING:TYPO_SPELLING
---
 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 0205d7de8e..1a56a0f6c4 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -77,6 +77,8 @@ headers = files('rte_pmd_ntnic.h')
 # all sources
 sources = files(
     'dpdk_mod_reg.c',
+    'drivers/net/ntnic/driver/modules/adapter/nt4ga_pci_ta_tg.c',
+    'drivers/net/ntnic/driver/modules/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',
@@ -86,10 +88,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.44.0


  parent reply	other threads:[~2024-05-31 15:49 UTC|newest]

Thread overview: 101+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-30 14:48 [PATCH v1 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-05-30 14:48 ` [PATCH v1 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 07/17] net/ntnic: add API " Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 13/17] net/ntnic: add adapter initialization Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-05-30 14:49 ` [PATCH v1 17/17] net/ntnic: add NIM module Serhii Iliushyk
2024-05-31 15:47 ` [PATCH v2 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 07/17] net/ntnic: add API " Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-05-31 15:47   ` Serhii Iliushyk [this message]
2024-05-31 15:47   ` [PATCH v2 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-05-31 15:47   ` [PATCH v2 17/17] net/ntnic: add NIM module Serhii Iliushyk
2024-06-03 16:17 ` [PATCH v3 01/17] net/ntnic: Add registers for NapaTech SmartNiC Serhii Iliushyk
2024-06-03 16:17   ` [PATCH v3 02/17] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 03/17] net/ntnic: add interfaces for " Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 04/17] net/ntnic: add FPGA model implementation Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 05/17] net/ntnic: add NTNIC adapter interfaces Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 06/17] net/ntnic: add interfaces for PMD driver modules Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 07/17] net/ntnic: add API " Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 08/17] net/ntnic: add interfaces for flow API engine Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 09/17] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 10/17] net/ntnic: add Logs and utilities implementation Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 11/17] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 12/17] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 13/17] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 14/17] net/ntnic: add adapter initialization API Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 15/17] net/ntnic: add link management module Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 16/17] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-03 16:18   ` [PATCH v3 17/17] net/ntnic: add NIM module Serhii Iliushyk
2024-06-04 10:29   ` [PATCH v3 01/17] net/ntnic: Add registers for NapaTech SmartNiC Mykola Kostenok
2024-06-07 13:03     ` Serhii Iliushyk
2024-06-12  8:50       ` Ferruh Yigit
2024-06-12  8:55         ` Ferruh Yigit
2024-06-26 19:55 ` [PATCH v4 01/23] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 02/23] net/ntnic: add logging implementation Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 03/23] net/ntnic: add minimal initialization for PCI device Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 04/23] net/ntnic: add NT utilities implementation Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 05/23] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 06/23] net/ntnic: add NT NIC driver dependencies Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 07/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 08/23] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 09/23] net/ntnic: add registers and FPGA model for NapaTech NIC Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 10/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 11/23] net/ntnic: add FPGA initialization functionality Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 12/23] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 13/23] net/ntnic: add reset module for " Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 14/23] net/ntnic: add clock profiles " Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 15/23] net/ntnic: add MAC and packet features Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 16/23] net/ntnic: add link management module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 17/23] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 18/23] net/ntnic: add NIM module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 19/23] net/ntnic: add QSFP support Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 20/23] net/ntnic: add QSFP28 support Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 21/23] net/ntnic: add GPIO PHY module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 22/23] net/ntnic: add MAC PCS register interface module Serhii Iliushyk
2024-06-26 19:55   ` [PATCH v4 23/23] net/ntnic: add GMF (Generic MAC Feeder) module Serhii Iliushyk
2024-06-27  7:38 ` [PATCH v5 01/23] net/ntnic: add ethdev and makes PMD available Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 02/23] net/ntnic: add logging implementation Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 03/23] net/ntnic: add minimal initialization for PCI device Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 04/23] net/ntnic: add NT utilities implementation Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 05/23] net/ntnic: add VFIO module Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 06/23] net/ntnic: add NT NIC driver dependencies Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 07/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 08/23] net/ntnic: add adapter initialization Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 09/23] net/ntnic: add registers and FPGA model for NapaTech NIC Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 10/23] net/ntnic: add core platform functionality Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 11/23] net/ntnic: add FPGA initialization functionality Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 12/23] net/ntnic: add support of the NT200A0X smartNIC Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 13/23] net/ntnic: add reset module for " Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 14/23] net/ntnic: add clock profiles " Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 15/23] net/ntnic: add MAC and packet features Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 16/23] net/ntnic: add link management module Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 17/23] net/ntnic: add link 100G module Serhii Iliushyk
2024-06-27  7:38   ` [PATCH v5 18/23] net/ntnic: add NIM module Serhii Iliushyk
2024-06-27  7:39   ` [PATCH v5 19/23] net/ntnic: add QSFP support Serhii Iliushyk
2024-06-27  7:39   ` [PATCH v5 20/23] net/ntnic: add QSFP28 support Serhii Iliushyk
2024-06-27  7:39   ` [PATCH v5 21/23] net/ntnic: add GPIO PHY module Serhii Iliushyk
2024-06-27  7:39   ` [PATCH v5 22/23] net/ntnic: add MAC PCS register interface module Serhii Iliushyk
2024-06-27  7:39   ` [PATCH v5 23/23] net/ntnic: add GMF (Generic MAC Feeder) module Serhii Iliushyk

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240531154731.1856874-13-sil-plv@napatech.com \
    --to=sil-plv@napatech.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=ckm@napatech.com \
    --cc=dev@dpdk.org \
    --cc=ferruh.yigit@amd.com \
    --cc=mko-plv@napatech.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).