summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/stmicro
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
commit734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch)
treece2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/ethernet/stmicro
parent864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff)
parent1a2c35c49cc7c6be860038b8442b52424a8f123c (diff)
downloadlinux-next-stable.tar.gz
linux-next-stable.zip
Merge tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextstable
Pull networking updates from Paolo Abeni: "This includes a significant number of low-priority fixes - especially in the NIC drivers area - that we deferred from the last few RCs in the previous cycle, which can't be easily outlined in the synopsis below. Core & protocols: - Continue the work on removing rtnl_lock dependencies: - per-netns ARP and ND neighbour tables, with the default settings now configurable per netns (inheritance from init_net is controlled by the new net.core.neigh_inherit_init_net sysctl) - RCU-protected configuration and rtnl_lock-less fill_info() for hsr, netkit, sit, vlan and vxlan devices - per-netns device unregistration for ip tunnel and gre devices - use the netdev instance lock instead of rtnl_lock when reading speed and duplex via sysfs - batch the flushing of uncached routes at device unregistration time, relieving rtnl_lock contention on netns dismantle. - Add hardware pacing offload support, admin-controlled via a new rtnetlink attribute, with sch_fq integration (implemented in idpf). - Re-segment BIG TCP GSO packets into GSO packets fitting the egress device limits instead of plain MSS-sized segments, improving throughput for mixed BIG TCP / non-BIG TCP paths by up to 70%. - Generate RSS keys that spread flows over all the RX queues; the previous random keys could leave some queues unused. - Optimize the bridge VLAN forwarding paths: cache the port-VLAN in fdb entries and consider only VLAN members when flooding, for ~10% more Mpps in the fdb forwarding path. - A few TCP/IP performance improvements aimed at efficient cache usage and reducing contention. - Add the TCP ROCCET congestion control, a CUBIC variant aimed at avoiding bufferbloat in cellular networks. - Continue the conversion of getsockopt callbacks to sockopt_t. - Add an ethtool command to list the front-facing ports of an interface, including SFP cages and modules, using the phy_port representation. - Enable BIG TCP support for bareudp tunnels. - Remove the IPV6_ADDRFORM socket option and disallow buggy TCP state transitions (listen() + shutdown() + connect() and connect() + connect(AF_UNSPEC) + listen()). - Remove most BUG_ON()s from core packet processing, replacing them with error paths. Wireless: - Latency improvements, including lockless station get/dump and beacon updates, plus RTNL reduction and locking cleanups. - Infrastructure for non-station TX/RX with MLO. - NAN (Wi-Fi Aware) Instant Communication support and other NAN improvements. - Control frame Integrity Protocol (CIP) and protected TWT support. - Improvements for AP regulatory checks. Netfilter: - Disable br_netfilter in user namespaces and schedule it for removal in 2027. - Restrict the raw table in nft_compat to the specific family, hook and priority. - Prepare the flowtable for mixed IPv4-over-IPv6 tunneling (and vice versa). - Use GFP_KERNEL_ACCOUNT for most netfilter allocations, for better integration with memcg. - Allow discovering conntrack zone filtering support in ctnetlink dumps, and fix nfnetlink_hook handling of large dumps. Deletions: - Move the LLC type 2 sockets out of tree. There are no known users and the code attracts a never-ending stream of LLM-driven fixes. Device drivers: - New drivers: - Meta Platforms NIC (mpnic), the second-generation NIC from Meta - initial skeleton of the Nebula-matrix NICs driver (nbl) - X-Powers AC200/AC300 Fast Ethernet PHYs - Ethernet high-speed NICs: - Intel: - i40e: XDP metadata support for RX hash and VLAN tag - ice: expose the E825 TSPLL state via the DPLL subsystem - nVidia/Mellanox: - move the data direct infrastructure from IB to core, in preparation for netdev support. - reduce the memory used per offloaded TC rule - Meta (fbnic): - support large io_uring zero-copy RX buffers - Microsoft vNIC (mana): - add BQL support - Ethernet NICs consumer, and embedded: - Airoha: - LRO offload support - Cadence (macb): - context swapping for ringparam and MTU changes, avoiding buffer loss under memory pressure - Mucse (rnpgbe): - TX/RX and link status support - NXP (enetc): - SR-IOV improvements and VF support for ENETC v4 - Qualcomm (ppe): - IPQ5424 support - Realtek PCI (r8169): - phylink support - RSS support for RTL8127 - Renesas (ravb): - gPTP support for R-Car Gen4 - Synopsys (stmmac): - XDP multi-buffer support on the TX side - tc mqprio hw queue priority offload - support for RK3568 SGMII, Allwinner H616 EMAC1, UltraRISC DP1000 and Qualcomm sa8255p - Ethernet switches: - Mediatek (mt7530): - fast age and change conduit support - Microchip (ksz): - support for KSZ8995XA/KS8995XA - Microchip (lan966x): - PCIe FDMA support, ~20x throughput increase in PCIe endpoint - Microchip (sparx5): - Time Aware Shaper (taprio) offload - Motorcomm (yt921x): - LED and SerDes PCS support - Realtek (rtl8365mb): - EEE support - Ethernet PHYs: - Broadcom: - enable jumbo frames on BCM54xx - Realtek: - support for RTL8261CE_CG and RTL8261D_VM - MDIO support for RTL838x and RTL839x - WiFi drivers: - Broadcom (brcmfmac): - FT/OKC roaming offload - Intel (iwlwifi): - UHR sniffer support - support for newer UEFI/BIOS tables - MediaTek (mt76): - mt7925 NAN improvements - offchannel/scan improvements and MLO fixes - Morse Micro (mm81x): - monitor mode support - Qualcomm (ath12k): - IPQ5332 platform infrastructure - Realtek (rtw88): - RTL8723BS support - SDIO TX performance improvements - Realtek (rtw89): - P2P_DEVICE support - BT-coex updates for RTL8922D - Bluetooth: - move H:4 reassembly into the Bluetooth core - add generic support for vendor packets - MGMT: add security level changed event - Bluetooth drivers: - btusb: support Bulk Serialization Mode - Intel: support Draco and Titan Lake, allow unsigned firmware - Mediatek: MT7928 support - Qualcomm: QCC2072 support - Broadcom: Synaptics 4384 support" * tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2126 commits) Bluetooth: btusb: disable by default Qualcomm multi-subsystem tipc: fix broadcast sender hang on last acker departure tipc: fix use-after-free of broadcast reception link at node release r8169: clear TX LPI on RTL8168h at link up after phylink conversion tun: Require CAP_NET_ADMIN to attach to an ownerless device net/sched/sch_cake: drop dead nla_nest_end() error check net/sched/sch_cake: widen buffer_used to u64 net/sched/sch_cake: reduce parent backlog when clearing a tin net/sched/sch_cake: serialize reconfiguration with the datapath tg3: Add Datto SIRIS 5 1U Server to restart AER quirk table net/mlx5e: Stop asserting for RTNL when closing channels net: shrink the payload-only part of skb dataref to one bit net: wwan: iosm: validate msg_head before indexing msg_ring net: bgmac: rate limit RX error messages selftests: net: veth: test XDP_TX to a peer with GRO enabled veth: schedule the peer's NAPI after XDP_TX when only GRO is enabled pppoe: delete session hash entries by pointer match net: lan969x: don't disable hardirqs in fdma tx clear net: sparx5/lan969x: do not clobber the PSFP cycle timer update period net: lan969x: fix DSM calendar for empty taxis ...
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile1
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c44
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c265
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c131
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c77
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ultrarisc.c52
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c88
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c18
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h18
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c17
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c495
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c39
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c17
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c120
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c137
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c2
30 files changed, 1094 insertions, 539 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index e3dd5adda5ac..c6559e749c0c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -170,6 +170,7 @@ config DWMAC_ROCKCHIP
default ARCH_ROCKCHIP
depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
select MFD_SYSCON
+ select PCS_XPCS_ROCKCHIP
help
Support for Ethernet controller on Rockchip RK3288 SoC.
@@ -319,6 +320,17 @@ config DWMAC_THEAD
the stmmac device driver. This driver is used for T-HEAD TH1520
ethernet controller.
+config DWMAC_ULTRARISC
+ tristate "UltraRISC DWMAC support"
+ default ARCH_ULTRARISC
+ depends on OF && (ARCH_ULTRARISC || COMPILE_TEST)
+ help
+ Support for the Ethernet controller on the UltraRISC DP1000 SoC.
+
+ This selects the UltraRISC platform specific glue layer support
+ for the stmmac device driver. This driver is used for the DP1000
+ GMAC Ethernet controller.
+
config DWMAC_IMX8
tristate "NXP IMX8 DWMAC support"
default ARCH_MXC
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index a1cea2f57252..b16a456b730e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
obj-$(CONFIG_DWMAC_SUN8I) += dwmac-sun8i.o
obj-$(CONFIG_DWMAC_SUN55I) += dwmac-sun55i.o
obj-$(CONFIG_DWMAC_THEAD) += dwmac-thead.o
+obj-$(CONFIG_DWMAC_ULTRARISC) += dwmac-ultrarisc.o
obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o
obj-$(CONFIG_DWMAC_INTEL_PLAT) += dwmac-intel-plat.o
obj-$(CONFIG_DWMAC_LOONGSON1) += dwmac-loongson1.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 927ea6230073..c74bc7c9750e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -158,7 +158,6 @@ struct stmmac_extra_stats {
unsigned long fatal_bus_error_irq;
/* Tx/Rx IRQ Events */
unsigned long rx_early_irq;
- unsigned long threshold;
unsigned long irq_receive_pmt_irq_n;
/* MMC info */
unsigned long mmc_tx_irq_n;
@@ -419,6 +418,8 @@ struct dma_features {
unsigned int half_duplex;
unsigned int hash_filter;
unsigned int multi_addr;
+ unsigned int additional_32_addr;
+ unsigned int additional_64_addr;
unsigned int pcs;
unsigned int sma_mdio;
unsigned int pmt_remote_wake_up;
@@ -627,6 +628,7 @@ struct mac_device_info {
void __iomem *pcsr; /* vpointer to device CSRs */
unsigned int multicast_filter_bins;
unsigned int unicast_filter_entries;
+ unsigned int multi_addr;
unsigned int mcast_bits_log2;
unsigned int rx_csum;
unsigned int num_vlan;
@@ -635,6 +637,8 @@ struct mac_device_info {
u8 vlan_fail_q;
bool hw_vlan_en;
bool reverse_sgmii_enable;
+ bool additional_32_addr;
+ bool additional_64_addr;
/* This spinlock protects read-modify-write of the interrupt
* mask/enable registers.
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
index eab8c13fbdcc..0d654ec07f33 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
@@ -198,18 +198,9 @@ static int eic7700_dwmac_probe(struct platform_device *pdev)
/*
* The MAC silicon unconditionally adds ~2 ns TX delay; prevent
* the PHY from also adding TX delay to avoid doubling it.
- *
- * DT specifies rgmii-id (TX from MAC silicon, RX from PHY);
- * override to rgmii-rxid so the PHY only adds its RX delay.
+ * The common platform code adjusts the PHY interface accordingly.
*/
- if (data->has_internal_tx_delay) {
- plat_dat->phy_interface =
- phy_fix_phy_mode_for_mac_delays(plat_dat->phy_interface,
- true, false);
- if (plat_dat->phy_interface == PHY_INTERFACE_MODE_NA)
- return dev_err_probe(&pdev->dev, -EINVAL,
- "phy interface mode is NA\n");
- }
+ plat_dat->has_internal_tx_delay = data->has_internal_tx_delay;
/* Read rx-internal-delay-ps and update rx_clk delay */
if (!of_property_read_u32(pdev->dev.of_node,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index f5f9fa67ecd7..4308dccbf257 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -603,13 +603,47 @@ static void common_default_data(struct plat_stmmacenet_data *plat)
plat->mdio_bus_data->needs_reset = true;
}
+static int intel_mgbe_pcs_init(struct stmmac_priv *priv)
+{
+ struct fwnode_handle *devnode, *pcsnode;
+ struct dw_xpcs *xpcs;
+ int addr;
+
+ devnode = dev_fwnode(priv->device);
+
+ if (fwnode_property_present(devnode, "pcs-handle")) {
+ pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
+ xpcs = xpcs_create_fwnode(pcsnode);
+ fwnode_handle_put(pcsnode);
+ } else {
+ addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
+ xpcs = xpcs_create_mdiodev(priv->mii, addr);
+ }
+
+ if (IS_ERR(xpcs))
+ return PTR_ERR(xpcs);
+
+ xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
+
+ priv->hw->xpcs = xpcs;
+ return 0;
+}
+
+static void intel_mgbe_pcs_exit(struct stmmac_priv *priv)
+{
+ if (!priv->hw->xpcs)
+ return;
+
+ xpcs_destroy(priv->hw->xpcs);
+ priv->hw->xpcs = NULL;
+}
+
static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv,
phy_interface_t interface)
{
- /* plat->mdio_bus_data->has_xpcs has been set true, so there
- * should always be an XPCS. The original code would always
- * return this if present.
- */
+ if (!priv->hw->xpcs)
+ return NULL;
+
return xpcs_to_phylink_pcs(priv->hw->xpcs);
}
@@ -733,6 +767,8 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->phy_interface == PHY_INTERFACE_MODE_1000BASEX) {
plat->mdio_bus_data->pcs_mask = BIT_U32(INTEL_MGBE_XPCS_ADDR);
plat->default_an_inband = true;
+ plat->pcs_init = intel_mgbe_pcs_init;
+ plat->pcs_exit = intel_mgbe_pcs_exit;
plat->select_pcs = intel_mgbe_select_pcs;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index ac7d6d3e205a..d59be76a2091 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -5,6 +5,7 @@
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
@@ -81,6 +82,8 @@
#define SGMII_10M_RX_CLK_DVDR 0x31
+struct qcom_ethqos;
+
struct ethqos_emac_por {
unsigned int offset;
unsigned int value;
@@ -95,6 +98,13 @@ struct ethqos_emac_driver_data {
const char *link_clk_name;
struct dwmac4_addrs dwmac4_addrs;
bool needs_sgmii_loopback;
+ /* Fixed PTP reference clock rate, used on SCMI platforms where
+ * firmware does not implement the clock protocol allowing linux to
+ * query it.
+ */
+ unsigned long ptp_clk_rate;
+ int (*setup)(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat);
};
struct qcom_ethqos {
@@ -103,12 +113,8 @@ struct qcom_ethqos {
struct clk *link_clk;
struct phy *serdes_phy;
phy_interface_t phy_mode;
-
- const struct ethqos_emac_por *rgmii_por;
- unsigned int num_rgmii_por;
- bool rgmii_config_loopback_en;
- bool has_emac_ge_3;
- bool needs_sgmii_loopback;
+ struct dev_pm_domain_list *pds;
+ const struct ethqos_emac_driver_data *data;
};
static u32 rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
@@ -189,8 +195,9 @@ static int ethqos_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
static void
qcom_ethqos_set_sgmii_loopback(struct qcom_ethqos *ethqos, bool enable)
{
- if (!ethqos->needs_sgmii_loopback ||
- ethqos->phy_mode != PHY_INTERFACE_MODE_2500BASEX)
+ if (!ethqos->data->needs_sgmii_loopback ||
+ (ethqos->phy_mode != PHY_INTERFACE_MODE_2500BASEX &&
+ ethqos->phy_mode != PHY_INTERFACE_MODE_SGMII))
return;
rgmii_updatel(ethqos,
@@ -204,6 +211,11 @@ static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos)
rgmii_setmask(ethqos, RGMII_CONFIG_FUNC_CLK_EN, RGMII_IO_MACRO_CONFIG);
}
+static int ethqos_hlos_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat);
+static int ethqos_scmi_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat);
+
static const struct ethqos_emac_por emac_v2_3_0_por[] = {
{ .offset = RGMII_IO_MACRO_CONFIG, .value = 0x00C01343 },
{ .offset = SDCC_HC_REG_DLL_CONFIG, .value = 0x2004642C },
@@ -218,6 +230,7 @@ static const struct ethqos_emac_driver_data emac_v2_3_0_data = {
.num_rgmii_por = ARRAY_SIZE(emac_v2_3_0_por),
.rgmii_config_loopback_en = true,
.has_emac_ge_3 = false,
+ .setup = ethqos_hlos_setup,
};
static const struct ethqos_emac_por emac_v2_1_0_por[] = {
@@ -234,6 +247,7 @@ static const struct ethqos_emac_driver_data emac_v2_1_0_data = {
.num_rgmii_por = ARRAY_SIZE(emac_v2_1_0_por),
.rgmii_config_loopback_en = false,
.has_emac_ge_3 = false,
+ .setup = ethqos_hlos_setup,
};
static const struct ethqos_emac_por emac_v3_0_0_por[] = {
@@ -266,6 +280,7 @@ static const struct ethqos_emac_driver_data emac_v3_0_0_data = {
.mtl_low_cred = 0x00008024,
.mtl_low_cred_offset = 0x1000,
},
+ .setup = ethqos_hlos_setup,
};
static const struct ethqos_emac_por emac_v4_0_0_por[] = {
@@ -301,6 +316,31 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = {
.mtl_low_cred = 0x00008024,
.mtl_low_cred_offset = 0x1000,
},
+ .setup = ethqos_hlos_setup,
+};
+
+static const struct ethqos_emac_driver_data emac_v4_0_0_scmi_data = {
+ .has_emac_ge_3 = true,
+ .needs_sgmii_loopback = true,
+ .dma_addr_width = 36,
+ .ptp_clk_rate = 230400000,
+ .dwmac4_addrs = {
+ .dma_chan = 0x00008100,
+ .dma_chan_offset = 0x1000,
+ .mtl_chan = 0x00008000,
+ .mtl_chan_offset = 0x1000,
+ .mtl_ets_ctrl = 0x00008010,
+ .mtl_ets_ctrl_offset = 0x1000,
+ .mtl_txq_weight = 0x00008018,
+ .mtl_txq_weight_offset = 0x1000,
+ .mtl_send_slp_cred = 0x0000801c,
+ .mtl_send_slp_cred_offset = 0x1000,
+ .mtl_high_cred = 0x00008020,
+ .mtl_high_cred_offset = 0x1000,
+ .mtl_low_cred = 0x00008024,
+ .mtl_low_cred_offset = 0x1000,
+ },
+ .setup = ethqos_scmi_setup,
};
static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
@@ -322,7 +362,7 @@ static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
/* Set DLL_EN */
rgmii_setmask(ethqos, SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
- if (!ethqos->has_emac_ge_3) {
+ if (!ethqos->data->has_emac_ge_3) {
rgmii_clrmask(ethqos, SDCC_DLL_MCLK_GATING_EN,
SDCC_HC_REG_DLL_CONFIG);
@@ -352,7 +392,7 @@ static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
rgmii_setmask(ethqos, SDCC_DLL_CONFIG2_DDR_CAL_EN,
SDCC_HC_REG_DLL_CONFIG2);
- if (!ethqos->has_emac_ge_3) {
+ if (!ethqos->data->has_emac_ge_3) {
rgmii_clrmask(ethqos, SDCC_DLL_CONFIG2_DLL_CLOCK_DIS,
SDCC_HC_REG_DLL_CONFIG2);
@@ -432,7 +472,7 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
rgmii_clrmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
RGMII_IO_MACRO_CONFIG2);
- if (speed == SPEED_1000 || ethqos->has_emac_ge_3)
+ if (speed == SPEED_1000 || ethqos->data->has_emac_ge_3)
rgmii_setmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
RGMII_IO_MACRO_CONFIG2);
else
@@ -456,7 +496,7 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
* in practice this becomes PRG_RCLK_DLY = 52 * 4 /
* (2 * RX delay ns)
*/
- if (ethqos->has_emac_ge_3) {
+ if (ethqos->data->has_emac_ge_3) {
/* 0.9 ns */
prg_rclk_dly = 115;
} else {
@@ -472,7 +512,7 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
SDCC_HC_REG_DDR_CONFIG);
}
- if (ethqos->rgmii_config_loopback_en)
+ if (ethqos->data->rgmii_config_loopback_en)
loopback = RGMII_CONFIG_LOOPBACK_EN;
else
loopback = 0;
@@ -495,9 +535,9 @@ static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
dev = &ethqos->pdev->dev;
/* Reset to POR values and enable clk */
- for (i = 0; i < ethqos->num_rgmii_por; i++)
- rgmii_writel(ethqos, ethqos->rgmii_por[i].value,
- ethqos->rgmii_por[i].offset);
+ for (i = 0; i < ethqos->data->num_rgmii_por; i++)
+ rgmii_writel(ethqos, ethqos->data->rgmii_por[i].value,
+ ethqos->data->rgmii_por[i].offset);
ethqos_set_func_clk_en(ethqos);
@@ -511,7 +551,7 @@ static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
rgmii_setmask(ethqos, SDCC_DLL_CONFIG_PDN,
SDCC_HC_REG_DLL_CONFIG);
- if (ethqos->has_emac_ge_3) {
+ if (ethqos->data->has_emac_ge_3) {
if (speed == SPEED_1000) {
rgmii_writel(ethqos, 0x1800000, SDCC_TEST_CTL);
rgmii_writel(ethqos, 0x2C010800, SDCC_USR_CTL);
@@ -538,7 +578,7 @@ static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
SDCC_HC_REG_DLL_CONFIG);
/* Set USR_CTL bit 26 with mask of 3 bits */
- if (!ethqos->has_emac_ge_3)
+ if (!ethqos->data->has_emac_ge_3)
rgmii_updatel(ethqos, GENMASK(26, 24), BIT(26),
SDCC_USR_CTL);
@@ -601,10 +641,22 @@ static int qcom_ethqos_serdes_powerup(struct net_device *ndev, void *priv)
if (ret)
return ret;
+ if (ethqos->phy_mode == PHY_INTERFACE_MODE_SGMII ||
+ ethqos->phy_mode == PHY_INTERFACE_MODE_2500BASEX) {
+ ret = phy_set_mode_ext(ethqos->serdes_phy, PHY_MODE_ETHERNET,
+ ethqos->phy_mode);
+ if (ret)
+ goto err_out;
+ }
+
ret = phy_power_on(ethqos->serdes_phy);
if (ret)
- phy_exit(ethqos->serdes_phy);
+ goto err_out;
+
+ return 0;
+err_out:
+ phy_exit(ethqos->serdes_phy);
return ret;
}
@@ -625,6 +677,12 @@ static int ethqos_mac_finish_serdes(struct net_device *ndev, void *priv,
qcom_ethqos_set_sgmii_loopback(ethqos, false);
+ /* The interface mode is only known once phylink has resolved it and
+ * may change at runtime, so keep it in sync for later
+ * serdes_powerup() and qcom_ethqos_set_sgmii_loopback() calls.
+ */
+ ethqos->phy_mode = interface;
+
if (interface == PHY_INTERFACE_MODE_SGMII ||
interface == PHY_INTERFACE_MODE_2500BASEX)
ret = phy_set_mode_ext(ethqos->serdes_phy, PHY_MODE_ETHERNET,
@@ -681,6 +739,136 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv)
netdev_dbg(priv->dev, "PTP rate %lu\n", plat_dat->clk_ptp_rate);
}
+static int ethqos_hlos_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat)
+{
+ struct platform_device *pdev = ethqos->pdev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ethqos->rgmii_base = devm_platform_ioremap_resource_byname(pdev, "rgmii");
+ if (IS_ERR(ethqos->rgmii_base))
+ return dev_err_probe(dev, PTR_ERR(ethqos->rgmii_base),
+ "Failed to map rgmii resource\n");
+
+ ethqos->link_clk = devm_clk_get(dev, ethqos->data->link_clk_name ?: "rgmii");
+ if (IS_ERR(ethqos->link_clk))
+ return dev_err_probe(dev, PTR_ERR(ethqos->link_clk),
+ "Failed to get link_clk\n");
+
+ plat_dat->clks_config = ethqos_clks_config;
+
+ ret = ethqos_clks_config(ethqos, true);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos);
+ if (ret)
+ return ret;
+
+ ethqos_set_clk_tx_rate(ethqos, NULL, plat_dat->phy_interface, SPEED_1000);
+ qcom_ethqos_set_sgmii_loopback(ethqos, true);
+ ethqos_set_func_clk_en(ethqos);
+
+ switch (ethqos->phy_mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ plat_dat->fix_mac_speed = ethqos_fix_mac_speed_rgmii;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ plat_dat->fix_mac_speed = ethqos_fix_mac_speed_sgmii;
+ break;
+ default:
+ break;
+ }
+
+ plat_dat->set_clk_tx_rate = ethqos_set_clk_tx_rate;
+ plat_dat->dump_debug_regs = rgmii_dump;
+
+ return 0;
+}
+
+static const char *const ethqos_scmi_pd_names[] = { "core", "mdio" };
+
+static int ethqos_scmi_setup(struct qcom_ethqos *ethqos,
+ struct plat_stmmacenet_data *plat_dat)
+{
+ const struct dev_pm_domain_attach_data pd_data = {
+ .pd_names = ethqos_scmi_pd_names,
+ .num_pd_names = ARRAY_SIZE(ethqos_scmi_pd_names),
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ };
+
+ struct platform_device *pdev = ethqos->pdev;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = devm_pm_domain_attach_list(dev, &pd_data, &ethqos->pds);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to attach power domains\n");
+
+ /*
+ * The MAC AXI/AHB clocks are owned by firmware and brought up through
+ * the "core" and "mdio" SCMI power domains above, while the SerDes
+ * lane is powered by the SerDes PHY's own power domain from
+ * serdes_powerup() in stmmac_open(). The MAC wrapper itself, however
+ * is in the kernel's register space: the wrapper bit that loops the
+ * PHY TX clock into the MAC's clk_rx_i - needed because no recovered
+ * RX clock exists yet - is not configured by firmware. Without it,
+ * clk_rx_i never toggles and the DMA SW-reset polled in
+ * dwmac4_dma_reset() never completes.
+ *
+ * Map the wrapper and program the same loopback/functional clock bits
+ * the non-firmware platforms rely on (see ethqos_clks_config) so the
+ * RX clock is present by the time the DMA engine is reset.
+ */
+ ethqos->rgmii_base = devm_platform_ioremap_resource_byname(pdev, "rgmii");
+ if (IS_ERR(ethqos->rgmii_base))
+ return dev_err_probe(dev, PTR_ERR(ethqos->rgmii_base),
+ "Failed to map rgmii resource\n");
+
+ /*
+ * Register clks_config to run on every subsequent runtime resume,
+ * which stmmac performs after the power domains are on but before
+ * serdes_powerup() and the DMA reset. This alone isn't enough for
+ * the very first reset done from stmmac_open(), though: probe()
+ * fakes the device into RPM_ACTIVE without invoking the resume
+ * callback, so whether clks_config() has run by then depends on
+ * whether the device has already gone through one real suspend/resume
+ * cycle in the meantime. Call it here explicitly so the wrapper is
+ * always configured before the first DMA reset regardless of that
+ * timing.
+ */
+ plat_dat->clks_config = ethqos_clks_config;
+
+ ret = ethqos_clks_config(ethqos, true);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos);
+ if (ret)
+ return ret;
+
+ plat_dat->clk_ptp_rate = ethqos->data->ptp_clk_rate;
+
+ switch (ethqos->phy_mode) {
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ plat_dat->fix_mac_speed = ethqos_fix_mac_speed_sgmii;
+ break;
+ default:
+ /* The firmware-managed SerDes only supports SGMII modes. */
+ return dev_err_probe(dev, -EINVAL, "Unsupported phy-mode: %s\n",
+ phy_modes(ethqos->phy_mode));
+ }
+
+ return 0;
+}
+
static int qcom_ethqos_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -702,23 +890,20 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
"dt configuration failed\n");
}
- plat_dat->clks_config = ethqos_clks_config;
-
ethqos = devm_kzalloc(dev, sizeof(*ethqos), GFP_KERNEL);
if (!ethqos)
return -ENOMEM;
ethqos->phy_mode = plat_dat->phy_interface;
+
switch (ethqos->phy_mode) {
case PHY_INTERFACE_MODE_RGMII:
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
- plat_dat->fix_mac_speed = ethqos_fix_mac_speed_rgmii;
break;
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_SGMII:
- plat_dat->fix_mac_speed = ethqos_fix_mac_speed_sgmii;
plat_dat->mac_finish = ethqos_mac_finish_serdes;
break;
default:
@@ -728,28 +913,13 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
}
ethqos->pdev = pdev;
- ethqos->rgmii_base = devm_platform_ioremap_resource_byname(pdev, "rgmii");
- if (IS_ERR(ethqos->rgmii_base))
- return dev_err_probe(dev, PTR_ERR(ethqos->rgmii_base),
- "Failed to map rgmii resource\n");
-
data = of_device_get_match_data(dev);
- ethqos->rgmii_por = data->rgmii_por;
- ethqos->num_rgmii_por = data->num_rgmii_por;
- ethqos->rgmii_config_loopback_en = data->rgmii_config_loopback_en;
- ethqos->has_emac_ge_3 = data->has_emac_ge_3;
- ethqos->needs_sgmii_loopback = data->needs_sgmii_loopback;
+ ethqos->data = data;
- ethqos->link_clk = devm_clk_get(dev, data->link_clk_name ?: "rgmii");
- if (IS_ERR(ethqos->link_clk))
- return dev_err_probe(dev, PTR_ERR(ethqos->link_clk),
- "Failed to get link_clk\n");
-
- ret = ethqos_clks_config(ethqos, true);
- if (ret)
- return ret;
+ if (WARN_ON(!data->setup))
+ return -EINVAL;
- ret = devm_add_action_or_reset(dev, ethqos_clks_disable, ethqos);
+ ret = data->setup(ethqos, plat_dat);
if (ret)
return ret;
@@ -758,12 +928,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(ethqos->serdes_phy),
"Failed to get serdes phy\n");
- ethqos_set_clk_tx_rate(ethqos, NULL, plat_dat->phy_interface,
- SPEED_1000);
-
- qcom_ethqos_set_sgmii_loopback(ethqos, true);
- ethqos_set_func_clk_en(ethqos);
-
/* The clocks are controlled by firmware, so we don't know for certain
* what clock rate is being used. Hardware documentation mentions that
* the AHB slave clock will be in the range of 50 to 100MHz, which
@@ -771,11 +935,9 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
*/
plat_dat->clk_csr = STMMAC_CSR_60_100M;
plat_dat->bsp_priv = ethqos;
- plat_dat->set_clk_tx_rate = ethqos_set_clk_tx_rate;
- plat_dat->dump_debug_regs = rgmii_dump;
plat_dat->ptp_clk_freq_config = ethqos_ptp_clk_freq_config;
plat_dat->core_type = DWMAC_CORE_GMAC4;
- if (ethqos->has_emac_ge_3)
+ if (data->has_emac_ge_3)
plat_dat->dwmac4_addrs = &data->dwmac4_addrs;
plat_dat->pmt = true;
if (of_property_read_bool(np, "snps,tso"))
@@ -799,6 +961,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
static const struct of_device_id qcom_ethqos_match[] = {
{ .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data},
+ { .compatible = "qcom,sa8255p-ethqos", .data = &emac_v4_0_0_scmi_data},
{ .compatible = "qcom,sa8775p-ethqos", .data = &emac_v4_0_0_data},
{ .compatible = "qcom,sc8280xp-ethqos", .data = &emac_v3_0_0_data},
{ .compatible = "qcom,sm8150-ethqos", .data = &emac_v2_1_0_data},
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
index 19f34e18bfef..9af32c26f9c1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
@@ -81,8 +81,11 @@ static int renesas_gmac_pcs_init(struct stmmac_priv *priv)
static void renesas_gmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- miic_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ miic_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index 72bdbcb5e863..c4db919ec164 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
+#include <linux/pcs/pcs-xpcs-rk.h>
#include <linux/pm_runtime.h>
#include "stmmac_platform.h"
@@ -47,6 +48,7 @@ struct rk_gmac_ops {
void (*set_to_rgmii)(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay);
void (*set_to_rmii)(struct rk_priv_data *bsp_priv);
+ void (*set_to_sgmii)(struct rk_priv_data *bsp_priv);
int (*set_speed)(struct rk_priv_data *bsp_priv,
phy_interface_t interface, int speed);
void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv);
@@ -63,6 +65,7 @@ struct rk_gmac_ops {
bool clock_grf_reg_in_php;
bool supports_rgmii;
bool supports_rmii;
+ bool supports_sgmii;
bool php_grf_required;
bool regs_valid;
u32 regs[];
@@ -98,6 +101,7 @@ struct rk_priv_data {
bool integrated_phy;
bool supports_rgmii;
bool supports_rmii;
+ bool supports_sgmii;
struct clk_bulk_data *clks;
int num_clks;
@@ -809,6 +813,8 @@ static const struct rk_gmac_ops rk3528_ops = {
#define RK3568_GRF_GMAC1_CON1 0x038c
/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */
+#define RK3568_GMAC_MODE_RMII_RGMII GRF_CLR_BIT(7)
+#define RK3568_GMAC_MODE_SGMII_QSGMII GRF_BIT(7)
#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1)
@@ -836,6 +842,16 @@ static int rk3568_init(struct rk_priv_data *bsp_priv)
}
}
+static void rk3568_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+ u32 con1;
+
+ con1 = (bsp_priv->id == 1) ? RK3568_GRF_GMAC1_CON1 :
+ RK3568_GRF_GMAC0_CON1;
+
+ regmap_write(bsp_priv->grf, con1, RK3568_GMAC_MODE_RMII_RGMII);
+}
+
static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
@@ -851,19 +867,31 @@ static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));
regmap_write(bsp_priv->grf, con1,
+ RK3568_GMAC_MODE_RMII_RGMII |
RK3568_GMAC_RXCLK_DLY_ENABLE |
RK3568_GMAC_TXCLK_DLY_ENABLE);
}
+static void rk3568_set_to_sgmii(struct rk_priv_data *bsp_priv)
+{
+ u32 con1;
+
+ con1 = (bsp_priv->id == 1) ? RK3568_GRF_GMAC1_CON1 :
+ RK3568_GRF_GMAC0_CON1;
+
+ regmap_write(bsp_priv->grf, con1, RK3568_GMAC_MODE_SGMII_QSGMII);
+}
+
static const struct rk_gmac_ops rk3568_ops = {
.init = rk3568_init,
+ .set_to_rmii = rk3568_set_to_rmii,
.set_to_rgmii = rk3568_set_to_rgmii,
+ .set_to_sgmii = rk3568_set_to_sgmii,
+
.set_speed = rk_set_clk_mac_speed,
.gmac_phy_intf_sel_mask = GENMASK_U16(6, 4),
- .supports_rmii = true,
-
.regs_valid = true,
.regs = {
0xfe2a0000, /* gmac0 */
@@ -1211,6 +1239,44 @@ static void rk_phy_powerdown(struct rk_priv_data *bsp_priv)
dev_err(bsp_priv->dev, "fail to disable phy-supply\n");
}
+static int rk_pcs_init(struct stmmac_priv *priv)
+{
+ struct rk_priv_data *bsp_priv = priv->plat->bsp_priv;
+ struct device_node *node = priv->device->of_node;
+ struct device_node *pcs_node;
+ struct dw_xpcs *xpcs;
+
+ pcs_node = of_parse_phandle(node, "pcs-handle", 0);
+ if (!pcs_node)
+ return -ENODEV;
+
+ xpcs = xpcs_rk_create(priv->device, pcs_node, bsp_priv->id);
+ of_node_put(pcs_node);
+ if (IS_ERR(xpcs))
+ return PTR_ERR(xpcs);
+
+ priv->hw->xpcs = xpcs;
+ return 0;
+}
+
+static void rk_pcs_exit(struct stmmac_priv *priv)
+{
+ if (!priv->hw->xpcs)
+ return;
+
+ xpcs_destroy(priv->hw->xpcs);
+ priv->hw->xpcs = NULL;
+}
+
+static struct phylink_pcs *rk_select_pcs(struct stmmac_priv *priv,
+ phy_interface_t interface)
+{
+ if (!priv->hw->xpcs)
+ return NULL;
+
+ return xpcs_to_phylink_pcs(priv->hw->xpcs);
+}
+
static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
const struct rk_gmac_ops *ops)
@@ -1333,6 +1399,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
bsp_priv->supports_rgmii = ops->supports_rgmii || !!ops->set_to_rgmii;
bsp_priv->supports_rmii = ops->supports_rmii || !!ops->set_to_rmii;
+ bsp_priv->supports_sgmii = ops->supports_sgmii || !!ops->set_to_sgmii;
if (ops->init) {
ret = ops->init(bsp_priv);
@@ -1364,6 +1431,10 @@ static int rk_gmac_check_ops(struct rk_priv_data *bsp_priv)
if (!bsp_priv->supports_rmii)
return -EINVAL;
break;
+ case PHY_INTERFACE_MODE_SGMII:
+ if (!bsp_priv->supports_sgmii)
+ return -EINVAL;
+ break;
default:
dev_err(bsp_priv->dev,
"unsupported interface %d", bsp_priv->phy_iface);
@@ -1382,16 +1453,19 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
if (ret)
return ret;
+ ret = gmac_clk_enable(bsp_priv, true);
+ if (ret)
+ return ret;
+
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_SGMII)
+ goto set_mode;
+
ret = rk_get_phy_intf_sel(bsp_priv->phy_iface);
if (ret < 0)
- return ret;
+ goto clk_disable;
intf = ret;
- ret = gmac_clk_enable(bsp_priv, true);
- if (ret)
- return ret;
-
if (bsp_priv->gmac_phy_intf_sel_mask ||
bsp_priv->gmac_rmii_mode_mask) {
/* If defined, encode the phy_intf_sel value */
@@ -1402,10 +1476,8 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
bsp_priv->gmac_rmii_mode_mask);
ret = rk_write_gmac_grf_reg(bsp_priv, val);
- if (ret < 0) {
- gmac_clk_enable(bsp_priv, false);
- return ret;
- }
+ if (ret < 0)
+ goto clk_disable;
}
if (bsp_priv->clock.rmii_mode_mask) {
@@ -1413,13 +1485,12 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
bsp_priv->clock.rmii_mode_mask);
ret = rk_write_clock_grf_reg(bsp_priv, val);
- if (ret < 0) {
- gmac_clk_enable(bsp_priv, false);
- return ret;
- }
+ if (ret < 0)
+ goto clk_disable;
}
- /*rmii or rgmii*/
+set_mode:
+ /* rmii, rgmii, sgmii */
switch (bsp_priv->phy_iface) {
case PHY_INTERFACE_MODE_RGMII:
dev_info(dev, "init for RGMII\n");
@@ -1450,15 +1521,20 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
if (bsp_priv->ops->set_to_rmii)
bsp_priv->ops->set_to_rmii(bsp_priv);
break;
+ case PHY_INTERFACE_MODE_SGMII:
+ dev_info(dev, "init for SGMII\n");
+ if (bsp_priv->ops->set_to_sgmii)
+ bsp_priv->ops->set_to_sgmii(bsp_priv);
+ break;
default:
dev_err(dev, "NO interface defined!\n");
+ ret = -EINVAL;
+ goto clk_disable;
}
ret = rk_phy_powerup(bsp_priv);
- if (ret) {
- gmac_clk_enable(bsp_priv, false);
- return ret;
- }
+ if (ret)
+ goto clk_disable;
pm_runtime_get_sync(dev);
@@ -1466,6 +1542,10 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
bsp_priv->ops->integrated_phy_powerup(bsp_priv);
return 0;
+
+clk_disable:
+ gmac_clk_enable(bsp_priv, false);
+ return ret;
}
static void rk_gmac_powerdown(struct rk_priv_data *gmac)
@@ -1605,6 +1685,17 @@ static int rk_gmac_probe(struct platform_device *pdev)
plat_dat->suspend = rk_gmac_suspend;
plat_dat->resume = rk_gmac_resume;
+ if (plat_dat->phy_interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII clock always runs at 125 MHz */
+ plat_dat->set_clk_tx_rate = NULL;
+
+ /* SGMII requires a PCS */
+ plat_dat->default_an_inband = true;
+ plat_dat->pcs_init = rk_pcs_init;
+ plat_dat->pcs_exit = rk_pcs_exit;
+ plat_dat->select_pcs = rk_select_pcs;
+ }
+
plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data);
if (IS_ERR(plat_dat->bsp_priv))
return PTR_ERR(plat_dat->bsp_priv);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
index 13634965bc19..01df4776edb3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rzn1.c
@@ -35,8 +35,11 @@ static int rzn1_dwmac_pcs_init(struct stmmac_priv *priv)
static void rzn1_dwmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- miic_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ miic_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *rzn1_dwmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 1d7f0a57d288..6d4bc1fe8f75 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -539,8 +539,11 @@ static int socfpga_dwmac_pcs_init(struct stmmac_priv *priv)
static void socfpga_dwmac_pcs_exit(struct stmmac_priv *priv)
{
- if (priv->hw->phylink_pcs)
- lynx_pcs_destroy(priv->hw->phylink_pcs);
+ if (!priv->hw->phylink_pcs)
+ return;
+
+ lynx_pcs_destroy(priv->hw->phylink_pcs);
+ priv->hw->phylink_pcs = NULL;
}
static struct phylink_pcs *socfpga_dwmac_select_pcs(struct stmmac_priv *priv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
index e02ad3762b5f..f62c803665ad 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
@@ -55,14 +55,10 @@ static int sophgo_dwmac_probe(struct platform_device *pdev)
return ret;
data = device_get_match_data(&pdev->dev);
- if (data && data->has_internal_rx_delay) {
- plat_dat->phy_interface = phy_fix_phy_mode_for_mac_delays(plat_dat->phy_interface,
- false, true);
- if (plat_dat->phy_interface == PHY_INTERFACE_MODE_NA)
- return -EINVAL;
- }
-
- return stmmac_dvr_probe(dev, plat_dat, &stmmac_res);
+ if (data)
+ plat_dat->has_internal_rx_delay = data->has_internal_rx_delay;
+
+ return stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
}
static const struct sophgo_dwmac_data sg2042_dwmac_data = {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 48c52eb96233..47954537b29e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -81,6 +81,13 @@ static const struct reg_field sun8i_syscon_reg_field = {
.msb = 31,
};
+/* EMAC1 clock register @ 0x34 in the "system control" address range */
+static const struct reg_field sun8i_syscon_reg_field_emac1 = {
+ .reg = 0x34,
+ .lsb = 0,
+ .msb = 31,
+};
+
/* EMAC clock register @ 0x164 in the CCU address range */
static const struct reg_field sun8i_ccu_reg_field = {
.reg = 0x164,
@@ -143,6 +150,13 @@ static const struct emac_variant emac_variant_h6 = {
.tx_delay_max = 7,
};
+static const struct emac_variant emac_variant_h616_emac1 = {
+ .syscon_field = &sun8i_syscon_reg_field_emac1,
+ /* The co-packaged AC200/AC300 PHY does not use the H3 PHY controls. */
+ .soc_has_internal_phy = false,
+ .support_rmii = true,
+};
+
#define EMAC_BASIC_CTL0 0x00
#define EMAC_BASIC_CTL1 0x04
#define EMAC_INT_STA 0x08
@@ -269,11 +283,33 @@ static const struct emac_variant emac_variant_h6 = {
#define SYSCON_ETCS_EXT_GMII 0x1
#define SYSCON_ETCS_INT_GMII 0x2
+static int sun8i_dwmac_reset(void __iomem *ioaddr)
+{
+ u32 v;
+
+ v = readl(ioaddr + EMAC_BASIC_CTL1);
+ writel(v | 0x01, ioaddr + EMAC_BASIC_CTL1);
+
+ /* The timeout was previously set to 10ms, but some board (OrangePI0)
+ * need more if no cable plugged. 100ms seems OK
+ */
+ return readl_poll_timeout(ioaddr + EMAC_BASIC_CTL1, v,
+ !(v & 0x01), 100, 100000);
+}
+
/* sun8i_dwmac_dma_reset() - reset the EMAC
* Called from stmmac via stmmac_dma_ops->reset
*/
static int sun8i_dwmac_dma_reset(void __iomem *ioaddr)
{
+ int ret;
+
+ writel(0, ioaddr + EMAC_INT_EN);
+
+ /* The PHY receive clock must be running for the reset to complete. */
+ ret = sun8i_dwmac_reset(ioaddr);
+
+ /* Leave DMA and interrupts disabled even if the reset timed out. */
writel(0, ioaddr + EMAC_RX_CTL1);
writel(0, ioaddr + EMAC_TX_CTL1);
writel(0, ioaddr + EMAC_RX_FRM_FLT);
@@ -281,7 +317,7 @@ static int sun8i_dwmac_dma_reset(void __iomem *ioaddr)
writel(0, ioaddr + EMAC_TX_DESC_LIST);
writel(0, ioaddr + EMAC_INT_EN);
writel(0x1FFFFFF, ioaddr + EMAC_INT_STA);
- return 0;
+ return ret;
}
/* sun8i_dwmac_dma_init() - initialize the EMAC
@@ -738,27 +774,6 @@ static void sun8i_dwmac_flow_ctrl(struct mac_device_info *hw,
writel(v, ioaddr + EMAC_TX_FLOW_CTL);
}
-static int sun8i_dwmac_reset(struct stmmac_priv *priv)
-{
- u32 v;
- int err;
-
- v = readl(priv->ioaddr + EMAC_BASIC_CTL1);
- writel(v | 0x01, priv->ioaddr + EMAC_BASIC_CTL1);
-
- /* The timeout was previously set to 10ms, but some board (OrangePI0)
- * need more if no cable plugged. 100ms seems OK
- */
- err = readl_poll_timeout(priv->ioaddr + EMAC_BASIC_CTL1, v,
- !(v & 0x01), 100, 100000);
-
- if (err) {
- dev_err(priv->device, "EMAC reset timeout\n");
- return err;
- }
- return 0;
-}
-
/* Search in mdio-mux node for internal PHY node and get its clk/reset */
static int get_ephy_nodes(struct stmmac_priv *priv)
{
@@ -783,18 +798,22 @@ static int get_ephy_nodes(struct stmmac_priv *priv)
/* Seek for internal PHY */
for_each_child_of_node_scoped(mdio_internal, iphynode) {
- gmac->ephy_clk = of_clk_get(iphynode, 0);
- if (IS_ERR(gmac->ephy_clk))
+ struct clk *ephy_clk;
+
+ ephy_clk = of_clk_get(iphynode, 0);
+ if (IS_ERR(ephy_clk))
continue;
gmac->rst_ephy = of_reset_control_get_exclusive(iphynode, NULL);
if (IS_ERR(gmac->rst_ephy)) {
ret = PTR_ERR(gmac->rst_ephy);
+ clk_put(ephy_clk);
if (ret == -EPROBE_DEFER) {
of_node_put(mdio_internal);
return ret;
}
continue;
}
+ gmac->ephy_clk = ephy_clk;
dev_info(priv->device, "Found internal PHY node\n");
of_node_put(mdio_internal);
return 0;
@@ -895,7 +914,9 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
/* After changing syscon value, the MAC need reset or it will
* use the last value (and so the last PHY set).
*/
- ret = sun8i_dwmac_reset(priv);
+ ret = sun8i_dwmac_reset(priv->ioaddr);
+ if (ret)
+ dev_err(priv->device, "EMAC reset timeout\n");
}
return ret;
}
@@ -1217,10 +1238,6 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to register mux\n");
goto dwmac_mux;
}
- } else {
- ret = sun8i_dwmac_reset(priv);
- if (ret)
- goto dwmac_remove;
}
pm_runtime_put(&pdev->dev);
@@ -1278,6 +1295,8 @@ static const struct of_device_id sun8i_dwmac_match[] = {
.data = &emac_variant_a64 },
{ .compatible = "allwinner,sun50i-h6-emac",
.data = &emac_variant_h6 },
+ { .compatible = "allwinner,sun50i-h616-emac1",
+ .data = &emac_variant_h616_emac1 },
{ }
};
MODULE_DEVICE_TABLE(of, sun8i_dwmac_match);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ultrarisc.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ultrarisc.c
new file mode 100644
index 000000000000..0b62a75b782a
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ultrarisc.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UltraRISC DWMAC platform driver
+ *
+ * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "stmmac_platform.h"
+
+static int ultrarisc_dwmac_probe(struct platform_device *pdev)
+{
+ struct plat_stmmacenet_data *plat_dat;
+ struct stmmac_resources stmmac_res;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get resources\n");
+
+ plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return dev_err_probe(dev, PTR_ERR(plat_dat),
+ "failed to parse DT parameters\n");
+
+ plat_dat->has_internal_tx_delay = true;
+ plat_dat->has_internal_rx_delay = true;
+
+ return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
+}
+
+static const struct of_device_id ultrarisc_dwmac_match[] = {
+ { .compatible = "ultrarisc,dp1000-gmac" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ultrarisc_dwmac_match);
+
+static struct platform_driver ultrarisc_dwmac_driver = {
+ .probe = ultrarisc_dwmac_probe,
+ .driver = {
+ .name = "ultrarisc-dwmac",
+ .pm = &stmmac_pltfr_pm_ops,
+ .of_match_table = ultrarisc_dwmac_match,
+ },
+};
+module_platform_driver(ultrarisc_dwmac_driver);
+
+MODULE_DESCRIPTION("UltraRISC DWMAC platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 6382836828ba..696846531229 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -75,8 +75,6 @@
#define GMAC_PACKET_FILTER_IPFE BIT(20)
#define GMAC_PACKET_FILTER_RA BIT(31)
-#define GMAC_MAX_PERFECT_ADDRESSES 128
-
/* MAC RX Queue Enable */
#define GMAC_RX_QUEUE_CLEAR(queue) ~(GENMASK(1, 0) << ((queue) * 2))
#define GMAC_RX_AV_QUEUE_ENABLE(queue) BIT((queue) * 2)
@@ -176,7 +174,9 @@ enum power_event {
/* MAC HW features0 bitmap */
#define GMAC_HW_FEAT_SAVLANINS BIT(27)
-#define GMAC_HW_FEAT_ADDMAC BIT(18)
+#define GMAC_HW_FEAT_MACADR64SEL BIT(24)
+#define GMAC_HW_FEAT_MACADR32SEL BIT(23)
+#define GMAC_HW_FEAT_ADDMAC GENMASK(22, 18)
#define GMAC_HW_FEAT_RXCOESEL BIT(16)
#define GMAC_HW_FEAT_TXCOSEL BIT(14)
#define GMAC_HW_FEAT_EEESEL BIT(13)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index 18b357b257cc..259e2ebed8c1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -48,9 +48,14 @@ static void dwmac4_core_init(struct mac_device_info *hw,
value = readl(ioaddr + GMAC_CONFIG);
writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONFIG);
- /* Configure LPI 1us counter to number of CSR clock ticks in 1us - 1 */
+ /* Configure LPI 1us counter to number of CSR clock ticks in 1us - 1.
+ * Platforms with firmware-managed clocks may not expose the CSR clock
+ * to linux, in which case there's no meaningful value to program.
+ */
clk_rate = clk_get_rate(priv->plat->stmmac_clk);
- writel((clk_rate / 1000000) - 1, ioaddr + GMAC4_MAC_ONEUS_TIC_COUNTER);
+ if (clk_rate >= 1000000)
+ writel((clk_rate / 1000000) - 1,
+ ioaddr + GMAC4_MAC_ONEUS_TIC_COUNTER);
/* Enable GMAC interrupts */
writel(GMAC_INT_DEFAULT_ENABLE, ioaddr + GMAC_INT_EN);
@@ -365,22 +370,63 @@ static void dwmac4_pmt(struct mac_device_info *hw, unsigned long mode)
writel(pmt, ioaddr + GMAC_PMT);
}
+/**
+ * dwmac4_umac_addr_slot - Get the UC filter slot for the Nth MAC address
+ * @hw: The MAC device info
+ * @reg_n: The MAC address's index in the UC list
+ *
+ * On dwmac4 the slots available for Unicast MAC filtering are configured when
+ * integrating the IP.
+ *
+ * - Slot 0 is always available, used to store the primary MAC address
+ * - From 1 to 31, the number of implemented slots is set in hw->multi_addr
+ * - From 32 to 63, the range is available if hw->additional_32_addr
+ * is set
+ * - From 64 to 127, the range is available if hw->additional_64_addr is set
+ *
+ * All the ranges can be configured independently (e.g. 64 -> 127 can be
+ * available, but not 32 -> 63)
+ *
+ * Returns: The physical slot index in the UC filter
+ */
+static unsigned int dwmac4_umac_addr_slot(struct mac_device_info *hw,
+ unsigned int reg_n)
+{
+ unsigned int empty_slots = 0;
+
+ /* reg_n is in the 1-31 bank : 1 to 1 mapping */
+ if (reg_n < (hw->multi_addr + 1))
+ return reg_n;
+
+ /* Gap between the last address in the 1->31 range and the next slot */
+ if (hw->additional_32_addr)
+ empty_slots = 32 - (hw->multi_addr + 1);
+ else if (hw->additional_64_addr)
+ empty_slots = 64 - (hw->multi_addr + 1);
+
+ return reg_n + empty_slots;
+}
+
static void dwmac4_set_umac_addr(struct mac_device_info *hw,
const unsigned char *addr, unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
+ unsigned int slot;
- stmmac_dwmac4_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
- GMAC_ADDR_LOW(reg_n));
+ slot = dwmac4_umac_addr_slot(hw, reg_n);
+ stmmac_dwmac4_set_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(slot),
+ GMAC_ADDR_LOW(slot));
}
static void dwmac4_get_umac_addr(struct mac_device_info *hw,
unsigned char *addr, unsigned int reg_n)
{
void __iomem *ioaddr = hw->pcsr;
+ unsigned int slot;
- stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(reg_n),
- GMAC_ADDR_LOW(reg_n));
+ slot = dwmac4_umac_addr_slot(hw, reg_n);
+ stmmac_get_mac_addr(ioaddr, addr, GMAC_ADDR_HIGH(slot),
+ GMAC_ADDR_LOW(slot));
}
static int dwmac4_set_lpi_mode(struct mac_device_info *hw,
@@ -522,9 +568,6 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
/* Handle multiple unicast addresses */
if (netdev_uc_count(dev) + 1 > hw->unicast_filter_entries) {
- /* Switch to promiscuous mode if more than 128 addrs
- * are required
- */
value |= GMAC_PACKET_FILTER_PR;
} else {
struct netdev_hw_addr *ha;
@@ -535,9 +578,11 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
reg++;
}
- while (reg < GMAC_MAX_PERFECT_ADDRESSES) {
- writel(0, ioaddr + GMAC_ADDR_HIGH(reg));
- writel(0, ioaddr + GMAC_ADDR_LOW(reg));
+ while (reg < hw->unicast_filter_entries) {
+ unsigned int slot = dwmac4_umac_addr_slot(hw, reg);
+
+ writel(0, ioaddr + GMAC_ADDR_HIGH(slot));
+ writel(0, ioaddr + GMAC_ADDR_LOW(slot));
reg++;
}
}
@@ -779,22 +824,6 @@ static void dwmac4_sarc_configure(void __iomem *ioaddr, int val)
writel(value, ioaddr + GMAC_CONFIG);
}
-static void dwmac4_set_arp_offload(struct mac_device_info *hw, bool en,
- u32 addr)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
-
- writel(addr, ioaddr + GMAC_ARP_ADDR);
-
- value = readl(ioaddr + GMAC_CONFIG);
- if (en)
- value |= GMAC_CONFIG_ARPEN;
- else
- value &= ~GMAC_CONFIG_ARPEN;
- writel(value, ioaddr + GMAC_CONFIG);
-}
-
static int dwmac4_config_l3_filter(struct mac_device_info *hw, u32 filter_no,
bool en, bool ipv6, bool sa, bool inv,
u32 match)
@@ -926,7 +955,6 @@ const struct stmmac_ops dwmac4_ops = {
.set_filter = dwmac4_set_filter,
.set_mac_loopback = dwmac4_set_mac_loopback,
.sarc_configure = dwmac4_sarc_configure,
- .set_arp_offload = dwmac4_set_arp_offload,
.config_l3_filter = dwmac4_config_l3_filter,
.config_l4_filter = dwmac4_config_l4_filter,
};
@@ -963,7 +991,6 @@ const struct stmmac_ops dwmac410_ops = {
.flex_pps_config = dwmac5_flex_pps_config,
.set_mac_loopback = dwmac4_set_mac_loopback,
.sarc_configure = dwmac4_sarc_configure,
- .set_arp_offload = dwmac4_set_arp_offload,
.config_l3_filter = dwmac4_config_l3_filter,
.config_l4_filter = dwmac4_config_l4_filter,
.fpe_map_preemption_class = dwmac5_fpe_map_preemption_class,
@@ -1005,7 +1032,6 @@ const struct stmmac_ops dwmac510_ops = {
.flex_pps_config = dwmac5_flex_pps_config,
.set_mac_loopback = dwmac4_set_mac_loopback,
.sarc_configure = dwmac4_sarc_configure,
- .set_arp_offload = dwmac4_set_arp_offload,
.config_l3_filter = dwmac4_config_l3_filter,
.config_l4_filter = dwmac4_config_l4_filter,
.fpe_map_preemption_class = dwmac5_fpe_map_preemption_class,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 23ffe1adcd0d..14ac3f0e51f7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -388,6 +388,8 @@ static int dwmac4_get_hw_feature(void __iomem *ioaddr,
dma_cap->half_duplex = (hw_cap & GMAC_HW_FEAT_HDSEL) >> 2;
dma_cap->vlhash = (hw_cap & GMAC_HW_FEAT_VLHASH) >> 4;
dma_cap->multi_addr = (hw_cap & GMAC_HW_FEAT_ADDMAC) >> 18;
+ dma_cap->additional_32_addr = (hw_cap & GMAC_HW_FEAT_MACADR32SEL) >> 23;
+ dma_cap->additional_64_addr = (hw_cap & GMAC_HW_FEAT_MACADR64SEL) >> 24;
dma_cap->pcs = (hw_cap & GMAC_HW_FEAT_PCSSEL) >> 3;
dma_cap->sma_mdio = (hw_cap & GMAC_HW_FEAT_SMASEL) >> 5;
dma_cap->pmt_remote_wake_up = (hw_cap & GMAC_HW_FEAT_RWKSEL) >> 6;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index fc6ddb51c682..1a88cbaed70c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -1410,22 +1410,6 @@ static int dwxgmac2_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
return 0;
}
-static void dwxgmac2_set_arp_offload(struct mac_device_info *hw, bool en,
- u32 addr)
-{
- void __iomem *ioaddr = hw->pcsr;
- u32 value;
-
- writel(addr, ioaddr + XGMAC_ARP_ADDR);
-
- value = readl(ioaddr + XGMAC_RX_CONFIG);
- if (en)
- value |= XGMAC_CONFIG_ARPEN;
- else
- value &= ~XGMAC_CONFIG_ARPEN;
- writel(value, ioaddr + XGMAC_RX_CONFIG);
-}
-
const struct stmmac_ops dwxgmac210_ops = {
.core_init = dwxgmac2_core_init,
.irq_modify = dwxgmac2_irq_modify,
@@ -1464,7 +1448,6 @@ const struct stmmac_ops dwxgmac210_ops = {
.sarc_configure = dwxgmac2_sarc_configure,
.config_l3_filter = dwxgmac2_config_l3_filter,
.config_l4_filter = dwxgmac2_config_l4_filter,
- .set_arp_offload = dwxgmac2_set_arp_offload,
.fpe_map_preemption_class = dwxgmac3_fpe_map_preemption_class,
};
@@ -1519,7 +1502,6 @@ const struct stmmac_ops dwxlgmac2_ops = {
.sarc_configure = dwxgmac2_sarc_configure,
.config_l3_filter = dwxgmac2_config_l3_filter,
.config_l4_filter = dwxgmac2_config_l4_filter,
- .set_arp_offload = dwxgmac2_set_arp_offload,
.fpe_map_preemption_class = dwxgmac3_fpe_map_preemption_class,
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index ff83858ebc1f..0a6e82a155c0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -346,7 +346,7 @@ static int dwxgmac2_dma_interrupt(struct stmmac_priv *priv,
u64_stats_update_end(&stats->syncp);
ret |= handle_rx;
}
- if (likely(intr_status & (XGMAC_TI | XGMAC_TBU))) {
+ if (likely(intr_status & XGMAC_TI)) {
u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->tx_normal_irq_n[chan]);
u64_stats_update_end(&stats->syncp);
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 5e2654c91b41..50b590d67d87 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -407,7 +407,6 @@ struct stmmac_ops {
int (*config_l4_filter)(struct mac_device_info *hw, u32 filter_no,
bool en, bool udp, bool sa, bool inv,
u32 match);
- void (*set_arp_offload)(struct mac_device_info *hw, bool en, u32 addr);
int (*fpe_map_preemption_class)(struct net_device *ndev,
struct netlink_ext_ack *extack,
u32 pclass);
@@ -491,11 +490,11 @@ struct stmmac_ops {
stmmac_do_callback(__priv, mac, config_l3_filter, __args)
#define stmmac_config_l4_filter(__priv, __args...) \
stmmac_do_callback(__priv, mac, config_l4_filter, __args)
-#define stmmac_set_arp_offload(__priv, __args...) \
- stmmac_do_void_callback(__priv, mac, set_arp_offload, __args)
#define stmmac_fpe_map_preemption_class(__priv, __args...) \
stmmac_do_callback(__priv, mac, fpe_map_preemption_class, __args)
+struct ptp_system_timestamp;
+
/* PTP and HW Timer helpers */
struct stmmac_hwtimestamp {
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
@@ -505,7 +504,8 @@ struct stmmac_hwtimestamp {
int (*config_addend) (void __iomem *ioaddr, u32 addend);
int (*adjust_systime) (void __iomem *ioaddr, u32 sec, u32 nsec,
int add_sub, int gmac4);
- void (*get_systime) (void __iomem *ioaddr, u64 *systime);
+ void (*get_systime)(void __iomem *ioaddr, u64 *systime,
+ struct ptp_system_timestamp *sts);
void (*get_ptptime)(void __iomem *ioaddr, u64 *ptp_time);
void (*timestamp_interrupt)(struct stmmac_priv *priv);
void (*hwtstamp_correct_latency)(struct stmmac_priv *priv);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index d2d387f45c10..93292014c393 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -270,6 +270,7 @@ struct stmmac_priv {
bool sph_capable;
u32 sarc_type;
u32 rx_riwt[MTL_MAX_RX_QUEUES];
+ u32 tx_threshold[MTL_MAX_TX_QUEUES];
int hwts_rx_en;
bool tsfupdt_coarse;
@@ -299,6 +300,15 @@ struct stmmac_priv {
/* Protect est parameters */
struct mutex est_lock;
struct stmmac_est *est;
+
+ struct {
+ bool enabled;
+ u32 prio[MTL_MAX_TX_QUEUES];
+ bool prio_offload;
+ u32 num_tx_queues;
+ u8 algo;
+ } xmit_qdisc;
+
struct dma_features dma_cap;
struct stmmac_counters mmc;
int hw_cap_support;
@@ -329,6 +339,8 @@ struct stmmac_priv {
struct kernel_hwtstamp_config tstamp_config;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_ops;
+ bool ptp_enabled;
+
unsigned int default_addend;
u32 sub_second_inc;
u32 systime_flags;
@@ -419,11 +431,17 @@ int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev);
+static inline bool stmmac_check_timestamp_cap(struct stmmac_priv *priv)
+{
+ return priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp;
+}
+
static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
{
return !!priv->xdp_prog;
}
+void stmmac_configure_cbs(struct stmmac_priv *priv);
void stmmac_disable_rx_queue(struct stmmac_priv *priv, u32 queue);
void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue);
void stmmac_disable_tx_queue(struct stmmac_priv *priv, u32 queue);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 1be5310ca766..c616a1c95e5d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -89,7 +89,6 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = {
STMMAC_STAT(fatal_bus_error_irq),
/* Tx/Rx IRQ Events */
STMMAC_STAT(rx_early_irq),
- STMMAC_STAT(threshold),
STMMAC_STAT(irq_receive_pmt_irq_n),
/* MMC info */
STMMAC_STAT(mmc_tx_irq_n),
@@ -289,6 +288,7 @@ static const struct stmmac_stats stmmac_mmc[] = {
static const char stmmac_qstats_tx_string[][ETH_GSTRING_LEN] = {
"tx_pkt_n",
"tx_irq_n",
+ "tx_threshold",
#define STMMAC_TXQ_STATS ARRAY_SIZE(stmmac_qstats_tx_string)
};
@@ -496,6 +496,7 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
*data++ = pkt_n;
*data++ = stmmac_get_tx_normal_irq_n(priv, q);
+ *data++ = priv->tx_threshold[q];
}
for (q = 0; q < rx_cnt; q++) {
@@ -798,7 +799,8 @@ static int __stmmac_get_coalesce(struct net_device *dev,
if (queue < tx_cnt) {
ec->tx_coalesce_usecs = priv->tx_coal_timer[queue];
- ec->tx_max_coalesced_frames = priv->tx_coal_frames[queue];
+ ec->tx_max_coalesced_frames =
+ READ_ONCE(priv->tx_coal_frames[queue]);
} else {
ec->tx_coalesce_usecs = 0;
ec->tx_max_coalesced_frames = 0;
@@ -887,14 +889,14 @@ static int __stmmac_set_coalesce(struct net_device *dev,
int i;
for (i = 0; i < tx_cnt; i++) {
- priv->tx_coal_frames[i] =
- ec->tx_max_coalesced_frames;
+ WRITE_ONCE(priv->tx_coal_frames[i],
+ ec->tx_max_coalesced_frames);
priv->tx_coal_timer[i] =
ec->tx_coalesce_usecs;
}
} else if (queue < tx_cnt) {
- priv->tx_coal_frames[queue] =
- ec->tx_max_coalesced_frames;
+ WRITE_ONCE(priv->tx_coal_frames[queue],
+ ec->tx_max_coalesced_frames);
priv->tx_coal_timer[queue] =
ec->tx_coalesce_usecs;
}
@@ -1007,8 +1009,7 @@ static int stmmac_get_ts_info(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if ((priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) {
-
+ if (stmmac_check_timestamp_cap(priv)) {
info->so_timestamping = SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_TX_HARDWARE |
SOF_TIMESTAMPING_RX_HARDWARE |
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
index c889204a7aa5..b6b5ef7c8fc4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
@@ -230,7 +230,7 @@ int dwmac5_fpe_map_preemption_class(struct net_device *ndev,
if (count == 1)
continue;
- if (priv->plat->tx_sched_algorithm == MTL_TX_ALGORITHM_SP) {
+ if (priv->xmit_qdisc.algo == MTL_TX_ALGORITHM_SP) {
NL_SET_ERR_MSG_MOD(extack, ALG_ERR_MSG);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
index b9a985fa772c..870c740b4a50 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_hwtstamp.c
@@ -183,7 +183,8 @@ static int adjust_systime(void __iomem *ioaddr, u32 sec, u32 nsec,
10, 100000);
}
-static void get_systime(void __iomem *ioaddr, u64 *systime)
+static void get_systime(void __iomem *ioaddr, u64 *systime,
+ struct ptp_system_timestamp *sts)
{
u64 ns, sec0, sec1;
@@ -191,8 +192,10 @@ static void get_systime(void __iomem *ioaddr, u64 *systime)
sec1 = readl_relaxed(ioaddr + PTP_STSR);
do {
sec0 = sec1;
+ ptp_read_system_prets(sts);
/* Get the TSSS value */
- ns = readl_relaxed(ioaddr + PTP_STNSR);
+ ns = readl(ioaddr + PTP_STNSR);
+ ptp_read_system_postts(sts);
/* Get the TSS value */
sec1 = readl_relaxed(ioaddr + PTP_STSR);
} while (sec0 != sec1);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index b837e8e27a35..b20e5038eead 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -653,7 +653,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
u32 ts_master_en = 0;
u32 ts_event_en = 0;
- if (!(priv->dma_cap.time_stamp || priv->adv_ts)) {
+ if (!priv->plat->clk_ptp_rate ||
+ !(priv->dma_cap.time_stamp || priv->adv_ts)) {
NL_SET_ERR_MSG_MOD(extack, "No support for HW time stamping");
priv->hwts_tx_en = 0;
priv->hwts_rx_en = 0;
@@ -843,7 +844,7 @@ static int stmmac_hwtstamp_get(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
+ if (!stmmac_check_timestamp_cap(priv))
return -EOPNOTSUPP;
*config = priv->tstamp_config;
@@ -866,11 +867,6 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
{
struct timespec64 now;
- if (!priv->plat->clk_ptp_rate) {
- netdev_err(priv->dev, "Invalid PTP clock rate");
- return -EINVAL;
- }
-
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
priv->systime_flags = systime_flags;
@@ -885,26 +881,37 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
return 0;
}
+static int stmmac_init_ptp_clk_freq(struct stmmac_priv *priv)
+{
+ if (priv->plat->ptp_clk_freq_config)
+ priv->plat->ptp_clk_freq_config(priv);
+
+ if (!priv->plat->clk_ptp_rate) {
+ netdev_info(priv->dev, "PTP clock rate not configured\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* stmmac_init_timestamping - initialise timestamping
* @priv: driver private structure
- * Description: this is to verify if the HW supports the PTPv1 or PTPv2.
- * This is done by looking at the HW cap. register.
- * This function also registers the ptp driver.
+ *
+ * Description: initialise the hardware timestamping counter, reset the
+ * timestamping configuration and derive the advanced timestamping flags from
+ * the HW capabilities. The caller must have ensured a valid PTP reference
+ * clock rate (see stmmac_init_ptp_clk_freq()); the configured state is valid
+ * as long as the interface is open and not suspended, and this function is
+ * re-run on resume.
+ *
+ * Return: 0 on success, a negative errno otherwise.
*/
static int stmmac_init_timestamping(struct stmmac_priv *priv)
{
bool xmac = dwmac_is_xmac(priv->plat->core_type);
int ret;
- if (priv->plat->ptp_clk_freq_config)
- priv->plat->ptp_clk_freq_config(priv);
-
- if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp)) {
- netdev_info(priv->dev, "PTP not supported by HW\n");
- return -EOPNOTSUPP;
- }
-
ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE |
PTP_TCR_TSCFUPDT);
if (ret) {
@@ -937,24 +944,48 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)
return 0;
}
-static void stmmac_setup_ptp(struct stmmac_priv *priv)
+static int stmmac_setup_ptp(struct stmmac_priv *priv)
{
int ret;
+ if (!stmmac_check_timestamp_cap(priv)) {
+ netdev_info(priv->dev, "PTP not supported\n");
+ return 0;
+ }
+
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
- if (ret < 0)
+ if (ret < 0) {
netdev_warn(priv->dev,
"failed to enable PTP reference clock: %pe\n",
ERR_PTR(ret));
+ return ret;
+ }
+
+ if (stmmac_init_ptp_clk_freq(priv)) {
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ return 0;
+ }
+
+ ret = stmmac_init_timestamping(priv);
+ if (ret) {
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ return ret;
+ }
+
+ stmmac_ptp_register(priv);
+ priv->ptp_enabled = true;
- if (stmmac_init_timestamping(priv) == 0)
- stmmac_ptp_register(priv);
+ return 0;
}
static void stmmac_release_ptp(struct stmmac_priv *priv)
{
+ if (!priv->ptp_enabled)
+ return;
+
stmmac_ptp_unregister(priv);
clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ priv->ptp_enabled = false;
}
static void stmmac_legacy_serdes_power_down(struct stmmac_priv *priv)
@@ -1742,8 +1773,10 @@ static void stmmac_free_tx_buffer(struct stmmac_priv *priv,
tx_q->xdpf[i] = NULL;
}
- if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XSK_TX)
+ if (tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_XSK_TX) {
tx_q->xsk_frames_done++;
+ tx_q->tx_skbuff_dma[i].buf_type = STMMAC_TXBUF_T_SKB;
+ }
if (tx_q->tx_skbuff[i] &&
tx_q->tx_skbuff_dma[i].buf_type == STMMAC_TXBUF_T_SKB) {
@@ -2591,7 +2624,6 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
}
if (priv->plat->force_thresh_dma_mode) {
- txmode = tc;
rxmode = tc;
} else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
/*
@@ -2603,9 +2635,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
*/
txmode = SF_DMA_MODE;
rxmode = SF_DMA_MODE;
- priv->xstats.threshold = SF_DMA_MODE;
} else {
- txmode = tc;
rxmode = SF_DMA_MODE;
}
@@ -2624,8 +2654,11 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
for (chan = 0; chan < tx_channels_count; chan++) {
qmode = priv->plat->tx_queues_cfg[chan].mode_to_use;
- stmmac_dma_tx_mode(priv, priv->ioaddr, txmode, chan,
- txfifosz, qmode);
+ if (txmode == SF_DMA_MODE)
+ priv->tx_threshold[chan] = SF_DMA_MODE;
+
+ stmmac_dma_tx_mode(priv, priv->ioaddr, priv->tx_threshold[chan],
+ chan, txfifosz, qmode);
}
}
@@ -2685,7 +2718,6 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue);
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
- bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
struct xsk_buff_pool *pool = tx_q->xsk_pool;
unsigned int entry = tx_q->cur_tx;
struct dma_desc *tx_desc = NULL;
@@ -2702,7 +2734,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
struct stmmac_metadata_request meta_req;
struct xsk_tx_metadata *meta = NULL;
dma_addr_t dma_addr;
- bool set_ic;
+ bool set_ic = false;
+ u32 tx_coal;
/* We are sharing with slow path and stop XSK TX desc submission when
* available TX ring is less than threshold.
@@ -2743,12 +2776,9 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
tx_q->tx_count_frames++;
- if (!priv->tx_coal_frames[queue])
- set_ic = false;
- else if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0)
+ tx_coal = READ_ONCE(priv->tx_coal_frames[queue]);
+ if (tx_coal && !(tx_q->tx_count_frames % tx_coal))
set_ic = true;
- else
- set_ic = false;
meta_req.priv = priv;
meta_req.tx_desc = tx_desc;
@@ -2764,7 +2794,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
}
stmmac_prepare_tx_desc(priv, tx_desc, 1, xdp_desc.len,
- csum, priv->descriptor_mode, true, true,
+ false, priv->descriptor_mode, true, true,
xdp_desc.len);
stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
@@ -2794,17 +2824,19 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
static void stmmac_bump_dma_threshold(struct stmmac_priv *priv, u32 chan)
{
- if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && tc <= 256) {
- tc += 64;
+ u32 thresh = priv->tx_threshold[chan];
- if (priv->plat->force_thresh_dma_mode)
- stmmac_set_dma_operation_mode(priv, tc, tc, chan);
- else
- stmmac_set_dma_operation_mode(priv, tc, SF_DMA_MODE,
- chan);
+ if (unlikely(thresh == SF_DMA_MODE) || thresh > 256)
+ return;
- priv->xstats.threshold = tc;
- }
+ thresh += 64;
+
+ if (priv->plat->force_thresh_dma_mode)
+ stmmac_set_dma_operation_mode(priv, thresh, tc, chan);
+ else
+ stmmac_set_dma_operation_mode(priv, thresh, SF_DMA_MODE, chan);
+
+ priv->tx_threshold[chan] = thresh;
}
/**
@@ -2926,8 +2958,11 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue,
tx_q->xdpf[entry] = NULL;
}
- if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XSK_TX)
+ if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_XSK_TX) {
tx_q->xsk_frames_done++;
+ /* Teardown must not count this slot again. */
+ tx_q->tx_skbuff_dma[entry].buf_type = STMMAC_TXBUF_T_SKB;
+ }
if (tx_q->tx_skbuff_dma[entry].buf_type == STMMAC_TXBUF_T_SKB) {
if (likely(skb)) {
@@ -3390,7 +3425,7 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv)
for (chan = 0; chan < tx_channel_count; chan++) {
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[chan];
- priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES;
+ WRITE_ONCE(priv->tx_coal_frames[chan], STMMAC_TX_FRAMES);
priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER;
hrtimer_setup(&tx_q->txtimer, stmmac_tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
@@ -3439,7 +3474,7 @@ static void stmmac_set_tx_queue_weight(struct stmmac_priv *priv)
* @priv: driver private structure
* Description: It is used for configuring CBS in AVB TX queues
*/
-static void stmmac_configure_cbs(struct stmmac_priv *priv)
+void stmmac_configure_cbs(struct stmmac_priv *priv)
{
u8 tx_queues_count = priv->plat->tx_queues_to_use;
u32 mode_to_use;
@@ -3504,17 +3539,11 @@ static void stmmac_mac_config_rx_queues_prio(struct stmmac_priv *priv)
*/
static void stmmac_mac_config_tx_queues_prio(struct stmmac_priv *priv)
{
- u8 tx_queues_count = priv->plat->tx_queues_to_use;
- u8 queue;
- u32 prio;
-
- for (queue = 0; queue < tx_queues_count; queue++) {
- if (!priv->plat->tx_queues_cfg[queue].use_prio)
- continue;
+ int i;
- prio = priv->plat->tx_queues_cfg[queue].prio;
- stmmac_tx_queue_prio(priv, priv->hw, prio, queue);
- }
+ for (i = 0; i < priv->plat->tx_queues_to_use; i++)
+ stmmac_tx_queue_prio(priv, priv->hw,
+ priv->xmit_qdisc.prio[i], i);
}
/**
@@ -3575,7 +3604,7 @@ static void stmmac_mtl_configuration(struct stmmac_priv *priv)
/* Configure MTL TX algorithms */
if (tx_queues_count > 1)
stmmac_prog_mtl_tx_algorithms(priv, priv->hw,
- priv->plat->tx_sched_algorithm);
+ priv->xmit_qdisc.algo);
/* Configure CBS in AVB TX queues */
if (tx_queues_count > 1)
@@ -3749,7 +3778,7 @@ static int stmmac_hw_setup(struct net_device *dev)
/* Configure real RX and TX queues */
netif_set_real_num_rx_queues(dev, priv->plat->rx_queues_to_use);
- netif_set_real_num_tx_queues(dev, priv->plat->tx_queues_to_use);
+ netif_set_real_num_tx_queues(dev, priv->xmit_qdisc.num_tx_queues);
/* Start the ball rolling... */
stmmac_start_all_dma(priv);
@@ -3799,13 +3828,13 @@ static void stmmac_free_irq(struct net_device *dev,
free_irq(msi->sfty_ce_irq, dev);
fallthrough;
case REQ_IRQ_ERR_SFTY_CE:
- if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
- free_irq(priv->wol_irq, dev);
- fallthrough;
- case REQ_IRQ_ERR_SFTY:
if (priv->sfty_irq > 0 && priv->sfty_irq != dev->irq)
free_irq(priv->sfty_irq, dev);
fallthrough;
+ case REQ_IRQ_ERR_SFTY:
+ if (priv->wol_irq > 0 && priv->wol_irq != dev->irq)
+ free_irq(priv->wol_irq, dev);
+ fallthrough;
case REQ_IRQ_ERR_WOL:
free_irq(dev->irq, dev);
fallthrough;
@@ -4165,10 +4194,12 @@ static int __stmmac_open(struct net_device *dev,
ret = stmmac_hw_setup(dev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
- goto init_error;
+ return ret;
}
- stmmac_setup_ptp(priv);
+ ret = stmmac_setup_ptp(priv);
+ if (ret)
+ goto ptp_error;
stmmac_init_coalesce(priv);
@@ -4189,11 +4220,16 @@ static int __stmmac_open(struct net_device *dev,
irq_error:
phylink_stop(priv->phylink);
+ stmmac_stop_all_dma(priv);
+
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
stmmac_release_ptp(priv);
-init_error:
+ptp_error:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+
return ret;
}
@@ -4521,10 +4557,10 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
struct stmmac_txq_stats *txq_stats;
+ u32 tx_coal, pay_len, mss, queue;
int i, first_tx, nfrags, ndesc;
struct stmmac_tx_queue *tx_q;
bool set_ic, is_last_segment;
- u32 pay_len, mss, queue;
dma_addr_t des;
u8 hdr;
@@ -4568,9 +4604,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_set_mss(priv, mss_desc, mss);
tx_q->mss = mss;
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
- priv->dma_conf.dma_tx_size);
- WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
}
if (netif_msg_tx_queued(priv)) {
@@ -4581,6 +4614,9 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
+ if (mss_desc)
+ first_entry = STMMAC_NEXT_ENTRY(first_entry,
+ priv->dma_conf.dma_tx_size);
entry = first_entry;
WARN_ON(tx_q->tx_skbuff[entry]);
@@ -4643,16 +4679,16 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
priv->dma_conf.dma_tx_size);
tx_q->tx_count_frames += tx_packets;
+ tx_coal = READ_ONCE(priv->tx_coal_frames[queue]);
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
set_ic = true;
- else if (!priv->tx_coal_frames[queue])
+ else if (!tx_coal)
set_ic = false;
else if (!netdev_xmit_more())
set_ic = true;
- else if (tx_packets > priv->tx_coal_frames[queue])
+ else if (tx_packets > tx_coal)
set_ic = true;
- else if ((tx_q->tx_count_frames %
- priv->tx_coal_frames[queue]) < tx_packets)
+ else if ((tx_q->tx_count_frames % tx_coal) < tx_packets)
set_ic = true;
else
set_ic = false;
@@ -4749,6 +4785,16 @@ error_dma_unmap:
priv->dma_conf.dma_tx_size);
}
error:
+ if (mss_desc) {
+ /* The context descriptor never reached the DMA, so the MAC is
+ * still programmed with the previous MSS. Invalidate the cache
+ * instead of leaving it ahead of the hardware; zero is the
+ * value stmmac_reset_tx_queue() leaves behind.
+ */
+ stmmac_release_tx_desc(priv, mss_desc, priv->descriptor_mode);
+ tx_q->mss = 0;
+ }
+
dev_err(priv->device, "Tx dma map failed\n");
dev_kfree_skb(skb);
priv->xstats.tx_dropped++;
@@ -4797,9 +4843,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
struct dma_desc *desc, *first_desc;
struct stmmac_tx_queue *tx_q;
int i, csum_insertion = 0;
+ u32 tx_coal, sdu_len;
int entry, first_tx;
dma_addr_t dma_addr;
- u32 sdu_len;
if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en)
stmmac_stop_sw_lpi(priv);
@@ -4940,16 +4986,16 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
tx_packets = CIRC_CNT(entry + 1, first_tx, priv->dma_conf.dma_tx_size);
tx_q->tx_count_frames += tx_packets;
+ tx_coal = READ_ONCE(priv->tx_coal_frames[queue]);
if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en)
set_ic = true;
- else if (!priv->tx_coal_frames[queue])
+ else if (!tx_coal)
set_ic = false;
else if (!netdev_xmit_more())
set_ic = true;
- else if (tx_packets > priv->tx_coal_frames[queue])
+ else if (tx_packets > tx_coal)
set_ic = true;
- else if ((tx_q->tx_count_frames %
- priv->tx_coal_frames[queue]) < tx_packets)
+ else if ((tx_q->tx_count_frames % tx_coal) < tx_packets)
set_ic = true;
else
set_ic = false;
@@ -5071,6 +5117,22 @@ static void stmmac_rx_vlan(struct net_device *dev, struct sk_buff *skb)
}
}
+static bool stmmac_rx_need_ic(struct stmmac_priv *priv,
+ struct stmmac_rx_queue *rx_q, u32 queue)
+{
+ u32 frames = priv->rx_coal_frames[queue];
+
+ /* without the RX watchdog every descriptor needs IC */
+ if (!priv->use_riwt)
+ return true;
+
+ if (!frames || ++rx_q->rx_count_frames < frames)
+ return false;
+
+ rx_q->rx_count_frames = 0;
+ return true;
+}
+
/**
* stmmac_rx_refill - refill used skb preallocated buffers
* @priv: driver private structure
@@ -5116,15 +5178,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
stmmac_rx_check_buf2_cap(priv));
stmmac_refill_desc3(priv, rx_q, p);
- rx_q->rx_count_frames++;
- rx_q->rx_count_frames += priv->rx_coal_frames[queue];
- if (rx_q->rx_count_frames > priv->rx_coal_frames[queue])
- rx_q->rx_count_frames = 0;
-
- use_rx_wd = !priv->rx_coal_frames[queue];
- use_rx_wd |= rx_q->rx_count_frames > 0;
- if (!priv->use_riwt)
- use_rx_wd = false;
+ use_rx_wd = !stmmac_rx_need_ic(priv, rx_q, queue);
dma_wmb();
stmmac_set_rx_owner(priv, p, use_rx_wd);
@@ -5207,77 +5261,127 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
struct xdp_frame *xdpf, bool dma_map)
{
- struct stmmac_txq_stats *txq_stats = &priv->xstats.txq_stats[queue];
+ struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf);
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
bool csum = !priv->plat->tx_queues_cfg[queue].coe_unsupported;
+ unsigned int txq_thr = STMMAC_TX_THRESH(priv);
+ unsigned int first_entry = tx_q->cur_tx;
unsigned int entry = tx_q->cur_tx;
- enum stmmac_txbuf_type buf_type;
- struct dma_desc *tx_desc;
- dma_addr_t dma_addr;
- bool set_ic;
+ unsigned int num_frames = 1;
+ struct dma_desc *desc;
+ u32 tx_coal;
+ int i = 0;
- if (stmmac_tx_avail(priv, queue) < STMMAC_TX_THRESH(priv))
+ if (unlikely(xdp_frame_has_frags(xdpf)))
+ num_frames += sinfo->nr_frags;
+
+ if (stmmac_tx_avail(priv, queue) < max(num_frames, txq_thr))
return STMMAC_XDP_CONSUMED;
if (priv->est && priv->est->enable &&
priv->est->max_sdu[queue] &&
- xdpf->len > priv->est->max_sdu[queue]) {
+ xdp_get_frame_len(xdpf) > priv->est->max_sdu[queue]) {
priv->xstats.max_sdu_txq_drop[queue]++;
return STMMAC_XDP_CONSUMED;
}
- tx_desc = stmmac_get_tx_desc(priv, tx_q, entry);
- if (dma_map) {
- dma_addr = dma_map_single(priv->device, xdpf->data,
- xdpf->len, DMA_TO_DEVICE);
- if (dma_mapping_error(priv->device, dma_addr))
- return STMMAC_XDP_CONSUMED;
-
- buf_type = STMMAC_TXBUF_T_XDP_NDO;
- } else {
- struct page *page = virt_to_page(xdpf->data);
-
- dma_addr = page_pool_get_dma_addr(page) + sizeof(*xdpf) +
- xdpf->headroom;
- dma_sync_single_for_device(priv->device, dma_addr,
- xdpf->len, DMA_BIDIRECTIONAL);
+ /* Disable TX checksum offload for jumbo frames on cores where
+ * checksum insertion is broken for oversized frames, matching the
+ * stmmac_fix_features() workaround used by the skb path.
+ */
+ if (priv->plat->bugged_jumbo &&
+ xdp_get_frame_len(xdpf) > ETH_FRAME_LEN)
+ csum = false;
+
+ while (true) {
+ skb_frag_t *frag = i ? &sinfo->frags[i - 1] : NULL;
+ int len = frag ? skb_frag_size(frag) : xdpf->len;
+ bool last_frame = i == num_frames - 1;
+ enum stmmac_txbuf_type buf_type;
+ dma_addr_t dma_addr;
- buf_type = STMMAC_TXBUF_T_XDP_TX;
- }
+ desc = stmmac_get_tx_desc(priv, tx_q, entry);
+ if (dma_map) {
+ if (frag)
+ dma_addr = skb_frag_dma_map(priv->device,
+ frag, 0, len,
+ DMA_TO_DEVICE);
+ else
+ dma_addr = dma_map_single(priv->device,
+ xdpf->data, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(priv->device, dma_addr))
+ goto error_dma_unmap;
- stmmac_set_tx_dma_entry(tx_q, entry, buf_type, dma_addr, xdpf->len,
- false);
- stmmac_set_tx_dma_last_segment(tx_q, entry);
+ buf_type = STMMAC_TXBUF_T_XDP_NDO;
+ } else {
+ struct page *page;
- tx_q->xdpf[entry] = xdpf;
+ page = frag ? skb_frag_page(frag)
+ : virt_to_head_page(xdpf->data);
+ dma_addr = page_pool_get_dma_addr(page);
+ if (frag)
+ dma_addr += skb_frag_off(frag);
+ else
+ dma_addr += sizeof(*xdpf) + xdpf->headroom;
+ dma_sync_single_for_device(priv->device, dma_addr,
+ len, DMA_BIDIRECTIONAL);
+ buf_type = STMMAC_TXBUF_T_XDP_TX;
+ }
- stmmac_set_desc_addr(priv, tx_desc, dma_addr);
+ stmmac_set_tx_dma_entry(tx_q, entry, buf_type, dma_addr, len,
+ dma_map && frag);
+ stmmac_set_desc_addr(priv, desc, dma_addr);
+ stmmac_prepare_tx_desc(priv, desc, !i, len, csum,
+ priv->descriptor_mode, !!i, last_frame,
+ xdp_get_frame_len(xdpf));
+ tx_q->xdpf[entry] = last_frame ? xdpf : NULL;
+ if (last_frame) {
+ stmmac_set_tx_dma_last_segment(tx_q, entry);
+ break;
+ }
- stmmac_prepare_tx_desc(priv, tx_desc, 1, xdpf->len,
- csum, priv->descriptor_mode, true, true,
- xdpf->len);
+ entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
+ i++;
+ }
+ tx_q->tx_count_frames += num_frames;
- tx_q->tx_count_frames++;
+ tx_coal = READ_ONCE(priv->tx_coal_frames[queue]);
+ if (tx_coal && (tx_q->tx_count_frames % tx_coal) < num_frames) {
+ struct stmmac_txq_stats *txq_stats;
- if (tx_q->tx_count_frames % priv->tx_coal_frames[queue] == 0)
- set_ic = true;
- else
- set_ic = false;
-
- if (set_ic) {
+ desc = stmmac_get_tx_desc(priv, tx_q, entry);
+ stmmac_set_tx_ic(priv, desc);
tx_q->tx_count_frames = 0;
- stmmac_set_tx_ic(priv, tx_desc);
+
+ txq_stats = &priv->xstats.txq_stats[queue];
u64_stats_update_begin(&txq_stats->q_syncp);
u64_stats_inc(&txq_stats->q.tx_set_ic_bit);
u64_stats_update_end(&txq_stats->q_syncp);
}
+ /* Set the OWN bit on the first descriptor now that all descriptors
+ * for this xdp_frame are populated.
+ */
+ desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
+ dma_wmb();
+ stmmac_set_tx_owner(priv, desc);
+ tx_q->cur_tx = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
stmmac_enable_dma_transmission(priv, priv->ioaddr, queue);
- entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_tx_size);
- tx_q->cur_tx = entry;
-
return STMMAC_XDP_TX;
+
+error_dma_unmap:
+ while (first_entry != entry) {
+ desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
+ stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
+ stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
+ first_entry);
+ first_entry = STMMAC_NEXT_ENTRY(first_entry,
+ priv->dma_conf.dma_tx_size);
+ }
+
+ return STMMAC_XDP_CONSUMED;
}
static int stmmac_xdp_get_tx_queue(struct stmmac_priv *priv,
@@ -5492,15 +5596,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
stmmac_set_desc_sec_addr(priv, rx_desc, 0, false);
stmmac_refill_desc3(priv, rx_q, rx_desc);
- rx_q->rx_count_frames++;
- rx_q->rx_count_frames += priv->rx_coal_frames[queue];
- if (rx_q->rx_count_frames > priv->rx_coal_frames[queue])
- rx_q->rx_count_frames = 0;
-
- use_rx_wd = !priv->rx_coal_frames[queue];
- use_rx_wd |= rx_q->rx_count_frames > 0;
- if (!priv->use_riwt)
- use_rx_wd = false;
+ use_rx_wd = !stmmac_rx_need_ic(priv, rx_q, queue);
dma_wmb();
stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
@@ -6176,7 +6272,12 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
return -EINVAL;
- if (netif_running(dev)) {
+ /* Normal-size frames use the same buffers and MAC receive limits.
+ * In particular, do not disturb a live AF_XDP pool: XDP does not
+ * support jumbo frames, so it never needs the ring replacement below.
+ */
+ if (netif_running(dev) &&
+ (dev->mtu > ETH_DATA_LEN || mtu > ETH_DATA_LEN)) {
netdev_dbg(priv->dev, "restarting interface to change its MTU\n");
/* Try to allocate the new DMA conf with the new mtu */
dma_conf = stmmac_setup_dma_desc(priv, mtu);
@@ -6493,24 +6594,6 @@ static int stmmac_setup_tc(struct net_device *ndev, enum tc_setup_type type,
}
}
-static u16 stmmac_select_queue(struct net_device *dev, struct sk_buff *skb,
- struct net_device *sb_dev)
-{
- int gso = skb_shinfo(skb)->gso_type;
-
- if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6 | SKB_GSO_UDP_L4)) {
- /*
- * There is no way to determine the number of TSO/USO
- * capable Queues. Let's use always the Queue 0
- * because if TSO/USO is supported then at least this
- * one will be capable.
- */
- return 0;
- }
-
- return netdev_pick_tx(dev, skb, NULL) % dev->real_num_tx_queues;
-}
-
static int stmmac_set_mac_address(struct net_device *ndev, void *addr)
{
struct stmmac_priv *priv = netdev_priv(ndev);
@@ -6647,6 +6730,18 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
seq_printf(seq,
"\tNumber of Additional MAC address registers: %d\n",
priv->dma_cap.multi_addr);
+ } else if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
+ seq_printf(seq,
+ "\tNumber of MAC address registers (1-31): %d\n",
+ priv->dma_cap.multi_addr);
+ seq_printf(seq,
+ "\tAdditional 32 MAC address registers (32-63): %s\n",
+ priv->dma_cap.additional_32_addr ? "Y" : "N");
+ seq_printf(seq,
+ "\tAdditional 64 MAC address registers (64-127): %s\n",
+ priv->dma_cap.additional_64_addr ? "Y" : "N");
+ seq_printf(seq, "\tHash Filter: %s\n",
+ (priv->dma_cap.hash_filter) ? "Y" : "N");
} else {
seq_printf(seq, "\tHash Filter: %s\n",
(priv->dma_cap.hash_filter) ? "Y" : "N");
@@ -7243,6 +7338,8 @@ int stmmac_xdp_open(struct net_device *dev)
return 0;
irq_error:
+ stmmac_stop_all_dma(priv);
+
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
hrtimer_cancel(&priv->dma_conf.tx_queue[chan].txtimer);
@@ -7354,7 +7451,6 @@ static const struct net_device_ops stmmac_netdev_ops = {
.ndo_eth_ioctl = stmmac_ioctl,
.ndo_get_stats64 = stmmac_get_stats64,
.ndo_setup_tc = stmmac_setup_tc,
- .ndo_select_queue = stmmac_select_queue,
.ndo_set_mac_address = stmmac_set_mac_address,
.ndo_vlan_rx_add_vid = stmmac_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = stmmac_vlan_rx_kill_vid,
@@ -7536,6 +7632,33 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
priv->plat->tx_fifo_size = priv->dma_cap.tx_fifo_size;
}
+ /* On DWMAC4 we can get the exact number of perfect filter entries from
+ * the HW_Features.
+ */
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
+ priv->hw->multi_addr = priv->dma_cap.multi_addr;
+ priv->hw->additional_32_addr =
+ !!priv->dma_cap.additional_32_addr;
+ priv->hw->additional_64_addr =
+ !!priv->dma_cap.additional_64_addr;
+
+ /* We always have one slot for the primary MAC */
+ priv->hw->unicast_filter_entries = 1;
+
+ /* How many slots in the 1 -> 31 range */
+ priv->hw->unicast_filter_entries += priv->hw->multi_addr;
+
+ /* Additional 32 entries in the 32 -> 63 range */
+ if (priv->hw->additional_32_addr)
+ priv->hw->unicast_filter_entries += 32;
+
+ /* Additional 64 entries in the 64 -> 127 range, can be enabled
+ * independently of the 32 -> 63 range
+ */
+ if (priv->hw->additional_64_addr)
+ priv->hw->unicast_filter_entries += 64;
+ }
+
priv->hw->vlan_fail_q_en =
(priv->plat->flags & STMMAC_FLAG_VLAN_FAIL_Q_EN);
priv->hw->vlan_fail_q = priv->plat->vlan_fail_q;
@@ -7633,6 +7756,10 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
struct stmmac_priv *priv = netdev_priv(dev);
int ret = 0, i;
+ if (priv->xmit_qdisc.enabled &&
+ tx_cnt < priv->xmit_qdisc.num_tx_queues)
+ return -EINVAL;
+
if (netif_running(dev))
stmmac_release(dev);
@@ -7640,6 +7767,9 @@ int stmmac_reinit_queues(struct net_device *dev, u8 rx_cnt, u8 tx_cnt)
priv->plat->rx_queues_to_use = rx_cnt;
priv->plat->tx_queues_to_use = tx_cnt;
+ if (!priv->xmit_qdisc.enabled)
+ priv->xmit_qdisc.num_tx_queues = tx_cnt;
+
if (!netif_is_rxfh_configured(dev))
for (i = 0; i < ARRAY_SIZE(priv->rss.table); i++)
priv->rss.table[i] = ethtool_rxfh_indir_default(i,
@@ -7754,8 +7884,7 @@ static int stmmac_register_devlink(struct stmmac_priv *priv)
/* For now, what is exposed over devlink is only relevant when
* timestamping is available and we have a valid ptp clock rate
*/
- if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) ||
- !priv->plat->clk_ptp_rate)
+ if (!stmmac_check_timestamp_cap(priv) || !priv->plat->clk_ptp_rate)
return 0;
priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv),
@@ -7876,6 +8005,15 @@ static int __stmmac_dvr_probe(struct device *device,
priv->wol_irq = res->wol_irq;
priv->sfty_irq = res->sfty_irq;
+ /* Default xmit qdisc configuration */
+ for (i = 0; i < ARRAY_SIZE(priv->plat->tx_queues_cfg); i++) {
+ if (!priv->plat->tx_queues_cfg[i].use_prio)
+ continue;
+
+ priv->xmit_qdisc.prio[i] = priv->plat->tx_queues_cfg[i].prio;
+ }
+ priv->xmit_qdisc.algo = priv->plat->tx_sched_algorithm;
+
if (priv->plat->flags & STMMAC_FLAG_MULTI_MSI_EN) {
ret = stmmac_msi_init(priv, res);
if (ret)
@@ -7952,6 +8090,9 @@ static int __stmmac_dvr_probe(struct device *device,
ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
NETDEV_XDP_ACT_XSK_ZEROCOPY;
+ /* Default qdisc num_tx_queues */
+ priv->xmit_qdisc.num_tx_queues = priv->plat->tx_queues_to_use;
+
ret = stmmac_tc_init(priv, priv);
if (!ret) {
ndev->hw_features |= NETIF_F_HW_TC;
@@ -8023,7 +8164,8 @@ static int __stmmac_dvr_probe(struct device *device,
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
- priv->xstats.threshold = tc;
+ for (i = 0; i < MTL_MAX_TX_QUEUES; i++)
+ priv->tx_threshold[i] = tc;
/* Initialize RSS */
rxq = priv->plat->rx_queues_to_use;
@@ -8364,14 +8506,19 @@ int stmmac_resume(struct device *dev)
ret = stmmac_hw_setup(ndev);
if (ret < 0) {
netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
- stmmac_legacy_serdes_power_down(priv);
- mutex_unlock(&priv->lock);
- rtnl_unlock();
- return ret;
+ goto error_unlock;
}
- stmmac_init_timestamping(priv);
+ if (priv->ptp_enabled) {
+ if (stmmac_init_ptp_clk_freq(priv))
+ goto init_coalesce;
+ ret = stmmac_init_timestamping(priv);
+ if (ret)
+ goto error_stop_dma;
+ }
+
+init_coalesce:
stmmac_init_coalesce(priv);
phylink_rx_clk_stop_block(priv->phylink);
stmmac_set_rx_mode(ndev);
@@ -8394,6 +8541,16 @@ int stmmac_resume(struct device *dev)
netif_device_attach(ndev);
return 0;
+
+error_stop_dma:
+ stmmac_stop_all_dma(priv);
+ stmmac_mac_set(priv, priv->ioaddr, false);
+error_unlock:
+ stmmac_legacy_serdes_power_down(priv);
+ mutex_unlock(&priv->lock);
+ rtnl_unlock();
+
+ return ret;
}
EXPORT_SYMBOL_GPL(stmmac_resume);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index afe98ff5bdcb..7396b68899c6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -426,35 +426,14 @@ int stmmac_mdio_reset(struct mii_bus *bus)
int stmmac_pcs_setup(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- struct fwnode_handle *devnode, *pcsnode;
- struct dw_xpcs *xpcs = NULL;
- int addr, ret;
-
- devnode = dev_fwnode(priv->device);
-
- if (priv->plat->pcs_init) {
- ret = priv->plat->pcs_init(priv);
- } else if (fwnode_property_present(devnode, "pcs-handle")) {
- pcsnode = fwnode_find_reference(devnode, "pcs-handle", 0);
- xpcs = xpcs_create_fwnode(pcsnode);
- fwnode_handle_put(pcsnode);
- ret = PTR_ERR_OR_ZERO(xpcs);
- } else if (priv->plat->mdio_bus_data &&
- priv->plat->mdio_bus_data->pcs_mask) {
- addr = ffs(priv->plat->mdio_bus_data->pcs_mask) - 1;
- xpcs = xpcs_create_mdiodev(priv->mii, addr);
- ret = PTR_ERR_OR_ZERO(xpcs);
- } else {
+ int ret;
+
+ if (!priv->plat->pcs_init)
return 0;
- }
+ ret = priv->plat->pcs_init(priv);
if (ret)
- return dev_err_probe(priv->device, ret, "No xPCS found\n");
-
- if (xpcs)
- xpcs_config_eee_mult_fact(xpcs, priv->plat->mult_fact_100ns);
-
- priv->hw->xpcs = xpcs;
+ return dev_err_probe(priv->device, ret, "Failed to initialize PCS\n");
return 0;
}
@@ -463,14 +442,10 @@ void stmmac_pcs_clean(struct net_device *ndev)
{
struct stmmac_priv *priv = netdev_priv(ndev);
- if (priv->plat->pcs_exit)
- priv->plat->pcs_exit(priv);
-
- if (!priv->hw->xpcs)
+ if (!priv->plat->pcs_exit)
return;
- xpcs_destroy(priv->hw->xpcs);
- priv->hw->xpcs = NULL;
+ priv->plat->pcs_exit(priv);
}
struct stmmac_clk_rate {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 6128ed1bd521..3ea16d618215 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -16,6 +16,7 @@
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
+#include <linux/phy.h>
#include "stmmac.h"
#include "stmmac_platform.h"
@@ -847,6 +848,16 @@ int stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res)
{
+ if (plat->has_internal_tx_delay || plat->has_internal_rx_delay) {
+ plat->phy_interface =
+ phy_fix_phy_mode_for_mac_delays(plat->phy_interface,
+ plat->has_internal_tx_delay,
+ plat->has_internal_rx_delay);
+ if (plat->phy_interface == PHY_INTERFACE_MODE_NA)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "unsupported phy interface mode\n");
+ }
+
if (!plat->suspend && plat->exit)
plat->suspend = stmmac_plat_suspend;
if (!plat->resume && plat->init)
@@ -959,7 +970,8 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev)
if (!priv->wolopts) {
/* Disable clock in case of PWM is off */
- clk_disable_unprepare(priv->plat->clk_ptp_ref);
+ if (priv->ptp_enabled)
+ clk_disable_unprepare(priv->plat->clk_ptp_ref);
ret = pm_runtime_force_suspend(dev);
if (ret)
@@ -984,6 +996,9 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
if (ret)
return ret;
+ if (!priv->ptp_enabled)
+ return 0;
+
ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
if (ret < 0) {
netdev_warn(priv->dev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index 3bfcc9760dce..e54a3c887826 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -89,7 +89,8 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
u64 cycle_time;
mutex_lock(&priv->est_lock);
- priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, &current_time);
+ priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops,
+ &current_time, NULL);
current_time_ns = timespec64_to_ktime(current_time);
time.tv_nsec = priv->est->btr_reserve[0];
time.tv_sec = priv->est->btr_reserve[1];
@@ -114,15 +115,15 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
}
/**
- * stmmac_get_time
- *
+ * stmmac_get_timex - read the PTP clock, with system timestamps if requested
* @ptp: pointer to ptp_clock_info structure
* @ts: pointer to hold time/result
+ * @sts: pointer to hold system timestamps around the read, or NULL
*
- * Description: this function will read the current time from the
- * hardware clock and store it in @ts.
+ * Return: 0
*/
-static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
+static int stmmac_get_timex(struct ptp_clock_info *ptp, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
{
struct stmmac_priv *priv =
container_of(ptp, struct stmmac_priv, ptp_clock_ops);
@@ -130,7 +131,7 @@ static int stmmac_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts)
u64 ns = 0;
read_lock_irqsave(&priv->ptp_lock, flags);
- stmmac_get_systime(priv, priv->ptpaddr, &ns);
+ stmmac_get_systime(priv, priv->ptpaddr, &ns, sts);
read_unlock_irqrestore(&priv->ptp_lock, flags);
*ts = ns_to_timespec64(ns);
@@ -200,7 +201,7 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
target_ns = cfg->start.tv_nsec + ((u64)cfg->start.tv_sec * NSEC_PER_SEC);
- stmmac_get_systime(priv, priv->ptpaddr, &ns);
+ stmmac_get_systime(priv, priv->ptpaddr, &ns, NULL);
if (ns > TIME64_MAX - PTP_SAFE_TIME_OFFSET_NS)
return -EINVAL;
@@ -304,7 +305,7 @@ const struct ptp_clock_info stmmac_ptp_clock_ops = {
.pps = 0,
.adjfine = stmmac_adjust_freq,
.adjtime = stmmac_adjust_time,
- .gettime64 = stmmac_get_time,
+ .gettimex64 = stmmac_get_timex,
.settime64 = stmmac_set_time,
.enable = stmmac_enable,
};
@@ -321,7 +322,7 @@ const struct ptp_clock_info dwmac1000_ptp_clock_ops = {
.pps = 0,
.adjfine = stmmac_adjust_freq,
.adjtime = stmmac_adjust_time,
- .gettime64 = stmmac_get_time,
+ .gettimex64 = stmmac_get_timex,
.settime64 = stmmac_set_time,
.enable = dwmac1000_ptp_enable,
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index c485217ba880..40811df8f2b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -217,24 +217,6 @@ static struct sk_buff *stmmac_test_get_udp_skb(struct stmmac_priv *priv,
return skb;
}
-static struct sk_buff *stmmac_test_get_arp_skb(struct stmmac_priv *priv,
- struct stmmac_packet_attrs *attr)
-{
- __be32 ip_src = htonl(attr->ip_src);
- __be32 ip_dst = htonl(attr->ip_dst);
- struct sk_buff *skb = NULL;
-
- skb = arp_create(ARPOP_REQUEST, ETH_P_ARP, ip_dst, priv->dev, ip_src,
- NULL, attr->src, attr->dst);
- if (!skb)
- return NULL;
-
- skb->pkt_type = PACKET_HOST;
- skb->dev = priv->dev;
-
- return skb;
-}
-
struct stmmac_test_priv {
struct stmmac_packet_attrs *packet;
struct packet_type pt;
@@ -262,7 +244,7 @@ static int stmmac_test_loopback_validate(struct sk_buff *skb,
struct tcphdr *thdr;
struct iphdr *ihdr;
- skb = skb_unshare(skb, GFP_ATOMIC);
+ skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
goto out;
@@ -938,7 +920,7 @@ static int stmmac_test_vlan_validate(struct sk_buff *skb,
proto = tpriv->svlan ? ETH_P_8021AD : ETH_P_8021Q;
- skb = skb_unshare(skb, GFP_ATOMIC);
+ skb = skb_unclone_full(skb, GFP_ATOMIC);
if (!skb)
goto out;
@@ -1693,97 +1675,6 @@ static int stmmac_test_l4filt_sa_udp(struct stmmac_priv *priv)
return __stmmac_test_l4filt(priv, 0, dummy_port, 0, ~0, true);
}
-static int stmmac_test_arp_validate(struct sk_buff *skb,
- struct net_device *ndev,
- struct packet_type *pt,
- struct net_device *orig_ndev)
-{
- struct stmmac_test_priv *tpriv = pt->af_packet_priv;
- struct ethhdr *ehdr;
- struct arphdr *ahdr;
-
- ehdr = (struct ethhdr *)skb_mac_header(skb);
- if (!ether_addr_equal_unaligned(ehdr->h_dest, tpriv->packet->src))
- goto out;
-
- ahdr = arp_hdr(skb);
- if (ahdr->ar_op != htons(ARPOP_REPLY))
- goto out;
-
- tpriv->ok = true;
- complete(&tpriv->comp);
-out:
- kfree_skb(skb);
- return 0;
-}
-
-static int stmmac_test_arpoffload(struct stmmac_priv *priv)
-{
- unsigned char src[ETH_ALEN] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
- unsigned char dst[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
- struct stmmac_packet_attrs attr = { };
- struct stmmac_test_priv *tpriv;
- struct sk_buff *skb = NULL;
- u32 ip_addr = 0xdeadcafe;
- u32 ip_src = 0xdeadbeef;
- int ret;
-
- if (!priv->dma_cap.arpoffsel)
- return -EOPNOTSUPP;
-
- tpriv = kzalloc_obj(*tpriv);
- if (!tpriv)
- return -ENOMEM;
-
- tpriv->ok = false;
- init_completion(&tpriv->comp);
-
- tpriv->pt.type = htons(ETH_P_ARP);
- tpriv->pt.func = stmmac_test_arp_validate;
- tpriv->pt.dev = priv->dev;
- tpriv->pt.af_packet_priv = tpriv;
- tpriv->packet = &attr;
- dev_add_pack(&tpriv->pt);
-
- attr.src = src;
- attr.ip_src = ip_src;
- attr.dst = dst;
- attr.ip_dst = ip_addr;
-
- skb = stmmac_test_get_arp_skb(priv, &attr);
- if (!skb) {
- ret = -ENOMEM;
- goto cleanup;
- }
-
- ret = stmmac_set_arp_offload(priv, priv->hw, true, ip_addr);
- if (ret) {
- kfree_skb(skb);
- goto cleanup;
- }
-
- ret = dev_set_promiscuity(priv->dev, 1);
- if (ret) {
- kfree_skb(skb);
- goto cleanup;
- }
-
- ret = dev_direct_xmit(skb, 0);
- if (ret)
- goto cleanup_promisc;
-
- wait_for_completion_timeout(&tpriv->comp, STMMAC_LB_TIMEOUT);
- ret = tpriv->ok ? 0 : -ETIMEDOUT;
-
-cleanup_promisc:
- dev_set_promiscuity(priv->dev, -1);
-cleanup:
- stmmac_set_arp_offload(priv, priv->hw, false, 0x0);
- dev_remove_pack(&tpriv->pt);
- kfree(tpriv);
- return ret;
-}
-
static int __stmmac_test_jumbo(struct stmmac_priv *priv, u16 queue)
{
struct stmmac_packet_attrs attr = { };
@@ -1887,7 +1778,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv)
return ret;
read_lock_irqsave(&priv->ptp_lock, flags);
- stmmac_get_systime(priv, priv->ptpaddr, &curr_time);
+ stmmac_get_systime(priv, priv->ptpaddr, &curr_time, NULL);
read_unlock_irqrestore(&priv->ptp_lock, flags);
if (!curr_time) {
@@ -1909,7 +1800,7 @@ static int stmmac_test_tbs(struct stmmac_priv *priv)
/* Check if expected time has elapsed */
read_lock_irqsave(&priv->ptp_lock, flags);
- stmmac_get_systime(priv, priv->ptpaddr, &curr_time);
+ stmmac_get_systime(priv, priv->ptpaddr, &curr_time, NULL);
read_unlock_irqrestore(&priv->ptp_lock, flags);
if ((curr_time - start_time) < STMMAC_TBS_LT_OFFSET)
@@ -2005,9 +1896,6 @@ static const struct stmmac_test {
.name = "L4 SA UDP Filtering ",
.fn = stmmac_test_l4filt_sa_udp,
}, {
- .name = "ARP Offload ",
- .fn = stmmac_test_arpoffload,
- }, {
.name = "Jumbo Frame ",
.fn = stmmac_test_jumbo,
}, {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 42a00446e9b4..e137fb9012d4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -1070,7 +1070,8 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
mutex_lock(&priv->est_lock);
/* Adjust for real system time */
- priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, &current_time);
+ priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops, &current_time,
+ NULL);
current_time_ns = timespec64_to_ktime(current_time);
time = stmmac_calc_tas_basetime(qopt->base_time, current_time_ns,
qopt->cycle_time);
@@ -1266,12 +1267,105 @@ static int stmmac_reset_tc_mqprio(struct net_device *ndev,
{
struct stmmac_priv *priv = netdev_priv(ndev);
+ if (priv->xmit_qdisc.prio_offload) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->plat->tx_queues_cfg); i++) {
+ u32 prio;
+
+ if (priv->plat->tx_queues_cfg[i].use_prio)
+ prio = priv->plat->tx_queues_cfg[i].prio;
+ else
+ prio = 0;
+
+ priv->xmit_qdisc.prio[i] = prio;
+ if (i < priv->plat->tx_queues_to_use)
+ stmmac_tx_queue_prio(priv, priv->hw, prio, i);
+ }
+
+ stmmac_prog_mtl_tx_algorithms(priv, priv->hw,
+ priv->plat->tx_sched_algorithm);
+ /* Re-configure AVB queues if necessary */
+ stmmac_configure_cbs(priv);
+ priv->xmit_qdisc.algo = priv->plat->tx_sched_algorithm;
+ priv->xmit_qdisc.prio_offload = false;
+ }
+
+ priv->xmit_qdisc.num_tx_queues = priv->plat->tx_queues_to_use;
+ priv->xmit_qdisc.enabled = false;
+
netdev_reset_tc(ndev);
netif_set_real_num_tx_queues(ndev, priv->plat->tx_queues_to_use);
return stmmac_fpe_map_preemption_class(priv, ndev, extack, 0);
}
+static void tc_mqprio_config_queue_prio(struct stmmac_priv *priv,
+ struct tc_mqprio_qopt *qopt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->plat->tx_queues_cfg); i++) {
+ u32 prio = 0;
+ int j;
+
+ for (j = 0; j < qopt->num_tc; j++) {
+ int p;
+
+ if (qopt->offset[j] != i)
+ continue;
+
+ /* The PSTQX/PSTC priority map is 8 bits wide, so only
+ * priorities 0-7 can be represented in hardware.
+ * Priorities 8-15 are handled in software by the
+ * kernel through the netdev prio_tc_map.
+ */
+ for (p = 0; p < 8; p++) {
+ if (qopt->prio_tc_map[p] == j)
+ prio |= BIT(p);
+ }
+ break;
+ }
+
+ priv->xmit_qdisc.prio[i] = prio;
+ if (i < priv->plat->tx_queues_to_use)
+ stmmac_tx_queue_prio(priv, priv->hw, prio, i);
+ }
+
+ stmmac_prog_mtl_tx_algorithms(priv, priv->hw, MTL_TX_ALGORITHM_SP);
+ /* Re-configure AVB queues if necessary */
+ stmmac_configure_cbs(priv);
+ priv->xmit_qdisc.algo = MTL_TX_ALGORITHM_SP;
+ priv->xmit_qdisc.prio_offload = true;
+}
+
+static int tc_mqprio_validate_chan_mode(struct stmmac_priv *priv,
+ struct tc_mqprio_qopt_offload *mqprio)
+{
+ struct tc_mqprio_qopt *qopt = &mqprio->qopt;
+ int i;
+
+ if (!priv->dma_cap.dcben) {
+ NL_SET_ERR_MSG_MOD(mqprio->extack,
+ "hw DCB is required to offload mqprio");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < qopt->num_tc; i++) {
+ /* The offload switches the MTL scheduler to strict
+ * priority, which only supports a 1:1 TC to TX queue
+ * mapping.
+ */
+ if (qopt->count[i] > 1 || qopt->offset[i] != i) {
+ NL_SET_ERR_MSG_MOD(mqprio->extack,
+ "SP requires 1:1 TXQ map");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
struct tc_mqprio_qopt_offload *mqprio)
{
@@ -1282,13 +1376,34 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
struct tc_mqprio_qopt *qopt = &mqprio->qopt;
struct net_device *ndev = priv->dev;
u8 ndev_prio_tc_map[TC_BITMASK + 1];
- int i, err, ndev_ntc;
+ int i, err, ndev_ntc, mode;
if (!qopt->num_tc)
return stmmac_reset_tc_mqprio(ndev, extack);
- if (qopt->num_tc > ARRAY_SIZE(tc_to_txq))
- return -EINVAL;
+ for (i = 0; i < qopt->num_tc; i++) {
+ if (qopt->offset[i] + qopt->count[i] >
+ priv->plat->tx_queues_to_use) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Queues exceed hw TX queues");
+ return -EINVAL;
+ }
+
+ tc_to_txq[i] = (struct netdev_tc_txq) {
+ .count = qopt->count[i],
+ .offset = qopt->offset[i],
+ };
+ num_tx_queues = max(num_tx_queues,
+ qopt->offset[i] + qopt->count[i]);
+ }
+
+ mode = mqprio->flags & TC_MQPRIO_F_MODE ? mqprio->mode
+ : TC_MQPRIO_MODE_DCB;
+ if (mode == TC_MQPRIO_MODE_CHANNEL) {
+ err = tc_mqprio_validate_chan_mode(priv, mqprio);
+ if (err)
+ return err;
+ }
/* save current tc values for reset */
ndev_ntc = netdev_get_num_tc(ndev);
@@ -1298,14 +1413,6 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
for (i = 0; i < ARRAY_SIZE(ndev_prio_tc_map); i++)
ndev_prio_tc_map[i] = READ_ONCE(ndev->prio_tc_map[i]);
- for (i = 0; i < qopt->num_tc; i++) {
- tc_to_txq[i] = (struct netdev_tc_txq) {
- .count = qopt->count[i],
- .offset = qopt->offset[i],
- };
- num_tx_queues += qopt->count[i];
- }
-
err = stmmac_set_ndev_tcs(ndev, qopt->num_tc, tc_to_txq);
if (err)
goto error_reset_tc;
@@ -1320,6 +1427,12 @@ static int tc_setup_dwmac510_mqprio(struct stmmac_priv *priv,
if (err)
goto error_reset_num_tx_queues;
+ if (mode == TC_MQPRIO_MODE_CHANNEL)
+ tc_mqprio_config_queue_prio(priv, qopt);
+
+ priv->xmit_qdisc.num_tx_queues = num_tx_queues;
+ priv->xmit_qdisc.enabled = true;
+
return 0;
error_reset_num_tx_queues:
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
index d7e4db7224b0..9af9e0cb366c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
@@ -135,7 +135,7 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
stmmac_xdp_open(dev);
if (prog)
- xdp_features_set_redirect_target(dev, false);
+ xdp_features_set_redirect_target(dev, true);
return 0;
}