diff --git a/target/linux/ipq40xx/patches-6.12/713-net-qualcomm-ipqess-dual-switch-support.patch b/target/linux/ipq40xx/patches-6.12/713-net-qualcomm-ipqess-dual-switch-support.patch deleted file mode 100644 index bcbd35d23e..0000000000 --- a/target/linux/ipq40xx/patches-6.12/713-net-qualcomm-ipqess-dual-switch-support.patch +++ /dev/null @@ -1,686 +0,0 @@ -From: mooleshacat -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 ---- - 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 - * Copyright (c) 2020 - 2021, Gabor Juhos - * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * Copyright (c) 2026, mooleshacat - * - */ - -@@ -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 "); - MODULE_AUTHOR("Gabor Juhos "); - MODULE_AUTHOR("Maxime Chevallier "); -+MODULE_AUTHOR("mooleshacat "); - 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 - * Copyright (c) 2020 - 2021, Gabor Juhos - * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * Copyright (c) 2026, mooleshacat - * - */ - -@@ -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 -- * Copyright (c) 2021 - 2022, Maxime Chevallier -- * -- */ -- --#include --#include --#include --#include -- --#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 -+ * Copyright (c) 2021 - 2022, Maxime Chevallier -+ * Copyright (c) 2026, mooleshacat -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+ -+#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; -+}