Commit 757e2821 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Pull opertaing performance points (OPP) framework fixes for 5.10-rc1
from Viresh Kumar:

"- Return -EPROBE_DEFER properly from dev_pm_opp_get_opp_table()
   (Stephan Gerhold).

 - Minor cleanups around required-opps (Stephan Gerhold).

 - Extends opp-supported-hw property to contain multiple versions
   (Viresh Kumar).

 - Multiple cleanups around dev_pm_opp_attach_genpd() (Viresh Kumar).

 - Multiple fixes, cleanups in the OPP core for overall better design
   (Viresh Kumar)."

* 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  opp: Allow opp-level to be set to 0
  opp: Prevent memory leak in dev_pm_opp_attach_genpd()
  ARM: tegra: Pass multiple versions in opp-supported-hw property
  opp: Allow opp-supported-hw to contain multiple versions
  dt-bindings: opp: Allow opp-supported-hw to contain multiple versions
  opp: Set required OPPs in reverse order when scaling down
  opp: Reduce code duplication in _set_required_opps()
  opp: Drop unnecessary check from dev_pm_opp_attach_genpd()
  opp: Handle multiple calls for same OPP table in _of_add_opp_table_v1()
  opp: Allow dev_pm_opp_get_opp_table() to return -EPROBE_DEFER
  opp: Remove _dev_pm_opp_find_and_remove_table() wrapper
  opp: Split out _opp_set_rate_zero()
  opp: Reuse the enabled flag in !target_freq path
  opp: Rename regulator_enabled and use it as status of all resources
