mirror of https://gitee.com/openkylin/linux.git
mmc: sdhci-msm: Factor out sdhci_msm_hc_select_mode
This factors out sdhci_msm_hc_select_mode to later use it during enhanced_strobe mode select. It also further breaks sdhci_msm_hc_select_mode into separate functions for configuring HS400 mode or other modes. Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org> Tested-by: Jeremy McNicoll <jeremymc@redhat.com> Acked-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
73a85069f9
commit
b54aaa8a4f
|
@ -464,6 +464,119 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void msm_hc_select_default(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 config;
|
||||
|
||||
if (!msm_host->use_cdclp533) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
config &= ~CORE_PWRSAVE_DLL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_DFLT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/*
|
||||
* Disable HC_SELECT_IN to be able to use the UHS mode select
|
||||
* configuration from Host Control2 register for all other
|
||||
* modes.
|
||||
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
|
||||
* in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_SELECT_IN_EN;
|
||||
config &= ~CORE_HC_SELECT_IN_MASK;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void msm_hc_select_hs400(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 config, dll_lock;
|
||||
int rc;
|
||||
|
||||
/* Select the divided clock (free running MCLK/2) */
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_HS400;
|
||||
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
/*
|
||||
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
|
||||
* register
|
||||
*/
|
||||
if (msm_host->tuning_done && !msm_host->calibration_done) {
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config |= CORE_HC_SELECT_IN_HS400;
|
||||
config |= CORE_HC_SELECT_IN_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
|
||||
/*
|
||||
* Poll on DLL_LOCK or DDR_DLL_LOCK bits in
|
||||
* CORE_DLL_STATUS to be set. This should get set
|
||||
* within 15 us at 200 MHz.
|
||||
*/
|
||||
rc = readl_relaxed_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);
|
||||
}
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
}
|
||||
|
||||
/*
|
||||
* sdhci_msm_hc_select_mode :- In general all timing modes are
|
||||
* controlled via UHS mode select in Host Control2 register.
|
||||
* eMMC specific HS200/HS400 doesn't have their respective modes
|
||||
* defined here, hence we use these values.
|
||||
*
|
||||
* HS200 - SDR104 (Since they both are equivalent in functionality)
|
||||
* HS400 - This involves multiple configurations
|
||||
* Initially SDR104 - when tuning is required as HS200
|
||||
* Then when switching to DDR @ 400MHz (HS400) we use
|
||||
* the vendor specific HC_SELECT_IN to control the mode.
|
||||
*
|
||||
* In addition to controlling the modes we also need to select the
|
||||
* correct input clock for DLL depending on the mode.
|
||||
*
|
||||
* HS400 - divided clock (free running MCLK/2)
|
||||
* All other modes - default (free running MCLK)
|
||||
*/
|
||||
void sdhci_msm_hc_select_mode(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
|
||||
if (ios.timing == MMC_TIMING_MMC_HS400)
|
||||
msm_hc_select_hs400(host);
|
||||
else
|
||||
msm_hc_select_default(host);
|
||||
}
|
||||
|
||||
static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -894,7 +1007,6 @@ 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 = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios curr_ios = host->mmc->ios;
|
||||
u32 config, dll_lock;
|
||||
int rc;
|
||||
|
||||
if (!clock) {
|
||||
|
@ -913,93 +1025,8 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
curr_ios.timing == MMC_TIMING_MMC_DDR52 ||
|
||||
curr_ios.timing == MMC_TIMING_MMC_HS400)
|
||||
clock *= 2;
|
||||
/*
|
||||
* In general all timing modes are controlled via UHS mode select in
|
||||
* Host Control2 register. eMMC specific HS200/HS400 doesn't have
|
||||
* their respective modes defined here, hence we use these values.
|
||||
*
|
||||
* HS200 - SDR104 (Since they both are equivalent in functionality)
|
||||
* HS400 - This involves multiple configurations
|
||||
* Initially SDR104 - when tuning is required as HS200
|
||||
* Then when switching to DDR @ 400MHz (HS400) we use
|
||||
* the vendor specific HC_SELECT_IN to control the mode.
|
||||
*
|
||||
* In addition to controlling the modes we also need to select the
|
||||
* correct input clock for DLL depending on the mode.
|
||||
*
|
||||
* HS400 - divided clock (free running MCLK/2)
|
||||
* All other modes - default (free running MCLK)
|
||||
*/
|
||||
if (curr_ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
/* Select the divided clock (free running MCLK/2) */
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_HS400;
|
||||
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
/*
|
||||
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
|
||||
* register
|
||||
*/
|
||||
if (msm_host->tuning_done && !msm_host->calibration_done) {
|
||||
/*
|
||||
* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
|
||||
* field in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config |= CORE_HC_SELECT_IN_HS400;
|
||||
config |= CORE_HC_SELECT_IN_EN;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
if (!msm_host->clk_rate && !msm_host->use_cdclp533) {
|
||||
/*
|
||||
* Poll on DLL_LOCK or DDR_DLL_LOCK bits in
|
||||
* CORE_DLL_STATUS to be set. This should get set
|
||||
* within 15 us at 200 MHz.
|
||||
*/
|
||||
rc = readl_relaxed_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) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
config &= ~CORE_PWRSAVE_DLL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
CORE_VENDOR_SPEC3);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_MCLK_SEL_MASK;
|
||||
config |= CORE_HC_MCLK_SEL_DFLT;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
|
||||
/*
|
||||
* Disable HC_SELECT_IN to be able to use the UHS mode select
|
||||
* configuration from Host Control2 register for all other
|
||||
* modes.
|
||||
* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field
|
||||
* in VENDOR_SPEC_FUNC
|
||||
*/
|
||||
config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC);
|
||||
config &= ~CORE_HC_SELECT_IN_EN;
|
||||
config &= ~CORE_HC_SELECT_IN_MASK;
|
||||
writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure above writes impacting free running MCLK are completed
|
||||
* before changing the clk_rate at GCC.
|
||||
*/
|
||||
wmb();
|
||||
sdhci_msm_hc_select_mode(host);
|
||||
|
||||
rc = clk_set_rate(msm_host->clk, clock);
|
||||
if (rc) {
|
||||
|
|
Loading…
Reference in New Issue