[v3,10/15] net/dpaa2: support recycle loopback port

Message ID 20220103100129.23965-11-nipun.gupta@nxp.com (mailing list archive)
State Accepted, archived
Delegated to: Ferruh Yigit
Headers
Series features and fixes on NXP eth devices |

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Nipun Gupta Jan. 3, 2022, 10:01 a.m. UTC
  From: Jun Yang <jun.yang@nxp.com>

DPAA2 recycle port is used for configuring the device
in the loopback mode. Loopback configuration can be at
dpni level or at serdes level.

Signed-off-by: Jun Yang <jun.yang@nxp.com>
---
 drivers/bus/fslmc/portal/dpaa2_hw_pvt.h |   3 +-
 drivers/net/dpaa2/dpaa2_ethdev.c        |  32 +-
 drivers/net/dpaa2/dpaa2_ethdev.h        |  23 +
 drivers/net/dpaa2/dpaa2_recycle.c       | 780 ++++++++++++++++++++++++
 drivers/net/dpaa2/mc/dpni.c             |  32 +
 drivers/net/dpaa2/mc/fsl_dpni_cmd.h     |   1 +
 drivers/net/dpaa2/meson.build           |   1 +
 7 files changed, 870 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/dpaa2/dpaa2_recycle.c
  

Comments

David Marchand Feb. 1, 2022, 9:27 a.m. UTC | #1
Hello guys,

On Mon, Jan 3, 2022 at 11:02 AM <nipun.gupta@nxp.com> wrote:
> diff --git a/drivers/net/dpaa2/dpaa2_recycle.c b/drivers/net/dpaa2/dpaa2_recycle.c
> new file mode 100644
> index 0000000000..e274d24ead
> --- /dev/null
> +++ b/drivers/net/dpaa2/dpaa2_recycle.c
> @@ -0,0 +1,780 @@
> +/* * SPDX-License-Identifier: BSD-3-Clause
> + *
> + *   Copyright 2019-2021 NXP
> + *
> + */
> +
> +#include <time.h>
> +#include <net/if.h>
> +
> +#include <rte_mbuf.h>
> +#include <ethdev_driver.h>
> +#include <rte_malloc.h>
> +#include <rte_memcpy.h>
> +#include <rte_string_fns.h>
> +#include <rte_cycles.h>
> +#include <rte_kvargs.h>
> +#include <rte_dev.h>
> +#include <rte_fslmc.h>
> +#include <rte_flow_driver.h>
> +
> +#include "dpaa2_pmd_logs.h"
> +#include <fslmc_vfio.h>
> +#include <dpaa2_hw_pvt.h>
> +#include <dpaa2_hw_mempool.h>
> +#include <dpaa2_hw_dpio.h>
> +#include <mc/fsl_dpmng.h>
> +#include "dpaa2_ethdev.h"
> +#include "dpaa2_sparser.h"
> +#include <fsl_qbman_debug.h>
> +
> +#include <rte_io.h>
> +#include <unistd.h>
> +#include <sys/mman.h>
> +
> +#define PAGE_SIZE                      (sysconf(_SC_PAGESIZE))
> +#define PAGE_MASK                      (~(PAGE_SIZE - 1))

This patch breaks compilation in Alpine Linux in next-net and main repositories.
Can you provide a fix?
Thanks.


This was initially reported by UNH:
http://mails.dpdk.org/archives/test-report/2022-January/250111.html

FAILED: drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o
ccache cc -Idrivers/a715181@@tmp_rte_net_dpaa2@sta -Idrivers
-I../drivers -Idrivers/net/dpaa2 -I../drivers/net/dpaa2
-I../drivers/net/dpaa2/base -I../drivers/net/dpaa2/mc -Ilib/ethdev
-I../lib/ethdev -I. -I../ -Iconfig -I../config -Ilib/eal/include
-I../lib/eal/include -Ilib/eal/linux/include
-I../lib/eal/linux/include -Ilib/eal/x86/include
-I../lib/eal/x86/include -Ilib/eal/common -I../lib/eal/common
-Ilib/eal -I../lib/eal -Ilib/kvargs -I../lib/kvargs
-Ilib/telemetry/../metrics -I../lib/telemetry/../metrics
-Ilib/telemetry -I../lib/telemetry -Ilib/net -I../lib/net -Ilib/mbuf
-I../lib/mbuf -Ilib/mempool -I../lib/mempool -Ilib/ring -I../lib/ring
-Ilib/meter -I../lib/meter -Idrivers/bus/pci -I../drivers/bus/pci
-I../drivers/bus/pci/linux -Ilib/pci -I../lib/pci -Idrivers/bus/vdev
-I../drivers/bus/vdev -Idrivers/mempool/dpaa2
-I../drivers/mempool/dpaa2 -Idrivers/bus/fslmc -I../drivers/bus/fslmc
-I../drivers/bus/fslmc/mc -I../drivers/bus/fslmc/qbman/include
-I../drivers/bus/fslmc/portal -Idrivers/common/dpaax
-I../drivers/common/dpaax -I../drivers/common/dpaax/caamflib
-Ilib/eventdev -I../lib/eventdev -Ilib/hash -I../lib/hash -Ilib/rcu
-I../lib/rcu -Ilib/timer -I../lib/timer -Ilib/cryptodev
-I../lib/cryptodev -fdiagnostics-color=always -pipe
-D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Werror -O3 -include
rte_config.h -Wextra -Wcast-qual -Wdeprecated -Wformat
-Wformat-nonliteral -Wformat-security -Wmissing-declarations
-Wmissing-prototypes -Wnested-externs -Wold-style-definition
-Wpointer-arith -Wsign-compare -Wstrict-prototypes -Wundef
-Wwrite-strings -Wno-address-of-packed-member -Wno-packed-not-aligned
-Wno-missing-field-initializers -Wno-zero-length-bounds -D_GNU_SOURCE
-fPIC -march=native -DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API
-Wno-format-truncation -DRTE_LOG_DEFAULT_LOGTYPE=pmd.net.dpaa2  -MD
-MQ 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o'
-MF 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o.d'
-o 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o'
-c ../drivers/net/dpaa2/dpaa2_recycle.c
../drivers/net/dpaa2/dpaa2_recycle.c:35: error: "PAGE_SIZE" redefined [-Werror]
   35 | #define PAGE_SIZE   (sysconf(_SC_PAGESIZE))
      |
In file included from /usr/include/fortify/stdlib.h:29,
                 from ../lib/eal/include/rte_common.h:20,
                 from ../lib/mbuf/rte_mbuf.h:36,
                 from ../drivers/net/dpaa2/dpaa2_recycle.c:10:
/usr/include/limits.h:97: note: this is the location of the previous definition
   97 | #define PAGE_SIZE PAGESIZE
      |
cc1: all warnings being treated as errors
  
Nipun Gupta Feb. 1, 2022, 9:34 a.m. UTC | #2
Hi David,
	Sure, we will send a patch asap.

