diff --git a/.mailmap b/.mailmap
index 3843868716..145ce726ba 100644
--- a/.mailmap
+++ b/.mailmap
@@ -885,6 +885,7 @@ Marcin Wojtas <mw@semihalf.com>
 Marcin Zapolski <marcinx.a.zapolski@intel.com>
 Marco Varlese <mvarlese@suse.de>
 Marc Sune <marcdevel@gmail.com> <marc.sune@bisdn.de>
+Marek Pazdan <mpazdan@arista.com>
 Maria Lingemark <maria.lingemark@ericsson.com>
 Mario Carrillo <mario.alfredo.c.arevalo@intel.com>
 Mário Kuka <kuka@cesnet.cz>
diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index b7759e38a8..7fdd134467 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -65,6 +65,7 @@
 #include "cmdline_cman.h"
 #include "cmdline_mtr.h"
 #include "cmdline_tm.h"
+#include "cmdline_settings.h"
 #include "bpf_cmd.h"
 
 static struct cmdline *testpmd_cl;
@@ -255,6 +256,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"show port (port_id) flow_ctrl"
 			"	Show flow control info of a port.\n\n"
 
+			"show port (port_id) link settings"
+			"	Show link settings of a port.\n\n"
+
 			"dump_physmem\n"
 			"    Dumps all physical memory segment layouts\n\n"
 
@@ -661,6 +665,9 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"(max_thresh) (prob_inv)]\n"
 			"    Set congestion management configuration\n\n"
 
+			"set port (port_id) link settings advertising (bitmap_str)"
+			"	Set link advertising settings of a port.\n\n"
+
 			, list_pkt_forwarding_modes()
 		);
 	}
@@ -13219,6 +13226,8 @@ static cmdline_parse_ctx_t builtin_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set_macfwd,
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_set_autoneg,
 	(cmdline_parse_inst_t *)&cmd_link_flow_control_show,
+	(cmdline_parse_inst_t *)&cmd_link_settings_show,
+	(cmdline_parse_inst_t *)&cmd_link_settings_set_advertising,
 	(cmdline_parse_inst_t *)&cmd_priority_flow_control_set,
 	(cmdline_parse_inst_t *)&cmd_queue_priority_flow_control_set,
 	(cmdline_parse_inst_t *)&cmd_config_dcb,
