aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/samsung/clk-pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/samsung/clk-pll.c')
-rw-r--r--drivers/clk/samsung/clk-pll.c154
1 files changed, 107 insertions, 47 deletions
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 48139bd510f1..9617825daabb 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -890,22 +890,14 @@ static const struct clk_ops samsung_s3c2440_mpll_clk_ops = {
#define PLL2550X_M_SHIFT (4)
#define PLL2550X_S_SHIFT (0)
-struct samsung_clk_pll2550x {
- struct clk_hw hw;
- const void __iomem *reg_base;
- unsigned long offset;
-};
-
-#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
-
static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
- struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
u32 r, p, m, s, pll_stat;
u64 fvco = parent_rate;
- pll_stat = readl_relaxed(pll->reg_base + pll->offset * 3);
+ pll_stat = readl_relaxed(pll->con_reg);
r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
if (!r)
return 0;
@@ -923,43 +915,6 @@ static const struct clk_ops samsung_pll2550x_clk_ops = {
.recalc_rate = samsung_pll2550x_recalc_rate,
};
-struct clk * __init samsung_clk_register_pll2550x(const char *name,
- const char *pname, const void __iomem *reg_base,
- const unsigned long offset)
-{
- struct samsung_clk_pll2550x *pll;
- struct clk *clk;
- struct clk_init_data init;
-
- pll = kzalloc(sizeof(*pll), GFP_KERNEL);
- if (!pll) {
- pr_err("%s: could not allocate pll clk %s\n", __func__, name);
- return NULL;
- }
-
- init.name = name;
- init.ops = &samsung_pll2550x_clk_ops;
- init.flags = CLK_GET_RATE_NOCACHE;
- init.parent_names = &pname;
- init.num_parents = 1;
-
- pll->hw.init = &init;
- pll->reg_base = reg_base;
- pll->offset = offset;
-
- clk = clk_register(NULL, &pll->hw);
- if (IS_ERR(clk)) {
- pr_err("%s: failed to register pll clock %s\n", __func__,
- name);
- kfree(pll);
- }
-
- if (clk_register_clkdev(clk, name, NULL))
- pr_err("%s: failed to register lookup for %s", __func__, name);
-
- return clk;
-}
-
/*
* PLL2550xx Clock Type
*/
@@ -1063,6 +1018,102 @@ static const struct clk_ops samsung_pll2550xx_clk_min_ops = {
};
/*
+ * PLL2650x Clock Type
+ */
+
+/* Maximum lock time can be 3000 * PDIV cycles */
+#define PLL2650X_LOCK_FACTOR 3000
+
+#define PLL2650X_M_MASK 0x1ff
+#define PLL2650X_P_MASK 0x3f
+#define PLL2650X_S_MASK 0x7
+#define PLL2650X_K_MASK 0xffff
+#define PLL2650X_LOCK_STAT_MASK 0x1
+#define PLL2650X_M_SHIFT 16
+#define PLL2650X_P_SHIFT 8
+#define PLL2650X_S_SHIFT 0
+#define PLL2650X_K_SHIFT 0
+#define PLL2650X_LOCK_STAT_SHIFT 29
+#define PLL2650X_PLL_ENABLE_SHIFT 31
+
+static unsigned long samsung_pll2650x_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u64 fout = parent_rate;
+ u32 mdiv, pdiv, sdiv, pll_con0, pll_con1;
+ s16 kdiv;
+
+ pll_con0 = readl_relaxed(pll->con_reg);
+ mdiv = (pll_con0 >> PLL2650X_M_SHIFT) & PLL2650X_M_MASK;
+ pdiv = (pll_con0 >> PLL2650X_P_SHIFT) & PLL2650X_P_MASK;
+ sdiv = (pll_con0 >> PLL2650X_S_SHIFT) & PLL2650X_S_MASK;
+
+ pll_con1 = readl_relaxed(pll->con_reg + 4);
+ kdiv = (s16)((pll_con1 >> PLL2650X_K_SHIFT) & PLL2650X_K_MASK);
+
+ fout *= (mdiv << 16) + kdiv;
+ do_div(fout, (pdiv << sdiv));
+ fout >>= 16;
+
+ return (unsigned long)fout;
+}
+
+static int samsung_pll2650x_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ const struct samsung_pll_rate_table *rate;
+ u32 con0, con1;
+
+ /* Get required rate settings from table */
+ rate = samsung_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, clk_hw_get_name(hw));
+ return -EINVAL;
+ }
+
+ con0 = readl_relaxed(pll->con_reg);
+ con1 = readl_relaxed(pll->con_reg + 4);
+
+ /* Set PLL lock time. */
+ writel_relaxed(rate->pdiv * PLL2650X_LOCK_FACTOR, pll->lock_reg);
+
+ /* Change PLL PMS values */
+ con0 &= ~((PLL2650X_M_MASK << PLL2650X_M_SHIFT) |
+ (PLL2650X_P_MASK << PLL2650X_P_SHIFT) |
+ (PLL2650X_S_MASK << PLL2650X_S_SHIFT));
+ con0 |= (rate->mdiv << PLL2650X_M_SHIFT) |
+ (rate->pdiv << PLL2650X_P_SHIFT) |
+ (rate->sdiv << PLL2650X_S_SHIFT);
+ con0 |= (1 << PLL2650X_PLL_ENABLE_SHIFT);
+ writel_relaxed(con0, pll->con_reg);
+
+ con1 &= ~(PLL2650X_K_MASK << PLL2650X_K_SHIFT);
+ con1 |= ((rate->kdiv & PLL2650X_K_MASK) << PLL2650X_K_SHIFT);
+ writel_relaxed(con1, pll->con_reg + 4);
+
+ do {
+ cpu_relax();
+ con0 = readl_relaxed(pll->con_reg);
+ } while (!(con0 & (PLL2650X_LOCK_STAT_MASK
+ << PLL2650X_LOCK_STAT_SHIFT)));
+
+ return 0;
+}
+
+static const struct clk_ops samsung_pll2650x_clk_ops = {
+ .recalc_rate = samsung_pll2650x_recalc_rate,
+ .round_rate = samsung_pll_round_rate,
+ .set_rate = samsung_pll2650x_set_rate,
+};
+
+static const struct clk_ops samsung_pll2650x_clk_min_ops = {
+ .recalc_rate = samsung_pll2650x_recalc_rate,
+};
+
+/*
* PLL2650XX Clock Type
*/
@@ -1263,12 +1314,21 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
else
init.ops = &samsung_s3c2440_mpll_clk_ops;
break;
+ case pll_2550x:
+ init.ops = &samsung_pll2550x_clk_ops;
+ break;
case pll_2550xx:
if (!pll->rate_table)
init.ops = &samsung_pll2550xx_clk_min_ops;
else
init.ops = &samsung_pll2550xx_clk_ops;
break;
+ case pll_2650x:
+ if (!pll->rate_table)
+ init.ops = &samsung_pll2650x_clk_min_ops;
+ else
+ init.ops = &samsung_pll2650x_clk_ops;
+ break;
case pll_2650xx:
if (!pll->rate_table)
init.ops = &samsung_pll2650xx_clk_min_ops;