mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 20:48:49 +08:00
mmc: sdhci-acpi: Fix HS400 tuning for AMDI0040
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: 34597a3f60
("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>
This commit is contained in:
parent
9123e3a74e
commit
61d7437ed1
@ -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
|
||||
|
||||
@ -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,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user