diff --git a/app/test-pmd/cmdline_settings.c b/app/test-pmd/cmdline_settings.c
new file mode 100644
index 0000000000..4db6bd4e9a
--- /dev/null
+++ b/app/test-pmd/cmdline_settings.c
@@ -0,0 +1,516 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Arista Networks, Inc.  All rights reserved.
+ * Arista Networks, Inc. Confidential and Proprietary
+ */
+
+#include <stdio.h>
+#include <rte_ethdev.h>
+
+#include <cmdline_parse_string.h>
+#include <cmdline_parse_num.h>
+
+#include "testpmd.h"
+
+/* *** GET CURRENT ETHERNET LINK SETTINGS *** */
+struct cmd_link_settings_show {
+	cmdline_fixed_string_t show;
+	cmdline_fixed_string_t port;
+	portid_t port_id;
+	cmdline_fixed_string_t link;
+	cmdline_fixed_string_t settings;
+};
+
+cmdline_parse_token_string_t cmd_ls_show_show =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_show,
+				show, "show");
+cmdline_parse_token_string_t cmd_ls_show_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_show,
+				port, "port");
+cmdline_parse_token_num_t cmd_ls_show_portid =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_settings_show,
+				port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_ls_show_link =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_show,
+				link, "link");
+cmdline_parse_token_string_t cmd_ls_show_settings =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_show,
+				settings, "settings");
+
+/* Print link capability flags (supported, advertised or lp_advertised).
+ * Assumes that the corresponding SUPPORTED and ADVERTISED flags are equal.
+ */
+static void dump_link_caps(const char *prefix, const char *an_prefix,
+		const uint32_t *mask, int link_mode_only)
+{
+	static const struct {
+		int same_line; /* print on same line as previous */
+		unsigned int bit_index;
+		const char *name;
+	} mode_defs[] = {
+		{ 0, RTE_LINK_MODE_10baseT_Half_BIT,
+		  "10baseT/Half" },
+		{ 1, RTE_LINK_MODE_10baseT_Full_BIT,
+		  "10baseT/Full" },
+		{ 0, RTE_LINK_MODE_100baseT_Half_BIT,
+		  "100baseT/Half" },
+		{ 1, RTE_LINK_MODE_100baseT_Full_BIT,
+		  "100baseT/Full" },
+		{ 0, RTE_LINK_MODE_100baseT1_Full_BIT,
+		  "100baseT1/Full" },
+		{ 0, RTE_LINK_MODE_1000baseT_Half_BIT,
+		  "1000baseT/Half" },
+		{ 1, RTE_LINK_MODE_1000baseT_Full_BIT,
+		  "1000baseT/Full" },
+		{ 0, RTE_LINK_MODE_1000baseT1_Full_BIT,
+		  "1000baseT1/Full" },
+		{ 0, RTE_LINK_MODE_1000baseKX_Full_BIT,
+		  "1000baseKX/Full" },
+		{ 0, RTE_LINK_MODE_2500baseX_Full_BIT,
+		  "2500baseX/Full" },
+		{ 0, RTE_LINK_MODE_10000baseT_Full_BIT,
+		  "10000baseT/Full" },
+		{ 0, RTE_LINK_MODE_10000baseKX4_Full_BIT,
+		  "10000baseKX4/Full" },
+		{ 0, RTE_LINK_MODE_10000baseKR_Full_BIT,
+		  "10000baseKR/Full" },
+		{ 0, RTE_LINK_MODE_10000baseR_FEC_BIT,
+		  "10000baseR_FEC" },
+		{ 0, RTE_LINK_MODE_20000baseMLD2_Full_BIT,
+		  "20000baseMLD2/Full" },
+		{ 0, RTE_LINK_MODE_20000baseKR2_Full_BIT,
+		  "20000baseKR2/Full" },
+		{ 0, RTE_LINK_MODE_40000baseKR4_Full_BIT,
+		  "40000baseKR4/Full" },
+		{ 0, RTE_LINK_MODE_40000baseCR4_Full_BIT,
+		  "40000baseCR4/Full" },
+		{ 0, RTE_LINK_MODE_40000baseSR4_Full_BIT,
+		  "40000baseSR4/Full" },
+		{ 0, RTE_LINK_MODE_40000baseLR4_Full_BIT,
+		  "40000baseLR4/Full" },
+		{ 0, RTE_LINK_MODE_56000baseKR4_Full_BIT,
+		  "56000baseKR4/Full" },
+		{ 0, RTE_LINK_MODE_56000baseCR4_Full_BIT,
+		  "56000baseCR4/Full" },
+		{ 0, RTE_LINK_MODE_56000baseSR4_Full_BIT,
+		  "56000baseSR4/Full" },
+		{ 0, RTE_LINK_MODE_56000baseLR4_Full_BIT,
+		  "56000baseLR4/Full" },
+		{ 0, RTE_LINK_MODE_25000baseCR_Full_BIT,
+		  "25000baseCR/Full" },
+		{ 0, RTE_LINK_MODE_25000baseKR_Full_BIT,
+		  "25000baseKR/Full" },
+		{ 0, RTE_LINK_MODE_25000baseSR_Full_BIT,
+		  "25000baseSR/Full" },
+		{ 0, RTE_LINK_MODE_50000baseCR2_Full_BIT,
+		  "50000baseCR2/Full" },
+		{ 0, RTE_LINK_MODE_50000baseKR2_Full_BIT,
+		  "50000baseKR2/Full" },
+		{ 0, RTE_LINK_MODE_100000baseKR4_Full_BIT,
+		  "100000baseKR4/Full" },
+		{ 0, RTE_LINK_MODE_100000baseSR4_Full_BIT,
+		  "100000baseSR4/Full" },
+		{ 0, RTE_LINK_MODE_100000baseCR4_Full_BIT,
+		  "100000baseCR4/Full" },
+		{ 0, RTE_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+		  "100000baseLR4_ER4/Full" },
+		{ 0, RTE_LINK_MODE_50000baseSR2_Full_BIT,
+		  "50000baseSR2/Full" },
+		{ 0, RTE_LINK_MODE_1000baseX_Full_BIT,
+		  "1000baseX/Full" },
+		{ 0, RTE_LINK_MODE_10000baseCR_Full_BIT,
+		  "10000baseCR/Full" },
+		{ 0, RTE_LINK_MODE_10000baseSR_Full_BIT,
+		  "10000baseSR/Full" },
+		{ 0, RTE_LINK_MODE_10000baseLR_Full_BIT,
+		  "10000baseLR/Full" },
+		{ 0, RTE_LINK_MODE_10000baseLRM_Full_BIT,
+		  "10000baseLRM/Full" },
+		{ 0, RTE_LINK_MODE_10000baseER_Full_BIT,
+		  "10000baseER/Full" },
+		{ 0, RTE_LINK_MODE_2500baseT_Full_BIT,
+		  "2500baseT/Full" },
+		{ 0, RTE_LINK_MODE_5000baseT_Full_BIT,
+		  "5000baseT/Full" },
+		{ 0, RTE_LINK_MODE_50000baseKR_Full_BIT,
+		  "50000baseKR/Full" },
+		{ 0, RTE_LINK_MODE_50000baseSR_Full_BIT,
+		  "50000baseSR/Full" },
+		{ 0, RTE_LINK_MODE_50000baseCR_Full_BIT,
+		  "50000baseCR/Full" },
+		{ 0, RTE_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+		  "50000baseLR_ER_FR/Full" },
+		{ 0, RTE_LINK_MODE_50000baseDR_Full_BIT,
+		  "50000baseDR/Full" },
+		{ 0, RTE_LINK_MODE_100000baseKR2_Full_BIT,
+		  "100000baseKR2/Full" },
+		{ 0, RTE_LINK_MODE_100000baseSR2_Full_BIT,
+		  "100000baseSR2/Full" },
+		{ 0, RTE_LINK_MODE_100000baseCR2_Full_BIT,
+		  "100000baseCR2/Full" },
+		{ 0, RTE_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
+		  "100000baseLR2_ER2_FR2/Full" },
+		{ 0, RTE_LINK_MODE_100000baseDR2_Full_BIT,
+		  "100000baseDR2/Full" },
+		{ 0, RTE_LINK_MODE_200000baseKR4_Full_BIT,
+		  "200000baseKR4/Full" },
+		{ 0, RTE_LINK_MODE_200000baseSR4_Full_BIT,
+		  "200000baseSR4/Full" },
+		{ 0, RTE_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT,
+		  "200000baseLR4_ER4_FR4/Full" },
+		{ 0, RTE_LINK_MODE_200000baseDR4_Full_BIT,
+		  "200000baseDR4/Full" },
+		{ 0, RTE_LINK_MODE_200000baseCR4_Full_BIT,
+		  "200000baseCR4/Full" },
+	};
+	int indent;
+	int did1;
+	int new_line_pend;
+	int fecreported = 0;
+
+	/* Indent just like the separate functions used to */
+	indent = strlen(prefix) + 14;
+	if (indent < 24)
+		indent = 24;
+
+	fprintf(stdout, "       %s link modes:%*s", prefix,
+		indent - (int)strlen(prefix) - 12, "");
+	did1 = 0;
+	new_line_pend = 0;
+	for (uint32_t i = 0; i < RTE_DIM(mode_defs); i++) {
+		if (did1 && !mode_defs[i].same_line)
+			new_line_pend = 1;
+		if (get_bit(mask, mode_defs[i].bit_index)) {
+			if (new_line_pend) {
+				fprintf(stdout, "\n");
+				fprintf(stdout, "       %*s", indent, "");
+				new_line_pend = 0;
+			}
+			did1++;
+			fprintf(stdout, "%s ", mode_defs[i].name);
+		}
+	}
+	if (did1 == 0)
+		fprintf(stdout, "Not reported");
+	fprintf(stdout, "\n");
+
+	if (!link_mode_only) {
+		fprintf(stdout, "       %s pause frame use: ", prefix);
+		if (get_bit(mask, RTE_LINK_MODE_Pause_BIT)) {
+			fprintf(stdout, "Symmetric");
+			if (get_bit(mask, RTE_LINK_MODE_Asym_Pause_BIT))
+				fprintf(stdout, " Receive-only");
+			fprintf(stdout, "\n");
+		} else {
+			if (get_bit(mask, RTE_LINK_MODE_Asym_Pause_BIT))
+				fprintf(stdout, "Transmit-only\n");
+			else
+				fprintf(stdout, "No\n");
+		}
+
+		fprintf(stdout, "       %s auto-negotiation: ", an_prefix);
+		if (get_bit(mask, RTE_LINK_MODE_Autoneg_BIT))
+			fprintf(stdout, "Yes\n");
+		else
+			fprintf(stdout, "No\n");
+
+		fprintf(stdout, "       %s FEC modes:", prefix);
+		if (get_bit(mask, RTE_LINK_MODE_FEC_NONE_BIT)) {
+			fprintf(stdout, " None");
+			fecreported = 1;
+		}
+		if (get_bit(mask, RTE_LINK_MODE_FEC_BASER_BIT)) {
+			fprintf(stdout, " BaseR");
+			fecreported = 1;
+		}
+		if (get_bit(mask, RTE_LINK_MODE_FEC_RS_BIT)) {
+			fprintf(stdout, " RS");
+			fecreported = 1;
+		}
+		if (!fecreported)
+			fprintf(stdout, " Not reported");
+		fprintf(stdout, "\n");
+	}
+}
+
+static void
+dump_supported(const struct rte_link_settings *link_settings)
+{
+	fprintf(stdout, "       Supported ports: [ ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_TP_BIT))
+		fprintf(stdout, "TP ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_AUI_BIT))
+		fprintf(stdout, "AUI ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_BNC_BIT))
+		fprintf(stdout, "BNC ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_MII_BIT))
+		fprintf(stdout, "MII ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_FIBRE_BIT))
+		fprintf(stdout, "FIBRE ");
+	if (get_bit(link_settings->link_modes.supported,
+		    RTE_LINK_MODE_Backplane_BIT))
+		fprintf(stdout, "Backplane ");
+	fprintf(stdout, "]\n");
+
+	dump_link_caps("Supported", "Supports",
+		       link_settings->link_modes.supported, 0);
+}
+
+
+
+static void
+cmd_link_settings_show_parsed(void *parsed_result,
+			      __rte_unused struct cmdline *cl,
+			      __rte_unused void *data)
+{
+	struct cmd_link_settings_show *res = parsed_result;
+	static const char *info_border = "*********************";
+	struct rte_link_settings link_settings;
+	int ret;
+
+	memset(&link_settings, 0x00, sizeof(link_settings));
+	ret = rte_eth_dev_get_link_settings(res->port_id, &link_settings);
+	if (ret != 0) {
+		fprintf(stderr,
+			"Failed to get current link settings: err = %d\n",
+			ret);
+		return;
+	}
+
+	printf("\n%s Link settings for port %-2d %s\n",
+		info_border, res->port_id, info_border);
+
+	dump_supported(&link_settings);
+	dump_link_caps("Advertised", "Advertised",
+			link_settings.link_modes.advertising, 0);
+
+	bool show_lp_advertising = false;
+	for (uint32_t idx = 0; idx < RTE_DIM(link_settings.link_modes.lp_advertising); idx++) {
+		if (link_settings.link_modes.lp_advertising[idx] != 0) {
+			show_lp_advertising = true;
+			break;
+		}
+	}
+
+	if (show_lp_advertising) {
+		dump_link_caps("Link partner advertised",
+				"Link partner advertised",
+				link_settings.link_modes.lp_advertising, 0);
+	}
+
+	fprintf(stdout, "       Speed: ");
+	if (link_settings.base.link.link_speed == 0
+	    || link_settings.base.link.link_speed == (uint16_t)(-1)
+	    || link_settings.base.link.link_speed == (uint32_t)(-1))
+		fprintf(stdout, "Unknown!\n");
+	else
+		fprintf(stdout, "%uMb/s\n", link_settings.base.link.link_speed);
+
+	fprintf(stdout, "       Duplex: ");
+	switch (link_settings.base.link.link_duplex) {
+	case RTE_ETH_LINK_HALF_DUPLEX:
+		fprintf(stdout, "Half\n");
+		break;
+	case RTE_ETH_LINK_FULL_DUPLEX:
+		fprintf(stdout, "Full\n");
+		break;
+	default:
+		fprintf(stdout, "Unknown! (%i)\n", link_settings.base.link.link_duplex);
+		break;
+	};
+
+	fprintf(stdout, "       Port: ");
+	switch (link_settings.base.port) {
+	case RTE_PORT_TP:
+		fprintf(stdout, "Twisted Pair\n");
+		break;
+	case RTE_PORT_AUI:
+		fprintf(stdout, "AUI\n");
+		break;
+	case RTE_PORT_BNC:
+		fprintf(stdout, "BNC\n");
+		break;
+	case RTE_PORT_MII:
+		fprintf(stdout, "MII\n");
+		break;
+	case RTE_PORT_FIBRE:
+		fprintf(stdout, "FIBRE\n");
+		break;
+	case RTE_PORT_DA:
+		fprintf(stdout, "Direct Attach Copper\n");
+		break;
+	case RTE_PORT_NONE:
+		fprintf(stdout, "None\n");
+		break;
+	case RTE_PORT_OTHER:
+		fprintf(stdout, "Other\n");
+		break;
+	default:
+		fprintf(stdout, "Unknown! (%i)\n", link_settings.base.port);
+		break;
+	};
+
+	fprintf(stdout, "       PHYAD: %d\n", link_settings.base.phy_address);
+
+	fprintf(stdout, "       Auto-negotiation: %s\n",
+		(link_settings.base.link.link_autoneg == RTE_AUTONEG_DISABLE) ?
+		"off" : "on");
+
+	if (link_settings.base.port == RTE_PORT_TP) {
+		fprintf(stdout, "       MDI-X: ");
+		if (link_settings.base.eth_tp_mdix_ctrl == RTE_TP_MDI) {
+			fprintf(stdout, "off (forced)\n");
+		} else if (link_settings.base.eth_tp_mdix_ctrl
+			   == RTE_TP_MDI_X) {
+			fprintf(stdout, "on (forced)\n");
+		} else {
+			switch (link_settings.base.eth_tp_mdix) {
+			case RTE_TP_MDI:
+				fprintf(stdout, "off");
+				break;
+			case RTE_TP_MDI_X:
+				fprintf(stdout, "on");
+				break;
+			default:
+				fprintf(stdout, "Unknown");
+				break;
+			}
+			if (link_settings.base.eth_tp_mdix_ctrl
+			    == RTE_TP_MDI_AUTO)
+				fprintf(stdout, " (auto)");
+			fprintf(stdout, "\n");
+		}
+	}
+}
+
+cmdline_parse_inst_t cmd_link_settings_show = {
+	.f = cmd_link_settings_show_parsed,
+	.data = NULL,
+	.help_str = "show port <port_id> link settings",
+	.tokens = {
+		(void *)&cmd_ls_show_show,
+		(void *)&cmd_ls_show_port,
+		(void *)&cmd_ls_show_portid,
+		(void *)&cmd_ls_show_link,
+		(void *)&cmd_ls_show_settings,
+		NULL,
+	},
+};
+
+static int parse_hex_u32_bitmap(const char *s,
+				unsigned int nbits, uint32_t *result)
+{
+	const unsigned int nwords = RTE_DIV_ROUND_UP(nbits, 32);
+	size_t slen = strlen(s);
+	size_t i;
+
+	/* ignore optional '0x' prefix */
+	if ((slen > 2) && (strncasecmp(s, "0x", 2) == 0)) {
+		slen -= 2;
+		s += 2;
+	}
+
+	if (slen > 8 * nwords)  /* up to 2 digits per byte */
+		return -1;
+
+	memset(result, 0, 4 * nwords);
+	for (i = 0; i < slen; ++i) {
+		const unsigned int shift = (slen - 1 - i) * 4;
+		uint32_t *dest = &result[shift / 32];
+		uint32_t nibble;
+
+		if ('a' <= s[i] && s[i] <= 'f')
+			nibble = 0xa + (s[i] - 'a');
+		else if ('A' <= s[i] && s[i] <= 'F')
+			nibble = 0xa + (s[i] - 'A');
+		else if ('0' <= s[i] && s[i] <= '9')
+			nibble = (s[i] - '0');
+		else
+			return -1;
+
+		*dest |= (nibble << (shift % 32));
+	}
+
+	return 0;
+}
+
+/* *** SET ETHERNET LINK SETTINGS *** */
+struct cmd_link_settings_set {
+	cmdline_fixed_string_t set;
+	cmdline_fixed_string_t port;
+	portid_t port_id;
+	cmdline_fixed_string_t link;
+	cmdline_fixed_string_t settings;
+	cmdline_fixed_string_t advertising;
+	cmdline_multi_string_t bitmap_str;
+};
+
+cmdline_parse_token_string_t cmd_ls_set_set =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				set, "set");
+cmdline_parse_token_string_t cmd_ls_set_port =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				port, "port");
+cmdline_parse_token_num_t cmd_ls_set_portid =
+	TOKEN_NUM_INITIALIZER(struct cmd_link_settings_set,
+				port_id, RTE_UINT16);
+cmdline_parse_token_string_t cmd_ls_set_link =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				link, "link");
+cmdline_parse_token_string_t cmd_ls_set_settings =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				settings, "settings");
+cmdline_parse_token_string_t cmd_ls_set_advertising =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				advertising, "advertising");
+cmdline_parse_token_string_t cmd_ls_set_bitmap_str =
+	TOKEN_STRING_INITIALIZER(struct cmd_link_settings_set,
+				bitmap_str, TOKEN_STRING_MULTI);
+
+static void
+cmd_link_settings_set_advertising_parsed(void *parsed_result,
+			      __rte_unused struct cmdline *cl,
+			      __rte_unused void *data)
+{
+	struct cmd_link_settings_set *res = parsed_result;
+	struct rte_link_settings link_settings;
+	int ret;
+
+	if (parse_hex_u32_bitmap(res->bitmap_str,
+				__RTE_LINK_MODE_MASK_NBITS,
+				link_settings.link_modes.advertising)) {
+		fprintf(stderr, "cannot parse bitmap_str\n");
+		return;
+
+	}
+
+	link_settings.base.link.link_autoneg = RTE_AUTONEG_ENABLE;
+
+	ret = rte_eth_dev_set_link_settings(res->port_id, &link_settings);
+	if (ret != 0) {
+		fprintf(stderr,
+			"Failed to set link settings: err = %d\n",
+			ret);
+	}
+}
+
+cmdline_parse_inst_t cmd_link_settings_set_advertising = {
+	.f = cmd_link_settings_set_advertising_parsed,
+	.data = NULL,
+	.help_str = "set port <port_id> link settings advertising <bitmap_str>"
+		" Bitmap_str - bitmap respresentation of advertising speeds",
+	.tokens = {
+		(void *)&cmd_ls_set_set,
+		(void *)&cmd_ls_set_port,
+		(void *)&cmd_ls_set_portid,
+		(void *)&cmd_ls_set_link,
+		(void *)&cmd_ls_set_settings,
+		(void *)&cmd_ls_set_advertising,
+		(void *)&cmd_ls_set_bitmap_str,
+		NULL,
+	},
+};
diff --git a/app/test-pmd/cmdline_settings.h b/app/test-pmd/cmdline_settings.h
new file mode 100644
index 0000000000..1bcd75f7ef
--- /dev/null
+++ b/app/test-pmd/cmdline_settings.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2024 Arista Networks, Inc.  All rights reserved.
+ * Arista Networks, Inc. Confidential and Proprietary
+ */
+
+#ifndef _CMDLINE_SETTINGS_H
+#define _CMDLINE_SETTINGS_H
+
+/* Link settings CLI */
+extern cmdline_parse_inst_t cmd_link_settings_show;
+extern cmdline_parse_inst_t cmd_link_settings_set_advertising;
+
+#endif /* _CMDLINE_SETTINGS_H */
+
diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build
index 719f875be0..7aadc2ec42 100644
--- a/app/test-pmd/meson.build
+++ b/app/test-pmd/meson.build
@@ -11,6 +11,7 @@ sources = files(
         'cmdline_flow.c',
         'cmdline_mtr.c',
         'cmdline_tm.c',
+        'cmdline_settings.c',
         'cmd_flex_item.c',
         'config.c',
         'csumonly.c',
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 0afae7d771..1873b45d23 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -1187,6 +1187,7 @@ void eth_set_allmulticast_mode(uint16_t port, int enable);
 int eth_link_get_nowait_print_err(uint16_t port_id, struct rte_eth_link *link);
 int eth_macaddr_get_print_err(uint16_t port_id,
 			struct rte_ether_addr *mac_addr);
+int get_bit(const uint32_t *ptr, uint32_t pos);
 
 /* Functions to display the set of MAC addresses added to a port*/
 void show_macs(portid_t port_id);
diff --git a/app/test-pmd/util.c b/app/test-pmd/util.c
index bf9b639d95..4d26776cc7 100644
--- a/app/test-pmd/util.c
+++ b/app/test-pmd/util.c
@@ -17,6 +17,8 @@
 
 #define MAX_STRING_LEN 8192
 
+#define UINT_32_BIT_WIDTH (CHAR_BIT * sizeof(uint32_t))
+
 #define MKDUMPSTR(buf, buf_size, cur_len, ...) \
 do { \
 	if (cur_len >= buf_size) \
@@ -525,3 +527,11 @@ eth_macaddr_get_print_err(uint16_t port_id, struct rte_ether_addr *mac_addr)
 
 	return ret;
 }
+
+int
+get_bit(const uint32_t *ptr, uint32_t pos)
+{
+	uint32_t byte_idx = pos / UINT_32_BIT_WIDTH;
+	uint32_t bit_idx = pos % UINT_32_BIT_WIDTH;
+	return (ptr[byte_idx] >> bit_idx) & 0x1;
+}
diff --git a/drivers/net/igc/igc_ethdev.c b/drivers/net/igc/igc_ethdev.c
index 87d7f7caa0..af11a6a3dd 100644
--- a/drivers/net/igc/igc_ethdev.c
+++ b/drivers/net/igc/igc_ethdev.c
@@ -91,6 +91,8 @@
 uint64_t igc_tx_timestamp_dynflag;
 int igc_tx_timestamp_dynfield_offset = -1;
 
+#define IGC_32_BIT_WIDTH (CHAR_BIT * sizeof(uint32_t))
+
 static const struct rte_eth_desc_lim rx_desc_lim = {
 	.nb_max = IGC_MAX_RXD,
 	.nb_min = IGC_MIN_RXD,
@@ -273,6 +275,10 @@ static int eth_igc_timesync_read_time(struct rte_eth_dev *dev,
 static int eth_igc_timesync_write_time(struct rte_eth_dev *dev,
 				   const struct timespec *timestamp);
 static int eth_igc_read_clock(struct rte_eth_dev *dev, uint64_t *clock);
+static int eth_igc_get_link_settings(struct rte_eth_dev *dev,
+				struct rte_link_settings *link_settings);
+static int eth_igc_set_link_settings(struct rte_eth_dev *dev,
+				const struct rte_link_settings *link_settings);
 
 static const struct eth_dev_ops eth_igc_ops = {
 	.dev_configure		= eth_igc_configure,
@@ -334,6 +340,8 @@ static const struct eth_dev_ops eth_igc_ops = {
 	.timesync_read_time	= eth_igc_timesync_read_time,
 	.timesync_write_time	= eth_igc_timesync_write_time,
 	.read_clock             = eth_igc_read_clock,
+	.get_link_settings      = eth_igc_get_link_settings,
+	.set_link_settings      = eth_igc_set_link_settings,
 };
 
 /*
@@ -532,8 +540,8 @@ eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete)
 			}
 		}
 	} else {
-		link.link_speed = 0;
-		link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+		link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+		link.link_duplex = RTE_ETH_LINK_UNKNOWN_DUPLEX;
 		link.link_status = RTE_ETH_LINK_DOWN;
 		link.link_autoneg = RTE_ETH_LINK_FIXED;
 	}
@@ -2876,6 +2884,223 @@ eth_igc_read_clock(__rte_unused struct rte_eth_dev *dev, uint64_t *clock)
 	return 0;
 }
 
+static inline void
+igc_set_bit(uint32_t *ptr, uint32_t pos)
+{
+	uint32_t byte_idx = pos / IGC_32_BIT_WIDTH;
+	uint32_t bit_idx = pos % IGC_32_BIT_WIDTH;
+	uint32_t tmp;
+
+	tmp = ptr[byte_idx];
+	tmp &= ~(RTE_BIT32(bit_idx));
+	tmp |= RTE_BIT32(bit_idx);
+	ptr[byte_idx] = tmp;
+}
+
+static inline uint8_t
+igc_get_bit(const uint32_t *ptr, uint32_t pos)
+{
+	uint32_t byte_idx = pos / IGC_32_BIT_WIDTH;
+	uint32_t bit_idx = pos % IGC_32_BIT_WIDTH;
+
+	return (ptr[byte_idx] >> bit_idx) & 0x1;
+}
+
+static int eth_igc_get_link_settings(struct rte_eth_dev *dev,
+				     struct rte_link_settings *link_settings)
+{
+	s32 ret_val = IGC_SUCCESS;
+	s32 link_check = 0;
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	uint32_t *supported = link_settings->link_modes.supported;
+	uint32_t *advertising = link_settings->link_modes.advertising;
+	uint32_t *lp_advertising = link_settings->link_modes.lp_advertising;
+
+	memset(supported, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+	memset(advertising, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+	memset(lp_advertising, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+
+	igc_set_bit(supported, RTE_LINK_MODE_10baseT_Half_BIT);
+	igc_set_bit(supported, RTE_LINK_MODE_10baseT_Full_BIT);
+	igc_set_bit(supported, RTE_LINK_MODE_100baseT_Half_BIT);
+	igc_set_bit(supported, RTE_LINK_MODE_100baseT_Full_BIT);
+	igc_set_bit(supported, RTE_LINK_MODE_1000baseT_Full_BIT);
+	igc_set_bit(supported, RTE_LINK_MODE_2500baseT_Full_BIT);
+
+	/* twisted pair */
+	link_settings->base.port = RTE_PORT_TP;
+	/* MDIO address of PHY (transceiver) */
+	link_settings->base.phy_address = hw->phy.addr;
+	igc_set_bit(supported, RTE_LINK_MODE_TP_BIT);
+	igc_set_bit(advertising, RTE_LINK_MODE_TP_BIT);
+
+	if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
+		igc_set_bit(advertising, RTE_LINK_MODE_10baseT_Half_BIT);
+	if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
+		igc_set_bit(advertising, RTE_LINK_MODE_10baseT_Full_BIT);
+	if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
+		igc_set_bit(advertising, RTE_LINK_MODE_100baseT_Half_BIT);
+	if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
+		igc_set_bit(advertising, RTE_LINK_MODE_100baseT_Full_BIT);
+	if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
+		igc_set_bit(advertising, RTE_LINK_MODE_1000baseT_Full_BIT);
+	if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
+		igc_set_bit(advertising, RTE_LINK_MODE_2500baseT_Full_BIT);
+
+	/* set autoneg settings */
+	if (hw->mac.autoneg == 1) {
+		igc_set_bit(supported, RTE_LINK_MODE_Autoneg_BIT);
+		igc_set_bit(advertising, RTE_LINK_MODE_Autoneg_BIT);
+		link_settings->base.link.link_autoneg = RTE_AUTONEG_ENABLE;
+	} else {
+		link_settings->base.link.link_autoneg = RTE_AUTONEG_DISABLE;
+	}
+
+	/* Set pause flow control settings */
+	igc_set_bit(supported, RTE_LINK_MODE_Pause_BIT);
+
+	switch (hw->fc.requested_mode) {
+	case igc_fc_full:
+		igc_set_bit(advertising, RTE_LINK_MODE_Pause_BIT);
+		break;
+	case igc_fc_rx_pause:
+		igc_set_bit(advertising, RTE_LINK_MODE_Pause_BIT);
+		igc_set_bit(advertising, RTE_LINK_MODE_Asym_Pause_BIT);
+		break;
+	case igc_fc_tx_pause:
+		igc_set_bit(advertising, RTE_LINK_MODE_Asym_Pause_BIT);
+		break;
+	default:
+		break;
+	}
+
+	hw->mac.get_link_status = 1;
+	/* Read the real link status */
+	switch (hw->phy.media_type) {
+	case igc_media_type_copper:
+		/* Do the work to read phy */
+		igc_check_for_link(hw);
+		link_check = !hw->mac.get_link_status;
+		break;
+	case igc_media_type_fiber:
+		igc_check_for_link(hw);
+		link_check = (IGC_READ_REG(hw, IGC_STATUS) &
+			      IGC_STATUS_LU);
+		break;
+	case igc_media_type_internal_serdes:
+		igc_check_for_link(hw);
+		link_check = hw->mac.serdes_has_link;
+		break;
+	default:
+		break;
+	}
+
+	/* Now we check if a transition has happened */
+	if (link_check) {
+		uint16_t duplex, speed;
+		hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
+		link_settings->base.link.link_duplex = (duplex == FULL_DUPLEX) ?
+				RTE_ETH_LINK_FULL_DUPLEX :
+				RTE_ETH_LINK_HALF_DUPLEX;
+		link_settings->base.link.link_speed = speed;
+		link_settings->base.link.link_status = RTE_ETH_LINK_UP;
+		link_settings->base.link.link_autoneg = !(dev->data->dev_conf.link_speeds &
+				RTE_ETH_LINK_SPEED_FIXED);
+		if (speed == SPEED_2500) {
+			uint32_t tipg = IGC_READ_REG(hw, IGC_TIPG);
+			if ((tipg & IGC_TIPG_IPGT_MASK) != 0x0b) {
+				tipg &= ~IGC_TIPG_IPGT_MASK;
+				tipg |= 0x0b;
+				IGC_WRITE_REG(hw, IGC_TIPG, tipg);
+			}
+		}
+	} else {
+		link_settings->base.link.link_speed = 0;
+		link_settings->base.link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+		link_settings->base.link.link_status = RTE_ETH_LINK_DOWN;
+		link_settings->base.link.link_autoneg = RTE_ETH_LINK_FIXED;
+	}
+
+	/* MDI-X => 2; MDI =>1; Invalid =>0 */
+	if (hw->phy.media_type == igc_media_type_copper)
+		link_settings->base.eth_tp_mdix = hw->phy.is_mdix ? RTE_TP_MDI_X :
+						      RTE_TP_MDI;
+	else
+		link_settings->base.eth_tp_mdix = RTE_TP_MDI_INVALID;
+
+	if (hw->phy.mdix == 0) /* AUTO_ALL_MODES */
+		link_settings->base.eth_tp_mdix_ctrl = RTE_TP_MDI_AUTO;
+	else
+		link_settings->base.eth_tp_mdix_ctrl = hw->phy.mdix;
+
+	return ret_val;
+}
+
+static int eth_igc_set_link_settings(struct rte_eth_dev *dev,
+			 const struct rte_link_settings *link_settings)
+{
+	struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
+	struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev);
+	const uint32_t *advertising = link_settings->link_modes.advertising;
+	int ret = IGC_SUCCESS;
+
+	if (igc_check_reset_block(hw)) {
+		PMD_DRV_LOG(ERR, "Cannot change link characteristics when reset is active\n");
+		return -EINVAL;
+	}
+
+	/* MDI setting is only allowed when autoneg enabled because
+	 * some hardware doesn't allow MDI setting when speed or
+	 * duplex is forced.
+	 */
+	if (link_settings->base.eth_tp_mdix_ctrl) {
+		if (link_settings->base.eth_tp_mdix_ctrl != RTE_TP_MDI_AUTO &&
+		    link_settings->base.link.link_autoneg != RTE_AUTONEG_ENABLE) {
+			PMD_DRV_LOG(ERR, "Forcing MDI/MDI-X state is not supported "
+					 "when link speed and/or duplex are forced\n");
+			return -EINVAL;
+		}
+	}
+
+	/* All modes for igc but one fits into first byte of bitmask */
+	uint32_t igc_advertising = link_settings->link_modes.advertising[0];
+	/* Converting to u32 drops RTE_LINK_MODE_2500baseT_Full_BIT.
+	 * We have to check this and convert it to ADVERTISE_2500_FULL
+	 * (aka RTE_LINK_MODE_2500baseX_Full_BIT) explicitly.
+	 */
+	if (igc_get_bit(advertising, RTE_LINK_MODE_2500baseT_Full_BIT))
+		igc_advertising |= ADVERTISE_2500_FULL;
+
+	if (link_settings->base.link.link_autoneg == RTE_AUTONEG_ENABLE) {
+		hw->mac.autoneg = 1;
+		hw->phy.autoneg_advertised = igc_advertising;
+		hw->fc.requested_mode = igc_fc_default;
+	} else {
+		PMD_DRV_LOG(INFO, "Force mode currently not supported\n");
+	}
+
+	/* MDI-X => 2; MDI => 1; Auto => 3 */
+	if (link_settings->base.eth_tp_mdix_ctrl) {
+		/* fix up the value for auto (3 => 0) as zero is mapped
+		 * internally to auto
+		 */
+		if (link_settings->base.eth_tp_mdix_ctrl == RTE_TP_MDI_AUTO)
+			hw->phy.mdix = 0; /* AUTO_ALL_MODES */
+		else
+			hw->phy.mdix = link_settings->base.eth_tp_mdix_ctrl;
+	}
+
+	/* reset the link */
+	if (!adapter->stopped) {
+		eth_igc_set_link_down(dev);
+		eth_igc_set_link_up(dev);
+	} else {
+		eth_igc_reset(dev);
+	}
+
+	return ret;
+}
+
 static int
 eth_igc_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
 	struct rte_pci_device *pci_dev)
diff --git a/drivers/net/ixgbe/base/ixgbe_phy.h b/drivers/net/ixgbe/base/ixgbe_phy.h
index ceefbb3e68..f4d22951be 100644
--- a/drivers/net/ixgbe/base/ixgbe_phy.h
+++ b/drivers/net/ixgbe/base/ixgbe_phy.h
@@ -125,6 +125,13 @@
 /* SFP+ SFF-8472 Compliance */
 #define IXGBE_SFF_SFF_8472_UNSUP	0x00
 
+#define IXGBE_SPEED_10000	10000
+
+/* Duplex, half or full. */
+#define IXGBE_DUPLEX_HALF	0x00
+#define IXGBE_DUPLEX_FULL	0x01
+#define IXGBE_DUPLEX_UNKNOWN	0xff
+
 s32 ixgbe_init_phy_ops_generic(struct ixgbe_hw *hw);
 bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr);
 enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id);
