Merge "wil6210: support IPA offload"

This commit is contained in:
qctecmdr 2019-12-17 13:15:50 -08:00 • committed by Gerrit - the friendly Code Review server
commit 77a29a8c4d
17 changed files with 1549 additions and 159 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"
@ -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",

View file

@ -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 <linux/interrupt.h>
@ -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;

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

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

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

View file

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

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

View file

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

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

View file

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

View file

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

View file

@ -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);
};
/**

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