PM / Domains: Performance state support
Subsystems (13)
v11 cover PM / Domains: Performance state support
Hi,
This version contains the changes we discussed during Linaro Connect.
Some platforms have the capability to configure the performance state of their power domains. The process of configuring the performance state is pretty much platform dependent and we may need to work with a wide range of configurables. For some platforms, like Qcom, it can be a positive integer value alone, while in other cases it can be voltage levels, etc.
The power-domain framework until now was only designed for the idle state management of the device and this needs to change in order to reuse the power-domain framework for active state management of the devices.
The first patch updates the genpd framework to supply new APIs to support active state management and the second patch uses them from the OPP core. The third patch adds a new API to the OPP core to get performance state corresponding to OPPs (This should rather come via DT and would be removed once we have fixed bindings for performance states).
Rest of the patches [4-7/7] are included to show how user drivers would end up using the new APIs and these patches aren't ready to get merged yet and are marked clearly like that. Moreover some of them may go via SoC specific trees instead of the PM tree.
This is currently tested by:
- /me by hacking the kernel a bit with virtual power-domains for the ARM 64 hikey platform.
- Rajendra Nayak, on msm8996 platform (Qcom) with MMC controller.
Thanks Rajendra for helping me testing this out.
I also had a chat with Rajendra and we should be able to get a Qualcomm specific power domain driver (which uses these changes) in coming weeks.
I am targeting the first 3 patches for 4.15-rc1, if possible.
Pushed here as well:
https://git.linaro.org/people/viresh.kumar/linux.git/log/?h=opp/genpd-performance-state
Rebased over: pm/linux-next
V10->V11:
- Dropped propagation to master domains of a subdomain, as we don't have such requirements yet and we may not have 1 to 1 relation between the states of subdomains and masters.
- Updated genpd API to get performance state directly instead of frequency. The conversion of frequency to performance state is now done by OPP core instead, which is more logical.
- Only the mmc patch is kept and other user patches are dropped, anyway none of those is getting merged right now.
- Updates to the rpm power domain driver and a new OPP driver is added as well.
V9->V10:
- Performance state of masters is updated before the state of the genpd (Ulf).
- 2/7 and 3/7 are swapped.
V8->V9:
- Renamed genpd callbacks and internal routines.
- dev_pm_genpd_has_performance_state() simplified a lot and doesn't check master hierarchy now. Rather a new patch (2/7) is added to take care of that and WARN if no master has set genpd_set_performance_state() callback.
- Update is propagated to the masters even if the genpd's callback is already called.
- Exit _genpd_reeval_performance_state() early if no state change is required and it gets an additional argument (new state of the device/subdomain).
- Taken care of genpd on/off cases.
- s/parent/master everywhere in comments and logs.
- Better explanations in logs, comments etc.
- All the other patches (3-7/7) are same as V8. (Just minor update in 5/7 to use the updated callback names).
V7->V8:
- Ulf helped a lot in reviewing V7 and pointed out couple of issues, specially in locking while dealing with a hierarchy of power domains.
- All those locking issues are sorted out now, even for the complex cases.
- genpd_lookup_dev() is used in pm_genpd_has_performance_state() to make sure we have a valid genpd available for the device.
- Validation of performance state callbacks isn't done anymore in pm_genpd_init() as it gets called very early and the binding of subdomains to their parent domains happens later. This is handled in pm_genpd_has_performance_state() now, which is called from user drivers.
- User driver changes (not to be merged) are included for the first time here, to demonstrate how changes would look finally.
V6->V7:
- Almost a rewrite, only two patches against 9 in earlier version.
- No bindings updated now and domain's performance state aren't passed via DT for now (until we know how users are going to use it).
- We also skipped the QoS framework completely and new APIs are provided directly by genpd.
V5->V6:
- Use freq/voltage in OPP table as it is for power domain and don't create "domain-performance-level" property
- Create new "power-domain-opp" property for the devices.
- Take care of domain providers that provide multiple domains and extend "operating-points-v2" property to contain a list of phandles
- Update code according to those bindings.
V4->V5:
- Only 3 patches were resent and 2 of them are Acked from Ulf.
V3->V4:
- Use OPP table for genpd devices as well.
- Add struct device to genpd, in order to reuse OPP infrastructure.
- Based over: https://marc.info/?l=linux-kernel&m=148972988002317&w=2
- Fixed examples in DT document to have voltage in target,min,max order.
V2->V3:
- Based over latest pm/linux-next
- Bindings and code are merged together
- Lots of updates in bindings
- the performance-states node is present within the power-domain now, instead of its phandle.
- performance-level property is replaced by "reg".
- domain-performance-state property of the consumers contain an integer value now instead of phandle.
- Lots of updates to the code as well
- Patch "PM / QOS: Add default case to the switch" is merged with other patches and the code is changed a bit as well.
- Don't pass 'type' to dev_pm_qos_add_notifier(), rather handle all notifiers with a single list. A new patch is added for that.
- The OPP framework patch can be applied now and has proper SoB from
me.
- Dropped "PM / domain: Save/restore performance state at runtime suspend/resume".
- Drop all WARN().
- Tested-by Rajendra nayak.
V1->V2:
- Based over latest pm/linux-next
- It is mostly a resend of what is sent earlier as this series hasn't got any reviews so far and Rafael suggested that its better I resend it.
- Only the 4/6 patch got an update, which was shared earlier as reply to V1 as well. It has got several fixes for taking care of power domain hierarchy, etc.
--
viresh
Rajendra Nayak (3):
soc: qcom: rpmpd: Add driver to model cx/mx power domains
soc: qcom: rpmpd: Add support for set performance state
mmc: sdhci-msm: Adapt the driver to use OPPs to set clocks/performance
state
Viresh Kumar (4):
PM / Domains: Add support to select performance-state of domains
OPP: Support updating performance state of device's power domain
OPP: Add dev_pm_opp_{un}register_get_pstate_helper()
OPP: qcom: Add support to get performance states corresponding to OPPs
.../devicetree/bindings/power/qcom,rpmpd.txt | 9 + arch/arm64/boot/dts/qcom/msm8996.dtsi | 39 +++ drivers/base/power/domain.c | 179 ++++++++++- drivers/clk/qcom/gcc-msm8996.c | 8 +- drivers/mmc/host/sdhci-msm.c | 39 ++- drivers/opp/Makefile | 2 + drivers/opp/core.c | 135 ++++++++- drivers/opp/debugfs.c | 3 + drivers/opp/opp.h | 6 + drivers/opp/qcom-rpmpd.c | 107 +++++++ drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rpmpd.c | 330 +++++++++++++++++++++ include/linux/pm_domain.h | 13 + include/linux/pm_opp.h | 10 + 15 files changed, 873 insertions(+), 17 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmpd.txt create mode 100644 drivers/opp/qcom-rpmpd.c create mode 100644 drivers/soc/qcom/rpmpd.c
Signature · 1 line
2.7.4
PATCH v11 1/7 PM / Domains: Add support to select performance-state of domains
Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);
This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the genpd->genpd_set_performance_state() callback, if implemented, else -ENODEV is returned to the caller.
The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*genpd_set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state);
This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd in a platform dependent way.
The power domains can avoid supplying above callback, if they don't support setting performance-states.
A TODO comment is also added to _genpd_reeval_performance_state(). This feature will be required once we have hardware that needs to propagate the performance state changes to master domains.
--- drivers/base/power/domain.c | 179 +++++++++++++++++++++++++++++++++++++++++++- include/linux/pm_domain.h | 13 ++++ 2 files changed, 190 insertions(+), 2 deletions(-)
drivers/base/power/domain.c +177 -2
@@ -237,6 +237,169 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/* Returns negative errors or 0 on success */ +static int _genpd_set_performance_state (struct generic_pm_domain *genpd, +int state) +{ +int ret; + + ret = genpd->genpd_set_performance_state (genpd, state); +if (!ret) + genpd->performance_state = state; + +return ret; +} + +/* +* Re-evaluate performance state of a genpd. Finds the highest requested +* performance state by the devices within the genpd and then change genpd's +* performance state (if required). +* +* @genpd: PM domain whose state needs to be reevaluated. +* @state: Newly requested performance state of the device for which this +* routine is called. +* +* Returns negative errors or 0 on success. +*/ +static int _genpd_reeval_performance_state (struct generic_pm_domain *genpd, +int state) +{ +struct generic_pm_domain_data *pd_data; +struct pm_domain_data *pdd; + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) +return 0 ; + +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd); + +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +/* +* TODO: Traverse all subdomains of the genpd. This will be +* required once we have hardware that needs to propagate the +* performance state changes. +*/ + +update_state: +return _genpd_set_performance_state (genpd, state); +} + +static void __genpd_dev_update_performance_state (struct device *dev,int state) +{ +struct generic_pm_domain_data *gpd_data; + +spin_lock_irq (&dev->power.lock); + +if (!dev->power.subsys_data || !dev->power.subsys_data->domain_data) { +WARN_ON (1 ); + goto unlock; + } + + gpd_data =to_gpd_data (dev->power.subsys_data->domain_data); + gpd_data->performance_state = state; + +unlock: +spin_unlock_irq (&dev->power.lock); +} + +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be adjusted. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the user driver guarantees that the genpd wouldn't be +* detached while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{ +struct generic_pm_domain *genpd; +int ret; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +genpd_lock (genpd); + +if (!genpd->genpd_set_performance_state) { + ret = -ENODEV ; + goto unlock; + } + +if (!genpd_status_on (genpd)) { + ret = -EBUSY ; + goto unlock; + } + + ret =_genpd_reeval_performance_state (genpd, state); +if (!ret) { +/* +* Since we are passing "state" as well to +* _genpd_reeval_performance_state(), we don't need to call +* __genpd_dev_update_performance_state() before updating +* genpd's state with the above call. Update it only after the +* state of master domain is updated. +*/ +__genpd_dev_update_performance_state (dev, state); + } + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); + +static int _genpd_on_update_performance_state (struct generic_pm_domain *genpd) +{ +int ret, prev = genpd->prev_performance_state; + +if (likely (!prev)) +return 0 ; + + ret =_genpd_set_performance_state (genpd, prev); +if (ret) { +pr_err ("%s: Failed to restore performance state to %d (%d)\n" , + genpd->name, prev, ret); + }else { + genpd->prev_performance_state =0 ; + } + +return ret; +} + +static void _genpd_off_update_performance_state (struct generic_pm_domain *genpd) +{ +int ret, state = genpd->performance_state; + +if (likely (!state)) +return ; + + ret =_genpd_set_performance_state (genpd,0 ); +if (ret) { +pr_err ("%s: Failed to set performance state to 0 (%d)\n" , + genpd->name, ret); + }else { + genpd->prev_performance_state = state; + } +} +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;
@@ -388,6 +551,8 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,return ret; } +_genpd_off_update_performance_state (genpd); + genpd->status =GPD_STATE_POWER_OFF ;genpd_update_accounting (genpd);
@@ -437,15 +602,21 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) } } - ret =_genpd_power_on (genpd, true); + ret =_genpd_on_update_performance_state (genpd);if (ret) goto err; + ret =_genpd_power_on (genpd, true); +if (ret) + goto err_power_on; + genpd->status =GPD_STATE_ACTIVE ;genpd_update_accounting (genpd);return 0 ; + err_power_on: +_genpd_off_update_performance_state (genpd); err:list_for_each_entry_continue_reverse (link, &genpd->slave_links,
@@ -803,6 +974,8 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,if (_genpd_power_off (genpd, false))return ; +_genpd_off_update_performance_state (genpd); + genpd->status =GPD_STATE_POWER_OFF ;list_for_each_entry (link, &genpd->slave_links, slave_node) {
@@ -848,7 +1021,9 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,genpd_unlock (link->master); } -_genpd_power_on (genpd, false); +if (!_genpd_on_update_performance_state (genpd)) +if (_genpd_power_on (genpd, false)) +_genpd_off_update_performance_state (genpd); genpd->status =GPD_STATE_ACTIVE ; }
include/linux/pm_domain.h +13 -0
@@ -64,8 +64,12 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Max requested performance state */ +unsigned int prev_performance_state;/* Performance state before power off */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*genpd_set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;
@@ -121,6 +125,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };
@@ -148,6 +153,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;
@@ -188,6 +195,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif
Signature · 1 line
2.7.4
Re: [PATCH V11 1/7] PM / Domains: Add support to select performance-state of domains
Quoted 32 lines · Viresh Kumar
Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the
genpd->genpd_set_performance_state()callback, if implemented, else-ENODEVis returned to the caller.The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*genpd_set_performance_state)(struct
generic_pm_domain*genpd,unsigned int state);This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd in a platform dependent way.
Quoted 6 lines
The power domains can avoid supplying above callback, if they don't support setting performance-states.
A TODO comment is also added to
_genpd_reeval_performance_state(). This feature will be required once we have hardware that needs to propagate the performance state changes to master domains.
Perhaps re-phrase this to being about "a limitation that can be fixed later if needed" rather than an TODO. That applies also to the comment in the code.
Quoted 13 lines
Tested-by Rajendra Nayak <[email protected]>Signed-off-by Viresh Kumar <[email protected]>--- drivers/base/power/domain.c | 179 +++++++++++++++++++++++++++++++++++++++++++- include/linux/pm_domain.h | 13 ++++ 2 files changed, 190 insertions(+), 2 deletions(-)drivers/base/power/domain.c +0
-0
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 242
Quoted 6 lines
drivers/base/power/domain.c +3
-0@@ -237,6 +237,169 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/* Returns negative errors or 0 on success */ +static int _genpd_set_performance_state (struct generic_pm_domain *genpd, +int state)
Please rename the function to: genpd_set_performance_state().
Quoted 11 lines
drivers/base/power/domain.c +11
-0+{ +int ret; + + ret = genpd->genpd_set_performance_state (genpd, state); +if (!ret) + genpd->performance_state = state; + +return ret; +} + +/*
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 263
Quoted 10 lines
drivers/base/power/domain.c +10
-0+* Re-evaluate performance state of a genpd. Finds the highest requested +* performance state by the devices within the genpd and then change genpd's +* performance state (if required). +* +* @genpd: PM domain whose state needs to be reevaluated. +* @state: Newly requested performance state of the device for which this +* routine is called. +* +* Returns negative errors or 0 on success. +*/
This isn't an exported function, so you may slim down this information quite a bit.
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 265
Quoted 2 lines
drivers/base/power/domain.c +2
-0+static int _genpd_reeval_performance_state (struct generic_pm_domain *genpd, +int state)
By looking how this function is used, I suggest you to rename it to genpd_update_performance_state() and also fold in the code from the __genpd_dev_update_performance_state(). I think that would simplify the code a bit.
Quoted 15 lines
drivers/base/power/domain.c +15
-0+{ +struct generic_pm_domain_data *pd_data; +struct pm_domain_data *pdd; + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) +return 0 ; + +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd);
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 290
Quoted 10 lines
drivers/base/power/domain.c +10
-0+ +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +/* +* TODO: Traverse all subdomains of the genpd. This will be +* required once we have hardware that needs to propagate the +* performance state changes. +*/
As mentioned, please re-phrase this to be about a limitation rather than a TODO.
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 300
Quoted 10 lines
drivers/base/power/domain.c +10
-0+ +update_state: +return _genpd_set_performance_state (genpd, state); +} + +static void __genpd_dev_update_performance_state (struct device *dev,int state) +{ +struct generic_pm_domain_data *gpd_data; + +spin_lock_irq (&dev->power.lock);
I remember we already discussed this lock, but I did second thought around this. Apologize for coming back to this again.
As you state in the function header of dev_pm_genpd_set_performance_state(), users of this function must guarantee that the device don't get detached from its genpd while calling it. For that reason, the ->domain_data pointer will always be valid when __genpd_dev_update_performance_state() is executed. In other words, there's no reason to use the lock to protect access to it.
Quoted 30 lines
drivers/base/power/domain.c +30
-0+ +if (!dev->power.subsys_data || !dev->power.subsys_data->domain_data) { +WARN_ON (1 ); + goto unlock; + } + + gpd_data =to_gpd_data (dev->power.subsys_data->domain_data); + gpd_data->performance_state = state; + +unlock: +spin_unlock_irq (&dev->power.lock); +} + +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be adjusted. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the user driver guarantees that the genpd wouldn't be +* detached while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 340
Quoted 10 lines
drivers/base/power/domain.c +10
-0+struct generic_pm_domain *genpd; +int ret; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +genpd_lock (genpd); + +if (!genpd->genpd_set_performance_state) {
Because we expect the ->genpd_set_performance_state() to be assigned once and at initialization (before pm_genpd_init() is called), you can move this check outside the genpd lock.
If the reason for the lock, is to avoid the genpd from being removed!? Then, that is also managed by stating that users of dev_pm_genpd_set_performance_state() must guarantee the device won't get detached from its genpd when calling this function.
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 346
Quoted 6 lines
drivers/base/power/domain.c +6
-0+ ret = -ENODEV ; + goto unlock; + } + +if (!genpd_status_on (genpd)) { + ret = -EBUSY ;
To me, it seems feasible to allow users to request a new performance state, no matter of whether the PM domain is powered on or off.
In case the genpd is off, we should only compute a new aggregated performance state value, but not call ->genpd_set_performance_state(). More comments about that below.
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 358
Quoted 12 lines
drivers/base/power/domain.c +12
-0+ goto unlock; + } + + ret =_genpd_reeval_performance_state (genpd, state); +if (!ret) { +/* +* Since we are passing "state" as well to +* _genpd_reeval_performance_state(), we don't need to call +* __genpd_dev_update_performance_state() before updating +* genpd's state with the above call. Update it only after the +* state of master domain is updated. +*/
This looks like an old comment from earlier version. I guess you should remove it!?
Quoted 48 lines
drivers/base/power/domain.c +44
-0+__genpd_dev_update_performance_state (dev, state); + } + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); + +static int _genpd_on_update_performance_state (struct generic_pm_domain *genpd) +{ +int ret, prev = genpd->prev_performance_state; + +if (likely (!prev)) +return 0 ; + + ret =_genpd_set_performance_state (genpd, prev); +if (ret) { +pr_err ("%s: Failed to restore performance state to %d (%d)\n" , + genpd->name, prev, ret); + }else { + genpd->prev_performance_state =0 ; + } + +return ret; +} + +static void _genpd_off_update_performance_state (struct generic_pm_domain *genpd) +{ +int ret, state = genpd->performance_state; + +if (likely (!state)) +return ; + + ret =_genpd_set_performance_state (genpd,0 ); +if (ret) { +pr_err ("%s: Failed to set performance state to 0 (%d)\n" , + genpd->name, ret); + }else { + genpd->prev_performance_state = state; + } +} +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;
drivers/base/power/domain.c static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, · line 555
Quoted 5 lines
drivers/base/power/domain.c +2
-0@@ -388,6 +551,8 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,return ret; } +_genpd_off_update_performance_state (genpd); +
This hole thing of keeping track of the previous performance state, while powering off seems a bit unnecessary complex in my opinion.
Instead I suggest to pick a more simple approach, like this:
*) Allow users to update the performance state even when the genpd is powered off. As stated above.
**) In _genpd_power_on(), invoke the _genpd_set_performance_state(), after the ->power_on() callback has been invoked an succeeded. Whether
_genpd_set_performance_state() fails in this case, there is not much
we can do, but just print a warning.
***) During power off, just don't care about the current performance state, but instead leave that to the genpd client to be managed via the ->power_off() callback.
In this way, the ->genpd_set_performance_state() is only called when the genpd is powered on. That should be easier for the genpd clients to cope with.
Moreover, most of this related code can then be dropped and so can the "unsigned int prev_performance_state" in the genpd struct.
Quoted 51 lines
drivers/base/power/domain.c +12
-2genpd->status =GPD_STATE_POWER_OFF ;genpd_update_accounting (genpd);@@ -437,15 +602,21 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth) } } - ret =_genpd_power_on (genpd, true); + ret =_genpd_on_update_performance_state (genpd);if (ret) goto err; + ret =_genpd_power_on (genpd, true); +if (ret) + goto err_power_on; + genpd->status =GPD_STATE_ACTIVE ;genpd_update_accounting (genpd);return 0 ; + err_power_on: +_genpd_off_update_performance_state (genpd); err:list_for_each_entry_continue_reverse (link, &genpd->slave_links,@@ -803,6 +974,8 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,if (_genpd_power_off (genpd, false))return ; +_genpd_off_update_performance_state (genpd); + genpd->status =GPD_STATE_POWER_OFF ;list_for_each_entry (link, &genpd->slave_links, slave_node) {@@ -848,7 +1021,9 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,genpd_unlock (link->master); } -_genpd_power_on (genpd, false); +if (!_genpd_on_update_performance_state (genpd)) +if (_genpd_power_on (genpd, false)) +_genpd_off_update_performance_state (genpd); genpd->status =GPD_STATE_ACTIVE ; }include/linux/pm_domain.h +0
-0
include/linux/pm_domain.h struct generic_pm_domain { · line 72
Quoted 9 lines
include/linux/pm_domain.h +4
-0@@ -64,8 +64,12 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Max requested performance state */ +unsigned int prev_performance_state;/* Performance state before power off */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*genpd_set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);
Please rename this callback to "set_performance_state".
Quoted 34 lines
include/linux/pm_domain.h +9
-1struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;@@ -121,6 +125,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };@@ -148,6 +153,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;@@ -188,6 +195,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif --
Quoted 2 lines
2.7.4
Kind regards
Uffe
PATCH v12 1/7 PM / Domains: Add support to select performance-state of domains
In reply to Viresh KumarSome platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);
This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the genpd->set_performance_state() callback, if implemented, else -ENODEV is returned to the caller.
The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state);
This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way).
The power domains can avoid supplying above callback, if they don't support setting performance-states.
Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it.
---
Only the first patch has got updates (out of the first 3 patches, which are going to get applied for now) and so I am resending only the first one here.
V12:
- Always allow setting performance state, even if genpd is off. Don't call the callback in that case.
- Set performance state only when powering ON the genpd and not during power OFF. The driver can take care of it.
- Merge several routines and remove unnecessary locking.
- Update comments and log a bit.
drivers/base/power/domain.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 ++++++ 2 files changed, 101 insertions(+)
drivers/base/power/domain.c +89 -0
@@ -237,6 +237,86 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be set. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the users guarantee that the genpd wouldn't be detached +* while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{ +struct generic_pm_domain *genpd; +struct generic_pm_domain_data *pd_data; +struct pm_domain_data *pdd; +int ret =0 ; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +if (!genpd->set_performance_state) +return -EINVAL ; + +if (!dev->power.subsys_data || !dev->power.subsys_data->domain_data) { +WARN_ON (1 ); +return -EINVAL ; + } + +genpd_lock (genpd); + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) +return 0 ; + +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd); + +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +/* +* We aren't propagating performance state changes of a subdomain to its +* masters as we don't have hardware that needs it. Over that, the +* performance states of subdomain and its masters may not have +* one-to-one mapping and would require additional information. We can +* get back to this once we have hardware that needs it. For that +* reason, we don't have to consider performance state of the subdomains +* of genpd here. +*/ + +update_state: +if (genpd_status_on (genpd)) { + ret = genpd->set_performance_state (genpd, state); +if (ret) + goto unlock; + } + + genpd->performance_state = state; + pd_data =to_gpd_data (dev->power.subsys_data->domain_data); + pd_data->performance_state = state; + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;
@@ -256,6 +336,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); + +if (unlikely (genpd->performance_state)) { + ret = genpd->set_performance_state (genpd, genpd->performance_state); +if (ret) { +pr_warn ("%s: Failed to set performance state %d (%d)\n" , + genpd->name, genpd->performance_state, ret); + } + } +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret;
include/linux/pm_domain.h +12 -0
@@ -64,8 +64,11 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Max requested performance state */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;
@@ -121,6 +124,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };
@@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;
@@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif
Signature · 1 line
2.15.0.rc1.236.g92ea95045093
Re: [PATCH V12 1/7] PM / Domains: Add support to select performance-state of domains
Quoted 97 lines · Viresh Kumar
Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the
genpd->set_performance_state()callback, if implemented, else-ENODEVis returned to the caller.The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*set_performance_state)(struct
generic_pm_domain*genpd,unsigned int state);This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way).
The power domains can avoid supplying above callback, if they don't support setting performance-states.
Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it.
Tested-by Rajendra Nayak <[email protected]>Signed-off-by Viresh Kumar <[email protected]>---
Only the first patch has got updates (out of the first 3 patches, which are going to get applied for now) and so I am resending only the first one here.V12:
- Always allow setting performance state, even if genpd is off. Don't call the callback in that case.
- Set performance state only when powering ON the genpd and not during power OFF. The driver can take care of it.
- Merge several routines and remove unnecessary locking.
- Update comments and log a bit.drivers/base/power/domain.c | 89 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 ++++++ 2 files changed, 101 insertions(+)drivers/base/power/domain.c +29
-0@@ -237,6 +237,86 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be set. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the users guarantee that the genpd wouldn't be detached +* while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{ +struct generic_pm_domain *genpd; +struct generic_pm_domain_data *pd_data; +struct pm_domain_data *pdd; +int ret =0 ; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +if (!genpd->set_performance_state) +return -EINVAL ; +
drivers/base/power/domain.c static void genpd_update_accounting(struct generic_pm_domain *genpd) · line 278
Quoted 10 lines
drivers/base/power/domain.c +10
-0+if (!dev->power.subsys_data || !dev->power.subsys_data->domain_data) { +WARN_ON (1 ); +return -EINVAL ; + } + +genpd_lock (genpd); + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) +return 0 ;
You can't just return 0 here.
1) The lock must be released.
2) You need to update the device's performance_state (pd_data->performance_state = state)
Quoted 45 lines
drivers/base/power/domain.c +41
-0+ +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd); + +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +/* +* We aren't propagating performance state changes of a subdomain to its +* masters as we don't have hardware that needs it. Over that, the +* performance states of subdomain and its masters may not have +* one-to-one mapping and would require additional information. We can +* get back to this once we have hardware that needs it. For that +* reason, we don't have to consider performance state of the subdomains +* of genpd here. +*/ + +update_state: +if (genpd_status_on (genpd)) { + ret = genpd->set_performance_state (genpd, state); +if (ret) + goto unlock; + } + + genpd->performance_state = state; + pd_data =to_gpd_data (dev->power.subsys_data->domain_data); + pd_data->performance_state = state; + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;
drivers/base/power/domain.c static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) · line 340
Quoted 5 lines
drivers/base/power/domain.c +2
-0@@ -256,6 +336,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); + +if (unlikely (genpd->performance_state)) {
If the aggregated performance_state has been decreased to 0 while the genpd was powered off, then I think you need to set the performance state to zero as well.
Perhaps better to check if (genpd->set_performance_state) and if assigned, call it with whatever value genpd->performance_state has.
Quoted 57 lines
drivers/base/power/domain.c +7
-0+ ret = genpd->set_performance_state (genpd, genpd->performance_state); +if (ret) { +pr_warn ("%s: Failed to set performance state %d (%d)\n" , + genpd->name, genpd->performance_state, ret); + } + } +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret;include/linux/pm_domain.h +12
-1@@ -64,8 +64,11 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Max requested performance state */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;@@ -121,6 +124,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };@@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;@@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif --
Quoted 2 lines
2.15.0.rc1.236.g92ea95045093
Kind regards
Uffe
PATCH v13 1/7 PM / Domains: Add support to select performance-state of domains
In reply to Viresh KumarSome platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);
This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the genpd->set_performance_state() callback, if implemented, else -ENODEV is returned to the caller.
The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state);
This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way).
The power domains can avoid supplying above callback, if they don't support setting performance-states.
Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it.
---
V13:
- Don't return directly from a locked section, drop the lock as well.
- Update the performance_state field for the device even in the case state == genpd->performance_state.
- Always call genpd->set_performance_state() from power-on.
- Update device's performance state before traversing the list of devices to find highest state, otherwise we will take previous state of the device into account.
- Check state again after traversing the list of devices to see if state changed.
drivers/base/power/domain.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 ++++++ 2 files changed, 110 insertions(+)
drivers/base/power/domain.c +98 -0
@@ -237,6 +237,95 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be set. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the users guarantee that the genpd wouldn't be detached +* while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{ +struct generic_pm_domain *genpd; +struct generic_pm_domain_data *gpd_data, *pd_data; +struct pm_domain_data *pdd; +unsigned int prev; +int ret =0 ; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +if (unlikely (!genpd->set_performance_state)) +return -EINVAL ; + +if (unlikely (!dev->power.subsys_data || + !dev->power.subsys_data->domain_data)) { +WARN_ON (1 ); +return -EINVAL ; + } + +genpd_lock (genpd); + + gpd_data =to_gpd_data (dev->power.subsys_data->domain_data); + prev = gpd_data->performance_state; + gpd_data->performance_state = state; + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) + goto unlock; + +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd); + +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +if (state == genpd->performance_state) + goto unlock; + +/* +* We aren't propagating performance state changes of a subdomain to its +* masters as we don't have hardware that needs it. Over that, the +* performance states of subdomain and its masters may not have +* one-to-one mapping and would require additional information. We can +* get back to this once we have hardware that needs it. For that +* reason, we don't have to consider performance state of the subdomains +* of genpd here. +*/ + +update_state: +if (genpd_status_on (genpd)) { + ret = genpd->set_performance_state (genpd, state); +if (ret) { + gpd_data->performance_state = prev; + goto unlock; + } + } + + genpd->performance_state = state; + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;
@@ -256,6 +345,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); + +if (unlikely (genpd->set_performance_state)) { + ret = genpd->set_performance_state (genpd, genpd->performance_state); +if (ret) { +pr_warn ("%s: Failed to set performance state %d (%d)\n" , + genpd->name, genpd->performance_state, ret); + } + } +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret;
include/linux/pm_domain.h +12 -0
@@ -64,8 +64,11 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Aggregated max performance state */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;
@@ -121,6 +124,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };
@@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;
@@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif
Signature · 1 line
2.15.0.rc1.236.g92ea95045093
Re: [PATCH V13 1/7] PM / Domains: Add support to select performance-state of domains
Quoted 35 lines · Viresh Kumar
Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the
genpd->set_performance_state()callback, if implemented, else-ENODEVis returned to the caller.The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*set_performance_state)(struct
generic_pm_domain*genpd,unsigned int state);This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way).
The power domains can avoid supplying above callback, if they don't support setting performance-states.
Quoted 8 lines
Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it.
Tested-by Rajendra Nayak <[email protected]>Signed-off-by Viresh Kumar <[email protected]>
Yeah, this looks nice a clean now!
Kind regards
Uffe
Quoted 181 lines
---
V13:
- Don't return directly from a locked section, drop the lock as well.
- Update theperformance_statefield for the device even in the case state ==genpd->performance_state.
- Always callgenpd->set_performance_state()from power-on.
- Update device's performance state before traversing the list of devices to find highest state, otherwise we will take previous state of the device into account.
- Check state again after traversing the list of devices to see if state changed.drivers/base/power/domain.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 ++++++ 2 files changed, 110 insertions(+)drivers/base/power/domain.c +98
-0@@ -237,6 +237,95 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)static inline void genpd_update_accounting (struct generic_pm_domain *genpd) {}#endif +/** +* dev_pm_genpd_set_performance_state- Set performance state of device's power +* domain. +* +* @dev: Device for which the performance-state needs to be set. +* @state: Target performance state of the device. This can be set as 0 when the +* device doesn't have any performance state constraints left (And so +* the device wouldn't participate anymore to find the target +* performance state of the genpd). +* +* It is assumed that the users guarantee that the genpd wouldn't be detached +* while this routine is getting called. +* +* Returns 0 on success and negative error values on failures. +*/ +int dev_pm_genpd_set_performance_state (struct device *dev,unsigned int state) +{ +struct generic_pm_domain *genpd; +struct generic_pm_domain_data *gpd_data, *pd_data; +struct pm_domain_data *pdd; +unsigned int prev; +int ret =0 ; + + genpd =dev_to_genpd (dev); +if (IS_ERR (genpd)) +return -ENODEV ; + +if (unlikely (!genpd->set_performance_state)) +return -EINVAL ; + +if (unlikely (!dev->power.subsys_data || + !dev->power.subsys_data->domain_data)) { +WARN_ON (1 ); +return -EINVAL ; + } + +genpd_lock (genpd); + + gpd_data =to_gpd_data (dev->power.subsys_data->domain_data); + prev = gpd_data->performance_state; + gpd_data->performance_state = state; + +/* New requested state is same as Max requested state */ +if (state == genpd->performance_state) + goto unlock; + +/* New requested state is higher than Max requested state */ +if (state > genpd->performance_state) + goto update_state; + +/* Traverse all devices within the domain */ +list_for_each_entry (pdd, &genpd->dev_list, list_node) { + pd_data =to_gpd_data (pdd); + +if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + +if (state == genpd->performance_state) + goto unlock; + +/* +* We aren't propagating performance state changes of a subdomain to its +* masters as we don't have hardware that needs it. Over that, the +* performance states of subdomain and its masters may not have +* one-to-one mapping and would require additional information. We can +* get back to this once we have hardware that needs it. For that +* reason, we don't have to consider performance state of the subdomains +* of genpd here. +*/ + +update_state: +if (genpd_status_on (genpd)) { + ret = genpd->set_performance_state (genpd, state); +if (ret) { + gpd_data->performance_state = prev; + goto unlock; + } + } + + genpd->performance_state = state; + +unlock: +genpd_unlock (genpd); + +return ret; +} +EXPORT_SYMBOL_GPL (dev_pm_genpd_set_performance_state); +static int _genpd_power_on (struct generic_pm_domain *genpd,bool timed) {unsigned int state_idx = genpd->state_idx;@@ -256,6 +345,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); + +if (unlikely (genpd->set_performance_state)) { + ret = genpd->set_performance_state (genpd, genpd->performance_state); +if (ret) { +pr_warn ("%s: Failed to set performance state %d (%d)\n" , + genpd->name, genpd->performance_state, ret); + } + } +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret;include/linux/pm_domain.h +12
-1@@ -64,8 +64,11 @@ struct generic_pm_domain {unsigned int device_count;/* Number of devices */ unsigned int suspended_count;/* System suspend device counter */ unsigned int prepared_count;/* Suspend counter of prepared devices */ +unsigned int performance_state;/* Aggregated max performance state */ int (*power_off)(struct generic_pm_domain *domain);int (*power_on)(struct generic_pm_domain *domain); +int (*set_performance_state)(struct generic_pm_domain *genpd, +unsigned int state);struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;@@ -121,6 +124,7 @@ struct generic_pm_domain_data {struct pm_domain_data base;struct gpd_timing_data td;struct notifier_block nb; +unsigned int performance_state;void *data; };@@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,extern int pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off);extern int pm_genpd_remove (struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state);extern struct dev_power_governor simple_qos_governor;extern struct dev_power_governor pm_domain_always_on_gov;@@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state (struct device *dev, +unsigned int state) +{ +return -ENOTSUPP ; +} +#define simple_qos_governor (*(struct dev_power_governor *)(NULL))#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))#endif --2.15.0.rc1.236.g92ea95045093
Re: [PATCH V13 1/7] PM / Domains: Add support to select performance-state of domains
In reply to Viresh KumarQuoted 35 lines · Viresh Kumar
Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms.
The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state.
This patch adds a new genpd API, which is called by user drivers (like OPP framework):
- int dev_pm_genpd_set_performance_state(struct device *dev,
unsigned int state);This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the
genpd->set_performance_state()callback, if implemented, else-ENODEVis returned to the caller.The PM domain drivers need to implement the following callback if they want to support performance states.
- int (*set_performance_state)(struct
generic_pm_domain*genpd,unsigned int state);This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way).
The power domains can avoid supplying above callback, if they don't support setting performance-states.
Quoted 8 lines
Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it.
Tested-by Rajendra Nayak <[email protected]>Signed-off-by Viresh Kumar <[email protected]>
OK, I really like the OPP-related changes suggested by Ulf, and you've removed a lot of the complexity that made it a bit confusing to follow. It's definitley cleaned up and much easier to follow.
Thanks for your persistence. This is definitely a needed feature.
I have some usecases in mind where the performance state might need to be selected based on OPP voltage, but that's now a change that can be added later when that feature is needed.
Re: [PATCH V13 1/7] PM / Domains: Add support to select performance-state of domains
Quoted 3 lines · Kevin Hilman
I have some usecases in mind where the performance state might need to be selected based on OPP voltage, but that's now a change that can be added later when that feature is needed.
I will be more than happy to get that in.
Quoted 1 line
Reviewed-by Kevin Hilman <[email protected]>
Thanks.
Do you think its a good time to get some bindings for this stuff now? The bindings would be required for only users and not PM domains themselves for now. Users are:
- OPP core: need per-OPP entry value for performance state.
- Devices: devices directly using the PM domain without OPP core (don't have DVFS). They can use the same property.
It might look similar to the properties I initially started with, I can just get them refreshed.
Signature · 1 line
viresh
PATCH v11 2/7 OPP: Support updating performance state of device's power domain
In reply to Viresh KumarThe genpd framework now provides an API to request device's power domain to update its performance state. Use that interface from the OPP core for devices whose power domains support performance states.
Note that this commit doesn't add any mechanism by which performance states are made available to the OPP core. That would be done by a later commit.
Note that the current implementation is restricted to the case where the device doesn't have separate regulators for itself. We shouldn't over engineer the code before we have real use case for them. We can always come back and add more code to support such cases later on.
--- drivers/opp/core.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++- drivers/opp/debugfs.c | 3 +++ drivers/opp/opp.h | 4 ++++ 3 files changed, 63 insertions(+), 1 deletion(-)
drivers/opp/core.c +56 -1
@@ -19,6 +19,7 @@#include <linux/slab.h> #include <linux/device.h> #include <linux/export.h> +#include <linux/pm_domain.h> #include <linux/regulator/consumer.h> #include "opp.h"
@@ -535,6 +536,44 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,return ret; } +static inline int +_generic_set_opp_domain (struct device *dev,struct clk *clk, +unsigned long old_freq,unsigned long freq, +unsigned int old_pstate,unsigned int new_pstate) +{ +int ret; + +/* Scaling up? Scale domain performance state before frequency */ +if (freq > old_freq) { + ret =dev_pm_genpd_set_performance_state (dev, new_pstate); +if (ret) +return ret; + } + + ret =_generic_set_opp_clk_only (dev, clk, old_freq, freq); +if (ret) + goto restore_domain_state; + +/* Scaling down? Scale domain performance state after frequency */ +if (freq < old_freq) { + ret =dev_pm_genpd_set_performance_state (dev, new_pstate); +if (ret) + goto restore_freq; + } + +return 0 ; + +restore_freq: +if (_generic_set_opp_clk_only (dev, clk, freq, old_freq)) +dev_err (dev,"%s: failed to restore old-freq (%lu Hz)\n" , + __func__, old_freq); +restore_domain_state: +if (freq > old_freq) +dev_pm_genpd_set_performance_state (dev, old_pstate); + +return ret; +} + static int _generic_set_opp_regulator(const struct opp_table *opp_table, struct device *dev, unsigned long old_freq,
@@ -653,7 +692,16 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)/* Only frequency scaling */ if (!opp_table->regulators) { - ret =_generic_set_opp_clk_only (dev, clk, old_freq, freq); +/* +* We don't support devices with both regulator and +* domain performance-state for now. +*/ +if (opp_table->genpd_performance_state) + ret =_generic_set_opp_domain (dev, clk, old_freq, freq, +IS_ERR (old_opp) ?0 : old_opp->pstate, + opp->pstate); +else + ret =_generic_set_opp_clk_only (dev, clk, old_freq, freq); }else if (!opp_table->set_opp) { ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq, IS_ERR(old_opp) ? NULL : old_opp->supplies,
@@ -1706,6 +1754,13 @@ void _dev_pm_opp_remove_table(struct opp_table *opp_table, struct device *dev,if (remove_all || !opp->dynamic)dev_pm_opp_put (opp); } + +/* +* The OPP table is getting removed, drop the performance state +* constraints. +*/ +if (opp_table->genpd_performance_state) +dev_pm_genpd_set_performance_state (dev,0 ); }else {_remove_opp_dev (_find_opp_dev (dev, opp_table), opp_table); }
drivers/opp/debugfs.c +3 -0
@@ -99,6 +99,9 @@ int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)if (!debugfs_create_bool ("suspend" ,S_IRUGO , d, &opp->suspend))return -ENOMEM ; +if (!debugfs_create_u32 ("performance_state" ,S_IRUGO , d, &opp->pstate)) +return -ENOMEM ; +if (!debugfs_create_ulong ("rate_hz" ,S_IRUGO , d, &opp->rate))return -ENOMEM ;
drivers/opp/opp.h +4 -0
@@ -58,6 +58,7 @@ extern struct list_head opp_tables;* @dynamic: not-created from static DT entries. * @turbo: true if turbo (boost) OPP * @suspend: true if suspend OPP +* @pstate: Device's power domain's performance state. * @rate: Frequency in hertz * @supplies: Power supplies voltage/current values * @clock_latency_ns: Latency (in nanoseconds) of switching to this OPP's
@@ -76,6 +77,7 @@ struct dev_pm_opp {bool dynamic;bool turbo;bool suspend; +unsigned int pstate;unsigned long rate;struct dev_pm_opp_supply *supplies;
@@ -135,6 +137,7 @@ enum opp_table_access {* @clk: Device's clock handle * @regulators: Supply regulators * @regulator_count: Number of power supply regulators +* @genpd_performance_state: Device's power domain support performance state. * @set_opp: Platform specific set_opp callback * @set_opp_data: Data to be passed to set_opp callback * @dentry: debugfs dentry pointer of the real device directory (not links).
@@ -170,6 +173,7 @@ struct opp_table {struct clk *clk;struct regulator **regulators;unsigned int regulator_count; +bool genpd_performance_state;int (*set_opp)(struct dev_pm_set_opp_data *data);struct dev_pm_set_opp_data *set_opp_data;
Signature · 1 line
2.7.4
PATCH v11 3/7 OPP: Add dev_pm_opp_{un}register_get_pstate_helper()
In reply to Viresh KumarThis adds the dev_pm_opp_{un}register_get_pstate_helper() helper routines which will be used to set the get_pstate() callback for a device. This callback will be later called internally by the OPP core to get performance state corresponding to an OPP.
This is required temporarily until the time we have proper DT bindings to include the performance state information.
--- drivers/opp/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/opp/opp.h | 2 ++ include/linux/pm_opp.h | 10 +++++++ 3 files changed, 90 insertions(+)
drivers/opp/core.c +78 -0
@@ -1036,6 +1036,9 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,return ret; } +if (opp_table->get_pstate) + new_opp->pstate = opp_table->get_pstate (dev, new_opp->rate); +list_add (&new_opp->node, head);mutex_unlock (&opp_table->lock);
@@ -1548,6 +1551,81 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)EXPORT_SYMBOL_GPL (dev_pm_opp_unregister_set_opp_helper);/** +* dev_pm_opp_register_get_pstate_helper() - Register get_pstate() helper. +* @dev: Device for which the helper is getting registered. +* @get_pstate: Helper. +* +* TODO: Remove this callback after the same information is available via Device +* Tree. +* +* This allows a platform to initialize the performance states of individual +* OPPs for its devices, until we get similar information directly from DT. +* +* This must be called before the OPPs are initialized for the device. +*/ +struct opp_table *dev_pm_opp_register_get_pstate_helper (struct device *dev, +int (*get_pstate)(struct device *dev,unsigned long rate)) +{ +struct opp_table *opp_table; +int ret; + +if (!get_pstate) +return ERR_PTR (-EINVAL ); + + opp_table =dev_pm_opp_get_opp_table (dev); +if (!opp_table) +return ERR_PTR (-ENOMEM ); + +/* This should be called before OPPs are initialized */ +if (WARN_ON (!list_empty (&opp_table->opp_list))) { + ret = -EBUSY ; + goto err; + } + +/* Already have genpd_performance_state set */ +if (WARN_ON (opp_table->genpd_performance_state)) { + ret = -EBUSY ; + goto err; + } + + opp_table->genpd_performance_state = true; + opp_table->get_pstate = get_pstate; + +return opp_table; + +err: +dev_pm_opp_put_opp_table (opp_table); + +return ERR_PTR (ret); +} +EXPORT_SYMBOL_GPL (dev_pm_opp_register_get_pstate_helper); + +/** +* dev_pm_opp_unregister_get_pstate_helper() - Releases resources blocked for +* get_pstate() helper +* @opp_table: OPP table returned from dev_pm_opp_register_get_pstate_helper(). +* +* Release resources blocked for platform specific get_pstate() helper. +*/ +void dev_pm_opp_unregister_get_pstate_helper (struct opp_table *opp_table) +{ +if (!opp_table->genpd_performance_state) { +pr_err ("%s: Doesn't have performance states set\n" , + __func__); +return ; + } + +/* Make sure there are no concurrent readers while updating opp_table */ +WARN_ON (!list_empty (&opp_table->opp_list)); + + opp_table->genpd_performance_state = false; + opp_table->get_pstate =NULL ; + +dev_pm_opp_put_opp_table (opp_table); +} +EXPORT_SYMBOL_GPL (dev_pm_opp_unregister_get_pstate_helper); + +/** * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation * @freq: Frequency in Hz for this OPP
drivers/opp/opp.h +2 -0
@@ -140,6 +140,7 @@ enum opp_table_access {* @genpd_performance_state: Device's power domain support performance state. * @set_opp: Platform specific set_opp callback * @set_opp_data: Data to be passed to set_opp callback +* @get_pstate: Platform specific get_pstate callback * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. *
@@ -177,6 +178,7 @@ struct opp_table {int (*set_opp)(struct dev_pm_set_opp_data *data);struct dev_pm_set_opp_data *set_opp_data; +int (*get_pstate)(struct device *dev,unsigned long rate);#ifdef CONFIG_DEBUG_FS struct dentry *dentry;
include/linux/pm_opp.h +10 -0
@@ -125,6 +125,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);void dev_pm_opp_put_clkname (struct opp_table *opp_table);struct opp_table *dev_pm_opp_register_set_opp_helper (struct device *dev,int (*set_opp)(struct dev_pm_set_opp_data *data));void dev_pm_opp_unregister_set_opp_helper (struct opp_table *opp_table); +struct opp_table *dev_pm_opp_register_get_pstate_helper (struct device *dev,int (*get_pstate)(struct device *dev,unsigned long rate)); +void dev_pm_opp_unregister_get_pstate_helper (struct opp_table *opp_table);int dev_pm_opp_set_rate (struct device *dev,unsigned long target_freq);int dev_pm_opp_set_sharing_cpus (struct device *cpu_dev,const struct cpumask *cpumask);int dev_pm_opp_get_sharing_cpus (struct device *cpu_dev,struct cpumask *cpumask);
@@ -245,6 +247,14 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct devicestatic inline void dev_pm_opp_unregister_set_opp_helper (struct opp_table *opp_table) {} +static inline struct opp_table *dev_pm_opp_register_get_pstate_helper (struct device *dev, +int (*get_pstate)(struct device *dev,unsigned long rate)) +{ +return ERR_PTR (-ENOTSUPP ); +} + +static inline void dev_pm_opp_unregister_get_pstate_helper (struct opp_table *opp_table) {} +static inline struct opp_table *dev_pm_opp_set_prop_name (struct device *dev,const char *name) {return ERR_PTR (-ENOTSUPP );
Signature · 1 line
2.7.4
PATCH v11 4/7 soc: qcom: rpmpd: Add driver to model cx/mx power domains
In reply to Viresh KumarFrom: Rajendra Nayak <[email protected]>
NOT FOR MERGE.
The cx/mx power domains just pass the performance state set by the consumers to the RPM (Remote Power manager) which then takes care of setting the appropriate voltage on the corresponding rails to meet the performance needs.
Add data for all power domains on msm8996.
--- .../devicetree/bindings/power/qcom,rpmpd.txt | 9 + arch/arm64/boot/dts/qcom/msm8996.dtsi | 5 + drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/rpmpd.c | 307 +++++++++++++++++++++ 5 files changed, 331 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmpd.txt create mode 100644 drivers/soc/qcom/rpmpd.c
Documentation/devicetree/bindings/power/qcom,rpmpd.txt new file +9 -0
@@ -0,0 +1,9 @@ +Qualcomm RPM Power domains + +* For RPM power domains, we communicate a performance state to RPM +which then translates it into a corresponding voltage on a rail. + +Required Properties: + - compatible: Should be one of the following: + * qcom,rpmpd-msm8996: RPM Powerdomain for the msm8996 family of SoC. + - power-domain-cells: number of cells in power domain specifier must be 1.
arch/arm64/boot/dts/qcom/msm8996.dtsi +5 -0
@@ -496,6 +496,11 @@ status = "disabled"; }; + rpmpd: qcom,rpmpd { + compatible = "qcom,rpmpd-msm8996", "qcom,rpmpd"; + #power-domain-cells = <1>; + }; + sdhc2: sdhci@74a4900 { status = "disabled"; compatible = "qcom,sdhci-msm-v4";
drivers/soc/qcom/Kconfig +9 -0
@@ -35,6 +35,15 @@ config QCOM_PM modes. It interface with various system drivers to put the cores in low power modes. +config QCOM_RPMPD + tristate "Qualcomm RPM Power domain driver" + depends on MFD_QCOM_RPM && QCOM_SMD_RPM + help + QCOM RPM power domain driver to support power domain with + performance states. The driver communicates a performance state + value to RPM which then translates it into corresponding voltage + for the voltage rail. + config QCOM_SMEM tristate "Qualcomm Shared Memory Manager (SMEM)" depends on ARCH_QCOM
drivers/soc/qcom/Makefile +1 -0
@@ -8,3 +8,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o +obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
drivers/soc/qcom/rpmpd.c new file +307 -0
307 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v11 5/7 soc: qcom: rpmpd: Add support for set performance state
In reply to Viresh KumarFrom: Rajendra Nayak <[email protected]>
NOT FOR MERGE.
With genpd now expecting power domain drivers supporting performance state to support set performance state callbacks, add support for it in the rpmpd driver.
--- drivers/soc/qcom/rpmpd.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
drivers/soc/qcom/rpmpd.c +23 -0
@@ -230,6 +230,28 @@ static int rpmpd_power_off(struct generic_pm_domain *domain)return ret; } +static int rpmpd_set_performance (struct generic_pm_domain *domain, +unsigned int state) +{ +int ret =0 ; +struct rpmpd *pd =domain_to_rpmpd (domain); + +mutex_lock (&rpmpd_lock); + + pd->corner = state; + +if (!pd->enabled && (pd->key !=KEY_FLOOR_CORNER )) + goto out; + + ret =rpmpd_aggregate_corner (pd); + +out: +mutex_unlock (&rpmpd_lock); + +return ret; +} + +static int rpmpd_probe (struct platform_device *pdev) {int i;
@@ -267,6 +289,7 @@ static int rpmpd_probe(struct platform_device *pdev) rpmpds[i]->rpm = rpm; rpmpds[i]->pd.power_off = rpmpd_power_off; rpmpds[i]->pd.power_on = rpmpd_power_on; + rpmpds[i]->pd.genpd_set_performance_state = rpmpd_set_performance;pm_genpd_init (&rpmpds[i]->pd,NULL , true); data->domains[i] = &rpmpds[i]->pd;
Signature · 1 line
2.7.4
PATCH v11 6/7 OPP: qcom: Add support to get performance states corresponding to OPPs
In reply to Viresh KumarNOT FOR MERGE.
The OPP core needs to know the performance state for each OPP that the device supports, if we want to update performance states of the device's PM domain.
Add support for Qualcomm's rpmpd for the same.
--- drivers/opp/Makefile | 2 + drivers/opp/qcom-rpmpd.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 drivers/opp/qcom-rpmpd.c
drivers/opp/Makefile +2 -0
@@ -2,3 +2,5 @@ ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG obj-y += core.o cpu.o obj-$(CONFIG_OF) += of.o obj-$(CONFIG_DEBUG_FS) += debugfs.o + +obj-$(CONFIG_QCOM_RPMPD) += qcom-rpmpd.o
drivers/opp/qcom-rpmpd.c new file +107 -0
107 lines not on this page · raw message
Signature · 1 line
2.7.4
PATCH v11 7/7 mmc: sdhci-msm: Adapt the driver to use OPPs to set clocks/performance state
In reply to Viresh KumarFrom: Rajendra Nayak <[email protected]>
NOT FOR MERGE.
SDHCI driver needs to set a performance state along with scaling its clocks. Modify the driver to use the newly introducded powerdomain performance state based OPPs to scale clocks as well as set an appropriate powerdomain performance state.
The patch also adds OPPs for sdhci device on msm8996.
The changes have to be validated by populating similar OPP tables on all other devices which use the sdhci driver. This is for now validated only on msm8996 and with missing OPP tables for other devices is known to break those platforms.
--- arch/arm64/boot/dts/qcom/msm8996.dtsi | 34 ++++++++++++++++++++++++++++++ drivers/clk/qcom/gcc-msm8996.c | 8 +++---- drivers/mmc/host/sdhci-msm.c | 39 ++++++++++++++++++++++++++--------- 3 files changed, 67 insertions(+), 14 deletions(-)
arch/arm64/boot/dts/qcom/msm8996.dtsi +34 -0
@@ -515,8 +515,42 @@ <&gcc GCC_SDCC2_APPS_CLK>, <&xo_board>; bus-width = <4>; + power-domains = <&rpmpd 0>; + operating-points-v2 = <&sdhc_opp_table>; }; + sdhc_opp_table: opp_table { + compatible = "operating-points-v2"; + + opp@400000 { + opp-hz = /bits/ 64 <400000>; + }; + + opp@20000000 { + opp-hz = /bits/ 64 <20000000>; + }; + + opp@25000000 { + opp-hz = /bits/ 64 <25000000>; + }; + + opp@50000000 { + opp-hz = /bits/ 64 <50000000>; + }; + + opp@96000000 { + opp-hz = /bits/ 64 <96000000>; + }; + + opp@192000000 { + opp-hz = /bits/ 64 <192000000>; + }; + + opp@384000000 { + opp-hz = /bits/ 64 <384000000>; + }; + }; + msmgpio: pinctrl@1010000 { compatible = "qcom,msm8996-pinctrl"; reg = <0x01010000 0x300000>;
drivers/clk/qcom/gcc-msm8996.c +4 -4
@@ -460,7 +460,7 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { .name = "sdcc1_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4_gpll0_early_div, .num_parents = 4, - .ops = &clk_rcg2_floor_ops, + .ops = &clk_rcg2_ops, }, };
@@ -505,7 +505,7 @@ static struct clk_rcg2 sdcc2_apps_clk_src = { .name = "sdcc2_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4, .num_parents = 3, - .ops = &clk_rcg2_floor_ops, + .ops = &clk_rcg2_ops, }, };
@@ -519,7 +519,7 @@ static struct clk_rcg2 sdcc3_apps_clk_src = { .name = "sdcc3_apps_clk_src", .parent_names = gcc_xo_gpll0_gpll4, .num_parents = 3, - .ops = &clk_rcg2_floor_ops, + .ops = &clk_rcg2_ops, }, };
@@ -543,7 +543,7 @@ static struct clk_rcg2 sdcc4_apps_clk_src = { .name = "sdcc4_apps_clk_src", .parent_names = gcc_xo_gpll0, .num_parents = 2, - .ops = &clk_rcg2_floor_ops, + .ops = &clk_rcg2_ops, }, };
drivers/mmc/host/sdhci-msm.c +29 -10
@@ -21,6 +21,7 @@#include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/iopoll.h> +#include <linux/pm_opp.h> #include "sdhci-pltfm.h"
@@ -131,6 +132,7 @@ struct sdhci_msm_host {struct clk *pclk;/* SDHC peripheral bus clock */ struct clk *bus_clk;/* SDHC bus voter clock */ struct clk *xo_clk;/* TCXO clk needed for FLL feature of cm_dll*/ +struct opp_table *opp_table;unsigned long clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset;
@@ -140,7 +142,7 @@ struct sdhci_msm_host {bool use_cdclp533; }; -static unsigned int msm_get_clock_rate_for_bus_mode (struct sdhci_host *host, +static long unsigned int msm_get_clock_rate_for_bus_mode (struct sdhci_host *host,unsigned int clock) {struct mmc_ios ios = host->mmc->ios;
@@ -165,16 +167,22 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios;int rc; +struct device *dev = &msm_host->pdev->dev; +struct dev_pm_opp *opp; +long unsigned int freq; + + freq =msm_get_clock_rate_for_bus_mode (host, clock); + opp =dev_pm_opp_find_freq_floor (dev, &freq); +if (IS_ERR (opp)) +pr_err ("%s: failed to find OPP for %u at timing %d\n" , +mmc_hostname (host->mmc), clock, curr_ios.timing); + + rc =dev_pm_opp_set_rate (dev, freq); +if (rc) +pr_err ("%s: error in setting opp\n" , __func__); + + msm_host->clk_rate = freq; - clock =msm_get_clock_rate_for_bus_mode (host, clock); - rc =clk_set_rate (msm_host->clk, clock); -if (rc) { -pr_err ("%s: Failed to set clock at rate %u at timing %d\n" , -mmc_hostname (host->mmc), clock, - curr_ios.timing); -return ; - } - msm_host->clk_rate = clock;pr_debug ("%s: Setting clock at rate %lu at timing %d\n" ,mmc_hostname (host->mmc),clk_get_rate (msm_host->clk), curr_ios.timing);
@@ -1268,6 +1276,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } +/* Set up the OPP table */ + msm_host->opp_table =dev_pm_opp_set_clkname (&pdev->dev,"core" ); + + ret =dev_pm_opp_of_add_table (&pdev->dev); +if (ret) +dev_warn (&pdev->dev,"%s: No OPP table specified\n" , __func__); +pm_runtime_get_noresume (&pdev->dev);pm_runtime_set_active (&pdev->dev);pm_runtime_enable (&pdev->dev);
@@ -1289,6 +1304,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)pm_runtime_disable (&pdev->dev);pm_runtime_set_suspended (&pdev->dev);pm_runtime_put_noidle (&pdev->dev); +dev_pm_opp_of_remove_table (&pdev->dev); +dev_pm_opp_put_clkname (msm_host->opp_table); clk_disable:clk_disable_unprepare (msm_host->clk); pclk_disable:
@@ -1314,6 +1331,8 @@ static int sdhci_msm_remove(struct platform_device *pdev)pm_runtime_get_sync (&pdev->dev);pm_runtime_disable (&pdev->dev);pm_runtime_put_noidle (&pdev->dev); +dev_pm_opp_of_remove_table (&pdev->dev); +dev_pm_opp_put_clkname (msm_host->opp_table);clk_disable_unprepare (msm_host->clk);clk_disable_unprepare (msm_host->pclk);
Signature · 1 line
2.7.4
Quoted 32 lines · Viresh Kumar
Hi,
This version contains the changes we discussed during Linaro Connect.
Some platforms have the capability to configure the performance state of their power domains. The process of configuring the performance state is pretty much platform dependent and we may need to work with a wide range of configurables. For some platforms, like Qcom, it can be a positive integer value alone, while in other cases it can be voltage levels, etc.
The power-domain framework until now was only designed for the idle state management of the device and this needs to change in order to reuse the power-domain framework for active state management of the devices.
The first patch updates the genpd framework to supply new APIs to support active state management and the second patch uses them from the OPP core. The third patch adds a new API to the OPP core to get performance state corresponding to OPPs (This should rather come via DT and would be removed once we have fixed bindings for performance states).
Rest of the patches [4-7/7] are included to show how user drivers would end up using the new APIs and these patches aren't ready to get merged yet and are marked clearly like that. Moreover some of them may go via SoC specific trees instead of the PM tree.
This is currently tested by:
- /me by hacking the kernel a bit with virtual power-domains for the ARM 64 hikey platform.
- Rajendra Nayak, on msm8996 platform (Qcom) with MMC controller.
Quoted 6 lines
Thanks Rajendra for helping me testing this out.
I also had a chat with Rajendra and we should be able to get a Qualcomm specific power domain driver (which uses these changes) in coming weeks.
I am targeting the first 3 patches for 4.15-rc1, if possible.
I have looked through the series and overall it looks okay to me. I think my comments on patch1 should be rather simple to address - and so I agree that aiming for 4.15rc1 seems like a reasonable plan.
Kind regards
Uffe