[v10,21/21] net/ntnic: add physical layer control module

Message ID 20240717133313.3104239-21-sil-plv@napatech.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series [v10,01/21] net/ntnic: add ethdev and makes PMD available |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/github-robot: build success github build: passed
ci/intel-Functional success Functional PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-marvell-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-abi-testing success Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS

Commit Message

Serhii Iliushyk July 17, 2024, 1:33 p.m. UTC
Adds functionality to control the physical layer of the OSI model.
This takes the form of the module
MAC PCS (Media Access Control Physical Coding Sublayer).
The functionality is used by the 100G link functionality to establish link.

Signed-off-by: Serhii Iliushyk <sil-plv@napatech.com>
---
v10
* Remove GTY_RX_BUF_STAT initialization
---
 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    | 864 ++++++++++++++++++
 6 files changed, 1508 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
  

Patch

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 ed0b89d417..8f0afa1f60 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);
 
diff --git a/drivers/net/ntnic/meson.build b/drivers/net/ntnic/meson.build
index 6dd972cf7d..9df2378f7c 100644
--- a/drivers/net/ntnic/meson.build
+++ b/drivers/net/ntnic/meson.build
@@ -38,6 +38,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..398f4ffd15
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c
@@ -0,0 +1,864 @@ 
+/*
+ * 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_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;
+}