clk: handle reentrant clk_set_rate() calls from clock supply regulators
When clk_set_rate() is called, clk_calc_new_rates() and clk_calc_subtree() are used to determine the new rate needed for each clock in the affected subtree as well as to vote for the voltage level needed by each clocks' new rate. The regulator supplying a given clocks' voltage may itself depend upon a clock (such as for the I2C bus). This can then result in a reentrant clk_set_rate() call from within clk_calc_new_rate(). set_rate_nesting_count only ensures that vdd_class voltage votes are not removed as a result of reentrant clk_set_rate() calls from clk_change_rate(). Therefore, the clk_set_rate() call from within clk_calc_new_rates() results in the voltage votes for old rates being removed before the rates are physically changed during clk_change_rate(). This can then lead to undervolting and CPU lock-ups when scaling down the rate of a CPU clock. Avoid this issue by incrementing set_rate_nesting_count before clk_calc_new_rates(). This ensures that set_rate_nesting_count > 0 in the recursive clk_set_rate() call. That way, the old rate voltage votes are correctly removed only upon returning from the original clk_set_rate() call. Change-Id: Iec4cc103a8ba09daf413eea07bbcafae1ac71900 Signed-off-by: Shefali Jain <shefjain@codeaurora.org>
This commit is contained in:
committed by
David Collins
parent
af96f07f77
commit
352850e657
@@ -2396,6 +2396,8 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
if (clk_core_rate_is_protected(core))
|
||||
return -EBUSY;
|
||||
|
||||
set_rate_nesting_count++;
|
||||
|
||||
/* calculate new rates and get the topmost changed clock */
|
||||
top = clk_calc_new_rates(core, req_rate);
|
||||
if (!top) {
|
||||
@@ -2419,7 +2421,6 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
}
|
||||
|
||||
/* change the rates */
|
||||
set_rate_nesting_count++;
|
||||
ret = clk_change_rate(top);
|
||||
set_rate_nesting_count--;
|
||||
if (ret) {
|
||||
@@ -2447,6 +2448,7 @@ post_rate_change_err:
|
||||
return ret;
|
||||
|
||||
pre_rate_change_err:
|
||||
set_rate_nesting_count--;
|
||||
if (set_rate_nesting_count == 0) {
|
||||
clk_unvote_new_rate_vdd();
|
||||
clk_cleanup_vdd_votes();
|
||||
|
||||
Reference in New Issue
Block a user