OPP: Simplify required-opp handling
Subsystems (2)
v3 cover OPP: Simplify required-opp handling
Hello,
Configuring the required OPP was never properly implemented, we just took an exception for genpds and configured them directly, while leaving out all other required OPP types.
Now that a standard call to dev_pm_opp_set_opp() takes care of configuring the opp->level too, the special handling for genpds can be avoided by simply calling dev_pm_opp_set_opp() for the required OPPs, which shall eventually configure the corresponding level for genpds.
This also makes it possible for us to configure other type of required OPPs (no concrete users yet though), via the same path. This is how other frameworks take care of parent nodes, like clock, regulators, etc, where we recursively call the same helper.
Pushed here:
git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git opp/required-opps
V2->V3:
- Dropped patch 1/3, merged.
- Added a new commit to avoid propagation and a WARN() for parent genpd case.
V1->V2:
- Support opp-level 0, drop vote i.e..
- Fix OPP pointer while calling dev_pm_opp_set_opp() recursively.
- Minor checks and fixes.
- Add Reviewed-by from Ulf.
--
Viresh
Viresh Kumar (3):
OPP: Use _set_opp_level() for single genpd case
OPP: Call dev_pm_opp_set_opp() for required OPPs
OPP: Don't set OPP recursively for a parent genpd
drivers/opp/core.c | 180 ++++++++++++++++++++++------------------- drivers/opp/of.c | 49 ++++++++--- drivers/opp/opp.h | 8 +- include/linux/pm_opp.h | 7 +- 4 files changed, 144 insertions(+), 100 deletions(-)
Signature · 1 line
2.31.1.272.g89b43f80a514
PATCH v3 1/3 OPP: Use _set_opp_level() for single genpd case
There are two genpd (as required-opp) cases that we need to handle, devices with a single genpd and ones with multiple genpds.
The multiple genpds case is clear, where the OPP core calls dev_pm_domain_attach_by_name() for them and uses the virtual devices returned by this helper to call dev_pm_domain_set_performance_state() later to change the performance state.
The single genpd case however requires special handling as we need to use the same dev structure (instead of a virtual one provided by genpd core) for setting the performance state via dev_pm_domain_set_performance_state().
As we move towards more generic code to take care of the required OPPs, where we will recursively call dev_pm_opp_set_opp() for all the required OPPs, the above special case becomes a problem.
It doesn't make sense for a device's DT entry to have both "opp-level" and single "required-opps" entry pointing to a genpd's OPP, as that would make the OPP core call dev_pm_domain_set_performance_state() for two different values for the same device structure. And so we can reuse the 'opp->level" field in such a case and call _set_opp_level() for the device.
--- drivers/opp/core.c | 6 ++++-- drivers/opp/of.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-)
drivers/opp/core.c +4 -2
@@ -1088,10 +1088,12 @@ static int _opp_set_required_opps_generic(struct device *dev,static int _opp_set_required_opps_genpd (struct device *dev,struct opp_table *opp_table,struct dev_pm_opp *opp,bool scaling_down) { -struct device **genpd_virt_devs = - opp_table->genpd_virt_devs ? opp_table->genpd_virt_devs : &dev; +struct device **genpd_virt_devs = opp_table->genpd_virt_devs;int index, target, delta, ret; +if (!genpd_virt_devs) +return 0 ; +/* Scaling up? Set required OPPs in normal order, else reverse */ if (!scaling_down) { index =0 ;
drivers/opp/of.c +28 -3
@@ -296,7 +296,7 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)of_node_put (opp->np); } -static int _link_required_opps (struct dev_pm_opp *opp, +static int _link_required_opps (struct dev_pm_opp *opp,struct opp_table *opp_table,struct opp_table *required_table,int index) {struct device_node *np;
@@ -314,6 +314,31 @@ static int _link_required_opps(struct dev_pm_opp *opp,return -ENODEV ; } +/* +* There are two genpd (as required-opp) cases that we need to handle, +* devices with a single genpd and ones with multiple genpds. +* +* The single genpd case requires special handling as we need to use the +* same `dev` structure (instead of a virtual one provided by genpd +* core) for setting the performance state. +* +* It doesn't make sense for a device's DT entry to have both +* "opp-level" and single "required-opps" entry pointing to a genpd's +* OPP, as that would make the OPP core call +* dev_pm_domain_set_performance_state() for two different values for +* the same device structure. Lets treat single genpd configuration as a +* case where the OPP's level is directly available without required-opp +* link in the DT. +* +* Just update the `level` with the right value, which +* dev_pm_opp_set_opp() will take care of in the normal path itself. +*/ +if (required_table->is_genpd && opp_table->required_opp_count ==1 && + !opp_table->genpd_virt_devs) { +if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) + opp->level = opp->required_opps[0 ]->level; + } +return 0 ; }
@@ -338,7 +363,7 @@ static int _of_opp_alloc_required_opps(struct opp_table *opp_table,if (IS_ERR_OR_NULL (required_table))continue ; - ret =_link_required_opps (opp, required_table, i); + ret =_link_required_opps (opp, opp_table, required_table, i);if (ret) goto free_required_opps; }
@@ -359,7 +384,7 @@ static int lazy_link_required_opps(struct opp_table *opp_table,int ret;list_for_each_entry (opp, &opp_table->opp_list, node) { - ret =_link_required_opps (opp, new_table, index); + ret =_link_required_opps (opp, opp_table, new_table, index);if (ret)return ret; }
Signature · 1 line
2.31.1.272.g89b43f80a514
PATCH v3 2/3 OPP: Call dev_pm_opp_set_opp() for required OPPs
In reply to Viresh KumarConfiguring the required OPP was never properly implemented, we just took an exception for genpds and configured them directly, while leaving out all other required OPP types.
Now that a standard call to dev_pm_opp_set_opp() takes care of configuring the opp->level too, the special handling for genpds can be avoided by simply calling dev_pm_opp_set_opp() for the required OPPs, which shall eventually configure the corresponding level for genpds.
This also makes it possible for us to configure other type of required OPPs (no concrete users yet though), via the same path. This is how other frameworks take care of parent nodes, like clock, regulators, etc, where we recursively call the same helper.
In order to call dev_pm_opp_set_opp() for the virtual genpd devices, they must share the OPP table of the genpd. Call _add_opp_dev() for them to get that done.
This commit also extends the struct dev_pm_opp_config to pass required devices, for non-genpd cases, which can be used to call dev_pm_opp_set_opp() for the non-genpd required devices.
--- drivers/opp/core.c | 168 ++++++++++++++++++++--------------------- drivers/opp/of.c | 17 +++-- drivers/opp/opp.h | 8 +- include/linux/pm_opp.h | 7 +- 4 files changed, 100 insertions(+), 100 deletions(-)
drivers/opp/core.c +82 -86
@@ -1054,48 +1054,22 @@ static int _set_opp_bw(const struct opp_table *opp_table,return 0 ; } -static int _set_performance_state (struct device *dev,struct device *pd_dev, -struct dev_pm_opp *opp,int i) -{ -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" , -dev_name (pd_dev), pstate, ret); - } - -return ret; -} - -static int _opp_set_required_opps_generic (struct device *dev, -struct opp_table *opp_table,struct dev_pm_opp *opp,bool scaling_down) -{ -dev_err (dev,"setting required-opps isn't supported for non-genpd devices\n" ); -return -ENOENT ; -} - -static int _opp_set_required_opps_genpd (struct device *dev, -struct opp_table *opp_table,struct dev_pm_opp *opp,bool scaling_down) +/* This is only called for PM domain for now */ +static int _set_required_opps (struct device *dev,struct opp_table *opp_table, +struct dev_pm_opp *opp,bool up) { -struct device **genpd_virt_devs = opp_table->genpd_virt_devs; +struct device **devs = opp_table->required_devs;int index, target, delta, ret; -if (!genpd_virt_devs) +if (!devs)return 0 ; +/* required-opps not fully initialized yet */ +if (lazy_linking_pending (opp_table)) +return -EBUSY ; +/* Scaling up? Set required OPPs in normal order, else reverse */ -if (!scaling_down) { +if (up) { index =0 ; target = opp_table->required_opp_count; delta =1 ;
@@ -1106,9 +1080,11 @@ static int _opp_set_required_opps_genpd(struct device *dev, }while (index != target) { - ret =_set_performance_state (dev, genpd_virt_devs[index], opp, index); -if (ret) -return ret; +if (devs[index]) { + ret =dev_pm_opp_set_opp (devs[index], opp->required_opps[index]); +if (ret) +return ret; + } index += delta; }
@@ -1116,34 +1092,6 @@ static int _opp_set_required_opps_genpd(struct device *dev,return 0 ; } -/* This is only called for PM domain for now */ -static int _set_required_opps (struct device *dev,struct opp_table *opp_table, -struct dev_pm_opp *opp,bool up) -{ -/* required-opps not fully initialized yet */ -if (lazy_linking_pending (opp_table)) -return -EBUSY ; - -if (opp_table->set_required_opps) -return opp_table->set_required_opps (dev, opp_table, opp, up); - -return 0 ; -} - -/* Update set_required_opps handler */ -void _update_set_required_opps (struct opp_table *opp_table) -{ -/* Already set */ -if (opp_table->set_required_opps) -return ; - -/* All required OPPs will belong to genpd or none */ -if (opp_table->required_opp_tables[0 ]->is_genpd) - opp_table->set_required_opps = _opp_set_required_opps_genpd; -else - opp_table->set_required_opps = _opp_set_required_opps_generic; -} -static int _set_opp_level (struct device *dev,struct opp_table *opp_table,struct dev_pm_opp *opp) {
@@ -2406,19 +2354,13 @@ static void _opp_detach_genpd(struct opp_table *opp_table) {int index; -if (!opp_table->genpd_virt_devs) -return ; -for (index =0 ; index < opp_table->required_opp_count; index++) { -if (!opp_table->genpd_virt_devs[index]) +if (!opp_table->required_devs[index])continue ; -dev_pm_domain_detach (opp_table->genpd_virt_devs[index], false); - opp_table->genpd_virt_devs[index] =NULL ; +dev_pm_domain_detach (opp_table->required_devs[index], false); + opp_table->required_devs[index] =NULL ; } - -kfree (opp_table->genpd_virt_devs); - opp_table->genpd_virt_devs =NULL ; }/*
@@ -2445,14 +2387,14 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,int index =0 , ret = -EINVAL ;const char *const *name = names; -if (opp_table->genpd_virt_devs) -return 0 ; +if (!opp_table->required_devs) { +dev_err (dev,"Required OPPs not available, can't attach genpd\n" ); +return -EINVAL ; + } - opp_table->genpd_virt_devs =kcalloc (opp_table->required_opp_count, -sizeof (*opp_table->genpd_virt_devs), -GFP_KERNEL ); -if (!opp_table->genpd_virt_devs) -return -ENOMEM ; +/* Checking only the first one is enough ? */ +if (opp_table->required_devs[0 ]) +return 0 ;while (*name) {if (index >= opp_table->required_opp_count) {
@@ -2468,13 +2410,25 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, goto err; } - opp_table->genpd_virt_devs[index] = virt_dev; +/* +* Add the virtual genpd device as a user of the OPP table, so +* we can call dev_pm_opp_set_opp() on it directly. +* +* This will be automatically removed when the OPP table is +* removed, don't need to handle that here. +*/ +if (!_add_opp_dev (virt_dev, opp_table->required_opp_tables[index])) { + ret = -ENOMEM ; + goto err; + } + + opp_table->required_devs[index] = virt_dev; index++; name++; }if (virt_devs) - *virt_devs = opp_table->genpd_virt_devs; + *virt_devs = opp_table->required_devs;return 0 ;
@@ -2484,10 +2438,42 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev, } +static int _opp_set_required_devs (struct opp_table *opp_table, +struct device *dev, +struct device **required_devs) +{ +int i; + +if (!opp_table->required_devs) { +dev_err (dev,"Required OPPs not available, can't set required devs\n" ); +return -EINVAL ; + } + +/* Another device that shares the OPP table has set the required devs ? */ +if (opp_table->required_devs[0 ]) +return 0 ; + +for (i =0 ; i < opp_table->required_opp_count; i++) + opp_table->required_devs[i] = required_devs[i]; + +return 0 ; +} + +static void _opp_put_required_devs (struct opp_table *opp_table) +{ +int i; + +for (i =0 ; i < opp_table->required_opp_count; i++) + opp_table->required_devs[i] =NULL ; +} +static void _opp_clear_config (struct opp_config_data *data) { -if (data->flags &OPP_CONFIG_GENPD ) +if (data->flags &OPP_CONFIG_REQUIRED_DEVS ) +_opp_put_required_devs (data->opp_table); +else if (data->flags &OPP_CONFIG_GENPD )_opp_detach_genpd (data->opp_table); +if (data->flags &OPP_CONFIG_REGULATOR )_opp_put_regulators (data->opp_table);if (data->flags &OPP_CONFIG_SUPPORTED_HW )
@@ -2601,12 +2587,22 @@ int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)/* Attach genpds */ if (config->genpd_names) { +if (config->required_devs) + goto err; + ret =_opp_attach_genpd (opp_table, dev, config->genpd_names, config->virt_devs);if (ret) goto err; data->flags |=OPP_CONFIG_GENPD ; + }else if (config->required_devs) { + ret =_opp_set_required_devs (opp_table, dev, + config->required_devs); +if (ret) + goto err; + + data->flags |=OPP_CONFIG_REQUIRED_DEVS ; } ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX),
drivers/opp/of.c +10 -7
@@ -165,7 +165,7 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,struct opp_table **required_opp_tables;struct device_node *required_np, *np;bool lazy = false; -int count, i; +int count, i, size;/* Traversing the first OPP node is all we need */ np =of_get_next_available_child (opp_np,NULL );
@@ -179,12 +179,13 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,if (count <=0 ) goto put_np; - required_opp_tables =kcalloc (count,sizeof (*required_opp_tables), -GFP_KERNEL ); + size =sizeof (*required_opp_tables) +sizeof (*opp_table->required_devs); + required_opp_tables =kcalloc (count, size,GFP_KERNEL );if (!required_opp_tables) goto put_np; opp_table->required_opp_tables = required_opp_tables; + opp_table->required_devs = (void *)(required_opp_tables + count); opp_table->required_opp_count = count;for (i =0 ; i < count; i++) {
@@ -208,8 +209,6 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,mutex_lock (&opp_table_lock);list_add (&opp_table->lazy, &lazy_opp_tables);mutex_unlock (&opp_table_lock); - }else { -_update_set_required_opps (opp_table); } goto put_np;
@@ -332,9 +331,14 @@ static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_tab* * Just update the `level` with the right value, which * dev_pm_opp_set_opp() will take care of in the normal path itself. +* +* There is another case though, where a genpd's OPP table has +* required-opps set to a parent genpd. The OPP core expects the user to +* set the respective required `struct device` pointer via +* dev_pm_opp_set_config(). */ if (required_table->is_genpd && opp_table->required_opp_count ==1 && - !opp_table->genpd_virt_devs) { + !opp_table->required_devs[0 ]) {if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) opp->level = opp->required_opps[0 ]->level; }
@@ -447,7 +451,6 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)/* All required opp-tables found, remove from lazy list */ if (!lazy) { -_update_set_required_opps (opp_table);list_del_init (&opp_table->lazy);list_for_each_entry (opp, &opp_table->opp_list, node)
drivers/opp/opp.h +3 -5
@@ -35,6 +35,7 @@ extern struct list_head opp_tables;#define OPP_CONFIG_PROP_NAME BIT(3)#define OPP_CONFIG_SUPPORTED_HW BIT(4)#define OPP_CONFIG_GENPD BIT(5) +#define OPP_CONFIG_REQUIRED_DEVS BIT(6)/** * struct opp_config_data - data for set config operations
@@ -160,9 +161,9 @@ enum opp_table_access {* @rate_clk_single: Currently configured frequency for single clk. * @current_opp: Currently configured OPP for the table. * @suspend_opp: Pointer to OPP to be used during device suspend. -* @genpd_virt_devs: List of virtual devices for multiple genpd support. * @required_opp_tables: List of device OPP tables that are required by OPPs in * this table. +* @required_devs: List of devices for required OPP tables. * @required_opp_count: Number of required devices. * @supported_hw: Array of version number to support. * @supported_hw_count: Number of elements in supported_hw array.
@@ -180,7 +181,6 @@ enum opp_table_access {* @path_count: Number of interconnect paths * @enabled: Set to true if the device's resources are enabled/configured. * @is_genpd: Marks if the OPP table belongs to a genpd. -* @set_required_opps: Helper responsible to set required OPPs. * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. *
@@ -211,8 +211,8 @@ struct opp_table {struct dev_pm_opp *current_opp;struct dev_pm_opp *suspend_opp; -struct device **genpd_virt_devs;struct opp_table **required_opp_tables; +struct device **required_devs;unsigned int required_opp_count;unsigned int *supported_hw;
@@ -229,8 +229,6 @@ struct opp_table {unsigned int path_count;bool enabled;bool is_genpd; -int (*set_required_opps)(struct device *dev, -struct opp_table *opp_table,struct dev_pm_opp *opp,bool scaling_down);#ifdef CONFIG_DEBUG_FS struct dentry *dentry;
include/linux/pm_opp.h +5 -2
@@ -74,8 +74,10 @@ typedef int (*config_clks_t)(struct device *dev, struct opp_table *opp_table,* @supported_hw_count: Number of elements in the array. * @regulator_names: Array of pointers to the names of the regulator, NULL terminated. * @genpd_names: Null terminated array of pointers containing names of genpd to -* attach. -* @virt_devs: Pointer to return the array of virtual devices. +* attach. Mutually exclusive with required_devs. +* @virt_devs: Pointer to return the array of genpd virtual devices. Mutually +* exclusive with required_devs. +* @required_devs: Required OPP devices. Mutually exclusive with genpd_names/virt_devs. * * This structure contains platform specific OPP configurations for the device. */
@@ -90,6 +92,7 @@ struct dev_pm_opp_config {const char *const *regulator_names;const char *const *genpd_names;struct device ***virt_devs; +struct device **required_devs; };#define OPP_LEVEL_UNSET U32_MAX
Signature · 1 line
2.31.1.272.g89b43f80a514
PATCH v3 3/3 OPP: Don't set OPP recursively for a parent genpd
In reply to Viresh KumarLike other frameworks (clk, regulator, etc.) genpd core too takes care of propagation to performance state to parent genpds. The OPP core shouldn't attempt the same, or it may result in undefined behavior.
Add checks at various places to take care of the same.
--- drivers/opp/core.c | 16 +++++++++++++++- drivers/opp/of.c | 7 +++++-- 2 files changed, 20 insertions(+), 3 deletions(-)
drivers/opp/core.c +15 -1
@@ -2392,6 +2392,12 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,return -EINVAL ; } +/* Genpd core takes care of propagation to parent genpd */ +if (opp_table->is_genpd) { +dev_err (dev,"%s: Operation not supported for genpds\n" , __func__); +return -EOPNOTSUPP ; + } +/* Checking only the first one is enough ? */ if (opp_table->required_devs[0 ])return 0 ;
@@ -2453,8 +2459,16 @@ static int _opp_set_required_devs(struct opp_table *opp_table,if (opp_table->required_devs[0 ])return 0 ; -for (i =0 ; i < opp_table->required_opp_count; i++) +for (i =0 ; i < opp_table->required_opp_count; i++) { +/* Genpd core takes care of propagation to parent genpd */ +if (required_devs[i] && opp_table->is_genpd && + opp_table->required_opp_tables[i]->is_genpd) { +dev_err (dev,"%s: Operation not supported for genpds\n" , __func__); +return -EOPNOTSUPP ; + } + opp_table->required_devs[i] = required_devs[i]; + }return 0 ; }
drivers/opp/of.c +5 -2
@@ -339,8 +339,11 @@ static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_tab*/ if (required_table->is_genpd && opp_table->required_opp_count ==1 && !opp_table->required_devs[0 ]) { -if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) - opp->level = opp->required_opps[0 ]->level; +/* Genpd core takes care of propagation to parent genpd */ +if (!opp_table->is_genpd) { +if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) + opp->level = opp->required_opps[0 ]->level; + } }return 0 ;
Signature · 1 line
2.31.1.272.g89b43f80a514
Re: [PATCH V3 3/3] OPP: Don't set OPP recursively for a parent genpd
Quoted 8 lines · Viresh Kumar
Like other frameworks (clk, regulator, etc.) genpd core too takes care of propagation to performance state to parent genpds. The OPP core shouldn't attempt the same, or it may result in undefined behavior.
Add checks at various places to take care of the same.
Signed-off-by Viresh Kumar <[email protected]>
I think this looks good to me, even if I think it's a bit tricky to consider the various scenarios. :-) Feel free to add:
Kind regards
Uffe
Quoted 61 lines
--- drivers/opp/core.c | 16 +++++++++++++++- drivers/opp/of.c | 7 +++++-- 2 files changed, 20 insertions(+), 3 deletions(-)drivers/opp/core.c +15
-1@@ -2392,6 +2392,12 @@ static int _opp_attach_genpd(struct opp_table *opp_table, struct device *dev,return -EINVAL ; } +/* Genpd core takes care of propagation to parent genpd */ +if (opp_table->is_genpd) { +dev_err (dev,"%s: Operation not supported for genpds\n" , __func__); +return -EOPNOTSUPP ; + } +/* Checking only the first one is enough ? */ if (opp_table->required_devs[0 ])return 0 ;@@ -2453,8 +2459,16 @@ static int _opp_set_required_devs(struct opp_table *opp_table,if (opp_table->required_devs[0 ])return 0 ; -for (i =0 ; i < opp_table->required_opp_count; i++) +for (i =0 ; i < opp_table->required_opp_count; i++) { +/* Genpd core takes care of propagation to parent genpd */ +if (required_devs[i] && opp_table->is_genpd && + opp_table->required_opp_tables[i]->is_genpd) { +dev_err (dev,"%s: Operation not supported for genpds\n" , __func__); +return -EOPNOTSUPP ; + } + opp_table->required_devs[i] = required_devs[i]; + }return 0 ; }drivers/opp/of.c +5
-3@@ -339,8 +339,11 @@ static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *opp_tab*/ if (required_table->is_genpd && opp_table->required_opp_count ==1 && !opp_table->required_devs[0 ]) { -if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) - opp->level = opp->required_opps[0 ]->level; +/* Genpd core takes care of propagation to parent genpd */ +if (!opp_table->is_genpd) { +if (!WARN_ON (opp->level !=OPP_LEVEL_UNSET )) + opp->level = opp->required_opps[0 ]->level; + } }return 0 ; --2.31.1.272.g89b43f80a514
Quoted 9 lines · Viresh Kumar
Configuring the required OPP was never properly implemented, we just took an exception for genpds and configured them directly, while leaving out all other required OPP types.
Now that a standard call to
dev_pm_opp_set_opp()takes care of configuring theopp->leveltoo, the special handling for genpds can be avoided by simply callingdev_pm_opp_set_opp()for the required OPPs, which shall eventually configure the corresponding level for genpds.
Quoted 9 lines
This also makes it possible for us to configure other type of required OPPs (no concrete users yet though), via the same path. This is how other frameworks take care of parent nodes, like clock, regulators, etc, where we recursively call the same helper.
Pushed here:
git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.gitopp/required-opps
Sorry for the delay. I tested the "opp/linux-next" branch (which seems to contain the changes in this series already now) with the following configurations:
- Single genpd used for cpufreq (MSM8909): Works
- Multiple genpd used for cpufreq (MSM8916): Works
- Single genpd used for cpufreq + parent genpd (MSM8916): Works, warning gone
Thanks for fixing this! :-)
I guess I'm too late now but FWIW:
Quoted 27 lines
V2->V3:
- Dropped patch 1/3, merged.
- Added a new commit to avoid propagation and aWARN()for parent genpd case.
V1->V2:
- Support opp-level 0, drop vote i.e..
- Fix OPP pointer while callingdev_pm_opp_set_opp()recursively.
- Minor checks and fixes.
- Add Reviewed-by from Ulf.--
VireshViresh Kumar (3):
OPP: Use_set_opp_level()for single genpd case
OPP: Calldev_pm_opp_set_opp()for required OPPs
OPP: Don't set OPP recursively for a parent genpddrivers/opp/core.c | 180 ++++++++++++++++++++++------------------- drivers/opp/of.c | 49 ++++++++--- drivers/opp/opp.h | 8 +- include/linux/pm_opp.h | 7 +- 4 files changed, 144 insertions(+), 100 deletions(-)Signature · 1 line
2.31.1.272.g89b43f80a514
Signature · 2 lines
Stephan Gerhold <[email protected]> Kernkonzept GmbH at Dresden, Germany, HRB 31129, CEO Dr.-Ing. Michael Hohmuth
Quoted 9 lines · Stephan Gerhold
Sorry for the delay. I tested the "opp/linux-next" branch (which seems to contain the changes in this series already now) with the following configurations:
- Single genpd used for cpufreq (MSM8909): Works
- Multiple genpd used for cpufreq (MSM8916): Works
- Single genpd used for cpufreq + parent genpd (MSM8916): Works, warning goneThanks for fixing this! :-)
Thanks a lot.
Quoted 3 lines
I guess I'm too late now but FWIW:
Tested-by Stephan Gerhold <[email protected]>
Updated the commits with your tag :)
Signature · 1 line
viresh