diff --git a/drivers/net/ixgbe/ixgbe_ethdev.c b/drivers/net/ixgbe/ixgbe_ethdev.c
index c61c52b296..2fd4a34b55 100644
--- a/drivers/net/ixgbe/ixgbe_ethdev.c
+++ b/drivers/net/ixgbe/ixgbe_ethdev.c
@@ -94,6 +94,7 @@
 #define IXGBE_4_BIT_MASK   RTE_LEN2MASK(IXGBE_4_BIT_WIDTH, uint8_t)
 #define IXGBE_8_BIT_WIDTH  CHAR_BIT
 #define IXGBE_8_BIT_MASK   UINT8_MAX
+#define IXGBE_32_BIT_WIDTH (CHAR_BIT * sizeof(uint32_t))
 
 #define IXGBEVF_PMD_NAME "rte_ixgbevf_pmd" /* PMD name */
 
@@ -130,6 +131,8 @@
 
 #define IXGBE_DEVARG_FIBER_SDP3_NOT_TX_DISABLE	"fiber_sdp3_no_tx_disable"
 
+#define IXGBE_ISBACKPLANE(type)             ((type) == ixgbe_media_type_backplane)
+
 static const char * const ixgbe_valid_arguments[] = {
 	IXGBE_DEVARG_FIBER_SDP3_NOT_TX_DISABLE,
 	NULL
@@ -353,6 +356,10 @@ static int ixgbe_dev_udp_tunnel_port_add(struct rte_eth_dev *dev,
 					 struct rte_eth_udp_tunnel *udp_tunnel);
 static int ixgbe_dev_udp_tunnel_port_del(struct rte_eth_dev *dev,
 					 struct rte_eth_udp_tunnel *udp_tunnel);
+static int ixgbe_get_link_settings(struct rte_eth_dev *dev,
+				   struct rte_link_settings *link_settings);
+static int ixgbe_set_link_settings(struct rte_eth_dev *dev,
+				   const struct rte_link_settings *link_settings);
 static int ixgbe_filter_restore(struct rte_eth_dev *dev);
 static void ixgbe_l2_tunnel_conf(struct rte_eth_dev *dev);
 static int ixgbe_wait_for_link_up(struct ixgbe_hw *hw);
@@ -564,6 +571,8 @@ static const struct eth_dev_ops ixgbe_eth_dev_ops = {
 	.tm_ops_get           = ixgbe_tm_ops_get,
 	.tx_done_cleanup      = ixgbe_dev_tx_done_cleanup,
 	.get_monitor_addr     = ixgbe_get_monitor_addr,
+	.get_link_settings    = ixgbe_get_link_settings,
+	.set_link_settings    = ixgbe_set_link_settings,
 };
 
 /*
@@ -7791,6 +7800,392 @@ ixgbe_dev_udp_tunnel_port_del(struct rte_eth_dev *dev,
 	return ret;
 }
 
+static inline void
+ixgbe_set_bit(uint32_t *ptr, uint32_t pos)
+{
+	uint32_t byte_idx = pos / IXGBE_32_BIT_WIDTH;
+	uint32_t bit_idx = pos % IXGBE_32_BIT_WIDTH;
+	uint32_t tmp;
+
+	tmp = ptr[byte_idx];
+	tmp &= ~(1 << bit_idx);
+	tmp |= 1 << bit_idx;
+	ptr[byte_idx] = tmp;
+}
+
+static inline uint8_t
+ixgbe_get_bit(const uint32_t *ptr, uint32_t pos)
+{
+	uint32_t byte_idx = pos / IXGBE_32_BIT_WIDTH;
+	uint32_t bit_idx = pos % IXGBE_32_BIT_WIDTH;
+
+	return (ptr[byte_idx] >> bit_idx) & 0x1;
+}
+
+
+static inline bool
+ixgbe_is_subset(const uint32_t *bitmap1, const uint32_t *bitmap2,
+		uint32_t nbits)
+{
+	uint32_t full_bytes_cnt = nbits / IXGBE_32_BIT_WIDTH;
+	uint32_t last_byte_bit_idx = nbits % IXGBE_32_BIT_WIDTH;
+
+	for (uint32_t idx = 0; idx < full_bytes_cnt; idx++) {
+		if (bitmap1[idx] & ~bitmap2[idx])
+			return false;
+	}
+
+	uint32_t last_chunk_mask = ((1ULL << last_byte_bit_idx) - 1) |
+				    (1ULL << last_byte_bit_idx);
+	if ((bitmap1[full_bytes_cnt] & ~bitmap2[full_bytes_cnt]) & last_chunk_mask)
+		return false;
+
+	return true;
+}
+
+static void ixgbe_set_supported_10gtypes(struct ixgbe_hw *hw,
+					 uint32_t *bitmap)
+{
+	if (!IXGBE_ISBACKPLANE(hw->phy.media_type)) {
+		ixgbe_set_bit(bitmap, RTE_LINK_MODE_10000baseT_Full_BIT);
+		return;
+	}
+
+	switch (hw->device_id) {
+	case IXGBE_DEV_ID_82598:
+	case IXGBE_DEV_ID_82599_KX4:
+	case IXGBE_DEV_ID_82599_KX4_MEZZ:
+	case IXGBE_DEV_ID_X550EM_X_KX4:
+		ixgbe_set_bit(bitmap, RTE_LINK_MODE_10000baseKX4_Full_BIT);
+		break;
+	case IXGBE_DEV_ID_82598_BX:
+	case IXGBE_DEV_ID_82599_KR:
+	case IXGBE_DEV_ID_X550EM_X_KR:
+	case IXGBE_DEV_ID_X550EM_X_XFI:
+		ixgbe_set_bit(bitmap, RTE_LINK_MODE_10000baseKR_Full_BIT);
+		break;
+	default:
+		ixgbe_set_bit(bitmap, RTE_LINK_MODE_10000baseKX4_Full_BIT);
+		ixgbe_set_bit(bitmap, RTE_LINK_MODE_10000baseKR_Full_BIT);
+		break;
+	}
+}
+
+static int ixgbe_get_link_settings(struct rte_eth_dev *dev,
+				   struct rte_link_settings *link_settings)
+{
+	int ret_val = IXGBE_SUCCESS;
+	int wait = 1;
+	bool link_up;
+	bool autoneg;
+	uint32_t esdp_reg;
+	ixgbe_link_speed link_speed = IXGBE_LINK_SPEED_UNKNOWN;
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	struct ixgbe_adapter *ad = dev->data->dev_private;
+	uint32_t *supported = link_settings->link_modes.supported;
+	uint32_t *advertising = link_settings->link_modes.advertising;
+	uint32_t *lp_advertising = link_settings->link_modes.lp_advertising;
+
+	memset(supported, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+	memset(advertising, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+	memset(lp_advertising, 0x00, __RTE_LINK_MODE_MASK_NU32 * sizeof(uint32_t));
+
+	/* Get supported speeds */
+	ret_val = ixgbe_get_link_capabilities(hw, &link_speed, &autoneg);
+	if (ret_val != IXGBE_SUCCESS)
+		return ret_val;
+
+	if (link_speed & IXGBE_LINK_SPEED_10GB_FULL) {
+		ixgbe_set_supported_10gtypes(hw, supported);
+		ixgbe_set_supported_10gtypes(hw, advertising);
+	}
+
+	if (link_speed & IXGBE_LINK_SPEED_5GB_FULL)
+		ixgbe_set_bit(supported, RTE_LINK_MODE_5000baseT_Full_BIT);
+
+	if (link_speed & IXGBE_LINK_SPEED_2_5GB_FULL)
+		ixgbe_set_bit(supported, RTE_LINK_MODE_2500baseT_Full_BIT);
+
+	if (link_speed & IXGBE_LINK_SPEED_1GB_FULL) {
+		if (IXGBE_ISBACKPLANE(hw->phy.media_type)) {
+			ixgbe_set_bit(supported, RTE_LINK_MODE_1000baseKX_Full_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_1000baseKX_Full_BIT);
+		} else {
+			ixgbe_set_bit(supported, RTE_LINK_MODE_1000baseT_Full_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_1000baseT_Full_BIT);
+		}
+	}
+	if (link_speed & IXGBE_LINK_SPEED_100_FULL) {
+		ixgbe_set_bit(supported, RTE_LINK_MODE_100baseT_Full_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_100baseT_Full_BIT);
+	}
+	if (link_speed & IXGBE_LINK_SPEED_10_FULL) {
+		ixgbe_set_bit(supported, RTE_LINK_MODE_10baseT_Full_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_10baseT_Full_BIT);
+	}
+
+	/* set the advertised speeds */
+	if (hw->phy.autoneg_advertised) {
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10_FULL)
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_10baseT_Full_BIT);
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_100_FULL)
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_100baseT_Full_BIT);
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_10GB_FULL)
+			ixgbe_set_supported_10gtypes(hw, advertising);
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_1GB_FULL) {
+			if (ixgbe_get_bit(supported, RTE_LINK_MODE_1000baseKX_Full_BIT))
+				ixgbe_set_bit(advertising,
+					      RTE_LINK_MODE_1000baseKX_Full_BIT);
+			else
+				ixgbe_set_bit(advertising,
+					      RTE_LINK_MODE_1000baseT_Full_BIT);
+		}
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_5GB_FULL)
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_5000baseT_Full_BIT);
+		if (hw->phy.autoneg_advertised & IXGBE_LINK_SPEED_2_5GB_FULL)
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_2500baseT_Full_BIT);
+	} else {
+		if (hw->phy.multispeed_fiber && !autoneg) {
+			if (link_speed & IXGBE_LINK_SPEED_10GB_FULL)
+				ixgbe_set_bit(advertising,
+					      RTE_LINK_MODE_10000baseT_Full_BIT);
+		}
+	}
+
+	if (autoneg) {
+		ixgbe_set_bit(supported, RTE_LINK_MODE_Autoneg_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Autoneg_BIT);
+		link_settings->base.link.link_autoneg = RTE_AUTONEG_ENABLE;
+	} else {
+		link_settings->base.link.link_autoneg = RTE_AUTONEG_DISABLE;
+	}
+
+	switch (hw->phy.type) {
+	case ixgbe_phy_tn:
+	case ixgbe_phy_aq:
+	case ixgbe_phy_x550em_ext_t:
+	case ixgbe_phy_fw:
+	case ixgbe_phy_cu_unknown:
+		ixgbe_set_bit(supported, RTE_LINK_MODE_TP_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_TP_BIT);
+		link_settings->base.port = RTE_PORT_TP;
+		break;
+	case ixgbe_phy_qt:
+		ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+		link_settings->base.port = RTE_PORT_FIBRE;
+		break;
+	case ixgbe_phy_nl:
+	case ixgbe_phy_sfp_passive_tyco:
+	case ixgbe_phy_sfp_passive_unknown:
+	case ixgbe_phy_sfp_ftl:
+	case ixgbe_phy_sfp_avago:
+	case ixgbe_phy_sfp_intel:
+	case ixgbe_phy_sfp_unknown:
+	case ixgbe_phy_qsfp_passive_unknown:
+	case ixgbe_phy_qsfp_active_unknown:
+	case ixgbe_phy_qsfp_intel:
+	case ixgbe_phy_qsfp_unknown:
+		/* SFP+ devices, further checking needed */
+		switch (hw->phy.sfp_type) {
+		case ixgbe_sfp_type_da_cu:
+		case ixgbe_sfp_type_da_cu_core0:
+		case ixgbe_sfp_type_da_cu_core1:
+			ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+			link_settings->base.port = RTE_PORT_DA;
+			break;
+		case ixgbe_sfp_type_sr:
+		case ixgbe_sfp_type_lr:
+		case ixgbe_sfp_type_srlr_core0:
+		case ixgbe_sfp_type_srlr_core1:
+		case ixgbe_sfp_type_1g_sx_core0:
+		case ixgbe_sfp_type_1g_sx_core1:
+		case ixgbe_sfp_type_1g_lx_core0:
+		case ixgbe_sfp_type_1g_lx_core1:
+			ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+			link_settings->base.port = RTE_PORT_FIBRE;
+			break;
+		case ixgbe_sfp_type_not_present:
+			ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+			link_settings->base.port = RTE_PORT_NONE;
+			break;
+		case ixgbe_sfp_type_1g_cu_core0:
+		case ixgbe_sfp_type_1g_cu_core1:
+			ixgbe_set_bit(supported, RTE_LINK_MODE_TP_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_TP_BIT);
+			link_settings->base.port = RTE_PORT_TP;
+			break;
+		case ixgbe_sfp_type_unknown:
+		default:
+			ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+			ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+			link_settings->base.port = RTE_PORT_OTHER;
+			break;
+		}
+		break;
+	case ixgbe_phy_xaui:
+		ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+		link_settings->base.port = RTE_PORT_NONE;
+		break;
+	case ixgbe_phy_unknown:
+	case ixgbe_phy_generic:
+	case ixgbe_phy_sfp_unsupported:
+	default:
+		ixgbe_set_bit(supported, RTE_LINK_MODE_FIBRE_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_FIBRE_BIT);
+		link_settings->base.port = RTE_PORT_OTHER;
+		break;
+	}
+
+	/* Indicate pause support */
+	ixgbe_set_bit(supported, RTE_LINK_MODE_Pause_BIT);
+	switch (hw->fc.requested_mode) {
+	case ixgbe_fc_full:
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Pause_BIT);
+		break;
+	case ixgbe_fc_rx_pause:
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Pause_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Asym_Pause_BIT);
+		break;
+	case ixgbe_fc_tx_pause:
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Asym_Pause_BIT);
+		break;
+	default:
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Pause_BIT);
+		ixgbe_set_bit(advertising, RTE_LINK_MODE_Asym_Pause_BIT);
+	}
+
+	link_settings->base.link.link_status = RTE_ETH_LINK_DOWN;
+	link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+	link_settings->base.link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+
+	hw->mac.get_link_status = true;
+	/* check if it needs to wait to complete, if lsc interrupt is enabled */
+	if (dev->data->dev_conf.intr_conf.lsc != 0)
+		wait = 0;
+
+	ret_val = ixgbe_check_link(hw, &link_speed, &link_up, wait);
+	if (ret_val != IXGBE_SUCCESS)
+		return ret_val;
+
+	if (ixgbe_get_media_type(hw) == ixgbe_media_type_fiber &&
+		!ad->sdp3_no_tx_disable) {
+		esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP);
+		if ((esdp_reg & IXGBE_ESDP_SDP3))
+			link_up = 0;
+	}
+
+	if (link_up) {
+		link_settings->base.link.link_status = RTE_ETH_LINK_UP;
+		link_settings->base.link.link_status = RTE_ETH_LINK_FULL_DUPLEX;
+		switch (link_speed) {
+		default:
+		case IXGBE_LINK_SPEED_UNKNOWN:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+			break;
+
+		case IXGBE_LINK_SPEED_10_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_10M;
+			break;
+
+		case IXGBE_LINK_SPEED_100_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_100M;
+			break;
+
+		case IXGBE_LINK_SPEED_1GB_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_1G;
+			break;
+
+		case IXGBE_LINK_SPEED_2_5GB_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_2_5G;
+			break;
+
+		case IXGBE_LINK_SPEED_5GB_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_5G;
+			break;
+
+		case IXGBE_LINK_SPEED_10GB_FULL:
+			link_settings->base.link.link_speed = RTE_ETH_SPEED_NUM_10G;
+			break;
+		}
+	}
+
+	return ret_val;
+}
+
+static int ixgbe_set_link_settings(struct rte_eth_dev *dev,
+				   const struct rte_link_settings *link_settings)
+{
+	struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+	const uint32_t *supported = link_settings->link_modes.supported;
+	const uint32_t *advertising = link_settings->link_modes.advertising;
+
+	u32 advertised, old;
+	int ret = 0;
+
+	if (hw->phy.media_type == ixgbe_media_type_copper ||
+	    (IXGBE_ISBACKPLANE(hw->phy.media_type)) ||
+	    hw->phy.multispeed_fiber) {
+		/*
+		 * Check if advertising is subset of supported
+		 */
+		if (!ixgbe_is_subset(advertising, supported,
+				     __RTE_LINK_MODE_MASK_NBITS))
+			return -EINVAL;
+
+		/* only allow one speed at a time if no autoneg */
+		if (!link_settings->base.link.link_autoneg && hw->phy.multispeed_fiber) {
+			if (ixgbe_get_bit(advertising,
+					  RTE_LINK_MODE_10000baseT_Full_BIT) &&
+			    ixgbe_get_bit(advertising,
+					  RTE_LINK_MODE_1000baseT_Full_BIT))
+				return -EINVAL;
+		}
+
+		old = hw->phy.autoneg_advertised;
+		advertised = 0;
+		if ((ixgbe_get_bit(advertising, RTE_LINK_MODE_10000baseT_Full_BIT)) ||
+		    (ixgbe_get_bit(advertising, RTE_LINK_MODE_10000baseKR_Full_BIT)) ||
+		    (ixgbe_get_bit(advertising, RTE_LINK_MODE_10000baseKX4_Full_BIT)))
+			advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+		if (ixgbe_get_bit(advertising, RTE_LINK_MODE_5000baseT_Full_BIT))
+			advertised |= IXGBE_LINK_SPEED_5GB_FULL;
+		if (ixgbe_get_bit(advertising, RTE_LINK_MODE_2500baseT_Full_BIT))
+			advertised |= IXGBE_LINK_SPEED_2_5GB_FULL;
+		if ((ixgbe_get_bit(advertising, RTE_LINK_MODE_1000baseT_Full_BIT)) ||
+		    (ixgbe_get_bit(advertising, RTE_LINK_MODE_1000baseKX_Full_BIT)))
+			advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+		if (ixgbe_get_bit(advertising, RTE_LINK_MODE_100baseT_Full_BIT))
+			advertised |= IXGBE_LINK_SPEED_100_FULL;
+		if (ixgbe_get_bit(advertising, RTE_LINK_MODE_10baseT_Full_BIT))
+			advertised |= IXGBE_LINK_SPEED_10_FULL;
+		if (old == advertised)
+			return ret;
+
+		hw->mac.autotry_restart = true;
+		ret = hw->mac.ops.setup_link(hw, advertised, true);
+		if (ret) {
+			PMD_DRV_LOG(WARNING, "setup link failed with code %d\n", ret);
+			hw->mac.ops.setup_link(hw, old, true);
+		}
+	} else {
+		/* in this case we currently only support 10Gb/FULL */
+		u32 speed = link_settings->base.link.link_speed;
+
+		if (link_settings->base.link.link_autoneg == RTE_AUTONEG_ENABLE ||
+		    (!ixgbe_get_bit(advertising, RTE_LINK_MODE_10000baseT_Full_BIT)) ||
+		    (speed + link_settings->base.link.link_duplex !=
+						IXGBE_SPEED_10000 + IXGBE_DUPLEX_FULL))
+			return -EINVAL;
+	}
+
+	return ret;
+}
+
 static int
 ixgbevf_dev_promiscuous_enable(struct rte_eth_dev *dev)
 {
diff --git a/lib/ethdev/ethdev_driver.h b/lib/ethdev/ethdev_driver.h
index 0dbf2dd6a2..ee8f786b99 100644
--- a/lib/ethdev/ethdev_driver.h
+++ b/lib/ethdev/ethdev_driver.h
@@ -1119,6 +1119,34 @@ typedef const uint32_t *(*eth_buffer_split_supported_hdr_ptypes_get_t)(struct rt
  */
 typedef int (*eth_dev_priv_dump_t)(struct rte_eth_dev *dev, FILE *file);
 
+/** @internal Retrieve physical link settings of a port.
+ *
+ * @param dev
+ *   Port (ethdev) handle
+ *
+ * @param[out] settings
+ *   Physical port link settings.
+ *
+ * @return
+ *   Negative errno value on error, zero otherwise
+ */
+typedef int (*eth_get_link_settings_t)(struct rte_eth_dev *dev,
+				 struct rte_link_settings *settings);
+
+/** @internal Configure physical link settings of a port.
+ *
+ * @param dev
+ *   Port (ethdev) handle
+ *
+ * @param settings
+ *   Physical port link settings.
+ *
+ * @return
+ *   Negative errno value on error, zero otherwise
+ */
+typedef int (*eth_set_link_settings_t)(struct rte_eth_dev *dev,
+				 const struct rte_link_settings *settings);
+
 /**
  * @internal Set Rx queue available descriptors threshold.
  * @see rte_eth_rx_avail_thresh_set()
@@ -1474,6 +1502,11 @@ struct eth_dev_ops {
 	eth_count_aggr_ports_t count_aggr_ports;
 	/** Map a Tx queue with an aggregated port of the DPDK port */
 	eth_map_aggr_tx_affinity_t map_aggr_tx_affinity;
+
+	/** Retrieve physical link settings */
+	eth_get_link_settings_t get_link_settings;
+	/** Configure physical link settings */
+	eth_set_link_settings_t set_link_settings;
 };
 
 /**
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index f1c658f49e..d2c52921ef 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -6844,6 +6844,32 @@ rte_eth_dev_priv_dump(uint16_t port_id, FILE *file)
 	return eth_err(port_id, (*dev->dev_ops->eth_dev_priv_dump)(dev, file));
 }
 
+int rte_eth_dev_get_link_settings(uint16_t port_id,
+				  struct rte_link_settings *settings)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->get_link_settings == NULL)
+		return -ENOTSUP;
+	return eth_err(port_id, (*dev->dev_ops->get_link_settings)(dev, settings));
+}
+
+int rte_eth_dev_set_link_settings(uint16_t port_id,
+				  const struct rte_link_settings *settings)
+{
+	struct rte_eth_dev *dev;
+
+	RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV);
+	dev = &rte_eth_devices[port_id];
+
+	if (*dev->dev_ops->set_link_settings == NULL)
+		return -ENOTSUP;
+	return eth_err(port_id, (*dev->dev_ops->set_link_settings)(dev, settings));
+}
+
 int
 rte_eth_rx_descriptor_dump(uint16_t port_id, uint16_t queue_id,
 			   uint16_t offset, uint16_t num, FILE *file)
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index 147257d6a2..66aad925d0 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -335,7 +335,7 @@ struct rte_eth_stats {
 __extension__
 struct __rte_aligned(8) rte_eth_link { /**< aligned for atomic64 read/write */
 	uint32_t link_speed;        /**< RTE_ETH_SPEED_NUM_ */
-	uint16_t link_duplex  : 1;  /**< RTE_ETH_LINK_[HALF/FULL]_DUPLEX */
+	uint16_t link_duplex  : 2;  /**< RTE_ETH_LINK_[HALF/FULL/UNKNOWN]_DUPLEX */
 	uint16_t link_autoneg : 1;  /**< RTE_ETH_LINK_[AUTONEG/FIXED] */
 	uint16_t link_status  : 1;  /**< RTE_ETH_LINK_[DOWN/UP] */
 };