Regards,
Nipun

> -----Original Message-----
> From: David Marchand <david.marchand@redhat.com>
> Sent: 01 February 2022 14:58
> To: Nipun Gupta <nipun.gupta@nxp.com>; Jun Yang <jun.yang@nxp.com>
> Cc: dev <dev@dpdk.org>; Thomas Monjalon <thomas@monjalon.net>; Yigit,
> Ferruh <ferruh.yigit@intel.com>; Hemant Agrawal <hemant.agrawal@nxp.com>;
> Stephen Hemminger <stephen@networkplumber.org>
> Subject: Re: [PATCH v3 10/15] net/dpaa2: support recycle loopback port
> 
> Hello guys,
> 
> On Mon, Jan 3, 2022 at 11:02 AM <nipun.gupta@nxp.com> wrote:
> > diff --git a/drivers/net/dpaa2/dpaa2_recycle.c
> b/drivers/net/dpaa2/dpaa2_recycle.c
> > new file mode 100644
> > index 0000000000..e274d24ead
> > --- /dev/null
> > +++ b/drivers/net/dpaa2/dpaa2_recycle.c
> > @@ -0,0 +1,780 @@
> > +/* * SPDX-License-Identifier: BSD-3-Clause
> > + *
> > + *   Copyright 2019-2021 NXP
> > + *
> > + */
> > +
> > +#include <time.h>
> > +#include <net/if.h>
> > +
> > +#include <rte_mbuf.h>
> > +#include <ethdev_driver.h>
> > +#include <rte_malloc.h>
> > +#include <rte_memcpy.h>
> > +#include <rte_string_fns.h>
> > +#include <rte_cycles.h>
> > +#include <rte_kvargs.h>
> > +#include <rte_dev.h>
> > +#include <rte_fslmc.h>
> > +#include <rte_flow_driver.h>
> > +
> > +#include "dpaa2_pmd_logs.h"
> > +#include <fslmc_vfio.h>
> > +#include <dpaa2_hw_pvt.h>
> > +#include <dpaa2_hw_mempool.h>
> > +#include <dpaa2_hw_dpio.h>
> > +#include <mc/fsl_dpmng.h>
> > +#include "dpaa2_ethdev.h"
> > +#include "dpaa2_sparser.h"
> > +#include <fsl_qbman_debug.h>
> > +
> > +#include <rte_io.h>
> > +#include <unistd.h>
> > +#include <sys/mman.h>
> > +
> > +#define PAGE_SIZE                      (sysconf(_SC_PAGESIZE))
> > +#define PAGE_MASK                      (~(PAGE_SIZE - 1))
> 
> This patch breaks compilation in Alpine Linux in next-net and main repositories.
> Can you provide a fix?
> Thanks.
> 
> 
> This was initially reported by UNH:
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fmails.dpd
> k.org%2Farchives%2Ftest-report%2F2022-
> January%2F250111.html&amp;data=04%7C01%7Cnipun.gupta%40nxp.com%7C
> a8213a91a0014639650d08d9e5652600%7C686ea1d3bc2b4c6fa92cd99c5c3016
> 35%7C0%7C0%7C637793044866714334%7CUnknown%7CTWFpbGZsb3d8eyJWI
> joiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C300
> 0&amp;sdata=b%2BT8BinlyFWaYSLaaOLtNTN9TA0epJAxN0Si9jbom7I%3D&amp
> ;reserved=0
> 
> FAILED:
> drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o
> ccache cc -Idrivers/a715181@@tmp_rte_net_dpaa2@sta -Idrivers
> -I../drivers -Idrivers/net/dpaa2 -I../drivers/net/dpaa2
> -I../drivers/net/dpaa2/base -I../drivers/net/dpaa2/mc -Ilib/ethdev
> -I../lib/ethdev -I. -I../ -Iconfig -I../config -Ilib/eal/include
> -I../lib/eal/include -Ilib/eal/linux/include
> -I../lib/eal/linux/include -Ilib/eal/x86/include
> -I../lib/eal/x86/include -Ilib/eal/common -I../lib/eal/common
> -Ilib/eal -I../lib/eal -Ilib/kvargs -I../lib/kvargs
> -Ilib/telemetry/../metrics -I../lib/telemetry/../metrics
> -Ilib/telemetry -I../lib/telemetry -Ilib/net -I../lib/net -Ilib/mbuf
> -I../lib/mbuf -Ilib/mempool -I../lib/mempool -Ilib/ring -I../lib/ring
> -Ilib/meter -I../lib/meter -Idrivers/bus/pci -I../drivers/bus/pci
> -I../drivers/bus/pci/linux -Ilib/pci -I../lib/pci -Idrivers/bus/vdev
> -I../drivers/bus/vdev -Idrivers/mempool/dpaa2
> -I../drivers/mempool/dpaa2 -Idrivers/bus/fslmc -I../drivers/bus/fslmc
> -I../drivers/bus/fslmc/mc -I../drivers/bus/fslmc/qbman/include
> -I../drivers/bus/fslmc/portal -Idrivers/common/dpaax
> -I../drivers/common/dpaax -I../drivers/common/dpaax/caamflib
> -Ilib/eventdev -I../lib/eventdev -Ilib/hash -I../lib/hash -Ilib/rcu
> -I../lib/rcu -Ilib/timer -I../lib/timer -Ilib/cryptodev
> -I../lib/cryptodev -fdiagnostics-color=always -pipe
> -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Werror -O3 -include
> rte_config.h -Wextra -Wcast-qual -Wdeprecated -Wformat
> -Wformat-nonliteral -Wformat-security -Wmissing-declarations
> -Wmissing-prototypes -Wnested-externs -Wold-style-definition
> -Wpointer-arith -Wsign-compare -Wstrict-prototypes -Wundef
> -Wwrite-strings -Wno-address-of-packed-member -Wno-packed-not-aligned
> -Wno-missing-field-initializers -Wno-zero-length-bounds -D_GNU_SOURCE
> -fPIC -march=native -DALLOW_EXPERIMENTAL_API -DALLOW_INTERNAL_API
> -Wno-format-truncation -DRTE_LOG_DEFAULT_LOGTYPE=pmd.net.dpaa2  -MD
> -MQ
> 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o'
> -MF
> 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o.d'
> -o
> 'drivers/a715181@@tmp_rte_net_dpaa2@sta/net_dpaa2_dpaa2_recycle.c.o'
> -c ../drivers/net/dpaa2/dpaa2_recycle.c
> ../drivers/net/dpaa2/dpaa2_recycle.c:35: error: "PAGE_SIZE" redefined [-Werror]
>    35 | #define PAGE_SIZE   (sysconf(_SC_PAGESIZE))
>       |
> In file included from /usr/include/fortify/stdlib.h:29,
>                  from ../lib/eal/include/rte_common.h:20,
>                  from ../lib/mbuf/rte_mbuf.h:36,
>                  from ../drivers/net/dpaa2/dpaa2_recycle.c:10:
> /usr/include/limits.h:97: note: this is the location of the previous definition
>    97 | #define PAGE_SIZE PAGESIZE
>       |
> cc1: all warnings being treated as errors
> 
> 
> 
> --
> David Marchand
  
