#define SDHCI_VNDR_CAP_OVERRIDES_0 0x10c
#define SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_SHIFT 8
+#define SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_MASK 0x3F
#define SDHCI_VNDR_MISC_CTRL 0x120
#define SDHCI_VNDR_MISC_CTRL_ENABLE_SDR104_SUPPORT 0x8
#define SDMMC_VENDOR_IO_TRIM_CNTRL_0 0x1AC
#define SDMMC_VENDOR_IO_TRIM_CNTRL_0_SEL_VREG_MASK 0x4
+#define SDMMC_VNDR_IO_TRIM_CNTRL_0 0x1AC
+#define SDMMC_VNDR_IO_TRIM_CNTRL_0_SEL_VREG 0x4
/* Erratum: Version register is invalid in HW */
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
/* Erratum: Enable block gap interrupt detection */
struct pinctrl_state *schmitt_enable[2];
struct pinctrl_state *schmitt_disable[2];
int drive_group_sel;
+ u32 dll_calib;
};
static unsigned int boot_volt_req_refcount;
if (uhs == MMC_TIMING_MMC_HS400) {
ctrl_2 = sdhci_readl(host, SDHCI_VNDR_CAP_OVERRIDES_0);
- ctrl_2 &= ~(0x3F <<
+ ctrl_2 &= ~(SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_MASK <<
SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_SHIFT);
- ctrl_2 |= ((plat->dqs_trim_delay & 0x3F) <<
+ ctrl_2 |= ((plat->dqs_trim_delay &
+ SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_MASK) <<
SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_SHIFT);
sdhci_writel(host, ctrl_2, SDHCI_VNDR_CAP_OVERRIDES_0);
}
vendor_ctrl |= SDHCI_VNDR_TUN_CTRL_RETUNE_REQ_EN;
sdhci_writel(host, vendor_ctrl, SDHCI_VNDR_TUN_CTRL);
}
+ /* Restore DLL calibration and DQS Trim delay values */
+ if (plat->dll_calib_needed && tegra_host->dll_calib)
+ sdhci_writel(host, tegra_host->dll_calib,
+ SDHCI_VNDR_DLLCAL_CFG);
+ if (plat->dqs_trim_delay) {
+ misc_ctrl = ((plat->dqs_trim_delay &
+ SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_MASK) <<
+ SDHCI_VNDR_CAP_OVERRIDES_0_DQS_TRIM_SHIFT);
+ sdhci_writel(host, misc_ctrl, SDHCI_VNDR_CAP_OVERRIDES_0);
+ }
+
+#if defined(CONFIG_ARCH_TEGRA_21x_SOC)
+ /* Enable Band Gap trimmers and VIO supply.
+ * Wait for 3usec after enabling the trimmers.
+ */
+ misc_ctrl = sdhci_readl(host, SDMMC_VNDR_IO_TRIM_CNTRL_0);
+ misc_ctrl &= ~(SDMMC_VNDR_IO_TRIM_CNTRL_0_SEL_VREG);
+ sdhci_writel(host, misc_ctrl, SDMMC_VNDR_IO_TRIM_CNTRL_0);
+ udelay(3);
+#endif
}
static int tegra_sdhci_buswidth(struct sdhci_host *sdhci, int bus_width)
{
u32 dll_cfg;
unsigned timeout = 5;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhci);
+ struct sdhci_tegra *tegra_host = pltfm_host->priv;
dll_cfg = sdhci_readl(sdhci, SDHCI_VNDR_DLLCAL_CFG);
dll_cfg |= SDHCI_VNDR_DLLCAL_CFG_EN_CALIBRATE;
if (!timeout) {
dev_err(mmc_dev(sdhci->mmc), "DLL calibration is failed\n");
}
+ tegra_host->dll_calib = sdhci_readl(sdhci, SDHCI_VNDR_DLLCAL_CFG);
}
static void tegra_sdhci_update_sdmmc_pinctrl_register(struct sdhci_host *sdhci,
SDHCI_QUIRK2_NON_STD_VOLTAGE_SWITCHING |
SDHCI_QUIRK2_NO_CALC_MAX_DISCARD_TO |
SDHCI_QUIRK2_REG_ACCESS_REQ_HOST_CLK |
+ SDHCI_QUIRK2_HOST_OFF_CARD_ON |
+ SDHCI_QUIRK2_USE_64BIT_ADDR |
SDHCI_QUIRK2_SUPPORT_64BIT_DMA,
.ops = &tegra_sdhci_ops,
};
plat->mmc_data.built_in = of_property_read_bool(np, "built-in");
plat->update_pinctrl_settings = of_property_read_bool(np,
"update-pinctrl-settings");
+ plat->dll_calib_needed = of_property_read_bool(np, "dll-calib-needed");
plat->disable_clock_gate = of_property_read_bool(np,
"disable-clock-gate");
of_property_read_u8(np, "default-drv-type", &plat->default_drv_type);