Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a821f5d authored by Daniel Drake's avatar Daniel Drake Committed by John W. Linville
Browse files

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: default avatarDaniel Drake <dsd@laptop.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 43a1c272
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -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);