wil6210: support IPA offload

Add support for data path IPA offload in AP mode. Highlights of IPA
offload mode:
Upon start_ap, wil6210 registers with IPA driver and passes Rx ring
info. Upon client connect, wil6210 notifies IPA driver and passes Tx
ring info.
Rx and Tx rings are allocated by wil6210 and later processed by IPA
uC and GSI.
Tx broadcast ring is not offloaded and is processed by wil6210 using
polling timer.
Two Tx status rings are used - one for all unicast and another for
broadcast.
MSI is routed to IPA uC. IPA handles Rx/Tx interrupts. MISC interrupt
is routed back from IPA uC to wil6210 using callback function.

New module param ipa_offload controls this feature, disabled by
default.

Change-Id: I9169401342a9478ca9aa032e8c96b13031778e3a
Signed-off-by: Dedy Lansky <dlansky@codeaurora.org>
Signed-off-by: Alexei Avshalom Lazar <ailizaro@codeaurora.org>
This commit is contained in:
Dedy Lansky 2019-12-11 15:35:58 +02:00 • committed by Alexei Avshalom Lazar
commit 039432d304
16 changed files with 1265 additions and 24 deletions

View file

@ -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

View file

@ -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)

View file

@ -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"
@ -2161,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;
@ -2184,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);
@ -2206,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;
}
@ -2396,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

View file

@ -224,6 +224,9 @@ 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);
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);
@ -531,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;
@ -600,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;

View file

@ -0,0 +1,982 @@
// SPDX-License-Identifier: ISC
/* Copyright (c) 2019 The Linux Foundation. All rights reserved.
*/
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/msi.h>
#include <linux/irq.h>
#include <linux/ipa_wigig.h>
#include <linux/iommu.h>
#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 = &dd;
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);
}

View file

@ -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 <linux/types.h>
#ifdef CONFIG_WIL6210_IPA
#include <linux/msi.h>
#include <linux/msm_ipa.h>
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 */

View file

@ -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;
@ -576,6 +581,10 @@ void wil_fw_recovery(struct wil6210_priv *wil)
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);
@ -624,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");
@ -639,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",

View file

@ -9,6 +9,7 @@
#include <linux/rtnetlink.h>
#include "wil6210.h"
#include "txrx.h"
#include "ipa.h"
static bool alt_ifname; /* = false; */
module_param(alt_ifname, bool, 0444);
@ -179,13 +180,15 @@ static int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget)
int tx_done;
/* There is only one status TX ring */
struct wil_status_ring *sring;
int sring_idx = wil->ipa_handle ?
wil_ipa_get_bcast_sring_id(wil) : wil->tx_sring_idx;
if (wil->tx_sring_idx >= WIL6210_MAX_STATUS_RINGS) {
if (sring_idx >= WIL6210_MAX_STATUS_RINGS) {
napi_complete(napi);
return 0;
}
sring = &wil->srings[wil->tx_sring_idx];
sring = &wil->srings[sring_idx];
if (!sring->va) {
napi_complete(napi);
return 0;
@ -195,7 +198,8 @@ static int wil6210_netdev_poll_tx_edma(struct napi_struct *napi, int budget)
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");
}
@ -354,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));
@ -567,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);

View file

@ -12,6 +12,7 @@
#include "wil6210.h"
#include <linux/rtnetlink.h>
#include <linux/pm_runtime.h>
#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;

View file

@ -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);
@ -2223,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.
@ -2377,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);

View file

@ -694,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 */

View file

@ -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;
@ -120,6 +120,10 @@ static int wil_init_tx_sring(struct wil6210_priv *wil, u16 status_ring_size,
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;
@ -145,7 +149,8 @@ static int wil_tx_init_edma(struct wil6210_priv *wil)
wil->tx_status_ring_order > WIL_SRING_SIZE_ORDER_MAX)
wil->tx_status_ring_order = WIL_TX_SRING_SIZE_ORDER_DEFAULT;
sring_size = 1 << wil->tx_status_ring_order;
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
@ -352,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;
@ -436,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;
@ -490,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);
@ -498,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;
@ -629,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,
@ -742,6 +761,10 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id,
if (!vif->privacy)
txdata->dot1x_open = true;
/* 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) {
@ -749,6 +772,13 @@ static int wil_ring_init_tx_edma(struct wil6210_vif *vif, int ring_id,
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);
@ -1127,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;
@ -1565,11 +1593,25 @@ static int wil_ring_init_bcast_edma(struct wil6210_vif *vif, int ring_id,
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, sring_id);
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;
@ -1596,6 +1638,11 @@ 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;
}
@ -1629,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);

View file

@ -596,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 */

View file

@ -1084,6 +1084,7 @@ struct wil6210_priv {
u32 max_ampdu_size;
struct work_struct pci_linkdown_recovery_worker;
void *ipa_handle;
};
#define wil_to_wiphy(i) (i->wiphy)
@ -1320,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);

View file

@ -31,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,
};

View file

@ -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)) {