From 7c7baa32e0d110d5446113f5513fca84731bddd3 Mon Sep 17 00:00:00 2001
From: Suruchi Agarwal <quic_suruchia@quicinc.com>
Date: Thu, 21 Mar 2024 16:21:19 -0700
Subject: [PATCH 40/50] net: ethernet: qualcomm: Add Rx Ethernet DMA support

Add Rx queues, rings, descriptors configurations and
DMA support for the EDMA.

Change-Id: I612bcd661e74d5bf3ecb33de10fd5298d18ff7e9
Co-developed-by: Pavithra R <quic_pavir@quicinc.com>
Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
---
 drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
 drivers/net/ethernet/qualcomm/ppe/edma.c      | 171 +++-
 drivers/net/ethernet/qualcomm/ppe/edma.h      |  18 +-
 .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 964 ++++++++++++++++++
 .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   |  48 +
 drivers/net/ethernet/qualcomm/ppe/edma_port.c |  39 +-
 drivers/net/ethernet/qualcomm/ppe/edma_port.h |  31 +
 drivers/net/ethernet/qualcomm/ppe/edma_rx.c   | 622 +++++++++++
 drivers/net/ethernet/qualcomm/ppe/edma_rx.h   | 287 ++++++
 9 files changed, 2177 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
 create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
 create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_rx.c
 create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_rx.h

--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
+++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
 qcom-ppe-objs := ppe.o ppe_config.o ppe_api.o ppe_debugfs.o ppe_port.o
 
 #EDMA
-qcom-ppe-objs += edma.o edma_port.o
\ No newline at end of file
+qcom-ppe-objs += edma.o edma_cfg_rx.o edma_port.o edma_rx.o
\ No newline at end of file
--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
+++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
@@ -18,12 +18,23 @@
 #include <linux/reset.h>
 
 #include "edma.h"
+#include "edma_cfg_rx.h"
 #include "ppe_regs.h"
 
 #define EDMA_IRQ_NAME_SIZE		32
 
 /* Global EDMA context. */
 struct edma_context *edma_ctx;
+static char **edma_rxdesc_irq_name;
+
+/* Module params. */
+static int page_mode;
+module_param(page_mode, int, 0);
+MODULE_PARM_DESC(page_mode, "Enable page mode (default:0)");
+
+static int rx_buff_size;
+module_param(rx_buff_size, int, 0640);
+MODULE_PARM_DESC(rx_buff_size, "Rx Buffer size for Jumbo MRU value (default:0)");
 
 /* Priority to multi-queue mapping. */
 static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
@@ -178,6 +189,59 @@ static int edma_configure_ucast_prio_map
 	return ret;
 }
 
+static int edma_irq_register(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	int ret;
+	u32 i;
+
+	/* Request IRQ for RXDESC rings. */
+	edma_rxdesc_irq_name = kzalloc((sizeof(char *) * rx->num_rings),
+				       GFP_KERNEL);
+	if (!edma_rxdesc_irq_name)
+		return -ENOMEM;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		edma_rxdesc_irq_name[i] = kzalloc((sizeof(char *) * EDMA_IRQ_NAME_SIZE),
+						  GFP_KERNEL);
+		if (!edma_rxdesc_irq_name[i]) {
+			ret = -ENOMEM;
+			goto rxdesc_irq_name_alloc_fail;
+		}
+
+		snprintf(edma_rxdesc_irq_name[i], 20, "edma_rxdesc_%d",
+			 rx->ring_start + i);
+
+		irq_set_status_flags(edma_ctx->intr_info.intr_rx[i], IRQ_DISABLE_UNLAZY);
+
+		ret = request_irq(edma_ctx->intr_info.intr_rx[i],
+				  edma_rx_handle_irq, IRQF_SHARED,
+				  edma_rxdesc_irq_name[i],
+				  (void *)&edma_ctx->rx_rings[i]);
+		if (ret) {
+			pr_err("RXDESC ring IRQ:%d request failed\n",
+			       edma_ctx->intr_info.intr_rx[i]);
+			goto rx_desc_ring_intr_req_fail;
+		}
+
+		pr_debug("RXDESC ring: %d IRQ:%d request success: %s\n",
+			 rx->ring_start + i,
+			 edma_ctx->intr_info.intr_rx[i],
+			 edma_rxdesc_irq_name[i]);
+	}
+
+	return 0;
+
+rx_desc_ring_intr_req_fail:
+	for (i = 0; i < rx->num_rings; i++)
+		kfree(edma_rxdesc_irq_name[i]);
+rxdesc_irq_name_alloc_fail:
+	kfree(edma_rxdesc_irq_name);
+
+	return ret;
+}
+
 static int edma_irq_init(void)
 {
 	struct edma_hw_info *hw_info = edma_ctx->hw_info;
@@ -260,6 +324,16 @@ static int edma_irq_init(void)
 	return 0;
 }
 
