summaryrefslogtreecommitdiff
path: root/drivers/clk/rockchip/clk_rk3288.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/rockchip/clk_rk3288.c')
-rw-r--r--drivers/clk/rockchip/clk_rk3288.c124
1 files changed, 124 insertions, 0 deletions
diff --git a/drivers/clk/rockchip/clk_rk3288.c b/drivers/clk/rockchip/clk_rk3288.c
index a5596d7e05..adcc0a6d0c 100644
--- a/drivers/clk/rockchip/clk_rk3288.c
+++ b/drivers/clk/rockchip/clk_rk3288.c
@@ -499,6 +499,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_MASK) >> EMMC_PLL_SHIFT;
div = (con & EMMC_DIV_MASK) >> EMMC_DIV_SHIFT;
@@ -648,7 +649,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;
@@ -763,9 +766,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)