Commit 9202d7b6 authored by Maya Erez's avatar Maya Erez Committed by Kalle Valo
Browse files

wil6210: add support for enhanced DMA TX data flows



The enhanced DMA TX data path is handled using a descriptor
ring per connection and a single status ring.

The driver gets TX completions via the TX status ring. Each
status message points to the completed descriptor ring and
includes the number of completed descriptors in this ring.

Non TSO enhanced DMA TX descriptors are similar to legacy DMA
TX descriptors, hence the same transmit function can be used.

However, enhanced DMA TSO frames division is performed by the
HW, hence a new function is added to handle enhanced DMA TSO.

Signed-off-by: default avatarGidon Studinski <gidons@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 96c93589
Loading
Loading
Loading
Loading
+105 −21
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
				    (~(BIT_DMA_EP_RX_ICR_RX_HTRSH)))
#define WIL6210_IMC_TX		(BIT_DMA_EP_TX_ICR_TX_DONE | \
				BIT_DMA_EP_TX_ICR_TX_DONE_N(0))
#define WIL6210_IMC_TX_EDMA		BIT_TX_STATUS_IRQ
#define WIL6210_IMC_MISC_NO_HALP	(ISR_MISC_FW_READY | \
					 ISR_MISC_MBOX_EVT | \
					 ISR_MISC_FW_ERROR)
@@ -87,6 +88,12 @@ static void wil6210_mask_irq_tx(struct wil6210_priv *wil)
	      WIL6210_IRQ_DISABLE);
}

static void wil6210_mask_irq_tx_edma(struct wil6210_priv *wil)
{
	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMS),
	      WIL6210_IRQ_DISABLE);
}

static void wil6210_mask_irq_rx(struct wil6210_priv *wil)
{
	wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMS),
@@ -125,6 +132,12 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil)
	      WIL6210_IMC_TX);
}

void wil6210_unmask_irq_tx_edma(struct wil6210_priv *wil)
{
	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, IMC),
	      WIL6210_IMC_TX_EDMA);
}

void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
	bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0;
@@ -164,6 +177,7 @@ void wil_mask_irq(struct wil6210_priv *wil)
	wil_dbg_irq(wil, "mask_irq\n");

	wil6210_mask_irq_tx(wil);
	wil6210_mask_irq_tx_edma(wil);
	wil6210_mask_irq_rx(wil);
	wil6210_mask_irq_misc(wil, true);
	wil6210_mask_irq_pseudo(wil);
@@ -179,10 +193,16 @@ void wil_unmask_irq(struct wil6210_priv *wil)
	      WIL_ICR_ICC_VALUE);
	wil_w(wil, RGF_DMA_EP_MISC_ICR + offsetof(struct RGF_ICR, ICC),
	      WIL_ICR_ICC_MISC_VALUE);
	wil_w(wil, RGF_INT_GEN_TX_ICR + offsetof(struct RGF_ICR, ICC),
	      WIL_ICR_ICC_VALUE);

	wil6210_unmask_irq_pseudo(wil);
	if (wil->use_enhanced_dma_hw) {
		wil6210_unmask_irq_tx_edma(wil);
	} else {
		wil6210_unmask_irq_tx(wil);
		wil6210_unmask_irq_rx(wil);
	}
	wil6210_unmask_irq_misc(wil, true);
}

@@ -315,6 +335,49 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
	return IRQ_HANDLED;
}

static irqreturn_t wil6210_irq_tx_edma(int irq, void *cookie)
{
	struct wil6210_priv *wil = cookie;
	u32 isr = wil_ioread32_and_clear(wil->csr +
					 HOSTADDR(RGF_INT_GEN_TX_ICR) +
					 offsetof(struct RGF_ICR, ICR));
	bool need_unmask = true;

	trace_wil6210_irq_tx(isr);
	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);

	if (unlikely(!isr)) {
		wil_err(wil, "spurious IRQ: TX\n");
		return IRQ_NONE;
	}

	wil6210_mask_irq_tx_edma(wil);

	if (likely(isr & BIT_TX_STATUS_IRQ)) {
		wil_dbg_irq(wil, "TX status ring\n");
		isr &= ~BIT_TX_STATUS_IRQ;
		if (likely(test_bit(wil_status_fwready, wil->status))) {
			wil_dbg_txrx(wil, "NAPI(Tx) schedule\n");
			need_unmask = false;
			napi_schedule(&wil->napi_tx);
		} else {
			wil_err(wil, "Got Tx status ring IRQ while in reset\n");
		}
	}

	if (unlikely(isr))
		wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);

	/* Tx IRQ will be enabled when NAPI processing finished */

	atomic_inc(&wil->isr_count_tx);

	if (unlikely(need_unmask))
		wil6210_unmask_irq_tx_edma(wil);

	return IRQ_HANDLED;
}

