mmc: core: Allow changing bus frequency for SD/eMMC cards in runtime

Currently, bus frequency is set during the card initialization and
never changed until a new card is inserted. In some low power use
cases, scaling the clock frequencies while the card is in transfer
state would allow power savings. This change allows bus frequency
to be changed after the card initialization.

Change-Id: Iac064221199f69d162d91f5311becd735c15700a
Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
[merez@codeaurora.org: remove mmc and sd ops_unsafe settings as they are not in 3.14]
Signed-off-by: Maya Erez <merez@codeaurora.org>
[venkatg@codeaurora.org: Fix refactor of mmc_card_* functions as used
in 3.14 kernel]
Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
[subhashj@codeaurora.org: fixed trivial merge conflicts & fixed
compilation error]
Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
Sujit Reddy Thumma 2014-12-03 09:22:39 +02:00 committed by Subhash Jadavani
parent 3d6d2ddeaf
commit f3bca6c776
3 changed files with 132 additions and 0 deletions

View file

@ -28,6 +28,7 @@ struct mmc_bus_ops {
int (*alive)(struct mmc_host *);
int (*shutdown)(struct mmc_host *);
int (*reset)(struct mmc_host *);
int (*change_bus_speed)(struct mmc_host *, unsigned long *);
};
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);

View file

@ -1298,6 +1298,69 @@ err:
return err;
}
/**
* mmc_change_bus_speed() - Change MMC card bus frequency at runtime
* @host: pointer to mmc host structure
* @freq: pointer to desired frequency to be set
*
* Change the MMC card bus frequency at runtime after the card is
* initialized. Callers are expected to make sure of the card's
* state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
*
* If the frequency to change is greater than max. supported by card,
* *freq is changed to max. supported by card and if it is less than min.
* supported by host, *freq is changed to min. supported by host.
*/
static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
{
int err = 0;
struct mmc_card *card;
mmc_claim_host(host);
/*
* Assign card pointer after claiming host to avoid race
* conditions that may arise during removal of the card.
*/
card = host->card;
if (!card || !freq) {
err = -EINVAL;
goto out;
}
if (mmc_card_hs(card) || mmc_card_hs200(card)
|| mmc_card_ddr52(card)) {
if (*freq > card->ext_csd.hs_max_dtr)
*freq = card->ext_csd.hs_max_dtr;
} else if (*freq > card->csd.max_dtr) {
*freq = card->csd.max_dtr;
}
if (*freq < host->f_min)
*freq = host->f_min;
mmc_set_clock(host, (unsigned int) (*freq));
if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
/*
* We try to probe host driver for tuning for any
* frequency, it is host driver responsibility to
* perform actual tuning only when required.
*/
mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK_HS200);
mmc_host_clk_release(card->host);
if (err)
pr_warn("%s: %s: tuning execution failed %d\n",
mmc_hostname(card->host), __func__, err);
}
out:
mmc_release_host(host);
return err;
}
/*
* Activate High Speed or HS200 mode if supported.
*/
@ -1996,6 +2059,7 @@ static const struct mmc_bus_ops mmc_ops = {
.runtime_resume = mmc_runtime_resume,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.change_bus_speed = mmc_change_bus_speed,
.reset = mmc_reset,
};

View file

@ -569,6 +569,72 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return 0;
}
/**
* mmc_sd_change_bus_speed() - Change SD card bus frequency at runtime
* @host: pointer to mmc host structure
* @freq: pointer to desired frequency to be set
*
* Change the SD card bus frequency at runtime after the card is
* initialized. Callers are expected to make sure of the card's
* state (DATA/RCV/TRANSFER) beforing changing the frequency at runtime.
*
* If the frequency to change is greater than max. supported by card,
* *freq is changed to max. supported by card and if it is less than min.
* supported by host, *freq is changed to min. supported by host.
*/
static int mmc_sd_change_bus_speed(struct mmc_host *host, unsigned long *freq)
{
int err = 0;
struct mmc_card *card;
mmc_claim_host(host);
/*
* Assign card pointer after claiming host to avoid race
* conditions that may arise during removal of the card.
*/
card = host->card;
/* sanity checks */
if (!card || !freq) {
err = -EINVAL;
goto out;
}
if (mmc_card_uhs(card)) {
if (*freq > card->sw_caps.uhs_max_dtr)
*freq = card->sw_caps.uhs_max_dtr;
} else {
if (*freq > mmc_sd_get_max_clock(card))
*freq = mmc_sd_get_max_clock(card);
}
if (*freq < host->f_min)
*freq = host->f_min;
mmc_set_clock(host, (unsigned int) (*freq));
if (!mmc_host_is_spi(card->host) && mmc_card_uhs(card)
&& card->host->ops->execute_tuning) {
/*
* We try to probe host driver for tuning for any
* frequency, it is host driver responsibility to
* perform actual tuning only when required.
*/
mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK);
mmc_host_clk_release(card->host);
if (err)
pr_warn("%s: %s: tuning execution failed %d\n",
mmc_hostname(card->host), __func__, err);
}
out:
mmc_release_host(host);
return err;
}
/*
* UHS-I specific initialization procedure
*/
@ -1253,6 +1319,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.resume = mmc_sd_resume,
.alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
.change_bus_speed = mmc_sd_change_bus_speed,
.reset = mmc_sd_reset,
};