mwifiex: add recovery code if the firmware becomes wedged.

The firmware can get into a state where it does not respond and the
driver does not attempt a reset.

The symptom in the log messages is:
	mwifiex_sdio mmc1:0001:1: host_to_card, write iomem (1) failed: -1

Add code to issue a reset when the driver gets into this state to aid
recovery. For now this is off by default and can be enabled by writing
a non-zero value into debugfs firmware_recover.

In addition, add test code to simulate the failure. Writing a non-zero
value into debugfs firmware_wedge immediately simulates the firmware
not responding, and if firmware_recover is enabled, this variable is
cleared in reset.

See b/31916833

Change-Id: I8a5a3b9e9438a1af3ee144dc9a4cb0be5c0c978a
diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c
index 0b9c580..587f39c 100644
--- a/drivers/net/wireless/marvell/mwifiex/debugfs.c
+++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c
@@ -25,6 +25,11 @@
 
 static struct dentry *mwifiex_dfs_dir;
 
+u32 firmware_wedge = 0;
+u32 firmware_recover = 0;
+EXPORT_SYMBOL(firmware_wedge);
+EXPORT_SYMBOL(firmware_recover);
+
 static char *bss_modes[] = {
 	"UNSPECIFIED",
 	"ADHOC",
@@ -968,6 +973,11 @@
 	MWIFIEX_DFS_ADD_FILE(debug_mask);
 	MWIFIEX_DFS_ADD_FILE(timeshare_coex);
 	MWIFIEX_DFS_ADD_FILE(reset);
+
+	debugfs_create_u32("firmware_wedge", 0644, priv->dfs_dev_dir,
+			   &firmware_wedge);
+	debugfs_create_u32("firmware_recover", 0644, priv->dfs_dev_dir,
+			   &firmware_recover);
 }
 
 /*
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index c264ead..acc7afc 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -44,10 +44,14 @@
  * is removed, and will be set to TRUE for module removal when
  * module_exit function is called.
  */
+extern u32 firmware_recover;
+extern u32 firmware_wedge;
+
 static u8 user_rmmod;
 
 static struct mwifiex_if_ops sdio_ops;
 static unsigned long iface_work_flags;
+static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter);
 
 static struct semaphore add_remove_card_sem;
 
@@ -569,6 +573,9 @@
 
 	do {
 		ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port);
+		if (firmware_wedge)
+			ret = -1;
+
 		if (ret) {
 			i++;
 			mwifiex_dbg(adapter, ERROR,
@@ -579,8 +586,16 @@
 					    "write CFG reg failed\n");
 
 			ret = -1;
-			if (i > MAX_WRITE_IOMEM_RETRY)
+			if (i > MAX_WRITE_IOMEM_RETRY) {
+				if (firmware_recover) {
+					mwifiex_dbg(adapter, ERROR,
+						"host_to_card, reset. "
+						"firmware_wedge: %d",
+						firmware_wedge);
+					mwifiex_sdio_card_reset(adapter);
+				}
 				return ret;
+			}
 		}
 	} while (ret == -1);
 
@@ -2163,6 +2178,7 @@
 	 * We run it in a totally independent workqueue.
 	 */
 
+	firmware_wedge = 0;
 	mwifiex_dbg(adapter, WARN, "Resetting card...\n");
 	mmc_remove_host(target);
 	/* 200ms delay is based on experiment with sdhci controller */