Skip to content

Commit

Permalink
mmc: sdhci-acpi: Fix HS400 tuning for AMDI0040
Browse files Browse the repository at this point in the history
The AMD eMMC Controller can only use the tuned clock while in HS200 and
HS400 mode. If we switch to a different mode, we need to disable the
tuned clock. If we have previously performed tuning and switch back to
HS200 or HS400, we can re-enable the tuned clock.

Previously the tuned clock was not getting disabled when switching to
DDR52 which is part of the HS400 tuning sequence.

Fixes: 34597a3 ("mmc: sdhci-acpi: Add support for ACPI HID of AMD Controller with HS400")
Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/20200819125832.v2.1.Ie8f0689ec9f449203328b37409d1cf06b565f331@changeid
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
Raul E Rangel authored and Ulf Hansson committed Aug 21, 2020
1 parent 9123e3a commit 61d7437
Showing 1 changed file with 57 additions and 10 deletions.
67 changes: 57 additions & 10 deletions drivers/mmc/host/sdhci-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,11 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd = {
.caps = MMC_CAP_NONREMOVABLE,
};

struct amd_sdhci_host {
bool tuned_clock;
bool dll_enabled;
};

/* AMD sdhci reset dll register. */
#define SDHCI_AMD_RESET_DLL_REGISTER 0x908

Expand All @@ -555,26 +560,66 @@ static void sdhci_acpi_amd_hs400_dll(struct sdhci_host *host)
}

/*
* For AMD Platform it is required to disable the tuning
* bit first controller to bring to HS Mode from HS200
* mode, later enable to tune to HS400 mode.
* The initialization sequence for HS400 is:
* HS->HS200->Perform Tuning->HS->HS400
*
* The re-tuning sequence is:
* HS400->DDR52->HS->HS200->Perform Tuning->HS->HS400
*
* The AMD eMMC Controller can only use the tuned clock while in HS200 and HS400
* mode. If we switch to a different mode, we need to disable the tuned clock.
* If we have previously performed tuning and switch back to HS200 or
* HS400, we can re-enable the tuned clock.
*
*/
static void amd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_acpi_host *acpi_host = sdhci_priv(host);
struct amd_sdhci_host *amd_host = sdhci_acpi_priv(acpi_host);
unsigned int old_timing = host->timing;
u16 val;

sdhci_set_ios(mmc, ios);
if (old_timing == MMC_TIMING_MMC_HS200 &&
ios->timing == MMC_TIMING_MMC_HS)
sdhci_writew(host, 0x9, SDHCI_HOST_CONTROL2);
if (old_timing != MMC_TIMING_MMC_HS400 &&
ios->timing == MMC_TIMING_MMC_HS400) {
sdhci_writew(host, 0x80, SDHCI_HOST_CONTROL2);
sdhci_acpi_amd_hs400_dll(host);

if (old_timing != host->timing && amd_host->tuned_clock) {
if (host->timing == MMC_TIMING_MMC_HS400 ||
host->timing == MMC_TIMING_MMC_HS200) {
val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
val |= SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
} else {
val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
val &= ~SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
}

/* DLL is only required for HS400 */
if (host->timing == MMC_TIMING_MMC_HS400 &&
!amd_host->dll_enabled) {
sdhci_acpi_amd_hs400_dll(host);
amd_host->dll_enabled = true;
}
}
}

static int amd_sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
int err;
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_acpi_host *acpi_host = sdhci_priv(host);
struct amd_sdhci_host *amd_host = sdhci_acpi_priv(acpi_host);

amd_host->tuned_clock = false;

err = sdhci_execute_tuning(mmc, opcode);

if (!err && !host->tuning_err)
amd_host->tuned_clock = true;

return err;
}

static const struct sdhci_ops sdhci_acpi_ops_amd = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
Expand Down Expand Up @@ -602,6 +647,7 @@ static int sdhci_acpi_emmc_amd_probe_slot(struct platform_device *pdev,

host->mmc_host_ops.select_drive_strength = amd_select_drive_strength;
host->mmc_host_ops.set_ios = amd_set_ios;
host->mmc_host_ops.execute_tuning = amd_sdhci_execute_tuning;
return 0;
}

Expand All @@ -613,6 +659,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_amd_emmc = {
SDHCI_QUIRK_32BIT_ADMA_SIZE,
.quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
.probe_slot = sdhci_acpi_emmc_amd_probe_slot,
.priv_size = sizeof(struct amd_sdhci_host),
};

struct sdhci_acpi_uid_slot {
Expand Down

0 comments on commit 61d7437

Please sign in to comment.