From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <dev-bounces@dpdk.org>
Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124])
	by inbox.dpdk.org (Postfix) with ESMTP id BBA1E4414A;
	Mon,  3 Jun 2024 18:21:24 +0200 (CEST)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id CAABC42F01;
	Mon,  3 Jun 2024 18:19:05 +0200 (CEST)
Received: from egress-ip42b.ess.de.barracuda.com
 (egress-ip42b.ess.de.barracuda.com [18.185.115.246])
 by mails.dpdk.org (Postfix) with ESMTP id 5353442EF8
 for <dev@dpdk.org>; Mon,  3 Jun 2024 18:19:03 +0200 (CEST)
Received: from EUR04-HE1-obe.outbound.protection.outlook.com
 (mail-he1eur04lp2045.outbound.protection.outlook.com [104.47.13.45]) by
 mx-outbound43-249.eu-central-1c.ess.aws.cudaops.com (version=TLSv1.2
 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO);
 Mon, 03 Jun 2024 16:19:00 +0000
ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;
 b=jeMUM4GSmmgRb28e2lo0P4nqxsXlnrqK2JSFE6pDUltTDPV49UyOd8ppNcK1XroEQtBM6O9CwxFezY0dpx8aEFESgnJ1tjfWE9pQ9mjyKDjwL+PU5Pxmd/LLQmi8oKYQd53cZ0+73aFOIg9gptPrQWTLeQm/dvVpWyfm8WEVm6nW+R3+UPcYz8JcrBSuMORSPpJp3RKJu9ijApKok8Uo6b+vo0Jqv2/LrkVJHiqUNiDZCZz6PvJCHbEmP8P0A+rf2q2XX/s1yLruo7bl+JCxeVglr6znEN7mVaOWz1fAY43Fvxnbhy6V4+pL9h5SgTJthkSrdTZxoyThKQPrnTKY6Q==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; 
 s=arcselector9901;
 h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;
 bh=+A0f6Z23H89ZI0YhpAyXQMYMOAIQQfWQWXOuXTxzOrg=;
 b=hb+5U3wgR7g/F7X4hR4rWdnCL8ZQcWk4aUnMRYQZc9om+pHN9azhBBRxnBHlr15Riy9on0wVqG/zPdEHMtwS8Itx53R7Z7wX0NPfFLiPFEX4ZbCHdyccqefOq3XCLagtGBjYjKhSsQ391ct3meEdck2BocUZwmiUeE9q3YqqcjvvoNEqeP2hWOa4o3+Z6vhlKlpFIvwSG33noxL+Mo3OjSQXlfXSfMA+5Qwf4B4ig/UD28lnhqlvq4AeFaMxgdWXEh0J8IDLQT3Gc4yGreNgTfaduzMbo2kiwXiM+8j15MUOkDWrH/wLjPY6keA52gTjuN87sLrhkLSDolA0v4Afsg==
ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=fail (sender ip is
 178.72.21.4) smtp.rcpttodomain=dpdk.org smtp.mailfrom=napatech.com;
 dmarc=fail (p=reject sp=reject pct=100) action=oreject
 header.from=napatech.com; dkim=none (message not signed); arc=none (0)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=napatech.com;
 s=selector1;
 h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
 bh=+A0f6Z23H89ZI0YhpAyXQMYMOAIQQfWQWXOuXTxzOrg=;
 b=GD5U791MU/fXpeiDeNOyuPZ/mlAbOPLsgSQrBPV5RZo/c2Z42AID0G7G+UcMuBxOKQmhPCl/Ap42TyNTGqBRTNBHRabqcrwvfd9eECCGNw1jTGhvzp5OV4BaDfxTNWRza8X39cJsxVRdRFz7TtylVdMN7lybyO+4GnMqEyKeEXg=
Received: from AM6P191CA0086.EURP191.PROD.OUTLOOK.COM (2603:10a6:209:8a::27)
 by DBAP190MB0886.EURP190.PROD.OUTLOOK.COM (2603:10a6:10:1b0::16) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7633.27; Mon, 3 Jun
 2024 16:18:41 +0000
Received: from AMS0EPF00000196.eurprd05.prod.outlook.com
 (2603:10a6:209:8a:cafe::d) by AM6P191CA0086.outlook.office365.com
 (2603:10a6:209:8a::27) with Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7633.29 via Frontend
 Transport; Mon, 3 Jun 2024 16:18:41 +0000
X-MS-Exchange-Authentication-Results: spf=fail (sender IP is 178.72.21.4)
 smtp.mailfrom=napatech.com; dkim=none (message not signed)
 header.d=none;dmarc=fail action=oreject header.from=napatech.com;
Received-SPF: Fail (protection.outlook.com: domain of napatech.com does not
 designate 178.72.21.4 as permitted sender) receiver=protection.outlook.com;
 client-ip=178.72.21.4; helo=localhost.localdomain;
