]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
qualcommbe: v6.12: add PPE driver (part 2)
authorAlexandru Gagniuc <mr.nuke.me@gmail.com>
Wed, 14 May 2025 03:03:38 +0000 (22:03 -0500)
committerRobert Marko <robimarko@gmail.com>
Sat, 31 May 2025 10:25:48 +0000 (12:25 +0200)
Add the second part of the PPE driver. This includes the EDMA and
network device support. This part does not appear to have been
officially submitted for upstream review. The series is taken from
target/linux/qualcommbe/patches-6.6, and had to be heavily modified
in order to compile of v6.12. Changes to patches are noted in the
respective patch body.

Also add the PPE and EDMA nodes in this series.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/18796
Signed-off-by: Robert Marko <robimarko@gmail.com>
18 files changed:
target/linux/qualcommbe/patches-6.12/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch [new file with mode: 0644]
target/linux/qualcommbe/patches-6.12/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch [new file with mode: 0644]

diff --git a/target/linux/qualcommbe/patches-6.12/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch b/target/linux/qualcommbe/patches-6.12/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch
new file mode 100644 (file)
index 0000000..d6292f8
--- /dev/null
@@ -0,0 +1,201 @@
+From 93cf3297818ee61607f0a8d1d34e4fb7fcde3cdf Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 26 Dec 2023 20:18:09 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE scheduler config
+
+PPE scheduler config determines the priority of scheduling the
+packet. The scheduler config is used for supporting the QoS
+offload in PPE hardware.
+
+Change-Id: I4811bd133074757371775a6a69a1cc3cfaa8d0d0
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: rebase patch on top of PPE driver submission from 20250209.
+        Add the ppe_queue_priority_set() function and its
+        dependencies. They will be used in the edma support in
+        susequent changes.
+        ppe_queue_priority_set() used to be part of ppe_api.c, and
+        is hereby moved to ppe_config.c .
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ .../net/ethernet/qualcomm/ppe/ppe_config.c    | 141 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_config.h    |   5 +
+ 2 files changed, 146 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.c
+@@ -864,6 +864,51 @@ static int ppe_scheduler_l0_queue_map_se
+                                 val);
+ }
++/* Get the first level scheduler configuration. */
++static int ppe_scheduler_l0_queue_map_get(struct ppe_device *ppe_dev,
++                                        int node_id, int *port,
++                                        struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      reg = PPE_L0_FLOW_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->flow_id = FIELD_GET(PPE_L0_FLOW_MAP_TBL_FLOW_ID, val);
++      scheduler_cfg->pri = FIELD_GET(PPE_L0_FLOW_MAP_TBL_C_PRI, val);
++      scheduler_cfg->drr_node_wt = FIELD_GET(PPE_L0_FLOW_MAP_TBL_C_NODE_WT, val);
++
++      reg = PPE_L0_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg->flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg->pri) *
++            PPE_L0_C_FLOW_CFG_TBL_INC;
++
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->drr_node_id = FIELD_GET(PPE_L0_C_FLOW_CFG_TBL_NODE_ID, val);
++      scheduler_cfg->unit_is_packet = FIELD_GET(PPE_L0_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, val);
++
++      reg = PPE_L0_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_PORT_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      *port = FIELD_GET(PPE_L0_FLOW_PORT_MAP_TBL_PORT_NUM, val);
++
++      reg = PPE_L0_COMP_CFG_TBL_ADDR + node_id * PPE_L0_COMP_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->frame_mode = FIELD_GET(PPE_L0_COMP_CFG_TBL_NODE_METER_LEN, val);
++
++      return 0;
++}
++
+ /* Set the PPE flow level scheduler configuration. */
+ static int ppe_scheduler_l1_queue_map_set(struct ppe_device *ppe_dev,
+                                         int node_id, int port,
+@@ -916,6 +961,50 @@ static int ppe_scheduler_l1_queue_map_se
+       return regmap_update_bits(ppe_dev->regmap, reg, PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
+ }
++/* Get the second level scheduler configuration. */
++static int ppe_scheduler_l1_queue_map_get(struct ppe_device *ppe_dev,
++                                        int node_id, int *port,
++                                        struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      u32 val, reg;
++      int ret;
++
++      reg = PPE_L1_FLOW_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->flow_id = FIELD_GET(PPE_L1_FLOW_MAP_TBL_FLOW_ID, val);
++      scheduler_cfg->pri = FIELD_GET(PPE_L1_FLOW_MAP_TBL_C_PRI, val);
++      scheduler_cfg->drr_node_wt = FIELD_GET(PPE_L1_FLOW_MAP_TBL_C_NODE_WT, val);
++
++      reg = PPE_L1_C_FLOW_CFG_TBL_ADDR +
++            (scheduler_cfg->flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg->pri) *
++            PPE_L1_C_FLOW_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->drr_node_id = FIELD_GET(PPE_L1_C_FLOW_CFG_TBL_NODE_ID, val);
++      scheduler_cfg->unit_is_packet = FIELD_GET(PPE_L1_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, val);
++
++      reg = PPE_L1_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_PORT_MAP_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      *port = FIELD_GET(PPE_L1_FLOW_PORT_MAP_TBL_PORT_NUM, val);
++
++      reg = PPE_L1_COMP_CFG_TBL_ADDR + node_id * PPE_L1_COMP_CFG_TBL_INC;
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret)
++              return ret;
++
++      scheduler_cfg->frame_mode = FIELD_GET(PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
++
++      return 0;
++}
++
+ /**
+  * ppe_queue_scheduler_set - Configure scheduler for PPE hardware queue
+  * @ppe_dev: PPE device
+@@ -942,6 +1031,58 @@ int ppe_queue_scheduler_set(struct ppe_d
+ }
+ /**
++ * ppe_queue_scheduler_get - get QoS scheduler of PPE hardware queue
++ * @ppe_dev: PPE device
++ * @node_id: PPE node ID
++ * @flow_level: Flow level scheduler or queue level scheduler
++ * @port: PPE port ID to get scheduler config
++ * @scheduler_cfg: QoS scheduler configuration
++ *
++ * The hardware QoS function is supported by PPE, the current scheduler
++ * configuration can be acquired based on the queue ID of PPE port.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_queue_scheduler_get(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int *port,
++                          struct ppe_scheduler_cfg *scheduler_cfg)
++{
++      if (flow_level)
++              return ppe_scheduler_l1_queue_map_get(ppe_dev, node_id,
++                                                    port, scheduler_cfg);
++
++      return ppe_scheduler_l0_queue_map_get(ppe_dev, node_id,
++                                            port, scheduler_cfg);
++}
++
++
++/**
++ * ppe_queue_priority_set - set scheduler priority of PPE hardware queue
++ * @ppe_dev: PPE device
++ * @node_id: PPE hardware node ID, which is either queue ID or flow ID
++ * @priority: Qos scheduler priority
++ *
++ * Configure scheduler priority of PPE hardware queque, the maximum node
++ * ID supported is PPE_QUEUE_ID_NUM added by PPE_FLOW_ID_NUM, queue ID
++ * belongs to level 0, flow ID belongs to level 1 in the packet pipeline.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_queue_priority_set(struct ppe_device *ppe_dev,
++                         int node_id, int priority)
++{
++      struct ppe_scheduler_cfg sch_cfg;
++      int ret, port, level = 0;
++
++      ret = ppe_queue_scheduler_get(ppe_dev, node_id, level, &port, &sch_cfg);
++      if (ret)
++              return ret;
++
++      sch_cfg.pri = priority;
++      return ppe_queue_scheduler_set(ppe_dev, node_id, level, port, sch_cfg);
++}
++
++/**
+  * ppe_queue_ucast_base_set - Set PPE unicast queue base ID and profile ID
+  * @ppe_dev: PPE device
+  * @queue_dst: PPE queue destination configuration
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_config.h
+@@ -291,6 +291,11 @@ int ppe_hw_config(struct ppe_device *ppe
+ int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
+                           int node_id, bool flow_level, int port,
+                           struct ppe_scheduler_cfg scheduler_cfg);
++int ppe_queue_scheduler_get(struct ppe_device *ppe_dev,
++                          int node_id, bool flow_level, int *port,
++                          struct ppe_scheduler_cfg *scheduler_cfg);
++int ppe_queue_priority_set(struct ppe_device *ppe_dev,
++                         int queue_id, int priority);
+ int ppe_queue_ucast_base_set(struct ppe_device *ppe_dev,
+                            struct ppe_queue_ucast_dest queue_dst,
+                            int queue_base,
diff --git a/target/linux/qualcommbe/patches-6.12/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch b/target/linux/qualcommbe/patches-6.12/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch
new file mode 100644 (file)
index 0000000..348e834
--- /dev/null
@@ -0,0 +1,1040 @@
+From dbb3711ab25ea410ad5286b2f39dccd954cda225 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Thu, 29 Feb 2024 16:59:53 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add phylink support for PPE MAC
+ ports
+
+Add MAC initialization and phylink functions for PPE MAC ports.
+
+Change-Id: I39dcba671732392bcfa2e734473fd083989bfbec
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig        |   3 +
+ drivers/net/ethernet/qualcomm/ppe/Makefile   |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe.c      |   9 +
+ drivers/net/ethernet/qualcomm/ppe/ppe.h      |   2 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 728 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  76 ++
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 124 ++++
+ 7 files changed, 943 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -66,6 +66,9 @@ config QCOM_PPE
+       depends on HAS_IOMEM && OF
+       depends on COMMON_CLK
+       select REGMAP_MMIO
++      select PHYLINK
++      select PCS_QCOM_IPQ_UNIPHY
++      select SFP
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -4,4 +4,4 @@
+ #
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+-qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o
++qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -17,6 +17,7 @@
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
++#include "ppe_port.h"
+ #define PPE_PORT_MAX          8
+ #define PPE_CLK_RATE          353000000
+@@ -200,6 +201,11 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE HW config failed\n");
++      ret = ppe_port_mac_init(ppe_dev);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                                   "PPE Port MAC initialization failed\n");
++
+       ppe_debugfs_setup(ppe_dev);
+       platform_set_drvdata(pdev, ppe_dev);
+@@ -212,6 +218,9 @@ static void qcom_ppe_remove(struct platf
+       ppe_dev = platform_get_drvdata(pdev);
+       ppe_debugfs_teardown(ppe_dev);
++      ppe_port_mac_deinit(ppe_dev);
++
++      platform_set_drvdata(pdev, NULL);
+ }
+ static const struct of_device_id qcom_ppe_of_match[] = {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
+@@ -20,6 +20,7 @@ struct dentry;
+  * @clk_rate: PPE clock rate.
+  * @num_ports: Number of PPE ports.
+  * @debugfs_root: Debugfs root entry.
++ * @ports: PPE MAC ports.
+  * @num_icc_paths: Number of interconnect paths.
+  * @icc_paths: Interconnect path array.
+  *
+@@ -33,6 +34,7 @@ struct ppe_device {
+       unsigned long clk_rate;
+       unsigned int num_ports;
+       struct dentry *debugfs_root;
++      struct ppe_ports *ports;
+       unsigned int num_icc_paths;
+       struct icc_bulk_data icc_paths[] __counted_by(num_icc_paths);
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -0,0 +1,728 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* PPE Port MAC initialization and PPE port MAC functions. */
++
++#include <linux/clk.h>
++#include <linux/of_net.h>
++#include <linux/pcs/pcs-qcom-ipq-uniphy.h>
++#include <linux/phylink.h>
++#include <linux/reset.h>
++#include <linux/regmap.h>
++#include <linux/rtnetlink.h>
++
++#include "ppe.h"
++#include "ppe_port.h"
++#include "ppe_regs.h"
++
++/* PPE MAC max frame size which including 4bytes FCS */
++#define PPE_PORT_MAC_MAX_FRAME_SIZE           0x3000
++
++/* PPE BM port start for PPE MAC ports */
++#define PPE_BM_PORT_MAC_START                 7
++
++/* PPE port clock and reset name */
++static const char * const ppe_port_clk_rst_name[] = {
++      [PPE_PORT_CLK_RST_MAC] = "port_mac",
++      [PPE_PORT_CLK_RST_RX] = "port_rx",
++      [PPE_PORT_CLK_RST_TX] = "port_tx",
++};
++
++/* PPE port and MAC reset */
++static int ppe_port_mac_reset(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_assert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]);
++      if (ret)
++              goto error;
++
++      /* 150ms delay is required by hardware to reset PPE port and MAC */
++      msleep(150);
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_MAC]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_RX]);
++      if (ret)
++              goto error;
++
++      ret = reset_control_deassert(ppe_port->rstcs[PPE_PORT_CLK_RST_TX]);
++      if (ret)
++              goto error;
++
++      return ret;
++
++error:
++      dev_err(ppe_dev->dev, "%s: port %d reset fail %d\n",
++              __func__, ppe_port->port_id, ret);
++      return ret;
++}
++
++/* PPE port MAC configuration for phylink */
++static void ppe_port_mac_config(struct phylink_config *config,
++                              unsigned int mode,
++                              const struct phylink_link_state *state)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      enum ppe_mac_type mac_type;
++      u32 val, mask;
++      int ret;
++
++      switch (state->interface) {
++      case PHY_INTERFACE_MODE_2500BASEX:
++      case PHY_INTERFACE_MODE_USXGMII:
++      case PHY_INTERFACE_MODE_10GBASER:
++      case PHY_INTERFACE_MODE_10G_QXGMII:
++              mac_type = PPE_MAC_TYPE_XGMAC;
++              break;
++      case PHY_INTERFACE_MODE_QSGMII:
++      case PHY_INTERFACE_MODE_PSGMII:
++      case PHY_INTERFACE_MODE_SGMII:
++      case PHY_INTERFACE_MODE_1000BASEX:
++              mac_type = PPE_MAC_TYPE_GMAC;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Unsupport interface %s\n",
++                      __func__, phy_modes(state->interface));
++              return;
++      }
++
++      /* Reset Port MAC for GMAC */
++      if (mac_type == PPE_MAC_TYPE_GMAC) {
++              ret = ppe_port_mac_reset(ppe_port);
++              if (ret)
++                      goto err_mac_config;
++      }
++
++      /* Port mux to select GMAC or XGMAC */
++      mask = PPE_PORT_SEL_XGMAC(port);
++      val = mac_type == PPE_MAC_TYPE_GMAC ? 0 : mask;
++      ret = regmap_update_bits(ppe_dev->regmap,
++                               PPE_PORT_MUX_CTRL_ADDR,
++                               mask, val);
++      if (ret)
++              goto err_mac_config;
++
++      ppe_port->mac_type = mac_type;
++
++      return;
++
++err_mac_config:
++      dev_err(ppe_dev->dev, "%s: port %d MAC config fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port GMAC link up configuration */
++static int ppe_port_gmac_link_up(struct ppe_port *ppe_port, int speed,
++                               int duplex, bool tx_pause, bool rx_pause)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* Set GMAC speed */
++      switch (speed) {
++      case SPEED_1000:
++              val = GMAC_SPEED_1000;
++              break;
++      case SPEED_100:
++              val = GMAC_SPEED_100;
++              break;
++      case SPEED_10:
++              val = GMAC_SPEED_10;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Invalid GMAC speed %s\n",
++                      __func__, phy_speed_to_str(speed));
++              return -EINVAL;
++      }
++
++      reg = PPE_PORT_GMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_SPEED_ADDR,
++                               GMAC_SPEED_M, val);
++      if (ret)
++              return ret;
++
++      /* Set duplex, flow control and enable GMAC */
++      val = GMAC_TRXEN;
++      if (duplex == DUPLEX_FULL)
++              val |= GMAC_DUPLEX_FULL;
++      if (tx_pause)
++              val |= GMAC_TXFCEN;
++      if (rx_pause)
++              val |= GMAC_RXFCEN;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_ENABLE_ADDR,
++                               GMAC_ENABLE_ALL, val);
++
++      return ret;
++}
++
++/* PPE port XGMAC link up configuration */
++static int ppe_port_xgmac_link_up(struct ppe_port *ppe_port,
++                                phy_interface_t interface,
++                                int speed, int duplex,
++                                bool tx_pause, bool rx_pause)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* Set XGMAC TX speed and enable TX */
++      switch (speed) {
++      case SPEED_10000:
++              if (interface == PHY_INTERFACE_MODE_USXGMII)
++                      val = XGMAC_SPEED_10000_USXGMII;
++              else
++                      val = XGMAC_SPEED_10000;
++              break;
++      case SPEED_5000:
++              val = XGMAC_SPEED_5000;
++              break;
++      case SPEED_2500:
++              if (interface == PHY_INTERFACE_MODE_USXGMII ||
++                  interface == PHY_INTERFACE_MODE_10G_QXGMII)
++                      val = XGMAC_SPEED_2500_USXGMII;
++              else
++                      val = XGMAC_SPEED_2500;
++              break;
++      case SPEED_1000:
++              val = XGMAC_SPEED_1000;
++              break;
++      case SPEED_100:
++              val = XGMAC_SPEED_100;
++              break;
++      case SPEED_10:
++              val = XGMAC_SPEED_10;
++              break;
++      default:
++              dev_err(ppe_dev->dev, "%s: Invalid XGMAC speed %s\n",
++                      __func__, phy_speed_to_str(speed));
++              return -EINVAL;
++      }
++
++      reg = PPE_PORT_XGMAC_ADDR(port);
++      val |= XGMAC_TXEN;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR,
++                               XGMAC_SPEED_M | XGMAC_TXEN, val);
++      if (ret)
++              return ret;
++
++      /* Set XGMAC TX flow control */
++      val = FIELD_PREP(XGMAC_PAUSE_TIME_M, FIELD_MAX(XGMAC_PAUSE_TIME_M));
++      val |= tx_pause ? XGMAC_TXFCEN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_FLOW_CTRL_ADDR,
++                               XGMAC_PAUSE_TIME_M | XGMAC_TXFCEN, val);
++      if (ret)
++              return ret;
++
++      /* Set XGMAC RX flow control */
++      val = rx_pause ? XGMAC_RXFCEN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_FLOW_CTRL_ADDR,
++                               XGMAC_RXFCEN, val);
++      if (ret)
++              return ret;
++
++      /* Enable XGMAC RX*/
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR,
++                               XGMAC_RXEN, XGMAC_RXEN);
++
++      return ret;
++}
++
++/* PPE port MAC link up configuration for phylink */
++static void ppe_port_mac_link_up(struct phylink_config *config,
++                               struct phy_device *phy,
++                               unsigned int mode,
++                               phy_interface_t interface,
++                               int speed, int duplex,
++                               bool tx_pause, bool rx_pause)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      enum ppe_mac_type mac_type = ppe_port->mac_type;
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      if (mac_type == PPE_MAC_TYPE_GMAC)
++              ret = ppe_port_gmac_link_up(ppe_port,
++                                          speed, duplex, tx_pause, rx_pause);
++      else
++              ret = ppe_port_xgmac_link_up(ppe_port, interface,
++                                           speed, duplex, tx_pause, rx_pause);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      /* Set PPE port BM flow control */
++      reg = PPE_BM_PORT_FC_MODE_ADDR +
++              PPE_BM_PORT_FC_MODE_INC * (port + PPE_BM_PORT_MAC_START);
++      val = tx_pause ? PPE_BM_PORT_FC_MODE_EN : 0;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_BM_PORT_FC_MODE_EN, val);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      /* Enable PPE port TX */
++      reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_PORT_BRIDGE_TXMAC_EN,
++                               PPE_PORT_BRIDGE_TXMAC_EN);
++      if (ret)
++              goto err_port_mac_link_up;
++
++      return;
++
++err_port_mac_link_up:
++      dev_err(ppe_dev->dev, "%s: port %d link up fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port MAC link down configuration for phylink */
++static void ppe_port_mac_link_down(struct phylink_config *config,
++                                 unsigned int mode,
++                                 phy_interface_t interface)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      enum ppe_mac_type mac_type = ppe_port->mac_type;
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg;
++
++      /* Disable PPE port TX */
++      reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_PORT_BRIDGE_TXMAC_EN, 0);
++      if (ret)
++              goto err_port_mac_link_down;
++
++      /* Disable PPE MAC */
++      if (mac_type == PPE_MAC_TYPE_GMAC) {
++              reg = PPE_PORT_GMAC_ADDR(port) + GMAC_ENABLE_ADDR;
++              ret = regmap_update_bits(ppe_dev->regmap, reg, GMAC_TRXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++      } else {
++              reg = PPE_PORT_XGMAC_ADDR(port);
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       reg + XGMAC_RX_CONFIG_ADDR,
++                                       XGMAC_RXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       reg + XGMAC_TX_CONFIG_ADDR,
++                                       XGMAC_TXEN, 0);
++              if (ret)
++                      goto err_port_mac_link_down;
++      }
++
++      return;
++
++err_port_mac_link_down:
++      dev_err(ppe_dev->dev, "%s: port %d link down fail %d\n",
++              __func__, port, ret);
++}
++
++/* PPE port MAC PCS selection for phylink */
++static
++struct phylink_pcs *ppe_port_mac_select_pcs(struct phylink_config *config,
++                                          phy_interface_t interface)
++{
++      struct ppe_port *ppe_port = container_of(config, struct ppe_port,
++                                               phylink_config);
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 val;
++
++      /* PPE port5 can connects with PCS0 or PCS1. In PSGMII
++       * mode, it selects PCS0; otherwise, it selects PCS1.
++       */
++      if (port == 5) {
++              val = interface == PHY_INTERFACE_MODE_PSGMII ?
++                      0 : PPE_PORT5_SEL_PCS1;
++              ret = regmap_update_bits(ppe_dev->regmap,
++                                       PPE_PORT_MUX_CTRL_ADDR,
++                                       PPE_PORT5_SEL_PCS1, val);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "%s: port5 select PCS fail %d\n",
++                              __func__, ret);
++                      return NULL;
++              }
++      }
++
++      return ppe_port->pcs;
++}
++
++static const struct phylink_mac_ops ppe_phylink_ops = {
++      .mac_config = ppe_port_mac_config,
++      .mac_link_up = ppe_port_mac_link_up,
++      .mac_link_down = ppe_port_mac_link_down,
++      .mac_select_pcs = ppe_port_mac_select_pcs,
++};
++
++/**
++ * ppe_port_phylink_setup() - Set phylink instance for the given PPE port
++ * @ppe_port: PPE port
++ * @netdev: Netdevice
++ *
++ * Description: Wrapper function to help setup phylink for the PPE port
++ * specified by @ppe_port and associated with the net device @netdev.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_phylink_setup(struct ppe_port *ppe_port, struct net_device *netdev)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      struct device_node *pcs_node;
++      int ret;
++
++      /* Create PCS */
++      pcs_node = of_parse_phandle(ppe_port->np, "pcs-handle", 0);
++      if (!pcs_node)
++              return -ENODEV;
++
++      ppe_port->pcs = ipq_unipcs_create(pcs_node);
++      of_node_put(pcs_node);
++      if (IS_ERR(ppe_port->pcs)) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to create PCS\n",
++                      __func__, ppe_port->port_id);
++              return PTR_ERR(ppe_port->pcs);
++      }
++
++      /* Port phylink capability */
++      ppe_port->phylink_config.dev = &netdev->dev;
++      ppe_port->phylink_config.type = PHYLINK_NETDEV;
++      ppe_port->phylink_config.mac_capabilities = MAC_ASYM_PAUSE |
++              MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000 |
++              MAC_2500FD | MAC_5000FD | MAC_10000FD;
++      __set_bit(PHY_INTERFACE_MODE_QSGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_PSGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_SGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_1000BASEX,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_2500BASEX,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_USXGMII,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_10GBASER,
++                ppe_port->phylink_config.supported_interfaces);
++      __set_bit(PHY_INTERFACE_MODE_10G_QXGMII,
++                ppe_port->phylink_config.supported_interfaces);
++
++      /* Create phylink */
++      ppe_port->phylink = phylink_create(&ppe_port->phylink_config,
++                                         of_fwnode_handle(ppe_port->np),
++                                         ppe_port->interface,
++                                         &ppe_phylink_ops);
++      if (IS_ERR(ppe_port->phylink)) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to create phylink\n",
++                      __func__, ppe_port->port_id);
++              ret = PTR_ERR(ppe_port->phylink);
++              goto err_free_pcs;
++      }
++
++      /* Connect phylink */
++      ret = phylink_of_phy_connect(ppe_port->phylink, ppe_port->np, 0);
++      if (ret) {
++              dev_err(ppe_dev->dev, "%s: port %d failed to connect phylink\n",
++                      __func__, ppe_port->port_id);
++              goto err_free_phylink;
++      }
++
++      return 0;
++
++err_free_phylink:
++      phylink_destroy(ppe_port->phylink);
++      ppe_port->phylink = NULL;
++err_free_pcs:
++      ipq_unipcs_destroy(ppe_port->pcs);
++      ppe_port->pcs = NULL;
++      return ret;
++}
++
++/**
++ * ppe_port_phylink_destroy() - Destroy phylink instance for the given PPE port
++ * @ppe_port: PPE port
++ *
++ * Description: Wrapper function to help destroy phylink for the PPE port
++ * specified by @ppe_port.
++ */
++void ppe_port_phylink_destroy(struct ppe_port *ppe_port)
++{
++      /* Destroy phylink */
++      if (ppe_port->phylink) {
++              rtnl_lock();
++              phylink_disconnect_phy(ppe_port->phylink);
++              rtnl_unlock();
++              phylink_destroy(ppe_port->phylink);
++              ppe_port->phylink = NULL;
++      }
++
++      /* Destroy PCS */
++      if (ppe_port->pcs) {
++              ipq_unipcs_destroy(ppe_port->pcs);
++              ppe_port->pcs = NULL;
++      }
++}
++
++/* PPE port clock initialization */
++static int ppe_port_clock_init(struct ppe_port *ppe_port)
++{
++      struct device_node *port_node = ppe_port->np;
++      struct reset_control *rstc;
++      struct clk *clk;
++      int i, j, ret;
++
++      for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) {
++              /* Get PPE port resets which will be used to reset PPE
++               * port and MAC.
++               */
++              rstc = of_reset_control_get_exclusive(port_node,
++                                                    ppe_port_clk_rst_name[i]);
++              if (IS_ERR(rstc)) {
++                      ret =  PTR_ERR(rstc);
++                      goto err_rst;
++              }
++
++              clk = of_clk_get_by_name(port_node, ppe_port_clk_rst_name[i]);
++              if (IS_ERR(clk)) {
++                      ret = PTR_ERR(clk);
++                      goto err_clk_get;
++              }
++
++              ret = clk_prepare_enable(clk);
++              if (ret)
++                      goto err_clk_en;
++
++              ppe_port->clks[i] = clk;
++              ppe_port->rstcs[i] = rstc;
++      }
++
++      return 0;
++
++err_clk_en:
++      clk_put(clk);
++err_clk_get:
++      reset_control_put(rstc);
++err_rst:
++      for (j = 0; j < i; j++) {
++              clk_disable_unprepare(ppe_port->clks[j]);
++              clk_put(ppe_port->clks[j]);
++              reset_control_put(ppe_port->rstcs[j]);
++      }
++
++      return ret;
++}
++
++/* PPE port clock deinitialization */
++static void ppe_port_clock_deinit(struct ppe_port *ppe_port)
++{
++      int i;
++
++      for (i = 0; i < PPE_PORT_CLK_RST_MAX; i++) {
++              clk_disable_unprepare(ppe_port->clks[i]);
++              clk_put(ppe_port->clks[i]);
++              reset_control_put(ppe_port->rstcs[i]);
++      }
++}
++
++/* PPE port MAC hardware init configuration */
++static int ppe_port_mac_hw_init(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int ret, port = ppe_port->port_id;
++      u32 reg, val;
++
++      /* GMAC RX and TX are initialized as disabled */
++      reg = PPE_PORT_GMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap,
++                               reg + GMAC_ENABLE_ADDR, GMAC_TRXEN, 0);
++      if (ret)
++              return ret;
++
++      /* GMAC max frame size configuration */
++      val = FIELD_PREP(GMAC_JUMBO_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_JUMBO_SIZE_ADDR,
++                               GMAC_JUMBO_SIZE_M, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(GMAC_MAXFRAME_SIZE_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      val |= FIELD_PREP(GMAC_TX_THD_M, 0x1);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_CTRL_ADDR,
++                               GMAC_CTRL_MASK, val);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(GMAC_HIGH_IPG_M, 0xc);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_DBG_CTRL_ADDR,
++                               GMAC_HIGH_IPG_M, val);
++      if (ret)
++              return ret;
++
++      /* Enable and reset GMAC MIB counters and set as read clear
++       * mode, the GMAC MIB counters will be cleared after reading.
++       */
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR,
++                               GMAC_MIB_CTRL_MASK, GMAC_MIB_CTRL_MASK);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + GMAC_MIB_CTRL_ADDR,
++                               GMAC_MIB_RST, 0);
++      if (ret)
++              return ret;
++
++      /* XGMAC RX and TX disabled and max frame size configuration */
++      reg = PPE_PORT_XGMAC_ADDR(port);
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_TX_CONFIG_ADDR,
++                               XGMAC_TXEN | XGMAC_JD, XGMAC_JD);
++      if (ret)
++              return ret;
++
++      val = FIELD_PREP(XGMAC_GPSL_M, PPE_PORT_MAC_MAX_FRAME_SIZE);
++      val |= XGMAC_GPSLEN;
++      val |= XGMAC_CST;
++      val |= XGMAC_ACS;
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_RX_CONFIG_ADDR,
++                               XGMAC_RX_CONFIG_MASK, val);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_WD_TIMEOUT_ADDR,
++                               XGMAC_WD_TIMEOUT_MASK, XGMAC_WD_TIMEOUT_VAL);
++      if (ret)
++              return ret;
++
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_PKT_FILTER_ADDR,
++                               XGMAC_PKT_FILTER_MASK, XGMAC_PKT_FILTER_VAL);
++      if (ret)
++              return ret;
++
++      /* Enable and reset XGMAC MIB counters */
++      ret = regmap_update_bits(ppe_dev->regmap, reg + XGMAC_MMC_CTRL_ADDR,
++                               XGMAC_MCF | XGMAC_CNTRST, XGMAC_CNTRST);
++
++      return ret;
++}
++
++/**
++ * ppe_port_mac_init() - Initialization of PPE ports for the PPE device
++ * @ppe_dev: PPE device
++ *
++ * Description: Initialize the PPE MAC ports on the PPE device specified
++ * by @ppe_dev.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_mac_init(struct ppe_device *ppe_dev)
++{
++      struct device_node *ports_node, *port_node;
++      int port, num, ret, j, i = 0;
++      struct ppe_ports *ppe_ports;
++      phy_interface_t phy_mode;
++
++      ports_node = of_get_child_by_name(ppe_dev->dev->of_node,
++                                        "ethernet-ports");
++      if (!ports_node) {
++              dev_err(ppe_dev->dev, "Failed to get ports node\n");
++              return -ENODEV;
++      }
++
++      num = of_get_available_child_count(ports_node);
++
++      ppe_ports = devm_kzalloc(ppe_dev->dev,
++                               struct_size(ppe_ports, port, num),
++                               GFP_KERNEL);
++      if (!ppe_ports) {
++              ret = -ENOMEM;
++              goto err_ports_node;
++      }
++
++      ppe_dev->ports = ppe_ports;
++      ppe_ports->num = num;
++
++      for_each_available_child_of_node(ports_node, port_node) {
++              ret = of_property_read_u32(port_node, "reg", &port);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to get port id\n");
++                      goto err_port_node;
++              }
++
++              ret = of_get_phy_mode(port_node, &phy_mode);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to get phy mode\n");
++                      goto err_port_node;
++              }
++
++              ppe_ports->port[i].ppe_dev = ppe_dev;
++              ppe_ports->port[i].port_id = port;
++              ppe_ports->port[i].np = port_node;
++              ppe_ports->port[i].interface = phy_mode;
++
++              ret = ppe_port_clock_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize port clocks\n");
++                      goto err_port_clk;
++              }
++
++              ret = ppe_port_mac_hw_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize MAC hardware\n");
++                      goto err_port_node;
++              }
++
++              i++;
++      }
++
++      of_node_put(ports_node);
++      return 0;
++
++err_port_clk:
++      for (j = 0; j < i; j++)
++              ppe_port_clock_deinit(&ppe_ports->port[j]);
++err_port_node:
++      of_node_put(port_node);
++err_ports_node:
++      of_node_put(ports_node);
++      return ret;
++}
++
++/**
++ * ppe_port_mac_deinit() - Deinitialization of PPE ports for the PPE device
++ * @ppe_dev: PPE device
++ *
++ * Description: Deinitialize the PPE MAC ports on the PPE device specified
++ * by @ppe_dev.
++ */
++void ppe_port_mac_deinit(struct ppe_device *ppe_dev)
++{
++      struct ppe_port *ppe_port;
++      int i;
++
++      for (i = 0; i < ppe_dev->ports->num; i++) {
++              ppe_port = &ppe_dev->ports->port[i];
++              ppe_port_clock_deinit(ppe_port);
++      }
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -0,0 +1,76 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ *
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __PPE_PORT_H__
++#define __PPE_PORT_H__
++
++#include <linux/phylink.h>
++
++/**
++ * enum ppe_port_clk_rst_type - PPE port clock and reset ID type
++ * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC
++ * @PPE_PORT_CLK_RST_RX: The clock and reset ID for port receive path
++ * @PPE_PORT_CLK_RST_TX: The clock and reset for port transmit path
++ * @PPE_PORT_CLK_RST_MAX: The maximum of port clock and reset
++ */
++enum ppe_port_clk_rst_type {
++      PPE_PORT_CLK_RST_MAC,
++      PPE_PORT_CLK_RST_RX,
++      PPE_PORT_CLK_RST_TX,
++      PPE_PORT_CLK_RST_MAX,
++};
++
++/**
++ * enum ppe_mac_type - PPE MAC type
++ * @PPE_MAC_TYPE_GMAC: GMAC type
++ * @PPE_MAC_TYPE_XGMAC: XGMAC type
++ */
++enum ppe_mac_type {
++      PPE_MAC_TYPE_GMAC,
++      PPE_MAC_TYPE_XGMAC,
++};
++
++/**
++ * struct ppe_port - Private data for each PPE port
++ * @phylink: Linux phylink instance
++ * @phylink_config: Linux phylink configurations
++ * @pcs: Linux phylink PCS instance
++ * @np: Port device tree node
++ * @ppe_dev: Back pointer to PPE device private data
++ * @interface: Port interface mode
++ * @mac_type: Port MAC type, GMAC or XGMAC
++ * @port_id: Port ID
++ * @clks: Port clocks
++ * @rstcs: Port resets
++ */
++struct ppe_port {
++      struct phylink *phylink;
++      struct phylink_config phylink_config;
++      struct phylink_pcs *pcs;
++      struct device_node *np;
++      struct ppe_device *ppe_dev;
++      phy_interface_t interface;
++      enum ppe_mac_type mac_type;
++      int port_id;
++      struct clk *clks[PPE_PORT_CLK_RST_MAX];
++      struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX];
++};
++
++/**
++ * struct ppe_ports - Array of PPE ports
++ * @num: Number of PPE ports
++ * @port: Each PPE port private data
++ */
++struct ppe_ports {
++      unsigned int num;
++      struct ppe_port port[] __counted_by(num);
++};
++
++int ppe_port_mac_init(struct ppe_device *ppe_dev);
++void ppe_port_mac_deinit(struct ppe_device *ppe_dev);
++int ppe_port_phylink_setup(struct ppe_port *ppe_port,
++                         struct net_device *netdev);
++void ppe_port_phylink_destroy(struct ppe_port *ppe_port);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -9,6 +9,17 @@
+ #include <linux/bitfield.h>
++/* PPE port mux select control register */
++#define PPE_PORT_MUX_CTRL_ADDR                        0x10
++#define PPE_PORT6_SEL_XGMAC                   BIT(13)
++#define PPE_PORT5_SEL_XGMAC                   BIT(12)
++#define PPE_PORT4_SEL_XGMAC                   BIT(11)
++#define PPE_PORT3_SEL_XGMAC                   BIT(10)
++#define PPE_PORT2_SEL_XGMAC                   BIT(9)
++#define PPE_PORT1_SEL_XGMAC                   BIT(8)
++#define PPE_PORT5_SEL_PCS1                    BIT(4)
++#define PPE_PORT_SEL_XGMAC(x)                 (BIT(8) << ((x) - 1))
++
+ /* PPE scheduler configurations for buffer manager block. */
+ #define PPE_BM_SCH_CTRL_ADDR                  0xb000
+ #define PPE_BM_SCH_CTRL_INC                   4
+@@ -556,4 +567,117 @@
+ #define PPE_ENQ_OPR_TBL_ENTRIES                       300
+ #define PPE_ENQ_OPR_TBL_INC                   0x10
+ #define PPE_ENQ_OPR_TBL_ENQ_DISABLE           BIT(0)
++
++/* PPE GMAC and XGMAC register base address */
++#define PPE_PORT_GMAC_ADDR(x)                 (0x001000 + ((x) - 1) * 0x200)
++#define PPE_PORT_XGMAC_ADDR(x)                        (0x500000 + ((x) - 1) * 0x4000)
++
++/* GMAC enable register */
++#define GMAC_ENABLE_ADDR                      0x0
++#define GMAC_TXFCEN                           BIT(6)
++#define GMAC_RXFCEN                           BIT(5)
++#define GMAC_DUPLEX_FULL                      BIT(4)
++#define GMAC_TXEN                             BIT(1)
++#define GMAC_RXEN                             BIT(0)
++
++#define GMAC_TRXEN                            \
++      (GMAC_TXEN | GMAC_RXEN)
++#define GMAC_ENABLE_ALL                               \
++      (GMAC_TXFCEN | GMAC_RXFCEN | GMAC_DUPLEX_FULL | GMAC_TXEN | GMAC_RXEN)
++
++/* GMAC speed register */
++#define GMAC_SPEED_ADDR                               0x4
++#define GMAC_SPEED_M                          GENMASK(1, 0)
++#define GMAC_SPEED_10                         0
++#define GMAC_SPEED_100                                1
++#define GMAC_SPEED_1000                               2
++
++/* GMAC control register */
++#define GMAC_CTRL_ADDR                                0x18
++#define GMAC_TX_THD_M                         GENMASK(27, 24)
++#define GMAC_MAXFRAME_SIZE_M                  GENMASK(21, 8)
++#define GMAC_CRS_SEL                          BIT(6)
++
++#define GMAC_CTRL_MASK                                \
++      (GMAC_TX_THD_M | GMAC_MAXFRAME_SIZE_M | GMAC_CRS_SEL)
++
++/* GMAC debug control register */
++#define GMAC_DBG_CTRL_ADDR                    0x1c
++#define GMAC_HIGH_IPG_M                               GENMASK(15, 8)
++
++/* GMAC jumbo size register */
++#define GMAC_JUMBO_SIZE_ADDR                  0x30
++#define GMAC_JUMBO_SIZE_M                     GENMASK(13, 0)
++
++/* GMAC MIB control register */
++#define GMAC_MIB_CTRL_ADDR                    0x34
++#define GMAC_MIB_RD_CLR                               BIT(2)
++#define GMAC_MIB_RST                          BIT(1)
++#define GMAC_MIB_EN                           BIT(0)
++
++#define GMAC_MIB_CTRL_MASK                    \
++      (GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN)
++
++/* XGMAC TX configuration register */
++#define XGMAC_TX_CONFIG_ADDR                  0x0
++#define XGMAC_SPEED_M                         GENMASK(31, 29)
++#define XGMAC_SPEED_10000_USXGMII             FIELD_PREP(XGMAC_SPEED_M, 4)
++#define XGMAC_SPEED_10000                     FIELD_PREP(XGMAC_SPEED_M, 0)
++#define XGMAC_SPEED_5000                      FIELD_PREP(XGMAC_SPEED_M, 5)
++#define XGMAC_SPEED_2500_USXGMII              FIELD_PREP(XGMAC_SPEED_M, 6)
++#define XGMAC_SPEED_2500                      FIELD_PREP(XGMAC_SPEED_M, 2)
++#define XGMAC_SPEED_1000                      FIELD_PREP(XGMAC_SPEED_M, 3)
++#define XGMAC_SPEED_100                               XGMAC_SPEED_1000
++#define XGMAC_SPEED_10                                XGMAC_SPEED_1000
++#define XGMAC_JD                              BIT(16)
++#define XGMAC_TXEN                            BIT(0)
++
++/* XGMAC RX configuration register */
++#define XGMAC_RX_CONFIG_ADDR                  0x4
++#define XGMAC_GPSL_M                          GENMASK(29, 16)
++#define XGMAC_WD                              BIT(7)
++#define XGMAC_GPSLEN                          BIT(6)
++#define XGMAC_CST                             BIT(2)
++#define XGMAC_ACS                             BIT(1)
++#define XGMAC_RXEN                            BIT(0)
++
++#define XGMAC_RX_CONFIG_MASK                  \
++      (XGMAC_GPSL_M | XGMAC_WD | XGMAC_GPSLEN | XGMAC_CST | \
++       XGMAC_ACS | XGMAC_RXEN)
++
++/* XGMAC packet filter register */
++#define XGMAC_PKT_FILTER_ADDR                 0x8
++#define XGMAC_RA                              BIT(31)
++#define XGMAC_PCF_M                           GENMASK(7, 6)
++#define XGMAC_PR                              BIT(0)
++
++#define XGMAC_PKT_FILTER_MASK                 \
++      (XGMAC_RA | XGMAC_PCF_M | XGMAC_PR)
++#define XGMAC_PKT_FILTER_VAL                  \
++      (XGMAC_RA | XGMAC_PR | FIELD_PREP(XGMAC_PCF_M, 0x2))
++
++/* XGMAC watchdog timeout register */
++#define XGMAC_WD_TIMEOUT_ADDR                 0xc
++#define XGMAC_PWE                             BIT(8)
++#define XGMAC_WTO_M                           GENMASK(3, 0)
++
++#define XGMAC_WD_TIMEOUT_MASK                 \
++      (XGMAC_PWE | XGMAC_WTO_M)
++#define XGMAC_WD_TIMEOUT_VAL                  \
++      (XGMAC_PWE | FIELD_PREP(XGMAC_WTO_M, 0xb))
++
++/* XGMAC TX flow control register */
++#define XGMAC_TX_FLOW_CTRL_ADDR                       0x70
++#define XGMAC_PAUSE_TIME_M                    GENMASK(31, 16)
++#define XGMAC_TXFCEN                          BIT(1)
++
++/* XGMAC RX flow control register */
++#define XGMAC_RX_FLOW_CTRL_ADDR                       0x90
++#define XGMAC_RXFCEN                          BIT(0)
++
++/* XGMAC management counters control register */
++#define XGMAC_MMC_CTRL_ADDR                   0x800
++#define XGMAC_MCF                             BIT(3)
++#define XGMAC_CNTRST                          BIT(0)
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.12/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch b/target/linux/qualcommbe/patches-6.12/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch
new file mode 100644 (file)
index 0000000..1430692
--- /dev/null
@@ -0,0 +1,673 @@
+From dbcc0d01241a1353d8e11e764cf7fcd390ae3f1f Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Thu, 29 Feb 2024 20:16:14 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE port MAC MIB statistics
+ functions
+
+Add PPE port MAC MIB statistics functions which are used by netdev
+ops and ethtool. For GMAC, a polling task is scheduled to read the
+MIB counters periodically to avoid 32bit register counter overflow.
+
+Change-Id: Ic20e240061278f77d703f652e1f7d959db8fac37
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 465 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  13 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h |  91 ++++
+ 3 files changed, 569 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -23,6 +23,122 @@
+ /* PPE BM port start for PPE MAC ports */
+ #define PPE_BM_PORT_MAC_START                 7
++/* Poll interval time to poll GMAC MIBs for overflow protection,
++ * the time should ensure that the 32bit GMAC packet counter
++ * register would not overflow within this time at line rate
++ * speed for 64B packet size.
++ */
++#define PPE_GMIB_POLL_INTERVAL_MS             120000
++
++#define PPE_MAC_MIB_DESC(_s, _o, _n)          \
++      {                                       \
++              .size = (_s),                   \
++              .offset = (_o),                 \
++              .name = (_n),                   \
++      }
++
++/* PPE MAC MIB description */
++struct ppe_mac_mib_info {
++      u32 size;
++      u32 offset;
++      const char *name;
++};
++
++/* PPE GMAC MIB statistics type */
++enum ppe_gmib_stats_type {
++      gmib_rx_broadcast,
++      gmib_rx_pause,
++      gmib_rx_multicast,
++      gmib_rx_fcserr,
++      gmib_rx_alignerr,
++      gmib_rx_runt,
++      gmib_rx_frag,
++      gmib_rx_jumbofcserr,
++      gmib_rx_jumboalignerr,
++      gmib_rx_pkt64,
++      gmib_rx_pkt65to127,
++      gmib_rx_pkt128to255,
++      gmib_rx_pkt256to511,
++      gmib_rx_pkt512to1023,
++      gmib_rx_pkt1024to1518,
++      gmib_rx_pkt1519tomax,
++      gmib_rx_toolong,
++      gmib_rx_bytes_g,
++      gmib_rx_bytes_b,
++      gmib_rx_unicast,
++      gmib_tx_broadcast,
++      gmib_tx_pause,
++      gmib_tx_multicast,
++      gmib_tx_underrun,
++      gmib_tx_pkt64,
++      gmib_tx_pkt65to127,
++      gmib_tx_pkt128to255,
++      gmib_tx_pkt256to511,
++      gmib_tx_pkt512to1023,
++      gmib_tx_pkt1024to1518,
++      gmib_tx_pkt1519tomax,
++      gmib_tx_bytes,
++      gmib_tx_collisions,
++      gmib_tx_abortcol,
++      gmib_tx_multicol,
++      gmib_tx_singlecol,
++      gmib_tx_excdeffer,
++      gmib_tx_deffer,
++      gmib_tx_latecol,
++      gmib_tx_unicast,
++};
++
++/* PPE XGMAC MIB statistics type */
++enum ppe_xgmib_stats_type {
++      xgmib_tx_bytes,
++      xgmib_tx_frames,
++      xgmib_tx_broadcast_g,
++      xgmib_tx_multicast_g,
++      xgmib_tx_pkt64,
++      xgmib_tx_pkt65to127,
++      xgmib_tx_pkt128to255,
++      xgmib_tx_pkt256to511,
++      xgmib_tx_pkt512to1023,
++      xgmib_tx_pkt1024tomax,
++      xgmib_tx_unicast,
++      xgmib_tx_multicast,
++      xgmib_tx_broadcast,
++      xgmib_tx_underflow_err,
++      xgmib_tx_bytes_g,
++      xgmib_tx_frames_g,
++      xgmib_tx_pause,
++      xgmib_tx_vlan_g,
++      xgmib_tx_lpi_usec,
++      xgmib_tx_lpi_tran,
++      xgmib_rx_frames,
++      xgmib_rx_bytes,
++      xgmib_rx_bytes_g,
++      xgmib_rx_broadcast_g,
++      xgmib_rx_multicast_g,
++      xgmib_rx_crc_err,
++      xgmib_rx_runt_err,
++      xgmib_rx_jabber_err,
++      xgmib_rx_undersize_g,
++      xgmib_rx_oversize_g,
++      xgmib_rx_pkt64,
++      xgmib_rx_pkt65to127,
++      xgmib_rx_pkt128to255,
++      xgmib_rx_pkt256to511,
++      xgmib_rx_pkt512to1023,
++      xgmib_rx_pkt1024tomax,
++      xgmib_rx_unicast_g,
++      xgmib_rx_len_err,
++      xgmib_rx_outofrange_err,
++      xgmib_rx_pause,
++      xgmib_rx_fifo_overflow,
++      xgmib_rx_vlan,
++      xgmib_rx_wdog_err,
++      xgmib_rx_lpi_usec,
++      xgmib_rx_lpi_tran,
++      xgmib_rx_drop_frames,
++      xgmib_rx_drop_bytes,
++};
++
+ /* PPE port clock and reset name */
+ static const char * const ppe_port_clk_rst_name[] = {
+       [PPE_PORT_CLK_RST_MAC] = "port_mac",
+@@ -30,6 +146,322 @@ static const char * const ppe_port_clk_r
+       [PPE_PORT_CLK_RST_TX] = "port_tx",
+ };
++/* PPE GMAC MIB statistics description information */
++static const struct ppe_mac_mib_info gmib_info[] = {
++      PPE_MAC_MIB_DESC(4, GMAC_RXBROAD_ADDR, "rx_broadcast"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPAUSE_ADDR, "rx_pause"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXMULTI_ADDR, "rx_multicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXFCSERR_ADDR, "rx_fcserr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXALIGNERR_ADDR, "rx_alignerr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXRUNT_ADDR, "rx_runt"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXFRAG_ADDR, "rx_frag"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOFCSERR_ADDR, "rx_jumbofcserr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXJUMBOALIGNERR_ADDR, "rx_jumboalignerr"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT64_ADDR, "rx_pkt64"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT65TO127_ADDR, "rx_pkt65to127"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT128TO255_ADDR, "rx_pkt128to255"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT256TO511_ADDR, "rx_pkt256to511"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT512TO1023_ADDR, "rx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT1024TO1518_ADDR, "rx_pkt1024to1518"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXPKT1519TOX_ADDR, "rx_pkt1519tomax"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXTOOLONG_ADDR, "rx_toolong"),
++      PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, GMAC_RXBYTE_B_ADDR, "rx_bytes_b"),
++      PPE_MAC_MIB_DESC(4, GMAC_RXUNI_ADDR, "rx_unicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXBROAD_ADDR, "tx_broadcast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPAUSE_ADDR, "tx_pause"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXMULTI_ADDR, "tx_multicast"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXUNDERRUN_ADDR, "tx_underrun"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT64_ADDR, "tx_pkt64"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT65TO127_ADDR, "tx_pkt65to127"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT128TO255_ADDR, "tx_pkt128to255"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT256TO511_ADDR, "tx_pkt256to511"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT512TO1023_ADDR, "tx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT1024TO1518_ADDR, "tx_pkt1024to1518"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXPKT1519TOX_ADDR, "tx_pkt1519tomax"),
++      PPE_MAC_MIB_DESC(8, GMAC_TXBYTE_ADDR, "tx_bytes"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXCOLLISIONS_ADDR, "tx_collisions"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXABORTCOL_ADDR, "tx_abortcol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXMULTICOL_ADDR, "tx_multicol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXSINGLECOL_ADDR, "tx_singlecol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXEXCESSIVEDEFER_ADDR, "tx_excdeffer"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXDEFER_ADDR, "tx_deffer"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXLATECOL_ADDR, "tx_latecol"),
++      PPE_MAC_MIB_DESC(4, GMAC_TXUNI_ADDR, "tx_unicast"),
++};
++
++/* PPE XGMAC MIB statistics description information */
++static const struct ppe_mac_mib_info xgmib_info[] = {
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_GB_ADDR, "tx_bytes"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_GB_ADDR, "tx_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_G_ADDR, "tx_broadcast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_G_ADDR, "tx_multicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT64_GB_ADDR, "tx_pkt64"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT65TO127_GB_ADDR, "tx_pkt65to127"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT128TO255_GB_ADDR, "tx_pkt128to255"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT256TO511_GB_ADDR, "tx_pkt256to511"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT512TO1023_GB_ADDR, "tx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT1024TOMAX_GB_ADDR, "tx_pkt1024tomax"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXUNI_GB_ADDR, "tx_unicast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXMULTI_GB_ADDR, "tx_multicast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBROAD_GB_ADDR, "tx_broadcast"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXUNDERFLOW_ERR_ADDR, "tx_underflow_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXBYTE_G_ADDR, "tx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPKT_G_ADDR, "tx_frames_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXPAUSE_ADDR, "tx_pause"),
++      PPE_MAC_MIB_DESC(8, XGMAC_TXVLAN_G_ADDR, "tx_vlan_g"),
++      PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_USEC_ADDR, "tx_lpi_usec"),
++      PPE_MAC_MIB_DESC(4, XGMAC_TXLPI_TRAN_ADDR, "tx_lpi_tran"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT_GB_ADDR, "rx_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_GB_ADDR, "rx_bytes"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBYTE_G_ADDR, "rx_bytes_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXBROAD_G_ADDR, "rx_broadcast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXMULTI_G_ADDR, "rx_multicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXCRC_ERR_ADDR, "rx_crc_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXRUNT_ERR_ADDR, "rx_runt_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXJABBER_ERR_ADDR, "rx_jabber_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXUNDERSIZE_G_ADDR, "rx_undersize_g"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXOVERSIZE_G_ADDR, "rx_oversize_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT64_GB_ADDR, "rx_pkt64"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT65TO127_GB_ADDR, "rx_pkt65to127"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT128TO255_GB_ADDR, "rx_pkt128to255"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT256TO511_GB_ADDR, "rx_pkt256to511"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT512TO1023_GB_ADDR, "rx_pkt512to1023"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPKT1024TOMAX_GB_ADDR, "rx_pkt1024tomax"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXUNI_G_ADDR, "rx_unicast_g"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXLEN_ERR_ADDR, "rx_len_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXOUTOFRANGE_ADDR, "rx_outofrange_err"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXPAUSE_ADDR, "rx_pause"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXFIFOOVERFLOW_ADDR, "rx_fifo_overflow"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXVLAN_GB_ADDR, "rx_vlan"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXWATCHDOG_ERR_ADDR, "rx_wdog_err"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_USEC_ADDR, "rx_lpi_usec"),
++      PPE_MAC_MIB_DESC(4, XGMAC_RXLPI_TRAN_ADDR, "rx_lpi_tran"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARD_GB_ADDR, "rx_drop_frames"),
++      PPE_MAC_MIB_DESC(8, XGMAC_RXDISCARDBYTE_GB_ADDR, "rx_drop_bytes"),
++};
++
++/* Get GMAC MIBs from registers and accumulate to PPE port GMIB stats array */
++static void ppe_port_gmib_update(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      const struct ppe_mac_mib_info *mib;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      int i, ret;
++
++      for (i = 0; i < ARRAY_SIZE(gmib_info); i++) {
++              mib = &gmib_info[i];
++              reg = PPE_PORT_GMAC_ADDR(port) + mib->offset;
++
++              ret = regmap_read(ppe_dev->regmap, reg, &val);
++              if (ret) {
++                      dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++                      continue;
++              }
++
++              ppe_port->gmib_stats[i] += val;
++              if (mib->size == 8) {
++                      ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
++                      if (ret) {
++                              dev_warn(ppe_dev->dev, "%s: %d\n",
++                                       __func__, ret);
++                              continue;
++                      }
++
++                      ppe_port->gmib_stats[i] += (u64)val << 32;
++              }
++      }
++}
++
++/* Polling task to read GMIB statistics to avoid GMIB 32bit register overflow */
++static void ppe_port_gmib_stats_poll(struct work_struct *work)
++{
++      struct ppe_port *ppe_port = container_of(work, struct ppe_port,
++                                               gmib_read.work);
++      spin_lock(&ppe_port->gmib_stats_lock);
++      ppe_port_gmib_update(ppe_port);
++      spin_unlock(&ppe_port->gmib_stats_lock);
++
++      schedule_delayed_work(&ppe_port->gmib_read,
++                            msecs_to_jiffies(PPE_GMIB_POLL_INTERVAL_MS));
++}
++
++/* Get the XGMAC MIB counter based on the specific MIB stats type */
++static u64 ppe_port_xgmib_get(struct ppe_port *ppe_port,
++                            enum ppe_xgmib_stats_type xgmib_type)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      const struct ppe_mac_mib_info *mib;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      u64 data = 0;
++      int ret;
++
++      mib = &xgmib_info[xgmib_type];
++      reg = PPE_PORT_XGMAC_ADDR(port) + mib->offset;
++
++      ret = regmap_read(ppe_dev->regmap, reg, &val);
++      if (ret) {
++              dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++              goto data_return;
++      }
++
++      data = val;
++      if (mib->size == 8) {
++              ret = regmap_read(ppe_dev->regmap, reg + 4, &val);
++              if (ret) {
++                      dev_warn(ppe_dev->dev, "%s: %d\n", __func__, ret);
++                      goto data_return;
++              }
++
++              data |= (u64)val << 32;
++      }
++
++data_return:
++      return data;
++}
++
++/**
++ * ppe_port_get_sset_count() - Get PPE port statistics string count
++ * @ppe_port: PPE port
++ * @sset: string set ID
++ *
++ * Description: Get the MAC statistics string count for the PPE port
++ * specified by @ppe_port.
++ *
++ * Return: The count of the statistics string.
++ */
++int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset)
++{
++      if (sset != ETH_SS_STATS)
++              return 0;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC)
++              return ARRAY_SIZE(gmib_info);
++      else
++              return ARRAY_SIZE(xgmib_info);
++}
++
++/**
++ * ppe_port_get_strings() - Get PPE port statistics strings
++ * @ppe_port: PPE port
++ * @stringset: string set ID
++ * @data: pointer to statistics strings
++ *
++ * Description: Get the MAC statistics stings for the PPE port
++ * specified by @ppe_port. The strings are stored in the buffer
++ * indicated by @data which used in the ethtool ops.
++ */
++void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data)
++{
++      int i;
++
++      if (stringset != ETH_SS_STATS)
++              return;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
++                      strscpy(data + i * ETH_GSTRING_LEN, gmib_info[i].name,
++                              ETH_GSTRING_LEN);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
++                      strscpy(data + i * ETH_GSTRING_LEN, xgmib_info[i].name,
++                              ETH_GSTRING_LEN);
++      }
++}
++
++/**
++ * ppe_port_get_ethtool_stats() - Get PPE port ethtool statistics
++ * @ppe_port: PPE port
++ * @data: pointer to statistics data
++ *
++ * Description: Get the MAC statistics for the PPE port specified
++ * by @ppe_port. The statistics are stored in the buffer indicated
++ * by @data which used in the ethtool ops.
++ */
++void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data)
++{
++      int i;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              spin_lock(&ppe_port->gmib_stats_lock);
++
++              ppe_port_gmib_update(ppe_port);
++              for (i = 0; i < ARRAY_SIZE(gmib_info); i++)
++                      data[i] = ppe_port->gmib_stats[i];
++
++              spin_unlock(&ppe_port->gmib_stats_lock);
++      } else {
++              for (i = 0; i < ARRAY_SIZE(xgmib_info); i++)
++                      data[i] = ppe_port_xgmib_get(ppe_port, i);
++      }
++}
++
++/**
++ * ppe_port_get_stats64() - Get PPE port statistics
++ * @ppe_port: PPE port
++ * @s: statistics pointer
++ *
++ * Description: Get the MAC statistics for the PPE port specified
++ * by @ppe_port.
++ */
++void ppe_port_get_stats64(struct ppe_port *ppe_port,
++                        struct rtnl_link_stats64 *s)
++{
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              u64 *src = ppe_port->gmib_stats;
++
++              spin_lock(&ppe_port->gmib_stats_lock);
++
++              ppe_port_gmib_update(ppe_port);
++
++              s->rx_packets = src[gmib_rx_unicast] +
++                      src[gmib_rx_broadcast] + src[gmib_rx_multicast];
++
++              s->tx_packets = src[gmib_tx_unicast] +
++                      src[gmib_tx_broadcast] + src[gmib_tx_multicast];
++
++              s->rx_bytes = src[gmib_rx_bytes_g];
++              s->tx_bytes = src[gmib_tx_bytes];
++              s->multicast = src[gmib_rx_multicast];
++
++              s->rx_crc_errors = src[gmib_rx_fcserr] + src[gmib_rx_frag];
++              s->rx_frame_errors = src[gmib_rx_alignerr];
++              s->rx_errors = s->rx_crc_errors + s->rx_frame_errors;
++              s->rx_dropped = src[gmib_rx_toolong] + s->rx_errors;
++
++              s->tx_fifo_errors = src[gmib_tx_underrun];
++              s->tx_aborted_errors = src[gmib_tx_abortcol];
++              s->tx_errors = s->tx_fifo_errors + s->tx_aborted_errors;
++              s->collisions = src[gmib_tx_collisions];
++
++              spin_unlock(&ppe_port->gmib_stats_lock);
++      } else {
++              s->multicast = ppe_port_xgmib_get(ppe_port, xgmib_rx_multicast_g);
++
++              s->rx_packets = s->multicast;
++              s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_unicast_g);
++              s->rx_packets += ppe_port_xgmib_get(ppe_port, xgmib_rx_broadcast_g);
++
++              s->tx_packets = ppe_port_xgmib_get(ppe_port, xgmib_tx_frames);
++              s->rx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_rx_bytes);
++              s->tx_bytes = ppe_port_xgmib_get(ppe_port, xgmib_tx_bytes);
++
++              s->rx_crc_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_crc_err);
++              s->rx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_fifo_overflow);
++
++              s->rx_length_errors = ppe_port_xgmib_get(ppe_port, xgmib_rx_len_err);
++              s->rx_errors = s->rx_crc_errors +
++                      s->rx_fifo_errors + s->rx_length_errors;
++              s->rx_dropped = s->rx_errors;
++
++              s->tx_fifo_errors = ppe_port_xgmib_get(ppe_port, xgmib_tx_underflow_err);
++              s->tx_errors = s->tx_packets -
++                      ppe_port_xgmib_get(ppe_port, xgmib_tx_frames_g);
++      }
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+@@ -261,6 +693,9 @@ static void ppe_port_mac_link_up(struct
+       int ret, port = ppe_port->port_id;
+       u32 reg, val;
++      /* Start GMIB statistics polling */
++      schedule_delayed_work(&ppe_port->gmib_read, 0);
++
+       if (mac_type == PPE_MAC_TYPE_GMAC)
+               ret = ppe_port_gmac_link_up(ppe_port,
+                                           speed, duplex, tx_pause, rx_pause);
+@@ -306,6 +741,9 @@ static void ppe_port_mac_link_down(struc
+       int ret, port = ppe_port->port_id;
+       u32 reg;
++      /* Stop GMIB statistics polling */
++      cancel_delayed_work_sync(&ppe_port->gmib_read);
++
+       /* Disable PPE port TX */
+       reg = PPE_PORT_BRIDGE_CTRL_ADDR + PPE_PORT_BRIDGE_CTRL_INC * port;
+       ret = regmap_update_bits(ppe_dev->regmap, reg,
+@@ -627,6 +1065,27 @@ static int ppe_port_mac_hw_init(struct p
+       return ret;
+ }
++/* PPE port MAC MIB work task initialization */
++static int ppe_port_mac_mib_work_init(struct ppe_port *ppe_port)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      u64 *gstats;
++
++      gstats = devm_kzalloc(ppe_dev->dev,
++                            sizeof(*gstats) * ARRAY_SIZE(gmib_info),
++                            GFP_KERNEL);
++      if (!gstats)
++              return -ENOMEM;
++
++      ppe_port->gmib_stats = gstats;
++
++      spin_lock_init(&ppe_port->gmib_stats_lock);
++      INIT_DELAYED_WORK(&ppe_port->gmib_read,
++                        ppe_port_gmib_stats_poll);
++
++      return 0;
++}
++
+ /**
+  * ppe_port_mac_init() - Initialization of PPE ports for the PPE device
+  * @ppe_dev: PPE device
+@@ -693,6 +1152,12 @@ int ppe_port_mac_init(struct ppe_device
+                       goto err_port_node;
+               }
++              ret = ppe_port_mac_mib_work_init(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "Failed to initialize MAC MIB work\n");
++                      goto err_port_node;
++              }
++
+               i++;
+       }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -8,6 +8,8 @@
+ #include <linux/phylink.h>
++struct rtnl_link_stats64;
++
+ /**
+  * enum ppe_port_clk_rst_type - PPE port clock and reset ID type
+  * @PPE_PORT_CLK_RST_MAC: The clock and reset ID for port MAC
+@@ -44,6 +46,9 @@ enum ppe_mac_type {
+  * @port_id: Port ID
+  * @clks: Port clocks
+  * @rstcs: Port resets
++ * @gmib_read: Delay work task for GMAC MIB statistics polling function
++ * @gmib_stats: GMAC MIB statistics array
++ * @gmib_stats_lock: Lock to protect GMAC MIB statistics
+  */
+ struct ppe_port {
+       struct phylink *phylink;
+@@ -56,6 +61,9 @@ struct ppe_port {
+       int port_id;
+       struct clk *clks[PPE_PORT_CLK_RST_MAX];
+       struct reset_control *rstcs[PPE_PORT_CLK_RST_MAX];
++      struct delayed_work gmib_read;
++      u64 *gmib_stats;
++      spinlock_t gmib_stats_lock; /* Protects GMIB stats */
+ };
+ /**
+@@ -73,4 +81,9 @@ void ppe_port_mac_deinit(struct ppe_devi
+ int ppe_port_phylink_setup(struct ppe_port *ppe_port,
+                          struct net_device *netdev);
+ void ppe_port_phylink_destroy(struct ppe_port *ppe_port);
++int ppe_port_get_sset_count(struct ppe_port *ppe_port, int sset);
++void ppe_port_get_strings(struct ppe_port *ppe_port, u32 stringset, u8 *data);
++void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data);
++void ppe_port_get_stats64(struct ppe_port *ppe_port,
++                        struct rtnl_link_stats64 *s);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -618,6 +618,48 @@
+ #define GMAC_MIB_CTRL_MASK                    \
+       (GMAC_MIB_RD_CLR | GMAC_MIB_RST | GMAC_MIB_EN)
++/* GMAC MIB counter registers */
++#define GMAC_RXBROAD_ADDR                     0x40
++#define GMAC_RXPAUSE_ADDR                     0x44
++#define GMAC_RXMULTI_ADDR                     0x48
++#define GMAC_RXFCSERR_ADDR                    0x4C
++#define GMAC_RXALIGNERR_ADDR                  0x50
++#define GMAC_RXRUNT_ADDR                      0x54
++#define GMAC_RXFRAG_ADDR                      0x58
++#define GMAC_RXJUMBOFCSERR_ADDR                       0x5C
++#define GMAC_RXJUMBOALIGNERR_ADDR             0x60
++#define GMAC_RXPKT64_ADDR                     0x64
++#define GMAC_RXPKT65TO127_ADDR                        0x68
++#define GMAC_RXPKT128TO255_ADDR                       0x6C
++#define GMAC_RXPKT256TO511_ADDR                       0x70
++#define GMAC_RXPKT512TO1023_ADDR              0x74
++#define GMAC_RXPKT1024TO1518_ADDR             0x78
++#define GMAC_RXPKT1519TOX_ADDR                        0x7C
++#define GMAC_RXTOOLONG_ADDR                   0x80
++#define GMAC_RXBYTE_G_ADDR                    0x84
++#define GMAC_RXBYTE_B_ADDR                    0x8C
++#define GMAC_RXUNI_ADDR                               0x94
++#define GMAC_TXBROAD_ADDR                     0xA0
++#define GMAC_TXPAUSE_ADDR                     0xA4
++#define GMAC_TXMULTI_ADDR                     0xA8
++#define GMAC_TXUNDERRUN_ADDR                  0xAC
++#define GMAC_TXPKT64_ADDR                     0xB0
++#define GMAC_TXPKT65TO127_ADDR                        0xB4
++#define GMAC_TXPKT128TO255_ADDR                       0xB8
++#define GMAC_TXPKT256TO511_ADDR                       0xBC
++#define GMAC_TXPKT512TO1023_ADDR              0xC0
++#define GMAC_TXPKT1024TO1518_ADDR             0xC4
++#define GMAC_TXPKT1519TOX_ADDR                        0xC8
++#define GMAC_TXBYTE_ADDR                      0xCC
++#define GMAC_TXCOLLISIONS_ADDR                        0xD4
++#define GMAC_TXABORTCOL_ADDR                  0xD8
++#define GMAC_TXMULTICOL_ADDR                  0xDC
++#define GMAC_TXSINGLECOL_ADDR                 0xE0
++#define GMAC_TXEXCESSIVEDEFER_ADDR            0xE4
++#define GMAC_TXDEFER_ADDR                     0xE8
++#define GMAC_TXLATECOL_ADDR                   0xEC
++#define GMAC_TXUNI_ADDR                               0xF0
++
+ /* XGMAC TX configuration register */
+ #define XGMAC_TX_CONFIG_ADDR                  0x0
+ #define XGMAC_SPEED_M                         GENMASK(31, 29)
+@@ -680,4 +722,53 @@
+ #define XGMAC_MCF                             BIT(3)
+ #define XGMAC_CNTRST                          BIT(0)
++/* XGMAC MIB counter registers */
++#define XGMAC_TXBYTE_GB_ADDR                  0x814
++#define XGMAC_TXPKT_GB_ADDR                   0x81C
++#define XGMAC_TXBROAD_G_ADDR                  0x824
++#define XGMAC_TXMULTI_G_ADDR                  0x82C
++#define XGMAC_TXPKT64_GB_ADDR                 0x834
++#define XGMAC_TXPKT65TO127_GB_ADDR            0x83C
++#define XGMAC_TXPKT128TO255_GB_ADDR           0x844
++#define XGMAC_TXPKT256TO511_GB_ADDR           0x84C
++#define XGMAC_TXPKT512TO1023_GB_ADDR          0x854
++#define XGMAC_TXPKT1024TOMAX_GB_ADDR          0x85C
++#define XGMAC_TXUNI_GB_ADDR                   0x864
++#define XGMAC_TXMULTI_GB_ADDR                 0x86C
++#define XGMAC_TXBROAD_GB_ADDR                 0x874
++#define XGMAC_TXUNDERFLOW_ERR_ADDR            0x87C
++#define XGMAC_TXBYTE_G_ADDR                   0x884
++#define XGMAC_TXPKT_G_ADDR                    0x88C
++#define XGMAC_TXPAUSE_ADDR                    0x894
++#define XGMAC_TXVLAN_G_ADDR                   0x89C
++#define XGMAC_TXLPI_USEC_ADDR                 0x8A4
++#define XGMAC_TXLPI_TRAN_ADDR                 0x8A8
++#define XGMAC_RXPKT_GB_ADDR                   0x900
++#define XGMAC_RXBYTE_GB_ADDR                  0x908
++#define XGMAC_RXBYTE_G_ADDR                   0x910
++#define XGMAC_RXBROAD_G_ADDR                  0x918
++#define XGMAC_RXMULTI_G_ADDR                  0x920
++#define XGMAC_RXCRC_ERR_ADDR                  0x928
++#define XGMAC_RXRUNT_ERR_ADDR                 0x930
++#define XGMAC_RXJABBER_ERR_ADDR                       0x934
++#define XGMAC_RXUNDERSIZE_G_ADDR              0x938
++#define XGMAC_RXOVERSIZE_G_ADDR                       0x93C
++#define XGMAC_RXPKT64_GB_ADDR                 0x940
++#define XGMAC_RXPKT65TO127_GB_ADDR            0x948
++#define XGMAC_RXPKT128TO255_GB_ADDR           0x950
++#define XGMAC_RXPKT256TO511_GB_ADDR           0x958
++#define XGMAC_RXPKT512TO1023_GB_ADDR          0x960
++#define XGMAC_RXPKT1024TOMAX_GB_ADDR          0x968
++#define XGMAC_RXUNI_G_ADDR                    0x970
++#define XGMAC_RXLEN_ERR_ADDR                  0x978
++#define XGMAC_RXOUTOFRANGE_ADDR                       0x980
++#define XGMAC_RXPAUSE_ADDR                    0x988
++#define XGMAC_RXFIFOOVERFLOW_ADDR             0x990
++#define XGMAC_RXVLAN_GB_ADDR                  0x998
++#define XGMAC_RXWATCHDOG_ERR_ADDR             0x9A0
++#define XGMAC_RXLPI_USEC_ADDR                 0x9A4
++#define XGMAC_RXLPI_TRAN_ADDR                 0x9A8
++#define XGMAC_RXDISCARD_GB_ADDR                       0x9AC
++#define XGMAC_RXDISCARDBYTE_GB_ADDR           0x9B4
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.12/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch b/target/linux/qualcommbe/patches-6.12/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch
new file mode 100644 (file)
index 0000000..856a1ed
--- /dev/null
@@ -0,0 +1,172 @@
+From 55fbbc8ef90df27a16bca1613a793a578b79a384 Mon Sep 17 00:00:00 2001
+From: Lei Wei <quic_leiwei@quicinc.com>
+Date: Fri, 1 Mar 2024 13:36:26 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add PPE port MAC address and EEE
+ functions
+
+Add PPE port MAC address set and EEE set API functions which
+will be used by netdev ops and ethtool.
+
+Change-Id: Id2b3b06ae940b3b6f5227d927316329cdf3caeaa
+Signed-off-by: Lei Wei <quic_leiwei@quicinc.com>
+Alex G: use struct ethtool_keee instead of ethtool_eee
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 75 ++++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  3 +
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 29 ++++++++
+ 3 files changed, 107 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -462,6 +462,81 @@ void ppe_port_get_stats64(struct ppe_por
+       }
+ }
++/**
++ * ppe_port_set_mac_address() - Set PPE port MAC address
++ * @ppe_port: PPE port
++ * @addr: MAC address
++ *
++ * Description: Set MAC address for the given PPE port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      u32 reg, val;
++      int ret;
++
++      if (ppe_port->mac_type == PPE_MAC_TYPE_GMAC) {
++              reg = PPE_PORT_GMAC_ADDR(port);
++              val = (addr[5] << 8) | addr[4];
++              ret = regmap_write(ppe_dev->regmap, reg + GMAC_GOL_ADDR0_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = (addr[0] << 24) | (addr[1] << 16) |
++                    (addr[2] << 8) | addr[3];
++              ret = regmap_write(ppe_dev->regmap, reg + GMAC_GOL_ADDR1_ADDR, val);
++              if (ret)
++                      return ret;
++      } else {
++              reg = PPE_PORT_XGMAC_ADDR(port);
++              val = (addr[5] << 8) | addr[4] | XGMAC_ADDR_EN;
++              ret = regmap_write(ppe_dev->regmap, reg + XGMAC_ADDR0_H_ADDR, val);
++              if (ret)
++                      return ret;
++
++              val = (addr[3] << 24) | (addr[2] << 16) |
++                    (addr[1] << 8) | addr[0];
++              ret = regmap_write(ppe_dev->regmap, reg + XGMAC_ADDR0_L_ADDR, val);
++              if (ret)
++                      return ret;
++      }
++
++      return 0;
++}
++
++/**
++ * ppe_port_set_mac_eee() - Set EEE configuration for PPE port MAC
++ * @ppe_port: PPE port
++ * @eee: EEE settings
++ *
++ * Description: Set port MAC EEE settings for the given PPE port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      int port = ppe_port->port_id;
++      u32 val;
++      int ret;
++
++      ret = regmap_read(ppe_dev->regmap, PPE_LPI_EN_ADDR, &val);
++      if (ret)
++              return ret;
++
++      if (eee->tx_lpi_enabled)
++              val |= PPE_LPI_PORT_EN(port);
++      else
++              val &= ~PPE_LPI_PORT_EN(port);
++
++      ret = regmap_write(ppe_dev->regmap, PPE_LPI_EN_ADDR, val);
++
++      return ret;
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -8,6 +8,7 @@
+ #include <linux/phylink.h>
++struct ethtool_keee;
+ struct rtnl_link_stats64;
+ /**
+@@ -86,4 +87,6 @@ void ppe_port_get_strings(struct ppe_por
+ void ppe_port_get_ethtool_stats(struct ppe_port *ppe_port, u64 *data);
+ void ppe_port_get_stats64(struct ppe_port *ppe_port,
+                         struct rtnl_link_stats64 *s);
++int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr);
++int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee);
+ #endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -20,6 +20,16 @@
+ #define PPE_PORT5_SEL_PCS1                    BIT(4)
+ #define PPE_PORT_SEL_XGMAC(x)                 (BIT(8) << ((x) - 1))
++/* PPE port LPI enable register */
++#define PPE_LPI_EN_ADDR                               0x400
++#define PPE_LPI_PORT1_EN                      BIT(0)
++#define PPE_LPI_PORT2_EN                      BIT(1)
++#define PPE_LPI_PORT3_EN                      BIT(2)
++#define PPE_LPI_PORT4_EN                      BIT(3)
++#define PPE_LPI_PORT5_EN                      BIT(4)
++#define PPE_LPI_PORT6_EN                      BIT(5)
++#define PPE_LPI_PORT_EN(x)                    (BIT(0) << ((x) - 1))
++
+ /* PPE scheduler configurations for buffer manager block. */
+ #define PPE_BM_SCH_CTRL_ADDR                  0xb000
+ #define PPE_BM_SCH_CTRL_INC                   4
+@@ -592,6 +602,17 @@
+ #define GMAC_SPEED_100                                1
+ #define GMAC_SPEED_1000                               2
++/* GMAC MAC address register */
++#define GMAC_GOL_ADDR0_ADDR                   0x8
++#define GMAC_ADDR_BYTE5                               GENMASK(15, 8)
++#define GMAC_ADDR_BYTE4                               GENMASK(7, 0)
++
++#define GMAC_GOL_ADDR1_ADDR                   0xC
++#define GMAC_ADDR_BYTE0                               GENMASK(31, 24)
++#define GMAC_ADDR_BYTE1                               GENMASK(23, 16)
++#define GMAC_ADDR_BYTE2                               GENMASK(15, 8)
++#define GMAC_ADDR_BYTE3                               GENMASK(7, 0)
++
+ /* GMAC control register */
+ #define GMAC_CTRL_ADDR                                0x18
+ #define GMAC_TX_THD_M                         GENMASK(27, 24)
+@@ -717,6 +738,14 @@
+ #define XGMAC_RX_FLOW_CTRL_ADDR                       0x90
+ #define XGMAC_RXFCEN                          BIT(0)
++/* XGMAC MAC address register */
++#define XGMAC_ADDR0_H_ADDR                    0x300
++#define XGMAC_ADDR_EN                         BIT(31)
++#define XGMAC_ADDRH                           GENMASK(15, 0)
++
++#define XGMAC_ADDR0_L_ADDR                    0x304
++#define XGMAC_ADDRL                           GENMASK(31, 0)
++
+ /* XGMAC management counters control register */
+ #define XGMAC_MMC_CTRL_ADDR                   0x800
+ #define XGMAC_MCF                             BIT(3)
diff --git a/target/linux/qualcommbe/patches-6.12/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch b/target/linux/qualcommbe/patches-6.12/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch
new file mode 100644 (file)
index 0000000..ae81ddb
--- /dev/null
@@ -0,0 +1,78 @@
+From 3981aeae5dd43dea94a0ec10f0b2977ebd102560 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Tue, 5 Mar 2024 16:42:56 +0800
+Subject: [PATCH] net: ethernet: qualcomm: Add API to configure PPE port max
+ frame size
+
+This function is called when the MTU of an ethernet port is
+configured. It limits the size of packet passed through the
+ethernet port.
+
+Change-Id: I2a4dcd04407156d73770d2becbb7cbc0d56b3754
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c | 44 ++++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.h |  1 +
+ 2 files changed, 45 insertions(+)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -537,6 +537,50 @@ int ppe_port_set_mac_eee(struct ppe_port
+       return ret;
+ }
++/**
++ * ppe_port_set_maxframe() - Set port maximum frame size
++ * @ppe_port: PPE port structure
++ * @maxframe_size: Maximum frame size supported by PPE port
++ *
++ * Description: Set MTU of network interface specified by @ppe_port.
++ *
++ * Return: 0 upon success or a negative error upon failure.
++ */
++int ppe_port_set_maxframe(struct ppe_port *ppe_port, int maxframe_size)
++{
++      struct ppe_device *ppe_dev = ppe_port->ppe_dev;
++      u32 reg, val, mru_mtu_val[3];
++      int port = ppe_port->port_id;
++      int ret;
++
++      /* The max frame size should be MTU added by ETH_HLEN in PPE. */
++      maxframe_size += ETH_HLEN;
++
++      /* MAC takes cover the FCS for the calculation of frame size. */
++      if (maxframe_size > PPE_PORT_MAC_MAX_FRAME_SIZE - ETH_FCS_LEN)
++              return -EINVAL;
++
++      reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * port;
++      val = FIELD_PREP(PPE_MC_MTU_CTRL_TBL_MTU, maxframe_size);
++      ret = regmap_update_bits(ppe_dev->regmap, reg,
++                               PPE_MC_MTU_CTRL_TBL_MTU,
++                               val);
++      if (ret)
++              return ret;
++
++      reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * port;
++      ret = regmap_bulk_read(ppe_dev->regmap, reg,
++                             mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++      if (ret)
++              return ret;
++
++      PPE_MRU_MTU_CTRL_SET_MRU(mru_mtu_val, maxframe_size);
++      PPE_MRU_MTU_CTRL_SET_MTU(mru_mtu_val, maxframe_size);
++
++      return regmap_bulk_write(ppe_dev->regmap, reg,
++                               mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
++}
++
+ /* PPE port and MAC reset */
+ static int ppe_port_mac_reset(struct ppe_port *ppe_port)
+ {
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.h
+@@ -89,4 +89,5 @@ void ppe_port_get_stats64(struct ppe_por
+                         struct rtnl_link_stats64 *s);
+ int ppe_port_set_mac_address(struct ppe_port *ppe_port, const u8 *addr);
+ int ppe_port_set_mac_eee(struct ppe_port *ppe_port, struct ethtool_keee *eee);
++int ppe_port_set_maxframe(struct ppe_port *ppe_port, int maxframe_size);
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.12/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch b/target/linux/qualcommbe/patches-6.12/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch
new file mode 100644 (file)
index 0000000..0160efd
--- /dev/null
@@ -0,0 +1,932 @@
+From 00d4f3cb4f5d1e6924151a4551f06b6a82bf0146 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Wed, 28 Feb 2024 11:25:15 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add EDMA support for QCOM IPQ9574
+ chipset.
+
+Add the infrastructure functions such as Makefile,
+EDMA hardware configuration, clock and IRQ initializations.
+
+Change-Id: I64f65e554e70e9095b0cf3636fec421569ae6895
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Co-developed-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Signed-off-by: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Alex G: use "ppe_config.h" header instead of "ppe_api.h"
+        add missing definitions and functions from ppe_api:
+        - enum ppe_queue_class_type {}
+        - ppe_edma_queue_offset_config()
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile   |   3 +
+ drivers/net/ethernet/qualcomm/ppe/edma.c     | 480 +++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h     | 113 +++++
+ drivers/net/ethernet/qualcomm/ppe/ppe.c      |  10 +-
+ drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 253 ++++++++++
+ 5 files changed, 858 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma.h
+
+--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
++++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
+@@ -5,3 +5,6 @@
+ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
+ qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
++
++#EDMA
++qcom-ppe-objs += edma.o
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -0,0 +1,480 @@
++// SPDX-License-Identifier: GPL-2.0-only
++ /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++  */
++
++ /* Qualcomm Ethernet DMA driver setup, HW configuration, clocks and
++  * interrupt initializations.
++  */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/netdevice.h>
++#include <linux/of_irq.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include "edma.h"
++#include "ppe_regs.h"
++
++#define EDMA_IRQ_NAME_SIZE            32
++
++/* Global EDMA context. */
++struct edma_context *edma_ctx;
++
++/* Priority to multi-queue mapping. */
++static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
++      0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7};
++
++enum edma_clk_id {
++      EDMA_CLK,
++      EDMA_CFG_CLK,
++      EDMA_CLK_MAX
++};
++
++static const char * const clock_name[EDMA_CLK_MAX] = {
++      [EDMA_CLK] = "edma",
++      [EDMA_CFG_CLK] = "edma-cfg",
++};
++
++/* Rx Fill ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_rxfill_ring_info = {
++      .max_rings = 8,
++      .ring_start = 4,
++      .num_rings = 4,
++};
++
++/* Rx ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_rx_ring_info = {
++      .max_rings = 24,
++      .ring_start = 20,
++      .num_rings = 4,
++};
++
++/* Tx ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_tx_ring_info = {
++      .max_rings = 32,
++      .ring_start = 8,
++      .num_rings = 24,
++};
++
++/* Tx complete ring info for IPQ9574. */
++static struct edma_ring_info ipq9574_txcmpl_ring_info = {
++      .max_rings = 32,
++      .ring_start = 8,
++      .num_rings = 24,
++};
++
++/* HW info for IPQ9574. */
++static struct edma_hw_info ipq9574_hw_info = {
++      .rxfill = &ipq9574_rxfill_ring_info,
++      .rx = &ipq9574_rx_ring_info,
++      .tx = &ipq9574_tx_ring_info,
++      .txcmpl = &ipq9574_txcmpl_ring_info,
++      .max_ports = 6,
++      .napi_budget_rx = 128,
++      .napi_budget_tx = 512,
++};
++
++static int edma_clock_set_and_enable(struct device *dev,
++                                   const char *id, unsigned long rate)
++{
++      struct device_node *edma_np;
++      struct clk *clk = NULL;
++      int ret;
++
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++
++      clk = devm_get_clk_from_child(dev, edma_np, id);
++      if (IS_ERR(clk)) {
++              dev_err(dev, "clk %s get failed\n", id);
++              of_node_put(edma_np);
++              return PTR_ERR(clk);
++      }
++
++      ret = clk_set_rate(clk, rate);
++      if (ret) {
++              dev_err(dev, "set %lu rate for %s failed\n", rate, id);
++              of_node_put(edma_np);
++              return ret;
++      }
++
++      ret = clk_prepare_enable(clk);
++      if (ret) {
++              dev_err(dev, "clk %s enable failed\n", id);
++              of_node_put(edma_np);
++              return ret;
++      }
++
++      of_node_put(edma_np);
++
++      dev_dbg(dev, "set %lu rate for %s\n", rate, id);
++
++      return 0;
++}
++
++static int edma_clock_init(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      unsigned long ppe_rate;
++      int ret;
++
++      ppe_rate = ppe_dev->clk_rate;
++
++      ret = edma_clock_set_and_enable(dev, clock_name[EDMA_CLK],
++                                      ppe_rate);
++      if (ret)
++              return ret;
++
++      ret = edma_clock_set_and_enable(dev, clock_name[EDMA_CFG_CLK],
++                                      ppe_rate);
++      if (ret)
++              return ret;
++
++      return 0;
++}
++
++/**
++ * edma_configure_ucast_prio_map_tbl - Configure unicast priority map table.
++ *
++ * Map int_priority values to priority class and initialize
++ * unicast priority map table for default profile_id.
++ */
++static int edma_configure_ucast_prio_map_tbl(void)
++{
++      u8 pri_class, int_pri;
++      int ret = 0;
++
++      /* Set the priority class value for every possible priority. */
++      for (int_pri = 0; int_pri < PPE_QUEUE_INTER_PRI_NUM; int_pri++) {
++              pri_class = edma_pri_map[int_pri];
++
++              /* Priority offset should be less than maximum supported
++               * queue priority.
++               */
++              if (pri_class > EDMA_PRI_MAX_PER_CORE - 1) {
++                      pr_err("Configured incorrect priority offset: %d\n",
++                             pri_class);
++                      return -EINVAL;
++              }
++
++              ret = ppe_edma_queue_offset_config(edma_ctx->ppe_dev,
++                                                 PPE_QUEUE_CLASS_PRIORITY, int_pri, pri_class);
++
++              if (ret) {
++                      pr_err("Failed with error: %d to set queue priority class for int_pri: %d for profile_id: %d\n",
++                             ret, int_pri, 0);
++                      return ret;
++              }
++
++              pr_debug("profile_id: %d, int_priority: %d, pri_class: %d\n",
++                       0, int_pri, pri_class);
++      }
++
++      return ret;
++}
++
++static int edma_irq_init(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_ring_info *rx = hw_info->rx;
++      char edma_irq_name[EDMA_IRQ_NAME_SIZE];
++      struct device *dev = ppe_dev->dev;
++      struct platform_device *pdev;
++      struct device_node *edma_np;
++      u32 i;
++
++      pdev = to_platform_device(dev);
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++      edma_ctx->intr_info.intr_txcmpl = kzalloc((sizeof(*edma_ctx->intr_info.intr_txcmpl) *
++                                                txcmpl->num_rings), GFP_KERNEL);
++      if (!edma_ctx->intr_info.intr_txcmpl) {
++              of_node_put(edma_np);
++              return -ENOMEM;
++      }
++
++      /* Get TXCMPL rings IRQ numbers. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              snprintf(edma_irq_name, sizeof(edma_irq_name), "edma_txcmpl_%d",
++                       txcmpl->ring_start + i);
++              edma_ctx->intr_info.intr_txcmpl[i] = of_irq_get_byname(edma_np, edma_irq_name);
++              if (edma_ctx->intr_info.intr_txcmpl[i] < 0) {
++                      dev_err(dev, "%s: txcmpl_info.intr[%u] irq get failed\n",
++                              edma_np->name, i);
++                      of_node_put(edma_np);
++                      kfree(edma_ctx->intr_info.intr_txcmpl);
++                      return edma_ctx->intr_info.intr_txcmpl[i];
++              }
++
++              dev_dbg(dev, "%s: intr_info.intr_txcmpl[%u] = %u\n",
++                      edma_np->name, i, edma_ctx->intr_info.intr_txcmpl[i]);
++      }
++
++      edma_ctx->intr_info.intr_rx = kzalloc((sizeof(*edma_ctx->intr_info.intr_rx) *
++                                            rx->num_rings), GFP_KERNEL);
++      if (!edma_ctx->intr_info.intr_rx) {
++              of_node_put(edma_np);
++              kfree(edma_ctx->intr_info.intr_txcmpl);
++              return -ENOMEM;
++      }
++
++      /* Get RXDESC rings IRQ numbers. */
++      for (i = 0; i < rx->num_rings; i++) {
++              snprintf(edma_irq_name, sizeof(edma_irq_name), "edma_rxdesc_%d",
++                       rx->ring_start + i);
++              edma_ctx->intr_info.intr_rx[i] = of_irq_get_byname(edma_np, edma_irq_name);
++              if (edma_ctx->intr_info.intr_rx[i] < 0) {
++                      dev_err(dev, "%s: rx_queue_map_info.intr[%u] irq get failed\n",
++                              edma_np->name, i);
++                      of_node_put(edma_np);
++                      kfree(edma_ctx->intr_info.intr_rx);
++                      kfree(edma_ctx->intr_info.intr_txcmpl);
++                      return edma_ctx->intr_info.intr_rx[i];
++              }
++
++              dev_dbg(dev, "%s: intr_info.intr_rx[%u] = %u\n",
++                      edma_np->name, i, edma_ctx->intr_info.intr_rx[i]);
++      }
++
++      /* Get misc IRQ number. */
++      edma_ctx->intr_info.intr_misc = of_irq_get_byname(edma_np, "edma_misc");
++      if (edma_ctx->intr_info.intr_misc < 0) {
++              dev_err(dev, "%s: misc_intr irq get failed\n", edma_np->name);
++              of_node_put(edma_np);
++              kfree(edma_ctx->intr_info.intr_rx);
++              kfree(edma_ctx->intr_info.intr_txcmpl);
++              return edma_ctx->intr_info.intr_misc;
++      }
++
++      of_node_put(edma_np);
++
++      dev_dbg(dev, "%s: misc IRQ:%u\n", edma_np->name,
++              edma_ctx->intr_info.intr_misc);
++
++      return 0;
++}
++
++static int edma_hw_reset(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      struct reset_control *edma_hw_rst;
++      struct device_node *edma_np;
++      const char *reset_string;
++      u32 count, i;
++      int ret;
++
++      /* Count and parse reset names from DTSI. */
++      edma_np = of_get_child_by_name(dev->of_node, "edma");
++      count = of_property_count_strings(edma_np, "reset-names");
++      if (count < 0) {
++              dev_err(dev, "EDMA reset entry not found\n");
++              of_node_put(edma_np);
++              return -EINVAL;
++      }
++
++      for (i = 0; i < count; i++) {
++              ret = of_property_read_string_index(edma_np, "reset-names",
++                                                  i, &reset_string);
++              if (ret) {
++                      dev_err(dev, "Error reading reset-names");
++                      of_node_put(edma_np);
++                      return -EINVAL;
++              }
++
++              edma_hw_rst = of_reset_control_get_exclusive(edma_np, reset_string);
++              if (IS_ERR(edma_hw_rst)) {
++                      of_node_put(edma_np);
++                      return PTR_ERR(edma_hw_rst);
++              }
++
++              /* 100ms delay is required by hardware to reset EDMA. */
++              reset_control_assert(edma_hw_rst);
++              fsleep(100);
++
++              reset_control_deassert(edma_hw_rst);
++              fsleep(100);
++
++              reset_control_put(edma_hw_rst);
++              dev_dbg(dev, "EDMA HW reset, i:%d reset_string:%s\n", i, reset_string);
++      }
++
++      of_node_put(edma_np);
++
++      return 0;
++}
++
++static int edma_hw_configure(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;
++      u32 data, reg;
++      int ret;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MAS_CTRL_ADDR;
++      ret = regmap_read(regmap, reg, &data);
++      if (ret)
++              return ret;
++
++      pr_debug("EDMA ver %d hw init\n", data);
++
++      /* Setup private data structure. */
++      edma_ctx->intr_info.intr_mask_rx = EDMA_RXDESC_INT_MASK_PKT_INT;
++      edma_ctx->intr_info.intr_mask_txcmpl = EDMA_TX_INT_MASK_PKT_INT;
++
++      /* Reset EDMA. */
++      ret = edma_hw_reset();
++      if (ret) {
++              pr_err("Error in resetting the hardware. ret: %d\n", ret);
++              return ret;
++      }
++
++      /* Allocate memory for netdevices. */
++      edma_ctx->netdev_arr = kzalloc((sizeof(**edma_ctx->netdev_arr) *
++                                       hw_info->max_ports),
++                                       GFP_KERNEL);
++      if (!edma_ctx->netdev_arr)
++              return -ENOMEM;
++
++      /* Configure DMA request priority, DMA read burst length,
++       * and AXI write size.
++       */
++      data = FIELD_PREP(EDMA_DMAR_BURST_LEN_MASK, EDMA_BURST_LEN_ENABLE);
++      data |= FIELD_PREP(EDMA_DMAR_REQ_PRI_MASK, 0);
++      data |= FIELD_PREP(EDMA_DMAR_TXDATA_OUTSTANDING_NUM_MASK, 31);
++      data |= FIELD_PREP(EDMA_DMAR_TXDESC_OUTSTANDING_NUM_MASK, 7);
++      data |= FIELD_PREP(EDMA_DMAR_RXFILL_OUTSTANDING_NUM_MASK, 7);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_DMAR_CTRL_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Configure Tx Timeout Threshold. */
++      data = EDMA_TX_TIMEOUT_THRESH_VAL;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_TIMEOUT_THRESH_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Set Miscellaneous error mask. */
++      data = EDMA_MISC_AXI_RD_ERR_MASK |
++              EDMA_MISC_AXI_WR_ERR_MASK |
++              EDMA_MISC_RX_DESC_FIFO_FULL_MASK |
++              EDMA_MISC_RX_ERR_BUF_SIZE_MASK |
++              EDMA_MISC_TX_SRAM_FULL_MASK |
++              EDMA_MISC_TX_CMPL_BUF_FULL_MASK |
++              EDMA_MISC_DATA_LEN_ERR_MASK;
++      data |= EDMA_MISC_TX_TIMEOUT_MASK;
++      edma_ctx->intr_info.intr_mask_misc = data;
++
++      /* Global EDMA enable and padding enable. */
++      data = EDMA_PORT_PAD_EN | EDMA_PORT_EDMA_EN;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_PORT_CTRL_ADDR;
++      ret = regmap_write(regmap, reg, data);
++      if (ret)
++              return ret;
++
++      /* Initialize unicast priority map table. */
++      ret = (int)edma_configure_ucast_prio_map_tbl();
++      if (ret) {
++              pr_err("Failed to initialize unicast priority map table: %d\n",
++                     ret);
++              kfree(edma_ctx->netdev_arr);
++              return ret;
++      }
++
++      return 0;
++}
++
++/**
++ * edma_destroy - EDMA Destroy.
++ * @ppe_dev: PPE device
++ *
++ * Free the memory allocated during setup.
++ */
++void edma_destroy(struct ppe_device *ppe_dev)
++{
++      kfree(edma_ctx->intr_info.intr_rx);
++      kfree(edma_ctx->intr_info.intr_txcmpl);
++      kfree(edma_ctx->netdev_arr);
++}
++
++/**
++ * edma_setup - EDMA Setup.
++ * @ppe_dev: PPE device
++ *
++ * Configure Ethernet global ctx, clocks, hardware and interrupts.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int edma_setup(struct ppe_device *ppe_dev)
++{
++      struct device *dev = ppe_dev->dev;
++      int ret;
++
++      edma_ctx = devm_kzalloc(dev, sizeof(*edma_ctx), GFP_KERNEL);
++      if (!edma_ctx)
++              return -ENOMEM;
++
++      edma_ctx->hw_info = &ipq9574_hw_info;
++      edma_ctx->ppe_dev = ppe_dev;
++
++      /* Configure the EDMA common clocks. */
++      ret = edma_clock_init();
++      if (ret) {
++              dev_err(dev, "Error in configuring the EDMA clocks\n");
++              return ret;
++      }
++
++      dev_dbg(dev, "QCOM EDMA common clocks are configured\n");
++
++      ret = edma_hw_configure();
++      if (ret) {
++              dev_err(dev, "Error in edma configuration\n");
++              return ret;
++      }
++
++      ret = edma_irq_init();
++      if (ret) {
++              dev_err(dev, "Error in irq initialization\n");
++              return ret;
++      }
++
++      dev_info(dev, "EDMA configuration successful\n");
++
++      return 0;
++}
++
++/**
++ * ppe_edma_queue_offset_config - Configure queue offset for EDMA interface
++ * @ppe_dev: PPE device
++ * @class: The class to configure queue offset
++ * @index: Class index, internal priority or hash value
++ * @queue_offset: Queue offset value
++ *
++ * PPE EDMA queue offset is configured based on the PPE internal priority or
++ * RSS hash value, the profile ID is fixed to 0 for EDMA interface.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
++                               enum ppe_queue_class_type class,
++                               int index, int queue_offset)
++{
++      if (class == PPE_QUEUE_CLASS_PRIORITY)
++              return ppe_queue_ucast_offset_pri_set(ppe_dev, 0,
++                                                    index, queue_offset);
++
++      return ppe_queue_ucast_offset_hash_set(ppe_dev, 0,
++                                             index, queue_offset);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -0,0 +1,113 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_MAIN__
++#define __EDMA_MAIN__
++
++#include "ppe_config.h"
++
++/* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
++ *
++ * One timer unit is 128 clock cycles.
++ *
++ * So, therefore the microsecond to timer unit calculation is:
++ * Timer unit = time in microseconds / (one clock cycle in microsecond * cycles in 1 timer unit)
++ *            = ('x' microsecond * EDMA clock frequency in MHz ('y') / 128).
++ *
++ */
++#define EDMA_CYCLE_PER_TIMER_UNIT     128
++#define EDMA_MICROSEC_TO_TIMER_UNIT(x, y)     ((x) * (y) / EDMA_CYCLE_PER_TIMER_UNIT)
++#define MHZ                   1000000UL
++
++/* EDMA profile ID. */
++#define EDMA_CPU_PORT_PROFILE_ID  0
++
++/* Number of PPE queue priorities supported per ARM core. */
++#define EDMA_PRI_MAX_PER_CORE 8
++
++/**
++ * enum ppe_queue_class_type - PPE queue class type
++ * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
++ * @PPE_QUEUE_CLASS_HASH: Queue offset configured from RSS hash.
++ */
++enum ppe_queue_class_type {
++      PPE_QUEUE_CLASS_PRIORITY,
++      PPE_QUEUE_CLASS_HASH,
++};
++
++/**
++ * struct edma_ring_info - EDMA ring data structure.
++ * @max_rings: Maximum number of rings
++ * @ring_start: Ring start ID
++ * @num_rings: Number of rings
++ */
++struct edma_ring_info {
++      u32 max_rings;
++      u32 ring_start;
++      u32 num_rings;
++};
++
++/**
++ * struct edma_hw_info - EDMA hardware data structure.
++ * @rxfill: Rx Fill ring information
++ * @rx: Rx Desc ring information
++ * @tx: Tx Desc ring information
++ * @txcmpl: Tx complete ring information
++ * @max_ports: Maximum number of ports
++ * @napi_budget_rx: Rx NAPI budget
++ * @napi_budget_tx: Tx NAPI budget
++ */
++struct edma_hw_info {
++      struct edma_ring_info *rxfill;
++      struct edma_ring_info *rx;
++      struct edma_ring_info *tx;
++      struct edma_ring_info *txcmpl;
++      u32 max_ports;
++      u32 napi_budget_rx;
++      u32 napi_budget_tx;
++};
++
++/**
++ * struct edma_intr_info - EDMA interrupt data structure.
++ * @intr_mask_rx: RX interrupt mask
++ * @intr_rx: Rx interrupts
++ * @intr_mask_txcmpl: Tx completion interrupt mask
++ * @intr_txcmpl: Tx completion interrupts
++ * @intr_mask_misc: Miscellaneous interrupt mask
++ * @intr_misc: Miscellaneous interrupts
++ */
++struct edma_intr_info {
++      u32 intr_mask_rx;
++      u32 *intr_rx;
++      u32 intr_mask_txcmpl;
++      u32 *intr_txcmpl;
++      u32 intr_mask_misc;
++      u32 intr_misc;
++};
++
++/**
++ * struct edma_context - EDMA context.
++ * @netdev_arr: Net device for each EDMA port
++ * @ppe_dev: PPE device
++ * @hw_info: EDMA Hardware info
++ * @intr_info: EDMA Interrupt info
++ */
++struct edma_context {
++      struct net_device **netdev_arr;
++      struct ppe_device *ppe_dev;
++      struct edma_hw_info *hw_info;
++      struct edma_intr_info intr_info;
++};
++
++/* Global EDMA context. */
++extern struct edma_context *edma_ctx;
++
++void edma_destroy(struct ppe_device *ppe_dev);
++int edma_setup(struct ppe_device *ppe_dev);
++int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
++                               enum ppe_queue_class_type class,
++                               int index, int queue_offset);
++
++
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
+@@ -14,6 +14,7 @@
+ #include <linux/regmap.h>
+ #include <linux/reset.h>
++#include "edma.h"
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
+@@ -201,10 +202,16 @@ static int qcom_ppe_probe(struct platfor
+       if (ret)
+               return dev_err_probe(dev, ret, "PPE HW config failed\n");
+-      ret = ppe_port_mac_init(ppe_dev);
++      ret = edma_setup(ppe_dev);
+       if (ret)
++              return dev_err_probe(dev, ret, "EDMA setup failed\n");
++
++      ret = ppe_port_mac_init(ppe_dev);
++      if (ret) {
++              edma_destroy(ppe_dev);
+               return dev_err_probe(dev, ret,
+                                    "PPE Port MAC initialization failed\n");
++      }
+       ppe_debugfs_setup(ppe_dev);
+       platform_set_drvdata(pdev, ppe_dev);
+@@ -219,6 +226,7 @@ static void qcom_ppe_remove(struct platf
+       ppe_dev = platform_get_drvdata(pdev);
+       ppe_debugfs_teardown(ppe_dev);
+       ppe_port_mac_deinit(ppe_dev);
++      edma_destroy(ppe_dev);
+       platform_set_drvdata(pdev, NULL);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
+@@ -800,4 +800,257 @@
+ #define XGMAC_RXDISCARD_GB_ADDR                       0x9AC
+ #define XGMAC_RXDISCARDBYTE_GB_ADDR           0x9B4
++#define EDMA_BASE_OFFSET                      0xb00000
++
++/* EDMA register offsets */
++#define EDMA_REG_MAS_CTRL_ADDR                        0x0
++#define EDMA_REG_PORT_CTRL_ADDR                       0x4
++#define EDMA_REG_VLAN_CTRL_ADDR                       0x8
++#define EDMA_REG_RXDESC2FILL_MAP_0_ADDR               0x14
++#define EDMA_REG_RXDESC2FILL_MAP_1_ADDR               0x18
++#define EDMA_REG_RXDESC2FILL_MAP_2_ADDR               0x1c
++#define EDMA_REG_TXQ_CTRL_ADDR                        0x20
++#define EDMA_REG_TXQ_CTRL_2_ADDR              0x24
++#define EDMA_REG_TXQ_FC_0_ADDR                        0x28
++#define EDMA_REG_TXQ_FC_1_ADDR                        0x30
++#define EDMA_REG_TXQ_FC_2_ADDR                        0x34
++#define EDMA_REG_TXQ_FC_3_ADDR                        0x38
++#define EDMA_REG_RXQ_CTRL_ADDR                        0x3c
++#define EDMA_REG_MISC_ERR_QID_ADDR            0x40
++#define EDMA_REG_RXQ_FC_THRE_ADDR             0x44
++#define EDMA_REG_DMAR_CTRL_ADDR                       0x48
++#define EDMA_REG_AXIR_CTRL_ADDR                       0x4c
++#define EDMA_REG_AXIW_CTRL_ADDR                       0x50
++#define EDMA_REG_MIN_MSS_ADDR                 0x54
++#define EDMA_REG_LOOPBACK_CTRL_ADDR           0x58
++#define EDMA_REG_MISC_INT_STAT_ADDR           0x5c
++#define EDMA_REG_MISC_INT_MASK_ADDR           0x60
++#define EDMA_REG_DBG_CTRL_ADDR                        0x64
++#define EDMA_REG_DBG_DATA_ADDR                        0x68
++#define EDMA_REG_TX_TIMEOUT_THRESH_ADDR               0x6c
++#define EDMA_REG_REQ0_FIFO_THRESH_ADDR                0x80
++#define EDMA_REG_WB_OS_THRESH_ADDR            0x84
++#define EDMA_REG_MISC_ERR_QID_REG2_ADDR               0x88
++#define EDMA_REG_TXDESC2CMPL_MAP_0_ADDR               0x8c
++#define EDMA_REG_TXDESC2CMPL_MAP_1_ADDR               0x90
++#define EDMA_REG_TXDESC2CMPL_MAP_2_ADDR               0x94
++#define EDMA_REG_TXDESC2CMPL_MAP_3_ADDR               0x98
++#define EDMA_REG_TXDESC2CMPL_MAP_4_ADDR               0x9c
++#define EDMA_REG_TXDESC2CMPL_MAP_5_ADDR               0xa0
++
++/* Tx descriptor ring configuration register addresses */
++#define EDMA_REG_TXDESC_BA(n)         (0x1000 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_PROD_IDX(n)   (0x1004 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_CONS_IDX(n)   (0x1008 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_RING_SIZE(n)  (0x100c + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_CTRL(n)               (0x1010 + (0x1000 * (n)))
++#define EDMA_REG_TXDESC_BA2(n)                (0x1014 + (0x1000 * (n)))
++
++/* RxFill ring configuration register addresses */
++#define EDMA_REG_RXFILL_BA(n)         (0x29000 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_PROD_IDX(n)   (0x29004 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_CONS_IDX(n)   (0x29008 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_RING_SIZE(n)  (0x2900c + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_BUFFER1_SIZE(n)       (0x29010 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_FC_THRE(n)    (0x29014 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_UGT_THRE(n)   (0x29018 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_RING_EN(n)    (0x2901c + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_DISABLE(n)    (0x29020 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_DISABLE_DONE(n)       (0x29024 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_INT_STAT(n)   (0x31000 + (0x1000 * (n)))
++#define EDMA_REG_RXFILL_INT_MASK(n)   (0x31004 + (0x1000 * (n)))
++
++/* Rx descriptor ring configuration register addresses */
++#define EDMA_REG_RXDESC_BA(n)         (0x39000 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_PROD_IDX(n)   (0x39004 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_CONS_IDX(n)   (0x39008 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_RING_SIZE(n)  (0x3900c + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_FC_THRE(n)    (0x39010 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_UGT_THRE(n)   (0x39014 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_CTRL(n)               (0x39018 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_BPC(n)                (0x3901c + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_DISABLE(n)    (0x39020 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_DISABLE_DONE(n)       (0x39024 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_PREHEADER_BA(n)       (0x39028 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_INT_STAT(n)   (0x59000 + (0x1000 * (n)))
++#define EDMA_REG_RXDESC_INT_MASK(n)   (0x59004 + (0x1000 * (n)))
++
++#define EDMA_REG_RX_MOD_TIMER(n)      (0x59008 + (0x1000 * (n)))
++#define EDMA_REG_RX_INT_CTRL(n)               (0x5900c + (0x1000 * (n)))
++
++/* Tx completion ring configuration register addresses */
++#define EDMA_REG_TXCMPL_BA(n)         (0x79000 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_PROD_IDX(n)   (0x79004 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_CONS_IDX(n)   (0x79008 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_RING_SIZE(n)  (0x7900c + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_UGT_THRE(n)   (0x79010 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_CTRL(n)               (0x79014 + (0x1000 * (n)))
++#define EDMA_REG_TXCMPL_BPC(n)                (0x79018 + (0x1000 * (n)))
++
++#define EDMA_REG_TX_INT_STAT(n)               (0x99000 + (0x1000 * (n)))
++#define EDMA_REG_TX_INT_MASK(n)               (0x99004 + (0x1000 * (n)))
++#define EDMA_REG_TX_MOD_TIMER(n)      (0x99008 + (0x1000 * (n)))
++#define EDMA_REG_TX_INT_CTRL(n)               (0x9900c + (0x1000 * (n)))
++
++/* EDMA_QID2RID_TABLE_MEM register field masks */
++#define EDMA_RX_RING_ID_QUEUE0_MASK   GENMASK(7, 0)
++#define EDMA_RX_RING_ID_QUEUE1_MASK   GENMASK(15, 8)
++#define EDMA_RX_RING_ID_QUEUE2_MASK   GENMASK(23, 16)
++#define EDMA_RX_RING_ID_QUEUE3_MASK   GENMASK(31, 24)
++
++/* EDMA_REG_PORT_CTRL register bit definitions */
++#define EDMA_PORT_PAD_EN                      0x1
++#define EDMA_PORT_EDMA_EN                     0x2
++
++/* EDMA_REG_DMAR_CTRL register field masks */
++#define EDMA_DMAR_REQ_PRI_MASK                        GENMASK(2, 0)
++#define EDMA_DMAR_BURST_LEN_MASK              BIT(3)
++#define EDMA_DMAR_TXDATA_OUTSTANDING_NUM_MASK GENMASK(8, 4)
++#define EDMA_DMAR_TXDESC_OUTSTANDING_NUM_MASK GENMASK(11, 9)
++#define EDMA_DMAR_RXFILL_OUTSTANDING_NUM_MASK GENMASK(14, 12)
++
++#define EDMA_BURST_LEN_ENABLE                 0
++
++/* Tx timeout threshold */
++#define EDMA_TX_TIMEOUT_THRESH_VAL            0xFFFF
++
++/* Rx descriptor ring base address mask */
++#define EDMA_RXDESC_BA_MASK                   0xffffffff
++
++/* Rx Descriptor ring pre-header base address mask */
++#define EDMA_RXDESC_PREHEADER_BA_MASK         0xffffffff
++
++/* Tx descriptor prod ring index mask */
++#define EDMA_TXDESC_PROD_IDX_MASK             0xffff
++
++/* Tx descriptor consumer ring index mask */
++#define EDMA_TXDESC_CONS_IDX_MASK             0xffff
++
++/* Tx descriptor ring size mask */
++#define EDMA_TXDESC_RING_SIZE_MASK            0xffff
++
++/* Tx descriptor ring enable */
++#define EDMA_TXDESC_TX_ENABLE                 0x1
++
++#define EDMA_TXDESC_CTRL_TXEN_MASK            BIT(0)
++#define EDMA_TXDESC_CTRL_FC_GRP_ID_MASK               GENMASK(3, 1)
++
++/* Tx completion ring prod index mask */
++#define EDMA_TXCMPL_PROD_IDX_MASK             0xffff
++
++/* Tx completion ring urgent threshold mask */
++#define EDMA_TXCMPL_LOW_THRE_MASK             0xffff
++#define EDMA_TXCMPL_LOW_THRE_SHIFT            0
++
++/* EDMA_REG_TX_MOD_TIMER mask */
++#define EDMA_TX_MOD_TIMER_INIT_MASK           0xffff
++#define EDMA_TX_MOD_TIMER_INIT_SHIFT          0
++
++/* Rx fill ring prod index mask */
++#define EDMA_RXFILL_PROD_IDX_MASK             0xffff
++
++/* Rx fill ring consumer index mask */
++#define EDMA_RXFILL_CONS_IDX_MASK             0xffff
++
++/* Rx fill ring size mask */
++#define EDMA_RXFILL_RING_SIZE_MASK            0xffff
++
++/* Rx fill ring flow control threshold masks */
++#define EDMA_RXFILL_FC_XON_THRE_MASK          0x7ff
++#define EDMA_RXFILL_FC_XON_THRE_SHIFT         12
++#define EDMA_RXFILL_FC_XOFF_THRE_MASK         0x7ff
++#define EDMA_RXFILL_FC_XOFF_THRE_SHIFT                0
++
++/* Rx fill ring enable bit */
++#define EDMA_RXFILL_RING_EN                   0x1
++
++/* Rx desc ring prod index mask */
++#define EDMA_RXDESC_PROD_IDX_MASK             0xffff
++
++/* Rx descriptor ring cons index mask */
++#define EDMA_RXDESC_CONS_IDX_MASK             0xffff
++
++/* Rx descriptor ring size masks */
++#define EDMA_RXDESC_RING_SIZE_MASK            0xffff
++#define EDMA_RXDESC_PL_OFFSET_MASK            0x1ff
++#define EDMA_RXDESC_PL_OFFSET_SHIFT           16
++#define EDMA_RXDESC_PL_DEFAULT_VALUE          0
++
++/* Rx descriptor ring flow control threshold masks */
++#define EDMA_RXDESC_FC_XON_THRE_MASK          0x7ff
++#define EDMA_RXDESC_FC_XON_THRE_SHIFT         12
++#define EDMA_RXDESC_FC_XOFF_THRE_MASK         0x7ff
++#define EDMA_RXDESC_FC_XOFF_THRE_SHIFT                0
++
++/* Rx descriptor ring urgent threshold mask */
++#define EDMA_RXDESC_LOW_THRE_MASK             0xffff
++#define EDMA_RXDESC_LOW_THRE_SHIFT            0
++
++/* Rx descriptor ring enable bit */
++#define EDMA_RXDESC_RX_EN                     0x1
++
++/* Tx interrupt status bit */
++#define EDMA_TX_INT_MASK_PKT_INT              0x1
++
++/* Rx interrupt mask */
++#define EDMA_RXDESC_INT_MASK_PKT_INT          0x1
++
++#define EDMA_MASK_INT_DISABLE                 0x0
++#define EDMA_MASK_INT_CLEAR                   0x0
++
++/* EDMA_REG_RX_MOD_TIMER register field masks */
++#define EDMA_RX_MOD_TIMER_INIT_MASK           0xffff
++#define EDMA_RX_MOD_TIMER_INIT_SHIFT          0
++
++/* EDMA Ring mask */
++#define EDMA_RING_DMA_MASK                    0xffffffff
++
++/* RXDESC threshold interrupt. */
++#define EDMA_RXDESC_UGT_INT_STAT              0x2
++
++/* RXDESC timer interrupt */
++#define EDMA_RXDESC_PKT_INT_STAT              0x1
++
++/* RXDESC Interrupt status mask */
++#define EDMA_RXDESC_RING_INT_STATUS_MASK \
++      (EDMA_RXDESC_UGT_INT_STAT | EDMA_RXDESC_PKT_INT_STAT)
++
++/* TXCMPL threshold interrupt. */
++#define EDMA_TXCMPL_UGT_INT_STAT              0x2
++
++/* TXCMPL timer interrupt */
++#define EDMA_TXCMPL_PKT_INT_STAT              0x1
++
++/* TXCMPL Interrupt status mask */
++#define EDMA_TXCMPL_RING_INT_STATUS_MASK \
++      (EDMA_TXCMPL_UGT_INT_STAT | EDMA_TXCMPL_PKT_INT_STAT)
++
++#define EDMA_TXCMPL_RETMODE_OPAQUE            0x0
++
++#define EDMA_RXDESC_LOW_THRE                  0
++#define EDMA_RX_MOD_TIMER_INIT                        1000
++#define EDMA_RX_NE_INT_EN                     0x2
++
++#define EDMA_TX_MOD_TIMER                     150
++
++#define EDMA_TX_INITIAL_PROD_IDX              0x0
++#define EDMA_TX_NE_INT_EN                     0x2
++
++/* EDMA misc error mask */
++#define EDMA_MISC_AXI_RD_ERR_MASK             BIT(0)
++#define EDMA_MISC_AXI_WR_ERR_MASK             BIT(1)
++#define EDMA_MISC_RX_DESC_FIFO_FULL_MASK      BIT(2)
++#define EDMA_MISC_RX_ERR_BUF_SIZE_MASK                BIT(3)
++#define EDMA_MISC_TX_SRAM_FULL_MASK           BIT(4)
++#define EDMA_MISC_TX_CMPL_BUF_FULL_MASK               BIT(5)
++
++#define EDMA_MISC_DATA_LEN_ERR_MASK           BIT(6)
++#define EDMA_MISC_TX_TIMEOUT_MASK             BIT(7)
++
++/* EDMA txdesc2cmpl map */
++#define EDMA_TXDESC2CMPL_MAP_TXDESC_MASK              0x1F
++
++/* EDMA rxdesc2fill map */
++#define EDMA_RXDESC2FILL_MAP_RXDESC_MASK      0x7
++
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.12/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch b/target/linux/qualcommbe/patches-6.12/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch
new file mode 100644 (file)
index 0000000..55de486
--- /dev/null
@@ -0,0 +1,397 @@
+From 5dc80c468c668d855d76b323f09bbadb95cc3147 Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:14:46 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add netdevice support for QCOM
+ IPQ9574 chipset.
+
+Add EDMA ports and netdevice operations for QCOM IPQ9574 chipset.
+
+Change-Id: I08b2eff52b4ef0d6d428c1c416f5580ef010973f
+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.h      |   3 +
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 270 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.h |  31 ++
+ drivers/net/ethernet/qualcomm/ppe/ppe_port.c  |  19 ++
+ 5 files changed, 324 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_port.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_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o
++qcom-ppe-objs += edma.o edma_port.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -26,6 +26,9 @@
+ /* Number of PPE queue priorities supported per ARM core. */
+ #define EDMA_PRI_MAX_PER_CORE 8
++/* Interface ID start. */
++#define EDMA_START_IFNUM   1
++
+ /**
+  * enum ppe_queue_class_type - PPE queue class type
+  * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -0,0 +1,270 @@
++// SPDX-License-Identifier: GPL-2.0-only
++ /* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++  */
++
++/* EDMA port initialization, configuration and netdevice ops handling */
++
++#include <linux/etherdevice.h>
++#include <linux/net.h>
++#include <linux/netdevice.h>
++#include <linux/of_net.h>
++#include <linux/phylink.h>
++#include <linux/printk.h>
++
++#include "edma.h"
++#include "edma_port.h"
++#include "ppe_regs.h"
++
++/* Number of netdev queues. */
++#define EDMA_NETDEV_QUEUE_NUM 4
++
++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)
++{
++      int cpu = get_cpu();
++
++      put_cpu();
++
++      return cpu;
++}
++
++static int edma_port_open(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      /* Inform the Linux Networking stack about the hardware capability of
++       * checksum offloading and other features. Each port is
++       * responsible to maintain the feature set it supports.
++       */
++      netdev->features |= EDMA_NETDEV_FEATURES;
++      netdev->hw_features |= EDMA_NETDEV_FEATURES;
++      netdev->vlan_features |= EDMA_NETDEV_FEATURES;
++      netdev->wanted_features |= EDMA_NETDEV_FEATURES;
++
++      ppe_port  = port_priv->ppe_port;
++
++      if (ppe_port->phylink)
++              phylink_start(ppe_port->phylink);
++
++      netif_start_queue(netdev);
++
++      return 0;
++}
++
++static int edma_port_close(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netif_stop_queue(netdev);
++
++      ppe_port  = port_priv->ppe_port;
++
++      /* Phylink close. */
++      if (ppe_port->phylink)
++              phylink_stop(ppe_port->phylink);
++
++      return 0;
++}
++
++static int edma_port_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *ppe_port;
++      int ret = -EINVAL;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      ppe_port = port_priv->ppe_port;
++      if (ppe_port->phylink)
++              return phylink_mii_ioctl(ppe_port->phylink, ifr, cmd);
++
++      return ret;
++}
++
++static int edma_port_change_mtu(struct net_device *netdev, int mtu)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netdev->mtu = mtu;
++
++      return ppe_port_set_maxframe(port_priv->ppe_port, mtu);
++}
++
++static netdev_features_t edma_port_feature_check(__maybe_unused struct sk_buff *skb,
++                                               __maybe_unused struct net_device *netdev,
++                                               netdev_features_t features)
++{
++      return features;
++}
++
++static void edma_port_get_stats64(struct net_device *netdev,
++                                struct rtnl_link_stats64 *stats)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++
++      if (!port_priv)
++              return;
++
++      ppe_port_get_stats64(port_priv->ppe_port, stats);
++}
++
++static int edma_port_set_mac_address(struct net_device *netdev, void *macaddr)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct sockaddr *addr = (struct sockaddr *)macaddr;
++      int ret;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      netdev_dbg(netdev, "AddrFamily: %d, %0x:%0x:%0x:%0x:%0x:%0x\n",
++                 addr->sa_family, addr->sa_data[0], addr->sa_data[1],
++                 addr->sa_data[2], addr->sa_data[3], addr->sa_data[4],
++                 addr->sa_data[5]);
++
++      ret = eth_prepare_mac_addr_change(netdev, addr);
++      if (ret)
++              return ret;
++
++      if (ppe_port_set_mac_address(port_priv->ppe_port, (u8 *)addr)) {
++              netdev_err(netdev, "set mac address failed for dev: %s\n", netdev->name);
++              return -EINVAL;
++      }
++
++      eth_commit_mac_addr_change(netdev, addr);
++
++      return 0;
++}
++
++static const struct net_device_ops edma_port_netdev_ops = {
++      .ndo_open = edma_port_open,
++      .ndo_stop = edma_port_close,
++      .ndo_get_stats64 = edma_port_get_stats64,
++      .ndo_set_mac_address = edma_port_set_mac_address,
++      .ndo_validate_addr = eth_validate_addr,
++      .ndo_change_mtu = edma_port_change_mtu,
++      .ndo_eth_ioctl = edma_port_ioctl,
++      .ndo_features_check = edma_port_feature_check,
++      .ndo_select_queue = edma_port_select_queue,
++};
++
++/**
++ * edma_port_destroy - EDMA port destroy.
++ * @port: PPE port
++ *
++ * Unregister and free the netdevice.
++ */
++void edma_port_destroy(struct ppe_port *port)
++{
++      int port_id = port->port_id;
++      struct net_device *netdev = edma_ctx->netdev_arr[port_id - 1];
++
++      unregister_netdev(netdev);
++      free_netdev(netdev);
++      ppe_port_phylink_destroy(port);
++      edma_ctx->netdev_arr[port_id - 1] = NULL;
++}
++
++/**
++ * edma_port_setup - EDMA port Setup.
++ * @port: PPE port
++ *
++ * Initialize and register the netdevice.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int edma_port_setup(struct ppe_port *port)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device_node *np = port->np;
++      struct edma_port_priv *port_priv;
++      int port_id = port->port_id;
++      struct net_device *netdev;
++      u8 mac_addr[ETH_ALEN];
++      int ret = 0;
++      u8 *maddr;
++
++      netdev = alloc_etherdev_mqs(sizeof(struct edma_port_priv),
++                                  EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
++      if (!netdev) {
++              pr_err("alloc_etherdev() failed\n");
++              return -ENOMEM;
++      }
++
++      SET_NETDEV_DEV(netdev, ppe_dev->dev);
++      netdev->dev.of_node = np;
++
++      /* max_mtu is set to 1500 in ether_setup(). */
++      netdev->max_mtu = ETH_MAX_MTU;
++
++      port_priv = netdev_priv(netdev);
++      memset((void *)port_priv, 0, sizeof(struct edma_port_priv));
++
++      port_priv->ppe_port = port;
++      port_priv->netdev = netdev;
++      netdev->watchdog_timeo = 5 * HZ;
++      netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
++      netdev->netdev_ops = &edma_port_netdev_ops;
++      netdev->gso_max_segs = GSO_MAX_SEGS;
++
++      maddr = mac_addr;
++      if (of_get_mac_address(np, maddr))
++              maddr = NULL;
++
++      if (maddr && is_valid_ether_addr(maddr)) {
++              eth_hw_addr_set(netdev, maddr);
++      } else {
++              eth_hw_addr_random(netdev);
++              netdev_info(netdev, "GMAC%d Using random MAC address - %pM\n",
++                          port_id, netdev->dev_addr);
++      }
++
++      netdev_dbg(netdev, "Configuring the port %s(qcom-id:%d)\n",
++                 netdev->name, port_id);
++
++      /* We expect 'port_id' to correspond to ports numbers on SoC.
++       * These begin from '1' and hence we subtract
++       * one when using it as an array index.
++       */
++      edma_ctx->netdev_arr[port_id - 1] = netdev;
++
++      /* Setup phylink. */
++      ret = ppe_port_phylink_setup(port, netdev);
++      if (ret) {
++              netdev_dbg(netdev, "EDMA port phylink setup for netdevice %s\n",
++                         netdev->name);
++              goto port_phylink_setup_fail;
++      }
++
++      /* Register the network interface. */
++      ret = register_netdev(netdev);
++      if (ret) {
++              netdev_dbg(netdev, "Error registering netdevice %s\n",
++                         netdev->name);
++              goto register_netdev_fail;
++      }
++
++      netdev_dbg(netdev, "Setup EDMA port GMAC%d done\n", port_id);
++      return ret;
++
++register_netdev_fail:
++      ppe_port_phylink_destroy(port);
++port_phylink_setup_fail:
++      free_netdev(netdev);
++      edma_ctx->netdev_arr[port_id - 1] = NULL;
++
++      return ret;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+@@ -0,0 +1,31 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_PORTS__
++#define __EDMA_PORTS__
++
++#include "ppe_port.h"
++
++#define EDMA_NETDEV_FEATURES          (NETIF_F_FRAGLIST \
++                                      | NETIF_F_SG \
++                                      | NETIF_F_RXCSUM \
++                                      | NETIF_F_HW_CSUM \
++                                      | NETIF_F_TSO \
++                                      | NETIF_F_TSO6)
++
++/**
++ * struct edma_port_priv - EDMA port priv structure.
++ * @ppe_port: Pointer to PPE port
++ * @netdev: Corresponding netdevice
++ * @flags: Feature flags
++ */
++struct edma_port_priv {
++      struct ppe_port *ppe_port;
++      struct net_device *netdev;
++      unsigned long flags;
++};
++
++void edma_port_destroy(struct ppe_port *port);
++int edma_port_setup(struct ppe_port *port);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_port.c
+@@ -13,6 +13,7 @@
+ #include <linux/regmap.h>
+ #include <linux/rtnetlink.h>
++#include "edma_port.h"
+ #include "ppe.h"
+ #include "ppe_port.h"
+ #include "ppe_regs.h"
+@@ -1277,12 +1278,26 @@ int ppe_port_mac_init(struct ppe_device
+                       goto err_port_node;
+               }
++              ret = edma_port_setup(&ppe_ports->port[i]);
++              if (ret) {
++                      dev_err(ppe_dev->dev, "QCOM EDMA port setup failed\n");
++                      i--;
++                      goto err_port_setup;
++              }
++
+               i++;
+       }
+       of_node_put(ports_node);
+       return 0;
++err_port_setup:
++      /* Destroy edma ports created till now */
++      while (i >= 0) {
++              edma_port_destroy(&ppe_ports->port[i]);
++              i--;
++      }
++
+ err_port_clk:
+       for (j = 0; j < i; j++)
+               ppe_port_clock_deinit(&ppe_ports->port[j]);
+@@ -1307,6 +1322,10 @@ void ppe_port_mac_deinit(struct ppe_devi
+       for (i = 0; i < ppe_dev->ports->num; i++) {
+               ppe_port = &ppe_dev->ports->port[i];
++
++              /* Destroy all phylinks and edma ports */
++              edma_port_destroy(ppe_port);
++
+               ppe_port_clock_deinit(ppe_port);
+       }
+ }
diff --git a/target/linux/qualcommbe/patches-6.12/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch b/target/linux/qualcommbe/patches-6.12/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch
new file mode 100644 (file)
index 0000000..eb10a00
--- /dev/null
@@ -0,0 +1,2454 @@
+From b5c8c5d3888328321e8be1db50b75dff8f514e51 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] 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>
+Alex G: add missing functions that were previously in ppe_api.c:
+        - ppe_edma_queue_resource_get()
+        - ppe_edma_ring_to_queues_config()
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 214 +++-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  22 +-
+ .../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, 2224 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_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_port.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_port.o edma_rx.o
+--- 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;
+@@ -478,3 +645,46 @@ int ppe_edma_queue_offset_config(struct
+       return ppe_queue_ucast_offset_hash_set(ppe_dev, 0,
+                                              index, queue_offset);
+ }
++
++/**
++ * ppe_edma_queue_resource_get - Get EDMA queue resource
++ * @ppe_dev: PPE device
++ * @type: Resource type
++ * @res_start: Resource start ID returned
++ * @res_end: Resource end ID returned
++ *
++ * PPE EDMA queue resource includes unicast queue and multicast queue.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_queue_resource_get(struct ppe_device *ppe_dev, int type,
++                              int *res_start, int *res_end)
++{
++      if (type != PPE_RES_UCAST && type != PPE_RES_MCAST)
++              return -EINVAL;
++
++      return ppe_port_resource_get(ppe_dev, 0, type, res_start, res_end);
++};
++
++/**
++ * ppe_edma_ring_to_queues_config - Map EDMA ring to PPE queues
++ * @ppe_dev: PPE device
++ * @ring_id: EDMA ring ID
++ * @num: Number of queues mapped to EDMA ring
++ * @queues: PPE queue IDs
++ *
++ * PPE queues are configured to map with the special EDMA ring ID.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++int ppe_edma_ring_to_queues_config(struct ppe_device *ppe_dev, int ring_id,
++                                 int num, int queues[] __counted_by(num))
++{
++      u32 queue_bmap[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT] = {};
++      int index;
++
++      for (index = 0; index < num; index++)
++              queue_bmap[queues[index] / 32] |= BIT_MASK(queues[index] % 32);
++
++      return ppe_ring_queue_map_set(ppe_dev, ring_id, queue_bmap);
++}
+--- 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_config.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)); })
++
+ /**
+  * enum ppe_queue_class_type - PPE queue class type
+  * @PPE_QUEUE_CLASS_PRIORITY: Queue offset configured from internal priority
+@@ -92,18 +98,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);
+@@ -111,6 +127,10 @@ int edma_setup(struct ppe_device *ppe_de
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
++int ppe_edma_queue_resource_get(struct ppe_device *ppe_dev, int type,
++                              int *res_start, int *res_end);
++int ppe_edma_ring_to_queues_config(struct ppe_device *ppe_dev, int ring_id,
++                                 int num, int queues[] __counted_by(num));
+ #endif
+--- /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
diff --git a/target/linux/qualcommbe/patches-6.12/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch b/target/linux/qualcommbe/patches-6.12/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch
new file mode 100644 (file)
index 0000000..39d300d
--- /dev/null
@@ -0,0 +1,2363 @@
+From 339d3a5365f150a78ed405684e379fee3acdbe90 Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:26:29 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add Tx Ethernet DMA support
+
+Add Tx queues, rings, descriptors configurations and
+DMA support for the EDMA.
+
+Change-Id: Idfb0e1fe5ac494d614097d6c97dd15d63bbce8e6
+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      |  97 ++-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |   7 +
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.c   | 648 ++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.h   |  28 +
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 136 +++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.h |  35 +
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.c   | 808 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.h   | 302 +++++++
+ 9 files changed, 2055 insertions(+), 8 deletions(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_tx.c
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_tx.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_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_port.o edma_rx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_port.o edma_rx.o edma_tx.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -18,6 +18,7 @@
+ #include <linux/reset.h>
+ #include "edma.h"
++#include "edma_cfg_tx.h"
+ #include "edma_cfg_rx.h"
+ #include "ppe_regs.h"
+@@ -25,6 +26,7 @@
+ /* Global EDMA context. */
+ struct edma_context *edma_ctx;
++static char **edma_txcmpl_irq_name;
+ static char **edma_rxdesc_irq_name;
+ /* Module params. */
+@@ -192,22 +194,59 @@ static int edma_configure_ucast_prio_map
+ static int edma_irq_register(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
+       struct edma_ring_info *rx = hw_info->rx;
+       int ret;
+       u32 i;
++      /* Request IRQ for TXCMPL rings. */
++      edma_txcmpl_irq_name = kzalloc((sizeof(char *) * txcmpl->num_rings), GFP_KERNEL);
++      if (!edma_txcmpl_irq_name)
++              return -ENOMEM;
++
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              edma_txcmpl_irq_name[i] = kzalloc((sizeof(char *) * EDMA_IRQ_NAME_SIZE),
++                                                GFP_KERNEL);
++              if (!edma_txcmpl_irq_name[i]) {
++                      ret = -ENOMEM;
++                      goto txcmpl_ring_irq_name_alloc_fail;
++              }
++
++              snprintf(edma_txcmpl_irq_name[i], EDMA_IRQ_NAME_SIZE, "edma_txcmpl_%d",
++                       txcmpl->ring_start + i);
++
++              irq_set_status_flags(edma_ctx->intr_info.intr_txcmpl[i], IRQ_DISABLE_UNLAZY);
++
++              ret = request_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                                edma_tx_handle_irq, IRQF_SHARED,
++                                edma_txcmpl_irq_name[i],
++                                (void *)&edma_ctx->txcmpl_rings[i]);
++              if (ret) {
++                      pr_err("TXCMPL ring IRQ:%d request %d failed\n",
++                             edma_ctx->intr_info.intr_txcmpl[i], i);
++                      goto txcmpl_ring_intr_req_fail;
++              }
++
++              pr_debug("TXCMPL ring: %d IRQ:%d request success: %s\n",
++                       txcmpl->ring_start + i,
++                       edma_ctx->intr_info.intr_txcmpl[i],
++                       edma_txcmpl_irq_name[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;
++      if (!edma_rxdesc_irq_name) {
++              ret = -ENOMEM;
++              goto rxdesc_irq_name_alloc_fail;
++      }
+       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;
++                      goto rxdesc_ring_irq_name_alloc_fail;
+               }
+               snprintf(edma_rxdesc_irq_name[i], 20, "edma_rxdesc_%d",
+@@ -236,8 +275,19 @@ static int edma_irq_register(void)
+ 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:
++rxdesc_ring_irq_name_alloc_fail:
+       kfree(edma_rxdesc_irq_name);
++rxdesc_irq_name_alloc_fail:
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_txcmpl[i]);
++              free_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                       (void *)&edma_ctx->txcmpl_rings[i]);
++      }
++txcmpl_ring_intr_req_fail:
++      for (i = 0; i < txcmpl->num_rings; i++)
++              kfree(edma_txcmpl_irq_name[i]);
++txcmpl_ring_irq_name_alloc_fail:
++      kfree(edma_txcmpl_irq_name);
+       return ret;
+ }
+@@ -326,12 +376,22 @@ static int edma_irq_init(void)
+ static int edma_alloc_rings(void)
+ {
++      if (edma_cfg_tx_rings_alloc()) {
++              pr_err("Error in allocating Tx rings\n");
++              return -ENOMEM;
++      }
++
+       if (edma_cfg_rx_rings_alloc()) {
+               pr_err("Error in allocating Rx rings\n");
+-              return -ENOMEM;
++              goto rx_rings_alloc_fail;
+       }
+       return 0;
++
++rx_rings_alloc_fail:
++      edma_cfg_tx_rings_cleanup();
++
++      return -ENOMEM;
+ }
+ static int edma_hw_reset(void)
+@@ -389,7 +449,7 @@ static int edma_hw_configure(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;
+-      u32 data, reg;
++      u32 data, reg, i;
+       int ret;
+       reg = EDMA_BASE_OFFSET + EDMA_REG_MAS_CTRL_ADDR;
+@@ -439,11 +499,17 @@ static int edma_hw_configure(void)
+       }
+       /* Disable interrupts. */
++      for (i = 1; i <= hw_info->max_ports; i++)
++              edma_cfg_tx_disable_interrupts(i);
++
+       edma_cfg_rx_disable_interrupts();
+       edma_cfg_rx_rings_disable();
+       edma_cfg_rx_ring_mappings();
++      edma_cfg_tx_ring_mappings();
++
++      edma_cfg_tx_rings();
+       ret = edma_cfg_rx_rings();
+       if (ret) {
+@@ -520,6 +586,7 @@ configure_ucast_prio_map_tbl_failed:
+       edma_cfg_rx_napi_delete();
+       edma_cfg_rx_rings_disable();
+ edma_cfg_rx_rings_failed:
++      edma_cfg_tx_rings_cleanup();
+       edma_cfg_rx_rings_cleanup();
+ edma_alloc_rings_failed:
+       free_netdev(edma_ctx->dummy_dev);
+@@ -538,13 +605,27 @@ dummy_dev_alloc_failed:
+ void edma_destroy(struct ppe_device *ppe_dev)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
+       /* Disable interrupts. */
++      for (i = 1; i <= hw_info->max_ports; i++)
++              edma_cfg_tx_disable_interrupts(i);
++
+       edma_cfg_rx_disable_interrupts();
+-      /* Free IRQ for RXDESC rings. */
++      /* Free IRQ for TXCMPL rings. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              synchronize_irq(edma_ctx->intr_info.intr_txcmpl[i]);
++
++              free_irq(edma_ctx->intr_info.intr_txcmpl[i],
++                       (void *)&edma_ctx->txcmpl_rings[i]);
++              kfree(edma_txcmpl_irq_name[i]);
++      }
++      kfree(edma_txcmpl_irq_name);
++
++      /* 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],
+@@ -560,6 +641,7 @@ void edma_destroy(struct ppe_device *ppe
+       edma_cfg_rx_napi_delete();
+       edma_cfg_rx_rings_disable();
+       edma_cfg_rx_rings_cleanup();
++      edma_cfg_tx_rings_cleanup();
+       free_netdev(edma_ctx->dummy_dev);
+       kfree(edma_ctx->netdev_arr);
+@@ -585,6 +667,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;
++      edma_ctx->tx_requeue_stop = false;
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -7,6 +7,7 @@
+ #include "ppe_config.h"
+ #include "edma_rx.h"
++#include "edma_tx.h"
+ /* One clock cycle = 1/(EDMA clock frequency in Mhz) micro seconds.
+  *
+@@ -104,8 +105,11 @@ struct edma_intr_info {
+  * @intr_info: EDMA Interrupt info
+  * @rxfill_rings: Rx fill Rings, SW is producer
+  * @rx_rings: Rx Desc Rings, SW is consumer
++ * @tx_rings: Tx Descriptor Ring, SW is producer
++ * @txcmpl_rings: Tx complete Ring, SW is consumer
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
++ * @tx_requeue_stop: Tx requeue stop enabled or disabled
+  */
+ struct edma_context {
+       struct net_device **netdev_arr;
+@@ -115,8 +119,11 @@ struct edma_context {
+       struct edma_intr_info intr_info;
+       struct edma_rxfill_ring *rxfill_rings;
+       struct edma_rxdesc_ring *rx_rings;
++      struct edma_txdesc_ring *tx_rings;
++      struct edma_txcmpl_ring *txcmpl_rings;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
++      bool tx_requeue_stop;
+ };
+ /* Global EDMA context */
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+@@ -0,0 +1,648 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Configure rings, Buffers and NAPI for transmit path along with
++ * providing APIs to enable, disable, clean and map the Tx rings.
++ */
++
++#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_tx.h"
++#include "edma_port.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++static void edma_cfg_txcmpl_ring_cleanup(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev =  ppe_dev->dev;
++
++      /* Free any buffers assigned to any descriptors. */
++      edma_tx_complete(EDMA_TX_RING_SIZE - 1, txcmpl_ring);
++
++      /* Free TxCmpl ring descriptors. */
++      dma_free_coherent(dev, sizeof(struct edma_txcmpl_desc)
++                        * txcmpl_ring->count, txcmpl_ring->desc,
++                        txcmpl_ring->dma);
++      txcmpl_ring->desc = NULL;
++      txcmpl_ring->dma = (dma_addr_t)0;
++}
++
++static int edma_cfg_txcmpl_ring_setup(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev =  ppe_dev->dev;
++
++      /* Allocate RxFill ring descriptors. */
++      txcmpl_ring->desc = dma_alloc_coherent(dev, sizeof(struct edma_txcmpl_desc)
++                                             * txcmpl_ring->count,
++                                             &txcmpl_ring->dma,
++                                             GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txcmpl_ring->desc))
++              return -ENOMEM;
++
++      return 0;
++}
++
++static void edma_cfg_tx_desc_ring_cleanup(struct edma_txdesc_ring *txdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_pri *txdesc = NULL;
++      struct device *dev =  ppe_dev->dev;
++      u32 prod_idx, cons_idx, data, reg;
++      struct sk_buff *skb = NULL;
++
++      /* Free any buffers assigned to any descriptors. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      prod_idx = data & EDMA_TXDESC_PROD_IDX_MASK;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CONS_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      cons_idx = data & EDMA_TXDESC_CONS_IDX_MASK;
++
++      /* Walk active list, obtain skb from descriptor and free it. */
++      while (cons_idx != prod_idx) {
++              txdesc = EDMA_TXDESC_PRI_DESC(txdesc_ring, cons_idx);
++              skb = (struct sk_buff *)EDMA_TXDESC_OPAQUE_GET(txdesc);
++              dev_kfree_skb_any(skb);
++
++              cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++      }
++
++      /* Free Tx ring descriptors. */
++      dma_free_coherent(dev, (sizeof(struct edma_txdesc_pri)
++                        * txdesc_ring->count),
++                        txdesc_ring->pdesc,
++                        txdesc_ring->pdma);
++      txdesc_ring->pdesc = NULL;
++      txdesc_ring->pdma = (dma_addr_t)0;
++
++      /* Free any buffers assigned to any secondary descriptors. */
++      dma_free_coherent(dev, (sizeof(struct edma_txdesc_sec)
++                        * txdesc_ring->count),
++                        txdesc_ring->sdesc,
++                        txdesc_ring->sdma);
++      txdesc_ring->sdesc = NULL;
++      txdesc_ring->sdma = (dma_addr_t)0;
++}
++
++static int edma_cfg_tx_desc_ring_setup(struct edma_txdesc_ring *txdesc_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++
++      /* Allocate RxFill ring descriptors. */
++      txdesc_ring->pdesc = dma_alloc_coherent(dev, sizeof(struct edma_txdesc_pri)
++                                              * txdesc_ring->count,
++                                              &txdesc_ring->pdma,
++                                              GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txdesc_ring->pdesc))
++              return -ENOMEM;
++
++      txdesc_ring->sdesc = dma_alloc_coherent(dev, sizeof(struct edma_txdesc_sec)
++                                              * txdesc_ring->count,
++                                              &txdesc_ring->sdma,
++                                              GFP_KERNEL | __GFP_ZERO);
++
++      if (unlikely(!txdesc_ring->sdesc)) {
++              dma_free_coherent(dev, (sizeof(struct edma_txdesc_pri)
++                                * txdesc_ring->count),
++                                txdesc_ring->pdesc,
++                                txdesc_ring->pdma);
++              txdesc_ring->pdesc = NULL;
++              txdesc_ring->pdma = (dma_addr_t)0;
++              return -ENOMEM;
++      }
++
++      return 0;
++}
++
++static void edma_cfg_tx_desc_ring_configure(struct edma_txdesc_ring *txdesc_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_TXDESC_BA(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->pdma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_BA2(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->sdma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_RING_SIZE(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)(txdesc_ring->count & EDMA_TXDESC_RING_SIZE_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_write(regmap, reg, (u32)EDMA_TX_INITIAL_PROD_IDX);
++
++      data = FIELD_PREP(EDMA_TXDESC_CTRL_FC_GRP_ID_MASK, txdesc_ring->fc_grp_id);
++
++      /* Configure group ID for flow control for this Tx ring. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++      regmap_write(regmap, reg, data);
++}
++
++static void edma_cfg_txcmpl_ring_configure(struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 data, reg;
++
++      /* Configure TxCmpl ring base address. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_BA(txcmpl_ring->id);
++      regmap_write(regmap, reg, (u32)(txcmpl_ring->dma & EDMA_RING_DMA_MASK));
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_RING_SIZE(txcmpl_ring->id);
++      regmap_write(regmap, reg, (u32)(txcmpl_ring->count & EDMA_TXDESC_RING_SIZE_MASK));
++
++      /* Set TxCmpl ret mode to opaque. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CTRL(txcmpl_ring->id);
++      regmap_write(regmap, reg, EDMA_TXCMPL_RETMODE_OPAQUE);
++
++      /* Configure the Mitigation timer. */
++      data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_TX_MITIGATION_TIMER_DEF,
++                                         ppe_dev->clk_rate / MHZ);
++      data = ((data & EDMA_TX_MOD_TIMER_INIT_MASK)
++              << EDMA_TX_MOD_TIMER_INIT_SHIFT);
++      pr_debug("EDMA Tx mitigation timer value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_MOD_TIMER(txcmpl_ring->id);
++      regmap_write(regmap, reg, data);
++
++      /* Configure the Mitigation packet count. */
++      data = (EDMA_TX_MITIGATION_PKT_CNT_DEF & EDMA_TXCMPL_LOW_THRE_MASK)
++              << EDMA_TXCMPL_LOW_THRE_SHIFT;
++      pr_debug("EDMA Tx mitigation packet count value: %d\n", data);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_UGT_THRE(txcmpl_ring->id);
++      regmap_write(regmap, reg, data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_CTRL(txcmpl_ring->id);
++      regmap_write(regmap, reg, EDMA_TX_NE_INT_EN);
++}
++
++/**
++ * edma_cfg_tx_fill_per_port_tx_map - Fill Tx ring mapping.
++ * @netdev: Netdevice.
++ * @port_id: Port ID.
++ *
++ * Fill per-port Tx ring mapping in net device private area.
++ */
++void edma_cfg_tx_fill_per_port_tx_map(struct net_device *netdev, u32 port_id)
++{
++      u32 i;
++
++      /* Ring to core mapping is done in order starting from 0 for port 1. */
++      for_each_possible_cpu(i) {
++              struct edma_port_priv *port_dev = (struct edma_port_priv *)netdev_priv(netdev);
++              struct edma_txdesc_ring *txdesc_ring;
++              u32 txdesc_ring_id;
++
++              txdesc_ring_id = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[txdesc_ring_id];
++              port_dev->txr_map[i] = txdesc_ring;
++      }
++}
++
++/**
++ * edma_cfg_tx_rings_enable - Enable Tx rings.
++ *
++ * Enable Tx rings.
++ */
++void edma_cfg_tx_rings_enable(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_ring *txdesc_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[ring_idx];
++              u32 data;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++              regmap_read(regmap, reg, &data);
++              data |= FIELD_PREP(EDMA_TXDESC_CTRL_TXEN_MASK, EDMA_TXDESC_TX_ENABLE);
++
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_tx_rings_disable - Disable Tx rings.
++ *
++ * Disable Tx rings.
++ */
++void edma_cfg_tx_rings_disable(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_ring *txdesc_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txdesc_ring = &edma_ctx->tx_rings[ring_idx];
++              u32 data;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CTRL(txdesc_ring->id);
++              regmap_read(regmap, reg, &data);
++              data &= ~EDMA_TXDESC_TX_ENABLE;
++              regmap_write(regmap, reg, data);
++      }
++}
++
++/**
++ * edma_cfg_tx_ring_mappings - Map Tx to Tx complete rings.
++ *
++ * Map Tx to Tx complete rings.
++ */
++void edma_cfg_tx_ring_mappings(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 desc_index, i, data, reg;
++
++      /* Clear the TXDESC2CMPL_MAP_xx reg before setting up
++       * the mapping. This register holds TXDESC to TXFILL ring
++       * mapping.
++       */
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR, 0);
++      regmap_write(regmap, EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR, 0);
++      desc_index = txcmpl->ring_start;
++
++      /* 6 registers to hold the completion mapping for total 32
++       * TX desc rings (0-5, 6-11, 12-17, 18-23, 24-29 and rest).
++       * In each entry 5 bits hold the mapping for a particular TX desc ring.
++       */
++      for (i = tx->ring_start; i < tx->ring_start + tx->num_rings; i++) {
++              u32 reg, data;
++
++              if (i >= 0 && i <= 5)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR;
++              else if (i >= 6 && i <= 11)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR;
++              else if (i >= 12 && i <= 17)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR;
++              else if (i >= 18 && i <= 23)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR;
++              else if (i >= 24 && i <= 29)
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR;
++              else
++                      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR;
++
++              pr_debug("Configure Tx desc:%u to use TxCmpl:%u\n", i, desc_index);
++
++              /* Set the Tx complete descriptor ring number in the mapping register.
++               * E.g. If (txcmpl ring)desc_index = 31, (txdesc ring)i = 28.
++               *      reg = EDMA_REG_TXDESC2CMPL_MAP_4_ADDR
++               *      data |= (desc_index & 0x1F) << ((i % 6) * 5);
++               *      data |= (0x1F << 20); -
++               *      This sets 11111 at 20th bit of register EDMA_REG_TXDESC2CMPL_MAP_4_ADDR.
++               */
++              regmap_read(regmap, reg, &data);
++              data |= (desc_index & EDMA_TXDESC2CMPL_MAP_TXDESC_MASK) << ((i % 6) * 5);
++              regmap_write(regmap, reg, data);
++
++              desc_index++;
++              if (desc_index == txcmpl->ring_start + txcmpl->num_rings)
++                      desc_index = txcmpl->ring_start;
++      }
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_0_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_0_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_1_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_1_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_2_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_2_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_3_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_3_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_4_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_4_ADDR: 0x%x\n", data);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC2CMPL_MAP_5_ADDR;
++      regmap_read(regmap, reg, &data);
++      pr_debug("EDMA_REG_TXDESC2CMPL_MAP_5_ADDR: 0x%x\n", data);
++}
++
++static int edma_cfg_tx_rings_setup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i, j = 0;
++
++      /* Set Txdesc flow control group id, same as port number. */
++      for (i = 0; i < hw_info->max_ports; i++) {
++              for_each_possible_cpu(j) {
++                      struct edma_txdesc_ring *txdesc_ring = NULL;
++                      u32 txdesc_idx = (i * num_possible_cpus()) + j;
++
++                      txdesc_ring = &edma_ctx->tx_rings[txdesc_idx];
++                      txdesc_ring->fc_grp_id = i + 1;
++              }
++      }
++
++      /* Allocate TxDesc ring descriptors. */
++      for (i = 0; i < tx->num_rings; i++) {
++              struct edma_txdesc_ring *txdesc_ring = NULL;
++              int ret;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              txdesc_ring->count = EDMA_TX_RING_SIZE;
++              txdesc_ring->id = tx->ring_start + i;
++
++              ret = edma_cfg_tx_desc_ring_setup(txdesc_ring);
++              if (ret) {
++                      pr_err("Error in setting up %d txdesc ring. ret: %d",
++                             txdesc_ring->id, ret);
++                      while (i-- >= 0)
++                              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++                      return -ENOMEM;
++              }
++      }
++
++      /* Allocate TxCmpl ring descriptors. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              struct edma_txcmpl_ring *txcmpl_ring = NULL;
++              int ret;
++
++              txcmpl_ring = &edma_ctx->txcmpl_rings[i];
++              txcmpl_ring->count = EDMA_TX_RING_SIZE;
++              txcmpl_ring->id = txcmpl->ring_start + i;
++
++              ret = edma_cfg_txcmpl_ring_setup(txcmpl_ring);
++              if (ret != 0) {
++                      pr_err("Error in setting up %d TxCmpl ring. ret: %d",
++                             txcmpl_ring->id, ret);
++                      while (i-- >= 0)
++                              edma_cfg_txcmpl_ring_cleanup(&edma_ctx->txcmpl_rings[i]);
++
++                      goto txcmpl_mem_alloc_fail;
++              }
++      }
++
++      pr_debug("Tx descriptor count for Tx desc and Tx complete rings: %d\n",
++               EDMA_TX_RING_SIZE);
++
++      return 0;
++
++txcmpl_mem_alloc_fail:
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_tx_rings_alloc - Allocate EDMA Tx rings.
++ *
++ * Allocate EDMA Tx rings.
++ */
++int edma_cfg_tx_rings_alloc(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++
++      edma_ctx->tx_rings = kzalloc((sizeof(*edma_ctx->tx_rings) * tx->num_rings),
++                                   GFP_KERNEL);
++      if (!edma_ctx->tx_rings)
++              return -ENOMEM;
++
++      edma_ctx->txcmpl_rings = kzalloc((sizeof(*edma_ctx->txcmpl_rings) * txcmpl->num_rings),
++                                       GFP_KERNEL);
++      if (!edma_ctx->txcmpl_rings)
++              goto txcmpl_ring_alloc_fail;
++
++      pr_debug("Num rings - TxDesc:%u (%u-%u) TxCmpl:%u (%u-%u)\n",
++               tx->num_rings, tx->ring_start,
++              (tx->ring_start + tx->num_rings - 1),
++              txcmpl->num_rings, txcmpl->ring_start,
++              (txcmpl->ring_start + txcmpl->num_rings - 1));
++
++      if (edma_cfg_tx_rings_setup()) {
++              pr_err("Error in setting up tx rings\n");
++              goto tx_rings_setup_fail;
++      }
++
++      return 0;
++
++tx_rings_setup_fail:
++      kfree(edma_ctx->txcmpl_rings);
++      edma_ctx->txcmpl_rings = NULL;
++
++txcmpl_ring_alloc_fail:
++      kfree(edma_ctx->tx_rings);
++      edma_ctx->tx_rings = NULL;
++
++      return -ENOMEM;
++}
++
++/**
++ * edma_cfg_tx_rings_cleanup - Cleanup EDMA Tx rings.
++ *
++ * Cleanup EDMA Tx rings.
++ */
++void edma_cfg_tx_rings_cleanup(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i;
++
++      /* Free any buffers assigned to any descriptors. */
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_cleanup(&edma_ctx->tx_rings[i]);
++
++      /* Free Tx completion descriptors. */
++      for (i = 0; i < txcmpl->num_rings; i++)
++              edma_cfg_txcmpl_ring_cleanup(&edma_ctx->txcmpl_rings[i]);
++
++      kfree(edma_ctx->tx_rings);
++      kfree(edma_ctx->txcmpl_rings);
++      edma_ctx->tx_rings = NULL;
++      edma_ctx->txcmpl_rings = NULL;
++}
++
++/**
++ * edma_cfg_tx_rings - Configure EDMA Tx rings.
++ *
++ * Configure EDMA Tx rings.
++ */
++void edma_cfg_tx_rings(void)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      u32 i;
++
++      /* Configure Tx desc ring. */
++      for (i = 0; i < tx->num_rings; i++)
++              edma_cfg_tx_desc_ring_configure(&edma_ctx->tx_rings[i]);
++
++      /* Configure TxCmpl ring. */
++      for (i = 0; i < txcmpl->num_rings; i++)
++              edma_cfg_txcmpl_ring_configure(&edma_ctx->txcmpl_rings[i]);
++}
++
++/**
++ * edma_cfg_tx_disable_interrupts - EDMA disable TX interrupts.
++ *
++ * Disable TX interrupt masks.
++ */
++void edma_cfg_tx_disable_interrupts(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
++      }
++}
++
++/**
++ * edma_cfg_tx_enable_interrupts - EDMA enable TX interrupts.
++ *
++ * Enable TX interrupt masks.
++ */
++void edma_cfg_tx_enable_interrupts(u32 port_id)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx, reg;
++
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_txcmpl);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_enable - EDMA Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Enable Tx NAPI.
++ */
++void edma_cfg_tx_napi_enable(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Enabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              napi_enable(&txcmpl_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_disable - Disable Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Disable Tx NAPI.
++ */
++void edma_cfg_tx_napi_disable(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Disabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              napi_disable(&txcmpl_ring->napi);
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_delete - Delete Tx NAPI.
++ * @port_id: Port ID.
++ *
++ * Delete Tx NAPI.
++ */
++void edma_cfg_tx_napi_delete(u32 port_id)
++{
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Disabling Tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              if (!txcmpl_ring->napi_added)
++                      continue;
++
++              netif_napi_del(&txcmpl_ring->napi);
++              txcmpl_ring->napi_added = false;
++      }
++}
++
++/**
++ * edma_cfg_tx_napi_add - TX NAPI add.
++ * @netdev: Netdevice.
++ * @port_id: Port ID.
++ *
++ * TX NAPI add.
++ */
++void edma_cfg_tx_napi_add(struct net_device *netdev, u32 port_id)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_txcmpl_ring *txcmpl_ring;
++      u32 i, ring_idx;
++
++      /* Adding tx napi for a interface with each queue. */
++      for_each_possible_cpu(i) {
++              ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
++              txcmpl_ring = &edma_ctx->txcmpl_rings[ring_idx];
++              netif_napi_add_weight(netdev, &txcmpl_ring->napi,
++                                    edma_tx_napi_poll, hw_info->napi_budget_tx);
++              txcmpl_ring->napi_added = true;
++              netdev_dbg(netdev, "Napi added for txcmpl ring: %u\n", txcmpl_ring->id);
++      }
++
++      netdev_dbg(netdev, "Tx NAPI budget: %d\n", hw_info->napi_budget_tx);
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+@@ -0,0 +1,28 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_CFG_TX__
++#define __EDMA_CFG_TX__
++
++/* Tx mitigation timer's default value. */
++#define EDMA_TX_MITIGATION_TIMER_DEF  250
++
++/* Tx mitigation packet count default value. */
++#define EDMA_TX_MITIGATION_PKT_CNT_DEF        16
++
++void edma_cfg_tx_rings(void);
++int edma_cfg_tx_rings_alloc(void);
++void edma_cfg_tx_rings_cleanup(void);
++void edma_cfg_tx_disable_interrupts(u32 port_id);
++void edma_cfg_tx_enable_interrupts(u32 port_id);
++void edma_cfg_tx_napi_enable(u32 port_id);
++void edma_cfg_tx_napi_disable(u32 port_id);
++void edma_cfg_tx_napi_delete(u32 port_id);
++void edma_cfg_tx_napi_add(struct net_device *netdevice, u32 macid);
++void edma_cfg_tx_ring_mappings(void);
++void edma_cfg_txcmpl_mapping_fill(void);
++void edma_cfg_tx_rings_enable(u32 port_id);
++void edma_cfg_tx_rings_disable(u32 port_id);
++void edma_cfg_tx_fill_per_port_tx_map(struct net_device *netdev, u32 macid);
++#endif
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -13,6 +13,7 @@
+ #include "edma.h"
+ #include "edma_cfg_rx.h"
++#include "edma_cfg_tx.h"
+ #include "edma_port.h"
+ #include "ppe_regs.h"
+@@ -35,6 +36,15 @@ static int edma_port_stats_alloc(struct
+               return -ENOMEM;
+       }
++      port_priv->pcpu_stats.tx_stats =
++              netdev_alloc_pcpu_stats(struct edma_port_tx_stats);
++      if (!port_priv->pcpu_stats.tx_stats) {
++              netdev_err(netdev, "Per-cpu EDMA Tx stats alloc failed for %s\n",
++                         netdev->name);
++              free_percpu(port_priv->pcpu_stats.rx_stats);
++              return -ENOMEM;
++      }
++
+       return 0;
+ }
+@@ -43,6 +53,28 @@ static void edma_port_stats_free(struct
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       free_percpu(port_priv->pcpu_stats.rx_stats);
++      free_percpu(port_priv->pcpu_stats.tx_stats);
++}
++
++static void edma_port_configure(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      edma_cfg_tx_fill_per_port_tx_map(netdev, port_id);
++      edma_cfg_tx_rings_enable(port_id);
++      edma_cfg_tx_napi_add(netdev, port_id);
++}
++
++static void edma_port_deconfigure(struct net_device *netdev)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      edma_cfg_tx_napi_delete(port_id);
++      edma_cfg_tx_rings_disable(port_id);
+ }
+ static u16 __maybe_unused edma_port_select_queue(__maybe_unused struct net_device *netdev,
+@@ -60,6 +92,7 @@ static int edma_port_open(struct net_dev
+ {
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       struct ppe_port *ppe_port;
++      int port_id;
+       if (!port_priv)
+               return -EINVAL;
+@@ -74,10 +107,14 @@ static int edma_port_open(struct net_dev
+       netdev->wanted_features |= EDMA_NETDEV_FEATURES;
+       ppe_port  = port_priv->ppe_port;
++      port_id = ppe_port->port_id;
+       if (ppe_port->phylink)
+               phylink_start(ppe_port->phylink);
++      edma_cfg_tx_napi_enable(port_id);
++      edma_cfg_tx_enable_interrupts(port_id);
++
+       netif_start_queue(netdev);
+       return 0;
+@@ -87,13 +124,21 @@ static int edma_port_close(struct net_de
+ {
+       struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
+       struct ppe_port *ppe_port;
++      int port_id;
+       if (!port_priv)
+               return -EINVAL;
+       netif_stop_queue(netdev);
++      /* 20ms delay would provide a plenty of margin to take care of in-flight packets. */
++      msleep(20);
++
+       ppe_port  = port_priv->ppe_port;
++      port_id = ppe_port->port_id;
++
++      edma_cfg_tx_disable_interrupts(port_id);
++      edma_cfg_tx_napi_disable(port_id);
+       /* Phylink close. */
+       if (ppe_port->phylink)
+@@ -137,6 +182,92 @@ static netdev_features_t edma_port_featu
+       return features;
+ }
++static netdev_tx_t edma_port_xmit(struct sk_buff *skb,
++                                struct net_device *dev)
++{
++      struct edma_port_priv *port_priv = NULL;
++      struct edma_port_pcpu_stats *pcpu_stats;
++      struct edma_txdesc_ring *txdesc_ring;
++      struct edma_port_tx_stats *stats;
++      enum edma_tx_gso_status result;
++      struct sk_buff *segs = NULL;
++      u8 cpu_id;
++      u32 skbq;
++      int ret;
++
++      if (!skb || !dev)
++              return NETDEV_TX_OK;
++
++      port_priv = netdev_priv(dev);
++
++      /* Select a TX ring. */
++      skbq = (skb_get_queue_mapping(skb) & (num_possible_cpus() - 1));
++
++      txdesc_ring = (struct edma_txdesc_ring *)port_priv->txr_map[skbq];
++
++      pcpu_stats = &port_priv->pcpu_stats;
++      stats = this_cpu_ptr(pcpu_stats->tx_stats);
++
++      /* HW does not support TSO for packets with more than or equal to
++       * 32 segments. Perform SW GSO for such packets.
++       */
++      result = edma_tx_gso_segment(skb, dev, &segs);
++      if (likely(result == EDMA_TX_GSO_NOT_NEEDED)) {
++              /* Transmit the packet. */
++              ret = edma_tx_ring_xmit(dev, skb, txdesc_ring, stats);
++
++              if (unlikely(ret == EDMA_TX_FAIL_NO_DESC)) {
++                      if (likely(!edma_ctx->tx_requeue_stop)) {
++                              cpu_id = smp_processor_id();
++                              netdev_dbg(dev, "Stopping tx queue due to lack oftx descriptors\n");
++                              u64_stats_update_begin(&stats->syncp);
++                              ++stats->tx_queue_stopped[cpu_id];
++                              u64_stats_update_end(&stats->syncp);
++                              netif_tx_stop_queue(netdev_get_tx_queue(dev, skbq));
++                              return NETDEV_TX_BUSY;
++                      }
++              }
++
++              if (unlikely(ret != EDMA_TX_OK)) {
++                      dev_kfree_skb_any(skb);
++                      u64_stats_update_begin(&stats->syncp);
++                      ++stats->tx_drops;
++                      u64_stats_update_end(&stats->syncp);
++              }
++
++              return NETDEV_TX_OK;
++      } else if (unlikely(result == EDMA_TX_GSO_FAIL)) {
++              netdev_dbg(dev, "%p: SW GSO failed for segment size: %d\n",
++                         skb, skb_shinfo(skb)->gso_segs);
++              dev_kfree_skb_any(skb);
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->tx_gso_drop_pkts;
++              u64_stats_update_end(&stats->syncp);
++              return NETDEV_TX_OK;
++      }
++
++      u64_stats_update_begin(&stats->syncp);
++      ++stats->tx_gso_pkts;
++      u64_stats_update_end(&stats->syncp);
++
++      dev_kfree_skb_any(skb);
++      while (segs) {
++              skb = segs;
++              segs = segs->next;
++
++              /* Transmit the packet. */
++              ret = edma_tx_ring_xmit(dev, skb, txdesc_ring, stats);
++              if (unlikely(ret != EDMA_TX_OK)) {
++                      dev_kfree_skb_any(skb);
++                      u64_stats_update_begin(&stats->syncp);
++                      ++stats->tx_drops;
++                      u64_stats_update_end(&stats->syncp);
++              }
++      }
++
++      return NETDEV_TX_OK;
++}
++
+ static void edma_port_get_stats64(struct net_device *netdev,
+                                 struct rtnl_link_stats64 *stats)
+ {
+@@ -179,6 +310,7 @@ static int edma_port_set_mac_address(str
+ static const struct net_device_ops edma_port_netdev_ops = {
+       .ndo_open = edma_port_open,
+       .ndo_stop = edma_port_close,
++      .ndo_start_xmit = edma_port_xmit,
+       .ndo_get_stats64 = edma_port_get_stats64,
+       .ndo_set_mac_address = edma_port_set_mac_address,
+       .ndo_validate_addr = eth_validate_addr,
+@@ -199,6 +331,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_deconfigure(netdev);
+       edma_port_stats_free(netdev);
+       unregister_netdev(netdev);
+       free_netdev(netdev);
+@@ -276,6 +409,8 @@ int edma_port_setup(struct ppe_port *por
+        */
+       edma_ctx->netdev_arr[port_id - 1] = netdev;
++      edma_port_configure(netdev);
++
+       /* Setup phylink. */
+       ret = ppe_port_phylink_setup(port, netdev);
+       if (ret) {
+@@ -298,6 +433,7 @@ int edma_port_setup(struct ppe_port *por
+ register_netdev_fail:
+       ppe_port_phylink_destroy(port);
+ port_phylink_setup_fail:
++      edma_port_deconfigure(netdev);
+       edma_ctx->netdev_arr[port_id - 1] = NULL;
+       edma_port_stats_free(netdev);
+ stats_alloc_fail:
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.h
+@@ -7,6 +7,8 @@
+ #include "ppe_port.h"
++#define EDMA_PORT_MAX_CORE            4
++
+ #define EDMA_NETDEV_FEATURES          (NETIF_F_FRAGLIST \
+                                       | NETIF_F_SG \
+                                       | NETIF_F_RXCSUM \
+@@ -35,11 +37,43 @@ struct edma_port_rx_stats {
+ };
+ /**
++ * struct edma_port_tx_stats - EDMA TX port per CPU stats for the port.
++ * @tx_pkts: Number of Tx packets
++ * @tx_bytes: Number of Tx bytes
++ * @tx_drops: Number of Tx drops
++ * @tx_nr_frag_pkts: Number of Tx nr_frag packets
++ * @tx_fraglist_pkts: Number of Tx fraglist packets
++ * @tx_fraglist_with_nr_frags_pkts:  Number of Tx packets with fraglist and nr_frags
++ * @tx_tso_pkts: Number of Tx TSO packets
++ * @tx_tso_drop_pkts: Number of Tx TSO drop packets
++ * @tx_gso_pkts: Number of Tx GSO packets
++ * @tx_gso_drop_pkts: Number of Tx GSO drop packets
++ * @tx_queue_stopped: Number of Tx queue stopped packets
++ * @syncp: Synchronization pointer
++ */
++struct edma_port_tx_stats {
++      u64 tx_pkts;
++      u64 tx_bytes;
++      u64 tx_drops;
++      u64 tx_nr_frag_pkts;
++      u64 tx_fraglist_pkts;
++      u64 tx_fraglist_with_nr_frags_pkts;
++      u64 tx_tso_pkts;
++      u64 tx_tso_drop_pkts;
++      u64 tx_gso_pkts;
++      u64 tx_gso_drop_pkts;
++      u64 tx_queue_stopped[EDMA_PORT_MAX_CORE];
++      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
++ * @tx_stats: Per CPU Tx statistics
+  */
+ struct edma_port_pcpu_stats {
+       struct edma_port_rx_stats __percpu *rx_stats;
++      struct edma_port_tx_stats __percpu *tx_stats;
+ };
+ /**
+@@ -54,6 +88,7 @@ struct edma_port_priv {
+       struct ppe_port *ppe_port;
+       struct net_device *netdev;
+       struct edma_port_pcpu_stats pcpu_stats;
++      struct edma_txdesc_ring *txr_map[EDMA_PORT_MAX_CORE];
+       unsigned long flags;
+ };
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.c
+@@ -0,0 +1,808 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* Provide APIs to alloc Tx Buffers, fill the Tx descriptors and transmit
++ * Scatter Gather and linear packets, Tx complete to free the skb after transmit.
++ */
++
++#include <linux/dma-mapping.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <net/gso.h>
++#include <linux/regmap.h>
++
++#include "edma.h"
++#include "edma_cfg_tx.h"
++#include "edma_port.h"
++#include "ppe.h"
++#include "ppe_regs.h"
++
++static u32 edma_tx_num_descs_for_sg(struct sk_buff *skb)
++{
++      u32 nr_frags_first = 0, num_tx_desc_needed = 0;
++
++      /* Check if we have enough Tx descriptors for SG. */
++      if (unlikely(skb_shinfo(skb)->nr_frags)) {
++              nr_frags_first = skb_shinfo(skb)->nr_frags;
++              WARN_ON_ONCE(nr_frags_first > MAX_SKB_FRAGS);
++              num_tx_desc_needed += nr_frags_first;
++      }
++
++      /* Walk through fraglist skbs making a note of nr_frags
++       * One Tx desc for fraglist skb. Fraglist skb may have
++       * further nr_frags.
++       */
++      if (unlikely(skb_has_frag_list(skb))) {
++              struct sk_buff *iter_skb;
++
++              skb_walk_frags(skb, iter_skb) {
++                      u32 nr_frags = skb_shinfo(iter_skb)->nr_frags;
++
++                      WARN_ON_ONCE(nr_frags > MAX_SKB_FRAGS);
++                      num_tx_desc_needed += (1 + nr_frags);
++              }
++      }
++
++      return (num_tx_desc_needed + 1);
++}
++
++/**
++ * edma_tx_gso_segment - Tx GSO.
++ * @skb: Socket Buffer.
++ * @netdev: Netdevice.
++ * @segs: SKB segments from GSO.
++ *
++ * Format skbs into GSOs.
++ *
++ * Return 1 on success, error code on failure.
++ */
++enum edma_tx_gso_status edma_tx_gso_segment(struct sk_buff *skb,
++                                          struct net_device *netdev, struct sk_buff **segs)
++{
++      u32 num_tx_desc_needed;
++
++      /* Check is skb is non-linear to proceed. */
++      if (likely(!skb_is_nonlinear(skb)))
++              return EDMA_TX_GSO_NOT_NEEDED;
++
++      /* Check if TSO is enabled. If so, return as skb doesn't
++       * need to be segmented by linux.
++       */
++      if (netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) {
++              num_tx_desc_needed = edma_tx_num_descs_for_sg(skb);
++              if (likely(num_tx_desc_needed <= EDMA_TX_TSO_SEG_MAX))
++                      return EDMA_TX_GSO_NOT_NEEDED;
++      }
++
++      /* GSO segmentation of the skb into multiple segments. */
++      *segs = skb_gso_segment(skb, netdev->features
++              & ~(NETIF_F_TSO | NETIF_F_TSO6));
++
++      /* Check for error in GSO segmentation. */
++      if (IS_ERR_OR_NULL(*segs)) {
++              netdev_info(netdev, "Tx gso fail\n");
++              return EDMA_TX_GSO_FAIL;
++      }
++
++      return EDMA_TX_GSO_SUCCEED;
++}
++
++/**
++ * edma_tx_complete - Reap Tx completion descriptors.
++ * @work_to_do: Work to do.
++ * @txcmpl_ring: Tx Completion ring.
++ *
++ * Reap Tx completion descriptors of the transmitted
++ * packets and free the corresponding SKBs.
++ *
++ * Return the number descriptors for which Tx complete is done.
++ */
++u32 edma_tx_complete(u32 work_to_do, struct edma_txcmpl_ring *txcmpl_ring)
++{
++      struct edma_txcmpl_stats *txcmpl_stats = &txcmpl_ring->txcmpl_stats;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 cons_idx, end_idx, data, cpu_id;
++      struct device *dev = ppe_dev->dev;
++      u32 avail, count, txcmpl_errors;
++      struct edma_txcmpl_desc *txcmpl;
++      u32 prod_idx = 0, more_bit = 0;
++      struct netdev_queue *nq;
++      struct sk_buff *skb;
++      u32 reg;
++
++      cons_idx = txcmpl_ring->cons_idx;
++
++      if (likely(txcmpl_ring->avail_pkt >= work_to_do)) {
++              avail = work_to_do;
++      } else {
++              /* Get TXCMPL ring producer index. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_PROD_IDX(txcmpl_ring->id);
++              regmap_read(regmap, reg, &data);
++              prod_idx = data & EDMA_TXCMPL_PROD_IDX_MASK;
++
++              avail = EDMA_DESC_AVAIL_COUNT(prod_idx, cons_idx, EDMA_TX_RING_SIZE);
++              txcmpl_ring->avail_pkt = avail;
++
++              if (unlikely(!avail)) {
++                      dev_dbg(dev, "No available descriptors are pending for %d txcmpl ring\n",
++                              txcmpl_ring->id);
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->no_pending_desc;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++                      return 0;
++              }
++
++              avail = min(avail, work_to_do);
++      }
++
++      count = avail;
++
++      end_idx = (cons_idx + avail) & EDMA_TX_RING_SIZE_MASK;
++      txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++
++      /* Instead of freeing the skb, it might be better to save and use
++       * for Rxfill.
++       */
++      while (likely(avail--)) {
++              /* The last descriptor holds the SKB pointer for scattered frames.
++               * So skip the descriptors with more bit set.
++               */
++              more_bit = EDMA_TXCMPL_MORE_BIT_GET(txcmpl);
++              if (unlikely(more_bit)) {
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->desc_with_more_bit;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++                      cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++                      txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++                      continue;
++              }
++
++              /* Find and free the skb for Tx completion. */
++              skb = (struct sk_buff *)EDMA_TXCMPL_OPAQUE_GET(txcmpl);
++              if (unlikely(!skb)) {
++                      if (net_ratelimit())
++                              dev_warn(dev, "Invalid cons_idx:%u prod_idx:%u word2:%x word3:%x\n",
++                                       cons_idx, prod_idx, txcmpl->word2, txcmpl->word3);
++
++                      u64_stats_update_begin(&txcmpl_stats->syncp);
++                      ++txcmpl_stats->invalid_buffer;
++                      u64_stats_update_end(&txcmpl_stats->syncp);
++              } else {
++                      dev_dbg(dev, "TXCMPL: skb:%p, skb->len %d, skb->data_len %d, cons_idx:%d prod_idx:%d word2:0x%x word3:0x%x\n",
++                              skb, skb->len, skb->data_len, cons_idx, prod_idx,
++                              txcmpl->word2, txcmpl->word3);
++
++                      txcmpl_errors = EDMA_TXCOMP_RING_ERROR_GET(txcmpl->word3);
++                      if (unlikely(txcmpl_errors)) {
++                              if (net_ratelimit())
++                                      dev_err(dev, "Error 0x%0x observed in tx complete %d ring\n",
++                                              txcmpl_errors, txcmpl_ring->id);
++
++                              u64_stats_update_begin(&txcmpl_stats->syncp);
++                              ++txcmpl_stats->errors;
++                              u64_stats_update_end(&txcmpl_stats->syncp);
++                      }
++
++                      /* Retrieve pool id for unmapping.
++                       * 0 for linear skb and (pool id - 1) represents nr_frag index.
++                       */
++                      if (!EDMA_TXCOMP_POOL_ID_GET(txcmpl)) {
++                              dma_unmap_single(dev, virt_to_phys(skb->data),
++                                               skb->len, DMA_TO_DEVICE);
++                      } else {
++                              u8 frag_index = (EDMA_TXCOMP_POOL_ID_GET(txcmpl) - 1);
++                              skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_index];
++
++                              dma_unmap_page(dev, virt_to_phys(frag),
++                                             PAGE_SIZE, DMA_TO_DEVICE);
++                      }
++
++                      dev_kfree_skb(skb);
++              }
++
++              cons_idx = ((cons_idx + 1) & EDMA_TX_RING_SIZE_MASK);
++              txcmpl = EDMA_TXCMPL_DESC(txcmpl_ring, cons_idx);
++      }
++
++      txcmpl_ring->cons_idx = cons_idx;
++      txcmpl_ring->avail_pkt -= count;
++
++      dev_dbg(dev, "TXCMPL:%u count:%u prod_idx:%u cons_idx:%u\n",
++              txcmpl_ring->id, count, prod_idx, cons_idx);
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CONS_IDX(txcmpl_ring->id);
++      regmap_write(regmap, reg, cons_idx);
++
++      /* If tx_requeue_stop disabled (tx_requeue_stop = 0)
++       * Fetch the tx queue of interface and check if it is stopped.
++       * if queue is stopped and interface is up, wake up this queue.
++       */
++      if (unlikely(!edma_ctx->tx_requeue_stop)) {
++              cpu_id = smp_processor_id();
++              nq = netdev_get_tx_queue(txcmpl_ring->napi.dev, cpu_id);
++              if (unlikely(netif_tx_queue_stopped(nq)) &&
++                  netif_carrier_ok(txcmpl_ring->napi.dev)) {
++                      dev_dbg(dev, "Waking queue number %d, for interface %s\n",
++                              cpu_id, txcmpl_ring->napi.dev->name);
++                      __netif_tx_lock(nq, cpu_id);
++                      netif_tx_wake_queue(nq);
++                      __netif_tx_unlock(nq);
++              }
++      }
++
++      return count;
++}
++
++/**
++ * edma_tx_napi_poll - EDMA TX NAPI handler.
++ * @napi: NAPI structure.
++ * @budget: Tx NAPI Budget.
++ *
++ * EDMA TX NAPI handler.
++ */
++int edma_tx_napi_poll(struct napi_struct *napi, int budget)
++{
++      struct edma_txcmpl_ring *txcmpl_ring = (struct edma_txcmpl_ring *)napi;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 txcmpl_intr_status;
++      int work_done = 0;
++      u32 data, reg;
++
++      do {
++              work_done += edma_tx_complete(budget - work_done, txcmpl_ring);
++              if (work_done >= budget)
++                      return work_done;
++
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_STAT(txcmpl_ring->id);
++              regmap_read(regmap, reg, &data);
++              txcmpl_intr_status = data & EDMA_TXCMPL_RING_INT_STATUS_MASK;
++      } while (txcmpl_intr_status);
++
++      /* No more packets to process. Finish NAPI processing. */
++      napi_complete(napi);
++
++      /* Set TXCMPL ring interrupt mask. */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++      regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_txcmpl);
++
++      return work_done;
++}
++
++/**
++ * edma_tx_handle_irq - Tx IRQ Handler.
++ * @irq: Interrupt request.
++ * @ctx: Context.
++ *
++ * Process TX IRQ and schedule NAPI.
++ *
++ * Return IRQ handler code.
++ */
++irqreturn_t edma_tx_handle_irq(int irq, void *ctx)
++{
++      struct edma_txcmpl_ring *txcmpl_ring = (struct edma_txcmpl_ring *)ctx;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      pr_debug("irq: irq=%d txcmpl_ring_id=%u\n", irq, txcmpl_ring->id);
++      if (likely(napi_schedule_prep(&txcmpl_ring->napi))) {
++              /* Disable TxCmpl intr. */
++              reg = EDMA_BASE_OFFSET + EDMA_REG_TX_INT_MASK(txcmpl_ring->id);
++              regmap_write(regmap, reg, EDMA_MASK_INT_DISABLE);
++              __napi_schedule(&txcmpl_ring->napi);
++      }
++
++      return IRQ_HANDLED;
++}
++
++static void edma_tx_dma_unmap_frags(struct sk_buff *skb, u32 nr_frags)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      u32 buf_len = 0;
++      u8 i = 0;
++
++      for (i = 0; i < skb_shinfo(skb)->nr_frags - nr_frags; i++) {
++              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
++
++              /* DMA mapping was not done for zero size segments. */
++              buf_len = skb_frag_size(frag);
++              if (unlikely(buf_len == 0))
++                      continue;
++
++              dma_unmap_page(dev, virt_to_phys(frag), PAGE_SIZE,
++                             DMA_TO_DEVICE);
++      }
++}
++
++static u32 edma_tx_skb_nr_frags(struct edma_txdesc_ring *txdesc_ring,
++                              struct edma_txdesc_pri **txdesc, struct sk_buff *skb,
++                              u32 *hw_next_to_use, u32 *invalid_frag)
++{
++      u32 nr_frags = 0, buf_len = 0, num_descs = 0, start_idx = 0, end_idx = 0;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 start_hw_next_to_use = *hw_next_to_use;
++      struct edma_txdesc_pri *txd = *txdesc;
++      struct device *dev = ppe_dev->dev;
++      u8 i = 0;
++
++      /* Hold onto the index mapped to *txdesc.
++       * This will be the index previous to that of current *hw_next_to_use.
++       */
++      start_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK)
++              & EDMA_TX_RING_SIZE_MASK);
++
++      /* Handle if the skb has nr_frags. */
++      nr_frags = skb_shinfo(skb)->nr_frags;
++      num_descs = nr_frags;
++      i = 0;
++
++      while (nr_frags--) {
++              skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
++              dma_addr_t buff_addr;
++
++              buf_len = skb_frag_size(frag);
++
++              /* Zero size segment can lead EDMA HW to hang so, we don't want to
++               * process them. Zero size segment can happen during TSO operation
++               * if there is nothing but header in the primary segment.
++               */
++              if (unlikely(buf_len == 0)) {
++                      num_descs--;
++                      i++;
++                      continue;
++              }
++
++              /* Setting the MORE bit on the previous Tx descriptor.
++               * Note: We will flush this descriptor as well later.
++               */
++              EDMA_TXDESC_MORE_BIT_SET(txd, 1);
++              EDMA_TXDESC_ENDIAN_SET(txd);
++
++              txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++              memset(txd, 0, sizeof(struct edma_txdesc_pri));
++              buff_addr = skb_frag_dma_map(dev, frag, 0, buf_len,
++                                           DMA_TO_DEVICE);
++              if (dma_mapping_error(dev, buff_addr)) {
++                      dev_dbg(dev, "Unable to dma first descriptor for nr_frags tx\n");
++                      *hw_next_to_use = start_hw_next_to_use;
++                      *invalid_frag = nr_frags;
++                      return 0;
++              }
++
++              EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++              EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++              EDMA_TXDESC_POOL_ID_SET(txd, (i + 1));
++
++              *hw_next_to_use = ((*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK);
++              i++;
++      }
++
++      EDMA_TXDESC_ENDIAN_SET(txd);
++
++      /* This will be the index previous to that of current *hw_next_to_use. */
++      end_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK) & EDMA_TX_RING_SIZE_MASK);
++
++      *txdesc = txd;
++
++      return num_descs;
++}
++
++static void edma_tx_fill_pp_desc(struct edma_port_priv *port_priv,
++                               struct edma_txdesc_pri *txd, struct sk_buff *skb,
++      struct edma_port_tx_stats *stats)
++{
++      struct ppe_port *port = port_priv->ppe_port;
++      int port_id = port->port_id;
++
++      /* Offload L3/L4 checksum computation. */
++      if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) {
++              EDMA_TXDESC_ADV_OFFLOAD_SET(txd);
++              EDMA_TXDESC_IP_CSUM_SET(txd);
++              EDMA_TXDESC_L4_CSUM_SET(txd);
++      }
++
++      /* Check if the packet needs TSO
++       * This will be mostly true for SG packets.
++       */
++      if (unlikely(skb_is_gso(skb))) {
++              if ((skb_shinfo(skb)->gso_type == SKB_GSO_TCPV4) ||
++                  (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6)) {
++                      u32 mss = skb_shinfo(skb)->gso_size;
++
++                      /* If MSS<256, HW will do TSO using MSS=256,
++                       * if MSS>10K, HW will do TSO using MSS=10K,
++                       * else HW will report error 0x200000 in Tx Cmpl.
++                       */
++                      if (mss < EDMA_TX_TSO_MSS_MIN)
++                              mss = EDMA_TX_TSO_MSS_MIN;
++                      else if (mss > EDMA_TX_TSO_MSS_MAX)
++                              mss = EDMA_TX_TSO_MSS_MAX;
++
++                      EDMA_TXDESC_TSO_ENABLE_SET(txd, 1);
++                      EDMA_TXDESC_MSS_SET(txd, mss);
++
++                      /* Update tso stats. */
++                      u64_stats_update_begin(&stats->syncp);
++                      stats->tx_tso_pkts++;
++                      u64_stats_update_end(&stats->syncp);
++              }
++      }
++
++      /* Set destination information in the descriptor. */
++      EDMA_TXDESC_SERVICE_CODE_SET(txd, PPE_EDMA_SC_BYPASS_ID);
++      EDMA_DST_INFO_SET(txd, port_id);
++}
++
++static struct edma_txdesc_pri *edma_tx_skb_first_desc(struct edma_port_priv *port_priv,
++                                                    struct edma_txdesc_ring *txdesc_ring,
++                                                    struct sk_buff *skb, u32 *hw_next_to_use,
++                                                    struct edma_port_tx_stats *stats)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct edma_txdesc_pri *txd = NULL;
++      struct device *dev = ppe_dev->dev;
++      dma_addr_t buff_addr;
++      u32 buf_len = 0;
++
++      /* Get the packet length. */
++      buf_len = skb_headlen(skb);
++      txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++      memset(txd, 0, sizeof(struct edma_txdesc_pri));
++
++      /* Set the data pointer as the buffer address in the descriptor. */
++      buff_addr = dma_map_single(dev, skb->data, buf_len, DMA_TO_DEVICE);
++      if (dma_mapping_error(dev, buff_addr)) {
++              dev_dbg(dev, "Unable to dma first descriptor for tx\n");
++              return NULL;
++      }
++
++      EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++      EDMA_TXDESC_POOL_ID_SET(txd, 0);
++      edma_tx_fill_pp_desc(port_priv, txd, skb, stats);
++
++      /* Set packet length in the descriptor. */
++      EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++      *hw_next_to_use = (*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK;
++
++      return txd;
++}
++
++static void edma_tx_handle_dma_err(struct sk_buff *skb, u32 num_sg_frag_list)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct device *dev = ppe_dev->dev;
++      struct sk_buff *iter_skb = NULL;
++      u32 cnt_sg_frag_list = 0;
++
++      /* Walk through all fraglist skbs. */
++      skb_walk_frags(skb, iter_skb) {
++              if (skb_headlen(iter_skb)) {
++                      dma_unmap_single(dev, virt_to_phys(iter_skb->data),
++                                       skb_headlen(iter_skb), DMA_TO_DEVICE);
++                      cnt_sg_frag_list += 1;
++              }
++
++              if (cnt_sg_frag_list == num_sg_frag_list)
++                      return;
++
++              /* skb fraglist skb had nr_frags, unmap that memory. */
++              u32 nr_frags = skb_shinfo(iter_skb)->nr_frags;
++
++              if (nr_frags == 0)
++                      continue;
++
++              for (int i = 0; i < nr_frags; i++) {
++                      skb_frag_t *frag = &skb_shinfo(iter_skb)->frags[i];
++
++                      /* DMA mapping was not done for zero size segments. */
++                      if (unlikely(skb_frag_size(frag) == 0))
++                              continue;
++
++                      dma_unmap_page(dev, virt_to_phys(frag),
++                                     PAGE_SIZE, DMA_TO_DEVICE);
++                      cnt_sg_frag_list += 1;
++                      if (cnt_sg_frag_list == num_sg_frag_list)
++                              return;
++              }
++      }
++}
++
++static u32 edma_tx_skb_sg_fill_desc(struct edma_txdesc_ring *txdesc_ring,
++                                  struct edma_txdesc_pri **txdesc,
++                                  struct sk_buff *skb, u32 *hw_next_to_use,
++                                  struct edma_port_tx_stats *stats)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 start_hw_next_to_use = 0, invalid_frag = 0;
++      struct edma_txdesc_pri *txd = *txdesc;
++      struct device *dev = ppe_dev->dev;
++      struct sk_buff *iter_skb = NULL;
++      u32 buf_len = 0, num_descs = 0;
++      u32 num_sg_frag_list = 0;
++
++      /* Head skb processed already. */
++      num_descs++;
++
++      if (unlikely(skb_has_frag_list(skb))) {
++              struct edma_txdesc_pri *start_desc = NULL;
++              u32 start_idx = 0, end_idx = 0;
++
++              /* Hold onto the index mapped to txd.
++               * This will be the index previous to that of current *hw_next_to_use.
++               */
++              start_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK)
++                           & EDMA_TX_RING_SIZE_MASK);
++              start_desc = txd;
++              start_hw_next_to_use = *hw_next_to_use;
++
++              /* Walk through all fraglist skbs. */
++              skb_walk_frags(skb, iter_skb) {
++                      dma_addr_t buff_addr;
++                      u32 num_nr_frag = 0;
++
++                      /* This case could happen during the packet decapsulation.
++                       * All header content might be removed.
++                       */
++                      buf_len = skb_headlen(iter_skb);
++                      if (unlikely(buf_len == 0))
++                              goto skip_primary;
++
++                      /* We make sure to flush this descriptor later. */
++                      EDMA_TXDESC_MORE_BIT_SET(txd, 1);
++                      EDMA_TXDESC_ENDIAN_SET(txd);
++
++                      txd = EDMA_TXDESC_PRI_DESC(txdesc_ring, *hw_next_to_use);
++                      memset(txd, 0, sizeof(struct edma_txdesc_pri));
++                      buff_addr = dma_map_single(dev, iter_skb->data,
++                                                 buf_len, DMA_TO_DEVICE);
++                      if (dma_mapping_error(dev, buff_addr)) {
++                              dev_dbg(dev, "Unable to dma for fraglist\n");
++                              goto dma_err;
++                      }
++
++                      EDMA_TXDESC_BUFFER_ADDR_SET(txd, buff_addr);
++                      EDMA_TXDESC_DATA_LEN_SET(txd, buf_len);
++                      EDMA_TXDESC_POOL_ID_SET(txd, 0);
++
++                      *hw_next_to_use = (*hw_next_to_use + 1) & EDMA_TX_RING_SIZE_MASK;
++                      num_descs += 1;
++                      num_sg_frag_list += 1;
++
++                      /* skb fraglist skb can have nr_frags. */
++skip_primary:
++                      if (unlikely(skb_shinfo(iter_skb)->nr_frags)) {
++                              num_nr_frag = edma_tx_skb_nr_frags(txdesc_ring, &txd,
++                                                                 iter_skb, hw_next_to_use,
++                                                                 &invalid_frag);
++                              if (unlikely(!num_nr_frag)) {
++                                      dev_dbg(dev, "No descriptor available for ring %d\n",
++                                              txdesc_ring->id);
++                                      edma_tx_dma_unmap_frags(iter_skb, invalid_frag);
++                                      goto dma_err;
++                              }
++
++                              num_descs += num_nr_frag;
++                              num_sg_frag_list += num_nr_frag;
++
++                              /* Update fraglist with nr_frag stats. */
++                              u64_stats_update_begin(&stats->syncp);
++                              stats->tx_fraglist_with_nr_frags_pkts++;
++                              u64_stats_update_end(&stats->syncp);
++                      }
++              }
++
++              EDMA_TXDESC_ENDIAN_SET(txd);
++
++              /* This will be the index previous to
++               * that of current *hw_next_to_use.
++               */
++              end_idx = (((*hw_next_to_use) + EDMA_TX_RING_SIZE_MASK) &
++                         EDMA_TX_RING_SIZE_MASK);
++
++              /* Update frag_list stats. */
++              u64_stats_update_begin(&stats->syncp);
++              stats->tx_fraglist_pkts++;
++              u64_stats_update_end(&stats->syncp);
++      } else {
++              /* Process skb with nr_frags. */
++              num_descs += edma_tx_skb_nr_frags(txdesc_ring, &txd, skb,
++                                                hw_next_to_use, &invalid_frag);
++              if (unlikely(!num_descs)) {
++                      dev_dbg(dev, "No descriptor available for ring %d\n", txdesc_ring->id);
++                      edma_tx_dma_unmap_frags(skb, invalid_frag);
++                      *txdesc = NULL;
++                      return num_descs;
++              }
++
++              u64_stats_update_begin(&stats->syncp);
++              stats->tx_nr_frag_pkts++;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      dev_dbg(dev, "skb:%p num_descs_filled: %u, nr_frags %u, frag_list fragments %u\n",
++              skb, num_descs, skb_shinfo(skb)->nr_frags, num_sg_frag_list);
++
++      *txdesc = txd;
++
++      return num_descs;
++
++dma_err:
++      if (!num_sg_frag_list)
++              goto reset_state;
++
++      edma_tx_handle_dma_err(skb, num_sg_frag_list);
++
++reset_state:
++      *hw_next_to_use = start_hw_next_to_use;
++      *txdesc = NULL;
++
++      return 0;
++}
++
++static u32 edma_tx_avail_desc(struct edma_txdesc_ring *txdesc_ring,
++                            u32 hw_next_to_use)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      u32 data = 0, avail = 0, hw_next_to_clean = 0;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_CONS_IDX(txdesc_ring->id);
++      regmap_read(regmap, reg, &data);
++      hw_next_to_clean = data & EDMA_TXDESC_CONS_IDX_MASK;
++
++      avail = EDMA_DESC_AVAIL_COUNT(hw_next_to_clean - 1,
++                                    hw_next_to_use, EDMA_TX_RING_SIZE);
++
++      return avail;
++}
++
++/**
++ * edma_tx_ring_xmit - Transmit a packet.
++ * @netdev: Netdevice.
++ * @skb: Socket Buffer.
++ * @txdesc_ring: Tx Descriptor ring.
++ * @stats: EDMA Tx Statistics.
++ *
++ * Check for available descriptors, fill the descriptors
++ * and transmit both linear and non linear packets.
++ *
++ * Return 0 on success, negative error code on failure.
++ */
++enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
++                                    struct sk_buff *skb, struct edma_txdesc_ring *txdesc_ring,
++                              struct edma_port_tx_stats *stats)
++{
++      struct edma_txdesc_stats *txdesc_stats = &txdesc_ring->txdesc_stats;
++      struct edma_port_priv *port_priv = netdev_priv(netdev);
++      u32 num_tx_desc_needed = 0, num_desc_filled = 0;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct ppe_port *port = port_priv->ppe_port;
++      struct regmap *regmap = ppe_dev->regmap;
++      struct edma_txdesc_pri *txdesc = NULL;
++      struct device *dev = ppe_dev->dev;
++      int port_id = port->port_id;
++      u32 hw_next_to_use = 0;
++      u32 reg;
++
++      hw_next_to_use = txdesc_ring->prod_idx;
++
++      if (unlikely(!(txdesc_ring->avail_desc)))  {
++              txdesc_ring->avail_desc = edma_tx_avail_desc(txdesc_ring,
++                                                           hw_next_to_use);
++              if (unlikely(!txdesc_ring->avail_desc)) {
++                      netdev_dbg(netdev, "No available descriptors are present at %d ring\n",
++                                 txdesc_ring->id);
++
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++      }
++
++      /* Process head skb for linear skb.
++       * Process head skb + nr_frags + fraglist for non linear skb.
++       */
++      if (likely(!skb_is_nonlinear(skb))) {
++              txdesc = edma_tx_skb_first_desc(port_priv, txdesc_ring, skb,
++                                              &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++
++              EDMA_TXDESC_ENDIAN_SET(txdesc);
++              num_desc_filled++;
++      } else {
++              num_tx_desc_needed = edma_tx_num_descs_for_sg(skb);
++
++              /* HW does not support TSO for packets with more than 32 segments.
++               * HW hangs up if it sees more than 32 segments. Kernel Perform GSO
++               * for such packets with netdev gso_max_segs set to 32.
++               */
++              if (unlikely(num_tx_desc_needed > EDMA_TX_TSO_SEG_MAX)) {
++                      netdev_dbg(netdev, "Number of segments %u more than %u for %d ring\n",
++                                 num_tx_desc_needed, EDMA_TX_TSO_SEG_MAX, txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->tso_max_seg_exceed;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++
++                      u64_stats_update_begin(&stats->syncp);
++                      stats->tx_tso_drop_pkts++;
++                      u64_stats_update_end(&stats->syncp);
++
++                      return EDMA_TX_FAIL;
++              }
++
++              if (unlikely(num_tx_desc_needed > txdesc_ring->avail_desc)) {
++                      txdesc_ring->avail_desc = edma_tx_avail_desc(txdesc_ring,
++                                                                   hw_next_to_use);
++                      if (num_tx_desc_needed > txdesc_ring->avail_desc) {
++                              u64_stats_update_begin(&txdesc_stats->syncp);
++                              ++txdesc_stats->no_desc_avail;
++                              u64_stats_update_end(&txdesc_stats->syncp);
++                              netdev_dbg(netdev, "Not enough available descriptors are present at %d ring for SG packet. Needed %d, currently available %d\n",
++                                         txdesc_ring->id, num_tx_desc_needed,
++                                         txdesc_ring->avail_desc);
++                              return EDMA_TX_FAIL_NO_DESC;
++                      }
++              }
++
++              txdesc = edma_tx_skb_first_desc(port_priv, txdesc_ring, skb,
++                                              &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No non-linear descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++
++              num_desc_filled = edma_tx_skb_sg_fill_desc(txdesc_ring,
++                                                         &txdesc, skb, &hw_next_to_use, stats);
++              if (unlikely(!txdesc)) {
++                      netdev_dbg(netdev, "No descriptor available for ring %d\n",
++                                 txdesc_ring->id);
++                      dma_unmap_single(dev, virt_to_phys(skb->data),
++                                       skb->len, DMA_TO_DEVICE);
++                      u64_stats_update_begin(&txdesc_stats->syncp);
++                      ++txdesc_stats->no_desc_avail;
++                      u64_stats_update_end(&txdesc_stats->syncp);
++                      return EDMA_TX_FAIL_NO_DESC;
++              }
++      }
++
++      /* Set the skb pointer to the descriptor's opaque field/s
++       * on the last descriptor of the packet/SG packet.
++       */
++      EDMA_TXDESC_OPAQUE_SET(txdesc, skb);
++
++      /* Update producer index. */
++      txdesc_ring->prod_idx = hw_next_to_use & EDMA_TXDESC_PROD_IDX_MASK;
++      txdesc_ring->avail_desc -= num_desc_filled;
++
++      netdev_dbg(netdev, "%s: skb:%p tx_ring:%u proto:0x%x skb->len:%d\n port:%u prod_idx:%u ip_summed:0x%x\n",
++                 netdev->name, skb, txdesc_ring->id, ntohs(skb->protocol),
++               skb->len, port_id, hw_next_to_use, skb->ip_summed);
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_TXDESC_PROD_IDX(txdesc_ring->id);
++      regmap_write(regmap, reg, txdesc_ring->prod_idx);
++
++      u64_stats_update_begin(&stats->syncp);
++      stats->tx_pkts++;
++      stats->tx_bytes += skb->len;
++      u64_stats_update_end(&stats->syncp);
++
++      return EDMA_TX_OK;
++}
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
+@@ -0,0 +1,302 @@
++/* SPDX-License-Identifier: GPL-2.0-only
++ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++#ifndef __EDMA_TX__
++#define __EDMA_TX__
++
++#include "edma_port.h"
++
++#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_TXCMPL_DESC(R, i)                EDMA_GET_DESC(R, i, \
++                                              struct edma_txcmpl_desc)
++#define EDMA_TXDESC_PRI_DESC(R, i)    EDMA_GET_PDESC(R, i, \
++                                              struct edma_txdesc_pri)
++#define EDMA_TXDESC_SEC_DESC(R, i)    EDMA_GET_SDESC(R, i, \
++                                              struct edma_txdesc_sec)
++
++#define EDMA_DESC_AVAIL_COUNT(head, tail, _max) ({ \
++                      typeof(_max) (max) = (_max); \
++                      ((((head) - (tail)) + \
++                      (max)) & ((max) - 1)); })
++
++#define EDMA_TX_RING_SIZE               2048
++#define EDMA_TX_RING_SIZE_MASK                (EDMA_TX_RING_SIZE - 1)
++
++/* Max segment processing capacity of HW for TSO. */
++#define EDMA_TX_TSO_SEG_MAX           32
++
++/* HW defined low and high MSS size. */
++#define EDMA_TX_TSO_MSS_MIN           256
++#define EDMA_TX_TSO_MSS_MAX           10240
++
++#define EDMA_DST_PORT_TYPE            2
++#define EDMA_DST_PORT_TYPE_SHIFT      28
++#define EDMA_DST_PORT_TYPE_MASK               (0xf << EDMA_DST_PORT_TYPE_SHIFT)
++#define EDMA_DST_PORT_ID_SHIFT                16
++#define EDMA_DST_PORT_ID_MASK         (0xfff << EDMA_DST_PORT_ID_SHIFT)
++
++#define EDMA_DST_PORT_TYPE_SET(x)     (((x) << EDMA_DST_PORT_TYPE_SHIFT) & \
++                                                      EDMA_DST_PORT_TYPE_MASK)
++#define EDMA_DST_PORT_ID_SET(x)               (((x) << EDMA_DST_PORT_ID_SHIFT) & \
++                                                      EDMA_DST_PORT_ID_MASK)
++#define EDMA_DST_INFO_SET(desc, x)    ((desc)->word4 |= \
++      (EDMA_DST_PORT_TYPE_SET(EDMA_DST_PORT_TYPE) | EDMA_DST_PORT_ID_SET(x)))
++
++#define EDMA_TXDESC_TSO_ENABLE_MASK           BIT(24)
++#define EDMA_TXDESC_TSO_ENABLE_SET(desc, x)   ((desc)->word5 |= \
++                              FIELD_PREP(EDMA_TXDESC_TSO_ENABLE_MASK, x))
++#define EDMA_TXDESC_MSS_MASK                  GENMASK(31, 16)
++#define EDMA_TXDESC_MSS_SET(desc, x)          ((desc)->word6 |= \
++                                      FIELD_PREP(EDMA_TXDESC_MSS_MASK, x))
++#define EDMA_TXDESC_MORE_BIT_MASK     BIT(30)
++#define EDMA_TXDESC_MORE_BIT_SET(desc, x)     ((desc)->word1 |= \
++                              FIELD_PREP(EDMA_TXDESC_MORE_BIT_MASK, x))
++
++#define EDMA_TXDESC_ADV_OFFSET_BIT    BIT(31)
++#define EDMA_TXDESC_ADV_OFFLOAD_SET(desc)     ((desc)->word5 |= \
++                                      FIELD_PREP(EDMA_TXDESC_ADV_OFFSET_BIT, 1))
++#define EDMA_TXDESC_IP_CSUM_BIT               BIT(25)
++#define EDMA_TXDESC_IP_CSUM_SET(desc)         ((desc)->word5 |= \
++                                      FIELD_PREP(EDMA_TXDESC_IP_CSUM_BIT, 1))
++
++#define EDMA_TXDESC_L4_CSUM_SET_MASK   GENMASK(27, 26)
++#define EDMA_TXDESC_L4_CSUM_SET(desc)  ((desc)->word5 |= \
++                             (FIELD_PREP(EDMA_TXDESC_L4_CSUM_SET_MASK, 1)))
++
++#define EDMA_TXDESC_POOL_ID_SET_MASK  GENMASK(24, 18)
++#define EDMA_TXDESC_POOL_ID_SET(desc, x)      ((desc)->word5 |= \
++                              (FIELD_PREP(EDMA_TXDESC_POOL_ID_SET_MASK, x)))
++
++#define EDMA_TXDESC_DATA_LEN_SET(desc, x)     ((desc)->word5 |= ((x) & 0x1ffff))
++#define EDMA_TXDESC_SERVICE_CODE_MASK GENMASK(24, 16)
++#define EDMA_TXDESC_SERVICE_CODE_SET(desc, x) ((desc)->word1 |= \
++                              (FIELD_PREP(EDMA_TXDESC_SERVICE_CODE_MASK, x)))
++#define EDMA_TXDESC_BUFFER_ADDR_SET(desc, addr)       (((desc)->word0) = (addr))
++
++#ifdef __LP64__
++#define EDMA_TXDESC_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      (((u64)(desc)->word3 << 32) | (desc)->word2); })
++
++#define EDMA_TXCMPL_OPAQUE_GET(_desc) ({ \
++                      typeof(_desc) (desc) = (_desc); \
++                      (((u64)(desc)->word1 << 32) | \
++                      (desc)->word0); })
++
++#define EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)  ((desc)->word2 = \
++                                              (u32)(uintptr_t)(ptr))
++
++#define EDMA_TXDESC_OPAQUE_HI_SET(desc, ptr)  ((desc)->word3 = \
++                                              (u32)((u64)(ptr) >> 32))
++
++#define EDMA_TXDESC_OPAQUE_SET(_desc, _ptr)   do { \
++      typeof(_desc) (desc) = (_desc); \
++      typeof(_ptr) (ptr) = (_ptr); \
++      EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr); \
++      EDMA_TXDESC_OPAQUE_HI_SET(desc, ptr); \
++} while (0)
++#else
++#define EDMA_TXCMPL_OPAQUE_GET(desc)          ((desc)->word0)
++#define EDMA_TXDESC_OPAQUE_GET(desc)          ((desc)->word2)
++#define EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)  ((desc)->word2 = (u32)(uintptr_t)ptr)
++
++#define EDMA_TXDESC_OPAQUE_SET(desc, ptr)     \
++                                      EDMA_TXDESC_OPAQUE_LO_SET(desc, ptr)
++#endif
++#define EDMA_TXCMPL_MORE_BIT_MASK     BIT(30)
++
++#define EDMA_TXCMPL_MORE_BIT_GET(desc)        ((le32_to_cpu((__force __le32)((desc)->word2))) & \
++                                      EDMA_TXCMPL_MORE_BIT_MASK)
++
++#define EDMA_TXCOMP_RING_ERROR_MASK   GENMASK(22, 0)
++
++#define EDMA_TXCOMP_RING_ERROR_GET(x) ((le32_to_cpu((__force __le32)x)) & \
++                                      EDMA_TXCOMP_RING_ERROR_MASK)
++
++#define EDMA_TXCOMP_POOL_ID_MASK      GENMASK(5, 0)
++
++#define EDMA_TXCOMP_POOL_ID_GET(desc) ((le32_to_cpu((__force __le32)((desc)->word2))) & \
++                                      EDMA_TXCOMP_POOL_ID_MASK)
++
++/* Opaque values are set in word2 and word3,
++ * they are not accessed by the EDMA HW,
++ * so endianness conversion is not needed.
++ */
++#define EDMA_TXDESC_ENDIAN_SET(_desc) ({ \
++      typeof(_desc) (desc) = (_desc); \
++      cpu_to_le32s(&((desc)->word0)); \
++      cpu_to_le32s(&((desc)->word1)); \
++      cpu_to_le32s(&((desc)->word4)); \
++      cpu_to_le32s(&((desc)->word5)); \
++      cpu_to_le32s(&((desc)->word6)); \
++      cpu_to_le32s(&((desc)->word7)); \
++})
++
++/* EDMA Tx GSO status */
++enum edma_tx_status {
++      EDMA_TX_OK = 0,                 /* Tx success. */
++      EDMA_TX_FAIL_NO_DESC = 1,       /* Not enough descriptors. */
++      EDMA_TX_FAIL = 2,               /* Tx failure. */
++};
++
++/* EDMA TX GSO status */
++enum edma_tx_gso_status {
++      EDMA_TX_GSO_NOT_NEEDED = 0,
++              /* Packet has segment count less than TX_TSO_SEG_MAX. */
++      EDMA_TX_GSO_SUCCEED = 1,
++              /* GSO Succeed. */
++      EDMA_TX_GSO_FAIL = 2,
++              /* GSO failed, drop the packet. */
++};
++
++/**
++ * struct edma_txcmpl_stats - EDMA TX complete ring statistics.
++ * @invalid_buffer: Invalid buffer address received.
++ * @errors: Other Tx complete descriptor errors indicated by the hardware.
++ * @desc_with_more_bit: Packet's segment transmit count.
++ * @no_pending_desc: No descriptor is pending for processing.
++ * @syncp: Synchronization pointer.
++ */
++struct edma_txcmpl_stats {
++      u64 invalid_buffer;
++      u64 errors;
++      u64 desc_with_more_bit;
++      u64 no_pending_desc;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_txdesc_stats - EDMA Tx descriptor ring statistics.
++ * @no_desc_avail: No descriptor available to transmit.
++ * @tso_max_seg_exceed: Packets extending EDMA_TX_TSO_SEG_MAX segments.
++ * @syncp: Synchronization pointer.
++ */
++struct edma_txdesc_stats {
++      u64 no_desc_avail;
++      u64 tso_max_seg_exceed;
++      struct u64_stats_sync syncp;
++};
++
++/**
++ * struct edma_txdesc_pri - EDMA primary TX descriptor.
++ * @word0: Low 32-bit of buffer address.
++ * @word1: Buffer recycling, PTP tag flag, PRI valid flag.
++ * @word2: Low 32-bit of opaque value.
++ * @word3: High 32-bit of opaque value.
++ * @word4: Source/Destination port info.
++ * @word5: VLAN offload, csum mode, ip_csum_en, tso_en, data len.
++ * @word6: MSS/hash_value/PTP tag, data offset.
++ * @word7: L4/L3 offset, PROT type, L2 type, CVLAN/SVLAN tag, service code.
++ */
++struct edma_txdesc_pri {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++/**
++ * struct edma_txdesc_sec - EDMA secondary TX descriptor.
++ * @word0: Reserved.
++ * @word1: Custom csum offset, payload offset, TTL/NAT action.
++ * @word2: NAPT translated port, DSCP value, TTL value.
++ * @word3: Flow index value and valid flag.
++ * @word4: Reserved.
++ * @word5: Reserved.
++ * @word6: CVLAN/SVLAN command.
++ * @word7: CVLAN/SVLAN tag value.
++ */
++struct edma_txdesc_sec {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++      u32 word4;
++      u32 word5;
++      u32 word6;
++      u32 word7;
++};
++
++/**
++ * struct edma_txcmpl_desc - EDMA TX complete descriptor.
++ * @word0: Low 32-bit opaque value.
++ * @word1: High 32-bit opaque value.
++ * @word2: More fragment, transmit ring id, pool id.
++ * @word3: Error indications.
++ */
++struct edma_txcmpl_desc {
++      u32 word0;
++      u32 word1;
++      u32 word2;
++      u32 word3;
++};
++
++/**
++ * struct edma_txdesc_ring - EDMA TX descriptor ring
++ * @prod_idx: Producer index
++ * @id: Tx ring number
++ * @avail_desc: Number of available descriptor to process
++ * @pdesc: Primary descriptor ring virtual address
++ * @pdma: Primary descriptor ring physical address
++ * @sdesc: Secondary descriptor ring virtual address
++ * @tx_desc_stats: Tx descriptor ring statistics
++ * @sdma: Secondary descriptor ring physical address
++ * @count: Number of descriptors
++ * @fc_grp_id: Flow control group ID
++ */
++struct edma_txdesc_ring {
++      u32 prod_idx;
++      u32 id;
++      u32 avail_desc;
++      struct edma_txdesc_pri *pdesc;
++      dma_addr_t pdma;
++      struct edma_txdesc_sec *sdesc;
++      struct edma_txdesc_stats txdesc_stats;
++      dma_addr_t sdma;
++      u32 count;
++      u8 fc_grp_id;
++};
++
++/**
++ * struct edma_txcmpl_ring - EDMA TX complete ring
++ * @napi: NAPI
++ * @cons_idx: Consumer index
++ * @avail_pkt: Number of available packets to process
++ * @desc: Descriptor ring virtual address
++ * @id: Txcmpl ring number
++ * @tx_cmpl_stats: Tx complete ring statistics
++ * @dma: Descriptor ring physical address
++ * @count: Number of descriptors in the ring
++ * @napi_added: Flag to indicate NAPI add status
++ */
++struct edma_txcmpl_ring {
++      struct napi_struct napi;
++      u32 cons_idx;
++      u32 avail_pkt;
++      struct edma_txcmpl_desc *desc;
++      u32 id;
++      struct edma_txcmpl_stats txcmpl_stats;
++      dma_addr_t dma;
++      u32 count;
++      bool napi_added;
++};
++
++enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
++                                    struct sk_buff *skb,
++                             struct edma_txdesc_ring *txdesc_ring,
++                             struct edma_port_tx_stats *stats);
++u32 edma_tx_complete(u32 work_to_do,
++                   struct edma_txcmpl_ring *txcmpl_ring);
++irqreturn_t edma_tx_handle_irq(int irq, void *ctx);
++int edma_tx_napi_poll(struct napi_struct *napi, int budget);
++enum edma_tx_gso_status edma_tx_gso_segment(struct sk_buff *skb,
++                                          struct net_device *netdev, struct sk_buff **segs);
++
++#endif
diff --git a/target/linux/qualcommbe/patches-6.12/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch b/target/linux/qualcommbe/patches-6.12/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch
new file mode 100644 (file)
index 0000000..0bdfb0c
--- /dev/null
@@ -0,0 +1,730 @@
+From 8a924457c0b71acee96c8f78ef386e2a354a2aca Mon Sep 17 00:00:00 2001
+From: Suruchi Agarwal <quic_suruchia@quicinc.com>
+Date: Thu, 21 Mar 2024 16:31:04 -0700
+Subject: [PATCH] net: ethernet: qualcomm: Add miscellaneous error interrupts
+ and counters
+
+Miscellaneous error interrupts, EDMA Tx/Rx and error counters are supported
+using debugfs framework.
+
+Change-Id: I7da8b978a7e93947b03a45269a81b401f35da31c
+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      | 162 ++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  30 ++
+ .../net/ethernet/qualcomm/ppe/edma_debugfs.c  | 370 ++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/ppe_debugfs.c   |  17 +
+ 5 files changed, 580 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_debugfs.c
+
+--- 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_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_port.o edma_rx.o edma_tx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -152,6 +152,42 @@ static int edma_clock_init(void)
+ }
+ /**
++ * edma_err_stats_alloc - Allocate stats memory
++ *
++ * Allocate memory for per-CPU error stats.
++ */
++int edma_err_stats_alloc(void)
++{
++      u32 i;
++
++      edma_ctx->err_stats = alloc_percpu(*edma_ctx->err_stats);
++      if (!edma_ctx->err_stats)
++              return -ENOMEM;
++
++      for_each_possible_cpu(i) {
++              struct edma_err_stats *stats;
++
++              stats = per_cpu_ptr(edma_ctx->err_stats, i);
++              u64_stats_init(&stats->syncp);
++      }
++
++      return 0;
++}
++
++/**
++ * edma_err_stats_free - Free stats memory
++ *
++ * Free memory of per-CPU error stats.
++ */
++void edma_err_stats_free(void)
++{
++      if (edma_ctx->err_stats) {
++              free_percpu(edma_ctx->err_stats);
++              edma_ctx->err_stats = NULL;
++      }
++}
++
++/**
+  * edma_configure_ucast_prio_map_tbl - Configure unicast priority map table.
+  *
+  * Map int_priority values to priority class and initialize
+@@ -191,11 +227,113 @@ static int edma_configure_ucast_prio_map
+       return ret;
+ }
++static void edma_disable_misc_interrupt(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_MASK_ADDR;
++      regmap_write(regmap, reg, EDMA_MASK_INT_CLEAR);
++}
++
++static void edma_enable_misc_interrupt(void)
++{
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 reg;
++
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_MASK_ADDR;
++      regmap_write(regmap, reg, edma_ctx->intr_info.intr_mask_misc);
++}
++
++static irqreturn_t edma_misc_handle_irq(int irq,
++                                      __maybe_unused void *ctx)
++{
++      struct edma_err_stats *stats = this_cpu_ptr(edma_ctx->err_stats);
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
++      struct regmap *regmap = ppe_dev->regmap;
++      u32 misc_intr_status, data, reg;
++
++      /* Read Misc intr status */
++      reg = EDMA_BASE_OFFSET + EDMA_REG_MISC_INT_STAT_ADDR;
++      regmap_read(regmap, reg, &data);
++      misc_intr_status = data & edma_ctx->intr_info.intr_mask_misc;
++
++      pr_debug("Received misc irq %d, status: %d\n", irq, misc_intr_status);
++
++      if (FIELD_GET(EDMA_MISC_AXI_RD_ERR_MASK, misc_intr_status)) {
++              pr_err("MISC AXI read error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_axi_read_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_AXI_WR_ERR_MASK, misc_intr_status)) {
++              pr_err("MISC AXI write error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_axi_write_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_RX_DESC_FIFO_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Rx descriptor fifo full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_rxdesc_fifo_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_RX_ERR_BUF_SIZE_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Rx buffer size error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_rx_buf_size_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_SRAM_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx SRAM full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_sram_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_CMPL_BUF_FULL_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx complete buffer full error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_txcmpl_buf_full;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_DATA_LEN_ERR_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC data length error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_data_len_err;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      if (FIELD_GET(EDMA_MISC_TX_TIMEOUT_MASK, misc_intr_status)) {
++              if (net_ratelimit())
++                      pr_err("MISC Tx timeout error received\n");
++              u64_stats_update_begin(&stats->syncp);
++              ++stats->edma_tx_timeout;
++              u64_stats_update_end(&stats->syncp);
++      }
++
++      return IRQ_HANDLED;
++}
++
+ static int edma_irq_register(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+       struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct ppe_device *ppe_dev = edma_ctx->ppe_dev;
+       struct edma_ring_info *rx = hw_info->rx;
++      struct device *dev = ppe_dev->dev;
+       int ret;
+       u32 i;
+@@ -270,8 +408,25 @@ static int edma_irq_register(void)
+                        edma_rxdesc_irq_name[i]);
+       }
++      /* Request Misc IRQ */
++      ret = request_irq(edma_ctx->intr_info.intr_misc, edma_misc_handle_irq,
++                        IRQF_SHARED, "edma_misc",
++                        (void *)dev);
++      if (ret) {
++              pr_err("MISC IRQ:%d request failed\n",
++                     edma_ctx->intr_info.intr_misc);
++              goto misc_intr_req_fail;
++      }
++
+       return 0;
++misc_intr_req_fail:
++      /* 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]);
++      }
+ rx_desc_ring_intr_req_fail:
+       for (i = 0; i < rx->num_rings; i++)
+               kfree(edma_rxdesc_irq_name[i]);
+@@ -503,6 +658,7 @@ static int edma_hw_configure(void)
+               edma_cfg_tx_disable_interrupts(i);
+       edma_cfg_rx_disable_interrupts();
++      edma_disable_misc_interrupt();
+       edma_cfg_rx_rings_disable();
+@@ -614,6 +770,7 @@ void edma_destroy(struct ppe_device *ppe
+               edma_cfg_tx_disable_interrupts(i);
+       edma_cfg_rx_disable_interrupts();
++      edma_disable_misc_interrupt();
+       /* Free IRQ for TXCMPL rings. */
+       for (i = 0; i < txcmpl->num_rings; i++) {
+@@ -634,6 +791,10 @@ void edma_destroy(struct ppe_device *ppe
+       }
+       kfree(edma_rxdesc_irq_name);
++      /* Free Misc IRQ */
++      synchronize_irq(edma_ctx->intr_info.intr_misc);
++      free_irq(edma_ctx->intr_info.intr_misc, (void *)(ppe_dev->dev));
++
+       kfree(edma_ctx->intr_info.intr_rx);
+       kfree(edma_ctx->intr_info.intr_txcmpl);
+@@ -699,6 +860,7 @@ int edma_setup(struct ppe_device *ppe_de
+       }
+       edma_cfg_rx_enable_interrupts();
++      edma_enable_misc_interrupt();
+       dev_info(dev, "EDMA configuration successful\n");
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -47,6 +47,30 @@ enum ppe_queue_class_type {
+ };
+ /**
++ * struct edma_err_stats - EDMA error stats
++ * @edma_axi_read_err: AXI read error
++ * @edma_axi_write_err: AXI write error
++ * @edma_rxdesc_fifo_full: Rx desc FIFO full error
++ * @edma_rx_buf_size_err: Rx buffer size too small error
++ * @edma_tx_sram_full: Tx packet SRAM buffer full error
++ * @edma_tx_data_len_err: Tx data length error
++ * @edma_tx_timeout: Tx timeout error
++ * @edma_txcmpl_buf_full: Tx completion buffer full error
++ * @syncp: Synchronization pointer
++ */
++struct edma_err_stats {
++      u64 edma_axi_read_err;
++      u64 edma_axi_write_err;
++      u64 edma_rxdesc_fifo_full;
++      u64 edma_rx_buf_size_err;
++      u64 edma_tx_sram_full;
++      u64 edma_tx_data_len_err;
++      u64 edma_tx_timeout;
++      u64 edma_txcmpl_buf_full;
++      struct u64_stats_sync syncp;
++};
++
++/**
+  * struct edma_ring_info - EDMA ring data structure.
+  * @max_rings: Maximum number of rings
+  * @ring_start: Ring start ID
+@@ -107,6 +131,7 @@ struct edma_intr_info {
+  * @rx_rings: Rx Desc Rings, SW is consumer
+  * @tx_rings: Tx Descriptor Ring, SW is producer
+  * @txcmpl_rings: Tx complete Ring, SW is consumer
++ * @err_stats: Per CPU error statistics
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
+  * @tx_requeue_stop: Tx requeue stop enabled or disabled
+@@ -121,6 +146,7 @@ struct edma_context {
+       struct edma_rxdesc_ring *rx_rings;
+       struct edma_txdesc_ring *tx_rings;
+       struct edma_txcmpl_ring *txcmpl_rings;
++      struct edma_err_stats __percpu *err_stats;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
+       bool tx_requeue_stop;
+@@ -129,8 +155,12 @@ struct edma_context {
+ /* Global EDMA context */
+ extern struct edma_context *edma_ctx;
++int edma_err_stats_alloc(void);
++void edma_err_stats_free(void);
+ void edma_destroy(struct ppe_device *ppe_dev);
+ int edma_setup(struct ppe_device *ppe_dev);
++void edma_debugfs_teardown(void);
++int edma_debugfs_setup(struct ppe_device *ppe_dev);
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_debugfs.c
+@@ -0,0 +1,370 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* EDMA debugfs routines for display of Tx/Rx counters. */
++
++#include <linux/cpumask.h>
++#include <linux/debugfs.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/printk.h>
++
++#include "edma.h"
++
++#define EDMA_STATS_BANNER_MAX_LEN       80
++#define EDMA_RX_RING_STATS_NODE_NAME    "EDMA_RX"
++#define EDMA_TX_RING_STATS_NODE_NAME    "EDMA_TX"
++#define EDMA_ERR_STATS_NODE_NAME        "EDMA_ERR"
++
++static struct dentry *edma_dentry;
++static struct dentry *stats_dentry;
++
++static void edma_debugfs_print_banner(struct seq_file *m, char *node)
++{
++      u32 banner_char_len, i;
++
++      for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++)
++              seq_puts(m, "_");
++      banner_char_len = (EDMA_STATS_BANNER_MAX_LEN - (strlen(node) + 2)) / 2;
++      seq_puts(m, "\n\n");
++
++      for (i = 0; i < banner_char_len; i++)
++              seq_puts(m, "<");
++      seq_printf(m, " %s ", node);
++
++      for (i = 0; i < banner_char_len; i++)
++              seq_puts(m, ">");
++      seq_puts(m, "\n");
++
++      for (i = 0; i < EDMA_STATS_BANNER_MAX_LEN; i++)
++              seq_puts(m, "_");
++      seq_puts(m, "\n\n");
++}
++
++static int edma_debugfs_rx_rings_stats_show(struct seq_file *m,
++                                          void __maybe_unused *p)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *rxfill = hw_info->rxfill;
++      struct edma_rxfill_stats *rxfill_stats;
++      struct edma_rxdesc_stats *rxdesc_stats;
++      struct edma_ring_info *rx = hw_info->rx;
++      unsigned int start;
++      u32 i;
++
++      rxfill_stats = kcalloc(rxfill->num_rings, sizeof(*rxfill_stats), GFP_KERNEL);
++      if (!rxfill_stats)
++              return -ENOMEM;
++
++      rxdesc_stats = kcalloc(rx->num_rings, sizeof(*rxdesc_stats), GFP_KERNEL);
++      if (!rxdesc_stats) {
++              kfree(rxfill_stats);
++              return -ENOMEM;
++      }
++
++      /* Get stats for Rx fill rings. */
++      for (i = 0; i < rxfill->num_rings; i++) {
++              struct edma_rxfill_ring *rxfill_ring;
++              struct edma_rxfill_stats *stats;
++
++              rxfill_ring = &edma_ctx->rxfill_rings[i];
++              stats = &rxfill_ring->rxfill_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      rxfill_stats[i].alloc_failed = stats->alloc_failed;
++                      rxfill_stats[i].page_alloc_failed = stats->page_alloc_failed;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      /* Get stats for Rx Desc rings. */
++      for (i = 0; i < rx->num_rings; i++) {
++              struct edma_rxdesc_ring *rxdesc_ring;
++              struct edma_rxdesc_stats *stats;
++
++              rxdesc_ring = &edma_ctx->rx_rings[i];
++              stats = &rxdesc_ring->rxdesc_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      rxdesc_stats[i].src_port_inval = stats->src_port_inval;
++                      rxdesc_stats[i].src_port_inval_type = stats->src_port_inval_type;
++                      rxdesc_stats[i].src_port_inval_netdev = stats->src_port_inval_netdev;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_RX_RING_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA RX descriptor rings stats:\n\n");
++      for (i = 0; i < rx->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA RX descriptor %d ring stats:\n", i + rx->ring_start);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval = %llu\n",
++                         i + rx->ring_start, rxdesc_stats[i].src_port_inval);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_type = %llu\n",
++                         i + rx->ring_start, rxdesc_stats[i].src_port_inval_type);
++              seq_printf(m, "\t\t rxdesc[%d]:src_port_inval_netdev = %llu\n",
++                         i + rx->ring_start,
++                         rxdesc_stats[i].src_port_inval_netdev);
++              seq_puts(m, "\n");
++      }
++
++      seq_puts(m, "\n#EDMA RX fill rings stats:\n\n");
++      for (i = 0; i < rxfill->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA RX fill %d ring stats:\n", i + rxfill->ring_start);
++              seq_printf(m, "\t\t rxfill[%d]:alloc_failed = %llu\n",
++                         i + rxfill->ring_start, rxfill_stats[i].alloc_failed);
++              seq_printf(m, "\t\t rxfill[%d]:page_alloc_failed = %llu\n",
++                         i + rxfill->ring_start, rxfill_stats[i].page_alloc_failed);
++              seq_puts(m, "\n");
++      }
++
++      kfree(rxfill_stats);
++      kfree(rxdesc_stats);
++      return 0;
++}
++
++static int edma_debugfs_tx_rings_stats_show(struct seq_file *m,
++                                          void __maybe_unused *p)
++{
++      struct edma_hw_info *hw_info = edma_ctx->hw_info;
++      struct edma_ring_info *txcmpl = hw_info->txcmpl;
++      struct edma_ring_info *tx = hw_info->tx;
++      struct edma_txcmpl_stats *txcmpl_stats;
++      struct edma_txdesc_stats *txdesc_stats;
++      unsigned int start;
++      u32 i;
++
++      txcmpl_stats = kcalloc(txcmpl->num_rings, sizeof(*txcmpl_stats), GFP_KERNEL);
++      if (!txcmpl_stats)
++              return -ENOMEM;
++
++      txdesc_stats = kcalloc(tx->num_rings, sizeof(*txdesc_stats), GFP_KERNEL);
++      if (!txdesc_stats) {
++              kfree(txcmpl_stats);
++              return -ENOMEM;
++      }
++
++      /* Get stats for Tx desc rings. */
++      for (i = 0; i < tx->num_rings; i++) {
++              struct edma_txdesc_ring *txdesc_ring;
++              struct edma_txdesc_stats *stats;
++
++              txdesc_ring = &edma_ctx->tx_rings[i];
++              stats = &txdesc_ring->txdesc_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      txdesc_stats[i].no_desc_avail = stats->no_desc_avail;
++                      txdesc_stats[i].tso_max_seg_exceed = stats->tso_max_seg_exceed;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      /* Get stats for Tx Complete rings. */
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              struct edma_txcmpl_ring *txcmpl_ring;
++              struct edma_txcmpl_stats *stats;
++
++              txcmpl_ring = &edma_ctx->txcmpl_rings[i];
++              stats = &txcmpl_ring->txcmpl_stats;
++              do {
++                      start = u64_stats_fetch_begin(&stats->syncp);
++                      txcmpl_stats[i].invalid_buffer = stats->invalid_buffer;
++                      txcmpl_stats[i].errors = stats->errors;
++                      txcmpl_stats[i].desc_with_more_bit = stats->desc_with_more_bit;
++                      txcmpl_stats[i].no_pending_desc = stats->no_pending_desc;
++              } while (u64_stats_fetch_retry(&stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_TX_RING_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA TX complete rings stats:\n\n");
++      for (i = 0; i < txcmpl->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA TX complete %d ring stats:\n", i + txcmpl->ring_start);
++              seq_printf(m, "\t\t txcmpl[%d]:invalid_buffer = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].invalid_buffer);
++              seq_printf(m, "\t\t txcmpl[%d]:errors = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].errors);
++              seq_printf(m, "\t\t txcmpl[%d]:desc_with_more_bit = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].desc_with_more_bit);
++              seq_printf(m, "\t\t txcmpl[%d]:no_pending_desc = %llu\n",
++                         i + txcmpl->ring_start, txcmpl_stats[i].no_pending_desc);
++              seq_puts(m, "\n");
++      }
++
++      seq_puts(m, "\n#EDMA TX descriptor rings stats:\n\n");
++      for (i = 0; i < tx->num_rings; i++) {
++              seq_printf(m, "\t\tEDMA TX descriptor %d ring stats:\n", i + tx->ring_start);
++              seq_printf(m, "\t\t txdesc[%d]:no_desc_avail = %llu\n",
++                         i + tx->ring_start, txdesc_stats[i].no_desc_avail);
++              seq_printf(m, "\t\t txdesc[%d]:tso_max_seg_exceed = %llu\n",
++                         i + tx->ring_start, txdesc_stats[i].tso_max_seg_exceed);
++              seq_puts(m, "\n");
++      }
++
++      kfree(txcmpl_stats);
++      kfree(txdesc_stats);
++      return 0;
++}
++
++static int edma_debugfs_err_stats_show(struct seq_file *m,
++                                     void __maybe_unused *p)
++{
++      struct edma_err_stats *err_stats, *pcpu_err_stats;
++      unsigned int start;
++      u32 cpu;
++
++      err_stats = kzalloc(sizeof(*err_stats), GFP_KERNEL);
++      if (!err_stats)
++              return -ENOMEM;
++
++      /* Get percpu EDMA miscellaneous stats. */
++      for_each_possible_cpu(cpu) {
++              pcpu_err_stats = per_cpu_ptr(edma_ctx->err_stats, cpu);
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_err_stats->syncp);
++                      err_stats->edma_axi_read_err +=
++                              pcpu_err_stats->edma_axi_read_err;
++                      err_stats->edma_axi_write_err +=
++                              pcpu_err_stats->edma_axi_write_err;
++                      err_stats->edma_rxdesc_fifo_full +=
++                              pcpu_err_stats->edma_rxdesc_fifo_full;
++                      err_stats->edma_rx_buf_size_err +=
++                              pcpu_err_stats->edma_rx_buf_size_err;
++                      err_stats->edma_tx_sram_full +=
++                              pcpu_err_stats->edma_tx_sram_full;
++                      err_stats->edma_tx_data_len_err +=
++                              pcpu_err_stats->edma_tx_data_len_err;
++                      err_stats->edma_tx_timeout +=
++                              pcpu_err_stats->edma_tx_timeout;
++                      err_stats->edma_txcmpl_buf_full +=
++                              pcpu_err_stats->edma_txcmpl_buf_full;
++              } while (u64_stats_fetch_retry(&pcpu_err_stats->syncp, start));
++      }
++
++      edma_debugfs_print_banner(m, EDMA_ERR_STATS_NODE_NAME);
++
++      seq_puts(m, "\n#EDMA error stats:\n\n");
++      seq_printf(m, "\t\t axi read error = %llu\n",
++                 err_stats->edma_axi_read_err);
++      seq_printf(m, "\t\t axi write error = %llu\n",
++                 err_stats->edma_axi_write_err);
++      seq_printf(m, "\t\t Rx descriptor fifo full = %llu\n",
++                 err_stats->edma_rxdesc_fifo_full);
++      seq_printf(m, "\t\t Rx buffer size error = %llu\n",
++                 err_stats->edma_rx_buf_size_err);
++      seq_printf(m, "\t\t Tx SRAM full = %llu\n",
++                 err_stats->edma_tx_sram_full);
++      seq_printf(m, "\t\t Tx data length error = %llu\n",
++                 err_stats->edma_tx_data_len_err);
++      seq_printf(m, "\t\t Tx timeout = %llu\n",
++                 err_stats->edma_tx_timeout);
++      seq_printf(m, "\t\t Tx completion buffer full = %llu\n",
++                 err_stats->edma_txcmpl_buf_full);
++
++      kfree(err_stats);
++      return 0;
++}
++
++static int edma_debugs_rx_rings_stats_open(struct inode *inode,
++                                         struct file *file)
++{
++      return single_open(file, edma_debugfs_rx_rings_stats_show,
++                         inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_rx_rings_file_ops = {
++      .open = edma_debugs_rx_rings_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++static int edma_debugs_tx_rings_stats_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, edma_debugfs_tx_rings_stats_show, inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_tx_rings_file_ops = {
++      .open = edma_debugs_tx_rings_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++static int edma_debugs_err_stats_open(struct inode *inode, struct file *file)
++{
++      return single_open(file, edma_debugfs_err_stats_show, inode->i_private);
++}
++
++static const struct file_operations edma_debugfs_misc_file_ops = {
++      .open = edma_debugs_err_stats_open,
++      .read = seq_read,
++      .llseek = seq_lseek,
++      .release = seq_release
++};
++
++/**
++ * edma_debugfs_teardown - EDMA debugfs teardown.
++ *
++ * EDMA debugfs teardown and free stats memory.
++ */
++void edma_debugfs_teardown(void)
++{
++      /* Free EDMA miscellaneous stats memory */
++      edma_err_stats_free();
++
++      debugfs_remove_recursive(edma_dentry);
++      edma_dentry = NULL;
++      stats_dentry = NULL;
++}
++
++/**
++ * edma_debugfs_setup - EDMA debugfs setup.
++ * @ppe_dev: PPE Device
++ *
++ * EDMA debugfs setup.
++ */
++int edma_debugfs_setup(struct ppe_device *ppe_dev)
++{
++      edma_dentry = debugfs_create_dir("edma", ppe_dev->debugfs_root);
++      if (!edma_dentry) {
++              pr_err("Unable to create debugfs edma directory in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      stats_dentry = debugfs_create_dir("stats", edma_dentry);
++      if (!stats_dentry) {
++              pr_err("Unable to create debugfs stats directory in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("rx_ring_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_rx_rings_file_ops)) {
++              pr_err("Unable to create Rx rings statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("tx_ring_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_tx_rings_file_ops)) {
++              pr_err("Unable to create Tx rings statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      /* Allocate memory for EDMA miscellaneous stats */
++      if (edma_err_stats_alloc() < 0) {
++              pr_err("Unable to allocate miscellaneous percpu stats\n");
++              goto debugfs_dir_failed;
++      }
++
++      if (!debugfs_create_file("err_stats", 0444, stats_dentry,
++                               NULL, &edma_debugfs_misc_file_ops)) {
++              pr_err("Unable to create EDMA miscellaneous statistics file entry in debugfs\n");
++              goto debugfs_dir_failed;
++      }
++
++      return 0;
++
++debugfs_dir_failed:
++      debugfs_remove_recursive(edma_dentry);
++      edma_dentry = NULL;
++      stats_dentry = NULL;
++      return -ENOMEM;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
++++ b/drivers/net/ethernet/qualcomm/ppe/ppe_debugfs.c
+@@ -7,9 +7,11 @@
+ #include <linux/bitfield.h>
+ #include <linux/debugfs.h>
++#include <linux/netdevice.h>
+ #include <linux/regmap.h>
+ #include <linux/seq_file.h>
++#include "edma.h"
+ #include "ppe.h"
+ #include "ppe_config.h"
+ #include "ppe_debugfs.h"
+@@ -678,15 +680,30 @@ static const struct file_operations ppe_
+ void ppe_debugfs_setup(struct ppe_device *ppe_dev)
+ {
++      int ret;
++
+       ppe_dev->debugfs_root = debugfs_create_dir("ppe", NULL);
+       debugfs_create_file("packet_counters", 0444,
+                           ppe_dev->debugfs_root,
+                           ppe_dev,
+                           &ppe_debugfs_packet_counter_fops);
++
++      if (!ppe_dev->debugfs_root) {
++              dev_err(ppe_dev->dev, "Error in PPE debugfs setup\n");
++              return;
++      }
++
++      ret = edma_debugfs_setup(ppe_dev);
++      if (ret) {
++              dev_err(ppe_dev->dev, "Error in EDMA debugfs setup API. ret: %d\n", ret);
++              debugfs_remove_recursive(ppe_dev->debugfs_root);
++              ppe_dev->debugfs_root = NULL;
++      }
+ }
+ void ppe_debugfs_teardown(struct ppe_device *ppe_dev)
+ {
++      edma_debugfs_teardown();
+       debugfs_remove_recursive(ppe_dev->debugfs_root);
+       ppe_dev->debugfs_root = NULL;
+ }
diff --git a/target/linux/qualcommbe/patches-6.12/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch b/target/linux/qualcommbe/patches-6.12/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch
new file mode 100644 (file)
index 0000000..4e0103d
--- /dev/null
@@ -0,0 +1,344 @@
+From bd61a680fb657eb65272225f18c93fe338c700da Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Thu, 30 May 2024 20:46:36 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add ethtool support for EDMA
+
+ethtool ops can be used for EDMA netdevice configuration and statistics.
+
+Change-Id: I57fc19415dacbe51fed000520336463938220609
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Alex G: use struct ethtool_keee instead of ethtool_eee
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/Makefile    |   2 +-
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |   1 +
+ .../net/ethernet/qualcomm/ppe/edma_ethtool.c  | 294 ++++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c |   1 +
+ 4 files changed, 297 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
+
+--- 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_debugfs.o ppe_port.o
+ #EDMA
+-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o
++qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o edma_ethtool.o
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -161,6 +161,7 @@ void edma_destroy(struct ppe_device *ppe
+ int edma_setup(struct ppe_device *ppe_dev);
+ void edma_debugfs_teardown(void);
+ int edma_debugfs_setup(struct ppe_device *ppe_dev);
++void edma_set_ethtool_ops(struct net_device *netdev);
+ int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
+                                enum ppe_queue_class_type class,
+                                int index, int queue_offset);
+--- /dev/null
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
+@@ -0,0 +1,294 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
++ */
++
++/* ethtool support for EDMA */
++
++#include <linux/cpumask.h>
++#include <linux/ethtool.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/phylink.h>
++
++#include "edma.h"
++#include "edma_port.h"
++
++struct edma_ethtool_stats {
++      u8 stat_string[ETH_GSTRING_LEN];
++      u32 stat_offset;
++};
++
++/**
++ * struct edma_gmac_stats - Per-GMAC statistics.
++ * @rx_packets: Number of RX packets
++ * @rx_bytes: Number of RX bytes
++ * @rx_dropped: Number of RX dropped packets
++ * @rx_fraglist_packets: Number of RX fraglist packets
++ * @rx_nr_frag_packets: Number of RX nr fragment packets
++ * @rx_nr_frag_headroom_err: Number of RX nr fragment packets with headroom error
++ * @tx_packets: Number of TX packets
++ * @tx_bytes: Number of TX bytes
++ * @tx_dropped: Number of TX dropped packets
++ * @tx_nr_frag_packets: Number of TX nr fragment packets
++ * @tx_fraglist_packets: Number of TX fraglist packets
++ * @tx_fraglist_with_nr_frags_packets: Number of TX fraglist packets with nr fragments
++ * @tx_tso_packets: Number of TX TCP segmentation offload packets
++ * @tx_tso_drop_packets: Number of TX TCP segmentation dropped packets
++ * @tx_gso_packets: Number of TX SW GSO packets
++ * @tx_gso_drop_packets: Number of TX SW GSO dropped packets
++ * @tx_queue_stopped: Number of times Queue got stopped
++ */
++struct edma_gmac_stats {
++      u64 rx_packets;
++      u64 rx_bytes;
++      u64 rx_dropped;
++      u64 rx_fraglist_packets;
++      u64 rx_nr_frag_packets;
++      u64 rx_nr_frag_headroom_err;
++      u64 tx_packets;
++      u64 tx_bytes;
++      u64 tx_dropped;
++      u64 tx_nr_frag_packets;
++      u64 tx_fraglist_packets;
++      u64 tx_fraglist_with_nr_frags_packets;
++      u64 tx_tso_packets;
++      u64 tx_tso_drop_packets;
++      u64 tx_gso_packets;
++      u64 tx_gso_drop_packets;
++      u64 tx_queue_stopped[EDMA_MAX_CORE];
++};
++
++#define EDMA_STAT(m)          offsetof(struct edma_gmac_stats, m)
++
++static const struct edma_ethtool_stats edma_gstrings_stats[] = {
++      {"rx_bytes", EDMA_STAT(rx_bytes)},
++      {"rx_packets", EDMA_STAT(rx_packets)},
++      {"rx_dropped", EDMA_STAT(rx_dropped)},
++      {"rx_fraglist_packets", EDMA_STAT(rx_fraglist_packets)},
++      {"rx_nr_frag_packets", EDMA_STAT(rx_nr_frag_packets)},
++      {"rx_nr_frag_headroom_err", EDMA_STAT(rx_nr_frag_headroom_err)},
++      {"tx_bytes", EDMA_STAT(tx_bytes)},
++      {"tx_packets", EDMA_STAT(tx_packets)},
++      {"tx_dropped", EDMA_STAT(tx_dropped)},
++      {"tx_nr_frag_packets", EDMA_STAT(tx_nr_frag_packets)},
++      {"tx_fraglist_packets", EDMA_STAT(tx_fraglist_packets)},
++      {"tx_fraglist_nr_frags_packets", EDMA_STAT(tx_fraglist_with_nr_frags_packets)},
++      {"tx_tso_packets", EDMA_STAT(tx_tso_packets)},
++      {"tx_tso_drop_packets", EDMA_STAT(tx_tso_drop_packets)},
++      {"tx_gso_packets", EDMA_STAT(tx_gso_packets)},
++      {"tx_gso_drop_packets", EDMA_STAT(tx_gso_drop_packets)},
++      {"tx_queue_stopped_cpu0", EDMA_STAT(tx_queue_stopped[0])},
++      {"tx_queue_stopped_cpu1", EDMA_STAT(tx_queue_stopped[1])},
++      {"tx_queue_stopped_cpu2", EDMA_STAT(tx_queue_stopped[2])},
++      {"tx_queue_stopped_cpu3", EDMA_STAT(tx_queue_stopped[3])},
++};
++
++#define EDMA_STATS_LEN                ARRAY_SIZE(edma_gstrings_stats)
++
++static void edma_port_get_stats(struct net_device *netdev,
++                              struct edma_gmac_stats *stats)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct edma_port_rx_stats *pcpu_rx_stats;
++      struct edma_port_tx_stats *pcpu_tx_stats;
++      int i;
++
++      memset(stats, 0, sizeof(struct edma_port_pcpu_stats));
++
++      for_each_possible_cpu(i) {
++              struct edma_port_rx_stats rxp;
++              struct edma_port_tx_stats txp;
++              unsigned int start;
++
++              pcpu_rx_stats = per_cpu_ptr(port_priv->pcpu_stats.rx_stats, i);
++
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_rx_stats->syncp);
++                      memcpy(&rxp, pcpu_rx_stats, sizeof(*pcpu_rx_stats));
++              } while (u64_stats_fetch_retry(&pcpu_rx_stats->syncp, start));
++
++              stats->rx_packets += rxp.rx_pkts;
++              stats->rx_bytes += rxp.rx_bytes;
++              stats->rx_dropped += rxp.rx_drops;
++              stats->rx_nr_frag_packets += rxp.rx_nr_frag_pkts;
++              stats->rx_fraglist_packets += rxp.rx_fraglist_pkts;
++              stats->rx_nr_frag_headroom_err += rxp.rx_nr_frag_headroom_err;
++
++              pcpu_tx_stats = per_cpu_ptr(port_priv->pcpu_stats.tx_stats, i);
++
++              do {
++                      start = u64_stats_fetch_begin(&pcpu_tx_stats->syncp);
++                      memcpy(&txp, pcpu_tx_stats, sizeof(*pcpu_tx_stats));
++              } while (u64_stats_fetch_retry(&pcpu_tx_stats->syncp, start));
++
++              stats->tx_packets += txp.tx_pkts;
++              stats->tx_bytes += txp.tx_bytes;
++              stats->tx_dropped += txp.tx_drops;
++              stats->tx_nr_frag_packets += txp.tx_nr_frag_pkts;
++              stats->tx_fraglist_packets += txp.tx_fraglist_pkts;
++              stats->tx_fraglist_with_nr_frags_packets += txp.tx_fraglist_with_nr_frags_pkts;
++              stats->tx_tso_packets += txp.tx_tso_pkts;
++              stats->tx_tso_drop_packets += txp.tx_tso_drop_pkts;
++              stats->tx_gso_packets += txp.tx_gso_pkts;
++              stats->tx_gso_drop_packets += txp.tx_gso_drop_pkts;
++              stats->tx_queue_stopped[i] += txp.tx_queue_stopped[i];
++      }
++}
++
++static void edma_get_ethtool_stats(struct net_device *netdev,
++                                 __maybe_unused struct ethtool_stats *stats,
++                                 u64 *data)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct edma_gmac_stats edma_stats;
++      u64 *mib_data;
++      int i;
++      u8 *p;
++
++      if (!port_priv)
++              return;
++
++      /* Get the DMA Driver statistics from the data plane if available. */
++      memset(&edma_stats, 0, sizeof(struct edma_gmac_stats));
++      edma_port_get_stats(netdev, &edma_stats);
++
++       /* Populate data plane statistics. */
++      for (i = 0; i < EDMA_STATS_LEN; i++) {
++              p = ((u8 *)(&edma_stats) + edma_gstrings_stats[i].stat_offset);
++              data[i] = *(u64 *)p;
++      }
++
++       /* Get the GMAC MIB statistics along with the DMA driver statistics. */
++      mib_data = &data[EDMA_STATS_LEN];
++      ppe_port_get_ethtool_stats(port_priv->ppe_port, mib_data);
++}
++
++static int edma_get_strset_count(struct net_device *netdev, int sset)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      int sset_count = 0;
++
++      if (!port_priv || sset != ETH_SS_STATS)
++              return 0;
++
++      sset_count = ppe_port_get_sset_count(port_priv->ppe_port, sset);
++
++      return (EDMA_STATS_LEN + sset_count);
++}
++
++static void edma_get_strings(struct net_device *netdev, u32 stringset,
++                           u8 *data)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      int i;
++
++      if (!port_priv || stringset != ETH_SS_STATS)
++              return;
++
++      for (i = 0; i < EDMA_STATS_LEN; i++) {
++              memcpy(data, edma_gstrings_stats[i].stat_string,
++                     strlen(edma_gstrings_stats[i].stat_string));
++              data += ETH_GSTRING_LEN;
++      }
++
++      ppe_port_get_strings(port_priv->ppe_port, stringset, data);
++}
++
++static int edma_get_link_ksettings(struct net_device *netdev,
++                                 struct ethtool_link_ksettings *cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_ksettings_get(port->phylink, cmd);
++}
++
++static int edma_set_link_ksettings(struct net_device *netdev,
++                                 const struct ethtool_link_ksettings *cmd)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_ksettings_set(port->phylink, cmd);
++}
++
++static void edma_get_pauseparam(struct net_device *netdev,
++                              struct ethtool_pauseparam *pause)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return;
++
++      phylink_ethtool_get_pauseparam(port->phylink, pause);
++}
++
++static int edma_set_pauseparam(struct net_device *netdev,
++                             struct ethtool_pauseparam *pause)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_set_pauseparam(port->phylink, pause);
++}
++
++static int edma_get_eee(struct net_device *netdev, struct ethtool_keee *eee)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      return phylink_ethtool_get_eee(port->phylink, eee);
++}
++
++static int edma_set_eee(struct net_device *netdev, struct ethtool_keee *eee)
++{
++      struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
++      struct ppe_port *port =  port_priv->ppe_port;
++      int ret;
++
++      if (!port_priv)
++              return -EINVAL;
++
++      ret = ppe_port_set_mac_eee(port_priv->ppe_port, eee);
++      if (ret)
++              return ret;
++
++      return phylink_ethtool_set_eee(port->phylink, eee);
++}
++
++static const struct ethtool_ops edma_ethtool_ops = {
++      .get_strings = &edma_get_strings,
++      .get_sset_count = &edma_get_strset_count,
++      .get_ethtool_stats = &edma_get_ethtool_stats,
++      .get_link = &ethtool_op_get_link,
++      .get_link_ksettings = edma_get_link_ksettings,
++      .set_link_ksettings = edma_set_link_ksettings,
++      .get_pauseparam = &edma_get_pauseparam,
++      .set_pauseparam = &edma_set_pauseparam,
++      .get_eee = &edma_get_eee,
++      .set_eee = &edma_set_eee,
++};
++
++/**
++ * edma_set_ethtool_ops - Set ethtool operations
++ * @netdev: Netdevice
++ *
++ * Set ethtool operations.
++ */
++void edma_set_ethtool_ops(struct net_device *netdev)
++{
++      netdev->ethtool_ops = &edma_ethtool_ops;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -380,6 +380,7 @@ int edma_port_setup(struct ppe_port *por
+       netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+       netdev->netdev_ops = &edma_port_netdev_ops;
+       netdev->gso_max_segs = GSO_MAX_SEGS;
++      edma_set_ethtool_ops(netdev);
+       maddr = mac_addr;
+       if (of_get_mac_address(np, maddr))
diff --git a/target/linux/qualcommbe/patches-6.12/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch b/target/linux/qualcommbe/patches-6.12/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch
new file mode 100644 (file)
index 0000000..65eb3c6
--- /dev/null
@@ -0,0 +1,286 @@
+From 2ecec7e47e269e05cdd393c34aae51d4866070c6 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Tue, 11 Jun 2024 00:00:46 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add module parameters for driver
+ tunings
+
+Add module params and corresponding functionality for Tx/Rx
+mitigation timer/packet count, napi budget and tx requeue stop.
+
+Change-Id: I1717559c931bba4f355ee06ab89f289818400ca2
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 35 +++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 29 +++++++++++++--
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   | 21 +++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.c   | 29 +++++++++++++--
+ .../net/ethernet/qualcomm/ppe/edma_cfg_tx.h   | 16 +++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma_rx.h   |  4 +++
+ drivers/net/ethernet/qualcomm/ppe/edma_tx.h   |  4 +++
+ 7 files changed, 134 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -38,6 +38,38 @@ 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)");
++int edma_rx_napi_budget = EDMA_RX_NAPI_WORK_DEF;
++module_param(edma_rx_napi_budget, int, 0444);
++MODULE_PARM_DESC(edma_rx_napi_budget, "Rx NAPI budget (default:128, min:16, max:512)");
++
++int edma_tx_napi_budget = EDMA_TX_NAPI_WORK_DEF;
++module_param(edma_tx_napi_budget, int, 0444);
++MODULE_PARM_DESC(edma_tx_napi_budget, "Tx NAPI budget (default:512 for ipq95xx, min:16, max:512)");
++
++int edma_rx_mitigation_pkt_cnt = EDMA_RX_MITIGATION_PKT_CNT_DEF;
++module_param(edma_rx_mitigation_pkt_cnt, int, 0444);
++MODULE_PARM_DESC(edma_rx_mitigation_pkt_cnt,
++               "Rx mitigation packet count value (default:16, min:0, max: 256)");
++
++s32 edma_rx_mitigation_timer = EDMA_RX_MITIGATION_TIMER_DEF;
++module_param(edma_rx_mitigation_timer, int, 0444);
++MODULE_PARM_DESC(edma_dp_rx_mitigation_timer,
++               "Rx mitigation timer value in microseconds (default:25, min:0, max: 1000)");
++
++int edma_tx_mitigation_timer = EDMA_TX_MITIGATION_TIMER_DEF;
++module_param(edma_tx_mitigation_timer, int, 0444);
++MODULE_PARM_DESC(edma_tx_mitigation_timer,
++               "Tx mitigation timer value in microseconds (default:250, min:0, max: 1000)");
++
++int edma_tx_mitigation_pkt_cnt = EDMA_TX_MITIGATION_PKT_CNT_DEF;
++module_param(edma_tx_mitigation_pkt_cnt, int, 0444);
++MODULE_PARM_DESC(edma_tx_mitigation_pkt_cnt,
++               "Tx mitigation packet count value (default:16, min:0, max: 256)");
++
++static int tx_requeue_stop;
++module_param(tx_requeue_stop, int, 0640);
++MODULE_PARM_DESC(tx_requeue_stop, "Disable Tx requeue function (default:0)");
++
+ /* Priority to multi-queue mapping. */
+ static u8 edma_pri_map[PPE_QUEUE_INTER_PRI_NUM] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+@@ -828,7 +860,10 @@ 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;
++
+       edma_ctx->tx_requeue_stop = false;
++      if (tx_requeue_stop != 0)
++              edma_ctx->tx_requeue_stop = true;
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+@@ -166,6 +166,24 @@ static void edma_cfg_rx_desc_ring_config
+       reg = EDMA_BASE_OFFSET + EDMA_REG_RXDESC_RING_SIZE(rxdesc_ring->ring_id);
+       regmap_write(regmap, reg, data);
++      /* Validate mitigation timer value */
++      if (edma_rx_mitigation_timer < EDMA_RX_MITIGATION_TIMER_MIN ||
++          edma_rx_mitigation_timer > EDMA_RX_MITIGATION_TIMER_MAX) {
++              pr_err("Invalid Rx mitigation timer configured:%d for ring:%d. Using the default timer value:%d\n",
++                     edma_rx_mitigation_timer, rxdesc_ring->ring_id,
++                      EDMA_RX_MITIGATION_TIMER_DEF);
++              edma_rx_mitigation_timer = EDMA_RX_MITIGATION_TIMER_DEF;
++      }
++
++      /* Validate mitigation packet count value */
++      if (edma_rx_mitigation_pkt_cnt < EDMA_RX_MITIGATION_PKT_CNT_MIN ||
++          edma_rx_mitigation_pkt_cnt > EDMA_RX_MITIGATION_PKT_CNT_MAX) {
++              pr_err("Invalid Rx mitigation packet count configured:%d for ring:%d. Using the default packet counter value:%d\n",
++                     edma_rx_mitigation_timer, rxdesc_ring->ring_id,
++                      EDMA_RX_MITIGATION_PKT_CNT_DEF);
++              edma_rx_mitigation_pkt_cnt = EDMA_RX_MITIGATION_PKT_CNT_DEF;
++      }
++
+       /* Configure the Mitigation timer */
+       data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_RX_MITIGATION_TIMER_DEF,
+                                          ppe_dev->clk_rate / MHZ);
+@@ -176,7 +194,7 @@ static void edma_cfg_rx_desc_ring_config
+       regmap_write(regmap, reg, data);
+       /* Configure the Mitigation packet count */
+-      data = (EDMA_RX_MITIGATION_PKT_CNT_DEF & EDMA_RXDESC_LOW_THRE_MASK)
++      data = (edma_rx_mitigation_pkt_cnt & 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);
+@@ -915,6 +933,13 @@ void edma_cfg_rx_napi_add(void)
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
++      if (edma_rx_napi_budget < EDMA_RX_NAPI_WORK_MIN ||
++          edma_rx_napi_budget > EDMA_RX_NAPI_WORK_MAX) {
++              pr_err("Incorrect Rx NAPI budget: %d, setting to default: %d",
++                     edma_rx_napi_budget, hw_info->napi_budget_rx);
++              edma_rx_napi_budget = hw_info->napi_budget_rx;
++      }
++
+       for (i = 0; i < rx->num_rings; i++) {
+               struct edma_rxdesc_ring *rxdesc_ring = &edma_ctx->rx_rings[i];
+@@ -923,7 +948,7 @@ void edma_cfg_rx_napi_add(void)
+               rxdesc_ring->napi_added = true;
+       }
+-      netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", hw_info->napi_budget_rx);
++      netdev_dbg(edma_ctx->dummy_dev, "Rx NAPI budget: %d\n", edma_rx_napi_budget);
+ }
+ /**
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+@@ -5,6 +5,15 @@
+ #ifndef __EDMA_CFG_RX__
+ #define __EDMA_CFG_RX__
++/* Rx default NAPI budget */
++#define EDMA_RX_NAPI_WORK_DEF         128
++
++/* RX minimum NAPI budget */
++#define EDMA_RX_NAPI_WORK_MIN         16
++
++/* Rx maximum NAPI budget */
++#define EDMA_RX_NAPI_WORK_MAX         512
++
+ /* SKB payload size used in page mode */
+ #define EDMA_RX_PAGE_MODE_SKB_SIZE    256
+@@ -22,9 +31,21 @@
+ /* Rx mitigation timer's default value in microseconds */
+ #define EDMA_RX_MITIGATION_TIMER_DEF  25
++/* Rx mitigation timer's minimum value in microseconds */
++#define EDMA_RX_MITIGATION_TIMER_MIN  0
++
++/* Rx mitigation timer's maximum value in microseconds */
++#define EDMA_RX_MITIGATION_TIMER_MAX  1000
++
+ /* Rx mitigation packet count's default value */
+ #define EDMA_RX_MITIGATION_PKT_CNT_DEF        16
++/* Rx mitigation packet count's minimum value */
++#define EDMA_RX_MITIGATION_PKT_CNT_MIN        0
++
++/* Rx mitigation packet count's maximum value */
++#define EDMA_RX_MITIGATION_PKT_CNT_MAX        256
++
+ /* Default bitmap of cores for RPS to ARM cores */
+ #define EDMA_RX_DEFAULT_BITMAP        ((1 << EDMA_MAX_CORE) - 1)
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.c
+@@ -170,6 +170,24 @@ static void edma_cfg_txcmpl_ring_configu
+       reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_CTRL(txcmpl_ring->id);
+       regmap_write(regmap, reg, EDMA_TXCMPL_RETMODE_OPAQUE);
++      /* Validate mitigation timer value */
++      if (edma_tx_mitigation_timer < EDMA_TX_MITIGATION_TIMER_MIN ||
++          edma_tx_mitigation_timer > EDMA_TX_MITIGATION_TIMER_MAX) {
++              pr_err("Invalid Tx mitigation timer configured:%d for ring:%d. Using the default timer value:%d\n",
++                     edma_tx_mitigation_timer, txcmpl_ring->id,
++                     EDMA_TX_MITIGATION_TIMER_DEF);
++              edma_tx_mitigation_timer = EDMA_TX_MITIGATION_TIMER_DEF;
++      }
++
++      /* Validate mitigation packet count value */
++      if (edma_tx_mitigation_pkt_cnt < EDMA_TX_MITIGATION_PKT_CNT_MIN ||
++          edma_tx_mitigation_pkt_cnt > EDMA_TX_MITIGATION_PKT_CNT_MAX) {
++              pr_err("Invalid Tx mitigation packet count configured:%d for ring:%d. Using the default packet counter value:%d\n",
++                     edma_tx_mitigation_timer, txcmpl_ring->id,
++                     EDMA_TX_MITIGATION_PKT_CNT_DEF);
++              edma_tx_mitigation_pkt_cnt = EDMA_TX_MITIGATION_PKT_CNT_DEF;
++      }
++
+       /* Configure the Mitigation timer. */
+       data = EDMA_MICROSEC_TO_TIMER_UNIT(EDMA_TX_MITIGATION_TIMER_DEF,
+                                          ppe_dev->clk_rate / MHZ);
+@@ -180,7 +198,7 @@ static void edma_cfg_txcmpl_ring_configu
+       regmap_write(regmap, reg, data);
+       /* Configure the Mitigation packet count. */
+-      data = (EDMA_TX_MITIGATION_PKT_CNT_DEF & EDMA_TXCMPL_LOW_THRE_MASK)
++      data = (edma_tx_mitigation_pkt_cnt & EDMA_TXCMPL_LOW_THRE_MASK)
+               << EDMA_TXCMPL_LOW_THRE_SHIFT;
+       pr_debug("EDMA Tx mitigation packet count value: %d\n", data);
+       reg = EDMA_BASE_OFFSET + EDMA_REG_TXCMPL_UGT_THRE(txcmpl_ring->id);
+@@ -634,6 +652,13 @@ void edma_cfg_tx_napi_add(struct net_dev
+       struct edma_txcmpl_ring *txcmpl_ring;
+       u32 i, ring_idx;
++      if (edma_tx_napi_budget < EDMA_TX_NAPI_WORK_MIN ||
++          edma_tx_napi_budget > EDMA_TX_NAPI_WORK_MAX) {
++              pr_err("Incorrect Tx NAPI budget: %d, setting to default: %d",
++                     edma_tx_napi_budget, hw_info->napi_budget_tx);
++              edma_tx_napi_budget = hw_info->napi_budget_tx;
++      }
++
+       /* Adding tx napi for a interface with each queue. */
+       for_each_possible_cpu(i) {
+               ring_idx = ((port_id - 1) * num_possible_cpus()) + i;
+@@ -644,5 +669,5 @@ void edma_cfg_tx_napi_add(struct net_dev
+               netdev_dbg(netdev, "Napi added for txcmpl ring: %u\n", txcmpl_ring->id);
+       }
+-      netdev_dbg(netdev, "Tx NAPI budget: %d\n", hw_info->napi_budget_tx);
++      netdev_dbg(netdev, "Tx NAPI budget: %d\n", edma_tx_napi_budget);
+ }
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_tx.h
+@@ -5,12 +5,28 @@
+ #ifndef __EDMA_CFG_TX__
+ #define __EDMA_CFG_TX__
++#define EDMA_TX_NAPI_WORK_DEF 512
++#define EDMA_TX_NAPI_WORK_MIN 16
++#define EDMA_TX_NAPI_WORK_MAX 512
++
+ /* Tx mitigation timer's default value. */
+ #define EDMA_TX_MITIGATION_TIMER_DEF  250
++/* Tx mitigation timer's minimum value in microseconds */
++#define EDMA_TX_MITIGATION_TIMER_MIN  0
++
++/* Tx mitigation timer's maximum value in microseconds */
++#define EDMA_TX_MITIGATION_TIMER_MAX  1000
++
+ /* Tx mitigation packet count default value. */
+ #define EDMA_TX_MITIGATION_PKT_CNT_DEF        16
++/* Tx mitigation packet count's minimum value */
++#define EDMA_TX_MITIGATION_PKT_CNT_MIN        0
++
++/* Tx mitigation packet count's maximum value */
++#define EDMA_TX_MITIGATION_PKT_CNT_MAX        256
++
+ void edma_cfg_tx_rings(void);
+ int edma_cfg_tx_rings_alloc(void);
+ void edma_cfg_tx_rings_cleanup(void);
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_rx.h
+@@ -281,6 +281,10 @@ struct edma_rxdesc_ring {
+       struct sk_buff *last;
+ };
++extern int edma_rx_napi_budget;
++extern int edma_rx_mitigation_timer;
++extern int edma_rx_mitigation_pkt_cnt;
++
+ 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);
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_tx.h
+@@ -288,6 +288,10 @@ struct edma_txcmpl_ring {
+       bool napi_added;
+ };
++extern int edma_tx_napi_budget;
++extern int edma_tx_mitigation_timer;
++extern int edma_tx_mitigation_pkt_cnt;
++
+ enum edma_tx_status edma_tx_ring_xmit(struct net_device *netdev,
+                                     struct sk_buff *skb,
+                              struct edma_txdesc_ring *txdesc_ring,
diff --git a/target/linux/qualcommbe/patches-6.12/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch b/target/linux/qualcommbe/patches-6.12/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch
new file mode 100644 (file)
index 0000000..c697001
--- /dev/null
@@ -0,0 +1,145 @@
+From dcac735a715c13a817d65ae371564cf2793330b2 Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Tue, 11 Jun 2024 01:43:22 +0530
+Subject: [PATCH] net: ethernet: qualcomm: Add sysctl for RPS bitmap
+
+Add sysctl to configure RPS bitmap for EDMA receive.
+This bitmap is used to configure the set of ARM cores
+used to receive packets from EDMA.
+
+Change-Id: Ie0e7d5971db93ea1494608a9e79c4abb13ce69b6
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+Alex G: Use **const** ctl_table argument for .proc_handler
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma.c      | 23 ++++++++++++++++
+ drivers/net/ethernet/qualcomm/ppe/edma.h      |  2 ++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.c   | 27 +++++++++++++++++++
+ .../net/ethernet/qualcomm/ppe/edma_cfg_rx.h   |  6 ++++-
+ 4 files changed, 57 insertions(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.c
+@@ -797,6 +797,11 @@ void edma_destroy(struct ppe_device *ppe
+       struct edma_ring_info *rx = hw_info->rx;
+       u32 i;
++      if (edma_ctx->rx_rps_ctl_table_hdr) {
++              unregister_sysctl_table(edma_ctx->rx_rps_ctl_table_hdr);
++              edma_ctx->rx_rps_ctl_table_hdr = NULL;
++      }
++
+       /* Disable interrupts. */
+       for (i = 1; i <= hw_info->max_ports; i++)
+               edma_cfg_tx_disable_interrupts(i);
+@@ -840,6 +845,17 @@ void edma_destroy(struct ppe_device *ppe
+       kfree(edma_ctx->netdev_arr);
+ }
++/* EDMA Rx RPS core sysctl table */
++static struct ctl_table edma_rx_rps_core_table[] = {
++      {
++              .procname       =       "rps_bitmap_cores",
++              .data           =       &edma_cfg_rx_rps_bitmap_cores,
++              .maxlen         =       sizeof(int),
++              .mode           =       0644,
++              .proc_handler   =       edma_cfg_rx_rps_bitmap
++      },
++};
++
+ /**
+  * edma_setup - EDMA Setup.
+  * @ppe_dev: PPE device
+@@ -865,6 +881,13 @@ int edma_setup(struct ppe_device *ppe_de
+       if (tx_requeue_stop != 0)
+               edma_ctx->tx_requeue_stop = true;
++      edma_ctx->rx_rps_ctl_table_hdr = register_sysctl("net/edma",
++                                                       edma_rx_rps_core_table);
++      if (!edma_ctx->rx_rps_ctl_table_hdr) {
++              pr_err("Rx rps sysctl table configuration failed\n");
++              return -EINVAL;
++      }
++
+       /* Configure the EDMA common clocks. */
+       ret = edma_clock_init();
+       if (ret) {
+--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
+@@ -132,6 +132,7 @@ struct edma_intr_info {
+  * @tx_rings: Tx Descriptor Ring, SW is producer
+  * @txcmpl_rings: Tx complete Ring, SW is consumer
+  * @err_stats: Per CPU error statistics
++ * @rx_rps_ctl_table_hdr: Rx RPS sysctl table
+  * @rx_page_mode: Page mode enabled or disabled
+  * @rx_buf_size: Rx buffer size for Jumbo MRU
+  * @tx_requeue_stop: Tx requeue stop enabled or disabled
+@@ -147,6 +148,7 @@ struct edma_context {
+       struct edma_txdesc_ring *tx_rings;
+       struct edma_txcmpl_ring *txcmpl_rings;
+       struct edma_err_stats __percpu *err_stats;
++      struct ctl_table_header *rx_rps_ctl_table_hdr;
+       u32 rx_page_mode;
+       u32 rx_buf_size;
+       bool tx_requeue_stop;
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.c
+@@ -43,6 +43,8 @@ static u32 edma_rx_ring_queue_map[][EDMA
+                                               { 6, 14, 22, 30 },
+                                               { 7, 15, 23, 31 }};
++u32 edma_cfg_rx_rps_bitmap_cores = EDMA_RX_DEFAULT_BITMAP;
++
+ static int edma_cfg_rx_desc_rings_reset_queue_mapping(void)
+ {
+       struct edma_hw_info *hw_info = edma_ctx->hw_info;
+@@ -987,3 +989,28 @@ int edma_cfg_rx_rps_hash_map(void)
+       return 0;
+ }
++
++/* Configure RPS hash mapping based on bitmap */
++int edma_cfg_rx_rps_bitmap(const struct ctl_table *table, int write,
++                         void *buffer, size_t *lenp, loff_t *ppos)
++{
++      int ret;
++
++      ret = proc_dointvec(table, write, buffer, lenp, ppos);
++
++      if (!write)
++              return ret;
++
++      if (!edma_cfg_rx_rps_bitmap_cores ||
++          edma_cfg_rx_rps_bitmap_cores > EDMA_RX_DEFAULT_BITMAP) {
++              pr_warn("Incorrect CPU bitmap: %x. Setting it to default value: %d",
++                      edma_cfg_rx_rps_bitmap_cores, EDMA_RX_DEFAULT_BITMAP);
++              edma_cfg_rx_rps_bitmap_cores = EDMA_RX_DEFAULT_BITMAP;
++      }
++
++      ret = edma_cfg_rx_rps_hash_map();
++
++      pr_info("EDMA RPS bitmap value: %d\n", edma_cfg_rx_rps_bitmap_cores);
++
++      return ret;
++}
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_cfg_rx.h
+@@ -49,6 +49,8 @@
+ /* Default bitmap of cores for RPS to ARM cores */
+ #define EDMA_RX_DEFAULT_BITMAP        ((1 << EDMA_MAX_CORE) - 1)
++extern u32 edma_cfg_rx_rps_bitmap_cores;
++
+ int edma_cfg_rx_rings(void);
+ int edma_cfg_rx_rings_alloc(void);
+ void edma_cfg_rx_ring_mappings(void);
+@@ -64,6 +66,8 @@ 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,
++int edma_cfg_rx_rps(const struct ctl_table *table, int write,
+                   void *buffer, size_t *lenp, loff_t *ppos);
++int edma_cfg_rx_rps_bitmap(const struct ctl_table *table, int write,
++                         void *buffer, size_t *lenp, loff_t *ppos);
+ #endif
diff --git a/target/linux/qualcommbe/patches-6.12/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch b/target/linux/qualcommbe/patches-6.12/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch
new file mode 100644 (file)
index 0000000..79af169
--- /dev/null
@@ -0,0 +1,48 @@
+From a809433c9b6a418dd886f12a5dcb3376f73bf2a7 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 4 Dec 2024 01:37:05 +0100
+Subject: [PATCH] net: ethernet: qualcomm: Add support for label property for
+ EDMA port
+
+Add support for label property for EDMA port. This is useful to define
+custom name in DTS for specific ethernet port instead of assigning a
+dynamic name at runtime.
+
+This also improve the log output by using modern APIs.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/ppe/edma_port.c | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/ethernet/qualcomm/ppe/edma_port.c
++++ b/drivers/net/ethernet/qualcomm/ppe/edma_port.c
+@@ -355,13 +355,25 @@ int edma_port_setup(struct ppe_port *por
+       int port_id = port->port_id;
+       struct net_device *netdev;
+       u8 mac_addr[ETH_ALEN];
++      const char *name;
++      int assign_type;
+       int ret = 0;
+       u8 *maddr;
+-      netdev = alloc_etherdev_mqs(sizeof(struct edma_port_priv),
+-                                  EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
++      name = of_get_property(np, "label", NULL);
++      if (name) {
++              assign_type = NET_NAME_PREDICTABLE;
++      } else {
++              name = "eth%d";
++              assign_type = NET_NAME_ENUM;
++      }
++
++      netdev = alloc_netdev_mqs(sizeof(struct edma_port_priv),
++                                name, assign_type,
++                                ether_setup,
++                                EDMA_NETDEV_QUEUE_NUM, EDMA_NETDEV_QUEUE_NUM);
+       if (!netdev) {
+-              pr_err("alloc_etherdev() failed\n");
++              dev_err(ppe_dev->dev, "alloc_netdev_mqs() failed\n");
+               return -ENOMEM;
+       }
diff --git a/target/linux/qualcommbe/patches-6.12/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch b/target/linux/qualcommbe/patches-6.12/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch
new file mode 100644 (file)
index 0000000..a0d15cf
--- /dev/null
@@ -0,0 +1,30 @@
+From 9c4ad75f17788a64c1e37d0b9e19ca157e01c80a Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Mon, 9 Dec 2024 18:19:06 +0100
+Subject: [PATCH] net: ethernet: qualcomm: ppe: Fix unmet dependency with
+ QCOM_PPE
+
+Fix unmet dependency with QCOM_PPE on selecting SFP.
+
+WARNING: unmet direct dependencies detected for SFP
+  Depends on [m]: NETDEVICES [=y] && PHYLIB [=y] && I2C [=y] && PHYLINK [=y] && (HWMON [=m] || HWMON [=m]=n [=n])
+  Selected by [y]:
+  - QCOM_PPE [=y] && NETDEVICES [=y] && ETHERNET [=y] && NET_VENDOR_QUALCOMM [=y] && HAS_IOMEM [=y] && OF [=y] && COMMON_CLK [=y]
+
+This permit correct compilation of the modules with SFP enabled.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -68,7 +68,6 @@ config QCOM_PPE
+       select REGMAP_MMIO
+       select PHYLINK
+       select PCS_QCOM_IPQ_UNIPHY
+-      select SFP
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
diff --git a/target/linux/qualcommbe/patches-6.12/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch b/target/linux/qualcommbe/patches-6.12/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch
new file mode 100644 (file)
index 0000000..3893c5c
--- /dev/null
@@ -0,0 +1,24 @@
+From ac41b401d274a4004027fa4000d801cd28c51f4c Mon Sep 17 00:00:00 2001
+From: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+Date: Tue, 13 May 2025 13:41:37 -0500
+Subject: [PATCH] net: ethernet: qualcomm: ppe: select correct PCS dependency
+
+The config symbol for the PCS driver has changed to PCS_QCOM_IPQ9574,
+since the original submission. Update Kconfig accordingly.
+
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ drivers/net/ethernet/qualcomm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/qualcomm/Kconfig
++++ b/drivers/net/ethernet/qualcomm/Kconfig
+@@ -67,7 +67,7 @@ config QCOM_PPE
+       depends on COMMON_CLK
+       select REGMAP_MMIO
+       select PHYLINK
+-      select PCS_QCOM_IPQ_UNIPHY
++      select PCS_QCOM_IPQ9574
+       help
+         This driver supports the Qualcomm Technologies, Inc. packet
+         process engine (PPE) available with IPQ SoC. The PPE includes
diff --git a/target/linux/qualcommbe/patches-6.12/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch b/target/linux/qualcommbe/patches-6.12/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch
new file mode 100644 (file)
index 0000000..a243c00
--- /dev/null
@@ -0,0 +1,72 @@
+From bbf706ecfd4295d73c8217d5220573dd51d7a081 Mon Sep 17 00:00:00 2001
+From: Luo Jie <quic_luoj@quicinc.com>
+Date: Fri, 1 Mar 2024 14:46:45 +0800
+Subject: [PATCH] arm64: dts: qcom: Add IPQ9574 PPE base device node
+
+PPE is the packet process engine on the Qualcomm IPQ platform,
+which is connected with the external switch or PHY device via
+the UNIPHY (PCS).
+
+Change-Id: I254bd48c218aa4eab54f697a2ad149f5a93b682c
+Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
+Alex G: Add "qcom_ppe" label to PPE node
+Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 39 +++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -13,6 +13,7 @@
+ #include <dt-bindings/interconnect/qcom,ipq9574.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
++#include <dt-bindings/reset/qcom,ipq9574-nsscc.h>
+ #include <dt-bindings/thermal/thermal.h>
+ / {
+@@ -1269,6 +1270,44 @@
+                       #interconnect-cells = <1>;
+               };
++              qcom_ppe: ethernet@3a000000 {
++                      compatible = "qcom,ipq9574-ppe";
++                      reg = <0x3a000000 0xbef800>;
++                      ranges;
++                      #address-cells = <1>;
++                      #size-cells = <1>;
++                      clocks = <&nsscc NSS_CC_PPE_SWITCH_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_CFG_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_IPE_CLK>,
++                               <&nsscc NSS_CC_PPE_SWITCH_BTQ_CLK>;
++                      clock-names = "ppe",
++                                    "ppe_cfg",
++                                    "ppe_ipe",
++                                    "ppe_btq";
++                      resets = <&nsscc PPE_FULL_RESET>;
++                      interconnects = <&nsscc MASTER_NSSNOC_PPE
++                                       &nsscc SLAVE_NSSNOC_PPE>,
++                                      <&nsscc MASTER_NSSNOC_PPE_CFG
++                                       &nsscc SLAVE_NSSNOC_PPE_CFG>,
++                                      <&gcc MASTER_NSSNOC_QOSGEN_REF
++                                       &gcc SLAVE_NSSNOC_QOSGEN_REF>,
++                                      <&gcc MASTER_NSSNOC_TIMEOUT_REF
++                                       &gcc SLAVE_NSSNOC_TIMEOUT_REF>,
++                                      <&gcc MASTER_MEM_NOC_NSSNOC
++                                       &gcc SLAVE_MEM_NOC_NSSNOC>,
++                                      <&gcc MASTER_NSSNOC_MEMNOC
++                                       &gcc SLAVE_NSSNOC_MEMNOC>,
++                                      <&gcc MASTER_NSSNOC_MEM_NOC_1
++                                       &gcc SLAVE_NSSNOC_MEM_NOC_1>;
++                      interconnect-names = "ppe",
++                                           "ppe_cfg",
++                                           "qos_gen",
++                                           "timeout_ref",
++                                           "nssnoc_memnoc",
++                                           "memnoc_nssnoc",
++                                           "memnoc_nssnoc_1";
++              };
++
+               pcs_uniphy0: ethernet-pcs@7a00000 {
+                       compatible = "qcom,ipq9574-pcs";
+                       reg = <0x7a00000 0x10000>;
diff --git a/target/linux/qualcommbe/patches-6.12/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch b/target/linux/qualcommbe/patches-6.12/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch
new file mode 100644 (file)
index 0000000..74187f5
--- /dev/null
@@ -0,0 +1,91 @@
+From bd50babc7db2a35d98236a0386173dccd6c6374b Mon Sep 17 00:00:00 2001
+From: Pavithra R <quic_pavir@quicinc.com>
+Date: Wed, 6 Mar 2024 22:29:41 +0530
+Subject: [PATCH] arm64: dts: qcom: Add EDMA node for IPQ9574
+
+Add EDMA (Ethernet DMA) device tree node for IPQ9574 to
+enable ethernet support.
+
+Change-Id: I87d7c50f2485c8670948dce305000337f6499f8b
+Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
+---
+ arch/arm64/boot/dts/qcom/ipq9574.dtsi | 68 +++++++++++++++++++++++++++
+ 1 file changed, 68 insertions(+)
+
+--- a/arch/arm64/boot/dts/qcom/ipq9574.dtsi
++++ b/arch/arm64/boot/dts/qcom/ipq9574.dtsi
+@@ -1306,6 +1306,74 @@
+                                            "nssnoc_memnoc",
+                                            "memnoc_nssnoc",
+                                            "memnoc_nssnoc_1";
++
++                      edma {
++                              compatible = "qcom,ipq9574-edma";
++                              clocks = <&nsscc NSS_CC_PPE_EDMA_CLK>,
++                                       <&nsscc NSS_CC_PPE_EDMA_CFG_CLK>;
++                              clock-names = "edma",
++                                            "edma-cfg";
++                              resets = <&nsscc EDMA_HW_RESET>;
++                              reset-names = "edma_rst";
++                              interrupts = <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 374 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 375 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 376 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 377 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 380 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 381 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 382 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 383 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 505 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 504 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 503 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 502 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 501 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 500 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 351 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 352 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>,
++                                           <GIC_SPI 499 IRQ_TYPE_LEVEL_HIGH>;
++                              interrupt-names = "edma_txcmpl_8",
++                                                "edma_txcmpl_9",
++                                                "edma_txcmpl_10",
++                                                "edma_txcmpl_11",
++                                                "edma_txcmpl_12",
++                                                "edma_txcmpl_13",
++                                                "edma_txcmpl_14",
++                                                "edma_txcmpl_15",
++                                                "edma_txcmpl_16",
++                                                "edma_txcmpl_17",
++                                                "edma_txcmpl_18",
++                                                "edma_txcmpl_19",
++                                                "edma_txcmpl_20",
++                                                "edma_txcmpl_21",
++                                                "edma_txcmpl_22",
++                                                "edma_txcmpl_23",
++                                                "edma_txcmpl_24",
++                                                "edma_txcmpl_25",
++                                                "edma_txcmpl_26",
++                                                "edma_txcmpl_27",
++                                                "edma_txcmpl_28",
++                                                "edma_txcmpl_29",
++                                                "edma_txcmpl_30",
++                                                "edma_txcmpl_31",
++                                                "edma_rxdesc_20",
++                                                "edma_rxdesc_21",
++                                                "edma_rxdesc_22",
++                                                "edma_rxdesc_23",
++                                                "edma_misc";
++                      };
+               };
+               pcs_uniphy0: ethernet-pcs@7a00000 {