Commit 3403960c authored by Mengyuan Lou's avatar Mengyuan Lou Committed by Jakub Kicinski
Browse files

net: wangxun: libwx add tx offload functions



Add tx offload functions for wx_xmit_frame_ring which
includes wx_encode_tx_desc_ptype, wx_tso and wx_tx_csum.
which supports ngbe and txgbe to implement tx offload
function.

Signed-off-by: default avatarMengyuan Lou <mengyuanlou@net-swift.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 6f4b9814
Loading
Loading
Loading
Loading
+457 −5
Original line number Diff line number Diff line
@@ -2,9 +2,14 @@
/* Copyright (c) 2019 - 2022 Beijing WangXun Technology Co., Ltd. */

#include <linux/etherdevice.h>
#include <net/ip6_checksum.h>
#include <net/page_pool.h>
#include <net/inet_ecn.h>
#include <linux/iopoll.h>
#include <linux/sctp.h>
#include <linux/pci.h>
#include <net/tcp.h>
#include <net/ip.h>

#include "wx_type.h"
#include "wx_lib.h"
@@ -707,11 +712,50 @@ static int wx_maybe_stop_tx(struct wx_ring *tx_ring, u16 size)
	return 0;
}

static u32 wx_tx_cmd_type(u32 tx_flags)
{
	/* set type for advanced descriptor with frame checksum insertion */
	u32 cmd_type = WX_TXD_DTYP_DATA | WX_TXD_IFCS;

	/* set HW vlan bit if vlan is present */
	cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_HW_VLAN, WX_TXD_VLE);
	/* set segmentation enable bits for TSO/FSO */
	cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_TSO, WX_TXD_TSE);
	/* set timestamp bit if present */
	cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_TSTAMP, WX_TXD_MAC_TSTAMP);
	cmd_type |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_LINKSEC, WX_TXD_LINKSEC);

	return cmd_type;
}

static void wx_tx_olinfo_status(union wx_tx_desc *tx_desc,
				u32 tx_flags, unsigned int paylen)
{
	u32 olinfo_status = paylen << WX_TXD_PAYLEN_SHIFT;

	/* enable L4 checksum for TSO and TX checksum offload */
	olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_CSUM, WX_TXD_L4CS);
	/* enable IPv4 checksum for TSO */
	olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_IPV4, WX_TXD_IIPCS);
	/* enable outer IPv4 checksum for TSO */
	olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_OUTER_IPV4,
				     WX_TXD_EIPCS);
	/* Check Context must be set if Tx switch is enabled, which it
	 * always is for case where virtual functions are running
	 */
	olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_CC, WX_TXD_CC);
	olinfo_status |= WX_SET_FLAG(tx_flags, WX_TX_FLAGS_IPSEC,
				     WX_TXD_IPSEC);
	tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
}

