wil_print_bcon_data(bcon);
        }
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
+
        mutex_lock(&wil->mutex);
 
        __wil_down(wil);
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
+
        mutex_lock(&wil->mutex);
 
        rc = wmi_pcp_stop(wil);
 
        .llseek         = seq_lseek,
 };
 
+/*---------recovery------------*/
+/* mode = [manual|auto]
+ * state = [idle|pending|running]
+ */
+static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       char buf[80];
+       int n;
+       static const char * const sstate[] = {"idle", "pending", "running"};
+
+       n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n",
+                    no_fw_recovery ? "manual" : "auto",
+                    sstate[wil->recovery_state]);
+
+       n = min_t(int, n, sizeof(buf));
+
+       return simple_read_from_buffer(user_buf, count, ppos,
+                                      buf, n);
+}
+
+static ssize_t wil_write_file_recovery(struct file *file,
+                                      const char __user *buf_,
+                                      size_t count, loff_t *ppos)
+{
+       struct wil6210_priv *wil = file->private_data;
+       static const char run_command[] = "run";
+       char buf[sizeof(run_command) + 1]; /* to detect "runx" */
+       ssize_t rc;
+
+       if (wil->recovery_state != fw_recovery_pending) {
+               wil_err(wil, "No recovery pending\n");
+               return -EINVAL;
+       }
+
+       if (*ppos != 0) {
+               wil_err(wil, "Offset [%d]\n", (int)*ppos);
+               return -EINVAL;
+       }
+
+       if (count > sizeof(buf)) {
+               wil_err(wil, "Input too long, len = %d\n", (int)count);
+               return -EINVAL;
+       }
+
+       rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count);
+       if (rc < 0)
+               return rc;
+
+       buf[rc] = '\0';
+       if (0 == strcmp(buf, run_command))
+               wil_set_recovery_state(wil, fw_recovery_running);
+       else
+               wil_err(wil, "Bad recovery command \"%s\"\n", buf);
+
+       return rc;
+}
+
+static const struct file_operations fops_recovery = {
+       .read = wil_read_file_recovery,
+       .write = wil_write_file_recovery,
+       .open  = simple_open,
+};
+
 /*---------Station matrix------------*/
 static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
 {
        {"freq",        S_IRUGO,                &fops_freq},
        {"link",        S_IRUGO,                &fops_link},
        {"info",        S_IRUGO,                &fops_info},
+       {"recovery",    S_IRUGO | S_IWUSR,      &fops_recovery},
 };
 
 static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
        WIL_FIELD(status,       S_IRUGO | S_IWUSR,      doff_ulong),
        WIL_FIELD(fw_version,   S_IRUGO,                doff_u32),
        WIL_FIELD(hw_version,   S_IRUGO,                doff_x32),
+       WIL_FIELD(recovery_count, S_IRUGO,              doff_u32),
        {},
 };
 
 
 #define WAIT_FOR_DISCONNECT_TIMEOUT_MS 2000
 #define WAIT_FOR_DISCONNECT_INTERVAL_MS 10
 
-static bool no_fw_recovery;
+bool no_fw_recovery;
 module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
+MODULE_PARM_DESC(no_fw_recovery, " disable automatic FW error recovery");
 
 static bool no_fw_load = true;
 module_param(no_fw_load, bool, S_IRUGO | S_IWUSR);
        schedule_work(&wil->fw_error_worker);
 }
 
