aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Jacques Hiblot <jjhiblot@ti.com>2018-01-30 16:01:35 +0100
committerJaehoon Chung <jh80.chung@samsung.com>2018-02-19 16:58:54 +0900
commit14761caeee3ba453d051c0db3246fbccc5a5b136 (patch)
tree6c5cfeb733ad1fbab6b9dc5aa2084c5f951f5a16
parent9b3fc21837dc32eda9656f264f74719ea77311a2 (diff)
downloadu-boot-14761caeee3ba453d051c0db3246fbccc5a5b136.zip
u-boot-14761caeee3ba453d051c0db3246fbccc5a5b136.tar.gz
u-boot-14761caeee3ba453d051c0db3246fbccc5a5b136.tar.bz2
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 <kishon@ti.com> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
-rw-r--r--arch/arm/include/asm/omap_mmc.h21
-rw-r--r--drivers/mmc/omap_hsmmc.c122
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 6aca9e9..cf9f1c5 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 2f4909e..69a7c2e 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 = {