diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 6c80a0c41e34..13e06f96d0c9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1227,6 +1227,8 @@ int mmc_set_auto_bkops(struct mmc_card *card, bool enable) mmc_update_bkops_auto_off(&card->bkops.stats); } card->ext_csd.man_bkops_en = bkops_en; + pr_debug("%s: %s: bkops state %x\n", + mmc_hostname(card->host), __func__, bkops_en); } out: return ret; @@ -1246,9 +1248,7 @@ void mmc_check_bkops(struct mmc_card *card) BUG_ON(!card); - if (unlikely(!mmc_card_configured_manual_bkops(card))) - return; - if (mmc_card_doing_bkops(card) || mmc_card_doing_auto_bkops(card)) + if (mmc_card_doing_bkops(card)) return; err = mmc_read_bkops_status(card); @@ -1258,12 +1258,12 @@ void mmc_check_bkops(struct mmc_card *card) return; } + card->bkops.needs_check = false; + mmc_update_bkops_level(&card->bkops.stats, card->ext_csd.raw_bkops_status); - if (card->ext_csd.raw_bkops_status < EXT_CSD_BKOPS_LEVEL_2) - return; - card->bkops.needs_manual = true; + card->bkops.needs_bkops = card->ext_csd.raw_bkops_status > 0; } EXPORT_SYMBOL(mmc_check_bkops); @@ -1296,7 +1296,7 @@ void mmc_start_manual_bkops(struct mmc_card *card) } else { mmc_card_set_doing_bkops(card); mmc_update_bkops_start(&card->bkops.stats); - card->bkops.needs_manual = false; + card->bkops.needs_bkops = false; } mmc_retune_release(card->host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4447bc6f8e76..f61a3b965f55 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2713,6 +2713,64 @@ static int mmc_resume(struct mmc_host *host) return err; } +#define MAX_DEFER_SUSPEND_COUNTER 20 +static bool mmc_process_bkops(struct mmc_host *host) +{ + int err = 0; + bool is_running = false; + u32 status; + + mmc_claim_host(host); + if (mmc_card_cmdq(host->card)) { + BUG_ON(host->cmdq_ctx.active_reqs); + + err = mmc_cmdq_halt(host, true); + if (err) { + pr_err("%s: halt: failed: %d\n", __func__, err); + goto unhalt; + } + } + + if (mmc_card_doing_bkops(host->card)) { + /* check that manual bkops finished */ + err = mmc_send_status(host->card, &status); + if (err) { + pr_err("%s: Get card status fail\n", __func__); + goto unhalt; + } + if (R1_CURRENT_STATE(status) != R1_STATE_PRG) { + mmc_card_clr_doing_bkops(host->card); + goto unhalt; + } + } else { + mmc_check_bkops(host->card); + } + + if (host->card->bkops.needs_bkops && + !mmc_card_support_auto_bkops(host->card)) + mmc_start_manual_bkops(host->card); + +unhalt: + if (mmc_card_cmdq(host->card)) { + err = mmc_cmdq_halt(host, false); + if (err) + pr_err("%s: unhalt: failed: %d\n", __func__, err); + } + mmc_release_host(host); + + if (host->card->bkops.needs_bkops || + mmc_card_doing_bkops(host->card)) { + if (host->card->bkops.retry_counter++ < + MAX_DEFER_SUSPEND_COUNTER) { + host->card->bkops.needs_check = true; + is_running = true; + } else { + host->card->bkops.retry_counter = 0; + } + } + return is_running; +} + /* * Callback for runtime_suspend. */ @@ -2724,6 +2782,12 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; + if (mmc_process_bkops(host)) { + pm_runtime_mark_last_busy(&host->card->dev); + pr_debug("%s: defered, need bkops\n", __func__); + return -EBUSY; + } + err = _mmc_suspend(host, true); if (err) pr_err("%s: error %d doing aggressive suspend\n", diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 17c5f9d69801..ca0d98dfc643 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2016 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -876,8 +876,8 @@ skip_cqterri: * exception once the queue is empty */ BUG_ON(!mmc->card); - if (mmc_card_configured_manual_bkops(mmc->card) && - !mmc_card_configured_auto_bkops(mmc->card)) + if (mmc_card_configured_manual_bkops(mmc->card) || + mmc_card_configured_auto_bkops(mmc->card)) mmc->card->bkops.needs_check = true; mrq->cmdq_req->resp_err = true; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index d88f6a027679..0f0c9963950e 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -327,7 +327,8 @@ struct mmc_bkops_stats { struct mmc_bkops_info { struct mmc_bkops_stats stats; bool needs_check; - bool needs_manual; + bool needs_bkops; + u32 retry_counter; }; enum mmc_pon_type {