@@ -343,13 +343,27 @@ struct __rte_aligned(8) rte_eth_link { /**< aligned for atomic64 read/write */
 /**@{@name Link negotiation
  * Constants used in link management.
  */
-#define RTE_ETH_LINK_HALF_DUPLEX 0 /**< Half-duplex connection (see link_duplex). */
-#define RTE_ETH_LINK_FULL_DUPLEX 1 /**< Full-duplex connection (see link_duplex). */
-#define RTE_ETH_LINK_DOWN        0 /**< Link is down (see link_status). */
-#define RTE_ETH_LINK_UP          1 /**< Link is up (see link_status). */
-#define RTE_ETH_LINK_FIXED       0 /**< No autonegotiation (see link_autoneg). */
-#define RTE_ETH_LINK_AUTONEG     1 /**< Autonegotiated (see link_autoneg). */
-#define RTE_ETH_LINK_MAX_STR_LEN 40 /**< Max length of default link string. */
+#define RTE_ETH_LINK_HALF_DUPLEX	0 /**< Half-duplex connection (see link_duplex). */
+#define RTE_ETH_LINK_FULL_DUPLEX	1 /**< Full-duplex connection (see link_duplex). */
+#define RTE_ETH_LINK_UNKNOWN_DUPLEX	2 /**</ Unknown-duplex (see link_duplex). */
+#define RTE_ETH_LINK_DOWN		0 /**< Link is down (see link_status). */
+#define RTE_ETH_LINK_UP			1 /**< Link is up (see link_status). */
+#define RTE_ETH_LINK_FIXED		0 /**< No autonegotiation (see link_autoneg). */
+#define RTE_ETH_LINK_AUTONEG		1 /**< Autonegotiated (see link_autoneg). */
+#define RTE_ETH_LINK_MAX_STR_LEN	40 /**< Max length of default link string. */
+/**@}*/
+
+/**@{@name Link negotiation
+ * Constants used in link management to specify connector port.
+ */
+#define RTE_PORT_TP             0x00
+#define RTE_PORT_AUI            0x01
+#define RTE_PORT_MII            0x02
+#define RTE_PORT_FIBRE          0x03
+#define RTE_PORT_BNC            0x04
+#define RTE_PORT_DA             0x05
+#define RTE_PORT_NONE           0xef
+#define RTE_PORT_OTHER          0xff
 /**@}*/
 
 /**
@@ -1433,6 +1447,147 @@ struct rte_eth_pfc_queue_conf {
 	} tx_pause; /* Valid when (mode == FC_TX_PAUSE || mode == FC_FULL) */
 };
 
