Add ntnic 100G link support.

Signed-off-by: Serhii Iliushyk <sil-...@napatech.com>
---
v2:
* Fixed WARNING:REPEATED_WORD
---
 drivers/net/ntnic/include/nt4ga_link.h        |   8 +
 .../link_mgmt/link_100g/nt4ga_link_100g.c     | 840 ++++++++++++++++
 .../link_mgmt/link_100g/nt4ga_link_100g.h     |  11 +
 .../net/ntnic/nthw/core/include/nthw_core.h   |   2 +
 .../ntnic/nthw/core/include/nthw_gpio_phy.h   |  50 +
 .../ntnic/nthw/core/include/nthw_mac_pcs.h    | 263 +++++
 drivers/net/ntnic/nthw/core/nthw_gpio_phy.c   | 144 +++
 drivers/net/ntnic/nthw/core/nthw_mac_pcs.c    | 950 ++++++++++++++++++
 8 files changed, 2268 insertions(+)
 create mode 100644 drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
 create mode 100644 drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
 create mode 100644 drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h
 create mode 100644 drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
 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 49e1c5d672..1d7565f614 100644
--- a/drivers/net/ntnic/include/nt4ga_link.h
+++ b/drivers/net/ntnic/include/nt4ga_link.h
@@ -115,8 +115,16 @@ typedef struct port_action_s {
        uint32_t port_lpbk_mode;
 } port_action_t;
 
