
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.xu@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)