Received: from localhost.localdomain (178.72.21.4) by
 AMS0EPF00000196.mail.protection.outlook.com (10.167.16.217) with Microsoft
 SMTP Server id 15.20.7633.15 via Frontend Transport; Mon, 3 Jun 2024 16:18:41
 +0000
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 v3 13/17] net/ntnic: add adapter initialization
Date: Mon,  3 Jun 2024 18:18:10 +0200
Message-ID: <20240603161822.59552-13-sil-plv@napatech.com>
X-Mailer: git-send-email 2.45.0
In-Reply-To: <20240603161822.59552-1-sil-plv@napatech.com>
References: <20240530144929.4127931-1-sil-plv@napatech.com>
 <20240603161822.59552-1-sil-plv@napatech.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-EOPAttributedMessage: 0
X-MS-PublicTrafficType: Email
X-MS-TrafficTypeDiagnostic: AMS0EPF00000196:EE_|DBAP190MB0886:EE_
Content-Type: text/plain
X-MS-Office365-Filtering-Correlation-Id: 1314658a-a051-43a6-1617-08dc83e8d55c
X-MS-Exchange-SenderADCheck: 1
X-MS-Exchange-AntiSpam-Relay: 0
X-Microsoft-Antispam: BCL:0;
 ARA:13230031|82310400017|376005|36860700004|1800799015; 
X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?cIbWx4NUVJQeAclrzy5ox1Pdoyj54dC2lYfBhDcR4wxzfxkueG4+YrJA79UR?=
 =?us-ascii?Q?2mNvHnAOlg75wgwY/BEOGQLJIQy07Ye71w2xp4eJbPhJ2/Fv04mC2sg3Tfpq?=
 =?us-ascii?Q?fD19dTiDtvPKIWsuCTDOsqlaTRPLI/w8Svu61W7V04rHjQVt8U5ameQI6/Sb?=
 =?us-ascii?Q?MUGJ8+eru1+BIDlXTNHBsTFIjmyGhTj970sLpNtWQBcAIbkWI18qhhvKwmQb?=
 =?us-ascii?Q?riVRl4W++n54qvWlRXhUjIzrakVO4w0kU4rIX2Qa8kIaBj5mUHY0jeBT9gHD?=
 =?us-ascii?Q?LlSfV5aqmJfkmdoiQ93Fn9i9/A8TRoeHVG6CXwudv3xgvrp3z4JiVUKSS74p?=
 =?us-ascii?Q?g0VQYk6o0INnj+piIbIGmctIws2zuxEqWSijyKbPO6LphTzVNTxRYyN3HYqG?=
 =?us-ascii?Q?0zu6GRlNeZB/PdDbbA2+cXh1GjHYTejzbxinnYRs3ixKZk9pCFKNsyf+XnAm?=
 =?us-ascii?Q?41cZYe/dIqPpxuRC7duZmfN9qwGDuOEYxToCP3HCB/POfQiV6l7Ev2X5/Nbs?=
 =?us-ascii?Q?eVBoywaJviDlm+S0gByPVFsQtO7l3GrRnU+uOm9ScZA8V5QjD5E1OdHBaumG?=
 =?us-ascii?Q?Rmp5gyqrdnorDfV3F+414X1Vy5J/9LdrXIRgxj2TAJHnGxODsUS/GH0KuIhx?=
 =?us-ascii?Q?eeDwyViY6hFIb7bNnjAl2oHvAzpO5TytDDv8E/6Y3VY+zfn1g+Hxhx3YTcxA?=
 =?us-ascii?Q?IGK4Um5w+lY57n0P0RJzJj/PQZq0x5WwdDNqpXn+AGbNPSi5fu/9tDN5EuM6?=
 =?us-ascii?Q?EpbujBy1pE/mDU9mSop0Zmd9JZnCRadqIDxFhnkcMkAjVzohA+kT/xcJrN+e?=
 =?us-ascii?Q?lk0lPMouwT6uYecNwvejPf3QgJfv01TSEjVRQTMJjzp+mCY9qHUnjzorBR+L?=
 =?us-ascii?Q?J07Hs8UbJiRF1nv2WrDMelGwc7/qSm7u9IDvCx94UiDm3TgPUR/FMnAySH/4?=
 =?us-ascii?Q?1dhdjXcnVRC74bfvKPcdDhWOkOBZ7X1zJcNeY1Bd6Z2rx928r7gb/YjL2Y5T?=
 =?us-ascii?Q?r1MbaelgypbQkyj7KmLUvHVe910T7zvih3MG/5ZlD/fdd0X3t8QGXgONWPL4?=
 =?us-ascii?Q?owIBx0hy30/5t1wMCEtzYvWFyuoPzTxIPz1eFXLmrNKG9LGOGhHs+OXWwXP8?=
 =?us-ascii?Q?t7jZoDpNk9aOR4wIHCSHQrIMtHk4JmyNO+XSQ+u3QFMiEma/TvZFrk6tJvib?=
 =?us-ascii?Q?bvP9tLbLnxmIpMzEBzu2AGBlDFBUScjP5jrc3DKISohDKxyc1qzfQil4fqkz?=
 =?us-ascii?Q?2dafA+sRgETSVHKIKADx6BWYFTcajpxTp43MoHwrlTfYix6c8rjzXvFF773d?=
 =?us-ascii?Q?QpqQRZNiJ9x3SnjoZykeq9ISCAHHXatvMhmvLssLiATuAIoshYAg1yYSQ+l1?=
 =?us-ascii?Q?pjpaI+wW0cdYbzGiFgv7PXwi7Ouk?=
