Merge branch 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull more operating performance points (OPP) framework changes for v4.21 from Viresh Kumar: "- Fix missing OPP debugfs directory (Viresh Kumar). - Make genpd performance states orthogonal to idlestates (Ulf Hansson). - Propagate performance state changes from genpd to its master (Viresh Kumar). - Minor improvement of some OPP helpers (Viresh Kumar)." * 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: PM / Domains: Propagate performance state updates PM / Domains: Factorize dev_pm_genpd_set_performance_state() PM / Domains: Save OPP table pointer in genpd OPP: Don't return 0 on error from of_get_required_opp_performance_state() OPP: Add dev_pm_opp_xlate_performance_state() helper OPP: Improve _find_table_of_opp_np() PM / Domains: Make genpd performance states orthogonal to the idlestates OPP: Fix missing debugfs supply directory for OPPs OPP: Use opp_table->regulators to verify no regulator case
This commit is contained in:
@@ -239,6 +239,127 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
|
||||
static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
|
||||
#endif
|
||||
|
||||
static int _genpd_reeval_performance_state(struct generic_pm_domain *genpd,
|
||||
unsigned int state)
|
||||
{
|
||||
struct generic_pm_domain_data *pd_data;
|
||||
struct pm_domain_data *pdd;
|
||||
struct gpd_link *link;
|
||||
|
||||
/* New requested state is same as Max requested state */
|
||||
if (state == genpd->performance_state)
|
||||
return state;
|
||||
|
||||
/* New requested state is higher than Max requested state */
|
||||
if (state > genpd->performance_state)
|
||||
return state;
|
||||
|
||||
/* Traverse all devices within the domain */
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
|
||||
pd_data = to_gpd_data(pdd);
|
||||
|
||||
if (pd_data->performance_state > state)
|
||||
state = pd_data->performance_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Traverse all sub-domains within the domain. This can be
|
||||
* done without any additional locking as the link->performance_state
|
||||
* field is protected by the master genpd->lock, which is already taken.
|
||||
*
|
||||
* Also note that link->performance_state (subdomain's performance state
|
||||
* requirement to master domain) is different from
|
||||
* link->slave->performance_state (current performance state requirement
|
||||
* of the devices/sub-domains of the subdomain) and so can have a
|
||||
* different value.
|
||||
*
|
||||
* Note that we also take vote from powered-off sub-domains into account
|
||||
* as the same is done for devices right now.
|
||||
*/
|
||||
list_for_each_entry(link, &genpd->master_links, master_node) {
|
||||
if (link->performance_state > state)
|
||||
state = link->performance_state;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
|
||||
unsigned int state, int depth)
|
||||
{
|
||||
struct generic_pm_domain *master;
|
||||
struct gpd_link *link;
|
||||
int master_state, ret;
|
||||
|
||||
if (state == genpd->performance_state)
|
||||
return 0;
|
||||
|
||||
/* Propagate to masters of genpd */
|
||||
list_for_each_entry(link, &genpd->slave_links, slave_node) {
|
||||
master = link->master;
|
||||
|
||||
if (!master->set_performance_state)
|
||||
continue;
|
||||
|
||||
/* Find master's performance state */
|
||||
ret = dev_pm_opp_xlate_performance_state(genpd->opp_table,
|
||||
master->opp_table,
|
||||
state);
|
||||
if (unlikely(ret < 0))
|
||||
goto err;
|
||||
|
||||
master_state = ret;
|
||||
|
||||
genpd_lock_nested(master, depth + 1);
|
||||
|
||||
link->prev_performance_state = link->performance_state;
|
||||
link->performance_state = master_state;
|
||||
master_state = _genpd_reeval_performance_state(master,
|
||||
master_state);
|
||||
ret = _genpd_set_performance_state(master, master_state, depth + 1);
|
||||
if (ret)
|
||||
link->performance_state = link->prev_performance_state;
|
||||
|
||||
genpd_unlock(master);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = genpd->set_performance_state(genpd, state);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
genpd->performance_state = state;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
/* Encountered an error, lets rollback */
|
||||
list_for_each_entry_continue_reverse(link, &genpd->slave_links,
|
||||
slave_node) {
|
||||
master = link->master;
|
||||
|
||||
if (!master->set_performance_state)
|
||||
continue;
|
||||
|
||||
genpd_lock_nested(master, depth + 1);
|
||||
|
||||
master_state = link->prev_performance_state;
|
||||
link->performance_state = master_state;
|
||||
|
||||
master_state = _genpd_reeval_performance_state(master,
|
||||
master_state);
|
||||
if (_genpd_set_performance_state(master, master_state, depth + 1)) {
|
||||
pr_err("%s: Failed to roll back to %d performance state\n",
|
||||
master->name, master_state);
|
||||
}
|
||||
|
||||
genpd_unlock(master);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_genpd_set_performance_state- Set performance state of device's power
|
||||
* domain.
|
||||
@@ -257,10 +378,9 @@ static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {}
|
||||
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
|
||||
{
|
||||
struct generic_pm_domain *genpd;
|
||||
struct generic_pm_domain_data *gpd_data, *pd_data;
|
||||
struct pm_domain_data *pdd;
|
||||
struct generic_pm_domain_data *gpd_data;
|
||||
unsigned int prev;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
genpd = dev_to_genpd(dev);
|
||||
if (IS_ERR(genpd))
|
||||
@@ -281,47 +401,11 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
|
||||
prev = gpd_data->performance_state;
|
||||
gpd_data->performance_state = state;
|
||||
|
||||
/* New requested state is same as Max requested state */
|
||||
if (state == genpd->performance_state)
|
||||
goto unlock;
|
||||
state = _genpd_reeval_performance_state(genpd, state);
|
||||
ret = _genpd_set_performance_state(genpd, state, 0);
|
||||
if (ret)
|
||||
gpd_data->performance_state = prev;
|
||||
|
||||
/* New requested state is higher than Max requested state */
|
||||
if (state > genpd->performance_state)
|
||||
goto update_state;
|
||||
|
||||
/* Traverse all devices within the domain */
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
|
||||
pd_data = to_gpd_data(pdd);
|
||||
|
||||
if (pd_data->performance_state > state)
|
||||
state = pd_data->performance_state;
|
||||
}
|
||||
|
||||
if (state == genpd->performance_state)
|
||||
goto unlock;
|
||||
|
||||
/*
|
||||
* We aren't propagating performance state changes of a subdomain to its
|
||||
* masters as we don't have hardware that needs it. Over that, the
|
||||
* performance states of subdomain and its masters may not have
|
||||
* one-to-one mapping and would require additional information. We can
|
||||
* get back to this once we have hardware that needs it. For that
|
||||
* reason, we don't have to consider performance state of the subdomains
|
||||
* of genpd here.
|
||||
*/
|
||||
|
||||
update_state:
|
||||
if (genpd_status_on(genpd)) {
|
||||
ret = genpd->set_performance_state(genpd, state);
|
||||
if (ret) {
|
||||
gpd_data->performance_state = prev;
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
genpd->performance_state = state;
|
||||
|
||||
unlock:
|
||||
genpd_unlock(genpd);
|
||||
|
||||
return ret;
|
||||
@@ -347,15 +431,6 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)
|
||||
return ret;
|
||||
|
||||
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
|
||||
|
||||
if (unlikely(genpd->set_performance_state)) {
|
||||
ret = genpd->set_performance_state(genpd, genpd->performance_state);
|
||||
if (ret) {
|
||||
pr_warn("%s: Failed to set performance state %d (%d)\n",
|
||||
genpd->name, genpd->performance_state, ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)
|
||||
return ret;
|
||||
|
||||
@@ -1907,12 +1982,21 @@ int of_genpd_add_provider_simple(struct device_node *np,
|
||||
ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save table for faster processing while setting performance
|
||||
* state.
|
||||
*/
|
||||
genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
|
||||
WARN_ON(!genpd->opp_table);
|
||||
}
|
||||
|
||||
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
|
||||
if (ret) {
|
||||
if (genpd->set_performance_state)
|
||||
if (genpd->set_performance_state) {
|
||||
dev_pm_opp_put_opp_table(genpd->opp_table);
|
||||
dev_pm_opp_of_remove_table(&genpd->dev);
|
||||
}
|
||||
|
||||
goto unlock;
|
||||
}
|
||||
@@ -1965,6 +2049,13 @@ int of_genpd_add_provider_onecell(struct device_node *np,
|
||||
i, ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save table for faster processing while setting
|
||||
* performance state.
|
||||
*/
|
||||
genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
|
||||
WARN_ON(!genpd->opp_table);
|
||||
}
|
||||
|
||||
genpd->provider = &np->fwnode;
|
||||
@@ -1989,8 +2080,10 @@ error:
|
||||
genpd->provider = NULL;
|
||||
genpd->has_provider = false;
|
||||
|
||||
if (genpd->set_performance_state)
|
||||
if (genpd->set_performance_state) {
|
||||
dev_pm_opp_put_opp_table(genpd->opp_table);
|
||||
dev_pm_opp_of_remove_table(&genpd->dev);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&gpd_list_lock);
|
||||
@@ -2024,6 +2117,7 @@ void of_genpd_del_provider(struct device_node *np)
|
||||
if (!gpd->set_performance_state)
|
||||
continue;
|
||||
|
||||
dev_pm_opp_put_opp_table(gpd->opp_table);
|
||||
dev_pm_opp_of_remove_table(&gpd->dev);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user