diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
| commit | 734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch) | |
| tree | ce2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/ethernet/stmicro | |
| parent | 864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff) | |
| parent | 1a2c35c49cc7c6be860038b8442b52424a8f123c (diff) | |
| download | linux-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')
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 = ðqos->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, ðqos->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, ¤t_time); + priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops, + ¤t_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, ¤t_time); + priv->ptp_clock_ops.gettimex64(&priv->ptp_clock_ops, ¤t_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; } |
