[U-Boot] [PATCH 16/33] rockchip: clk: rk3288: add support for the clock phase
Ziyuan Xu
xzy.xu at rock-chips.com
Mon May 15 06:07:10 UTC 2017
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 at 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
More information about the U-Boot
mailing list