static void wx_tx_map(struct wx_ring *tx_ring,
		      struct wx_tx_buffer *first)
		      struct wx_tx_buffer *first,
		      const u8 hdr_len)
{
	struct sk_buff *skb = first->skb;
	struct wx_tx_buffer *tx_buffer;
	u32 tx_flags = first->tx_flags;
	u16 i = tx_ring->next_to_use;
	unsigned int data_len, size;
	union wx_tx_desc *tx_desc;
@@ -719,10 +763,9 @@ static void wx_tx_map(struct wx_ring *tx_ring,
	dma_addr_t dma;
	u32 cmd_type;

	cmd_type = WX_TXD_DTYP_DATA | WX_TXD_IFCS;
	cmd_type = wx_tx_cmd_type(tx_flags);
	tx_desc = WX_TX_DESC(tx_ring, i);

	tx_desc->read.olinfo_status = cpu_to_le32(skb->len << WX_TXD_PAYLEN_SHIFT);
	wx_tx_olinfo_status(tx_desc, tx_flags, skb->len - hdr_len);

	size = skb_headlen(skb);
	data_len = skb->data_len;
@@ -838,12 +881,399 @@ static void wx_tx_map(struct wx_ring *tx_ring,
	tx_ring->next_to_use = i;
}

static void wx_tx_ctxtdesc(struct wx_ring *tx_ring, u32 vlan_macip_lens,
			   u32 fcoe_sof_eof, u32 type_tucmd, u32 mss_l4len_idx)
{
	struct wx_tx_context_desc *context_desc;
	u16 i = tx_ring->next_to_use;

	context_desc = WX_TX_CTXTDESC(tx_ring, i);
	i++;
	tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;

	/* set bits to identify this as an advanced context descriptor */
	type_tucmd |= WX_TXD_DTYP_CTXT;
	context_desc->vlan_macip_lens   = cpu_to_le32(vlan_macip_lens);
	context_desc->seqnum_seed       = cpu_to_le32(fcoe_sof_eof);
	context_desc->type_tucmd_mlhl   = cpu_to_le32(type_tucmd);
	context_desc->mss_l4len_idx     = cpu_to_le32(mss_l4len_idx);
}

static void wx_get_ipv6_proto(struct sk_buff *skb, int offset, u8 *nexthdr)
{
	struct ipv6hdr *hdr = (struct ipv6hdr *)(skb->data + offset);

	*nexthdr = hdr->nexthdr;
	offset += sizeof(struct ipv6hdr);
	while (ipv6_ext_hdr(*nexthdr)) {
		struct ipv6_opt_hdr _hdr, *hp;

		if (*nexthdr == NEXTHDR_NONE)
			return;
		hp = skb_header_pointer(skb, offset, sizeof(_hdr), &_hdr);
		if (!hp)
			return;
		if (*nexthdr == NEXTHDR_FRAGMENT)
			break;
		*nexthdr = hp->nexthdr;
	}
}

union network_header {
	struct iphdr *ipv4;
	struct ipv6hdr *ipv6;
	void *raw;
};

static u8 wx_encode_tx_desc_ptype(const struct wx_tx_buffer *first)
{
	u8 tun_prot = 0, l4_prot = 0, ptype = 0;
	struct sk_buff *skb = first->skb;

	if (skb->encapsulation) {
		union network_header hdr;

		switch (first->protocol) {
		case htons(ETH_P_IP):
			tun_prot = ip_hdr(skb)->protocol;
			ptype = WX_PTYPE_TUN_IPV4;
			break;
		case htons(ETH_P_IPV6):
			wx_get_ipv6_proto(skb, skb_network_offset(skb), &tun_prot);
			ptype = WX_PTYPE_TUN_IPV6;
			break;
		default:
			return ptype;
		}

		if (tun_prot == IPPROTO_IPIP) {
			hdr.raw = (void *)inner_ip_hdr(skb);
			ptype |= WX_PTYPE_PKT_IPIP;
		} else if (tun_prot == IPPROTO_UDP) {
			hdr.raw = (void *)inner_ip_hdr(skb);
			if (skb->inner_protocol_type != ENCAP_TYPE_ETHER ||
			    skb->inner_protocol != htons(ETH_P_TEB)) {
				ptype |= WX_PTYPE_PKT_IG;
			} else {
				if (((struct ethhdr *)skb_inner_mac_header(skb))->h_proto
				     == htons(ETH_P_8021Q))
					ptype |= WX_PTYPE_PKT_IGMV;
				else
					ptype |= WX_PTYPE_PKT_IGM;
			}

		} else if (tun_prot == IPPROTO_GRE) {
			hdr.raw = (void *)inner_ip_hdr(skb);
			if (skb->inner_protocol ==  htons(ETH_P_IP) ||
			    skb->inner_protocol ==  htons(ETH_P_IPV6)) {
				ptype |= WX_PTYPE_PKT_IG;
			} else {
				if (((struct ethhdr *)skb_inner_mac_header(skb))->h_proto
				    == htons(ETH_P_8021Q))
					ptype |= WX_PTYPE_PKT_IGMV;
				else
					ptype |= WX_PTYPE_PKT_IGM;
			}
		} else {
			return ptype;
		}

		switch (hdr.ipv4->version) {
		case IPVERSION:
			l4_prot = hdr.ipv4->protocol;
			break;
		case 6:
			wx_get_ipv6_proto(skb, skb_inner_network_offset(skb), &l4_prot);
			ptype |= WX_PTYPE_PKT_IPV6;
			break;
		default:
			return ptype;
		}
	} else {
		switch (first->protocol) {
		case htons(ETH_P_IP):
			l4_prot = ip_hdr(skb)->protocol;
			ptype = WX_PTYPE_PKT_IP;
			break;
		case htons(ETH_P_IPV6):
			wx_get_ipv6_proto(skb, skb_network_offset(skb), &l4_prot);
			ptype = WX_PTYPE_PKT_IP | WX_PTYPE_PKT_IPV6;
			break;
		default:
			return WX_PTYPE_PKT_MAC | WX_PTYPE_TYP_MAC;
		}
	}
	switch (l4_prot) {
	case IPPROTO_TCP:
		ptype |= WX_PTYPE_TYP_TCP;
		break;
	case IPPROTO_UDP:
		ptype |= WX_PTYPE_TYP_UDP;
		break;
	case IPPROTO_SCTP:
		ptype |= WX_PTYPE_TYP_SCTP;
		break;
	default:
		ptype |= WX_PTYPE_TYP_IP;
		break;
	}

	return ptype;
}

static int wx_tso(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
		  u8 *hdr_len, u8 ptype)
{
	u32 vlan_macip_lens, type_tucmd, mss_l4len_idx;
	struct net_device *netdev = tx_ring->netdev;
	u32 l4len, tunhdr_eiplen_tunlen = 0;
	struct sk_buff *skb = first->skb;
	bool enc = skb->encapsulation;
	struct ipv6hdr *ipv6h;
	struct tcphdr *tcph;
	struct iphdr *iph;
	u8 tun_prot = 0;
	int err;

	if (skb->ip_summed != CHECKSUM_PARTIAL)
		return 0;

	if (!skb_is_gso(skb))
		return 0;

	err = skb_cow_head(skb, 0);
	if (err < 0)
		return err;

	/* indicates the inner headers in the skbuff are valid. */
	iph = enc ? inner_ip_hdr(skb) : ip_hdr(skb);
	if (iph->version == 4) {
		tcph = enc ? inner_tcp_hdr(skb) : tcp_hdr(skb);
		iph->tot_len = 0;
		iph->check = 0;
		tcph->check = ~csum_tcpudp_magic(iph->saddr,
						 iph->daddr, 0,
						 IPPROTO_TCP, 0);
		first->tx_flags |= WX_TX_FLAGS_TSO |
				   WX_TX_FLAGS_CSUM |
				   WX_TX_FLAGS_IPV4 |
				   WX_TX_FLAGS_CC;
	} else if (iph->version == 6 && skb_is_gso_v6(skb)) {
		ipv6h = enc ? inner_ipv6_hdr(skb) : ipv6_hdr(skb);
		tcph = enc ? inner_tcp_hdr(skb) : tcp_hdr(skb);
		ipv6h->payload_len = 0;
		tcph->check = ~csum_ipv6_magic(&ipv6h->saddr,
					       &ipv6h->daddr, 0,
					       IPPROTO_TCP, 0);
		first->tx_flags |= WX_TX_FLAGS_TSO |
				   WX_TX_FLAGS_CSUM |
				   WX_TX_FLAGS_CC;
	}

	/* compute header lengths */
	l4len = enc ? inner_tcp_hdrlen(skb) : tcp_hdrlen(skb);
	*hdr_len = enc ? (skb_inner_transport_header(skb) - skb->data) :
			 skb_transport_offset(skb);
	*hdr_len += l4len;

	/* update gso size and bytecount with header size */
	first->gso_segs = skb_shinfo(skb)->gso_segs;
	first->bytecount += (first->gso_segs - 1) * *hdr_len;

	/* mss_l4len_id: use 0 as index for TSO */
	mss_l4len_idx = l4len << WX_TXD_L4LEN_SHIFT;
	mss_l4len_idx |= skb_shinfo(skb)->gso_size << WX_TXD_MSS_SHIFT;

	/* vlan_macip_lens: HEADLEN, MACLEN, VLAN tag */
	if (enc) {
		switch (first->protocol) {
		case htons(ETH_P_IP):
			tun_prot = ip_hdr(skb)->protocol;
			first->tx_flags |= WX_TX_FLAGS_OUTER_IPV4;
			break;
		case htons(ETH_P_IPV6):
			tun_prot = ipv6_hdr(skb)->nexthdr;
			break;
		default:
			break;
		}
		switch (tun_prot) {
		case IPPROTO_UDP:
			tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_UDP;
			tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) <<
						 WX_TXD_OUTER_IPLEN_SHIFT) |
						(((skb_inner_mac_header(skb) -
						skb_transport_header(skb)) >> 1) <<
						WX_TXD_TUNNEL_LEN_SHIFT);
			break;
		case IPPROTO_GRE:
			tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_GRE;
			tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) <<
						 WX_TXD_OUTER_IPLEN_SHIFT) |
						(((skb_inner_mac_header(skb) -
						skb_transport_header(skb)) >> 1) <<
						WX_TXD_TUNNEL_LEN_SHIFT);
			break;
		case IPPROTO_IPIP:
			tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb) -
						(char *)ip_hdr(skb)) >> 2) <<
						WX_TXD_OUTER_IPLEN_SHIFT;
			break;
		default:
			break;
		}
		vlan_macip_lens = skb_inner_network_header_len(skb) >> 1;
	} else {
		vlan_macip_lens = skb_network_header_len(skb) >> 1;
	}

	vlan_macip_lens |= skb_network_offset(skb) << WX_TXD_MACLEN_SHIFT;
	vlan_macip_lens |= first->tx_flags & WX_TX_FLAGS_VLAN_MASK;

	type_tucmd = ptype << 24;
	if (skb->vlan_proto == htons(ETH_P_8021AD) &&
	    netdev->features & NETIF_F_HW_VLAN_STAG_TX)
		type_tucmd |= WX_SET_FLAG(first->tx_flags,
					  WX_TX_FLAGS_HW_VLAN,
					  0x1 << WX_TXD_TAG_TPID_SEL_SHIFT);
	wx_tx_ctxtdesc(tx_ring, vlan_macip_lens, tunhdr_eiplen_tunlen,
		       type_tucmd, mss_l4len_idx);

	return 1;
}