+static int edma_alloc_rings(void)
+{
+	if (edma_cfg_rx_rings_alloc()) {
+		pr_err("Error in allocating Rx rings\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
 static int edma_hw_reset(void)
 {
 	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
@@ -343,6 +417,40 @@ static int edma_hw_configure(void)
 	if (!edma_ctx->netdev_arr)
 		return -ENOMEM;
 
+	edma_ctx->dummy_dev = alloc_netdev_dummy(0);
+	if (!edma_ctx->dummy_dev) {
+		ret = -ENOMEM;
+		pr_err("Failed to allocate dummy device. ret: %d\n", ret);
+		goto dummy_dev_alloc_failed;
+	}
+
+	/* Set EDMA jumbo MRU if enabled or set page mode. */
+	if (edma_ctx->rx_buf_size) {
+		edma_ctx->rx_page_mode = false;
+		pr_debug("Rx Jumbo mru is enabled: %d\n", edma_ctx->rx_buf_size);
+	} else {
+		edma_ctx->rx_page_mode = page_mode;
+	}
+
+	ret = edma_alloc_rings();
+	if (ret) {
+		pr_err("Error in initializaing the rings. ret: %d\n", ret);
+		goto edma_alloc_rings_failed;
+	}
+
+	/* Disable interrupts. */
+	edma_cfg_rx_disable_interrupts();
+
+	edma_cfg_rx_rings_disable();
+
+	edma_cfg_rx_ring_mappings();
+
+	ret = edma_cfg_rx_rings();
+	if (ret) {
+		pr_err("Error in configuring Rx rings. ret: %d\n", ret);
+		goto edma_cfg_rx_rings_failed;
+	}
+
 	/* Configure DMA request priority, DMA read burst length,
 	 * and AXI write size.
 	 */
@@ -376,6 +484,10 @@ static int edma_hw_configure(void)
 	data |= EDMA_MISC_TX_TIMEOUT_MASK;
 	edma_ctx->intr_info.intr_mask_misc = data;
 
+	edma_cfg_rx_rings_enable();
+	edma_cfg_rx_napi_add();
+	edma_cfg_rx_napi_enable();
+
 	/* Global EDMA enable and padding enable. */
 	data = EDMA_PORT_PAD_EN | EDMA_PORT_EDMA_EN;
 
@@ -389,11 +501,32 @@ static int edma_hw_configure(void)
 	if (ret) {
 		pr_err("Failed to initialize unicast priority map table: %d\n",
 		       ret);
-		kfree(edma_ctx->netdev_arr);
-		return ret;
+		goto configure_ucast_prio_map_tbl_failed;
+	}
+
+	/* Initialize RPS hash map table. */
+	ret = edma_cfg_rx_rps_hash_map();
+	if (ret) {
+		pr_err("Failed to configure rps hash table: %d\n",
+		       ret);
+		goto edma_cfg_rx_rps_hash_map_failed;
 	}
 
 	return 0;
+
+edma_cfg_rx_rps_hash_map_failed:
+configure_ucast_prio_map_tbl_failed:
+	edma_cfg_rx_napi_disable();
+	edma_cfg_rx_napi_delete();
+	edma_cfg_rx_rings_disable();
+edma_cfg_rx_rings_failed:
+	edma_cfg_rx_rings_cleanup();
+edma_alloc_rings_failed:
+	free_netdev(edma_ctx->dummy_dev);
+dummy_dev_alloc_failed:
+	kfree(edma_ctx->netdev_arr);
+
+	return ret;
 }
 
 /**
@@ -404,8 +537,31 @@ static int edma_hw_configure(void)
  */
 void edma_destroy(struct ppe_device *ppe_dev)
 {
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	/* Disable interrupts. */
+	edma_cfg_rx_disable_interrupts();
+
+	/* Free IRQ for RXDESC rings. */
+	for (i = 0; i < rx->num_rings; i++) {
+		synchronize_irq(edma_ctx->intr_info.intr_rx[i]);
+		free_irq(edma_ctx->intr_info.intr_rx[i],
+			 (void *)&edma_ctx->rx_rings[i]);
+		kfree(edma_rxdesc_irq_name[i]);
+	}
+	kfree(edma_rxdesc_irq_name);
+
 	kfree(edma_ctx->intr_info.intr_rx);
 	kfree(edma_ctx->intr_info.intr_txcmpl);
+
+	edma_cfg_rx_napi_disable();
+	edma_cfg_rx_napi_delete();
+	edma_cfg_rx_rings_disable();
+	edma_cfg_rx_rings_cleanup();
+
+	free_netdev(edma_ctx->dummy_dev);
 	kfree(edma_ctx->netdev_arr);
 }
 
@@ -428,6 +584,7 @@ int edma_setup(struct ppe_device *ppe_de
 
 	edma_ctx->hw_info = &ipq9574_hw_info;
 	edma_ctx->ppe_dev = ppe_dev;
+	edma_ctx->rx_buf_size = rx_buff_size;
 
 	/* Configure the EDMA common clocks. */
 	ret = edma_clock_init();
@@ -450,6 +607,16 @@ int edma_setup(struct ppe_device *ppe_de
 		return ret;
 	}
 
+	ret = edma_irq_register();
+	if (ret) {
+		dev_err(dev, "Error in irq registration\n");
+		kfree(edma_ctx->intr_info.intr_rx);
+		kfree(edma_ctx->intr_info.intr_txcmpl);
+		return ret;
+	}
+
+	edma_cfg_rx_enable_interrupts();
+
 	dev_info(dev, "EDMA configuration successful\n");
 
 	return 0;
--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
+++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
@@ -6,6 +6,7 @@
 #define __EDMA_MAIN__
 
 #include "ppe_api.h"
+#include "edma_rx.h"
 
 /* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
  *
@@ -29,6 +30,11 @@
 /* Interface ID start. */
 #define EDMA_START_IFNUM   1
 
+#define EDMA_DESC_AVAIL_COUNT(head, tail, _max) ({ \
+			typeof(_max) (max) = (_max); \
+			((((head) - (tail)) + \
+			(max)) & ((max) - 1)); })
+
 /**
  * struct edma_ring_info - EDMA ring data structure.
  * @max_rings: Maximum number of rings
@@ -82,18 +88,28 @@ struct edma_intr_info {
 /**
  * struct edma_context - EDMA context.
  * @netdev_arr: Net device for each EDMA port
+ * @dummy_dev: Dummy netdevice for RX DMA
  * @ppe_dev: PPE device
  * @hw_info: EDMA Hardware info
  * @intr_info: EDMA Interrupt info
+ * @rxfill_rings: Rx fill Rings, SW is producer
+ * @rx_rings: Rx Desc Rings, SW is consumer
+ * @rx_page_mode: Page mode enabled or disabled
+ * @rx_buf_size: Rx buffer size for Jumbo MRU
  */
 struct edma_context {
 	struct net_device **netdev_arr;
+	struct net_device *dummy_dev;
 	struct ppe_device *ppe_dev;
 	struct edma_hw_info *hw_info;
 	struct edma_intr_info intr_info;
+	struct edma_rxfill_ring *rxfill_rings;
+	struct edma_rxdesc_ring *rx_rings;
+	u32 rx_page_mode;
+	u32 rx_buf_size;
 };
 
-/* Global EDMA context. */
+/* Global EDMA context */
 extern struct edma_context *edma_ctx;
 
 void edma_destroy(struct ppe_device *ppe_dev);
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/* Configure rings, Buffers and NAPI for receive path along with
+ * providing APIs to enable, disable, clean and map the Rx rings.
+ */
+
+#include <linux/cpumask.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+#include <linux/skbuff.h>
+
+#include "edma.h"
+#include "edma_cfg_rx.h"
+#include "ppe.h"
+#include "ppe_regs.h"
+
+/* EDMA Queue ID to Ring ID Table. */
+#define EDMA_QID2RID_TABLE_MEM(q)	(0xb9000 + (0x4 * (q)))
+
+/* Rx ring queue offset. */
+#define EDMA_QUEUE_OFFSET(q_id)	((q_id) / EDMA_MAX_PRI_PER_CORE)
+
+/* Rx EDMA maximum queue supported. */
+#define EDMA_CPU_PORT_QUEUE_MAX(queue_start)	\
+			((queue_start) + (EDMA_MAX_PRI_PER_CORE * num_possible_cpus()) - 1)
+
+/* EDMA Queue ID to Ring ID configuration. */
+#define EDMA_QID2RID_NUM_PER_REG	4
+
+int rx_queues[] = {0, 8, 16, 24};
+
+static u32 edma_rx_ring_queue_map[][EDMA_MAX_CORE] = {{ 0, 8, 16, 24 },
+						{ 1, 9, 17, 25 },
+						{ 2, 10, 18, 26 },
+						{ 3, 11, 19, 27 },
+						{ 4, 12, 20, 28 },
+						{ 5, 13, 21, 29 },
+						{ 6, 14, 22, 30 },
+						{ 7, 15, 23, 31 }};
+
+static int edma_cfg_rx_desc_rings_reset_queue_mapping(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, ret;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		ret = ppe_edma_ring_to_queues_config(edma_ctx->ppe_dev, rxdesc_ring->ring_id,
+						     ARRAY_SIZE(rx_queues), rx_queues);
+		if (ret) {
+			pr_err("Error in unmapping rxdesc ring %d to PPE queue mapping to disable its backpressure configuration\n",
+			       i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int edma_cfg_rx_desc_ring_reset_queue_priority(u32 rxdesc_ring_idx)
+{
+	u32 i, queue_id, ret;
+
+	for (i = 0; i < EDMA_MAX_PRI_PER_CORE; i++) {
+		queue_id = edma_rx_ring_queue_map[i][rxdesc_ring_idx];
+
+		ret = ppe_queue_priority_set(edma_ctx->ppe_dev, queue_id, i);
+		if (ret) {
+			pr_err("Error in resetting %u queue's priority\n",
+			       queue_id);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int edma_cfg_rx_desc_ring_reset_queue_config(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, ret;
+
+	if (unlikely(rx->num_rings > num_possible_cpus())) {
+		pr_err("Invalid count of rxdesc rings: %d\n",
+		       rx->num_rings);
+		return -EINVAL;
+	}
+
+	/* Unmap Rxdesc ring to PPE queue mapping */
+	ret = edma_cfg_rx_desc_rings_reset_queue_mapping();
+	if (ret)	{
+		pr_err("Error in resetting Rx desc ring backpressure config\n");
+		return ret;
+	}
+
+	/* Reset the priority for PPE queues mapped to Rx rings */
+	for (i = 0; i < rx->num_rings; i++) {
+		ret =  edma_cfg_rx_desc_ring_reset_queue_priority(i);
+		if (ret)	{
+			pr_err("Error in resetting ring:%d queue's priority\n",
+			       i + rx->ring_start);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int edma_cfg_rx_desc_ring_to_queue_mapping(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+	int ret;
+
+	/* Rxdesc ring to PPE queue mapping */
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		ret = ppe_edma_ring_to_queues_config(edma_ctx->ppe_dev,
+						     rxdesc_ring->ring_id,
+						ARRAY_SIZE(rx_queues), rx_queues);
+		if (ret) {
+			pr_err("Error in configuring Rx ring to PPE queue mapping, ret: %d, id: %d\n",
+			       ret, rxdesc_ring->ring_id);
+			if (!edma_cfg_rx_desc_rings_reset_queue_mapping())
+				pr_err("Error in resetting Rx desc ringbackpressure configurations\n");
+
+			return ret;
+		}
+
+		pr_debug("Rx desc ring %d to PPE queue mapping for backpressure:\n",
+			 rxdesc_ring->ring_id);
+	}
+
+	return 0;
+}
+
+static void edma_cfg_rx_desc_ring_configure(struct edma_rxdesc_ring *rxdesc_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	u32 data, reg;
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_BA(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, (u32)(rxdesc_ring->pdma & EDMA_RXDESC_BA_MASK));
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PREHEADER_BA(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, (u32)(rxdesc_ring->sdma & EDMA_RXDESC_PREHEADER_BA_MASK));
+
+	data = rxdesc_ring->count & EDMA_RXDESC_RING_SIZE_MASK;
+	data |= (EDMA_RXDESC_PL_DEFAULT_VALUE & EDMA_RXDESC_PL_OFFSET_MASK)
+		 << EDMA_RXDESC_PL_OFFSET_SHIFT;
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_RING_SIZE(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, data);
+
+	/* Configure the Mitigation timer */
+	data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_RX_MITIGATION_TIMER_DEF,
+					   ppe_dev->clk_rate / MHZ);
+	data = ((data & EDMA_RX_MOD_TIMER_INIT_MASK)
+			<< EDMA_RX_MOD_TIMER_INIT_SHIFT);
+	pr_debug("EDMA Rx mitigation timer value: %d\n", data);
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RX_MOD_TIMER(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, data);
+
+	/* Configure the Mitigation packet count */
+	data = (EDMA_RX_MITIGATION_PKT_CNT_DEF & EDMA_RXDESC_LOW_THRE_MASK)
+			<< EDMA_RXDESC_LOW_THRE_SHIFT;
+	pr_debug("EDMA Rx mitigation packet count value: %d\n", data);
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_UGT_THRE(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, data);
+
+	/* Enable ring. Set ret mode to 'opaque'. */
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RX_INT_CTRL(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, EDMA_RX_NE_INT_EN);
+}
+
+static void edma_cfg_rx_qid_to_rx_desc_ring_mapping(void)
+{
+	u32 desc_index, ring_index, reg_index, data, q_id;
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 mcast_start, mcast_end, reg;
+	int ret;
+
+	desc_index = (rx->ring_start & EDMA_RX_RING_ID_MASK);
+
+	/* Here map all the queues to ring. */
+	for (q_id = EDMA_RX_QUEUE_START;
+		q_id <= EDMA_CPU_PORT_QUEUE_MAX(EDMA_RX_QUEUE_START);
+			q_id += EDMA_QID2RID_NUM_PER_REG) {
+		reg_index = q_id / EDMA_QID2RID_NUM_PER_REG;
+		ring_index = desc_index + EDMA_QUEUE_OFFSET(q_id);
+
+		data = FIELD_PREP(EDMA_RX_RING_ID_QUEUE0_MASK, ring_index);
+		data |= FIELD_PREP(EDMA_RX_RING_ID_QUEUE1_MASK, ring_index);
+		data |=	FIELD_PREP(EDMA_RX_RING_ID_QUEUE2_MASK, ring_index);
+		data |=	FIELD_PREP(EDMA_RX_RING_ID_QUEUE3_MASK, ring_index);
+
+		reg = EDMA_BASE_OFFSET + EDMA_QID2RID_TABLE_MEM(reg_index);
+		regmap_write(regmap, reg, data);
+		pr_debug("Configure QID2RID: %d reg:0x%x to 0x%x, desc_index: %d, reg_index: %d\n",
+			 q_id, EDMA_QID2RID_TABLE_MEM(reg_index), data, desc_index, reg_index);
+	}
+
+	ret = ppe_edma_queue_resource_get(edma_ctx->ppe_dev, PPE_RES_MCAST,
+					  &mcast_start, &mcast_end);
+	if (ret < 0) {
+		pr_err("Error in extracting multicast queue values\n");
+		return;
+	}
+
+	/* Map multicast queues to the first Rx ring. */
+	desc_index = (rx->ring_start & EDMA_RX_RING_ID_MASK);
+	for (q_id = mcast_start; q_id <= mcast_end;
+			q_id += EDMA_QID2RID_NUM_PER_REG) {
+		reg_index = q_id / EDMA_QID2RID_NUM_PER_REG;
+
+		data = FIELD_PREP(EDMA_RX_RING_ID_QUEUE0_MASK, desc_index);
+		data |=	FIELD_PREP(EDMA_RX_RING_ID_QUEUE1_MASK, desc_index);
+		data |=	FIELD_PREP(EDMA_RX_RING_ID_QUEUE2_MASK, desc_index);
+		data |=	FIELD_PREP(EDMA_RX_RING_ID_QUEUE3_MASK, desc_index);
+
+		reg = EDMA_BASE_OFFSET + EDMA_QID2RID_TABLE_MEM(reg_index);
+		regmap_write(regmap, reg, data);
+
+		pr_debug("Configure QID2RID: %d reg:0x%x to 0x%x\n",
+			 q_id, EDMA_QID2RID_TABLE_MEM(reg_index), data);
+	}
+}
+
+static void edma_cfg_rx_rings_to_rx_fill_mapping(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, data, reg;
+
+	regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR, 0);
+	regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR, 0);
+	regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR, 0);
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
+		u32 data, reg, ring_id;
+
+		ring_id = rxdesc_ring->ring_id;
+		if (ring_id >= 0 && ring_id <= 9)
+			reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR;
+		else if (ring_id >= 10 && ring_id <= 19)
+			reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR;
+		else
+			reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR;
+
+		pr_debug("Configure RXDESC:%u to use RXFILL:%u\n",
+			 ring_id,
+			 rxdesc_ring->rxfill->ring_id);
+
+		 /* Set the Rx fill ring number in the mapping register. */
+		regmap_read(regmap, reg, &data);
+		data |= (rxdesc_ring->rxfill->ring_id &
+			 EDMA_RXDESC2FILL_MAP_RXDESC_MASK) <<
+			 ((ring_id % 10) * 3);
+		regmap_write(regmap, reg, data);
+	}
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_0_ADDR;
+	regmap_read(regmap, reg, &data);
+	pr_debug("EDMA_REG_RXDESC2FILL_MAP_0_ADDR: 0x%x\n", data);
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_1_ADDR;
+	regmap_read(regmap, reg, &data);
+	pr_debug("EDMA_REG_RXDESC2FILL_MAP_1_ADDR: 0x%x\n", data);
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC2FILL_MAP_2_ADDR;
+	regmap_read(regmap, reg, &data);
+	pr_debug("EDMA_REG_RXDESC2FILL_MAP_2_ADDR: 0x%x\n", data);
+}
+
+/**
+ * edma_cfg_rx_rings_enable - Enable Rx and Rxfill rings
+ *
+ * Enable Rx and Rxfill rings.
+ */
+void edma_cfg_rx_rings_enable(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, reg;
+
+	/* Enable Rx rings */
+	for (i = rx->ring_start; i < rx->ring_start + rx->num_rings; i++) {
+		u32 data;
+
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CTRL(i);
+		regmap_read(regmap, reg, &data);
+		data |= EDMA_RXDESC_RX_EN;
+		regmap_write(regmap, reg, data);
+	}
+
+	for (i = rxfill->ring_start; i < rxfill->ring_start + rxfill->num_rings; i++) {
+		u32 data;
+
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_EN(i);
+		regmap_read(regmap, reg, &data);
+		data |= EDMA_RXFILL_RING_EN;
+		regmap_write(regmap, reg, data);
+	}
+}
+
+/**
+ * edma_cfg_rx_rings_disable - Disable Rx and Rxfill rings
+ *
+ * Disable Rx and Rxfill rings.
+ */
+void edma_cfg_rx_rings_disable(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, reg;
+
+	/* Disable Rx rings */
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring = NULL;
+		u32 data;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CTRL(rxdesc_ring->ring_id);
+		regmap_read(regmap, reg, &data);
+		data &= ~EDMA_RXDESC_RX_EN;
+		regmap_write(regmap, reg, data);
+	}
+
+	/* Disable RxFill Rings */
+	for (i = 0; i < rxfill->num_rings; i++) {
+		struct edma_rxfill_ring *rxfill_ring = NULL;
+		u32 data;
+
+		rxfill_ring = &edma_ctx->rxfill_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_EN(rxfill_ring->ring_id);
+		regmap_read(regmap, reg, &data);
+		data &= ~EDMA_RXFILL_RING_EN;
+		regmap_write(regmap, reg, data);
+	}
+}
+
+/**
+ * edma_cfg_rx_mappings - Setup RX ring mapping
+ *
+ * Setup queue ID to Rx desc ring mapping.
+ */
+void edma_cfg_rx_ring_mappings(void)
+{
+	edma_cfg_rx_qid_to_rx_desc_ring_mapping();
+	edma_cfg_rx_rings_to_rx_fill_mapping();
+}
+
+static void edma_cfg_rx_fill_ring_cleanup(struct edma_rxfill_ring *rxfill_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct device *dev = ppe_dev->dev;
+	u16 cons_idx, curr_idx;
+	u32 data, reg;
+
+	/* Get RxFill ring producer index */
+	curr_idx = rxfill_ring->prod_idx & EDMA_RXFILL_PROD_IDX_MASK;
+
+	/* Get RxFill ring consumer index */
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_CONS_IDX(rxfill_ring->ring_id);
+	regmap_read(regmap, reg, &data);
+	cons_idx = data & EDMA_RXFILL_CONS_IDX_MASK;
+
+	while (curr_idx != cons_idx) {
+		struct edma_rxfill_desc *rxfill_desc;
+		struct sk_buff *skb;
+
+		/* Get RxFill descriptor */
+		rxfill_desc = EDMA_RXFILL_DESC(rxfill_ring, cons_idx);
+
+		cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
+
+		/* Get skb from opaque */
+		skb = (struct sk_buff *)EDMA_RXFILL_OPAQUE_GET(rxfill_desc);
+		if (unlikely(!skb)) {
+			pr_err("Empty skb reference at index:%d\n",
+			       cons_idx);
+			continue;
+		}
+
+		dev_kfree_skb_any(skb);
+	}
+
+	/* Free RxFill ring descriptors */
+	dma_free_coherent(dev, (sizeof(struct edma_rxfill_desc)
+			   * rxfill_ring->count),
+			   rxfill_ring->desc, rxfill_ring->dma);
+	rxfill_ring->desc = NULL;
+	rxfill_ring->dma = (dma_addr_t)0;
+}
+
+static int edma_cfg_rx_fill_ring_dma_alloc(struct edma_rxfill_ring *rxfill_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct device *dev = ppe_dev->dev;
+
+	/* Allocate RxFill ring descriptors */
+	rxfill_ring->desc = dma_alloc_coherent(dev, (sizeof(struct edma_rxfill_desc)
+					       * rxfill_ring->count),
+					       &rxfill_ring->dma,
+					       GFP_KERNEL | __GFP_ZERO);
+	if (unlikely(!rxfill_ring->desc))
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int edma_cfg_rx_desc_ring_dma_alloc(struct edma_rxdesc_ring *rxdesc_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct device *dev = ppe_dev->dev;
+
+	rxdesc_ring->pdesc = dma_alloc_coherent(dev, (sizeof(struct edma_rxdesc_pri)
+						* rxdesc_ring->count),
+				&rxdesc_ring->pdma, GFP_KERNEL | __GFP_ZERO);
+	if (unlikely(!rxdesc_ring->pdesc))
+		return -ENOMEM;
+
+	rxdesc_ring->sdesc = dma_alloc_coherent(dev, (sizeof(struct edma_rxdesc_sec)
+				* rxdesc_ring->count),
+				&rxdesc_ring->sdma, GFP_KERNEL | __GFP_ZERO);
+	if (unlikely(!rxdesc_ring->sdesc)) {
+		dma_free_coherent(dev, (sizeof(struct edma_rxdesc_pri)
+				  * rxdesc_ring->count),
+				  rxdesc_ring->pdesc,
+				  rxdesc_ring->pdma);
+		rxdesc_ring->pdesc = NULL;
+		rxdesc_ring->pdma = (dma_addr_t)0;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void edma_cfg_rx_desc_ring_cleanup(struct edma_rxdesc_ring *rxdesc_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct device *dev = ppe_dev->dev;
+	u32 prod_idx, cons_idx, reg;
+
+	/* Get Rxdesc consumer & producer indices */
+	cons_idx = rxdesc_ring->cons_idx & EDMA_RXDESC_CONS_IDX_MASK;
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PROD_IDX(rxdesc_ring->ring_id);
+	regmap_read(regmap, reg, &prod_idx);
+	prod_idx = prod_idx & EDMA_RXDESC_PROD_IDX_MASK;
+
+	/* Free any buffers assigned to any descriptors */
+	while (cons_idx != prod_idx) {
+		struct edma_rxdesc_pri *rxdesc_pri =
+			EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
+		struct sk_buff *skb;
+
+		/* Update consumer index */
+		cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
+
+		/* Get opaque from Rxdesc */
+		skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(rxdesc_pri);
+		if (unlikely(!skb)) {
+			pr_warn("Empty skb reference at index:%d\n",
+				cons_idx);
+			continue;
+		}
+
+		dev_kfree_skb_any(skb);
+	}
+
+	/* Update the consumer index */
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, cons_idx);
+
+	/* Free Rxdesc ring descriptor */
+	dma_free_coherent(dev, (sizeof(struct edma_rxdesc_pri)
+			  * rxdesc_ring->count), rxdesc_ring->pdesc,
+			  rxdesc_ring->pdma);
+	rxdesc_ring->pdesc = NULL;
+	rxdesc_ring->pdma = (dma_addr_t)0;
+
+	/* Free any buffers assigned to any secondary ring descriptors */
+	dma_free_coherent(dev, (sizeof(struct edma_rxdesc_sec)
+			  * rxdesc_ring->count), rxdesc_ring->sdesc,
+			  rxdesc_ring->sdma);
+	rxdesc_ring->sdesc = NULL;
+	rxdesc_ring->sdma = (dma_addr_t)0;
+}
+
+static int edma_cfg_rx_rings_setup(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 ring_idx, alloc_size, buf_len;
+
+	/* Set buffer allocation size */
+	if (edma_ctx->rx_buf_size) {
+		alloc_size = edma_ctx->rx_buf_size +
+				EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN;
+		buf_len = alloc_size - EDMA_RX_SKB_HEADROOM - NET_IP_ALIGN;
+	} else if (edma_ctx->rx_page_mode) {
+		alloc_size = EDMA_RX_PAGE_MODE_SKB_SIZE +
+				EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN;
+		buf_len = PAGE_SIZE;
+	} else {
+		alloc_size = EDMA_RX_BUFFER_SIZE;
+		buf_len = alloc_size - EDMA_RX_SKB_HEADROOM - NET_IP_ALIGN;
+	}
+
+	pr_debug("EDMA ctx:%p rx_ring alloc_size=%d, buf_len=%d\n",
+		 edma_ctx, alloc_size, buf_len);
+
+	/* Allocate Rx fill ring descriptors */
+	for (ring_idx = 0; ring_idx < rxfill->num_rings; ring_idx++) {
+		u32 ret;
+		struct edma_rxfill_ring *rxfill_ring = NULL;
+
+		rxfill_ring = &edma_ctx->rxfill_rings[ring_idx];
+		rxfill_ring->count = EDMA_RX_RING_SIZE;
+		rxfill_ring->ring_id = rxfill->ring_start + ring_idx;
+		rxfill_ring->alloc_size = alloc_size;
+		rxfill_ring->buf_len = buf_len;
+		rxfill_ring->page_mode = edma_ctx->rx_page_mode;
+
+		ret = edma_cfg_rx_fill_ring_dma_alloc(rxfill_ring);
+		if (ret) {
+			pr_err("Error in setting up %d rxfill ring. ret: %d",
+			       rxfill_ring->ring_id, ret);
+			while (--ring_idx >= 0)
+				edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[ring_idx]);
+
+			return -ENOMEM;
+		}
+	}
+
+	/* Allocate RxDesc ring descriptors */
+	for (ring_idx = 0; ring_idx < rx->num_rings; ring_idx++) {
+		u32 index, queue_id = EDMA_RX_QUEUE_START;
+		struct edma_rxdesc_ring *rxdesc_ring = NULL;
+		u32 ret;
+
+		rxdesc_ring = &edma_ctx->rx_rings[ring_idx];
+		rxdesc_ring->count = EDMA_RX_RING_SIZE;
+		rxdesc_ring->ring_id = rx->ring_start + ring_idx;
+
+		if (queue_id > EDMA_CPU_PORT_QUEUE_MAX(EDMA_RX_QUEUE_START)) {
+			pr_err("Invalid queue_id: %d\n", queue_id);
+			while (--ring_idx >= 0)
+				edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[ring_idx]);
+
+			goto rxdesc_mem_alloc_fail;
+		}
+
+		/* Create a mapping between RX Desc ring and Rx fill ring.
+		 * Number of fill rings are lesser than the descriptor rings
+		 * Share the fill rings across descriptor rings.
+		 */
+		index = rxfill->ring_start +
+				(ring_idx % rxfill->num_rings);
+		rxdesc_ring->rxfill = &edma_ctx->rxfill_rings[index
+						- rxfill->ring_start];
+
+		ret = edma_cfg_rx_desc_ring_dma_alloc(rxdesc_ring);
+		if (ret) {
+			pr_err("Error in setting up %d rxdesc ring. ret: %d",
+			       rxdesc_ring->ring_id, ret);
+			while (--ring_idx >= 0)
+				edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[ring_idx]);
+
+			goto rxdesc_mem_alloc_fail;
+		}
+	}
+
+	pr_debug("Rx descriptor count for Rx desc and Rx fill rings : %d\n",
+		 EDMA_RX_RING_SIZE);
+
+	return 0;
+
+rxdesc_mem_alloc_fail:
+	for (ring_idx = 0; ring_idx < rxfill->num_rings; ring_idx++)
+		edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[ring_idx]);
+
+	return -ENOMEM;
+}
+
+/**
+ * edma_cfg_rx_buff_size_setup - Configure EDMA Rx jumbo buffer
+ *
+ * Configure EDMA Rx jumbo buffer
+ */
+void edma_cfg_rx_buff_size_setup(void)
+{
+	if (edma_ctx->rx_buf_size) {
+		edma_ctx->rx_page_mode = false;
+		pr_debug("Rx Jumbo mru is enabled: %d\n", edma_ctx->rx_buf_size);
+	}
+}
+
+/**
+ * edma_cfg_rx_rings_alloc - Allocate EDMA Rx rings
+ *
+ * Allocate EDMA Rx rings.
+ *
+ * Return 0 on success, negative error code on failure.
+ */
+int edma_cfg_rx_rings_alloc(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct edma_ring_info *rx = hw_info->rx;
+	int ret;
+
+	edma_ctx->rxfill_rings = kzalloc((sizeof(*edma_ctx->rxfill_rings) *
+					 rxfill->num_rings),
+					 GFP_KERNEL);
+	if (!edma_ctx->rxfill_rings)
+		return -ENOMEM;
+
+	edma_ctx->rx_rings = kzalloc((sizeof(*edma_ctx->rx_rings) *
+					 rx->num_rings),
+					 GFP_KERNEL);
+	if (!edma_ctx->rx_rings)
+		goto rxdesc_ring_alloc_fail;
+
+	pr_debug("RxDesc:%u rx (%u-%u) RxFill:%u (%u-%u)\n",
+		 rx->num_rings, rx->ring_start,
+		 (rx->ring_start + rx->num_rings - 1),
+		 rxfill->num_rings, rxfill->ring_start,
+		 (rxfill->ring_start + rxfill->num_rings - 1));
+
+	if (edma_cfg_rx_rings_setup()) {
+		pr_err("Error in setting up Rx rings\n");
+		goto rx_rings_setup_fail;
+	}
+
+	/* Reset Rx descriptor ring mapped queue's configurations */
+	ret = edma_cfg_rx_desc_ring_reset_queue_config();
+	if (ret) {
+		pr_err("Error in resetting the Rx descriptor rings configurations\n");
+		edma_cfg_rx_rings_cleanup();
+		return ret;
+	}
+
+	return 0;
+
+rx_rings_setup_fail:
+	kfree(edma_ctx->rx_rings);
+	edma_ctx->rx_rings = NULL;
+rxdesc_ring_alloc_fail:
+	kfree(edma_ctx->rxfill_rings);
+	edma_ctx->rxfill_rings = NULL;
+
+	return -ENOMEM;
+}
+
+/**
+ * edma_cfg_rx_rings_cleanup - Cleanup EDMA Rx rings
+ *
+ * Cleanup EDMA Rx rings
+ */
+void edma_cfg_rx_rings_cleanup(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	/* Free RxFill ring descriptors */
+	for (i = 0; i < rxfill->num_rings; i++)
+		edma_cfg_rx_fill_ring_cleanup(&edma_ctx->rxfill_rings[i]);
+
+	/* Free Rx completion ring descriptors */
+	for (i = 0; i < rx->num_rings; i++)
+		edma_cfg_rx_desc_ring_cleanup(&edma_ctx->rx_rings[i]);
+
+	kfree(edma_ctx->rxfill_rings);
+	kfree(edma_ctx->rx_rings);
+	edma_ctx->rxfill_rings = NULL;
+	edma_ctx->rx_rings = NULL;
+}
+
+static void edma_cfg_rx_fill_ring_configure(struct edma_rxfill_ring *rxfill_ring)
+{
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	u32 ring_sz, reg;
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_BA(rxfill_ring->ring_id);
+	regmap_write(regmap, reg, (u32)(rxfill_ring->dma & EDMA_RING_DMA_MASK));
+
+	ring_sz = rxfill_ring->count & EDMA_RXFILL_RING_SIZE_MASK;
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_RING_SIZE(rxfill_ring->ring_id);
+	regmap_write(regmap, reg, ring_sz);
+
+	edma_rx_alloc_buffer(rxfill_ring, rxfill_ring->count - 1);
+}
+
+static void edma_cfg_rx_desc_ring_flow_control(u32 threshold_xoff, u32 threshold_xon)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 data, i, reg;
+
+	data = (threshold_xoff & EDMA_RXDESC_FC_XOFF_THRE_MASK) << EDMA_RXDESC_FC_XOFF_THRE_SHIFT;
+	data |= ((threshold_xon & EDMA_RXDESC_FC_XON_THRE_MASK) << EDMA_RXDESC_FC_XON_THRE_SHIFT);
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_FC_THRE(rxdesc_ring->ring_id);
+		regmap_write(regmap, reg, data);
+	}
+}
+
+static void edma_cfg_rx_fill_ring_flow_control(int threshold_xoff, int threshold_xon)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	u32 data, i, reg;
+
+	data = (threshold_xoff & EDMA_RXFILL_FC_XOFF_THRE_MASK) << EDMA_RXFILL_FC_XOFF_THRE_SHIFT;
+	data |= ((threshold_xon & EDMA_RXFILL_FC_XON_THRE_MASK) << EDMA_RXFILL_FC_XON_THRE_SHIFT);
+
+	for (i = 0; i < rxfill->num_rings; i++) {
+		struct edma_rxfill_ring *rxfill_ring;
+
+		rxfill_ring = &edma_ctx->rxfill_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_FC_THRE(rxfill_ring->ring_id);
+		regmap_write(regmap, reg, data);
+	}
+}
+
+/**
+ * edma_cfg_rx_rings - Configure EDMA Rx rings.
+ *
+ * Configure EDMA Rx rings.
+ */
+int edma_cfg_rx_rings(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rxfill = hw_info->rxfill;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	for (i = 0; i < rxfill->num_rings; i++)
+		edma_cfg_rx_fill_ring_configure(&edma_ctx->rxfill_rings[i]);
+
+	for (i = 0; i < rx->num_rings; i++)
+		edma_cfg_rx_desc_ring_configure(&edma_ctx->rx_rings[i]);
+
+	/* Configure Rx flow control configurations */
+	edma_cfg_rx_desc_ring_flow_control(EDMA_RX_FC_XOFF_DEF, EDMA_RX_FC_XON_DEF);
+	edma_cfg_rx_fill_ring_flow_control(EDMA_RX_FC_XOFF_DEF, EDMA_RX_FC_XON_DEF);
+
+	return edma_cfg_rx_desc_ring_to_queue_mapping();
+}
+
+/**
+ * edma_cfg_rx_disable_interrupts - EDMA disable RX interrupts
+ *
+ * Disable RX interrupt masks
+ */
+void edma_cfg_rx_disable_interrupts(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, reg;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring =
+				&edma_ctx->rx_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
+		regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
+	}
+}
+
+/**
+ * edma_cfg_rx_enable_interrupts - EDMA enable RX interrupts
+ *
+ * Enable RX interrupt masks
+ */
+void edma_cfg_rx_enable_interrupts(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i, reg;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring =
+				&edma_ctx->rx_rings[i];
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
+		regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_rx);
+	}
+}
+
+/**
+ * edma_cfg_rx_napi_disable - Disable NAPI for Rx
+ *
+ * Disable NAPI for Rx
+ */
+void edma_cfg_rx_napi_disable(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		if (!rxdesc_ring->napi_added)
+			continue;
+
+		napi_disable(&rxdesc_ring->napi);
+	}
+}
+
+/**
+ * edma_cfg_rx_napi_enable - Enable NAPI for Rx
+ *
+ * Enable NAPI for Rx
+ */
+void edma_cfg_rx_napi_enable(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		if (!rxdesc_ring->napi_added)
+			continue;
+
+		napi_enable(&rxdesc_ring->napi);
+	}
+}
+
+/**
+ * edma_cfg_rx_napi_delete - Delete Rx NAPI
+ *
+ * Delete RX NAPI
+ */
+void edma_cfg_rx_napi_delete(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring;
+
+		rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		if (!rxdesc_ring->napi_added)
+			continue;
+
+		netif_napi_del(&rxdesc_ring->napi);
+		rxdesc_ring->napi_added = false;
+	}
+}
+
+/* Add Rx NAPI */
+/**
+ * edma_cfg_rx_napi_add - Add Rx NAPI
+ * @netdev: Netdevice
+ *
+ * Add RX NAPI
+ */
+void edma_cfg_rx_napi_add(void)
+{
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct edma_ring_info *rx = hw_info->rx;
+	u32 i;
+
+	for (i = 0; i < rx->num_rings; i++) {
+		struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
+
+		netif_napi_add_weight(edma_ctx->dummy_dev, &rxdesc_ring->napi,
+				      edma_rx_napi_poll, hw_info->napi_budget_rx);
+		rxdesc_ring->napi_added = true;
+	}
+
+	netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", hw_info->napi_budget_rx);
+}
+
+/**
+ * edma_cfg_rx_rps_hash_map - Configure rx rps hash map.
+ *
+ * Initialize and configure RPS hash map for queues
+ */
+int edma_cfg_rx_rps_hash_map(void)
+{
+	cpumask_t edma_rps_cpumask = {{EDMA_RX_DEFAULT_BITMAP}};
+	int map_len = 0, idx = 0, ret = 0;
+	u32 q_off = EDMA_RX_QUEUE_START;
+	u32 q_map[EDMA_MAX_CORE] = {0};
+	u32 hash, cpu;
+
+	/* Map all possible hash values to queues used by the EDMA Rx
+	 * rings based on a bitmask, which represents the cores to be mapped.
+	 * These queues are expected to be mapped to different Rx rings
+	 * which are assigned to different cores using IRQ affinity configuration.
+	 */
+	for_each_cpu(cpu, &edma_rps_cpumask) {
+		q_map[map_len] = q_off + (cpu * EDMA_MAX_PRI_PER_CORE);
+		map_len++;
+	}
+
+	for (hash = 0; hash < PPE_QUEUE_HASH_NUM; hash++) {
+		ret = ppe_edma_queue_offset_config(edma_ctx->ppe_dev,
+						   PPE_QUEUE_CLASS_HASH, hash, q_map[idx]);
+		if (ret)
+			return ret;
+
+		pr_debug("profile_id: %u, hash: %u, q_off: %u\n",
+			 EDMA_CPU_PORT_PROFILE_ID, hash, q_map[idx]);
+		idx = (idx + 1) % map_len;
+	}
+
+	return 0;
+}
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __EDMA_CFG_RX__
+#define __EDMA_CFG_RX__
+
+/* SKB payload size used in page mode */
+#define EDMA_RX_PAGE_MODE_SKB_SIZE	256
+
+/* Rx flow control X-OFF default value */
+#define EDMA_RX_FC_XOFF_DEF	32
+
+/* Rx flow control X-ON default value */
+#define EDMA_RX_FC_XON_DEF	64
+
+/* Rx AC flow control original threshold */
+#define EDMA_RX_AC_FC_THRE_ORIG		0x190
+
+/* Rx AC flow control default threshold */
+#define EDMA_RX_AC_FC_THRES_DEF		0x104
+/* Rx mitigation timer's default value in microseconds */
+#define EDMA_RX_MITIGATION_TIMER_DEF	25
+
+/* Rx mitigation packet count's default value */
+#define EDMA_RX_MITIGATION_PKT_CNT_DEF	16
+
+/* Default bitmap of cores for RPS to ARM cores */
+#define EDMA_RX_DEFAULT_BITMAP	((1 << EDMA_MAX_CORE) - 1)
+
+int edma_cfg_rx_rings(void);
+int edma_cfg_rx_rings_alloc(void);
+void edma_cfg_rx_ring_mappings(void);
+void edma_cfg_rx_rings_cleanup(void);
+void edma_cfg_rx_disable_interrupts(void);
+void edma_cfg_rx_enable_interrupts(void);
+void edma_cfg_rx_napi_disable(void);
+void edma_cfg_rx_napi_enable(void);
+void edma_cfg_rx_napi_delete(void);
+void edma_cfg_rx_napi_add(void);
+void edma_cfg_rx_mapping(void);
+void edma_cfg_rx_rings_enable(void);
+void edma_cfg_rx_rings_disable(void);
+void edma_cfg_rx_buff_size_setup(void);
+int edma_cfg_rx_rps_hash_map(void);
+int edma_cfg_rx_rps(struct ctl_table *table, int write,
+		    void *buffer, size_t *lenp, loff_t *ppos);
+#endif
--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
@@ -12,12 +12,39 @@
 #include <linux/printk.h>
 
 #include "edma.h"
