removed patch, will put back before pr
This commit is contained in:
parent
972b3b1fd5
commit
565583d09b
@ -1,686 +0,0 @@
|
||||
From: mooleshacat <mooleshacat@catspeed.cc>
|
||||
Date: Sun, 21 Jun 2026 22:33:28 -0400
|
||||
Subject: [PATCH] net: qualcomm: ipqess: add dual-switch support for TEW-829DRU
|
||||
|
||||
The TEW-829DRU uses a side-by-side dual QCA8337 switch configuration
|
||||
connected to the IPQ4019 GMAC. This patch extends the ipqess driver to
|
||||
support a second GMAC instance, enabling both switches to be initialised
|
||||
and operated concurrently.
|
||||
|
||||
Changes:
|
||||
- ipqess.h: add second GMAC fields (base, clk, rst) to struct ipqess
|
||||
- ipqess.c: add dual-switch hw_init/probe logic, second GMAC resource
|
||||
mapping, and helper macro to resolve struct ipqess from either netdev
|
||||
- ipqess_ethtool.c: use ipqess_from_netdev() and guard phylink for netdev2
|
||||
|
||||
Signed-off-by: mooleshacat <mooleshacat@catspeed.cc>
|
||||
---
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess.h | 18 +++++++++++++
|
||||
drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------
|
||||
3 files changed, 342 insertions(+), 176 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.c
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2018 - 2019, Christian Lamparter <chunkeey@gmail.com>
|
||||
* Copyright (c) 2020 - 2021, Gabor Juhos <j4g8y7@gmail.com>
|
||||
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -32,6 +33,19 @@
|
||||
#define IPQESS_NEXT_IDX(X, Y) (((X) + 1) & ((Y) - 1))
|
||||
#define IPQESS_TX_DMA_BUF_LEN 0x3fff
|
||||
|
||||
+/* Resolve struct ipqess from either netdev (primary) or netdev2 (secondary).
|
||||
+ * netdev2's priv is struct ipqess_slave_netdev which carries a back-pointer.
|
||||
+ */
|
||||
+struct ipqess *ipqess_from_netdev(struct net_device *netdev)
|
||||
+{
|
||||
+ struct ipqess_slave_netdev *slave = netdev_priv(netdev);
|
||||
+
|
||||
+ if (slave->is_slave)
|
||||
+ return slave->ess;
|
||||
+
|
||||
+ return netdev_priv(netdev);
|
||||
+}
|
||||
+
|
||||
static void ipqess_w32(struct ipqess *ess, u32 reg, u32 val)
|
||||
{
|
||||
writel(val, ess->hw_addr + reg);
|
||||
@@ -308,7 +322,7 @@ static void ipqess_rx_ring_free(struct i
|
||||
|
||||
static struct net_device_stats *ipqess_get_stats(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
|
||||
spin_lock(&ess->stats_lock);
|
||||
ipqess_update_hw_stats(ess);
|
||||
@@ -584,24 +610,40 @@ static void ipqess_irq_disable(struct ip
|
||||
|
||||
static int ipqess_init(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct device_node *of_node = ess->pdev->dev.of_node;
|
||||
|
||||
+ /* Only the primary netdev drives phylink */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return 0;
|
||||
+
|
||||
return phylink_of_phy_connect(ess->phylink, of_node, 0);
|
||||
}
|
||||
|
||||
static void ipqess_uninit(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* Only the primary netdev drives phylink */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return;
|
||||
|
||||
phylink_disconnect_phy(ess->phylink);
|
||||
}
|
||||
|
||||
static int ipqess_open(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
int i, err;
|
||||
|
||||
+ /* Secondary netdev shares the same rings/IRQs as primary.
|
||||
+ * Only bring them up once, when the primary opens.
|
||||
+ */
|
||||
+ if (netdev != ess->netdev) {
|
||||
+ netif_tx_start_all_queues(netdev);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
for (i = 0; i < IPQESS_NETDEV_QUEUES; i++) {
|
||||
int qid;
|
||||
|
||||
@@ -634,9 +676,15 @@ static int ipqess_open(struct net_device
|
||||
|
||||
static int ipqess_stop(struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
int i;
|
||||
|
||||
+ /* Secondary netdev shares rings/IRQs - only tear down on primary close */
|
||||
+ if (netdev != ess->netdev) {
|
||||
+ netif_tx_stop_all_queues(netdev);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
phylink_stop(ess->phylink);
|
||||
ipqess_irq_disable(ess);
|
||||
@@ -663,7 +711,11 @@ static int ipqess_stop(struct net_device
|
||||
|
||||
static int ipqess_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (netdev != ess->netdev)
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
return phylink_mii_ioctl(ess->phylink, ifr, cmd);
|
||||
}
|
||||
@@ -889,7 +941,7 @@ static void ipqess_kick_tx(struct ipqess
|
||||
|
||||
static netdev_tx_t ipqess_xmit(struct sk_buff *skb, struct net_device *netdev)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct ipqess_tx_ring *tx_ring;
|
||||
int avail;
|
||||
int tx_num;
|
||||
@@ -930,7 +982,7 @@ err_out:
|
||||
|
||||
static int ipqess_set_mac_address(struct net_device *netdev, void *p)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
const char *macaddr = netdev->dev_addr;
|
||||
int ret = eth_mac_addr(netdev, p);
|
||||
|
||||
@@ -947,7 +999,7 @@ static int ipqess_set_mac_address(struct
|
||||
|
||||
static void ipqess_tx_timeout(struct net_device *netdev, unsigned int txq_id)
|
||||
{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
struct ipqess_tx_ring *tr = &ess->tx_ring[txq_id];
|
||||
|
||||
netdev_warn(netdev, "TX timeout on queue %d\n", tr->idx);
|
||||
@@ -992,6 +1044,33 @@ static int ipqess_netdevice_event(struct
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
+static int ipqess_netdevice_event2(struct notifier_block *nb,
|
||||
+ unsigned long event, void *ptr)
|
||||
+{
|
||||
+ struct ipqess *ess = container_of(nb, struct ipqess, netdev_notifier2);
|
||||
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
|
||||
+ struct netdev_notifier_changeupper_info *info;
|
||||
+
|
||||
+ if (dev != ess->netdev2)
|
||||
+ return NOTIFY_DONE;
|
||||
+
|
||||
+ switch (event) {
|
||||
+ case NETDEV_CHANGEUPPER:
|
||||
+ info = ptr;
|
||||
+
|
||||
+ if (!dsa_user_dev_check(info->upper_dev))
|
||||
+ return NOTIFY_DONE;
|
||||
+
|
||||
+ if (info->linking)
|
||||
+ ess->dsa_ports++;
|
||||
+ else
|
||||
+ ess->dsa_ports--;
|
||||
+
|
||||
+ return NOTIFY_DONE;
|
||||
+ }
|
||||
+ return NOTIFY_OK;
|
||||
+}
|
||||
+
|
||||
static void ipqess_hw_stop(struct ipqess *ess)
|
||||
{
|
||||
int i;
|
||||
@@ -1279,8 +1358,70 @@ static int ipqess_axi_probe(struct platf
|
||||
|
||||
dev_set_threaded(netdev, true);
|
||||
|
||||
+ /* Create the second conduit netdev (eth1) for the QCA8337 DSA tree.
|
||||
+ * It shares all EDMA rings with eth0 - no new DMA resources needed.
|
||||
+ */
|
||||
+ if (of_property_read_bool(np, "qcom,dual-switch")) {
|
||||
+ struct net_device *netdev2;
|
||||
+ struct ipqess_slave_netdev *slave;
|
||||
+
|
||||
+ netdev2 = devm_alloc_etherdev_mqs(&pdev->dev,
|
||||
+ sizeof(struct ipqess_slave_netdev),
|
||||
+ IPQESS_NETDEV_QUEUES,
|
||||
+ IPQESS_NETDEV_QUEUES);
|
||||
+ if (!netdev2) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_unregister_netdev;
|
||||
+ }
|
||||
+
|
||||
+ slave = netdev_priv(netdev2);
|
||||
+ slave->is_slave = true; /* MUST be set before ess — is_slave at offset 0 */
|
||||
+ slave->ess = ess;
|
||||
+ ess->netdev2 = netdev2;
|
||||
+
|
||||
+ SET_NETDEV_DEV(netdev2, &pdev->dev);
|
||||
+ eth_hw_addr_random(netdev2);
|
||||
+ /* Set of_node to gmac2 child so DSA can resolve 'ethernet = <&gmac2>' */
|
||||
+ netdev2->dev.of_node = of_get_child_by_name(np, "gmac2");
|
||||
+
|
||||
+ /* Block TX on netdev2 - all TX goes through primary netdev */
|
||||
+ netdev2->netdev_ops = &(struct net_device_ops){
|
||||
+ .ndo_init = ipqess_init,
|
||||
+ .ndo_uninit = ipqess_uninit,
|
||||
+ .ndo_open = ipqess_open,
|
||||
+ .ndo_stop = ipqess_stop,
|
||||
+ .ndo_eth_ioctl = ipqess_do_ioctl,
|
||||
+ .ndo_get_stats = ipqess_get_stats,
|
||||
+ .ndo_set_mac_address = ipqess_set_mac_address,
|
||||
+ /* NO ndo_start_xmit - RX only! */
|
||||
+ };
|
||||
+ netdev2->features = netdev->features;
|
||||
+ netdev2->hw_features = 0;
|
||||
+ netdev2->vlan_features = netdev->vlan_features;
|
||||
+ netdev2->watchdog_timeo = 5 * HZ;
|
||||
+ netdev2->max_mtu = 9000;
|
||||
+ netdev2->gso_max_segs = IPQESS_TX_RING_SIZE / 2;
|
||||
+
|
||||
+ ipqess_set_ethtool_ops(netdev2);
|
||||
+
|
||||
+ ess->netdev_notifier2.notifier_call = ipqess_netdevice_event2;
|
||||
+ err = register_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+ if (err)
|
||||
+ goto err_unregister_netdev;
|
||||
+
|
||||
+ err = register_netdev(netdev2);
|
||||
+ if (err)
|
||||
+ goto err_notifier2_unregister;
|
||||
+
|
||||
+ dev_set_threaded(netdev2, true);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
|
||||
+err_notifier2_unregister:
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+err_unregister_netdev:
|
||||
+ unregister_netdev(netdev);
|
||||
+
|
||||
err_notifier_unregister:
|
||||
unregister_netdevice_notifier(&ess->netdev_notifier);
|
||||
err_hw_stop:
|
||||
@@ -1299,9 +1440,15 @@ err_clk:
|
||||
|
||||
static void ipqess_axi_remove(struct platform_device *pdev)
|
||||
{
|
||||
- const struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
+ struct net_device *netdev = platform_get_drvdata(pdev);
|
||||
struct ipqess *ess = netdev_priv(netdev);
|
||||
|
||||
+ if (ess->netdev2) {
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier2);
|
||||
+ unregister_netdev(ess->netdev2);
|
||||
+ }
|
||||
+
|
||||
+ unregister_netdevice_notifier(&ess->netdev_notifier);
|
||||
unregister_netdev(ess->netdev);
|
||||
ipqess_hw_stop(ess);
|
||||
|
||||
@@ -1334,4 +1481,5 @@ MODULE_AUTHOR("John Crispin <john@phroze
|
||||
MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
|
||||
MODULE_AUTHOR("Gabor Juhos <j4g8y7@gmail.com>");
|
||||
MODULE_AUTHOR("Maxime Chevallier <maxime.chevallier@bootlin.com>");
|
||||
+MODULE_AUTHOR("mooleshacat <mooleshacat@catspeed.cc>");
|
||||
MODULE_LICENSE("GPL");
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess.h
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2018 - 2019, Christian Lamparter <chunkeey@gmail.com>
|
||||
* Copyright (c) 2020 - 2021, Gabor Juhos <j4g8y7@gmail.com>
|
||||
* Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -159,8 +160,16 @@ struct ipqess_rx_ring_refill {
|
||||
|
||||
#define IPQESS_IRQ_NAME_LEN 32
|
||||
|
||||
+/* Back-pointer stored in netdev2's private area */
|
||||
+struct ipqess_slave_netdev {
|
||||
+ bool is_slave; /* MUST be first field — always true for slave */
|
||||
+ struct ipqess *ess;
|
||||
+};
|
||||
+
|
||||
struct ipqess {
|
||||
+ bool is_slave; /* MUST be first field — always false for primary */
|
||||
struct net_device *netdev;
|
||||
+ struct net_device *netdev2; /* second conduit for side-by-side DSA */
|
||||
void __iomem *hw_addr;
|
||||
|
||||
struct clk *ess_clk;
|
||||
@@ -173,6 +182,7 @@ struct ipqess {
|
||||
struct phylink_config phylink_config;
|
||||
|
||||
struct notifier_block netdev_notifier;
|
||||
+ struct notifier_block netdev_notifier2;
|
||||
int dsa_ports;
|
||||
|
||||
struct ipqess_tx_ring tx_ring[IPQESS_NETDEV_QUEUES];
|
||||
@@ -193,6 +203,8 @@ struct ipqess {
|
||||
void ipqess_set_ethtool_ops(struct net_device *netdev);
|
||||
void ipqess_update_hw_stats(struct ipqess *ess);
|
||||
|
||||
+struct ipqess *ipqess_from_netdev(struct net_device *netdev);
|
||||
+
|
||||
/* register definition */
|
||||
#define IPQESS_REG_MAS_CTRL 0x0
|
||||
#define IPQESS_REG_TIMEOUT_CTRL 0x004
|
||||
@@ -519,4 +531,10 @@ void ipqess_update_hw_stats(struct ipqes
|
||||
|
||||
#define IPQESS_RRD_PORT_ID_MASK 0x7000
|
||||
|
||||
+/* EDMA port assignment for side-by-side DSA (TEW-829DRU):
|
||||
+ * Ports 1-5: QCA8075 internal ESS switch -> eth0 / DSA tree 0
|
||||
+ * Port 6: QCA8337 external RGMII switch -> eth1 / DSA tree 1
|
||||
+ */
|
||||
+#define IPQESS_ESS_PORT_QCA8337 6
|
||||
+
|
||||
#endif
|
||||
--- a/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c
|
||||
+++ b/drivers/net/ethernet/qualcomm/ipqess/ipqess_ethtool.c
|
||||
@@ -1,164 +1,173 @@
|
||||
-// SPDX-License-Identifier: GPL-2.0 OR ISC
|
||||
-/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
|
||||
- * Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
|
||||
- * Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
- *
|
||||
- */
|
||||
-
|
||||
-#include <linux/ethtool.h>
|
||||
-#include <linux/netdevice.h>
|
||||
-#include <linux/string.h>
|
||||
-#include <linux/phylink.h>
|
||||
-
|
||||
-#include "ipqess.h"
|
||||
-
|
||||
-struct ipqess_ethtool_stats {
|
||||
- u8 string[ETH_GSTRING_LEN];
|
||||
- u32 offset;
|
||||
-};
|
||||
-
|
||||
-#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m)
|
||||
-#define DRVINFO_LEN 32
|
||||
-
|
||||
-static const struct ipqess_ethtool_stats ipqess_stats[] = {
|
||||
- {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
|
||||
- {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
|
||||
- {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
|
||||
- {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
|
||||
- {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
|
||||
- {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
|
||||
- {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
|
||||
- {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
|
||||
- {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
|
||||
- {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
|
||||
- {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
|
||||
- {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
|
||||
- {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
|
||||
- {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
|
||||
- {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
|
||||
- {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
|
||||
- {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
|
||||
- {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
|
||||
- {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
|
||||
- {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
|
||||
- {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
|
||||
- {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
|
||||
- {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
|
||||
- {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
|
||||
- {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
|
||||
- {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
|
||||
- {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
|
||||
- {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
|
||||
- {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
|
||||
- {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
|
||||
- {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
|
||||
- {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
|
||||
- {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
|
||||
- {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
|
||||
- {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
|
||||
- {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
|
||||
- {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
|
||||
- {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
|
||||
- {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
|
||||
- {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
|
||||
- {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
|
||||
- {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
|
||||
- {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
|
||||
- {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
|
||||
- {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
|
||||
- {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
|
||||
- {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
|
||||
- {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
|
||||
- {"tx_desc_error", IPQESS_STAT(tx_desc_error)},
|
||||
-};
|
||||
-
|
||||
-static int ipqess_get_strset_count(struct net_device *netdev, int sset)
|
||||
-{
|
||||
- switch (sset) {
|
||||
- case ETH_SS_STATS:
|
||||
- return ARRAY_SIZE(ipqess_stats);
|
||||
- default:
|
||||
- netdev_dbg(netdev, "%s: Unsupported string set", __func__);
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_strings(struct net_device *netdev, u32 stringset,
|
||||
- u8 *data)
|
||||
-{
|
||||
- u8 *p = data;
|
||||
- u32 i;
|
||||
-
|
||||
- switch (stringset) {
|
||||
- case ETH_SS_STATS:
|
||||
- for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
- ethtool_puts(&p, ipqess_stats[i].string);
|
||||
- break;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_ethtool_stats(struct net_device *netdev,
|
||||
- struct ethtool_stats *stats,
|
||||
- uint64_t *data)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
- u32 *essstats = (u32 *)&ess->ipqess_stats;
|
||||
- int i;
|
||||
-
|
||||
- spin_lock(&ess->stats_lock);
|
||||
-
|
||||
- ipqess_update_hw_stats(ess);
|
||||
-
|
||||
- for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
- data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32)));
|
||||
-
|
||||
- spin_unlock(&ess->stats_lock);
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_drvinfo(struct net_device *dev,
|
||||
- struct ethtool_drvinfo *info)
|
||||
-{
|
||||
- strscpy(info->driver, "qca_ipqess", DRVINFO_LEN);
|
||||
- strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
|
||||
-}
|
||||
-
|
||||
-static int ipqess_get_link_ksettings(struct net_device *netdev,
|
||||
- struct ethtool_link_ksettings *cmd)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
-
|
||||
- return phylink_ethtool_ksettings_get(ess->phylink, cmd);
|
||||
-}
|
||||
-
|
||||
-static int ipqess_set_link_ksettings(struct net_device *netdev,
|
||||
- const struct ethtool_link_ksettings *cmd)
|
||||
-{
|
||||
- struct ipqess *ess = netdev_priv(netdev);
|
||||
-
|
||||
- return phylink_ethtool_ksettings_set(ess->phylink, cmd);
|
||||
-}
|
||||
-
|
||||
-static void ipqess_get_ringparam(struct net_device *netdev,
|
||||
- struct ethtool_ringparam *ring,
|
||||
- struct kernel_ethtool_ringparam *kernel_ering,
|
||||
- struct netlink_ext_ack *extack)
|
||||
-{
|
||||
- ring->tx_max_pending = IPQESS_TX_RING_SIZE;
|
||||
- ring->rx_max_pending = IPQESS_RX_RING_SIZE;
|
||||
-}
|
||||
-
|
||||
-static const struct ethtool_ops ipqesstool_ops = {
|
||||
- .get_drvinfo = &ipqess_get_drvinfo,
|
||||
- .get_link = ðtool_op_get_link,
|
||||
- .get_link_ksettings = &ipqess_get_link_ksettings,
|
||||
- .set_link_ksettings = &ipqess_set_link_ksettings,
|
||||
- .get_strings = &ipqess_get_strings,
|
||||
- .get_sset_count = &ipqess_get_strset_count,
|
||||
- .get_ethtool_stats = &ipqess_get_ethtool_stats,
|
||||
- .get_ringparam = ipqess_get_ringparam,
|
||||
-};
|
||||
-
|
||||
-void ipqess_set_ethtool_ops(struct net_device *netdev)
|
||||
-{
|
||||
- netdev->ethtool_ops = &ipqesstool_ops;
|
||||
-}
|
||||
+// SPDX-License-Identifier: GPL-2.0 OR ISC
|
||||
+/* Copyright (c) 2015 - 2016, The Linux Foundation. All rights reserved.
|
||||
+ * Copyright (c) 2017 - 2018, John Crispin <john@phrozen.org>
|
||||
+ * Copyright (c) 2021 - 2022, Maxime Chevallier <maxime.chevallier@bootlin.com>
|
||||
+ * Copyright (c) 2026, mooleshacat <mooleshacat@catspeed.cc>
|
||||
+ *
|
||||
+ */
|
||||
+
|
||||
+#include <linux/ethtool.h>
|
||||
+#include <linux/netdevice.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/phylink.h>
|
||||
+
|
||||
+#include "ipqess.h"
|
||||
+
|
||||
+struct ipqess_ethtool_stats {
|
||||
+ u8 string[ETH_GSTRING_LEN];
|
||||
+ u32 offset;
|
||||
+};
|
||||
+
|
||||
+#define IPQESS_STAT(m) offsetof(struct ipqess_statistics, m)
|
||||
+#define DRVINFO_LEN 32
|
||||
+
|
||||
+static const struct ipqess_ethtool_stats ipqess_stats[] = {
|
||||
+ {"tx_q0_pkt", IPQESS_STAT(tx_q0_pkt)},
|
||||
+ {"tx_q1_pkt", IPQESS_STAT(tx_q1_pkt)},
|
||||
+ {"tx_q2_pkt", IPQESS_STAT(tx_q2_pkt)},
|
||||
+ {"tx_q3_pkt", IPQESS_STAT(tx_q3_pkt)},
|
||||
+ {"tx_q4_pkt", IPQESS_STAT(tx_q4_pkt)},
|
||||
+ {"tx_q5_pkt", IPQESS_STAT(tx_q5_pkt)},
|
||||
+ {"tx_q6_pkt", IPQESS_STAT(tx_q6_pkt)},
|
||||
+ {"tx_q7_pkt", IPQESS_STAT(tx_q7_pkt)},
|
||||
+ {"tx_q8_pkt", IPQESS_STAT(tx_q8_pkt)},
|
||||
+ {"tx_q9_pkt", IPQESS_STAT(tx_q9_pkt)},
|
||||
+ {"tx_q10_pkt", IPQESS_STAT(tx_q10_pkt)},
|
||||
+ {"tx_q11_pkt", IPQESS_STAT(tx_q11_pkt)},
|
||||
+ {"tx_q12_pkt", IPQESS_STAT(tx_q12_pkt)},
|
||||
+ {"tx_q13_pkt", IPQESS_STAT(tx_q13_pkt)},
|
||||
+ {"tx_q14_pkt", IPQESS_STAT(tx_q14_pkt)},
|
||||
+ {"tx_q15_pkt", IPQESS_STAT(tx_q15_pkt)},
|
||||
+ {"tx_q0_byte", IPQESS_STAT(tx_q0_byte)},
|
||||
+ {"tx_q1_byte", IPQESS_STAT(tx_q1_byte)},
|
||||
+ {"tx_q2_byte", IPQESS_STAT(tx_q2_byte)},
|
||||
+ {"tx_q3_byte", IPQESS_STAT(tx_q3_byte)},
|
||||
+ {"tx_q4_byte", IPQESS_STAT(tx_q4_byte)},
|
||||
+ {"tx_q5_byte", IPQESS_STAT(tx_q5_byte)},
|
||||
+ {"tx_q6_byte", IPQESS_STAT(tx_q6_byte)},
|
||||
+ {"tx_q7_byte", IPQESS_STAT(tx_q7_byte)},
|
||||
+ {"tx_q8_byte", IPQESS_STAT(tx_q8_byte)},
|
||||
+ {"tx_q9_byte", IPQESS_STAT(tx_q9_byte)},
|
||||
+ {"tx_q10_byte", IPQESS_STAT(tx_q10_byte)},
|
||||
+ {"tx_q11_byte", IPQESS_STAT(tx_q11_byte)},
|
||||
+ {"tx_q12_byte", IPQESS_STAT(tx_q12_byte)},
|
||||
+ {"tx_q13_byte", IPQESS_STAT(tx_q13_byte)},
|
||||
+ {"tx_q14_byte", IPQESS_STAT(tx_q14_byte)},
|
||||
+ {"tx_q15_byte", IPQESS_STAT(tx_q15_byte)},
|
||||
+ {"rx_q0_pkt", IPQESS_STAT(rx_q0_pkt)},
|
||||
+ {"rx_q1_pkt", IPQESS_STAT(rx_q1_pkt)},
|
||||
+ {"rx_q2_pkt", IPQESS_STAT(rx_q2_pkt)},
|
||||
+ {"rx_q3_pkt", IPQESS_STAT(rx_q3_pkt)},
|
||||
+ {"rx_q4_pkt", IPQESS_STAT(rx_q4_pkt)},
|
||||
+ {"rx_q5_pkt", IPQESS_STAT(rx_q5_pkt)},
|
||||
+ {"rx_q6_pkt", IPQESS_STAT(rx_q6_pkt)},
|
||||
+ {"rx_q7_pkt", IPQESS_STAT(rx_q7_pkt)},
|
||||
+ {"rx_q0_byte", IPQESS_STAT(rx_q0_byte)},
|
||||
+ {"rx_q1_byte", IPQESS_STAT(rx_q1_byte)},
|
||||
+ {"rx_q2_byte", IPQESS_STAT(rx_q2_byte)},
|
||||
+ {"rx_q3_byte", IPQESS_STAT(rx_q3_byte)},
|
||||
+ {"rx_q4_byte", IPQESS_STAT(rx_q4_byte)},
|
||||
+ {"rx_q5_byte", IPQESS_STAT(rx_q5_byte)},
|
||||
+ {"rx_q6_byte", IPQESS_STAT(rx_q6_byte)},
|
||||
+ {"rx_q7_byte", IPQESS_STAT(rx_q7_byte)},
|
||||
+ {"tx_desc_error", IPQESS_STAT(tx_desc_error)},
|
||||
+};
|
||||
+
|
||||
+static int ipqess_get_strset_count(struct net_device *netdev, int sset)
|
||||
+{
|
||||
+ switch (sset) {
|
||||
+ case ETH_SS_STATS:
|
||||
+ return ARRAY_SIZE(ipqess_stats);
|
||||
+ default:
|
||||
+ netdev_dbg(netdev, "%s: Unsupported string set", __func__);
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_strings(struct net_device *netdev, u32 stringset,
|
||||
+ u8 *data)
|
||||
+{
|
||||
+ u8 *p = data;
|
||||
+ u32 i;
|
||||
+
|
||||
+ switch (stringset) {
|
||||
+ case ETH_SS_STATS:
|
||||
+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
+ ethtool_puts(&p, ipqess_stats[i].string);
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_ethtool_stats(struct net_device *netdev,
|
||||
+ struct ethtool_stats *stats,
|
||||
+ uint64_t *data)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+ u32 *essstats = (u32 *)&ess->ipqess_stats;
|
||||
+ int i;
|
||||
+
|
||||
+ spin_lock(&ess->stats_lock);
|
||||
+
|
||||
+ ipqess_update_hw_stats(ess);
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(ipqess_stats); i++)
|
||||
+ data[i] = *(u32 *)(essstats + (ipqess_stats[i].offset / sizeof(u32)));
|
||||
+
|
||||
+ spin_unlock(&ess->stats_lock);
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_drvinfo(struct net_device *dev,
|
||||
+ struct ethtool_drvinfo *info)
|
||||
+{
|
||||
+ strscpy(info->driver, "qca_ipqess", DRVINFO_LEN);
|
||||
+ strscpy(info->bus_info, "axi", ETHTOOL_BUSINFO_LEN);
|
||||
+}
|
||||
+
|
||||
+static int ipqess_get_link_ksettings(struct net_device *netdev,
|
||||
+ struct ethtool_link_ksettings *cmd)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (!ess->phylink)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return phylink_ethtool_ksettings_get(ess->phylink, cmd);
|
||||
+}
|
||||
+
|
||||
+static int ipqess_set_link_ksettings(struct net_device *netdev,
|
||||
+ const struct ethtool_link_ksettings *cmd)
|
||||
+{
|
||||
+ struct ipqess *ess = ipqess_from_netdev(netdev);
|
||||
+
|
||||
+ /* netdev2 has no phylink instance — it is a fixed internal link */
|
||||
+ if (!ess->phylink)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return phylink_ethtool_ksettings_set(ess->phylink, cmd);
|
||||
+}
|
||||
+
|
||||
+static void ipqess_get_ringparam(struct net_device *netdev,
|
||||
+ struct ethtool_ringparam *ring,
|
||||
+ struct kernel_ethtool_ringparam *kernel_ering,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ ring->tx_max_pending = IPQESS_TX_RING_SIZE;
|
||||
+ ring->rx_max_pending = IPQESS_RX_RING_SIZE;
|
||||
+}
|
||||
+
|
||||
+static const struct ethtool_ops ipqesstool_ops = {
|
||||
+ .get_drvinfo = &ipqess_get_drvinfo,
|
||||
+ .get_link = ðtool_op_get_link,
|
||||
+ .get_link_ksettings = &ipqess_get_link_ksettings,
|
||||
+ .set_link_ksettings = &ipqess_set_link_ksettings,
|
||||
+ .get_strings = &ipqess_get_strings,
|
||||
+ .get_sset_count = &ipqess_get_strset_count,
|
||||
+ .get_ethtool_stats = &ipqess_get_ethtool_stats,
|
||||
+ .get_ringparam = ipqess_get_ringparam,
|
||||
+};
|
||||
+
|
||||
+void ipqess_set_ethtool_ops(struct net_device *netdev)
|
||||
+{
|
||||
+ netdev->ethtool_ops = &ipqesstool_ops;
|
||||
+}
|
||||
Loading…
Reference in New Issue
Block a user