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 <sthumma@codeaurora.org>
This commit is contained in:
Sujit Reddy Thumma 2014-04-21 12:56:21 +05:30
parent d09bc854df
commit dec9d9b888
2 changed files with 36 additions and 4 deletions

View File

@ -169,9 +169,35 @@ 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) {
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));
@ -334,8 +360,11 @@ 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();
}
}
host->ops->request(host, mrq);
@ -2984,7 +3013,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)) {

View File

@ -445,6 +445,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;