static void wx_tx_csum(struct wx_ring *tx_ring, struct wx_tx_buffer *first,
		       u8 ptype)
{
	u32 tunhdr_eiplen_tunlen = 0, vlan_macip_lens = 0;
	struct net_device *netdev = tx_ring->netdev;
	u32 mss_l4len_idx = 0, type_tucmd;
	struct sk_buff *skb = first->skb;
	u8 tun_prot = 0;

	if (skb->ip_summed != CHECKSUM_PARTIAL) {
		if (!(first->tx_flags & WX_TX_FLAGS_HW_VLAN) &&
		    !(first->tx_flags & WX_TX_FLAGS_CC))
			return;
		vlan_macip_lens = skb_network_offset(skb) <<
				  WX_TXD_MACLEN_SHIFT;
	} else {
		u8 l4_prot = 0;
		union {
			struct iphdr *ipv4;
			struct ipv6hdr *ipv6;
			u8 *raw;
		} network_hdr;
		union {
			struct tcphdr *tcphdr;
			u8 *raw;
		} transport_hdr;

		if (skb->encapsulation) {
			network_hdr.raw = skb_inner_network_header(skb);
			transport_hdr.raw = skb_inner_transport_header(skb);
			vlan_macip_lens = skb_network_offset(skb) <<
					  WX_TXD_MACLEN_SHIFT;
			switch (first->protocol) {
			case htons(ETH_P_IP):
				tun_prot = ip_hdr(skb)->protocol;
				break;
			case htons(ETH_P_IPV6):
				tun_prot = ipv6_hdr(skb)->nexthdr;
				break;
			default:
				return;
			}
			switch (tun_prot) {
			case IPPROTO_UDP:
				tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_UDP;
				tunhdr_eiplen_tunlen |=
					((skb_network_header_len(skb) >> 2) <<
					WX_TXD_OUTER_IPLEN_SHIFT) |
					(((skb_inner_mac_header(skb) -
					skb_transport_header(skb)) >> 1) <<
					WX_TXD_TUNNEL_LEN_SHIFT);
				break;
			case IPPROTO_GRE:
				tunhdr_eiplen_tunlen = WX_TXD_TUNNEL_GRE;
				tunhdr_eiplen_tunlen |= ((skb_network_header_len(skb) >> 2) <<
							 WX_TXD_OUTER_IPLEN_SHIFT) |
							 (((skb_inner_mac_header(skb) -
							    skb_transport_header(skb)) >> 1) <<
							  WX_TXD_TUNNEL_LEN_SHIFT);
				break;
			case IPPROTO_IPIP:
				tunhdr_eiplen_tunlen = (((char *)inner_ip_hdr(skb) -
							(char *)ip_hdr(skb)) >> 2) <<
							WX_TXD_OUTER_IPLEN_SHIFT;
				break;
			default:
				break;
			}

		} else {
			network_hdr.raw = skb_network_header(skb);
			transport_hdr.raw = skb_transport_header(skb);
			vlan_macip_lens = skb_network_offset(skb) <<
					  WX_TXD_MACLEN_SHIFT;
		}

		switch (network_hdr.ipv4->version) {
		case IPVERSION:
			vlan_macip_lens |= (transport_hdr.raw - network_hdr.raw) >> 1;
			l4_prot = network_hdr.ipv4->protocol;
			break;
		case 6:
			vlan_macip_lens |= (transport_hdr.raw - network_hdr.raw) >> 1;
			l4_prot = network_hdr.ipv6->nexthdr;
			break;
		default:
			break;
		}

		switch (l4_prot) {
		case IPPROTO_TCP:
		mss_l4len_idx = (transport_hdr.tcphdr->doff * 4) <<
				WX_TXD_L4LEN_SHIFT;
			break;
		case IPPROTO_SCTP:
			mss_l4len_idx = sizeof(struct sctphdr) <<
					WX_TXD_L4LEN_SHIFT;
			break;
		case IPPROTO_UDP:
			mss_l4len_idx = sizeof(struct udphdr) <<
					WX_TXD_L4LEN_SHIFT;
			break;
		default:
			break;
		}

		/* update TX checksum flag */
		first->tx_flags |= WX_TX_FLAGS_CSUM;
	}
	first->tx_flags |= WX_TX_FLAGS_CC;
	/* vlan_macip_lens: MACLEN, VLAN tag */
	vlan_macip_lens |= first->tx_flags & WX_TX_FLAGS_VLAN_MASK;

	type_tucmd = ptype << 24;
	if (skb->vlan_proto == htons(ETH_P_8021AD) &&
	    netdev->features & NETIF_F_HW_VLAN_STAG_TX)
		type_tucmd |= WX_SET_FLAG(first->tx_flags,
					  WX_TX_FLAGS_HW_VLAN,
					  0x1 << WX_TXD_TAG_TPID_SEL_SHIFT);
	wx_tx_ctxtdesc(tx_ring, vlan_macip_lens, tunhdr_eiplen_tunlen,
		       type_tucmd, mss_l4len_idx);
}

