mmc: sdhci-msm: Add HS400 platform support
The following msm platform specific changes are added to support HS400. - Allow tuning for HS400 mode. - Configure HS400 timing mode using the VENDOR_SPECIFIC_FUNC register. Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org> Signed-off-by: Ritesh Harjani <riteshh@codeaurora.org> Acked-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
b12d44db4b
commit
ff06ce4178
@ -32,6 +32,7 @@
|
|||||||
#define HC_MODE_EN 0x1
|
#define HC_MODE_EN 0x1
|
||||||
#define CORE_POWER 0x0
|
#define CORE_POWER 0x0
|
||||||
#define CORE_SW_RST BIT(7)
|
#define CORE_SW_RST BIT(7)
|
||||||
|
#define FF_CLK_SW_RST_DIS BIT(13)
|
||||||
|
|
||||||
#define CORE_PWRCTL_STATUS 0xdc
|
#define CORE_PWRCTL_STATUS 0xdc
|
||||||
#define CORE_PWRCTL_MASK 0xe0
|
#define CORE_PWRCTL_MASK 0xe0
|
||||||
@ -65,10 +66,17 @@
|
|||||||
|
|
||||||
#define CORE_VENDOR_SPEC 0x10c
|
#define CORE_VENDOR_SPEC 0x10c
|
||||||
#define CORE_CLK_PWRSAVE BIT(1)
|
#define CORE_CLK_PWRSAVE BIT(1)
|
||||||
|
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
||||||
|
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
||||||
|
#define CORE_HC_MCLK_SEL_MASK (3 << 8)
|
||||||
|
#define CORE_HC_SELECT_IN_EN BIT(18)
|
||||||
|
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
||||||
|
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
||||||
|
|
||||||
#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
|
#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c
|
||||||
|
|
||||||
#define SDHCI_MSM_MIN_CLOCK 400000
|
#define SDHCI_MSM_MIN_CLOCK 400000
|
||||||
|
#define CORE_FREQ_100MHZ (100 * 1000 * 1000)
|
||||||
|
|
||||||
#define CDR_SELEXT_SHIFT 20
|
#define CDR_SELEXT_SHIFT 20
|
||||||
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
|
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
|
||||||
@ -87,6 +95,8 @@ struct sdhci_msm_host {
|
|||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
bool use_14lpp_dll_reset;
|
bool use_14lpp_dll_reset;
|
||||||
|
bool tuning_done;
|
||||||
|
bool calibration_done;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Platform specific tuning */
|
/* Platform specific tuning */
|
||||||
@ -174,8 +184,8 @@ out:
|
|||||||
* Find out the greatest range of consecuitive selected
|
* Find out the greatest range of consecuitive selected
|
||||||
* DLL clock output phases that can be used as sampling
|
* DLL clock output phases that can be used as sampling
|
||||||
* setting for SD3.0 UHS-I card read operation (in SDR104
|
* setting for SD3.0 UHS-I card read operation (in SDR104
|
||||||
* timing mode) or for eMMC4.5 card read operation (in HS200
|
* timing mode) or for eMMC4.5 card read operation (in
|
||||||
* timing mode).
|
* HS400/HS200 timing mode).
|
||||||
* Select the 3/4 of the range and configure the DLL with the
|
* Select the 3/4 of the range and configure the DLL with the
|
||||||
* selected DLL clock output phase.
|
* selected DLL clock output phase.
|
||||||
*/
|
*/
|
||||||
@ -424,9 +434,10 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||||||
* Tuning is required for SDR104, HS200 and HS400 cards and
|
* Tuning is required for SDR104, HS200 and HS400 cards and
|
||||||
* if clock frequency is greater than 100MHz in these modes.
|
* if clock frequency is greater than 100MHz in these modes.
|
||||||
*/
|
*/
|
||||||
if (host->clock <= 100 * 1000 * 1000 ||
|
if (host->clock <= CORE_FREQ_100MHZ ||
|
||||||
!((ios.timing == MMC_TIMING_MMC_HS200) ||
|
!(ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||||
(ios.timing == MMC_TIMING_UHS_SDR104)))
|
ios.timing == MMC_TIMING_MMC_HS200 ||
|
||||||
|
ios.timing == MMC_TIMING_UHS_SDR104))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
@ -477,6 +488,8 @@ retry:
|
|||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
msm_host->tuning_done = true;
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,7 +497,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|||||||
unsigned int uhs)
|
unsigned int uhs)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
u16 ctrl_2;
|
u16 ctrl_2;
|
||||||
|
u32 config;
|
||||||
|
|
||||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
/* Select Bus Speed Mode for host */
|
/* Select Bus Speed Mode for host */
|
||||||
@ -499,6 +515,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|||||||
case MMC_TIMING_UHS_SDR50:
|
case MMC_TIMING_UHS_SDR50:
|
||||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
||||||
break;
|
break;
|
||||||
|
case MMC_TIMING_MMC_HS400:
|
||||||
case MMC_TIMING_MMC_HS200:
|
case MMC_TIMING_MMC_HS200:
|
||||||
case MMC_TIMING_UHS_SDR104:
|
case MMC_TIMING_UHS_SDR104:
|
||||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||||
@ -515,11 +532,29 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
|
|||||||
* provide feedback clock, the mode selection can be any value less
|
* provide feedback clock, the mode selection can be any value less
|
||||||
* than 3'b011 in bits [2:0] of HOST CONTROL2 register.
|
* than 3'b011 in bits [2:0] of HOST CONTROL2 register.
|
||||||
*/
|
*/
|
||||||
if (host->clock <= 100000000 &&
|
if (host->clock <= CORE_FREQ_100MHZ) {
|
||||||
(uhs == MMC_TIMING_MMC_HS400 ||
|
if (uhs == MMC_TIMING_MMC_HS400 ||
|
||||||
uhs == MMC_TIMING_MMC_HS200 ||
|
uhs == MMC_TIMING_MMC_HS200 ||
|
||||||
uhs == MMC_TIMING_UHS_SDR104))
|
uhs == MMC_TIMING_UHS_SDR104)
|
||||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||||
|
/*
|
||||||
|
* DLL is not required for clock <= 100MHz
|
||||||
|
* Thus, make sure DLL it is disabled when not required
|
||||||
|
*/
|
||||||
|
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||||
|
config |= CORE_DLL_RST;
|
||||||
|
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||||
|
|
||||||
|
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
|
||||||
|
config |= CORE_DLL_PDN;
|
||||||
|
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The DLL needs to be restored and CDCLP533 recalibrated
|
||||||
|
* when the clock frequency is set back to 400MHz.
|
||||||
|
*/
|
||||||
|
msm_host->calibration_done = false;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
|
dev_dbg(mmc_dev(mmc), "%s: clock=%u uhs=%u ctrl_2=0x%x\n",
|
||||||
mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
|
mmc_hostname(host->mmc), host->clock, uhs, ctrl_2);
|
||||||
@ -611,6 +646,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_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
struct mmc_ios curr_ios = host->mmc->ios;
|
struct mmc_ios curr_ios = host->mmc->ios;
|
||||||
|
u32 config;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (!clock) {
|
if (!clock) {
|
||||||
@ -629,6 +665,68 @@ 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_DDR52 ||
|
||||||
curr_ios.timing == MMC_TIMING_MMC_HS400)
|
curr_ios.timing == MMC_TIMING_MMC_HS400)
|
||||||
clock *= 2;
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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();
|
||||||
|
|
||||||
rc = clk_set_rate(msm_host->clk, clock);
|
rc = clk_set_rate(msm_host->clk, clock);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
@ -776,6 +874,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||||||
/* Set HC_MODE_EN bit in HC_MODE register */
|
/* Set HC_MODE_EN bit in HC_MODE register */
|
||||||
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
|
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
|
||||||
|
|
||||||
|
config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE);
|
||||||
|
config |= FF_CLK_SW_RST_DIS;
|
||||||
|
writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE);
|
||||||
|
|
||||||
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
|
||||||
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
|
dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
|
||||||
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
|
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
|
||||||
|
Loading…
Reference in New Issue
Block a user