+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_gfg_t gfg[NUM_ADAPTER_PORTS_MAX];
+       nthw_gpio_phy_t gpio_phy[NUM_ADAPTER_PORTS_MAX];
+} adapter_100g_t;
+
 typedef union adapter_var_s {
        nim_i2c_ctx_t nim_ctx[NUM_ADAPTER_PORTS_MAX];   /* First field in all 
the adaptors type */
+       adapter_100g_t var100g;
 } adapter_var_u;
 
 typedef struct nt4ga_link_s {
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
new file mode 100644
index 0000000000..a429b96450
--- /dev/null
+++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.c
@@ -0,0 +1,840 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "nt_util.h"
+#include "ntlog.h"
+#include "i2c_nim.h"
+#include "nt4ga_adapter.h"
+#include "nt4ga_link_100g.h"
+#include "ntnic_mod_reg.h"
+
+#include <string.h>    /* memcmp, memset */
+
+/*
+ * Prototypes
+ */
+static int _swap_tx_rx_polarity(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs, 
int port, bool swap);
+static int _reset_rx(adapter_info_t *drv, nthw_mac_pcs_t *mac_pcs);
+static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, 
nthw_fpga_t *fpga);
+
+/*
+ * Structs and types definitions
+ */
+enum link_up_state {
+       RESET,  /* A valid signal is detected by NO local faults. */
+       EXPECT_NO_LF,   /* After that we check NO latched local fault bit 
before */
+       /* de-asserting Remote fault indication. */
+       WAIT_STABLE_LINK,       /* Now we expect the link is up. */
+       MONITOR_LINK    /* After link-up we monitor link state. */
+};
+
+typedef struct _monitoring_state {
+       /* Fields below are set by monitoring thread */
+       enum link_up_state m_link_up_state;
+       enum nt_link_state_e link_state;
+       enum nt_link_state_e latch_link_state;
+       int m_time_out;
+} _monitoring_state_t, *_monitoring_state_p;
+
+/*
+ * Global variables
+ */
+
+/*
+ * External state, to be set by the network driver.
+ */
+
+/*
+ * Utility functions
+ */
+
+/*
+ * Init 100G link ops variables
+ */
+static struct link_ops_s link_100g_ops = {
+       .link_init = nt4ga_link_100g_ports_init,
+};
+
+static void __attribute__((constructor(65535))) link_100g_init(void)
+{
+       register_100g_link_ops(&link_100g_ops);
+}
+
+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, ETHDEV, "%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, ETHDEV, "%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, ETHDEV, "%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, ETHDEV, "%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_NT200A01) ||
+               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, ETHDEV, "%s\n", lsbuf[adapter_no][port]);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Check whether a NIM module is present
+ */
+static bool _nim_is_present(nthw_gpio_phy_t *gpio_phy, uint8_t if_no)
+{
+       assert(if_no < NUM_ADAPTER_PORTS_MAX);
+
+       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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Reset TX
+ */
+
+/*
+ * 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;
+}
+
+/*
+ * 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()
+ */
+static int _create_nim(adapter_info_t *drv, nthw_fpga_t *fpga, int port, bool 
enable)
+{
+       int res = 0;
+       const uint8_t valid_nim_id = 17U;
+       nthw_gpio_phy_t *gpio_phy;
+       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];
+
+       (void)fpga;     /* unused */
+       assert(port >= 0 && port < NUM_ADAPTER_PORTS_MAX);
+       assert(link_info->variables_initialized);
+
+       gpio_phy = &link_info->u.var100g.gpio_phy[port];
+       nim_ctx = &link_info->u.var100g.nim_ctx[port];
+
+       /*
+        * Check NIM is present before doing GPIO PHY reset.
+        */
+       if (!_nim_is_present(gpio_phy, (uint8_t)port)) {
+               NT_LOG(INF, ETHDEV, "%s: NIM module is absent\n", 
drv->mp_port_id_str[port]);
+               return 0;
+       }
+
+       if (!enable) {
+               _disable_rx(drv, mac_pcs);
+               _disable_tx(drv, mac_pcs);
+               _reset_rx(drv, mac_pcs);
+       }
+
+       /*
+        * Perform PHY reset.
+        */
+       NT_LOG(DBG, ETHDEV, "%s: Performing NIM reset\n", 
drv->mp_port_id_str[port]);
+       nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, true);
+       nt_os_wait_usec(100000);/* pause 0.1s */
+       nthw_gpio_phy_set_reset(gpio_phy, (uint8_t)port, false);
+
+       /*
+        * Wait a little after a module has been inserted before trying to 
access I2C
+        * data, otherwise the module will not respond correctly.
+        */
+       nt_os_wait_usec(1000000);       /* pause 1.0s */
+
+       if (!_nim_is_present(gpio_phy, (uint8_t)port)) {
+               NT_LOG(DBG, ETHDEV, "%s: NIM module is no longer absent!\n",
+                       drv->mp_port_id_str[port]);
+               return -1;
+       }
+
+       res = construct_and_preinit_nim(nim_ctx, NULL, port,
+                       ((struct adapter_info_s *)drv)->nim_sensors,
+                       &((struct adapter_info_s *)drv)->nim_sensors_cnt[port]);
+
+       if (res)
+               return res;
+
+       res = nim_state_build(nim_ctx, &nim);
+
+       if (res)
+               return res;
+
+       NT_LOG(DBG, NTHW, "%s: NIM id = %u (%s), br = %u, vendor = '%s', pn = 
'%s', sn='%s'\n",
+               drv->mp_port_id_str[port], nim_ctx->nim_id, 
nim_id_to_text(nim_ctx->nim_id), nim.br,
+               nim_ctx->vendor_name, nim_ctx->prod_no, nim_ctx->serial_no);
+
+       /*
+        * Does the driver support the NIM module type?
+        */
+       if (nim_ctx->nim_id != valid_nim_id) {
+               NT_LOG(ERR, NTHW, "%s: The driver does not support the NIM 
module type %s\n",
+                       drv->mp_port_id_str[port], 
nim_id_to_text(nim_ctx->nim_id));
+               NT_LOG(DBG, NTHW, "%s: The driver supports the NIM module type 
%s\n",
+                       drv->mp_port_id_str[port], 
nim_id_to_text(valid_nim_id));
+               return -1;
+       }
+
+       if (enable) {
+               NT_LOG(DBG, ETHDEV, "%s: De-asserting low power\n", 
drv->mp_port_id_str[port]);
+               nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, false);
+
+       } else {
+               NT_LOG(DBG, ETHDEV, "%s: Asserting low power\n", 
drv->mp_port_id_str[port]);
+               nthw_gpio_phy_set_low_power(gpio_phy, (uint8_t)port, true);
+       }
+
+       return res;
+}
+
+/*
+ * Initialize one 100 Gbps port.
+ * The function shall not assume anything about the state of the adapter
+ * and/or 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;
+       nthw_gfg_t *gfg_mod_p;
+
+       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];
+       gfg_mod_p = &link_info->u.var100g.gfg[port];
+
+       /*
+        * Phase 1. Pre-state machine (`port init` functions)
+        * 1.1) nt4ga_adapter::port_init()
+        */
+
+       /* No adapter set-up here, only state variables */
+
+       /* 1.2) MyPort::init() */
+       link_info->link_info[port].link_speed = NT_LINK_SPEED_100G;
+       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 ||
+               (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && 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, ETHDEV, "%s: Setting up port %d\n", 
drv->mp_port_id_str[port], port);
+
+       NT_LOG(DBG, ETHDEV, "%s: Port %d: PHY TX enable\n", 
drv->mp_port_id_str[port], port);
+       _enable_tx(drv, mac_pcs);
+       _reset_rx(drv, mac_pcs);
+
+       /* 2.2) Nt4gaPort::setup() */
+       if (nthw_gmf_init(NULL, fpga, port) == 0) {
+               nthw_gmf_t gmf;
+
+               if (nthw_gmf_init(&gmf, fpga, port) == 0)
+                       nthw_gmf_set_enable(&gmf, true);
+       }
+
+       /* Phase 3. Link state machine steps */
+
+       /* 3.1) Create NIM, ::createNim() */
+       res = _create_nim(drv, fpga, port, true);
+
+       if (res) {
+               NT_LOG(WRN, ETHDEV, "%s: NIM initialization failed\n", 
drv->mp_port_id_str[port]);
+               return res;
+       }
+
+       NT_LOG(DBG, ETHDEV, "%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, ETHDEV, "%s: Port %d: HOST FEC enabled\n", 
drv->mp_port_id_str[port], port);
+
+       if (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && hw_id == 1) {
+               const uint8_t tuning_sr4[2][4][3] = {
+                       { { 8, 15, 8 }, { 8, 15, 9 }, { 7, 15, 9 }, { 6, 15, 8 
} },
+                       { { 6, 15, 8 }, { 3, 15, 12 }, { 7, 15, 9 }, { 7, 15, 8 
} }
+               };
+
+               uint8_t lane = 0;
+
+               for (lane = 0; lane < 4; lane++) {
+                       uint8_t pre, diff, post;
+
+                       /* Use short-range tuning values */
+                       pre = tuning_sr4[port][lane][0];
+                       diff = tuning_sr4[port][lane][1];
+                       post = tuning_sr4[port][lane][2];
+
+                       nthw_mac_pcs_set_gty_tx_tuning(mac_pcs, lane, pre, 
diff, post);
+               }
+
+       } else if (adapter_id == NT_HW_ADAPTER_ID_NT200A02 ||
+               (adapter_id == NT_HW_ADAPTER_ID_NT200A01 && 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, ETHDEV, "%s: Unhandled AdapterId/HwId: 
%02x_hwid%d\n", __func__,
+                       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);
+
+       (void)gfg_mod_p;/* unused; */
+
+       nthw_mac_pcs_set_host_loopback(mac_pcs, false);
+
+       return res;
+}
+
+static void read_nim_sensors(adapter_info_t *drv, int port)
+{
+       if (drv->nim_sensors[port] != NULL) {
+               nthw_spis_t *t_spi = new_sensors_t_spi(drv->fpga_info.mp_fpga);
+
+               if (t_spi) {
+                       for (struct nim_sensor_group *ptr = 
drv->nim_sensors[port]; ptr != NULL;
+                               ptr = ptr->next) {
+                               if (ptr->read)
+                                       ptr->read(ptr, t_spi);
+                       }
+
+                       nthw_spis_delete(t_spi);
+               }
+       }
+}
+
+/*
+ * State machine shared between kernel and userland
+ */
+static int _common_ptp_nim_state_machine(void *data)
+{
+       adapter_info_t *drv = (adapter_info_t *)data;
+       fpga_info_t *fpga_info = &drv->fpga_info;
+       nt4ga_link_t *link_info = &drv->nt4ga_link;
+       nthw_fpga_t *fpga = fpga_info->mp_fpga;
+       const int adapter_no = drv->adapter_no;
+       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) {
+               NT_LOG(ERR, ETHDEV, "%s: fpga is NULL\n", 
drv->mp_adapter_id_str);
+               goto NT4GA_LINK_100G_MON_EXIT;
+       }
+
+       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;
+       memset(last_lpbk_mode, 0, sizeof(last_lpbk_mode));
+
+       if (monitor_task_is_running[adapter_no])
+               NT_LOG(DBG, ETHDEV, "%s: link state machine running...\n", 
drv->mp_adapter_id_str);
+
+       while (monitor_task_is_running[adapter_no]) {
+               int i;
+               static bool reported_link[NUM_ADAPTER_PORTS_MAX] = { false };
+
+               /* Read sensors */
+               if (drv->adapter_sensors != NULL) {
+                       nthw_spis_t *t_spi = 
new_sensors_t_spi(drv->fpga_info.mp_fpga);
+
+                       if (t_spi) {
+                               for (struct nt_sensor_group *ptr = 
drv->adapter_sensors;
+                                       ptr != NULL;
+                                       ptr = ptr->next) {
+                                       ptr->read(ptr, t_spi);
+                               }
+
+                               nthw_spis_delete(t_spi);
+                       }
+               }
+
+               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;
+                       const bool enable_port = !is_port_disabled && 
was_port_disabled;
+
+                       if (!monitor_task_is_running[adapter_no])       /* stop 
quickly */
+                               break;
+
+                       /* Reading NIM sensors */
+                       read_nim_sensors(drv, i);
+
+                       /* Has the administrative port state changed? */
+                       assert(!(disable_port && enable_port));
+
+                       if (disable_port) {
+                               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, fpga, i, false);
+                               NT_LOG(DBG, ETHDEV, "%s: Port %i is disabled\n",
+                                       drv->mp_port_id_str[i], i);
+                               continue;
+                       }
+
+                       if (enable_port) {
+                               link_state[i].link_disabled = false;
+                               NT_LOG(DBG, ETHDEV, "%s: Port %i is enabled\n",
+                                       drv->mp_port_id_str[i], i);
+                       }
+
+                       if (is_port_disabled)
+                               continue;
+
+                       if (link_info->port_action[i].port_lpbk_mode != 
last_lpbk_mode[i]) {
+                               /* Loopback mode has changed. Do something */
+                               if (!_nim_is_present(&gpio_phy[i], (uint8_t)i)) 
{
+                                       /*
+                                        * If there is no Nim present, we need 
to initialize the
+                                        * port anyway
+                                        */
+                                       _port_init(drv, fpga, i);
+                               }
+
+                               NT_LOG(INF, ETHDEV, "%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;
+
+                               last_lpbk_mode[i] = 
link_info->port_action[i].port_lpbk_mode;
+                               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, ETHDEV, "%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, ETHDEV, "%s: NIM module inserted\n",
+                                       drv->mp_port_id_str[i]);
+
+                               if (_port_init(drv, fpga, i)) {
+                                       NT_LOG(ERR, ETHDEV,
+                                               "%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, ETHDEV, "%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, ETHDEV,
+                                       "%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, ETHDEV, "%s: NIM module 
initialized\n",
+                                       drv->mp_port_id_str[i]);
+                               continue;
+                       }
+
+                       if (reported_link[i] != new_link_state.link_up) {
+                               NT_LOG(INF, ETHDEV, "%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])
+                       nt_os_wait_usec(5 * 100000U);   /* 5 x 0.1s = 0.5s */
+       }
+
+NT4GA_LINK_100G_MON_EXIT:
+
+       NT_LOG(DBG, ETHDEV, "%s: Stopped NT4GA 100 Gbps link monitoring 
thread.\n",
+               drv->mp_adapter_id_str);
+
+       return 0;
+}
+
+/*
+ * Userland NIM state machine
+ */
+static void *nt4ga_link_100g_mon(void *data)
+{
+       (void)_common_ptp_nim_state_machine(data);
+
+       return NULL;
+}
+
+/*
+ * Initialize all ports
+ * The driver calls this function during initialization (of the driver).
+ */
+static int nt4ga_link_100g_ports_init(struct adapter_info_s *p_adapter_info, 
nthw_fpga_t *fpga)
+{
+       fpga_info_t *fpga_info = &p_adapter_info->fpga_info;
+       const int adapter_no = p_adapter_info->adapter_no;
+       const int nb_ports = fpga_info->n_phy_ports;
+       int res = 0;
+
+       NT_LOG(DBG, ETHDEV, "%s: Initializing ports\n", 
p_adapter_info->mp_adapter_id_str);
+
+       /*
+        * Initialize global variables
+        */
+       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_gfg_t *gfg_mod = p_adapter_info->nt4ga_link.u.var100g.gfg;
+               nthw_gpio_phy_t *gpio_phy = 
p_adapter_info->nt4ga_link.u.var100g.gpio_phy;
+               int i;
+
+               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 */);
+
+                       if (res != 0)
+                               break;
+
+                       nim_ctx[i].instance = instance;
+                       nim_ctx[i].devaddr = 0x50;      /* 0xA0 / 2 */
+                       nim_ctx[i].regaddr = 0U;
+                       nim_ctx[i].type = I2C_HWIIC;
+                       res = nthw_gfg_init(&gfg_mod[i], fpga, 0 /* Only one 
instance */);
+
+                       if (res != 0)
+                               break;
+
+                       res = nthw_gpio_phy_init(&gpio_phy[i], fpga, 0 /* Only 
one instance */);
+
+                       if (res != 0)
+                               break;
+               }
+
+               if (res == 0) {
+                       p_adapter_info->nt4ga_link.speed_capa = 
NT_LINK_SPEED_100G;
+                       p_adapter_info->nt4ga_link.variables_initialized = true;
+               }
+       }
+
+       /* Create state-machine thread */
+       if (res == 0) {
+               if (!monitor_task_is_running[adapter_no]) {
+                       res = pthread_create(&monitor_tasks[adapter_no], NULL, 
nt4ga_link_100g_mon,
+                                       p_adapter_info);
+               }
+       }
+
+       return res;
+}
diff --git a/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h 
b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h
new file mode 100644
index 0000000000..0e6103d9a0
--- /dev/null
+++ b/drivers/net/ntnic/link_mgmt/link_100g/nt4ga_link_100g.h
@@ -0,0 +1,11 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NT4GA_LINK_100G_H_
+#define NT4GA_LINK_100G_H_
+
+#include "nthw_drv.h"
+
+#endif /* NT4GA_LINK_100G_H_ */
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_core.h 
b/drivers/net/ntnic/nthw/core/include/nthw_core.h
index b72766cc5c..ba7d85b92a 100644
--- a/drivers/net/ntnic/nthw/core/include/nthw_core.h
+++ b/drivers/net/ntnic/nthw/core/include/nthw_core.h
@@ -21,6 +21,8 @@
 
 #include "nthw_gfg.h"
 #include "nthw_gmf.h"
