From 3332d5a9e4fe04b14c3008d432a031c22a38bd2c Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Tue, 9 Dec 2014 12:55:38 +0200 Subject: [PATCH] mmc: core: Fix clock frequency transitions during invalid states eMMC and SD card specifications restrict the usage of a class of commands while commands in other class are in progress. For example, during erase operations the SD/eMMC spec. allows only CMD35, CMD36, CMD38. If clock scaling is enabled and decide to scale up the clocks it may be possible that CMD19/21 tuning commands are sent in between erase commands, which is illegal as per specification. Fix such illegal transactions to the card and also make clock scaling statistics accountable only for read/write commands instead of time consuming commands, like CMD38 erase, where transactions are independent of bus frequency. Change-Id: Iffba175787837e7f95bde8970f19d0f0f9d7d67d Signed-off-by: Sujit Reddy Thumma [merez@codeaurora.org: fix conflicts as mmc_update_clk_scaling is missing on 3.14] Signed-off-by: Maya Erez --- drivers/mmc/core/core.c | 51 ++++++++++++++++++++++++++++++++++------ include/linux/mmc/host.h | 2 ++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 26ae35e9d023..188ace67504b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -127,6 +127,41 @@ static inline void mmc_should_fail_request(struct mmc_host *host, #endif /* CONFIG_FAIL_MMC_REQUEST */ +static inline void +mmc_clk_scaling_update_state(struct mmc_host *host, struct mmc_request *mrq) +{ + if (mrq) { + switch (mrq->cmd->opcode) { + case MMC_READ_SINGLE_BLOCK: + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + host->clk_scaling.invalid_state = false; + break; + default: + host->clk_scaling.invalid_state = true; + break; + } + } else { + /* + * force clock scaling transitions, + * if other conditions are met + */ + host->clk_scaling.invalid_state = false; + } + + return; +} + +static inline void mmc_update_clk_scaling(struct mmc_host *host) +{ + if (host->clk_scaling.enable && !host->clk_scaling.invalid_state) { + host->clk_scaling.busy_time_us += + ktime_to_us(ktime_sub(ktime_get(), + host->clk_scaling.start_busy)); + host->clk_scaling.start_busy = ktime_get(); + } +} /** * mmc_request_done - finish processing an MMC request * @host: MMC host which completed request @@ -142,10 +177,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) #ifdef CONFIG_MMC_PERF_PROFILING ktime_t diff; #endif - if (host->card && host->clk_scaling.enable) - host->clk_scaling.busy_time_us += - ktime_to_us(ktime_sub(ktime_get(), - host->clk_scaling.start_busy)); + if (host->card) + mmc_update_clk_scaling(host); /* Flag re-tuning needed on CRC errors */ if ((cmd->opcode != MMC_SEND_TUNING_BLOCK && @@ -340,8 +373,11 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) * frequency will be done after current thread * releases host. */ - mmc_clk_scaling(host, false); - host->clk_scaling.start_busy = ktime_get(); + mmc_clk_scaling_update_state(host, mrq); + if (!host->clk_scaling.invalid_state) { + mmc_clk_scaling(host, false); + host->clk_scaling.start_busy = ktime_get(); + } } __mmc_start_request(host, mrq); @@ -2811,7 +2847,8 @@ static bool mmc_is_vaild_state_for_clk_scaling(struct mmc_host *host) * this mode. */ if (!card || (mmc_card_mmc(card) && - card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB)) + card->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB) + || host->clk_scaling.invalid_state) goto out; if (mmc_send_status(card, &status)) { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 82cb8b901e8b..ff861fd2517b 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -450,6 +450,8 @@ struct mmc_host { bool enable; bool initialized; bool in_progress; + /* freq. transitions are not allowed in invalid state */ + bool invalid_state; struct delayed_work work; enum mmc_load state; } clk_scaling;