static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
				      struct wx_ring *tx_ring)
{
	u16 count = TXD_USE_COUNT(skb_headlen(skb));
	struct wx_tx_buffer *first;
	u8 hdr_len = 0, ptype;
	unsigned short f;
	u32 tx_flags = 0;
	int tso;

	/* need: 1 descriptor per page * PAGE_SIZE/WX_MAX_DATA_PER_TXD,
	 *       + 1 desc for skb_headlen/WX_MAX_DATA_PER_TXD,
@@ -864,7 +1294,29 @@ static netdev_tx_t wx_xmit_frame_ring(struct sk_buff *skb,
	first->bytecount = skb->len;
	first->gso_segs = 1;

	wx_tx_map(tx_ring, first);
	/* if we have a HW VLAN tag being added default to the HW one */
	if (skb_vlan_tag_present(skb)) {
		tx_flags |= skb_vlan_tag_get(skb) << WX_TX_FLAGS_VLAN_SHIFT;
		tx_flags |= WX_TX_FLAGS_HW_VLAN;
	}

	/* record initial flags and protocol */
	first->tx_flags = tx_flags;
	first->protocol = vlan_get_protocol(skb);

	ptype = wx_encode_tx_desc_ptype(first);

	tso = wx_tso(tx_ring, first, &hdr_len, ptype);
	if (tso < 0)
		goto out_drop;
	else if (!tso)
		wx_tx_csum(tx_ring, first, ptype);
	wx_tx_map(tx_ring, first, hdr_len);

