wil6210: fw error recovery
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Mon, 17 Mar 2014 13:34:19 +0000 (15:34 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 17 Mar 2014 17:44:18 +0000 (13:44 -0400)
upon fw error interrupt - in STA mode, disconnect/cancel scan and
then reset FW/HW
added module param - no_fw_recovery which is false by default

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/main.c
drivers/net/wireless/ath/wil6210/wil6210.h

index 848ed85..f6a12e7 100644 (file)
@@ -265,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                u16 chnl[4];
        } __packed cmd;
        uint i, n;
+       int rc;
 
        if (wil->scan_request) {
                wil_err(wil, "Already scanning\n");
@@ -305,8 +306,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
                             request->channels[i]->center_freq);
        }
 
-       return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
+       rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
                        cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
+
+       if (rc)
+               wil->scan_request = NULL;
+
+       return rc;
 }
 
 static int wil_cfg80211_connect(struct wiphy *wiphy,
index 201cf06..5824cd4 100644 (file)
@@ -328,6 +328,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie)
        if (isr & ISR_MISC_FW_ERROR) {
                wil_notify_fw_error(wil);
                isr &= ~ISR_MISC_FW_ERROR;
+               wil_fw_error_recovery(wil);
        }
 
        if (isr & ISR_MISC_MBOX_EVT) {
index 0831d4c..32ac1b9 100644 (file)
 #include "wil6210.h"
 #include "txrx.h"
 
+static bool no_fw_recovery;
+module_param(no_fw_recovery, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(no_fw_recovery, " disable FW error recovery");
+
 /*
  * Due to a hardware issue,
  * one has to read/write to/from NIC in 32-bit chunks;
@@ -144,6 +148,36 @@ static void wil_connect_timer_fn(ulong x)
        schedule_work(&wil->disconnect_worker);
 }
 
+static void wil_fw_error_worker(struct work_struct *work)
+{
+       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;
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_STATION:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_MONITOR:
+               wil_info(wil, "fw error recovery started...\n");
+               wil_reset(wil);
+
+               /* need to re-allocate Rx ring after reset */
+               wil_rx_init(wil);
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               /* recovery in these modes is done by upper layers */
+               break;
+       default:
+               break;
+       }
+}
+
 static int wil_find_free_vring(struct wil6210_priv *wil)
 {
        int i;
@@ -196,6 +230,7 @@ int wil_priv_init(struct wil6210_priv *wil)
        INIT_WORK(&wil->connect_worker, wil_connect_worker);
        INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker);
        INIT_WORK(&wil->wmi_event_worker, wmi_event_worker);
+       INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker);
 
        INIT_LIST_HEAD(&wil->pending_wmi_ev);
        spin_lock_init(&wil->wmi_ev_lock);
@@ -222,6 +257,7 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid)
 void wil_priv_deinit(struct wil6210_priv *wil)
 {
        cancel_work_sync(&wil->disconnect_worker);
+       cancel_work_sync(&wil->fw_error_worker);
        wil6210_disconnect(wil, NULL);
        wmi_event_flush(wil);
        destroy_workqueue(wil->wmi_wq_conn);
@@ -335,6 +371,13 @@ int wil_reset(struct wil6210_priv *wil)
                napi_synchronize(&wil->napi_tx);
        }
 
+       if (wil->scan_request) {
+               wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+                            wil->scan_request);
+               cfg80211_scan_done(wil->scan_request, true);
+               wil->scan_request = NULL;
+       }
+
        cancel_work_sync(&wil->disconnect_worker);
        wil6210_disconnect(wil, NULL);
 
@@ -363,6 +406,11 @@ int wil_reset(struct wil6210_priv *wil)
        return rc;
 }
 
+void wil_fw_error_recovery(struct wil6210_priv *wil)
+{
+       wil_dbg_misc(wil, "starting fw error recovery\n");
+       schedule_work(&wil->fw_error_worker);
+}
 
 void wil_link_on(struct wil6210_priv *wil)
 {
index 89cbdc3..80573f7 100644 (file)
@@ -370,6 +370,7 @@ struct wil6210_priv {
        struct workqueue_struct *wmi_wq_conn; /* for connect worker */
        struct work_struct connect_worker;
        struct work_struct disconnect_worker;
+       struct work_struct fw_error_worker;     /* for FW error recovery */
        struct timer_list connect_timer;
        int pending_connect_cid;
        struct list_head pending_wmi_ev;
@@ -447,6 +448,7 @@ void wil_if_remove(struct wil6210_priv *wil);
 int wil_priv_init(struct wil6210_priv *wil);
 void wil_priv_deinit(struct wil6210_priv *wil);
 int wil_reset(struct wil6210_priv *wil);
+void wil_fw_error_recovery(struct wil6210_priv *wil);
 void wil_link_on(struct wil6210_priv *wil);
 void wil_link_off(struct wil6210_priv *wil);
 int wil_up(struct wil6210_priv *wil);