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 0C39145509;
	Thu, 27 Jun 2024 09:42:42 +0200 (CEST)
Received: from mails.dpdk.org (localhost [127.0.0.1])
	by mails.dpdk.org (Postfix) with ESMTP id 9BD6341132;
	Thu, 27 Jun 2024 09:40:04 +0200 (CEST)
Received: from egress-ip11b.ess.de.barracuda.com
 (egress-ip11b.ess.de.barracuda.com [18.185.115.215])
 by mails.dpdk.org (Postfix) with ESMTP id B5EB040ED2
 for <dev@dpdk.org>; Thu, 27 Jun 2024 09:39:57 +0200 (CEST)
Received: from EUR05-DB8-obe.outbound.protection.outlook.com
 (mail-db8eur05lp2105.outbound.protection.outlook.com [104.47.17.105]) by
 mx-outbound43-83.eu-central-1c.ess.aws.cudaops.com (version=TLSv1.2
 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO);
 Thu, 27 Jun 2024 07:39:54 +0000
ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;
 b=SV83ycG5+fXMeXNX+tuIGJ9GoFWqx29wWY1FLT5lepHQzlIM2vkzZ/3r045XuqVWxN9cp6QfJt/qjJD8THjwoCqjntJffczf2IgkET4iHd7BbVB4dR29FDmg/BUeKgEMifpIUU121qNB/ye51CQXg2nTvh0gmITPtvE5XjG1Phh/1fWcD0AY0xUlUnXq1QwGKxYQQ7u+jsyU0NWTHmoN0j/k5gxeY61JrYUUTm6VcTzUHdqva9h4/0BrLBSqHWBQXwuwKWqzch4Fzz8F+mFEQTyMqqrrLl2OEgGdueVtd+OMmPOoahdTahExnYYayUFmjwMPlErpl5eh/vqRwu9tYA==
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=Rqcx4i+UJATmmIcqAE1GdRK1vBJDNmVcvgeJJAceEHY=;
 b=eg7vhQTnRxfjwJNu3Lz3jnWCLcHmCR7PcU5nX5FKfdghf6DIO8HTbKP/UpNMO6pgTXa7dEyHC+JEumZ6AfjllPvczhmdTh+GhrdHHQPPgXlDZVLxOYfI0ZI2mSAUvVxKHfJzTP25SR82o3QsYjDlHoEcNkATWtdU3M6kNezV42AtMvYDUXqs05uaQVMAEmrX6nGp6vI9kfnE5PzOREGuJFkEjL+LGY8z9Tih90FGL3mfjl6xQZhxguYqOvzp26Dbv3SPi/9FgXqSm3NQM8WqGtj9gkrCLrJMASQwuO3kW8UbIlO8Fm7dMzreZ/nDZ4tdxpFGSY45j0itPijXOFwKGw==
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=Rqcx4i+UJATmmIcqAE1GdRK1vBJDNmVcvgeJJAceEHY=;
 b=BaeoWOlomqemnWPrwxuUxj1JPpgpZDedCCAtFooDAMec7h+83DNxfbcem1619MD+KWGZGVO26NLYuiXHS2yabefEmetzfha0wi5jWMVAInsHSLVLi7rcbXkR7ATb3uoQ7Tos3Ut782tPrS5mDXR9FyPnvxeoE2rUMzDtRsbLZcE=
Received: from DUZPR01CA0346.eurprd01.prod.exchangelabs.com
 (2603:10a6:10:4b8::29) by DB9P190MB1307.EURP190.PROD.OUTLOOK.COM
 (2603:10a6:10:1fe::9) with Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7698.35; Thu, 27 Jun
 2024 07:39:47 +0000
Received: from DB1PEPF000509E2.eurprd03.prod.outlook.com
 (2603:10a6:10:4b8:cafe::40) by DUZPR01CA0346.outlook.office365.com
 (2603:10a6:10:4b8::29) with Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7719.25 via Frontend
 Transport; Thu, 27 Jun 2024 07:39:47 +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
 DB1PEPF000509E2.mail.protection.outlook.com (10.167.242.52) with Microsoft
 SMTP Server id 15.20.7677.15 via Frontend Transport; Thu, 27 Jun 2024
 07:39:47 +0000
From: Serhii Iliushyk <sil-plv@napatech.com>
To: dev@dpdk.org
Cc: mko-plv@napatech.com, sil-plv@napatech.com, ckm@napatech.com,
 andrew.rybchenko@oktetlabs.ru, ferruh.yigit@amd.com
Subject: [PATCH v5 22/23] net/ntnic: add MAC PCS register interface module
Date: Thu, 27 Jun 2024 09:39:03 +0200
Message-ID: <20240627073918.2039719-22-sil-plv@napatech.com>
X-Mailer: git-send-email 2.45.0
In-Reply-To: <20240627073918.2039719-1-sil-plv@napatech.com>
References: <20240530144929.4127931-1-sil-plv@napatech.com>
 <20240627073918.2039719-1-sil-plv@napatech.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-EOPAttributedMessage: 0
X-MS-PublicTrafficType: Email
X-MS-TrafficTypeDiagnostic: DB1PEPF000509E2:EE_|DB9P190MB1307:EE_
Content-Type: text/plain
X-MS-Office365-Filtering-Correlation-Id: 1d3e28de-6917-481a-1a18-08dc967c520d
X-MS-Exchange-SenderADCheck: 1
X-MS-Exchange-AntiSpam-Relay: 0
X-Microsoft-Antispam: BCL:0;
 ARA:13230040|1800799024|82310400026|376014|36860700013; 
