diff options
author | Jerome Brunet <jbrunet@baylibre.com> | 2018-02-14 08:43:39 -0500 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2018-03-12 18:10:26 -0400 |
commit | b15ee490e16324c35b51f04bad54ae45a2cefd29 (patch) | |
tree | dfeb7a5ca3267f44b15ab44013e9d2af637a7b2e | |
parent | fe3f338f0cb2ed4d4f06da054c21ae2f8a36ef2d (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.c | 36 | ||||
-rw-r--r-- | include/linux/clk-provider.h | 15 |
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 | } |
343 | EXPORT_SYMBOL_GPL(divider_round_rate_parent); | 343 | EXPORT_SYMBOL_GPL(divider_round_rate_parent); |
344 | 344 | ||
345 | long 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 | } | ||
364 | EXPORT_SYMBOL_GPL(divider_ro_round_rate_parent); | ||
365 | |||
366 | |||
345 | static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, | 367 | static 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); |
423 | long 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); | ||
423 | int divider_get_val(unsigned long rate, unsigned long parent_rate, | 427 | int 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 | ||
787 | static 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 | */ |