static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
{
	struct wil6210_priv *wil = cookie;
@@ -531,30 +594,45 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie)
 */
static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause)
{
	u32 icm_rx = 0, icr_rx = 0, imv_rx = 0;
	u32 icm_tx, icr_tx, imv_tx;
	u32 icm_misc, icr_misc, imv_misc;

	if (!test_bit(wil_status_irqen, wil->status)) {
		u32 icm_rx = wil_ioread32_and_clear(wil->csr +
		if (wil->use_enhanced_dma_hw) {
			icm_tx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_INT_GEN_TX_ICR) +
					offsetof(struct RGF_ICR, ICM));
			icr_tx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_INT_GEN_TX_ICR) +
					offsetof(struct RGF_ICR, ICR));
			imv_tx = wil_r(wil, RGF_INT_GEN_TX_ICR +
					   offsetof(struct RGF_ICR, IMV));
		} else {
			icm_rx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_DMA_EP_RX_ICR) +
					offsetof(struct RGF_ICR, ICM));
		u32 icr_rx = wil_ioread32_and_clear(wil->csr +
			icr_rx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_DMA_EP_RX_ICR) +
					offsetof(struct RGF_ICR, ICR));
		u32 imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR +
			imv_rx = wil_r(wil, RGF_DMA_EP_RX_ICR +
				   offsetof(struct RGF_ICR, IMV));
		u32 icm_tx = wil_ioread32_and_clear(wil->csr +
			icm_tx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_DMA_EP_TX_ICR) +
					offsetof(struct RGF_ICR, ICM));
		u32 icr_tx = wil_ioread32_and_clear(wil->csr +
			icr_tx = wil_ioread32_and_clear(wil->csr +
					HOSTADDR(RGF_DMA_EP_TX_ICR) +
					offsetof(struct RGF_ICR, ICR));
		u32 imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR +
			imv_tx = wil_r(wil, RGF_DMA_EP_TX_ICR +
					   offsetof(struct RGF_ICR, IMV));
		u32 icm_misc = wil_ioread32_and_clear(wil->csr +
		}
		icm_misc = wil_ioread32_and_clear(wil->csr +
				HOSTADDR(RGF_DMA_EP_MISC_ICR) +
				offsetof(struct RGF_ICR, ICM));
		u32 icr_misc = wil_ioread32_and_clear(wil->csr +
		icr_misc = wil_ioread32_and_clear(wil->csr +
				HOSTADDR(RGF_DMA_EP_MISC_ICR) +
				offsetof(struct RGF_ICR, ICR));
		u32 imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR +
		imv_misc = wil_r(wil, RGF_DMA_EP_MISC_ICR +
				     offsetof(struct RGF_ICR, IMV));

		/* HALP interrupt can be unmasked when misc interrupts are
@@ -617,7 +695,7 @@ static irqreturn_t wil6210_hardirq(int irq, void *cookie)
		rc = IRQ_WAKE_THREAD;

	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) &&
	    (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD))
	    (wil->txrx_ops.irq_tx(irq, cookie) == IRQ_WAKE_THREAD))
		rc = IRQ_WAKE_THREAD;

	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) &&
@@ -645,6 +723,8 @@ void wil6210_clear_irq(struct wil6210_priv *wil)
		    offsetof(struct RGF_ICR, ICR));
	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) +
		    offsetof(struct RGF_ICR, ICR));
	wil_clear32(wil->csr + HOSTADDR(RGF_INT_GEN_TX_ICR) +
		    offsetof(struct RGF_ICR, ICR));
	wil_clear32(wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
		    offsetof(struct RGF_ICR, ICR));
	wmb(); /* make sure write completed */
@@ -673,6 +753,10 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq, bool use_msi)

	wil_dbg_misc(wil, "init_irq: %s\n", use_msi ? "MSI" : "INTx");

	if (wil->use_enhanced_dma_hw)
		wil->txrx_ops.irq_tx = wil6210_irq_tx_edma;
	else
		wil->txrx_ops.irq_tx = wil6210_irq_tx;
	rc = request_threaded_irq(irq, wil6210_hardirq,
				  wil6210_thread_irq,
				  use_msi ? 0 : IRQF_SHARED,
+33 −3
Original line number Diff line number Diff line
@@ -157,6 +157,30 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
	return min(tx_done, budget);
}

static int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget)
{
	struct wil6210_priv *wil = container_of(napi, struct wil6210_priv,
						napi_tx);
	int tx_done;
	/* There is only one status TX ring */
	struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx];

	if (!sring->va)
		return 0;

	tx_done = wil_tx_sring_handler(wil, sring);

	if (tx_done < budget) {
		napi_complete(napi);
		wil6210_unmask_irq_tx_edma(wil);
		wil_dbg_txrx(wil, "NAPI TX complete\n");
	}

	wil_dbg_txrx(wil, "NAPI TX poll(%d) done %d\n", budget, tx_done);

	return min(tx_done, budget);
}