+#include "nthw_gpio_phy.h"
+#include "nthw_mac_pcs.h"
 #include "nthw_mac_pcs_xxv.h"
 #include "nthw_mac_tfg.h"
 #include "nthw_sdc.h"
diff --git a/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h 
b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
new file mode 100644
index 0000000000..2cc8b65eab
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_gpio_phy.h
@@ -0,0 +1,50 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#ifndef NTHW_GPIO_PHY_H_
+#define NTHW_GPIO_PHY_H_
+
+#define GPIO_PHY_INTERFACES (2)
+
+typedef struct {
+       nthw_field_t *cfg_fld_lp_mode;  /* Cfg Low Power Mode */
+       nthw_field_t *cfg_int;  /* Cfg Port Interrupt */
+       nthw_field_t *cfg_reset;/* Cfg Reset */
+       nthw_field_t *cfg_mod_prs;      /* Cfg Module Present */
+       nthw_field_t *cfg_pll_int;      /* Cfg PLL Interrupt */
+       nthw_field_t *cfg_port_rxlos;   /* Emulate Cfg Port RXLOS */
+
+       nthw_field_t *gpio_fld_lp_mode; /* Gpio Low Power Mode */
+       nthw_field_t *gpio_int; /* Gpio Port Interrupt */
+       nthw_field_t *gpio_reset;       /* Gpio Reset */
+       nthw_field_t *gpio_mod_prs;     /* Gpio Module Present */
+       nthw_field_t *gpio_pll_int;     /* Gpio PLL Interrupt */
+       nthw_field_t *gpio_port_rxlos;  /* Emulate Gpio Port RXLOS */
+} gpio_phy_fields_t;
+
+struct nthw_gpio_phy {
+       nthw_fpga_t *mp_fpga;
+       nthw_module_t *mp_mod_gpio_phy;
+       int mn_instance;
+
+       /* Registers */
+       nthw_register_t *mp_reg_config;
+       nthw_register_t *mp_reg_gpio;
+
+       /* Fields */
+       gpio_phy_fields_t mpa_fields[GPIO_PHY_INTERFACES];
+};
+
+typedef struct nthw_gpio_phy nthw_gpio_phy_t;
+typedef struct nthw_gpio_phy nthw_gpio_phy;
+
+nthw_gpio_phy_t *nthw_gpio_phy_new(void);
+int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int 
n_instance);
+
+bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no);
+void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool 
enable);
+void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable);
+
+#endif /* NTHW_GPIO_PHY_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..9243389bef
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/include/nthw_mac_pcs.h
@@ -0,0 +1,263 @@
+/*
+ * 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;
+typedef struct nthw_mac_pcs nthw_mac_pcs;
+
+nthw_mac_pcs_t *nthw_mac_pcs_new(void);
+int nthw_mac_pcs_init(nthw_mac_pcs_t *p, nthw_fpga_t *p_fpga, int n_instance);
+void nthw_mac_pcs_delete(nthw_mac_pcs_t *p);
+
+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);
+bool nthw_mac_pcs_is_ddr3_calib_done(nthw_mac_pcs_t *p);
+/* wrapper - for ease of use */
+void nthw_mac_pcs_tx_host_enable(nthw_mac_pcs_t *p, bool enable);
+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);
+void nthw_mac_pcs_get_status(nthw_mac_pcs_t *p, uint8_t *status);
+bool nthw_mac_pcs_get_hi_ber(nthw_mac_pcs_t *p);
+
+void nthw_mac_pcs_get_link_summary1(nthw_mac_pcs_t *p,
+       uint32_t *p_status,
+       uint32_t *p_status_latch,
+       uint32_t *p_aligned,
+       uint32_t *p_local_fault,
+       uint32_t *p_remote_fault);
+
+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_gpio_phy.c 
b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
new file mode 100644
index 0000000000..c67977341b
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_gpio_phy.c
@@ -0,0 +1,144 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Napatech A/S
+ */
+
+#include "ntlog.h"
+
+#include "nthw_drv.h"
+#include "nthw_register.h"
+
+#include "nthw_gpio_phy.h"
+
+int nthw_gpio_phy_init(nthw_gpio_phy_t *p, nthw_fpga_t *p_fpga, int n_instance)
+{
+       nthw_module_t *p_mod = nthw_fpga_query_module(p_fpga, MOD_GPIO_PHY, 
n_instance);
+
+       if (p == NULL)
+               return p_mod == NULL ? -1 : 0;
+
+       if (p_mod == NULL) {
+               NT_LOG(ERR, NTHW, "%s: GPIO_PHY %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_gpio_phy = p_mod;
+
+       /* Registers */
+       p->mp_reg_config = nthw_module_get_register(p->mp_mod_gpio_phy, 
GPIO_PHY_CFG);
+       p->mp_reg_gpio = nthw_module_get_register(p->mp_mod_gpio_phy, 
GPIO_PHY_GPIO);
+
+       /* PORT-0, config fields */
+       p->mpa_fields[0].cfg_fld_lp_mode =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT0_LPMODE);
+       p->mpa_fields[0].cfg_int =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT0_INT_B);
+       p->mpa_fields[0].cfg_reset =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT0_RESET_B);
+       p->mpa_fields[0].cfg_mod_prs =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT0_MODPRS_B);
+
+       /* PORT-0, Non-mandatory fields (query_field) */
+       p->mpa_fields[0].cfg_pll_int =
+               nthw_register_query_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT0_PLL_INTR);
+       p->mpa_fields[0].cfg_port_rxlos =
+               nthw_register_query_field(p->mp_reg_config, 
GPIO_PHY_CFG_E_PORT0_RXLOS);
+
+       /* PORT-1, config fields */
+       p->mpa_fields[1].cfg_fld_lp_mode =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT1_LPMODE);
+       p->mpa_fields[1].cfg_int =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT1_INT_B);
+       p->mpa_fields[1].cfg_reset =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT1_RESET_B);
+       p->mpa_fields[1].cfg_mod_prs =
+               nthw_register_get_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT1_MODPRS_B);
+
+       /* PORT-1, Non-mandatory fields (query_field) */
+       p->mpa_fields[1].cfg_pll_int =
+               nthw_register_query_field(p->mp_reg_config, 
GPIO_PHY_CFG_PORT1_PLL_INTR);
+       p->mpa_fields[1].cfg_port_rxlos =
+               nthw_register_query_field(p->mp_reg_config, 
GPIO_PHY_CFG_E_PORT1_RXLOS);
+
+       /* PORT-0, gpio fields */
+       p->mpa_fields[0].gpio_fld_lp_mode =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT0_LPMODE);
+       p->mpa_fields[0].gpio_int =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT0_INT_B);
+       p->mpa_fields[0].gpio_reset =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT0_RESET_B);
+       p->mpa_fields[0].gpio_mod_prs =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT0_MODPRS_B);
+
+       /* PORT-0, Non-mandatory fields (query_field) */
+       p->mpa_fields[0].gpio_pll_int =
+               nthw_register_query_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT0_PLL_INTR);
+       p->mpa_fields[0].gpio_port_rxlos =
+               nthw_register_query_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_E_PORT0_RXLOS);
+
+       /* PORT-1, gpio fields */
+       p->mpa_fields[1].gpio_fld_lp_mode =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT1_LPMODE);
+       p->mpa_fields[1].gpio_int =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT1_INT_B);
+       p->mpa_fields[1].gpio_reset =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT1_RESET_B);
+       p->mpa_fields[1].gpio_mod_prs =
+               nthw_register_get_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT1_MODPRS_B);
+
+       /* PORT-1, Non-mandatory fields (query_field) */
+       p->mpa_fields[1].gpio_pll_int =
+               nthw_register_query_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_PORT1_PLL_INTR);
+       p->mpa_fields[1].gpio_port_rxlos =
+               nthw_register_query_field(p->mp_reg_gpio, 
GPIO_PHY_GPIO_E_PORT1_RXLOS);
+
+       nthw_register_update(p->mp_reg_config);
+
+       return 0;
+}
+
+bool nthw_gpio_phy_is_module_present(nthw_gpio_phy_t *p, uint8_t if_no)
+{
+       if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+               assert(false);
+               return false;
+       }
+
+       /* NOTE: This is a negated GPIO PIN "MODPRS_B" */
+       return nthw_field_get_updated(p->mpa_fields[if_no].gpio_mod_prs) == 0U 
? true : false;
+}
+
+void nthw_gpio_phy_set_low_power(nthw_gpio_phy_t *p, uint8_t if_no, bool 
enable)
+{
+       if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+               assert(false);
+               return;
+       }
+
+       if (enable)
+               nthw_field_set_flush(p->mpa_fields[if_no].gpio_fld_lp_mode);
+
+       else
+               nthw_field_clr_flush(p->mpa_fields[if_no].gpio_fld_lp_mode);
+
+       nthw_field_clr_flush(p->mpa_fields[if_no].cfg_fld_lp_mode);     /* 
enable output */
+}
+
+void nthw_gpio_phy_set_reset(nthw_gpio_phy_t *p, uint8_t if_no, bool enable)
+{
+       if (if_no >= ARRAY_SIZE(p->mpa_fields)) {
+               assert(false);
+               return;
+       }
+
+       if (enable)
+               nthw_field_clr_flush(p->mpa_fields[if_no].gpio_reset);
+
+       else
+               nthw_field_set_flush(p->mpa_fields[if_no].gpio_reset);
+
+       nthw_field_clr_flush(p->mpa_fields[if_no].cfg_reset);   /* enable 
output */
+}
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..1e2c9587b5
--- /dev/null
+++ b/drivers/net/ntnic/nthw/core/nthw_mac_pcs.c
@@ -0,0 +1,950 @@
+/*
+ * 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;
+
+nthw_mac_pcs_t *nthw_mac_pcs_new(void)
+{
+       nthw_mac_pcs_t *p = malloc(sizeof(nthw_mac_pcs_t));
+
+       if (p)
+               memset(p, 0, sizeof(nthw_mac_pcs_t));
+
+       return p;
+}
+
+void nthw_mac_pcs_delete(nthw_mac_pcs_t *p)
+{
+       if (p) {
+               memset(p, 0, sizeof(nthw_mac_pcs_t));
+               free(p);
+       }
+}
+
+/*
+ * 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;
+}
+
+/* wrapper - for ease of use */
+void nthw_mac_pcs_tx_host_enable(nthw_mac_pcs_t *p, bool enable)
+{
+       nthw_mac_pcs_set_tx_sel_host(p, enable);
+       nthw_mac_pcs_set_tx_sel_tfg(p, !enable);
+}
+
+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));
+
+#if defined(DEBUG)
+       {
+               uint8_t lane;
+
+               for (lane = 0; lane < c_pcs_lanes; lane++) {
+                       if (lane_bit_errors[lane]) {
+                               NT_LOG(DBG, NTHW, "Port %u: pcsLane %2u: BIP8 
errors: %u\n",
+                                       p->m_port_no, lane, 
lane_bit_errors[lane]);
+                       }
+               }
+       }
+#else
+       (void)c_pcs_lanes;      /* unused - kill warning */
+#endif
+}
+
+void nthw_mac_pcs_get_status(nthw_mac_pcs_t *p, uint8_t *status)
+{
+       *status = nthw_field_get_updated(p->mp_fld_stat_pcs_rx_status) & 0x01;
+}
+
+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 */
+
+#ifdef DEBUG
+
+       if (enable) {
+               NT_LOG(DBG, NTHW, "Port %u: FEC valid: %u\n", p->m_port_no,
+                       nthw_field_get_updated(p->mp_field_fec_stat_valid));
+
+       } else {
+               NT_LOG(DBG, NTHW, "Port %u: FEC bypass: %u\n", p->m_port_no,
+                       nthw_field_get_updated(p->mp_field_fec_stat_bypass));
+       }
+
+#endif
+}
+
+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.44.0


Reply via email to