diff options
Diffstat (limited to 'drivers/clk/clk-fractional-divider.c')
| -rw-r--r-- | drivers/clk/clk-fractional-divider.c | 68 |
1 files changed, 47 insertions, 21 deletions
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 6affe3565025..cd36a6e27f25 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -71,6 +71,7 @@ static void clk_fd_get_div(struct clk_hw *hw, struct u32_fract *fract) struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long flags = 0; unsigned long m, n; + u32 mmask, nmask; u32 val; if (fd->lock) @@ -85,8 +86,11 @@ static void clk_fd_get_div(struct clk_hw *hw, struct u32_fract *fract) else __release(fd->lock); - m = (val & fd->mmask) >> fd->mshift; - n = (val & fd->nmask) >> fd->nshift; + mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift; + nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift; + + m = (val & mmask) >> fd->mshift; + n = (val & nmask) >> fd->nshift; if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { m++; @@ -119,6 +123,7 @@ void clk_fractional_divider_general_approximation(struct clk_hw *hw, unsigned long *m, unsigned long *n) { struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long max_m, max_n; /* * Get rate closer to *parent_rate to guarantee there is no overflow @@ -134,30 +139,44 @@ void clk_fractional_divider_general_approximation(struct clk_hw *hw, rate <<= scale - fd->nwidth; } - rational_best_approximation(rate, *parent_rate, - GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), - m, n); + if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { + max_m = BIT(fd->mwidth); + max_n = BIT(fd->nwidth); + } else { + max_m = GENMASK(fd->mwidth - 1, 0); + max_n = GENMASK(fd->nwidth - 1, 0); + } + + rational_best_approximation(rate, *parent_rate, max_m, max_n, m, n); } +EXPORT_SYMBOL_GPL(clk_fractional_divider_general_approximation); -static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int clk_fd_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long m, n; u64 ret; - if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate)) - return *parent_rate; + if (!req->rate || (!clk_hw_can_set_rate_parent(hw) && req->rate >= req->best_parent_rate)) { + req->rate = req->best_parent_rate; + + return 0; + } if (fd->approximation) - fd->approximation(hw, rate, parent_rate, &m, &n); + fd->approximation(hw, req->rate, &req->best_parent_rate, &m, &n); else - clk_fractional_divider_general_approximation(hw, rate, parent_rate, &m, &n); + clk_fractional_divider_general_approximation(hw, req->rate, + &req->best_parent_rate, + &m, &n); - ret = (u64)*parent_rate * m; + ret = (u64)req->best_parent_rate * m; do_div(ret, n); - return ret; + req->rate = ret; + + return 0; } static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, @@ -165,25 +184,34 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, { struct clk_fractional_divider *fd = to_clk_fd(hw); unsigned long flags = 0; - unsigned long m, n; + unsigned long m, n, max_m, max_n; + u32 mmask, nmask; u32 val; - rational_best_approximation(rate, parent_rate, - GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), - &m, &n); + if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { + max_m = BIT(fd->mwidth); + max_n = BIT(fd->nwidth); + } else { + max_m = GENMASK(fd->mwidth - 1, 0); + max_n = GENMASK(fd->nwidth - 1, 0); + } + rational_best_approximation(rate, parent_rate, max_m, max_n, &m, &n); if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { m--; n--; } + mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift; + nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift; + if (fd->lock) spin_lock_irqsave(fd->lock, flags); else __acquire(fd->lock); val = clk_fd_readl(fd); - val &= ~(fd->mmask | fd->nmask); + val &= ~(mmask | nmask); val |= (m << fd->mshift) | (n << fd->nshift); clk_fd_writel(fd, val); @@ -229,7 +257,7 @@ static void clk_fd_debug_init(struct clk_hw *hw, struct dentry *dentry) const struct clk_ops clk_fractional_divider_ops = { .recalc_rate = clk_fd_recalc_rate, - .round_rate = clk_fd_round_rate, + .determine_rate = clk_fd_determine_rate, .set_rate = clk_fd_set_rate, #ifdef CONFIG_DEBUG_FS .debug_init = clk_fd_debug_init, @@ -260,10 +288,8 @@ struct clk_hw *clk_hw_register_fractional_divider(struct device *dev, fd->reg = reg; fd->mshift = mshift; fd->mwidth = mwidth; - fd->mmask = GENMASK(mwidth - 1, 0) << mshift; fd->nshift = nshift; fd->nwidth = nwidth; - fd->nmask = GENMASK(nwidth - 1, 0) << nshift; fd->flags = clk_divider_flags; fd->lock = lock; fd->hw.init = &init; |