Thomas Monjalon Feb. 1, 2022, 9:43 a.m. UTC | #3
01/02/2022 10:34, Nipun Gupta:
> 
> Hi David,
> 	Sure, we will send a patch asap.

Just need this:
#ifndef PAGE_SIZE
#define PAGE_SIZE           (sysconf(_SC_PAGESIZE))
#endif


> From: David Marchand <david.marchand@redhat.com>
> > 
> > Hello guys,
> > 
> > > +#define PAGE_SIZE                      (sysconf(_SC_PAGESIZE))
> > > +#define PAGE_MASK                      (~(PAGE_SIZE - 1))
> > 
> > This patch breaks compilation in Alpine Linux in next-net and main repositories.

That's a pity I missed it when pulling.
Please be more cautious with errors reported by the CI, thanks.
  

Patch

diff --git a/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h b/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
index 8cb4d404aa..4d0f7e4b5d 100644
--- a/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
+++ b/drivers/bus/fslmc/portal/dpaa2_hw_pvt.h
@@ -1,7 +1,7 @@ 
 /* SPDX-License-Identifier: BSD-3-Clause
  *
  *   Copyright (c) 2016 Freescale Semiconductor, Inc. All rights reserved.
- *   Copyright 2016-2020 NXP
+ *   Copyright 2016-2021 NXP
  *
  */
 
@@ -176,6 +176,7 @@  struct dpaa2_queue {
 	uint16_t nb_desc;
 	uint16_t resv;
 	uint64_t offloads;
+	uint64_t lpbk_cntx;
 } __rte_cache_aligned;
 
 struct swp_active_dqs {
diff --git a/drivers/net/dpaa2/dpaa2_ethdev.c b/drivers/net/dpaa2/dpaa2_ethdev.c
index a45beed75f..d81f8cb07a 100644
--- a/drivers/net/dpaa2/dpaa2_ethdev.c
+++ b/drivers/net/dpaa2/dpaa2_ethdev.c
@@ -668,6 +668,30 @@  dpaa2_eth_dev_configure(struct rte_eth_dev *dev)
 	if (rx_offloads & RTE_ETH_RX_OFFLOAD_VLAN_FILTER)
 		dpaa2_vlan_offload_set(dev, RTE_ETH_VLAN_FILTER_MASK);
 
+	if (eth_conf->lpbk_mode) {
+		ret = dpaa2_dev_recycle_config(dev);
+		if (ret) {
+			DPAA2_PMD_ERR("Error to configure %s to recycle port.",
+				dev->data->name);
+
+			return ret;
+		}
+	} else {
+		/** User may disable loopback mode by calling
+		 * "dev_configure" with lpbk_mode cleared.
+		 * No matter the port was configured recycle or not,
+		 * recycle de-configure is called here.
+		 * If port is not recycled, the de-configure will return directly.
+		 */
+		ret = dpaa2_dev_recycle_deconfig(dev);
+		if (ret) {
+			DPAA2_PMD_ERR("Error to de-configure recycle port %s.",
+				dev->data->name);
+
+			return ret;
+		}
+	}
+
 	dpaa2_tm_init(dev);
 
 	return 0;
@@ -2601,6 +2625,9 @@  dpaa2_dev_init(struct rte_eth_dev *eth_dev)
 		return -1;
 	}
 
+	if (eth_dev->data->dev_conf.lpbk_mode)
+		dpaa2_dev_recycle_deconfig(eth_dev);
+
 	/* Clean the device first */
 	ret = dpni_reset(dpni_dev, CMD_PRI_LOW, priv->token);
 	if (ret) {
@@ -2624,6 +2651,7 @@  dpaa2_dev_init(struct rte_eth_dev *eth_dev)
 	priv->dist_queues = attr.num_queues;
 	priv->num_channels = attr.num_channels;
 	priv->channel_inuse = 0;
+	rte_spinlock_init(&priv->lpbk_qp_lock);
 
 	/* only if the custom CG is enabled */
 	if (attr.options & DPNI_OPT_CUSTOM_CG)
@@ -2808,7 +2836,9 @@  dpaa2_dev_init(struct rte_eth_dev *eth_dev)
 			return ret;
 		}
 	}
-	RTE_LOG(INFO, PMD, "%s: netdev created\n", eth_dev->data->name);
+	RTE_LOG(INFO, PMD, "%s: netdev created, connected to %s\n",
+		eth_dev->data->name, dpaa2_dev->ep_name);
+
 	return 0;
 init_err:
 	dpaa2_dev_close(eth_dev);
diff --git a/drivers/net/dpaa2/dpaa2_ethdev.h b/drivers/net/dpaa2/dpaa2_ethdev.h
index bd33a22a8e..b032da9eff 100644
--- a/drivers/net/dpaa2/dpaa2_ethdev.h
+++ b/drivers/net/dpaa2/dpaa2_ethdev.h
@@ -11,6 +11,7 @@ 
 #include <rte_event_eth_rx_adapter.h>
 #include <rte_pmd_dpaa2.h>
 
+#include <rte_fslmc.h>
 #include <dpaa2_hw_pvt.h>
 #include "dpaa2_tm.h"
 
@@ -65,6 +66,18 @@ 
 /* Tx confirmation enabled */
 #define DPAA2_TX_CONF_ENABLE	0x06
 
+/* HW loopback the egress traffic to self ingress*/
+#define DPAA2_TX_MAC_LOOPBACK_MODE 0x20
+
+#define DPAA2_TX_SERDES_LOOPBACK_MODE 0x40
+
+#define DPAA2_TX_DPNI_LOOPBACK_MODE 0x80
+
+#define DPAA2_TX_LOOPBACK_MODE \
+	(DPAA2_TX_MAC_LOOPBACK_MODE | \
+	DPAA2_TX_SERDES_LOOPBACK_MODE | \
+	DPAA2_TX_DPNI_LOOPBACK_MODE)
+
 #define DPAA2_RSS_OFFLOAD_ALL ( \
 	RTE_ETH_RSS_L2_PAYLOAD | \
 	RTE_ETH_RSS_IP | \
@@ -192,6 +205,7 @@  struct dpaa2_dev_priv {
 	struct dpaa2_queue *next_tx_conf_queue;
 
 	struct rte_eth_dev *eth_dev; /**< Pointer back to holding ethdev */
+	rte_spinlock_t lpbk_qp_lock;
 
 	uint8_t channel_inuse;
 	LIST_HEAD(, rte_flow) flows; /**< Configured flow rule handles. */
@@ -268,4 +282,13 @@  int dpaa2_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
 						uint32_t flags __rte_unused);
 int dpaa2_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
 					  struct timespec *timestamp);
