Commit 073d3d2c authored by Viresh Kumar's avatar Viresh Kumar

OPP: Level zero is valid

The level zero can be used by some OPPs to drop performance state vote
for the device. It is perfectly fine to allow the same.

_set_opp_level() considers it as an invalid value currently and returns
early.

In order to support this properly, initialize the level field with
U32_MAX, which denotes unused level field.
Reported-by: default avatarStephan Gerhold <stephan.gerhold@kernkonzept.com>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Tested-by: default avatarStephan Gerhold <stephan.gerhold@kernkonzept.com>
Signed-off-by: default avatarViresh Kumar <viresh.kumar@linaro.org>
parent 4c58e9d8
...@@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed); ...@@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed);
* @opp: opp for which level value has to be returned for * @opp: opp for which level value has to be returned for
* *
* Return: level read from device tree corresponding to the opp, else * Return: level read from device tree corresponding to the opp, else
* return 0. * return U32_MAX.
*/ */
unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp)
{ {
...@@ -221,7 +221,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level); ...@@ -221,7 +221,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level);
* @index: index of the required opp * @index: index of the required opp
* *
* Return: performance state read from device tree corresponding to the * Return: performance state read from device tree corresponding to the
* required opp, else return 0. * required opp, else return U32_MAX.
*/ */
unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp,
unsigned int index) unsigned int index)
...@@ -808,6 +808,14 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, ...@@ -808,6 +808,14 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
opp = _find_key_ceil(dev, &temp, 0, true, _read_level, NULL); opp = _find_key_ceil(dev, &temp, 0, true, _read_level, NULL);
/* False match */
if (temp == OPP_LEVEL_UNSET) {
dev_err(dev, "%s: OPP levels aren't available\n", __func__);
dev_pm_opp_put(opp);
return ERR_PTR(-ENODEV);
}
*level = temp; *level = temp;
return opp; return opp;
} }
...@@ -1049,12 +1057,18 @@ static int _set_opp_bw(const struct opp_table *opp_table, ...@@ -1049,12 +1057,18 @@ static int _set_opp_bw(const struct opp_table *opp_table,
static int _set_performance_state(struct device *dev, struct device *pd_dev, static int _set_performance_state(struct device *dev, struct device *pd_dev,
struct dev_pm_opp *opp, int i) struct dev_pm_opp *opp, int i)
{ {
unsigned int pstate = likely(opp) ? opp->required_opps[i]->level: 0; unsigned int pstate = 0;
int ret; int ret;
if (!pd_dev) if (!pd_dev)
return 0; return 0;
if (likely(opp)) {
pstate = opp->required_opps[i]->level;
if (pstate == OPP_LEVEL_UNSET)
return 0;
}
ret = dev_pm_domain_set_performance_state(pd_dev, pstate); ret = dev_pm_domain_set_performance_state(pd_dev, pstate);
if (ret) { if (ret) {
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n", dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
...@@ -1135,7 +1149,7 @@ static int _set_opp_level(struct device *dev, struct opp_table *opp_table, ...@@ -1135,7 +1149,7 @@ static int _set_opp_level(struct device *dev, struct opp_table *opp_table,
int ret = 0; int ret = 0;
if (opp) { if (opp) {
if (!opp->level) if (opp->level == OPP_LEVEL_UNSET)
return 0; return 0;
level = opp->level; level = opp->level;
...@@ -1867,6 +1881,8 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table) ...@@ -1867,6 +1881,8 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table)
INIT_LIST_HEAD(&opp->node); INIT_LIST_HEAD(&opp->node);
opp->level = OPP_LEVEL_UNSET;
return opp; return opp;
} }
......
...@@ -1393,8 +1393,14 @@ int of_get_required_opp_performance_state(struct device_node *np, int index) ...@@ -1393,8 +1393,14 @@ int of_get_required_opp_performance_state(struct device_node *np, int index)
opp = _find_opp_of_np(opp_table, required_np); opp = _find_opp_of_np(opp_table, required_np);
if (opp) { if (opp) {
pstate = opp->level; if (opp->level == OPP_LEVEL_UNSET) {
pr_err("%s: OPP levels aren't available for %pOF\n",
__func__, np);
} else {
pstate = opp->level;
}
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
} }
dev_pm_opp_put_opp_table(opp_table); dev_pm_opp_put_opp_table(opp_table);
......
...@@ -92,9 +92,12 @@ struct dev_pm_opp_config { ...@@ -92,9 +92,12 @@ struct dev_pm_opp_config {
struct device ***virt_devs; struct device ***virt_devs;
}; };
#define OPP_LEVEL_UNSET U32_MAX
/** /**
* struct dev_pm_opp_data - The data to use to initialize an OPP. * struct dev_pm_opp_data - The data to use to initialize an OPP.
* @level: The performance level for the OPP. * @level: The performance level for the OPP. Set level to OPP_LEVEL_UNSET if
* level field isn't used.
* @freq: The clock rate in Hz for the OPP. * @freq: The clock rate in Hz for the OPP.
* @u_volt: The voltage in uV for the OPP. * @u_volt: The voltage in uV for the OPP.
*/ */
......
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