Skip to content

Commit

Permalink
OPP: Level zero is valid
Browse files Browse the repository at this point in the history
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: Stephan Gerhold <stephan.gerhold@kernkonzept.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Tested-by: Stephan Gerhold <stephan.gerhold@kernkonzept.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
  • Loading branch information
Viresh Kumar committed Nov 28, 2023
1 parent 4c58e9d commit 073d3d2
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 6 deletions.
24 changes: 20 additions & 4 deletions drivers/opp/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed);
* @opp: opp for which level value has to be returned for
*
* 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)
{
Expand All @@ -221,7 +221,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level);
* @index: index of the required opp
*
* 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 index)
Expand Down Expand Up @@ -808,6 +808,14 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
struct dev_pm_opp *opp;

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;
return opp;
}
Expand Down Expand Up @@ -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,
struct dev_pm_opp *opp, int i)
{
unsigned int pstate = likely(opp) ? opp->required_opps[i]->level: 0;
unsigned int pstate = 0;
int ret;

if (!pd_dev)
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);
if (ret) {
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
Expand Down Expand Up @@ -1135,7 +1149,7 @@ static int _set_opp_level(struct device *dev, struct opp_table *opp_table,
int ret = 0;

if (opp) {
if (!opp->level)
if (opp->level == OPP_LEVEL_UNSET)
return 0;

level = opp->level;
Expand Down Expand Up @@ -1867,6 +1881,8 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table)

INIT_LIST_HEAD(&opp->node);

opp->level = OPP_LEVEL_UNSET;

return opp;
}

Expand Down
8 changes: 7 additions & 1 deletion drivers/opp/of.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
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_table(opp_table);
Expand Down
5 changes: 4 additions & 1 deletion include/linux/pm_opp.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,12 @@ struct dev_pm_opp_config {
struct device ***virt_devs;
};

#define OPP_LEVEL_UNSET U32_MAX

/**
* 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.
* @u_volt: The voltage in uV for the OPP.
*/
Expand Down

0 comments on commit 073d3d2

Please sign in to comment.