From 5c8d7435461c642e7d807e474bf11a6de1e9c052 Mon Sep 17 00:00:00 2001 From: Konstantin Dorfman Date: Thu, 31 Dec 2015 14:45:48 +0200 Subject: [PATCH] mmc: core: postpone runtime suspend in case BKOPS is required Some devices require long BKOPs time in order to provide high performance. In the current solution, the host disables auto BKOPs or stops manual BKOPs in runtime suspend, regardless of the device need for BKOPs. This patch adds a check for device BKOPs status and defers suspend in case when BKOPs need. CRs-Fixed: 979630 Change-Id: Ib38d1ce58e4195d4969e9a367b5738c8e598d0ba Signed-off-by: Konstantin Dorfman [subhashj@codeaurora.org: fixed merge conflicts] Signed-off-by: Subhash Jadavani --- drivers/mmc/core/core.c | 14 ++++---- drivers/mmc/core/mmc.c | 64 +++++++++++++++++++++++++++++++++++++ drivers/mmc/host/cmdq_hci.c | 6 ++-- include/linux/mmc/card.h | 3 +- 4 files changed, 76 insertions(+), 11 deletions(-) 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 {