static void wil_dev_setup(struct net_device *dev)
{
	ether_setup(dev);
@@ -420,10 +444,16 @@ int wil_if_add(struct wil6210_priv *wil)
	init_dummy_netdev(&wil->napi_ndev);
	netif_napi_add(&wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
		       WIL6210_NAPI_BUDGET);
	if (wil->use_enhanced_dma_hw)
		netif_tx_napi_add(&wil->napi_ndev,
				  &wil->napi_tx, wil6210_netdev_poll_tx_edma,
				  WIL6210_NAPI_BUDGET);
	else
		netif_tx_napi_add(&wil->napi_ndev,
				  &wil->napi_tx, wil6210_netdev_poll_tx,
				  WIL6210_NAPI_BUDGET);


	wil_update_net_queues_bh(wil, vif, NULL, true);

	rtnl_lock();
+25 −0
Original line number Diff line number Diff line
@@ -226,6 +226,31 @@ TRACE_EVENT(wil6210_tx_done,
		  __entry->err)
);

TRACE_EVENT(wil6210_tx_status,
	    TP_PROTO(struct wil_ring_tx_status *msg, u16 index,
		     unsigned int len),
	    TP_ARGS(msg, index, len),
	    TP_STRUCT__entry(__field(u16, index)
			     __field(unsigned int, len)
			     __field(u8, num_descs)
			     __field(u8, ring_id)
			     __field(u8, status)
			     __field(u8, mcs)

	    ),
	    TP_fast_assign(__entry->index = index;
			   __entry->len = len;
			   __entry->num_descs = msg->num_descriptors;
			   __entry->ring_id = msg->ring_id;
			   __entry->status = msg->status;
			   __entry->mcs = wil_tx_status_get_mcs(msg);
	    ),
	    TP_printk(
		      "ring_id %d swtail 0x%x len %d num_descs %d status 0x%x mcs %d",
		      __entry->ring_id, __entry->index, __entry->len,
		      __entry->num_descs, __entry->status, __entry->mcs)
);

#endif /* WIL6210_TRACE_H || TRACE_HEADER_MULTI_READ*/

#if defined(CONFIG_WIL6210_TRACING) && !defined(__CHECKER__)
+93 −101
Original line number Diff line number Diff line
@@ -117,12 +117,6 @@ bool wil_is_tx_idle(struct wil6210_priv *wil)
	return true;
}

/* wil_val_in_range - check if value in [min,max) */
static inline bool wil_val_in_range(int val, int min, int max)
{
	return val >= min && val < max;
}

static int wil_vring_alloc(struct wil6210_priv *wil, struct wil_ring *vring)
{
	struct device *dev = wil_to_dev(wil);
@@ -184,9 +178,10 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct wil_ring *vring)
	return 0;
}