parents 549738f1 a5663c9b
......@@ -154,25 +154,27 @@ Optional properties:
- opp-suspend: Marks the OPP to be used during device suspend. If multiple OPPs
in the table have this, the OPP with highest opp-hz will be used.
- opp-supported-hw: This enables us to select only a subset of OPPs from the
larger OPP table, based on what version of the hardware we are running on. We
still can't have multiple nodes with the same opp-hz value in OPP table.
It's a user defined array containing a hierarchy of hardware version numbers,
supported by the OPP. For example: a platform with hierarchy of three levels
of versions (A, B and C), this field should be like <X Y Z>, where X
corresponds to Version hierarchy A, Y corresponds to version hierarchy B and Z
corresponds to version hierarchy C.
Each level of hierarchy is represented by a 32 bit value, and so there can be
only 32 different supported version per hierarchy. i.e. 1 bit per version. A
value of 0xFFFFFFFF will enable the OPP for all versions for that hierarchy
level. And a value of 0x00000000 will disable the OPP completely, and so we
never want that to happen.
If 32 values aren't sufficient for a version hierarchy, than that version
hierarchy can be contained in multiple 32 bit values. i.e. <X Y Z1 Z2> in the
above example, Z1 & Z2 refer to the version hierarchy Z.
- opp-supported-hw: This property allows a platform to enable only a subset of
the OPPs from the larger set present in the OPP table, based on the current
version of the hardware (already known to the operating system).
Each block present in the array of blocks in this property, represents a
sub-group of hardware versions supported by the OPP. i.e. <sub-group A>,
<sub-group B>, etc. The OPP will be enabled if _any_ of these sub-groups match
the hardware's version.
Each sub-group is a platform defined array representing the hierarchy of
hardware versions supported by the platform. For a platform with three
hierarchical levels of version (X.Y.Z), this field shall look like
opp-supported-hw = <X1 Y1 Z1>, <X2 Y2 Z2>, <X3 Y3 Z3>.
Each level (eg. X1) in version hierarchy is represented by a 32 bit value, one
bit per version and so there can be maximum 32 versions per level. Logical AND
(&) operation is performed for each level with the hardware's level version
and a non-zero output for _all_ the levels in a sub-group means the OPP is
supported by hardware. A value of 0xFFFFFFFF for each level in the sub-group
will enable the OPP for all versions for the hardware.
- status: Marks the node enabled/disabled.
......@@ -503,7 +505,6 @@ Example 5: opp-supported-hw
*/
opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>
opp-hz = /bits/ 64 <600000000>;
opp-microvolt = <915000 900000 925000>;
...
};
......@@ -516,7 +517,17 @@ Example 5: opp-supported-hw
*/
opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>
opp-hz = /bits/ 64 <800000000>;
opp-microvolt = <915000 900000 925000>;
...
};
opp-900000000 {
/*
* Supports:
* - All cuts and substrate where process version is 0x2.
* - All cuts and process where substrate version is 0x2.
*/
opp-supported-hw = <0xFFFFFFFF 0xFFFFFFFF 0x02>, <0xFFFFFFFF 0x01 0xFFFFFFFF>
opp-hz = /bits/ 64 <900000000>;
...
};
};
......
......@@ -26,14 +26,6 @@ opp@456000000,800 {
opp-microvolt = <800000 800000 1125000>;
};
opp@456000000,800,2,2 {
opp-microvolt = <800000 800000 1125000>;
};
opp@456000000,800,3,2 {
opp-microvolt = <800000 800000 1125000>;
};
opp@456000000,825 {
opp-microvolt = <825000 825000 1125000>;
};
......@@ -46,10 +38,6 @@ opp@608000000,800 {
opp-microvolt = <800000 800000 1125000>;
};
opp@608000000,800,3,2 {
opp-microvolt = <800000 800000 1125000>;
};
opp@608000000,825 {
opp-microvolt = <825000 825000 1125000>;
};
......@@ -78,18 +66,6 @@ opp@760000000,875 {
opp-microvolt = <875000 875000 1125000>;
};
opp@760000000,875,1,1 {
opp-microvolt = <875000 875000 1125000>;
};
opp@760000000,875,0,2 {
opp-microvolt = <875000 875000 1125000>;
};
opp@760000000,875,1,2 {
opp-microvolt = <875000 875000 1125000>;
};
opp@760000000,900 {
opp-microvolt = <900000 900000 1125000>;
};
......@@ -134,14 +110,6 @@ opp@912000000,950 {
opp-microvolt = <950000 950000 1125000>;
};
opp@912000000,950,0,2 {
opp-microvolt = <950000 950000 1125000>;
};
opp@912000000,950,2,2 {
opp-microvolt = <950000 950000 1125000>;
};
opp@912000000,1000 {
opp-microvolt = <1000000 1000000 1125000>;
};
......@@ -170,10 +138,6 @@ opp@1000000000,1000 {
opp-microvolt = <1000000 1000000 1125000>;
};
opp@1000000000,1000,0,2 {
opp-microvolt = <1000000 1000000 1125000>;
};
opp@1000000000,1025 {
opp-microvolt = <1025000 1025000 1125000>;
};
......
......@@ -37,19 +37,8 @@ opp@456000000,750 {
opp@456000000,800 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x03 0x0006>;
opp-hz = /bits/ 64 <456000000>;
};
opp@456000000,800,2,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x04 0x0004>;
opp-hz = /bits/ 64 <456000000>;
};
opp@456000000,800,3,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x08 0x0004>;
opp-supported-hw = <0x03 0x0006>, <0x04 0x0004>,
<0x08 0x0004>;
opp-hz = /bits/ 64 <456000000>;
};
......@@ -67,13 +56,7 @@ opp@608000000,750 {
opp@608000000,800 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x04 0x0006>;
opp-hz = /bits/ 64 <608000000>;
};
opp@608000000,800,3,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x08 0x0004>;
opp-supported-hw = <0x04 0x0006>, <0x08 0x0004>;
opp-hz = /bits/ 64 <608000000>;
};
......@@ -115,25 +98,8 @@ opp@760000000,850 {
opp@760000000,875 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x04 0x0001>;
opp-hz = /bits/ 64 <760000000>;
};
opp@760000000,875,1,1 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x02 0x0002>;
opp-hz = /bits/ 64 <760000000>;
};
opp@760000000,875,0,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x01 0x0004>;
opp-hz = /bits/ 64 <760000000>;
};
opp@760000000,875,1,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x02 0x0004>;
opp-supported-hw = <0x04 0x0001>, <0x02 0x0002>,
<0x01 0x0004>, <0x02 0x0004>;
opp-hz = /bits/ 64 <760000000>;
};
......@@ -199,19 +165,8 @@ opp@912000000,925 {
opp@912000000,950 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x02 0x0006>;
opp-hz = /bits/ 64 <912000000>;
};
opp@912000000,950,0,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x01 0x0004>;
opp-hz = /bits/ 64 <912000000>;
};
opp@912000000,950,2,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x04 0x0004>;
opp-supported-hw = <0x02 0x0006>, <0x01 0x0004>,
<0x04 0x0004>;
opp-hz = /bits/ 64 <912000000>;
};
......@@ -253,13 +208,7 @@ opp@1000000000,975 {
opp@1000000000,1000 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x02 0x0006>;
opp-hz = /bits/ 64 <1000000000>;
};
opp@1000000000,1000,0,2 {
clock-latency-ns = <400000>;
opp-supported-hw = <0x01 0x0004>;
opp-supported-hw = <0x02 0x0006>, <0x01 0x0004>;
opp-hz = /bits/ 64 <1000000000>;
};
......
This diff is collapsed.
......@@ -2044,8 +2044,9 @@ int of_genpd_add_provider_simple(struct device_node *np,
if (genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table(&genpd->dev);
if (ret) {
dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
ret);
if (ret != -EPROBE_DEFER)
dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
ret);
goto unlock;
}
......@@ -2054,7 +2055,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
* state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
WARN_ON(!genpd->opp_table);
WARN_ON(IS_ERR(genpd->opp_table));
}
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
......@@ -2111,8 +2112,9 @@ int of_genpd_add_provider_onecell(struct device_node *np,
if (genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
if (ret) {
dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
i, ret);
if (ret != -EPROBE_DEFER)
dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
i, ret);
goto error;
}
......@@ -2121,7 +2123,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
* performance state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
WARN_ON(!genpd->opp_table);
WARN_ON(IS_ERR(genpd->opp_table));
}
genpd->provider = &np->fwnode;
......
This diff is collapsed.
......@@ -124,7 +124,7 @@ void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask,
continue;
}
_dev_pm_opp_find_and_remove_table(cpu_dev);
dev_pm_opp_remove_table(cpu_dev);
}
}
......
......@@ -434,9 +434,9 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths);
static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
struct device_node *np)
{
unsigned int count = opp_table->supported_hw_count;
u32 version;
int ret;
unsigned int levels = opp_table->supported_hw_count;
int count, versions, ret, i, j;
u32 val;
if (!opp_table->supported_hw) {
/*
......@@ -451,21 +451,40 @@ static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table,
return true;
}
while (count--) {
ret = of_property_read_u32_index(np, "opp-supported-hw", count,
&version);
if (ret) {
dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
__func__, count, ret);
return false;
count = of_property_count_u32_elems(np, "opp-supported-hw");
if (count <= 0 || count % levels) {
dev_err(dev, "%s: Invalid opp-supported-hw property (%d)\n",
__func__, count);
return false;
}
versions = count / levels;
/* All levels in at least one of the versions should match */
for (i = 0; i < versions; i++) {
bool supported = true;
for (j = 0; j < levels; j++) {
ret = of_property_read_u32_index(np, "opp-supported-hw",
i * levels + j, &val);
if (ret) {
dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n",
__func__, i * levels + j, ret);
return false;
}
/* Check if the level is supported */
if (!(val & opp_table->supported_hw[j])) {
supported = false;
break;
}
}
/* Both of these are bitwise masks of the versions */
if (!(version & opp_table->supported_hw[count]))
return false;
if (supported)
return true;
}
return true;
return false;
}
static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
......@@ -616,7 +635,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev,
*/
void dev_pm_opp_of_remove_table(struct device *dev)
{
_dev_pm_opp_find_and_remove_table(dev);
dev_pm_opp_remove_table(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table);
......@@ -823,7 +842,7 @@ static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table,
static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
{
struct device_node *np;
int ret, count = 0, pstate_count = 0;
int ret, count = 0;
struct dev_pm_opp *opp;
/* OPP table is already initialized for the device */
......@@ -857,20 +876,14 @@ static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table)
goto remove_static_opp;
}
list_for_each_entry(opp, &opp_table->opp_list, node)
pstate_count += !!opp->pstate;
/* Either all or none of the nodes shall have performance state set */
if (pstate_count && pstate_count != count) {
dev_err(dev, "Not all nodes have performance state set (%d: %d)\n",
count, pstate_count);
ret = -ENOENT;
goto remove_static_opp;
list_for_each_entry(opp, &opp_table->opp_list, node) {
/* Any non-zero performance state would enable the feature */
if (opp->pstate) {
opp_table->genpd_performance_state = true;
break;
}
}
if (pstate_count)
opp_table->genpd_performance_state = true;
return 0;
remove_static_opp:
......@@ -886,11 +899,25 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
const __be32 *val;
int nr, ret = 0;
mutex_lock(&opp_table->lock);
if (opp_table->parsed_static_opps) {
opp_table->parsed_static_opps++;
mutex_unlock(&opp_table->lock);
return 0;
}
opp_table->parsed_static_opps = 1;
mutex_unlock(&opp_table->lock);
prop = of_find_property(dev->of_node, "operating-points", NULL);
if (!prop)
return -ENODEV;
if (!prop->value)
return -ENODATA;
if (!prop) {
ret = -ENODEV;
goto remove_static_opp;
}
if (!prop->value) {
ret = -ENODATA;
goto remove_static_opp;
}
/*
* Each OPP is a set of tuples consisting of frequency and
......@@ -899,13 +926,10 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
nr = prop->length / sizeof(u32);
if (nr % 2) {
dev_err(dev, "%s: Invalid OPP table\n", __func__);
return -EINVAL;
ret = -EINVAL;
goto remove_static_opp;
}
mutex_lock(&opp_table->lock);
opp_table->parsed_static_opps = 1;
mutex_unlock(&opp_table->lock);
val = prop->value;
while (nr) {
unsigned long freq = be32_to_cpup(val++) * 1000;
......@@ -915,12 +939,14 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
if (ret) {
dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
__func__, freq, ret);
_opp_remove_all_static(opp_table);
return ret;
goto remove_static_opp;
}
nr -= 2;
}
remove_static_opp:
_opp_remove_all_static(opp_table);
return ret;
}
......@@ -947,8 +973,8 @@ int dev_pm_opp_of_add_table(struct device *dev)
int ret;
opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0);
if (!opp_table)
return -ENOMEM;
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
/*
* OPPs have two version of bindings now. Also try the old (v1)
......@@ -1002,8 +1028,8 @@ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
}
opp_table = dev_pm_opp_get_opp_table_indexed(dev, index);
if (!opp_table)
return -ENOMEM;
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
ret = _of_add_opp_table_v2(dev, opp_table);
if (ret)
......
......@@ -147,11 +147,11 @@ enum opp_table_access {
* @clk: Device's clock handle
* @regulators: Supply regulators
* @regulator_count: Number of power supply regulators. Its value can be -1
* @regulator_enabled: Set to true if regulators were previously enabled.
* (uninitialized), 0 (no opp-microvolt property) or > 0 (has opp-microvolt
* property).
* @paths: Interconnect path handles
* @path_count: Number of interconnect paths
* @enabled: Set to true if the device's resources are enabled/configured.
* @genpd_performance_state: Device's power domain support performance state.
* @is_genpd: Marks if the OPP table belongs to a genpd.
* @set_opp: Platform specific set_opp callback
......@@ -195,9 +195,9 @@ struct opp_table {
struct clk *clk;
struct regulator **regulators;
int regulator_count;
bool regulator_enabled;
struct icc_path **paths;
unsigned int path_count;
bool enabled;
bool genpd_performance_state;
bool is_genpd;
......@@ -217,7 +217,6 @@ 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_find_and_remove_table(struct device *dev);
struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
void _opp_free(struct dev_pm_opp *opp);
int _opp_compare_key(struct dev_pm_opp *opp1, struct dev_pm_opp *opp2);
......
......@@ -93,7 +93,7 @@ static int exynos_asv_update_opps(struct exynos_asv *asv)
continue;
opp_table = dev_pm_opp_get_opp_table(cpu);
if (IS_ERR_OR_NULL(opp_table))
if (IS_ERR(opp_table))
continue;
if (!last_opp_table || opp_table != last_opp_table) {
......
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