diff options
Diffstat (limited to 'drivers/clk/meson/clk-pll.c')
| -rw-r--r-- | drivers/clk/meson/clk-pll.c | 41 |
1 files changed, 32 insertions, 9 deletions
diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c index 01341553f50b..218c769c6d50 100644 --- a/drivers/clk/meson/clk-pll.c +++ b/drivers/clk/meson/clk-pll.c @@ -34,6 +34,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/io.h> +#include <linux/math64.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/slab.h> @@ -51,9 +52,8 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, { struct meson_clk_pll *pll = to_meson_clk_pll(hw); struct parm *p; - unsigned long parent_rate_mhz = parent_rate / 1000000; - unsigned long rate_mhz; - u16 n, m, frac = 0, od, od2 = 0; + u64 rate; + u16 n, m, frac = 0, od, od2 = 0, od3 = 0; u32 reg; p = &pll->n; @@ -74,17 +74,23 @@ static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, od2 = PARM_GET(p->width, p->shift, reg); } + p = &pll->od3; + if (p->width) { + reg = readl(pll->base + p->reg_off); + od3 = PARM_GET(p->width, p->shift, reg); + } + + rate = (u64)m * parent_rate; + p = &pll->frac; if (p->width) { reg = readl(pll->base + p->reg_off); frac = PARM_GET(p->width, p->shift, reg); - rate_mhz = (parent_rate_mhz * m + \ - (parent_rate_mhz * frac >> 12)) * 2 / n; - rate_mhz = rate_mhz >> od >> od2; - } else - rate_mhz = (parent_rate_mhz * m / n) >> od >> od2; - return rate_mhz * 1000000; + rate += mul_u64_u32_shr(parent_rate, frac, p->width); + } + + return div_u64(rate, n) >> od >> od2 >> od3; } static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, @@ -94,6 +100,13 @@ static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, const struct pll_rate_table *rate_table = pll->rate_table; int i; + /* + * if the table is missing, just return the current rate + * since we don't have the other available frequencies + */ + if (!rate_table) + return meson_clk_pll_recalc_rate(hw, *parent_rate); + for (i = 0; i < pll->rate_count; i++) { if (rate <= rate_table[i].rate) return rate_table[i].rate; @@ -109,6 +122,9 @@ static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_ const struct pll_rate_table *rate_table = pll->rate_table; int i; + if (!rate_table) + return NULL; + for (i = 0; i < pll->rate_count; i++) { if (rate == rate_table[i].rate) return &rate_table[i]; @@ -215,6 +231,13 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, writel(reg, pll->base + p->reg_off); } + p = &pll->od3; + if (p->width) { + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->od3); + writel(reg, pll->base + p->reg_off); + } + p = &pll->frac; if (p->width) { reg = readl(pll->base + p->reg_off); |
