Commit d31ab357 authored by Amitkumar Karwar's avatar Amitkumar Karwar Committed by John W. Linville

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 9a821f5d "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: default avatarAmitkumar Karwar <akarwar@marvell.com>
Signed-off-by: default avatarBing Zhao <bzhao@marvell.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent b2cb1a90
...@@ -944,6 +944,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context) ...@@ -944,6 +944,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
} }
if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
mwifiex_init_fw_complete(adapter); mwifiex_init_fw_complete(adapter);
if (adapter->if_ops.card_reset)
adapter->if_ops.card_reset(adapter);
} }
/* /*
......
...@@ -600,6 +600,7 @@ struct mwifiex_if_ops { ...@@ -600,6 +600,7 @@ struct mwifiex_if_ops {
int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *);
int (*data_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 *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *);
void (*card_reset) (struct mwifiex_adapter *);
}; };
struct mwifiex_adapter { struct mwifiex_adapter {
......
...@@ -1748,6 +1748,37 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) ...@@ -1748,6 +1748,37 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
port, card->mp_data_port_mask); 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 = { static struct mwifiex_if_ops sdio_ops = {
.init_if = mwifiex_init_sdio, .init_if = mwifiex_init_sdio,
.cleanup_if = mwifiex_cleanup_sdio, .cleanup_if = mwifiex_cleanup_sdio,
...@@ -1766,6 +1797,7 @@ static struct mwifiex_if_ops sdio_ops = { ...@@ -1766,6 +1797,7 @@ static struct mwifiex_if_ops sdio_ops = {
.cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf,
.cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
.event_complete = mwifiex_sdio_event_complete, .event_complete = mwifiex_sdio_event_complete,
.card_reset = mwifiex_sdio_card_reset,
}; };
/* /*
...@@ -1803,6 +1835,7 @@ mwifiex_sdio_cleanup_module(void) ...@@ -1803,6 +1835,7 @@ mwifiex_sdio_cleanup_module(void)
/* Set the flag as user is removing this module. */ /* Set the flag as user is removing this module. */
user_rmmod = 1; user_rmmod = 1;
cancel_work_sync(&card_reset_work);
sdio_unregister_driver(&mwifiex_sdio); sdio_unregister_driver(&mwifiex_sdio);
} }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_func.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include "main.h" #include "main.h"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment