qualcommax: qca_edma: add TSO support

A descriptor carries a segmentation enable and the preheader the MSS, so
the engine cuts an oversized frame into segments and writes the headers
of each. The conduit advertises NETIF_F_TSO and NETIF_F_TSO6, the stack
hands over one frame per window instead of one per segment, and the
checksums the engine already generates cover the segments it produced.

Assisted-by: Claude:claude-opus-5
Signed-off-by: Julius Bairaktaris <julius@bairaktaris.de>
Link: https://github.com/openwrt/openwrt/pull/25117
Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
Julius Bairaktaris authored and Robert Marko committed 2026-09-11 12:09:50 +02:00
1 parent ed30f802ed
commit e1ecaf5fe6
2 files changed
+23 -5

No files matched your search

@@ -673,6 +673,20 @@ static void edma_tx_csum(struct sk_buff *skb, struct edma_tx_preheader *txph,
txph->tx_pre6 |= EDMA_TX_PRE6_IP_CSUM_EN;
}
/* Segmentation is one bit in every descriptor of the frame and the segment
* size in the preheader. The engine writes the headers of each segment it
* cuts, so the checksums it is already asked for cover what it produced.
*/
static u32 edma_tx_tso(struct sk_buff *skb, struct edma_tx_preheader *txph)
{
if (!skb_is_gso(skb))
return 0;
txph->tx_pre6 |= skb_shinfo(skb)->gso_size & EDMA_TX_PRE6_MSS_MASK;
return EDMA_TXDESC_TSO_EN;
}
static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *netdev,
struct sk_buff *skb,
struct edma_ring *txdesc_ring)
@@ -686,7 +700,7 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net
u16 ndesc = shinfo->nr_frags + 1;
struct edma_txdesc *txdesc;
u16 prod, cons, dst_info;
u32 val, idx, i, len, bytes;
u32 val, idx, i, len, bytes, tso;
dma_addr_t head_dma;
bool taken = false;
__be16 proto;
@@ -739,6 +753,7 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net
txph->dst_info = dst_info;
edma_tx_csum(skb, txph, proto);
tso = edma_tx_tso(skb, txph);
txdesc_ring->skb_store[idx] = skb;
txph->opaque = idx;
@@ -752,7 +767,7 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net
txdesc = EDMA_TXDESC_DESC(txdesc_ring, idx);
txdesc->buffer_addr = cpu_to_le32(head_dma);
txdesc->word1 = (1 << EDMA_TXDESC_PREHEADER_SHIFT) |
txdesc->word1 = tso | (1 << EDMA_TXDESC_PREHEADER_SHIFT) |
(ndesc > 1 ? EDMA_TXDESC_MORE : 0) |
((EDMA_TX_PREHDR_SIZE & EDMA_TXDESC_DATA_OFFSET_MASK)
<< EDMA_TXDESC_DATA_OFFSET_SHIFT) |
@@ -771,8 +786,8 @@ static netdev_tx_t edma_ring_xmit(struct edma_priv *priv, struct net_device *net
txdesc_ring->skb_store[fidx] = skb;
txdesc = EDMA_TXDESC_DESC(txdesc_ring, fidx);
txdesc->buffer_addr = cpu_to_le32(dma);
txdesc->word1 = (i + 1 < shinfo->nr_frags ?
EDMA_TXDESC_MORE : 0) |
txdesc->word1 = tso | (i + 1 < shinfo->nr_frags ?
EDMA_TXDESC_MORE : 0) |
(len & EDMA_TXDESC_DATA_LENGTH_MASK);
}
@@ -1493,7 +1508,8 @@ static int edma_probe(struct platform_device *pdev)
netdev->dev.of_node = dev->of_node;
netdev->netdev_ops = &edma_netdev_ops;
netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM | NETIF_F_SG;
NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO |
NETIF_F_TSO6;
netdev->features = NETIF_F_GRO | netdev->hw_features;
/* A DSA user port takes its features from the conduit's vlan_features. */
netdev->vlan_features = netdev->hw_features;
@@ -186,6 +186,7 @@
/* TX descriptor fields */
#define EDMA_TXDESC_MORE BIT(30)
#define EDMA_TXDESC_TSO_EN BIT(28)
#define EDMA_TXDESC_PREHEADER_SHIFT 29
#define EDMA_TXDESC_PREHEADER BIT(EDMA_TXDESC_PREHEADER_SHIFT)
#define EDMA_TXDESC_DATA_OFFSET_SHIFT 16
@@ -195,6 +196,7 @@
/* TX preheader fields */
#define EDMA_TX_PRE4_ADV_OFFLOAD_EN BIT(28)
#define EDMA_TX_PRE6_CSUM_MODE_L4 (0x1 << 29)
#define EDMA_TX_PRE6_MSS_MASK 0x3fff
#define EDMA_TX_PRE6_IP_CSUM_EN BIT(31)
/* Preheader fields */