clk: Move VDD voltage voting from core clock to top clock
Current vdd voting happens from top to core (clock on which the rate has been requested), which has issues with sources which are slewing. In the cases where the PLLs are slewing the RCGs should have the voltage already maintained for the new frequency before the PLLs are slewed. Modify clk_core_set_rate_nolock() so that it votes for all new frequency voltages for the core clock and each of its parent clocks before physically changing any rates. Change-Id: Idf02bedcfb05a37e0c944ebe36d2dcf9032ae0d8 Signed-off-by: Taniya Das <tdas@codeaurora.org>
This commit is contained in:
committed by
Gerrit - the friendly Code Review server
parent
3b3e314bad
commit
61dad2894f
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
|
||||
* Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
|
||||
* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -67,7 +67,9 @@ struct clk_core {
|
||||
unsigned long rate;
|
||||
unsigned long req_rate;
|
||||
unsigned long new_rate;
|
||||
unsigned long old_rate;
|
||||
struct clk_core *new_parent;
|
||||
struct clk_core *old_parent;
|
||||
struct clk_core *new_child;
|
||||
unsigned long flags;
|
||||
bool orphan;
|
||||
@@ -2081,7 +2083,7 @@ static int clk_change_rate(struct clk_core *core)
|
||||
struct clk_core *parent = NULL;
|
||||
int rc = 0;
|
||||
|
||||
old_rate = core->rate;
|
||||
core->old_rate = old_rate = core->rate;
|
||||
|
||||
if (core->new_parent) {
|
||||
parent = core->new_parent;
|
||||
@@ -2095,6 +2097,8 @@ static int clk_change_rate(struct clk_core *core)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
core->old_parent = core->parent;
|
||||
|
||||
if (core->flags & CLK_SET_RATE_UNGATE) {
|
||||
unsigned long flags;
|
||||
|
||||
@@ -2106,15 +2110,9 @@ static int clk_change_rate(struct clk_core *core)
|
||||
|
||||
trace_clk_set_rate(core, core->new_rate);
|
||||
|
||||
/* Enforce vdd requirements for new frequency. */
|
||||
if (core->prepare_count) {
|
||||
rc = clk_vote_rate_vdd(core, core->new_rate);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (core->new_parent && core->new_parent != core->parent) {
|
||||
old_parent = __clk_set_parent_before(core, core->new_parent);
|
||||
core->old_parent = old_parent;
|
||||
trace_clk_set_parent(core, core->new_parent);
|
||||
|
||||
if (core->ops->set_rate_and_parent) {
|
||||
@@ -2144,10 +2142,6 @@ static int clk_change_rate(struct clk_core *core)
|
||||
|
||||
trace_clk_set_rate_complete(core, core->new_rate);
|
||||
|
||||
/* Release vdd requirements for old frequency. */
|
||||
if (core->prepare_count)
|
||||
clk_unvote_rate_vdd(core, old_rate);
|
||||
|
||||
core->rate = clk_recalc(core, best_parent_rate);
|
||||
|
||||
if (core->flags & CLK_SET_RATE_UNGATE) {
|
||||
@@ -2185,13 +2179,7 @@ static int clk_change_rate(struct clk_core *core)
|
||||
if (core->new_child)
|
||||
rc = clk_change_rate(core->new_child);
|
||||
|
||||
clk_pm_runtime_put(core);
|
||||
return rc;
|
||||
|
||||
err_set_rate:
|
||||
if (core->prepare_count)
|
||||
clk_unvote_rate_vdd(core, core->new_rate);
|
||||
out:
|
||||
clk_pm_runtime_put(core);
|
||||
return rc;
|
||||
}
|
||||
@@ -2223,6 +2211,74 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
|
||||
return ret ? 0 : req.rate;
|
||||
}
|
||||
|
||||
static int vote_vdd_up(struct clk_core *core)
|
||||
{
|
||||
struct clk_core *parent = NULL;
|
||||
int ret, cur_level, next_level;
|
||||
|
||||
/* sanity */
|
||||
if (IS_ERR_OR_NULL(core))
|
||||
return 0;
|
||||
|
||||
if (core->vdd_class) {
|
||||
cur_level = clk_find_vdd_level(core, core->rate);
|
||||
next_level = clk_find_vdd_level(core, core->new_rate);
|
||||
if (cur_level == next_level)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* save parent rate, if it exists */
|
||||
if (core->new_parent)
|
||||
parent = core->new_parent;
|
||||
else if (core->parent)
|
||||
parent = core->parent;
|
||||
|
||||
if (core->prepare_count && core->new_rate) {
|
||||
ret = clk_vote_rate_vdd(core, core->new_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
vote_vdd_up(parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vote_vdd_down(struct clk_core *core)
|
||||
{
|
||||
struct clk_core *parent;
|
||||
unsigned long rate;
|
||||
int cur_level, old_level;
|
||||
|
||||
/* sanity */
|
||||
if (IS_ERR_OR_NULL(core))
|
||||
return 0;
|
||||
|
||||
rate = core->old_rate;
|
||||
|
||||
/* New rate set was a failure */
|
||||
if (DIV_ROUND_CLOSEST(core->rate, 1000) !=
|
||||
DIV_ROUND_CLOSEST(core->new_rate, 1000))
|
||||
rate = core->new_rate;
|
||||
|
||||
if (core->vdd_class) {
|
||||
cur_level = clk_find_vdd_level(core, core->rate);
|
||||
old_level = clk_find_vdd_level(core, core->old_rate);
|
||||
if ((cur_level == old_level)
|
||||
|| !core->vdd_class->level_votes[old_level])
|
||||
return 0;
|
||||
}
|
||||
|
||||
parent = core->old_parent;
|
||||
|
||||
if (core->prepare_count && rate)
|
||||
clk_unvote_rate_vdd(core, rate);
|
||||
|
||||
vote_vdd_down(parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
unsigned long req_rate)
|
||||
{
|
||||
@@ -2262,16 +2318,26 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Enforce the VDD for new frequency */
|
||||
ret = vote_vdd_up(core);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* change the rates */
|
||||
ret = clk_change_rate(top);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to set %s clock to run at %lu\n", __func__,
|
||||
top->name, req_rate);
|
||||
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
|
||||
return ret;
|
||||
/* Release vdd requirements for new frequency. */
|
||||
vote_vdd_down(core);
|
||||
goto err;
|
||||
}
|
||||
|
||||
core->req_rate = req_rate;
|
||||
/* Release vdd requirements for old frequency. */
|
||||
vote_vdd_down(core);
|
||||
|
||||
err:
|
||||
clk_pm_runtime_put(core);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user