mmc: core: Add support for HS400 re-tuning

HS400 re-tuning must be done in HS200 mode. Add
the ability to switch from HS400 mode to HS200
mode before re-tuning and switch back to HS400
after re-tuning.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Adrian Hunter 2015-05-07 13:10:20 +03:00 committed by Ulf Hansson
parent ed16f58dc0
commit 6376f69d20
3 changed files with 108 additions and 1 deletions

View File

@ -88,6 +88,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
int mmc_execute_tuning(struct mmc_card *card);
int mmc_hs200_to_hs400(struct mmc_card *card);
int mmc_hs400_to_hs200(struct mmc_card *card);
#endif

View File

@ -340,6 +340,7 @@ void mmc_retune_release(struct mmc_host *host)
int mmc_retune(struct mmc_host *host)
{
bool return_to_hs400 = false;
int err;
if (host->retune_now)
@ -354,8 +355,24 @@ int mmc_retune(struct mmc_host *host)
host->doing_retune = 1;
err = mmc_execute_tuning(host->card);
if (host->ios.timing == MMC_TIMING_MMC_HS400) {
err = mmc_hs400_to_hs200(host->card);
if (err)
goto out;
return_to_hs400 = true;
if (host->ops->prepare_hs400_tuning)
host->ops->prepare_hs400_tuning(host, &host->ios);
}
err = mmc_execute_tuning(host->card);
if (err)
goto out;
if (return_to_hs400)
err = mmc_hs200_to_hs400(host->card);
out:
host->doing_retune = 0;
return err;

View File

@ -1092,6 +1092,94 @@ static int mmc_select_hs400(struct mmc_card *card)
return 0;
}
int mmc_hs200_to_hs400(struct mmc_card *card)
{
return mmc_select_hs400(card);
}
/* Caller must hold re-tuning */
static int mmc_switch_status(struct mmc_card *card)
{
u32 status;
int err;
err = mmc_send_status(card, &status);
if (err)
return err;
return mmc_switch_status_error(card->host, status);
}
int mmc_hs400_to_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
bool send_status = true;
unsigned int max_dtr;
int err;
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
send_status = false;
/* Reduce frequency to HS */
max_dtr = card->ext_csd.hs_max_dtr;
mmc_set_clock(host, max_dtr);
/* Switch HS400 to HS DDR */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time,
true, send_status, true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
if (!send_status) {
err = mmc_switch_status(card);
if (err)
goto out_err;
}
/* Switch HS DDR to HS */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH,
EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time,
true, send_status, true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_HS);
if (!send_status) {
err = mmc_switch_status(card);
if (err)
goto out_err;
}
/* Switch HS to HS200 */
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
EXT_CSD_TIMING_HS200,
card->ext_csd.generic_cmd6_time, true, send_status,
true);
if (err)
goto out_err;
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
if (!send_status) {
err = mmc_switch_status(card);
if (err)
goto out_err;
}
mmc_set_bus_speed(card);
return 0;
out_err:
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
__func__, err);
return err;
}
/*
* For device supporting HS200 mode, the following sequence
* should be done before executing the tuning process.