static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) { unsigned int min, max; /* * Start search for minimum tap value at 10, as smaller values are * may wrongly be reported as working but fail at higher speeds, * according to the TRM. */ min = 10; while (min < 255) { tegra_sdhci_set_tap(host, min); if (!mmc_send_tuning(host->mmc, opcode, NULL)) break; min++; } /* Find the maximum tap value that still passes. */ max = min + 1; while (max < 255) { tegra_sdhci_set_tap(host, max); if (mmc_send_tuning(host->mmc, opcode, NULL)) { max--; break; } max++; } /* The TRM states the ideal tap value is at 75% in the passing range. */ tegra_sdhci_set_tap(host, min + ((max - min) * 3 / 4)); return mmc_send_tuning(host->mmc, opcode, NULL); }
static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; u32 misc_ctrl, clk_ctrl, pad_ctrl; sdhci_reset(host, mask); if (!(mask & SDHCI_RESET_ALL)) return; tegra_sdhci_set_tap(host, tegra_host->default_tap); misc_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_MISC_CTRL); clk_ctrl = sdhci_readl(host, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); misc_ctrl &= ~(SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 | SDHCI_MISC_CTRL_ENABLE_SDR50 | SDHCI_MISC_CTRL_ENABLE_DDR50 | SDHCI_MISC_CTRL_ENABLE_SDR104); clk_ctrl &= ~(SDHCI_CLOCK_CTRL_TRIM_MASK | SDHCI_CLOCK_CTRL_SPI_MODE_CLKEN_OVERRIDE); if (tegra_sdhci_is_pad_and_regulator_valid(host)) { /* Erratum: Enable SDHCI spec v3.00 support */ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300; /* Advertise UHS modes as supported by host */ if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR50) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR50; if (soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_DDR50; if (soc_data->nvquirks & NVQUIRK_ENABLE_SDR104) misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDR104; if (soc_data->nvquirks & SDHCI_MISC_CTRL_ENABLE_SDR50) clk_ctrl |= SDHCI_CLOCK_CTRL_SDR50_TUNING_OVERRIDE; } clk_ctrl |= tegra_host->default_trim << SDHCI_CLOCK_CTRL_TRIM_SHIFT; sdhci_writel(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL); sdhci_writel(host, clk_ctrl, SDHCI_TEGRA_VENDOR_CLOCK_CTRL); if (soc_data->nvquirks & NVQUIRK_HAS_PADCALIB) { pad_ctrl = sdhci_readl(host, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); pad_ctrl &= ~SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK; pad_ctrl |= SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL; sdhci_writel(host, pad_ctrl, SDHCI_TEGRA_SDMEM_COMP_PADCTRL); tegra_host->pad_calib_required = true; } tegra_host->ddr_signaling = false; }
static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); bool set_default_tap = false; bool set_dqs_trim = false; bool do_hs400_dll_cal = false; switch (timing) { case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: /* Don't set default tap on tunable modes. */ break; case MMC_TIMING_MMC_HS400: set_dqs_trim = true; do_hs400_dll_cal = true; break; case MMC_TIMING_MMC_DDR52: case MMC_TIMING_UHS_DDR50: tegra_host->ddr_signaling = true; set_default_tap = true; break; default: set_default_tap = true; break; } sdhci_set_uhs_signaling(host, timing); tegra_sdhci_pad_autocalib(host); if (set_default_tap) tegra_sdhci_set_tap(host, tegra_host->default_tap); if (set_dqs_trim) tegra_sdhci_set_dqs_trim(host, tegra_host->dqs_trim); if (do_hs400_dll_cal) tegra_sdhci_hs400_dll_cal(host); }