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