| From f23cc3ba491af77395cea3f9d51204398729f26b Mon Sep 17 00:00:00 2001 |
| From: Raul E Rangel <rrangel@chromium.org> |
| Date: Mon, 28 Sep 2020 15:59:20 -0600 |
| Subject: mmc: sdhci-acpi: AMDI0040: Set SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
| |
| From: Raul E Rangel <rrangel@chromium.org> |
| |
| commit f23cc3ba491af77395cea3f9d51204398729f26b upstream. |
| |
| This change fixes HS400 tuning for devices with invalid presets. |
| |
| SDHCI presets are not currently used for eMMC HS/HS200/HS400, but are |
| used for DDR52. The HS400 retuning sequence is: |
| |
| HS400->DDR52->HS->HS200->Perform Tuning->HS->HS400 |
| |
| This means that when HS400 tuning happens, we transition through DDR52 |
| for a very brief period. This causes presets to be enabled |
| unintentionally and stay enabled when transitioning back to HS200 or |
| HS400. Some firmware has invalid presets, so we end up with driver |
| strengths that can cause I/O problems. |
| |
| Fixes: 34597a3f60b1 ("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> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20200928154718.1.Icc21d4b2f354e83e26e57e270dc952f5fe0b0a40@changeid |
| Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/mmc/host/sdhci-acpi.c | 37 +++++++++++++++++++++++++++++++++++++ |
| 1 file changed, 37 insertions(+) |
| |
| --- a/drivers/mmc/host/sdhci-acpi.c |
| +++ b/drivers/mmc/host/sdhci-acpi.c |
| @@ -662,6 +662,43 @@ static int sdhci_acpi_emmc_amd_probe_slo |
| (host->mmc->caps & MMC_CAP_1_8V_DDR)) |
| host->mmc->caps2 = MMC_CAP2_HS400_1_8V; |
| |
| + /* |
| + * There are two types of presets out in the wild: |
| + * 1) Default/broken presets. |
| + * These presets have two sets of problems: |
| + * a) The clock divisor for SDR12, SDR25, and SDR50 is too small. |
| + * This results in clock frequencies that are 2x higher than |
| + * acceptable. i.e., SDR12 = 25 MHz, SDR25 = 50 MHz, SDR50 = |
| + * 100 MHz.x |
| + * b) The HS200 and HS400 driver strengths don't match. |
| + * By default, the SDR104 preset register has a driver strength of |
| + * A, but the (internal) HS400 preset register has a driver |
| + * strength of B. As part of initializing HS400, HS200 tuning |
| + * needs to be performed. Having different driver strengths |
| + * between tuning and operation is wrong. It results in different |
| + * rise/fall times that lead to incorrect sampling. |
| + * 2) Firmware with properly initialized presets. |
| + * These presets have proper clock divisors. i.e., SDR12 => 12MHz, |
| + * SDR25 => 25 MHz, SDR50 => 50 MHz. Additionally the HS200 and |
| + * HS400 preset driver strengths match. |
| + * |
| + * Enabling presets for HS400 doesn't work for the following reasons: |
| + * 1) sdhci_set_ios has a hard coded list of timings that are used |
| + * to determine if presets should be enabled. |
| + * 2) sdhci_get_preset_value is using a non-standard register to |
| + * read out HS400 presets. The AMD controller doesn't support this |
| + * non-standard register. In fact, it doesn't expose the HS400 |
| + * preset register anywhere in the SDHCI memory map. This results |
| + * in reading a garbage value and using the wrong presets. |
| + * |
| + * Since HS400 and HS200 presets must be identical, we could |
| + * instead use the the SDR104 preset register. |
| + * |
| + * If the above issues are resolved we could remove this quirk for |
| + * firmware that that has valid presets (i.e., SDR12 <= 12 MHz). |
| + */ |
| + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; |
| + |
| 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; |