+#include "edma_cfg_rx.h"
 #include "edma_port.h"
 #include "ppe_regs.h"
 
 /* Number of netdev queues. */
 #define EDMA_NETDEV_QUEUE_NUM	4
 
+static int edma_port_stats_alloc(struct net_device *netdev)
+{
+	struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+
+	if (!port_priv)
+		return -EINVAL;
+
+	/* Allocate per-cpu stats memory. */
+	port_priv->pcpu_stats.rx_stats =
+		netdev_alloc_pcpu_stats(struct edma_port_rx_stats);
+	if (!port_priv->pcpu_stats.rx_stats) {
+		netdev_err(netdev, "Per-cpu EDMA Rx stats alloc failed for %s\n",
+			   netdev->name);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void edma_port_stats_free(struct net_device *netdev)
+{
+	struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+
+	free_percpu(port_priv->pcpu_stats.rx_stats);
+}
+
 static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev,
 						 __maybe_unused struct sk_buff *skb,
 				__maybe_unused struct net_device *sb_dev)
@@ -172,6 +199,7 @@ void edma_port_destroy(struct ppe_port *
 	int port_id = port->port_id;
 	struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1];
 
+	edma_port_stats_free(netdev);
 	unregister_netdev(netdev);
 	free_netdev(netdev);
 	ppe_port_phylink_destroy(port);
@@ -232,6 +260,13 @@ int edma_port_setup(struct ppe_port *por
 			    port_id, netdev->dev_addr);
 	}
 
