clk: correct vdd_class voting scheme used during clock rate changes
Correct the method used to account for vdd_class voltage level
requirements for each clock during a clk_set_rate() call.
This ensures that the ref count for each level does not
become out of sync during reentrant clk_set_rate() calls
or during complex rate change and re-parenting situations.
Implement the following behavior for clocks which have a
vdd_class specified:
clk_core_prepare():
- Calculate the voltage level needed for core->rate
- Vote for this voltage level
- Set both vdd_class_vote and new_vdd_class_vote to this level
clk_core_unprepare():
- Unvote for core->vdd_class_vote
- Set both vdd_class_vote and new_vdd_class_vote to 0
clk_core_set_rate_nolock() (and the helper functions it calls):
- While traversing the clock tree and calculating core->new_rate
for each affected clock:
- Calculate next_level that is needed for core->new_rate
- If next_level != core->new_vdd_class_vote:
- Vote for next_level
- Insert core->rate_change_node into clk_rate_change_list
- If rate_change_node is already in the list, then
Unvote core->new_vdd_class_vote since it is being changed
- Set core->new_vdd_class_vote = next_level
- If an error is encountered *before* any physical clock rates
are changed, then loop over all clocks in clk_rate_change_list:
- Unvote for core->new_vdd_class_vote
- Set core->new_vdd_class_vote = core->vdd_class_vote
- Remove core->rate_change_node from the list
- If an error is encountered *after* any physical clock rates
are changed, then loop over all clocks in clk_rate_change_list:
- If core->vdd_class_vote > core->new_vdd_class_vote:
- Vote for core->vdd_class_vote
- Unvote for core->new_vdd_class_vote
- Set core->new_vdd_class_vote = core->vdd_class_vote
- This ensures that the maximum voltage level needed for
either core->rate or core->new_rate is configured since
the physical state of the clock isn't well known after
an error.
- Unvote for core->vdd_class_vote
- Set core->vdd_class_vote = core->new_vdd_class_vote
- Remove core->rate_change_node from the list
- If all physical clock rates are changed successfully (including
in reentrant clk_core_set_rate_nolock() calls), then loop over
all clocks in clk_rate_change_list:
- Unvote for core->vdd_class_vote
- Set core->vdd_class_vote = core->new_vdd_class_vote
- Remove core->rate_change_node from the list
Since clk_rate_change_list is shared between the original
invocation of clk_core_set_rate_nolock() and reentrant calls
to the function resulting from set_rate() callback ops that
explicitly call clk_set_rate(), special care must be taken
when inserting and removing elements. Insertions must occur
atomically with new_vdd_class_vote voting. Removals must only
occur from the original invocation of clk_core_set_rate_nolock()
and not in the reentrant calls. Use a static nesting count to
tell the original and reentrant calls apart.
Change-Id: I3bfebf20101a0bf22a79a33188a63dfff0af11cc
Signed-off-by:
David Collins <collinsd@codeaurora.org>
Loading
Please register or sign in to comment