summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerge Semin <fancer.lancer@gmail.com>2025-08-13 20:10:49 +0300
committerSerge Semin <fancer.lancer@gmail.com>2026-08-13 20:33:44 +0300
commit7dcefeeb505d48a34ca44db31d131c9dc5685a2b (patch)
treed688a8d40151d8f9ebf0146e09b4f84af106b3ce
parent9407bf274087632814b5b337bc0c8bdf9dc89c42 (diff)
downloadlinux-7dcefeeb505d48a34ca44db31d131c9dc5685a2b.tar.gz
linux-7dcefeeb505d48a34ca44db31d131c9dc5685a2b.zip
net: stmmac: dwmac1000: Detect and handle Jumbo frames
The driver setups the frame size limits by using the MAC_CONTROL.JD and MAC_CONTROL.JE flags. The former one is responsible for the transmit Jabber engine disabling and the later one enables the Jumbo frames support for both Rx and Tx path. But if the MAC_CONTROL.JD is set MAC_CONTROL.JE won't affect the Tx path and only works for the Rx traffic. Thus the driver currently permits to transmit frames up to 16K bytes irrespective to the MTU value, to receive frames of up to 1518(1522), 2000, 9018(9022) bytes with no packet dropped due to the giant frame status, to get truncated frames over 2048 and 10240 bytes. Moreover giant frames dropping isn't working by default for the DW GMAC controllers of v3.70a and newer when there is no Giant frame status reported via the Rx DMA-descriptors (if Advanced Timestamp or Rx COE v1/v2 features is selected during the IP-core synthesizing). It isn't right to consider the MTU setting that relaxed and to have the device working so much differently depending on the controller version. Moreover based on what is said in [1] the most preferred solution would be to drop the oversized frames instead of passing them up to the networking core subsystem. Also note even though [1] says that the upper layer protocols must not pass a socket buffer (skb) to a device to transmit with more data than the MTU in fact it does at least in case of the pktgen module. In case of DW GMAC the driver can work way better to comply with what the networking subsystem requires. First of all the MAC_CONTROL.JD can be cleared thus enabling the MAC internal watchdog to truncate the oversized frames transmission on 2048 or 10240 outgoing octets. Secondly starting from v3.70a there is a watchdog timeout register which can be used to fine-tune the MAC to truncate the recv frames of the size greater than specified. Finally since the v3.70a IP-core there has been the DMA_CONTROL.DGF flag activating the Giant frames dropping. All the features above can be used to implement a more clever Jumbo frames support so the network interface behaviour would be unified across various DW GMAC IP-core versions (and looking a bit ahead across modern DW QoS Ether/XGMAC/etc). So DW GMAC v3.70a and newer controllers will have implemented the next MTU semantics in the driver: 1. MTU <= 1500: truncate xmit frames greater than 2048, drop recv frames greater than 1518(1522). 2. MTU <= 1978: truncate xmit frames greater than 2048, drop recv frames greater than 1996(2000). 3. 1978 < MTU <= 9000: truncate xmit frames greater than 10240 and recv frames greater than ALIGN_UP(MTU+22, 1024), drop recv frames greater than 9018(9022). 4. 9000 < MTU <= 16383: truncate xmit and recv frames greater than 16K bytes, drop recv frames greater than 9018(9022). This shall not only extend the Jumbo-frames support, but also shall speed up the oversized frames handling since the truncated packets will likely to fit into a single Rx DMA-descriptor, which then will be just dropped as erroneous and re-initialized. In case of the DW GMAC older than v3.70a version the semantics will be as follows: 1. MTU <= 1500: truncate xmit and recv frames greater than 2048, drop recv frames greater than 1518(1522). 2. 1500 < MTU <= 9000: truncate xmit and recv frames greater than 10240, drop recv frames greater than 9018(9022). 3. 9000 < MTU <= 16383: truncate xmit and recv frames greater than 16K, drop recv frames greater than 9018(9022). It's almost the same as before except that the oversized transmitted frames will be truncated from now. Note the driver currently just drops the frames with over Giant frame size so the describes recv truncation seems unnecessary. But it will be utilized in the NETIF_F_RXALL feature implementation. [1] Documentation/networking/netdevices.rst Fixes: 2618abb73c89 ("stmmac: Fix kernel crashes for jumbo frames") Fixes: 84c9f8c41df9 ("net: stmmac: Add ip version to dts bindings") Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h13
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c11
3 files changed, 40 insertions, 8 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 1e095ba64127..a424f5884e43 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -69,6 +69,9 @@ enum power_event {
#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */
#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */
+/* GMAC Watchdog timeout for received frames register */
+#define GMAC_WDT 0x000000dc
+
/* SGMII/RGMII status register */
#define GMAC_RGSMIIIS_LNKMODE BIT(0)
#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1)
@@ -86,6 +89,7 @@ enum power_event {
/* GMAC Configuration defines */
#define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */
#define GMAC_CONTROL_CST 0x02000000 /* CRC Stripping for Type Frames */
+#define GMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on receive */
#define GMAC_CONTROL_JD 0x00400000 /* Jabber disable */
#define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */
#define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */
@@ -101,8 +105,7 @@ enum inter_frame_gap {
#define GMAC_CONTROL_DM 0x00000800 /* Duplex Mode */
#define GMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */
-#define GMAC_CORE_INIT (GMAC_CONTROL_JD | GMAC_CONTROL_PS | \
- GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
+#define GMAC_CORE_INIT (GMAC_CONTROL_PS | GMAC_CONTROL_BE | GMAC_CONTROL_DCRS)
/* GMAC Frame Filter defines */
#define GMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */
@@ -116,6 +119,9 @@ enum inter_frame_gap {
#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */
#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
+/* Watchdog timeout for received frames defines */
+#define GMAC_WDT_PWE BIT(16)
+#define GMAC_WDT_WTO GENMASK(13, 0)
/* DEBUG Register defines */
/* MTL TxStatus FIFO */
@@ -207,6 +213,9 @@ enum ttc_control {
#define DMA_CONTROL_EFC 0x00000100
#define DMA_CONTROL_FEF 0x00000080
#define DMA_CONTROL_FUF 0x00000040
+#define DMA_CONTROL_DGF 0x00000020
+#define DMA_CONTROL_PEF_MASK (DMA_CONTROL_DT | DMA_CONTROL_FEF | \
+ DMA_CONTROL_FUF | DMA_CONTROL_DGF)
/* Receive flow control activation field
* RFA field in DMA control register, bits 23,10:9
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index 1c7562fab394..312d22a52ea5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -35,6 +35,7 @@ static int dwmac1000_pcs_init(struct stmmac_priv *priv)
static void dwmac1000_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
+ struct stmmac_priv *priv = netdev_priv(dev);
void __iomem *ioaddr = hw->pcsr;
int mtu = dev->mtu;
u32 value;
@@ -42,13 +43,30 @@ static void dwmac1000_core_init(struct mac_device_info *hw,
/* Configure GMAC core */
value = readl(ioaddr + GMAC_CONTROL);
- if (mtu > 1500)
- value |= GMAC_CONTROL_2K;
- if (mtu > 2000)
+ /* Frame length limits (giant status(+VLAN):truncated by watchdog) */
+ if (mtu > 9000) /* Rx <= 9018(9022):16383 && Tx <= 16383 */
+ value |= GMAC_CONTROL_JE | GMAC_CONTROL_WD | GMAC_CONTROL_JD;
+ else if (mtu > 1978) /* Rx <= 9018(9022):10240 && Tx <= 10240 */
+ value |= GMAC_CONTROL_JE;
+ else if (priv->synopsys_id < DWMAC_CORE_3_70 && mtu > 1500)
value |= GMAC_CONTROL_JE;
+ else if (mtu > 1500) /* Rx <= 1996(2000):2048 && Tx <= 2048 */
+ value |= GMAC_CONTROL_2K;
+ /* else 64 <= Rx <= 1518(1522):2048 && Tx <= 2048 */
writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONTROL);
+ /* Over 2K, 3K, ..., 9K (10K and 16K-1) frames will be truncated on Rx */
+ if (mtu > 1500 && mtu <= 9000) {
+ value = ALIGN(mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, SZ_1K);
+ value = FIELD_PREP(GMAC_WDT_WTO, value) | GMAC_WDT_PWE;
+ } else {
+ value = 0;
+ }
+
+ /* Over giant frame watchdog fine-tuning (available since v3.70a) */
+ writel(value, ioaddr + GMAC_WDT);
+
/* Mask GMAC interrupts */
writel(GMAC_INT_DEFAULT_MASK, ioaddr + GMAC_INT_MASK);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index a3052e29c8a2..3a545442e903 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -148,10 +148,15 @@ static void dwmac1000_dma_operation_mode_rx(struct stmmac_priv *priv,
}
/* Permit errorneous frames (IP/TCP/UDP csum, overflow, giant, etc) */
- if (rxall)
+ csr6 &= ~DMA_CONTROL_PEF_MASK;
+ if (rxall) {
csr6 |= DMA_CONTROL_DT | DMA_CONTROL_FEF | DMA_CONTROL_FUF;
- else
- csr6 &= ~(DMA_CONTROL_DT | DMA_CONTROL_FEF | DMA_CONTROL_FUF);
+ } else {
+ /* Drop giant frames to unify the GMACs semantics (available
+ * since v3.70a if no giant frame status reported via Rx descs).
+ */
+ csr6 |= DMA_CONTROL_DGF;
+ }
/* Configure flow control based on rx fifo size */
csr6 = dwmac1000_configure_fc(csr6, fifosz);