+	/* Allocate memory for EDMA port statistics. */
+	ret = edma_port_stats_alloc(netdev);
+	if (ret) {
+		netdev_dbg(netdev, "EDMA port stats alloc failed\n");
+		goto stats_alloc_fail;
+	}
+
 	netdev_dbg(netdev, "Configuring the port %s(qcom-id:%d)\n",
 		   netdev->name, port_id);
 
@@ -263,8 +298,10 @@ int edma_port_setup(struct ppe_port *por
 register_netdev_fail:
 	ppe_port_phylink_destroy(port);
 port_phylink_setup_fail:
-	free_netdev(netdev);
 	edma_ctx->netdev_arr[port_id - 1] = NULL;
+	edma_port_stats_free(netdev);
+stats_alloc_fail:
+	free_netdev(netdev);
 
 	return ret;
 }
--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
@@ -15,14 +15,45 @@
 					| NETIF_F_TSO6)
 
 /**
+ * struct edma_port_rx_stats - EDMA RX per CPU stats for the port.
+ * @rx_pkts: Number of Rx packets
+ * @rx_bytes: Number of Rx bytes
+ * @rx_drops: Number of Rx drops
+ * @rx_nr_frag_pkts: Number of Rx nr_frags packets
+ * @rx_fraglist_pkts: Number of Rx fraglist packets
+ * @rx_nr_frag_headroom_err: nr_frags headroom error packets
+ * @syncp: Synchronization pointer
+ */
+struct edma_port_rx_stats {
+	u64 rx_pkts;
+	u64 rx_bytes;
+	u64 rx_drops;
+	u64 rx_nr_frag_pkts;
+	u64 rx_fraglist_pkts;
+	u64 rx_nr_frag_headroom_err;
+	struct u64_stats_sync syncp;
+};
+
+/**
+ * struct edma_port_pcpu_stats - EDMA per cpu stats data structure for the port.
+ * @rx_stats: Per CPU Rx statistics
+ */
+struct edma_port_pcpu_stats {
+	struct edma_port_rx_stats __percpu *rx_stats;
+};
+
+/**
  * struct edma_port_priv - EDMA port priv structure.
  * @ppe_port: Pointer to PPE port
  * @netdev: Corresponding netdevice
+ * @pcpu_stats: Per CPU netdev statistics
+ * @txr_map: Tx ring per-core mapping
  * @flags: Feature flags
  */
 struct edma_port_priv {
 	struct ppe_port *ppe_port;
 	struct net_device *netdev;
+	struct edma_port_pcpu_stats pcpu_stats;
 	unsigned long flags;
 };
 
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/* Provides APIs to alloc Rx Buffers, reap the buffers, receive and
+ * process linear and Scatter Gather packets.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/regmap.h>
+
+#include "edma.h"
+#include "edma_cfg_rx.h"
+#include "edma_port.h"
+#include "ppe.h"
+#include "ppe_regs.h"
+
+static int edma_rx_alloc_buffer_list(struct edma_rxfill_ring *rxfill_ring, int alloc_count)
+{
+	struct edma_rxfill_stats *rxfill_stats = &rxfill_ring->rxfill_stats;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	u32 rx_alloc_size = rxfill_ring->alloc_size;
+	struct regmap *regmap = ppe_dev->regmap;
+	bool page_mode = rxfill_ring->page_mode;
+	struct edma_rxfill_desc *rxfill_desc;
+	u32 buf_len = rxfill_ring->buf_len;
+	struct device *dev = ppe_dev->dev;
+	u16 prod_idx, start_idx;
+	u16 num_alloc = 0;
+	u32 reg;
+
+	prod_idx = rxfill_ring->prod_idx;
+	start_idx = prod_idx;
+
+	while (likely(alloc_count--)) {
+		dma_addr_t buff_addr;
+		struct sk_buff *skb;
+		struct page *pg;
+
+		rxfill_desc = EDMA_RXFILL_DESC(rxfill_ring, prod_idx);
+
+		skb = dev_alloc_skb(rx_alloc_size);
+		if (unlikely(!skb)) {
+			u64_stats_update_begin(&rxfill_stats->syncp);
+			++rxfill_stats->alloc_failed;
+			u64_stats_update_end(&rxfill_stats->syncp);
+			break;
+		}
+
+		skb_reserve(skb, EDMA_RX_SKB_HEADROOM + NET_IP_ALIGN);
+
+		if (likely(!page_mode)) {
+			buff_addr = dma_map_single(dev, skb->data, rx_alloc_size, DMA_FROM_DEVICE);
+			if (dma_mapping_error(dev, buff_addr)) {
+				dev_dbg(dev, "edma_context:%p Unable to dma for non page mode",
+					edma_ctx);
+				dev_kfree_skb_any(skb);
+				break;
+			}
+		} else {
+			pg = alloc_page(GFP_ATOMIC);
+			if (unlikely(!pg)) {
+				u64_stats_update_begin(&rxfill_stats->syncp);
+				++rxfill_stats->page_alloc_failed;
+				u64_stats_update_end(&rxfill_stats->syncp);
+				dev_kfree_skb_any(skb);
+				dev_dbg(dev, "edma_context:%p Unable to allocate page",
+					edma_ctx);
+				break;
+			}
+
+			buff_addr = dma_map_page(dev, pg, 0, PAGE_SIZE, DMA_FROM_DEVICE);
+			if (dma_mapping_error(dev, buff_addr)) {
+				dev_dbg(dev, "edma_context:%p Mapping error for page mode",
+					edma_ctx);
+				__free_page(pg);
+				dev_kfree_skb_any(skb);
+				break;
+			}
+
+			skb_fill_page_desc(skb, 0, pg, 0, PAGE_SIZE);
+		}
+
+		EDMA_RXFILL_BUFFER_ADDR_SET(rxfill_desc, buff_addr);
+
+		EDMA_RXFILL_OPAQUE_LO_SET(rxfill_desc, skb);
+#ifdef __LP64__
+		EDMA_RXFILL_OPAQUE_HI_SET(rxfill_desc, skb);
+#endif
+		EDMA_RXFILL_PACKET_LEN_SET(rxfill_desc,
+					   (u32)(buf_len) & EDMA_RXFILL_BUF_SIZE_MASK);
+		prod_idx = (prod_idx + 1) & EDMA_RX_RING_SIZE_MASK;
+		num_alloc++;
+	}
+
+	if (likely(num_alloc)) {
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXFILL_PROD_IDX(rxfill_ring->ring_id);
+		regmap_write(regmap, reg, prod_idx);
+		rxfill_ring->prod_idx = prod_idx;
+	}
+
+	return num_alloc;
+}
+
+/**
+ * edma_rx_alloc_buffer - EDMA Rx alloc buffer.
+ * @rxfill_ring: EDMA Rxfill ring
+ * @alloc_count: Number of rings to alloc
+ *
+ * Alloc Rx buffers for RxFill ring.
+ *
+ * Return the number of rings allocated.
+ */
+int edma_rx_alloc_buffer(struct edma_rxfill_ring *rxfill_ring, int alloc_count)
+{
+	return edma_rx_alloc_buffer_list(rxfill_ring, alloc_count);
+}
+
+/* Mark ip_summed appropriately in the skb as per the L3/L4 checksum
+ * status in descriptor.
+ */
+static void edma_rx_checksum_verify(struct edma_rxdesc_pri *rxdesc_pri,
+				    struct sk_buff *skb)
+{
+	u8 pid = EDMA_RXDESC_PID_GET(rxdesc_pri);
+
+	skb_checksum_none_assert(skb);
+
+	if (likely(EDMA_RX_PID_IS_IPV4(pid))) {
+		if (likely(EDMA_RXDESC_L3CSUM_STATUS_GET(rxdesc_pri)) &&
+		    likely(EDMA_RXDESC_L4CSUM_STATUS_GET(rxdesc_pri)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+	} else if (likely(EDMA_RX_PID_IS_IPV6(pid))) {
+		if (likely(EDMA_RXDESC_L4CSUM_STATUS_GET(rxdesc_pri)))
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+	}
+}
+
+static void edma_rx_process_last_segment(struct edma_rxdesc_ring *rxdesc_ring,
+					 struct edma_rxdesc_pri *rxdesc_pri,
+					 struct sk_buff *skb)
+{
+	bool page_mode = rxdesc_ring->rxfill->page_mode;
+	struct edma_port_pcpu_stats *pcpu_stats;
+	struct edma_port_rx_stats *rx_stats;
+	struct edma_port_priv *port_dev;
+	struct sk_buff *skb_head;
+	struct net_device *dev;
+	u32 pkt_length;
+
+	/* Get packet length. */
+	pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
+
+	skb_head = rxdesc_ring->head;
+	dev = skb_head->dev;
+
+	/* Check Rx checksum offload status. */
+	if (likely(dev->features & NETIF_F_RXCSUM))
+		edma_rx_checksum_verify(rxdesc_pri, skb_head);
+
+	/* Get stats for the netdevice. */
+	port_dev = netdev_priv(dev);
+	pcpu_stats = &port_dev->pcpu_stats;
+	rx_stats = this_cpu_ptr(pcpu_stats->rx_stats);
+
+	if (unlikely(page_mode)) {
+		if (unlikely(!pskb_may_pull(skb_head, ETH_HLEN))) {
+			/* Discard the SKB that we have been building,
+			 * in addition to the SKB linked to current descriptor.
+			 */
+			dev_kfree_skb_any(skb_head);
+			rxdesc_ring->head = NULL;
+			rxdesc_ring->last = NULL;
+			rxdesc_ring->pdesc_head = NULL;
+
+			u64_stats_update_begin(&rx_stats->syncp);
+			rx_stats->rx_nr_frag_headroom_err++;
+			u64_stats_update_end(&rx_stats->syncp);
+
+			return;
+		}
+	}
+
+	if (unlikely(!pskb_pull(skb_head, EDMA_RXDESC_DATA_OFFSET_GET(rxdesc_ring->pdesc_head)))) {
+		dev_kfree_skb_any(skb_head);
+		rxdesc_ring->head = NULL;
+		rxdesc_ring->last = NULL;
+		rxdesc_ring->pdesc_head = NULL;
+
+		u64_stats_update_begin(&rx_stats->syncp);
+		rx_stats->rx_nr_frag_headroom_err++;
+		u64_stats_update_end(&rx_stats->syncp);
+
+		return;
+	}
+
+	u64_stats_update_begin(&rx_stats->syncp);
+	rx_stats->rx_pkts++;
+	rx_stats->rx_bytes += skb_head->len;
+	rx_stats->rx_nr_frag_pkts += (u64)page_mode;
+	rx_stats->rx_fraglist_pkts += (u64)(!page_mode);
+	u64_stats_update_end(&rx_stats->syncp);
+
+	pr_debug("edma_context:%p skb:%p Jumbo pkt_length:%u\n",
+		 edma_ctx, skb_head, skb_head->len);
+
+	skb_head->protocol = eth_type_trans(skb_head, dev);
+
+	/* Send packet up the stack. */
+	if (dev->features & NETIF_F_GRO)
+		napi_gro_receive(&rxdesc_ring->napi, skb_head);
+	else
+		netif_receive_skb(skb_head);
+
+	rxdesc_ring->head = NULL;
+	rxdesc_ring->last = NULL;
+	rxdesc_ring->pdesc_head = NULL;
+}
+
+static void edma_rx_handle_frag_list(struct edma_rxdesc_ring *rxdesc_ring,
+				     struct edma_rxdesc_pri *rxdesc_pri,
+				     struct sk_buff *skb)
+{
+	u32 pkt_length;
+
+	/* Get packet length. */
+	pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
+	pr_debug("edma_context:%p skb:%p fragment pkt_length:%u\n",
+		 edma_ctx, skb, pkt_length);
+
+	if (!(rxdesc_ring->head)) {
+		skb_put(skb, pkt_length);
+		rxdesc_ring->head = skb;
+		rxdesc_ring->last = NULL;
+		rxdesc_ring->pdesc_head = rxdesc_pri;
+
+		return;
+	}
+
+	/* Append it to the fraglist of head if this is second frame
+	 * If not second frame append to tail.
+	 */
+	skb_put(skb, pkt_length);
+	if (!skb_has_frag_list(rxdesc_ring->head))
+		skb_shinfo(rxdesc_ring->head)->frag_list = skb;
+	else
+		rxdesc_ring->last->next = skb;
+
+	rxdesc_ring->last = skb;
+	rxdesc_ring->last->next = NULL;
+	rxdesc_ring->head->len += pkt_length;
+	rxdesc_ring->head->data_len += pkt_length;
+	rxdesc_ring->head->truesize += skb->truesize;
+
+	/* If there are more segments for this packet,
+	 * then we have nothing to do. Otherwise process
+	 * last segment and send packet to stack.
+	 */
+	if (EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))
+		return;
+
+	edma_rx_process_last_segment(rxdesc_ring, rxdesc_pri, skb);
+}
+
+static void edma_rx_handle_nr_frags(struct edma_rxdesc_ring *rxdesc_ring,
+				    struct edma_rxdesc_pri *rxdesc_pri,
+				    struct sk_buff *skb)
+{
+	skb_frag_t *frag = NULL;
+	u32 pkt_length;
+
+	/* Get packet length. */
+	pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
+	pr_debug("edma_context:%p skb:%p fragment pkt_length:%u\n",
+		 edma_ctx, skb, pkt_length);
+
+	if (!(rxdesc_ring->head)) {
+		skb->len = pkt_length;
+		skb->data_len = pkt_length;
+		skb->truesize = SKB_TRUESIZE(PAGE_SIZE);
+		rxdesc_ring->head = skb;
+		rxdesc_ring->last = NULL;
+		rxdesc_ring->pdesc_head = rxdesc_pri;
+
+		return;
+	}
+
+	frag = &skb_shinfo(skb)->frags[0];
+
+	/* Append current frag at correct index as nr_frag of parent. */
+	skb_add_rx_frag(rxdesc_ring->head, skb_shinfo(rxdesc_ring->head)->nr_frags,
+			skb_frag_page(frag), 0, pkt_length, PAGE_SIZE);
+	skb_shinfo(skb)->nr_frags = 0;
+
+	/* Free the SKB after we have appended its frag page to the head skb. */
+	dev_kfree_skb_any(skb);
+
+	/* If there are more segments for this packet,
+	 * then we have nothing to do. Otherwise process
+	 * last segment and send packet to stack.
+	 */
+	if (EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))
+		return;
+
+	edma_rx_process_last_segment(rxdesc_ring, rxdesc_pri, skb);
+}
+
+static bool edma_rx_handle_linear_packets(struct edma_rxdesc_ring *rxdesc_ring,
+					  struct edma_rxdesc_pri *rxdesc_pri,
+					  struct sk_buff *skb)
+{
+	bool page_mode = rxdesc_ring->rxfill->page_mode;
+	struct edma_port_pcpu_stats *pcpu_stats;
+	struct edma_port_rx_stats *rx_stats;
+	struct edma_port_priv *port_dev;
+	skb_frag_t *frag = NULL;
+	u32 pkt_length;
+
+	/* Get stats for the netdevice. */
+	port_dev = netdev_priv(skb->dev);
+	pcpu_stats = &port_dev->pcpu_stats;
+	rx_stats = this_cpu_ptr(pcpu_stats->rx_stats);
+
+	/* Get packet length. */
+	pkt_length = EDMA_RXDESC_PACKET_LEN_GET(rxdesc_pri);
+
+	if (likely(!page_mode)) {
+		skb_put(skb, pkt_length);
+		goto send_to_stack;
+	}
+
+	/* Handle linear packet in page mode. */
+	frag = &skb_shinfo(skb)->frags[0];
+	skb_add_rx_frag(skb, 0, skb_frag_page(frag), 0, pkt_length, PAGE_SIZE);
+
+	/* Pull ethernet header into SKB data area for header processing. */
+	if (unlikely(!pskb_may_pull(skb, ETH_HLEN))) {
+		u64_stats_update_begin(&rx_stats->syncp);
+		rx_stats->rx_nr_frag_headroom_err++;
+		u64_stats_update_end(&rx_stats->syncp);
+		dev_kfree_skb_any(skb);
+
+		return false;
+	}
+
+send_to_stack:
+
+	__skb_pull(skb, EDMA_RXDESC_DATA_OFFSET_GET(rxdesc_pri));
+
+	/* Check Rx checksum offload status. */
+	if (likely(skb->dev->features & NETIF_F_RXCSUM))
+		edma_rx_checksum_verify(rxdesc_pri, skb);
+
+	u64_stats_update_begin(&rx_stats->syncp);
+	rx_stats->rx_pkts++;
+	rx_stats->rx_bytes += pkt_length;
+	rx_stats->rx_nr_frag_pkts += (u64)page_mode;
+	u64_stats_update_end(&rx_stats->syncp);
+
+	skb->protocol = eth_type_trans(skb, skb->dev);
+	if (skb->dev->features & NETIF_F_GRO)
+		napi_gro_receive(&rxdesc_ring->napi, skb);
+	else
+		netif_receive_skb(skb);
+
+	netdev_dbg(skb->dev, "edma_context:%p, skb:%p pkt_length:%u\n",
+		   edma_ctx, skb, skb->len);
+
+	return true;
+}
+
+static struct net_device *edma_rx_get_src_dev(struct edma_rxdesc_stats *rxdesc_stats,
+					      struct edma_rxdesc_pri *rxdesc_pri,
+					      struct sk_buff *skb)
+{
+	u32 src_info = EDMA_RXDESC_SRC_INFO_GET(rxdesc_pri);
+	struct edma_hw_info *hw_info = edma_ctx->hw_info;
+	struct net_device *ndev = NULL;
+	u8 src_port_num;
+
+	/* Check src_info. */
+	if (likely((src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK)
+	    == EDMA_RXDESC_SRCINFO_TYPE_PORTID)) {
+		src_port_num = src_info & EDMA_RXDESC_PORTNUM_BITS;
+	} else {
+		if (net_ratelimit()) {
+			pr_warn("Invalid src info_type:0x%x. Drop skb:%p\n",
+				(src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK), skb);
+		}
+
+		u64_stats_update_begin(&rxdesc_stats->syncp);
+		++rxdesc_stats->src_port_inval_type;
+		u64_stats_update_end(&rxdesc_stats->syncp);
+
+		return NULL;
+	}
+
+	/* Packet with PP source. */
+	if (likely(src_port_num <= hw_info->max_ports)) {
+		if (unlikely(src_port_num < EDMA_START_IFNUM)) {
+			if (net_ratelimit())
+				pr_warn("Port number error :%d. Drop skb:%p\n",
+					src_port_num, skb);
+
+			u64_stats_update_begin(&rxdesc_stats->syncp);
+			++rxdesc_stats->src_port_inval;
+			u64_stats_update_end(&rxdesc_stats->syncp);
+
+			return NULL;
+		}
+
+		/* Get netdev for this port using the source port
+		 * number as index into the netdev array. We need to
+		 * subtract one since the indices start form '0' and
+		 * port numbers start from '1'.
+		 */
+		ndev = edma_ctx->netdev_arr[src_port_num - 1];
+	}
+
+	if (likely(ndev))
+		return ndev;
+
+	if (net_ratelimit())
+		pr_warn("Netdev Null src_info_type:0x%x src port num:%d Drop skb:%p\n",
+			(src_info & EDMA_RXDESC_SRCINFO_TYPE_MASK),
+			src_port_num, skb);
+
+	u64_stats_update_begin(&rxdesc_stats->syncp);
+	++rxdesc_stats->src_port_inval_netdev;
+	u64_stats_update_end(&rxdesc_stats->syncp);
+
+	return NULL;
+}
+
+static int edma_rx_reap(struct edma_rxdesc_ring *rxdesc_ring, int budget)
+{
+	struct edma_rxdesc_stats *rxdesc_stats = &rxdesc_ring->rxdesc_stats;
+	u32 alloc_size = rxdesc_ring->rxfill->alloc_size;
+	bool page_mode = rxdesc_ring->rxfill->page_mode;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct edma_rxdesc_pri *next_rxdesc_pri;
+	struct regmap *regmap = ppe_dev->regmap;
+	struct device *dev = ppe_dev->dev;
+	u32 prod_idx, cons_idx, end_idx;
+	u32 work_to_do, work_done = 0;
+	struct sk_buff *next_skb;
+	u32 work_leftover, reg;
+
+	/* Get Rx ring producer and consumer indices. */
+	cons_idx = rxdesc_ring->cons_idx;
+
+	if (likely(rxdesc_ring->work_leftover > EDMA_RX_MAX_PROCESS)) {
+		work_to_do = rxdesc_ring->work_leftover;
+	} else {
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_PROD_IDX(rxdesc_ring->ring_id);
+		regmap_read(regmap, reg, &prod_idx);
+		prod_idx = prod_idx & EDMA_RXDESC_PROD_IDX_MASK;
+		work_to_do = EDMA_DESC_AVAIL_COUNT(prod_idx,
+						   cons_idx, EDMA_RX_RING_SIZE);
+		rxdesc_ring->work_leftover = work_to_do;
+	}
+
+	if (work_to_do > budget)
+		work_to_do = budget;
+
+	rxdesc_ring->work_leftover -= work_to_do;
+	end_idx = (cons_idx + work_to_do) & EDMA_RX_RING_SIZE_MASK;
+	next_rxdesc_pri = EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
+
+	/* Get opaque from RXDESC. */
+	next_skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
+
+	work_leftover = work_to_do & (EDMA_RX_MAX_PROCESS - 1);
+	while (likely(work_to_do--)) {
+		struct edma_rxdesc_pri *rxdesc_pri;
+		struct net_device *ndev;
+		struct sk_buff *skb;
+		dma_addr_t dma_addr;
+
+		skb = next_skb;
+		rxdesc_pri = next_rxdesc_pri;
+		dma_addr = EDMA_RXDESC_BUFFER_ADDR_GET(rxdesc_pri);
+
+		if (!page_mode)
+			dma_unmap_single(dev, dma_addr, alloc_size,
+					 DMA_TO_DEVICE);
+		else
+			dma_unmap_page(dev, dma_addr, PAGE_SIZE, DMA_TO_DEVICE);
+
+		/* Update consumer index. */
+		cons_idx = (cons_idx + 1) & EDMA_RX_RING_SIZE_MASK;
+
+		/* Get the next Rx descriptor. */
+		next_rxdesc_pri = EDMA_RXDESC_PRI_DESC(rxdesc_ring, cons_idx);
+
+		/* Handle linear packets or initial segments first. */
+		if (likely(!(rxdesc_ring->head))) {
+			ndev = edma_rx_get_src_dev(rxdesc_stats, rxdesc_pri, skb);
+			if (unlikely(!ndev)) {
+				dev_kfree_skb_any(skb);
+				goto next_rx_desc;
+			}
+
+			/* Update skb fields for head skb. */
+			skb->dev = ndev;
+			skb->skb_iif = ndev->ifindex;
+
+			/* Handle linear packets. */
+			if (likely(!EDMA_RXDESC_MORE_BIT_GET(rxdesc_pri))) {
+				next_skb =
+					(struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
+
+				if (unlikely(!
+					     edma_rx_handle_linear_packets(rxdesc_ring,
+									   rxdesc_pri, skb)))
+					dev_kfree_skb_any(skb);
+
+				goto next_rx_desc;
+			}
+		}
+
+		next_skb = (struct sk_buff *)EDMA_RXDESC_OPAQUE_GET(next_rxdesc_pri);
+
+		/* Handle scatter frame processing for first/middle/last segments. */
+		page_mode ? edma_rx_handle_nr_frags(rxdesc_ring, rxdesc_pri, skb) :
+			edma_rx_handle_frag_list(rxdesc_ring, rxdesc_pri, skb);
+
+next_rx_desc:
+		/* Update work done. */
+		work_done++;
+
+		/* Check if we can refill EDMA_RX_MAX_PROCESS worth buffers,
+		 * if yes, refill and update index before continuing.
+		 */
+		if (unlikely(!(work_done & (EDMA_RX_MAX_PROCESS - 1)))) {
+			reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
+			regmap_write(regmap, reg, cons_idx);
+			rxdesc_ring->cons_idx = cons_idx;
+			edma_rx_alloc_buffer_list(rxdesc_ring->rxfill, EDMA_RX_MAX_PROCESS);
+		}
+	}
+
+	/* Check if we need to refill and update
+	 * index for any buffers before exit.
+	 */
+	if (unlikely(work_leftover)) {
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_CONS_IDX(rxdesc_ring->ring_id);
+		regmap_write(regmap, reg, cons_idx);
+		rxdesc_ring->cons_idx = cons_idx;
+		edma_rx_alloc_buffer_list(rxdesc_ring->rxfill, work_leftover);
+	}
+
+	return work_done;
+}
+
+/**
+ * edma_rx_napi_poll - EDMA Rx napi poll.
+ * @napi: NAPI structure
+ * @budget: Rx NAPI budget
+ *
+ * EDMA RX NAPI handler to handle the NAPI poll.
+ *
+ * Return the number of packets processed.
+ */
+int edma_rx_napi_poll(struct napi_struct *napi, int budget)
+{
+	struct edma_rxdesc_ring *rxdesc_ring = (struct edma_rxdesc_ring *)napi;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	int work_done = 0;
+	u32 status, reg;
+
+	do {
+		work_done += edma_rx_reap(rxdesc_ring, budget - work_done);
+		if (likely(work_done >= budget))
+			return work_done;
+
+		/* Check if there are more packets to process. */
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_STAT(rxdesc_ring->ring_id);
+		regmap_read(regmap, reg, &status);
+		status = status & EDMA_RXDESC_RING_INT_STATUS_MASK;
+	} while (likely(status));
+
+	napi_complete(napi);
+
+	reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
+	regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_rx);
+
+	return work_done;
+}
+
+/**
+ * edma_rx_handle_irq - EDMA Rx handle irq.
+ * @irq: Interrupt to handle
+ * @ctx: Context
+ *
+ * Process RX IRQ and schedule NAPI.
+ *
+ * Return IRQ_HANDLED(1) on success.
+ */
+irqreturn_t edma_rx_handle_irq(int irq, void *ctx)
+{
+	struct edma_rxdesc_ring *rxdesc_ring = (struct edma_rxdesc_ring *)ctx;
+	struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+	struct regmap *regmap = ppe_dev->regmap;
+	u32 reg;
+
+	if (likely(napi_schedule_prep(&rxdesc_ring->napi))) {
+		/* Disable RxDesc interrupt. */
+		reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_INT_MASK(rxdesc_ring->ring_id);
+		regmap_write(regmap, reg, EDMA_MASK_INT_DISABLE);
+		__napi_schedule(&rxdesc_ring->napi);
+	}
+
+	return IRQ_HANDLED;
+}
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
@@ -0,0 +1,287 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __EDMA_RX__
+#define __EDMA_RX__
+
+#include <linux/netdevice.h>
+
+#define EDMA_RXFILL_RING_PER_CORE_MAX	1
+#define EDMA_RXDESC_RING_PER_CORE_MAX	1
+
+/* Max Rx processing without replenishing RxFill ring. */
+#define EDMA_RX_MAX_PROCESS		32
+
+#define EDMA_RX_SKB_HEADROOM		128
+#define EDMA_RX_QUEUE_START		0
+#define EDMA_RX_BUFFER_SIZE		1984
+#define EDMA_MAX_CORE			4
+
+#define EDMA_GET_DESC(R, i, type)	(&(((type *)((R)->desc))[(i)]))
+#define EDMA_GET_PDESC(R, i, type)	(&(((type *)((R)->pdesc))[(i)]))
+#define EDMA_GET_SDESC(R, i, type)	(&(((type *)((R)->sdesc))[(i)]))
+#define EDMA_RXFILL_DESC(R, i)		EDMA_GET_DESC(R, i, \
+						struct edma_rxfill_desc)
+#define EDMA_RXDESC_PRI_DESC(R, i)	EDMA_GET_PDESC(R, i, \
+						struct edma_rxdesc_pri)
+#define EDMA_RXDESC_SEC_DESC(R, i)	EDMA_GET_SDESC(R, i, \
+						struct edma_rxdesc_sec)
+
+#define EDMA_RX_RING_SIZE	2048
+
+#define EDMA_RX_RING_SIZE_MASK	(EDMA_RX_RING_SIZE - 1)
+#define EDMA_RX_RING_ID_MASK		0x1F
+
+#define EDMA_MAX_PRI_PER_CORE      8
+#define EDMA_RX_PID_IPV4_MAX		0x3
+#define EDMA_RX_PID_IPV6		0x4
+#define EDMA_RX_PID_IS_IPV4(pid)	(!((pid) & (~EDMA_RX_PID_IPV4_MAX)))
+#define EDMA_RX_PID_IS_IPV6(pid)	(!(!((pid) & EDMA_RX_PID_IPV6)))
+
+#define EDMA_RXDESC_BUFFER_ADDR_GET(desc)	\
+				((u32)(le32_to_cpu((__force __le32)((desc)->word0))))
+#define EDMA_RXDESC_OPAQUE_GET(_desc) ({ \
+			typeof(_desc) (desc) = (_desc); \
+			((uintptr_t)((u64)((desc)->word2) | \
+			((u64)((desc)->word3) << 0x20))); })
+
+#define EDMA_RXDESC_SRCINFO_TYPE_PORTID		0x2000
+#define EDMA_RXDESC_SRCINFO_TYPE_MASK		0xF000
+#define EDMA_RXDESC_L3CSUM_STATUS_MASK		BIT(13)
+#define EDMA_RXDESC_L4CSUM_STATUS_MASK		BIT(12)
+#define EDMA_RXDESC_PORTNUM_BITS		0x0FFF
+
+#define EDMA_RXDESC_PACKET_LEN_MASK		0x3FFFF
+#define EDMA_RXDESC_PACKET_LEN_GET(_desc) ({ \
+			typeof(_desc) (desc) = (_desc); \
+			((le32_to_cpu((__force __le32)((desc)->word5))) & \
+			EDMA_RXDESC_PACKET_LEN_MASK); })
+
+#define EDMA_RXDESC_MORE_BIT_MASK		0x40000000
+#define EDMA_RXDESC_MORE_BIT_GET(desc)		((le32_to_cpu((__force __le32)((desc)->word1))) & \
+						EDMA_RXDESC_MORE_BIT_MASK)
+#define EDMA_RXDESC_SRC_DST_INFO_GET(desc)	\
+				((u32)((le32_to_cpu((__force __le32)((desc)->word4)))))
+
+#define EDMA_RXDESC_L3_OFFSET_MASK	GENMASK(23, 16)
+#define EDMA_RXDESC_L3_OFFSET_GET(desc)	FIELD_GET(EDMA_RXDESC_L3_OFFSET_MASK, \
+					le32_to_cpu((__force __le32)((desc)->word7)))
+
+#define EDMA_RXDESC_PID_MASK		GENMASK(15, 12)
+#define EDMA_RXDESC_PID_GET(desc)	FIELD_GET(EDMA_RXDESC_PID_MASK, \
+					le32_to_cpu((__force __le32)((desc)->word7)))
+
+#define EDMA_RXDESC_DST_INFO_MASK	GENMASK(31, 16)
+#define EDMA_RXDESC_DST_INFO_GET(desc)	FIELD_GET(EDMA_RXDESC_DST_INFO_MASK, \
+					le32_to_cpu((__force __le32)((desc)->word4)))
+
+#define EDMA_RXDESC_SRC_INFO_MASK	GENMASK(15, 0)
+#define EDMA_RXDESC_SRC_INFO_GET(desc)	FIELD_GET(EDMA_RXDESC_SRC_INFO_MASK, \
+					le32_to_cpu((__force __le32)((desc)->word4)))
+
+#define EDMA_RXDESC_PORT_ID_MASK	GENMASK(11, 0)
+#define EDMA_RXDESC_PORT_ID_GET(x)	FIELD_GET(EDMA_RXDESC_PORT_ID_MASK, x)
+
+#define EDMA_RXDESC_SRC_PORT_ID_GET(desc)	(EDMA_RXDESC_PORT_ID_GET \
+						(EDMA_RXDESC_SRC_INFO_GET(desc)))
+#define EDMA_RXDESC_DST_PORT_ID_GET(desc)	(EDMA_RXDESC_PORT_ID_GET \
+						(EDMA_RXDESC_DST_INFO_GET(desc)))
+
+#define EDMA_RXDESC_DST_PORT		(0x2 << EDMA_RXDESC_PID_SHIFT)
+
+#define EDMA_RXDESC_L3CSUM_STATUS_GET(desc)	FIELD_GET(EDMA_RXDESC_L3CSUM_STATUS_MASK, \
+						le32_to_cpu((__force __le32)(desc)->word6))
+#define EDMA_RXDESC_L4CSUM_STATUS_GET(desc)	FIELD_GET(EDMA_RXDESC_L4CSUM_STATUS_MASK, \
+						le32_to_cpu((__force __le32)(desc)->word6))
+
+#define EDMA_RXDESC_DATA_OFFSET_MASK		GENMASK(11, 0)
+#define EDMA_RXDESC_DATA_OFFSET_GET(desc)	FIELD_GET(EDMA_RXDESC_DATA_OFFSET_MASK, \
+						le32_to_cpu((__force __le32)(desc)->word6))
+
+#define EDMA_RXFILL_BUF_SIZE_MASK		0xFFFF
+#define EDMA_RXFILL_BUF_SIZE_SHIFT		16
+
+/* Opaque values are not accessed by the EDMA HW,
+ * so endianness conversion is not needed.
+ */
+
+#define EDMA_RXFILL_OPAQUE_LO_SET(desc, ptr)	(((desc)->word2) = \
+						(u32)(uintptr_t)(ptr))
+#ifdef __LP64__
+#define EDMA_RXFILL_OPAQUE_HI_SET(desc, ptr)	(((desc)->word3) = \
+						(u32)((u64)(ptr) >> 0x20))
+#endif
+
+#define EDMA_RXFILL_OPAQUE_GET(_desc) ({ \
+			typeof(_desc) (desc) = (_desc); \
+			((uintptr_t)((u64)((desc)->word2) | \
+			((u64)((desc)->word3) << 0x20))); })
+
+#define EDMA_RXFILL_PACKET_LEN_SET(desc, len)	{ \
+	(((desc)->word1) = (u32)((((u32)len) << EDMA_RXFILL_BUF_SIZE_SHIFT) & \
+						0xFFFF0000)); \
+}
+
+#define EDMA_RXFILL_BUFFER_ADDR_SET(desc, addr)	(((desc)->word0) = (u32)(addr))
+
+/* Opaque values are set in word2 and word3, they are not accessed by the EDMA HW,
+ * so endianness conversion is not needed.
+ */
+#define EDMA_RXFILL_ENDIAN_SET(_desc) ({ \
+	typeof(_desc) (desc) = (_desc); \
+	cpu_to_le32s(&((desc)->word0)); \
+	cpu_to_le32s(&((desc)->word1)); \
+})
+
+/* RX DESC size shift to obtain index from descriptor pointer. */
+#define EDMA_RXDESC_SIZE_SHIFT		5
+
+/**
+ * struct edma_rxdesc_stats - RX descriptor ring stats.
+ * @src_port_inval: Invalid source port number
+ * @src_port_inval_type: Source type is not PORT ID
+ * @src_port_inval_netdev: Invalid net device for the source port
+ * @syncp: Synchronization pointer
+ */
+struct edma_rxdesc_stats {
+	u64 src_port_inval;
+	u64 src_port_inval_type;
+	u64 src_port_inval_netdev;
+	struct u64_stats_sync syncp;
+};
+
+/**
+ * struct edma_rxfill_stats - Rx fill descriptor ring stats.
+ * @alloc_failed: Buffer allocation failure count
+ * @page_alloc_failed: Page allocation failure count for page mode
+ * @syncp: Synchronization pointer
+ */
+struct edma_rxfill_stats {
+	u64 alloc_failed;
+	u64 page_alloc_failed;
+	struct u64_stats_sync syncp;
+};
+
+/**
+ * struct edma_rxdesc_pri - Rx descriptor.
+ * @word0: Buffer address
+ * @word1: More bit, priority bit, service code
+ * @word2: Opaque low bits
+ * @word3: Opaque high bits
+ * @word4: Destination and source information
+ * @word5: WiFi QoS, data length
+ * @word6: Hash value, check sum status
+ * @word7: DSCP, packet offsets
+ */
+struct edma_rxdesc_pri {
+	u32 word0;
+	u32 word1;
+	u32 word2;
+	u32 word3;
+	u32 word4;
+	u32 word5;
+	u32 word6;
+	u32 word7;
+};
+
+ /**
+  * struct edma_rxdesc_sec - Rx secondary descriptor.
+  * @word0: Timestamp
+  * @word1: Secondary checksum status
+  * @word2: QoS tag
+  * @word3: Flow index details
+  * @word4: Secondary packet offsets
+  * @word5: Multicast bit, checksum
+  * @word6: SVLAN, CVLAN
+  * @word7: Secondary SVLAN, CVLAN
+  */
+struct edma_rxdesc_sec {
+	u32 word0;
+	u32 word1;
+	u32 word2;
+	u32 word3;
+	u32 word4;
+	u32 word5;
+	u32 word6;
+	u32 word7;
+};
+
+/**
+ * struct edma_rxfill_desc - RxFill descriptor.
+ * @word0: Buffer address
+ * @word1: Buffer size
+ * @word2: Opaque low bits
+ * @word3: Opaque high bits
+ */
+struct edma_rxfill_desc {
+	u32 word0;
+	u32 word1;
+	u32 word2;
+	u32 word3;
+};
+
+/**
+ * struct edma_rxfill_ring - RxFill ring
+ * @ring_id: RxFill ring number
+ * @count: Number of descriptors in the ring
+ * @prod_idx: Ring producer index
+ * @alloc_size: Buffer size to allocate
+ * @desc: Descriptor ring virtual address
+ * @dma: Descriptor ring physical address
+ * @buf_len: Buffer length for rxfill descriptor
+ * @page_mode: Page mode for Rx processing
+ * @rx_fill_stats: Rx fill ring statistics
+ */
+struct edma_rxfill_ring {
+	u32 ring_id;
+	u32 count;
+	u32 prod_idx;
+	u32 alloc_size;
+	struct edma_rxfill_desc *desc;
+	dma_addr_t dma;
+	u32 buf_len;
+	bool page_mode;
+	struct edma_rxfill_stats rxfill_stats;
+};
+
+/**
+ * struct edma_rxdesc_ring - RxDesc ring
+ * @napi: Pointer to napi
+ * @ring_id: Rxdesc ring number
+ * @count: Number of descriptors in the ring
+ * @work_leftover: Leftover descriptors to be processed
+ * @cons_idx: Ring consumer index
+ * @pdesc: Primary descriptor ring virtual address
+ * @pdesc_head: Primary descriptor head in case of scatter-gather frame
+ * @sdesc: Secondary descriptor ring virtual address
+ * @rxdesc_stats: Rx descriptor ring statistics
+ * @rxfill: RxFill ring used
+ * @napi_added: Flag to indicate NAPI add status
+ * @pdma: Primary descriptor ring physical address
+ * @sdma: Secondary descriptor ring physical address
+ * @head: Head of the skb list in case of scatter-gather frame
+ * @last: Last skb of the skb list in case of scatter-gather frame
+ */
+struct edma_rxdesc_ring {
+	struct napi_struct napi;
+	u32 ring_id;
+	u32 count;
+	u32 work_leftover;
+	u32 cons_idx;
+	struct edma_rxdesc_pri *pdesc;
+	struct edma_rxdesc_pri *pdesc_head;
+	struct edma_rxdesc_sec *sdesc;
+	struct edma_rxdesc_stats rxdesc_stats;
+	struct edma_rxfill_ring *rxfill;
+	bool napi_added;
+	dma_addr_t pdma;
+	dma_addr_t sdma;
+	struct sk_buff *head;
+	struct sk_buff *last;
+};
+
+irqreturn_t edma_rx_handle_irq(int irq, void *ctx);
+int edma_rx_alloc_buffer(struct edma_rxfill_ring *rxfill_ring, int alloc_count);
+int edma_rx_napi_poll(struct napi_struct *napi, int budget);
+#endif