static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d,
static void wil_txdesc_unmap(struct device *dev, union wil_tx_desc *desc,
			     struct wil_ctx *ctx)
{
	struct vring_tx_desc *d = &desc->legacy;
	dma_addr_t pa = wil_desc_addr(&d->dma.addr);
	u16 dmalen = le16_to_cpu(d->dma.length);

@@ -239,7 +234,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring)
				continue;
			}
			*d = *_d;
			wil_txdesc_unmap(dev, d, ctx);
			wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx);
			if (ctx->skb)
				dev_kfree_skb_any(ctx->skb);
			vring->swtail = wil_ring_next_tail(vring);
@@ -887,6 +882,30 @@ static void wil_rx_fini(struct wil6210_priv *wil)
		wil_vring_free(wil, vring);
}

static int wil_tx_desc_map(union wil_tx_desc *desc, dma_addr_t pa,
			   u32 len, int vring_index)
{
	struct vring_tx_desc *d = &desc->legacy;

	wil_desc_addr_set(&d->dma.addr, pa);
	d->dma.ip_length = 0;
	/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
	d->dma.b11 = 0/*14 | BIT(7)*/;
	d->dma.error = 0;
	d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
	d->dma.length = cpu_to_le16((u16)len);
	d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
	d->mac.d[0] = 0;
	d->mac.d[1] = 0;
	d->mac.d[2] = 0;
	d->mac.ucode_cmd = 0;
	/* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */
	d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) |
		      (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS);

	return 0;
}

void wil_tx_data_init(struct wil_ring_tx_data *txdata)
{
	spin_lock_bh(&txdata->lock);
@@ -1114,8 +1133,8 @@ static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil,
	return NULL;
}

static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
			struct wil_ring *vring, struct sk_buff *skb);
static int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif,
		       struct wil_ring *ring, struct sk_buff *skb);

static struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil,
					     struct wil6210_vif *vif,
@@ -1258,7 +1277,7 @@ found:
		if (skb2) {
			wil_dbg_txrx(wil, "BCAST DUP -> ring %d\n", i);
			wil_set_da_for_vring(wil, skb2, i);
			wil_tx_vring(wil, vif, v2, skb2);
			wil_tx_ring(wil, vif, v2, skb2);
		} else {
			wil_err(wil, "skb_copy failed\n");
		}
@@ -1267,28 +1286,6 @@ found:
	return v;
}

static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len,
			   int vring_index)
{
	wil_desc_addr_set(&d->dma.addr, pa);
	d->dma.ip_length = 0;
	/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/
	d->dma.b11 = 0/*14 | BIT(7)*/;
	d->dma.error = 0;
	d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
	d->dma.length = cpu_to_le16((u16)len);
	d->dma.d0 = (vring_index << DMA_CFG_DESC_TX_0_QID_POS);
	d->mac.d[0] = 0;
	d->mac.d[1] = 0;
	d->mac.d[2] = 0;
	d->mac.ucode_cmd = 0;
	/* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */
	d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) |
		      (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS);

	return 0;
}

static inline
void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags)
{
@@ -1498,7 +1495,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif,
		goto err_exit;
	}

	wil_tx_desc_map(hdr_desc, pa, hdrlen, vring_index);
	wil->txrx_ops.tx_desc_map((union wil_tx_desc *)hdr_desc, pa,
				  hdrlen, vring_index);
	wil_tx_desc_offload_setup_tso(hdr_desc, skb, wil_tso_type_hdr, is_ipv4,
				      tcp_hdr_len, skb_net_hdr_len);
	wil_tx_last_desc(hdr_desc);
@@ -1565,7 +1563,8 @@ static int __wil_tx_vring_tso(struct wil6210_priv *wil, struct wil6210_vif *vif,
				d = &desc_mem;
			}

			wil_tx_desc_map(d, pa, lenmss, vring_index);
			wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d,
						  pa, lenmss, vring_index);
			wil_tx_desc_offload_setup_tso(d, skb, desc_tso_type,
						      is_ipv4, tcp_hdr_len,
						      skb_net_hdr_len);
@@ -1680,7 +1679,7 @@ mem_error:
		*d = *_desc;
		_desc->dma.status = TX_DMA_STATUS_DU;
		ctx = &vring->ctx[i];
		wil_txdesc_unmap(dev, d, ctx);
		wil_txdesc_unmap(dev, (union wil_tx_desc *)d, ctx);
		memset(ctx, 0, sizeof(*ctx));
		descs_used--;
	}