+
+int dpaa2_dev_recycle_config(struct rte_eth_dev *eth_dev);
+int dpaa2_dev_recycle_deconfig(struct rte_eth_dev *eth_dev);
+int dpaa2_dev_recycle_qp_setup(struct rte_dpaa2_device *dpaa2_dev,
+	uint16_t qidx, uint64_t cntx,
+	eth_rx_burst_t tx_lpbk, eth_tx_burst_t rx_lpbk,
+	struct dpaa2_queue **txq,
+	struct dpaa2_queue **rxq);
+
 #endif /* _DPAA2_ETHDEV_H */
diff --git a/drivers/net/dpaa2/dpaa2_recycle.c b/drivers/net/dpaa2/dpaa2_recycle.c
new file mode 100644
index 0000000000..e274d24ead
--- /dev/null
+++ b/drivers/net/dpaa2/dpaa2_recycle.c
@@ -0,0 +1,780 @@ 
+/* * SPDX-License-Identifier: BSD-3-Clause
+ *
+ *   Copyright 2019-2021 NXP
+ *
+ */
+
+#include <time.h>
+#include <net/if.h>
+
+#include <rte_mbuf.h>
+#include <ethdev_driver.h>
+#include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_string_fns.h>
+#include <rte_cycles.h>
+#include <rte_kvargs.h>
+#include <rte_dev.h>
+#include <rte_fslmc.h>
+#include <rte_flow_driver.h>
+
+#include "dpaa2_pmd_logs.h"
+#include <fslmc_vfio.h>
+#include <dpaa2_hw_pvt.h>
+#include <dpaa2_hw_mempool.h>
+#include <dpaa2_hw_dpio.h>
+#include <mc/fsl_dpmng.h>
+#include "dpaa2_ethdev.h"
+#include "dpaa2_sparser.h"
+#include <fsl_qbman_debug.h>
+
+#include <rte_io.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#define PAGE_SIZE			(sysconf(_SC_PAGESIZE))
+#define PAGE_MASK			(~(PAGE_SIZE - 1))
+
+#define LSX_SERDES_LAN_NB		8
+#define LSX_SERDES_REG_BASE		0x1ea0000
+#define LSX_LB_EN_BIT			0x10000000
+
+#define CONFIG_SYS_IMMR			0x01000000
+
+#define CONFIG_SYS_FSL_GUTS_ADDR	(CONFIG_SYS_IMMR + 0x00E00000)
+#define CONFIG_SYS_FSL_SERDES_ADDR	(CONFIG_SYS_IMMR + 0xEA0000)
+
+#define FSL_LX_SRDS1_PRTCL_SHIFT	16
+#define FSL_LX_SRDS2_PRTCL_SHIFT	21
+#define FSL_LX_SRDS3_PRTCL_SHIFT	26
+
+#define FSL_LS_SRDS1_PRTCL_SHIFT	16
+#define FSL_LS_SRDS2_PRTCL_SHIFT	0
+
+#define FSL_LX_SRDS1_REGSR		29
+#define FSL_LX_SRDS2_REGSR		29
+#define FSL_LX_SRDS3_REGSR		29
+
+#define FSL_LS_SRDS1_REGSR		29
+#define FSL_LS_SRDS2_REGSR		30
+
+#define FSL_LX_SRDS1_PRTCL_MASK		0x001F0000
+#define FSL_LX_SRDS2_PRTCL_MASK		0x03E00000
+#define FSL_LX_SRDS3_PRTCL_MASK		0x7C000000
+
+#define FSL_LS_SRDS1_PRTCL_MASK		0xFFFF0000
+#define FSL_LS_SRDS2_PRTCL_MASK		0x0000FFFF
+
+struct ccsr_lx_serdes_lan {
+	uint8_t unused1[0xa0];
+	uint32_t lnatcsr0;
+	uint8_t unused2[0x100 - 0xa4];
+} __rte_packed;
+
+struct ccsr_lx_serdes {
+	uint8_t unused0[0x800];
+	struct ccsr_lx_serdes_lan lane[LSX_SERDES_LAN_NB];
+} __rte_packed;
+
+struct ccsr_ls_serdes {
+	uint8_t unused[0x800];
+	struct serdes_lane {
+		uint32_t gcr0;   /* General Control Register 0 */
+		uint32_t gcr1;   /* General Control Register 1 */
+		uint32_t gcr2;   /* General Control Register 2 */
+		uint32_t ssc0;   /* Speed Switch Control 0 */
+		uint32_t rec0;   /* Receive Equalization Control 0 */
+		uint32_t rec1;   /* Receive Equalization Control 1 */
+		uint32_t tec0;   /* Transmit Equalization Control 0 */
+		uint32_t ssc1;   /* Speed Switch Control 1 */
+		uint32_t ttlc;
+		uint32_t rev[6];
+		uint32_t tsc3;
+	} lane[LSX_SERDES_LAN_NB];
+	uint8_t res5[0x19fc - 0xa00];
+} __rte_packed;
+
+struct ccsr_gur {
+	uint32_t	porsr1;		/* POR status 1 */
+	uint32_t	porsr2;		/* POR status 2 */
+	uint8_t	res_008[0x20 - 0x8];
+	uint32_t	gpporcr1; /* General-purpose POR configuration */
+	uint32_t	gpporcr2; /* General-purpose POR configuration 2 */
+	uint32_t	gpporcr3;
+	uint32_t	gpporcr4;
+	uint8_t	res_030[0x60 - 0x30];
+	uint32_t	dcfg_fusesr;	/* Fuse status register */
+	uint8_t	res_064[0x70 - 0x64];
+	uint32_t	devdisr;	/* Device disable control 1 */
+	uint32_t	devdisr2;	/* Device disable control 2 */
+	uint32_t	devdisr3;	/* Device disable control 3 */
+	uint32_t	devdisr4;	/* Device disable control 4 */
+	uint32_t	devdisr5;	/* Device disable control 5 */
+	uint32_t	devdisr6;	/* Device disable control 6 */
+	uint8_t	res_088[0x94 - 0x88];
+	uint32_t	coredisr;	/* Device disable control 7 */
+	uint8_t	res_098[0xa0 - 0x98];
+	uint32_t	pvr;		/* Processor version */
+	uint32_t	svr;		/* System version */
+	uint8_t	res_0a8[0x100 - 0xa8];
+	uint32_t	rcwsr[30];	/* Reset control word status */
+
+	uint8_t	res_178[0x200 - 0x178];
+	uint32_t	scratchrw[16];	/* Scratch Read/Write */
+	uint8_t	res_240[0x300 - 0x240];
+	uint32_t	scratchw1r[4];	/* Scratch Read (Write once) */
+	uint8_t	res_310[0x400 - 0x310];
+	uint32_t	bootlocptrl; /* Boot location pointer low-order addr */
+	uint32_t	bootlocptrh; /* Boot location pointer high-order addr */
+	uint8_t	res_408[0x520 - 0x408];
+	uint32_t	usb1_amqr;
+	uint32_t	usb2_amqr;
+	uint8_t	res_528[0x530 - 0x528];	/* add more registers when needed */
+	uint32_t	sdmm1_amqr;
+	uint32_t	sdmm2_amqr;
+	uint8_t	res_538[0x550 - 0x538];	/* add more registers when needed */
+	uint32_t	sata1_amqr;
+	uint32_t	sata2_amqr;
+	uint32_t	sata3_amqr;
+	uint32_t	sata4_amqr;
+	uint8_t	res_560[0x570 - 0x560];	/* add more registers when needed */
+	uint32_t	misc1_amqr;
+	uint8_t	res_574[0x590 - 0x574];	/* add more registers when needed */
+	uint32_t	spare1_amqr;
+	uint32_t	spare2_amqr;
+	uint32_t	spare3_amqr;
+	uint8_t	res_59c[0x620 - 0x59c];	/* add more registers when needed */
+	uint32_t	gencr[7];	/* General Control Registers */
+	uint8_t	res_63c[0x640 - 0x63c];	/* add more registers when needed */
+	uint32_t	cgensr1;	/* Core General Status Register */
+	uint8_t	res_644[0x660 - 0x644];	/* add more registers when needed */
+	uint32_t	cgencr1;	/* Core General Control Register */
+	uint8_t	res_664[0x740 - 0x664];	/* add more registers when needed */
+	uint32_t	tp_ityp[64];	/* Topology Initiator Type Register */
+	struct {
+		uint32_t	upper;
+		uint32_t	lower;
+	} tp_cluster[4];	/* Core cluster n Topology Register */
+	uint8_t	res_864[0x920 - 0x864];	/* add more registers when needed */
+	uint32_t ioqoscr[8];	/*I/O Quality of Services Register */
+	uint32_t uccr;
+	uint8_t	res_944[0x960 - 0x944];	/* add more registers when needed */
+	uint32_t ftmcr;
+	uint8_t	res_964[0x990 - 0x964];	/* add more registers when needed */
+	uint32_t coredisablesr;
+	uint8_t	res_994[0xa00 - 0x994];	/* add more registers when needed */
+	uint32_t sdbgcr; /*Secure Debug Confifuration Register */
+	uint8_t	res_a04[0xbf8 - 0xa04];	/* add more registers when needed */
+	uint32_t ipbrr1;
+	uint32_t ipbrr2;
+	uint8_t	res_858[0x1000 - 0xc00];
+} __rte_packed;
+
+static void *lsx_ccsr_map_region(uint64_t addr, size_t len)
+{
+	int fd;
+	void *tmp;
+	uint64_t start;
+	uint64_t offset;
+
+	fd = open("/dev/mem", O_RDWR);
+	if (fd < 0) {
+		DPAA2_PMD_ERR("Fail to open /dev/mem");
+		return NULL;
+	}
+
+	start = addr & PAGE_MASK;
+	offset = addr - start;
+	len = len & PAGE_MASK;
+	if (len < (size_t)PAGE_SIZE)
+		len = PAGE_SIZE;
+
+	tmp = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, start);
+
+	close(fd);
+
+	if (tmp != MAP_FAILED)
+		return (uint8_t *)tmp + offset;
+	else
+		return NULL;
+}
+
+static const uint8_t ls_sd1_prot_idx_map[] = {
+	0x03, 0x05, 0x07, 0x09, 0x0a, 0x0c, 0x0e,
+	0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c,
+	0x1e, 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a,
+	0x2b, 0x2d, 0x2e, 0x30, 0x32, 0x33, 0x35,
+	0x37, 0x39, 0x3b, 0x4b, 0x4c, 0x4d, 0x58
+};
+
+static const uint8_t ls_sd2_prot_idx_map[] = {
+	0x07, 0x09, 0x0a, 0x0c, 0x0e, 0x10, 0x12,
+	0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20,
+	0x22, 0x24, 0x3d, 0x3f, 0x41, 0x43, 0x45,
+	0x47, 0x49, 0x4f, 0x50, 0x51, 0x52, 0x53,
+	0x54, 0x55, 0x56, 0x57
+};
+
+static const uint8_t ls_sd1_eth_loopback_support[][LSX_SERDES_LAN_NB] = {
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x03*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x05*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x07*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x09*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0a*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0c*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0e*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x10*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x12*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x14*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x16*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x18*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1a*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1c*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1e*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x20*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x22*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x24*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x26*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x28*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x2a*/
+
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 0x2b*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 0x2d*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 0x2e*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 0x30*/
+
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x32*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x33*/
+
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x35*/
+	{1, 1, 0, 0, 0, 0, 0, 0}, /* 0x37*/
+
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 0x39*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 0x3b*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x4b*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 0x4c*/
+	{0, 0, 1, 1, 0, 0, 1, 1}, /* 0x4d*/
+	{0, 0, 0, 0, 0, 0, 1, 1}  /* 0x58*/
+};
+
+static const uint8_t ls_sd2_eth_loopback_support[][LSX_SERDES_LAN_NB] = {
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x07*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x09*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0a*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0c*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x0e*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x10*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x12*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x14*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x16*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x18*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1a*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1c*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x1e*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x20*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x22*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 0x24*/
+
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x3d*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x3f*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x41*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x43*/
+
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x45*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 0x47*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x49*/
+
+	{0, 0, 1, 1, 0, 0, 1, 1}, /* 0x4f*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x50*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0x51*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 0x52*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 0x53*/
+	{0, 0, 1, 1, 0, 0, 1, 1}, /* 0x54*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 0x55*/
+	{0, 0, 1, 1, 0, 0, 1, 1}, /* 0x56*/
+	{0, 0, 0, 0, 0, 0, 1, 1}  /* 0x57*/
+};
+
+enum lsx_serdes_id {
+	LSX_SERDES_1 = 1,
+	LSX_SERDES_2 = 2
+};
+
+static const uint8_t lx_sd1_loopback_support[][LSX_SERDES_LAN_NB] = {
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 1 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 2 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 3 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 4 prot*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 5 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 6 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 7 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 8 prot*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 9 prot*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 10 prot*/
+	{0, 0, 1, 1, 0, 0, 1, 1}, /* 11 prot*/
+	{0, 0, 0, 0, 0, 0, 1, 1}, /* 12 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 13 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 14 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 15 prot*/
+	{0, 0, 1, 1, 0, 0, 0, 0}, /* 16 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 17 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 18 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 19 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 20 prot*/
+	{1, 1, 1, 1, 0, 0, 1, 1}, /* 21 prot*/
+	{1, 1, 1, 1, 0, 0, 1, 1}  /* 22 prot*/
+};
+
+static const uint8_t lx_sd2_loopback_support[][LSX_SERDES_LAN_NB] = {
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 0 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 1 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 2 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 3 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 4 prot*/
+	{0, 0, 0, 0, 0, 0, 0, 0}, /* 5 prot*/
+	{0, 0, 0, 0, 1, 1, 1, 1}, /* 6 prot*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 7 prot*/
+	{0, 0, 0, 0, 0, 0, 1, 1}, /* 8 prot*/
+	{1, 1, 1, 1, 1, 1, 1, 1}, /* 9 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 10 prot*/
+	{0, 1, 1, 1, 0, 1, 1, 1}, /* 11 prot*/
+	{1, 1, 1, 1, 0, 0, 0, 0}, /* 12 prot*/
+	{0, 0, 0, 0, 0, 0, 1, 1}, /* 13 prot*/
+	{0, 0, 1, 1, 0, 0, 1, 1}  /* 14 prot*/
+};
+
+static inline int
+ls_mac_to_serdes_id(uint8_t mac_id)
+{
+	if (mac_id >= 1 && mac_id <= 8)
+		return LSX_SERDES_1;
+	if (mac_id >= 9 && mac_id <= 16)
+		return LSX_SERDES_2;
+
+	return -1;
+}
+
+static inline int
+lx_mac_to_serdes_id(uint8_t mac_id)
+{
+	if (mac_id >= 1 && mac_id <= 10)
+		return LSX_SERDES_1;
+	if (mac_id >= 11 && mac_id <= 18)
+		return LSX_SERDES_2;
+
+	return -1;
+}
+
+static inline int
+ls_serdes_cfg_to_idx(uint8_t sd_cfg, int sd_id)
+{
+	int i;
+
+	if (sd_id == LSX_SERDES_1) {
+		for (i = 0; i < (int)sizeof(ls_sd1_prot_idx_map); i++) {
+			if (ls_sd1_prot_idx_map[i] == sd_cfg)
+				return i;
+		}
+	} else if (sd_id == LSX_SERDES_2) {
+		for (i = 0; i < (int)sizeof(ls_sd2_prot_idx_map); i++) {
+			if (ls_sd2_prot_idx_map[i] == sd_cfg)
+				return i;
+		}
+	}
+
+	return -1;
+}
+
+static inline int
+lx_serdes_cfg_to_idx(uint8_t sd_cfg, int sd_id __rte_unused)
+{
+	return sd_cfg;
+}
+
+static inline int
+ls_mac_serdes_lpbk_support(uint16_t mac_id,
+	uint16_t *serdes_id, uint16_t *lan_id)
+{
+	struct ccsr_gur *gur_base =
+		lsx_ccsr_map_region(CONFIG_SYS_FSL_GUTS_ADDR,
+			sizeof(struct ccsr_gur) / 64 * 64 + 64);
+	uint32_t sd_cfg;
+	int sd_id, sd_idx;
+	uint16_t lan_id_tmp = 0;
+	const uint8_t *ls_sd_loopback_support;
+
+	sd_id = ls_mac_to_serdes_id(mac_id);
+
+	if (sd_id == LSX_SERDES_1) {
+		sd_cfg = rte_read32(&gur_base->rcwsr[FSL_LS_SRDS1_REGSR - 1]) &
+				FSL_LS_SRDS1_PRTCL_MASK;
+		sd_cfg >>= FSL_LS_SRDS1_PRTCL_SHIFT;
+	} else if (sd_id == LSX_SERDES_2) {
+		sd_cfg = rte_read32(&gur_base->rcwsr[FSL_LS_SRDS2_REGSR - 1]) &
+				FSL_LS_SRDS2_PRTCL_MASK;
+		sd_cfg >>= FSL_LS_SRDS2_PRTCL_SHIFT;
+	} else {
+		return false;
+	}
+	sd_cfg = sd_cfg & 0xff;
+
+	sd_idx = ls_serdes_cfg_to_idx(sd_cfg, sd_id);
+	if (sd_idx < 0) {
+		DPAA2_PMD_ERR("Serdes protocol(0x%02x) does not exist\n",
+			sd_cfg);
+		return false;
+	}
+
+	if (sd_id == LSX_SERDES_1) {
+		ls_sd_loopback_support =
+			&ls_sd1_eth_loopback_support[sd_idx][0];
+	} else {
+		ls_sd_loopback_support =
+			&ls_sd2_eth_loopback_support[sd_idx][0];
+	}
+
+	if (sd_id == LSX_SERDES_1)
+		lan_id_tmp = (mac_id - 1);
+	else
+		lan_id_tmp = (mac_id - 9);
+
+	if (lan_id_tmp >= LSX_SERDES_LAN_NB) {
+		DPAA2_PMD_ERR("Invalid serdes lan(%d).", lan_id_tmp);
+		return false;
+	}
+
+	if (!ls_sd_loopback_support[lan_id_tmp])
+		return false;
+
+	if (lan_id)
+		*lan_id = lan_id_tmp;
+	if (serdes_id)
+		*serdes_id = sd_id;
+
+	return true;
+}
+
+static inline int
+lx_mac_serdes_lpbk_support(uint16_t mac_id,
+	uint16_t *serdes_id, uint16_t *lan_id)
+{
+	struct ccsr_gur *gur_base =
+		lsx_ccsr_map_region(CONFIG_SYS_FSL_GUTS_ADDR,
+			sizeof(struct ccsr_gur) / 64 * 64 + 64);
+	uint32_t sd_cfg;
+	int sd_id, sd_idx;
+	uint16_t lan_id_tmp = 0;
+	const uint8_t *lx_sd_loopback_support;
+
+	sd_id = lx_mac_to_serdes_id(mac_id);
+
+	if (sd_id == LSX_SERDES_1) {
+		sd_cfg = rte_read32(&gur_base->rcwsr[FSL_LX_SRDS1_REGSR - 1]) &
+				FSL_LX_SRDS1_PRTCL_MASK;
+		sd_cfg >>= FSL_LX_SRDS1_PRTCL_SHIFT;
+	} else if (sd_id == LSX_SERDES_2) {
+		sd_cfg = rte_read32(&gur_base->rcwsr[FSL_LX_SRDS2_REGSR - 1]) &
+				FSL_LX_SRDS2_PRTCL_MASK;
+		sd_cfg >>= FSL_LX_SRDS2_PRTCL_SHIFT;
+	} else {
+		return false;
+	}
+	sd_cfg = sd_cfg & 0xff;
+
+	sd_idx = lx_serdes_cfg_to_idx(sd_cfg, sd_id);
+	if (sd_idx < 0)
+		return false;
+
+	if (sd_id == LSX_SERDES_1)
+		lx_sd_loopback_support = &lx_sd1_loopback_support[sd_idx][0];
+	else
+		lx_sd_loopback_support = &lx_sd2_loopback_support[sd_idx][0];
+
+	if (sd_id == LSX_SERDES_1) {
+		if (mac_id == 1)
+			lan_id_tmp = 0;
+		else if (mac_id == 2)
+			lan_id_tmp = 4;
+		else
+			lan_id_tmp = (mac_id - 3);
+	} else {
+		if (mac_id == 11)
+			lan_id_tmp = 0;
+		else if (mac_id == 12)
+			lan_id_tmp = 1;
+		else if (mac_id == 13)
+			lan_id_tmp = 6;
+		else if (mac_id == 14)
+			lan_id_tmp = 7;
+		else if (mac_id == 15)
+			lan_id_tmp = 4;
+		else if (mac_id == 16)
+			lan_id_tmp = 5;
+		else if (mac_id == 17)
+			lan_id_tmp = 2;
+		else if (mac_id == 18)
+			lan_id_tmp = 3;
+		else
+			return false;
+	}
+
+	if (lan_id_tmp >= LSX_SERDES_LAN_NB)
+		return false;
+
+	if (!lx_sd_loopback_support[lan_id_tmp])
+		return false;
+
+	if (lan_id)
+		*lan_id = lan_id_tmp;
+	if (serdes_id)
+		*serdes_id = sd_id;
+
+	return true;
+}
+
+static inline int
+ls_serdes_eth_lpbk(uint16_t mac_id, int en)
+{
+	uint16_t serdes_id, lan_id;
+	int ret;
+	uint32_t data;
+	struct ccsr_ls_serdes *serdes_base;
+	void *reg = 0;
+
+	ret = ls_mac_serdes_lpbk_support(mac_id, &serdes_id, &lan_id);
+	if (!ret)
+		return -ENOTSUP;
+
+	serdes_base = lsx_ccsr_map_region(CONFIG_SYS_FSL_SERDES_ADDR +
+				(serdes_id - LSX_SERDES_1) * 0x10000,
+				sizeof(struct ccsr_ls_serdes) / 64 * 64 + 64);
+	if (!serdes_base) {
+		DPAA2_PMD_ERR("Serdes register map failed\n");
+		return -ENOMEM;
+	}
+
+	if (serdes_id == LSX_SERDES_1)
+		lan_id = LSX_SERDES_LAN_NB - lan_id - 1;
+
+	reg = &serdes_base->lane[lan_id].tsc3;
+
+	data = rte_read32(reg);
+	if (en)
+		rte_write32(data | LSX_LB_EN_BIT, reg);
+	else
+		rte_write32(data & (~LSX_LB_EN_BIT), reg);
+
+	return 0;
+}
+
+static inline int
+lx_serdes_eth_lpbk(uint16_t mac_id, int en)
+{
+	uint16_t serdes_id = 0xffff, lan_id = 0xffff;
+	int ret;
+	uint32_t data;
+	struct ccsr_lx_serdes *serdes_base;
+	void *reg = 0;
+
+	ret = lx_mac_serdes_lpbk_support(mac_id, &serdes_id, &lan_id);
+	if (!ret)
+		return -ENOTSUP;
+
+	serdes_base = lsx_ccsr_map_region(CONFIG_SYS_FSL_SERDES_ADDR +
+					(serdes_id - LSX_SERDES_1) * 0x10000,
+					sizeof(struct ccsr_lx_serdes) / 64 * 64 + 64);
+	if (!serdes_base) {
+		DPAA2_PMD_ERR("Serdes register map failed\n");
+		return -ENOMEM;
+	}
+
+	if (serdes_id == LSX_SERDES_1)
+		lan_id = LSX_SERDES_LAN_NB - lan_id - 1;
+
+	reg = &serdes_base->lane[lan_id].lnatcsr0;
+
+	data = rte_read32(reg);
+	if (en)
+		rte_write32(data | LSX_LB_EN_BIT, reg);
+	else
+		rte_write32(data & (~LSX_LB_EN_BIT), reg);
+
+	return 0;
+}
+
+/* Configure dpaa2 port as recycle port */
+int
+dpaa2_dev_recycle_config(struct rte_eth_dev *eth_dev)
+{
+	struct rte_device *dev = eth_dev->device;
+	struct dpaa2_dev_priv *priv = eth_dev->data->dev_private;
+	struct rte_dpaa2_device *dpaa2_dev =
+			container_of(dev, struct rte_dpaa2_device, device);
+	struct fsl_mc_io *dpni_dev = eth_dev->process_private;
+	struct dpni_port_cfg port_cfg;
+	int ret;
+
+	if (priv->flags & DPAA2_TX_LOOPBACK_MODE) {
+		DPAA2_PMD_INFO("%s has been configured recycle device.",
+			eth_dev->data->name);
+
+		return 0;
+	}
+
+	if (dpaa2_dev->ep_dev_type == DPAA2_MAC) {
+		/** For dpmac-dpni connection,
+		 * try setting serdes loopback as recycle device at first.
+		 */
+		if (dpaa2_svr_family == SVR_LS2088A) {
+			ret = ls_serdes_eth_lpbk(dpaa2_dev->ep_object_id, 1);
+			if (!ret) {
+				priv->flags |= DPAA2_TX_SERDES_LOOPBACK_MODE;
+				return 0;
+			}
+		} else if (dpaa2_svr_family == SVR_LX2160A) {
+			ret = lx_serdes_eth_lpbk(dpaa2_dev->ep_object_id, 1);
+			if (!ret) {
+				priv->flags |= DPAA2_TX_SERDES_LOOPBACK_MODE;
+				return 0;
+			}
+		} else {
+			DPAA2_PMD_DEBUG("Serdes loopback not support SoC(0x%08x)",
+				dpaa2_svr_family);
+		}
+
+		/** If serdes loopback is not supported for this mac,
+		 * trying set mac loopback.
+		 */
+
+		port_cfg.loopback_en = 1;
+		ret = dpni_set_port_cfg(dpni_dev, CMD_PRI_LOW,
+				priv->token,
+				DPNI_PORT_CFG_LOOPBACK,
+				&port_cfg);
+		if (ret) {
+			DPAA2_PMD_ERR("Error(%d) to enable loopback", ret);
+			return -ENOTSUP;
+		}
+
+		priv->flags |= DPAA2_TX_MAC_LOOPBACK_MODE;
+
+		return 0;
+	}
+
+	if (dpaa2_dev->ep_dev_type == DPAA2_ETH &&
+		dpaa2_dev->object_id == dpaa2_dev->ep_object_id) {
+		priv->flags |= DPAA2_TX_DPNI_LOOPBACK_MODE;
+
+		return 0;
+	}
+
+	return -ENOTSUP;
+}
+
+int
+dpaa2_dev_recycle_deconfig(struct rte_eth_dev *eth_dev)
+{
+	struct rte_device *dev = eth_dev->device;
+	struct dpaa2_dev_priv *priv = eth_dev->data->dev_private;
+	struct rte_dpaa2_device *dpaa2_dev =
+			container_of(dev, struct rte_dpaa2_device, device);
+	struct fsl_mc_io *dpni_dev = eth_dev->process_private;
+	struct dpni_port_cfg port_cfg;
+	int ret = 0;
+
+	if (!(priv->flags & DPAA2_TX_LOOPBACK_MODE))
+		return 0;
+
+	if (priv->flags & DPAA2_TX_SERDES_LOOPBACK_MODE) {
+		if (dpaa2_svr_family == SVR_LS2088A) {
+			ret = ls_serdes_eth_lpbk(dpaa2_dev->ep_object_id, 0);
+			if (ret) {
+				DPAA2_PMD_WARN("Error(%d) to disable Serdes loopback",
+					ret);
+			} else {
+				priv->flags &= ~DPAA2_TX_SERDES_LOOPBACK_MODE;
+			}
+		} else if (dpaa2_svr_family == SVR_LX2160A) {
+			ret = lx_serdes_eth_lpbk(dpaa2_dev->ep_object_id, 0);
+			if (ret) {
+				DPAA2_PMD_WARN("Error(%d) to disable Serdes loopback",
+					ret);
+			} else {
+				priv->flags &= ~DPAA2_TX_SERDES_LOOPBACK_MODE;
+			}
+		} else {
+			DPAA2_PMD_DEBUG("Serdes loopback not support SoC(0x%08x)",
+				dpaa2_svr_family);
+		}
+	}
+
+	if (priv->flags & DPAA2_TX_MAC_LOOPBACK_MODE) {
+		port_cfg.loopback_en = 0;
+		ret = dpni_set_port_cfg(dpni_dev, CMD_PRI_LOW,
+				priv->token,
+				DPNI_PORT_CFG_LOOPBACK,
+				&port_cfg);
+		if (ret) {
+			DPAA2_PMD_ERR("Error(%d) to disable TX mac loopback",
+				ret);
+		} else {
+			priv->flags &= ~DPAA2_TX_MAC_LOOPBACK_MODE;
+		}
+	}
+
+	if (priv->flags & DPAA2_TX_DPNI_LOOPBACK_MODE)
+		priv->flags &= ~DPAA2_TX_DPNI_LOOPBACK_MODE;
+
+	return ret;
+}
+
+int
+dpaa2_dev_recycle_qp_setup(struct rte_dpaa2_device *dpaa2_dev,
+	uint16_t qidx, uint64_t cntx,
+	eth_rx_burst_t tx_lpbk, eth_tx_burst_t rx_lpbk,
+	struct dpaa2_queue **txq,
+	struct dpaa2_queue **rxq)
+{
+	struct rte_eth_dev *dev;
+	struct rte_eth_dev_data *data;
+	struct dpaa2_queue *txq_tmp;
+	struct dpaa2_queue *rxq_tmp;
+	struct dpaa2_dev_priv *priv;
+
+	dev = dpaa2_dev->eth_dev;
+	data = dev->data;
+	priv = data->dev_private;
+
+	if (!(priv->flags & DPAA2_TX_LOOPBACK_MODE) &&
+		(tx_lpbk || rx_lpbk)) {
+		DPAA2_PMD_ERR("%s is NOT recycle device!", data->name);
+
+		return -EINVAL;
+	}
+
+	if (qidx >= data->nb_rx_queues || qidx >= data->nb_tx_queues)
+		return -EINVAL;
+
+	rte_spinlock_lock(&priv->lpbk_qp_lock);
+
+	if (tx_lpbk)
+		dev->tx_pkt_burst = tx_lpbk;
+
+	if (rx_lpbk)
+		dev->rx_pkt_burst = rx_lpbk;
+
+	txq_tmp = data->tx_queues[qidx];
+	txq_tmp->lpbk_cntx = cntx;
+	rxq_tmp = data->rx_queues[qidx];
+	rxq_tmp->lpbk_cntx = cntx;
+
+	if (txq)
+		*txq = txq_tmp;
+	if (rxq)
+		*rxq = rxq_tmp;
+
+	rte_spinlock_unlock(&priv->lpbk_qp_lock);
+
+	return 0;
+}
diff --git a/drivers/net/dpaa2/mc/dpni.c b/drivers/net/dpaa2/mc/dpni.c
index b7a65cb637..7a2bc15eb4 100644
--- a/drivers/net/dpaa2/mc/dpni.c
+++ b/drivers/net/dpaa2/mc/dpni.c
@@ -3087,3 +3087,35 @@  int dpni_get_custom_tpid(struct fsl_mc_io *mc_io, uint32_t cmd_flags,
 	return err;
 }
 