+/* Enable or disable autonegotiation. */
+#define RTE_AUTONEG_DISABLE         0x00
+#define RTE_AUTONEG_ENABLE          0x01
+
+/* MDI or MDI-X status/control - if MDI/MDI_X/AUTO is set then
+ * the driver is required to renegotiate link
+ */
+#define RTE_TP_MDI_INVALID      0x00 /* status: unknown; control: unsupported */
+#define RTE_TP_MDI              0x01 /* status: MDI;     control: force MDI */
+#define RTE_TP_MDI_X            0x02 /* status: MDI-X;   control: force MDI-X */
+#define RTE_TP_MDI_AUTO         0x03 /*                  control: auto-select */
+
+/* Link mode bit indices */
+enum rte_link_mode_bit_indices {
+	RTE_LINK_MODE_10baseT_Half_BIT	= 0,
+	RTE_LINK_MODE_10baseT_Full_BIT	= 1,
+	RTE_LINK_MODE_100baseT_Half_BIT	= 2,
+	RTE_LINK_MODE_100baseT_Full_BIT	= 3,
+	RTE_LINK_MODE_1000baseT_Half_BIT	= 4,
+	RTE_LINK_MODE_1000baseT_Full_BIT	= 5,
+	RTE_LINK_MODE_Autoneg_BIT		= 6,
+	RTE_LINK_MODE_TP_BIT		= 7,
+	RTE_LINK_MODE_AUI_BIT		= 8,
+	RTE_LINK_MODE_MII_BIT		= 9,
+	RTE_LINK_MODE_FIBRE_BIT		= 10,
+	RTE_LINK_MODE_BNC_BIT		= 11,
+	RTE_LINK_MODE_10000baseT_Full_BIT	= 12,
+	RTE_LINK_MODE_Pause_BIT		= 13,
+	RTE_LINK_MODE_Asym_Pause_BIT	= 14,
+	RTE_LINK_MODE_2500baseX_Full_BIT	= 15,
+	RTE_LINK_MODE_Backplane_BIT		= 16,
+	RTE_LINK_MODE_1000baseKX_Full_BIT	= 17,
+	RTE_LINK_MODE_10000baseKX4_Full_BIT	= 18,
+	RTE_LINK_MODE_10000baseKR_Full_BIT	= 19,
+	RTE_LINK_MODE_10000baseR_FEC_BIT	= 20,
+	RTE_LINK_MODE_20000baseMLD2_Full_BIT = 21,
+	RTE_LINK_MODE_20000baseKR2_Full_BIT	= 22,
+	RTE_LINK_MODE_40000baseKR4_Full_BIT	= 23,
+	RTE_LINK_MODE_40000baseCR4_Full_BIT	= 24,
+	RTE_LINK_MODE_40000baseSR4_Full_BIT	= 25,
+	RTE_LINK_MODE_40000baseLR4_Full_BIT	= 26,
+	RTE_LINK_MODE_56000baseKR4_Full_BIT	= 27,
+	RTE_LINK_MODE_56000baseCR4_Full_BIT	= 28,
+	RTE_LINK_MODE_56000baseSR4_Full_BIT	= 29,
+	RTE_LINK_MODE_56000baseLR4_Full_BIT	= 30,
+	RTE_LINK_MODE_25000baseCR_Full_BIT	= 31,
+	RTE_LINK_MODE_25000baseKR_Full_BIT	= 32,
+	RTE_LINK_MODE_25000baseSR_Full_BIT	= 33,
+	RTE_LINK_MODE_50000baseCR2_Full_BIT	= 34,
+	RTE_LINK_MODE_50000baseKR2_Full_BIT	= 35,
+	RTE_LINK_MODE_100000baseKR4_Full_BIT	= 36,
+	RTE_LINK_MODE_100000baseSR4_Full_BIT	= 37,
+	RTE_LINK_MODE_100000baseCR4_Full_BIT	= 38,
+	RTE_LINK_MODE_100000baseLR4_ER4_Full_BIT	= 39,
+	RTE_LINK_MODE_50000baseSR2_Full_BIT		= 40,
+	RTE_LINK_MODE_1000baseX_Full_BIT	= 41,
+	RTE_LINK_MODE_10000baseCR_Full_BIT	= 42,
+	RTE_LINK_MODE_10000baseSR_Full_BIT	= 43,
+	RTE_LINK_MODE_10000baseLR_Full_BIT	= 44,
+	RTE_LINK_MODE_10000baseLRM_Full_BIT	= 45,
+	RTE_LINK_MODE_10000baseER_Full_BIT	= 46,
+	RTE_LINK_MODE_2500baseT_Full_BIT	= 47,
+	RTE_LINK_MODE_5000baseT_Full_BIT	= 48,
+
+	RTE_LINK_MODE_FEC_NONE_BIT	= 49,
+	RTE_LINK_MODE_FEC_RS_BIT	= 50,
+	RTE_LINK_MODE_FEC_BASER_BIT	= 51,
+	RTE_LINK_MODE_50000baseKR_Full_BIT		 = 52,
+	RTE_LINK_MODE_50000baseSR_Full_BIT		 = 53,
+	RTE_LINK_MODE_50000baseCR_Full_BIT		 = 54,
+	RTE_LINK_MODE_50000baseLR_ER_FR_Full_BIT	 = 55,
+	RTE_LINK_MODE_50000baseDR_Full_BIT		 = 56,
+	RTE_LINK_MODE_100000baseKR2_Full_BIT	 = 57,
+	RTE_LINK_MODE_100000baseSR2_Full_BIT	 = 58,
+	RTE_LINK_MODE_100000baseCR2_Full_BIT	 = 59,
+	RTE_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60,
+	RTE_LINK_MODE_100000baseDR2_Full_BIT	 = 61,
+	RTE_LINK_MODE_200000baseKR4_Full_BIT	 = 62,
+	RTE_LINK_MODE_200000baseSR4_Full_BIT	 = 63,
+	RTE_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64,
+	RTE_LINK_MODE_200000baseDR4_Full_BIT	 = 65,
+	RTE_LINK_MODE_200000baseCR4_Full_BIT	 = 66,
+	RTE_LINK_MODE_100baseT1_Full_BIT		 = 67,
+	RTE_LINK_MODE_1000baseT1_Full_BIT		 = 68,
+	RTE_LINK_MODE_400000baseKR8_Full_BIT	 = 69,
+	RTE_LINK_MODE_400000baseSR8_Full_BIT	 = 70,
+	RTE_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
+	RTE_LINK_MODE_400000baseDR8_Full_BIT	 = 72,
+	RTE_LINK_MODE_400000baseCR8_Full_BIT	 = 73,
+	RTE_LINK_MODE_FEC_LLRS_BIT			 = 74,
+	RTE_LINK_MODE_100000baseKR_Full_BIT		 = 75,
+	RTE_LINK_MODE_100000baseSR_Full_BIT		 = 76,
+	RTE_LINK_MODE_100000baseLR_ER_FR_Full_BIT	 = 77,
+	RTE_LINK_MODE_100000baseCR_Full_BIT		 = 78,
+	RTE_LINK_MODE_100000baseDR_Full_BIT		 = 79,
+	RTE_LINK_MODE_200000baseKR2_Full_BIT	 = 80,
+	RTE_LINK_MODE_200000baseSR2_Full_BIT	 = 81,
+	RTE_LINK_MODE_200000baseLR2_ER2_FR2_Full_BIT = 82,
+	RTE_LINK_MODE_200000baseDR2_Full_BIT	 = 83,
+	RTE_LINK_MODE_200000baseCR2_Full_BIT	 = 84,
+	RTE_LINK_MODE_400000baseKR4_Full_BIT	 = 85,
+	RTE_LINK_MODE_400000baseSR4_Full_BIT	 = 86,
+	RTE_LINK_MODE_400000baseLR4_ER4_FR4_Full_BIT = 87,
+	RTE_LINK_MODE_400000baseDR4_Full_BIT	 = 88,
+	RTE_LINK_MODE_400000baseCR4_Full_BIT	 = 89,
+	RTE_LINK_MODE_100baseFX_Half_BIT		 = 90,
+	RTE_LINK_MODE_100baseFX_Full_BIT		 = 91,
+	/* must be last entry */
+	__RTE_LINK_MODE_MASK_NBITS
+};
+
+/* number of 32-bit words to store the user's link mode bitmaps */
+#define RTE_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#define __RTE_LINK_MODE_MASK_NU32                   \
+	RTE_DIV_ROUND_UP(__RTE_LINK_MODE_MASK_NBITS, 32)
+
+/**
+ * Link control and status
+ * @speed: Link speed (Mbps)
+ * @duplex: Duplex mode; one of %DUPLEX_*
+ * @port: Physical connector type; one of %PORT_*
+ * @autoneg: Enable/disable autonegotiation and auto-detection;
+ *	either %AUTONEG_DISABLE or %AUTONEG_ENABLE
+ */
+struct rte_link_base_settings {
+	struct rte_eth_link link;
+	uint8_t port;
+	uint8_t phy_address;
+	uint8_t eth_tp_mdix;
+	uint8_t eth_tp_mdix_ctrl;
+};
+
+struct rte_link_settings {
+	struct rte_link_base_settings base;
+	struct {
+		uint32_t supported[__RTE_LINK_MODE_MASK_NU32];
+		uint32_t advertising[__RTE_LINK_MODE_MASK_NU32];
+		uint32_t lp_advertising[__RTE_LINK_MODE_MASK_NU32];
+	} link_modes;
+};
+
 /**
  * Tunnel type for device-specific classifier configuration.
  * @see rte_eth_udp_tunnel
@@ -5936,6 +6091,50 @@ int rte_eth_cman_config_init(uint16_t port_id, struct rte_eth_cman_config *confi
 __rte_experimental
 int rte_eth_cman_config_set(uint16_t port_id, const struct rte_eth_cman_config *config);
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Retrieve link settings
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param settings
+ *   A pointer to a structure of type *rte_link_settings* to retrieve
+ *   link settings parameters for the given port.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_config_get does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_dev_get_link_settings(uint16_t port_id,
+				  struct rte_link_settings *settings);
+
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
+ *
+ * Configure link settings
+ *
+ * @param port_id
+ *   The port identifier of the Ethernet device.
+ * @param settings
+ *   A pointer to a structure of type *rte_link_settings* to configure
+ *   link settings parameters for the given port.
+ *
+ * @return
+ *   - (0) if successful.
+ *   - (-ENOTSUP) if support for cman_config_get does not exist.
+ *   - (-ENODEV) if *port_id* invalid.
+ *   - (-EINVAL) if bad parameter.
+ */
+__rte_experimental
+int rte_eth_dev_set_link_settings(uint16_t port_id,
+				  const struct rte_link_settings *settings);
+
 /**
  * @warning
  * @b EXPERIMENTAL: this API may change, or be removed, without prior notice
diff --git a/lib/ethdev/version.map b/lib/ethdev/version.map
index 79f6f5293b..5e138a3d10 100644
--- a/lib/ethdev/version.map
+++ b/lib/ethdev/version.map
@@ -325,6 +325,9 @@ EXPERIMENTAL {
 	rte_flow_template_table_resizable;
 	rte_flow_template_table_resize;
 	rte_flow_template_table_resize_complete;
+	rte_eth_dev_get_link_settings;
+	rte_eth_dev_set_link_settings;
+
 };
 
 INTERNAL {