@@ -1688,26 +1687,26 @@ err_exit:
	return rc;
}

static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
			  struct wil_ring *vring, struct sk_buff *skb)
static int __wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif,
			 struct wil_ring *ring, struct sk_buff *skb)
{
	struct device *dev = wil_to_dev(wil);
	struct vring_tx_desc dd, *d = &dd;
	volatile struct vring_tx_desc *_d;
	u32 swhead = vring->swhead;
	int avail = wil_ring_avail_tx(vring);
	u32 swhead = ring->swhead;
	int avail = wil_ring_avail_tx(ring);
	int nr_frags = skb_shinfo(skb)->nr_frags;
	uint f = 0;
	int vring_index = vring - wil->ring_tx;
	struct wil_ring_tx_data  *txdata = &wil->ring_tx_data[vring_index];
	int ring_index = ring - wil->ring_tx;
	struct wil_ring_tx_data  *txdata = &wil->ring_tx_data[ring_index];
	uint i = swhead;
	dma_addr_t pa;
	int used;
	bool mcast = (vring_index == vif->bcast_ring);
	bool mcast = (ring_index == vif->bcast_ring);
	uint len = skb_headlen(skb);

	wil_dbg_txrx(wil, "tx_ring: %d bytes to ring %d, nr_frags %d\n",
		     skb->len, vring_index, nr_frags);
		     skb->len, ring_index, nr_frags);

	if (unlikely(!txdata->enabled))
		return -EINVAL;
@@ -1715,23 +1714,24 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	if (unlikely(avail < 1 + nr_frags)) {
		wil_err_ratelimited(wil,
				    "Tx ring[%2d] full. No space for %d fragments\n",
				    vring_index, 1 + nr_frags);
				    ring_index, 1 + nr_frags);
		return -ENOMEM;
	}
	_d = &vring->va[i].tx.legacy;
	_d = &ring->va[i].tx.legacy;

	pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE);

	wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", vring_index,
	wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", ring_index,
		     skb_headlen(skb), skb->data, &pa);
	wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1,
			  skb->data, skb_headlen(skb), false);

	if (unlikely(dma_mapping_error(dev, pa)))
		return -EINVAL;
	vring->ctx[i].mapped_as = wil_mapped_as_single;
	ring->ctx[i].mapped_as = wil_mapped_as_single;
	/* 1-st segment */
	wil_tx_desc_map(d, pa, len, vring_index);
	wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, len,
				   ring_index);
	if (unlikely(mcast)) {
		d->mac.d[0] |= BIT(MAC_CFG_DESC_TX_0_MCS_EN_POS); /* MCS 0 */
		if (unlikely(len > WIL_BCAST_MCS0_LIMIT)) /* set MCS 1 */
@@ -1740,11 +1740,11 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	/* Process TCP/UDP checksum offloading */
	if (unlikely(wil_tx_desc_offload_setup(d, skb))) {
		wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n",
			vring_index);
			ring_index);
		goto dma_error;
	}

	vring->ctx[i].nr_frags = nr_frags;
	ring->ctx[i].nr_frags = nr_frags;
	wil_tx_desc_set_nr_frags(d, nr_frags + 1);

	/* middle segments */
