From ec30075badd13a3e2ffddd1c5dcb40e3c52202ed Mon Sep 17 00:00:00 2001 From: Pavithra R Date: Thu, 30 May 2024 20:46:36 +0530 Subject: [PATCH 43/50] 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 --- 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 diff --git a/drivers/net/ethernet/qualcomm/ppe/Makefile b/drivers/net/ethernet/qualcomm/ppe/Makefile index 45e1b103ec7a..cb9d30889d06 100644 --- a/drivers/net/ethernet/qualcomm/ppe/Makefile +++ b/drivers/net/ethernet/qualcomm/ppe/Makefile @@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o qcom-ppe-objs := ppe.o ppe_config.o ppe_api.o ppe_debugfs.o ppe_port.o #EDMA -qcom-ppe-objs += edma.o edma_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 diff --git a/drivers/net/ethernet/qualcomm/ppe/edma.h b/drivers/net/ethernet/qualcomm/ppe/edma.h index 6500d21b9eba..ac6d2fcc2983 100644 --- a/drivers/net/ethernet/qualcomm/ppe/edma.h +++ b/drivers/net/ethernet/qualcomm/ppe/edma.h @@ -151,4 +151,5 @@ 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); +void edma_set_ethtool_ops(struct net_device *netdev); #endif diff --git a/drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c b/drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c new file mode 100644 index 000000000000..eabc1e11b16f --- /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_eee *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_eee *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; +} diff --git a/drivers/net/ethernet/qualcomm/ppe/edma_port.c b/drivers/net/ethernet/qualcomm/ppe/edma_port.c index afa2b6479822..0b3b769a4a49 100644 --- 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 *port) 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)) -- 2.45.2