#include "clk-pll.h"
-
-//static unsigned long lpj_gpll;
-
-
static const struct apll_clk_set apll_table[] = {
// (_mhz, nr, nf, no, _periph_div, _aclk_div)
_RK3188_APLL_SET_CLKS(2208, 1, 92, 1, 8, 81),
}
}
-
-/* recalc_rate */
-static unsigned long clk_pll_recalc_rate_3188(struct clk_hw *hw,
- unsigned long parent_rate)
-{
- struct clk_pll *pll = to_clk_pll(hw);
- unsigned long rate;
-
-
- if (_RK3188_PLL_MODE_IS_NORM(pll->mode_offset, pll->mode_shift)) {
- u32 pll_con0 = readl(pll->reg + RK3188_PLL_CON(0));
- u32 pll_con1 = readl(pll->reg + RK3188_PLL_CON(1));
-
- u64 rate64 = (u64)parent_rate * RK3188_PLL_NF(pll_con1);
-
- do_div(rate64, RK3188_PLL_NR(pll_con0));
- do_div(rate64, RK3188_PLL_NO(pll_con0));
-
- rate = rate64;
- } else {
- /*FIXME*/
- rate = parent_rate;
- clk_debug("pll %s is in slow mode\n", __clk_get_name(hw->clk));
- }
-
- clk_debug("pll %s recalc rate =%lu\n", __clk_get_name(hw->clk), rate);
-
- return rate;
-}
-
-/* round_rate */
/* get rate that is most close to target */
static const struct apll_clk_set *apll_get_best_set(unsigned long rate,
const struct apll_clk_set *table)
return ps;
}
-static long clk_pll_round_rate_3188_apll(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+/* CLK_PLL_3188 type ops */
+static unsigned long clk_pll_recalc_rate_3188(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct clk *parent = __clk_get_parent(hw->clk);
+ struct clk_pll *pll = to_clk_pll(hw);
+ unsigned long rate;
- if (parent && (rate==__clk_get_rate(parent))) {
- clk_debug("pll %s round rate=%lu equal to parent rate\n",
- __clk_get_name(hw->clk), rate);
- return rate;
+
+ if (_RK3188_PLL_MODE_IS_NORM(pll->mode_offset, pll->mode_shift)) {
+ u32 pll_con0 = readl(pll->reg + RK3188_PLL_CON(0));
+ u32 pll_con1 = readl(pll->reg + RK3188_PLL_CON(1));
+
+ u64 rate64 = (u64)parent_rate * RK3188_PLL_NF(pll_con1);
+
+ do_div(rate64, RK3188_PLL_NR(pll_con0));
+ do_div(rate64, RK3188_PLL_NO(pll_con0));
+
+ rate = rate64;
+ } else {
+ /*FIXME*/
+ rate = parent_rate;
+ clk_debug("pll %s is in slow mode\n", __clk_get_name(hw->clk));
}
- return (apll_get_best_set(rate, apll_table)->rate);
+ clk_debug("pll %s recalc rate =%lu\n", __clk_get_name(hw->clk), rate);
+
+ return rate;
}
static long clk_pll_round_rate_3188(struct clk_hw *hw, unsigned long rate,
return (pll_com_get_best_set(rate, pll_com_table)->rate);
}
-/* set_rate */
-static int _pll_clk_set_rate_rk3188(struct pll_clk_set *clk_set,
- struct clk_hw *hw, spinlock_t *lock)
+static int _pll_clk_set_rate_3188(struct pll_clk_set *clk_set,
+ struct clk_hw *hw)
{
struct clk_pll *pll = to_clk_pll(hw);
unsigned long flags = 0;
clk_debug("%s start!\n", __func__);
- if(lock)
- spin_lock_irqsave(lock, flags);
+ if(pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
//enter slowmode
cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
//return from slow
cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
- if (lock)
- spin_unlock_irqrestore(lock, flags);
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
clk_debug("pll %s dump reg: con0=0x%08x, con1=0x%08x, mode=0x%08x\n",
__clk_get_name(hw->clk),
}
if (clk_set->rate == rate) {
- ret = _pll_clk_set_rate_rk3188(clk_set, hw, pll->lock);
+ ret = _pll_clk_set_rate_3188(clk_set, hw);
clk_debug("pll %s set rate=%lu OK!\n", __clk_get_name(hw->clk),
rate);
} else {
return ret;
}
-/* 1: use
- * 0: no use
- */
-#define USE_ARM_GPLL 1
+static const struct clk_ops clk_pll_ops_3188 = {
+ .recalc_rate = clk_pll_recalc_rate_3188,
+ .round_rate = clk_pll_round_rate_3188,
+ .set_rate = clk_pll_set_rate_3188,
+};
+
+
+/* CLK_PLL_3188_APLL type ops */
+static unsigned long clk_pll_recalc_rate_3188_apll(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return clk_pll_recalc_rate_3188(hw, parent_rate);
+}
+
+static long clk_pll_round_rate_3188_apll(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk *parent = __clk_get_parent(hw->clk);
+
+ if (parent && (rate==__clk_get_rate(parent))) {
+ clk_debug("pll %s round rate=%lu equal to parent rate\n",
+ __clk_get_name(hw->clk), rate);
+ return rate;
+ }
+
+ return (apll_get_best_set(rate, apll_table)->rate);
+}
+
+/* 1: use, 0: no use */
+#define RK3188_USE_ARM_GPLL 1
static int clk_pll_set_rate_3188_apll(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
}
-#if !USE_ARM_GPLL
+#if !RK3188_USE_ARM_GPLL
goto CHANGE_APLL;
#endif
RK3188_CRU_CLKSELS_CON(0));
sel_gpll = 1;
- //loops_per_jiffy = lpj_gpll / temp_div;
+ //loops_per_jiffy = CLK_LOOPS_RECALC(arm_gpll_rate) / temp_div;
smp_wmb();
local_irq_restore(flags);
return 0;
}
-static const struct clk_ops clk_pll_ops_3188 = {
- .recalc_rate = clk_pll_recalc_rate_3188,
- .round_rate = clk_pll_round_rate_3188,
- .set_rate = clk_pll_set_rate_3188,
-};
-
static const struct clk_ops clk_pll_ops_3188_apll = {
- .recalc_rate = clk_pll_recalc_rate_3188,
+ .recalc_rate = clk_pll_recalc_rate_3188_apll,
.round_rate = clk_pll_round_rate_3188_apll,
.set_rate = clk_pll_set_rate_3188_apll,
};
-static const struct clk_ops clk_pll_ops_3188plus = {
+
+/* CLK_PLL_3188PLUS type ops */
+static unsigned long clk_pll_recalc_rate_3188plus(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ unsigned long rate;
+
+
+ if (_RK3188_PLL_MODE_IS_NORM(pll->mode_offset, pll->mode_shift)) {
+ u32 pll_con0 = readl(pll->reg + RK3188_PLL_CON(0));
+ u32 pll_con1 = readl(pll->reg + RK3188_PLL_CON(1));
+
+ u64 rate64 = (u64)parent_rate * RK3188PLUS_PLL_NF(pll_con1);
+
+ do_div(rate64, RK3188PLUS_PLL_NR(pll_con0));
+ do_div(rate64, RK3188PLUS_PLL_NO(pll_con0));
+
+ rate = rate64;
+ } else {
+ /*FIXME*/
+ rate = parent_rate;
+ clk_debug("pll %s is in slow mode\n", __clk_get_name(hw->clk));
+ }
+
+ clk_debug("pll %s recalc rate =%lu\n", __clk_get_name(hw->clk), rate);
+
+ return rate;
+}
+
+static long clk_pll_round_rate_3188plus(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return clk_pll_round_rate_3188(hw, rate, prate);
+}
+
+static int _pll_clk_set_rate_3188plus(struct pll_clk_set *clk_set,
+ struct clk_hw *hw)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ unsigned long flags = 0;
+
+
+ clk_debug("%s start!\n", __func__);
+
+ if(pll->lock)
+ spin_lock_irqsave(pll->lock, flags);
+
+ //enter slowmode
+ cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
+
+ //enter rest
+ writel(_RK3188PLUS_PLL_RESET_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+ writel(clk_set->pllcon0, pll->reg + RK3188_PLL_CON(0));
+ writel(clk_set->pllcon1, pll->reg + RK3188_PLL_CON(1));
+ writel(clk_set->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
+ udelay(5);
+
+ //return from rest
+ writel(_RK3188PLUS_PLL_RESET_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+ //wating lock state
+ udelay(clk_set->rst_dly);
+
+ pll_wait_lock(hw);
+
+ //return from slow
+ cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
+
+ if (pll->lock)
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ clk_debug("pll %s dump reg: con0=0x%08x, con1=0x%08x, mode=0x%08x\n",
+ __clk_get_name(hw->clk),
+ readl(pll->reg + RK3188_PLL_CON(0)),
+ readl(pll->reg + RK3188_PLL_CON(1)),
+ cru_readl(pll->mode_offset));
+
+ clk_debug("%s end!\n", __func__);
+
+ return 0;
+}
+
+static int clk_pll_set_rate_3188plus(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ //struct clk_pll *pll = to_clk_pll(hw);
+ struct pll_clk_set *clk_set = (struct pll_clk_set *)(pll_com_table);
+ int ret = 0;
+
#if 0
- .recalc_rate = clk_pll_recalc_rate,
- .round_rate = clk_pll_round_rate,
- .set_rate = clk_pll_set_rate,
+ if (rate == parent_rate) {
+ clk_debug("pll %s set rate=%lu equal to parent rate\n",
+ __clk_get_name(hw->clk), rate);
+ cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift),
+ pll->mode_offset);
+ /* pll power down */
+ writel((0x1 << (16+1)) | (0x1<<1), pll->reg + RK3188_PLL_CON(3));
+ clk_debug("pll %s enter slow mode, set rate OK!\n",
+ __clk_get_name(hw->clk));
+ return 0;
+ }
#endif
+
+ while(clk_set->rate) {
+ if (clk_set->rate == rate) {
+ break;
+ }
+ clk_set++;
+ }
+
+ if (clk_set->rate == rate) {
+ ret = _pll_clk_set_rate_3188plus(clk_set, hw);
+ clk_debug("pll %s set rate=%lu OK!\n", __clk_get_name(hw->clk),
+ rate);
+ } else {
+ clk_err("pll %s is no corresponding rate=%lu\n",
+ __clk_get_name(hw->clk), rate);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct clk_ops clk_pll_ops_3188plus = {
+ .recalc_rate = clk_pll_recalc_rate_3188plus,
+ .round_rate = clk_pll_round_rate_3188plus,
+ .set_rate = clk_pll_set_rate_3188plus,
};
-static const struct clk_ops clk_pll_ops_3188plus_apll = {
+
+/* CLK_PLL_3188PLUS_APLL type ops */
+static unsigned long clk_pll_recalc_rate_3188plus_apll(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ return clk_pll_recalc_rate_3188plus(hw, parent_rate);
+}
+
+static long clk_pll_round_rate_3188plus_apll(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ return clk_pll_round_rate_3188_apll(hw, rate, prate);
+}
+
+/* 1: use, 0: no use */
+#define RK3188PLUS_USE_ARM_GPLL 1
+
+static int clk_pll_set_rate_3188plus_apll(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ struct clk *clk = hw->clk;
+ struct clk *arm_gpll = __clk_lookup("clk_arm_gpll");
+ unsigned long arm_gpll_rate;
+ const struct apll_clk_set *ps;
+ u32 old_aclk_div = 0, new_aclk_div = 0;
+ u32 temp_div;
+ unsigned long flags;
+ int sel_gpll = 0;
+
+#if 0
+ if (rate == parent_rate) {
+ clk_debug("pll %s set rate=%lu equal to parent rate\n",
+ __clk_get_name(hw->clk), rate);
+ cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift),
+ pll->mode_offset);
+ /* pll power down */
+ writel((0x1 << (16+1)) | (0x1<<1), pll->reg + RK3188_PLL_CON(3));
+ clk_debug("pll %s enter slow mode, set rate OK!\n",
+ __clk_get_name(hw->clk));
+ return 0;
+ }
+#endif
+
+
+#if !RK3188PLUS_USE_ARM_GPLL
+ goto CHANGE_APLL;
+#endif
+
+ /* prepare arm_gpll before reparent clk_core to it */
+ if (!arm_gpll) {
+ clk_err("clk arm_gpll is NULL!\n");
+ goto CHANGE_APLL;
+ }
+
+ /* In rk3188plus, arm_gpll and cpu_gpll share a same gate,
+ * and aclk_cpu selects cpu_gpll as parent, thus this
+ * gate must keep enabled.
+ */
#if 0
- .recalc_rate = clk_pll_recalc_rate,
- .round_rate = clk_pll_round_rate,
- .set_rate = clk_pll_set_rate,
+ if (clk_prepare(arm_gpll)) {
+ clk_err("fail to prepare arm_gpll path\n");
+ clk_unprepare(arm_gpll);
+ goto CHANGE_APLL;
+ }
+
+ if (clk_enable(arm_gpll)) {
+ clk_err("fail to enable arm_gpll path\n");
+ clk_disable(arm_gpll);
+ clk_unprepare(arm_gpll);
+ goto CHANGE_APLL;
+ }
#endif
+
+ arm_gpll_rate = __clk_get_rate(arm_gpll);
+ temp_div = DIV_ROUND_UP(arm_gpll_rate, __clk_get_rate(clk));
+ temp_div = (temp_div == 0) ? 1 : temp_div;
+ if (temp_div > RK3188_CORE_CLK_MAX_DIV) {
+ clk_debug("temp_div %d > max_div %d\n", temp_div,
+ RK3188_CORE_CLK_MAX_DIV);
+ clk_debug("can't get rate %lu from arm_gpll rate %lu\n",
+ __clk_get_rate(clk), arm_gpll_rate);
+ //clk_disable(arm_gpll);
+ //clk_unprepare(arm_gpll);
+ goto CHANGE_APLL;
+ }
+
+ local_irq_save(flags);
+
+ /* firstly set div, then select arm_gpll path */
+ cru_writel(RK3188_CORE_CLK_DIV_W_MSK|RK3188_CORE_CLK_DIV(temp_div),
+ RK3188_CRU_CLKSELS_CON(0));
+ cru_writel(RK3188_CORE_SEL_PLL_W_MSK|RK3188_CORE_SEL_GPLL,
+ RK3188_CRU_CLKSELS_CON(0));
+
+ sel_gpll = 1;
+ //loops_per_jiffy = CLK_LOOPS_RECALC(arm_gpll_rate) / temp_div;
+ smp_wmb();
+
+ local_irq_restore(flags);
+
+ clk_debug("temp select arm_gpll path, get rate %lu\n",
+ arm_gpll_rate/temp_div);
+ clk_debug("from arm_gpll rate %lu, temp_div %d\n", arm_gpll_rate,
+ temp_div);
+
+CHANGE_APLL:
+ ps = apll_get_best_set(rate, apll_table);
+ clk_debug("apll will set rate %lu\n", ps->rate);
+ clk_debug("table con:%08x,%08x,%08x, sel:%08x,%08x\n",
+ ps->pllcon0, ps->pllcon1, ps->pllcon2,
+ ps->clksel0, ps->clksel1);
+
+ local_irq_save(flags);
+
+ /* If core src don't select gpll, apll need to enter slow mode
+ * before reset
+ */
+ //FIXME
+ //if (!sel_gpll)
+ cru_writel(_RK3188_PLL_MODE_SLOW_SET(pll->mode_shift), pll->mode_offset);
+
+ /* PLL enter rest */
+ writel(_RK3188PLUS_PLL_RESET_SET(1), pll->reg + RK3188_PLL_CON(3));
+
+ writel(ps->pllcon0, pll->reg + RK3188_PLL_CON(0));
+ writel(ps->pllcon1, pll->reg + RK3188_PLL_CON(1));
+ writel(ps->pllcon2, pll->reg + RK3188_PLL_CON(2));
+
+ udelay(5);
+
+ /* return from rest */
+ writel(_RK3188PLUS_PLL_RESET_SET(0), pll->reg + RK3188_PLL_CON(3));
+
+ //wating lock state
+ udelay(ps->rst_dly);
+ pll_wait_lock(hw);
+
+ old_aclk_div = RK3188_GET_CORE_ACLK_VAL(cru_readl(RK3188_CRU_CLKSELS_CON(1)) &
+ RK3188_CORE_ACLK_MSK);
+ new_aclk_div = RK3188_GET_CORE_ACLK_VAL(ps->clksel1 & RK3188_CORE_ACLK_MSK);
+
+ if (new_aclk_div >= old_aclk_div) {
+ cru_writel(ps->clksel0, RK3188_CRU_CLKSELS_CON(0));
+ cru_writel(ps->clksel1, RK3188_CRU_CLKSELS_CON(1));
+ }
+
+ /* PLL return from slow mode */
+ //FIXME
+ //if (!sel_gpll)
+ cru_writel(_RK3188_PLL_MODE_NORM_SET(pll->mode_shift), pll->mode_offset);
+
+ /* reparent to apll, and set div to 1 */
+ if (sel_gpll) {
+ cru_writel(RK3188_CORE_SEL_PLL_W_MSK|RK3188_CORE_SEL_APLL,
+ RK3188_CRU_CLKSELS_CON(0));
+ cru_writel(RK3188_CORE_CLK_DIV_W_MSK|RK3188_CORE_CLK_DIV(1),
+ RK3188_CRU_CLKSELS_CON(0));
+ }
+
+ if (old_aclk_div > new_aclk_div) {
+ cru_writel(ps->clksel0, RK3188_CRU_CLKSELS_CON(0));
+ cru_writel(ps->clksel1, RK3188_CRU_CLKSELS_CON(1));
+ }
+
+ //loops_per_jiffy = ps->lpj;
+ smp_wmb();
+
+ local_irq_restore(flags);
+
+ if (sel_gpll) {
+ sel_gpll = 0;
+ //clk_disable(arm_gpll);
+ //clk_unprepare(arm_gpll);
+ }
+
+ //clk_debug("apll set loops_per_jiffy =%lu\n", loops_per_jiffy);
+
+ clk_debug("apll set rate %lu, con(%x,%x,%x,%x), sel(%x,%x)\n",
+ ps->rate,
+ readl(pll->reg + RK3188_PLL_CON(0)),
+ readl(pll->reg + RK3188_PLL_CON(1)),
+ readl(pll->reg + RK3188_PLL_CON(2)),
+ readl(pll->reg + RK3188_PLL_CON(3)),
+ cru_readl(RK3188_CRU_CLKSELS_CON(0)),
+ cru_readl(RK3188_CRU_CLKSELS_CON(1)));
+
+ return 0;
+}
+
+static const struct clk_ops clk_pll_ops_3188plus_apll = {
+ .recalc_rate = clk_pll_recalc_rate_3188plus_apll,
+ .round_rate = clk_pll_round_rate_3188plus_apll,
+ .set_rate = clk_pll_set_rate_3188plus_apll,
};
const struct clk_ops *rk_get_pll_ops(u32 pll_flags)
}
}
-
struct clk *rk_clk_register_pll(struct device *dev, const char *name,
const char *parent_name, unsigned long flags, void __iomem *reg,
u32 width, u32 mode_offset, u8 mode_shift,