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 index 00000000000..d6292f8bf50 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0337-net-ethernet-qualcomm-Add-PPE-scheduler-config.patch @@ -0,0 +1,201 @@ +From 93cf3297818ee61607f0a8d1d34e4fb7fcde3cdf Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +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 +--- + .../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 index 00000000000..348e834f809 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0338-net-ethernet-qualcomm-Add-phylink-support-for-PPE-MA.patch @@ -0,0 +1,1040 @@ +From dbb3711ab25ea410ad5286b2f39dccd954cda225 Mon Sep 17 00:00:00 2001 +From: Lei Wei +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++/** ++ * 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 + ++/* 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 index 00000000000..1430692a6fb --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0339-net-ethernet-qualcomm-Add-PPE-port-MAC-MIB-statistic.patch @@ -0,0 +1,673 @@ +From dbcc0d01241a1353d8e11e764cf7fcd390ae3f1f Mon Sep 17 00:00:00 2001 +From: Lei Wei +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 +--- + 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 + ++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 index 00000000000..856a1ed4ccb --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0340-net-ethernet-qualcomm-Add-PPE-port-MAC-address-and-E.patch @@ -0,0 +1,172 @@ +From 55fbbc8ef90df27a16bca1613a793a578b79a384 Mon Sep 17 00:00:00 2001 +From: Lei Wei +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 +Alex G: use struct ethtool_keee instead of ethtool_eee +Signed-off-by: Alexandru Gagniuc +--- + 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 + ++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 index 00000000000..ae81ddb19d1 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0341-net-ethernet-qualcomm-Add-API-to-configure-PPE-port-.patch @@ -0,0 +1,78 @@ +From 3981aeae5dd43dea94a0ec10f0b2977ebd102560 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +--- + 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 index 00000000000..0160efdacbc --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0342-net-ethernet-qualcomm-Add-EDMA-support-for-QCOM-IPQ9.patch @@ -0,0 +1,932 @@ +From 00d4f3cb4f5d1e6924151a4551f06b6a82bf0146 Mon Sep 17 00:00:00 2001 +From: Pavithra R +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 +Co-developed-by: Suruchi Agarwal +Signed-off-by: Suruchi Agarwal +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #include + ++#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 index 00000000000..55de486ed39 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0343-net-ethernet-qualcomm-Add-netdevice-support-for-QCOM.patch @@ -0,0 +1,397 @@ +From 5dc80c468c668d855d76b323f09bbadb95cc3147 Mon Sep 17 00:00:00 2001 +From: Suruchi Agarwal +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 +Signed-off-by: Pavithra R +Signed-off-by: Suruchi Agarwal +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 + #include + ++#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 index 00000000000..eb10a007457 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0344-net-ethernet-qualcomm-Add-Rx-Ethernet-DMA-support.patch @@ -0,0 +1,2454 @@ +From b5c8c5d3888328321e8be1db50b75dff8f514e51 Mon Sep 17 00:00:00 2001 +From: Suruchi Agarwal +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 +Signed-off-by: Pavithra R +Signed-off-by: Suruchi Agarwal +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 +--- + 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 + + #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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + + #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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++#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 index 00000000000..39d300de804 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0345-net-ethernet-qualcomm-Add-Tx-Ethernet-DMA-support.patch @@ -0,0 +1,2363 @@ +From 339d3a5365f150a78ed405684e379fee3acdbe90 Mon Sep 17 00:00:00 2001 +From: Suruchi Agarwal +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 +Signed-off-by: Pavithra R +Signed-off-by: Suruchi Agarwal +--- + 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 + + #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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 index 00000000000..0bdfb0c8970 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0346-net-ethernet-qualcomm-Add-miscellaneous-error-interr.patch @@ -0,0 +1,730 @@ +From 8a924457c0b71acee96c8f78ef386e2a354a2aca Mon Sep 17 00:00:00 2001 +From: Suruchi Agarwal +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 +Signed-off-by: Pavithra R +Signed-off-by: Suruchi Agarwal +--- + 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 ++#include ++#include ++#include ++#include ++ ++#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 + #include ++#include + #include + #include + ++#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 index 00000000000..4e0103db574 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0347-net-ethernet-qualcomm-Add-ethtool-support-for-EDMA.patch @@ -0,0 +1,344 @@ +From bd61a680fb657eb65272225f18c93fe338c700da Mon Sep 17 00:00:00 2001 +From: Pavithra R +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 +Alex G: use struct ethtool_keee instead of ethtool_eee +Signed-off-by: Alexandru Gagniuc +--- + 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 ++#include ++#include ++#include ++#include ++ ++#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 = ðtool_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 index 00000000000..65eb3c6c207 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0348-net-ethernet-qualcomm-Add-module-parameters-for-driv.patch @@ -0,0 +1,286 @@ +From 2ecec7e47e269e05cdd393c34aae51d4866070c6 Mon Sep 17 00:00:00 2001 +From: Pavithra R +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 +--- + 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 index 00000000000..c6970015d7d --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0349-net-ethernet-qualcomm-Add-sysctl-for-RPS-bitmap.patch @@ -0,0 +1,145 @@ +From dcac735a715c13a817d65ae371564cf2793330b2 Mon Sep 17 00:00:00 2001 +From: Pavithra R +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 +Alex G: Use **const** ctl_table argument for .proc_handler +Signed-off-by: Alexandru Gagniuc +--- + 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 index 00000000000..79af169c8e7 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0350-net-ethernet-qualcomm-Add-support-for-label-property.patch @@ -0,0 +1,48 @@ +From a809433c9b6a418dd886f12a5dcb3376f73bf2a7 Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +--- + 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 index 00000000000..a0d15cf2a06 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0351-net-ethernet-qualcomm-ppe-Fix-unmet-dependency-with-.patch @@ -0,0 +1,30 @@ +From 9c4ad75f17788a64c1e37d0b9e19ca157e01c80a Mon Sep 17 00:00:00 2001 +From: Christian Marangi +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 +--- + 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 index 00000000000..3893c5cd8de --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0352-net-ethernet-qualcomm-ppe-select-correct-PCS-depende.patch @@ -0,0 +1,24 @@ +From ac41b401d274a4004027fa4000d801cd28c51f4c Mon Sep 17 00:00:00 2001 +From: Alexandru Gagniuc +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 +--- + 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 index 00000000000..a243c009613 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0353-arm64-dts-qcom-Add-IPQ9574-PPE-base-device-node.patch @@ -0,0 +1,72 @@ +From bbf706ecfd4295d73c8217d5220573dd51d7a081 Mon Sep 17 00:00:00 2001 +From: Luo Jie +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 +Alex G: Add "qcom_ppe" label to PPE node +Signed-off-by: Alexandru Gagniuc +--- + 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 + #include + #include ++#include + #include + + / { +@@ -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 index 00000000000..74187f532d2 --- /dev/null +++ b/target/linux/qualcommbe/patches-6.12/0354-arm64-dts-qcom-Add-EDMA-node-for-IPQ9574.patch @@ -0,0 +1,91 @@ +From bd50babc7db2a35d98236a0386173dccd6c6374b Mon Sep 17 00:00:00 2001 +From: Pavithra R +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 +--- + 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 = , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ , ++ ; ++ 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 {