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: Viresh Kumar <viresh.kumar@linaro.org> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
b89469bdf0
commit
a1e8c13600
4 changed files with 96 additions and 54 deletions
|
@ -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);
|
||||
|
||||
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: device for which we do this operation
|
||||
|
@ -291,25 +308,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_suspend_opp_freq);
|
|||
int dev_pm_opp_get_opp_count(struct device *dev)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct dev_pm_opp *temp_opp;
|
||||
int count = 0;
|
||||
int count;
|
||||
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table)) {
|
||||
count = PTR_ERR(opp_table);
|
||||
dev_dbg(dev, "%s: OPP table not found (%d)\n",
|
||||
__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);
|
||||
|
||||
return count;
|
||||
|
@ -985,6 +994,43 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
|
|||
return true;
|
||||
}
|
||||
|
||||
static int _opp_is_duplicate(struct device *dev, struct dev_pm_opp *new_opp,
|
||||
struct opp_table *opp_table,
|
||||
struct list_head **head)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
|
||||
/*
|
||||
* Insert new OPP in order of increasing frequency and discard if
|
||||
* already present.
|
||||
*
|
||||
* Need to use &opp_table->opp_list in the condition part of the 'for'
|
||||
* loop, don't replace it with head otherwise it will become an infinite
|
||||
* loop.
|
||||
*/
|
||||
list_for_each_entry(opp, &opp_table->opp_list, node) {
|
||||
if (new_opp->rate > opp->rate) {
|
||||
*head = &opp->node;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new_opp->rate < opp->rate)
|
||||
return 0;
|
||||
|
||||
/* Duplicate OPPs */
|
||||
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
|
||||
__func__, opp->rate, opp->supplies[0].u_volt,
|
||||
opp->available, new_opp->rate,
|
||||
new_opp->supplies[0].u_volt, new_opp->available);
|
||||
|
||||
/* Should we compare voltages for all regulators here ? */
|
||||
return opp->available &&
|
||||
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.
|
||||
|
@ -996,44 +1042,20 @@ static bool _opp_supported_by_regulators(struct dev_pm_opp *opp,
|
|||
* 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 opp_table *opp_table, bool rate_not_available)
|
||||
{
|
||||
struct dev_pm_opp *opp;
|
||||
struct list_head *head;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Insert new OPP in order of increasing frequency and discard if
|
||||
* already present.
|
||||
*
|
||||
* Need to use &opp_table->opp_list in the condition part of the 'for'
|
||||
* loop, don't replace it with head otherwise it will become an infinite
|
||||
* loop.
|
||||
*/
|
||||
mutex_lock(&opp_table->lock);
|
||||
head = &opp_table->opp_list;
|
||||
|
||||
list_for_each_entry(opp, &opp_table->opp_list, node) {
|
||||
if (new_opp->rate > opp->rate) {
|
||||
head = &opp->node;
|
||||
continue;
|
||||
if (likely(!rate_not_available)) {
|
||||
ret = _opp_is_duplicate(dev, new_opp, opp_table, &head);
|
||||
if (ret) {
|
||||
mutex_unlock(&opp_table->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (new_opp->rate < opp->rate)
|
||||
break;
|
||||
|
||||
/* Duplicate OPPs */
|
||||
dev_warn(dev, "%s: duplicate OPPs detected. Existing: freq: %lu, volt: %lu, enabled: %d. New: freq: %lu, volt: %lu, enabled: %d\n",
|
||||
__func__, opp->rate, opp->supplies[0].u_volt,
|
||||
opp->available, new_opp->rate,
|
||||
new_opp->supplies[0].u_volt, new_opp->available);
|
||||
|
||||
/* Should we compare voltages for all regulators here ? */
|
||||
ret = opp->available &&
|
||||
new_opp->supplies[0].u_volt == opp->supplies[0].u_volt ? -EBUSY : -EEXIST;
|
||||
|
||||
mutex_unlock(&opp_table->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (opp_table->get_pstate)
|
||||
|
@ -1104,7 +1126,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
|
|||
new_opp->available = true;
|
||||
new_opp->dynamic = dynamic;
|
||||
|
||||
ret = _opp_add(dev, new_opp, opp_table);
|
||||
ret = _opp_add(dev, new_opp, opp_table, false);
|
||||
if (ret) {
|
||||
/* Don't return error for duplicate OPPs */
|
||||
if (ret == -EBUSY)
|
||||
|
|
|
@ -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 *d;
|
||||
unsigned long id;
|
||||
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 */
|
||||
d = debugfs_create_dir(name, pdentry);
|
||||
|
|
|
@ -292,6 +292,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
|
|||
u64 rate;
|
||||
u32 val;
|
||||
int ret;
|
||||
bool rate_not_available = false;
|
||||
|
||||
new_opp = _opp_allocate(opp_table);
|
||||
if (!new_opp)
|
||||
|
@ -299,8 +300,21 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
|
|||
|
||||
ret = of_property_read_u64(np, "opp-hz", &rate);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: opp-hz not found\n", __func__);
|
||||
goto free_opp;
|
||||
/* "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__);
|
||||
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 */
|
||||
|
@ -309,12 +323,6 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
|
|||
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->np = np;
|
||||
|
@ -328,7 +336,7 @@ static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev,
|
|||
if (ret)
|
||||
goto free_opp;
|
||||
|
||||
ret = _opp_add(dev, new_opp, opp_table);
|
||||
ret = _opp_add(dev, new_opp, opp_table, rate_not_available);
|
||||
if (ret) {
|
||||
/* Don't return error for duplicate OPPs */
|
||||
if (ret == -EBUSY)
|
||||
|
|
|
@ -188,13 +188,14 @@ struct opp_table {
|
|||
|
||||
/* Routines internal to opp core */
|
||||
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_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_find_and_remove_table(struct device *dev, bool remove_all);
|
||||
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
|
||||
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);
|
||||
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, bool of);
|
||||
struct opp_table *_add_opp_table(struct device *dev);
|
||||
|
|
Loading…
Reference in a new issue