aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Drake <dsd@laptop.org>2011-06-07 13:15:57 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-06-10 14:57:47 -0400
commit9a821f5d0fc36425e95f0f4ade4bbca8f0ebff4d (patch)
treecc87d063af26affab99a46595d10961daeab12f7
parent43a1c2721acd792aea370ee68ef054e18c944373 (diff)
libertas: add sd8686 reset_card support
At http://dev.laptop.org/ticket/10748 we are seeing a case of the libertas firmware randomly stopping responding to commands after resume. Careful monitoring of communications indicates a firmware or hardware bug, which has been reported to Marvell. Work around this issue by adding a reset_card method; this is automatically called when command timeouts are detected and provides an instant recovery to this situation. Signed-off-by: Daniel Drake <dsd@laptop.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c34
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)
892 892
893} 893}
894 894
895static struct mmc_host *reset_host;
896
897static void if_sdio_reset_card_worker(struct work_struct *work)
898{
899 /*
900 * The actual reset operation must be run outside of lbs_thread. This
901 * is because mmc_remove_host() will cause the device to be instantly
902 * destroyed, and the libertas driver then needs to end lbs_thread,
903 * leading to a deadlock.
904 *
905 * We run it in a workqueue totally independent from the if_sdio_card
906 * instance for that reason.
907 */
908
909 pr_info("Resetting card...");
910 mmc_remove_host(reset_host);
911 mmc_add_host(reset_host);
912}
913static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker);
914
915static void if_sdio_reset_card(struct lbs_private *priv)
916{
917 struct if_sdio_card *card = priv->card;
918
919 if (work_pending(&card_reset_work))
920 return;
921
922 reset_host = card->func->card->host;
923 schedule_work(&card_reset_work);
924}
925
895/*******************************************************************/ 926/*******************************************************************/
896/* SDIO callbacks */ 927/* SDIO callbacks */
897/*******************************************************************/ 928/*******************************************************************/
@@ -1065,6 +1096,7 @@ static int if_sdio_probe(struct sdio_func *func,
1065 priv->enter_deep_sleep = if_sdio_enter_deep_sleep; 1096 priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
1066 priv->exit_deep_sleep = if_sdio_exit_deep_sleep; 1097 priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
1067 priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; 1098 priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
1099 priv->reset_card = if_sdio_reset_card;
1068 1100
1069 sdio_claim_host(func); 1101 sdio_claim_host(func);
1070 1102
@@ -1301,6 +1333,8 @@ static void __exit if_sdio_exit_module(void)
1301 /* Set the flag as user is removing this module. */ 1333 /* Set the flag as user is removing this module. */
1302 user_rmmod = 1; 1334 user_rmmod = 1;
1303 1335
1336 cancel_work_sync(&card_reset_work);
1337
1304 sdio_unregister_driver(&if_sdio_driver); 1338 sdio_unregister_driver(&if_sdio_driver);
1305 1339
1306 lbs_deb_leave(LBS_DEB_SDIO); 1340 lbs_deb_leave(LBS_DEB_SDIO);