diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 7c139c2ff4f6..469b79ebe77b 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -80,6 +80,7 @@ #define CORE_DLL_STATUS 0x108 #define CORE_DLL_LOCK (1 << 7) +#define CORE_DDR_DLL_LOCK (1 << 11) #define CORE_VENDOR_SPEC 0x10C #define CORE_CLK_PWRSAVE (1 << 1) @@ -128,6 +129,15 @@ #define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SEL (1 << 0) #define CORE_START_CDC_TRAFFIC (1 << 6) +#define CORE_VENDOR_SPEC3 0x1B0 +#define CORE_PWRSAVE_DLL (1 << 3) + +#define CORE_DLL_CONFIG_2 0x1B4 +#define CORE_DDR_CAL_EN (1 << 0) + +#define CORE_DDR_CONFIG 0x1B8 +#define DDR_CONFIG_POR_VAL 0x80040853 + #define CORE_MCI_DATA_CNT 0x30 #define CORE_MCI_STATUS 0x34 @@ -291,6 +301,7 @@ struct sdhci_msm_host { bool en_auto_cmd21; struct device_attribute auto_cmd21_attr; atomic_t controller_clock; + bool use_cdclp533; }; enum vdd_io_level { @@ -705,32 +716,12 @@ out: static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) { - u32 wait_cnt; + u32 calib_done; int ret = 0; int cdc_err = 0; - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_msm_host *msm_host = pltfm_host->priv; pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__); - /* - * Retuning in HS400 (DDR mode) will fail, just reset the - * tuning block and restore the saved tuning phase. - */ - ret = msm_init_cm_dll(host); - if (ret) - goto out; - - /* Set the selected phase in delay line hw block */ - ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); - if (ret) - goto out; - - /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ - writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) - | CORE_CMD_DAT_TRACK_SEL), - host->ioaddr + CORE_DLL_CONFIG); - /* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG) & ~CORE_CDC_T4_DLY_SEL), @@ -800,18 +791,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) mb(); /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ - wait_cnt = 50; - while (!(readl_relaxed(host->ioaddr + CORE_CSR_CDC_STATUS0) - & CORE_CALIBRATION_DONE)) { - /* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ - if (--wait_cnt == 0) { - pr_err("%s: %s: CDC Calibration was not completed\n", + ret = readl_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0, + calib_done, (calib_done & CORE_CALIBRATION_DONE), 1, 50); + + if (ret == -ETIMEDOUT) { + pr_err("%s: %s: CDC Calibration was not completed\n", mmc_hostname(host->mmc), __func__); - ret = -ETIMEDOUT; - goto out; - } - /* wait for 1us before polling again */ - udelay(1); + goto out; } /* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ @@ -834,6 +820,81 @@ out: return ret; } +static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) +{ + u32 dll_status; + int ret = 0; + + pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__); + + /* + * Currently the CORE_DDR_CONFIG register defaults to desired + * configuration on reset. Currently reprogramming the power on + * reset (POR) value in case it might have been modified by + * bootloaders. In the future, if this changes, then the desired + * values will need to be programmed appropriately. + */ + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG); + + /* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */ + writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2) + | CORE_DDR_CAL_EN), + host->ioaddr + CORE_DLL_CONFIG_2); + + /* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */ + ret = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS, + dll_status, (dll_status & CORE_DDR_DLL_LOCK), 10, 1000); + + if (ret == -ETIMEDOUT) { + pr_err("%s: %s: CM_DLL_SDC4 Calibration was not completed\n", + mmc_hostname(host->mmc), __func__); + goto out; + } + + /* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3) + | CORE_PWRSAVE_DLL), + host->ioaddr + CORE_VENDOR_SPEC3); + mb(); +out: + pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc), + __func__, ret); + return ret; +} + +static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) +{ + int ret = 0; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = pltfm_host->priv; + + pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__); + + /* + * Retuning in HS400 (DDR mode) will fail, just reset the + * tuning block and restore the saved tuning phase. + */ + ret = msm_init_cm_dll(host); + if (ret) + goto out; + + /* Set the selected phase in delay line hw block */ + ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); + if (ret) + goto out; + + if (msm_host->use_cdclp533) + /* Calibrate CDCLP533 DLL HW */ + ret = sdhci_msm_cdclp533_calibration(host); + else + /* Calibrate CM_DLL_SDC4 HW */ + ret = sdhci_msm_cm_dll_sdc4_calibration(host); +out: + pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc), + __func__, ret); + return ret; +} + static void sdhci_msm_set_mmc_drv_type(struct sdhci_host *host, u32 opcode, u8 drv_type) { @@ -891,10 +952,10 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) pr_debug("%s: Enter %s\n", mmc_hostname(mmc), __func__); - /* CDCLP533 HW calibration is only required for HS400 mode*/ + /* CDC/SDC4 DLL HW calibration is only required for HS400 mode*/ if (msm_host->tuning_done && !msm_host->calibration_done && (mmc->ios.timing == MMC_TIMING_MMC_HS400)) { - rc = sdhci_msm_cdclp533_calibration(host); + rc = sdhci_msm_hs400_dll_calibration(host); spin_lock_irqsave(&host->lock, flags); if (!rc) msm_host->calibration_done = true; @@ -2378,7 +2439,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; struct mmc_ios curr_ios = host->mmc->ios; - u32 sup_clock, ddr_clock; + u32 sup_clock, ddr_clock, dll_lock; bool curr_pwrsave; if (!clock) { @@ -2465,7 +2526,27 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) | CORE_HC_SELECT_IN_EN), host->ioaddr + CORE_VENDOR_SPEC); } + if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) { + /* + * Poll on DLL_LOCK and DDR_DLL_LOCK bits in + * CORE_DLL_STATUS to be set. This should get set + * with in 15 us at 200 MHz. + */ + rc = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS, + dll_lock, (dll_lock & (CORE_DLL_LOCK | + CORE_DDR_DLL_LOCK)), 10, 1000); + if (rc == -ETIMEDOUT) + pr_err("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n", + mmc_hostname(host->mmc), + dll_lock); + } } else { + if (!msm_host->use_cdclp533) + /* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + writel_relaxed((readl_relaxed(host->ioaddr + + CORE_VENDOR_SPEC3) & ~CORE_PWRSAVE_DLL), + host->ioaddr + CORE_VENDOR_SPEC3); + /* Select the default clock (free running MCLK) */ writel_relaxed(((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) & ~CORE_HC_MCLK_SEL_MASK) @@ -2667,6 +2748,13 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, (readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES) | caps), host->ioaddr + CORE_VENDOR_SPEC_CAPABILITIES0); } + + /* + * SDCC 5 controller with major version 1, minor version 0x34 and later + * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. + */ + if ((major == 1) && (minor < 0x34)) + msm_host->use_cdclp533 = true; } static int sdhci_msm_probe(struct platform_device *pdev)