mmc: omap_hsmmc: Add tuning support
authorJean-Jacques Hiblot <jjhiblot@ti.com>
Tue, 30 Jan 2018 15:01:35 +0000 (16:01 +0100)
committerJaehoon Chung <jh80.chung@samsung.com>
Mon, 19 Feb 2018 07:58:54 +0000 (16:58 +0900)
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>
arch/arm/include/asm/omap_mmc.h
drivers/mmc/omap_hsmmc.c

index 6aca9e90cfde823c9e789d93a2c6fcc44c231c82..cf9f1c54ffb8a7663d943b854d6fc90ae589db5a 100644 (file)
@@ -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))
 
index 2f4909e34bd69a0dc764c1d041a6bfababa033f8..69a7c2eed4b33ba019537045e0937ca29ba06854 100644 (file)
@@ -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 = {