+static int wil_wait_for_recovery(struct wil6210_priv *wil)
+{
+       if (wait_event_interruptible(wil->wq, wil->recovery_state !=
+                                    fw_recovery_pending)) {
+               wil_err(wil, "Interrupt, canceling recovery\n");
+               return -ERESTARTSYS;
+       }
+       if (wil->recovery_state != fw_recovery_running) {
+               wil_info(wil, "Recovery cancelled\n");
+               return -EINTR;
+       }
+       wil_info(wil, "Proceed with recovery\n");
+       return 0;
+}
+
+void wil_set_recovery_state(struct wil6210_priv *wil, int state)
+{
+       wil_dbg_misc(wil, "%s(%d -> %d)\n", __func__,
+                    wil->recovery_state, state);
+
+       wil->recovery_state = state;
+       wake_up_interruptible(&wil->wq);
+}
+
 static void wil_fw_error_worker(struct work_struct *work)
 {
-       struct wil6210_priv *wil = container_of(work,
-                       struct wil6210_priv, fw_error_worker);
+       struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
+                                               fw_error_worker);
        struct wireless_dev *wdev = wil->wdev;
 
        wil_dbg_misc(wil, "fw error worker\n");
 
-       if (no_fw_recovery)
-               return;
-
        /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO
         * passed since last recovery attempt
         */
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_MONITOR:
-               wil_info(wil, "fw error recovery started (try %d)...\n",
+               wil_info(wil, "fw error recovery requested (try %d)...\n",
                         wil->recovery_count);
+               if (!no_fw_recovery)
+                       wil->recovery_state = fw_recovery_running;
+               if (0 != wil_wait_for_recovery(wil))
+                       break;
+
                __wil_down(wil);
                __wil_up(wil);
                break;
 
        INIT_LIST_HEAD(&wil->pending_wmi_ev);
        spin_lock_init(&wil->wmi_ev_lock);
+       init_waitqueue_head(&wil->wq);
 
        wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi");
        if (!wil->wmi_wq)
 {
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        del_timer_sync(&wil->scan_timer);
        cancel_work_sync(&wil->disconnect_worker);
        cancel_work_sync(&wil->fw_error_worker);
 void wil_fw_error_recovery(struct wil6210_priv *wil)
 {
        wil_dbg_misc(wil, "starting fw error recovery\n");
+       wil->recovery_state = fw_recovery_pending;
        schedule_work(&wil->fw_error_worker);
 }
 
 
        wil_dbg_misc(wil, "%s()\n", __func__);
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        mutex_lock(&wil->mutex);
        rc = __wil_down(wil);
        mutex_unlock(&wil->mutex);
 
 #include <linux/timex.h>
 #include "wil_platform.h"
 
+extern bool no_fw_recovery;
 
 #define WIL_NAME "wil6210"
 #define WIL_FW_NAME "wil6210.fw"
        unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)];
 };
 
+enum {
+       fw_recovery_idle = 0,
+       fw_recovery_pending = 1,
+       fw_recovery_running = 2,
+};
+
 struct wil6210_priv {
        struct pci_dev *pdev;
        int n_msi;
        u32 hw_version;
        struct wil_board *board;
        u8 n_mids; /* number of additional MIDs as reported by FW */
-       int recovery_count; /* num of FW recovery attempts in a short time */
+       u32 recovery_count; /* num of FW recovery attempts in a short time */
+       u32 recovery_state; /* FW recovery state machine */
        unsigned long last_fw_recovery; /* jiffies of last fw recovery */
+       wait_queue_head_t wq; /* for all wait_event() use */
        /* profile */
        u32 monitor_flags;
        u32 secure_pcp; /* create secure PCP? */
 int wil_reset(struct wil6210_priv *wil);
 void wil_set_itr_trsh(struct wil6210_priv *wil);
 void wil_fw_error_recovery(struct wil6210_priv *wil);
+void wil_set_recovery_state(struct wil6210_priv *wil, int state);
 void wil_link_on(struct wil6210_priv *wil);
 void wil_link_off(struct wil6210_priv *wil);
 int wil_up(struct wil6210_priv *wil);
 
 {
        wil_dbg_wmi(wil, "WMI: got FW ready event\n");
 
+       wil_set_recovery_state(wil, fw_recovery_idle);
        set_bit(wil_status_fwready, &wil->status);
        /* let the reset sequence continue */
        complete(&wil->wmi_ready);