summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJerome Brunet <jbrunet@baylibre.com>2018-02-14 08:43:39 -0500
committerStephen Boyd <sboyd@kernel.org>2018-03-12 18:10:26 -0400
commitb15ee490e16324c35b51f04bad54ae45a2cefd29 (patch)
treedfeb7a5ca3267f44b15ab44013e9d2af637a7b2e
parentfe3f338f0cb2ed4d4f06da054c21ae2f8a36ef2d (diff)
clk: divider: read-only divider can propagate rate change
When a divider clock has CLK_DIVIDER_READ_ONLY set, it means that the register shall be left un-touched, but it does not mean the clock should stop rate propagation if CLK_SET_RATE_PARENT is set This is properly handled in qcom clk-regmap-divider but it was not in the generic divider To fix this situation, introduce a new helper function divider_ro_round_rate, on the same model as divider_round_rate. Fixes: e6d5e7d90be9 ("clk-divider: Fix READ_ONLY when divider > 1") Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Tested-By: David Lechner <david@lechnology.com> Signed-off-by: Michael Turquette <mturquette@baylibre.com> Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--drivers/clk/clk-divider.c36
-rw-r--r--include/linux/clk-provider.h15
2 files changed, 45 insertions, 6 deletions
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 3c98d2650fa3..b6234a5da12d 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -342,19 +342,43 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
342} 342}
343EXPORT_SYMBOL_GPL(divider_round_rate_parent); 343EXPORT_SYMBOL_GPL(divider_round_rate_parent);
344 344
345long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
346 unsigned long rate, unsigned long *prate,
347 const struct clk_div_table *table, u8 width,
348 unsigned long flags, unsigned int val)
349{
350 int div;
351
352 div = _get_div(table, val, flags, width);
353
354 /* Even a read-only clock can propagate a rate change */
355 if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
356 if (!parent)
357 return -EINVAL;
358
359 *prate = clk_hw_round_rate(parent, rate * div);
360 }
361
362 return DIV_ROUND_UP_ULL((u64)*prate, div);
363}
364EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent);
365
366
345static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, 367static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
346 unsigned long *prate) 368 unsigned long *prate)
347{ 369{
348 struct clk_divider *divider = to_clk_divider(hw); 370 struct clk_divider *divider = to_clk_divider(hw);
349 int bestdiv;
350 371
351 /* if read only, just return current value */ 372 /* if read only, just return current value */
352 if (divider->flags & CLK_DIVIDER_READ_ONLY) { 373 if (divider->flags & CLK_DIVIDER_READ_ONLY) {
353 bestdiv = clk_readl(divider->reg) >> divider->shift; 374 u32 val;
354 bestdiv &= clk_div_mask(divider->width); 375
355 bestdiv = _get_div(divider->table, bestdiv, divider->flags, 376 val = clk_readl(divider->reg) >> divider->shift;
356 divider->width); 377 val &= clk_div_mask(divider->width);
357 return DIV_ROUND_UP_ULL((u64)*prate, bestdiv); 378
379 return divider_ro_round_rate(hw, rate, prate, divider->table,
380 divider->width, divider->flags,
381 val);
358 } 382 }
359 383
360 return divider_round_rate(hw, rate, prate, divider->table, 384 return divider_round_rate(hw, rate, prate, divider->table,
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index cb18526d69cb..210a890008f9 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -420,6 +420,10 @@ long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
420 unsigned long rate, unsigned long *prate, 420 unsigned long rate, unsigned long *prate,
421 const struct clk_div_table *table, 421 const struct clk_div_table *table,
422 u8 width, unsigned long flags); 422 u8 width, unsigned long flags);
423long divider_ro_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
424 unsigned long rate, unsigned long *prate,
425 const struct clk_div_table *table, u8 width,
426 unsigned long flags, unsigned int val);
423int divider_get_val(unsigned long rate, unsigned long parent_rate, 427int divider_get_val(unsigned long rate, unsigned long parent_rate,
424 const struct clk_div_table *table, u8 width, 428 const struct clk_div_table *table, u8 width,
425 unsigned long flags); 429 unsigned long flags);
@@ -780,6 +784,17 @@ static inline long divider_round_rate(struct clk_hw *hw, unsigned long rate,
780 rate, prate, table, width, flags); 784 rate, prate, table, width, flags);
781} 785}
782 786
787static inline long divider_ro_round_rate(struct clk_hw *hw, unsigned long rate,
788 unsigned long *prate,
789 const struct clk_div_table *table,
790 u8 width, unsigned long flags,
791 unsigned int val)
792{
793 return divider_ro_round_rate_parent(hw, clk_hw_get_parent(hw),
794 rate, prate, table, width, flags,
795 val);
796}
797
783/* 798/*
784 * FIXME clock api without lock protection 799 * FIXME clock api without lock protection
785 */ 800 */