forked from Openwrt-EcoNet/openwrt
Add the second part of the PPE driver. This includes the EDMA and network device support. This part does not appear to have been officially submitted for upstream review. The series is taken from target/linux/qualcommbe/patches-6.6, and had to be heavily modified in order to compile of v6.12. Changes to patches are noted in the respective patch body. Also add the PPE and EDMA nodes in this series. Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com> Link: https://github.com/openwrt/openwrt/pull/18796 Signed-off-by: Robert Marko <robimarko@gmail.com>
345 lines
12 KiB
Diff
345 lines
12 KiB
Diff
From bd61a680fb657eb65272225f18c93fe338c700da Mon Sep 17 00:00:00 2001
|
|
From: Pavithra R <quic_pavir@quicinc.com>
|
|
Date: Thu, 30 May 2024 20:46:36 +0530
|
|
Subject: [PATCH] net: ethernet: qualcomm: Add ethtool support for EDMA
|
|
|
|
ethtool ops can be used for EDMA netdevice configuration and statistics.
|
|
|
|
Change-Id: I57fc19415dacbe51fed000520336463938220609
|
|
Signed-off-by: Pavithra R <quic_pavir@quicinc.com>
|
|
Alex G: use struct ethtool_keee instead of ethtool_eee
|
|
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
|
|
---
|
|
drivers/net/ethernet/qualcomm/ppe/Makefile | 2 +-
|
|
drivers/net/ethernet/qualcomm/ppe/edma.h | 1 +
|
|
.../net/ethernet/qualcomm/ppe/edma_ethtool.c | 294 ++++++++++++++++++
|
|
drivers/net/ethernet/qualcomm/ppe/edma_port.c | 1 +
|
|
4 files changed, 297 insertions(+), 1 deletion(-)
|
|
create mode 100644 drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
|
|
|
|
--- a/drivers/net/ethernet/qualcomm/ppe/Makefile
|
|
+++ b/drivers/net/ethernet/qualcomm/ppe/Makefile
|
|
@@ -7,4 +7,4 @@ obj-$(CONFIG_QCOM_PPE) += qcom-ppe.o
|
|
qcom-ppe-objs := ppe.o ppe_config.o ppe_debugfs.o ppe_port.o
|
|
|
|
#EDMA
|
|
-qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o
|
|
+qcom-ppe-objs += edma.o edma_cfg_rx.o edma_cfg_tx.o edma_debugfs.o edma_port.o edma_rx.o edma_tx.o edma_ethtool.o
|
|
--- a/drivers/net/ethernet/qualcomm/ppe/edma.h
|
|
+++ b/drivers/net/ethernet/qualcomm/ppe/edma.h
|
|
@@ -161,6 +161,7 @@ void edma_destroy(struct ppe_device *ppe
|
|
int edma_setup(struct ppe_device *ppe_dev);
|
|
void edma_debugfs_teardown(void);
|
|
int edma_debugfs_setup(struct ppe_device *ppe_dev);
|
|
+void edma_set_ethtool_ops(struct net_device *netdev);
|
|
int ppe_edma_queue_offset_config(struct ppe_device *ppe_dev,
|
|
enum ppe_queue_class_type class,
|
|
int index, int queue_offset);
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/qualcomm/ppe/edma_ethtool.c
|
|
@@ -0,0 +1,294 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
+ */
|
|
+
|
|
+/* ethtool support for EDMA */
|
|
+
|
|
+#include <linux/cpumask.h>
|
|
+#include <linux/ethtool.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/phylink.h>
|
|
+
|
|
+#include "edma.h"
|
|
+#include "edma_port.h"
|
|
+
|
|
+struct edma_ethtool_stats {
|
|
+ u8 stat_string[ETH_GSTRING_LEN];
|
|
+ u32 stat_offset;
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct edma_gmac_stats - Per-GMAC statistics.
|
|
+ * @rx_packets: Number of RX packets
|
|
+ * @rx_bytes: Number of RX bytes
|
|
+ * @rx_dropped: Number of RX dropped packets
|
|
+ * @rx_fraglist_packets: Number of RX fraglist packets
|
|
+ * @rx_nr_frag_packets: Number of RX nr fragment packets
|
|
+ * @rx_nr_frag_headroom_err: Number of RX nr fragment packets with headroom error
|
|
+ * @tx_packets: Number of TX packets
|
|
+ * @tx_bytes: Number of TX bytes
|
|
+ * @tx_dropped: Number of TX dropped packets
|
|
+ * @tx_nr_frag_packets: Number of TX nr fragment packets
|
|
+ * @tx_fraglist_packets: Number of TX fraglist packets
|
|
+ * @tx_fraglist_with_nr_frags_packets: Number of TX fraglist packets with nr fragments
|
|
+ * @tx_tso_packets: Number of TX TCP segmentation offload packets
|
|
+ * @tx_tso_drop_packets: Number of TX TCP segmentation dropped packets
|
|
+ * @tx_gso_packets: Number of TX SW GSO packets
|
|
+ * @tx_gso_drop_packets: Number of TX SW GSO dropped packets
|
|
+ * @tx_queue_stopped: Number of times Queue got stopped
|
|
+ */
|
|
+struct edma_gmac_stats {
|
|
+ u64 rx_packets;
|
|
+ u64 rx_bytes;
|
|
+ u64 rx_dropped;
|
|
+ u64 rx_fraglist_packets;
|
|
+ u64 rx_nr_frag_packets;
|
|
+ u64 rx_nr_frag_headroom_err;
|
|
+ u64 tx_packets;
|
|
+ u64 tx_bytes;
|
|
+ u64 tx_dropped;
|
|
+ u64 tx_nr_frag_packets;
|
|
+ u64 tx_fraglist_packets;
|
|
+ u64 tx_fraglist_with_nr_frags_packets;
|
|
+ u64 tx_tso_packets;
|
|
+ u64 tx_tso_drop_packets;
|
|
+ u64 tx_gso_packets;
|
|
+ u64 tx_gso_drop_packets;
|
|
+ u64 tx_queue_stopped[EDMA_MAX_CORE];
|
|
+};
|
|
+
|
|
+#define EDMA_STAT(m) offsetof(struct edma_gmac_stats, m)
|
|
+
|
|
+static const struct edma_ethtool_stats edma_gstrings_stats[] = {
|
|
+ {"rx_bytes", EDMA_STAT(rx_bytes)},
|
|
+ {"rx_packets", EDMA_STAT(rx_packets)},
|
|
+ {"rx_dropped", EDMA_STAT(rx_dropped)},
|
|
+ {"rx_fraglist_packets", EDMA_STAT(rx_fraglist_packets)},
|
|
+ {"rx_nr_frag_packets", EDMA_STAT(rx_nr_frag_packets)},
|
|
+ {"rx_nr_frag_headroom_err", EDMA_STAT(rx_nr_frag_headroom_err)},
|
|
+ {"tx_bytes", EDMA_STAT(tx_bytes)},
|
|
+ {"tx_packets", EDMA_STAT(tx_packets)},
|
|
+ {"tx_dropped", EDMA_STAT(tx_dropped)},
|
|
+ {"tx_nr_frag_packets", EDMA_STAT(tx_nr_frag_packets)},
|
|
+ {"tx_fraglist_packets", EDMA_STAT(tx_fraglist_packets)},
|
|
+ {"tx_fraglist_nr_frags_packets", EDMA_STAT(tx_fraglist_with_nr_frags_packets)},
|
|
+ {"tx_tso_packets", EDMA_STAT(tx_tso_packets)},
|
|
+ {"tx_tso_drop_packets", EDMA_STAT(tx_tso_drop_packets)},
|
|
+ {"tx_gso_packets", EDMA_STAT(tx_gso_packets)},
|
|
+ {"tx_gso_drop_packets", EDMA_STAT(tx_gso_drop_packets)},
|
|
+ {"tx_queue_stopped_cpu0", EDMA_STAT(tx_queue_stopped[0])},
|
|
+ {"tx_queue_stopped_cpu1", EDMA_STAT(tx_queue_stopped[1])},
|
|
+ {"tx_queue_stopped_cpu2", EDMA_STAT(tx_queue_stopped[2])},
|
|
+ {"tx_queue_stopped_cpu3", EDMA_STAT(tx_queue_stopped[3])},
|
|
+};
|
|
+
|
|
+#define EDMA_STATS_LEN ARRAY_SIZE(edma_gstrings_stats)
|
|
+
|
|
+static void edma_port_get_stats(struct net_device *netdev,
|
|
+ struct edma_gmac_stats *stats)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct edma_port_rx_stats *pcpu_rx_stats;
|
|
+ struct edma_port_tx_stats *pcpu_tx_stats;
|
|
+ int i;
|
|
+
|
|
+ memset(stats, 0, sizeof(struct edma_port_pcpu_stats));
|
|
+
|
|
+ for_each_possible_cpu(i) {
|
|
+ struct edma_port_rx_stats rxp;
|
|
+ struct edma_port_tx_stats txp;
|
|
+ unsigned int start;
|
|
+
|
|
+ pcpu_rx_stats = per_cpu_ptr(port_priv->pcpu_stats.rx_stats, i);
|
|
+
|
|
+ do {
|
|
+ start = u64_stats_fetch_begin(&pcpu_rx_stats->syncp);
|
|
+ memcpy(&rxp, pcpu_rx_stats, sizeof(*pcpu_rx_stats));
|
|
+ } while (u64_stats_fetch_retry(&pcpu_rx_stats->syncp, start));
|
|
+
|
|
+ stats->rx_packets += rxp.rx_pkts;
|
|
+ stats->rx_bytes += rxp.rx_bytes;
|
|
+ stats->rx_dropped += rxp.rx_drops;
|
|
+ stats->rx_nr_frag_packets += rxp.rx_nr_frag_pkts;
|
|
+ stats->rx_fraglist_packets += rxp.rx_fraglist_pkts;
|
|
+ stats->rx_nr_frag_headroom_err += rxp.rx_nr_frag_headroom_err;
|
|
+
|
|
+ pcpu_tx_stats = per_cpu_ptr(port_priv->pcpu_stats.tx_stats, i);
|
|
+
|
|
+ do {
|
|
+ start = u64_stats_fetch_begin(&pcpu_tx_stats->syncp);
|
|
+ memcpy(&txp, pcpu_tx_stats, sizeof(*pcpu_tx_stats));
|
|
+ } while (u64_stats_fetch_retry(&pcpu_tx_stats->syncp, start));
|
|
+
|
|
+ stats->tx_packets += txp.tx_pkts;
|
|
+ stats->tx_bytes += txp.tx_bytes;
|
|
+ stats->tx_dropped += txp.tx_drops;
|
|
+ stats->tx_nr_frag_packets += txp.tx_nr_frag_pkts;
|
|
+ stats->tx_fraglist_packets += txp.tx_fraglist_pkts;
|
|
+ stats->tx_fraglist_with_nr_frags_packets += txp.tx_fraglist_with_nr_frags_pkts;
|
|
+ stats->tx_tso_packets += txp.tx_tso_pkts;
|
|
+ stats->tx_tso_drop_packets += txp.tx_tso_drop_pkts;
|
|
+ stats->tx_gso_packets += txp.tx_gso_pkts;
|
|
+ stats->tx_gso_drop_packets += txp.tx_gso_drop_pkts;
|
|
+ stats->tx_queue_stopped[i] += txp.tx_queue_stopped[i];
|
|
+ }
|
|
+}
|
|
+
|
|
+static void edma_get_ethtool_stats(struct net_device *netdev,
|
|
+ __maybe_unused struct ethtool_stats *stats,
|
|
+ u64 *data)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct edma_gmac_stats edma_stats;
|
|
+ u64 *mib_data;
|
|
+ int i;
|
|
+ u8 *p;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return;
|
|
+
|
|
+ /* Get the DMA Driver statistics from the data plane if available. */
|
|
+ memset(&edma_stats, 0, sizeof(struct edma_gmac_stats));
|
|
+ edma_port_get_stats(netdev, &edma_stats);
|
|
+
|
|
+ /* Populate data plane statistics. */
|
|
+ for (i = 0; i < EDMA_STATS_LEN; i++) {
|
|
+ p = ((u8 *)(&edma_stats) + edma_gstrings_stats[i].stat_offset);
|
|
+ data[i] = *(u64 *)p;
|
|
+ }
|
|
+
|
|
+ /* Get the GMAC MIB statistics along with the DMA driver statistics. */
|
|
+ mib_data = &data[EDMA_STATS_LEN];
|
|
+ ppe_port_get_ethtool_stats(port_priv->ppe_port, mib_data);
|
|
+}
|
|
+
|
|
+static int edma_get_strset_count(struct net_device *netdev, int sset)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ int sset_count = 0;
|
|
+
|
|
+ if (!port_priv || sset != ETH_SS_STATS)
|
|
+ return 0;
|
|
+
|
|
+ sset_count = ppe_port_get_sset_count(port_priv->ppe_port, sset);
|
|
+
|
|
+ return (EDMA_STATS_LEN + sset_count);
|
|
+}
|
|
+
|
|
+static void edma_get_strings(struct net_device *netdev, u32 stringset,
|
|
+ u8 *data)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ int i;
|
|
+
|
|
+ if (!port_priv || stringset != ETH_SS_STATS)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < EDMA_STATS_LEN; i++) {
|
|
+ memcpy(data, edma_gstrings_stats[i].stat_string,
|
|
+ strlen(edma_gstrings_stats[i].stat_string));
|
|
+ data += ETH_GSTRING_LEN;
|
|
+ }
|
|
+
|
|
+ ppe_port_get_strings(port_priv->ppe_port, stringset, data);
|
|
+}
|
|
+
|
|
+static int edma_get_link_ksettings(struct net_device *netdev,
|
|
+ struct ethtool_link_ksettings *cmd)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return phylink_ethtool_ksettings_get(port->phylink, cmd);
|
|
+}
|
|
+
|
|
+static int edma_set_link_ksettings(struct net_device *netdev,
|
|
+ const struct ethtool_link_ksettings *cmd)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return phylink_ethtool_ksettings_set(port->phylink, cmd);
|
|
+}
|
|
+
|
|
+static void edma_get_pauseparam(struct net_device *netdev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return;
|
|
+
|
|
+ phylink_ethtool_get_pauseparam(port->phylink, pause);
|
|
+}
|
|
+
|
|
+static int edma_set_pauseparam(struct net_device *netdev,
|
|
+ struct ethtool_pauseparam *pause)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return phylink_ethtool_set_pauseparam(port->phylink, pause);
|
|
+}
|
|
+
|
|
+static int edma_get_eee(struct net_device *netdev, struct ethtool_keee *eee)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return phylink_ethtool_get_eee(port->phylink, eee);
|
|
+}
|
|
+
|
|
+static int edma_set_eee(struct net_device *netdev, struct ethtool_keee *eee)
|
|
+{
|
|
+ struct edma_port_priv *port_priv = (struct edma_port_priv *)netdev_priv(netdev);
|
|
+ struct ppe_port *port = port_priv->ppe_port;
|
|
+ int ret;
|
|
+
|
|
+ if (!port_priv)
|
|
+ return -EINVAL;
|
|
+
|
|
+ ret = ppe_port_set_mac_eee(port_priv->ppe_port, eee);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return phylink_ethtool_set_eee(port->phylink, eee);
|
|
+}
|
|
+
|
|
+static const struct ethtool_ops edma_ethtool_ops = {
|
|
+ .get_strings = &edma_get_strings,
|
|
+ .get_sset_count = &edma_get_strset_count,
|
|
+ .get_ethtool_stats = &edma_get_ethtool_stats,
|
|
+ .get_link = ð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))
|