	return NETDEV_TX_OK;
out_drop:
	dev_kfree_skb_any(first->skb);
	first->skb = NULL;

	return NETDEV_TX_OK;
}
+84 −3
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

#include <linux/bitfield.h>
#include <linux/netdevice.h>
#include <net/ip.h>

#define WX_NCSI_SUP                             0x8000
#define WX_NCSI_MASK                            0x8000
@@ -315,9 +316,6 @@
#define TXD_USE_COUNT(S)     DIV_ROUND_UP((S), WX_MAX_DATA_PER_TXD)
#define DESC_NEEDED          (MAX_SKB_FRAGS + 4)

/* Ether Types */
#define WX_ETH_P_CNM                 0x22E7

#define WX_CFG_PORT_ST               0x14404

/******************* Receive Descriptor bit definitions **********************/
@@ -326,6 +324,29 @@

#define WX_RXD_ERR_RXE               BIT(29) /* Any MAC Error */

/* TUN */
#define WX_PTYPE_TUN_IPV4            0x80
#define WX_PTYPE_TUN_IPV6            0xC0

/* PKT for TUN */
#define WX_PTYPE_PKT_IPIP            0x00 /* IP+IP */
#define WX_PTYPE_PKT_IG              0x10 /* IP+GRE */
#define WX_PTYPE_PKT_IGM             0x20 /* IP+GRE+MAC */
#define WX_PTYPE_PKT_IGMV            0x30 /* IP+GRE+MAC+VLAN */
/* PKT for !TUN */
#define WX_PTYPE_PKT_MAC             0x10
#define WX_PTYPE_PKT_IP              0x20