X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?Wgj2XKPZNQ8fPAZkJ/aNSXH67GipO3/K3ABacEKz4qSqvFyVHSxhOEfCPgDB?=
 =?us-ascii?Q?Z9pJCcmw3RL17WteAO42JxpCUyeaOf4QQS/ex2cMf0HKNCgrplRirzNqIh2R?=
 =?us-ascii?Q?htR+qx0KPnv3KZe5ldlDaOOxRdGImqc7czYMrqql3zbFSqQPtHrb24HcggHR?=
 =?us-ascii?Q?2vxKJoMx295Jq7DKU/Mol6oldrad9dxeJETDhPbIyTUr66IBnMDjOoTb1jnp?=
 =?us-ascii?Q?wRTHfR5ph6EKuCcDlKlPGL52y5IIc0l9qD4+URd/gGy40kV0emzHxZWRIiz/?=
 =?us-ascii?Q?yOi3TfVHVBpWg7PACq8UWfZXo5Gpt7Ed8/gpWRjoJ8gXRNA0qTdYyKpL0zKb?=
 =?us-ascii?Q?NW6Pv4RHlWhrp6gIEnSUXoIX5Qrm9+BFw19TJVXKRvQxPUtsofL95YLzLKew?=
 =?us-ascii?Q?56UVBlYnjdOAqMmYEth6KVLhRDCaF/sVWXE0SDXeiyJRuusTHuu+Pr7UUlbN?=
 =?us-ascii?Q?wXIU5iDL6Vdtz/MZiAsqAV8FTwX7wlq4Bd5CP8ZQRjSH0C0xlS/j9d/zBLSF?=
 =?us-ascii?Q?DgGIQl2D+4q5GQLg2/ovZ0VazvFS1yp8qGs0b2BI5U6WRBv1mYjegxKoXmQL?=
 =?us-ascii?Q?sFrFQD/LJnTmOtMpVNtWtmGpzWke+9+RjtaY13aWUBto6hex+mDy3UA1TIYH?=
 =?us-ascii?Q?UNtKqAepdvc0Dqcgq27aiXv14WKBEpeW0IDluT9ZqQegcCj+xvZJ3pm3Emy+?=
 =?us-ascii?Q?jBO+UwjQwMbOOBKc0obXzj1yymMVVwlNURuu3HKmAtDazQawOnUzPA/3i/kc?=
 =?us-ascii?Q?n3ZFu+yCSeWIuj5JMvWQinGX4MDDtxMcOlyi20lXRrdtPkVYpk/nKyznJZXZ?=
 =?us-ascii?Q?nGwrXVmxrR1ySR9Nl8qVa/W/+EtS2l6KdZ33fyCB99gxMwCYiqdQdtc9Rjm0?=
 =?us-ascii?Q?cpxReUY04OWdpkUnNOUGgaOpX8JG885AhfN55BiAwrwakC6fA9b3rf1dO9Ey?=
 =?us-ascii?Q?eFwoWc/MHPSY71LZ4LjPQ7is0SjRK/E8sx0yXfAvGBz/UfwwNqRZhzUQHagj?=
 =?us-ascii?Q?ptOZi3CZaSWM3qdJIjKhRQXuuOjdx5sbUVXan5kmfjQDhGbF6Od8HTNkVffV?=
 =?us-ascii?Q?t8l0TBwFQ8IvjwS5+bUGG4vdRDPIhkOZ0r3AT0cWGYqGqFgQSoEDqM9rZ95g?=
 =?us-ascii?Q?q+ez1dhBoYjkFBhml2YACb4ujs35SeRhreuUNgKbalVyBTg6fH3MkAr49/GX?=
 =?us-ascii?Q?0E/6UBVpHcNGGvjGv0QPm9+dHD8CdDRyIOJt6DbZ9SE5uSxLtK51lL4AQuv6?=
 =?us-ascii?Q?dkRxhcz/oQVsh+4hvkWp5y+RiTVCfNCl9H6dlvII9DQ8bTXK098JPSgEVOO9?=
 =?us-ascii?Q?7IiMtgn8w2rBQuovuh7vlSb4d66ZK4pB9Dra0tNWvaL/X3eM07xEIvepj7jx?=
 =?us-ascii?Q?uNfyhMWubOqf963WHQJtL4q+zdDU?=
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:(13230040)(1800799024)(82310400026)(376014)(36860700013);
 DIR:OUT; SFP:1102; 
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-ChunkCount: 1
X-MS-Exchange-AntiSpam-ExternalHop-MessageData-0: Zz09O3NZneyMfYbpxto668BAYCWa+rYa9fK8HSLNGtrOYrfP7RgRzpXteM4jERMB5rNJKbhlBL+unRkWz59BBeLdgdavpC8kukmW3KVCeK5xntDrvais64yJOpptzFzaEHu15rSIVbMs+dqgN6J26LUp5iAMZy+hydnvkOS0dpcj2AbR0b0XBghxz9SS4wbVkegNidA9etQ/VBVsJl+RaMS/MctNxOoAN+DeMLOgY2jgIKOeAi476DBizhdPDO+Pzaab4mNy4mBaOO2C6ur0PXwCqkeWvwbI31fgrFh9P7fK00nl1fg2oFY3wUJ0JrV4aoqxgvRHjlo5zwfjImeVCP5KUfnquRrnYs4YGjWndmTb7wxV/60FH4N/S/VVwcf+1fndNhuWbJ0kzsNzv8sL4v29DuydG6M7fZmGM7pnoaU5eqIS6MFjN92fr0iRKheeeAd1Za1zG6QnmJ9vy3Xu1JlQSi4iaFhtyHOUCvQzQzQVASYwyX1CbaBmJEApp7qKTxMHkcMazHm2HF9BvXTC3ZV73ECJuIrKG270QcWrDYEYi0crrXJw+dscTUrmJEmUDQLh+10PuKfb/0+o8jr88/qIF8jRFf8sPtd7FRJ/wTD9FFEql4uX324UDIaM3yomkrShc00n9SD4SmNfsrlMVg==
X-OriginatorOrg: napatech.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 27 Jun 2024 07:39:47.3656 (UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: 1d3e28de-6917-481a-1a18-08dc967c520d
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: DB1PEPF000509E2.eurprd03.prod.outlook.com
X-MS-Exchange-CrossTenant-AuthAs: Anonymous
X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem
X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9P190MB1307
X-BESS-ID: 1719473990-311091-12767-2375-2
X-BESS-VER: 2019.1_20240620.2317
X-BESS-Apparent-Source-IP: 104.47.17.105
X-BESS-Parts: H4sIAAAAAAACA4uuVkqtKFGyUioBkjpK+cVKVqYWZmamQGYGUDTR3CDRMiXF0t
 AyJS3NMtXU2MjEODXVKNUwKc3cNDExSak2FgA41e1jQgAAAA==
X-BESS-Outbound-Spam-Score: 0.00
X-BESS-Outbound-Spam-Report: Code version 3.2,
 rules version 3.2.2.257231 [from 
 cloudscan9-62.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 API for MAC and PCS(Physical Coding Sublayer)

Signed-off-by: Serhii Iliushyk <sil-plv@napatech.com>
---
 drivers/net/ntnic/include/nt4ga_link.h        |   1 +
 .../link_mgmt/link_100g/nt4ga_link_100g.c     | 393 +++++++-
 drivers/net/ntnic/meson.build                 |   1 +
 .../net/ntnic/nthw/core/include/nthw_core.h   |   1 +
 .../ntnic/nthw/core/include/nthw_mac_pcs.h    | 250 +++++
 drivers/net/ntnic/nthw/core/nthw_mac_pcs.c    | 894 ++++++++++++++++++
 6 files changed, 1538 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_mac_pcs.c

diff --git a/drivers/net/ntnic/include/nt4ga_link.h b/drivers/net/ntnic/include/nt4ga_link.h
index 5a16afea2a..8366484830 100644
--- a/drivers/net/ntnic/include/nt4ga_link.h
+++ b/drivers/net/ntnic/include/nt4ga_link.h
@@ -78,6 +78,7 @@ typedef struct port_action_s {
 
 typedef struct adapter_100g_s {
 	nim_i2c_ctx_t nim_ctx[NUM_ADAPTER_PORTS_MAX];	/* Should be the first field */
+	nthw_mac_pcs_t mac_pcs100g[NUM_ADAPTER_PORTS_MAX];
 	nthw_gpio_phy_t gpio_phy[NUM_ADAPTER_PORTS_MAX];
 } adapter_100g_t;
 
diff --git a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
index 499f5de8eb..9d55733ddc 100644
--- a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
+++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
@@ -10,6 +10,168 @@
 #include "i2c_nim.h"
 #include "ntnic_mod_reg.h"
 
+/*
+ * Swap tx/rx polarity
+ */
+static int _swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs, int port, bool swap)
+{
+	const bool tx_polarity_swap[2][4] = { { true, true, false, false },
+		{ false, true, false, false }
+	};
+	const bool rx_polarity_swap[2][4] = { { false, true, true, true },
+		{ false, true, true, false }
+	};
+	uint8_t lane;
+
+	(void)drv;
+
+	for (lane = 0U; lane < 4U; lane++) {
+		if (swap) {
+			nthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane,
+				tx_polarity_swap[port][lane]);
+			nthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane,
+				rx_polarity_swap[port][lane]);
+
+		} else {
+			nthw_mac_pcs_swap_gty_tx_polarity(mac_pcs, lane, false);
+			nthw_mac_pcs_swap_gty_rx_polarity(mac_pcs, lane, false);
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Reset RX
+ */
+static int _reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	(void)drv;
+
+	nthw_mac_pcs_rx_path_rst(mac_pcs, true);
+	nt_os_wait_usec(10000);	/* 10ms */
+	nthw_mac_pcs_rx_path_rst(mac_pcs, false);
+	nt_os_wait_usec(10000);	/* 10ms */
+
+	return 0;
+}
+
+static void _set_loopback(struct adapter_info_s *p_adapter_info,
+	nthw_mac_pcs_t *mac_pcs,
+	int intf_no,
+	uint32_t mode,
+	uint32_t last_mode)
+{
+	bool swap_polerity = true;
+
+	switch (mode) {
+	case 1:
+		NT_LOG(INF, NTNIC, "%s: Applying host loopback\n",
+			p_adapter_info->mp_port_id_str[intf_no]);
+		nthw_mac_pcs_set_fec(mac_pcs, true);
+		nthw_mac_pcs_set_host_loopback(mac_pcs, true);
+		swap_polerity = false;
+		break;
+
+	case 2:
+		NT_LOG(INF, NTNIC, "%s: Applying line loopback\n",
+			p_adapter_info->mp_port_id_str[intf_no]);
+		nthw_mac_pcs_set_line_loopback(mac_pcs, true);
+		break;
+
+	default:
+		switch (last_mode) {
+		case 1:
+			NT_LOG(INF, NTNIC, "%s: Removing host loopback\n",
+				p_adapter_info->mp_port_id_str[intf_no]);
+			nthw_mac_pcs_set_host_loopback(mac_pcs, false);
+			break;
+
+		case 2:
+			NT_LOG(INF, NTNIC, "%s: Removing line loopback\n",
+				p_adapter_info->mp_port_id_str[intf_no]);
+			nthw_mac_pcs_set_line_loopback(mac_pcs, false);
+			break;
+
+		default:
+			/* Do nothing */
+			break;
+		}
+
+		break;
+	}
+
+	if (p_adapter_info->fpga_info.nthw_hw_info.hw_id == 2 ||
+		p_adapter_info->hw_info.n_nthw_adapter_id == NT_HW_ADAPTER_ID_NT200A02) {
+		(void)_swap_tx_rx_polarity(p_adapter_info, mac_pcs, intf_no, swap_polerity);
+	}
+
+	/* After changing the loopback the system must be properly reset */
+	_reset_rx(p_adapter_info, mac_pcs);
+
+	nt_os_wait_usec(10000);	/* 10ms - arbitrary choice */
+
+	if (!nthw_mac_pcs_is_rx_path_rst(mac_pcs)) {
+		nthw_mac_pcs_reset_bip_counters(mac_pcs);
+
+		if (!nthw_mac_pcs_get_fec_bypass(mac_pcs))
+			nthw_mac_pcs_reset_fec_counters(mac_pcs);
+	}
+}
+
+/*
+ * Function to retrieve the current state of a link (for one port)
+ */
+static int _link_state_build(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs,
+	nthw_gpio_phy_t *gpio_phy, int port, link_state_t *state,
+	bool is_port_disabled)
+{
+	uint32_t abs;
+	uint32_t phy_link_state;
+	uint32_t lh_abs;
+	uint32_t ll_phy_link_state;
+	uint32_t link_down_cnt;
+	uint32_t nim_interr;
+	uint32_t lh_local_fault;
+	uint32_t lh_remote_fault;
+	uint32_t lh_internal_local_fault;
+	uint32_t lh_received_local_fault;
+
+	memset(state, 0, sizeof(*state));
+	state->link_disabled = is_port_disabled;
+	nthw_mac_pcs_get_link_summary(mac_pcs, &abs, &phy_link_state, &lh_abs, &ll_phy_link_state,
+		&link_down_cnt, &nim_interr, &lh_local_fault,
+		&lh_remote_fault, &lh_internal_local_fault,
+		&lh_received_local_fault);
+
+	assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
+	state->nim_present = nthw_gpio_phy_is_module_present(gpio_phy, (uint8_t)port);
+	state->lh_nim_absent = !state->nim_present;
+	state->link_up = phy_link_state ? true : false;
+
+	{
+		static char lsbuf[NUM_ADAPTER_MAX][NUM_ADAPTER_PORTS_MAX][256];
+		char buf[255];
+		const int adapter_no = drv->adapter_no;
+		snprintf(buf, sizeof(buf),
+			"%s: Port = %d: abs = %u, phy_link_state = %u, lh_abs = %u, "
+			"ll_phy_link_state = %u, "
+			"link_down_cnt = %u, nim_interr = %u, lh_local_fault = %u, lh_remote_fault = "
+			"%u, lh_internal_local_fault = %u, lh_received_local_fault = %u",
+			drv->mp_adapter_id_str, mac_pcs->mn_instance, abs, phy_link_state, lh_abs,
+			ll_phy_link_state, link_down_cnt, nim_interr, lh_local_fault,
+			lh_remote_fault, lh_internal_local_fault, lh_received_local_fault);
+
+		if (strcmp(lsbuf[adapter_no][port], buf) != 0) {
+			snprintf(lsbuf[adapter_no][port], sizeof(lsbuf[adapter_no][port]), "%s",
+				buf);
+			lsbuf[adapter_no][port][sizeof(lsbuf[adapter_no][port]) - 1U] = '\0';
+			NT_LOG(DBG, NTNIC, "%s\n", lsbuf[adapter_no][port]);
+		}
+	}
+	return 0;
+}
+
 /*
  * Check whether a NIM module is present
  */
@@ -20,6 +182,69 @@ static bool _nim_is_present(nthw_gpio_phy_t *gpio_phy, uint8_t if_no)
 	return nthw_gpio_phy_is_module_present(gpio_phy, if_no);
 }
 
+/*
+ * Enable RX
+ */
+static int _enable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	(void)drv;	/* unused */
+	nthw_mac_pcs_set_rx_enable(mac_pcs, true);
+	return 0;
+}
+
+/*
+ * Enable TX
+ */
+static int _enable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	(void)drv;	/* unused */
+	nthw_mac_pcs_set_tx_enable(mac_pcs, true);
+	nthw_mac_pcs_set_tx_sel_host(mac_pcs, true);
+	return 0;
+}
+
+/*
+ * Disable RX
+ */
+static int _disable_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	(void)drv;	/* unused */
+	nthw_mac_pcs_set_rx_enable(mac_pcs, false);
+	return 0;
+}
+
+/*
+ * Disable TX
+ */
+static int _disable_tx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	(void)drv;	/* unused */
+	nthw_mac_pcs_set_tx_enable(mac_pcs, false);
+	nthw_mac_pcs_set_tx_sel_host(mac_pcs, false);
+	return 0;
+}
+
+/*
+ * Check link once NIM is installed and link can be expected.
+ */
+static int check_link_state(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs)
+{
+	bool rst_required;
+	bool ber;
+	bool fec_all_locked;
+
+	rst_required = nthw_mac_pcs_reset_required(mac_pcs);
+
+	ber = nthw_mac_pcs_get_hi_ber(mac_pcs);
+
+	fec_all_locked = nthw_mac_pcs_get_fec_stat_all_am_locked(mac_pcs);
+
+	if (rst_required || ber || !fec_all_locked)
+		_reset_rx(drv, mac_pcs);
+
+	return 0;
+}
+
 /*
  * Initialize NIM, Code based on nt200e3_2_ptp.cpp: MyPort::createNim()
  */
