diff options
Diffstat (limited to 'drivers/net/wireless/libertas/if_sdio.c')
| -rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 34 | 
1 files changed, 34 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 224e9853c48..387786e1b39 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -892,6 +892,37 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)  } +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ +	/* +	 * The actual reset operation must be run outside of lbs_thread. This +	 * is because mmc_remove_host() will cause the device to be instantly +	 * destroyed, and the libertas driver then needs to end lbs_thread, +	 * leading to a deadlock. +	 * +	 * We run it in a workqueue totally independent from the if_sdio_card +	 * instance for that reason. +	 */ + +	pr_info("Resetting card..."); +	mmc_remove_host(reset_host); +	mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ +	struct if_sdio_card *card = priv->card; + +	if (work_pending(&card_reset_work)) +		return; + +	reset_host = card->func->card->host; +	schedule_work(&card_reset_work); +} +  /*******************************************************************/  /* SDIO callbacks                                                  */  /*******************************************************************/ @@ -1065,6 +1096,7 @@ static int if_sdio_probe(struct sdio_func *func,  	priv->enter_deep_sleep = if_sdio_enter_deep_sleep;  	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;  	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; +	priv->reset_card = if_sdio_reset_card;  	sdio_claim_host(func); @@ -1301,6 +1333,8 @@ static void __exit if_sdio_exit_module(void)  	/* Set the flag as user is removing this module. */  	user_rmmod = 1; +	cancel_work_sync(&card_reset_work); +  	sdio_unregister_driver(&if_sdio_driver);  	lbs_deb_leave(LBS_DEB_SDIO);  |