summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/mwifiex/sdio.c
diff options
context:
space:
mode:
authorAmitkumar Karwar <akarwar@marvell.com>2012-11-01 18:44:14 -0700
committerJohn W. Linville <linville@tuxdriver.com>2012-11-14 14:55:37 -0500
commitd31ab3577eca0f74126ceb1d406710e620a155a0 (patch)
tree876368d0dd6f1ad002595a0b2e27e06d8826fa93 /drivers/net/wireless/mwifiex/sdio.c
parentb2cb1a900a104642401faea7bada3c48289402f4 (diff)
mwifiex: add support for SDIO card reset
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>
Diffstat (limited to 'drivers/net/wireless/mwifiex/sdio.c')
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index fc8a9bfa124..0d67333daa2 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -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);
}