From fd04a6d422355f49c9d41ffbde020ccb4949b3ce Mon Sep 17 00:00:00 2001 From: Maya Erez Date: Fri, 11 Jan 2019 16:16:25 +0200 Subject: [PATCH 1/4] wil6210: add support for pci linkdown recovery Some platforms can notify on pci linkdown events. Add pci linkdown recovery flow, invoked upon pci linkdown notification. A new wil6210 status flag is added, wil_status_pci_linkdown, to prevent pci suspend / resume until the recovery is completed. After the pci link is recovered in the platform pci recovery callback the device is being reset and the the interface and AP mode states are restored. Change-Id: I6e0f434d52704120c683c51fb4d664eccca16f01 Signed-off-by: Maya Erez [liord@codeaurora.org: fix merge conflicts] Signed-off-by: Lior David Signed-off-by: Alexei Avshalom Lazar --- drivers/net/wireless/ath/wil6210/main.c | 31 +++-- drivers/net/wireless/ath/wil6210/pcie_bus.c | 108 ++++++++++++++++++ drivers/net/wireless/ath/wil6210/pm.c | 19 ++- drivers/net/wireless/ath/wil6210/wil6210.h | 6 + .../net/wireless/ath/wil6210/wil_platform.h | 22 ++-- 5 files changed, 153 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index d4fb0ecf5bd7..19ba6360b9af 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -524,22 +524,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 + @@ -599,6 +593,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; @@ -711,6 +721,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 +841,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil_set_recovery_state(wil, fw_recovery_idle); cancel_work_sync(&wil->fw_error_worker); + cancel_work_sync(&wil->pci_linkdown_recovery_worker); wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 73520ea23b58..299b98583aac 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -285,6 +285,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 +402,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 +640,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime) struct wil6210_priv *wil = pci_get_drvdata(pdev); bool keep_radio_on, active_ifaces; + if (test_bit(wil_status_pci_linkdown, wil->status)) { + wil_dbg_pm(wil, "ignore resume during pci linkdown\n"); + return 0; + } + wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); mutex_lock(&wil->vif_mutex); diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index 56143e7670ed..34f5ea8e6520 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: ISC /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "wil6210.h" @@ -101,6 +90,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) goto out; } + if (test_bit(wil_status_pci_linkdown, wil->status)) { + wil_dbg_pm(wil, "Delay suspend during pci linkdown\n"); + rc = -EBUSY; + goto out; + } + mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index b2c047745b3f..85c194937986 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -654,6 +654,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 */ }; @@ -1081,6 +1082,8 @@ struct wil6210_priv { u32 max_agg_wsize; u32 max_ampdu_size; + + struct work_struct pci_linkdown_recovery_worker; }; #define wil_to_wiphy(i) (i->wiphy) @@ -1386,6 +1389,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); diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index bca090611477..19e00fa4bf39 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -1,17 +1,7 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * Copyright (c) 2019, The Linux Foundation. All rights reserved. */ #ifndef __WIL_PLATFORM_H__ @@ -27,6 +17,10 @@ enum wil_platform_event { WIL_PLATFORM_EVT_POST_SUSPEND = 4, }; +enum wil_platform_notif { + WIL_PLATFORM_NOTIF_PCI_LINKDOWN = 0, +}; + enum wil_platform_features { WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0, WIL_PLATFORM_FEATURE_TRIPLE_MSI = 1, @@ -52,6 +46,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 +58,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); }; /** From b5e9995cb84a9cb9fea49fc0dd9c3acf30c9f09f Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Mon, 1 Apr 2019 15:04:25 +0300 Subject: [PATCH 2/4] wil6210: support NL_60G_GEN_FW_RESET UNSPEC vendor command Userspace can use this command to reset the FW/HW. Currently this is supported only with WMI_ONLY FW. The recommended way to reset the FW/HW is by interface down/up. However there are cases where this is not allowed, specifically when using WMI_ONLY FW. To overcome this limitation, userspace can use NL_60G_GEN_FW_RESET instead. Change-Id: If047beaa76bcd6c3ce567a10db919a884d5ec89d Signed-off-by: Dedy Lansky --- drivers/net/wireless/ath/wil6210/cfg80211.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 4d8ac6ee0f7b..00029844afca 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -139,6 +139,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 { @@ -3707,6 +3709,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", From ba4295417874409acda58b9fba0c57af151aa278 Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 4 Dec 2019 10:20:40 +0200 Subject: [PATCH 3/4] wil6210: support multiple desc and status rings handling Support multiple status rings: - Move Tx status ring initialization code to separate function - Add status ring id argument to relevant functions instead of relaying on wil->tx_sring_idx - Store allocated Rx status ring id in wil->rx_sring_idx and use it instead of hard coded WIL_DEFAULT_RX_STATUS_RING_ID - Configure interrupt moderation for multiple rings - Allow allocation of descriptor ring without ctx array - Set irq_mode upon Tx ring allocation Enhance wil_wmi_cfg_def_rx_offload to enable/disable checksum offload. Change-Id: If7d346dec2c1ba35cf377235d4c520eca11be166 Signed-off-by: Dedy Lansky Signed-off-by: Alexei Avshalom Lazar --- drivers/net/wireless/ath/wil6210/interrupt.c | 20 +-- drivers/net/wireless/ath/wil6210/netdev.c | 12 +- drivers/net/wireless/ath/wil6210/txrx.c | 6 - drivers/net/wireless/ath/wil6210/txrx.h | 19 ++- drivers/net/wireless/ath/wil6210/txrx_edma.c | 121 ++++++++++++------- drivers/net/wireless/ath/wil6210/txrx_edma.h | 14 +-- drivers/net/wireless/ath/wil6210/wil6210.h | 13 +- drivers/net/wireless/ath/wil6210/wmi.c | 29 +++-- 8 files changed, 122 insertions(+), 112 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index b00a13d6d530..ea6af82c5495 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -1,18 +1,7 @@ +// SPDX-License-Identifier: ISC /* * Copyright (c) 2012-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include @@ -44,7 +33,7 @@ (~(BIT_DMA_EP_RX_ICR_RX_HTRSH))) #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) -#define WIL6210_IMC_TX_EDMA BIT_TX_STATUS_IRQ +#define WIL6210_IMC_TX_EDMA (0xFFFFFFFFUL) #define WIL6210_IMC_RX_EDMA BIT_RX_STATUS_IRQ #define WIL6210_IMC_MISC_NO_HALP (ISR_MISC_FW_READY | \ ISR_MISC_MBOX_EVT | \ @@ -226,6 +215,7 @@ void wil_unmask_irq(struct wil6210_priv *wil) void wil_configure_interrupt_moderation_edma(struct wil6210_priv *wil) { u32 moderation; + int i, num_int_lines = 2 /* Rx + Tx status */; wil_s(wil, RGF_INT_GEN_IDLE_TIME_LIMIT, WIL_EDMA_IDLE_TIME_LIMIT_USEC); @@ -234,8 +224,8 @@ 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); + 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) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 408bd7d57a5a..9d3555422b3c 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -178,10 +178,18 @@ 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; - if (!sring->va) + if (wil->tx_sring_idx >= WIL6210_MAX_STATUS_RINGS) { + napi_complete(napi); return 0; + } + + sring = &wil->srings[wil->tx_sring_idx]; + if (!sring->va) { + napi_complete(napi); + return 0; + } tx_done = wil_tx_sring_handler(wil, sring); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 84d72955fa56..f3729f49d50d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1636,12 +1636,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. diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 5120475b0cd7..337f0ffa6fe8 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -1,18 +1,7 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2012-2016 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef WIL6210_TXRX_H @@ -685,6 +674,12 @@ static inline void wil_skb_set_cid(struct sk_buff *skb, u8 cid) skb_rx_info->rx_info.cid = cid; } +static inline +void wil_tx_desc_set_nr_frags(struct vring_tx_desc *d, int nr_frags) +{ + d->mac.d[2] |= (nr_frags << MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +} + void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); void wil_netif_rx(struct sk_buff *skb, struct net_device *ndev, int cid, struct wil_net_stats *stats, bool gro); diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 122873843227..86689a1fb400 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -103,47 +103,58 @@ 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; + + 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 = 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; } @@ -374,7 +385,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 +397,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) @@ -490,7 +508,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; @@ -642,7 +660,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 +676,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 +723,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 +733,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,7 +742,8 @@ 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); + 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; @@ -1540,18 +1562,18 @@ 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); + ring_id, sring_id); lockdep_assert_held(&wil->mutex); 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 +1582,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; @@ -1579,11 +1601,18 @@ out: 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) diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index 136c51c338cf..c6dd2a0b1247 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -1,17 +1,6 @@ +/* SPDX-License-Identifier: ISC */ /* * Copyright (c) 2012-2016,2018-2019, The Linux Foundation. All rights reserved. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef WIL6210_TXRX_EDMA_H @@ -27,7 +16,6 @@ #define WIL_TX_SRING_SIZE_ORDER_DEFAULT (14) #define WIL_RX_BUFF_ARR_SIZE_DEFAULT (2600) -#define WIL_DEFAULT_RX_STATUS_RING_ID 0 #define WIL_RX_DESC_RING_ID 0 #define WIL_RX_STATUS_IRQ_IDX 0 #define WIL_TX_STATUS_IRQ_IDX 1 diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 85c194937986..a9962853f4ae 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -336,8 +336,7 @@ struct RGF_ICR { #define RGF_INT_COUNT_ON_SPECIAL_EVT (0x8b62d8) -#define RGF_INT_CTRL_INT_GEN_CFG_0 (0x8bc000) -#define RGF_INT_CTRL_INT_GEN_CFG_1 (0x8bc004) +#define RGF_INT_CTRL_INT_GEN_CFG (0x8bc000) #define RGF_INT_GEN_TIME_UNIT_LIMIT (0x8bc0c8) #define RGF_INT_GEN_CTRL (0x8bc0ec) @@ -1000,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 */ @@ -1485,14 +1485,15 @@ int wil_spec2wmi_ch(u8 spec_ch, u8 *wmi_ch); int reverse_memcmp(const void *cs, const void *ct, size_t count); /* WMI for enhanced DMA */ -int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id); +int wil_wmi_tx_sring_cfg(struct wil6210_priv *wil, int ring_id, u8 irq_mode); int wil_wmi_cfg_def_rx_offload(struct wil6210_priv *wil, - u16 max_rx_pl_per_desc); + u16 max_rx_pl_per_desc, bool checksum); int wil_wmi_rx_sring_add(struct wil6210_priv *wil, u16 ring_id); int wil_wmi_rx_desc_ring_add(struct wil6210_priv *wil, int status_ring_id); int wil_wmi_tx_desc_ring_add(struct wil6210_vif *vif, int ring_id, int cid, - int tid); -int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id); + int tid, int sring_id, u8 irq_mode); +int wil_wmi_bcast_desc_ring_add(struct wil6210_vif *vif, int ring_id, + int sring_id); int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid, u8 tid, u8 token, u16 status, bool amsdu, u16 agg_wsize, u16 timeout); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 4a5e5fb3e8f6..2098d67e5f34 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -2943,8 +2943,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 +2956,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 +3908,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 +3917,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 +3949,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 +3960,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 +4073,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 +4092,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 +4127,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 +4139,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 { From 039432d304bbab2a5f083eca3f3cbc8270d1065c Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Wed, 11 Dec 2019 15:35:58 +0200 Subject: [PATCH 4/4] 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 Signed-off-by: Alexei Avshalom Lazar --- drivers/net/wireless/ath/wil6210/Kconfig | 9 + drivers/net/wireless/ath/wil6210/Makefile | 1 + drivers/net/wireless/ath/wil6210/cfg80211.c | 31 + drivers/net/wireless/ath/wil6210/interrupt.c | 7 +- drivers/net/wireless/ath/wil6210/ipa.c | 982 ++++++++++++++++++ drivers/net/wireless/ath/wil6210/ipa.h | 90 ++ drivers/net/wireless/ath/wil6210/main.c | 15 +- drivers/net/wireless/ath/wil6210/netdev.c | 15 +- drivers/net/wireless/ath/wil6210/pcie_bus.c | 9 + drivers/net/wireless/ath/wil6210/txrx.c | 27 + drivers/net/wireless/ath/wil6210/txrx.h | 1 + drivers/net/wireless/ath/wil6210/txrx_edma.c | 85 +- drivers/net/wireless/ath/wil6210/txrx_edma.h | 9 + drivers/net/wireless/ath/wil6210/wil6210.h | 3 + .../net/wireless/ath/wil6210/wil_platform.h | 1 + drivers/net/wireless/ath/wil6210/wmi.c | 4 + 16 files changed, 1265 insertions(+), 24 deletions(-) create mode 100644 drivers/net/wireless/ath/wil6210/ipa.c create mode 100644 drivers/net/wireless/ath/wil6210/ipa.h diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 0d1a8dab30ed..d3f4cbbc630c 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -53,3 +53,12 @@ config WIL6210_DEBUGFS option if you are interested in debugging the driver. If unsure, say Y to make it easier to debug problems. + +config WIL6210_IPA + bool "wil6210 IPA offload support" + depends on WIL6210 + depends on ARCH_QCOM + default y + help + Say Y here to enable wil6210 driver support for data path + IPA offload diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 989b947c2e34..2a7683fbe7c0 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -22,6 +22,7 @@ wil6210-y += ethtool.o wil6210-y += wil_crash_dump.o wil6210-y += p2p.o wil6210-y += ftm.o +wil6210-$(CONFIG_WIL6210_IPA) += ipa.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 00029844afca..b612cdfc5a31 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -12,6 +12,7 @@ #include "wmi.h" #include "ftm.h" #include "fw.h" +#include "ipa.h" #define WIL_MAX_ROC_DURATION_MS 5000 #define WIL_BRD_SUFFIX_CN "CN" @@ -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 diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index ea6af82c5495..09f69ffa4625 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -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; diff --git a/drivers/net/wireless/ath/wil6210/ipa.c b/drivers/net/wireless/ath/wil6210/ipa.c new file mode 100644 index 000000000000..1148d837b20b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/ipa.c @@ -0,0 +1,982 @@ +// SPDX-License-Identifier: ISC +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include "wil6210.h" +#include "txrx_edma.h" +#include "txrx.h" +#include "ipa.h" + +#define WIL_IPA_MSI_CAP (0x50) +#define WIL_IPA_MSI_MSG_DATA 0xCAFE + +#define WIL_IPA_BCAST_POLL_MS 20 +#define WIL_IPA_READY_TO_MS 2000 + +/* IPA requires power of 2 buffer size */ +#define WIL_IPA_RX_BUFF_SIZE (8 * 1024) +#define WIL_IPA_TX_BUFF_SIZE (2 * 1024) + +u8 ipa_offload; +module_param(ipa_offload, byte, 0444); +MODULE_PARM_DESC(ipa_offload, " Enable IPA offload, default - disabled"); + +/* template IP headers */ +static struct ethhdr wil_ipa_v4_hdr = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}, + {0x01, 0x02, 0x03, 0x04, 0x05, 0x07}, + 0x0008 +}; + +static struct ethhdr wil_ipa_v6_hdr = { + {0x01, 0x02, 0x03, 0x04, 0x05, 0x08}, + {0x01, 0x02, 0x03, 0x04, 0x05, 0x09}, + 0xDD86 +}; + +int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + + return ipa->bcast_sring_id; +} + +void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, int bcast_sring_id) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + + ipa->bcast_sring_id = bcast_sring_id; +} + +static phys_addr_t wil_ipa_get_bar_base_pa(struct wil6210_priv *wil) +{ + struct pci_dev *pdev = wil->pdev; + struct resource *res = &pdev->resource[0]; + + return res->start; +} + +static void +wil_ipa_calc_head_tail(struct wil6210_priv *wil, int ring_id, int sring_id, + phys_addr_t *desc_hwhead, phys_addr_t *desc_hwtail, + phys_addr_t *status_hwhead, phys_addr_t *status_hwtail) +{ + phys_addr_t bar_base_pa = wil_ipa_get_bar_base_pa(wil); + + /* HWTAIL / HWHEAD calculation: + * for descriptor rings: + * 1. SW should update the SUBQ producer pointer by writing to + * SUBQ_RD_PTR (table of 64 entries, one for each Q), to update the + * HW that there are descriptors waiting + * 2. SW should read the SUBQ_CONS information, in order to track the + * used registers. SUBQ_CONS is a table of 32 entries, one for + * each Q pair. lower 16bits are for even ring_id and upper 16bits + * are for odd ring_id + * for status rings: + * 1. SW should read the COMPQ_PROD information + * 2. SW should write the COMPQ_RD_PTR which is actually the completion + * queue consumer pointer + */ + + *desc_hwhead = bar_base_pa + + HOSTADDR(RGF_DMA_SCM_SUBQ_CONS + 4 * (ring_id / 2)); + *status_hwhead = bar_base_pa + + HOSTADDR(RGF_DMA_SCM_COMPQ_PROD + 4 * (sring_id / 2)); + + *desc_hwtail = bar_base_pa + + HOSTADDR(RGF_SCM_PTRS_SUBQ_RD_PTR + 4 * ring_id); + *status_hwtail = bar_base_pa + + HOSTADDR(RGF_SCM_PTRS_COMPQ_RD_PTR + 4 * sring_id); + + wil_dbg_misc(wil, + "desc_hwhead %pad desc_hwtail %pad status_hwhead %pad status_hwtail %pad\n", + desc_hwhead, desc_hwtail, status_hwhead, status_hwtail); +} + +static void wil_ipa_rx(struct wil_ipa *ipa, struct sk_buff *skb) +{ + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct wil6210_vif *vif = ndev_to_vif(ndev); + int cid, len = skb->len; + u8 *sa = wil_skb_get_sa(skb); + struct wil_net_stats *stats; + + wil_dbg_txrx(wil, "ipa_rx %d bytes\n", len); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + if (unlikely(len < ETH_HLEN)) { + wil_err(wil, "ipa rx packet too short %d\n", len); + goto drop; + } + + cid = wil_find_cid(wil, vif->mid, sa); + if (unlikely(cid < 0)) { + wil_err_ratelimited(wil, "cid not found for sa %pM\n", sa); + goto drop; + } + + stats = &wil->sta[cid].stats; + + wil_netif_rx(skb, ndev, cid, stats, false); + + return; + +drop: + dev_kfree_skb(skb); + ndev->stats.rx_dropped++; +} + +static void wil_ipa_notify_cb(void *priv, enum ipa_dp_evt_type evt, + unsigned long data) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct sk_buff *skb; + + switch (evt) { + case IPA_RECEIVE: + skb = (struct sk_buff *)data; + wil_ipa_rx(ipa, skb); + break; + default: + wil_dbg_misc(ipa->wil, "unhandled ipa evt %d\n", evt); + break; + } +} + +static int wil_ipa_wigig_conn_rx_pipe_smmu(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_conn_rx_in_params_smmu in = { + .notify = wil_ipa_notify_cb, + .priv = ipa, + }; + struct ipa_wigig_conn_out_params out = {0}; + int rc; + struct wil_ring *rx_ring = &wil->ring_rx; + struct wil_status_ring *rx_sring = &wil->srings[wil->rx_sring_idx]; + + rc = dma_get_sgtable(dev, &in.pipe_smmu.desc_ring_base, + (void *)rx_ring->va, rx_ring->pa, + rx_ring->size * sizeof(rx_ring->va[0])); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for desc ring failed %d\n", rc); + return rc; + } + + in.pipe_smmu.desc_ring_base_iova = rx_ring->pa; + in.pipe_smmu.desc_ring_size = rx_ring->size * sizeof(rx_ring->va[0]); + + rc = dma_get_sgtable(dev, &in.pipe_smmu.status_ring_base, + rx_sring->va, rx_sring->pa, + rx_sring->size * rx_sring->elem_size); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for status ring failed %d\n", rc); + return rc; + } + in.pipe_smmu.status_ring_base_iova = rx_sring->pa; + in.pipe_smmu.status_ring_size = rx_sring->size * rx_sring->elem_size; + + rc = dma_get_sgtable(dev, &in.dbuff_smmu.data_buffer_base, + ipa->rx_buf.va, ipa->rx_buf.pa, ipa->rx_buf.sz); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for data buffer failed %d\n", rc); + return rc; + } + + wil_ipa_calc_head_tail(wil, WIL_RX_DESC_RING_ID, wil->rx_sring_idx, + &in.pipe_smmu.desc_ring_HWHEAD_pa, + &in.pipe_smmu.desc_ring_HWTAIL_pa, + &in.pipe_smmu.status_ring_HWHEAD_pa, + &in.pipe_smmu.status_ring_HWTAIL_pa); + + in.dbuff_smmu.data_buffer_base_iova = ipa->rx_buf.pa; + in.dbuff_smmu.data_buffer_size = wil->rx_buf_len; + + rc = ipa_wigig_conn_rx_pipe_smmu(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_rx_pipe_smmu failed %d\n", rc); + return rc; + } + + ipa->rx_client_type = out.client; + + return 0; +} + +static int wil_ipa_wigig_conn_rx_pipe(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct ipa_wigig_conn_rx_in_params in = { + .notify = wil_ipa_notify_cb, + .priv = ipa, + }; + struct ipa_wigig_conn_out_params out = {0}; + int rc; + struct wil_ring *rx_ring = &wil->ring_rx; + struct wil_status_ring *rx_sring = &wil->srings[wil->rx_sring_idx]; + + in.pipe.desc_ring_base_pa = rx_ring->pa; + in.pipe.desc_ring_size = rx_ring->size * sizeof(rx_ring->va[0]); + in.pipe.status_ring_base_pa = rx_sring->pa; + in.pipe.status_ring_size = rx_sring->size * rx_sring->elem_size; + + wil_ipa_calc_head_tail(wil, WIL_RX_DESC_RING_ID, wil->rx_sring_idx, + &in.pipe.desc_ring_HWHEAD_pa, + &in.pipe.desc_ring_HWTAIL_pa, + &in.pipe.status_ring_HWHEAD_pa, + &in.pipe.status_ring_HWTAIL_pa); + + in.dbuff.data_buffer_base_pa = ipa->rx_buf.pa; + in.dbuff.data_buffer_size = wil->rx_buf_len; + + wil_dbg_misc(wil, + "calling ipa_wigig_conn_rx_pipe, desc_ring_base_pa %pad status_ring_base_pa %pad data_buffer_base_pa %pad\n", + &in.pipe.desc_ring_base_pa, &in.pipe.status_ring_base_pa, + &in.dbuff.data_buffer_base_pa); + + rc = ipa_wigig_conn_rx_pipe(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_rx_pipe failed %d\n", rc); + return rc; + } + + ipa->rx_client_type = out.client; + + return 0; +} + +static int wil_ipa_wigig_reg_intf(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct ipa_wigig_reg_intf_in_params in = { + .netdev_name = ndev->name, + }; + int rc; + + ether_addr_copy(in.netdev_mac, ndev->dev_addr); + ether_addr_copy(wil_ipa_v4_hdr.h_source, ndev->dev_addr); + ether_addr_copy(wil_ipa_v6_hdr.h_source, ndev->dev_addr); + + in.hdr_info[IPA_IP_v4].hdr = (u8 *)&wil_ipa_v4_hdr; + in.hdr_info[IPA_IP_v4].hdr_len = sizeof(wil_ipa_v4_hdr); + in.hdr_info[IPA_IP_v4].dst_mac_addr_offset = + offsetof(struct ethhdr, h_dest); + in.hdr_info[IPA_IP_v4].hdr_type = IPA_HDR_L2_ETHERNET_II; + in.hdr_info[IPA_IP_v6].hdr = (u8 *)&wil_ipa_v6_hdr; + in.hdr_info[IPA_IP_v6].hdr_len = sizeof(wil_ipa_v6_hdr); + in.hdr_info[IPA_IP_v6].dst_mac_addr_offset = + offsetof(struct ethhdr, h_dest); + in.hdr_info[IPA_IP_v6].hdr_type = IPA_HDR_L2_ETHERNET_II; + + rc = ipa_wigig_reg_intf(&in); + if (rc) + wil_err(wil, "ipa_wigig_reg_intf failed %d\n", rc); + + return rc; +} + +static void wil_ipa_set_ring_buf(struct wil6210_priv *wil, + struct wil_ring *ring, u32 i) +{ + struct wil_ipa *ipa = (struct wil_ipa *)wil->ipa_handle; + size_t sz = wil->rx_buf_len; + dma_addr_t pa; + struct wil_rx_enhanced_desc dd, *d = ⅆ + struct wil_rx_enhanced_desc *_d = (struct wil_rx_enhanced_desc *) + &ring->va[i].rx.enhanced; + + memset(&dd, 0, sizeof(dd)); + + pa = ipa->rx_buf.pa + i * sz; + + wil_desc_set_addr_edma(&d->dma.addr, &d->dma.addr_high_high, pa); + d->dma.length = cpu_to_le16(sz); + d->mac.buff_id = cpu_to_le16(i); + *_d = *d; +} + +static void wil_ipa_rx_refill(struct wil6210_priv *wil) +{ + struct wil_ring *ring = &wil->ring_rx; + int i; + + for (i = 0; i < ring->size; i++) + wil_ipa_set_ring_buf(wil, ring, i); + ring->swhead = ring->size - 1; + + /* commit to HW */ + wil_w(wil, ring->hwtail, ring->swhead); +} + +static int wil_ipa_rx_alloc(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + u16 status_ring_size, desc_ring_size; + struct wil_ring *ring = &wil->ring_rx; + int rc, sring_id; + size_t elem_size = sizeof(struct wil_rx_status_compressed); + + /* in IPA offload must use compressed_rx_status and hw_reordering */ + if (!wil->use_compressed_rx_status || !wil->use_rx_hw_reordering) { + wil_err(wil, "invalid config for IPA offload (compressed %d, hw_reorder %d)\n", + wil->use_compressed_rx_status, + wil->use_rx_hw_reordering); + return -EINVAL; + } + + if (wil->num_rx_status_rings != 1) { + wil_err(wil, "invalid config for IPA offload (num_rx_status_rings %d)\n", + wil->num_rx_status_rings); + return -EINVAL; + } + + /* hard coded sizes for IPA offload */ + desc_ring_size = WIL_IPA_DESC_RING_SIZE; + status_ring_size = WIL_IPA_STATUS_RING_SIZE; + wil->rx_buf_len = WIL_IPA_RX_BUFF_SIZE; + + wil_dbg_misc(wil, + "rx_alloc, desc_ring_size=%u, status_ring_size=%u, elem_size=%zu\n", + desc_ring_size, status_ring_size, elem_size); + + rc = wil_wmi_cfg_def_rx_offload(wil, wil->rx_buf_len, false); + if (rc) + return rc; + + /* Allocate status ring */ + sring_id = wil_find_free_sring(wil); + if (sring_id < 0) + return -EFAULT; + wil->rx_sring_idx = sring_id; + rc = wil_init_rx_sring(wil, status_ring_size, elem_size, sring_id); + if (rc) + return rc; + + /* Allocate descriptor ring */ + rc = wil_init_rx_desc_ring(wil, desc_ring_size, wil->rx_sring_idx); + if (rc) + goto err_free_status; + + /* Allocate contiguous memory for Rx buffers, zero initialized */ + ipa->rx_buf.sz = desc_ring_size * wil->rx_buf_len; + ipa->rx_buf.va = + dma_alloc_attrs(dev, ipa->rx_buf.sz, &ipa->rx_buf.pa, + GFP_KERNEL | __GFP_ZERO, + DMA_ATTR_FORCE_CONTIGUOUS); + if (!ipa->rx_buf.va) { + rc = -ENOMEM; + goto err_free_desc; + } + + /* Fill descriptor ring with credits */ + wil_ipa_rx_refill(wil); + + return 0; + +err_free_desc: + wil_ring_free_edma(wil, ring); +err_free_status: + wil_sring_free(wil, &wil->srings[sring_id]); + + return rc; +} + +static void wil_ipa_rx_free(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = (struct wil6210_priv *)ipa->wil; + struct wil_ring *ring = &wil->ring_rx; + + wil_dbg_misc(wil, "ipa_rx_free\n"); + + wil_ring_free_edma(wil, ring); + wil_sring_free(wil, &wil->srings[wil->rx_sring_idx]); + + if (ipa->rx_buf.va) { + dma_free_attrs(wil_to_dev(wil), ipa->rx_buf.sz, + ipa->rx_buf.va, ipa->rx_buf.pa, + DMA_ATTR_FORCE_CONTIGUOUS); + ipa->rx_buf.va = NULL; + } +} + +int wil_ipa_start_ap(void *ipa_handle) +{ + struct wil_ipa *ipa = ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct pci_dev *pdev = wil->pdev; + struct msi_msg msi_msg = {0}; + int rc; + u16 msi_data; + + /* store original MSI info to be restored upon uninit */ + if (pci_read_config_dword(pdev, WIL_IPA_MSI_CAP + PCI_MSI_ADDRESS_LO, + &ipa->orig_msi_msg.address_lo) || + pci_read_config_dword(pdev, WIL_IPA_MSI_CAP + PCI_MSI_ADDRESS_HI, + &ipa->orig_msi_msg.address_hi) || + pci_read_config_word(pdev, WIL_IPA_MSI_CAP + PCI_MSI_DATA_64, + &msi_data)) { + wil_err(wil, "fail to read MSI address\n"); + return -EINVAL; + } + ipa->orig_msi_msg.data = msi_data; + + rc = wil_ipa_rx_alloc(ipa); + if (rc) + return rc; + + if (ipa->smmu_enabled) + rc = wil_ipa_wigig_conn_rx_pipe_smmu(ipa); + else + rc = wil_ipa_wigig_conn_rx_pipe(ipa); + if (rc) + goto rx_free; + + rc = ipa_wigig_set_perf_profile(WIL_MAX_BUS_REQUEST_KBPS * 8 / 1024); + if (rc) { + wil_err(wil, "ipa_wigig_set_perf_profile failed %d\n", rc); + goto err_disconn; + } + + rc = ipa_wigig_enable_pipe(ipa->rx_client_type); + if (rc) + goto err_disconn; + + rc = wil_ipa_wigig_reg_intf(ipa); + if (rc) + goto err_disable; + + wil_info(wil, "ipa intf registered. client %d\n", ipa->rx_client_type); + + /* route MSI to IPA uC */ + msi_msg.address_lo = lower_32_bits(ipa->uc_db_pa); + msi_msg.address_hi = upper_32_bits(ipa->uc_db_pa); + msi_msg.data = WIL_IPA_MSI_MSG_DATA; + pci_write_msi_msg(pdev->irq, &msi_msg); + + return 0; + +err_disable: + ipa_wigig_disable_pipe(ipa->rx_client_type); +err_disconn: + ipa_wigig_disconn_pipe(ipa->rx_client_type); + ipa->rx_client_type = IPA_CLIENT_MAX; +rx_free: + wil_ipa_rx_free(ipa); + + return rc; +} + +static int wil_ipa_wigig_conn_client_smmu(struct wil_ipa *ipa, int cid, + int ring_id, int sring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_conn_tx_in_params_smmu in = { { {0} } }; + struct ipa_wigig_conn_out_params out = {0}; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_status_ring *sring = &wil->srings[sring_id]; + struct wil_sta_info *sta = &wil->sta[cid]; + struct wil_ipa_conn *ipa_conn = &ipa->conn[cid]; + int rc, i; + size_t sz; + + rc = dma_get_sgtable(dev, &in.pipe_smmu.desc_ring_base, + (void *)ring->va, ring->pa, + ring->size * sizeof(ring->va[0])); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for desc ring failed %d\n", rc); + return rc; + } + + in.pipe_smmu.desc_ring_base_iova = ring->pa; + in.pipe_smmu.desc_ring_size = ring->size * sizeof(ring->va[0]); + + rc = dma_get_sgtable(dev, &in.pipe_smmu.status_ring_base, + sring->va, sring->pa, + sring->size * sring->elem_size); + if (rc < 0) { + wil_err(wil, "dma_get_sgtable for status ring failed %d\n", rc); + return rc; + } + in.pipe_smmu.status_ring_base_iova = sring->pa; + in.pipe_smmu.status_ring_size = sring->size * sring->elem_size; + + wil_ipa_calc_head_tail(wil, ring_id, sring_id, + &in.pipe_smmu.desc_ring_HWHEAD_pa, + &in.pipe_smmu.desc_ring_HWTAIL_pa, + &in.pipe_smmu.status_ring_HWHEAD_pa, + &in.pipe_smmu.status_ring_HWTAIL_pa); + + sz = ring->size * sizeof(in.dbuff_smmu.data_buffer_base[0]); + in.dbuff_smmu.data_buffer_base = kzalloc(sz, GFP_KERNEL); + if (!in.dbuff_smmu.data_buffer_base) + return -ENOMEM; + sz = ring->size * sizeof(in.dbuff_smmu.data_buffer_base_iova[0]); + in.dbuff_smmu.data_buffer_base_iova = kzalloc(sz, GFP_KERNEL); + if (!in.dbuff_smmu.data_buffer_base_iova) { + rc = -ENOMEM; + goto out; + } + + in.dbuff_smmu.num_buffers = ring->size; + for (i = 0; i < in.dbuff_smmu.num_buffers; i++) { + struct wil_dma_map_info *tx_buf = &ipa_conn->tx_bufs_addr[i]; + + rc = dma_get_sgtable(dev, &in.dbuff_smmu.data_buffer_base[i], + tx_buf->va, tx_buf->pa, + WIL_IPA_TX_BUFF_SIZE); + if (rc < 0) { + wil_err(wil, "sgtable tx buf %d failed %d\n", i, rc); + goto out; + } + in.dbuff_smmu.data_buffer_base_iova[i] = tx_buf->pa; + } + in.dbuff_smmu.data_buffer_size = WIL_IPA_TX_BUFF_SIZE; + + in.int_gen_tx_bit_num = ring_id; + ether_addr_copy(in.client_mac, sta->addr); + + rc = ipa_wigig_conn_client_smmu(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_client failed %d\n", rc); + goto out; + } + + ipa->conn[cid].ipa_client = out.client; + +out: + kfree(in.dbuff_smmu.data_buffer_base); + kfree(in.dbuff_smmu.data_buffer_base_iova); + + return rc; +} + +static int wil_ipa_wigig_conn_client(struct wil_ipa *ipa, int cid, int ring_id, + int sring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct ipa_wigig_conn_tx_in_params in = { {0} }; + struct ipa_wigig_conn_out_params out = {0}; + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_status_ring *sring = &wil->srings[sring_id]; + struct wil_sta_info *sta = &wil->sta[cid]; + int rc; + + in.pipe.desc_ring_base_pa = ring->pa; + in.pipe.desc_ring_size = ring->size * sizeof(ring->va[0]); + in.pipe.status_ring_base_pa = sring->pa; + in.pipe.status_ring_size = sring->size * sring->elem_size; + + wil_ipa_calc_head_tail(wil, ring_id, sring_id, + &in.pipe.desc_ring_HWHEAD_pa, + &in.pipe.desc_ring_HWTAIL_pa, + &in.pipe.status_ring_HWHEAD_pa, + &in.pipe.status_ring_HWTAIL_pa); + + in.dbuff.data_buffer_size = WIL_IPA_TX_BUFF_SIZE; + + in.int_gen_tx_bit_num = ring_id; + ether_addr_copy(in.client_mac, sta->addr); + + rc = ipa_wigig_conn_client(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_conn_client failed %d\n", rc); + return rc; + } + + ipa->conn[cid].ipa_client = out.client; + + return 0; +} + +static void wil_ipa_free_tx_bufs(struct wil_ipa *ipa, int cid) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct wil_ipa_conn *conn = &ipa->conn[cid]; + int i; + + for (i = 0; i < conn->tx_bufs_count; i++) { + struct wil_dma_map_info *tx_buf_addr = &conn->tx_bufs_addr[i]; + + if (tx_buf_addr->va) + dma_free_coherent(dev, WIL_IPA_TX_BUFF_SIZE, + tx_buf_addr->va, tx_buf_addr->pa); + } + + kfree(conn->tx_bufs_addr); + conn->tx_bufs_addr = NULL; +} + +static int wil_ipa_alloc_tx_bufs(struct wil_ipa *ipa, int cid, int ring_id) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct wil_ring *ring = &wil->ring_tx[ring_id]; + struct wil_ipa_conn *conn = &ipa->conn[cid]; + size_t sz; + int rc, i; + + sz = ring->size * sizeof(conn->tx_bufs_addr[0]); + conn->tx_bufs_addr = kzalloc(sz, GFP_KERNEL); + if (!conn->tx_bufs_addr) + return -ENOMEM; + + conn->tx_bufs_count = ring->size; + for (i = 0; i < ring->size; i++) { + struct wil_tx_enhanced_desc *d; + + /* note: IPA requires Tx buffers to be 256B aligned */ + conn->tx_bufs_addr[i].va = + dma_alloc_coherent(dev, WIL_IPA_TX_BUFF_SIZE, + &conn->tx_bufs_addr[i].pa, + GFP_KERNEL | __GFP_ZERO); + if (!conn->tx_bufs_addr[i].va) { + wil_err(wil, "tx buf DMA alloc error (i %d)\n", i); + rc = -ENOMEM; + goto err; + } + + d = (struct wil_tx_enhanced_desc *)&ring->va[i].tx.enhanced; + wil_tx_desc_map_edma((union wil_tx_desc *)d, + conn->tx_bufs_addr[i].pa, + WIL_IPA_TX_BUFF_SIZE, ring_id); + wil_tx_desc_set_nr_frags(&((union wil_tx_desc *)d)->legacy, 1); + } + + return 0; + +err: + wil_ipa_free_tx_bufs(ipa, cid); + + return rc; +} + +int wil_ipa_conn_client(void *ipa_handle, int cid, int ring_id, int sring_id) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct wil_ipa_conn *ipa_conn; + int rc; + + wil_info(wil, "ipa connect cid %d ring_id %d sring_id %d\n", + cid, ring_id, sring_id); + + rc = wil_ipa_alloc_tx_bufs(ipa, cid, ring_id); + if (rc) + return rc; + + if (ipa->smmu_enabled) + rc = wil_ipa_wigig_conn_client_smmu(ipa, cid, ring_id, + sring_id); + else + rc = wil_ipa_wigig_conn_client(ipa, cid, ring_id, sring_id); + if (rc) + goto err_free; + + ipa_conn = &ipa->conn[cid]; + rc = ipa_wigig_enable_pipe(ipa_conn->ipa_client); + if (rc) + goto err_disconn; + + wil_dbg_misc(wil, "ipa pipe enabled (client %d)\n", + ipa_conn->ipa_client); + + return 0; + +err_disconn: + ipa_wigig_disconn_pipe(ipa_conn->ipa_client); + ipa_conn->ipa_client = IPA_CLIENT_MAX; + +err_free: + wil_ipa_free_tx_bufs(ipa, cid); + + return rc; +} + +static void wil_ipa_start_bcast_timer(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + + if (timer_pending(&ipa->bcast_timer)) + return; + + wil_dbg_txrx(wil, "ipa start bcast timer\n"); + + mod_timer(&ipa->bcast_timer, + jiffies + msecs_to_jiffies(WIL_IPA_BCAST_POLL_MS)); +} + +int wil_ipa_tx(void *ipa_handle, struct wil_ring *ring, struct sk_buff *skb) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct net_device *ndev = wil->main_ndev; + struct wil_ipa_conn *ipa_conn; + struct wil_net_stats *stats; + int cid, rc; + const u8 *da; + unsigned int len = skb->len; + + wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb_headlen(skb), false); + + da = wil_skb_get_da(skb); + if (is_multicast_ether_addr(da)) { + wil_ipa_start_bcast_timer(ipa); + /* let wil_start_xmit() continue handling this (mcast) packet */ + return -EPROTONOSUPPORT; + } + + cid = wil_get_cid_by_ring(wil, ring); + if (cid >= max_assoc_sta) { + wil_dbg_txrx(wil, "ipa_tx invalid cid %d\n", cid); + return -EINVAL; + } + + ipa_conn = &ipa->conn[cid]; + + rc = ipa_wigig_tx_dp(ipa_conn->ipa_client, skb); + if (rc) + return rc; + /* skb could be freed after this point */ + + stats = &wil->sta[cid].stats; + stats->tx_packets++; + stats->tx_bytes += len; + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += len; + + return 0; +} + +void wil_ipa_disconn_client(void *ipa_handle, int cid) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil = ipa->wil; + struct wil_ipa_conn *ipa_conn = &ipa->conn[cid]; + + if (ipa_conn->ipa_client >= IPA_CLIENT_MAX) + return; + + wil_info(wil, "ipa disconnect cid %d (client %d)\n", + cid, ipa_conn->ipa_client); + + ipa_wigig_disable_pipe(ipa_conn->ipa_client); + ipa_wigig_disconn_pipe(ipa_conn->ipa_client); + wil_ipa_free_tx_bufs(ipa, cid); + + ipa_conn->ipa_client = IPA_CLIENT_MAX; +} + +static void wil_ipa_uc_ready_cb(void *priv) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_misc(wil, "got ipa uC ready cb\n"); + + complete(&ipa->ipa_uc_ready_comp); +} + +static void wil_ipa_wigig_misc_cb(void *priv) +{ + struct wil_ipa *ipa = (struct wil_ipa *)priv; + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_irq(wil, "got MISC callback from IPA\n"); + if (wil6210_irq_misc(0, wil) == IRQ_WAKE_THREAD) + wil6210_irq_misc_thread(0, wil); +} + +static int wil_ipa_wigig_init(struct wil_ipa *ipa) +{ + struct wil6210_priv *wil = ipa->wil; + struct device *dev = wil_to_dev(wil); + struct ipa_wigig_init_in_params in = {0}; + struct ipa_wigig_init_out_params out = {0}; + phys_addr_t bar_base_pa = wil_ipa_get_bar_base_pa(wil); + int rc; + u8 wil_smmu_en; + + ipa->domain = iommu_get_domain_for_dev(dev); + if (!ipa->domain) { + wil_err(wil, "iommu_get_domain failed\n"); + return -EINVAL; + } + + init_completion(&ipa->ipa_uc_ready_comp); + in.periph_baddr_pa = bar_base_pa; + in.pseudo_cause_pa = bar_base_pa + HOSTADDR(RGF_DMA_PSEUDO_CAUSE); + in.int_gen_tx_pa = bar_base_pa + HOSTADDR(RGF_INT_GEN_TX_ICR) + + offsetof(struct RGF_ICR, ICR); + in.int_gen_rx_pa = bar_base_pa + HOSTADDR(RGF_INT_GEN_RX_ICR) + + offsetof(struct RGF_ICR, ICR); + in.dma_ep_misc_pa = bar_base_pa + HOSTADDR(RGF_DMA_EP_MISC_ICR) + + offsetof(struct RGF_ICR, ICR); + in.notify = wil_ipa_uc_ready_cb; + in.int_notify = wil_ipa_wigig_misc_cb; + in.priv = ipa; + + rc = ipa_wigig_init(&in, &out); + if (rc) { + wil_err(wil, "ipa_wigig_init failed %d\n", rc); + return rc; + } + + if (!out.is_uc_ready) { + ulong to, left; + + to = msecs_to_jiffies(WIL_IPA_READY_TO_MS); + left = wait_for_completion_timeout(&ipa->ipa_uc_ready_comp, to); + if (left == 0) { + wil_err(wil, "IPA uC ready timeout\n"); + rc = -ETIME; + goto err; + } + } + + ipa->smmu_enabled = ipa_wigig_is_smmu_enabled(); + wil_smmu_en = test_bit(WIL_PLATFORM_CAPA_SMMU, wil->platform_capa); + if (ipa->smmu_enabled != wil_smmu_en) { + wil_err(wil, "smmu disagreement (ipa %d wil %d)\n", + ipa->smmu_enabled, wil_smmu_en); + rc = -EINVAL; + goto err; + } + + wil_info(wil, "IPA uC ready (early %d, uc_db_pa %pad, smmu %d)\n", + out.is_uc_ready, &out.uc_db_pa, ipa->smmu_enabled); + + if (ipa->smmu_enabled) { + phys_addr_t pa; + + pa = rounddown(out.uc_db_pa, PAGE_SIZE); + rc = iommu_map(ipa->domain, pa, pa, PAGE_SIZE, + IOMMU_READ | IOMMU_WRITE | IOMMU_MMIO); + if (rc) { + wil_err(wil, "iommu_map failed %d\n", rc); + goto err; + } + } + + ipa->uc_db_pa = out.uc_db_pa; + + return 0; + +err: + if (ipa->smmu_enabled && ipa->uc_db_pa) { + iommu_unmap(ipa->domain, ipa->uc_db_pa, PAGE_SIZE); + ipa->uc_db_pa = 0; + } + ipa_wigig_cleanup(); + + return rc; +} + +static void wil_ipa_bcast_fn(struct timer_list *t) +{ + struct wil_ipa *ipa = from_timer(ipa, t, bcast_timer); + struct wil6210_priv *wil = ipa->wil; + + wil_dbg_txrx(wil, "NAPI(Tx bcast) schedule\n"); + napi_schedule(&wil->napi_tx); +} + +void *wil_ipa_init(struct wil6210_priv *wil) +{ + struct wil_ipa *ipa; + int rc, i; + + wil_dbg_misc(wil, "wil_ipa init\n"); + + if (wil->max_vifs > 1) { + wil_err(wil, "IPA offload not supported with multi-VIF\n"); + return NULL; + } + + if (!test_bit(WMI_FW_CAPABILITY_IPA, wil->fw_capabilities)) { + wil_err(wil, "IPA offload not supported by FW\n"); + return NULL; + } + + ipa = kzalloc(sizeof(*ipa), GFP_KERNEL); + if (!ipa) + return NULL; + + ipa->wil = wil; + ipa->bcast_sring_id = WIL6210_MAX_STATUS_RINGS; + ipa->rx_client_type = IPA_CLIENT_MAX; + for (i = 0; i < WIL6210_MAX_CID; i++) + ipa->conn[i].ipa_client = IPA_CLIENT_MAX; + + timer_setup(&ipa->bcast_timer, wil_ipa_bcast_fn, 0); + + rc = wil_ipa_wigig_init(ipa); + if (rc) + goto err; + + return ipa; + +err: + kfree(ipa); + + return NULL; +} + +void wil_ipa_uninit(void *ipa_handle) +{ + struct wil_ipa *ipa = (struct wil_ipa *)ipa_handle; + struct wil6210_priv *wil; + struct device *dev; + int i; + + if (!ipa_handle) + return; + + wil = ipa->wil; + dev = wil_to_dev(wil); + wil_info(ipa->wil, "wil_ipa uninit\n"); + + del_timer_sync(&ipa->bcast_timer); + + for (i = 0; i < WIL6210_MAX_CID; i++) { + struct wil_ipa_conn *conn = &ipa->conn[i]; + + if (conn->ipa_client < IPA_CLIENT_MAX) { + ipa_wigig_disable_pipe(conn->ipa_client); + ipa_wigig_disconn_pipe(conn->ipa_client); + + wil_ipa_free_tx_bufs(ipa, i); + } + } + + if (ipa->rx_client_type < IPA_CLIENT_MAX) { + struct net_device *ndev = wil->main_ndev; + struct pci_dev *pdev = wil->pdev; + + ipa_wigig_dereg_intf(ndev->name); + ipa_wigig_disable_pipe(ipa->rx_client_type); + ipa_wigig_disconn_pipe(ipa->rx_client_type); + pci_write_msi_msg(pdev->irq, &ipa->orig_msi_msg); + + wil_ipa_rx_free(ipa); + } + + if (ipa->smmu_enabled && ipa->uc_db_pa) + iommu_unmap(ipa->domain, ipa->uc_db_pa, PAGE_SIZE); + + ipa_wigig_cleanup(); + kfree(ipa_handle); +} diff --git a/drivers/net/wireless/ath/wil6210/ipa.h b/drivers/net/wireless/ath/wil6210/ipa.h new file mode 100644 index 000000000000..6c563d225c6b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/ipa.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (c) 2019, The Linux Foundation. + * All rights reserved. + */ + +#ifndef WIL6210_IPA_H +#define WIL6210_IPA_H + +#include +#ifdef CONFIG_WIL6210_IPA +#include +#include +extern u8 ipa_offload; +#endif + +#define WIL_IPA_BCAST_DESC_RING_SIZE 512 +#define WIL_IPA_BCAST_SRING_SIZE (2 * WIL_IPA_BCAST_DESC_RING_SIZE) + +#define WIL_IPA_DESC_RING_SIZE 2000 +#define WIL_IPA_STATUS_RING_SIZE 2048 + +#define WIL_IPA_MAX_ASSOC_STA 4 + +#ifdef CONFIG_WIL6210_IPA + +struct wil_dma_map_info { + void *va; + phys_addr_t pa; +}; + +struct wil_ipa_conn { + enum ipa_client_type ipa_client; + struct wil_dma_map_info *tx_bufs_addr; + int tx_bufs_count; +}; + +struct wil_ipa_rx_buf { + dma_addr_t pa; + void *va; + size_t sz; +}; + +struct wil_ipa { + struct wil6210_priv *wil; + struct iommu_domain *domain; + struct timer_list bcast_timer; + int bcast_sring_id; + struct completion ipa_uc_ready_comp; + u8 smmu_enabled; + enum ipa_client_type rx_client_type; + phys_addr_t uc_db_pa; + struct wil_ipa_conn conn[WIL6210_MAX_CID]; + struct wil_ipa_rx_buf rx_buf; /* contiguous memory split into rx bufs */ + struct msi_msg orig_msi_msg; +}; + +static inline bool wil_ipa_offload(void) {return ipa_offload; } +void *wil_ipa_init(struct wil6210_priv *wil); +void wil_ipa_uninit(void *ipa_handle); +int wil_ipa_start_ap(void *ipa_handle); +int wil_ipa_conn_client(void *ipa_handle, int cid, int ring_id, int sring_id); +void wil_ipa_disconn_client(void *ipa_handle, int cid); +int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil); +void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, int bcast_sring_id); +int wil_ipa_tx(void *ipa_handle, struct wil_ring *ring, struct sk_buff *skb); + +#else /* CONFIG_WIL6210_IPA */ + +static inline bool wil_ipa_offload(void) {return false; } +static inline void *wil_ipa_init(struct wil6210_priv *wil) {return NULL; } +static inline void wil_ipa_uninit(void *ipa_handle) {} +static inline int wil_ipa_start_ap(void *ipa_handle) {return -EOPNOTSUPP; } +static inline int wil_ipa_conn_client(void *ipa_handle, + int cid, int ring_id, + int sring_id) {return -EOPNOTSUPP; } +static inline void wil_ipa_disconn_client(void *ipa_handle, int cid) {} +static inline int wil_ipa_get_bcast_sring_id(struct wil6210_priv *wil) +{ + return WIL6210_MAX_STATUS_RINGS; +} + +static inline void wil_ipa_set_bcast_sring_id(struct wil6210_priv *wil, + int bcast_sring_id) {} +static inline int wil_ipa_tx(void *ipa_handle, + struct wil_ring *ring, + struct sk_buff *skb) {return -EOPNOTSUPP; } + +#endif /* CONFIG_WIL6210_IPA */ + +#endif /* WIL6210_IPA_H */ diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 19ba6360b9af..9ccfe43d8cec 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -14,6 +14,7 @@ #include "txrx_edma.h" #include "wmi.h" #include "boot_loader.h" +#include "ipa.h" #define WAIT_FOR_HALP_VOTE_MS 100 #define WAIT_FOR_SCAN_ABORT_MS 1000 @@ -294,6 +295,10 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) if (wil->ring2cid_tid[i][0] == cid) wil_ring_fini_tx(wil, i); } + + if (wil->ipa_handle) + wil_ipa_disconn_client(wil->ipa_handle, cid); + /* statistics */ memset(&sta->stats, 0, sizeof(sta->stats)); sta->stats.tx_latency_min_us = U32_MAX; @@ -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", diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 9d3555422b3c..601be80e3dab 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -9,6 +9,7 @@ #include #include "wil6210.h" #include "txrx.h" +#include "ipa.h" static bool alt_ifname; /* = false; */ module_param(alt_ifname, bool, 0444); @@ -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); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 299b98583aac..dafe9ebf351e 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -12,6 +12,7 @@ #include "wil6210.h" #include #include +#include "ipa.h" static int n_msi = 3; module_param(n_msi, int, 0444); @@ -98,6 +99,9 @@ int wil_set_capabilities(struct wil6210_priv *wil) wil->use_enhanced_dma_hw = true; wil->use_rx_hw_reordering = true; wil->use_compressed_rx_status = true; + if (wil_ipa_offload()) + /* IPA offload must use single MSI */ + n_msi = 1; wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_TALYN : WIL_FW_NAME_TALYN; if (wil_fw_verify_file_exists(wil, wil_fw_name)) @@ -218,6 +222,11 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) wil->n_msi = n_msi; + if (wil->n_msi == 0 && wil_ipa_offload()) { + wil_err(wil, "IPA offload cannot use INTx\n"); + rc = -ENODEV; + goto stop_master; + } if (wil->n_msi == 0 && msi_only) { wil_err(wil, "Interrupt pin not routed, unable to use INTx\n"); rc = -ENODEV; diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index f3729f49d50d..c031151e99e9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -18,6 +18,7 @@ #include "txrx.h" #include "trace.h" #include "txrx_edma.h" +#include "ipa.h" bool rx_align_2; module_param(rx_align_2, bool, 0444); @@ -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); diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 337f0ffa6fe8..d6e69926caa9 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -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 */ diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.c b/drivers/net/wireless/ath/wil6210/txrx_edma.c index 86689a1fb400..40433cfa384b 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.c +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.c @@ -14,6 +14,7 @@ #include "txrx_edma.h" #include "txrx.h" #include "trace.h" +#include "ipa.h" /* Max number of entries (packets to complete) to update the hwtail of tx * status ring. Should be power of 2 @@ -44,7 +45,7 @@ static void wil_tx_desc_unmap_edma(struct device *dev, } } -static int wil_find_free_sring(struct wil6210_priv *wil) +int wil_find_free_sring(struct wil6210_priv *wil) { int i; @@ -56,8 +57,7 @@ static int wil_find_free_sring(struct wil6210_priv *wil) return -EINVAL; } -static void wil_sring_free(struct wil6210_priv *wil, - struct wil_status_ring *sring) +void wil_sring_free(struct wil6210_priv *wil, struct wil_status_ring *sring) { struct device *dev = wil_to_dev(wil); size_t sz; @@ -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); diff --git a/drivers/net/wireless/ath/wil6210/txrx_edma.h b/drivers/net/wireless/ath/wil6210/txrx_edma.h index c6dd2a0b1247..f04d443d9a5d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx_edma.h +++ b/drivers/net/wireless/ath/wil6210/txrx_edma.h @@ -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 */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a9962853f4ae..f4022c780302 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -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); diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index 19e00fa4bf39..d3816498c2dc 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -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, }; diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 2098d67e5f34..c72024d61e06 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -13,6 +13,7 @@ #include "wmi.h" #include "trace.h" #include "ftm.h" +#include "ipa.h" /* set the default max assoc sta to max supported by driver */ uint max_assoc_sta = WIL6210_MAX_CID; @@ -2276,6 +2277,9 @@ int wmi_pcp_start(struct wil6210_vif *vif, int bi, u8 wmi_nettype, return -EOPNOTSUPP; } + if (wil->ipa_handle && cmd.pcp_max_assoc_sta > WIL_IPA_MAX_ASSOC_STA) + cmd.pcp_max_assoc_sta = WIL_IPA_MAX_ASSOC_STA; + if (disable_ap_sme && !test_bit(WMI_FW_CAPABILITY_AP_SME_OFFLOAD_PARTIAL, wil->fw_capabilities)) {