From 14761caeee3ba453d051c0db3246fbccc5a5b136 Mon Sep 17 00:00:00 2001 From: Jean-Jacques Hiblot Date: Tue, 30 Jan 2018 16:01:35 +0100 Subject: [PATCH] mmc: omap_hsmmc: Add tuning support HS200/SDR104 requires tuning command to be sent to the card. Use the mmc_send_tuning library function to send the tuning command and configure the internal DLL. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Jean-Jacques Hiblot --- arch/arm/include/asm/omap_mmc.h | 21 +++++- drivers/mmc/omap_hsmmc.c | 122 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h index 6aca9e90cf..cf9f1c54ff 100644 --- a/arch/arm/include/asm/omap_mmc.h +++ b/arch/arm/include/asm/omap_mmc.h @@ -39,7 +39,9 @@ struct hsmmc { unsigned int sysstatus; /* 0x14 */ unsigned char res2[0x14]; unsigned int con; /* 0x2C */ - unsigned char res3[0xD4]; + unsigned int pwcnt; /* 0x30 */ + unsigned int dll; /* 0x34 */ + unsigned char res3[0xcc]; unsigned int blk; /* 0x104 */ unsigned int arg; /* 0x108 */ unsigned int cmd; /* 0x10C */ @@ -56,7 +58,8 @@ struct hsmmc { unsigned char res4[0x4]; unsigned int ac12; /* 0x13C */ unsigned int capa; /* 0x140 */ - unsigned char res5[0x10]; + unsigned int capa2; /* 0x144 */ + unsigned char res5[0xc]; unsigned int admaes; /* 0x154 */ unsigned int admasal; /* 0x158 */ }; @@ -173,6 +176,8 @@ struct omap_hsmmc_plat { #define IOV_1V8 1800000 #define AC12_ET BIT(22) +#define AC12_V1V8_SIGEN BIT(19) +#define AC12_SCLK_SEL BIT(23) #define AC12_UHSMC_MASK (7 << 16) #define AC12_UHSMC_DDR50 (4 << 16) #define AC12_UHSMC_SDR104 (3 << 16) @@ -199,6 +204,18 @@ struct omap_hsmmc_plat { /* Clock Configurations and Macros */ #define MMC_CLOCK_REFERENCE 96 /* MHz */ +/* DLL */ +#define DLL_SWT BIT(20) +#define DLL_FORCE_SR_C_SHIFT 13 +#define DLL_FORCE_SR_C_MASK 0x7f +#define DLL_FORCE_VALUE BIT(12) +#define DLL_CALIB BIT(1) + +#define MAX_PHASE_DELAY 0x7c + +/* CAPA2 */ +#define CAPA2_TSDR50 BIT(13) + #define mmc_reg_out(addr, mask, val)\ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr)) diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 2f4909e34b..69a7c2eed4 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -124,6 +124,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int siz); static void omap_hsmmc_start_clock(struct hsmmc *mmc_base); static void omap_hsmmc_stop_clock(struct hsmmc *mmc_base); +static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit); static inline struct omap_hsmmc_data *omap_hsmmc_get_data(struct mmc *mmc) { @@ -355,6 +356,124 @@ static void omap_hsmmc_set_capabilities(struct mmc *mmc) writel(val, &mmc_base->capa); } + +#ifdef MMC_SUPPORTS_TUNING +static void omap_hsmmc_disable_tuning(struct mmc *mmc) +{ + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->ac12); + val &= ~(AC12_SCLK_SEL); + writel(val, &mmc_base->ac12); + + val = readl(&mmc_base->dll); + val &= ~(DLL_FORCE_VALUE | DLL_SWT); + writel(val, &mmc_base->dll); +} + +static void omap_hsmmc_set_dll(struct mmc *mmc, int count) +{ + int i; + struct hsmmc *mmc_base; + struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc); + u32 val; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->dll); + val |= DLL_FORCE_VALUE; + val &= ~(DLL_FORCE_SR_C_MASK << DLL_FORCE_SR_C_SHIFT); + val |= (count << DLL_FORCE_SR_C_SHIFT); + writel(val, &mmc_base->dll); + + val |= DLL_CALIB; + writel(val, &mmc_base->dll); + for (i = 0; i < 1000; i++) { + if (readl(&mmc_base->dll) & DLL_CALIB) + break; + } + val &= ~DLL_CALIB; + writel(val, &mmc_base->dll); +} + +static int omap_hsmmc_execute_tuning(struct udevice *dev, uint opcode) +{ + struct omap_hsmmc_data *priv = dev_get_priv(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mmc *mmc = upriv->mmc; + struct hsmmc *mmc_base; + u32 val; + u8 cur_match, prev_match = 0; + int ret; + u32 phase_delay = 0; + u32 start_window = 0, max_window = 0; + u32 length = 0, max_len = 0; + + mmc_base = priv->base_addr; + val = readl(&mmc_base->capa2); + + /* clock tuning is not needed for upto 52MHz */ + if (!((mmc->selected_mode == MMC_HS_200) || + (mmc->selected_mode == UHS_SDR104) || + ((mmc->selected_mode == UHS_SDR50) && (val & CAPA2_TSDR50)))) + return 0; + + val = readl(&mmc_base->dll); + val |= DLL_SWT; + writel(val, &mmc_base->dll); + while (phase_delay <= MAX_PHASE_DELAY) { + omap_hsmmc_set_dll(mmc, phase_delay); + + cur_match = !mmc_send_tuning(mmc, opcode, NULL); + + if (cur_match) { + if (prev_match) { + length++; + } else { + start_window = phase_delay; + length = 1; + } + } + + if (length > max_len) { + max_window = start_window; + max_len = length; + } + + prev_match = cur_match; + phase_delay += 4; + } + + if (!max_len) { + ret = -EIO; + goto tuning_error; + } + + val = readl(&mmc_base->ac12); + if (!(val & AC12_SCLK_SEL)) { + ret = -EIO; + goto tuning_error; + } + + phase_delay = max_window + 4 * ((3 * max_len) >> 2); + omap_hsmmc_set_dll(mmc, phase_delay); + + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return 0; + +tuning_error: + + omap_hsmmc_disable_tuning(mmc); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRD); + mmc_reset_controller_fsm(mmc_base, SYSCTL_SRC); + + return ret; +} +#endif #endif static int omap_hsmmc_init_setup(struct mmc *mmc) @@ -1050,6 +1169,9 @@ static const struct dm_mmc_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_getcd, .get_wp = omap_hsmmc_getwp, #endif +#ifdef MMC_SUPPORTS_TUNING + .execute_tuning = omap_hsmmc_execute_tuning, +#endif }; #else static const struct mmc_ops omap_hsmmc_ops = { -- 2.39.5