This patch adds phase adjustment for mmc clock(ciu_sample), which is
used to select the optimal sampling point of a data input.

The phase shift is achieved through 255 delay elements(40-80
picoseconds),  and calculate the number of delay element via clock
frequency.

Signed-off-by: Ziyuan Xu <xzy...@rock-chips.com>
---

 drivers/clk/rockchip/clk_rk3288.c | 124 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)

diff --git a/drivers/clk/rockchip/clk_rk3288.c 
b/drivers/clk/rockchip/clk_rk3288.c
index b924a3b..3279e01 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -514,6 +514,7 @@ static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, 
uint gclk_rate,
        switch (periph) {
        case HCLK_EMMC:
        case SCLK_EMMC:
+       case SCLK_EMMC_SAMPLE:
                con = readl(&cru->cru_clksel_con[12]);
                mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
                div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
@@ -669,7 +670,9 @@ static ulong rk3288_clk_get_rate(struct clk *clk)
        case HCLK_SDMMC:
        case HCLK_SDIO0:
        case SCLK_EMMC:
+       case SCLK_EMMC_SAMPLE:
        case SCLK_SDMMC:
+       case SCLK_SDMMC_SAMPLE:
        case SCLK_SDIO0:
                new_rate = rockchip_mmc_get_clk(priv->cru, gclk_rate, clk->id);
                break;
@@ -784,9 +787,130 @@ static ulong rk3288_clk_set_rate(struct clk *clk, ulong 
rate)
        return new_rate;
 }
 
+#define ROCKCHIP_MMC_DELAY_SEL         BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK       0x3
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET   2
+#define ROCKCHIP_MMC_DELAYNUM_MASK     (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+
+#define PSECS_PER_SEC 1000000000000LL
+/*
+ * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 
57.8deg.
+ */
+#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
+
+int rockchip_mmc_get_phase(struct clk *clk)
+{
+       struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
+       struct rk3288_cru *cru = priv->cru;
+       u32 raw_value, delay_num;
+       u16 degrees = 0;
+       ulong rate;
+
+       rate = rk3288_clk_get_rate(clk);
+
+       if (rate < 0)
+               return rate;
+
+       if (clk->id == SCLK_EMMC_SAMPLE)
+               raw_value = readl(&cru->cru_emmc_con[1]);
+       else
+               raw_value = readl(&cru->cru_sdmmc_con[1]);
+
+       degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
+
+       if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
+               /* degrees/delaynum * 10000 */
+               unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
+                                       36 * (rate / 1000000);
+
+               delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
+               delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
+               degrees += DIV_ROUND_CLOSEST(delay_num * factor, 10000);
+       }
+
+       return degrees % 360;
+}
+
+int rockchip_mmc_set_phase(struct clk *clk, u32 degrees)
+{
+       struct rk3288_clk_priv *priv = dev_get_priv(clk->dev);
+       struct rk3288_cru *cru = priv->cru;
+       u8 nineties, remainder, delay_num;
+       u32 raw_value, delay;
+       ulong rate;
+
+       rate = rk3288_clk_get_rate(clk);
+
+       if (rate < 0)
+               return rate;
+
+       nineties = degrees / 90;
+       remainder = (degrees % 90);
+
+       /*
+        * Convert to delay; do a little extra work to make sure we
+        * don't overflow 32-bit / 64-bit numbers.
+        */
+       delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */
+       delay *= remainder;
+       delay = DIV_ROUND_CLOSEST(delay, (rate / 1000) * 36 *
+                               (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10));
+
+       delay_num = (u8)min_t(u32, delay, 255);
+
+       raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
+       raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
+       raw_value |= nineties;
+
+       if (clk->id == SCLK_EMMC_SAMPLE)
+               writel(raw_value | 0xffff0000, &cru->cru_emmc_con[1]);
+       else
+               writel(raw_value | 0xffff0000, &cru->cru_sdmmc_con[1]);
+
+       debug("mmc set_phase(%d) delay_nums=%u reg=%#x actual_degrees=%d\n",
+             degrees, delay_num, raw_value, rockchip_mmc_get_phase(clk));
+
+       return 0;
+}
+
+static int rk3288_clk_get_phase(struct clk *clk)
+{
+       int ret;
+
+       switch (clk->id) {
+       case SCLK_EMMC_SAMPLE:
+       case SCLK_SDMMC_SAMPLE:
+               ret = rockchip_mmc_get_phase(clk);
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return ret;
+}
+
+static int rk3288_clk_set_phase(struct clk *clk, int degrees)
+{
+       int ret;
+
+       switch (clk->id) {
+       case SCLK_EMMC_SAMPLE:
+       case SCLK_SDMMC_SAMPLE:
+               ret = rockchip_mmc_set_phase(clk, degrees);
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       return ret;
+}
+
 static struct clk_ops rk3288_clk_ops = {
        .get_rate       = rk3288_clk_get_rate,
        .set_rate       = rk3288_clk_set_rate,
+       .get_phase      = rk3288_clk_get_phase,
+       .set_phase      = rk3288_clk_set_phase,
 };
 
 static int rk3288_clk_ofdata_to_platdata(struct udevice *dev)
-- 
2.7.4


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to