Skip to content

Commit

Permalink
mmc: core: Add support for HS400 re-tuning
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Adrian Hunter authored and Ulf Hansson committed Jun 1, 2015
1 parent ed16f58 commit 6376f69
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
2 changes: 2 additions & 0 deletions drivers/mmc/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

17 changes: 17 additions & 0 deletions drivers/mmc/core/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -354,8 +355,24 @@ int mmc_retune(struct mmc_host *host)

host->doing_retune = 1;

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;
Expand Down
88 changes: 88 additions & 0 deletions drivers/mmc/core/mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 6376f69

Please sign in to comment.