@@ -1754,20 +1754,21 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
		int len = skb_frag_size(frag);

		*_d = *d;
		wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
		wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i);
		wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
				  (const void *)d, sizeof(*d), false);
		i = (swhead + f + 1) % vring->size;
		_d = &vring->va[i].tx.legacy;
		i = (swhead + f + 1) % ring->size;
		_d = &ring->va[i].tx.legacy;
		pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag),
				      DMA_TO_DEVICE);
		if (unlikely(dma_mapping_error(dev, pa))) {
			wil_err(wil, "Tx[%2d] failed to map fragment\n",
				vring_index);
				ring_index);
			goto dma_error;
		}
		vring->ctx[i].mapped_as = wil_mapped_as_page;
		wil_tx_desc_map(d, pa, len, vring_index);
		ring->ctx[i].mapped_as = wil_mapped_as_page;
		wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d,
					   pa, len, ring_index);
		/* no need to check return code -
		 * if it succeeded for 1-st descriptor,
		 * it will succeed here too
@@ -1779,7 +1780,7 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS);
	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
	*_d = *d;
	wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i);
	wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", ring_index, i);
	wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4,
			  (const void *)d, sizeof(*d), false);

@@ -1787,15 +1788,15 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	 * to prevent skb release before accounting
	 * in case of immediate "tx done"
	 */
	vring->ctx[i].skb = skb_get(skb);
	ring->ctx[i].skb = skb_get(skb);

	/* performance monitoring */
	used = wil_ring_used_tx(vring);
	used = wil_ring_used_tx(ring);
	if (wil_val_in_range(wil->ring_idle_trsh,
			     used, used + nr_frags + 1)) {
		txdata->idle += get_cycles() - txdata->last_idle;
		wil_dbg_txrx(wil,  "Ring[%2d] not idle %d -> %d\n",
			     vring_index, used, used + nr_frags + 1);
			     ring_index, used, used + nr_frags + 1);
	}

	/* Make sure to advance the head only after descriptor update is done.
@@ -1806,17 +1807,17 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	wmb();

	/* advance swhead */
	wil_ring_advance_head(vring, nr_frags + 1);
	wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", vring_index, swhead,
		     vring->swhead);
	trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
	wil_ring_advance_head(ring, nr_frags + 1);
	wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", ring_index, swhead,
		     ring->swhead);
	trace_wil6210_tx(ring_index, swhead, skb->len, nr_frags);

	/* make sure all writes to descriptors (shared memory) are done before
	 * committing them to HW
	 */
	wmb();

	wil_w(wil, vring->hwtail, vring->swhead);
	wil_w(wil, ring->hwtail, ring->swhead);

	return 0;
 dma_error:
@@ -1825,12 +1826,14 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	for (f = 0; f < nr_frags; f++) {
		struct wil_ctx *ctx;

		i = (swhead + f) % vring->size;
		ctx = &vring->ctx[i];
		_d = &vring->va[i].tx.legacy;
		i = (swhead + f) % ring->size;
		ctx = &ring->ctx[i];
		_d = &ring->va[i].tx.legacy;
		*d = *_d;
		_d->dma.status = TX_DMA_STATUS_DU;
		wil_txdesc_unmap(dev, d, ctx);
		wil->txrx_ops.tx_desc_unmap(dev,
					    (union wil_tx_desc *)d,
					    ctx);

		memset(ctx, 0, sizeof(*ctx));
	}
@@ -1838,10 +1841,10 @@ static int __wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
	return -EINVAL;
}

static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
			struct wil_ring *vring, struct sk_buff *skb)
static int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif,
		       struct wil_ring *ring, struct sk_buff *skb)
{
	int ring_index = vring - wil->ring_tx;
	int ring_index = ring - wil->ring_tx;
	struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index];
	int rc;

@@ -1856,8 +1859,8 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct wil6210_vif *vif,
		return -EINVAL;
	}

	rc = (skb_is_gso(skb) ? __wil_tx_vring_tso : __wil_tx_vring)
	     (wil, vif, vring, skb);
	rc = (skb_is_gso(skb) ? wil->txrx_ops.tx_ring_tso : __wil_tx_ring)
	     (wil, vif, ring, skb);

	spin_unlock(&txdata->lock);

@@ -1964,7 +1967,7 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
	struct wil6210_priv *wil = vif_to_wil(vif);
	struct ethhdr *eth = (void *)skb->data;
	bool bcast = is_multicast_ether_addr(eth->h_dest);
	struct wil_ring *vring;
	struct wil_ring *ring;
	static bool pr_once_fw;
	int rc;

