Commit a1e8c136 authored by Viresh Kumar's avatar Viresh Kumar

PM / OPP: "opp-hz" is optional for power domains

"opp-hz" property is optional for power domains now and we shouldn't
error out if it is missing for power domains.

This patch creates two new routines, _get_opp_count() and
_opp_is_duplicate(), by separating existing code from their parent
functions. Also skip duplicate OPP check for power domain OPPs as they
may not have any the "opp-hz" field, but a platform specific performance
state binding to uniquely identify OPP nodes.

By default the debugfs OPP nodes are named using the "rate" value, but
that isn't possible for the power domain OPP nodes and hence they use
the index of the OPP node in the OPP node list instead.
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent b89469bd
...@@ -281,6 +281,23 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) ...@@ -281,6 +281,23 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
} }
EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
int _get_opp_count(struct opp_table *opp_table)
{
struct dev_pm_opp *opp;
int count = 0;
mutex_lock(&opp_table->lock);
list_for_each_entry(opp, &opp_table->opp_list, node) {
if (opp->available)
count++;
}
mutex_unlock(&opp_table->lock);
return count;
}
/** /**
* dev_pm_opp_get_opp_count() - Get number of opps available in the opp table * dev_pm_opp_get_opp_count() - Get number of opps available in the opp table
* @dev: device for which we do this operation * @dev: device for which we do this operation
...@@ -291,25 +308,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq); ...@@ -291,25 +308,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
int dev_pm_opp_get_opp_count(struct device *dev) int dev_pm_opp_get_opp_count(struct device *dev)
{ {
struct opp_table *opp_table; struct opp_table *opp_table;
struct dev_pm_opp *temp_opp; int count;
int count = 0;
opp_table = _find_opp_table(dev); opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) { if (IS_ERR(opp_table)) {
count = PTR_ERR(opp_table); count = PTR_ERR(opp_table);
dev_dbg(dev, "%s: OPP table not found (%d)\n", dev_dbg(dev, "%s: OPP table not found (%d)\n",
__func__, count); __func__, count);
return count; return 0;
}
mutex_lock(&opp_table->lock);
list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available)
count++;
} }
mutex_unlock(&opp_table->lock); count = _get_opp_count(opp_table);
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
return count; return count;
...@@ -985,22 +994,11 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp, ...@@ -985,22 +994,11 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
return true; return true;
} }
/* static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
* Returns: struct opp_table *opp_table,
* 0: On success. And appropriate error message for duplicate OPPs. struct list_head **head)
* -EBUSY: For OPP with same freq/volt and is available. The callers of
* _opp_add() must return 0 if they receive -EBUSY from it. This is to make
* sure we don't print error messages unnecessarily if different parts of
* kernel try to initialize the OPP table.
* -EEXIST: For OPP with same freq but different volt or is unavailable. This
* should be considered an error by the callers of _opp_add().
*/
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
struct opp_table *opp_table)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
struct list_head *head;
int ret;
/* /*
* Insert new OPP in order of increasing frequency and discard if * Insert new OPP in order of increasing frequency and discard if
...@@ -1010,17 +1008,14 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, ...@@ -1010,17 +1008,14 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* loop, don't replace it with head otherwise it will become an infinite * loop, don't replace it with head otherwise it will become an infinite
* loop. * loop.
*/ */
mutex_lock(&opp_table->lock);
head = &opp_table->opp_list;
list_for_each_entry(opp, &opp_table->opp_list, node) { list_for_each_entry(opp, &opp_table->opp_list, node) {
if (new_opp->rate > opp->rate) { if (new_opp->rate > opp->rate) {
head = &opp->node; *head = &opp->node;
continue; continue;
} }
if (new_opp->rate < opp->rate) if (new_opp->rate < opp->rate)
break; return 0;
/* Duplicate OPPs */ /* Duplicate OPPs */
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n", dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
...@@ -1029,12 +1024,39 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, ...@@ -1029,12 +1024,39 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
new_opp->supplies[0].u_volt, new_opp->available); new_opp->supplies[0].u_volt, new_opp->available);
/* Should we compare voltages for all regulators here ? */ /* Should we compare voltages for all regulators here ? */
ret = opp->available && return opp->available &&
new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST; new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST;
}
return 0;
}
/*
* Returns:
* 0: On success. And appropriate error message for duplicate OPPs.
* -EBUSY: For OPP with same freq/volt and is available. The callers of
* _opp_add() must return 0 if they receive -EBUSY from it. This is to make
* sure we don't print error messages unnecessarily if different parts of
* kernel try to initialize the OPP table.
* -EEXIST: For OPP with same freq but different volt or is unavailable. This
* should be considered an error by the callers of _opp_add().
*/
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
struct opp_table *opp_table, bool rate_not_available)
{
struct list_head *head;
int ret;
mutex_lock(&opp_table->lock);
head = &opp_table->opp_list;
if (likely(!rate_not_available)) {
ret = _opp_is_duplicate(dev, new_opp, opp_table, &head);
if (ret) {
mutex_unlock(&opp_table->lock); mutex_unlock(&opp_table->lock);
return ret; return ret;
} }
}
if (opp_table->get_pstate) if (opp_table->get_pstate)
new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate); new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate);
...@@ -1104,7 +1126,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, ...@@ -1104,7 +1126,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
new_opp->available = true; new_opp->available = true;
new_opp->dynamic = dynamic; new_opp->dynamic = dynamic;
ret = _opp_add(dev, new_opp, opp_table); ret = _opp_add(dev, new_opp, opp_table, false);
if (ret) { if (ret) {
/* Don't return error for duplicate OPPs */ /* Don't return error for duplicate OPPs */
if (ret == -EBUSY) if (ret == -EBUSY)
......
...@@ -77,10 +77,21 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table) ...@@ -77,10 +77,21 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
{ {
struct dentry *pdentry = opp_table->dentry; struct dentry *pdentry = opp_table->dentry;
struct dentry *d; struct dentry *d;
unsigned long id;
char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */ char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
/* Rate is unique to each OPP, use it to give opp-name */ /*
snprintf(name, sizeof(name), "opp:%lu", opp->rate); * Get directory name for OPP.
*
* - Normally rate is unique to each OPP, use it to get unique opp-name.
* - For some devices rate isn't available, use index instead.
*/
if (likely(opp->rate))
id = opp->rate;
else
id = _get_opp_count(opp_table);
snprintf(name, sizeof(name), "opp:%lu", id);
/* Create per-opp directory */ /* Create per-opp directory */
d = debugfs_create_dir(name, pdentry); d = debugfs_create_dir(name, pdentry);
......
...@@ -292,6 +292,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev, ...@@ -292,6 +292,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
u64 rate; u64 rate;
u32 val; u32 val;
int ret; int ret;
bool rate_not_available = false;
new_opp = _opp_allocate(opp_table); new_opp = _opp_allocate(opp_table);
if (!new_opp) if (!new_opp)
...@@ -299,22 +300,29 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev, ...@@ -299,22 +300,29 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
ret = of_property_read_u64(np, "opp-hz", &rate); ret = of_property_read_u64(np, "opp-hz", &rate);
if (ret < 0) { if (ret < 0) {
/* "opp-hz" is optional for devices like power domains. */
if (!of_find_property(dev->of_node, "#power-domain-cells",
NULL)) {
dev_err(dev, "%s: opp-hz not found\n", __func__); dev_err(dev, "%s: opp-hz not found\n", __func__);
goto free_opp; goto free_opp;
} }
rate_not_available = true;
} else {
/*
* Rate is defined as an unsigned long in clk API, and so
* casting explicitly to its type. Must be fixed once rate is 64
* bit guaranteed in clk API.
*/
new_opp->rate = (unsigned long)rate;
}
/* Check if the OPP supports hardware's hierarchy of versions or not */ /* Check if the OPP supports hardware's hierarchy of versions or not */
if (!_opp_is_supported(dev, opp_table, np)) { if (!_opp_is_supported(dev, opp_table, np)) {
dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate);
goto free_opp; goto free_opp;
} }
/*
* Rate is defined as an unsigned long in clk API, and so casting
* explicitly to its type. Must be fixed once rate is 64 bit
* guaranteed in clk API.
*/
new_opp->rate = (unsigned long)rate;
new_opp->turbo = of_property_read_bool(np, "turbo-mode"); new_opp->turbo = of_property_read_bool(np, "turbo-mode");
new_opp->np = np; new_opp->np = np;
...@@ -328,7 +336,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev, ...@@ -328,7 +336,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
if (ret) if (ret)
goto free_opp; goto free_opp;
ret = _opp_add(dev, new_opp, opp_table); ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
if (ret) { if (ret) {
/* Don't return error for duplicate OPPs */ /* Don't return error for duplicate OPPs */
if (ret == -EBUSY) if (ret == -EBUSY)
......
...@@ -188,13 +188,14 @@ struct opp_table { ...@@ -188,13 +188,14 @@ struct opp_table {
/* Routines internal to opp core */ /* Routines internal to opp core */
void _get_opp_table_kref(struct opp_table *opp_table); void _get_opp_table_kref(struct opp_table *opp_table);
int _get_opp_count(struct opp_table *opp_table);
struct opp_table *_find_opp_table(struct device *dev); struct opp_table *_find_opp_table(struct device *dev);
struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table); struct opp_device *_add_opp_dev(const struct device *dev, struct opp_table *opp_table);
void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev, bool remove_all); void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev, bool remove_all);
void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all); void _dev_pm_opp_find_and_remove_table(struct device *dev, bool remove_all);
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table); struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
void _opp_free(struct dev_pm_opp *opp); void _opp_free(struct dev_pm_opp *opp);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table); int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table, bool rate_not_available);
int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic); int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of); void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of);
struct opp_table *_add_opp_table(struct device *dev); struct opp_table *_add_opp_table(struct device *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment