diff options
| author | Serge Semin <fancer.lancer@gmail.com> | 2025-08-19 20:57:53 +0300 |
|---|---|---|
| committer | Serge Semin <fancer.lancer@gmail.com> | 2026-08-13 20:31:13 +0300 |
| commit | b0e9393ef476879c58f476f1270978c77714ebf3 (patch) | |
| tree | b7dbfdac36362d44c7bc20229bb041132a24600f | |
| parent | 6573ddf62d8959787bd7323897decb84e503f9a4 (diff) | |
| download | linux-b0e9393ef476879c58f476f1270978c77714ebf3.tar.gz linux-b0e9393ef476879c58f476f1270978c77714ebf3.zip | |
net: stmmac: Add HW-accelerated FCS stripping supportgmac/fix7/eng
Currently the FCS field stripping is done purely by software. That is the
driver just reduces the frame length by the FCS size if NETIF_F_RXFCS
feature isn't enabled. In the meantime the Pad/FCS stripping can be done
by the hardware itself thus speeding up the incoming traffic processing a
bit.
The Auto Pad/FCS stripping can be enabled by means of the ACS and CST
flags in the MAC-control register of all the DW MAC IP-cores. The former
one enables the Pad/FCS stripping from the Ethernet 802.3 frames
(Length/Type field is less than 1536). The later flag switches on just the
CRC stripping for the Ethernet II type frames (Length/Type field is
greater or equal to 1536).
Ideally both flags could have been enabled to implement the denoted
feature and be done with it. But the problems are that first the IP-cores
prior DW GMAC v3.50a version don't support CST flag and second the ACS
flag causes all pads and FCS truncation on transferring frames from RPE to
RFC module up to the _length specified in the Ethertype-field_. If first
problem can be quite easy handled in the driver, the second one can't be
because it corrupts the DSA frames which have tags placed at the
Ethertype-field and looking like a very short Ethernet 802.3 frame for the
RPE module of DW MACs. That's why all of the previous commits trying to
fix the problem couldn't do that properly. The best solution would be to
just switch on CST and never enable the ACS flag on.
As a final and the most appropriate solution let's add the HW-accelerated
FCS stripping support for the Ethernet II frames only. It will be enabled
unless NETIF_F_RXFCS feature is request for DW GMAC v3.50a and higher, DW
QoS Ether and DW XGMAC/XLGMAC IP-cores.
Fixes: 929d43421ee5 ("net: stmmac: Disable automatic FCS/Pad stripping")
Fixes: 8cad443eacf6 ("net: stmmac: Fix reception of Broadcom switches tags")
Fixes: 3eeb29972b11 ("stmmac: fix automatic PAD/FCS stripping")
Signed-off-by: Serge Semin <fancer.lancer@gmail.com>
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 27 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 1 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 30 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c | 12 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h | 7 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c | 28 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/enh_desc.c | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/hwif.h | 7 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 3 | ||||
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 16 |
12 files changed, 138 insertions, 0 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 2a51768cf64f..1e095ba64127 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -85,6 +85,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_JD 0x00400000 /* Jabber disable */ #define GMAC_CONTROL_BE 0x00200000 /* Frame Burst Enable */ #define GMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index 83ec2c00b49a..1c7562fab394 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -72,6 +72,31 @@ static void dwmac1000_irq_modify(struct mac_device_info *hw, u32 disable, spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags); } +static void dwmac1000_rx_fcs_enable(struct mac_device_info *hw, bool enable) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + value = readl(ioaddr + GMAC_CONTROL); + + if (enable) + value &= ~GMAC_CONTROL_CST; + else + value |= GMAC_CONTROL_CST; + + writel(value, ioaddr + GMAC_CONTROL); +} + +static int dwmac1000_rx_fcs_status(struct mac_device_info *hw, int snps_id, + int status) +{ + /* CRC Stripping for Type Frames has been supported since v3.50a */ + if (snps_id < DWMAC_CORE_3_50 || unlikely(status & llc_snap)) + return -ENOTSUPP; + + return 0; +} + static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw, bool enable) { void __iomem *ioaddr = hw->pcsr; @@ -454,6 +479,8 @@ const struct stmmac_ops dwmac1000_ops = { .core_init = dwmac1000_core_init, .irq_modify = dwmac1000_irq_modify, .set_mac = stmmac_set_mac, + .rx_fcs = dwmac1000_rx_fcs_enable, + .rx_fcs_status = dwmac1000_rx_fcs_status, .rx_ipc = dwmac1000_rx_ipc_enable, .dump_regs = dwmac1000_dump_regs, .host_irq_status = dwmac1000_irq_status, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index 5c611dbc5abc..e58194fd0c0b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -156,6 +156,7 @@ enum power_event { #define GMAC_CONFIG_IPC BIT(27) #define GMAC_CONFIG_IPG GENMASK(26, 24) #define GMAC_CONFIG_2K BIT(22) +#define GMAC_CONFIG_CST BIT(21) #define GMAC_CONFIG_ACS BIT(20) #define GMAC_CONFIG_BE BIT(18) #define GMAC_CONFIG_JD BIT(17) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 5337376452a7..9f53a79aa70c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -318,6 +318,30 @@ static void dwmac4_dump_regs(struct mac_device_info *hw, u32 *reg_space) reg_space[i] = readl(ioaddr + i * 4); } +static void dwmac4_rx_fcs_enable(struct mac_device_info *hw, bool enable) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + value = readl(ioaddr + GMAC_CONFIG); + + if (enable) + value &= ~GMAC_CONFIG_CST; + else + value |= GMAC_CONFIG_CST; + + writel(value, ioaddr + GMAC_CONFIG); +} + +static int dwmac4_rx_fcs_status(struct mac_device_info *hw, int snps_id, + int status) +{ + if (unlikely(status & llc_snap)) + return -ENOTSUPP; + + return 0; +} + static int dwmac4_rx_ipc_enable(struct mac_device_info *hw, bool enable) { void __iomem *ioaddr = hw->pcsr; @@ -894,6 +918,8 @@ const struct stmmac_ops dwmac4_ops = { .irq_modify = dwmac4_irq_modify, .update_caps = dwmac4_update_caps, .set_mac = stmmac_set_mac, + .rx_fcs = dwmac4_rx_fcs_enable, + .rx_fcs_status = dwmac4_rx_fcs_status, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, .rx_queue_prio = dwmac4_rx_queue_priority, @@ -930,6 +956,8 @@ const struct stmmac_ops dwmac410_ops = { .irq_modify = dwmac4_irq_modify, .update_caps = dwmac4_update_caps, .set_mac = stmmac_dwmac4_set_mac, + .rx_fcs = dwmac4_rx_fcs_enable, + .rx_fcs_status = dwmac4_rx_fcs_status, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, .rx_queue_prio = dwmac4_rx_queue_priority, @@ -968,6 +996,8 @@ const struct stmmac_ops dwmac510_ops = { .irq_modify = dwmac4_irq_modify, .update_caps = dwmac4_update_caps, .set_mac = stmmac_dwmac4_set_mac, + .rx_fcs = dwmac4_rx_fcs_enable, + .rx_fcs_status = dwmac4_rx_fcs_status, .rx_ipc = dwmac4_rx_ipc_enable, .rx_queue_enable = dwmac4_rx_queue_enable, .rx_queue_prio = dwmac4_rx_queue_priority, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c index 1b72fa907610..f51da6c80717 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c @@ -111,6 +111,18 @@ static int dwmac4_wrback_get_rx_status(struct stmmac_extra_stats *x, x->dribbling_bit++; ret |= proto_err; } + } else { + int l2t = FIELD_GET(RDES3_PACKET_LEN_TYPE_MASK, rdes3); + + switch (l2t) { + case RDES3_PACKET_LENGTH: + ret |= llc_snap; + break; + case RDES3_PACKET_VLAN: + case RDES3_PACKET_DVLAN: + x->rx_vlan++; + break; + } } message_type = FIELD_GET(RDES1_PTP_MSG_TYPE_MASK, rdes1); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h index fb1fea5b0e6e..c089e2fdd901 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.h @@ -114,6 +114,13 @@ #define RDES3_PACKET_SIZE_MASK GENMASK(14, 0) #define RDES3_ERROR_SUMMARY BIT(15) #define RDES3_PACKET_LEN_TYPE_MASK GENMASK(18, 16) +#define RDES3_PACKET_LENGTH 0 +#define RDES3_PACKET_TYPE 1 +#define RDES3_PACKET_ARP 3 +#define RDES3_PACKET_VLAN 4 +#define RDES3_PACKET_DVLAN 5 +#define RDES3_PACKET_MAC_CONTROL 6 +#define RDES3_PACKET_OAM_PACKET 7 #define RDES3_DRIBBLE_ERROR BIT(19) #define RDES3_RECEIVE_ERROR BIT(20) #define RDES3_OVERFLOW_ERROR BIT(21) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c index 8be75536b284..13093b7defc5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c @@ -65,6 +65,30 @@ static void dwxgmac2_set_mac(void __iomem *ioaddr, bool enable) writel(rx, ioaddr + XGMAC_RX_CONFIG); } +static void dwxgmac2_rx_fcs(struct mac_device_info *hw, bool enable) +{ + void __iomem *ioaddr = hw->pcsr; + u32 value; + + value = readl(ioaddr + XGMAC_RX_CONFIG); + + if (enable) + value &= ~XGMAC_CONFIG_CST; + else + value |= XGMAC_CONFIG_CST; + + writel(value, ioaddr + XGMAC_RX_CONFIG); +} + +static int dwxgmac2_rx_fcs_status(struct mac_device_info *hw, int snps_id, + int status) +{ + if (unlikely(status & llc_snap)) + return -ENOTSUPP; + + return 0; +} + static int dwxgmac2_rx_ipc(struct mac_device_info *hw, bool enable) { void __iomem *ioaddr = hw->pcsr; @@ -1533,6 +1557,8 @@ const struct stmmac_ops dwxgmac210_ops = { .irq_modify = dwxgmac2_irq_modify, .update_caps = dwxgmac2_update_caps, .set_mac = dwxgmac2_set_mac, + .rx_fcs = dwxgmac2_rx_fcs, + .rx_fcs_status = dwxgmac2_rx_fcs_status, .rx_ipc = dwxgmac2_rx_ipc, .rx_queue_enable = dwxgmac2_rx_queue_enable, .rx_queue_prio = dwxgmac2_rx_queue_prio, @@ -1588,6 +1614,8 @@ const struct stmmac_ops dwxlgmac2_ops = { .core_init = dwxgmac2_core_init, .irq_modify = dwxgmac2_irq_modify, .set_mac = dwxgmac2_set_mac, + .rx_fcs = dwxgmac2_rx_fcs, + .rx_fcs_status = dwxgmac2_rx_fcs_status, .rx_ipc = dwxgmac2_rx_ipc, .rx_queue_enable = dwxlgmac2_rx_queue_enable, .rx_queue_prio = dwxgmac2_rx_queue_prio, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c index e6f48dd7a5b1..deb0a787cb04 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c @@ -42,6 +42,9 @@ static int dwxgmac2_get_rx_status(struct stmmac_extra_stats *x, int l2t = FIELD_GET(XGMAC_RDES3_ET_LT, rdes3); switch (l2t) { + case XGMAC_L2T_LENGTH_PACKET: + ret |= llc_snap; + break; case XGMAC_L2T_AV_CONTROL: x->av_pkt_rcvd++; break; diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c index 34cfe2fd26d2..5b4bae8c5e89 100644 --- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c @@ -176,6 +176,9 @@ static int enh_desc_get_rx_basic_status(unsigned int rdes0, return discard_frame; } + if (unlikely(!(rdes0 & RDES0_FRAME_TYPE))) + ret = llc_snap; + if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { x->rx_desc++; diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index e0495ee19a62..b1654e86179a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -322,6 +322,9 @@ struct stmmac_ops { void (*irq_modify)(struct mac_device_info *hw, u32 disable, u32 enable); /* Enable the MAC RX/TX */ void (*set_mac)(void __iomem *ioaddr, bool enable); + /* Enable and verify the Auto Pad/FCS stripping */ + void (*rx_fcs)(struct mac_device_info *hw, bool enable); + int (*rx_fcs_status)(struct mac_device_info *hw, int snps_id, int status); /* Enable and verify that the IPC module is supported */ int (*rx_ipc)(struct mac_device_info *hw, bool enable); /* Enable RX Queues */ @@ -426,6 +429,10 @@ struct stmmac_ops { stmmac_do_void_callback(__priv, mac, irq_modify, (__priv)->hw, __args) #define stmmac_mac_set(__priv, __args...) \ stmmac_do_void_callback(__priv, mac, set_mac, __args) +#define stmmac_rx_fcs(__priv, __args...) \ + stmmac_do_void_callback(__priv, mac, rx_fcs, __args) +#define stmmac_rx_fcs_status(__priv, __args...) \ + stmmac_do_callback(__priv, mac, rx_fcs_status, __args) #define stmmac_rx_ipc(__priv, __args...) \ stmmac_do_callback(__priv, mac, rx_ipc, __args) #define stmmac_rx_queue_enable(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index 4e0b552aba3b..a26b58f9b573 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -77,6 +77,9 @@ static int ndesc_get_rx_basic_status(unsigned int rdes0, return discard_frame; } + if (unlikely(!(rdes0 & RDES0_FRAME_TYPE))) + ret = llc_snap; + if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) { x->rx_desc++; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index f68ae9926689..b4ee92afe8c8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3718,6 +3718,9 @@ static int stmmac_hw_setup(struct net_device *dev) /* Initialize Safety Features */ stmmac_safety_feat_configuration(priv); + /* Initialize Auto Pad/FCS Stripping */ + stmmac_rx_fcs(priv, priv->hw, dev->features & NETIF_F_RXFCS); + /* Initialize Rx Checksum Offload Engine */ stmmac_rx_ipc(priv, priv->hw, dev->features & NETIF_F_RXCSUM); @@ -5187,9 +5190,19 @@ static unsigned int stmmac_fcs_trimming(struct stmmac_priv *priv, int status) { bool rxfcs = priv->dev->features & NETIF_F_RXFCS; + /* By default Pad/FCS is left attached to the frame */ if (rxfcs) return 0; + /* Auto Pad/FCS stripping is unavailable on the old controller and it + * is deliberately disabled for Ethernet II frames to prevent the MAC + * confusing some DSA frames with IEEE 802.3 frames and truncating them + * up to the Ethertype-length (see "Receive Protocol Engine Module" + * chapter for details). + */ + if (!stmmac_rx_fcs_status(priv, priv->hw, priv->synopsys_id, status)) + return 0; + return ETH_FCS_LEN; } @@ -6281,6 +6294,8 @@ static int stmmac_set_features(struct net_device *netdev, struct stmmac_priv *priv = netdev_priv(netdev); u32 chan; + stmmac_rx_fcs(priv, priv->hw, features & NETIF_F_RXFCS); + stmmac_rx_ipc(priv, priv->hw, features & NETIF_F_RXCSUM); for (chan = 0; chan < priv->plat->rx_queues_to_use; chan++) { @@ -8431,6 +8446,7 @@ static int __stmmac_dvr_probe(struct device *device, dwmac_is_xmac(priv->plat->core_type)) ndev->hw_features |= NETIF_F_RXALL; + /* Either hardware or software based FCS stripping supported */ ndev->hw_features |= NETIF_F_RXFCS; ndev->watchdog_timeo = msecs_to_jiffies(watchdog); |
