summaryrefslogtreecommitdiff
path: root/drivers/clk/rockchip/clk_rk3399.c
diff options
context:
space:
mode:
authorKever Yang <kever.yang@rock-chips.com>2016-08-12 17:47:15 +0800
committerSimon Glass <sjg@chromium.org>2016-09-22 07:57:02 -0600
commit5e79f443550a0aac1a4a118d956a48177972aa51 (patch)
tree02e0901b5762664d71737bc81f26756f101c56f7 /drivers/clk/rockchip/clk_rk3399.c
parent4a79ececebdaee108a6f18ddb5684e2590fa2de6 (diff)
clk: rk3399: add pmucru controller support
pmucru is a module like cru which is a clock controller manage some PLL and module clocks. Signed-off-by: Kever Yang <kever.yang@rock-chips.com> Acked-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/clk/rockchip/clk_rk3399.c')
-rw-r--r--drivers/clk/rockchip/clk_rk3399.c177
1 files changed, 173 insertions, 4 deletions
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 0b4ea828f6..ea0ce2aab1 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -23,6 +23,10 @@ struct rk3399_clk_priv {
ulong rate;
};
+struct rk3399_pmuclk_priv {
+ struct rk3399_pmucru *pmucru;
+};
+
struct pll_div {
u32 refdiv;
u32 fbdiv;
@@ -95,11 +99,11 @@ enum {
/* PMUCRU_CLKSEL_CON2 */
I2C_DIV_CON_MASK = 0x7f,
- I2C8_DIV_CON_SHIFT = 8,
- I2C0_DIV_CON_SHIFT = 0,
+ CLK_I2C8_DIV_CON_SHIFT = 8,
+ CLK_I2C0_DIV_CON_SHIFT = 0,
/* PMUCRU_CLKSEL_CON3 */
- I2C4_DIV_CON_SHIFT = 0,
+ CLK_I2C4_DIV_CON_SHIFT = 0,
/* CLKSEL_CON0 */
ACLKM_CORE_L_DIV_CON_SHIFT = 8,
@@ -507,6 +511,14 @@ void rk3399_configure_cpu(struct rk3399_cru *cru,
(con >> CLK_I2C ##bus## _DIV_CON_SHIFT) & \
I2C_DIV_CON_MASK;
+#define I2C_PMUCLK_REG_MASK(bus) \
+ (I2C_DIV_CON_MASK << \
+ CLK_I2C ##bus## _DIV_CON_SHIFT)
+
+#define I2C_PMUCLK_REG_VALUE(bus, clk_div) \
+ ((clk_div - 1) << \
+ CLK_I2C ##bus## _DIV_CON_SHIFT)
+
static ulong rk3399_i2c_get_clk(struct rk3399_cru *cru, ulong clk_id)
{
u32 div, con;
@@ -754,7 +766,7 @@ static ulong rk3399_clk_set_rate(struct clk *clk, ulong rate)
break;
case DCLK_VOP0:
case DCLK_VOP1:
- rate = rk3399_vop_set_clk(priv->cru, clk->id, rate);
+ ret = rk3399_vop_set_clk(priv->cru, clk->id, rate);
break;
default:
return -ENOENT;
@@ -830,3 +842,160 @@ U_BOOT_DRIVER(clk_rk3399) = {
.bind = rk3399_clk_bind,
.probe = rk3399_clk_probe,
};
+
+static ulong rk3399_i2c_get_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id)
+{
+ u32 div, con;
+
+ switch (clk_id) {
+ case SCLK_I2C0_PMU:
+ con = readl(&pmucru->pmucru_clksel[2]);
+ div = I2C_CLK_DIV_VALUE(con, 0);
+ break;
+ case SCLK_I2C4_PMU:
+ con = readl(&pmucru->pmucru_clksel[3]);
+ div = I2C_CLK_DIV_VALUE(con, 4);
+ break;
+ case SCLK_I2C8_PMU:
+ con = readl(&pmucru->pmucru_clksel[2]);
+ div = I2C_CLK_DIV_VALUE(con, 8);
+ break;
+ default:
+ printf("do not support this i2c bus\n");
+ return -EINVAL;
+ }
+
+ return DIV_TO_RATE(PPLL_HZ, div);
+}
+
+static ulong rk3399_i2c_set_pmuclk(struct rk3399_pmucru *pmucru, ulong clk_id,
+ uint hz)
+{
+ int src_clk_div;
+
+ src_clk_div = PPLL_HZ / hz;
+ assert(src_clk_div - 1 < 127);
+
+ switch (clk_id) {
+ case SCLK_I2C0_PMU:
+ rk_clrsetreg(&pmucru->pmucru_clksel[2], I2C_PMUCLK_REG_MASK(0),
+ I2C_PMUCLK_REG_VALUE(0, src_clk_div));
+ break;
+ case SCLK_I2C4_PMU:
+ rk_clrsetreg(&pmucru->pmucru_clksel[3], I2C_PMUCLK_REG_MASK(4),
+ I2C_PMUCLK_REG_VALUE(4, src_clk_div));
+ break;
+ case SCLK_I2C8_PMU:
+ rk_clrsetreg(&pmucru->pmucru_clksel[2], I2C_PMUCLK_REG_MASK(8),
+ I2C_PMUCLK_REG_VALUE(8, src_clk_div));
+ break;
+ default:
+ printf("do not support this i2c bus\n");
+ return -EINVAL;
+ }
+
+ return DIV_TO_RATE(PPLL_HZ, src_clk_div);
+}
+
+static ulong rk3399_pwm_get_clk(struct rk3399_pmucru *pmucru)
+{
+ u32 div, con;
+
+ /* PWM closk rate is same as pclk_pmu */
+ con = readl(&pmucru->pmucru_clksel[0]);
+ div = con & PMU_PCLK_DIV_CON_MASK;
+
+ return DIV_TO_RATE(PPLL_HZ, div);
+}
+
+static ulong rk3399_pmuclk_get_rate(struct clk *clk)
+{
+ struct rk3399_pmuclk_priv *priv = dev_get_priv(clk->dev);
+ ulong rate = 0;
+
+ switch (clk->id) {
+ case PCLK_RKPWM_PMU:
+ rate = rk3399_pwm_get_clk(priv->pmucru);
+ break;
+ case SCLK_I2C0_PMU:
+ case SCLK_I2C4_PMU:
+ case SCLK_I2C8_PMU:
+ rate = rk3399_i2c_get_pmuclk(priv->pmucru, clk->id);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return rate;
+}
+
+static ulong rk3399_pmuclk_set_rate(struct clk *clk, ulong rate)
+{
+ struct rk3399_pmuclk_priv *priv = dev_get_priv(clk->dev);
+ ulong ret = 0;
+
+ switch (clk->id) {
+ case SCLK_I2C0_PMU:
+ case SCLK_I2C4_PMU:
+ case SCLK_I2C8_PMU:
+ ret = rk3399_i2c_set_pmuclk(priv->pmucru, clk->id, rate);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return ret;
+}
+
+static struct clk_ops rk3399_pmuclk_ops = {
+ .get_rate = rk3399_pmuclk_get_rate,
+ .set_rate = rk3399_pmuclk_set_rate,
+};
+
+static void pmuclk_init(struct rk3399_pmucru *pmucru)
+{
+ u32 pclk_div;
+
+ /* configure pmu pll(ppll) */
+ rkclk_set_pll(&pmucru->ppll_con[0], &ppll_init_cfg);
+
+ /* configure pmu pclk */
+ pclk_div = PPLL_HZ / PMU_PCLK_HZ - 1;
+ assert((pclk_div + 1) * PMU_PCLK_HZ == PPLL_HZ && pclk_div < 0x1f);
+ rk_clrsetreg(&pmucru->pmucru_clksel[0],
+ PMU_PCLK_DIV_CON_MASK,
+ pclk_div << PMU_PCLK_DIV_CON_SHIFT);
+}
+
+static int rk3399_pmuclk_probe(struct udevice *dev)
+{
+ struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
+
+ pmuclk_init(priv->pmucru);
+
+ return 0;
+}
+
+static int rk3399_pmuclk_ofdata_to_platdata(struct udevice *dev)
+{
+ struct rk3399_pmuclk_priv *priv = dev_get_priv(dev);
+
+ priv->pmucru = (struct rk3399_pmucru *)dev_get_addr(dev);
+
+ return 0;
+}
+
+static const struct udevice_id rk3399_pmuclk_ids[] = {
+ { .compatible = "rockchip,rk3399-pmucru" },
+ { }
+};
+
+U_BOOT_DRIVER(pmuclk_rk3399) = {
+ .name = "pmuclk_rk3399",
+ .id = UCLASS_CLK,
+ .of_match = rk3399_pmuclk_ids,
+ .priv_auto_alloc_size = sizeof(struct rk3399_pmuclk_priv),
+ .ofdata_to_platdata = rk3399_pmuclk_ofdata_to_platdata,
+ .ops = &rk3399_pmuclk_ops,
+ .probe = rk3399_pmuclk_probe,
+};