mwifiex: add support for SDIO card reset
authorAmitkumar Karwar <akarwar@marvell.com>
Fri, 2 Nov 2012 01:44:14 +0000 (18:44 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 14 Nov 2012 19:55:37 +0000 (14:55 -0500)
When command timeout happens due to a bug in firmware/hardware,
the timeout handler just prints some debug information. User is
unable to reload the driver in this case.

Inspired by 9a821f5 "libertas: add sd8686 reset_card support",
this patch adds card reset support for SDIO interface when
command timeout happens. If the SDIO host contoller supports
MMC_POWER_OFF|UP|ON operations, the chip will be reset and the
firmware will be re-downloaded.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/mwifiex/cmdevt.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sdio.c
drivers/net/wireless/mwifiex/sdio.h

index da6c491..c9528b3 100644 (file)
@@ -944,6 +944,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
        }
        if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
                mwifiex_init_fw_complete(adapter);
+
+       if (adapter->if_ops.card_reset)
+               adapter->if_ops.card_reset(adapter);
 }
 
 /*
index 81f8772..68f3646 100644 (file)
@@ -600,6 +600,7 @@ struct mwifiex_if_ops {
        int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
        int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *);
        int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
+       void (*card_reset) (struct mwifiex_adapter *);
 };
 
 struct mwifiex_adapter {
index fc8a9bf..0d67333 100644 (file)
@@ -1748,6 +1748,37 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
                port, card->mp_data_port_mask);
 }
 
+static struct mmc_host *reset_host;
+static void sdio_card_reset_worker(struct work_struct *work)
+{
+       /* The actual reset operation must be run outside of driver thread.
+        * This is because mmc_remove_host() will cause the device to be
+        * instantly destroyed, and the driver then needs to end its thread,
+        * leading to a deadlock.
+        *
+        * We run it in a totally independent workqueue.
+        */
+
+       pr_err("Resetting card...\n");
+       mmc_remove_host(reset_host);
+       /* 20ms delay is based on experiment with sdhci controller */
+       mdelay(20);
+       mmc_add_host(reset_host);
+}
+static DECLARE_WORK(card_reset_work, sdio_card_reset_worker);
+
+/* This function resets the card */
+static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
+{
+       struct sdio_mmc_card *card = adapter->card;
+
+       if (work_pending(&card_reset_work))
+               return;
+
+       reset_host = card->func->card->host;
+       schedule_work(&card_reset_work);
+}
+
 static struct mwifiex_if_ops sdio_ops = {
        .init_if = mwifiex_init_sdio,
        .cleanup_if = mwifiex_cleanup_sdio,
@@ -1766,6 +1797,7 @@ static struct mwifiex_if_ops sdio_ops = {
        .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf,
        .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
        .event_complete = mwifiex_sdio_event_complete,
+       .card_reset = mwifiex_sdio_card_reset,
 };
 
 /*
@@ -1803,6 +1835,7 @@ mwifiex_sdio_cleanup_module(void)
        /* Set the flag as user is removing this module. */
        user_rmmod = 1;
 
+       cancel_work_sync(&card_reset_work);
        sdio_unregister_driver(&mwifiex_sdio);
 }
 
index 2103373..8cc5468 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/sdio_func.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
 
 #include "main.h"