Skip to content

Commit 073d3d2

Browse files
committed
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: 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>
1 parent 4c58e9d commit 073d3d2

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

drivers/opp/core.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed);
201201
* @opp: opp for which level value has to be returned for
202202
*
203203
* Return: level read from device tree corresponding to the opp, else
204-
* return 0.
204+
* return U32_MAX.
205205
*/
206206
unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp)
207207
{
@@ -221,7 +221,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_level);
221221
* @index: index of the required opp
222222
*
223223
* Return: performance state read from device tree corresponding to the
224-
* required opp, else return 0.
224+
* required opp, else return U32_MAX.
225225
*/
226226
unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp,
227227
unsigned int index)
@@ -808,6 +808,14 @@ struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev,
808808
struct dev_pm_opp *opp;
809809

810810
opp = _find_key_ceil(dev, &temp, 0, true, _read_level, NULL);
811+
812+
/* False match */
813+
if (temp == OPP_LEVEL_UNSET) {
814+
dev_err(dev, "%s: OPP levels aren't available\n", __func__);
815+
dev_pm_opp_put(opp);
816+
return ERR_PTR(-ENODEV);
817+
}
818+
811819
*level = temp;
812820
return opp;
813821
}
@@ -1049,12 +1057,18 @@ static int _set_opp_bw(const struct opp_table *opp_table,
10491057
static int _set_performance_state(struct device *dev, struct device *pd_dev,
10501058
struct dev_pm_opp *opp, int i)
10511059
{
1052-
unsigned int pstate = likely(opp) ? opp->required_opps[i]->level: 0;
1060+
unsigned int pstate = 0;
10531061
int ret;
10541062

10551063
if (!pd_dev)
10561064
return 0;
10571065

1066+
if (likely(opp)) {
1067+
pstate = opp->required_opps[i]->level;
1068+
if (pstate == OPP_LEVEL_UNSET)
1069+
return 0;
1070+
}
1071+
10581072
ret = dev_pm_domain_set_performance_state(pd_dev, pstate);
10591073
if (ret) {
10601074
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,
11351149
int ret = 0;
11361150

11371151
if (opp) {
1138-
if (!opp->level)
1152+
if (opp->level == OPP_LEVEL_UNSET)
11391153
return 0;
11401154

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

18681882
INIT_LIST_HEAD(&opp->node);
18691883

1884+
opp->level = OPP_LEVEL_UNSET;
1885+
18701886
return opp;
18711887
}
18721888

drivers/opp/of.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,8 +1393,14 @@ int of_get_required_opp_performance_state(struct device_node *np, int index)
13931393

13941394
opp = _find_opp_of_np(opp_table, required_np);
13951395
if (opp) {
1396-
pstate = opp->level;
1396+
if (opp->level == OPP_LEVEL_UNSET) {
1397+
pr_err("%s: OPP levels aren't available for %pOF\n",
1398+
__func__, np);
1399+
} else {
1400+
pstate = opp->level;
1401+
}
13971402
dev_pm_opp_put(opp);
1403+
13981404
}
13991405

14001406
dev_pm_opp_put_opp_table(opp_table);

include/linux/pm_opp.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,12 @@ struct dev_pm_opp_config {
9292
struct device ***virt_devs;
9393
};
9494

95+
#define OPP_LEVEL_UNSET U32_MAX
96+
9597
/**
9698
* struct dev_pm_opp_data - The data to use to initialize an OPP.
97-
* @level: The performance level for the OPP.
99+
* @level: The performance level for the OPP. Set level to OPP_LEVEL_UNSET if
100+
* level field isn't used.
98101
* @freq: The clock rate in Hz for the OPP.
99102
* @u_volt: The voltage in uV for the OPP.
100103
*/

0 commit comments

Comments
 (0)