/* TYP for PKT=mac */
#define WX_PTYPE_TYP_MAC             0x01
/* TYP for PKT=ip */
#define WX_PTYPE_PKT_IPV6            0x08
#define WX_PTYPE_TYP_IPFRAG          0x01
#define WX_PTYPE_TYP_IP              0x02
#define WX_PTYPE_TYP_UDP             0x03
#define WX_PTYPE_TYP_TCP             0x04
#define WX_PTYPE_TYP_SCTP            0x05

/*********************** Transmit Descriptor Config Masks ****************/
#define WX_TXD_STAT_DD               BIT(0)  /* Descriptor Done */
#define WX_TXD_DTYP_DATA             0       /* Adv Data Descriptor */
@@ -334,6 +355,49 @@
#define WX_TXD_IFCS                  BIT(25) /* Insert FCS */
#define WX_TXD_RS                    BIT(27) /* Report Status */

/*********************** Adv Transmit Descriptor Config Masks ****************/
#define WX_TXD_MAC_TSTAMP            BIT(19) /* IEEE1588 time stamp */
#define WX_TXD_DTYP_CTXT             BIT(20) /* Adv Context Desc */
#define WX_TXD_LINKSEC               BIT(26) /* enable linksec */
#define WX_TXD_VLE                   BIT(30) /* VLAN pkt enable */
#define WX_TXD_TSE                   BIT(31) /* TCP Seg enable */
#define WX_TXD_CC                    BIT(7) /* Check Context */
#define WX_TXD_IPSEC                 BIT(8) /* enable ipsec esp */
#define WX_TXD_L4CS                  BIT(9)
#define WX_TXD_IIPCS                 BIT(10)
#define WX_TXD_EIPCS                 BIT(11)
#define WX_TXD_PAYLEN_SHIFT          13 /* Adv desc PAYLEN shift */
#define WX_TXD_MACLEN_SHIFT          9  /* Adv ctxt desc mac len shift */
#define WX_TXD_TAG_TPID_SEL_SHIFT    11