@@ -31,6 +256,7 @@ static int _create_nim(adapter_info_t *drv, int port, bool enable)
 	nim_i2c_ctx_t *nim_ctx;
 	sfp_nim_state_t nim;
 	nt4ga_link_t *link_info = &drv->nt4ga_link;
+	nthw_mac_pcs_t *mac_pcs = &link_info->u.var100g.mac_pcs100g[port];
 
 	assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
 	assert(link_info->variables_initialized);
@@ -46,6 +272,12 @@ static int _create_nim(adapter_info_t *drv, int port, bool enable)
 		return 0;
 	}
 
+	if (!enable) {
+		_disable_rx(drv, mac_pcs);
+		_disable_tx(drv, mac_pcs);
+		_reset_rx(drv, mac_pcs);
+	}
+
 	/*
 	 * Perform PHY reset.
 	 */
@@ -114,14 +346,29 @@ static int _create_nim(adapter_info_t *drv, int port, bool enable)
  * The function shall not assume anything about the state of the adapter
  * and/or port.
  */
-static int _port_init(adapter_info_t *drv, int port)
+static int _port_init(adapter_info_t *drv, nthw_fpga_t *fpga, int port)
 {
+	int adapter_id;
+	int hw_id;
 	int res;
 	nt4ga_link_t *link_info = &drv->nt4ga_link;
 
+	nthw_mac_pcs_t *mac_pcs;
+
 	assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
 	assert(link_info->variables_initialized);
 
+	if (fpga && fpga->p_fpga_info) {
+		adapter_id = fpga->p_fpga_info->n_nthw_adapter_id;
+		hw_id = fpga->p_fpga_info->nthw_hw_info.hw_id;
+
+	} else {
+		adapter_id = -1;
+		hw_id = -1;
+	}
+
+	mac_pcs = &link_info->u.var100g.mac_pcs100g[port];
+
 	/*
 	 * Phase 1. Pre-state machine (`port init` functions)
 	 * 1.1) nt4ga_adapter::port_init()
@@ -134,6 +381,28 @@ static int _port_init(adapter_info_t *drv, int port)
 	link_info->link_info[port].link_duplex = NT_LINK_DUPLEX_FULL;
 	link_info->link_info[port].link_auto_neg = NT_LINK_AUTONEG_OFF;
 	link_info->speed_capa |= NT_LINK_SPEED_100G;
+	nthw_mac_pcs_set_led_mode(mac_pcs, NTHW_MAC_PCS_LED_AUTO);
+	nthw_mac_pcs_set_receiver_equalization_mode(mac_pcs, nthw_mac_pcs_receiver_mode_lpm);
+
+	/*
+	 * NT200A01 build 2 HW and NT200A02 that require GTY polarity swap
+	 * if (adapter is `NT200A01 build 2 HW or NT200A02`)
+	 */
+	if (adapter_id == NT_HW_ADAPTER_ID_NT200A02 || hw_id == 2)
+		(void)_swap_tx_rx_polarity(drv, mac_pcs, port, true);
+
+	nthw_mac_pcs_set_ts_eop(mac_pcs, true);	/* end-of-frame timestamping */
+
+	/* Work in ABSOLUTE timing mode, don't set IFG mode. */
+
+	/* Phase 2. Pre-state machine (`setup` functions) */
+
+	/* 2.1) nt200a0x.cpp:Myport::setup() */
+	NT_LOG(DBG, NTNIC, "%s: Setting up port %d\n", drv->mp_port_id_str[port], port);
+
+	NT_LOG(DBG, NTNIC, "%s: Port %d: PHY TX enable\n", drv->mp_port_id_str[port], port);
+	_enable_tx(drv, mac_pcs);
+	_reset_rx(drv, mac_pcs);
 
 	/* Phase 3. Link state machine steps */
 
@@ -147,6 +416,50 @@ static int _port_init(adapter_info_t *drv, int port)
 
 	NT_LOG(DBG, NTNIC, "%s: NIM initialized\n", drv->mp_port_id_str[port]);
 
+	/* 3.2) MyPort::nimReady() */
+
+	/* 3.3) MyPort::nimReady100Gb() */
+
+	/* Setting FEC resets the lane counter in one half of the GMF */
+	nthw_mac_pcs_set_fec(mac_pcs, true);
+	NT_LOG(DBG, NTNIC, "%s: Port %d: HOST FEC enabled\n", drv->mp_port_id_str[port], port);
+
+	if (adapter_id == NT_HW_ADAPTER_ID_NT200A02 || hw_id == 2) {
+		const uint8_t pre = 5;
+		const uint8_t diff = 25;
+		const uint8_t post = 12;
+
+		uint8_t lane = 0;
+
+		for (lane = 0; lane < 4; lane++)
+			nthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, diff, post);
+
+	} else {
+		NT_LOG(ERR, NTNIC, "Unhandled AdapterId/HwId: %02x_hwid%d\n", adapter_id, hw_id);
+		assert(0);
+	}
+
+	_reset_rx(drv, mac_pcs);
+
+	/*
+	 * 3.4) MyPort::setLinkState()
+	 *
+	 * Compensation = 1640 - dly
+	 * CMAC-core dly 188 ns
+	 * FEC no correction 87 ns
+	 * FEC active correction 211
+	 */
+	if (nthw_mac_pcs_get_fec_valid(mac_pcs))
+		nthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 211));
+
+	else
+		nthw_mac_pcs_set_timestamp_comp_rx(mac_pcs, (1640 - 188 - 87));
+
+	/* 3.5) uint32_t MyPort::macConfig(nt_link_state_t link_state) */
+	_enable_rx(drv, mac_pcs);
+
+	nthw_mac_pcs_set_host_loopback(mac_pcs, false);
+
 	return res;
 }
 
@@ -163,7 +476,9 @@ static int _common_ptp_nim_state_machine(void *data)
 	const int nb_ports = fpga_info->n_phy_ports;
 	uint32_t last_lpbk_mode[NUM_ADAPTER_PORTS_MAX];
 
+	nim_i2c_ctx_t *nim_ctx;
 	link_state_t *link_state;
+	nthw_mac_pcs_t *mac_pcs;
 	nthw_gpio_phy_t *gpio_phy;
 
 	if (!fpga) {
@@ -172,7 +487,9 @@ static int _common_ptp_nim_state_machine(void *data)
 	}
 
 	assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);
+	nim_ctx = link_info->u.var100g.nim_ctx;
 	link_state = link_info->link_state;
+	mac_pcs = link_info->u.var100g.mac_pcs100g;
 	gpio_phy = link_info->u.var100g.gpio_phy;
 
 	monitor_task_is_running[adapter_no] = 1;
@@ -183,8 +500,10 @@ static int _common_ptp_nim_state_machine(void *data)
 
 	while (monitor_task_is_running[adapter_no]) {
 		int i;
+		static bool reported_link[NUM_ADAPTER_PORTS_MAX] = { false };
 
 		for (i = 0; i < nb_ports; i++) {
+			link_state_t new_link_state;
 			const bool is_port_disabled = link_info->port_action[i].port_disable;
 			const bool was_port_disabled = link_state[i].link_disabled;
 			const bool disable_port = is_port_disabled && !was_port_disabled;
@@ -200,6 +519,7 @@ static int _common_ptp_nim_state_machine(void *data)
 				memset(&link_state[i], 0, sizeof(link_state[i]));
 				link_info->link_info[i].link_speed = NT_LINK_SPEED_UNKNOWN;
 				link_state[i].link_disabled = true;
+				reported_link[i] = false;
 				/* Turn off laser and LED, etc. */
 				(void)_create_nim(drv, i, false);
 				NT_LOG(DBG, NTNIC, "%s: Port %i is disabled\n",
@@ -223,12 +543,17 @@ static int _common_ptp_nim_state_machine(void *data)
 					 * If there is no Nim present, we need to initialize the
 					 * port anyway
 					 */
-					_port_init(drv, i);
+					_port_init(drv, fpga, i);
 				}
 
 				NT_LOG(INF, NTNIC, "%s: Loopback mode changed=%u\n",
 					drv->mp_port_id_str[i],
 					link_info->port_action[i].port_lpbk_mode);
+				_set_loopback(drv,
+					&mac_pcs[i],
+					i,
+					link_info->port_action[i].port_lpbk_mode,
+					last_lpbk_mode[i]);
 
 				if (link_info->port_action[i].port_lpbk_mode == 1)
 					link_state[i].link_up = true;
@@ -237,6 +562,65 @@ static int _common_ptp_nim_state_machine(void *data)
 				continue;
 			}
 
+			(void)_link_state_build(drv, &mac_pcs[i], &gpio_phy[i], i, &new_link_state,
+				is_port_disabled);
+
+			if (!new_link_state.nim_present) {
+				if (link_state[i].nim_present) {
+					NT_LOG(INF, NTNIC, "%s: NIM module removed\n",
+						drv->mp_port_id_str[i]);
+				}
+
+				link_state[i] = new_link_state;
+				continue;
+			}
+
+			/* NIM module is present */
+			if (new_link_state.lh_nim_absent || !link_state[i].nim_present) {
+				sfp_nim_state_t new_state;
+
+				NT_LOG(DBG, NTNIC, "%s: NIM module inserted\n",
+					drv->mp_port_id_str[i]);
+
+				if (_port_init(drv, fpga, i)) {
+					NT_LOG(ERR, NTNIC,
+						"%s: Failed to initialize NIM module\n",
+						drv->mp_port_id_str[i]);
+					continue;
+				}
+
+				if (nim_state_build(&nim_ctx[i], &new_state)) {
+					NT_LOG(ERR, NTNIC, "%s: Cannot read basic NIM data\n",
+						drv->mp_port_id_str[i]);
+					continue;
+				}
+
+				assert(new_state.br);	/* Cannot be zero if NIM is present */
+				NT_LOG(DBG, NTNIC,
+					"%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = '%s', sn='%s'\n",
+					drv->mp_port_id_str[i], nim_ctx->nim_id,
+					nim_id_to_text(nim_ctx->nim_id), (unsigned int)new_state.br,
+					nim_ctx->vendor_name, nim_ctx->prod_no, nim_ctx->serial_no);
+
+				(void)_link_state_build(drv, &mac_pcs[i], &gpio_phy[i], i,
+					&link_state[i], is_port_disabled);
+
+				NT_LOG(DBG, NTNIC, "%s: NIM module initialized\n",
+					drv->mp_port_id_str[i]);
+				continue;
+			}
+
+			if (reported_link[i] != new_link_state.link_up) {
+				NT_LOG(INF, NTNIC, "%s: link is %s\n", drv->mp_port_id_str[i],
+					(new_link_state.link_up ? "up" : "down"));
+				link_info->link_info[i].link_speed =
+					(new_link_state.link_up ? NT_LINK_SPEED_100G
+						: NT_LINK_SPEED_UNKNOWN);
+				link_state[i].link_up = new_link_state.link_up;
+				reported_link[i] = new_link_state.link_up;
+			}
+
+			check_link_state(drv, &mac_pcs[i]);
 		}	/* end-for */
 
 		if (monitor_task_is_running[adapter_no])
