mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 11:55:49 +07: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,
|
.caps = MMC_CAP_NONREMOVABLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct amd_sdhci_host {
|
||||||
|
bool tuned_clock;
|
||||||
|
bool dll_enabled;
|
||||||
|
};
|
||||||
|
|
||||||
/* AMD sdhci reset dll register. */
|
/* AMD sdhci reset dll register. */
|
||||||
#define SDHCI_AMD_RESET_DLL_REGISTER 0x908
|
#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
|
* The initialization sequence for HS400 is:
|
||||||
* bit first controller to bring to HS Mode from HS200
|
* HS->HS200->Perform Tuning->HS->HS400
|
||||||
* mode, later enable to tune to HS400 mode.
|
*
|
||||||
|
* 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)
|
static void amd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = mmc_priv(mmc);
|
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;
|
unsigned int old_timing = host->timing;
|
||||||
|
u16 val;
|
||||||
|
|
||||||
sdhci_set_ios(mmc, ios);
|
sdhci_set_ios(mmc, ios);
|
||||||
if (old_timing == MMC_TIMING_MMC_HS200 &&
|
|
||||||
ios->timing == MMC_TIMING_MMC_HS)
|
if (old_timing != host->timing && amd_host->tuned_clock) {
|
||||||
sdhci_writew(host, 0x9, SDHCI_HOST_CONTROL2);
|
if (host->timing == MMC_TIMING_MMC_HS400 ||
|
||||||
if (old_timing != MMC_TIMING_MMC_HS400 &&
|
host->timing == MMC_TIMING_MMC_HS200) {
|
||||||
ios->timing == MMC_TIMING_MMC_HS400) {
|
val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
sdhci_writew(host, 0x80, SDHCI_HOST_CONTROL2);
|
val |= SDHCI_CTRL_TUNED_CLK;
|
||||||
sdhci_acpi_amd_hs400_dll(host);
|
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 = {
|
static const struct sdhci_ops sdhci_acpi_ops_amd = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.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.select_drive_strength = amd_select_drive_strength;
|
||||||
host->mmc_host_ops.set_ios = amd_set_ios;
|
host->mmc_host_ops.set_ios = amd_set_ios;
|
||||||
|
host->mmc_host_ops.execute_tuning = amd_sdhci_execute_tuning;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,6 +659,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_amd_emmc = {
|
|||||||
SDHCI_QUIRK_32BIT_ADMA_SIZE,
|
SDHCI_QUIRK_32BIT_ADMA_SIZE,
|
||||||
.quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
|
.quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
|
||||||
.probe_slot = sdhci_acpi_emmc_amd_probe_slot,
|
.probe_slot = sdhci_acpi_emmc_amd_probe_slot,
|
||||||
|
.priv_size = sizeof(struct amd_sdhci_host),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_acpi_uid_slot {
|
struct sdhci_acpi_uid_slot {
|
||||||
|
Loading…
Reference in New Issue
Block a user