summaryrefslogtreecommitdiff
path: root/drivers/clk/imx/clk-pll14xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/imx/clk-pll14xx.c')
-rw-r--r--drivers/clk/imx/clk-pll14xx.c44
1 files changed, 40 insertions, 4 deletions
diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c
index 047f1d8fe323..92756b95453c 100644
--- a/drivers/clk/imx/clk-pll14xx.c
+++ b/drivers/clk/imx/clk-pll14xx.c
@@ -92,9 +92,12 @@ static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ const struct imx_pll14xx_rate_table *rate_table = pll->rate_table;
u32 mdiv, pdiv, sdiv, pll_div_ctl0, pll_div_ctl1;
short int kdiv;
u64 fvco = parent_rate;
+ long rate = 0;
+ int i;
pll_div_ctl0 = readl_relaxed(pll->base + 4);
pll_div_ctl1 = readl_relaxed(pll->base + 8);
@@ -103,13 +106,25 @@ static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw,
sdiv = (pll_div_ctl0 & SDIV_MASK) >> SDIV_SHIFT;
kdiv = pll_div_ctl1 & KDIV_MASK;
+ /*
+ * Sometimes, the recalculated rate has deviation due to
+ * the frac part. So find the accurate pll rate from the table
+ * first, if no match rate in the table, use the rate calculated
+ * from the equation below.
+ */
+ for (i = 0; i < pll->rate_count; i++) {
+ if (rate_table[i].pdiv == pdiv && rate_table[i].mdiv == mdiv &&
+ rate_table[i].sdiv == sdiv && rate_table[i].kdiv == kdiv)
+ rate = rate_table[i].rate;
+ }
+
/* fvco = (m * 65536 + k) * Fin / (p * 65536) */
fvco *= (mdiv * 65536 + kdiv);
pdiv *= 65536;
do_div(fvco, pdiv << sdiv);
- return fvco;
+ return rate ? (unsigned long) rate : (unsigned long)fvco;
}
static inline bool clk_pll14xx_mp_change(const struct imx_pll14xx_rate_table *rate,
@@ -315,6 +330,26 @@ static void clk_pll14xx_unprepare(struct clk_hw *hw)
writel_relaxed(val, pll->base + GNRL_CTL);
}
+void clk_set_delta_k(struct clk_hw *hw, short int delta_k)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+ short int k;
+ u32 val;
+
+ val = readl_relaxed(pll->base + 8);
+ k = (val & KDIV_MASK) + delta_k;
+ writel_relaxed(k << KDIV_SHIFT, pll->base + 8);
+}
+
+void clk_get_pll_setting(struct clk_hw *hw, u32 *pll_div_ctrl0,
+ u32 *pll_div_ctrl1)
+{
+ struct clk_pll14xx *pll = to_clk_pll14xx(hw);
+
+ *pll_div_ctrl0 = readl_relaxed(pll->base + 4);
+ *pll_div_ctrl1 = readl_relaxed(pll->base + 8);
+}
+
static const struct clk_ops clk_pll1416x_ops = {
.prepare = clk_pll14xx_prepare,
.unprepare = clk_pll14xx_unprepare,
@@ -337,8 +372,8 @@ static const struct clk_ops clk_pll1443x_ops = {
.set_rate = clk_pll1443x_set_rate,
};
-struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
- void __iomem *base,
+struct clk *imx_dev_clk_pll14xx(struct device *dev, const char *name,
+ const char *parent_name, void __iomem *base,
const struct imx_pll14xx_clk *pll_clk)
{
struct clk_pll14xx *pll;
@@ -380,7 +415,7 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
val &= ~BYPASS_MASK;
writel_relaxed(val, pll->base + GNRL_CTL);
- clk = clk_register(NULL, &pll->hw);
+ clk = clk_register(dev, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll %s %lu\n",
__func__, name, PTR_ERR(clk));
@@ -389,3 +424,4 @@ struct clk *imx_clk_pll14xx(const char *name, const char *parent_name,
return clk;
}
+EXPORT_SYMBOL_GPL(imx_dev_clk_pll14xx);