diff options
-rw-r--r-- | drivers/net/wireless/ath/wil6210/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/debugfs.c | 59 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/ethtool.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/interrupt.c | 21 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 81 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/pcie_bus.c | 47 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 90 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx_edma.c | 719 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx_edma.h | 54 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil6210.h | 62 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.c | 316 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.h | 166 |
13 files changed, 1513 insertions, 113 deletions
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 398edd2a7f2b..d3d61ae459e2 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -9,6 +9,7 @@ wil6210-$(CONFIG_WIL6210_DEBUGFS) += debugfs.o wil6210-y += wmi.o wil6210-y += interrupt.o wil6210-y += txrx.o +wil6210-y += txrx_edma.o wil6210-y += debug.o wil6210-y += rx_reorder.o wil6210-y += fw.o diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 55e03e9b49eb..212baf46393a 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1761,6 +1761,60 @@ static const struct file_operations fops_suspend_stats = { .open = simple_open, }; +/*---------compressed_rx_status---------*/ +static ssize_t wil_compressed_rx_status_write(struct file *file, + const char __user *buf, + size_t len, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct wil6210_priv *wil = s->private; + int compressed_rx_status; + int rc; + + rc = kstrtoint_from_user(buf, len, 0, &compressed_rx_status); + if (rc) { + wil_err(wil, "Invalid argument\n"); + return rc; + } + + if (wil_has_active_ifaces(wil, true, false)) { + wil_err(wil, "cannot change edma config after iface is up\n"); + return -EPERM; + } + + wil_info(wil, "%sable compressed_rx_status\n", + compressed_rx_status ? "En" : "Dis"); + + wil->use_compressed_rx_status = compressed_rx_status; + + return len; +} + +static int +wil_compressed_rx_status_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + + seq_printf(s, "%d\n", wil->use_compressed_rx_status); + + return 0; +} + +static int +wil_compressed_rx_status_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_compressed_rx_status_show, + inode->i_private); +} + +static const struct file_operations fops_compressed_rx_status = { + .open = wil_compressed_rx_status_seq_open, + .release = single_release, + .read = seq_read, + .write = wil_compressed_rx_status_write, + .llseek = seq_lseek, +}; + /*----------------*/ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, struct dentry *dbg) @@ -1814,6 +1868,7 @@ static const struct { {"fw_capabilities", 0444, &fops_fw_capabilities}, {"fw_version", 0444, &fops_fw_version}, {"suspend_stats", 0644, &fops_suspend_stats}, + {"compressed_rx_status", 0644, &fops_compressed_rx_status}, }; static void wil6210_debugfs_init_files(struct wil6210_priv *wil, @@ -1860,6 +1915,10 @@ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(abft_len, 0644, doff_u8), WIL_FIELD(wakeup_trigger, 0644, doff_u8), WIL_FIELD(ring_idle_trsh, 0644, doff_u32), + WIL_FIELD(num_rx_status_rings, 0644, doff_u8), + WIL_FIELD(rx_status_ring_order, 0644, doff_u32), + WIL_FIELD(tx_status_ring_order, 0644, doff_u32), + WIL_FIELD(rx_buff_id_count, 0644, doff_u32), {}, }; diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index e7ff41e623d2..a04c87ffd37b 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -101,7 +101,7 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, if (ret < 0) return ret; - wil_configure_interrupt_moderation(wil); + wil->txrx_ops.configure_interrupt_moderation(wil); wil_pm_runtime_put(wil); diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 84e9840c1752..311d48233165 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -186,6 +186,27 @@ void wil_unmask_irq(struct wil6210_priv *wil) wil6210_unmask_irq_misc(wil, true); } +void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) +{ + u32 moderation; + + wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); + + wil_s(wil, RGF_INT_GEN_TIME_UNIT_LIMIT, WIL_EDMA_TIME_UNIT_CLK_CYCLES); + + /* Update RX and TX moderation */ + moderation = wil->rx_max_burst_duration | + (WIL_EDMA_AGG_WATERMARK << WIL_EDMA_AGG_WATERMARK_POS); + wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_0, moderation); + wil_w(wil, RGF_INT_CTRL_INT_GEN_CFG_1, moderation); + + /* Treat special events as regular + * (set bit 0 to 0x1 and clear bits 1-8) + */ + wil_c(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1FE); + wil_s(wil, RGF_INT_COUNT_ON_SPECIAL_EVT, 0x1); +} + void wil_configure_interrupt_moderation(struct wil6210_priv *wil) { struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index bfe628481e22..c820c86918f0 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -21,11 +21,13 @@ #include "wil6210.h" #include "txrx.h" +#include "txrx_edma.h" #include "wmi.h" #include "boot_loader.h" #define WAIT_FOR_HALP_VOTE_MS 100 #define WAIT_FOR_SCAN_ABORT_MS 1000 +#define WIL_DEFAULT_NUM_RX_STATUS_RINGS 1 bool debug_fw; /* = false; */ module_param(debug_fw, bool, 0444); @@ -160,6 +162,37 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } +static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) +{ + struct wil_ring *ring = &wil->ring_tx[id]; + struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; + + lockdep_assert_held(&wil->mutex); + + if (!ring->va) + return; + + wil_dbg_misc(wil, "vring_fini_tx: id=%d\n", id); + + spin_lock_bh(&txdata->lock); + txdata->dot1x_open = false; + txdata->mid = U8_MAX; + txdata->enabled = 0; /* no Tx can be in progress or start anew */ + spin_unlock_bh(&txdata->lock); + /* napi_synchronize waits for completion of the current NAPI but will + * not prevent the next NAPI run. + * Add a memory barrier to guarantee that txdata->enabled is zeroed + * before napi_synchronize so that the next scheduled NAPI will not + * handle this vring + */ + wmb(); + /* make sure NAPI won't touch this vring */ + if (test_bit(wil_status_napi_en, wil->status)) + napi_synchronize(&wil->napi_tx); + + wil->txrx_ops.ring_fini_tx(wil, ring); +} + static void wil_disconnect_cid(struct wil6210_vif *vif, int cid, u16 reason_code, bool from_event) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) @@ -456,15 +489,16 @@ static void wil_fw_error_worker(struct work_struct *work) static int wil_find_free_ring(struct wil6210_priv *wil) { int i; + int min_ring_id = wil_get_min_tx_ring_id(wil); - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { if (!wil->ring_tx[i].va) return i; } return -EINVAL; } -int wil_tx_init(struct wil6210_vif *vif, int cid) +int wil_ring_init_tx(struct wil6210_vif *vif, int cid) { struct wil6210_priv *wil = vif_to_wil(vif); int rc = -EINVAL, ringid; @@ -482,7 +516,8 @@ int wil_tx_init(struct wil6210_vif *vif, int cid) wil_dbg_wmi(wil, "Configure for connection CID %d MID %d ring %d\n", cid, vif->mid, ringid); - rc = wil_vring_init_tx(vif, ringid, 1 << tx_ring_order, cid, 0); + rc = wil->txrx_ops.ring_init_tx(vif, ringid, 1 << tx_ring_order, + cid, 0); if (rc) wil_err(wil, "init TX for CID %d MID %d vring %d failed\n", cid, vif->mid, ringid); @@ -504,7 +539,7 @@ int wil_bcast_init(struct wil6210_vif *vif) return ri; vif->bcast_ring = ri; - rc = wil_vring_init_bcast(vif, ri, 1 << bcast_ring_order); + rc = wil->txrx_ops.ring_init_bcast(vif, ri, 1 << bcast_ring_order); if (rc) vif->bcast_ring = -1; @@ -594,6 +629,22 @@ int wil_priv_init(struct wil6210_priv *wil) wil->reply_mid = U8_MAX; wil->max_vifs = 1; + /* edma configuration can be updated via debugfs before allocation */ + wil->num_rx_status_rings = WIL_DEFAULT_NUM_RX_STATUS_RINGS; + wil->use_compressed_rx_status = true; + wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT; + + /* Rx status ring size should be bigger than the number of RX buffers + * in order to prevent backpressure on the status ring, which may + * cause HW freeze. + */ + wil->rx_status_ring_order = WIL_RX_SRING_SIZE_ORDER_DEFAULT; + /* Number of RX buffer IDs should be bigger than the RX descriptor + * ring size as in HW reorder flow, the HW can consume additional + * buffers before releasing the previous ones. + */ + wil->rx_buff_id_count = WIL_RX_BUFF_ARR_SIZE_DEFAULT; + return 0; out_wmi_wq: @@ -1312,7 +1363,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) rc = wil_target_reset(wil, no_flash); wil6210_clear_irq(wil); wil_enable_irq(wil); - wil_rx_fini(wil); + wil->txrx_ops.rx_fini(wil); + wil->txrx_ops.tx_fini(wil); if (rc) { if (!no_flash) wil_bl_crash_info(wil, true); @@ -1365,7 +1417,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) clear_bit(wil_status_resetting, wil->status); if (load_fw) { - wil_configure_interrupt_moderation(wil); wil_unmask_irq(wil); /* we just started MAC, wait for FW ready */ @@ -1380,6 +1431,8 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) return rc; } + wil->txrx_ops.configure_interrupt_moderation(wil); + rc = wil_restore_vifs(wil); if (rc) { wil_err(wil, "failed to restore vifs, rc %d\n", rc); @@ -1434,8 +1487,12 @@ int __wil_up(struct wil6210_priv *wil) if (rc) return rc; - /* Rx VRING. After MAC and beacon */ - rc = wil_rx_init(wil, 1 << rx_ring_order); + /* Rx RING. After MAC and beacon */ + rc = wil->txrx_ops.rx_init(wil, 1 << rx_ring_order); + if (rc) + return rc; + + rc = wil->txrx_ops.tx_init(wil); if (rc) return rc; @@ -1596,3 +1653,11 @@ void wil_halp_unvote(struct wil6210_priv *wil) mutex_unlock(&wil->halp.lock); } + +void wil_init_txrx_ops(struct wil6210_priv *wil) +{ + if (wil->use_enhanced_dma_hw) + wil_init_txrx_ops_edma(wil); + else + wil_init_txrx_ops_legacy_dma(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 3a7e406d0311..c01c5348b7e0 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -102,6 +102,7 @@ int wil_set_capabilities(struct wil6210_priv *wil) wil->rgf_fw_assert_code_addr = TALYN_RGF_FW_ASSERT_CODE; wil->rgf_ucode_assert_code_addr = TALYN_RGF_UCODE_ASSERT_CODE; set_bit(hw_capa_no_flash, wil->hw_capa); + wil->use_enhanced_dma_hw = true; break; default: wil_err(wil, "Unknown board hardware, chip_id 0x%08x, chip_revision 0x%08x\n", @@ -111,6 +112,8 @@ int wil_set_capabilities(struct wil6210_priv *wil) return -EINVAL; } + wil_init_txrx_ops(wil); + iccm_section = wil_find_fw_mapping("fw_code"); if (!iccm_section) { wil_err(wil, "fw_code section not found in fw_mapping\n"); @@ -266,8 +269,8 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) .fw_recovery = wil_platform_rop_fw_recovery, }; u32 bar_size = pci_resource_len(pdev, 0); - int dma_addr_size[] = {48, 40, 32}; /* keep descending order */ - int i; + int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ + int i, start_idx; /* check HW */ dev_info(&pdev->dev, WIL_NAME @@ -302,24 +305,6 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto if_free; } /* rollback to err_plat */ - - /* device supports >32bit addresses */ - for (i = 0; i < ARRAY_SIZE(dma_addr_size); i++) { - rc = dma_set_mask_and_coherent(dev, - DMA_BIT_MASK(dma_addr_size[i])); - if (rc) { - dev_err(dev, "dma_set_mask_and_coherent(%d) failed: %d\n", - dma_addr_size[i], rc); - continue; - } - dev_info(dev, "using dma mask %d", dma_addr_size[i]); - wil->dma_addr_size = dma_addr_size[i]; - break; - } - - if (wil->dma_addr_size == 0) - goto err_plat; - rc = pci_enable_device(pdev); if (rc && pdev->msi_enabled == 0) { wil_err(wil, @@ -359,6 +344,28 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil_err(wil, "wil_set_capabilities failed, rc %d\n", rc); goto err_iounmap; } + + /* device supports >32bit addresses. + * for legacy DMA start from 48 bit. + */ + start_idx = wil->use_enhanced_dma_hw ? 0 : 1; + + for (i = start_idx; i < ARRAY_SIZE(dma_addr_size); i++) { + rc = dma_set_mask_and_coherent(dev, + DMA_BIT_MASK(dma_addr_size[i])); + if (rc) { + dev_err(dev, "dma_set_mask_and_coherent(%d) failed: %d\n", + dma_addr_size[i], rc); + continue; + } + dev_info(dev, "using dma mask %d", dma_addr_size[i]); + wil->dma_addr_size = dma_addr_size[i]; + break; + } + + if (wil->dma_addr_size == 0) + goto err_iounmap; + wil6210_clear_irq(wil); /* FW should raise IRQ when ready */ diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 55946dec5110..2a359e164e08 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -202,14 +202,13 @@ static void wil_txdesc_unmap(struct device *dev, struct vring_tx_desc *d, } } -static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring, - int tx) +static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring) { struct device *dev = wil_to_dev(wil); size_t sz = vring->size * sizeof(vring->va[0]); lockdep_assert_held(&wil->mutex); - if (tx) { + if (!vring->is_rx) { int vring_index = vring - wil->ring_tx; wil_dbg_misc(wil, "free Tx vring %d [%d] 0x%p:%pad 0x%p\n", @@ -226,7 +225,7 @@ static void wil_vring_free(struct wil6210_priv *wil, struct wil_ring *vring, u16 dmalen; struct wil_ctx *ctx; - if (tx) { + if (!vring->is_rx) { struct vring_tx_desc dd, *d = ⅆ volatile struct vring_tx_desc *_d = &vring->va[vring->swtail].tx.legacy; @@ -843,7 +842,7 @@ static void wil_rx_buf_len_init(struct wil6210_priv *wil) } } -int wil_rx_init(struct wil6210_priv *wil, u16 size) +static int wil_rx_init(struct wil6210_priv *wil, u16 size) { struct wil_ring *vring = &wil->ring_rx; int rc; @@ -858,6 +857,7 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) wil_rx_buf_len_init(wil); vring->size = size; + vring->is_rx = true; rc = wil_vring_alloc(wil, vring); if (rc) return rc; @@ -872,22 +872,22 @@ int wil_rx_init(struct wil6210_priv *wil, u16 size) return 0; err_free: - wil_vring_free(wil, vring, 0); + wil_vring_free(wil, vring); return rc; } -void wil_rx_fini(struct wil6210_priv *wil) +static void wil_rx_fini(struct wil6210_priv *wil) { struct wil_ring *vring = &wil->ring_rx; wil_dbg_misc(wil, "rx_fini\n"); if (vring->va) - wil_vring_free(wil, vring, 0); + wil_vring_free(wil, vring); } -static inline void wil_tx_data_init(struct wil_ring_tx_data *txdata) +void wil_tx_data_init(struct wil_ring_tx_data *txdata) { spin_lock_bh(&txdata->lock); txdata->dot1x_open = 0; @@ -903,8 +903,8 @@ static inline void wil_tx_data_init(struct wil_ring_tx_data *txdata) spin_unlock_bh(&txdata->lock); } -int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, - int cid, int tid) +static int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, + int cid, int tid) { struct wil6210_priv *wil = vif_to_wil(vif); int rc; @@ -948,6 +948,7 @@ int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, } wil_tx_data_init(txdata); + vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -987,7 +988,7 @@ int wil_vring_init_tx(struct wil6210_vif *vif, int id, int size, txdata->dot1x_open = false; txdata->enabled = 0; spin_unlock_bh(&txdata->lock); - wil_vring_free(wil, vring, 1); + wil_vring_free(wil, vring); wil->ring2cid_tid[id][0] = WIL6210_MAX_CID; wil->ring2cid_tid[id][1] = 0; @@ -1032,6 +1033,7 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) } wil_tx_data_init(txdata); + vring->is_rx = false; vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -1069,43 +1071,12 @@ int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size) txdata->enabled = 0; txdata->dot1x_open = false; spin_unlock_bh(&txdata->lock); - wil_vring_free(wil, vring, 1); + wil_vring_free(wil, vring); out: return rc; } -void wil_ring_fini_tx(struct wil6210_priv *wil, int id) -{ - struct wil_ring *vring = &wil->ring_tx[id]; - struct wil_ring_tx_data *txdata = &wil->ring_tx_data[id]; - - lockdep_assert_held(&wil->mutex); - - if (!vring->va) - return; - - wil_dbg_misc(wil, "vring_fini_tx: id=%d\n", id); - - spin_lock_bh(&txdata->lock); - txdata->dot1x_open = false; - txdata->mid = U8_MAX; - txdata->enabled = 0; /* no Tx can be in progress or start anew */ - spin_unlock_bh(&txdata->lock); - /* napi_synchronize waits for completion of the current NAPI but will - * not prevent the next NAPI run. - * Add a memory barrier to guarantee that txdata->enabled is zeroed - * before napi_synchronize so that the next scheduled NAPI will not - * handle this vring - */ - wmb(); - /* make sure NAPI won't touch this vring */ - if (test_bit(wil_status_napi_en, wil->status)) - napi_synchronize(&wil->napi_tx); - - wil_vring_free(wil, vring, 1); -} - static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, struct wil6210_vif *vif, struct sk_buff *skb) @@ -1113,12 +1084,13 @@ static struct wil_ring *wil_find_tx_ucast(struct wil6210_priv *wil, int i; struct ethhdr *eth = (void *)skb->data; int cid = wil_find_cid(wil, vif->mid, eth->h_dest); + int min_ring_id = wil_get_min_tx_ring_id(wil); if (cid < 0) return NULL; /* TODO: fix for multiple TID */ - for (i = 0; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { + for (i = min_ring_id; i < ARRAY_SIZE(wil->ring2cid_tid); i++) { if (!wil->ring_tx_data[i].dot1x_open && skb->protocol != cpu_to_be16(ETH_P_PAE)) continue; @@ -1153,12 +1125,13 @@ static struct wil_ring *wil_find_tx_ring_sta(struct wil6210_priv *wil, int i; u8 cid; struct wil_ring_tx_data *txdata; + int min_ring_id = wil_get_min_tx_ring_id(wil); /* In the STA mode, it is expected to have only 1 VRING * for the AP we connected to. * find 1-st vring eligible for this skb and use it. */ - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { ring = &wil->ring_tx[i]; txdata = &wil->ring_tx_data[i]; if (!ring->va || !txdata->enabled || txdata->mid != vif->mid) @@ -1234,9 +1207,10 @@ static struct wil_ring *wil_find_tx_bcast_2(struct wil6210_priv *wil, struct ethhdr *eth = (void *)skb->data; char *src = eth->h_source; struct wil_ring_tx_data *txdata, *txdata2; + int min_ring_id = wil_get_min_tx_ring_id(wil); /* find 1-st vring eligible for data */ - for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + for (i = min_ring_id; i < WIL6210_MAX_TX_RINGS; i++) { v = &wil->ring_tx[i]; txdata = &wil->ring_tx_data[i]; if (!v->va || !txdata->enabled || txdata->mid != vif->mid) @@ -2201,3 +2175,25 @@ int wil_tx_complete(struct wil6210_vif *vif, int ringid) return done; } + +static inline int wil_tx_init(struct wil6210_priv *wil) +{ + return 0; +} + +static inline void wil_tx_fini(struct wil6210_priv *wil) {} + +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.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; + wil->txrx_ops.tx_init = wil_tx_init; + wil->txrx_ops.tx_fini = wil_tx_fini; + /* RX ops */ + wil->txrx_ops.rx_init = wil_rx_init; + wil->txrx_ops.rx_fini = wil_rx_fini; +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 66217f815726..4e3781d9e7ec 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -570,6 +570,12 @@ static inline int wil_ring_avail_tx(struct wil_ring *ring) return ring->size - wil_ring_used_tx(ring) - 1; } +static inline int wil_get_min_tx_ring_id(struct wil6210_priv *wil) +{ + /* In Enhanced DMA ring 0 is reserved for RX */ + return wil->use_enhanced_dma_hw ? 1 : 0; +} + 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, @@ -578,5 +584,7 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, int size, u16 ssn); void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r); +void wil_tx_data_init(struct wil_ring_tx_data *txdata); +void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil); #endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c new file mode 100644 index 000000000000..9773d400808d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -0,0 +1,719 @@ +/* + * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include <linux/moduleparam.h> +#include <linux/prefetch.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include "wil6210.h" +#include "txrx_edma.h" +#include "txrx.h" + +#define WIL_EDMA_MAX_DATA_OFFSET (2) + +static void wil_tx_desc_unmap_edma(struct device *dev, + struct wil_tx_enhanced_desc *d, + struct wil_ctx *ctx) +{ + dma_addr_t pa = wil_tx_desc_get_addr_edma(&d->dma); + u16 dmalen = le16_to_cpu(d->dma.length); + + switch (ctx->mapped_as) { + case wil_mapped_as_single: + dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE); + break; + case wil_mapped_as_page: + dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE); + break; + default: + break; + } +} + +static int wil_find_free_sring(struct wil6210_priv *wil) +{ + int i; + + for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) { + if (!wil->srings[i].va) + return i; + } + + return -EINVAL; +} + +static void wil_sring_free(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz; + + if (!sring || !sring->va) + return; + + sz = sring->elem_size * sring->size; + + wil_dbg_misc(wil, "status_ring_free, size(bytes)=%zu, 0x%p:%pad\n", + sz, sring->va, &sring->pa); + + dma_free_coherent(dev, sz, (void *)sring->va, sring->pa); + sring->pa = 0; + sring->va = NULL; +} + +static int wil_sring_alloc(struct wil6210_priv *wil, + struct wil_status_ring *sring) +{ + struct device *dev = wil_to_dev(wil); + size_t sz = sring->elem_size * sring->size; + + wil_dbg_misc(wil, "status_ring_alloc: size=%zu\n", sz); + + if (sz == 0) { + wil_err(wil, "Cannot allocate a zero size status ring\n"); + return -EINVAL; + } + + sring->swhead = 0; + + /* Status messages are allocated and initialized to 0. This is necessary + * since DR bit should be initialized to 0. + */ + sring->va = dma_zalloc_coherent(dev, sz, &sring->pa, GFP_KERNEL); + if (!sring->va) + return -ENOMEM; + + wil_dbg_misc(wil, "status_ring[%d] 0x%p:%pad\n", sring->size, sring->va, + &sring->pa); + + return 0; +} + +static int wil_tx_init_edma(struct wil6210_priv *wil) +{ + int ring_id = wil_find_free_sring(wil); + struct wil_status_ring *sring; + int rc; + u16 status_ring_size; + + if (wil->tx_status_ring_order < WIL_SRING_SIZE_ORDER_MIN || + wil->tx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX) + wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT; + + status_ring_size = 1 << wil->tx_status_ring_order; + + wil_dbg_misc(wil, "init TX sring: size=%u, ring_id=%u\n", + status_ring_size, ring_id); + + if (ring_id < 0) + return ring_id; + + /* Allocate Tx status ring. Tx descriptor rings will be + * allocated on WMI connect event + */ + sring = &wil->srings[ring_id]; + + sring->is_rx = false; + sring->size = status_ring_size; + sring->elem_size = sizeof(struct wil_ring_tx_status); + rc = wil_sring_alloc(wil, sring); + if (rc) + return rc; + + rc = wil_wmi_tx_sring_cfg(wil, ring_id); + if (rc) + goto out_free; + + sring->desc_rdy_pol = 1; + wil->tx_sring_idx = ring_id; + + return 0; +out_free: + wil_sring_free(wil, sring); + return rc; +} + +/** + * Allocate one skb for Rx descriptor RING + */ +static int wil_ring_alloc_skb_edma(struct wil6210_priv *wil, + struct wil_ring *ring, u32 i) +{ + struct device *dev = wil_to_dev(wil); + unsigned int sz = wil->rx_buf_len + ETH_HLEN + + WIL_EDMA_MAX_DATA_OFFSET; + dma_addr_t pa; + u16 buff_id; + struct list_head *active = &wil->rx_buff_mgmt.active; + struct list_head *free = &wil->rx_buff_mgmt.free; + struct wil_rx_buff *rx_buff; + struct wil_rx_buff *buff_arr = wil->rx_buff_mgmt.buff_arr; + struct sk_buff *skb; + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *) + &ring->va[i].rx.enhanced; + + if (unlikely(list_empty(free))) { + wil->rx_buff_mgmt.free_list_empty_cnt++; + return -EAGAIN; + } + + skb = dev_alloc_skb(sz); + if (unlikely(!skb)) + return -ENOMEM; + + skb_put(skb, sz); + + pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(dev, pa))) { + kfree_skb(skb); + return -ENOMEM; + } + + /* Get the buffer ID - the index of the rx buffer in the buff_arr */ + rx_buff = list_first_entry(free, struct wil_rx_buff, list); + buff_id = rx_buff->id; + + /* Move a buffer from the free list to the active list */ + list_move(&rx_buff->list, active); + + buff_arr[buff_id].skb = skb; + + wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); + d->dma.length = cpu_to_le16(sz); + d->mac.buff_id = cpu_to_le16(buff_id); + *_d = *d; + + /* Save the physical address in skb->cb for later use in dma_unmap */ + memcpy(skb->cb, &pa, sizeof(pa)); + + return 0; +} + +static int wil_rx_refill_edma(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + u32 next_head; + int rc = 0; + u32 swtail = *ring->edma_rx_swtail.va; + + for (; next_head = wil_ring_next_head(ring), (next_head != swtail); + ring->swhead = next_head) { + rc = wil_ring_alloc_skb_edma(wil, ring, ring->swhead); + if (unlikely(rc)) { + if (rc == -EAGAIN) + wil_dbg_txrx(wil, "No free buffer ID found\n"); + else + wil_err_ratelimited(wil, + "Error %d in refill desc[%d]\n", + rc, ring->swhead); + break; + } + } + + /* make sure all writes to descriptors (shared memory) are done before + * committing them to HW + */ + wmb(); + + wil_w(wil, ring->hwtail, ring->swhead); + + return rc; +} + +static void wil_move_all_rx_buff_to_free_list(struct wil6210_priv *wil, + struct wil_ring *ring) +{ + struct device *dev = wil_to_dev(wil); + u32 next_tail; + u32 swhead = (ring->swhead + 1) % ring->size; + dma_addr_t pa; + u16 dmalen; + + for (; next_tail = wil_ring_next_tail(ring), (next_tail != swhead); + ring->swtail = next_tail) { + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = + (struct wil_rx_enhanced_desc *) + &ring->va[ring->swtail].rx.enhanced; + struct sk_buff *skb; + u16 buff_id; + + *d = *_d; + pa = wil_rx_desc_get_addr_edma(&d->dma); + dmalen = le16_to_cpu(d->dma.length); + dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE); + + /* Extract the SKB from the rx_buff management array */ + buff_id = __le16_to_cpu(d->mac.buff_id); + if (buff_id >= wil->rx_buff_mgmt.size) { + wil_err(wil, "invalid buff_id %d\n", buff_id); + continue; + } + skb = wil->rx_buff_mgmt.buff_arr[buff_id].skb; + wil->rx_buff_mgmt.buff_arr[buff_id].skb = NULL; + if (unlikely(!skb)) + wil_err(wil, "No Rx skb at buff_id %d\n", buff_id); + else + kfree_skb(skb); + + /* Move the buffer from the active to the free list */ + list_move(&wil->rx_buff_mgmt.buff_arr[buff_id].list, + &wil->rx_buff_mgmt.free); + } +} + +static void wil_free_rx_buff_arr(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + + if (!wil->rx_buff_mgmt.buff_arr) + return; + + /* Move all the buffers to the free list in case active list is + * not empty in order to release all SKBs before deleting the array + */ + wil_move_all_rx_buff_to_free_list(wil, ring); + + kfree(wil->rx_buff_mgmt.buff_arr); + wil->rx_buff_mgmt.buff_arr = NULL; +} + +static int wil_init_rx_buff_arr(struct wil6210_priv *wil, + size_t size) +{ + struct wil_rx_buff *buff_arr; + struct list_head *active = &wil->rx_buff_mgmt.active; + struct list_head *free = &wil->rx_buff_mgmt.free; + int i; + + wil->rx_buff_mgmt.buff_arr = kcalloc(size, sizeof(struct wil_rx_buff), + GFP_KERNEL); + if (!wil->rx_buff_mgmt.buff_arr) + return -ENOMEM; + + /* Set list heads */ + INIT_LIST_HEAD(active); + INIT_LIST_HEAD(free); + + /* Linkify the list */ + buff_arr = wil->rx_buff_mgmt.buff_arr; + for (i = 0; i < size; i++) { + list_add(&buff_arr[i].list, free); + buff_arr[i].id = i; + } + + wil->rx_buff_mgmt.size = size; + + return 0; +} + +static int wil_init_rx_sring(struct wil6210_priv *wil, + u16 status_ring_size, + size_t elem_size, + u16 ring_id) +{ + struct wil_status_ring *sring = &wil->srings[ring_id]; + int rc; + + wil_dbg_misc(wil, "init RX sring: size=%u, ring_id=%u\n", sring->size, + ring_id); |