summaryrefslogtreecommitdiff
path: root/drivers/clk/meson/clk-pll.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/meson/clk-pll.c')
-rw-r--r--drivers/clk/meson/clk-pll.c41
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);