diff options
author | Daniel Drake <dsd@laptop.org> | 2011-06-07 13:15:57 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-06-10 14:57:47 -0400 |
commit | 9a821f5d0fc36425e95f0f4ade4bbca8f0ebff4d (patch) | |
tree | cc87d063af26affab99a46595d10961daeab12f7 | |
parent | 43a1c2721acd792aea370ee68ef054e18c944373 (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.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) | |||
892 | 892 | ||
893 | } | 893 | } |
894 | 894 | ||
895 | static struct mmc_host *reset_host; | ||
896 | |||
897 | static 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 | } | ||
913 | static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); | ||
914 | |||
915 | static 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); |