Loading drivers/net/wireless/ath/wil6210/cfg80211.c +5 −0 Original line number Original line Diff line number Diff line Loading @@ -1556,7 +1556,12 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, params->wait); params->wait); out: out: /* when the sent packet was not acked by receiver(ACK=0), rc will * be -EAGAIN. In this case this function needs to return success, * the ACK=0 will be reflected in tx_status. */ tx_status = (rc == 0); tx_status = (rc == 0); rc = (rc == -EAGAIN) ? 0 : rc; cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, tx_status, GFP_KERNEL); tx_status, GFP_KERNEL); Loading drivers/net/wireless/ath/wil6210/debugfs.c +22 −4 Original line number Original line Diff line number Diff line Loading @@ -269,6 +269,11 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix, wil_halp_vote(wil); wil_halp_vote(wil); if (wil_mem_access_lock(wil)) { wil_halp_unvote(wil); return; } wil_memcpy_fromio_32(&r, off, sizeof(r)); wil_memcpy_fromio_32(&r, off, sizeof(r)); wil_mbox_ring_le2cpus(&r); wil_mbox_ring_le2cpus(&r); /* /* Loading Loading @@ -334,6 +339,7 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix, } } out: out: seq_puts(s, "}\n"); seq_puts(s, "}\n"); wil_mem_access_unlock(wil); wil_halp_unvote(wil); wil_halp_unvote(wil); } } Loading Loading @@ -622,6 +628,12 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data) if (ret < 0) if (ret < 0) return ret; return ret; ret = wil_mem_access_lock(wil); if (ret) { wil_pm_runtime_put(wil); return ret; } a = wmi_buffer(wil, cpu_to_le32(mem_addr)); a = wmi_buffer(wil, cpu_to_le32(mem_addr)); if (a) if (a) Loading @@ -629,6 +641,8 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data) else else seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); wil_mem_access_unlock(wil); wil_pm_runtime_put(wil); wil_pm_runtime_put(wil); return 0; return 0; Loading Loading @@ -658,10 +672,6 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, size_t unaligned_bytes, aligned_count, ret; size_t unaligned_bytes, aligned_count, ret; int rc; int rc; if (test_bit(wil_status_suspending, wil_blob->wil->status) || test_bit(wil_status_suspended, wil_blob->wil->status)) return 0; if (pos < 0) if (pos < 0) return -EINVAL; return -EINVAL; Loading @@ -688,11 +698,19 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, return rc; return rc; } } rc = wil_mem_access_lock(wil); if (rc) { kfree(buf); wil_pm_runtime_put(wil); return rc; } wil_memcpy_fromio_32(buf, (const void __iomem *) wil_memcpy_fromio_32(buf, (const void __iomem *) wil_blob->blob.data + aligned_pos, aligned_count); wil_blob->blob.data + aligned_pos, aligned_count); ret = copy_to_user(user_buf, buf + unaligned_bytes, count); ret = copy_to_user(user_buf, buf + unaligned_bytes, count); wil_mem_access_unlock(wil); wil_pm_runtime_put(wil); wil_pm_runtime_put(wil); kfree(buf); kfree(buf); Loading drivers/net/wireless/ath/wil6210/main.c +53 −19 Original line number Original line Diff line number Diff line Loading @@ -173,6 +173,28 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } } } /* Device memory access is prohibited while reset or suspend. * wil_mem_access_lock protects accessing device memory in these cases */ int wil_mem_access_lock(struct wil6210_priv *wil) { if (!down_read_trylock(&wil->mem_lock)) return -EBUSY; if (test_bit(wil_status_suspending, wil->status) || test_bit(wil_status_suspended, wil->status)) { up_read(&wil->mem_lock); return -EBUSY; } return 0; } void wil_mem_access_unlock(struct wil6210_priv *wil) { up_read(&wil->mem_lock); } static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) { { struct wil_ring *ring = &wil->ring_tx[id]; struct wil_ring *ring = &wil->ring_tx[id]; Loading Loading @@ -503,22 +525,16 @@ bool wil_is_recovery_blocked(struct wil6210_priv *wil) return no_fw_recovery && (wil->recovery_state == fw_recovery_pending); 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 net_device *ndev = wil->main_ndev; struct wireless_dev *wdev; 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; 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 * passed since last recovery attempt */ */ if (time_is_after_jiffies(wil->last_fw_recovery + if (time_is_after_jiffies(wil->last_fw_recovery + Loading Loading @@ -578,6 +594,22 @@ static void wil_fw_error_worker(struct work_struct *work) rtnl_unlock(); 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) static int wil_find_free_ring(struct wil6210_priv *wil) { { int i; int i; Loading Loading @@ -690,11 +722,14 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_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); INIT_LIST_HEAD(&wil->pending_wmi_ev); spin_lock_init(&wil->wmi_ev_lock); spin_lock_init(&wil->wmi_ev_lock); spin_lock_init(&wil->net_queue_lock); spin_lock_init(&wil->net_queue_lock); init_waitqueue_head(&wil->wq); init_waitqueue_head(&wil->wq); init_rwsem(&wil->mem_lock); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); if (!wil->wmi_wq) if (!wil->wmi_wq) Loading Loading @@ -804,6 +839,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil_set_recovery_state(wil, fw_recovery_idle); wil_set_recovery_state(wil, fw_recovery_idle); cancel_work_sync(&wil->fw_error_worker); cancel_work_sync(&wil->fw_error_worker); cancel_work_sync(&wil->pci_linkdown_recovery_worker); wmi_event_flush(wil); wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); destroy_workqueue(wil->wmi_wq); Loading Loading @@ -1624,15 +1660,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) } } set_bit(wil_status_resetting, wil->status); set_bit(wil_status_resetting, wil->status); if (test_bit(wil_status_collecting_dumps, wil->status)) { /* Device collects crash dump, cancel the reset. * following crash dump collection, reset would take place. */ wil_dbg_misc(wil, "reject reset while collecting crash dump\n"); rc = -EBUSY; goto out; } mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); wil_abort_scan_all_vifs(wil, false); wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); Loading Loading @@ -1816,7 +1843,9 @@ int __wil_up(struct wil6210_priv *wil) WARN_ON(!mutex_is_locked(&wil->mutex)); WARN_ON(!mutex_is_locked(&wil->mutex)); down_write(&wil->mem_lock); rc = wil_reset(wil, true); rc = wil_reset(wil, true); up_write(&wil->mem_lock); if (rc) if (rc) return rc; return rc; Loading Loading @@ -1888,6 +1917,7 @@ int wil_up(struct wil6210_priv *wil) int __wil_down(struct wil6210_priv *wil) int __wil_down(struct wil6210_priv *wil) { { int rc; WARN_ON(!mutex_is_locked(&wil->mutex)); WARN_ON(!mutex_is_locked(&wil->mutex)); set_bit(wil_status_resetting, wil->status); set_bit(wil_status_resetting, wil->status); Loading @@ -1908,7 +1938,11 @@ int __wil_down(struct wil6210_priv *wil) wil_abort_scan_all_vifs(wil, false); wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); return wil_reset(wil, false); down_write(&wil->mem_lock); rc = wil_reset(wil, false); up_write(&wil->mem_lock); return rc; } } int wil_down(struct wil6210_priv *wil) int wil_down(struct wil6210_priv *wil) Loading drivers/net/wireless/ath/wil6210/pcie_bus.c +108 −0 Original line number Original line Diff line number Diff line Loading @@ -283,6 +283,108 @@ static int wil_platform_rop_fw_recovery(void *wil_handle) return 0; 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) static void wil_platform_ops_uninit(struct wil6210_priv *wil) { { if (wil->platform_ops.uninit) if (wil->platform_ops.uninit) Loading @@ -298,6 +400,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) const struct wil_platform_rops rops = { const struct wil_platform_rops rops = { .ramdump = wil_platform_rop_ramdump, .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, .fw_recovery = wil_platform_rop_fw_recovery, .notify = wil_platform_rop_notify, }; }; u32 bar_size = pci_resource_len(pdev, 0); u32 bar_size = pci_resource_len(pdev, 0); int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ Loading Loading @@ -536,6 +639,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime) struct wil6210_priv *wil = pci_get_drvdata(pdev); struct wil6210_priv *wil = pci_get_drvdata(pdev); bool keep_radio_on, active_ifaces; 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"); wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); Loading drivers/net/wireless/ath/wil6210/pm.c +25 −23 Original line number Original line Diff line number Diff line // SPDX-License-Identifier: ISC /* /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * 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" #include "wil6210.h" Loading Loading @@ -101,6 +90,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) goto out; 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); mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); Loading Loading @@ -195,14 +190,18 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) wil_dbg_pm(wil, "suspend keep radio on\n"); wil_dbg_pm(wil, "suspend keep radio on\n"); /* Prevent handling of new tx and wmi commands */ /* Prevent handling of new tx and wmi commands */ set_bit(wil_status_suspending, wil->status); rc = down_write_trylock(&wil->mem_lock); if (test_bit(wil_status_collecting_dumps, wil->status)) { if (!rc) { /* Device collects crash dump, cancel the suspend */ wil_err(wil, wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); "device is busy. down_write_trylock failed, returned (0x%x)\n", clear_bit(wil_status_suspending, wil->status); rc); wil->suspend_stats.rejected_by_host++; wil->suspend_stats.rejected_by_host++; return -EBUSY; return -EBUSY; } } set_bit(wil_status_suspending, wil->status); up_write(&wil->mem_lock); wil_pm_stop_all_net_queues(wil); wil_pm_stop_all_net_queues(wil); if (!wil_is_tx_idle(wil)) { if (!wil_is_tx_idle(wil)) { Loading Loading @@ -311,15 +310,18 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil) wil_dbg_pm(wil, "suspend radio off\n"); wil_dbg_pm(wil, "suspend radio off\n"); set_bit(wil_status_suspending, wil->status); rc = down_write_trylock(&wil->mem_lock); if (test_bit(wil_status_collecting_dumps, wil->status)) { if (!rc) { /* Device collects crash dump, cancel the suspend */ wil_err(wil, wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); "device is busy. down_write_trylock failed, returned (0x%x)\n", clear_bit(wil_status_suspending, wil->status); rc); wil->suspend_stats.rejected_by_host++; wil->suspend_stats.rejected_by_host++; return -EBUSY; return -EBUSY; } } set_bit(wil_status_suspending, wil->status); up_write(&wil->mem_lock); /* if netif up, hardware is alive, shut it down */ /* if netif up, hardware is alive, shut it down */ mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); active_ifaces = wil_has_active_ifaces(wil, true, false); Loading Loading
drivers/net/wireless/ath/wil6210/cfg80211.c +5 −0 Original line number Original line Diff line number Diff line Loading @@ -1556,7 +1556,12 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, params->wait); params->wait); out: out: /* when the sent packet was not acked by receiver(ACK=0), rc will * be -EAGAIN. In this case this function needs to return success, * the ACK=0 will be reflected in tx_status. */ tx_status = (rc == 0); tx_status = (rc == 0); rc = (rc == -EAGAIN) ? 0 : rc; cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len, tx_status, GFP_KERNEL); tx_status, GFP_KERNEL); Loading
drivers/net/wireless/ath/wil6210/debugfs.c +22 −4 Original line number Original line Diff line number Diff line Loading @@ -269,6 +269,11 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix, wil_halp_vote(wil); wil_halp_vote(wil); if (wil_mem_access_lock(wil)) { wil_halp_unvote(wil); return; } wil_memcpy_fromio_32(&r, off, sizeof(r)); wil_memcpy_fromio_32(&r, off, sizeof(r)); wil_mbox_ring_le2cpus(&r); wil_mbox_ring_le2cpus(&r); /* /* Loading Loading @@ -334,6 +339,7 @@ static void wil_print_mbox_ring(struct seq_file *s, const char *prefix, } } out: out: seq_puts(s, "}\n"); seq_puts(s, "}\n"); wil_mem_access_unlock(wil); wil_halp_unvote(wil); wil_halp_unvote(wil); } } Loading Loading @@ -622,6 +628,12 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data) if (ret < 0) if (ret < 0) return ret; return ret; ret = wil_mem_access_lock(wil); if (ret) { wil_pm_runtime_put(wil); return ret; } a = wmi_buffer(wil, cpu_to_le32(mem_addr)); a = wmi_buffer(wil, cpu_to_le32(mem_addr)); if (a) if (a) Loading @@ -629,6 +641,8 @@ static int wil_memread_debugfs_show(struct seq_file *s, void *data) else else seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); wil_mem_access_unlock(wil); wil_pm_runtime_put(wil); wil_pm_runtime_put(wil); return 0; return 0; Loading Loading @@ -658,10 +672,6 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, size_t unaligned_bytes, aligned_count, ret; size_t unaligned_bytes, aligned_count, ret; int rc; int rc; if (test_bit(wil_status_suspending, wil_blob->wil->status) || test_bit(wil_status_suspended, wil_blob->wil->status)) return 0; if (pos < 0) if (pos < 0) return -EINVAL; return -EINVAL; Loading @@ -688,11 +698,19 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, return rc; return rc; } } rc = wil_mem_access_lock(wil); if (rc) { kfree(buf); wil_pm_runtime_put(wil); return rc; } wil_memcpy_fromio_32(buf, (const void __iomem *) wil_memcpy_fromio_32(buf, (const void __iomem *) wil_blob->blob.data + aligned_pos, aligned_count); wil_blob->blob.data + aligned_pos, aligned_count); ret = copy_to_user(user_buf, buf + unaligned_bytes, count); ret = copy_to_user(user_buf, buf + unaligned_bytes, count); wil_mem_access_unlock(wil); wil_pm_runtime_put(wil); wil_pm_runtime_put(wil); kfree(buf); kfree(buf); Loading
drivers/net/wireless/ath/wil6210/main.c +53 −19 Original line number Original line Diff line number Diff line Loading @@ -173,6 +173,28 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, } } } } /* Device memory access is prohibited while reset or suspend. * wil_mem_access_lock protects accessing device memory in these cases */ int wil_mem_access_lock(struct wil6210_priv *wil) { if (!down_read_trylock(&wil->mem_lock)) return -EBUSY; if (test_bit(wil_status_suspending, wil->status) || test_bit(wil_status_suspended, wil->status)) { up_read(&wil->mem_lock); return -EBUSY; } return 0; } void wil_mem_access_unlock(struct wil6210_priv *wil) { up_read(&wil->mem_lock); } static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) static void wil_ring_fini_tx(struct wil6210_priv *wil, int id) { { struct wil_ring *ring = &wil->ring_tx[id]; struct wil_ring *ring = &wil->ring_tx[id]; Loading Loading @@ -503,22 +525,16 @@ bool wil_is_recovery_blocked(struct wil6210_priv *wil) return no_fw_recovery && (wil->recovery_state == fw_recovery_pending); 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 net_device *ndev = wil->main_ndev; struct wireless_dev *wdev; 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; 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 * passed since last recovery attempt */ */ if (time_is_after_jiffies(wil->last_fw_recovery + if (time_is_after_jiffies(wil->last_fw_recovery + Loading Loading @@ -578,6 +594,22 @@ static void wil_fw_error_worker(struct work_struct *work) rtnl_unlock(); 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) static int wil_find_free_ring(struct wil6210_priv *wil) { { int i; int i; Loading Loading @@ -690,11 +722,14 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_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); INIT_LIST_HEAD(&wil->pending_wmi_ev); spin_lock_init(&wil->wmi_ev_lock); spin_lock_init(&wil->wmi_ev_lock); spin_lock_init(&wil->net_queue_lock); spin_lock_init(&wil->net_queue_lock); init_waitqueue_head(&wil->wq); init_waitqueue_head(&wil->wq); init_rwsem(&wil->mem_lock); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); if (!wil->wmi_wq) if (!wil->wmi_wq) Loading Loading @@ -804,6 +839,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil_set_recovery_state(wil, fw_recovery_idle); wil_set_recovery_state(wil, fw_recovery_idle); cancel_work_sync(&wil->fw_error_worker); cancel_work_sync(&wil->fw_error_worker); cancel_work_sync(&wil->pci_linkdown_recovery_worker); wmi_event_flush(wil); wmi_event_flush(wil); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); destroy_workqueue(wil->wmi_wq); Loading Loading @@ -1624,15 +1660,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) } } set_bit(wil_status_resetting, wil->status); set_bit(wil_status_resetting, wil->status); if (test_bit(wil_status_collecting_dumps, wil->status)) { /* Device collects crash dump, cancel the reset. * following crash dump collection, reset would take place. */ wil_dbg_misc(wil, "reject reset while collecting crash dump\n"); rc = -EBUSY; goto out; } mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); wil_abort_scan_all_vifs(wil, false); wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); Loading Loading @@ -1816,7 +1843,9 @@ int __wil_up(struct wil6210_priv *wil) WARN_ON(!mutex_is_locked(&wil->mutex)); WARN_ON(!mutex_is_locked(&wil->mutex)); down_write(&wil->mem_lock); rc = wil_reset(wil, true); rc = wil_reset(wil, true); up_write(&wil->mem_lock); if (rc) if (rc) return rc; return rc; Loading Loading @@ -1888,6 +1917,7 @@ int wil_up(struct wil6210_priv *wil) int __wil_down(struct wil6210_priv *wil) int __wil_down(struct wil6210_priv *wil) { { int rc; WARN_ON(!mutex_is_locked(&wil->mutex)); WARN_ON(!mutex_is_locked(&wil->mutex)); set_bit(wil_status_resetting, wil->status); set_bit(wil_status_resetting, wil->status); Loading @@ -1908,7 +1938,11 @@ int __wil_down(struct wil6210_priv *wil) wil_abort_scan_all_vifs(wil, false); wil_abort_scan_all_vifs(wil, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); return wil_reset(wil, false); down_write(&wil->mem_lock); rc = wil_reset(wil, false); up_write(&wil->mem_lock); return rc; } } int wil_down(struct wil6210_priv *wil) int wil_down(struct wil6210_priv *wil) Loading
drivers/net/wireless/ath/wil6210/pcie_bus.c +108 −0 Original line number Original line Diff line number Diff line Loading @@ -283,6 +283,108 @@ static int wil_platform_rop_fw_recovery(void *wil_handle) return 0; 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) static void wil_platform_ops_uninit(struct wil6210_priv *wil) { { if (wil->platform_ops.uninit) if (wil->platform_ops.uninit) Loading @@ -298,6 +400,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) const struct wil_platform_rops rops = { const struct wil_platform_rops rops = { .ramdump = wil_platform_rop_ramdump, .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, .fw_recovery = wil_platform_rop_fw_recovery, .notify = wil_platform_rop_notify, }; }; u32 bar_size = pci_resource_len(pdev, 0); u32 bar_size = pci_resource_len(pdev, 0); int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ int dma_addr_size[] = {64, 48, 40, 32}; /* keep descending order */ Loading Loading @@ -536,6 +639,11 @@ static int wil6210_resume(struct device *dev, bool is_runtime) struct wil6210_priv *wil = pci_get_drvdata(pdev); struct wil6210_priv *wil = pci_get_drvdata(pdev); bool keep_radio_on, active_ifaces; 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"); wil_dbg_pm(wil, "resume: %s\n", is_runtime ? "runtime" : "system"); mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); Loading
drivers/net/wireless/ath/wil6210/pm.c +25 −23 Original line number Original line Diff line number Diff line // SPDX-License-Identifier: ISC /* /* * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * Copyright (c) 2018, The Linux Foundation. All rights reserved. * 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" #include "wil6210.h" Loading Loading @@ -101,6 +90,12 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) goto out; 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); mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); active_ifaces = wil_has_active_ifaces(wil, true, false); mutex_unlock(&wil->vif_mutex); mutex_unlock(&wil->vif_mutex); Loading Loading @@ -195,14 +190,18 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) wil_dbg_pm(wil, "suspend keep radio on\n"); wil_dbg_pm(wil, "suspend keep radio on\n"); /* Prevent handling of new tx and wmi commands */ /* Prevent handling of new tx and wmi commands */ set_bit(wil_status_suspending, wil->status); rc = down_write_trylock(&wil->mem_lock); if (test_bit(wil_status_collecting_dumps, wil->status)) { if (!rc) { /* Device collects crash dump, cancel the suspend */ wil_err(wil, wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); "device is busy. down_write_trylock failed, returned (0x%x)\n", clear_bit(wil_status_suspending, wil->status); rc); wil->suspend_stats.rejected_by_host++; wil->suspend_stats.rejected_by_host++; return -EBUSY; return -EBUSY; } } set_bit(wil_status_suspending, wil->status); up_write(&wil->mem_lock); wil_pm_stop_all_net_queues(wil); wil_pm_stop_all_net_queues(wil); if (!wil_is_tx_idle(wil)) { if (!wil_is_tx_idle(wil)) { Loading Loading @@ -311,15 +310,18 @@ static int wil_suspend_radio_off(struct wil6210_priv *wil) wil_dbg_pm(wil, "suspend radio off\n"); wil_dbg_pm(wil, "suspend radio off\n"); set_bit(wil_status_suspending, wil->status); rc = down_write_trylock(&wil->mem_lock); if (test_bit(wil_status_collecting_dumps, wil->status)) { if (!rc) { /* Device collects crash dump, cancel the suspend */ wil_err(wil, wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); "device is busy. down_write_trylock failed, returned (0x%x)\n", clear_bit(wil_status_suspending, wil->status); rc); wil->suspend_stats.rejected_by_host++; wil->suspend_stats.rejected_by_host++; return -EBUSY; return -EBUSY; } } set_bit(wil_status_suspending, wil->status); up_write(&wil->mem_lock); /* if netif up, hardware is alive, shut it down */ /* if netif up, hardware is alive, shut it down */ mutex_lock(&wil->vif_mutex); mutex_lock(&wil->vif_mutex); active_ifaces = wil_has_active_ifaces(wil, true, false); active_ifaces = wil_has_active_ifaces(wil, true, false); Loading