+/**
+ * dpni_set_port_cfg() - performs configurations at physical port connected on
+ *		this dpni. The command have effect only if dpni is connected to
+ *		another dpni object
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPNI object
+ * @flags:	Valid fields from port_cfg structure
+ * @port_cfg: Configuration data; one or more of DPNI_PORT_CFG_
+ * The command can be called only when dpni is connected to a dpmac object. If
+ * the dpni is unconnected or the endpoint is not a dpni it will return error.
+ * If dpmac endpoint is disconnected the settings will be lost
+ */
+int dpni_set_port_cfg(struct fsl_mc_io *mc_io, uint32_t cmd_flags,
+		uint16_t token, uint32_t flags, struct dpni_port_cfg *port_cfg)
+{
+	struct dpni_cmd_set_port_cfg *cmd_params;
+	struct mc_command cmd = { 0 };
+
+	/* prepare command */
+	cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_PORT_CFG,
+			cmd_flags, token);
+
+	cmd_params = (struct dpni_cmd_set_port_cfg *)cmd.params;
+	cmd_params->flags = cpu_to_le32(flags);
+	dpni_set_field(cmd_params->bit_params,	PORT_LOOPBACK_EN,
+			!!port_cfg->loopback_en);
+
+	/* send command to MC */
+	return mc_send_command(mc_io, &cmd);
+}
+
diff --git a/drivers/net/dpaa2/mc/fsl_dpni_cmd.h b/drivers/net/dpaa2/mc/fsl_dpni_cmd.h
index ed0bd7615a..b7bd7556af 100644
--- a/drivers/net/dpaa2/mc/fsl_dpni_cmd.h
+++ b/drivers/net/dpaa2/mc/fsl_dpni_cmd.h
@@ -119,6 +119,7 @@ 
 #define DPNI_CMDID_REMOVE_CUSTOM_TPID		DPNI_CMD(0x276)
 #define DPNI_CMDID_GET_CUSTOM_TPID		DPNI_CMD(0x277)
 #define DPNI_CMDID_GET_LINK_CFG			DPNI_CMD(0x278)
+#define DPNI_CMDID_SET_PORT_CFG			DPNI_CMD(0x27B)
 
 /* Macros for accessing command fields smaller than 1byte */
 #define DPNI_MASK(field)	\
diff --git a/drivers/net/dpaa2/meson.build b/drivers/net/dpaa2/meson.build
index 21b827a259..51598c048c 100644
--- a/drivers/net/dpaa2/meson.build
+++ b/drivers/net/dpaa2/meson.build
@@ -14,6 +14,7 @@  sources = files(
         'dpaa2_mux.c',
         'dpaa2_ethdev.c',
         'dpaa2_flow.c',
+        'dpaa2_recycle.c',
         'dpaa2_rxtx.c',
         'dpaa2_sparser.c',
         'dpaa2_ptp.c',