@@ -280,6 +664,7 @@ static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nth
 	assert(adapter_no >= 0 && adapter_no < NUM_ADAPTER_MAX);
 
 	if (res == 0 && !p_adapter_info->nt4ga_link.variables_initialized) {
+		nthw_mac_pcs_t *mac_pcs = p_adapter_info->nt4ga_link.u.var100g.mac_pcs100g;
 		nim_i2c_ctx_t *nim_ctx = p_adapter_info->nt4ga_link.u.var100g.nim_ctx;
 		nthw_gpio_phy_t *gpio_phy = p_adapter_info->nt4ga_link.u.var100g.gpio_phy;
 		int i;
@@ -287,6 +672,10 @@ static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, nth
 		for (i = 0; i < nb_ports; i++) {
 			/* 2 + adapter port number */
 			const uint8_t instance = (uint8_t)(2U + i);
+			res = nthw_mac_pcs_init(&mac_pcs[i], fpga, i /* int n_instance */);
+
+			if (res != 0)
+				break;
 
 			res = nthw_iic_init(&nim_ctx[i].hwiic, fpga, instance, 8 /* timing */);
 
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index eb0ead4835..77f87ffa8e 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -43,6 +43,7 @@ sources = files(
     'nthw/core/nthw_hif.c',
     'nthw/core/nthw_i2cm.c',
     'nthw/core/nthw_iic.c',
+    'nthw/core/nthw_mac_pcs.c',
     'nthw/core/nthw_pcie3.c',
     'nthw/core/nthw_sdc.c',
     'nthw/core/nthw_si5340.c',
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h b/drivers/net/ntnic/nthw/core/include/nthw_core.h
index 5cce56e13f..fe32891712 100644
--- a/drivers/net/ntnic/nthw/core/include/nthw_core.h
+++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h
@@ -18,6 +18,7 @@
 #include "nthw_i2cm.h"
 
 #include "nthw_gpio_phy.h"
+#include "nthw_mac_pcs.h"
 #include "nthw_sdc.h"
 
 #include "nthw_si5340.h"
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h b/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h
new file mode 100644
index 0000000000..8d0e81bd73
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h
@@ -0,0 +1,250 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NTHW_MAC_PCS_H_
+#define NTHW_MAC_PCS_H_
+
+enum nthw_mac_pcs_led_mode_e {
+	NTHW_MAC_PCS_LED_AUTO = 0x00,
+	NTHW_MAC_PCS_LED_ON = 0x01,
+	NTHW_MAC_PCS_LED_OFF = 0x02,
+	NTHW_MAC_PCS_LED_PORTID = 0x03,
+};
+
+#define nthw_mac_pcs_receiver_mode_dfe (0)
+#define nthw_mac_pcs_receiver_mode_lpm (1)
+
+struct nthw_mac_pcs {
+	uint8_t m_port_no;
+
+	nthw_fpga_t *mp_fpga;
+	nthw_module_t *mp_mod_mac_pcs;
+	int mn_instance;
+
+	/* Block lock status */
+	nthw_field_t *mp_fld_block_lock_lock;
+	uint32_t m_fld_block_lock_lock_mask;
+
+	/* Lane lock status */
+	nthw_field_t *mp_fld_vl_demuxed_lock;
+	uint32_t m_fld_vl_demuxed_lock_mask;
+
+	/* GTY_STAT */
+	nthw_field_t *mp_fld_gty_stat_rx_rst_done0;
+	nthw_field_t *mp_fld_gty_stat_rx_rst_done1;
+	nthw_field_t *mp_fld_gty_stat_rx_rst_done2;
+	nthw_field_t *mp_fld_gty_stat_rx_rst_done3;
+	nthw_field_t *mp_fld_gty_stat_tx_rst_done0;
+	nthw_field_t *mp_fld_gty_stat_tx_rst_done1;
+	nthw_field_t *mp_fld_gty_stat_tx_rst_done2;
+	nthw_field_t *mp_fld_gty_stat_tx_rst_done3;
+	uint32_t m_fld_gty_stat_rx_rst_done_mask;
+	uint32_t m_fld_gty_stat_tx_rst_done_mask;
+
+	/* GTY_LOOP */
+	nthw_register_t *mp_reg_gty_loop;
+	nthw_field_t *mp_fld_gty_loop_gt_loop0;
+	nthw_field_t *mp_fld_gty_loop_gt_loop1;
+	nthw_field_t *mp_fld_gty_loop_gt_loop2;
+	nthw_field_t *mp_fld_gty_loop_gt_loop3;
+
+	/* MAC_PCS_CONFIG */
+	nthw_field_t *mp_fld_pcs_config_tx_path_rst;
+	nthw_field_t *mp_fld_pcs_config_rx_path_rst;
+	nthw_field_t *mp_fld_pcs_config_rx_enable;
+	nthw_field_t *mp_fld_pcs_config_rx_force_resync;
+	nthw_field_t *mp_fld_pcs_config_rx_test_pattern;
+	nthw_field_t *mp_fld_pcs_config_tx_enable;
+	nthw_field_t *mp_fld_pcs_config_tx_send_idle;
+	nthw_field_t *mp_fld_pcs_config_tx_send_rfi;
+	nthw_field_t *mp_fld_pcs_config_tx_test_pattern;
+
+	/* STAT PCS */
+	nthw_field_t *mp_fld_stat_pcs_rx_status;
+	nthw_field_t *mp_fld_stat_pcs_rx_aligned;
+	nthw_field_t *mp_fld_stat_pcs_rx_aligned_err;
+	nthw_field_t *mp_fld_stat_pcs_rx_misaligned;
+	nthw_field_t *mp_fld_stat_pcs_rx_internal_local_fault;
+	nthw_field_t *mp_fld_stat_pcs_rx_received_local_fault;
+	nthw_field_t *mp_fld_stat_pcs_rx_local_fault;
+	nthw_field_t *mp_fld_stat_pcs_rx_remote_fault;
+	nthw_field_t *mp_fld_stat_pcs_rx_hi_ber;
+
+	/* STAT_PCS_RX_LATCH */
+	nthw_field_t *mp_fld_stat_pcs_rx_latch_status;
+
+	/* PHYMAC_MISC */
+	nthw_field_t *mp_fld_phymac_misc_tx_sel_host;
+	nthw_field_t *mp_fld_phymac_misc_tx_sel_tfg;
+	nthw_field_t *mp_fld_phymac_misc_tx_sel_rx_loop;
+	nthw_field_t *mp_fld_phymac_misc_ts_eop;
+
+	/* LINK_SUMMARY */
+	nthw_register_t *mp_reg_link_summary;
+	nthw_field_t *mp_fld_link_summary_abs;
+	nthw_field_t *mp_fld_link_summary_nt_phy_link_state;
+	nthw_field_t *mp_fld_link_summary_lh_abs;
+	nthw_field_t *mp_fld_link_summary_ll_nt_phy_link_state;
+	nthw_field_t *mp_fld_link_summary_link_down_cnt;
+	nthw_field_t *mp_fld_link_summary_nim_interr;
+	nthw_field_t *mp_fld_link_summary_lh_local_fault;
+	nthw_field_t *mp_fld_link_summary_lh_remote_fault;
+	nthw_field_t *mp_fld_link_summary_local_fault;
+	nthw_field_t *mp_fld_link_summary_remote_fault;
+
+	/* BIP_ERR */
+	nthw_register_t *mp_reg_bip_err;
+	nthw_field_t *mp_fld_reg_bip_err_bip_err;
+
+	/* FEC_CTRL */
+	nthw_register_t *mp_reg_fec_ctrl;
+	nthw_field_t *mp_field_fec_ctrl_reg_rs_fec_ctrl_in;
+
+	/* FEC_STAT */
+	nthw_register_t *mp_reg_fec_stat;
+	nthw_field_t *mp_field_fec_stat_bypass;
+	nthw_field_t *mp_field_fec_stat_valid;
+	nthw_field_t *mp_field_fec_stat_am_lock0;
+	nthw_field_t *mp_field_fec_stat_am_lock1;
+	nthw_field_t *mp_field_fec_stat_am_lock2;
+	nthw_field_t *mp_field_fec_stat_am_lock3;
+	nthw_field_t *mp_field_fec_stat_fec_lane_algn;
+
+	/* FEC Corrected code word count */
+	nthw_register_t *mp_reg_fec_cw_cnt;
+	nthw_field_t *mp_field_fec_cw_cnt_cw_cnt;
+
+	/* FEC Uncorrected code word count */
+	nthw_register_t *mp_reg_fec_ucw_cnt;
+	nthw_field_t *mp_field_fec_ucw_cnt_ucw_cnt;
+
+	/* GTY_RX_BUF_STAT */
+	nthw_register_t *mp_reg_gty_rx_buf_stat;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat0;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat1;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat2;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat3;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed0;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed1;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed2;
+	nthw_field_t *mp_field_gty_rx_buf_stat_rx_buf_stat_changed3;
+
+	/* GTY_PRE_CURSOR */
+	nthw_register_t *mp_reg_gty_pre_cursor;
+	nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr0;
+	nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr1;
+	nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr2;
+	nthw_field_t *mp_field_gty_pre_cursor_tx_pre_csr3;
+
+	/* GTY_DIFF_CTL */
+	nthw_register_t *mp_reg_gty_diff_ctl;
+	nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl0;
+	nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl1;
+	nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl2;
+	nthw_field_t *mp_field_gty_gty_diff_ctl_tx_diff_ctl3;
+
+	/* GTY_POST_CURSOR */
+	nthw_register_t *mp_reg_gty_post_cursor;
+	nthw_field_t *mp_field_gty_post_cursor_tx_post_csr0;
+	nthw_field_t *mp_field_gty_post_cursor_tx_post_csr1;
+	nthw_field_t *mp_field_gty_post_cursor_tx_post_csr2;
+	nthw_field_t *mp_field_gty_post_cursor_tx_post_csr3;
+
+	/* GTY_CTL */
+	nthw_register_t *mp_reg_gty_ctl;
+	nthw_register_t *mp_reg_gty_ctl_tx;
+	nthw_field_t *mp_field_gty_ctl_tx_pol0;
+	nthw_field_t *mp_field_gty_ctl_tx_pol1;
+	nthw_field_t *mp_field_gty_ctl_tx_pol2;
+	nthw_field_t *mp_field_gty_ctl_tx_pol3;
+	nthw_field_t *mp_field_gty_ctl_rx_pol0;
+	nthw_field_t *mp_field_gty_ctl_rx_pol1;
+	nthw_field_t *mp_field_gty_ctl_rx_pol2;
+	nthw_field_t *mp_field_gty_ctl_rx_pol3;
+	nthw_field_t *mp_field_gty_ctl_rx_lpm_en0;
+	nthw_field_t *mp_field_gty_ctl_rx_lpm_en1;
+	nthw_field_t *mp_field_gty_ctl_rx_lpm_en2;
+	nthw_field_t *mp_field_gty_ctl_rx_lpm_en3;
+	nthw_field_t *mp_field_gty_ctl_rx_equa_rst0;
+	nthw_field_t *mp_field_gty_ctl_rx_equa_rst1;
+	nthw_field_t *mp_field_gty_ctl_rx_equa_rst2;
+	nthw_field_t *mp_field_gty_ctl_rx_equa_rst3;
+
+	/* DEBOUNCE_CTRL */
+	nthw_register_t *mp_reg_debounce_ctrl;
+	nthw_field_t *mp_field_debounce_ctrl_nt_port_ctrl;
+
+	/* TIMESTAMP_COMP */
+	nthw_register_t *mp_reg_time_stamp_comp;
+	nthw_field_t *mp_field_time_stamp_comp_rx_dly;
+	nthw_field_t *mp_field_time_stamp_comp_tx_dly;
+
+	/* STAT_PCS_RX */
+	nthw_register_t *mp_reg_stat_pcs_rx;
+
+	/* STAT_PCS_RX */
+	nthw_register_t *mp_reg_stat_pcs_rx_latch;
+
+	/* PHYMAC_MISC */
+	nthw_register_t *mp_reg_phymac_misc;
+
+	/* BLOCK_LOCK */
+	nthw_register_t *mp_reg_block_lock;
+};
+
+typedef struct nthw_mac_pcs nthw_mac_pcs_t;
+
+int nthw_mac_pcs_init(nthw_mac_pcs_t *p, nthw_fpga_t *p_fpga, int n_instance);
+
+void nthw_mac_pcs_tx_path_rst(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_rx_path_rst(nthw_mac_pcs_t *p, bool enable);
+bool nthw_mac_pcs_is_rx_path_rst(nthw_mac_pcs_t *p);
+/* wrapper - for ease of use */
+void nthw_mac_pcs_set_rx_enable(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_tx_enable(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_tx_sel_host(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_tx_sel_tfg(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_ts_eop(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_host_loopback(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_set_line_loopback(nthw_mac_pcs_t *p, bool enable);
+void nthw_mac_pcs_reset_bip_counters(nthw_mac_pcs_t *p);
+bool nthw_mac_pcs_get_hi_ber(nthw_mac_pcs_t *p);
+
+void nthw_mac_pcs_get_link_summary(nthw_mac_pcs_t *p,
+	uint32_t *p_abs,
+	uint32_t *p_nt_phy_link_state,
+	uint32_t *p_lh_abs,
+	uint32_t *p_ll_nt_phy_link_state,
+	uint32_t *p_link_down_cnt,
+	uint32_t *p_nim_interr,
+	uint32_t *p_lh_local_fault,
+	uint32_t *p_lh_remote_fault,
+	uint32_t *p_local_fault,
+	uint32_t *p_remote_fault);
+
+bool nthw_mac_pcs_reset_required(nthw_mac_pcs_t *p);
+void nthw_mac_pcs_set_fec(nthw_mac_pcs_t *p, bool enable);
+bool nthw_mac_pcs_get_fec_bypass(nthw_mac_pcs_t *p);
+bool nthw_mac_pcs_get_fec_valid(nthw_mac_pcs_t *p);
+bool nthw_mac_pcs_get_fec_stat_all_am_locked(nthw_mac_pcs_t *p);
+void nthw_mac_pcs_reset_fec_counters(nthw_mac_pcs_t *p);
+void nthw_mac_pcs_set_gty_tx_tuning(nthw_mac_pcs_t *p,
+	uint8_t lane,
+	uint8_t tx_pre_csr,
+	uint8_t tx_diff_ctl,
+	uint8_t tx_post_csr);
+void nthw_mac_pcs_swap_gty_tx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap);
+void nthw_mac_pcs_swap_gty_rx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap);
+void nthw_mac_pcs_set_receiver_equalization_mode(nthw_mac_pcs_t *p, uint8_t mode);
+void nthw_mac_pcs_set_led_mode(nthw_mac_pcs_t *p, uint8_t mode);
+void nthw_mac_pcs_set_timestamp_comp_rx(nthw_mac_pcs_t *p, uint16_t rx_dly);
+void nthw_mac_pcs_set_port_no(nthw_mac_pcs_t *p, uint8_t port_no);
+
+uint32_t nthw_mac_pcs_get_fld_block_lock_lock(nthw_mac_pcs_t *p);
+uint32_t nthw_mac_pcs_get_fld_block_lock_lock_mask(nthw_mac_pcs_t *p);
+uint32_t nthw_mac_pcs_get_fld_lane_lock_lock(nthw_mac_pcs_t *p);
+uint32_t nthw_mac_pcs_get_fld_lane_lock_lock_mask(nthw_mac_pcs_t *p);
+
+#endif	/* NTHW_MAC_PCS_H_ */
diff --git a/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c
new file mode 100644
index 0000000000..76040ec9c0
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c
@@ -0,0 +1,894 @@
+/*
+ * 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_mac_pcs.h"
+
+#define NTHW_MAC_PCS_LANES (20)
+
+static const uint8_t c_pcs_lanes = NTHW_MAC_PCS_LANES;
+static const uint8_t c_mac_pcs_receiver_mode_dfe;
+
+/*
+ * Parameters:
+ *   p != NULL: init struct pointed to by p
+ *   p == NULL: check fpga module(s) presence (but no struct to init)
+ *
+ * Return value:
+ *  <0: if p == NULL then fpga module(s) is/are not present.
+ *      if p != NULL then fpga module(s) is/are not present, struct undefined
+ * ==0: if p == NULL then fpga module(s) is/are present (no struct to init)
+ *    : if p != NULL then fpga module(s) is/are present and struct initialized
+ */
+int nthw_mac_pcs_init(nthw_mac_pcs_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+	nthw_module_t *mod = nthw_fpga_query_module(p_fpga, MOD_MAC_PCS, n_instance);
+
+	if (p == NULL)
+		return mod == NULL ? -1 : 0;
+
+	if (mod == NULL) {
+		NT_LOG(ERR, NTHW, "%s: MAC_PCS %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_mac_pcs = mod;
+
+	assert(n_instance >= 0 && n_instance <= 255);
+	nthw_mac_pcs_set_port_no(p, (uint8_t)n_instance);
+
+	{
+		nthw_register_t *p_reg_block_lock, *p_reg_stat_pcs_rx, *p_reg_stat_pcs_rx_latch;
+		nthw_register_t *p_reg_vl_demuxed, *p_reg_gty_stat, *p_reg_pcs_config,
+				*p_reg_phymac_misc;
+		const int product_id = nthw_fpga_get_product_id(p_fpga);
+
+		p_reg_block_lock = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_BLOCK_LOCK);
+		p->mp_reg_block_lock = p_reg_block_lock;
+		p->mp_fld_block_lock_lock =
+			nthw_register_get_field(p_reg_block_lock, MAC_PCS_BLOCK_LOCK_LOCK);
+
+		p_reg_stat_pcs_rx =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_STAT_PCS_RX);
+		p->mp_reg_stat_pcs_rx = p_reg_stat_pcs_rx;
+		p->mp_fld_stat_pcs_rx_status =
+			nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_STATUS);
+		p->mp_fld_stat_pcs_rx_aligned =
+			nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_ALIGNED);
+		p->mp_fld_stat_pcs_rx_aligned_err =
+			nthw_register_get_field(p_reg_stat_pcs_rx,
+				MAC_PCS_STAT_PCS_RX_ALIGNED_ERR);
+		p->mp_fld_stat_pcs_rx_misaligned =
+			nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_MISALIGNED);
+		p->mp_fld_stat_pcs_rx_internal_local_fault =
+			nthw_register_get_field(p_reg_stat_pcs_rx,
+				MAC_PCS_STAT_PCS_RX_INTERNAL_LOCAL_FAULT);
+		p->mp_fld_stat_pcs_rx_received_local_fault =
+			nthw_register_get_field(p_reg_stat_pcs_rx,
+				MAC_PCS_STAT_PCS_RX_RECEIVED_LOCAL_FAULT);
+		p->mp_fld_stat_pcs_rx_local_fault =
+			nthw_register_get_field(p_reg_stat_pcs_rx,
+				MAC_PCS_STAT_PCS_RX_LOCAL_FAULT);
+		p->mp_fld_stat_pcs_rx_remote_fault =
+			nthw_register_get_field(p_reg_stat_pcs_rx,
+				MAC_PCS_STAT_PCS_RX_REMOTE_FAULT);
+		p->mp_fld_stat_pcs_rx_hi_ber =
+			nthw_register_get_field(p_reg_stat_pcs_rx, MAC_PCS_STAT_PCS_RX_HI_BER);
+
+		p_reg_stat_pcs_rx_latch =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_STAT_PCS_RX_LATCH);
+		p->mp_reg_stat_pcs_rx_latch = p_reg_stat_pcs_rx_latch;
+		p->mp_fld_stat_pcs_rx_latch_status =
+			nthw_register_get_field(p_reg_stat_pcs_rx_latch,
+				MAC_PCS_STAT_PCS_RX_LATCH_STATUS);
+
+		p_reg_vl_demuxed = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_VL_DEMUXED);
+		p->mp_fld_vl_demuxed_lock =
+			nthw_register_get_field(p_reg_vl_demuxed, MAC_PCS_VL_DEMUXED_LOCK);
+
+		p_reg_gty_stat = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_STAT);
+		p->mp_fld_gty_stat_tx_rst_done0 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_0);
+		p->mp_fld_gty_stat_tx_rst_done1 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_1);
+		p->mp_fld_gty_stat_tx_rst_done2 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_2);
+		p->mp_fld_gty_stat_tx_rst_done3 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_TX_RST_DONE_3);
+		p->mp_fld_gty_stat_rx_rst_done0 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_0);
+		p->mp_fld_gty_stat_rx_rst_done1 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_1);
+		p->mp_fld_gty_stat_rx_rst_done2 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_2);
+		p->mp_fld_gty_stat_rx_rst_done3 =
+			nthw_register_get_field(p_reg_gty_stat, MAC_PCS_GTY_STAT_RX_RST_DONE_3);
+
+		p->m_fld_block_lock_lock_mask = 0;
+		p->m_fld_vl_demuxed_lock_mask = 0;
+		p->m_fld_gty_stat_tx_rst_done_mask = 0;
+		p->m_fld_gty_stat_rx_rst_done_mask = 0;
+
+		if (product_id == 9563) {
+			/* NT200A01_2X100 implements 20 virtual lanes */
+			p->m_fld_block_lock_lock_mask = (1 << 20) - 1;
+			/* NT200A01_2X100 implements 20 virtual lanes */
+			p->m_fld_vl_demuxed_lock_mask = (1 << 20) - 1;
+			p->m_fld_gty_stat_tx_rst_done_mask =
+				1;	/* NT200A01_2X100 implements 4 GTY */
+			p->m_fld_gty_stat_rx_rst_done_mask =
+				1;	/* NT200A01_2X100 implements 4 GTY */
+
+		} else {
+			/* Remember to add new product_ids */
+			assert(0);
+		}
+
+		p_reg_pcs_config =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_MAC_PCS_CONFIG);
+		p->mp_fld_pcs_config_tx_path_rst =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_TX_PATH_RST);
+		p->mp_fld_pcs_config_rx_path_rst =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_RX_PATH_RST);
+		p->mp_fld_pcs_config_rx_enable =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_RX_ENABLE);
+		p->mp_fld_pcs_config_rx_force_resync =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_RX_FORCE_RESYNC);
+		p->mp_fld_pcs_config_rx_test_pattern =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_RX_TEST_PATTERN);
+		p->mp_fld_pcs_config_tx_enable =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_TX_ENABLE);
+		p->mp_fld_pcs_config_tx_send_idle =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_TX_SEND_IDLE);
+		p->mp_fld_pcs_config_tx_send_rfi =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_TX_SEND_RFI);
+		p->mp_fld_pcs_config_tx_test_pattern =
+			nthw_register_get_field(p_reg_pcs_config,
+				MAC_PCS_MAC_PCS_CONFIG_TX_TEST_PATTERN);
+
+		p->mp_reg_gty_loop = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_LOOP);
+		p->mp_fld_gty_loop_gt_loop0 =
+			nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_0);
+		p->mp_fld_gty_loop_gt_loop1 =
+			nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_1);
+		p->mp_fld_gty_loop_gt_loop2 =
+			nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_2);
+		p->mp_fld_gty_loop_gt_loop3 =
+			nthw_register_get_field(p->mp_reg_gty_loop, MAC_PCS_GTY_LOOP_GT_LOOP_3);
+
+		p_reg_phymac_misc =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_PHYMAC_MISC);
+		p->mp_reg_phymac_misc = p_reg_phymac_misc;
+		p->mp_fld_phymac_misc_tx_sel_host =
+			nthw_register_get_field(p_reg_phymac_misc,
+				MAC_PCS_PHYMAC_MISC_TX_SEL_HOST);
+		p->mp_fld_phymac_misc_tx_sel_tfg =
+			nthw_register_get_field(p_reg_phymac_misc, MAC_PCS_PHYMAC_MISC_TX_SEL_TFG);
+		p->mp_fld_phymac_misc_tx_sel_rx_loop =
+			nthw_register_get_field(p_reg_phymac_misc,
+				MAC_PCS_PHYMAC_MISC_TX_SEL_RX_LOOP);
+
+		/* SOP or EOP TIMESTAMP */
+		p->mp_fld_phymac_misc_ts_eop =
+			nthw_register_query_field(p_reg_phymac_misc, MAC_PCS_PHYMAC_MISC_TS_EOP);
+
+		p->mp_reg_link_summary =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_LINK_SUMMARY);
+		p->mp_fld_link_summary_abs =
+			nthw_register_get_field(p->mp_reg_link_summary, MAC_PCS_LINK_SUMMARY_ABS);
+		p->mp_fld_link_summary_nt_phy_link_state =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_NT_PHY_LINK_STATE);
+		p->mp_fld_link_summary_lh_abs =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LH_ABS);
+		p->mp_fld_link_summary_ll_nt_phy_link_state =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LL_PHY_LINK_STATE);
+		p->mp_fld_link_summary_link_down_cnt =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LINK_DOWN_CNT);
+		p->mp_fld_link_summary_nim_interr =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_NIM_INTERR);
+		p->mp_fld_link_summary_lh_local_fault =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LH_LOCAL_FAULT);
+		p->mp_fld_link_summary_lh_remote_fault =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LH_REMOTE_FAULT);
+		p->mp_fld_link_summary_local_fault =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_LOCAL_FAULT);
+		p->mp_fld_link_summary_remote_fault =
+			nthw_register_get_field(p->mp_reg_link_summary,
+				MAC_PCS_LINK_SUMMARY_REMOTE_FAULT);
+
+		p->mp_reg_bip_err = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_BIP_ERR);
+		p->mp_fld_reg_bip_err_bip_err =
+			nthw_register_get_field(p->mp_reg_bip_err, MAC_PCS_BIP_ERR_BIP_ERR);
+
+		p->mp_reg_fec_ctrl = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_CTRL);
+		p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in =
+			nthw_register_get_field(p->mp_reg_fec_ctrl,
+				MAC_PCS_FEC_CTRL_RS_FEC_CTRL_IN);
+
+		p->mp_reg_fec_stat = nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_STAT);
+		p->mp_field_fec_stat_bypass =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_BYPASS);
+		p->mp_field_fec_stat_valid =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_VALID);
+		p->mp_field_fec_stat_am_lock0 =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_0);
+		p->mp_field_fec_stat_am_lock1 =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_1);
+		p->mp_field_fec_stat_am_lock2 =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_2);
+		p->mp_field_fec_stat_am_lock3 =
+			nthw_register_get_field(p->mp_reg_fec_stat, MAC_PCS_FEC_STAT_AM_LOCK_3);
+		p->mp_field_fec_stat_fec_lane_algn =
+			nthw_register_get_field(p->mp_reg_fec_stat,
+				MAC_PCS_FEC_STAT_FEC_LANE_ALGN);
+
+		p->mp_reg_fec_cw_cnt =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_CW_CNT);
+		p->mp_field_fec_cw_cnt_cw_cnt =
+			nthw_register_get_field(p->mp_reg_fec_cw_cnt, MAC_PCS_FEC_CW_CNT_CW_CNT);
+
+		p->mp_reg_fec_ucw_cnt =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_FEC_UCW_CNT);
+		p->mp_field_fec_ucw_cnt_ucw_cnt =
+			nthw_register_get_field(p->mp_reg_fec_ucw_cnt,
+				MAC_PCS_FEC_UCW_CNT_UCW_CNT);
+
+		/* GTY_RX_BUF_STAT */
+#ifdef RXBUFSTAT
+		p->mp_reg_gty_rx_buf_stat =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_RX_BUF_STAT);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat0 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_0);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat1 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_1);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat2 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_2);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat3 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_3);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed0 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_0);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed1 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_1);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed2 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_2);
+		p->mp_field_gty_rx_buf_stat_rx_buf_stat_changed3 =
+			nthw_register_get_field(p->mp_reg_gty_rx_buf_stat,
+				MAC_PCS_GTY_RX_BUF_STAT_RX_BUF_STAT_CHANGED_3);
+#endif
+
+		/* GTY_PRE_CURSOR */
+		p->mp_reg_gty_pre_cursor =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_PRE_CURSOR);
+		p->mp_field_gty_pre_cursor_tx_pre_csr0 =
+			nthw_register_get_field(p->mp_reg_gty_pre_cursor,
+				MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_0);
+		p->mp_field_gty_pre_cursor_tx_pre_csr1 =
+			nthw_register_get_field(p->mp_reg_gty_pre_cursor,
+				MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_1);
+		p->mp_field_gty_pre_cursor_tx_pre_csr2 =
+			nthw_register_get_field(p->mp_reg_gty_pre_cursor,
+				MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_2);
+		p->mp_field_gty_pre_cursor_tx_pre_csr3 =
+			nthw_register_get_field(p->mp_reg_gty_pre_cursor,
+				MAC_PCS_GTY_PRE_CURSOR_TX_PRE_CSR_3);
+
+		/* GTY_DIFF_CTL */
+		p->mp_reg_gty_diff_ctl =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_DIFF_CTL);
+		p->mp_field_gty_gty_diff_ctl_tx_diff_ctl0 =
+			nthw_register_get_field(p->mp_reg_gty_diff_ctl,
+				MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_0);
+		p->mp_field_gty_gty_diff_ctl_tx_diff_ctl1 =
+			nthw_register_get_field(p->mp_reg_gty_diff_ctl,
+				MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_1);
+		p->mp_field_gty_gty_diff_ctl_tx_diff_ctl2 =
+			nthw_register_get_field(p->mp_reg_gty_diff_ctl,
+				MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_2);
+		p->mp_field_gty_gty_diff_ctl_tx_diff_ctl3 =
+			nthw_register_get_field(p->mp_reg_gty_diff_ctl,
+				MAC_PCS_GTY_DIFF_CTL_TX_DIFF_CTL_3);
+
+		/* GTY_POST_CURSOR */
+		p->mp_reg_gty_post_cursor =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_POST_CURSOR);
+		p->mp_field_gty_post_cursor_tx_post_csr0 =
+			nthw_register_get_field(p->mp_reg_gty_post_cursor,
+				MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_0);
+		p->mp_field_gty_post_cursor_tx_post_csr1 =
+			nthw_register_get_field(p->mp_reg_gty_post_cursor,
+				MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_1);
+		p->mp_field_gty_post_cursor_tx_post_csr2 =
+			nthw_register_get_field(p->mp_reg_gty_post_cursor,
+				MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_2);
+		p->mp_field_gty_post_cursor_tx_post_csr3 =
+			nthw_register_get_field(p->mp_reg_gty_post_cursor,
+				MAC_PCS_GTY_POST_CURSOR_TX_POST_CSR_3);
+
+		/* GTY_CTL */
+		p->mp_reg_gty_ctl = nthw_module_query_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL);
+
+		if (p->mp_reg_gty_ctl) {
+			p->mp_field_gty_ctl_tx_pol0 =
+				nthw_register_get_field(p->mp_reg_gty_ctl,
+					MAC_PCS_GTY_CTL_TX_POLARITY_0);
+			p->mp_field_gty_ctl_tx_pol1 =
+				nthw_register_get_field(p->mp_reg_gty_ctl,
+					MAC_PCS_GTY_CTL_TX_POLARITY_1);
+			p->mp_field_gty_ctl_tx_pol2 =
+				nthw_register_get_field(p->mp_reg_gty_ctl,
+					MAC_PCS_GTY_CTL_TX_POLARITY_2);
+			p->mp_field_gty_ctl_tx_pol3 =
+				nthw_register_get_field(p->mp_reg_gty_ctl,
+					MAC_PCS_GTY_CTL_TX_POLARITY_3);
+
+		} else {
+			p->mp_reg_gty_ctl =
+				nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL_RX);
+			p->mp_reg_gty_ctl_tx =
+				nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_GTY_CTL_TX);
+			p->mp_field_gty_ctl_tx_pol0 =
+				nthw_register_get_field(p->mp_reg_gty_ctl_tx,
+					MAC_PCS_GTY_CTL_TX_POLARITY_0);
+			p->mp_field_gty_ctl_tx_pol1 =
+				nthw_register_get_field(p->mp_reg_gty_ctl_tx,
+					MAC_PCS_GTY_CTL_TX_POLARITY_1);
+			p->mp_field_gty_ctl_tx_pol2 =
+				nthw_register_get_field(p->mp_reg_gty_ctl_tx,
+					MAC_PCS_GTY_CTL_TX_POLARITY_2);
+			p->mp_field_gty_ctl_tx_pol3 =
+				nthw_register_get_field(p->mp_reg_gty_ctl_tx,
+					MAC_PCS_GTY_CTL_TX_POLARITY_3);
+		}
+
+		p->mp_field_gty_ctl_rx_pol0 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_0);
+		p->mp_field_gty_ctl_rx_pol1 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_1);
+		p->mp_field_gty_ctl_rx_pol2 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_2);
+		p->mp_field_gty_ctl_rx_pol3 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_POLARITY_3);
+		p->mp_field_gty_ctl_rx_lpm_en0 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_0);
+		p->mp_field_gty_ctl_rx_lpm_en1 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_1);
+		p->mp_field_gty_ctl_rx_lpm_en2 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_2);
+		p->mp_field_gty_ctl_rx_lpm_en3 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_LPM_EN_3);
+		p->mp_field_gty_ctl_rx_equa_rst0 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_0);
+		p->mp_field_gty_ctl_rx_equa_rst1 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_1);
+		p->mp_field_gty_ctl_rx_equa_rst2 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_2);
+		p->mp_field_gty_ctl_rx_equa_rst3 =
+			nthw_register_get_field(p->mp_reg_gty_ctl, MAC_PCS_GTY_CTL_RX_EQUA_RST_3);
+
+		/* DEBOUNCE_CTRL */
+		p->mp_reg_debounce_ctrl =
+			nthw_module_get_register(p->mp_mod_mac_pcs, MAC_PCS_DEBOUNCE_CTRL);
+		p->mp_field_debounce_ctrl_nt_port_ctrl =
+			nthw_register_get_field(p->mp_reg_debounce_ctrl,
+				MAC_PCS_DEBOUNCE_CTRL_NT_PORT_CTRL);
+
+		p->mp_reg_time_stamp_comp =
+			nthw_module_query_register(p->mp_mod_mac_pcs, MAC_PCS_TIMESTAMP_COMP);
+
+		if (p->mp_reg_time_stamp_comp) {
+			/* TIMESTAMP_COMP */
+			p->mp_field_time_stamp_comp_rx_dly =
+				nthw_register_get_field(p->mp_reg_time_stamp_comp,
+					MAC_PCS_TIMESTAMP_COMP_RX_DLY);
+			p->mp_field_time_stamp_comp_tx_dly =
+				nthw_register_get_field(p->mp_reg_time_stamp_comp,
+					MAC_PCS_TIMESTAMP_COMP_TX_DLY);
+		}
+	}
+	return 0;
+}
+
+void nthw_mac_pcs_set_rx_enable(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_pcs_config_rx_enable);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_pcs_config_rx_enable);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_pcs_config_rx_enable);
+}
+
+void nthw_mac_pcs_set_tx_enable(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_pcs_config_tx_enable);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_pcs_config_tx_enable);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_pcs_config_tx_enable);
+}
+
+void nthw_mac_pcs_set_tx_sel_host(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_phymac_misc_tx_sel_host);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_phymac_misc_tx_sel_host);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_phymac_misc_tx_sel_host);
+}
+
+void nthw_mac_pcs_set_tx_sel_tfg(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_phymac_misc_tx_sel_tfg);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_phymac_misc_tx_sel_tfg);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_phymac_misc_tx_sel_tfg);
+}
+
+void nthw_mac_pcs_set_ts_eop(nthw_mac_pcs_t *p, bool enable)
+{
+	if (p->mp_fld_phymac_misc_ts_eop) {
+		nthw_field_get_updated(p->mp_fld_phymac_misc_ts_eop);
+
+		if (enable)
+			nthw_field_set_flush(p->mp_fld_phymac_misc_ts_eop);
+
+		else
+			nthw_field_clr_flush(p->mp_fld_phymac_misc_ts_eop);
+	}
+}
+
+void nthw_mac_pcs_tx_path_rst(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_pcs_config_tx_path_rst);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_pcs_config_tx_path_rst);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_pcs_config_tx_path_rst);
+}
+
+void nthw_mac_pcs_rx_path_rst(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_field_get_updated(p->mp_fld_pcs_config_rx_path_rst);
+
+	if (enable)
+		nthw_field_set_flush(p->mp_fld_pcs_config_rx_path_rst);
+
+	else
+		nthw_field_clr_flush(p->mp_fld_pcs_config_rx_path_rst);
+}
+
+bool nthw_mac_pcs_is_rx_path_rst(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_fld_pcs_config_rx_path_rst);
+}
+
+void nthw_mac_pcs_set_host_loopback(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_register_update(p->mp_reg_gty_loop);
+
+	if (enable) {
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 2);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 2);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 2);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 2);
+
+	} else {
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 0);
+	}
+
+	nthw_register_flush(p->mp_reg_gty_loop, 1);
+}
+
+void nthw_mac_pcs_set_line_loopback(nthw_mac_pcs_t *p, bool enable)
+{
+	nthw_register_update(p->mp_reg_gty_loop);
+
+	if (enable) {
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 4);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 4);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 4);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 4);
+
+	} else {
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop0, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop1, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop2, 0);
+		nthw_field_set_val32(p->mp_fld_gty_loop_gt_loop3, 0);
+	}
+
+	nthw_register_flush(p->mp_reg_gty_loop, 1);
+}
+
+void nthw_mac_pcs_reset_bip_counters(nthw_mac_pcs_t *p)
+{
+	uint32_t lane_bit_errors[NTHW_MAC_PCS_LANES];
+	nthw_register_update(p->mp_reg_bip_err);
+	nthw_field_get_val(p->mp_fld_reg_bip_err_bip_err, (uint32_t *)lane_bit_errors,
+		ARRAY_SIZE(lane_bit_errors));
+
+	(void)c_pcs_lanes;	/* unused - kill warning */
+}
+
+bool nthw_mac_pcs_get_hi_ber(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_fld_stat_pcs_rx_hi_ber);
+}
+
+void nthw_mac_pcs_get_link_summary(nthw_mac_pcs_t *p,
+	uint32_t *p_abs,
+	uint32_t *p_nt_phy_link_state,
+	uint32_t *p_lh_abs,
+	uint32_t *p_ll_nt_phy_link_state,
+	uint32_t *p_link_down_cnt,
+	uint32_t *p_nim_interr,
+	uint32_t *p_lh_local_fault,
+	uint32_t *p_lh_remote_fault,
+	uint32_t *p_local_fault,
+	uint32_t *p_remote_fault)
+{
+	nthw_register_update(p->mp_reg_link_summary);
+
+	if (p_abs)
+		*p_abs = nthw_field_get_val32(p->mp_fld_link_summary_abs);
+
+	if (p_nt_phy_link_state) {
+		*p_nt_phy_link_state =
+			nthw_field_get_val32(p->mp_fld_link_summary_nt_phy_link_state);
+	}
+
+	if (p_lh_abs)
+		*p_lh_abs = nthw_field_get_val32(p->mp_fld_link_summary_lh_abs);
+
+	if (p_ll_nt_phy_link_state) {
+		*p_ll_nt_phy_link_state =
+			nthw_field_get_val32(p->mp_fld_link_summary_ll_nt_phy_link_state);
+	}
+
+	if (p_link_down_cnt)
+		*p_link_down_cnt = nthw_field_get_val32(p->mp_fld_link_summary_link_down_cnt);
+
+	if (p_nim_interr)
+		*p_nim_interr = nthw_field_get_val32(p->mp_fld_link_summary_nim_interr);
+
+	if (p_lh_local_fault)
+		*p_lh_local_fault = nthw_field_get_val32(p->mp_fld_link_summary_lh_local_fault);
+
+	if (p_lh_remote_fault)
+		*p_lh_remote_fault = nthw_field_get_val32(p->mp_fld_link_summary_lh_remote_fault);
+
+	if (p_local_fault)
+		*p_local_fault = nthw_field_get_val32(p->mp_fld_link_summary_local_fault);
+
+	if (p_remote_fault)
+		*p_remote_fault = nthw_field_get_val32(p->mp_fld_link_summary_remote_fault);
+}
+
+/*
+ * Returns true if the lane/block lock bits indicate that a reset is required.
+ * This is the case if Block/Lane lock is not all zero but not all set either.
+ */
+bool nthw_mac_pcs_reset_required(nthw_mac_pcs_t *p)
+{
+	uint32_t block_lock = nthw_mac_pcs_get_fld_block_lock_lock(p);
+	uint32_t lane_lock = nthw_mac_pcs_get_fld_lane_lock_lock(p);
+	uint32_t block_lock_mask = nthw_mac_pcs_get_fld_block_lock_lock_mask(p);
+	uint32_t lane_lock_mask = nthw_mac_pcs_get_fld_lane_lock_lock_mask(p);
+
+	return ((block_lock != 0) && (block_lock != block_lock_mask)) ||
+		((lane_lock != 0) && (lane_lock != lane_lock_mask));
+}
+
+void nthw_mac_pcs_set_fec(nthw_mac_pcs_t *p, bool enable)
+{
+	NT_LOG(DBG, NTHW, "Port %u: Set FEC: %u\n", p->m_port_no, enable);
+
+	nthw_field_get_updated(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in);
+
+	if (enable)
+		nthw_field_set_val_flush32(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in, 0);
+
+	else
+		nthw_field_set_val_flush32(p->mp_field_fec_ctrl_reg_rs_fec_ctrl_in, (1 << 5) - 1);
+
+	/* Both Rx and Tx must be reset for new FEC state to become active */
+	nthw_mac_pcs_rx_path_rst(p, true);
+	nthw_mac_pcs_tx_path_rst(p, true);
+	nt_os_wait_usec(10000);	/* 10ms */
+
+	nthw_mac_pcs_rx_path_rst(p, false);
+	nthw_mac_pcs_tx_path_rst(p, false);
+	nt_os_wait_usec(10000);	/* 10ms */
+}
+
+bool nthw_mac_pcs_get_fec_bypass(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_field_fec_stat_bypass);
+}
+
+bool nthw_mac_pcs_get_fec_valid(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_field_fec_stat_valid);
+}
+
+bool nthw_mac_pcs_get_fec_stat_all_am_locked(nthw_mac_pcs_t *p)
+{
+	nthw_register_update(p->mp_reg_fec_stat);
+
+	if ((nthw_field_get_val32(p->mp_field_fec_stat_am_lock0)) &&
+		(nthw_field_get_val32(p->mp_field_fec_stat_am_lock1)) &&
+		(nthw_field_get_val32(p->mp_field_fec_stat_am_lock2)) &&
+		(nthw_field_get_val32(p->mp_field_fec_stat_am_lock3))) {
+		return true;
+	}
+
+	return false;
+}
+
+void nthw_mac_pcs_reset_fec_counters(nthw_mac_pcs_t *p)
+{
+	nthw_register_update(p->mp_reg_fec_cw_cnt);
+	nthw_register_update(p->mp_reg_fec_ucw_cnt);
+
+	if (nthw_field_get_val32(p->mp_field_fec_cw_cnt_cw_cnt)) {
+		NT_LOG(DBG, NTHW, "Port %u: FEC_CW_CNT: %u\n", p->m_port_no,
+			nthw_field_get_val32(p->mp_field_fec_cw_cnt_cw_cnt));
+	}
+
+	if (nthw_field_get_val32(p->mp_field_fec_ucw_cnt_ucw_cnt)) {
+		NT_LOG(DBG, NTHW, "Port %u: FEC_UCW_CNT: %u\n", p->m_port_no,
+			nthw_field_get_val32(p->mp_field_fec_ucw_cnt_ucw_cnt));
+	}
+}
+
+void nthw_mac_pcs_set_gty_tx_tuning(nthw_mac_pcs_t *p, uint8_t lane, uint8_t tx_pre_csr,
+	uint8_t tx_diff_ctl, uint8_t tx_post_csr)
+{
+	/* GTY_PRE_CURSOR */
+	nthw_register_update(p->mp_reg_gty_pre_cursor);
+
+	switch (lane) {
+	case 0:
+		nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr0,
+			tx_pre_csr & 0x1F);
+		break;
+
+	case 1:
+		nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr1,
+			tx_pre_csr & 0x1F);
+		break;
+
+	case 2:
+		nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr2,
+			tx_pre_csr & 0x1F);
+		break;
+
+	case 3:
+		nthw_field_set_val_flush32(p->mp_field_gty_pre_cursor_tx_pre_csr3,
+			tx_pre_csr & 0x1F);
+		break;
+	}
+
+	/* GTY_DIFF_CTL */
+	nthw_register_update(p->mp_reg_gty_diff_ctl);
+
+	switch (lane) {
+	case 0:
+		nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl0,
+			tx_diff_ctl & 0x1F);
+		break;
+
+	case 1:
+		nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl1,
+			tx_diff_ctl & 0x1F);
+		break;
+
+	case 2:
+		nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl2,
+			tx_diff_ctl & 0x1F);
+		break;
+
+	case 3:
+		nthw_field_set_val_flush32(p->mp_field_gty_gty_diff_ctl_tx_diff_ctl3,
+			tx_diff_ctl & 0x1F);
+		break;
+	}
+
+	/* GTY_POST_CURSOR */
+	nthw_register_update(p->mp_reg_gty_post_cursor);
+
+	switch (lane) {
+	case 0:
+		nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr0,
+			tx_post_csr & 0x1F);
+		break;
+
+	case 1:
+		nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr1,
+			tx_post_csr & 0x1F);
+		break;
+
+	case 2:
+		nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr2,
+			tx_post_csr & 0x1F);
+		break;
+
+	case 3:
+		nthw_field_set_val_flush32(p->mp_field_gty_post_cursor_tx_post_csr3,
+			tx_post_csr & 0x1F);
+		break;
+	}
+
+	NT_LOG(DBG, NTHW,
+		"Port %u, lane %u: GTY tx_pre_csr: %d, tx_diff_ctl: %d, tx_post_csr: %d\n",
+		p->m_port_no, lane, tx_pre_csr, tx_diff_ctl, tx_post_csr);
+}
+
+/*
+ * Set receiver equalization mode
+ *  0: enable DFE
+ *  1: enable LPM
+ *
+ * See UltraScale Architecture GTY Transceivers www.xilinx.com page 181,
+ * UG578 (v1.1) November 24, 2015
+ */
+void nthw_mac_pcs_set_receiver_equalization_mode(nthw_mac_pcs_t *p, uint8_t mode)
+{
+	nthw_register_update(p->mp_reg_gty_ctl);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en0, mode & 0x1);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en1, mode & 0x1);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_lpm_en2, mode & 0x1);
+	nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_lpm_en3, mode & 0x1);
+
+	/* Toggle reset */
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst0, 1);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst1, 1);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst2, 1);
+	nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_equa_rst3, 1);
+
+	nt_os_wait_usec(1000);	/* 1ms */
+
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst0, 0);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst1, 0);
+	nthw_field_set_val32(p->mp_field_gty_ctl_rx_equa_rst2, 0);
+	nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_equa_rst3, 0);
+
+	NT_LOG(DBG, NTHW, "Port %u: GTY receiver mode: %s\n", p->m_port_no,
+		(mode == c_mac_pcs_receiver_mode_dfe ? "DFE" : "LPM"));
+}
+
+void nthw_mac_pcs_swap_gty_tx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap)
+{
+	nthw_register_update(p->mp_reg_gty_ctl);
+
+	switch (lane) {
+	case 0:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol0, swap);
+		break;
+
+	case 1:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol1, swap);
+		break;
+
+	case 2:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol2, swap);
+		break;
+
+	case 3:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_tx_pol3, swap);
+		break;
+	}
+
+	NT_LOG(DBG, NTHW, "Port %u: set GTY Tx lane (%d) polarity: %d\n", p->m_port_no, lane,
+		swap);
+}
+
+void nthw_mac_pcs_swap_gty_rx_polarity(nthw_mac_pcs_t *p, uint8_t lane, bool swap)
+{
+	nthw_register_update(p->mp_reg_gty_ctl);
+
+	switch (lane) {
+	case 0:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol0, swap);
+		break;
+
+	case 1:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol1, swap);
+		break;
+
+	case 2:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol2, swap);
+		break;
+
+	case 3:
+		nthw_field_set_val_flush32(p->mp_field_gty_ctl_rx_pol3, swap);
+		break;
+	}
+
+	NT_LOG(DBG, NTHW, "Port %u: set GTY Rx lane (%d) polarity: %d\n", p->m_port_no, lane,
+		swap);
+}
+
+void nthw_mac_pcs_set_led_mode(nthw_mac_pcs_t *p, uint8_t mode)
+{
+	nthw_field_get_updated(p->mp_field_debounce_ctrl_nt_port_ctrl);
+	nthw_field_set_val_flush32(p->mp_field_debounce_ctrl_nt_port_ctrl, mode);
+}
+
+void nthw_mac_pcs_set_timestamp_comp_rx(nthw_mac_pcs_t *p, uint16_t rx_dly)
+{
+	if (p->mp_field_time_stamp_comp_rx_dly) {
+		nthw_field_get_updated(p->mp_field_time_stamp_comp_rx_dly);
+		nthw_field_set_val_flush32(p->mp_field_time_stamp_comp_rx_dly, rx_dly);
+	}
+}
+
+void nthw_mac_pcs_set_port_no(nthw_mac_pcs_t *p, uint8_t port_no)
+{
+	p->m_port_no = port_no;
+}
+
+uint32_t nthw_mac_pcs_get_fld_block_lock_lock(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_fld_block_lock_lock);
+}
+
+uint32_t nthw_mac_pcs_get_fld_block_lock_lock_mask(nthw_mac_pcs_t *p)
+{
+	return p->m_fld_block_lock_lock_mask;
+}
+
+uint32_t nthw_mac_pcs_get_fld_lane_lock_lock(nthw_mac_pcs_t *p)
+{
+	return nthw_field_get_updated(p->mp_fld_vl_demuxed_lock);
+}
+
+uint32_t nthw_mac_pcs_get_fld_lane_lock_lock_mask(nthw_mac_pcs_t *p)
+{
+	return p->m_fld_vl_demuxed_lock_mask;
+}
-- 
2.45.0