Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jerome Brunet and committed by
Stephen Boyd
b15ee490 fe3f338f

+45 -6
+30 -6
drivers/clk/clk-divider.c
··· 342 342 } 343 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 367 static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, 346 368 unsigned long *prate) 347 369 { 348 370 struct clk_divider *divider = to_clk_divider(hw); 349 - int bestdiv; 350 371 351 372 /* if read only, just return current value */ 352 373 if (divider->flags & CLK_DIVIDER_READ_ONLY) { 353 - bestdiv = clk_readl(divider->reg) >> divider->shift; 354 - bestdiv &= clk_div_mask(divider->width); 355 - bestdiv = _get_div(divider->table, bestdiv, divider->flags, 356 - divider->width); 357 - return DIV_ROUND_UP_ULL((u64)*prate, bestdiv); 374 + u32 val; 375 + 376 + val = clk_readl(divider->reg) >> divider->shift; 377 + val &= clk_div_mask(divider->width); 378 + 379 + return divider_ro_round_rate(hw, rate, prate, divider->table, 380 + divider->width, divider->flags, 381 + val); 358 382 } 359 383 360 384 return divider_round_rate(hw, rate, prate, divider->table,
+15
include/linux/clk-provider.h
··· 420 420 unsigned long rate, unsigned long *prate, 421 421 const struct clk_div_table *table, 422 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 427 int divider_get_val(unsigned long rate, unsigned long parent_rate, 424 428 const struct clk_div_table *table, u8 width, 425 429 unsigned long flags); ··· 782 778 { 783 779 return divider_round_rate_parent(hw, clk_hw_get_parent(hw), 784 780 rate, prate, table, width, flags); 781 + } 782 + 783 + static inline long divider_ro_round_rate(struct clk_hw *hw, unsigned long rate, 784 + unsigned long *prate, 785 + const struct clk_div_table *table, 786 + u8 width, unsigned long flags, 787 + unsigned int val) 788 + { 789 + return divider_ro_round_rate_parent(hw, clk_hw_get_parent(hw), 790 + rate, prate, table, width, flags, 791 + val); 785 792 } 786 793 787 794 /*