diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2013-07-16 03:54:35 -0400 |
---|---|---|
committer | Kalle Valo <kvalo@qca.qualcomm.com> | 2013-07-30 11:01:20 -0400 |
commit | affd321733eebc92b12cd329505f63e94ae80c93 (patch) | |
tree | 2e743c8a0a89f6e6057cd2c8f4b96769dfcdde17 | |
parent | 87571bf0b8b27d4a97848ce48de34fa6d3b12db8 (diff) |
ath10k: implement device recovery
Restart the hardware if FW crashes.
If FW crashes during recovery we leave the
hardware in a "wedged" state to avoid recursive
recoveries.
When in "wedged" state userspace may bring
interfaces down (to issue stop()) and then bring
one interface (to issue start()) to reload
hardware manually.
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.c | 30 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 19 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htc.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.c | 101 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/mac.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/pci.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/wmi.c | 7 |
7 files changed, 144 insertions, 19 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c37f79f6f8ce..7226c23b9569 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c | |||
@@ -476,6 +476,34 @@ static int ath10k_init_hw_params(struct ath10k *ar) | |||
476 | return 0; | 476 | return 0; |
477 | } | 477 | } |
478 | 478 | ||
479 | static void ath10k_core_restart(struct work_struct *work) | ||
480 | { | ||
481 | struct ath10k *ar = container_of(work, struct ath10k, restart_work); | ||
482 | |||
483 | mutex_lock(&ar->conf_mutex); | ||
484 | |||
485 | switch (ar->state) { | ||
486 | case ATH10K_STATE_ON: | ||
487 | ath10k_halt(ar); | ||
488 | ar->state = ATH10K_STATE_RESTARTING; | ||
489 | ieee80211_restart_hw(ar->hw); | ||
490 | break; | ||
491 | case ATH10K_STATE_OFF: | ||
492 | /* this can happen if driver is being unloaded */ | ||
493 | ath10k_warn("cannot restart a device that hasn't been started\n"); | ||
494 | break; | ||
495 | case ATH10K_STATE_RESTARTING: | ||
496 | case ATH10K_STATE_RESTARTED: | ||
497 | ar->state = ATH10K_STATE_WEDGED; | ||
498 | /* fall through */ | ||
499 | case ATH10K_STATE_WEDGED: | ||
500 | ath10k_warn("device is wedged, will not restart\n"); | ||
501 | break; | ||
502 | } | ||
503 | |||
504 | mutex_unlock(&ar->conf_mutex); | ||
505 | } | ||
506 | |||
479 | struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, | 507 | struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, |
480 | const struct ath10k_hif_ops *hif_ops) | 508 | const struct ath10k_hif_ops *hif_ops) |
481 | { | 509 | { |
@@ -519,6 +547,8 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, | |||
519 | 547 | ||
520 | init_waitqueue_head(&ar->event_queue); | 548 | init_waitqueue_head(&ar->event_queue); |
521 | 549 | ||
550 | INIT_WORK(&ar->restart_work, ath10k_core_restart); | ||
551 | |||
522 | return ar; | 552 | return ar; |
523 | 553 | ||
524 | err_wq: | 554 | err_wq: |
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 413f1c5a8119..9f21ecb239d7 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h | |||
@@ -250,6 +250,23 @@ struct ath10k_debug { | |||
250 | enum ath10k_state { | 250 | enum ath10k_state { |
251 | ATH10K_STATE_OFF = 0, | 251 | ATH10K_STATE_OFF = 0, |
252 | ATH10K_STATE_ON, | 252 | ATH10K_STATE_ON, |
253 | |||
254 | /* When doing firmware recovery the device is first powered down. | ||
255 | * mac80211 is supposed to call in to start() hook later on. It is | ||
256 | * however possible that driver unloading and firmware crash overlap. | ||
257 | * mac80211 can wait on conf_mutex in stop() while the device is | ||
258 | * stopped in ath10k_core_restart() work holding conf_mutex. The state | ||
259 | * RESTARTED means that the device is up and mac80211 has started hw | ||
260 | * reconfiguration. Once mac80211 is done with the reconfiguration we | ||
261 | * set the state to STATE_ON in restart_complete(). */ | ||
262 | ATH10K_STATE_RESTARTING, | ||
263 | ATH10K_STATE_RESTARTED, | ||
264 | |||
265 | /* The device has crashed while restarting hw. This state is like ON | ||
266 | * but commands are blocked in HTC and -ECOMM response is given. This | ||
267 | * prevents completion timeouts and makes the driver more responsive to | ||
268 | * userspace commands. This is also prevents recursive recovery. */ | ||
269 | ATH10K_STATE_WEDGED, | ||
253 | }; | 270 | }; |
254 | 271 | ||
255 | struct ath10k { | 272 | struct ath10k { |
@@ -355,6 +372,8 @@ struct ath10k { | |||
355 | 372 | ||
356 | enum ath10k_state state; | 373 | enum ath10k_state state; |
357 | 374 | ||
375 | struct work_struct restart_work; | ||
376 | |||
358 | #ifdef CONFIG_ATH10K_DEBUGFS | 377 | #ifdef CONFIG_ATH10K_DEBUGFS |
359 | struct ath10k_debug debug; | 378 | struct ath10k_debug debug; |
360 | #endif | 379 | #endif |
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index 7d5a36616b71..72e072c97588 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c | |||
@@ -246,6 +246,9 @@ int ath10k_htc_send(struct ath10k_htc *htc, | |||
246 | { | 246 | { |
247 | struct ath10k_htc_ep *ep = &htc->endpoint[eid]; | 247 | struct ath10k_htc_ep *ep = &htc->endpoint[eid]; |
248 | 248 | ||
249 | if (htc->ar->state == ATH10K_STATE_WEDGED) | ||
250 | return -ECOMM; | ||
251 | |||
249 | if (eid >= ATH10K_HTC_EP_COUNT) { | 252 | if (eid >= ATH10K_HTC_EP_COUNT) { |
250 | ath10k_warn("Invalid endpoint id: %d\n", eid); | 253 | ath10k_warn("Invalid endpoint id: %d\n", eid); |
251 | return -ENOENT; | 254 | return -ENOENT; |
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 2dfd446251b1..b1bb318d153f 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c | |||
@@ -1738,7 +1738,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, | |||
1738 | /* | 1738 | /* |
1739 | * Initialize various parameters with default vaules. | 1739 | * Initialize various parameters with default vaules. |
1740 | */ | 1740 | */ |
1741 | static void ath10k_halt(struct ath10k *ar) | 1741 | void ath10k_halt(struct ath10k *ar) |
1742 | { | 1742 | { |
1743 | lockdep_assert_held(&ar->conf_mutex); | 1743 | lockdep_assert_held(&ar->conf_mutex); |
1744 | 1744 | ||
@@ -1764,7 +1764,8 @@ static int ath10k_start(struct ieee80211_hw *hw) | |||
1764 | 1764 | ||
1765 | mutex_lock(&ar->conf_mutex); | 1765 | mutex_lock(&ar->conf_mutex); |
1766 | 1766 | ||
1767 | if (ar->state != ATH10K_STATE_OFF) { | 1767 | if (ar->state != ATH10K_STATE_OFF && |
1768 | ar->state != ATH10K_STATE_RESTARTING) { | ||
1768 | ret = -EINVAL; | 1769 | ret = -EINVAL; |
1769 | goto exit; | 1770 | goto exit; |
1770 | } | 1771 | } |
@@ -1784,6 +1785,11 @@ static int ath10k_start(struct ieee80211_hw *hw) | |||
1784 | goto exit; | 1785 | goto exit; |
1785 | } | 1786 | } |
1786 | 1787 | ||
1788 | if (ar->state == ATH10K_STATE_OFF) | ||
1789 | ar->state = ATH10K_STATE_ON; | ||
1790 | else if (ar->state == ATH10K_STATE_RESTARTING) | ||
1791 | ar->state = ATH10K_STATE_RESTARTED; | ||
1792 | |||
1787 | ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); | 1793 | ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); |
1788 | if (ret) | 1794 | if (ret) |
1789 | ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", | 1795 | ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", |
@@ -1806,22 +1812,47 @@ static void ath10k_stop(struct ieee80211_hw *hw) | |||
1806 | struct ath10k *ar = hw->priv; | 1812 | struct ath10k *ar = hw->priv; |
1807 | 1813 | ||
1808 | mutex_lock(&ar->conf_mutex); | 1814 | mutex_lock(&ar->conf_mutex); |
1809 | if (ar->state == ATH10K_STATE_ON) | 1815 | if (ar->state == ATH10K_STATE_ON || |
1816 | ar->state == ATH10K_STATE_RESTARTED || | ||
1817 | ar->state == ATH10K_STATE_WEDGED) | ||
1810 | ath10k_halt(ar); | 1818 | ath10k_halt(ar); |
1811 | 1819 | ||
1812 | ar->state = ATH10K_STATE_OFF; | 1820 | ar->state = ATH10K_STATE_OFF; |
1813 | mutex_unlock(&ar->conf_mutex); | 1821 | mutex_unlock(&ar->conf_mutex); |
1814 | 1822 | ||
1815 | cancel_work_sync(&ar->offchan_tx_work); | 1823 | cancel_work_sync(&ar->offchan_tx_work); |
1824 | cancel_work_sync(&ar->restart_work); | ||
1816 | } | 1825 | } |
1817 | 1826 | ||
1818 | static int ath10k_config(struct ieee80211_hw *hw, u32 changed) | 1827 | static void ath10k_config_ps(struct ath10k *ar) |
1819 | { | 1828 | { |
1820 | struct ath10k_generic_iter ar_iter; | 1829 | struct ath10k_generic_iter ar_iter; |
1830 | |||
1831 | lockdep_assert_held(&ar->conf_mutex); | ||
1832 | |||
1833 | /* During HW reconfiguration mac80211 reports all interfaces that were | ||
1834 | * running until reconfiguration was started. Since FW doesn't have any | ||
1835 | * vdevs at this point we must not iterate over this interface list. | ||
1836 | * This setting will be updated upon add_interface(). */ | ||
1837 | if (ar->state == ATH10K_STATE_RESTARTED) | ||
1838 | return; | ||
1839 | |||
1840 | memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); | ||
1841 | ar_iter.ar = ar; | ||
1842 | |||
1843 | ieee80211_iterate_active_interfaces_atomic( | ||
1844 | ar->hw, IEEE80211_IFACE_ITER_NORMAL, | ||
1845 | ath10k_ps_iter, &ar_iter); | ||
1846 | |||
1847 | if (ar_iter.ret) | ||
1848 | ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); | ||
1849 | } | ||
1850 | |||
1851 | static int ath10k_config(struct ieee80211_hw *hw, u32 changed) | ||
1852 | { | ||
1821 | struct ath10k *ar = hw->priv; | 1853 | struct ath10k *ar = hw->priv; |
1822 | struct ieee80211_conf *conf = &hw->conf; | 1854 | struct ieee80211_conf *conf = &hw->conf; |
1823 | int ret = 0; | 1855 | int ret = 0; |
1824 | u32 flags; | ||
1825 | 1856 | ||
1826 | mutex_lock(&ar->conf_mutex); | 1857 | mutex_lock(&ar->conf_mutex); |
1827 | 1858 | ||
@@ -1833,18 +1864,8 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) | |||
1833 | spin_unlock_bh(&ar->data_lock); | 1864 | spin_unlock_bh(&ar->data_lock); |
1834 | } | 1865 | } |
1835 | 1866 | ||
1836 | if (changed & IEEE80211_CONF_CHANGE_PS) { | 1867 | if (changed & IEEE80211_CONF_CHANGE_PS) |
1837 | memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); | 1868 | ath10k_config_ps(ar); |
1838 | ar_iter.ar = ar; | ||
1839 | flags = IEEE80211_IFACE_ITER_RESUME_ALL; | ||
1840 | |||
1841 | ieee80211_iterate_active_interfaces_atomic(hw, | ||
1842 | flags, | ||
1843 | ath10k_ps_iter, | ||
1844 | &ar_iter); | ||
1845 | |||
1846 | ret = ar_iter.ret; | ||
1847 | } | ||
1848 | 1869 | ||
1849 | if (changed & IEEE80211_CONF_CHANGE_MONITOR) { | 1870 | if (changed & IEEE80211_CONF_CHANGE_MONITOR) { |
1850 | if (conf->flags & IEEE80211_CONF_MONITOR) | 1871 | if (conf->flags & IEEE80211_CONF_MONITOR) |
@@ -1853,6 +1874,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) | |||
1853 | ret = ath10k_monitor_destroy(ar); | 1874 | ret = ath10k_monitor_destroy(ar); |
1854 | } | 1875 | } |
1855 | 1876 | ||
1877 | ath10k_wmi_flush_tx(ar); | ||
1856 | mutex_unlock(&ar->conf_mutex); | 1878 | mutex_unlock(&ar->conf_mutex); |
1857 | return ret; | 1879 | return ret; |
1858 | } | 1880 | } |
@@ -2695,6 +2717,13 @@ static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | |||
2695 | 2717 | ||
2696 | lockdep_assert_held(&arvif->ar->conf_mutex); | 2718 | lockdep_assert_held(&arvif->ar->conf_mutex); |
2697 | 2719 | ||
2720 | /* During HW reconfiguration mac80211 reports all interfaces that were | ||
2721 | * running until reconfiguration was started. Since FW doesn't have any | ||
2722 | * vdevs at this point we must not iterate over this interface list. | ||
2723 | * This setting will be updated upon add_interface(). */ | ||
2724 | if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) | ||
2725 | return; | ||
2726 | |||
2698 | rts = min_t(u32, rts, ATH10K_RTS_MAX); | 2727 | rts = min_t(u32, rts, ATH10K_RTS_MAX); |
2699 | 2728 | ||
2700 | ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, | 2729 | ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id, |
@@ -2735,6 +2764,13 @@ static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) | |||
2735 | 2764 | ||
2736 | lockdep_assert_held(&arvif->ar->conf_mutex); | 2765 | lockdep_assert_held(&arvif->ar->conf_mutex); |
2737 | 2766 | ||
2767 | /* During HW reconfiguration mac80211 reports all interfaces that were | ||
2768 | * running until reconfiguration was started. Since FW doesn't have any | ||
2769 | * vdevs at this point we must not iterate over this interface list. | ||
2770 | * This setting will be updated upon add_interface(). */ | ||
2771 | if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) | ||
2772 | return; | ||
2773 | |||
2738 | frag = clamp_t(u32, frag, | 2774 | frag = clamp_t(u32, frag, |
2739 | ATH10K_FRAGMT_THRESHOLD_MIN, | 2775 | ATH10K_FRAGMT_THRESHOLD_MIN, |
2740 | ATH10K_FRAGMT_THRESHOLD_MAX); | 2776 | ATH10K_FRAGMT_THRESHOLD_MAX); |
@@ -2773,6 +2809,7 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) | |||
2773 | static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) | 2809 | static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) |
2774 | { | 2810 | { |
2775 | struct ath10k *ar = hw->priv; | 2811 | struct ath10k *ar = hw->priv; |
2812 | bool skip; | ||
2776 | int ret; | 2813 | int ret; |
2777 | 2814 | ||
2778 | /* mac80211 doesn't care if we really xmit queued frames or not | 2815 | /* mac80211 doesn't care if we really xmit queued frames or not |
@@ -2782,17 +2819,26 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) | |||
2782 | 2819 | ||
2783 | mutex_lock(&ar->conf_mutex); | 2820 | mutex_lock(&ar->conf_mutex); |
2784 | 2821 | ||
2822 | if (ar->state == ATH10K_STATE_WEDGED) | ||
2823 | goto skip; | ||
2824 | |||
2785 | ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ | 2825 | ret = wait_event_timeout(ar->htt.empty_tx_wq, ({ |
2786 | bool empty; | 2826 | bool empty; |
2827 | |||
2787 | spin_lock_bh(&ar->htt.tx_lock); | 2828 | spin_lock_bh(&ar->htt.tx_lock); |
2788 | empty = bitmap_empty(ar->htt.used_msdu_ids, | 2829 | empty = bitmap_empty(ar->htt.used_msdu_ids, |
2789 | ar->htt.max_num_pending_tx); | 2830 | ar->htt.max_num_pending_tx); |
2790 | spin_unlock_bh(&ar->htt.tx_lock); | 2831 | spin_unlock_bh(&ar->htt.tx_lock); |
2791 | (empty); | 2832 | |
2833 | skip = (ar->state == ATH10K_STATE_WEDGED); | ||
2834 | |||
2835 | (empty || skip); | ||
2792 | }), ATH10K_FLUSH_TIMEOUT_HZ); | 2836 | }), ATH10K_FLUSH_TIMEOUT_HZ); |
2793 | if (ret <= 0) | 2837 | |
2838 | if (ret <= 0 || skip) | ||
2794 | ath10k_warn("tx not flushed\n"); | 2839 | ath10k_warn("tx not flushed\n"); |
2795 | 2840 | ||
2841 | skip: | ||
2796 | mutex_unlock(&ar->conf_mutex); | 2842 | mutex_unlock(&ar->conf_mutex); |
2797 | } | 2843 | } |
2798 | 2844 | ||
@@ -2866,6 +2912,22 @@ static int ath10k_resume(struct ieee80211_hw *hw) | |||
2866 | } | 2912 | } |
2867 | #endif | 2913 | #endif |
2868 | 2914 | ||
2915 | static void ath10k_restart_complete(struct ieee80211_hw *hw) | ||
2916 | { | ||
2917 | struct ath10k *ar = hw->priv; | ||
2918 | |||
2919 | mutex_lock(&ar->conf_mutex); | ||
2920 | |||
2921 | /* If device failed to restart it will be in a different state, e.g. | ||
2922 | * ATH10K_STATE_WEDGED */ | ||
2923 | if (ar->state == ATH10K_STATE_RESTARTED) { | ||
2924 | ath10k_info("device successfully recovered\n"); | ||
2925 | ar->state = ATH10K_STATE_ON; | ||
2926 | } | ||
2927 | |||
2928 | mutex_unlock(&ar->conf_mutex); | ||
2929 | } | ||
2930 | |||
2869 | static const struct ieee80211_ops ath10k_ops = { | 2931 | static const struct ieee80211_ops ath10k_ops = { |
2870 | .tx = ath10k_tx, | 2932 | .tx = ath10k_tx, |
2871 | .start = ath10k_start, | 2933 | .start = ath10k_start, |
@@ -2886,6 +2948,7 @@ static const struct ieee80211_ops ath10k_ops = { | |||
2886 | .set_frag_threshold = ath10k_set_frag_threshold, | 2948 | .set_frag_threshold = ath10k_set_frag_threshold, |
2887 | .flush = ath10k_flush, | 2949 | .flush = ath10k_flush, |
2888 | .tx_last_beacon = ath10k_tx_last_beacon, | 2950 | .tx_last_beacon = ath10k_tx_last_beacon, |
2951 | .restart_complete = ath10k_restart_complete, | ||
2889 | #ifdef CONFIG_PM | 2952 | #ifdef CONFIG_PM |
2890 | .suspend = ath10k_suspend, | 2953 | .suspend = ath10k_suspend, |
2891 | .resume = ath10k_resume, | 2954 | .resume = ath10k_resume, |
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 27fc92e58829..6fce9bfb19a5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h | |||
@@ -34,6 +34,7 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); | |||
34 | void ath10k_reset_scan(unsigned long ptr); | 34 | void ath10k_reset_scan(unsigned long ptr); |
35 | void ath10k_offchan_tx_purge(struct ath10k *ar); | 35 | void ath10k_offchan_tx_purge(struct ath10k *ar); |
36 | void ath10k_offchan_tx_work(struct work_struct *work); | 36 | void ath10k_offchan_tx_work(struct work_struct *work); |
37 | void ath10k_halt(struct ath10k *ar); | ||
37 | 38 | ||
38 | static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) | 39 | static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) |
39 | { | 40 | { |
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index bfe856166bb0..c71b488eba9f 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c | |||
@@ -720,6 +720,8 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) | |||
720 | reg_dump_values[i + 1], | 720 | reg_dump_values[i + 1], |
721 | reg_dump_values[i + 2], | 721 | reg_dump_values[i + 2], |
722 | reg_dump_values[i + 3]); | 722 | reg_dump_values[i + 3]); |
723 | |||
724 | ieee80211_queue_work(ar->hw, &ar->restart_work); | ||
723 | } | 725 | } |
724 | 726 | ||
725 | static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, | 727 | static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, |
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index b7e7e456b5de..0d25cd733afd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c | |||
@@ -27,6 +27,13 @@ void ath10k_wmi_flush_tx(struct ath10k *ar) | |||
27 | { | 27 | { |
28 | int ret; | 28 | int ret; |
29 | 29 | ||
30 | lockdep_assert_held(&ar->conf_mutex); | ||
31 | |||
32 | if (ar->state == ATH10K_STATE_WEDGED) { | ||
33 | ath10k_warn("wmi flush skipped - device is wedged anyway\n"); | ||
34 | return; | ||
35 | } | ||
36 | |||
30 | ret = wait_event_timeout(ar->wmi.wq, | 37 | ret = wait_event_timeout(ar->wmi.wq, |
31 | atomic_read(&ar->wmi.pending_tx_count) == 0, | 38 | atomic_read(&ar->wmi.pending_tx_count) == 0, |
32 | 5*HZ); | 39 | 5*HZ); |