162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/etherdevice.h> 762306a36Sopenharmony_ci#include <linux/moduleparam.h> 862306a36Sopenharmony_ci#include <linux/prefetch.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/list.h> 1162306a36Sopenharmony_ci#include <linux/ip.h> 1262306a36Sopenharmony_ci#include <linux/ipv6.h> 1362306a36Sopenharmony_ci#include "wil6210.h" 1462306a36Sopenharmony_ci#include "txrx_edma.h" 1562306a36Sopenharmony_ci#include "txrx.h" 1662306a36Sopenharmony_ci#include "trace.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* Max number of entries (packets to complete) to update the hwtail of tx 1962306a36Sopenharmony_ci * status ring. Should be power of 2 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#define WIL_EDMA_TX_SRING_UPDATE_HW_TAIL 128 2262306a36Sopenharmony_ci#define WIL_EDMA_MAX_DATA_OFFSET (2) 2362306a36Sopenharmony_ci/* RX buffer size must be aligned to 4 bytes */ 2462306a36Sopenharmony_ci#define WIL_EDMA_RX_BUF_LEN_DEFAULT (2048) 2562306a36Sopenharmony_ci#define MAX_INVALID_BUFF_ID_RETRY (3) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic void wil_tx_desc_unmap_edma(struct device *dev, 2862306a36Sopenharmony_ci union wil_tx_desc *desc, 2962306a36Sopenharmony_ci struct wil_ctx *ctx) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct wil_tx_enhanced_desc *d = (struct wil_tx_enhanced_desc *)desc; 3262306a36Sopenharmony_ci dma_addr_t pa = wil_tx_desc_get_addr_edma(&d->dma); 3362306a36Sopenharmony_ci u16 dmalen = le16_to_cpu(d->dma.length); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci switch (ctx->mapped_as) { 3662306a36Sopenharmony_ci case wil_mapped_as_single: 3762306a36Sopenharmony_ci dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci case wil_mapped_as_page: 4062306a36Sopenharmony_ci dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci default: 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int wil_find_free_sring(struct wil6210_priv *wil) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci int i; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) { 5262306a36Sopenharmony_ci if (!wil->srings[i].va) 5362306a36Sopenharmony_ci return i; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return -EINVAL; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void wil_sring_free(struct wil6210_priv *wil, 6062306a36Sopenharmony_ci struct wil_status_ring *sring) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 6362306a36Sopenharmony_ci size_t sz; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!sring || !sring->va) 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci sz = sring->elem_size * sring->size; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci wil_dbg_misc(wil, "status_ring_free, size(bytes)=%zu, 0x%p:%pad\n", 7162306a36Sopenharmony_ci sz, sring->va, &sring->pa); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci dma_free_coherent(dev, sz, (void *)sring->va, sring->pa); 7462306a36Sopenharmony_ci sring->pa = 0; 7562306a36Sopenharmony_ci sring->va = NULL; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int wil_sring_alloc(struct wil6210_priv *wil, 7962306a36Sopenharmony_ci struct wil_status_ring *sring) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 8262306a36Sopenharmony_ci size_t sz = sring->elem_size * sring->size; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci wil_dbg_misc(wil, "status_ring_alloc: size=%zu\n", sz); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (sz == 0) { 8762306a36Sopenharmony_ci wil_err(wil, "Cannot allocate a zero size status ring\n"); 8862306a36Sopenharmony_ci return -EINVAL; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci sring->swhead = 0; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Status messages are allocated and initialized to 0. This is necessary 9462306a36Sopenharmony_ci * since DR bit should be initialized to 0. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci sring->va = dma_alloc_coherent(dev, sz, &sring->pa, GFP_KERNEL); 9762306a36Sopenharmony_ci if (!sring->va) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci wil_dbg_misc(wil, "status_ring[%d] 0x%p:%pad\n", sring->size, sring->va, 10162306a36Sopenharmony_ci &sring->pa); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int wil_tx_init_edma(struct wil6210_priv *wil) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci int ring_id = wil_find_free_sring(wil); 10962306a36Sopenharmony_ci struct wil_status_ring *sring; 11062306a36Sopenharmony_ci int rc; 11162306a36Sopenharmony_ci u16 status_ring_size; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (wil->tx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN || 11462306a36Sopenharmony_ci wil->tx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX) 11562306a36Sopenharmony_ci wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci status_ring_size = 1 << wil->tx_status_ring_order; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci wil_dbg_misc(wil, "init TX sring: size=%u, ring_id=%u\n", 12062306a36Sopenharmony_ci status_ring_size, ring_id); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (ring_id < 0) 12362306a36Sopenharmony_ci return ring_id; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Allocate Tx status ring. Tx descriptor rings will be 12662306a36Sopenharmony_ci * allocated on WMI connect event 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci sring = &wil->srings[ring_id]; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci sring->is_rx = false; 13162306a36Sopenharmony_ci sring->size = status_ring_size; 13262306a36Sopenharmony_ci sring->elem_size = sizeof(struct wil_ring_tx_status); 13362306a36Sopenharmony_ci rc = wil_sring_alloc(wil, sring); 13462306a36Sopenharmony_ci if (rc) 13562306a36Sopenharmony_ci return rc; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rc = wil_wmi_tx_sring_cfg(wil, ring_id); 13862306a36Sopenharmony_ci if (rc) 13962306a36Sopenharmony_ci goto out_free; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci sring->desc_rdy_pol = 1; 14262306a36Sopenharmony_ci wil->tx_sring_idx = ring_id; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ciout_free: 14662306a36Sopenharmony_ci wil_sring_free(wil, sring); 14762306a36Sopenharmony_ci return rc; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* Allocate one skb for Rx descriptor RING */ 15162306a36Sopenharmony_cistatic int wil_ring_alloc_skb_edma(struct wil6210_priv *wil, 15262306a36Sopenharmony_ci struct wil_ring *ring, u32 i) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 15562306a36Sopenharmony_ci unsigned int sz = wil->rx_buf_len; 15662306a36Sopenharmony_ci dma_addr_t pa; 15762306a36Sopenharmony_ci u16 buff_id; 15862306a36Sopenharmony_ci struct list_head *active = &wil->rx_buff_mgmt.active; 15962306a36Sopenharmony_ci struct list_head *free = &wil->rx_buff_mgmt.free; 16062306a36Sopenharmony_ci struct wil_rx_buff *rx_buff; 16162306a36Sopenharmony_ci struct wil_rx_buff *buff_arr = wil->rx_buff_mgmt.buff_arr; 16262306a36Sopenharmony_ci struct sk_buff *skb; 16362306a36Sopenharmony_ci struct wil_rx_enhanced_desc dd, *d = ⅆ 16462306a36Sopenharmony_ci struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *) 16562306a36Sopenharmony_ci &ring->va[i].rx.enhanced; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (unlikely(list_empty(free))) { 16862306a36Sopenharmony_ci wil->rx_buff_mgmt.free_list_empty_cnt++; 16962306a36Sopenharmony_ci return -EAGAIN; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci skb = dev_alloc_skb(sz); 17362306a36Sopenharmony_ci if (unlikely(!skb)) 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci skb_put(skb, sz); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /** 17962306a36Sopenharmony_ci * Make sure that the network stack calculates checksum for packets 18062306a36Sopenharmony_ci * which failed the HW checksum calculation 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_NONE; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); 18562306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 18662306a36Sopenharmony_ci kfree_skb(skb); 18762306a36Sopenharmony_ci return -ENOMEM; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Get the buffer ID - the index of the rx buffer in the buff_arr */ 19162306a36Sopenharmony_ci rx_buff = list_first_entry(free, struct wil_rx_buff, list); 19262306a36Sopenharmony_ci buff_id = rx_buff->id; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* Move a buffer from the free list to the active list */ 19562306a36Sopenharmony_ci list_move(&rx_buff->list, active); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci buff_arr[buff_id].skb = skb; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); 20062306a36Sopenharmony_ci d->dma.length = cpu_to_le16(sz); 20162306a36Sopenharmony_ci d->mac.buff_id = cpu_to_le16(buff_id); 20262306a36Sopenharmony_ci *_d = *d; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Save the physical address in skb->cb for later use in dma_unmap */ 20562306a36Sopenharmony_ci memcpy(skb->cb, &pa, sizeof(pa)); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic inline 21162306a36Sopenharmony_civoid wil_get_next_rx_status_msg(struct wil_status_ring *sring, u8 *dr_bit, 21262306a36Sopenharmony_ci void *msg) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct wil_rx_status_compressed *_msg; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci _msg = (struct wil_rx_status_compressed *) 21762306a36Sopenharmony_ci (sring->va + (sring->elem_size * sring->swhead)); 21862306a36Sopenharmony_ci *dr_bit = WIL_GET_BITS(_msg->d0, 31, 31); 21962306a36Sopenharmony_ci /* make sure dr_bit is read before the rest of status msg */ 22062306a36Sopenharmony_ci rmb(); 22162306a36Sopenharmony_ci memcpy(msg, (void *)_msg, sring->elem_size); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic inline void wil_sring_advance_swhead(struct wil_status_ring *sring) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci sring->swhead = (sring->swhead + 1) % sring->size; 22762306a36Sopenharmony_ci if (sring->swhead == 0) 22862306a36Sopenharmony_ci sring->desc_rdy_pol = 1 - sring->desc_rdy_pol; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int wil_rx_refill_edma(struct wil6210_priv *wil) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 23462306a36Sopenharmony_ci u32 next_head; 23562306a36Sopenharmony_ci int rc = 0; 23662306a36Sopenharmony_ci ring->swtail = *ring->edma_rx_swtail.va; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (; next_head = wil_ring_next_head(ring), 23962306a36Sopenharmony_ci (next_head != ring->swtail); 24062306a36Sopenharmony_ci ring->swhead = next_head) { 24162306a36Sopenharmony_ci rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead); 24262306a36Sopenharmony_ci if (unlikely(rc)) { 24362306a36Sopenharmony_ci if (rc == -EAGAIN) 24462306a36Sopenharmony_ci wil_dbg_txrx(wil, "No free buffer ID found\n"); 24562306a36Sopenharmony_ci else 24662306a36Sopenharmony_ci wil_err_ratelimited(wil, 24762306a36Sopenharmony_ci "Error %d in refill desc[%d]\n", 24862306a36Sopenharmony_ci rc, ring->swhead); 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* make sure all writes to descriptors (shared memory) are done before 25462306a36Sopenharmony_ci * committing them to HW 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_ci wmb(); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci wil_w(wil, ring->hwtail, ring->swhead); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci return rc; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, 26462306a36Sopenharmony_ci struct wil_ring *ring) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 26762306a36Sopenharmony_ci struct list_head *active = &wil->rx_buff_mgmt.active; 26862306a36Sopenharmony_ci dma_addr_t pa; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!wil->rx_buff_mgmt.buff_arr) 27162306a36Sopenharmony_ci return; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci while (!list_empty(active)) { 27462306a36Sopenharmony_ci struct wil_rx_buff *rx_buff = 27562306a36Sopenharmony_ci list_first_entry(active, struct wil_rx_buff, list); 27662306a36Sopenharmony_ci struct sk_buff *skb = rx_buff->skb; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (unlikely(!skb)) { 27962306a36Sopenharmony_ci wil_err(wil, "No Rx skb at buff_id %d\n", rx_buff->id); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci rx_buff->skb = NULL; 28262306a36Sopenharmony_ci memcpy(&pa, skb->cb, sizeof(pa)); 28362306a36Sopenharmony_ci dma_unmap_single(dev, pa, wil->rx_buf_len, 28462306a36Sopenharmony_ci DMA_FROM_DEVICE); 28562306a36Sopenharmony_ci kfree_skb(skb); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Move the buffer from the active to the free list */ 28962306a36Sopenharmony_ci list_move(&rx_buff->list, &wil->rx_buff_mgmt.free); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic void wil_free_rx_buff_arr(struct wil6210_priv *wil) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!wil->rx_buff_mgmt.buff_arr) 29862306a36Sopenharmony_ci return; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Move all the buffers to the free list in case active list is 30162306a36Sopenharmony_ci * not empty in order to release all SKBs before deleting the array 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci wil_move_all_rx_buff_to_free_list(wil, ring); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci kfree(wil->rx_buff_mgmt.buff_arr); 30662306a36Sopenharmony_ci wil->rx_buff_mgmt.buff_arr = NULL; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int wil_init_rx_buff_arr(struct wil6210_priv *wil, 31062306a36Sopenharmony_ci size_t size) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct wil_rx_buff *buff_arr; 31362306a36Sopenharmony_ci struct list_head *active = &wil->rx_buff_mgmt.active; 31462306a36Sopenharmony_ci struct list_head *free = &wil->rx_buff_mgmt.free; 31562306a36Sopenharmony_ci int i; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci wil->rx_buff_mgmt.buff_arr = kcalloc(size + 1, 31862306a36Sopenharmony_ci sizeof(struct wil_rx_buff), 31962306a36Sopenharmony_ci GFP_KERNEL); 32062306a36Sopenharmony_ci if (!wil->rx_buff_mgmt.buff_arr) 32162306a36Sopenharmony_ci return -ENOMEM; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Set list heads */ 32462306a36Sopenharmony_ci INIT_LIST_HEAD(active); 32562306a36Sopenharmony_ci INIT_LIST_HEAD(free); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Linkify the list. 32862306a36Sopenharmony_ci * buffer id 0 should not be used (marks invalid id). 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci buff_arr = wil->rx_buff_mgmt.buff_arr; 33162306a36Sopenharmony_ci for (i = 1; i <= size; i++) { 33262306a36Sopenharmony_ci list_add(&buff_arr[i].list, free); 33362306a36Sopenharmony_ci buff_arr[i].id = i; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci wil->rx_buff_mgmt.size = size + 1; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return 0; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int wil_init_rx_sring(struct wil6210_priv *wil, 34262306a36Sopenharmony_ci u16 status_ring_size, 34362306a36Sopenharmony_ci size_t elem_size, 34462306a36Sopenharmony_ci u16 ring_id) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci struct wil_status_ring *sring = &wil->srings[ring_id]; 34762306a36Sopenharmony_ci int rc; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n", 35062306a36Sopenharmony_ci status_ring_size, ring_id); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci memset(&sring->rx_data, 0, sizeof(sring->rx_data)); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci sring->is_rx = true; 35562306a36Sopenharmony_ci sring->size = status_ring_size; 35662306a36Sopenharmony_ci sring->elem_size = elem_size; 35762306a36Sopenharmony_ci rc = wil_sring_alloc(wil, sring); 35862306a36Sopenharmony_ci if (rc) 35962306a36Sopenharmony_ci return rc; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci rc = wil_wmi_rx_sring_add(wil, ring_id); 36262306a36Sopenharmony_ci if (rc) 36362306a36Sopenharmony_ci goto out_free; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci sring->desc_rdy_pol = 1; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ciout_free: 36962306a36Sopenharmony_ci wil_sring_free(wil, sring); 37062306a36Sopenharmony_ci return rc; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic int wil_ring_alloc_desc_ring(struct wil6210_priv *wil, 37462306a36Sopenharmony_ci struct wil_ring *ring) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 37762306a36Sopenharmony_ci size_t sz = ring->size * sizeof(ring->va[0]); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci wil_dbg_misc(wil, "alloc_desc_ring:\n"); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(ring->va[0]) != 32); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ring->swhead = 0; 38462306a36Sopenharmony_ci ring->swtail = 0; 38562306a36Sopenharmony_ci ring->ctx = kcalloc(ring->size, sizeof(ring->ctx[0]), GFP_KERNEL); 38662306a36Sopenharmony_ci if (!ring->ctx) 38762306a36Sopenharmony_ci goto err; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci ring->va = dma_alloc_coherent(dev, sz, &ring->pa, GFP_KERNEL); 39062306a36Sopenharmony_ci if (!ring->va) 39162306a36Sopenharmony_ci goto err_free_ctx; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (ring->is_rx) { 39462306a36Sopenharmony_ci sz = sizeof(*ring->edma_rx_swtail.va); 39562306a36Sopenharmony_ci ring->edma_rx_swtail.va = 39662306a36Sopenharmony_ci dma_alloc_coherent(dev, sz, &ring->edma_rx_swtail.pa, 39762306a36Sopenharmony_ci GFP_KERNEL); 39862306a36Sopenharmony_ci if (!ring->edma_rx_swtail.va) 39962306a36Sopenharmony_ci goto err_free_va; 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci wil_dbg_misc(wil, "%s ring[%d] 0x%p:%pad 0x%p\n", 40362306a36Sopenharmony_ci ring->is_rx ? "RX" : "TX", 40462306a36Sopenharmony_ci ring->size, ring->va, &ring->pa, ring->ctx); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_cierr_free_va: 40862306a36Sopenharmony_ci dma_free_coherent(dev, ring->size * sizeof(ring->va[0]), 40962306a36Sopenharmony_ci (void *)ring->va, ring->pa); 41062306a36Sopenharmony_ci ring->va = NULL; 41162306a36Sopenharmony_cierr_free_ctx: 41262306a36Sopenharmony_ci kfree(ring->ctx); 41362306a36Sopenharmony_ci ring->ctx = NULL; 41462306a36Sopenharmony_cierr: 41562306a36Sopenharmony_ci return -ENOMEM; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 42162306a36Sopenharmony_ci size_t sz; 42262306a36Sopenharmony_ci int ring_index = 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!ring->va) 42562306a36Sopenharmony_ci return; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci sz = ring->size * sizeof(ring->va[0]); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 43062306a36Sopenharmony_ci if (ring->is_rx) { 43162306a36Sopenharmony_ci wil_dbg_misc(wil, "free Rx ring [%d] 0x%p:%pad 0x%p\n", 43262306a36Sopenharmony_ci ring->size, ring->va, 43362306a36Sopenharmony_ci &ring->pa, ring->ctx); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci wil_move_all_rx_buff_to_free_list(wil, ring); 43662306a36Sopenharmony_ci dma_free_coherent(dev, sizeof(*ring->edma_rx_swtail.va), 43762306a36Sopenharmony_ci ring->edma_rx_swtail.va, 43862306a36Sopenharmony_ci ring->edma_rx_swtail.pa); 43962306a36Sopenharmony_ci goto out; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* TX ring */ 44362306a36Sopenharmony_ci ring_index = ring - wil->ring_tx; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci wil_dbg_misc(wil, "free Tx ring %d [%d] 0x%p:%pad 0x%p\n", 44662306a36Sopenharmony_ci ring_index, ring->size, ring->va, 44762306a36Sopenharmony_ci &ring->pa, ring->ctx); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci while (!wil_ring_is_empty(ring)) { 45062306a36Sopenharmony_ci struct wil_ctx *ctx; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci struct wil_tx_enhanced_desc dd, *d = ⅆ 45362306a36Sopenharmony_ci struct wil_tx_enhanced_desc *_d = 45462306a36Sopenharmony_ci (struct wil_tx_enhanced_desc *) 45562306a36Sopenharmony_ci &ring->va[ring->swtail].tx.enhanced; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ctx = &ring->ctx[ring->swtail]; 45862306a36Sopenharmony_ci if (!ctx) { 45962306a36Sopenharmony_ci wil_dbg_txrx(wil, 46062306a36Sopenharmony_ci "ctx(%d) was already completed\n", 46162306a36Sopenharmony_ci ring->swtail); 46262306a36Sopenharmony_ci ring->swtail = wil_ring_next_tail(ring); 46362306a36Sopenharmony_ci continue; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci *d = *_d; 46662306a36Sopenharmony_ci wil_tx_desc_unmap_edma(dev, (union wil_tx_desc *)d, ctx); 46762306a36Sopenharmony_ci if (ctx->skb) 46862306a36Sopenharmony_ci dev_kfree_skb_any(ctx->skb); 46962306a36Sopenharmony_ci ring->swtail = wil_ring_next_tail(ring); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ciout: 47362306a36Sopenharmony_ci dma_free_coherent(dev, sz, (void *)ring->va, ring->pa); 47462306a36Sopenharmony_ci kfree(ring->ctx); 47562306a36Sopenharmony_ci ring->pa = 0; 47662306a36Sopenharmony_ci ring->va = NULL; 47762306a36Sopenharmony_ci ring->ctx = NULL; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, 48162306a36Sopenharmony_ci int status_ring_id) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 48462306a36Sopenharmony_ci int rc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci wil_dbg_misc(wil, "init RX desc ring\n"); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ring->size = desc_ring_size; 48962306a36Sopenharmony_ci ring->is_rx = true; 49062306a36Sopenharmony_ci rc = wil_ring_alloc_desc_ring(wil, ring); 49162306a36Sopenharmony_ci if (rc) 49262306a36Sopenharmony_ci return rc; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci rc = wil_wmi_rx_desc_ring_add(wil, status_ring_id); 49562306a36Sopenharmony_ci if (rc) 49662306a36Sopenharmony_ci goto out_free; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ciout_free: 50062306a36Sopenharmony_ci wil_ring_free_edma(wil, ring); 50162306a36Sopenharmony_ci return rc; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic void wil_get_reorder_params_edma(struct wil6210_priv *wil, 50562306a36Sopenharmony_ci struct sk_buff *skb, int *tid, 50662306a36Sopenharmony_ci int *cid, int *mid, u16 *seq, 50762306a36Sopenharmony_ci int *mcast, int *retry) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct wil_rx_status_extended *s = wil_skb_rxstatus(skb); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci *tid = wil_rx_status_get_tid(s); 51262306a36Sopenharmony_ci *cid = wil_rx_status_get_cid(s); 51362306a36Sopenharmony_ci *mid = wil_rx_status_get_mid(s); 51462306a36Sopenharmony_ci *seq = le16_to_cpu(wil_rx_status_get_seq(wil, s)); 51562306a36Sopenharmony_ci *mcast = wil_rx_status_get_mcast(s); 51662306a36Sopenharmony_ci *retry = wil_rx_status_get_retry(s); 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cistatic void wil_get_netif_rx_params_edma(struct sk_buff *skb, int *cid, 52062306a36Sopenharmony_ci int *security) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct wil_rx_status_extended *s = wil_skb_rxstatus(skb); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci *cid = wil_rx_status_get_cid(s); 52562306a36Sopenharmony_ci *security = wil_rx_status_get_security(s); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int wil_rx_crypto_check_edma(struct wil6210_priv *wil, 52962306a36Sopenharmony_ci struct sk_buff *skb) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct wil_rx_status_extended *st; 53262306a36Sopenharmony_ci int cid, tid, key_id, mc; 53362306a36Sopenharmony_ci struct wil_sta_info *s; 53462306a36Sopenharmony_ci struct wil_tid_crypto_rx *c; 53562306a36Sopenharmony_ci struct wil_tid_crypto_rx_single *cc; 53662306a36Sopenharmony_ci const u8 *pn; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* In HW reorder, HW is responsible for crypto check */ 53962306a36Sopenharmony_ci if (wil->use_rx_hw_reordering) 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci st = wil_skb_rxstatus(skb); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci cid = wil_rx_status_get_cid(st); 54562306a36Sopenharmony_ci tid = wil_rx_status_get_tid(st); 54662306a36Sopenharmony_ci key_id = wil_rx_status_get_key_id(st); 54762306a36Sopenharmony_ci mc = wil_rx_status_get_mcast(st); 54862306a36Sopenharmony_ci s = &wil->sta[cid]; 54962306a36Sopenharmony_ci c = mc ? &s->group_crypto_rx : &s->tid_crypto_rx[tid]; 55062306a36Sopenharmony_ci cc = &c->key_id[key_id]; 55162306a36Sopenharmony_ci pn = (u8 *)&st->ext.pn; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (!cc->key_set) { 55462306a36Sopenharmony_ci wil_err_ratelimited(wil, 55562306a36Sopenharmony_ci "Key missing. CID %d TID %d MCast %d KEY_ID %d\n", 55662306a36Sopenharmony_ci cid, tid, mc, key_id); 55762306a36Sopenharmony_ci return -EINVAL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (reverse_memcmp(pn, cc->pn, IEEE80211_GCMP_PN_LEN) <= 0) { 56162306a36Sopenharmony_ci wil_err_ratelimited(wil, 56262306a36Sopenharmony_ci "Replay attack. CID %d TID %d MCast %d KEY_ID %d PN %6phN last %6phN\n", 56362306a36Sopenharmony_ci cid, tid, mc, key_id, pn, cc->pn); 56462306a36Sopenharmony_ci return -EINVAL; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci memcpy(cc->pn, pn, IEEE80211_GCMP_PN_LEN); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic bool wil_is_rx_idle_edma(struct wil6210_priv *wil) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct wil_status_ring *sring; 57462306a36Sopenharmony_ci struct wil_rx_status_extended msg1; 57562306a36Sopenharmony_ci void *msg = &msg1; 57662306a36Sopenharmony_ci u8 dr_bit; 57762306a36Sopenharmony_ci int i; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci for (i = 0; i < wil->num_rx_status_rings; i++) { 58062306a36Sopenharmony_ci sring = &wil->srings[i]; 58162306a36Sopenharmony_ci if (!sring->va) 58262306a36Sopenharmony_ci continue; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci wil_get_next_rx_status_msg(sring, &dr_bit, msg); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Check if there are unhandled RX status messages */ 58762306a36Sopenharmony_ci if (dr_bit == sring->desc_rdy_pol) 58862306a36Sopenharmony_ci return false; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci return true; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_cistatic void wil_rx_buf_len_init_edma(struct wil6210_priv *wil) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci /* RX buffer size must be aligned to 4 bytes */ 59762306a36Sopenharmony_ci wil->rx_buf_len = rx_large_buf ? 59862306a36Sopenharmony_ci WIL_MAX_ETH_MTU : WIL_EDMA_RX_BUF_LEN_DEFAULT; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci u16 status_ring_size, desc_ring_size = 1 << desc_ring_order; 60462306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 60562306a36Sopenharmony_ci int rc; 60662306a36Sopenharmony_ci size_t elem_size = wil->use_compressed_rx_status ? 60762306a36Sopenharmony_ci sizeof(struct wil_rx_status_compressed) : 60862306a36Sopenharmony_ci sizeof(struct wil_rx_status_extended); 60962306a36Sopenharmony_ci int i; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* In SW reorder one must use extended status messages */ 61262306a36Sopenharmony_ci if (wil->use_compressed_rx_status && !wil->use_rx_hw_reordering) { 61362306a36Sopenharmony_ci wil_err(wil, 61462306a36Sopenharmony_ci "compressed RX status cannot be used with SW reorder\n"); 61562306a36Sopenharmony_ci return -EINVAL; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci if (wil->rx_status_ring_order <= desc_ring_order) 61862306a36Sopenharmony_ci /* make sure sring is larger than desc ring */ 61962306a36Sopenharmony_ci wil->rx_status_ring_order = desc_ring_order + 1; 62062306a36Sopenharmony_ci if (wil->rx_buff_id_count <= desc_ring_size) 62162306a36Sopenharmony_ci /* make sure we will not run out of buff_ids */ 62262306a36Sopenharmony_ci wil->rx_buff_id_count = desc_ring_size + 512; 62362306a36Sopenharmony_ci if (wil->rx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN || 62462306a36Sopenharmony_ci wil->rx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX) 62562306a36Sopenharmony_ci wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci status_ring_size = 1 << wil->rx_status_ring_order; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci wil_dbg_misc(wil, 63062306a36Sopenharmony_ci "rx_init, desc_ring_size=%u, status_ring_size=%u, elem_size=%zu\n", 63162306a36Sopenharmony_ci desc_ring_size, status_ring_size, elem_size); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci wil_rx_buf_len_init_edma(wil); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Use debugfs dbg_num_rx_srings if set, reserve one sring for TX */ 63662306a36Sopenharmony_ci if (wil->num_rx_status_rings > WIL6210_MAX_STATUS_RINGS - 1) 63762306a36Sopenharmony_ci wil->num_rx_status_rings = WIL6210_MAX_STATUS_RINGS - 1; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci wil_dbg_misc(wil, "rx_init: allocate %d status rings\n", 64062306a36Sopenharmony_ci wil->num_rx_status_rings); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len); 64362306a36Sopenharmony_ci if (rc) 64462306a36Sopenharmony_ci return rc; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Allocate status ring */ 64762306a36Sopenharmony_ci for (i = 0; i < wil->num_rx_status_rings; i++) { 64862306a36Sopenharmony_ci int sring_id = wil_find_free_sring(wil); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (sring_id < 0) { 65162306a36Sopenharmony_ci rc = -EFAULT; 65262306a36Sopenharmony_ci goto err_free_status; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci rc = wil_init_rx_sring(wil, status_ring_size, elem_size, 65562306a36Sopenharmony_ci sring_id); 65662306a36Sopenharmony_ci if (rc) 65762306a36Sopenharmony_ci goto err_free_status; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* Allocate descriptor ring */ 66162306a36Sopenharmony_ci rc = wil_init_rx_desc_ring(wil, desc_ring_size, 66262306a36Sopenharmony_ci WIL_DEFAULT_RX_STATUS_RING_ID); 66362306a36Sopenharmony_ci if (rc) 66462306a36Sopenharmony_ci goto err_free_status; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (wil->rx_buff_id_count >= status_ring_size) { 66762306a36Sopenharmony_ci wil_info(wil, 66862306a36Sopenharmony_ci "rx_buff_id_count %d exceeds sring_size %d. set it to %d\n", 66962306a36Sopenharmony_ci wil->rx_buff_id_count, status_ring_size, 67062306a36Sopenharmony_ci status_ring_size - 1); 67162306a36Sopenharmony_ci wil->rx_buff_id_count = status_ring_size - 1; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Allocate Rx buffer array */ 67562306a36Sopenharmony_ci rc = wil_init_rx_buff_arr(wil, wil->rx_buff_id_count); 67662306a36Sopenharmony_ci if (rc) 67762306a36Sopenharmony_ci goto err_free_desc; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Fill descriptor ring with credits */ 68062306a36Sopenharmony_ci rc = wil_rx_refill_edma(wil); 68162306a36Sopenharmony_ci if (rc) 68262306a36Sopenharmony_ci goto err_free_rx_buff_arr; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_cierr_free_rx_buff_arr: 68662306a36Sopenharmony_ci wil_free_rx_buff_arr(wil); 68762306a36Sopenharmony_cierr_free_desc: 68862306a36Sopenharmony_ci wil_ring_free_edma(wil, ring); 68962306a36Sopenharmony_cierr_free_status: 69062306a36Sopenharmony_ci for (i = 0; i < wil->num_rx_status_rings; i++) 69162306a36Sopenharmony_ci wil_sring_free(wil, &wil->srings[i]); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return rc; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, 69762306a36Sopenharmony_ci int size, int cid, int tid) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 70062306a36Sopenharmony_ci int rc; 70162306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[ring_id]; 70262306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci wil_dbg_misc(wil, 70762306a36Sopenharmony_ci "init TX ring: ring_id=%u, cid=%u, tid=%u, sring_id=%u\n", 70862306a36Sopenharmony_ci ring_id, cid, tid, wil->tx_sring_idx); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci wil_tx_data_init(txdata); 71162306a36Sopenharmony_ci ring->size = size; 71262306a36Sopenharmony_ci rc = wil_ring_alloc_desc_ring(wil, ring); 71362306a36Sopenharmony_ci if (rc) 71462306a36Sopenharmony_ci goto out; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][0] = cid; 71762306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1] = tid; 71862306a36Sopenharmony_ci if (!vif->privacy) 71962306a36Sopenharmony_ci txdata->dot1x_open = true; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci rc = wil_wmi_tx_desc_ring_add(vif, ring_id, cid, tid); 72262306a36Sopenharmony_ci if (rc) { 72362306a36Sopenharmony_ci wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed\n"); 72462306a36Sopenharmony_ci goto out_free; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (txdata->dot1x_open && agg_wsize >= 0) 72862306a36Sopenharmony_ci wil_addba_tx_request(wil, ring_id, agg_wsize); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci out_free: 73262306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 73362306a36Sopenharmony_ci txdata->dot1x_open = false; 73462306a36Sopenharmony_ci txdata->enabled = 0; 73562306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 73662306a36Sopenharmony_ci wil_ring_free_edma(wil, ring); 73762306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][0] = wil->max_assoc_sta; 73862306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1] = 0; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci out: 74162306a36Sopenharmony_ci return rc; 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int wil_tx_ring_modify_edma(struct wil6210_vif *vif, int ring_id, 74562306a36Sopenharmony_ci int cid, int tid) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci wil_err(wil, "ring modify is not supported for EDMA\n"); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return -EOPNOTSUPP; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci/* This function is used only for RX SW reorder */ 75562306a36Sopenharmony_cistatic int wil_check_bar(struct wil6210_priv *wil, void *msg, int cid, 75662306a36Sopenharmony_ci struct sk_buff *skb, struct wil_net_stats *stats) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci u8 ftype; 75962306a36Sopenharmony_ci u8 fc1; 76062306a36Sopenharmony_ci int mid; 76162306a36Sopenharmony_ci int tid; 76262306a36Sopenharmony_ci u16 seq; 76362306a36Sopenharmony_ci struct wil6210_vif *vif; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci ftype = wil_rx_status_get_frame_type(wil, msg); 76662306a36Sopenharmony_ci if (ftype == IEEE80211_FTYPE_DATA) 76762306a36Sopenharmony_ci return 0; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci fc1 = wil_rx_status_get_fc1(wil, msg); 77062306a36Sopenharmony_ci mid = wil_rx_status_get_mid(msg); 77162306a36Sopenharmony_ci tid = wil_rx_status_get_tid(msg); 77262306a36Sopenharmony_ci seq = le16_to_cpu(wil_rx_status_get_seq(wil, msg)); 77362306a36Sopenharmony_ci vif = wil->vifs[mid]; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (unlikely(!vif)) { 77662306a36Sopenharmony_ci wil_dbg_txrx(wil, "RX descriptor with invalid mid %d", mid); 77762306a36Sopenharmony_ci return -EAGAIN; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci wil_dbg_txrx(wil, 78162306a36Sopenharmony_ci "Non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", 78262306a36Sopenharmony_ci fc1, mid, cid, tid, seq); 78362306a36Sopenharmony_ci if (stats) 78462306a36Sopenharmony_ci stats->rx_non_data_frame++; 78562306a36Sopenharmony_ci if (wil_is_back_req(fc1)) { 78662306a36Sopenharmony_ci wil_dbg_txrx(wil, 78762306a36Sopenharmony_ci "BAR: MID %d CID %d TID %d Seq 0x%03x\n", 78862306a36Sopenharmony_ci mid, cid, tid, seq); 78962306a36Sopenharmony_ci wil_rx_bar(wil, vif, cid, tid, seq); 79062306a36Sopenharmony_ci } else { 79162306a36Sopenharmony_ci u32 sz = wil->use_compressed_rx_status ? 79262306a36Sopenharmony_ci sizeof(struct wil_rx_status_compressed) : 79362306a36Sopenharmony_ci sizeof(struct wil_rx_status_extended); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* print again all info. One can enable only this 79662306a36Sopenharmony_ci * without overhead for printing every Rx frame 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci wil_dbg_txrx(wil, 79962306a36Sopenharmony_ci "Unhandled non-data frame FC[7:0] 0x%02x MID %d CID %d TID %d Seq 0x%03x\n", 80062306a36Sopenharmony_ci fc1, mid, cid, tid, seq); 80162306a36Sopenharmony_ci wil_hex_dump_txrx("RxS ", DUMP_PREFIX_NONE, 32, 4, 80262306a36Sopenharmony_ci (const void *)msg, sz, false); 80362306a36Sopenharmony_ci wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, 80462306a36Sopenharmony_ci skb->data, skb_headlen(skb), false); 80562306a36Sopenharmony_ci } 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci return -EAGAIN; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int wil_rx_error_check_edma(struct wil6210_priv *wil, 81162306a36Sopenharmony_ci struct sk_buff *skb, 81262306a36Sopenharmony_ci struct wil_net_stats *stats) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci int l2_rx_status; 81562306a36Sopenharmony_ci void *msg = wil_skb_rxstatus(skb); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci l2_rx_status = wil_rx_status_get_l2_rx_status(msg); 81862306a36Sopenharmony_ci if (l2_rx_status != 0) { 81962306a36Sopenharmony_ci wil_dbg_txrx(wil, "L2 RX error, l2_rx_status=0x%x\n", 82062306a36Sopenharmony_ci l2_rx_status); 82162306a36Sopenharmony_ci /* Due to HW issue, KEY error will trigger a MIC error */ 82262306a36Sopenharmony_ci if (l2_rx_status == WIL_RX_EDMA_ERROR_MIC) { 82362306a36Sopenharmony_ci wil_err_ratelimited(wil, 82462306a36Sopenharmony_ci "L2 MIC/KEY error, dropping packet\n"); 82562306a36Sopenharmony_ci stats->rx_mic_error++; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci if (l2_rx_status == WIL_RX_EDMA_ERROR_KEY) { 82862306a36Sopenharmony_ci wil_err_ratelimited(wil, 82962306a36Sopenharmony_ci "L2 KEY error, dropping packet\n"); 83062306a36Sopenharmony_ci stats->rx_key_error++; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci if (l2_rx_status == WIL_RX_EDMA_ERROR_REPLAY) { 83362306a36Sopenharmony_ci wil_err_ratelimited(wil, 83462306a36Sopenharmony_ci "L2 REPLAY error, dropping packet\n"); 83562306a36Sopenharmony_ci stats->rx_replay++; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci if (l2_rx_status == WIL_RX_EDMA_ERROR_AMSDU) { 83862306a36Sopenharmony_ci wil_err_ratelimited(wil, 83962306a36Sopenharmony_ci "L2 AMSDU error, dropping packet\n"); 84062306a36Sopenharmony_ci stats->rx_amsdu_error++; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci return -EFAULT; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci skb->ip_summed = wil_rx_status_get_checksum(msg, stats); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic struct sk_buff *wil_sring_reap_rx_edma(struct wil6210_priv *wil, 85162306a36Sopenharmony_ci struct wil_status_ring *sring) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 85462306a36Sopenharmony_ci struct wil_rx_status_extended msg1; 85562306a36Sopenharmony_ci void *msg = &msg1; 85662306a36Sopenharmony_ci u16 buff_id; 85762306a36Sopenharmony_ci struct sk_buff *skb; 85862306a36Sopenharmony_ci dma_addr_t pa; 85962306a36Sopenharmony_ci struct wil_ring_rx_data *rxdata = &sring->rx_data; 86062306a36Sopenharmony_ci unsigned int sz = wil->rx_buf_len; 86162306a36Sopenharmony_ci struct wil_net_stats *stats = NULL; 86262306a36Sopenharmony_ci u16 dmalen; 86362306a36Sopenharmony_ci int cid; 86462306a36Sopenharmony_ci bool eop, headstolen; 86562306a36Sopenharmony_ci int delta; 86662306a36Sopenharmony_ci u8 dr_bit; 86762306a36Sopenharmony_ci u8 data_offset; 86862306a36Sopenharmony_ci struct wil_rx_status_extended *s; 86962306a36Sopenharmony_ci u16 sring_idx = sring - wil->srings; 87062306a36Sopenharmony_ci int invalid_buff_id_retry; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct wil_rx_status_extended) > sizeof(skb->cb)); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ciagain: 87562306a36Sopenharmony_ci wil_get_next_rx_status_msg(sring, &dr_bit, msg); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Completed handling all the ready status messages */ 87862306a36Sopenharmony_ci if (dr_bit != sring->desc_rdy_pol) 87962306a36Sopenharmony_ci return NULL; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci /* Extract the buffer ID from the status message */ 88262306a36Sopenharmony_ci buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci invalid_buff_id_retry = 0; 88562306a36Sopenharmony_ci while (!buff_id) { 88662306a36Sopenharmony_ci struct wil_rx_status_extended *s; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci wil_dbg_txrx(wil, 88962306a36Sopenharmony_ci "buff_id is not updated yet by HW, (swhead 0x%x)\n", 89062306a36Sopenharmony_ci sring->swhead); 89162306a36Sopenharmony_ci if (++invalid_buff_id_retry > MAX_INVALID_BUFF_ID_RETRY) 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Read the status message again */ 89562306a36Sopenharmony_ci s = (struct wil_rx_status_extended *) 89662306a36Sopenharmony_ci (sring->va + (sring->elem_size * sring->swhead)); 89762306a36Sopenharmony_ci *(struct wil_rx_status_extended *)msg = *s; 89862306a36Sopenharmony_ci buff_id = le16_to_cpu(wil_rx_status_get_buff_id(msg)); 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (unlikely(!wil_val_in_range(buff_id, 1, wil->rx_buff_mgmt.size))) { 90262306a36Sopenharmony_ci wil_err(wil, "Corrupt buff_id=%d, sring->swhead=%d\n", 90362306a36Sopenharmony_ci buff_id, sring->swhead); 90462306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, 90562306a36Sopenharmony_ci msg, wil->use_compressed_rx_status ? 90662306a36Sopenharmony_ci sizeof(struct wil_rx_status_compressed) : 90762306a36Sopenharmony_ci sizeof(struct wil_rx_status_extended), false); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci wil_rx_status_reset_buff_id(sring); 91062306a36Sopenharmony_ci wil_sring_advance_swhead(sring); 91162306a36Sopenharmony_ci sring->invalid_buff_id_cnt++; 91262306a36Sopenharmony_ci goto again; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Extract the SKB from the rx_buff management array */ 91662306a36Sopenharmony_ci skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; 91762306a36Sopenharmony_ci wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; 91862306a36Sopenharmony_ci if (!skb) { 91962306a36Sopenharmony_ci wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); 92062306a36Sopenharmony_ci wil_rx_status_reset_buff_id(sring); 92162306a36Sopenharmony_ci /* Move the buffer from the active list to the free list */ 92262306a36Sopenharmony_ci list_move_tail(&wil->rx_buff_mgmt.buff_arr[buff_id].list, 92362306a36Sopenharmony_ci &wil->rx_buff_mgmt.free); 92462306a36Sopenharmony_ci wil_sring_advance_swhead(sring); 92562306a36Sopenharmony_ci sring->invalid_buff_id_cnt++; 92662306a36Sopenharmony_ci goto again; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci wil_rx_status_reset_buff_id(sring); 93062306a36Sopenharmony_ci wil_sring_advance_swhead(sring); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci memcpy(&pa, skb->cb, sizeof(pa)); 93362306a36Sopenharmony_ci dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); 93462306a36Sopenharmony_ci dmalen = le16_to_cpu(wil_rx_status_get_length(msg)); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci trace_wil6210_rx_status(wil, wil->use_compressed_rx_status, buff_id, 93762306a36Sopenharmony_ci msg); 93862306a36Sopenharmony_ci wil_dbg_txrx(wil, "Rx, buff_id=%u, sring_idx=%u, dmalen=%u bytes\n", 93962306a36Sopenharmony_ci buff_id, sring_idx, dmalen); 94062306a36Sopenharmony_ci wil_hex_dump_txrx("RxS ", DUMP_PREFIX_NONE, 32, 4, 94162306a36Sopenharmony_ci (const void *)msg, wil->use_compressed_rx_status ? 94262306a36Sopenharmony_ci sizeof(struct wil_rx_status_compressed) : 94362306a36Sopenharmony_ci sizeof(struct wil_rx_status_extended), false); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci /* Move the buffer from the active list to the free list */ 94662306a36Sopenharmony_ci list_move_tail(&wil->rx_buff_mgmt.buff_arr[buff_id].list, 94762306a36Sopenharmony_ci &wil->rx_buff_mgmt.free); 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci eop = wil_rx_status_get_eop(msg); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci cid = wil_rx_status_get_cid(msg); 95262306a36Sopenharmony_ci if (unlikely(!wil_val_in_range(cid, 0, wil->max_assoc_sta))) { 95362306a36Sopenharmony_ci wil_err(wil, "Corrupt cid=%d, sring->swhead=%d\n", 95462306a36Sopenharmony_ci cid, sring->swhead); 95562306a36Sopenharmony_ci rxdata->skipping = true; 95662306a36Sopenharmony_ci goto skipping; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci stats = &wil->sta[cid].stats; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (unlikely(dmalen < ETH_HLEN)) { 96162306a36Sopenharmony_ci wil_dbg_txrx(wil, "Short frame, len = %d\n", dmalen); 96262306a36Sopenharmony_ci stats->rx_short_frame++; 96362306a36Sopenharmony_ci rxdata->skipping = true; 96462306a36Sopenharmony_ci goto skipping; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (unlikely(dmalen > sz)) { 96862306a36Sopenharmony_ci wil_err(wil, "Rx size too large: %d bytes!\n", dmalen); 96962306a36Sopenharmony_ci print_hex_dump(KERN_ERR, "RxS ", DUMP_PREFIX_OFFSET, 16, 1, 97062306a36Sopenharmony_ci msg, wil->use_compressed_rx_status ? 97162306a36Sopenharmony_ci sizeof(struct wil_rx_status_compressed) : 97262306a36Sopenharmony_ci sizeof(struct wil_rx_status_extended), false); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci stats->rx_large_frame++; 97562306a36Sopenharmony_ci rxdata->skipping = true; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ciskipping: 97962306a36Sopenharmony_ci /* skipping indicates if a certain SKB should be dropped. 98062306a36Sopenharmony_ci * It is set in case there is an error on the current SKB or in case 98162306a36Sopenharmony_ci * of RX chaining: as long as we manage to merge the SKBs it will 98262306a36Sopenharmony_ci * be false. once we have a bad SKB or we don't manage to merge SKBs 98362306a36Sopenharmony_ci * it will be set to the !EOP value of the current SKB. 98462306a36Sopenharmony_ci * This guarantees that all the following SKBs until EOP will also 98562306a36Sopenharmony_ci * get dropped. 98662306a36Sopenharmony_ci */ 98762306a36Sopenharmony_ci if (unlikely(rxdata->skipping)) { 98862306a36Sopenharmony_ci kfree_skb(skb); 98962306a36Sopenharmony_ci if (rxdata->skb) { 99062306a36Sopenharmony_ci kfree_skb(rxdata->skb); 99162306a36Sopenharmony_ci rxdata->skb = NULL; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci rxdata->skipping = !eop; 99462306a36Sopenharmony_ci goto again; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci skb_trim(skb, dmalen); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci prefetch(skb->data); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci if (!rxdata->skb) { 100262306a36Sopenharmony_ci rxdata->skb = skb; 100362306a36Sopenharmony_ci } else { 100462306a36Sopenharmony_ci if (likely(skb_try_coalesce(rxdata->skb, skb, &headstolen, 100562306a36Sopenharmony_ci &delta))) { 100662306a36Sopenharmony_ci kfree_skb_partial(skb, headstolen); 100762306a36Sopenharmony_ci } else { 100862306a36Sopenharmony_ci wil_err(wil, "failed to merge skbs!\n"); 100962306a36Sopenharmony_ci kfree_skb(skb); 101062306a36Sopenharmony_ci kfree_skb(rxdata->skb); 101162306a36Sopenharmony_ci rxdata->skb = NULL; 101262306a36Sopenharmony_ci rxdata->skipping = !eop; 101362306a36Sopenharmony_ci goto again; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (!eop) 101862306a36Sopenharmony_ci goto again; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* reaching here rxdata->skb always contains a full packet */ 102162306a36Sopenharmony_ci skb = rxdata->skb; 102262306a36Sopenharmony_ci rxdata->skb = NULL; 102362306a36Sopenharmony_ci rxdata->skipping = false; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (stats) { 102662306a36Sopenharmony_ci stats->last_mcs_rx = wil_rx_status_get_mcs(msg); 102762306a36Sopenharmony_ci if (stats->last_mcs_rx < ARRAY_SIZE(stats->rx_per_mcs)) 102862306a36Sopenharmony_ci stats->rx_per_mcs[stats->last_mcs_rx]++; 102962306a36Sopenharmony_ci else if (stats->last_mcs_rx == WIL_EXTENDED_MCS_26) 103062306a36Sopenharmony_ci stats->rx_per_mcs[WIL_BASE_MCS_FOR_EXTENDED_26]++; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci stats->last_cb_mode_rx = wil_rx_status_get_cb_mode(msg); 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (!wil->use_rx_hw_reordering && !wil->use_compressed_rx_status && 103662306a36Sopenharmony_ci wil_check_bar(wil, msg, cid, skb, stats) == -EAGAIN) { 103762306a36Sopenharmony_ci kfree_skb(skb); 103862306a36Sopenharmony_ci goto again; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* Compensate for the HW data alignment according to the status 104262306a36Sopenharmony_ci * message 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_ci data_offset = wil_rx_status_get_data_offset(msg); 104562306a36Sopenharmony_ci if (data_offset == 0xFF || 104662306a36Sopenharmony_ci data_offset > WIL_EDMA_MAX_DATA_OFFSET) { 104762306a36Sopenharmony_ci wil_err(wil, "Unexpected data offset %d\n", data_offset); 104862306a36Sopenharmony_ci kfree_skb(skb); 104962306a36Sopenharmony_ci goto again; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci skb_pull(skb, data_offset); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, 105562306a36Sopenharmony_ci skb->data, skb_headlen(skb), false); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Has to be done after dma_unmap_single as skb->cb is also 105862306a36Sopenharmony_ci * used for holding the pa 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_ci s = wil_skb_rxstatus(skb); 106162306a36Sopenharmony_ci memcpy(s, msg, sring->elem_size); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return skb; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_civoid wil_rx_handle_edma(struct wil6210_priv *wil, int *quota) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct net_device *ndev; 106962306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 107062306a36Sopenharmony_ci struct wil_status_ring *sring; 107162306a36Sopenharmony_ci struct sk_buff *skb; 107262306a36Sopenharmony_ci int i; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (unlikely(!ring->va)) { 107562306a36Sopenharmony_ci wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); 107662306a36Sopenharmony_ci return; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci wil_dbg_txrx(wil, "rx_handle\n"); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci for (i = 0; i < wil->num_rx_status_rings; i++) { 108162306a36Sopenharmony_ci sring = &wil->srings[i]; 108262306a36Sopenharmony_ci if (unlikely(!sring->va)) { 108362306a36Sopenharmony_ci wil_err(wil, 108462306a36Sopenharmony_ci "Rx IRQ while Rx status ring %d not yet initialized\n", 108562306a36Sopenharmony_ci i); 108662306a36Sopenharmony_ci continue; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci while ((*quota > 0) && 109062306a36Sopenharmony_ci (NULL != (skb = 109162306a36Sopenharmony_ci wil_sring_reap_rx_edma(wil, sring)))) { 109262306a36Sopenharmony_ci (*quota)--; 109362306a36Sopenharmony_ci if (wil->use_rx_hw_reordering) { 109462306a36Sopenharmony_ci void *msg = wil_skb_rxstatus(skb); 109562306a36Sopenharmony_ci int mid = wil_rx_status_get_mid(msg); 109662306a36Sopenharmony_ci struct wil6210_vif *vif = wil->vifs[mid]; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (unlikely(!vif)) { 109962306a36Sopenharmony_ci wil_dbg_txrx(wil, 110062306a36Sopenharmony_ci "RX desc invalid mid %d", 110162306a36Sopenharmony_ci mid); 110262306a36Sopenharmony_ci kfree_skb(skb); 110362306a36Sopenharmony_ci continue; 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci ndev = vif_to_ndev(vif); 110662306a36Sopenharmony_ci wil_netif_rx_any(skb, ndev); 110762306a36Sopenharmony_ci } else { 110862306a36Sopenharmony_ci wil_rx_reorder(wil, skb); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci wil_rx_refill_edma(wil); 111662306a36Sopenharmony_ci} 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int wil_tx_desc_map_edma(union wil_tx_desc *desc, 111962306a36Sopenharmony_ci dma_addr_t pa, 112062306a36Sopenharmony_ci u32 len, 112162306a36Sopenharmony_ci int ring_index) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct wil_tx_enhanced_desc *d = 112462306a36Sopenharmony_ci (struct wil_tx_enhanced_desc *)&desc->enhanced; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci memset(d, 0, sizeof(struct wil_tx_enhanced_desc)); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ 113162306a36Sopenharmony_ci d->dma.length = cpu_to_le16((u16)len); 113262306a36Sopenharmony_ci d->mac.d[0] = (ring_index << WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS); 113362306a36Sopenharmony_ci /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi; 113462306a36Sopenharmony_ci * 3 - eth mode 113562306a36Sopenharmony_ci */ 113662306a36Sopenharmony_ci d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | 113762306a36Sopenharmony_ci (0x3 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci return 0; 114062306a36Sopenharmony_ci} 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_cistatic inline void 114362306a36Sopenharmony_ciwil_get_next_tx_status_msg(struct wil_status_ring *sring, u8 *dr_bit, 114462306a36Sopenharmony_ci struct wil_ring_tx_status *msg) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci struct wil_ring_tx_status *_msg = (struct wil_ring_tx_status *) 114762306a36Sopenharmony_ci (sring->va + (sring->elem_size * sring->swhead)); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci *dr_bit = _msg->desc_ready >> TX_STATUS_DESC_READY_POS; 115062306a36Sopenharmony_ci /* make sure dr_bit is read before the rest of status msg */ 115162306a36Sopenharmony_ci rmb(); 115262306a36Sopenharmony_ci *msg = *_msg; 115362306a36Sopenharmony_ci} 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci/* Clean up transmitted skb's from the Tx descriptor RING. 115662306a36Sopenharmony_ci * Return number of descriptors cleared. 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ciint wil_tx_sring_handler(struct wil6210_priv *wil, 115962306a36Sopenharmony_ci struct wil_status_ring *sring) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct net_device *ndev; 116262306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 116362306a36Sopenharmony_ci struct wil_ring *ring = NULL; 116462306a36Sopenharmony_ci struct wil_ring_tx_data *txdata; 116562306a36Sopenharmony_ci /* Total number of completed descriptors in all descriptor rings */ 116662306a36Sopenharmony_ci int desc_cnt = 0; 116762306a36Sopenharmony_ci int cid; 116862306a36Sopenharmony_ci struct wil_net_stats *stats; 116962306a36Sopenharmony_ci struct wil_tx_enhanced_desc *_d; 117062306a36Sopenharmony_ci unsigned int ring_id; 117162306a36Sopenharmony_ci unsigned int num_descs, num_statuses = 0; 117262306a36Sopenharmony_ci int i; 117362306a36Sopenharmony_ci u8 dr_bit; /* Descriptor Ready bit */ 117462306a36Sopenharmony_ci struct wil_ring_tx_status msg; 117562306a36Sopenharmony_ci struct wil6210_vif *vif; 117662306a36Sopenharmony_ci int used_before_complete; 117762306a36Sopenharmony_ci int used_new; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci wil_get_next_tx_status_msg(sring, &dr_bit, &msg); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* Process completion messages while DR bit has the expected polarity */ 118262306a36Sopenharmony_ci while (dr_bit == sring->desc_rdy_pol) { 118362306a36Sopenharmony_ci num_descs = msg.num_descriptors; 118462306a36Sopenharmony_ci if (!num_descs) { 118562306a36Sopenharmony_ci wil_err(wil, "invalid num_descs 0\n"); 118662306a36Sopenharmony_ci goto again; 118762306a36Sopenharmony_ci } 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* Find the corresponding descriptor ring */ 119062306a36Sopenharmony_ci ring_id = msg.ring_id; 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci if (unlikely(ring_id >= WIL6210_MAX_TX_RINGS)) { 119362306a36Sopenharmony_ci wil_err(wil, "invalid ring id %d\n", ring_id); 119462306a36Sopenharmony_ci goto again; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci ring = &wil->ring_tx[ring_id]; 119762306a36Sopenharmony_ci if (unlikely(!ring->va)) { 119862306a36Sopenharmony_ci wil_err(wil, "Tx irq[%d]: ring not initialized\n", 119962306a36Sopenharmony_ci ring_id); 120062306a36Sopenharmony_ci goto again; 120162306a36Sopenharmony_ci } 120262306a36Sopenharmony_ci txdata = &wil->ring_tx_data[ring_id]; 120362306a36Sopenharmony_ci if (unlikely(!txdata->enabled)) { 120462306a36Sopenharmony_ci wil_info(wil, "Tx irq[%d]: ring disabled\n", ring_id); 120562306a36Sopenharmony_ci goto again; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci vif = wil->vifs[txdata->mid]; 120862306a36Sopenharmony_ci if (unlikely(!vif)) { 120962306a36Sopenharmony_ci wil_dbg_txrx(wil, "invalid MID %d for ring %d\n", 121062306a36Sopenharmony_ci txdata->mid, ring_id); 121162306a36Sopenharmony_ci goto again; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci ndev = vif_to_ndev(vif); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci cid = wil->ring2cid_tid[ring_id][0]; 121762306a36Sopenharmony_ci stats = (cid < wil->max_assoc_sta) ? &wil->sta[cid].stats : 121862306a36Sopenharmony_ci NULL; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci wil_dbg_txrx(wil, 122162306a36Sopenharmony_ci "tx_status: completed desc_ring (%d), num_descs (%d)\n", 122262306a36Sopenharmony_ci ring_id, num_descs); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci used_before_complete = wil_ring_used_tx(ring); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci for (i = 0 ; i < num_descs; ++i) { 122762306a36Sopenharmony_ci struct wil_ctx *ctx = &ring->ctx[ring->swtail]; 122862306a36Sopenharmony_ci struct wil_tx_enhanced_desc dd, *d = ⅆ 122962306a36Sopenharmony_ci u16 dmalen; 123062306a36Sopenharmony_ci struct sk_buff *skb = ctx->skb; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci _d = (struct wil_tx_enhanced_desc *) 123362306a36Sopenharmony_ci &ring->va[ring->swtail].tx.enhanced; 123462306a36Sopenharmony_ci *d = *_d; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci dmalen = le16_to_cpu(d->dma.length); 123762306a36Sopenharmony_ci trace_wil6210_tx_status(&msg, ring->swtail, dmalen); 123862306a36Sopenharmony_ci wil_dbg_txrx(wil, 123962306a36Sopenharmony_ci "TxC[%2d][%3d] : %d bytes, status 0x%02x\n", 124062306a36Sopenharmony_ci ring_id, ring->swtail, dmalen, 124162306a36Sopenharmony_ci msg.status); 124262306a36Sopenharmony_ci wil_hex_dump_txrx("TxS ", DUMP_PREFIX_NONE, 32, 4, 124362306a36Sopenharmony_ci (const void *)&msg, sizeof(msg), 124462306a36Sopenharmony_ci false); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci wil_tx_desc_unmap_edma(dev, 124762306a36Sopenharmony_ci (union wil_tx_desc *)d, 124862306a36Sopenharmony_ci ctx); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci if (skb) { 125162306a36Sopenharmony_ci if (likely(msg.status == 0)) { 125262306a36Sopenharmony_ci ndev->stats.tx_packets++; 125362306a36Sopenharmony_ci ndev->stats.tx_bytes += skb->len; 125462306a36Sopenharmony_ci if (stats) { 125562306a36Sopenharmony_ci stats->tx_packets++; 125662306a36Sopenharmony_ci stats->tx_bytes += skb->len; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci wil_tx_latency_calc(wil, skb, 125962306a36Sopenharmony_ci &wil->sta[cid]); 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci } else { 126262306a36Sopenharmony_ci ndev->stats.tx_errors++; 126362306a36Sopenharmony_ci if (stats) 126462306a36Sopenharmony_ci stats->tx_errors++; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (skb->protocol == cpu_to_be16(ETH_P_PAE)) 126862306a36Sopenharmony_ci wil_tx_complete_handle_eapol(vif, skb); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci wil_consume_skb(skb, msg.status == 0); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 127362306a36Sopenharmony_ci /* Make sure the ctx is zeroed before updating the tail 127462306a36Sopenharmony_ci * to prevent a case where wil_tx_ring will see 127562306a36Sopenharmony_ci * this descriptor as used and handle it before ctx zero 127662306a36Sopenharmony_ci * is completed. 127762306a36Sopenharmony_ci */ 127862306a36Sopenharmony_ci wmb(); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci ring->swtail = wil_ring_next_tail(ring); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci desc_cnt++; 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci /* performance monitoring */ 128662306a36Sopenharmony_ci used_new = wil_ring_used_tx(ring); 128762306a36Sopenharmony_ci if (wil_val_in_range(wil->ring_idle_trsh, 128862306a36Sopenharmony_ci used_new, used_before_complete)) { 128962306a36Sopenharmony_ci wil_dbg_txrx(wil, "Ring[%2d] idle %d -> %d\n", 129062306a36Sopenharmony_ci ring_id, used_before_complete, used_new); 129162306a36Sopenharmony_ci txdata->last_idle = get_cycles(); 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ciagain: 129562306a36Sopenharmony_ci num_statuses++; 129662306a36Sopenharmony_ci if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL == 0) 129762306a36Sopenharmony_ci /* update HW tail to allow HW to push new statuses */ 129862306a36Sopenharmony_ci wil_w(wil, sring->hwtail, sring->swhead); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci wil_sring_advance_swhead(sring); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci wil_get_next_tx_status_msg(sring, &dr_bit, &msg); 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* shall we wake net queues? */ 130662306a36Sopenharmony_ci if (desc_cnt) 130762306a36Sopenharmony_ci wil_update_net_queues(wil, vif, NULL, false); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (num_statuses % WIL_EDMA_TX_SRING_UPDATE_HW_TAIL != 0) 131062306a36Sopenharmony_ci /* Update the HW tail ptr (RD ptr) */ 131162306a36Sopenharmony_ci wil_w(wil, sring->hwtail, (sring->swhead - 1) % sring->size); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci return desc_cnt; 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci/* Sets the descriptor @d up for csum and/or TSO offloading. The corresponding 131762306a36Sopenharmony_ci * @skb is used to obtain the protocol and headers length. 131862306a36Sopenharmony_ci * @tso_desc_type is a descriptor type for TSO: 0 - a header, 1 - first data, 131962306a36Sopenharmony_ci * 2 - middle, 3 - last descriptor. 132062306a36Sopenharmony_ci */ 132162306a36Sopenharmony_cistatic void wil_tx_desc_offload_setup_tso_edma(struct wil_tx_enhanced_desc *d, 132262306a36Sopenharmony_ci int tso_desc_type, bool is_ipv4, 132362306a36Sopenharmony_ci int tcp_hdr_len, 132462306a36Sopenharmony_ci int skb_net_hdr_len, 132562306a36Sopenharmony_ci int mss) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci /* Number of descriptors */ 132862306a36Sopenharmony_ci d->mac.d[2] |= 1; 132962306a36Sopenharmony_ci /* Maximum Segment Size */ 133062306a36Sopenharmony_ci d->mac.tso_mss |= cpu_to_le16(mss >> 2); 133162306a36Sopenharmony_ci /* L4 header len: TCP header length */ 133262306a36Sopenharmony_ci d->dma.l4_hdr_len |= tcp_hdr_len & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK; 133362306a36Sopenharmony_ci /* EOP, TSO desc type, Segmentation enable, 133462306a36Sopenharmony_ci * Insert IPv4 and TCP / UDP Checksum 133562306a36Sopenharmony_ci */ 133662306a36Sopenharmony_ci d->dma.cmd |= BIT(WIL_EDMA_DESC_TX_CFG_EOP_POS) | 133762306a36Sopenharmony_ci tso_desc_type << WIL_EDMA_DESC_TX_CFG_TSO_DESC_TYPE_POS | 133862306a36Sopenharmony_ci BIT(WIL_EDMA_DESC_TX_CFG_SEG_EN_POS) | 133962306a36Sopenharmony_ci BIT(WIL_EDMA_DESC_TX_CFG_INSERT_IP_CHKSUM_POS) | 134062306a36Sopenharmony_ci BIT(WIL_EDMA_DESC_TX_CFG_INSERT_TCP_CHKSUM_POS); 134162306a36Sopenharmony_ci /* Calculate pseudo-header */ 134262306a36Sopenharmony_ci d->dma.w1 |= BIT(WIL_EDMA_DESC_TX_CFG_PSEUDO_HEADER_CALC_EN_POS) | 134362306a36Sopenharmony_ci BIT(WIL_EDMA_DESC_TX_CFG_L4_TYPE_POS); 134462306a36Sopenharmony_ci /* IP Header Length */ 134562306a36Sopenharmony_ci d->dma.ip_length |= skb_net_hdr_len; 134662306a36Sopenharmony_ci /* MAC header length and IP address family*/ 134762306a36Sopenharmony_ci d->dma.b11 |= ETH_HLEN | 134862306a36Sopenharmony_ci is_ipv4 << DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic int wil_tx_tso_gen_desc(struct wil6210_priv *wil, void *buff_addr, 135262306a36Sopenharmony_ci int len, uint i, int tso_desc_type, 135362306a36Sopenharmony_ci skb_frag_t *frag, struct wil_ring *ring, 135462306a36Sopenharmony_ci struct sk_buff *skb, bool is_ipv4, 135562306a36Sopenharmony_ci int tcp_hdr_len, int skb_net_hdr_len, 135662306a36Sopenharmony_ci int mss, int *descs_used) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 135962306a36Sopenharmony_ci struct wil_tx_enhanced_desc *_desc = (struct wil_tx_enhanced_desc *) 136062306a36Sopenharmony_ci &ring->va[i].tx.enhanced; 136162306a36Sopenharmony_ci struct wil_tx_enhanced_desc desc_mem, *d = &desc_mem; 136262306a36Sopenharmony_ci int ring_index = ring - wil->ring_tx; 136362306a36Sopenharmony_ci dma_addr_t pa; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci if (len == 0) 136662306a36Sopenharmony_ci return 0; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci if (!frag) { 136962306a36Sopenharmony_ci pa = dma_map_single(dev, buff_addr, len, DMA_TO_DEVICE); 137062306a36Sopenharmony_ci ring->ctx[i].mapped_as = wil_mapped_as_single; 137162306a36Sopenharmony_ci } else { 137262306a36Sopenharmony_ci pa = skb_frag_dma_map(dev, frag, 0, len, DMA_TO_DEVICE); 137362306a36Sopenharmony_ci ring->ctx[i].mapped_as = wil_mapped_as_page; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, pa))) { 137662306a36Sopenharmony_ci wil_err(wil, "TSO: Skb DMA map error\n"); 137762306a36Sopenharmony_ci return -EINVAL; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map((union wil_tx_desc *)d, pa, 138162306a36Sopenharmony_ci len, ring_index); 138262306a36Sopenharmony_ci wil_tx_desc_offload_setup_tso_edma(d, tso_desc_type, is_ipv4, 138362306a36Sopenharmony_ci tcp_hdr_len, 138462306a36Sopenharmony_ci skb_net_hdr_len, mss); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci /* hold reference to skb 138762306a36Sopenharmony_ci * to prevent skb release before accounting 138862306a36Sopenharmony_ci * in case of immediate "tx done" 138962306a36Sopenharmony_ci */ 139062306a36Sopenharmony_ci if (tso_desc_type == wil_tso_type_lst) 139162306a36Sopenharmony_ci ring->ctx[i].skb = skb_get(skb); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, 139462306a36Sopenharmony_ci (const void *)d, sizeof(*d), false); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci *_desc = *d; 139762306a36Sopenharmony_ci (*descs_used)++; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci return 0; 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic int __wil_tx_ring_tso_edma(struct wil6210_priv *wil, 140362306a36Sopenharmony_ci struct wil6210_vif *vif, 140462306a36Sopenharmony_ci struct wil_ring *ring, 140562306a36Sopenharmony_ci struct sk_buff *skb) 140662306a36Sopenharmony_ci{ 140762306a36Sopenharmony_ci int ring_index = ring - wil->ring_tx; 140862306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_index]; 140962306a36Sopenharmony_ci int nr_frags = skb_shinfo(skb)->nr_frags; 141062306a36Sopenharmony_ci int min_desc_required = nr_frags + 2; /* Headers, Head, Fragments */ 141162306a36Sopenharmony_ci int used, avail = wil_ring_avail_tx(ring); 141262306a36Sopenharmony_ci int f, hdrlen, headlen; 141362306a36Sopenharmony_ci int gso_type; 141462306a36Sopenharmony_ci bool is_ipv4; 141562306a36Sopenharmony_ci u32 swhead = ring->swhead; 141662306a36Sopenharmony_ci int descs_used = 0; /* total number of used descriptors */ 141762306a36Sopenharmony_ci int rc = -EINVAL; 141862306a36Sopenharmony_ci int tcp_hdr_len; 141962306a36Sopenharmony_ci int skb_net_hdr_len; 142062306a36Sopenharmony_ci int mss = skb_shinfo(skb)->gso_size; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci wil_dbg_txrx(wil, "tx_ring_tso: %d bytes to ring %d\n", skb->len, 142362306a36Sopenharmony_ci ring_index); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (unlikely(!txdata->enabled)) 142662306a36Sopenharmony_ci return -EINVAL; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci if (unlikely(avail < min_desc_required)) { 142962306a36Sopenharmony_ci wil_err_ratelimited(wil, 143062306a36Sopenharmony_ci "TSO: Tx ring[%2d] full. No space for %d fragments\n", 143162306a36Sopenharmony_ci ring_index, min_desc_required); 143262306a36Sopenharmony_ci return -ENOMEM; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci gso_type = skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV6 | SKB_GSO_TCPV4); 143662306a36Sopenharmony_ci switch (gso_type) { 143762306a36Sopenharmony_ci case SKB_GSO_TCPV4: 143862306a36Sopenharmony_ci is_ipv4 = true; 143962306a36Sopenharmony_ci break; 144062306a36Sopenharmony_ci case SKB_GSO_TCPV6: 144162306a36Sopenharmony_ci is_ipv4 = false; 144262306a36Sopenharmony_ci break; 144362306a36Sopenharmony_ci default: 144462306a36Sopenharmony_ci return -EINVAL; 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL) 144862306a36Sopenharmony_ci return -EINVAL; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci /* tcp header length and skb network header length are fixed for all 145162306a36Sopenharmony_ci * packet's descriptors - read them once here 145262306a36Sopenharmony_ci */ 145362306a36Sopenharmony_ci tcp_hdr_len = tcp_hdrlen(skb); 145462306a36Sopenharmony_ci skb_net_hdr_len = skb_network_header_len(skb); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* First descriptor must contain the header only 145762306a36Sopenharmony_ci * Header Length = MAC header len + IP header len + TCP header len 145862306a36Sopenharmony_ci */ 145962306a36Sopenharmony_ci hdrlen = ETH_HLEN + tcp_hdr_len + skb_net_hdr_len; 146062306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: process header descriptor, hdrlen %u\n", 146162306a36Sopenharmony_ci hdrlen); 146262306a36Sopenharmony_ci rc = wil_tx_tso_gen_desc(wil, skb->data, hdrlen, swhead, 146362306a36Sopenharmony_ci wil_tso_type_hdr, NULL, ring, skb, 146462306a36Sopenharmony_ci is_ipv4, tcp_hdr_len, skb_net_hdr_len, 146562306a36Sopenharmony_ci mss, &descs_used); 146662306a36Sopenharmony_ci if (rc) 146762306a36Sopenharmony_ci return -EINVAL; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci /* Second descriptor contains the head */ 147062306a36Sopenharmony_ci headlen = skb_headlen(skb) - hdrlen; 147162306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: process skb head, headlen %u\n", headlen); 147262306a36Sopenharmony_ci rc = wil_tx_tso_gen_desc(wil, skb->data + hdrlen, headlen, 147362306a36Sopenharmony_ci (swhead + descs_used) % ring->size, 147462306a36Sopenharmony_ci (nr_frags != 0) ? wil_tso_type_first : 147562306a36Sopenharmony_ci wil_tso_type_lst, NULL, ring, skb, 147662306a36Sopenharmony_ci is_ipv4, tcp_hdr_len, skb_net_hdr_len, 147762306a36Sopenharmony_ci mss, &descs_used); 147862306a36Sopenharmony_ci if (rc) 147962306a36Sopenharmony_ci goto mem_error; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci /* Rest of the descriptors are from the SKB fragments */ 148262306a36Sopenharmony_ci for (f = 0; f < nr_frags; f++) { 148362306a36Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; 148462306a36Sopenharmony_ci int len = skb_frag_size(frag); 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: frag[%d]: len %u, descs_used %d\n", f, 148762306a36Sopenharmony_ci len, descs_used); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci rc = wil_tx_tso_gen_desc(wil, NULL, len, 149062306a36Sopenharmony_ci (swhead + descs_used) % ring->size, 149162306a36Sopenharmony_ci (f != nr_frags - 1) ? 149262306a36Sopenharmony_ci wil_tso_type_mid : wil_tso_type_lst, 149362306a36Sopenharmony_ci frag, ring, skb, is_ipv4, 149462306a36Sopenharmony_ci tcp_hdr_len, skb_net_hdr_len, 149562306a36Sopenharmony_ci mss, &descs_used); 149662306a36Sopenharmony_ci if (rc) 149762306a36Sopenharmony_ci goto mem_error; 149862306a36Sopenharmony_ci } 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* performance monitoring */ 150162306a36Sopenharmony_ci used = wil_ring_used_tx(ring); 150262306a36Sopenharmony_ci if (wil_val_in_range(wil->ring_idle_trsh, 150362306a36Sopenharmony_ci used, used + descs_used)) { 150462306a36Sopenharmony_ci txdata->idle += get_cycles() - txdata->last_idle; 150562306a36Sopenharmony_ci wil_dbg_txrx(wil, "Ring[%2d] not idle %d -> %d\n", 150662306a36Sopenharmony_ci ring_index, used, used + descs_used); 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* advance swhead */ 151062306a36Sopenharmony_ci wil_ring_advance_head(ring, descs_used); 151162306a36Sopenharmony_ci wil_dbg_txrx(wil, "TSO: Tx swhead %d -> %d\n", swhead, ring->swhead); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci /* make sure all writes to descriptors (shared memory) are done before 151462306a36Sopenharmony_ci * committing them to HW 151562306a36Sopenharmony_ci */ 151662306a36Sopenharmony_ci wmb(); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (wil->tx_latency) 151962306a36Sopenharmony_ci *(ktime_t *)&skb->cb = ktime_get(); 152062306a36Sopenharmony_ci else 152162306a36Sopenharmony_ci memset(skb->cb, 0, sizeof(ktime_t)); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci wil_w(wil, ring->hwtail, ring->swhead); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_cimem_error: 152862306a36Sopenharmony_ci while (descs_used > 0) { 152962306a36Sopenharmony_ci struct device *dev = wil_to_dev(wil); 153062306a36Sopenharmony_ci struct wil_ctx *ctx; 153162306a36Sopenharmony_ci int i = (swhead + descs_used - 1) % ring->size; 153262306a36Sopenharmony_ci struct wil_tx_enhanced_desc dd, *d = ⅆ 153362306a36Sopenharmony_ci struct wil_tx_enhanced_desc *_desc = 153462306a36Sopenharmony_ci (struct wil_tx_enhanced_desc *) 153562306a36Sopenharmony_ci &ring->va[i].tx.enhanced; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci *d = *_desc; 153862306a36Sopenharmony_ci ctx = &ring->ctx[i]; 153962306a36Sopenharmony_ci wil_tx_desc_unmap_edma(dev, (union wil_tx_desc *)d, ctx); 154062306a36Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 154162306a36Sopenharmony_ci descs_used--; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci return rc; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, 154762306a36Sopenharmony_ci int size) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct wil6210_priv *wil = vif_to_wil(vif); 155062306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_tx[ring_id]; 155162306a36Sopenharmony_ci int rc; 155262306a36Sopenharmony_ci struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci wil_dbg_misc(wil, "init bcast: ring_id=%d, sring_id=%d\n", 155562306a36Sopenharmony_ci ring_id, wil->tx_sring_idx); 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci lockdep_assert_held(&wil->mutex); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci wil_tx_data_init(txdata); 156062306a36Sopenharmony_ci ring->size = size; 156162306a36Sopenharmony_ci ring->is_rx = false; 156262306a36Sopenharmony_ci rc = wil_ring_alloc_desc_ring(wil, ring); 156362306a36Sopenharmony_ci if (rc) 156462306a36Sopenharmony_ci goto out; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][0] = WIL6210_MAX_CID; /* CID */ 156762306a36Sopenharmony_ci wil->ring2cid_tid[ring_id][1] = 0; /* TID */ 156862306a36Sopenharmony_ci if (!vif->privacy) 156962306a36Sopenharmony_ci txdata->dot1x_open = true; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci rc = wil_wmi_bcast_desc_ring_add(vif, ring_id); 157262306a36Sopenharmony_ci if (rc) 157362306a36Sopenharmony_ci goto out_free; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci return 0; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci out_free: 157862306a36Sopenharmony_ci spin_lock_bh(&txdata->lock); 157962306a36Sopenharmony_ci txdata->enabled = 0; 158062306a36Sopenharmony_ci txdata->dot1x_open = false; 158162306a36Sopenharmony_ci spin_unlock_bh(&txdata->lock); 158262306a36Sopenharmony_ci wil_ring_free_edma(wil, ring); 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ciout: 158562306a36Sopenharmony_ci return rc; 158662306a36Sopenharmony_ci} 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cistatic void wil_tx_fini_edma(struct wil6210_priv *wil) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx]; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci wil_dbg_misc(wil, "free TX sring\n"); 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci wil_sring_free(wil, sring); 159562306a36Sopenharmony_ci} 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_cistatic void wil_rx_data_free(struct wil_status_ring *sring) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci if (!sring) 160062306a36Sopenharmony_ci return; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci kfree_skb(sring->rx_data.skb); 160362306a36Sopenharmony_ci sring->rx_data.skb = NULL; 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic void wil_rx_fini_edma(struct wil6210_priv *wil) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct wil_ring *ring = &wil->ring_rx; 160962306a36Sopenharmony_ci int i; 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci wil_dbg_misc(wil, "rx_fini_edma\n"); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci wil_ring_free_edma(wil, ring); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci for (i = 0; i < wil->num_rx_status_rings; i++) { 161662306a36Sopenharmony_ci wil_rx_data_free(&wil->srings[i]); 161762306a36Sopenharmony_ci wil_sring_free(wil, &wil->srings[i]); 161862306a36Sopenharmony_ci } 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci wil_free_rx_buff_arr(wil); 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_civoid wil_init_txrx_ops_edma(struct wil6210_priv *wil) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci wil->txrx_ops.configure_interrupt_moderation = 162662306a36Sopenharmony_ci wil_configure_interrupt_moderation_edma; 162762306a36Sopenharmony_ci /* TX ops */ 162862306a36Sopenharmony_ci wil->txrx_ops.ring_init_tx = wil_ring_init_tx_edma; 162962306a36Sopenharmony_ci wil->txrx_ops.ring_fini_tx = wil_ring_free_edma; 163062306a36Sopenharmony_ci wil->txrx_ops.ring_init_bcast = wil_ring_init_bcast_edma; 163162306a36Sopenharmony_ci wil->txrx_ops.tx_init = wil_tx_init_edma; 163262306a36Sopenharmony_ci wil->txrx_ops.tx_fini = wil_tx_fini_edma; 163362306a36Sopenharmony_ci wil->txrx_ops.tx_desc_map = wil_tx_desc_map_edma; 163462306a36Sopenharmony_ci wil->txrx_ops.tx_desc_unmap = wil_tx_desc_unmap_edma; 163562306a36Sopenharmony_ci wil->txrx_ops.tx_ring_tso = __wil_tx_ring_tso_edma; 163662306a36Sopenharmony_ci wil->txrx_ops.tx_ring_modify = wil_tx_ring_modify_edma; 163762306a36Sopenharmony_ci /* RX ops */ 163862306a36Sopenharmony_ci wil->txrx_ops.rx_init = wil_rx_init_edma; 163962306a36Sopenharmony_ci wil->txrx_ops.wmi_addba_rx_resp = wmi_addba_rx_resp_edma; 164062306a36Sopenharmony_ci wil->txrx_ops.get_reorder_params = wil_get_reorder_params_edma; 164162306a36Sopenharmony_ci wil->txrx_ops.get_netif_rx_params = wil_get_netif_rx_params_edma; 164262306a36Sopenharmony_ci wil->txrx_ops.rx_crypto_check = wil_rx_crypto_check_edma; 164362306a36Sopenharmony_ci wil->txrx_ops.rx_error_check = wil_rx_error_check_edma; 164462306a36Sopenharmony_ci wil->txrx_ops.is_rx_idle = wil_is_rx_idle_edma; 164562306a36Sopenharmony_ci wil->txrx_ops.rx_fini = wil_rx_fini_edma; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 1648