X-Forefront-Antispam-Report: CIP:178.72.21.4; CTRY:DK; LANG:en; SCL:1; SRV:;
 IPV:NLI; SFV:NSPM; H:localhost.localdomain; PTR:InfoDomainNonexistent;
 CAT:NONE; SFS:(13230031)(82310400017)(376005)(36860700004)(1800799015);
 DIR:OUT; SFP:1102; 
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: cFYJl3GanAgWfgFeP3hjvnnoUmrFeKrc+OxudFooMFDBw0C59KAR5bYq5inIZ/YfEGB/utR9Z8QtefRbcbLAzUwdvArWWCVIwvj7QydnLHuyoXAd2SIQdCpHcOsnbn0tYYM8SqPmDfJMdNHYUm0xSGv9aagTG54sUrLXrtXyD1fxIgpJR6VXqd2jOUcRNU9ED3JW1/9m+VdznnzmwidSTftNfK3z3cDqeuM5VPyqQVMd2NS87i6BrUavoEdJ3QTSpdDfFLmFsFa8nQhzL4hLsVJyJ4lkWAMectzWpFhRliY/pk5ve6H+PDnJd13cXZLqaJfsy0tjIAbvK8+OrdC8OgHkGTJg0WsqwFvxIHBH9vaGUirbePqCpNFpGJAJaK/uKCKJdcj3FCpSrRaaCkCm70KFhQaoUb9AqPGuv/oqFAmBVqLgDe56pd6mHGjsaFy8dHfOTkySGi3blydMGWDFigyMpx4o7iGGvwHDzEMdMxoDrKIBWmCIoiZaYAeNHJ/3dczONe35Z0Mx5qxooX+V5KCGeKLSYZR+FHhOFfMIeIJSDiQKRcbqmeLxMX9YJajkHvcr3mKFOe4U1I94ueEDq315IBHZn98b01+dbjsCITj3z0yrRb7fO4xurKnd9HQy+Iz8BieSJ0WffI1H41KmAQ==
X-OriginatorOrg: napatech.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 03 Jun 2024 16:18:41.3751 (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: 1314658a-a051-43a6-1617-08dc83e8d55c
X-MS-Exchange-CrossTenant-Id: c4540d0b-728a-4233-9da5-9ea30c7ec3ed
X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=c4540d0b-728a-4233-9da5-9ea30c7ec3ed; Ip=[178.72.21.4];
 Helo=[localhost.localdomain]
X-MS-Exchange-CrossTenant-AuthSource: AMS0EPF00000196.eurprd05.prod.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem
X-MS-Exchange-Transport-CrossTenantHeadersStamped: DBAP190MB0886
X-BESS-ID: 1717431523-311257-12794-8280-2
X-BESS-VER: 2019.1_20240530.1612
X-BESS-Apparent-Source-IP: 104.47.13.45
X-BESS-Parts: H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVmYWZhaGQGYGUDTZxNIw0SgtJc
 nAIC3VzCQlNdEoNcnIItXYxNwy0TLVQqk2FgDPQU34QgAAAA==
X-BESS-Outbound-Spam-Score: 0.00
X-BESS-Outbound-Spam-Report: Code version 3.2,
 rules version 3.2.2.256698 [from 
 cloudscan11-82.eu-central-1a.ess.aws.cudaops.com]
 Rule breakdown below
 pts rule name              description
 ---- ---------------------- --------------------------------
 0.00 BSF_BESS_OUTBOUND      META: BESS Outbound 
X-BESS-Outbound-Spam-Status: SCORE=0.00 using account:ESS113687 scores of
 KILL_LEVEL=7.0 tests=BSF_BESS_OUTBOUND
X-BESS-BRTS-Status: 1
X-BeenThere: dev@dpdk.org
X-Mailman-Version: 2.1.29
Precedence: list
List-Id: DPDK patches and discussions <dev.dpdk.org>
List-Unsubscribe: <https://mails.dpdk.org/options/dev>,
 <mailto:dev-request@dpdk.org?subject=unsubscribe>
List-Archive: <http://mails.dpdk.org/archives/dev/>
List-Post: <mailto:dev@dpdk.org>
List-Help: <mailto:dev-request@dpdk.org?subject=help>
List-Subscribe: <https://mails.dpdk.org/listinfo/dev>,
 <mailto:dev-request@dpdk.org?subject=subscribe>
Errors-To: dev-bounces@dpdk.org

Add ntnic HW interfaces (PCIe, I2C) API.

Signed-off-by: Serhii Iliushyk <sil-plv@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