diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 0d1a8dab30ed..d3f4cbbc630c 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -53,3 +53,12 @@ config WIL6210_DEBUGFS option if you are interested in debugging the driver. If unsure, say Y to make it easier to debug problems. + +config WIL6210_IPA + bool "wil6210 IPA offload support" + depends on WIL6210 + depends on ARCH_QCOM + default y + help + Say Y here to enable wil6210 driver support for data path + IPA offload diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 989b947c2e34..2a7683fbe7c0 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -22,6 +22,7 @@ wil6210-y += ethtool.o wil6210-y += wil_crash_dump.o wil6210-y += p2p.o wil6210-y += ftm.o +wil6210-$(CONFIG_WIL6210_IPA) += ipa.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4d8ac6ee0f7b..b612cdfc5a31 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -12,6 +12,7 @@ #include "wmi.h" #include "ftm.h" #include "fw.h" +#include "ipa.h" #define WIL_MAX_ROC_DURATION_MS 5000 #define WIL_BRD_SUFFIX_CN "CN" @@ -139,6 +140,8 @@ enum wil_nl_60g_evt_type { enum wil_nl_60g_debug_cmd { NL_60G_DBG_FORCE_WMI_SEND, + NL_60G_GEN_RADAR_ALLOC_BUFFER, + NL_60G_GEN_FW_RESET, }; struct wil_nl_60g_send_receive_wmi { @@ -2159,7 +2162,22 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, mutex_lock(&wil->mutex); if (!wil_has_other_active_ifaces(wil, ndev, true, false)) { + if (wil->ipa_handle) { + wil_ipa_uninit(wil->ipa_handle); + wil->ipa_handle = NULL; + } + __wil_down(wil); + + if (wil_ipa_offload()) { + wil->ipa_handle = wil_ipa_init(wil); + if (!wil->ipa_handle) { + wil_err(wil, "wil_ipa_init failed\n"); + rc = -ENOMEM; + goto out; + } + } + rc = __wil_up(wil); if (rc) goto out; @@ -2182,6 +2200,12 @@ static int _wil_cfg80211_start_ap(struct wiphy *wiphy, memcpy(vif->ssid, ssid, ssid_len); vif->ssid_len = ssid_len; + if (wil->ipa_handle) { + rc = wil_ipa_start_ap(wil->ipa_handle); + if (rc) + goto out; + } + netif_carrier_on(ndev); if (!wil_has_other_active_ifaces(wil, ndev, false, true)) wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS); @@ -2204,6 +2228,11 @@ err_pcp_start: if (!wil_has_other_active_ifaces(wil, ndev, false, true)) wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS); out: + if (rc && wil->ipa_handle) { + wil_ipa_uninit(wil->ipa_handle); + wil->ipa_handle = NULL; + } + mutex_unlock(&wil->mutex); return rc; } @@ -2394,6 +2423,10 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, memset(vif->gtk, 0, WMI_MAX_KEY_LEN); vif->gtk_len = 0; + if (wil->ipa_handle) { + wil_ipa_uninit(wil->ipa_handle); + wil->ipa_handle = NULL; + } if (last) __wil_down(wil); else @@ -3707,6 +3740,23 @@ static int wil_nl_60g_handle_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, wil_dbg_wmi(wil, "force sending wmi commands %d\n", wil->force_wmi_send); break; + case NL_60G_GEN_FW_RESET: + if (!test_bit(WMI_FW_CAPABILITY_WMI_ONLY, + wil->fw_capabilities)) { + rc = -EOPNOTSUPP; + break; + } + + wil_dbg_misc(wil, + "NL_60G_GEN_FW_RESET, resetting...\n"); + + mutex_lock(&wil->mutex); + down_write(&wil->mem_lock); + rc = wil_reset(wil, true); + up_write(&wil->mem_lock); + mutex_unlock(&wil->mutex); + + break; default: rc = -EINVAL; wil_err(wil, "invalid debug_cmd id %d", diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index b00a13d6d530..09f69ffa4625 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, 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 @@ -44,7 +33,7 @@ (~(BIT_DMA_EP_RX_ICR_RX_HTRSH))) #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) -#define WIL6210_IMC_TX_EDMA BIT_TX_STATUS_IRQ +#define WIL6210_IMC_TX_EDMA (0xFFFFFFFFUL) #define WIL6210_IMC_RX_EDMA BIT_RX_STATUS_IRQ #define WIL6210_IMC_MISC_NO_HALP (ISR_MISC_FW_READY | \ ISR_MISC_MBOX_EVT | \ @@ -226,6 +215,7 @@ void wil_unmask_irq(struct wil6210_priv *wil) void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) { u32 moderation; + int i, num_int_lines = 2 /* Rx + Tx status */; wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); @@ -234,8 +224,11 @@ void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) /* 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); + if (wil->ipa_handle) + /* additional int per client, for Tx desc ring */ + num_int_lines += max_assoc_sta; + for (i = 0; i < num_int_lines; i++) + wil_w(wil, i * 4 + RGF_INT_CTRL_INT_GEN_CFG, moderation); /* Treat special events as regular * (set bit 0 to 0x1 and clear bits 1-8) @@ -541,7 +534,7 @@ static bool wil_validate_mbox_regs(struct wil6210_priv *wil) return true; } -static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +irqreturn_t wil6210_irq_misc(int irq, void *cookie) { struct wil6210_priv *wil = cookie; u32 isr; @@ -610,7 +603,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) } } -static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) { struct wil6210_priv *wil = cookie; u32 isr = wil->isr_misc; diff --git a/drivers/net/wireless/ath/wil6210/ipa.c b/drivers/net/wireless/ath/wil6210/ipa.c new file mode 100644 index 000000000000..1148d837b20b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/ipa.c @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: ISC +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "wil6210.h" +#include "txrx_edma.h" +#include "txrx.h" +#include "ipa.h" + +#define WIL_IPA_MSI_CAP (0x50) +#define WIL_IPA_MSI_MSG_DATA 0xCAFE + +#define WIL_IPA_BCAST_POLL_MS 20 +#define WIL_IPA_READY_TO_MS 2000 + +/* IPA requires power of 2 buffer size */ +#define WIL_IPA_RX_BUFF_SIZE (8 * 1024) +#define WIL_IPA_TX_BUFF_SIZE (2 * 1024) + +u8 ipa_offload; +module_param(ipa_offload, byte, 0444); +MODULE_PARM_DESC(ipa_offload, " Enable IPA offload, default - disabled"); + +/* template IP headers */ +static struct ethhdr wil_ipa_v4_hdr = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, + {0x01, 0x02, 0x03, 0x04, 0x05, 0x07}, + 0x0008 +}; + +static struct ethhdr wil_ipa_v6_hdr = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x08}, + {0x01, 0x02, 0x03, 0x04, 0x05, 0x09}, + 0xDD86 +}; + +int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + + return ipa->bcast_sring_id; +} + +void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, int bcast_sring_id) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + + ipa->bcast_sring_id = bcast_sring_id; +} + +static phys_addr_t wil_ipa_get_bar_base_pa(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + struct resource *res = &pdev->resource[0]; + + return res->start; +} + +static void +wil_ipa_calc_head_tail(struct wil6210_priv *wil, int ring_id, int sring_id, + phys_addr_t *desc_hwhead, phys_addr_t *desc_hwtail, + phys_addr_t *status_hwhead, phys_addr_t *status_hwtail) +{ + phys_addr_t bar_base_pa = wil_ipa_get_bar_base_pa(wil); + + /* HWTAIL / HWHEAD calculation: + * for descriptor rings: + * 1. SW should update the SUBQ producer pointer by writing to + * SUBQ_RD_PTR (table of 64 entries, one for each Q), to update the + * HW that there are descriptors waiting + * 2. SW should read the SUBQ_CONS information, in order to track the + * used registers. SUBQ_CONS is a table of 32 entries, one for + * each Q pair. lower 16bits are for even ring_id and upper 16bits + * are for odd ring_id + * for status rings: + * 1. SW should read the COMPQ_PROD information + * 2. SW should write the COMPQ_RD_PTR which is actually the completion + * queue consumer pointer + */ + + *desc_hwhead = bar_base_pa + + HOSTADDR(RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2)); + *status_hwhead = bar_base_pa + + HOSTADDR(RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_id / 2)); + + *desc_hwtail = bar_base_pa + + HOSTADDR(RGF_SCM_PTRS_SUBQ_RD_PTR + 4 * ring_id); + *status_hwtail = bar_base_pa + + HOSTADDR(RGF_SCM_PTRS_COMPQ_RD_PTR + 4 * sring_id); + + wil_dbg_misc(wil, + "desc_hwhead %pad desc_hwtail %pad status_hwhead %pad status_hwtail %pad\n", + desc_hwhead, desc_hwtail, status_hwhead, status_hwtail); +} + +static void wil_ipa_rx(struct wil_ipa *ipa, struct sk_buff *skb) +{ + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + int cid, len = skb->len; + u8 *sa = wil_skb_get_sa(skb); + struct wil_net_stats *stats; + + wil_dbg_txrx(wil, "ipa_rx %d bytes\n", len); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(len < ETH_HLEN)) { + wil_err(wil, "ipa rx packet too short %d\n", len); + goto drop; + } + + cid = wil_find_cid(wil, vif->mid, sa); + if (unlikely(cid < 0)) { + wil_err_ratelimited(wil, "cid not found for sa %pM\n", sa); + goto drop; + } + + stats = &wil->sta[cid].stats; + + wil_netif_rx(skb, ndev, cid, stats, false); + + return; + +drop: + dev_kfree_skb(skb); + ndev->stats.rx_dropped++; +} + +static void wil_ipa_notify_cb(void *priv, enum ipa_dp_evt_type evt, + unsigned long data) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct sk_buff *skb; + + switch (evt) { + case IPA_RECEIVE: + skb = (struct sk_buff *)data; + wil_ipa_rx(ipa, skb); + break; + default: + wil_dbg_misc(ipa->wil, "unhandled ipa evt %d\n", evt); + break; + } +} + +static int wil_ipa_wigig_conn_rx_pipe_smmu(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_conn_rx_in_params_smmu in = { + .notify = wil_ipa_notify_cb, + .priv = ipa, + }; + struct ipa_wigig_conn_out_params out = {0}; + int rc; + struct wil_ring *rx_ring = &wil->ring_rx; + struct wil_status_ring *rx_sring = &wil->srings[wil->rx_sring_idx]; + + rc = dma_get_sgtable(dev, &in.pipe_smmu.desc_ring_base, + (void *)rx_ring->va, rx_ring->pa, + rx_ring->size * sizeof(rx_ring->va[0])); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for desc ring failed %d\n", rc); + return rc; + } + + in.pipe_smmu.desc_ring_base_iova = rx_ring->pa; + in.pipe_smmu.desc_ring_size = rx_ring->size * sizeof(rx_ring->va[0]); + + rc = dma_get_sgtable(dev, &in.pipe_smmu.status_ring_base, + rx_sring->va, rx_sring->pa, + rx_sring->size * rx_sring->elem_size); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for status ring failed %d\n", rc); + return rc; + } + in.pipe_smmu.status_ring_base_iova = rx_sring->pa; + in.pipe_smmu.status_ring_size = rx_sring->size * rx_sring->elem_size; + + rc = dma_get_sgtable(dev, &in.dbuff_smmu.data_buffer_base, + ipa->rx_buf.va, ipa->rx_buf.pa, ipa->rx_buf.sz); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for data buffer failed %d\n", rc); + return rc; + } + + wil_ipa_calc_head_tail(wil, WIL_RX_DESC_RING_ID, wil->rx_sring_idx, + &in.pipe_smmu.desc_ring_HWHEAD_pa, + &in.pipe_smmu.desc_ring_HWTAIL_pa, + &in.pipe_smmu.status_ring_HWHEAD_pa, + &in.pipe_smmu.status_ring_HWTAIL_pa); + + in.dbuff_smmu.data_buffer_base_iova = ipa->rx_buf.pa; + in.dbuff_smmu.data_buffer_size = wil->rx_buf_len; + + rc = ipa_wigig_conn_rx_pipe_smmu(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_rx_pipe_smmu failed %d\n", rc); + return rc; + } + + ipa->rx_client_type = out.client; + + return 0; +} + +static int wil_ipa_wigig_conn_rx_pipe(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct ipa_wigig_conn_rx_in_params in = { + .notify = wil_ipa_notify_cb, + .priv = ipa, + }; + struct ipa_wigig_conn_out_params out = {0}; + int rc; + struct wil_ring *rx_ring = &wil->ring_rx; + struct wil_status_ring *rx_sring = &wil->srings[wil->rx_sring_idx]; + + in.pipe.desc_ring_base_pa = rx_ring->pa; + in.pipe.desc_ring_size = rx_ring->size * sizeof(rx_ring->va[0]); + in.pipe.status_ring_base_pa = rx_sring->pa; + in.pipe.status_ring_size = rx_sring->size * rx_sring->elem_size; + + wil_ipa_calc_head_tail(wil, WIL_RX_DESC_RING_ID, wil->rx_sring_idx, + &in.pipe.desc_ring_HWHEAD_pa, + &in.pipe.desc_ring_HWTAIL_pa, + &in.pipe.status_ring_HWHEAD_pa, + &in.pipe.status_ring_HWTAIL_pa); + + in.dbuff.data_buffer_base_pa = ipa->rx_buf.pa; + in.dbuff.data_buffer_size = wil->rx_buf_len; + + wil_dbg_misc(wil, + "calling ipa_wigig_conn_rx_pipe, desc_ring_base_pa %pad status_ring_base_pa %pad data_buffer_base_pa %pad\n", + &in.pipe.desc_ring_base_pa, &in.pipe.status_ring_base_pa, + &in.dbuff.data_buffer_base_pa); + + rc = ipa_wigig_conn_rx_pipe(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_rx_pipe failed %d\n", rc); + return rc; + } + + ipa->rx_client_type = out.client; + + return 0; +} + +static int wil_ipa_wigig_reg_intf(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct ipa_wigig_reg_intf_in_params in = { + .netdev_name = ndev->name, + }; + int rc; + + ether_addr_copy(in.netdev_mac, ndev->dev_addr); + ether_addr_copy(wil_ipa_v4_hdr.h_source, ndev->dev_addr); + ether_addr_copy(wil_ipa_v6_hdr.h_source, ndev->dev_addr); + + in.hdr_info[IPA_IP_v4].hdr = (u8 *)&wil_ipa_v4_hdr; + in.hdr_info[IPA_IP_v4].hdr_len = sizeof(wil_ipa_v4_hdr); + in.hdr_info[IPA_IP_v4].dst_mac_addr_offset = + offsetof(struct ethhdr, h_dest); + in.hdr_info[IPA_IP_v4].hdr_type = IPA_HDR_L2_ETHERNET_II; + in.hdr_info[IPA_IP_v6].hdr = (u8 *)&wil_ipa_v6_hdr; + in.hdr_info[IPA_IP_v6].hdr_len = sizeof(wil_ipa_v6_hdr); + in.hdr_info[IPA_IP_v6].dst_mac_addr_offset = + offsetof(struct ethhdr, h_dest); + in.hdr_info[IPA_IP_v6].hdr_type = IPA_HDR_L2_ETHERNET_II; + + rc = ipa_wigig_reg_intf(&in); + if (rc) + wil_err(wil, "ipa_wigig_reg_intf failed %d\n", rc); + + return rc; +} + +static void wil_ipa_set_ring_buf(struct wil6210_priv *wil, + struct wil_ring *ring, u32 i) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + size_t sz = wil->rx_buf_len; + dma_addr_t pa; + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *) + &ring->va[i].rx.enhanced; + + memset(&dd, 0, sizeof(dd)); + + pa = ipa->rx_buf.pa + i * sz; + + 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(i); + *_d = *d; +} + +static void wil_ipa_rx_refill(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + int i; + + for (i = 0; i < ring->size; i++) + wil_ipa_set_ring_buf(wil, ring, i); + ring->swhead = ring->size - 1; + + /* commit to HW */ + wil_w(wil, ring->hwtail, ring->swhead); +} + +static int wil_ipa_rx_alloc(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + u16 status_ring_size, desc_ring_size; + struct wil_ring *ring = &wil->ring_rx; + int rc, sring_id; + size_t elem_size = sizeof(struct wil_rx_status_compressed); + + /* in IPA offload must use compressed_rx_status and hw_reordering */ + if (!wil->use_compressed_rx_status || !wil->use_rx_hw_reordering) { + wil_err(wil, "invalid config for IPA offload (compressed %d, hw_reorder %d)\n", + wil->use_compressed_rx_status, + wil->use_rx_hw_reordering); + return -EINVAL; + } + + if (wil->num_rx_status_rings != 1) { + wil_err(wil, "invalid config for IPA offload (num_rx_status_rings %d)\n", + wil->num_rx_status_rings); + return -EINVAL; + } + + /* hard coded sizes for IPA offload */ + desc_ring_size = WIL_IPA_DESC_RING_SIZE; + status_ring_size = WIL_IPA_STATUS_RING_SIZE; + wil->rx_buf_len = WIL_IPA_RX_BUFF_SIZE; + + wil_dbg_misc(wil, + "rx_alloc, desc_ring_size=%u, status_ring_size=%u, elem_size=%zu\n", + desc_ring_size, status_ring_size, elem_size); + + rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len, false); + if (rc) + return rc; + + /* Allocate status ring */ + sring_id = wil_find_free_sring(wil); + if (sring_id < 0) + return -EFAULT; + wil->rx_sring_idx = sring_id; + rc = wil_init_rx_sring(wil, status_ring_size, elem_size, sring_id); + if (rc) + return rc; + + /* Allocate descriptor ring */ + rc = wil_init_rx_desc_ring(wil, desc_ring_size, wil->rx_sring_idx); + if (rc) + goto err_free_status; + + /* Allocate contiguous memory for Rx buffers, zero initialized */ + ipa->rx_buf.sz = desc_ring_size * wil->rx_buf_len; + ipa->rx_buf.va = + dma_alloc_attrs(dev, ipa->rx_buf.sz, &ipa->rx_buf.pa, + GFP_KERNEL | __GFP_ZERO, + DMA_ATTR_FORCE_CONTIGUOUS); + if (!ipa->rx_buf.va) { + rc = -ENOMEM; + goto err_free_desc; + } + + /* Fill descriptor ring with credits */ + wil_ipa_rx_refill(wil); + + return 0; + +err_free_desc: + wil_ring_free_edma(wil, ring); +err_free_status: + wil_sring_free(wil, &wil->srings[sring_id]); + + return rc; +} + +static void wil_ipa_rx_free(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = (struct wil6210_priv *)ipa->wil; + struct wil_ring *ring = &wil->ring_rx; + + wil_dbg_misc(wil, "ipa_rx_free\n"); + + wil_ring_free_edma(wil, ring); + wil_sring_free(wil, &wil->srings[wil->rx_sring_idx]); + + if (ipa->rx_buf.va) { + dma_free_attrs(wil_to_dev(wil), ipa->rx_buf.sz, + ipa->rx_buf.va, ipa->rx_buf.pa, + DMA_ATTR_FORCE_CONTIGUOUS); + ipa->rx_buf.va = NULL; + } +} + +int wil_ipa_start_ap(void *ipa_handle) +{ + struct wil_ipa *ipa = ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct pci_dev *pdev = wil->pdev; + struct msi_msg msi_msg = {0}; + int rc; + u16 msi_data; + + /* store original MSI info to be restored upon uninit */ + if (pci_read_config_dword(pdev, WIL_IPA_MSI_CAP + PCI_MSI_ADDRESS_LO, + &ipa->orig_msi_msg.address_lo) || + pci_read_config_dword(pdev, WIL_IPA_MSI_CAP + PCI_MSI_ADDRESS_HI, + &ipa->orig_msi_msg.address_hi) || + pci_read_config_word(pdev, WIL_IPA_MSI_CAP + PCI_MSI_DATA_64, + &msi_data)) { + wil_err(wil, "fail to read MSI address\n"); + return -EINVAL; + } + ipa->orig_msi_msg.data = msi_data; + + rc = wil_ipa_rx_alloc(ipa); + if (rc) + return rc; + + if (ipa->smmu_enabled) + rc = wil_ipa_wigig_conn_rx_pipe_smmu(ipa); + else + rc = wil_ipa_wigig_conn_rx_pipe(ipa); + if (rc) + goto rx_free; + + rc = ipa_wigig_set_perf_profile(WIL_MAX_BUS_REQUEST_KBPS * 8 / 1024); + if (rc) { + wil_err(wil, "ipa_wigig_set_perf_profile failed %d\n", rc); + goto err_disconn; + } + + rc = ipa_wigig_enable_pipe(ipa->rx_client_type); + if (rc) + goto err_disconn; + + rc = wil_ipa_wigig_reg_intf(ipa); + if (rc) + goto err_disable; + + wil_info(wil, "ipa intf registered. client %d\n", ipa->rx_client_type); + + /* route MSI to IPA uC */ + msi_msg.address_lo = lower_32_bits(ipa->uc_db_pa); + msi_msg.address_hi = upper_32_bits(ipa->uc_db_pa); + msi_msg.data = WIL_IPA_MSI_MSG_DATA; + pci_write_msi_msg(pdev->irq, &msi_msg); + + return 0; + +err_disable: + ipa_wigig_disable_pipe(ipa->rx_client_type); +err_disconn: + ipa_wigig_disconn_pipe(ipa->rx_client_type); + ipa->rx_client_type = IPA_CLIENT_MAX; +rx_free: + wil_ipa_rx_free(ipa); + + return rc; +} + +static int wil_ipa_wigig_conn_client_smmu(struct wil_ipa *ipa, int cid, + int ring_id, int sring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_conn_tx_in_params_smmu in = { { {0} } }; + struct ipa_wigig_conn_out_params out = {0}; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_status_ring *sring = &wil->srings[sring_id]; + struct wil_sta_info *sta = &wil->sta[cid]; + struct wil_ipa_conn *ipa_conn = &ipa->conn[cid]; + int rc, i; + size_t sz; + + rc = dma_get_sgtable(dev, &in.pipe_smmu.desc_ring_base, + (void *)ring->va, ring->pa, + ring->size * sizeof(ring->va[0])); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for desc ring failed %d\n", rc); + return rc; + } + + in.pipe_smmu.desc_ring_base_iova = ring->pa; + in.pipe_smmu.desc_ring_size = ring->size * sizeof(ring->va[0]); + + rc = dma_get_sgtable(dev, &in.pipe_smmu.status_ring_base, + sring->va, sring->pa, + sring->size * sring->elem_size); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for status ring failed %d\n", rc); + return rc; + } + in.pipe_smmu.status_ring_base_iova = sring->pa; + in.pipe_smmu.status_ring_size = sring->size * sring->elem_size; + + wil_ipa_calc_head_tail(wil, ring_id, sring_id, + &in.pipe_smmu.desc_ring_HWHEAD_pa, + &in.pipe_smmu.desc_ring_HWTAIL_pa, + &in.pipe_smmu.status_ring_HWHEAD_pa, + &in.pipe_smmu.status_ring_HWTAIL_pa); + + sz = ring->size * sizeof(in.dbuff_smmu.data_buffer_base[0]); + in.dbuff_smmu.data_buffer_base = kzalloc(sz, GFP_KERNEL); + if (!in.dbuff_smmu.data_buffer_base) + return -ENOMEM; + sz = ring->size * sizeof(in.dbuff_smmu.data_buffer_base_iova[0]); + in.dbuff_smmu.data_buffer_base_iova = kzalloc(sz, GFP_KERNEL); + if (!in.dbuff_smmu.data_buffer_base_iova) { + rc = -ENOMEM; + goto out; + } + + in.dbuff_smmu.num_buffers = ring->size; + for (i = 0; i < in.dbuff_smmu.num_buffers; i++) { + struct wil_dma_map_info *tx_buf = &ipa_conn->tx_bufs_addr[i]; + + rc = dma_get_sgtable(dev, &in.dbuff_smmu.data_buffer_base[i], + tx_buf->va, tx_buf->pa, + WIL_IPA_TX_BUFF_SIZE); + if (rc < 0) { + wil_err(wil, "sgtable tx buf %d failed %d\n", i, rc); + goto out; + } + in.dbuff_smmu.data_buffer_base_iova[i] = tx_buf->pa; + } + in.dbuff_smmu.data_buffer_size = WIL_IPA_TX_BUFF_SIZE; + + in.int_gen_tx_bit_num = ring_id; + ether_addr_copy(in.client_mac, sta->addr); + + rc = ipa_wigig_conn_client_smmu(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_client failed %d\n", rc); + goto out; + } + + ipa->conn[cid].ipa_client = out.client; + +out: + kfree(in.dbuff_smmu.data_buffer_base); + kfree(in.dbuff_smmu.data_buffer_base_iova); + + return rc; +} + +static int wil_ipa_wigig_conn_client(struct wil_ipa *ipa, int cid, int ring_id, + int sring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct ipa_wigig_conn_tx_in_params in = { {0} }; + struct ipa_wigig_conn_out_params out = {0}; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_status_ring *sring = &wil->srings[sring_id]; + struct wil_sta_info *sta = &wil->sta[cid]; + int rc; + + in.pipe.desc_ring_base_pa = ring->pa; + in.pipe.desc_ring_size = ring->size * sizeof(ring->va[0]); + in.pipe.status_ring_base_pa = sring->pa; + in.pipe.status_ring_size = sring->size * sring->elem_size; + + wil_ipa_calc_head_tail(wil, ring_id, sring_id, + &in.pipe.desc_ring_HWHEAD_pa, + &in.pipe.desc_ring_HWTAIL_pa, + &in.pipe.status_ring_HWHEAD_pa, + &in.pipe.status_ring_HWTAIL_pa); + + in.dbuff.data_buffer_size = WIL_IPA_TX_BUFF_SIZE; + + in.int_gen_tx_bit_num = ring_id; + ether_addr_copy(in.client_mac, sta->addr); + + rc = ipa_wigig_conn_client(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_client failed %d\n", rc); + return rc; + } + + ipa->conn[cid].ipa_client = out.client; + + return 0; +} + +static void wil_ipa_free_tx_bufs(struct wil_ipa *ipa, int cid) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct wil_ipa_conn *conn = &ipa->conn[cid]; + int i; + + for (i = 0; i < conn->tx_bufs_count; i++) { + struct wil_dma_map_info *tx_buf_addr = &conn->tx_bufs_addr[i]; + + if (tx_buf_addr->va) + dma_free_coherent(dev, WIL_IPA_TX_BUFF_SIZE, + tx_buf_addr->va, tx_buf_addr->pa); + } + + kfree(conn->tx_bufs_addr); + conn->tx_bufs_addr = NULL; +} + +static int wil_ipa_alloc_tx_bufs(struct wil_ipa *ipa, int cid, int ring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_ipa_conn *conn = &ipa->conn[cid]; + size_t sz; + int rc, i; + + sz = ring->size * sizeof(conn->tx_bufs_addr[0]); + conn->tx_bufs_addr = kzalloc(sz, GFP_KERNEL); + if (!conn->tx_bufs_addr) + return -ENOMEM; + + conn->tx_bufs_count = ring->size; + for (i = 0; i < ring->size; i++) { + struct wil_tx_enhanced_desc *d; + + /* note: IPA requires Tx buffers to be 256B aligned */ + conn->tx_bufs_addr[i].va = + dma_alloc_coherent(dev, WIL_IPA_TX_BUFF_SIZE, + &conn->tx_bufs_addr[i].pa, + GFP_KERNEL | __GFP_ZERO); + if (!conn->tx_bufs_addr[i].va) { + wil_err(wil, "tx buf DMA alloc error (i %d)\n", i); + rc = -ENOMEM; + goto err; + } + + d = (struct wil_tx_enhanced_desc *)&ring->va[i].tx.enhanced; + wil_tx_desc_map_edma((union wil_tx_desc *)d, + conn->tx_bufs_addr[i].pa, + WIL_IPA_TX_BUFF_SIZE, ring_id); + wil_tx_desc_set_nr_frags(&((union wil_tx_desc *)d)->legacy, 1); + } + + return 0; + +err: + wil_ipa_free_tx_bufs(ipa, cid); + + return rc; +} + +int wil_ipa_conn_client(void *ipa_handle, int cid, int ring_id, int sring_id) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct wil_ipa_conn *ipa_conn; + int rc; + + wil_info(wil, "ipa connect cid %d ring_id %d sring_id %d\n", + cid, ring_id, sring_id); + + rc = wil_ipa_alloc_tx_bufs(ipa, cid, ring_id); + if (rc) + return rc; + + if (ipa->smmu_enabled) + rc = wil_ipa_wigig_conn_client_smmu(ipa, cid, ring_id, + sring_id); + else + rc = wil_ipa_wigig_conn_client(ipa, cid, ring_id, sring_id); + if (rc) + goto err_free; + + ipa_conn = &ipa->conn[cid]; + rc = ipa_wigig_enable_pipe(ipa_conn->ipa_client); + if (rc) + goto err_disconn; + + wil_dbg_misc(wil, "ipa pipe enabled (client %d)\n", + ipa_conn->ipa_client); + + return 0; + +err_disconn: + ipa_wigig_disconn_pipe(ipa_conn->ipa_client); + ipa_conn->ipa_client = IPA_CLIENT_MAX; + +err_free: + wil_ipa_free_tx_bufs(ipa, cid); + + return rc; +} + +static void wil_ipa_start_bcast_timer(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + + if (timer_pending(&ipa->bcast_timer)) + return; + + wil_dbg_txrx(wil, "ipa start bcast timer\n"); + + mod_timer(&ipa->bcast_timer, + jiffies + msecs_to_jiffies(WIL_IPA_BCAST_POLL_MS)); +} + +int wil_ipa_tx(void *ipa_handle, struct wil_ring *ring, struct sk_buff *skb) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct wil_ipa_conn *ipa_conn; + struct wil_net_stats *stats; + int cid, rc; + const u8 *da; + unsigned int len = skb->len; + + wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + da = wil_skb_get_da(skb); + if (is_multicast_ether_addr(da)) { + wil_ipa_start_bcast_timer(ipa); + /* let wil_start_xmit() continue handling this (mcast) packet */ + return -EPROTONOSUPPORT; + } + + cid = wil_get_cid_by_ring(wil, ring); + if (cid >= max_assoc_sta) { + wil_dbg_txrx(wil, "ipa_tx invalid cid %d\n", cid); + return -EINVAL; + } + + ipa_conn = &ipa->conn[cid]; + + rc = ipa_wigig_tx_dp(ipa_conn->ipa_client, skb); + if (rc) + return rc; + /* skb could be freed after this point */ + + stats = &wil->sta[cid].stats; + stats->tx_packets++; + stats->tx_bytes += len; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += len; + + return 0; +} + +void wil_ipa_disconn_client(void *ipa_handle, int cid) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct wil_ipa_conn *ipa_conn = &ipa->conn[cid]; + + if (ipa_conn->ipa_client >= IPA_CLIENT_MAX) + return; + + wil_info(wil, "ipa disconnect cid %d (client %d)\n", + cid, ipa_conn->ipa_client); + + ipa_wigig_disable_pipe(ipa_conn->ipa_client); + ipa_wigig_disconn_pipe(ipa_conn->ipa_client); + wil_ipa_free_tx_bufs(ipa, cid); + + ipa_conn->ipa_client = IPA_CLIENT_MAX; +} + +static void wil_ipa_uc_ready_cb(void *priv) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_misc(wil, "got ipa uC ready cb\n"); + + complete(&ipa->ipa_uc_ready_comp); +} + +static void wil_ipa_wigig_misc_cb(void *priv) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_irq(wil, "got MISC callback from IPA\n"); + if (wil6210_irq_misc(0, wil) == IRQ_WAKE_THREAD) + wil6210_irq_misc_thread(0, wil); +} + +static int wil_ipa_wigig_init(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_init_in_params in = {0}; + struct ipa_wigig_init_out_params out = {0}; + phys_addr_t bar_base_pa = wil_ipa_get_bar_base_pa(wil); + int rc; + u8 wil_smmu_en; + + ipa->domain = iommu_get_domain_for_dev(dev); + if (!ipa->domain) { + wil_err(wil, "iommu_get_domain failed\n"); + return -EINVAL; + } + + init_completion(&ipa->ipa_uc_ready_comp); + in.periph_baddr_pa = bar_base_pa; + in.pseudo_cause_pa = bar_base_pa + HOSTADDR(RGF_DMA_PSEUDO_CAUSE); + in.int_gen_tx_pa = bar_base_pa + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR); + in.int_gen_rx_pa = bar_base_pa + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR); + in.dma_ep_misc_pa = bar_base_pa + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR); + in.notify = wil_ipa_uc_ready_cb; + in.int_notify = wil_ipa_wigig_misc_cb; + in.priv = ipa; + + rc = ipa_wigig_init(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_init failed %d\n", rc); + return rc; + } + + if (!out.is_uc_ready) { + ulong to, left; + + to = msecs_to_jiffies(WIL_IPA_READY_TO_MS); + left = wait_for_completion_timeout(&ipa->ipa_uc_ready_comp, to); + if (left == 0) { + wil_err(wil, "IPA uC ready timeout\n"); + rc = -ETIME; + goto err; + } + } + + ipa->smmu_enabled = ipa_wigig_is_smmu_enabled(); + wil_smmu_en = test_bit(WIL_PLATFORM_CAPA_SMMU, wil->platform_capa); + if (ipa->smmu_enabled != wil_smmu_en) { + wil_err(wil, "smmu disagreement (ipa %d wil %d)\n", + ipa->smmu_enabled, wil_smmu_en); + rc = -EINVAL; + goto err; + } + + wil_info(wil, "IPA uC ready (early %d, uc_db_pa %pad, smmu %d)\n", + out.is_uc_ready, &out.uc_db_pa, ipa->smmu_enabled); + + if (ipa->smmu_enabled) { + phys_addr_t pa; + + pa = rounddown(out.uc_db_pa, PAGE_SIZE); + rc = iommu_map(ipa->domain, pa, pa, PAGE_SIZE, + IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO); + if (rc) { + wil_err(wil, "iommu_map failed %d\n", rc); + goto err; + } + } + + ipa->uc_db_pa = out.uc_db_pa; + + return 0; + +err: + if (ipa->smmu_enabled && ipa->uc_db_pa) { + iommu_unmap(ipa->domain, ipa->uc_db_pa, PAGE_SIZE); + ipa->uc_db_pa = 0; + } + ipa_wigig_cleanup(); + + return rc; +} + +static void wil_ipa_bcast_fn(struct timer_list *t) +{ + struct wil_ipa *ipa = from_timer(ipa, t, bcast_timer); + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_txrx(wil, "NAPI(Tx bcast) schedule\n"); + napi_schedule(&wil->napi_tx); +} + +void *wil_ipa_init(struct wil6210_priv *wil) +{ + struct wil_ipa *ipa; + int rc, i; + + wil_dbg_misc(wil, "wil_ipa init\n"); + + if (wil->max_vifs > 1) { + wil_err(wil, "IPA offload not supported with multi-VIF\n"); + return NULL; + } + + if (!test_bit(WMI_FW_CAPABILITY_IPA, wil->fw_capabilities)) { + wil_err(wil, "IPA offload not supported by FW\n"); + return NULL; + } + + ipa = kzalloc(sizeof(*ipa), GFP_KERNEL); + if (!ipa) + return NULL; + + ipa->wil = wil; + ipa->bcast_sring_id = WIL6210_MAX_STATUS_RINGS; + ipa->rx_client_type = IPA_CLIENT_MAX; + for (i = 0; i < WIL6210_MAX_CID; i++) + ipa->conn[i].ipa_client = IPA_CLIENT_MAX; + + timer_setup(&ipa->bcast_timer, wil_ipa_bcast_fn, 0); + + rc = wil_ipa_wigig_init(ipa); + if (rc) + goto err; + + return ipa; + +err: + kfree(ipa); + + return NULL; +} + +void wil_ipa_uninit(void *ipa_handle) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil; + struct device *dev; + int i; + + if (!ipa_handle) + return; + + wil = ipa->wil; + dev = wil_to_dev(wil); + wil_info(ipa->wil, "wil_ipa uninit\n"); + + del_timer_sync(&ipa->bcast_timer); + + for (i = 0; i < WIL6210_MAX_CID; i++) { + struct wil_ipa_conn *conn = &ipa->conn[i]; + + if (conn->ipa_client < IPA_CLIENT_MAX) { + ipa_wigig_disable_pipe(conn->ipa_client); + ipa_wigig_disconn_pipe(conn->ipa_client); + + wil_ipa_free_tx_bufs(ipa, i); + } + } + + if (ipa->rx_client_type < IPA_CLIENT_MAX) { + struct net_device *ndev = wil->main_ndev; + struct pci_dev *pdev = wil->pdev; + + ipa_wigig_dereg_intf(ndev->name); + ipa_wigig_disable_pipe(ipa->rx_client_type); + ipa_wigig_disconn_pipe(ipa->rx_client_type); + pci_write_msi_msg(pdev->irq, &ipa->orig_msi_msg); + + wil_ipa_rx_free(ipa); + } + + if (ipa->smmu_enabled && ipa->uc_db_pa) + iommu_unmap(ipa->domain, ipa->uc_db_pa, PAGE_SIZE); + + ipa_wigig_cleanup(); + kfree(ipa_handle); +} diff --git a/drivers/net/wireless/ath/wil6210/ipa.h b/drivers/net/wireless/ath/wil6210/ipa.h new file mode 100644 index 000000000000..6c563d225c6b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/ipa.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (c) 2019, The Linux Foundation. + * All rights reserved. + */ + +#ifndef WIL6210_IPA_H +#define WIL6210_IPA_H + +#include +#ifdef CONFIG_WIL6210_IPA +#include +#include +extern u8 ipa_offload; +#endif + +#define WIL_IPA_BCAST_DESC_RING_SIZE 512 +#define WIL_IPA_BCAST_SRING_SIZE (2 * WIL_IPA_BCAST_DESC_RING_SIZE) + +#define WIL_IPA_DESC_RING_SIZE 2000 +#define WIL_IPA_STATUS_RING_SIZE 2048 + +#define WIL_IPA_MAX_ASSOC_STA 4 + +#ifdef CONFIG_WIL6210_IPA + +struct wil_dma_map_info { + void *va; + phys_addr_t pa; +}; + +struct wil_ipa_conn { + enum ipa_client_type ipa_client; + struct wil_dma_map_info *tx_bufs_addr; + int tx_bufs_count; +}; + +struct wil_ipa_rx_buf { + dma_addr_t pa; + void *va; + size_t sz; +}; + +struct wil_ipa { + struct wil6210_priv *wil; + struct iommu_domain *domain; + struct timer_list bcast_timer; + int bcast_sring_id; + struct completion ipa_uc_ready_comp; + u8 smmu_enabled; + enum ipa_client_type rx_client_type; + phys_addr_t uc_db_pa; + struct wil_ipa_conn conn[WIL6210_MAX_CID]; + struct wil_ipa_rx_buf rx_buf; /* contiguous memory split into rx bufs */ + struct msi_msg orig_msi_msg; +}; + +static inline bool wil_ipa_offload(void) {return ipa_offload; } +void *wil_ipa_init(struct wil6210_priv *wil); +void wil_ipa_uninit(void *ipa_handle); +int wil_ipa_start_ap(void *ipa_handle); +int wil_ipa_conn_client(void *ipa_handle, int cid, int ring_id, int sring_id); +void wil_ipa_disconn_client(void *ipa_handle, int cid); +int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil); +void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, int bcast_sring_id); +int wil_ipa_tx(void *ipa_handle, struct wil_ring *ring, struct sk_buff *skb); + +#else /* CONFIG_WIL6210_IPA */ + +static inline bool wil_ipa_offload(void) {return false; } +static inline void *wil_ipa_init(struct wil6210_priv *wil) {return NULL; } +static inline void wil_ipa_uninit(void *ipa_handle) {} +static inline int wil_ipa_start_ap(void *ipa_handle) {return -EOPNOTSUPP; } +static inline int wil_ipa_conn_client(void *ipa_handle, + int cid, int ring_id, + int sring_id) {return -EOPNOTSUPP; } +static inline void wil_ipa_disconn_client(void *ipa_handle, int cid) {} +static inline int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil) +{ + return WIL6210_MAX_STATUS_RINGS; +} + +static inline void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, + int bcast_sring_id) {} +static inline int wil_ipa_tx(void *ipa_handle, + struct wil_ring *ring, + struct sk_buff *skb) {return -EOPNOTSUPP; } + +#endif /* CONFIG_WIL6210_IPA */ + +#endif /* WIL6210_IPA_H */ diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index d4fb0ecf5bd7..9ccfe43d8cec 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -14,6 +14,7 @@ #include "txrx_edma.h" #include "wmi.h" #include "boot_loader.h" +#include "ipa.h" #define WAIT_FOR_HALP_VOTE_MS 100 #define WAIT_FOR_SCAN_ABORT_MS 1000 @@ -294,6 +295,10 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) if (wil->ring2cid_tid[i][0] == cid) wil_ring_fini_tx(wil, i); } + + if (wil->ipa_handle) + wil_ipa_disconn_client(wil->ipa_handle, cid); + /* statistics */ memset(&sta->stats, 0, sizeof(sta->stats)); sta->stats.tx_latency_min_us = U32_MAX; @@ -524,22 +529,16 @@ bool wil_is_recovery_blocked(struct wil6210_priv *wil) return no_fw_recovery && (wil->recovery_state == fw_recovery_pending); } -static void wil_fw_error_worker(struct work_struct *work) +void wil_fw_recovery(struct wil6210_priv *wil) { - struct wil6210_priv *wil = container_of(work, struct wil6210_priv, - fw_error_worker); struct net_device *ndev = wil->main_ndev; struct wireless_dev *wdev; - wil_dbg_misc(wil, "fw error worker\n"); + wil_dbg_misc(wil, "fw recovery\n"); - if (!ndev || !(ndev->flags & IFF_UP)) { - wil_info(wil, "No recovery - interface is down\n"); - return; - } wdev = ndev->ieee80211_ptr; - /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO + /* increment @recovery_count if less than WIL6210_FW_RECOVERY_TO * passed since last recovery attempt */ if (time_is_after_jiffies(wil->last_fw_recovery + @@ -582,6 +581,10 @@ static void wil_fw_error_worker(struct work_struct *work) if (no_fw_recovery) /* upper layers do recovery */ break; /* silent recovery, upper layers will see disconnect */ + if (wil->ipa_handle) { + wil_ipa_uninit(wil->ipa_handle); + wil->ipa_handle = NULL; + } __wil_down(wil); __wil_up(wil); mutex_unlock(&wil->mutex); @@ -599,6 +602,22 @@ static void wil_fw_error_worker(struct work_struct *work) rtnl_unlock(); } +static void wil_fw_error_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + fw_error_worker); + struct net_device *ndev = wil->main_ndev; + + wil_dbg_misc(wil, "fw error worker\n"); + + if (!ndev || !(ndev->flags & IFF_UP)) { + wil_info(wil, "No recovery - interface is down\n"); + return; + } + + wil_fw_recovery(wil); +} + static int wil_find_free_ring(struct wil6210_priv *wil) { int i; @@ -614,7 +633,7 @@ static int wil_find_free_ring(struct wil6210_priv *wil) int wil_ring_init_tx(struct wil6210_vif *vif, int cid) { struct wil6210_priv *wil = vif_to_wil(vif); - int rc = -EINVAL, ringid; + int rc = -EINVAL, ringid, ring_size; if (cid < 0) { wil_err(wil, "No connection pending\n"); @@ -629,7 +648,9 @@ int wil_ring_init_tx(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->txrx_ops.ring_init_tx(vif, ringid, 1 << tx_ring_order, + ring_size = wil->ipa_handle ? + WIL_IPA_DESC_RING_SIZE : 1 << tx_ring_order; + rc = wil->txrx_ops.ring_init_tx(vif, ringid, ring_size, cid, 0); if (rc) wil_err(wil, "init TX for CID %d MID %d vring %d failed\n", @@ -711,6 +732,8 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); + INIT_WORK(&wil->pci_linkdown_recovery_worker, + wil_pci_linkdown_recovery_worker); INIT_LIST_HEAD(&wil->pending_wmi_ev); spin_lock_init(&wil->wmi_ev_lock); @@ -829,6 +852,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil_set_recovery_state(wil, fw_recovery_idle); cancel_work_sync(&wil->fw_error_worker); + cancel_work_sync(&wil->pci_linkdown_recovery_worker); wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 408bd7d57a5a..601be80e3dab 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -9,6 +9,7 @@ #include #include "wil6210.h" #include "txrx.h" +#include "ipa.h" static bool alt_ifname; /* = false; */ module_param(alt_ifname, bool, 0444); @@ -178,16 +179,27 @@ static int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget) napi_tx); int tx_done; /* There is only one status TX ring */ - struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx]; + struct wil_status_ring *sring; + int sring_idx = wil->ipa_handle ? + wil_ipa_get_bcast_sring_id(wil) : wil->tx_sring_idx; - if (!sring->va) + if (sring_idx >= WIL6210_MAX_STATUS_RINGS) { + napi_complete(napi); return 0; + } + + sring = &wil->srings[sring_idx]; + if (!sring->va) { + napi_complete(napi); + return 0; + } tx_done = wil_tx_sring_handler(wil, sring); if (tx_done < budget) { napi_complete(napi); - wil6210_unmask_irq_tx_edma(wil); + if (!wil->ipa_handle) + wil6210_unmask_irq_tx_edma(wil); wil_dbg_txrx(wil, "NAPI TX complete\n"); } @@ -346,6 +358,9 @@ wil_vif_alloc(struct wil6210_priv *wil, const char *name, ndev->hw_features = NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6; + if (wil_ipa_offload()) + ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_TSO | + NETIF_F_TSO6); ndev->features |= ndev->hw_features; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); @@ -559,6 +574,8 @@ void wil_if_remove(struct wil6210_priv *wil) wil_vif_remove(wil, 0); rtnl_unlock(); + wil_ipa_uninit(wil->ipa_handle); + wil->ipa_handle = NULL; netif_napi_del(&wil->napi_tx); netif_napi_del(&wil->napi_rx); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 73520ea23b58..dafe9ebf351e 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -12,6 +12,7 @@ #include "wil6210.h" #include #include +#include "ipa.h" static int n_msi = 3; module_param(n_msi, int, 0444); @@ -98,6 +99,9 @@ int wil_set_capabilities(struct wil6210_priv *wil) wil->use_enhanced_dma_hw = true; wil->use_rx_hw_reordering = true; wil->use_compressed_rx_status = true; + if (wil_ipa_offload()) + /* IPA offload must use single MSI */ + n_msi = 1; wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : WIL_FW_NAME_TALYN; if (wil_fw_verify_file_exists(wil, wil_fw_name)) @@ -218,6 +222,11 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) wil->n_msi = n_msi; + if (wil->n_msi == 0 && wil_ipa_offload()) { + wil_err(wil, "IPA offload cannot use INTx\n"); + rc = -ENODEV; + goto stop_master; + } if (wil->n_msi == 0 && msi_only) { wil_err(wil, "Interrupt pin not routed, unable to use INTx\n"); rc = -ENODEV; @@ -285,6 +294,108 @@ static int wil_platform_rop_fw_recovery(void *wil_handle) return 0; } +void wil_pci_linkdown_recovery_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + pci_linkdown_recovery_worker); + int rc, i; + struct wil6210_vif *vif; + struct net_device *ndev = wil->main_ndev; + + wil_dbg_misc(wil, "starting pci_linkdown recovery\n"); + + rtnl_lock(); + mutex_lock(&wil->mutex); + down_write(&wil->mem_lock); + clear_bit(wil_status_fwready, wil->status); + set_bit(wil_status_pci_linkdown, wil->status); + set_bit(wil_status_resetting, wil->status); + up_write(&wil->mem_lock); + + if (test_and_clear_bit(wil_status_napi_en, wil->status)) { + napi_disable(&wil->napi_rx); + napi_disable(&wil->napi_tx); + } + + mutex_unlock(&wil->mutex); + rtnl_unlock(); + + mutex_lock(&wil->mutex); + + mutex_lock(&wil->vif_mutex); + wil_ftm_stop_operations(wil); + wil_p2p_stop_radio_operations(wil); + wil_abort_scan_all_vifs(wil, false); + mutex_unlock(&wil->vif_mutex); + + for (i = 0; i < wil->max_vifs; i++) { + vif = wil->vifs[i]; + if (vif) { + cancel_work_sync(&vif->disconnect_worker); + wil6210_disconnect(vif, NULL, + WLAN_REASON_DEAUTH_LEAVING); + } + } + + wmi_event_flush(wil); + flush_workqueue(wil->wq_service); + flush_workqueue(wil->wmi_wq); + + /* Recover PCIe */ + if (wil->platform_ops.pci_linkdown_recovery) { + rc = + wil->platform_ops.pci_linkdown_recovery(wil->platform_handle); + if (rc) { + wil_err(wil, + "platform device failed to recover from pci linkdown (%d)\n", + rc); + mutex_unlock(&wil->mutex); + goto out; + } + } else { + wil_err(wil, + "platform device doesn't support pci_linkdown recovery\n"); + mutex_unlock(&wil->mutex); + goto out; + } + + if (!ndev || !(ndev->flags & IFF_UP)) { + wil_reset(wil, false); + mutex_unlock(&wil->mutex); + } else { + mutex_unlock(&wil->mutex); + wil->recovery_state = fw_recovery_pending; + wil_fw_recovery(wil); + } + +out: + return; +} + +static int wil_platform_rop_notify(void *wil_handle, + enum wil_platform_notif notif) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + switch (notif) { + case WIL_PLATFORM_NOTIF_PCI_LINKDOWN: + wil_info(wil, "received WIL_PLATFORM_NOTIF_PCI_LINKDOWN\n"); + clear_bit(wil_status_fwready, wil->status); + set_bit(wil_status_resetting, wil->status); + set_bit(wil_status_pci_linkdown, wil->status); + + schedule_work(&wil->pci_linkdown_recovery_worker); + break; + default: + break; + } + + return 0; +} + static void wil_platform_ops_uninit(struct wil6210_priv *wil) { if (wil->platform_ops.uninit) @@ -300,6 +411,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) const struct wil_platform_rops rops = { .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, + .notify = wil_platform_rop_notify, }; u32 bar_size = pci_resource_len(pdev, 0); int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ @@ -537,6 +649,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime) struct wil6210_priv *wil = pci_get_drvdata(pdev); bool keep_radio_on, active_ifaces; + if (test_bit(wil_status_pci_linkdown, wil->status)) { + wil_dbg_pm(wil, "ignore resume during pci linkdown\n"); + return 0; + } + wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); mutex_lock(&wil->vif_mutex); diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 56143e7670ed..34f5ea8e6520 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: ISC /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, 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 "wil6210.h" @@ -101,6 +90,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) goto out; } + if (test_bit(wil_status_pci_linkdown, wil->status)) { + wil_dbg_pm(wil, "Delay suspend during pci linkdown\n"); + rc = -EBUSY; + goto out; + } + mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 84d72955fa56..c031151e99e9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -18,6 +18,7 @@ #include "txrx.h" #include "trace.h" #include "txrx_edma.h" +#include "ipa.h" bool rx_align_2; module_param(rx_align_2, bool, 0444); @@ -1636,12 +1637,6 @@ found: return v; } -static inline -void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) -{ - d->mac.d[2] |= (nr_frags << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); -} - /** * Sets the descriptor @d up for csum and/or TSO offloading. The corresponding * @skb is used to obtain the protocol and headers length. @@ -2229,6 +2224,19 @@ static int wil_tx_ring(struct wil6210_priv *wil, struct wil6210_vif *vif, return rc; } +int wil_get_cid_by_ring(struct wil6210_priv *wil, struct wil_ring *ring) +{ + int ring_index = ring - wil->ring_tx; + + if (unlikely(ring_index < 0 || ring_index >= WIL6210_MAX_TX_RINGS)) { + wil_err(wil, "cid by ring 0x%pK: invalid ring index %d\n", + ring, ring_index); + return max_assoc_sta; + } + + return wil->ring2cid_tid[ring_index][0]; +} + /** * Check status of tx vrings and stop/wake net queues if needed * It will start/stop net queues of a specific VIF net_device. @@ -2383,6 +2391,19 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) wil_dbg_txrx(wil, "No Tx RING found for %pM\n", da); goto drop; } + + if (wil->ipa_handle) { + rc = wil_ipa_tx(wil->ipa_handle, ring, skb); + switch (rc) { + case 0: + return NETDEV_TX_OK; + case -EPROTONOSUPPORT: + break; + default: + return NETDEV_TX_BUSY; + } + } + /* set up vring entry */ rc = wil_tx_ring(wil, vif, ring, skb); diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 5120475b0cd7..d6e69926caa9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -1,18 +1,7 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, 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. */ #ifndef WIL6210_TXRX_H @@ -685,6 +674,12 @@ static inline void wil_skb_set_cid(struct sk_buff *skb, u8 cid) skb_rx_info->rx_info.cid = cid; } +static inline +void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) +{ + d->mac.d[2] |= (nr_frags << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +} + void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, struct wil_net_stats *stats, bool gro); @@ -699,5 +694,6 @@ void wil_tx_data_init(struct wil_ring_tx_data *txdata); void wil_init_txrx_ops_legacy_dma(struct wil6210_priv *wil); void wil_tx_latency_calc(struct wil6210_priv *wil, struct sk_buff *skb, struct wil_sta_info *sta); +int wil_get_cid_by_ring(struct wil6210_priv *wil, struct wil_ring *ring); #endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 122873843227..40433cfa384b 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -14,6 +14,7 @@ #include "txrx_edma.h" #include "txrx.h" #include "trace.h" +#include "ipa.h" /* Max number of entries (packets to complete) to update the hwtail of tx * status ring. Should be power of 2 @@ -44,7 +45,7 @@ static void wil_tx_desc_unmap_edma(struct device *dev, } } -static int wil_find_free_sring(struct wil6210_priv *wil) +int wil_find_free_sring(struct wil6210_priv *wil) { int i; @@ -56,8 +57,7 @@ static int wil_find_free_sring(struct wil6210_priv *wil) return -EINVAL; } -static void wil_sring_free(struct wil6210_priv *wil, - struct wil_status_ring *sring) +void wil_sring_free(struct wil6210_priv *wil, struct wil_status_ring *sring) { struct device *dev = wil_to_dev(wil); size_t sz; @@ -103,47 +103,63 @@ static int wil_sring_alloc(struct wil6210_priv *wil, return 0; } +static int wil_init_tx_sring(struct wil6210_priv *wil, u16 status_ring_size, + size_t elem_size, u16 sring_id) +{ + struct wil_status_ring *sring = &wil->srings[sring_id]; + int rc; + u8 irq_mode = WMI_RING_ADD_IRQ_MODE_ENABLE; + + wil_dbg_misc(wil, "init TX sring: size=%u, sring_id=%u\n", + status_ring_size, sring_id); + + sring->is_rx = false; + sring->size = status_ring_size; + sring->elem_size = elem_size; + rc = wil_sring_alloc(wil, sring); + if (rc) + return rc; + + /* in IPA mode, use interrupts from desc ring instead of sring */ + if (wil->ipa_handle) + irq_mode = WMI_RING_ADD_IRQ_MODE_DISABLE; + + rc = wil_wmi_tx_sring_cfg(wil, sring_id, irq_mode); + if (rc) + goto out_free; + + sring->desc_rdy_pol = 1; + + return 0; + +out_free: + wil_sring_free(wil, sring); + return rc; +} + 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; + int sring_id = wil_find_free_sring(wil), rc; + u16 sring_size; + + if (sring_id < 0) + return sring_id; 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; + sring_size = wil_ipa_offload() ? + WIL_IPA_STATUS_RING_SIZE : 1 << wil->tx_status_ring_order; /* Allocate Tx status ring. Tx descriptor rings will be * allocated on WMI connect event */ - sring = &wil->srings[ring_id]; + rc = wil_init_tx_sring(wil, sring_size, + sizeof(struct wil_ring_tx_status), sring_id); + if (!rc) + wil->tx_sring_idx = sring_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; } @@ -341,10 +357,8 @@ static int wil_init_rx_buff_arr(struct wil6210_priv *wil, return 0; } -static int wil_init_rx_sring(struct wil6210_priv *wil, - u16 status_ring_size, - size_t elem_size, - u16 ring_id) +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; @@ -374,7 +388,8 @@ out_free: } static int wil_ring_alloc_desc_ring(struct wil6210_priv *wil, - struct wil_ring *ring) + struct wil_ring *ring, + bool alloc_ctx) { struct device *dev = wil_to_dev(wil); size_t sz = ring->size * sizeof(ring->va[0]); @@ -385,9 +400,15 @@ static int wil_ring_alloc_desc_ring(struct wil6210_priv *wil, ring->swhead = 0; ring->swtail = 0; - ring->ctx = kcalloc(ring->size, sizeof(ring->ctx[0]), GFP_KERNEL); - if (!ring->ctx) - goto err; + + if (alloc_ctx) { + ring->ctx = kcalloc(ring->size, sizeof(ring->ctx[0]), + GFP_KERNEL); + if (!ring->ctx) + goto err; + } else { + ring->ctx = NULL; + } ring->va = dma_alloc_coherent(dev, sz, &ring->pa, GFP_KERNEL); if (!ring->va) @@ -418,7 +439,7 @@ err: return -ENOMEM; } -static void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) +void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) { struct device *dev = wil_to_dev(wil); size_t sz; @@ -472,6 +493,18 @@ static void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring) ring->swtail = wil_ring_next_tail(ring); } + if (wil->ipa_handle) { + int bcast_sring_id = wil_ipa_get_bcast_sring_id(wil); + + /* when freeing bcast desc ring, free the bcast status ring */ + if (wil->ring2cid_tid[ring_index][0] == WIL6210_MAX_CID && + bcast_sring_id < WIL6210_MAX_STATUS_RINGS) { + wil_sring_free(wil, &wil->srings[bcast_sring_id]); + wil_ipa_set_bcast_sring_id(wil, + WIL6210_MAX_STATUS_RINGS); + } + } + out: dma_free_coherent(dev, sz, (void *)ring->va, ring->pa); kfree(ring->ctx); @@ -480,8 +513,8 @@ out: ring->ctx = NULL; } -static int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, - int status_ring_id) +int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, + int status_ring_id) { struct wil_ring *ring = &wil->ring_rx; int rc; @@ -490,7 +523,7 @@ static int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, ring->size = desc_ring_size; ring->is_rx = true; - rc = wil_ring_alloc_desc_ring(wil, ring); + rc = wil_ring_alloc_desc_ring(wil, ring, true); if (rc) return rc; @@ -611,6 +644,10 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order) sizeof(struct wil_rx_status_extended); int i; + if (wil->ipa_handle) + /* in ipa offload, this is done as part of wil_ipa_start_ap */ + return 0; + /* In SW reorder one must use extended status messages */ if (wil->use_compressed_rx_status && !wil->use_rx_hw_reordering) { wil_err(wil, @@ -642,7 +679,7 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order) wil_dbg_misc(wil, "rx_init: allocate %d status rings\n", wil->num_rx_status_rings); - rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len); + rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len, true); if (rc) return rc; @@ -658,11 +695,13 @@ static int wil_rx_init_edma(struct wil6210_priv *wil, uint desc_ring_order) sring_id); if (rc) goto err_free_status; + + if (i == 0) + wil->rx_sring_idx = sring_id; } /* Allocate descriptor ring */ - rc = wil_init_rx_desc_ring(wil, desc_ring_size, - WIL_DEFAULT_RX_STATUS_RING_ID); + rc = wil_init_rx_desc_ring(wil, desc_ring_size, wil->rx_sring_idx); if (rc) goto err_free_status; @@ -703,6 +742,7 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, int rc; struct wil_ring *ring = &wil->ring_tx[ring_id]; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; + u8 irq_mode = WMI_RING_ADD_IRQ_MODE_DISABLE; lockdep_assert_held(&wil->mutex); @@ -712,7 +752,7 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, wil_tx_data_init(txdata); ring->size = size; - rc = wil_ring_alloc_desc_ring(wil, ring); + rc = wil_ring_alloc_desc_ring(wil, ring, true); if (rc) goto out; @@ -721,12 +761,24 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id, if (!vif->privacy) txdata->dot1x_open = true; - rc = wil_wmi_tx_desc_ring_add(vif, ring_id, cid, tid); + /* in IPA mode, use interrupts from desc ring instead of sring */ + if (wil->ipa_handle) + irq_mode = WMI_RING_ADD_IRQ_MODE_ENABLE; + + rc = wil_wmi_tx_desc_ring_add(vif, ring_id, cid, tid, wil->tx_sring_idx, + irq_mode); if (rc) { wil_err(wil, "WMI_TX_DESC_RING_ADD_CMD failed\n"); goto out_free; } + if (wil->ipa_handle) { + rc = wil_ipa_conn_client(wil->ipa_handle, cid, ring_id, + wil->tx_sring_idx); + if (rc) + goto out_free; + } + if (txdata->dot1x_open && agg_wsize >= 0) wil_addba_tx_request(wil, ring_id, agg_wsize); @@ -1105,10 +1157,8 @@ void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota) wil_rx_refill_edma(wil); } -static int wil_tx_desc_map_edma(union wil_tx_desc *desc, - dma_addr_t pa, - u32 len, - int ring_index) +int wil_tx_desc_map_edma(union wil_tx_desc *desc, dma_addr_t pa, u32 len, + int ring_index) { struct wil_tx_enhanced_desc *d = (struct wil_tx_enhanced_desc *)&desc->enhanced; @@ -1540,18 +1590,32 @@ static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, { struct wil6210_priv *wil = vif_to_wil(vif); struct wil_ring *ring = &wil->ring_tx[ring_id]; - int rc; + int rc, sring_id = wil->tx_sring_idx; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; - wil_dbg_misc(wil, "init bcast: ring_id=%d, sring_id=%d\n", - ring_id, wil->tx_sring_idx); + wil_dbg_misc(wil, "init bcast: ring_id=%d\n", ring_id); lockdep_assert_held(&wil->mutex); + if (wil->ipa_handle) { + /* alloc bcast status ring */ + sring_id = wil_find_free_sring(wil); + if (sring_id < 0) + return sring_id; + + rc = wil_init_tx_sring(wil, WIL_IPA_BCAST_SRING_SIZE, + sizeof(struct wil_ring_tx_status), + sring_id); + if (rc) + return rc; + + wil_ipa_set_bcast_sring_id(wil, sring_id); + } + wil_tx_data_init(txdata); ring->size = size; ring->is_rx = false; - rc = wil_ring_alloc_desc_ring(wil, ring); + rc = wil_ring_alloc_desc_ring(wil, ring, true); if (rc) goto out; @@ -1560,7 +1624,7 @@ static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, if (!vif->privacy) txdata->dot1x_open = true; - rc = wil_wmi_bcast_desc_ring_add(vif, ring_id); + rc = wil_wmi_bcast_desc_ring_add(vif, ring_id, sring_id); if (rc) goto out_free; @@ -1574,16 +1638,28 @@ static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id, wil_ring_free_edma(wil, ring); out: + if (wil->ipa_handle) { + wil_sring_free(wil, &wil->srings[sring_id]); + wil_ipa_set_bcast_sring_id(wil, WIL6210_MAX_STATUS_RINGS); + } + return rc; } static void wil_tx_fini_edma(struct wil6210_priv *wil) { - struct wil_status_ring *sring = &wil->srings[wil->tx_sring_idx]; + int i; - wil_dbg_misc(wil, "free TX sring\n"); + for (i = 0; i < WIL6210_MAX_STATUS_RINGS; i++) { + struct wil_status_ring *sring = &wil->srings[i]; - wil_sring_free(wil, sring); + if (!sring->va || sring->is_rx) + continue; + + wil_dbg_misc(wil, "free TX sring %d\n", i); + + wil_sring_free(wil, sring); + } } static void wil_rx_data_free(struct wil_status_ring *sring) @@ -1600,6 +1676,10 @@ static void wil_rx_fini_edma(struct wil6210_priv *wil) struct wil_ring *ring = &wil->ring_rx; int i; + if (wil->ipa_handle) + /* in ipa offload, this is done part of wil_ipa_uninit */ + return; + wil_dbg_misc(wil, "rx_fini_edma\n"); wil_ring_free_edma(wil, ring); diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index 136c51c338cf..f04d443d9a5d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -1,17 +1,6 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2012-2016,2018-2019, 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. */ #ifndef WIL6210_TXRX_EDMA_H @@ -27,7 +16,6 @@ #define WIL_TX_SRING_SIZE_ORDER_DEFAULT (14) #define WIL_RX_BUFF_ARR_SIZE_DEFAULT (2600) -#define WIL_DEFAULT_RX_STATUS_RING_ID 0 #define WIL_RX_DESC_RING_ID 0 #define WIL_RX_STATUS_IRQ_IDX 0 #define WIL_TX_STATUS_IRQ_IDX 1 @@ -608,6 +596,15 @@ int wil_tx_sring_handler(struct wil6210_priv *wil, struct wil_status_ring *sring); void wil_rx_handle_edma(struct wil6210_priv *wil, int *quota); void wil_init_txrx_ops_edma(struct wil6210_priv *wil); +int wil_find_free_sring(struct wil6210_priv *wil); +int wil_init_rx_desc_ring(struct wil6210_priv *wil, u16 desc_ring_size, + int status_ring_id); +int wil_init_rx_sring(struct wil6210_priv *wil, u16 status_ring_size, + size_t elem_size, u16 ring_id); +void wil_sring_free(struct wil6210_priv *wil, struct wil_status_ring *sring); +void wil_ring_free_edma(struct wil6210_priv *wil, struct wil_ring *ring); +int wil_tx_desc_map_edma(union wil_tx_desc *desc, dma_addr_t pa, u32 len, + int ring_index); #endif /* WIL6210_TXRX_EDMA_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index b2c047745b3f..f4022c780302 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -336,8 +336,7 @@ struct RGF_ICR { #define RGF_INT_COUNT_ON_SPECIAL_EVT (0x8b62d8) -#define RGF_INT_CTRL_INT_GEN_CFG_0 (0x8bc000) -#define RGF_INT_CTRL_INT_GEN_CFG_1 (0x8bc004) +#define RGF_INT_CTRL_INT_GEN_CFG (0x8bc000) #define RGF_INT_GEN_TIME_UNIT_LIMIT (0x8bc0c8) #define RGF_INT_GEN_CTRL (0x8bc0ec) @@ -654,6 +653,7 @@ enum { /* for wil6210_priv.status */ wil_status_suspending, /* suspend in progress */ wil_status_suspended, /* suspend completed, device is suspended */ wil_status_resuming, /* resume in progress */ + wil_status_pci_linkdown, /* pci linkdown occurred */ wil_status_last /* keep last */ }; @@ -999,6 +999,7 @@ struct wil6210_priv { struct wil_status_ring srings[WIL6210_MAX_STATUS_RINGS]; u8 num_rx_status_rings; int tx_sring_idx; + int rx_sring_idx; u8 ring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */ struct wil_sta_info sta[WIL6210_MAX_CID]; u32 ring_idle_trsh; /* HW fetches up to 16 descriptors at once */ @@ -1081,6 +1082,9 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; + + struct work_struct pci_linkdown_recovery_worker; + void *ipa_handle; }; #define wil_to_wiphy(i) (i->wiphy) @@ -1317,6 +1321,8 @@ void wil_configure_interrupt_moderation(struct wil6210_priv *wil); void wil_disable_irq(struct wil6210_priv *wil); void wil_enable_irq(struct wil6210_priv *wil); void wil6210_mask_halp(struct wil6210_priv *wil); +irqreturn_t wil6210_irq_misc(int irq, void *cookie); +irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie); /* P2P */ bool wil_p2p_is_social_scan(struct cfg80211_scan_request *request); @@ -1386,6 +1392,9 @@ void wil_enable_tx_key_worker(struct work_struct *work); void wil_init_txrx_ops(struct wil6210_priv *wil); +void wil_fw_recovery(struct wil6210_priv *wil); +void wil_pci_linkdown_recovery_worker(struct work_struct *work); + /* TX API */ int wil_ring_init_tx(struct wil6210_vif *vif, int cid); int wil_vring_init_bcast(struct wil6210_vif *vif, int id, int size); @@ -1479,14 +1488,15 @@ int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch); int reverse_memcmp(const void *cs, const void *ct, size_t count); /* WMI for enhanced DMA */ -int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id); +int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id, u8 irq_mode); int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, - u16 max_rx_pl_per_desc); + u16 max_rx_pl_per_desc, bool checksum); int wil_wmi_rx_sring_add(struct wil6210_priv *wil, u16 ring_id); int wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id); int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, - int tid); -int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id); + int tid, int sring_id, u8 irq_mode); +int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id, + int sring_id); int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index bca090611477..d3816498c2dc 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -1,17 +1,7 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. - * - * 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. + * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #ifndef __WIL_PLATFORM_H__ @@ -27,6 +17,10 @@ enum wil_platform_event { WIL_PLATFORM_EVT_POST_SUSPEND = 4, }; +enum wil_platform_notif { + WIL_PLATFORM_NOTIF_PCI_LINKDOWN = 0, +}; + enum wil_platform_features { WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0, WIL_PLATFORM_FEATURE_TRIPLE_MSI = 1, @@ -37,6 +31,7 @@ enum wil_platform_capa { WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND = 0, WIL_PLATFORM_CAPA_T_PWR_ON_0 = 1, WIL_PLATFORM_CAPA_EXT_CLK = 2, + WIL_PLATFORM_CAPA_SMMU = 3, WIL_PLATFORM_CAPA_MAX, }; @@ -52,6 +47,7 @@ struct wil_platform_ops { int (*notify)(void *handle, enum wil_platform_event evt); int (*get_capa)(void *handle); void (*set_features)(void *handle, int features); + int (*pci_linkdown_recovery)(void *handle); }; /** @@ -63,10 +59,13 @@ struct wil_platform_ops { * @fw_recovery: start a firmware recovery process. Called as * part of a crash recovery process which may include other * related platform subsystems. + * @notify: get notifications from the Platform driver, such as + * pci linkdown */ struct wil_platform_rops { int (*ramdump)(void *wil_handle, void *buf, uint32_t size); int (*fw_recovery)(void *wil_handle); + int (*notify)(void *wil_handle, enum wil_platform_notif notif); }; /** diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 4a5e5fb3e8f6..c72024d61e06 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -13,6 +13,7 @@ #include "wmi.h" #include "trace.h" #include "ftm.h" +#include "ipa.h" /* set the default max assoc sta to max supported by driver */ uint max_assoc_sta = WIL6210_MAX_CID; @@ -2276,6 +2277,9 @@ int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, return -EOPNOTSUPP; } + if (wil->ipa_handle && cmd.pcp_max_assoc_sta > WIL_IPA_MAX_ASSOC_STA) + cmd.pcp_max_assoc_sta = WIL_IPA_MAX_ASSOC_STA; + if (disable_ap_sme && !test_bit(WMI_FW_CAPABILITY_AP_SME_OFFLOAD_PARTIAL, wil->fw_capabilities)) { @@ -2943,8 +2947,8 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) | (agg_wsize << 6)), .ba_timeout = cpu_to_le16(timeout), - /* route all the connections to status ring 0 */ - .status_ring_id = WIL_DEFAULT_RX_STATUS_RING_ID, + /* route all the connections to same Rx status ring */ + .status_ring_id = wil->rx_sring_idx, }; struct { struct wmi_cmd_hdr wmi; @@ -2956,7 +2960,7 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, wil_dbg_wmi(wil, "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s, sring_id %d\n", cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-", - WIL_DEFAULT_RX_STATUS_RING_ID); + cmd.status_ring_id); rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_EDMA_CMDID, mid, &cmd, sizeof(cmd), WMI_RCP_ADDBA_RESP_SENT_EVENTID, &reply, @@ -3908,7 +3912,7 @@ int wmi_mgmt_tx_ext(struct wil6210_vif *vif, const u8 *buf, size_t len, return rc; } -int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id) +int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id, u8 irq_mode) { int rc; struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); @@ -3917,7 +3921,8 @@ int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id) .ring_cfg = { .ring_size = cpu_to_le16(sring->size), }, - .irq_index = WIL_TX_STATUS_IRQ_IDX + .irq_index = WIL_TX_STATUS_IRQ_IDX, + .irq_mode = irq_mode, }; struct { struct wmi_cmd_hdr hdr; @@ -3948,7 +3953,8 @@ int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id) return 0; } -int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, u16 max_rx_pl_per_desc) +int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, + u16 max_rx_pl_per_desc, bool checksum) { struct net_device *ndev = wil->main_ndev; struct wil6210_vif *vif = ndev_to_vif(ndev); @@ -3958,7 +3964,8 @@ int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, u16 max_rx_pl_per_desc) .max_rx_pl_per_desc = cpu_to_le16(max_rx_pl_per_desc), .decap_trans_type = WMI_DECAP_TYPE_802_3, .l2_802_3_offload_ctrl = 0, - .l3_l4_ctrl = 1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS, + .l3_l4_ctrl = + (checksum ? 1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS : 0), }; struct { struct wmi_cmd_hdr hdr; @@ -4070,10 +4077,9 @@ int wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id) } int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, - int tid) + int tid, int sring_id, u8 irq_mode) { struct wil6210_priv *wil = vif_to_wil(vif); - int sring_id = wil->tx_sring_idx; /* there is only one TX sring */ int rc; struct wil_ring *ring = &wil->ring_tx[ring_id]; struct wil_ring_tx_data *txdata = &wil->ring_tx_data[ring_id]; @@ -4090,7 +4096,9 @@ int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, .schd_params = { .priority = cpu_to_le16(0), .timeslot_us = cpu_to_le16(0xfff), - } + }, + .irq_index = ring_id, + .irq_mode = irq_mode, }; struct { struct wmi_cmd_hdr hdr; @@ -4123,7 +4131,8 @@ int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, return 0; } -int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id) +int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id, + int sring_id) { struct wil6210_priv *wil = vif_to_wil(vif); struct wil_ring *ring = &wil->ring_tx[ring_id]; @@ -4134,7 +4143,7 @@ int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id) .ring_id = ring_id, }, .max_msdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), - .status_ring_id = wil->tx_sring_idx, + .status_ring_id = sring_id, .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, }; struct {