@@ -1990,36 +1993,36 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
	/* find vring */
	if (vif->wdev.iftype == NL80211_IFTYPE_STATION && !vif->pbss) {
		/* in STA mode (ESS), all to same VRING (to AP) */
		vring = wil_find_tx_ring_sta(wil, vif, skb);
		ring = wil_find_tx_ring_sta(wil, vif, skb);
	} else if (bcast) {
		if (vif->pbss)
			/* in pbss, no bcast VRING - duplicate skb in
			 * all stations VRINGs
			 */
			vring = wil_find_tx_bcast_2(wil, vif, skb);
			ring = wil_find_tx_bcast_2(wil, vif, skb);
		else if (vif->wdev.iftype == NL80211_IFTYPE_AP)
			/* AP has a dedicated bcast VRING */
			vring = wil_find_tx_bcast_1(wil, vif, skb);
			ring = wil_find_tx_bcast_1(wil, vif, skb);
		else
			/* unexpected combination, fallback to duplicating
			 * the skb in all stations VRINGs
			 */
			vring = wil_find_tx_bcast_2(wil, vif, skb);
			ring = wil_find_tx_bcast_2(wil, vif, skb);
	} else {
		/* unicast, find specific VRING by dest. address */
		vring = wil_find_tx_ucast(wil, vif, skb);
		ring = wil_find_tx_ucast(wil, vif, skb);
	}
	if (unlikely(!vring)) {
	if (unlikely(!ring)) {
		wil_dbg_txrx(wil, "No Tx RING found for %pM\n", eth->h_dest);
		goto drop;
	}
	/* set up vring entry */
	rc = wil_tx_vring(wil, vif, vring, skb);
	rc = wil_tx_ring(wil, vif, ring, skb);

	switch (rc) {
	case 0:
		/* shall we stop net queues? */
		wil_update_net_queues_bh(wil, vif, vring, true);
		wil_update_net_queues_bh(wil, vif, ring, true);
		/* statistics will be updated on the tx_complete */
		dev_kfree_skb_any(skb);
		return NETDEV_TX_OK;
@@ -2035,22 +2038,6 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
	return NET_XMIT_DROP;
}

static inline bool wil_need_txstat(struct sk_buff *skb)
{
	struct ethhdr *eth = (void *)skb->data;

	return is_unicast_ether_addr(eth->h_dest) && skb->sk &&
	       (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS);
}

static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
{
	if (unlikely(wil_need_txstat(skb)))
		skb_complete_wifi_ack(skb, acked);
	else
		acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb);
}

/**
 * Clean up transmitted skb's from the Tx VRING
 *
@@ -2126,7 +2113,9 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid)
			wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4,
					  (const void *)d, sizeof(*d), false);

			wil_txdesc_unmap(dev, d, ctx);
			wil->txrx_ops.tx_desc_unmap(dev,
						    (union wil_tx_desc *)d,
						    ctx);

			if (skb) {
				if (likely(d->dma.error == 0)) {
@@ -2188,6 +2177,9 @@ void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil)
	wil->txrx_ops.configure_interrupt_moderation =
		wil_configure_interrupt_moderation;
	/* TX ops */
	wil->txrx_ops.tx_desc_map = wil_tx_desc_map;
	wil->txrx_ops.tx_desc_unmap = wil_txdesc_unmap;
	wil->txrx_ops.tx_ring_tso =  __wil_tx_vring_tso;
	wil->txrx_ops.ring_init_tx = wil_vring_init_tx;
	wil->txrx_ops.ring_fini_tx = wil_vring_free;
	wil->txrx_ops.ring_init_bcast = wil_vring_init_bcast;
+22 −0
Original line number Diff line number Diff line
@@ -555,6 +555,22 @@ static inline int wil_ring_is_full(struct wil_ring *ring)
	return wil_ring_next_tail(ring) == ring->swhead;
}

static inline bool wil_need_txstat(struct sk_buff *skb)
{
	struct ethhdr *eth = (void *)skb->data;

	return is_unicast_ether_addr(eth->h_dest) && skb->sk &&
	       (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS);
}

static inline void wil_consume_skb(struct sk_buff *skb, bool acked)
{
	if (unlikely(wil_need_txstat(skb)))
		skb_complete_wifi_ack(skb, acked);
	else
		acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb);
}

/* Used space in Tx ring */
static inline int wil_ring_used_tx(struct wil_ring *ring)
{
@@ -576,6 +592,12 @@ static inline int wil_get_min_tx_ring_id(struct wil6210_priv *wil)
	return wil->use_enhanced_dma_hw ? 1 : 0;
}

/* wil_val_in_range - check if value in [min,max) */
static inline bool wil_val_in_range(int val, int min, int max)
{
	return val >= min && val < max;
}

void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev);
void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb);
void wil_rx_bar(struct wil6210_priv *wil, struct wil6210_vif *vif,
Loading