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:
parent
3d6d2ddeaf
commit
f3bca6c776
3 changed files with 132 additions and 0 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue