mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
					
				
					 1 changed files with 57 additions and 10 deletions
				
			
		| 
						 | 
					@ -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 a new issue