#define WX_TXD_L4LEN_SHIFT           8  /* Adv ctxt L4LEN shift */
#define WX_TXD_MSS_SHIFT             16  /* Adv ctxt MSS shift */

#define WX_TXD_OUTER_IPLEN_SHIFT     12 /* Adv ctxt OUTERIPLEN shift */
#define WX_TXD_TUNNEL_LEN_SHIFT      21 /* Adv ctxt TUNNELLEN shift */
#define WX_TXD_TUNNEL_TYPE_SHIFT     11 /* Adv Tx Desc Tunnel Type shift */
#define WX_TXD_TUNNEL_UDP            FIELD_PREP(BIT(WX_TXD_TUNNEL_TYPE_SHIFT), 0)
#define WX_TXD_TUNNEL_GRE            FIELD_PREP(BIT(WX_TXD_TUNNEL_TYPE_SHIFT), 1)

enum wx_tx_flags {
	/* cmd_type flags */
	WX_TX_FLAGS_HW_VLAN	= 0x01,
	WX_TX_FLAGS_TSO		= 0x02,
	WX_TX_FLAGS_TSTAMP	= 0x04,

	/* olinfo flags */
	WX_TX_FLAGS_CC		= 0x08,
	WX_TX_FLAGS_IPV4	= 0x10,
	WX_TX_FLAGS_CSUM	= 0x20,
	WX_TX_FLAGS_OUTER_IPV4	= 0x100,
	WX_TX_FLAGS_LINKSEC	= 0x200,
	WX_TX_FLAGS_IPSEC	= 0x400,
};

/* VLAN info */
#define WX_TX_FLAGS_VLAN_MASK			GENMASK(31, 16)
#define WX_TX_FLAGS_VLAN_SHIFT			16

/* Host Interface Command Structures */
struct wx_hic_hdr {
	u8 cmd;
@@ -508,10 +572,25 @@ union wx_rx_desc {
	} wb;  /* writeback */
};

struct wx_tx_context_desc {
	__le32 vlan_macip_lens;
	__le32 seqnum_seed;
	__le32 type_tucmd_mlhl;
	__le32 mss_l4len_idx;
};

/* if _flag is in _input, return _result */
#define WX_SET_FLAG(_input, _flag, _result) \
	(((_flag) <= (_result)) ? \
	 ((u32)((_input) & (_flag)) * ((_result) / (_flag))) : \
	 ((u32)((_input) & (_flag)) / ((_flag) / (_result))))

#define WX_RX_DESC(R, i)     \
	(&(((union wx_rx_desc *)((R)->desc))[i]))
#define WX_TX_DESC(R, i)     \
	(&(((union wx_tx_desc *)((R)->desc))[i]))
#define WX_TX_CTXTDESC(R, i) \
	(&(((struct wx_tx_context_desc *)((R)->desc))[i]))

/* wrapper around a pointer to a socket buffer,
 * so a DMA handle can be stored along with the buffer
@@ -523,6 +602,8 @@ struct wx_tx_buffer {
	unsigned short gso_segs;
	DEFINE_DMA_UNMAP_ADDR(dma);
	DEFINE_DMA_UNMAP_LEN(len);
	__be16 protocol;
	u32 tx_flags;
};

struct wx_rx_buffer {