PM/Domains: Cluster idle support for ARM SoCs
Subsystems (16)
cover PM/Domains: Cluster idle support for ARM SoCs
Hi all,
This series is an attempt at doing an end-to-end PSCI OS initiated solution for SoC/Cluster idle for ARM v8 SoCs. The series is based on PM domains for CPUs [1], which lays the foundation for IRQ safe domains and CPU domains that use the IRQ safe property of a domain to power ON/OFF the domain when the CPUs are in idle (either cpuidle or hotplug). This patchset is superset of [1] providing a better picture of how the CPU domains are defined and used.
The gist of this series is that the topology of CPU and the hierarchy is provided in the DT for an SoC. A common library of functions help parse the DT to initialize the generic PM domains and attach sub-domains and CPU devices to those domains. On an ARM v8 SoC, the PSCI f/w controls the domains for low power modes when the CPUs are in idle. CPUs call runtime PM suspend when entering idle, which calls generic PM domains (genpd) that reference counts the domain usage by devices/sub-domains in the domain. When the last CPU in the domain powers down, genpd calls CPU PM domain core to power down the domain, which in turn sets up PSCI core to pass the cluster idle states along with the CPU idle state to the f/w.
PSCI v1.0 supports Platform coordinated and OS initiated modes. Platform coordinated PSCI lets PSCI determine the state of the cluster based on votes from individual CPUs. This simple solution may sometimes be inefficient, as the PSCI f/w is unaware of the CPU and cluster sleep length and the QoS requirement laid on the CPUs. OS initiated PSCI on the other hand lets Linux determine the state of the cluster and the coherency domain and pass them as arguments to the PSCI call when the last CPU enters idle. The complexity of determining the state of the cluster and last-man reference counting rests with the kernel.
By defining CPU domains using genpd, both ARM v7 and v8 based architectures can take advantage of the last man reference counting and cluster idle state determination in genpd to save power - by opportunistically flushing CPU caches, turning off supplementary hardware and powering off the CPU domain. This patchset makes it easy for ARM v8 based PSCI f/w that supports OS initiated do all that is needed from Linux by just specifying the CPU domain hierarchy and idle states supported by the CPU domains in DT. ARM v7 SoCs that control domains in Linux could still use this series in setting up CPU PM domains for the CPUs, however, they would setup the hierarchy and supply the domain power on/off callbacks.
This patchset builds upon genpd patches for multiple idle states from Axel [2] and Marc [3] and Lorenzo to unify cpuidle signatures across ARM 32 and 64 [4]. I made a few amends to their patches to split the key parts relevant to this efforts and rebase on top of 4.4-rc1. I have re-based this [7] on top of v4.4-rc1 and tested using Kumar's patches, which has since then been replaced with [5]. This series [7] has been tested on a QCOM APQ8016 Dragonboard with a PSCI f/w that supports OS initiated mode.
Key topics for focus -
1. DT definition of cluster node in topology node.
2. DT definition of domain idle states - in comparison to arm,idle-state
3. Determining the next wake up of the CPU therefore the cluster from clock events.
4. Gen PD governor for CPU clusters.
5. Some parts are currently ARM specific (like the cpuidle parts), they could be arch agnostic.
6. Optimizations to speed up the entire series.
Concerns -
Previous patches had raise concerns with latency added to the idle path. I have not addressed them in this series. I focused on getting the full solution in place at this time. A previous attempt at using runtime PM and genpd without the use of locks is here [6]. I will re-focus my effort of making this suitable for -RT kernel and optimizing the runtime paths and integrating them into a future version.
Thanks,
Lina
[1]. https://lwn.net/Articles/656793/
[2]. https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1001509.html [3]. https://lwn.net/Articles/658461/
[4]. http://www.spinics.net/lists/arm-kernel/msg451502.html
[5]. https://lkml.org/lkml/2015/10/26/651
[6]. https://patches.linaro.org/54565/
[7]. https://git.linaro.org/people/lina.iyer/linux-next.git/shortlog/refs/heads/genpd-psci-3
Axel Haslam (3):
PM / Domains: core changes for multiple states PM / Domains: make governor select deepest state PM / Domains: remove old power on/off latencies.
Kumar Gala (2):
arm64: dts: Add Qualcomm MSM8916, MTP8916, APQ8016, SBC8016 ids
devicetree: bindings: Document qcom,msm-id and qcom,board-id
Lina Iyer (19):
PM / Domain: Add additional state specific param
PM / Domains: Read domain residency from DT
PM / Domains: Support IRQ safe PM domains
PM / Domains: Attempt runtime suspend of IRQ safe parent domain
drivers: power: Introduce PM domains for CPUs/clusters
drivers: cpu: Define CPU devices as IRQ safe
ARM: cpuidle: Add runtime PM support for CPU idle
tick: get next wakeup event for the CPU
PM / Domains: Add next_wakeup to device's timing data
ARM: cpuidle: Record the next wakeup event of the CPU
drivers: cpu-pd: Record CPUs that are part of the domain
drivers: cpu-pd: Add PM Domain governor for CPUs
drivers: cpu-pd: Invoke CPU PM runtime on hotplug
Documentation: ARM: topology: 'cluster' property for cluster nodes
drivers: cpu-pd: Parse topology to setup CPU PM domains
drivers: firmware: PSCI: Export psci_has_ext_power_state()
ARM64: psci: Support cluster idle states for OS-Initated
ARM64: dts: Add PSCI cpuidle support for MSM8916
ARM64: dts: Define CPU power domain for MSM8916
Lorenzo Pieralisi (1):
ARM: cpuidle: remove cpu parameter from the cpuidle_ops suspend hook
Marc Titinger (2):
PM / Domains: Allow domain power states to be read from DT PM / Domains: add debugfs 'states' and 'timings' seq files
Documentation/arm/cpu-domains.txt | 52 ++ Documentation/devicetree/bindings/arm/msm/ids.txt | 65 +++ Documentation/devicetree/bindings/arm/topology.txt | 8 + .../devicetree/bindings/power/power_domain.txt | 76 +++ Documentation/power/devices.txt | 11 +- arch/arm/include/asm/cpuidle.h | 2 +- arch/arm/kernel/cpuidle.c | 2 +- arch/arm64/boot/dts/qcom/apq8016-sbc.dts | 2 + arch/arm64/boot/dts/qcom/msm8916-mtp.dts | 3 + arch/arm64/boot/dts/qcom/msm8916.dtsi | 76 +++ arch/arm64/kernel/psci.c | 54 +- drivers/base/cpu.c | 6 +- drivers/base/power/Makefile | 1 + drivers/base/power/cpu-pd.c | 507 ++++++++++++++++++ drivers/base/power/domain.c | 589 +++++++++++++++++++-- drivers/base/power/domain_governor.c | 70 ++- drivers/cpuidle/cpuidle-arm.c | 13 + drivers/firmware/psci.c | 2 +- drivers/soc/qcom/spm.c | 10 +- include/dt-bindings/arm/qcom-ids.h | 33 ++ include/linux/cpu-pd.h | 36 ++ include/linux/pm_domain.h | 32 +- include/linux/psci.h | 1 + include/linux/tick.h | 10 + kernel/time/tick-sched.c | 8 + 25 files changed, 1576 insertions(+), 93 deletions(-) create mode 100644 Documentation/arm/cpu-domains.txt create mode 100644 Documentation/devicetree/bindings/arm/msm/ids.txt create mode 100644 drivers/base/power/cpu-pd.c create mode 100644 include/dt-bindings/arm/qcom-ids.h create mode 100644 include/linux/cpu-pd.h
Signature · 1 line
2.1.4
RFC PATCH 1/27 PM / Domains: core changes for multiple states
From: Axel Haslam <[email protected]>
From: Axel Haslam <[email protected]>
Add the core changes to be able to declare multiple states. When trying to set a power domain to off, genpd will be able to choose from an array of states declared by the platform. The power on and off latencies are now tied to a state.
States should be declared in ascending order from shallowest to deepest, deepest meaning the state which takes longer to enter and exit.
the power_off and power_on function can use the 'state_idx' field of the generic_pm_domain structure, to distinguish between the different states and act accordingly.
Example:
static int pd1_power_on(struct generic_pm_domain *domain)
{
/* domain->state_idx = state the domain is coming from */
}static int pd1_power_off(struct generic_pm_domain *domain)
{
/* domain->state_idx = desired powered off state */
}const struct genpd_power_state pd_states[] = {
{
.name = "RET",
.power_on_latency_ns = ON_LATENCY_FAST,
.power_off_latency_ns = OFF_LATENCY_FAST,
},
{
.name = "DEEP_RET",
.power_on_latency_ns = ON_LATENCY_MED,
.power_off_latency_ns = OFF_LATENCY_MED,
},
{
.name = "OFF",
.power_on_latency_ns = ON_LATENCY_SLOW,
.power_off_latency_ns = OFF_LATENCY_SLOW,
}
};struct generic_pm_domain pd1 = {
.name = "PD1",
.power_on = pd1_power_on,
.power_off = pd1_power_off,
[...]
};int xxx_init_pm_domain(){
pd1->state = copy_of(pd_states); pd1->state_count = ARRAY_SIZE(pd_states); pm_genpd_init(pd1, true);
}
[Lina: Modified genpd state initialization and remove use of save_state_latency_ns in genpd timing data]
--- drivers/base/power/domain.c | 136 +++++++++++++++++++++++++++++++++-- drivers/base/power/domain_governor.c | 13 +++- include/linux/pm_domain.h | 10 +++ 3 files changed, 151 insertions(+), 8 deletions(-)
drivers/base/power/domain.c +131 -5
@@ -34,6 +34,12 @@ __ret; \ }) +#define GENPD_MAX_NAME_SIZE 20 + +static int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count); +static LIST_HEAD (gpd_list);static DEFINE_MUTEX (gpd_list_lock);
@@ -102,6 +108,7 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)static int genpd_power_on (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;
@@ -118,10 +125,10 @@ 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 (elapsed_ns <= genpd->power_on_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret; - genpd->power_on_latency_ns = elapsed_ns; + genpd->states[state_idx].power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"on" , elapsed_ns);
@@ -131,6 +138,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)static int genpd_power_off (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;
@@ -147,10 +155,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); -if (elapsed_ns <= genpd->power_off_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)return ret; - genpd->power_off_latency_ns = elapsed_ns; + genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"off" , elapsed_ns);
@@ -582,6 +590,8 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, || atomic_read(&genpd->sd_count) > 0)return ; +/* Choose the deepest state when suspending */ + genpd->state_idx = genpd->state_count -1 ;genpd_power_off (genpd, timed); genpd->status =GPD_STATE_POWER_OFF ;
@@ -1205,6 +1215,61 @@ static void genpd_free_dev_data(struct device *dev,dev_pm_put_subsys_data (dev); } +static int genpd_alloc_states_data (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +int ret =0 ; +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) { + ret = -EINVAL ; + goto err; + } + +if (!st || (st_count <1 )) { + ret = -EINVAL ; + goto err; + } + +/* Allocate the local memory to keep the states for this genpd */ + genpd->states =kcalloc (st_count,sizeof (*st),GFP_KERNEL ); +if (!genpd->states) { + ret = -ENOMEM ; + goto err; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].power_on_latency_ns = + st[i].power_on_latency_ns; + genpd->states[i].power_off_latency_ns = + st[i].power_off_latency_ns; + } + +/* +* Copy the latency values To keep compatibility with +* platforms that are not converted to use the multiple states. +* This will be removed once all platforms are converted to use +* multiple states. note that non converted platforms will use the +* default single off state. +*/ +if (genpd->power_on_latency_ns !=0 ) + genpd->states[0 ].power_on_latency_ns = + genpd->power_on_latency_ns; + +if (genpd->power_off_latency_ns !=0 ) + genpd->states[0 ].power_off_latency_ns = + genpd->power_off_latency_ns; + + genpd->state_count = st_count; + +/* to save memory, Name allocation will happen if debug is enabled */ +pm_genpd_alloc_states_names (genpd, st, st_count); + +err: +return ret; +} +/** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to.
@@ -1459,6 +1524,15 @@ static int pm_genpd_default_restore_state(struct device *dev)void pm_genpd_init (struct generic_pm_domain *genpd,struct dev_power_governor *gov,bool is_off) { +int ret; +static const struct genpd_power_state genpd_default_off[] = { + { + .name ="OFF" , + .power_off_latency_ns =0 , + .power_on_latency_ns =0 , + }, + }; +if (IS_ERR_OR_NULL (genpd))return ;
@@ -1503,6 +1577,19 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->dev_ops.start = pm_clk_resume; } +/* +* Allocate the default state if genpd states +* are not already defined. +*/ +if (!genpd->state_count) { + ret =genpd_alloc_states_data (genpd, genpd_default_off,1 ); +if (ret) +return ; + } + +/* Assume the deepest state on init*/ + genpd->state_idx = genpd->state_count -1 ; +mutex_lock (&gpd_list_lock);list_add (&genpd->gpd_list_node, &gpd_list);mutex_unlock (&gpd_list_lock);
@@ -1822,6 +1909,33 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);#include <linux/kobject.h> static struct dentry *pm_genpd_debugfs_dir; +static int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) +return -EINVAL ; + +if (genpd->state_count != st_count) { +pr_err ("Invalid allocated state count\n" ); +return -EINVAL ; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].name =kstrndup (st[i].name, +GENPD_MAX_NAME_SIZE ,GFP_KERNEL ); +if (!genpd->states[i].name) { +pr_err ("%s Failed to allocate state %d name.\n" , + genpd->name, i); +return -ENOMEM ; + } + } + +return 0 ; +} +/* * TODO: This function is a slightly modified version of rtpm_status_show * from sysfs.c, so generalize it.
@@ -1855,6 +1969,7 @@ static int pm_genpd_summary_one(struct seq_file *s, [GPD_STATE_ACTIVE] = "on", [GPD_STATE_POWER_OFF] = "off" }; +unsigned int state_idx = genpd->state_idx;struct pm_domain_data *pm_data;const char *kobj_path;struct gpd_link *link;
@@ -1866,7 +1981,11 @@ static int pm_genpd_summary_one(struct seq_file *s,if (WARN_ON (genpd->status >=ARRAY_SIZE (status_lookup))) goto exit; -seq_printf (s,"%-30s %-15s " , genpd->name, status_lookup[genpd->status]); + +seq_printf (s,"%-30s %-15s " , genpd->name, + (genpd->status ==GPD_STATE_POWER_OFF ) ? + genpd->states[state_idx].name : + status_lookup[genpd->status]);/* * Modifications on the list require holding locks on both
@@ -1954,4 +2073,11 @@ static void __exit pm_genpd_debug_exit(void) debugfs_remove_recursive(pm_genpd_debugfs_dir); }__exitcall (pm_genpd_debug_exit); +#else +static inline int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +return 0 ; +}#endif /* CONFIG_PM_ADVANCED_DEBUG */
drivers/base/power/domain_governor.c +10 -3
@@ -125,8 +125,12 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)return genpd->cached_power_down_ok; } - off_on_time_ns = genpd->power_off_latency_ns + - genpd->power_on_latency_ns; +/* +* Use the only available state, until multiple state support is added +* to the governor. +*/ + off_on_time_ns = genpd->states[0 ].power_off_latency_ns + + genpd->states[0 ].power_on_latency_ns; min_off_time_ns =-1 ;/*
@@ -203,8 +207,11 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)* The difference between the computed minimum subdomain or device off * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. +* Use the only available state, until multiple state support is added +* to the governor. */ - genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; + genpd->max_off_time_ns = min_off_time_ns - + genpd->states[0 ].power_on_latency_ns;return true; }
include/linux/pm_domain.h +10 -0
@@ -37,6 +37,12 @@ struct gpd_dev_ops {bool (*active_wakeup)(struct device *dev); }; +struct genpd_power_state { +char *name; +s64 power_off_latency_ns; +s64 power_on_latency_ns; +}; +struct generic_pm_domain {struct dev_pm_domain domain;/* PM domain operations */ struct list_head gpd_list_node;/* Node in the global PM domains list */
@@ -66,6 +72,10 @@ struct generic_pm_domain {void (*detach_dev)(struct generic_pm_domain *domain,struct device *dev);unsigned int flags;/* Bit field of configs for genpd */ +struct genpd_power_state *states; +unsigned int state_count;/* number of states */ +unsigned int state_idx;/* state that genpd will go to when off */ + };static inline struct generic_pm_domain *pd_to_genpd (struct dev_pm_domain *pd)
Signature · 1 line
2.1.4
RFC PATCH 1/27 PM / Domains: core changes for multiple states
Quoted 8 lines · Lina Iyer
From: Axel Haslam <[email protected]>
From: Axel Haslam <[email protected]>
Add the core changes to be able to declare multiple states. When trying to set a power domain to off, genpd will be able to choose from an array of states declared by the platform. The power on and off latencies are now tied to a state.
I would like to get an answer to *why* we need this.
For example, we should mention that some HWs supports these multiple states - at least.
Quoted 2 lines
States should be declared in ascending order from shallowest to deepest,
I guess *should* isn't good enough. Perhaps *shall* is better?
Quoted 5 lines
deepest meaning the state which takes longer to enter and exit.
the
power_offandpower_onfunction can use the 'state_idx' field of thegeneric_pm_domainstructure, to distinguish between the different states and act accordingly.
This needs some more explanation.
First, please use the wording of "callbacks", like ->power_on() to better describe what function you are talking about. Second, what is "state_idx"? What's does it really tell the SOC PM domain driver here?
Quoted 38 lines
Example:
static int
pd1_power_on(struct generic_pm_domain *domain){ /* domain->state_idx = state the domain is coming from */ }static int
pd1_power_off(struct generic_pm_domain *domain){ /* domain->state_idx = desired powered off state */ }const struct genpd_power_state pd_states[] = { { .name = "RET", .power_on_latency_ns = ON_LATENCY_FAST, .power_off_latency_ns = OFF_LATENCY_FAST, }, { .name = "DEEP_RET", .power_on_latency_ns = ON_LATENCY_MED, .power_off_latency_ns = OFF_LATENCY_MED, }, { .name = "OFF", .power_on_latency_ns = ON_LATENCY_SLOW, .power_off_latency_ns = OFF_LATENCY_SLOW, } };struct generic_pm_domain pd1 = { .name = "PD1", .power_on = pd1_power_on, .power_off = pd1_power_off, [...] };
Quoted 7 lines
int xxx_init_pm_domain(){
pd1->state = copy_of(pd_states); pd1->state_count = ARRAY_SIZE(pd_states); pm_genpd_init(pd1, true);}
Well, even if the above describes this quite good, I think it better belongs in the Documentation rather than in the change log.
Can we try to the improve the text in the change-log instead?
Quoted 18 lines
Signed-off-by Axel Haslam <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: Modified genpd state initialization and remove use of
save_state_latency_nsin genpd timing data]Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/domain.c | 136 +++++++++++++++++++++++++++++++++-- drivers/base/power/domain_governor.c | 13 +++- include/linux/pm_domain.h | 10 +++ 3 files changed, 151 insertions(+), 8 deletions(-)drivers/base/power/domain.c +0
-0
drivers/base/power/domain.c line 39
Quoted 6 lines
drivers/base/power/domain.c +3
-0@@ -34,6 +34,12 @@ __ret; \ }) +#define GENPD_MAX_NAME_SIZE 20 + +static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,
Please avoid forward declarations.
Also, we have just got rid of all *name* based genpd API/functions. I don't want us to start adding another.
Or perhaps it's just the name of the function that I don't like!? :-)
Quoted 58 lines
drivers/base/power/domain.c +11
-4+const struct genpd_power_state *st, + unsigned int st_count); +static LIST_HEAD (gpd_list);static DEFINE_MUTEX (gpd_list_lock);@@ -102,6 +108,7 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)static int genpd_power_on (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;@@ -118,10 +125,10 @@ 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 (elapsed_ns <= genpd->power_on_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret; - genpd->power_on_latency_ns = elapsed_ns; + genpd->states[state_idx].power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"on" , elapsed_ns);@@ -131,6 +138,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)static int genpd_power_off (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;@@ -147,10 +155,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); -if (elapsed_ns <= genpd->power_off_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)return ret; - genpd->power_off_latency_ns = elapsed_ns; + genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"off" , elapsed_ns);@@ -582,6 +590,8 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, || atomic_read(&genpd->sd_count) > 0)return ; +/* Choose the deepest state when suspending */ + genpd->state_idx = genpd->state_count -1 ;genpd_power_off (genpd, timed); genpd->status =GPD_STATE_POWER_OFF ;
drivers/base/power/domain.c static void genpd_free_dev_data(struct device *dev, · line 1218
Quoted 4 lines
drivers/base/power/domain.c +1
-0@@ -1205,6 +1215,61 @@ static void genpd_free_dev_data(struct device *dev,dev_pm_put_subsys_data (dev); } +static int genpd_alloc_states_data(struct generic_pm_domain *genpd,
In this patch I would rather just add *one* new "alloc" function and name it like below.
static int genpd_alloc_default_state(struct generic_pm_domain *genpd)
I assume the name I suggest for it, indicates what it needs to do and *not* needs to do.
Quoted 38 lines
drivers/base/power/domain.c +38
-0+const struct genpd_power_state *st, + unsigned int st_count) +{ + int ret = 0; +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) { + ret = -EINVAL ; + goto err; + } + +if (!st || (st_count <1 )) { + ret = -EINVAL ; + goto err; + } + +/* Allocate the local memory to keep the states for this genpd */ + genpd->states =kcalloc (st_count,sizeof (*st),GFP_KERNEL ); +if (!genpd->states) { + ret = -ENOMEM ; + goto err; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].power_on_latency_ns = + st[i].power_on_latency_ns; + genpd->states[i].power_off_latency_ns = + st[i].power_off_latency_ns; + } + +/* +* Copy the latency values To keep compatibility with +* platforms that are not converted to use the multiple states. +* This will be removed once all platforms are converted to use +* multiple states. note that non converted platforms will use the +* default single off state. +*/ +if (genpd->power_on_latency_ns !=0 )
drivers/base/power/domain.c static void genpd_free_dev_data(struct device *dev, · line 1266
Quoted 10 lines
drivers/base/power/domain.c +10
-0+ genpd->states[0 ].power_on_latency_ns = + genpd->power_on_latency_ns; + +if (genpd->power_off_latency_ns !=0 ) + genpd->states[0 ].power_off_latency_ns = + genpd->power_off_latency_ns; + + genpd->state_count = st_count; + +/* to save memory, Name allocation will happen if debug is enabled */
Urgh. :-)
I instead suggest we skip the entire "name" attribute" and just use the state_idx to provide the same information.
For sure at this point, this information won't be interpreted by "anybody". Reading an integer value instead of string, shouldn't be that difficult to understand.
Future wise, if we see that it could be beneficial to add the "name" attribute, we can do that then.
Removing the name attribute will also simplify this patch, quite much. Can you please do that.
Quoted 11 lines
drivers/base/power/domain.c +6
-0+pm_genpd_alloc_states_names (genpd, st, st_count); + +err: +return ret; +} +/** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. @@ -1459,6 +1524,15 @@ static int pm_genpd_default_restore_state(struct device *dev) void pm_genpd_init(struct generic_pm_domain *genpd,
drivers/base/power/domain.c static int pm_genpd_default_restore_state(struct device *dev) · line 1534
Quoted 10 lines
drivers/base/power/domain.c +8
-0struct dev_power_governor *gov, bool is_off) { + int ret; +static const struct genpd_power_state genpd_default_off[] = { + { + .name ="OFF" , + .power_off_latency_ns =0 , + .power_on_latency_ns =0 , + }, + };
I suggest you remove this and instead handle that within genpd_alloc_default_state().
Quoted 5 lines
drivers/base/power/domain.c +1
-0+if (IS_ERR_OR_NULL (genpd))return ;
drivers/base/power/domain.c void pm_genpd_init(struct generic_pm_domain *genpd, · line 1585
Quoted 9 lines
drivers/base/power/domain.c +6
-0@@ -1503,6 +1577,19 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->dev_ops.start = pm_clk_resume; } +/* +* Allocate the default state if genpd states +* are not already defined. +*/ +if (!genpd->state_count) { + ret =genpd_alloc_states_data (genpd, genpd_default_off,1 );
Call the new "genpd_alloc_default_state()" instead.
drivers/base/power/domain.c void pm_genpd_init(struct generic_pm_domain *genpd, · line 1591
Quoted 6 lines
drivers/base/power/domain.c +6
-0+if (ret) +return ; + } + +/* Assume the deepest state on init*/ + genpd->state_idx = genpd->state_count -1 ;
When the PM domain is powered on, in other words the genpd->status == GPD_STATE_ACTIVE, I guess this value doesn't matter much.
Although, what if the PM domain is powered off, don't you need to respect the value that might have been assigned by the SoC PM domain driver? Else you may at the next power on, indicate that you are coming from a state that's not the correct one.
Perhaps, you should only set genpd->state_idx = 0, from genpd_alloc_default_state() and in the other case leave the value as is?
Quoted 76 lines
drivers/base/power/domain.c +41
-1+mutex_lock (&gpd_list_lock);list_add (&genpd->gpd_list_node, &gpd_list);mutex_unlock (&gpd_list_lock);@@ -1822,6 +1909,33 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);#include <linux/kobject.h> static struct dentry *pm_genpd_debugfs_dir; +static int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) +return -EINVAL ; + +if (genpd->state_count != st_count) { +pr_err ("Invalid allocated state count\n" ); +return -EINVAL ; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].name =kstrndup (st[i].name, +GENPD_MAX_NAME_SIZE ,GFP_KERNEL ); +if (!genpd->states[i].name) { +pr_err ("%s Failed to allocate state %d name.\n" , + genpd->name, i); +return -ENOMEM ; + } + } + +return 0 ; +} +/* * TODO: This function is a slightly modified version of rtpm_status_show * from sysfs.c, so generalize it. @@ -1855,6 +1969,7 @@ static int pm_genpd_summary_one(struct seq_file *s, [GPD_STATE_ACTIVE] = "on", [GPD_STATE_POWER_OFF] = "off" }; +unsigned int state_idx = genpd->state_idx;struct pm_domain_data *pm_data;const char *kobj_path;struct gpd_link *link;@@ -1866,7 +1981,11 @@ static int pm_genpd_summary_one(struct seq_file *s,if (WARN_ON (genpd->status >=ARRAY_SIZE (status_lookup))) goto exit; -seq_printf (s,"%-30s %-15s " , genpd->name, status_lookup[genpd->status]); + +seq_printf (s,"%-30s %-15s " , genpd->name, + (genpd->status ==GPD_STATE_POWER_OFF ) ? + genpd->states[state_idx].name : + status_lookup[genpd->status]);/* * Modifications on the list require holding locks on both @@ -1954,4 +2073,11 @@ static void __exit pm_genpd_debug_exit(void) debugfs_remove_recursive(pm_genpd_debugfs_dir); }__exitcall (pm_genpd_debug_exit); +#else +static inline int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +return 0 ; +}#endif /* CONFIG_PM_ADVANCED_DEBUG */ drivers/base/power/domain_governor.c +0
-0
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 131
Quoted 9 lines
drivers/base/power/domain_governor.c +4
-2@@ -125,8 +125,12 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)return genpd->cached_power_down_ok; } - off_on_time_ns = genpd->power_off_latency_ns + - genpd->power_on_latency_ns; +/* +* Use the only available state, until multiple state support is added +* to the governor. +*/
So you want to delay this step into a later patch. I am fine with that, but you need to state that in the change-log.
Perhaps it's better to say that we will always try with the shallowest/deepest off state, and just ignore the other states in this phase?
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 133
Quoted 2 lines
drivers/base/power/domain_governor.c +2
-0+ off_on_time_ns = genpd->states[0 ].power_off_latency_ns + + genpd->states[0 ].power_on_latency_ns;
If you think my above idea seems reasonable, that should change the above to:
off_on_time_ns = genpd->states[genpd->state_count - 1].power_off_latency_ns +
genpd->states[genpd->state_count - 1].power_on_latency_ns;
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 214
Quoted 13 lines
drivers/base/power/domain_governor.c +4
-1min_off_time_ns =-1 ;/* @@ -203,8 +207,11 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)* The difference between the computed minimum subdomain or device off * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. +* Use the only available state, until multiple state support is added +* to the governor. */ - genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; + genpd->max_off_time_ns = min_off_time_ns - + genpd->states[0 ].power_on_latency_ns;
Ditto.
include/linux/pm_domain.h struct gpd_dev_ops { · line 41
Quoted 13 lines
drivers/base/power/domain_governor.c +0
-0return true; }include/linux/pm_domain.h +2
-0@@ -37,6 +37,12 @@ struct gpd_dev_ops {bool (*active_wakeup)(struct device *dev); }; +struct genpd_power_state { +char *name;
As stated, suggest to remove "name".
Quoted 19 lines
include/linux/pm_domain.h +8
-1+s64 power_off_latency_ns; +s64 power_on_latency_ns; +}; +struct generic_pm_domain {struct dev_pm_domain domain;/* PM domain operations */ struct list_head gpd_list_node;/* Node in the global PM domains list */ @@ -66,6 +72,10 @@ struct generic_pm_domain {void (*detach_dev)(struct generic_pm_domain *domain,struct device *dev);unsigned int flags;/* Bit field of configs for genpd */ +struct genpd_power_state *states; +unsigned int state_count;/* number of states */ +unsigned int state_idx;/* state that genpd will go to when off */ + };static inline struct generic_pm_domain *pd_to_genpd (struct dev_pm_domain *pd) --
Quoted 2 lines
2.1.4
Kind regards
Uffe
RFC PATCH 1/27 PM / Domains: core changes for multiple states
Quoted 182 lines · Ulf Hansson
Quoted 8 lines · Lina Iyer
From: Axel Haslam <[email protected]>
From: Axel Haslam <[email protected]>
Add the core changes to be able to declare multiple states. When trying to set a power domain to off, genpd will be able to choose from an array of states declared by the platform. The power on and off latencies are now tied to a state.
I would like to get an answer to *why* we need this.
For example, we should mention that some HWs supports these multiple states - at least.
Quoted 2 lines
States should be declared in ascending order from shallowest to deepest,
I guess *should* isn't good enough. Perhaps *shall* is better?
Quoted 5 lines
deepest meaning the state which takes longer to enter and exit.
the
power_offandpower_onfunction can use the 'state_idx' field of thegeneric_pm_domainstructure, to distinguish between the different states and act accordingly.This needs some more explanation.
First, please use the wording of "callbacks", like ->power_on() to better describe what function you are talking about. Second, what is "
state_idx"? What's does it really tell the SOC PM domain driver here?Quoted 38 lines
Example:
static int
pd1_power_on(struct generic_pm_domain *domain){ /* domain->state_idx = state the domain is coming from */ }static int
pd1_power_off(struct generic_pm_domain *domain){ /* domain->state_idx = desired powered off state */ }const struct genpd_power_state pd_states[] = { { .name = "RET", .power_on_latency_ns = ON_LATENCY_FAST, .power_off_latency_ns = OFF_LATENCY_FAST, }, { .name = "DEEP_RET", .power_on_latency_ns = ON_LATENCY_MED, .power_off_latency_ns = OFF_LATENCY_MED, }, { .name = "OFF", .power_on_latency_ns = ON_LATENCY_SLOW, .power_off_latency_ns = OFF_LATENCY_SLOW, } };struct generic_pm_domain pd1 = { .name = "PD1", .power_on = pd1_power_on, .power_off = pd1_power_off, [...] };Quoted 7 lines
int xxx_init_pm_domain(){
pd1->state = copy_of(pd_states); pd1->state_count = ARRAY_SIZE(pd_states); pm_genpd_init(pd1, true);}
Well, even if the above describes this quite good, I think it better belongs in the Documentation rather than in the change log.
Can we try to the improve the text in the change-log instead?
Quoted 18 lines
Signed-off-by Axel Haslam <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: Modified genpd state initialization and remove use of
save_state_latency_nsin genpd timing data]Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/domain.c | 136 +++++++++++++++++++++++++++++++++-- drivers/base/power/domain_governor.c | 13 +++- include/linux/pm_domain.h | 10 +++ 3 files changed, 151 insertions(+), 8 deletions(-)drivers/base/power/domain.c +0
-0drivers/base/power/domain.c line 39
Quoted 6 lines
drivers/base/power/domain.c +3
-0@@ -34,6 +34,12 @@ __ret; \ }) +#define GENPD_MAX_NAME_SIZE 20 + +static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,Please avoid forward declarations.
Also, we have just got rid of all *name* based genpd API/functions. I don't want us to start adding another.
Or perhaps it's just the name of the function that I don't like!? :-)
Quoted 58 lines
drivers/base/power/domain.c +11
-4+const struct genpd_power_state *st, + unsigned int st_count); +static LIST_HEAD (gpd_list);static DEFINE_MUTEX (gpd_list_lock);@@ -102,6 +108,7 @@ static void genpd_sd_counter_inc(struct generic_pm_domain *genpd)static int genpd_power_on (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;@@ -118,10 +125,10 @@ 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 (elapsed_ns <= genpd->power_on_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns)return ret; - genpd->power_on_latency_ns = elapsed_ns; + genpd->states[state_idx].power_on_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"on" , elapsed_ns);@@ -131,6 +138,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, bool timed)static int genpd_power_off (struct generic_pm_domain *genpd,bool timed) { +unsigned int state_idx = genpd->state_idx;ktime_t time_start;s64 elapsed_ns;int ret;@@ -147,10 +155,10 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed)return ret; elapsed_ns =ktime_to_ns (ktime_sub (ktime_get (), time_start)); -if (elapsed_ns <= genpd->power_off_latency_ns) +if (elapsed_ns <= genpd->states[state_idx].power_off_latency_ns)return ret; - genpd->power_off_latency_ns = elapsed_ns; + genpd->states[state_idx].power_off_latency_ns = elapsed_ns; genpd->max_off_time_changed = true;pr_debug ("%s: Power-%s latency exceeded, new value %lld ns\n" , genpd->name,"off" , elapsed_ns);@@ -582,6 +590,8 @@ static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd, || atomic_read(&genpd->sd_count) > 0)return ; +/* Choose the deepest state when suspending */ + genpd->state_idx = genpd->state_count -1 ;genpd_power_off (genpd, timed); genpd->status =GPD_STATE_POWER_OFF ;drivers/base/power/domain.c static void genpd_free_dev_data(struct device *dev, · line 1218
Quoted 4 lines
drivers/base/power/domain.c +1
-0@@ -1205,6 +1215,61 @@ static void genpd_free_dev_data(struct device *dev,dev_pm_put_subsys_data (dev); } +static int genpd_alloc_states_data(struct generic_pm_domain *genpd,In this patch I would rather just add *one* new "alloc" function and name it like below.
Quoted 4 lines
static int
genpd_alloc_default_state(struct generic_pm_domain *genpd)I assume the name I suggest for it, indicates what it needs to do and *not* needs to do.
Hi Ulf,
Thanks for your thorough review!
i will implement your suggestions for the next spin,
However I have a doubt about this comment, do you mean that i should get rid of genpd_alloc_states_data completly? i had a static number of states before but that was droped because o wasted memory if no states were needed.
if you just meant that it should be added in a separate patch, since i will be sqashing
this patch with "PM / Domains: make governor select deepest state" can
i keep it here? maybe it should go with the dt parsing patch from Marc?
Regards
Axel
Quoted 283 lines
Quoted 39 lines
drivers/base/power/domain.c +38
-0+const struct genpd_power_state *st, + unsigned int st_count) +{ + int ret = 0; +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) { + ret = -EINVAL ; + goto err; + } + +if (!st || (st_count <1 )) { + ret = -EINVAL ; + goto err; + } + +/* Allocate the local memory to keep the states for this genpd */ + genpd->states =kcalloc (st_count,sizeof (*st),GFP_KERNEL ); +if (!genpd->states) { + ret = -ENOMEM ; + goto err; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].power_on_latency_ns = + st[i].power_on_latency_ns; + genpd->states[i].power_off_latency_ns = + st[i].power_off_latency_ns; + } + +/* +* Copy the latency values To keep compatibility with +* platforms that are not converted to use the multiple states. +* This will be removed once all platforms are converted to use +* multiple states. note that non converted platforms will use the +* default single off state. +*/ +if (genpd->power_on_latency_ns !=0 )drivers/base/power/domain.c static void genpd_free_dev_data(struct device *dev, · line 1266
Quoted 10 lines
drivers/base/power/domain.c +10
-0+ genpd->states[0 ].power_on_latency_ns = + genpd->power_on_latency_ns; + +if (genpd->power_off_latency_ns !=0 ) + genpd->states[0 ].power_off_latency_ns = + genpd->power_off_latency_ns; + + genpd->state_count = st_count; + +/* to save memory, Name allocation will happen if debug is enabled */ Urgh. :-)
I instead suggest we skip the entire "name" attribute" and just use the
state_idxto provide the same information.For sure at this point, this information won't be interpreted by "anybody". Reading an integer value instead of string, shouldn't be that difficult to understand.
Future wise, if we see that it could be beneficial to add the "name" attribute, we can do that then.
Removing the name attribute will also simplify this patch, quite much. Can you please do that.
Quoted 11 lines
drivers/base/power/domain.c +6
-0+pm_genpd_alloc_states_names (genpd, st, st_count); + +err: +return ret; +} +/** * __pm_genpd_add_device - Add a device to an I/O PM domain. * @genpd: PM domain to add the device to. @@ -1459,6 +1524,15 @@ static int pm_genpd_default_restore_state(struct device *dev) void pm_genpd_init(struct generic_pm_domain *genpd,drivers/base/power/domain.c static int pm_genpd_default_restore_state(struct device *dev) · line 1534
Quoted 10 lines
drivers/base/power/domain.c +8
-0struct dev_power_governor *gov, bool is_off) { + int ret; +static const struct genpd_power_state genpd_default_off[] = { + { + .name ="OFF" , + .power_off_latency_ns =0 , + .power_on_latency_ns =0 , + }, + };I suggest you remove this and instead handle that within
genpd_alloc_default_state().Quoted 5 lines
drivers/base/power/domain.c +1
-0+if (IS_ERR_OR_NULL (genpd))return ;drivers/base/power/domain.c void pm_genpd_init(struct generic_pm_domain *genpd, · line 1585
Quoted 9 lines
drivers/base/power/domain.c +6
-0@@ -1503,6 +1577,19 @@ void pm_genpd_init(struct generic_pm_domain *genpd, genpd->dev_ops.start = pm_clk_resume; } +/* +* Allocate the default state if genpd states +* are not already defined. +*/ +if (!genpd->state_count) { + ret =genpd_alloc_states_data (genpd, genpd_default_off,1 );Call the new "
genpd_alloc_default_state()" instead.drivers/base/power/domain.c void pm_genpd_init(struct generic_pm_domain *genpd, · line 1591
Quoted 6 lines
drivers/base/power/domain.c +6
-0+if (ret) +return ; + } + +/* Assume the deepest state on init*/ + genpd->state_idx = genpd->state_count -1 ;When the PM domain is powered on, in other words the
genpd->status==GPD_STATE_ACTIVE, I guess this value doesn't matter much.Although, what if the PM domain is powered off, don't you need to respect the value that might have been assigned by the SoC PM domain driver? Else you may at the next power on, indicate that you are coming from a state that's not the correct one.
Perhaps, you should only set
genpd->state_idx= 0, fromgenpd_alloc_default_state()and in the other case leave the value as is?Quoted 76 lines
drivers/base/power/domain.c +41
-1+mutex_lock (&gpd_list_lock);list_add (&genpd->gpd_list_node, &gpd_list);mutex_unlock (&gpd_list_lock);@@ -1822,6 +1909,33 @@ EXPORT_SYMBOL_GPL(genpd_dev_pm_attach);#include <linux/kobject.h> static struct dentry *pm_genpd_debugfs_dir; +static int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +unsigned int i; + +if (IS_ERR_OR_NULL (genpd)) +return -EINVAL ; + +if (genpd->state_count != st_count) { +pr_err ("Invalid allocated state count\n" ); +return -EINVAL ; + } + +for (i =0 ; i < st_count; i++) { + genpd->states[i].name =kstrndup (st[i].name, +GENPD_MAX_NAME_SIZE ,GFP_KERNEL ); +if (!genpd->states[i].name) { +pr_err ("%s Failed to allocate state %d name.\n" , + genpd->name, i); +return -ENOMEM ; + } + } + +return 0 ; +} +/* * TODO: This function is a slightly modified version of rtpm_status_show * from sysfs.c, so generalize it. @@ -1855,6 +1969,7 @@ static int pm_genpd_summary_one(struct seq_file *s, [GPD_STATE_ACTIVE] = "on", [GPD_STATE_POWER_OFF] = "off" }; +unsigned int state_idx = genpd->state_idx;struct pm_domain_data *pm_data;const char *kobj_path;struct gpd_link *link;@@ -1866,7 +1981,11 @@ static int pm_genpd_summary_one(struct seq_file *s,if (WARN_ON (genpd->status >=ARRAY_SIZE (status_lookup))) goto exit; -seq_printf (s,"%-30s %-15s " , genpd->name, status_lookup[genpd->status]); + +seq_printf (s,"%-30s %-15s " , genpd->name, + (genpd->status ==GPD_STATE_POWER_OFF ) ? + genpd->states[state_idx].name : + status_lookup[genpd->status]);/* * Modifications on the list require holding locks on both @@ -1954,4 +2073,11 @@ static void __exit pm_genpd_debug_exit(void) debugfs_remove_recursive(pm_genpd_debugfs_dir); }__exitcall (pm_genpd_debug_exit); +#else +static inline int pm_genpd_alloc_states_names (struct generic_pm_domain *genpd, +const struct genpd_power_state *st, +unsigned int st_count) +{ +return 0 ; +}#endif /* CONFIG_PM_ADVANCED_DEBUG */ drivers/base/power/domain_governor.c +0
-0drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 131
Quoted 9 lines
drivers/base/power/domain_governor.c +4
-2@@ -125,8 +125,12 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)return genpd->cached_power_down_ok; } - off_on_time_ns = genpd->power_off_latency_ns + - genpd->power_on_latency_ns; +/* +* Use the only available state, until multiple state support is added +* to the governor. +*/ So you want to delay this step into a later patch. I am fine with that, but you need to state that in the change-log.
Perhaps it's better to say that we will always try with the shallowest/deepest off state, and just ignore the other states in this phase?
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 133
Quoted 2 lines
drivers/base/power/domain_governor.c +2
-0+ off_on_time_ns = genpd->states[0 ].power_off_latency_ns + + genpd->states[0 ].power_on_latency_ns;If you think my above idea seems reasonable, that should change the above to:
off_on_time_ns= genpd->states[genpd->state_count - 1].power_off_latency_ns +genpd->states[genpd->state_count - 1].power_on_latency_ns;drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 214
Quoted 13 lines
drivers/base/power/domain_governor.c +4
-1min_off_time_ns =-1 ;/* @@ -203,8 +207,11 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)* The difference between the computed minimum subdomain or device off * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. +* Use the only available state, until multiple state support is added +* to the governor. */ - genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns; + genpd->max_off_time_ns = min_off_time_ns - + genpd->states[0 ].power_on_latency_ns;Ditto.
include/linux/pm_domain.h struct gpd_dev_ops { · line 41
Quoted 13 lines
drivers/base/power/domain_governor.c +0
-0return true; }include/linux/pm_domain.h +2
-0@@ -37,6 +37,12 @@ struct gpd_dev_ops {bool (*active_wakeup)(struct device *dev); }; +struct genpd_power_state { +char *name;As stated, suggest to remove "name".
Quoted 19 lines
include/linux/pm_domain.h +8
-1+s64 power_off_latency_ns; +s64 power_on_latency_ns; +}; +struct generic_pm_domain {struct dev_pm_domain domain;/* PM domain operations */ struct list_head gpd_list_node;/* Node in the global PM domains list */ @@ -66,6 +72,10 @@ struct generic_pm_domain {void (*detach_dev)(struct generic_pm_domain *domain,struct device *dev);unsigned int flags;/* Bit field of configs for genpd */ +struct genpd_power_state *states; +unsigned int state_count;/* number of states */ +unsigned int state_idx;/* state that genpd will go to when off */ + };static inline struct generic_pm_domain *pd_to_genpd (struct dev_pm_domain *pd) --Quoted 2 lines
2.1.4
Kind regards
Uffe
RFC PATCH 1/27 PM / Domains: core changes for multiple states
[...]
Quoted 7 lines
Quoted 7 lines
Quoted 1 line
+static int genpd_alloc_states_data(struct
generic_pm_domain*genpd,In this patch I would rather just add *one* new "alloc" function and name it like below.
static int
genpd_alloc_default_state(struct generic_pm_domain *genpd)
Quoted 7 lines
Quoted 2 lines
I assume the name I suggest for it, indicates what it needs to do and *not* needs to do.
Hi Ulf,
Thanks for your thorough review!
i will implement your suggestions for the next spin,
Great, thanks!
Quoted 3 lines
However I have a doubt about this comment, do you mean that i should get rid of
genpd_alloc_states_datacompletly? i had a static number of states before
Yes, in this patch - but we still want to alloc the data on the heap.
Just alloc a state struct with size of one array.
Quoted 1 line
but that was droped because o wasted memory if no states were needed.
There will always be at least *one* state, so we shouldn't waste any memory following my suggestion above.
Quoted 3 lines
if you just meant that it should be added in a separate patch, since i will be sqashing
I think you need to go through review comments for each patch separately. In the end that might very well mean that you should completely drop some patches, rework some and even create some some new.
Quoted 2 lines
this patch with "PM / Domains: make governor select deepest state" can
i keep it here? maybe it should go with the dt parsing patch from Marc?
I don't think so.
Try to keep each logical change in a separate patch. That makes it easier to review and thus also to accept patches.
[...]
Kind regards
Uffe
RFC PATCH 2/27 PM / Domains: Allow domain power states to be read from DT
In reply to Lina IyerFrom: Marc Titinger <[email protected]>
From: Marc Titinger <[email protected]>
This patch allows cluster-level idle-states to being soaked in as generic domain power states, in order for the domain governor to chose the most efficient power state compatible with the device constraints. Similarly, devices can register power-states into the cluster domain, in a manner consistent with idle-states.
This is a attempt to address device-retention states for devices that are not hooked to runtime-pm, but feature a retention state handled by the same firmware that handles idle-states. For instance a L2 caches.
With Juno, in this example the idle-state 'cluster-sleep-0 ' is known from each cluster generic domain, as the deepest sate.
cat /sys/kernel/debug/pm_genpd/*
Domain State name Enter (ns) / Exit (ns)
-------------------------------------------------------------
a53_pd cluster-sleep-0 1500000 / 800000 a57_pd cluster-sleep-0 1500000 / 800000
domain status pstate slaves
/device runtime status
-----------------------------------------------------------------------a53_pd on
/devices/system/cpu/cpu0 active /devices/system/cpu/cpu3 suspended /devices/system/cpu/cpu4 suspended /devices/system/cpu/cpu5 suspended /devices/platform/D1 suspended
a57_pd cluster-sleep-0
/devices/system/cpu/cpu1 suspended
/devices/system/cpu/cpu2 suspended
[Lina: removed dependency on dynamic states, simplified initalization, added of_pm_genpd_init() API]
--- .../devicetree/bindings/power/power_domain.txt | 63 ++++++++++ drivers/base/power/domain.c | 128 +++++++++++++++++++++ include/linux/pm_domain.h | 6 + 3 files changed, 197 insertions(+)
Documentation/devicetree/bindings/power/power_domain.txt +63 -0
@@ -29,6 +29,44 @@ Optional properties: specified by this binding. More details about power domain specifier are available in the next section. +- power-states : A phandle of an idle-state that shall be soaked into a + generic domain power state. The power-state shall be + compatible with "linux,domain-state". + +==Power state== + +A PM domain power state describes an idle state of a domain and must be +have the following properties - + + - compatible + Usage: Required + Value type: <stringlist> + Definition: Must be "linux,domain-state" + + - entry-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency in + microseconds required to enter the idle state. + The exit-latency-us duration may be guaranteed + only after entry-latency-us has passed. + + - exit-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency + in microseconds required to exit the idle state. + + - wakeup-latency-us: + Usage: Not required if entry-latency-us and exit-latency-us + are provided. + Value type: <prop-encoded-array> + Definition: u32 value representing maximum delay between the + signaling the wakeup of the domain and the domain resuming + regular operation. + If omitted, this is assumed to be equal to: + entry-latency-us + exit-latency-us + Example: power: power-controller at 12340000 {
@@ -55,6 +93,31 @@ Example 2: #power-domain-cells = <1>; }; +Example 3: + + pm-domains { + a57_pd: a57_pd@ { + /* will have a57 platform ARM_PD_METHOD_OF_DECLARE*/ + compatible = "arm,pd","arm,cortex-a57"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + a53_pd: a53_pd@ { + /* will have a a53 platform ARM_PD_METHOD_OF_DECLARE*/ + compatible = "arm,pd","arm,cortex-a53"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + CLUSTER_SLEEP_0: power-state at 0 { + compatible = "pm-domain,power-state"; + entry-latency-us = <1000>; + exit-latency-us = <2000>; + }; + }; + + The nodes above define two power controllers: 'parent' and 'child'. Domains created by the 'child' power controller are subdomains of '0' power domain provided by the 'parent' power controller.
drivers/base/power/domain.c +128 -0
146 lines not on this page · raw message
include/linux/pm_domain.h +6 -0
@@ -213,6 +213,8 @@ struct generic_pm_domain *__of_genpd_xlate_onecell( void *data);int genpd_dev_pm_attach (struct device *dev); +int of_pm_genpd_init (struct device_node *dn,struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off);#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int __of_genpd_add_provider (struct device_node *np,genpd_xlate_t xlate,void *data)
@@ -234,6 +236,10 @@ static inline int genpd_dev_pm_attach(struct device *dev) {return -ENODEV ; } + +static inline int of_pm_genpd_init (struct device_node *dn, +struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off) {}#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int of_genpd_add_provider_simple(struct device_node *np,
Signature · 1 line
2.1.4
RFC PATCH 2/27 PM / Domains: Allow domain power states to be read from DT
Quoted 7 lines · Lina Iyer
From: Marc Titinger <[email protected]>
From: Marc Titinger <[email protected]>
This patch allows cluster-level idle-states to being soaked in as generic domain power states, in order for the domain governor to chose the most efficient power state compatible with the device constraints.
That was possible before this patch, although you are now making it possible to describe this in DT and also modifying genpd to parse this information.
Also, I don't get what this has to do with the governor. Of course that governor needs to care about the multiple states you added in patch1, but it should do that no matter if the information comes from DT or not, right.
Please re-phrase this to make it bit more clear.
Quoted 2 lines
Similarly, devices can register power-states into the cluster domain, in a manner consistent with idle-states.
I don't get this. Can you please elaborate?
Quoted 4 lines
This is a attempt to address device-retention states for devices that are not hooked to runtime-pm, but feature a retention state handled by the same firmware that handles idle-states. For instance a L2 caches.
I guess whether devices may use runtime PM or not, they still can be attached to a PM domain with multiple power states?
Quoted 24 lines
With Juno, in this example the idle-state 'cluster-sleep-0 ' is known from each cluster generic domain, as the deepest sate.
cat
/sys/kernel/debug/pm_genpd/*Domain State name Enter (ns) / Exit (ns)
-------------------------------------------------------------a53_pd cluster-sleep-0 1500000 / 800000 a57_pd cluster-sleep-0 1500000 / 800000domain status pstate slaves
/device runtime status
-----------------------------------------------------------------------a53_pdon/devices/system/cpu/cpu0 active /devices/system/cpu/cpu3 suspended /devices/system/cpu/cpu4 suspended /devices/system/cpu/cpu5 suspended /devices/platform/D1 suspended
a57_pdcluster-sleep-0
/devices/system/cpu/cpu1 suspended
/devices/system/cpu/cpu2 suspended
Quoted 6 lines
Signed-off-by Marc Titinger <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: removed dependency on dynamic states, simplified initalization, added
of_pm_genpd_init()API]--- .../devicetree/bindings/power/power_domain.txt | 63 ++++++++++
Please split the documentation of the suggested new DT bindings into a separate patch that is preceding this one.
Also, you need to send it to the DT maintainers.
Quoted 9 lines
drivers/base/power/domain.c | 128 +++++++++++++++++++++ include/linux/pm_domain.h | 6 + 3 files changed, 197 insertions(+)Documentation/devicetree/bindings/power/power_domain.txt +0
-0
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 34
Quoted 6 lines
Documentation/devicetree/bindings/power/power_domain.txt +3
-0@@ -29,6 +29,44 @@ Optional properties: specified by this binding. More details about power domain specifier are available in the next section. +- power-states : A phandle of an idle-state that shall be soaked into a + generic domain power state. The power-state shall be + compatible with "linux,domain-state".
Why mention the Linux specific "generic power domain" here?
Let's instead try to describe this without using specific terminology from Linux.
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 44
Quoted 10 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ +==Power state== + +A PM domain power state describes an idle state of a domain and must be +have the following properties - + + - compatible + Usage: Required + Value type: <stringlist> + Definition: Must be "linux,domain-state"
Why do you need a compatible string?
You already have the phandle available, I suppose you can use that instead of parsing for a new compatible string.
Quoted 14 lines
Documentation/devicetree/bindings/power/power_domain.txt +14
-0+ + - entry-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency in + microseconds required to enter the idle state. + The exit-latency-us duration may be guaranteed + only after entry-latency-us has passed. + + - exit-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency + in microseconds required to exit the idle state.
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 68
Quoted 10 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ + - wakeup-latency-us: + Usage: Not required if entry-latency-us and exit-latency-us + are provided. + Value type: <prop-encoded-array> + Definition: u32 value representing maximum delay between the + signaling the wakeup of the domain and the domain resuming + regular operation. + If omitted, this is assumed to be equal to: + entry-latency-us + exit-latency-us
I think we should decide which of the two alternatives to use, we shouldn't make it more complicated than needed.
Quoted 20 lines
Documentation/devicetree/bindings/power/power_domain.txt +13
-0+ Example: power: power-controller at 12340000 {@@ -55,6 +93,31 @@ Example 2: #power-domain-cells = <1>; }; +Example 3: + + pm-domains { + a57_pd: a57_pd@ { + /* will have a57 platform ARM_PD_METHOD_OF_DECLARE*/ + compatible = "arm,pd","arm,cortex-a57"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + a53_pd: a53_pd@ { + /* will have a a53 platform ARM_PD_METHOD_OF_DECLARE*/
Documentation/devicetree/bindings/power/power_domain.txt Example 2: · line 117
Quoted 10 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ compatible = "arm,pd","arm,cortex-a53"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + CLUSTER_SLEEP_0: power-state at 0 { + compatible = "pm-domain,power-state"; + entry-latency-us = <1000>; + exit-latency-us = <2000>; + };
I think we should also cover the case when power-controller has "#power-domain-cells = <[n>0]>".
Perhaps extending the first example is then better than adding a new one!?
Quoted 11 lines
Documentation/devicetree/bindings/power/power_domain.txt +3
-0+ }; + + The nodes above define two power controllers: 'parent' and 'child'. Domains created by the 'child' power controller are subdomains of '0' power domain provided by the 'parent' power controller.drivers/base/power/domain.c +0
-0
drivers/base/power/domain.c line 38
Quoted 4 lines
drivers/base/power/domain.c +1
-0@@ -35,6 +35,7 @@ })#define GENPD_MAX_NAME_SIZE 20 +#define GENPD_MAX_DOMAIN_STATES 10
Instead of setting a hard limit, let's pre-parse the DTB to get the number of power states. In that way, it should be fairly easy to convert the below code to be more dynamic.
Quoted 169 lines
drivers/base/power/domain.c +127
-0static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd, const struct genpd_power_state *st,@@ -1515,6 +1516,105 @@ static int pm_genpd_default_restore_state(struct device *dev)return cb ?cb (dev) :0 ; } +static const struct of_device_id power_state_match[] = { + { .compatible ="linux,domain-state" }, + { } +}; + +static int of_get_genpd_power_state (struct genpd_power_state *genpd_state, +struct device_node *state_node) +{ +const struct of_device_id *match_id; +int err =0 ; +u32 latency; + + match_id =of_match_node (power_state_match, state_node); +if (!match_id) +return -ENODEV ; + + err =of_property_read_u32 (state_node,"wakeup-latency-us" , &latency); +if (err) { +u32 entry_latency, exit_latency; + + err =of_property_read_u32 (state_node,"entry-latency-us" , + &entry_latency); +if (err) { +pr_debug (" * %s missing entry-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + err =of_property_read_u32 (state_node,"exit-latency-us" , + &exit_latency); +if (err) { +pr_debug (" * %s missing exit-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } +/* +* If wakeup-latency-us is missing, default to entry+exit +* latencies as defined in idle states bindings +*/ + latency = entry_latency + exit_latency; + } + + genpd_state->power_on_latency_ns =1000 * latency; + + err =of_property_read_u32 (state_node,"entry-latency-us" , &latency); +if (err) { +pr_debug (" * %s missing min-residency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + genpd_state->power_off_latency_ns =1000 * latency; + +return 0 ; +} + +static int of_genpd_device_parse_states (struct device_node *np, +struct genpd_power_state *genpd_states, +int *state_count) +{ +struct device_node *state_node; +int i, err =0 ; + +for (i =0 ;; i++) { +struct genpd_power_state genpd_state; + + state_node =of_parse_phandle (np,"power-states" , i); +if (!state_node) +break ; + +if (i ==GENPD_MAX_DOMAIN_STATES ) { + err = -ENOMEM ; +break ; + } + + err =of_get_genpd_power_state (&genpd_state, state_node); +if (err) { +pr_err + ("Parsing idle state node %s failed with err %d\n" , + state_node->full_name, err); + err = -EINVAL ; +break ; + } +#ifdef CONFIG_PM_ADVANCED_DEBUG + genpd_state.name =kstrndup (state_node->full_name, +GENPD_MAX_NAME_SIZE ,GFP_KERNEL ); +if (!genpd_state.name) + err = -ENOMEM ; +#endif +of_node_put (state_node); +memcpy (&genpd_states[i], &genpd_state,sizeof (genpd_state)); +#ifdef CONFIG_PM_ADVANCED_DEBUG +kfree (genpd_state.name); +#endif + } + *state_count = i; +return err; +} +/** * pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize. @@ -1596,6 +1696,34 @@ void pm_genpd_init(struct generic_pm_domain *genpd, }EXPORT_SYMBOL_GPL (pm_genpd_init); +int of_pm_genpd_init (struct device_node *dn,struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off) +{ +struct genpd_power_state states[GENPD_MAX_DOMAIN_STATES ] = { {0 } }; +int state_count =GENPD_MAX_DOMAIN_STATES ; +int ret; + +if (IS_ERR_OR_NULL (genpd)) +return -EINVAL ; + + ret =of_genpd_device_parse_states (dn, states, &state_count); +if (ret) { +pr_err ("Error parsing genpd states for %s\n" , genpd->name); +return ret; + } + + ret =genpd_alloc_states_data (genpd, states, state_count); +if (ret) { +pr_err ("Failed to allocate states for %s\n" , genpd->name); +return ret; + } + +pm_genpd_init (genpd, gov, is_off); + +return 0 ; +} +EXPORT_SYMBOL_GPL (of_pm_genpd_init); +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF /* * Device Tree based PM domain providers. include/linux/pm_domain.h +6
-1@@ -213,6 +213,8 @@ struct generic_pm_domain *__of_genpd_xlate_onecell( void *data);int genpd_dev_pm_attach (struct device *dev); +int of_pm_genpd_init (struct device_node *dn,struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off);#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int __of_genpd_add_provider (struct device_node *np,genpd_xlate_t xlate,void *data)@@ -234,6 +236,10 @@ static inline int genpd_dev_pm_attach(struct device *dev) {return -ENODEV ; } + +static inline int of_pm_genpd_init (struct device_node *dn, +struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off) {}#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int of_genpd_add_provider_simple(struct device_node *np, --
Quoted 2 lines
2.1.4
Instead of commenting directly to the changes above, let me instead raise a couple of concerns for the overall approach.
1)
The case when the power-controller node has "#power-domain-cells = <[n>0]>", isn't covered by $subject patch. I guess we should fix that!?
2)
I think that it needs to be the SOC PM domain driver that assigns the initial value for "genpd->state_idx", as I stated also for patch1 when I noticed pm_genpd_init() overrides that value.
If you share my view around this, it also means that the parsing of the DTB for "power-states" needs to be done prior the SOC PM domain driver assigns this initial value for "genpd->state_idx". Following your approach in $subject patch, means that its value needs to be updated *after* pm_genpd_init() have been called, perhaps not ideal.
Can we find a better way to deal with this? Maybe by instead of adding the of_pm_genpd_init() API, we should provide APIs which manages the DT parsing and the allocation of the struct genpd_power_state? Just an idea though.
Kind regards
Uffe
RFC PATCH 2/27 PM / Domains: Allow domain power states to be read from DT
Quoted 9 lines · Ulf Hansson
Quoted 7 lines · Lina Iyer
From: Marc Titinger <[email protected]>
From: Marc Titinger <[email protected]>
This patch allows cluster-level idle-states to being soaked in as generic domain power states, in order for the domain governor to chose the most efficient power state compatible with the device constraints.
Hi Ulf,
Quoted 3 lines
That was possible before this patch, although you are now making it possible to describe this in DT and also modifying genpd to parse this information.
please correct if I'm wrong, but I think that before you could delegate the power-off of a power domain to the platform code called from the cpu-idle driver by toggling enable of an idle-state from genpd.
What is proposed here is different: the idea is to prepare for an os-initiated mode for the cluster power management, where genpd keeps track of the last-man cpu (or more generally the last-man IP in the cluster) and calls the platform code to enter the most effective power state compatible with the QoS constraints for devices in the power-domain. IOW it is genpd that controls the plaftormn code to go to sleep or retention state X, instead of cpuidle-driver calling the method indexed for each state.
Quoted 6 lines
Also, I don't get what this has to do with the governor. Of course that governor needs to care about the multiple states you added in patch1, but it should do that no matter if the information comes from DT or not, right.
Agreed, that part should go to the cover-letter instead.
Quoted 1 line
Please re-phrase this to make it bit more clear.
Ok.
Quoted 5 lines
Quoted 3 lines
Similarly, devices can register power-states into the cluster domain, in a manner consistent with idle-states.
I don't get this. Can you please elaborate?
Alexes addition of "power states" to the power domains was to have a better representation of features of power controllers. For instance QoS may prevent to enter/exit complete power-off, but setting the core voltage to say 0.7v is feasible in terms of timing, and still better than full-power-on etc... Hence the domain has a list of possible states to chose upon, between full power-on and full-power-off, and genpd will call the platform code for this.
Now, this patch maps the idle-states as possible power states for the
domain: upon the last CPU runtime_put, the domain can chose the deepest state that can be reached given the enter/exit time available, and call the platform code for this. This selected state can be any of:
* cluster-sleep (power OFF)
* cluster-retention A (L2RAM retention for instance)
* cluster-retention B (some device is still on, like PMU or bus analyzer, healthckeck IP etc...)
...
* cluster-on, but lower voltage A
* cluster-on, but lower voltage B
etc...
Put short: CPUs, like any other devices in the domain may register a power state.
Quoted 8 lines
Quoted 5 lines
This is a attempt to address device-retention states for devices that are not hooked to runtime-pm, but feature a retention state handled by the same firmware that handles idle-states. For instance a L2 caches.
I guess whether devices may use runtime PM or not, they still can be attached to a PM domain with multiple power states?
yes.
Quoted 50 lines
Quoted 25 lines
With Juno, in this example the idle-state 'cluster-sleep-0 ' is known from each cluster generic domain, as the deepest sate.
cat
/sys/kernel/debug/pm_genpd/*Domain State name Enter (ns) / Exit (ns)
-------------------------------------------------------------a53_pd cluster-sleep-0 1500000 / 800000 a57_pd cluster-sleep-0 1500000 / 800000domain status pstate slaves
/device runtime status
-----------------------------------------------------------------------a53_pdon/devices/system/cpu/cpu0 active /devices/system/cpu/cpu3 suspended /devices/system/cpu/cpu4 suspended /devices/system/cpu/cpu5 suspended /devices/platform/D1 suspended
a57_pdcluster-sleep-0
/devices/system/cpu/cpu1 suspended
/devices/system/cpu/cpu2 suspendedQuoted 6 lines
Signed-off-by Marc Titinger <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: removed dependency on dynamic states, simplified initalization, added
of_pm_genpd_init()API]--- .../devicetree/bindings/power/power_domain.txt | 63 ++++++++++Please split the documentation of the suggested new DT bindings into a separate patch that is preceding this one.
Also, you need to send it to the DT maintainers.
Quoted 12 lines
drivers/base/power/domain.c | 128 +++++++++++++++++++++ include/linux/pm_domain.h | 6 + 3 files changed, 197 insertions(+)Documentation/devicetree/bindings/power/power_domain.txt +0
-0@@ -29,6 +29,44 @@ Optional properties: specified by this binding. More details about power domain specifier are available in the next section.
Quoted 8 lines
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 34
Quoted 3 lines
Documentation/devicetree/bindings/power/power_domain.txt +3
-0+- power-states : A phandle of an idle-state that shall be soaked into a + generic domain power state. The power-state shall be + compatible with "linux,domain-state".Why mention the Linux specific "generic power domain" here?
Let's instead try to describe this without using specific terminology from Linux.
Hmm, I will need Lina's help to answer this one. Following a previous review actually I dropped this power-states compatible, to stick with idle-states.
Quoted 36 lines
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 44
Quoted 11 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ +==Power state== + +A PM domain power state describes an idle state of a domain and must be +have the following properties - + + - compatible + Usage: Required + Value type: <stringlist> + Definition: Must be "linux,domain-state"Why do you need a compatible string?
You already have the phandle available, I suppose you can use that instead of parsing for a new compatible string.
Quoted 19 lines
Documentation/devicetree/bindings/power/power_domain.txt +19
-0+ + - entry-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency in + microseconds required to enter the idle state. + The exit-latency-us duration may be guaranteed + only after entry-latency-us has passed. + + - exit-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency + in microseconds required to exit the idle state. + + - wakeup-latency-us: + Usage: Not required if entry-latency-us and exit-latency-us + are provided. + Value type: <prop-encoded-array>
Quoted 8 lines
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 68
Quoted 5 lines
Documentation/devicetree/bindings/power/power_domain.txt +5
-0+ Definition: u32 value representing maximum delay between the + signaling the wakeup of the domain and the domain resuming + regular operation. + If omitted, this is assumed to be equal to: + entry-latency-us + exit-latency-usI think we should decide which of the two alternatives to use, we shouldn't make it more complicated than needed.
Agreed.
Quoted 233 lines
Quoted 21 lines
Documentation/devicetree/bindings/power/power_domain.txt +13
-0+ Example: power: power-controller at 12340000 {@@ -55,6 +93,31 @@ Example 2: #power-domain-cells = <1>; }; +Example 3: + + pm-domains { + a57_pd: a57_pd@ { + /* will have a57 platform ARM_PD_METHOD_OF_DECLARE*/ + compatible = "arm,pd","arm,cortex-a57"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + a53_pd: a53_pd@ { + /* will have a a53 platform ARM_PD_METHOD_OF_DECLARE*/Documentation/devicetree/bindings/power/power_domain.txt Example 2: · line 117
Quoted 10 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ compatible = "arm,pd","arm,cortex-a53"; + #power-domain-cells = <0>; + power-states = <&CLUSTER_SLEEP_0>; + }; + + CLUSTER_SLEEP_0: power-state at 0 { + compatible = "pm-domain,power-state"; + entry-latency-us = <1000>; + exit-latency-us = <2000>; + };I think we should also cover the case when power-controller has "#power-domain-cells = <[n>0]>".
Perhaps extending the first example is then better than adding a new one!?
Quoted 11 lines
Documentation/devicetree/bindings/power/power_domain.txt +3
-0+ }; + + The nodes above define two power controllers: 'parent' and 'child'. Domains created by the 'child' power controller are subdomains of '0' power domain provided by the 'parent' power controller.drivers/base/power/domain.c +0
-0drivers/base/power/domain.c line 38
Quoted 4 lines
drivers/base/power/domain.c +1
-0@@ -35,6 +35,7 @@ })#define GENPD_MAX_NAME_SIZE 20 +#define GENPD_MAX_DOMAIN_STATES 10Instead of setting a hard limit, let's pre-parse the DTB to get the number of power states. In that way, it should be fairly easy to convert the below code to be more dynamic.
Quoted 169 lines
drivers/base/power/domain.c +127
-0static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd, const struct genpd_power_state *st,@@ -1515,6 +1516,105 @@ static int pm_genpd_default_restore_state(struct device *dev)return cb ?cb (dev) :0 ; } +static const struct of_device_id power_state_match[] = { + { .compatible ="linux,domain-state" }, + { } +}; + +static int of_get_genpd_power_state (struct genpd_power_state *genpd_state, +struct device_node *state_node) +{ +const struct of_device_id *match_id; +int err =0 ; +u32 latency; + + match_id =of_match_node (power_state_match, state_node); +if (!match_id) +return -ENODEV ; + + err =of_property_read_u32 (state_node,"wakeup-latency-us" , &latency); +if (err) { +u32 entry_latency, exit_latency; + + err =of_property_read_u32 (state_node,"entry-latency-us" , + &entry_latency); +if (err) { +pr_debug (" * %s missing entry-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + err =of_property_read_u32 (state_node,"exit-latency-us" , + &exit_latency); +if (err) { +pr_debug (" * %s missing exit-latency-us property\n" , + state_node->full_name); +return -EINVAL ; + } +/* +* If wakeup-latency-us is missing, default to entry+exit +* latencies as defined in idle states bindings +*/ + latency = entry_latency + exit_latency; + } + + genpd_state->power_on_latency_ns =1000 * latency; + + err =of_property_read_u32 (state_node,"entry-latency-us" , &latency); +if (err) { +pr_debug (" * %s missing min-residency-us property\n" , + state_node->full_name); +return -EINVAL ; + } + + genpd_state->power_off_latency_ns =1000 * latency; + +return 0 ; +} + +static int of_genpd_device_parse_states (struct device_node *np, +struct genpd_power_state *genpd_states, +int *state_count) +{ +struct device_node *state_node; +int i, err =0 ; + +for (i =0 ;; i++) { +struct genpd_power_state genpd_state; + + state_node =of_parse_phandle (np,"power-states" , i); +if (!state_node) +break ; + +if (i ==GENPD_MAX_DOMAIN_STATES ) { + err = -ENOMEM ; +break ; + } + + err =of_get_genpd_power_state (&genpd_state, state_node); +if (err) { +pr_err + ("Parsing idle state node %s failed with err %d\n" , + state_node->full_name, err); + err = -EINVAL ; +break ; + } +#ifdef CONFIG_PM_ADVANCED_DEBUG + genpd_state.name =kstrndup (state_node->full_name, +GENPD_MAX_NAME_SIZE ,GFP_KERNEL ); +if (!genpd_state.name) + err = -ENOMEM ; +#endif +of_node_put (state_node); +memcpy (&genpd_states[i], &genpd_state,sizeof (genpd_state)); +#ifdef CONFIG_PM_ADVANCED_DEBUG +kfree (genpd_state.name); +#endif + } + *state_count = i; +return err; +} +/** * pm_genpd_init - Initialize a generic I/O PM domain object. * @genpd: PM domain object to initialize. @@ -1596,6 +1696,34 @@ void pm_genpd_init(struct generic_pm_domain *genpd, }EXPORT_SYMBOL_GPL (pm_genpd_init); +int of_pm_genpd_init (struct device_node *dn,struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off) +{ +struct genpd_power_state states[GENPD_MAX_DOMAIN_STATES ] = { {0 } }; +int state_count =GENPD_MAX_DOMAIN_STATES ; +int ret; + +if (IS_ERR_OR_NULL (genpd)) +return -EINVAL ; + + ret =of_genpd_device_parse_states (dn, states, &state_count); +if (ret) { +pr_err ("Error parsing genpd states for %s\n" , genpd->name); +return ret; + } + + ret =genpd_alloc_states_data (genpd, states, state_count); +if (ret) { +pr_err ("Failed to allocate states for %s\n" , genpd->name); +return ret; + } + +pm_genpd_init (genpd, gov, is_off); + +return 0 ; +} +EXPORT_SYMBOL_GPL (of_pm_genpd_init); +#ifdef CONFIG_PM_GENERIC_DOMAINS_OF /* * Device Tree based PM domain providers. include/linux/pm_domain.h +6
-1@@ -213,6 +213,8 @@ struct generic_pm_domain *__of_genpd_xlate_onecell( void *data);int genpd_dev_pm_attach (struct device *dev); +int of_pm_genpd_init (struct device_node *dn,struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off);#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int __of_genpd_add_provider (struct device_node *np,genpd_xlate_t xlate,void *data)@@ -234,6 +236,10 @@ static inline int genpd_dev_pm_attach(struct device *dev) {return -ENODEV ; } + +static inline int of_pm_genpd_init (struct device_node *dn, +struct generic_pm_domain *genpd, +struct dev_power_governor *gov,bool is_off) {}#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */ static inline int of_genpd_add_provider_simple(struct device_node *np, --Quoted 2 lines
2.1.4
Instead of commenting directly to the changes above, let me instead raise a couple of concerns for the overall approach.
Quoted 5 lines
1)
The case when the power-controller node has "#power-domain-cells = <[n>0]>", isn't covered by $subject patch. I guess we should fix that!?
Indeed, I must confess a bit of procrastination here, my apologies :)
Quoted 11 lines
2)
I think that it needs to be the SOC PM domain driver that assigns the initial value for "genpd->state_idx", as I stated also for patch1 when I noticedpm_genpd_init()overrides that value.dev_pm_domain_attach
If you share my view around this, it also means that the parsing of the DTB for "power-states" needs to be done prior the SOC PM domain driver assigns this initial value for "genpd->state_idx". Following your approach in $subject patch, means that its value needs to be updated *after*pm_genpd_init()have been called, perhaps not ideal.
Quoted 4 lines
Can we find a better way to deal with this? Maybe by instead of adding the
of_pm_genpd_init()API, we should provide APIs which manages the DT parsing and the allocation of the structgenpd_power_state? Just an idea though.
In my original patch, I was adding the states from the dev_pm_domain_attach (genpd_devm_pm_attach) API. But since it's called before we know if a matching device driver could be successfully probed anyway, I reckon those device-bound states can be parsed/sorted early on as well.
Regarding the initial (exploratory) purpose of my series of handling IPs with special retention states with genpd, this can probably be done more simply with the existing runtime PM framework as discussed in a previous review, let me quote Kevin here:
<quote>
... I think has a first pass, rather than add the additional complexity required for a dynamic set of genpd states, I think it's much better to start by assuming that all devices in the domain that affect the domain state should have an associated device and a driver using runtime PM. For example, performance montitoring units (PMUs) like CoreSight have this same issue, and the upstream support for those is already using runtime PM.
For really simple/dump devices like L2$ or similar, it might be that we don't need a real driver, but instead the CPU "devices" could do gets/puts on any dependent devices directly
</quote>
I will see with Lina how much I shall keep from this patch that is useful to the purpose of her series for os-initiated mode.
Many thanks,
Marc.
Quoted 5 lines
Kind regards
Uffe
RFC PATCH 2/27 PM / Domains: Allow domain power states to be read from DT
Quoted 17 lines · Marc Titinger
Quoted 16 lines · Ulf Hansson
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 34
Quoted 11 lines · Lina Iyer
Documentation/devicetree/bindings/power/power_domain.txt +3
-0@@ -29,6 +29,44 @@ Optional properties: specified by this binding. More details about power domain specifier are available in the next section. +- power-states : A phandle of an idle-state that shall be soaked into a + generic domain power state. The power-state shall be + compatible with "linux,domain-state".Why mention the Linux specific "generic power domain" here?
Quoted 7 lines
Quoted 2 lines
Let's instead try to describe this without using specific terminology from Linux.
Hmm, I will need Lina's help to answer this one. Following a previous review actually I dropped this power-states compatible, to stick with idle-states.
I didnt want this to be ARM specific. An arm,idle-state is too specific to be reused for generic PM domains, hence this compatible. We dont need a compatible property, but I thought it would be nice to have the property to give structure to the power state node.
Quoted 14 lines
Quoted 14 lines
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 44
Quoted 11 lines
Documentation/devicetree/bindings/power/power_domain.txt +10
-0+ +==Power state== + +A PM domain power state describes an idle state of a domain and must be +have the following properties - + + - compatible + Usage: Required + Value type: <stringlist> + Definition: Must be "linux,domain-state"Why do you need a compatible string?
Quoted 3 lines
Quoted 3 lines
You already have the phandle available, I suppose you can use that instead of parsing for a new compatible string.
Hmm.. okay.
Quoted 28 lines
Quoted 19 lines
Quoted 19 lines
Documentation/devicetree/bindings/power/power_domain.txt +19
-0+ + - entry-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency in + microseconds required to enter the idle state. + The exit-latency-us duration may be guaranteed + only after entry-latency-us has passed. + + - exit-latency-us + Usage: Not required if wakeup-latency-us is provided. + Value type: <prop-encoded-array> + Definition: u32 value representing worst case latency + in microseconds required to exit the idle state. + + - wakeup-latency-us: + Usage: Not required if entry-latency-us and exit-latency-us + are provided. + Value type: <prop-encoded-array>Quoted 8 lines
Documentation/devicetree/bindings/power/power_domain.txt Optional properties: · line 68
Quoted 5 lines
Documentation/devicetree/bindings/power/power_domain.txt +5
-0+ Definition: u32 value representing maximum delay between the + signaling the wakeup of the domain and the domain resuming + regular operation. + If omitted, this is assumed to be equal to: + entry-latency-us + exit-latency-usI think we should decide which of the two alternatives to use, we shouldn't make it more complicated than needed.
Quoted 2 lines
Agreed.
I like the entry + exit option.
Thanks,
Lina
RFC PATCH 2/27 PM / Domains: Allow domain power states to be read from DT
In reply to Marc TitingerQuoted 40 lines · Marc Titinger
Quoted 6 lines · Ulf Hansson
Quoted 2 lines · Lina Iyer
Similarly, devices can register power-states into the cluster domain, in a manner consistent with idle-states.
I don't get this. Can you please elaborate?
Alexes addition of "power states" to the power domains was to have a better representation of features of power controllers. For instance QoS may prevent to enter/exit complete power-off, but setting the core voltage to say 0.7v is feasible in terms of timing, and still better than full-power-on etc... Hence the domain has a list of possible states to chose upon, between full power-on and full-power-off, and genpd will call the platform code for this.
Now, this patch maps the idle-states as possible power states for the
domain: upon the last CPUruntime_put, the domain can chose the deepest
state that can be reached given the enter/exit time available, and call the platform code for this. This selected state can be any of:
* cluster-sleep (power OFF)
* cluster-retention A (L2RAM retention for instance)
* cluster-retention B (some device is still on, like PMU or bus analyzer, healthckeck IP etc...)
...
* cluster-on, but lower voltage A
* cluster-on, but lower voltage B
etc...Put short: CPUs, like any other devices in the domain may register a power state.
Quoted 6 lines
Quoted 5 lines
This is a attempt to address device-retention states for devices that are not hooked to runtime-pm, but feature a retention state handled by the same firmware that handles idle-states. For instance a L2 caches.
Quoted 6 lines
Quoted 2 lines
I guess whether devices may use runtime PM or not, they still can be attached to a PM domain with multiple power states?
yes.
From what I understand, it seems like you want to have a constraint on a
domain state based on the device's idle state. This seems more like a job for a governor. You could write a governor that chooses the domain state based on these dependencies.
I am not sure this can be solved generically without increasing the complexity of genpd idle states.
Thanks,
Lina
RFC PATCH 3/27 PM / Domain: Add additional state specific param
In reply to Lina IyerAllow domain states to hold additional state related data in a u32 value. This may be used by the platform driver.
--- Documentation/devicetree/bindings/power/power_domain.txt | 6 ++++++ drivers/base/power/domain.c | 6 ++++++ include/linux/pm_domain.h | 1 + 3 files changed, 13 insertions(+)
Documentation/devicetree/bindings/power/power_domain.txt +6 -0
@@ -67,6 +67,12 @@ have the following properties - If omitted, this is assumed to be equal to: entry-latency-us + exit-latency-us + - state-param: + Usage: Optional + Value type: <prop-encoded-array> + Definition: A u32 value as defined by the state. May be used + by the driver to hold state related u32 data. + Example: power: power-controller at 12340000 {
drivers/base/power/domain.c +6 -0
@@ -1245,6 +1245,7 @@ static int genpd_alloc_states_data(struct generic_pm_domain *genpd, st[i].power_on_latency_ns; genpd->states[i].power_off_latency_ns = st[i].power_off_latency_ns; + genpd->states[i].param = st[i].param; }/*
@@ -1527,6 +1528,7 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,const struct of_device_id *match_id;int err =0 ;u32 latency; +u32 param; match_id =of_match_node (power_state_match, state_node);if (!match_id)
@@ -1567,6 +1569,10 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,return -EINVAL ; } + err =of_property_read_u32 (state_node,"state-param" , ¶m); +if (!err) + genpd_state->param = param; + genpd_state->power_off_latency_ns =1000 * latency;return 0 ;
include/linux/pm_domain.h +1 -0
@@ -41,6 +41,7 @@ struct genpd_power_state {char *name;s64 power_off_latency_ns;s64 power_on_latency_ns; +u32 param; };struct generic_pm_domain {
Signature · 1 line
2.1.4
RFC PATCH 3/27 PM / Domain: Add additional state specific param
Quoted 2 lines · Lina Iyer
Allow domain states to hold additional state related data in a u32 value. This may be used by the platform driver.
Should probably expand the changelog here to give some examples how how this might be used by the platform driver.
Kevin
RFC PATCH 4/27 PM / Domains: make governor select deepest state
In reply to Lina IyerFrom: Axel Haslam <[email protected]>
Now that the structures of genpd can support multiple state definitions, add the logic in the governor to select the deepest possible state when powering down.
For this, create the new function power_down_ok_for_state which will test if a particular state will not violate the devices and sub-domains constraints.
default_power_down_ok is modified to try each state starting from the deepest until a valid state is found or there are no more states to test.
the resulting state will be valid until there are latency or constraint changes, thus, we can avoid looping every power_down, and use the cached results instead.
--- drivers/base/power/domain_governor.c | 75 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 30 deletions(-)
drivers/base/power/domain_governor.c +45 -30
@@ -98,7 +98,8 @@ static bool default_stop_ok(struct device *dev)* * This routine must be executed under the PM domain's lock. */ -static bool default_power_down_ok (struct dev_pm_domain *pd) +static bool power_down_ok_for_state (struct dev_pm_domain *pd, +unsigned int state) {struct generic_pm_domain *genpd =pd_to_genpd (pd);struct gpd_link *link;
@@ -106,31 +107,9 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)s64 min_off_time_ns;s64 off_on_time_ns; -if (genpd->max_off_time_changed) { -struct gpd_link *link; + off_on_time_ns = genpd->states[state].power_off_latency_ns + + genpd->states[state].power_on_latency_ns; -/* -* We have to invalidate the cached results for the masters, so -* use the observation that default_power_down_ok() is not -* going to be called for any master until this instance -* returns. -*/ -list_for_each_entry (link, &genpd->slave_links, slave_node) - link->master->max_off_time_changed = true; - - genpd->max_off_time_changed = false; - genpd->cached_power_down_ok = false; - genpd->max_off_time_ns =-1 ; - }else { -return genpd->cached_power_down_ok; - } - -/* -* Use the only available state, until multiple state support is added -* to the governor. -*/ - off_on_time_ns = genpd->states[0 ].power_off_latency_ns + - genpd->states[0 ].power_on_latency_ns; min_off_time_ns =-1 ;/*
@@ -193,8 +172,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) min_off_time_ns = constraint_ns; } - genpd->cached_power_down_ok = true; -/* * If the computed minimum device off time is negative, there are no * latency constraints, so the domain can spend arbitrary time in the
@@ -207,14 +184,52 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)* The difference between the computed minimum subdomain or device off * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. -* Use the only available state, until multiple state support is added -* to the governor. */ genpd->max_off_time_ns = min_off_time_ns - - genpd->states[0 ].power_on_latency_ns; + genpd->states[state].power_on_latency_ns;return true; } +static bool default_power_down_ok (struct dev_pm_domain *pd) +{ +struct generic_pm_domain *genpd =pd_to_genpd (pd); +unsigned int last_state_idx = genpd->state_count -1 ; +struct gpd_link *link; +bool retval = false; +unsigned int i; + +/* +* if there was no change on max_off_time, we can return the +* cached value and we dont need to find a new target_state +*/ +if (!genpd->max_off_time_changed) +return genpd->cached_power_down_ok; + +/* +* We have to invalidate the cached results for the masters, so +* use the observation that default_power_down_ok() is not +* going to be called for any master until this instance +* returns. +*/ +list_for_each_entry (link, &genpd->slave_links, slave_node) + link->master->max_off_time_changed = true; + + genpd->max_off_time_ns =-1 ; + genpd->max_off_time_changed = false; + +/* find a state to power down to, starting from the deepest */ +for (i =0 ; i < genpd->state_count; i++) { +if (power_down_ok_for_state (pd, last_state_idx - i)) { + genpd->state_idx = last_state_idx - i; + retval = true; +break ; + } + } + + genpd->cached_power_down_ok = retval; +return retval; +} +static bool always_on_power_down_ok (struct dev_pm_domain *domain) {return false;
Signature · 1 line
2.1.4
RFC PATCH 4/27 PM / Domains: make governor select deepest state
Quoted 13 lines · Lina Iyer
From: Axel Haslam <[email protected]>
Now that the structures of genpd can support multiple state definitions, add the logic in the governor to select the deepest possible state when powering down.
For this, create the new function
power_down_ok_for_statewhich will test if a particular state will not violate the devices and sub-domains constraints.
default_power_down_okis modified to try each state starting from the deepest until a valid state is found or there are no more states to test.
Quoted 8 lines
the resulting state will be valid until there are latency or constraint changes, thus, we can avoid looping every
power_down, and use the cached results instead.Signed-off-by Axel Haslam <[email protected]>--- drivers/base/power/domain_governor.c | 75 +++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 30 deletions(-)
Browsing the changes here and I think it looks good and quite trivial.
I would therefore suggest that you fold this change into patch1 as it's where it really belongs. Can you please do that?
drivers/base/power/domain_governor.c static bool default_stop_ok(struct device *dev) · line 102
Quoted 12 lines
drivers/base/power/domain_governor.c +2
-1@@ -98,7 +98,8 @@ static bool default_stop_ok(struct device *dev)* * This routine must be executed under the PM domain's lock. */ -static bool default_power_down_ok (struct dev_pm_domain *pd) +static bool power_down_ok_for_state (struct dev_pm_domain *pd, +unsigned int state)
I suggest you re-name this function "__default_power_down_ok()" instead.
Quoted 61 lines
drivers/base/power/domain_governor.c +5
-29{struct generic_pm_domain *genpd =pd_to_genpd (pd);struct gpd_link *link;@@ -106,31 +107,9 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)s64 min_off_time_ns;s64 off_on_time_ns; -if (genpd->max_off_time_changed) { -struct gpd_link *link; + off_on_time_ns = genpd->states[state].power_off_latency_ns + + genpd->states[state].power_on_latency_ns; -/* -* We have to invalidate the cached results for the masters, so -* use the observation that default_power_down_ok() is not -* going to be called for any master until this instance -* returns. -*/ -list_for_each_entry (link, &genpd->slave_links, slave_node) - link->master->max_off_time_changed = true; - - genpd->max_off_time_changed = false; - genpd->cached_power_down_ok = false; - genpd->max_off_time_ns =-1 ; - }else { -return genpd->cached_power_down_ok; - } - -/* -* Use the only available state, until multiple state support is added -* to the governor. -*/ - off_on_time_ns = genpd->states[0 ].power_off_latency_ns + - genpd->states[0 ].power_on_latency_ns; min_off_time_ns =-1 ;/* @@ -193,8 +172,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd) min_off_time_ns = constraint_ns; } - genpd->cached_power_down_ok = true; -/* * If the computed minimum device off time is negative, there are no * latency constraints, so the domain can spend arbitrary time in the @@ -207,14 +184,52 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)* The difference between the computed minimum subdomain or device off * time and the time needed to turn the domain on is the maximum * theoretical time this domain can spend in the "off" state. -* Use the only available state, until multiple state support is added -* to the governor. */ genpd->max_off_time_ns = min_off_time_ns - - genpd->states[0 ].power_on_latency_ns; + genpd->states[state].power_on_latency_ns;return true; } +static bool default_power_down_ok (struct dev_pm_domain *pd) +{
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 204
Quoted 10 lines
drivers/base/power/domain_governor.c +10
-0+struct generic_pm_domain *genpd =pd_to_genpd (pd); +unsigned int last_state_idx = genpd->state_count -1 ; +struct gpd_link *link; +bool retval = false; +unsigned int i; + +/* +* if there was no change on max_off_time, we can return the +* cached value and we dont need to find a new target_state +*/
The related code speaks for itself, I think you can drop the above comment.
Quoted 6 lines
drivers/base/power/domain_governor.c +6
-0+if (!genpd->max_off_time_changed) +return genpd->cached_power_down_ok; + +/* +* We have to invalidate the cached results for the masters, so +* use the observation that default_power_down_ok() is not
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 220
Quoted 10 lines
drivers/base/power/domain_governor.c +10
-0+* going to be called for any master until this instance +* returns. +*/ +list_for_each_entry (link, &genpd->slave_links, slave_node) + link->master->max_off_time_changed = true; + + genpd->max_off_time_ns =-1 ; + genpd->max_off_time_changed = false; + +/* find a state to power down to, starting from the deepest */
Please start sentence with an upper case letter and end it with a dot.
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 224
Quoted 4 lines
drivers/base/power/domain_governor.c +4
-0+for (i =0 ; i < genpd->state_count; i++) { +if (power_down_ok_for_state (pd, last_state_idx - i)) { + genpd->state_idx = last_state_idx - i; + retval = true;
Instead of assigning a local copy "retval", I think you can assign genpd->cached_power_down_ok and later below just return its value.
drivers/base/power/domain_governor.c static bool default_power_down_ok(struct dev_pm_domain *pd) · line 227
Quoted 3 lines
drivers/base/power/domain_governor.c +3
-0+break ; + } + }
I think you should convert this to a while loop instead as I think it becomes easier to understand what goes on.
Quoted 11 lines
drivers/base/power/domain_governor.c +5
-1+ + genpd->cached_power_down_ok = retval; +return retval; +} +static bool always_on_power_down_ok (struct dev_pm_domain *domain) {return false; --2.1.4
Kind regards
Uffe
RFC PATCH 5/27 PM / Domains: remove old power on/off latencies.
In reply to Lina IyerFrom: Axel Haslam <[email protected]>
Now that all known users have been converted to use state latencies, we can remove the latency field in the generic_pm_domain structure.
--- drivers/base/power/domain.c | 15 --------------- include/linux/pm_domain.h | 2 -- 2 files changed, 17 deletions(-)
drivers/base/power/domain.c +0 -15
@@ -1248,21 +1248,6 @@ static int genpd_alloc_states_data(struct generic_pm_domain *genpd, genpd->states[i].param = st[i].param; } -/* -* Copy the latency values To keep compatibility with -* platforms that are not converted to use the multiple states. -* This will be removed once all platforms are converted to use -* multiple states. note that non converted platforms will use the -* default single off state. -*/ -if (genpd->power_on_latency_ns !=0 ) - genpd->states[0 ].power_on_latency_ns = - genpd->power_on_latency_ns; - -if (genpd->power_off_latency_ns !=0 ) - genpd->states[0 ].power_off_latency_ns = - genpd->power_off_latency_ns; - genpd->state_count = st_count;/* to save memory, Name allocation will happen if debug is enabled */
include/linux/pm_domain.h +0 -2
@@ -61,9 +61,7 @@ struct generic_pm_domain {unsigned int prepared_count;/* Suspend counter of prepared devices */ bool suspend_power_off;/* Power status before system suspend */ int (*power_off)(struct generic_pm_domain *domain); -s64 power_off_latency_ns;int (*power_on)(struct generic_pm_domain *domain); -s64 power_on_latency_ns;struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed;
Signature · 1 line
2.1.4
PATCH 1/1 ARM: imx6: pm: declare pm domain latency on power_state struct.
From: Axel Haslam <[email protected]>
The generic_pm_domain structure uses an array of latencies to be able to declare multiple intermediate states.
Declare a single "OFF" state with the default latencies So that the power_off_latency_ns and power_on_latency_ns fields of generic_pm_domain structure can be eventualy removed.
--- arch/arm/mach-imx/gpc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)
arch/arm/mach-imx/gpc.c +12 -2
@@ -368,13 +368,23 @@ static struct generic_pm_domain imx6q_arm_domain = { .name = "ARM", }; +static struct genpd_power_state imx6q_arm_domain_states[] = { + { + .name ="OFF" , + .power_off_latency_ns =25000 , + .power_on_latency_ns =2000000 , + }, +}; +static struct pu_domain imx6q_pu_domain = { .base = { .name ="PU" , .power_off = imx6q_pm_pu_power_off, .power_on = imx6q_pm_pu_power_on, - .power_off_latency_ns =25000 , - .power_on_latency_ns =2000000 , + .gov =NULL , + .status =GPD_STATE_POWER_OFF , + .states = imx6q_arm_domain_states, + .state_count =ARRAY_SIZE (imx6q_arm_domain_states), }, };
Signature · 1 line
2.1.4
PATCH 1/1 ARM: imx6: pm: declare pm domain latency on power_state struct.
Am Mittwoch, den 18.11.2015, 07:57 -0700 schrieb Lina Iyer:
Quoted 21 lines
From: Axel Haslam <[email protected]>
The
generic_pm_domainstructure uses an array of latencies to be able to declare multiple intermediate states.Declare a single "OFF" state with the default latencies So that the
power_off_latency_nsandpower_on_latency_nsfields ofgeneric_pm_domainstructure can be eventualy removed.Signed-off-by Axel Haslam <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: pm_genpd_init() argument change]--- arch/arm/mach-imx/gpc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)arch/arm/mach-imx/gpc.c +0
-0
arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 371
Quoted 4 lines
arch/arm/mach-imx/gpc.c +1
-0@@ -368,13 +368,23 @@ static struct generic_pm_domain imx6q_arm_domain = { .name = "ARM", }; +static struct genpd_power_state imx6q_arm_domain_states[] = {
Those states are for the PU, not the ARM domain.
Quoted 6 lines
arch/arm/mach-imx/gpc.c +6
-0+ { + .name = "OFF", + .power_off_latency_ns = 25000, + .power_on_latency_ns = 2000000, + }, +};
arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 385
Quoted 10 lines
arch/arm/mach-imx/gpc.c +3
-2+static struct pu_domain imx6q_pu_domain = { .base = { .name ="PU" , .power_off = imx6q_pm_pu_power_off, .power_on = imx6q_pm_pu_power_on, - .power_off_latency_ns =25000 , - .power_on_latency_ns =2000000 , + .gov =NULL , + .status =GPD_STATE_POWER_OFF ,
The above 2 lines should not be needed. This is a static struct, so .gov will already be NULL. status is set by pm_genpd_init() and the above value is actively wrong.
arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 390
Quoted 5 lines
arch/arm/mach-imx/gpc.c +2
-0+ .states = imx6q_arm_domain_states, + .state_count = ARRAY_SIZE(imx6q_arm_domain_states), }, };
How urgent is this patch? I have a series to rework the GPC driver almost ready and I could fold this change in directly if it's okay for this to sit through a review of the rework.
Regards,
Lucas
Signature · 2 lines
Pengutronix e.K. | Lucas Stach | Industrial Linux Solutions | http://www.pengutronix.de/ |
PATCH 1/1 ARM: imx6: pm: declare pm domain latency on power_state struct.
Am Montag, den 23.11.2015, 14:31 +0100 schrieb Lucas Stach:
Quoted 50 lines
Am Mittwoch, den 18.11.2015, 07:57 -0700 schrieb Lina Iyer:
Quoted 21 lines
From: Axel Haslam <[email protected]>
The
generic_pm_domainstructure uses an array of latencies to be able to declare multiple intermediate states.Declare a single "OFF" state with the default latencies So that the
power_off_latency_nsandpower_on_latency_nsfields ofgeneric_pm_domainstructure can be eventualy removed.Signed-off-by Axel Haslam <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: pm_genpd_init() argument change]--- arch/arm/mach-imx/gpc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)arch/arm/mach-imx/gpc.c +0
-0arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 371
Quoted 4 lines
arch/arm/mach-imx/gpc.c +1
-0@@ -368,13 +368,23 @@ static struct generic_pm_domain imx6q_arm_domain = { .name = "ARM", }; +static struct genpd_power_state imx6q_arm_domain_states[] = {Those states are for the PU, not the ARM domain.
Quoted 6 lines
arch/arm/mach-imx/gpc.c +6
-0+ { + .name = "OFF", + .power_off_latency_ns = 25000, + .power_on_latency_ns = 2000000, + }, +};arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 385
Quoted 10 lines
arch/arm/mach-imx/gpc.c +3
-2+static struct pu_domain imx6q_pu_domain = { .base = { .name ="PU" , .power_off = imx6q_pm_pu_power_off, .power_on = imx6q_pm_pu_power_on, - .power_off_latency_ns =25000 , - .power_on_latency_ns =2000000 , + .gov =NULL , + .status =GPD_STATE_POWER_OFF ,The above 2 lines should not be needed. This is a static struct, so .gov will already be NULL. status is set by
pm_genpd_init()and the above value is actively wrong.
Quoted 9 lines
arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 390
Quoted 5 lines
arch/arm/mach-imx/gpc.c +2
-0+ .states = imx6q_arm_domain_states, + .state_count = ARRAY_SIZE(imx6q_arm_domain_states), }, };How urgent is this patch? I have a series to rework the GPC driver almost ready and I could fold this change in directly if it's okay for this to sit through a review of the rework.
And I just noticed this depends on changes that are not already in v4.4-rc. Why is this sent as a single patch and not as part of the series adding multiple state support to genpd?
Regards,
Lucas
Signature · 2 lines
Pengutronix e.K. | Lucas Stach | Industrial Linux Solutions | http://www.pengutronix.de/ |
PATCH 1/1 ARM: imx6: pm: declare pm domain latency on power_state struct.
Quoted 51 lines · Lucas Stach
Am Montag, den 23.11.2015, 14:31 +0100 schrieb Lucas Stach:
Quoted 50 lines
Am Mittwoch, den 18.11.2015, 07:57 -0700 schrieb Lina Iyer:
Quoted 21 lines
From: Axel Haslam <[email protected]>
The
generic_pm_domainstructure uses an array of latencies to be able to declare multiple intermediate states.Declare a single "OFF" state with the default latencies So that the
power_off_latency_nsandpower_on_latency_nsfields ofgeneric_pm_domainstructure can be eventualy removed.Signed-off-by Axel Haslam <[email protected]>Signed-off-by Lina Iyer <[email protected]>[Lina: pm_genpd_init() argument change]--- arch/arm/mach-imx/gpc.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-)arch/arm/mach-imx/gpc.c +0
-0arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 371
Quoted 4 lines
arch/arm/mach-imx/gpc.c +1
-0@@ -368,13 +368,23 @@ static struct generic_pm_domain imx6q_arm_domain = { .name = "ARM", }; +static struct genpd_power_state imx6q_arm_domain_states[] = {Those states are for the PU, not the ARM domain.
Quoted 6 lines
arch/arm/mach-imx/gpc.c +6
-0+ { + .name = "OFF", + .power_off_latency_ns = 25000, + .power_on_latency_ns = 2000000, + }, +};arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 385
Quoted 10 lines
arch/arm/mach-imx/gpc.c +3
-2+static struct pu_domain imx6q_pu_domain = { .base = { .name ="PU" , .power_off = imx6q_pm_pu_power_off, .power_on = imx6q_pm_pu_power_on, - .power_off_latency_ns =25000 , - .power_on_latency_ns =2000000 , + .gov =NULL , + .status =GPD_STATE_POWER_OFF ,The above 2 lines should not be needed. This is a static struct, so .gov will already be NULL. status is set by
pm_genpd_init()and the above value is actively wrong.
True.
Quoted 10 lines
Quoted 9 lines
arch/arm/mach-imx/gpc.c static struct generic_pm_domain imx6q_arm_domain = { · line 390
Quoted 5 lines
arch/arm/mach-imx/gpc.c +2
-0+ .states = imx6q_arm_domain_states, + .state_count = ARRAY_SIZE(imx6q_arm_domain_states), }, };How urgent is this patch? I have a series to rework the GPC driver almost ready and I could fold this change in directly if it's okay for this to sit through a review of the rework.
Quoted 4 lines
And I just noticed this depends on changes that are not already in v4.4-rc. Why is this sent as a single patch and not as part of the series adding multiple state support to genpd?
Sorry, since this is ARM 32, I had not comple tested it when I sent out the series. I sent this out after I realized it.
Thanks,
Lina
Quoted 7 lines
Regards,
LucasSignature · 2 lines
Pengutronix e.K. | Lucas Stach | Industrial Linux Solutions | http://www.pengutronix.de/ |
RFC PATCH 5/27 PM / Domains: remove old power on/off latencies.
In reply to Lina IyerQuoted 4 lines · Lina Iyer
From: Axel Haslam <[email protected]>
Now that all known users have been converted to use state latencies, we can remove the latency field in the
generic_pm_domainstructure.
I couldn't see that happen in this patchset.
As arch/arm/mach-imx/gpc.c is still using this, you will break that platform.
Kind regards
Uffe
Quoted 50 lines
Signed-off-by Axel Haslam <[email protected]>--- drivers/base/power/domain.c | 15 --------------- include/linux/pm_domain.h | 2 -- 2 files changed, 17 deletions(-)drivers/base/power/domain.c +0
-15@@ -1248,21 +1248,6 @@ static int genpd_alloc_states_data(struct generic_pm_domain *genpd, genpd->states[i].param = st[i].param; } -/* -* Copy the latency values To keep compatibility with -* platforms that are not converted to use the multiple states. -* This will be removed once all platforms are converted to use -* multiple states. note that non converted platforms will use the -* default single off state. -*/ -if (genpd->power_on_latency_ns !=0 ) - genpd->states[0 ].power_on_latency_ns = - genpd->power_on_latency_ns; - -if (genpd->power_off_latency_ns !=0 ) - genpd->states[0 ].power_off_latency_ns = - genpd->power_off_latency_ns; - genpd->state_count = st_count;/* to save memory, Name allocation will happen if debug is enabled */ include/linux/pm_domain.h +0
-3@@ -61,9 +61,7 @@ struct generic_pm_domain {unsigned int prepared_count;/* Suspend counter of prepared devices */ bool suspend_power_off;/* Power status before system suspend */ int (*power_off)(struct generic_pm_domain *domain); -s64 power_off_latency_ns;int (*power_on)(struct generic_pm_domain *domain); -s64 power_on_latency_ns;struct gpd_dev_ops dev_ops;s64 max_off_time_ns;/* Maximum allowed "suspended" time. */ bool max_off_time_changed; --2.1.4
RFC PATCH 6/27 PM / Domains: add debugfs 'states' and 'timings' seq files
In reply to Lina IyerFrom: Marc Titinger <[email protected]>
This purpose of these debug seq-files is to help investigate generic power domain state transitions, based on device constraints. requires the "multiple states" patches from Axel Haslam.
also rename 'summary' from 'pm_genpd_summary'
sample output for 'states'
==========================
Domain State name Eval = 2200nter + Exit = Min_off_on (ns)
-----------------------------------------------------------------------
a53_pd cluster-sleep-0 1500000+800000=2300000 a57_pd d1-retention 1000000+800000=1800000 a57_pd cluster-sleep-0 1500000+800000=2300000
sample output for 'timings'
===========================
Domain Devices, Timings in ns
Stop/Start Save/Restore, Effective
---------------------------------------------------- --- a53_pd
/cpus/cpu at 100 1060 /660 1580 /1940 ,0 (cached stop)
/cpus/cpu at 101 1060 /740 1520 /1600 ,0 (cached stop)
/cpus/cpu at 102 880 /620 1380 /1780 ,0 (cached stop)
/cpus/cpu at 103 1080 /640 1340 /1600 ,0 (cached stop)
a57_pd
/cpus/cpu at 0 1160 /740 3280 /1800 ,0 (cached stop)
/cpus/cpu at 1 780 /1400 1440 /2080 ,0 (cached stop)
/D1 600 /540 7140 /6420 ,2199460 (cached stop)--- drivers/base/power/domain.c | 115 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-)
drivers/base/power/domain.c +107 -8
@@ -2169,21 +2169,120 @@ static const struct file_operations pm_genpd_summary_fops = { .release = single_release, }; +static int pm_genpd_states_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s, +"\n Domain State name Enter + Exit = Min_off_on (ns)\n" ); +seq_puts (s, +"-----------------------------------------------------------------------\n" ); + +list_for_each_entry (genpd, &gpd_list, gpd_list_node) { + +int i; + +for (i =0 ; i < genpd->state_count; i++) { +seq_printf (s,"%-20s %-20s %lld+%lld=%lld\n" , + genpd->name, + genpd->states[i].name, + genpd->states[i].power_on_latency_ns, + genpd->states[i].power_off_latency_ns, + genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns); + } + + } + +seq_puts (s,"\n" ); + +return 0 ; +} + +static int pm_genpd_states_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_states_show,NULL ); +} + +static const struct file_operations pm_genpd_states_fops = { + .open = pm_genpd_states_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int pm_genpd_timing_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s,"\n Domain Devices, Timings in ns\n" ); +seq_puts (s, +" Stop/Start Save/Restore, Effective\n" ); +seq_puts (s, +"---------------------------------------------------- ---\n" ); + +list_for_each_entry (genpd, &gpd_list, gpd_list_node) { +struct pm_domain_data *pm_data; + +seq_printf (s,"%-30s" , genpd->name); + +list_for_each_entry (pm_data, &genpd->dev_list, list_node) { +struct gpd_timing_data *td = &to_gpd_data (pm_data)->td; + +if (!pm_data->dev->of_node) +continue ; + +seq_printf (s, +"\n %-20s %-6lld/%-6lld %-6lld/%-6lld,%lld %s%s" , + pm_data->dev->of_node->full_name, + td->stop_latency_ns, td->start_latency_ns, + td->save_state_latency_ns, + td->restore_state_latency_ns, + td->effective_constraint_ns, + td->cached_stop_ok ?"(cached stop) " :"" , + td->constraint_changed ?"(changed)" :"" ); + } +seq_puts (s,"\n" ); + } +return 0 ; +} + +static int pm_genpd_timing_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_timing_show,NULL ); +} + +static const struct file_operations pm_genpd_timing_fops = { + .open = pm_genpd_timing_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static int __init pm_genpd_debug_init (void ) { -struct dentry *d; +struct dentry *d =NULL ; pm_genpd_debugfs_dir =debugfs_create_dir ("pm_genpd" ,NULL ); -if (!pm_genpd_debugfs_dir) -return -ENOMEM ; +if (pm_genpd_debugfs_dir) + d =debugfs_create_file ("summary" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_summary_fops); +if (d) + d =debugfs_create_file ("states" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_states_fops); +if (d) + d =debugfs_create_file ("timings" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_timing_fops); +if (d) +return 0 ; - d =debugfs_create_file ("pm_genpd_summary" ,S_IRUGO , - pm_genpd_debugfs_dir,NULL , &pm_genpd_summary_fops); -if (!d) -return -ENOMEM ; +debugfs_remove_recursive (pm_genpd_debugfs_dir/*can be null*/ ); -return 0 ; +return -ENOMEM ; }late_initcall (pm_genpd_debug_init);
Signature · 1 line
2.1.4
RFC PATCH 6/27 PM / Domains: add debugfs 'states' and 'timings' seq files
Quoted 5 lines · Lina Iyer
From: Marc Titinger <[email protected]>
This purpose of these debug seq-files is to help investigate generic power domain state transitions, based on device constraints. requires the "multiple states" patches from Axel Haslam.
This last sentence doesn't belong in the change-log, please remove it.
Quoted 28 lines
also rename 'summary' from '
pm_genpd_summary'sample output for 'states'
==========================Domain State name Eval = 2200nter + Exit =
Min_off_on(ns)
-----------------------------------------------------------------------a53_pd cluster-sleep-0 1500000+800000=2300000 a57_pd d1-retention 1000000+800000=1800000 a57_pd cluster-sleep-0 1500000+800000=2300000sample output for 'timings'
===========================Domain Devices, Timings in ns
Stop/Start Save/Restore, Effective
---------------------------------------------------- ---a53_pd/cpus/cpu at 100 1060 /660 1580 /1940 ,0 (cached stop) /cpus/cpu at 101 1060 /740 1520 /1600 ,0 (cached stop) /cpus/cpu at 102 880 /620 1380 /1780 ,0 (cached stop) /cpus/cpu at 103 1080 /640 1340 /1600 ,0 (cached stop) a57_pd /cpus/cpu at 0 1160 /740 3280 /1800 ,0 (cached stop) /cpus/cpu at 1 780 /1400 1440 /2080 ,0 (cached stop) /D1 600 /540 7140 /6420 ,2199460 (cached stop)
Quoted 1 line
Signed-off-by Marc Titinger <[email protected]>
A general comment. Static functions in genpd shall start with one of the following prefix.
genpd_*
_genpd_*
__genpd_*
Please change accordingly.
Quoted 11 lines
--- drivers/base/power/domain.c | 115 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-)drivers/base/power/domain.c +0
-0@@ -2169,21 +2169,120 @@ static const struct file_operations pm_genpd_summary_fops = { .release = single_release, };
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2180
Quoted 10 lines
drivers/base/power/domain.c +9
-0+static int pm_genpd_states_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s, +"\n Domain State name Enter + Exit = Min_off_on (ns)\n" ); +seq_puts (s, +"-----------------------------------------------------------------------\n" ); +
You must hold the gpd_list_lock while traversing the gpd list.
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2185
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { + +int i; + +for (i =0 ; i < genpd->state_count; i++) {
To be sure you have valid data, you should hold the genpd lock here.
Quoted 28 lines
drivers/base/power/domain.c +28
-0+seq_printf (s,"%-20s %-20s %lld+%lld=%lld\n" , + genpd->name, + genpd->states[i].name, + genpd->states[i].power_on_latency_ns, + genpd->states[i].power_off_latency_ns, + genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns); + } + + } + +seq_puts (s,"\n" ); + +return 0 ; +} + +static int pm_genpd_states_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_states_show,NULL ); +} + +static const struct file_operations pm_genpd_states_fops = { + .open = pm_genpd_states_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2223
Quoted 10 lines
drivers/base/power/domain.c +10
-0+static int pm_genpd_timing_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s,"\n Domain Devices, Timings in ns\n" ); +seq_puts (s, +" Stop/Start Save/Restore, Effective\n" ); +seq_puts (s, +"---------------------------------------------------- ---\n" ); +
You must hold the gpd_list_lock while traversing the gpd list.
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2228
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { +struct pm_domain_data *pm_data; + +seq_printf (s,"%-30s" , genpd->name); +
You must hold the genpd lock while traversing the device list.
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2240
Quoted 12 lines
drivers/base/power/domain.c +12
-0+list_for_each_entry (pm_data, &genpd->dev_list, list_node) { +struct gpd_timing_data *td = &to_gpd_data (pm_data)->td; + +if (!pm_data->dev->of_node) +continue ; + + seq_printf(s, + "\n %-20s %-6lld/%-6lld %-6lld/%-6lld,%lld %s%s", + pm_data->dev->of_node->full_name, + td->stop_latency_ns, td->start_latency_ns, + td->save_state_latency_ns, + td->restore_state_latency_ns,
This needs a re-base as these values have been merged.
Quoted 21 lines
drivers/base/power/domain.c +21
-0+ td->effective_constraint_ns, + td->cached_stop_ok ?"(cached stop) " :"" , + td->constraint_changed ? "(changed)" : ""); + } +seq_puts (s,"\n" ); + } +return 0 ; +} + +static int pm_genpd_timing_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_timing_show,NULL ); +} + +static const struct file_operations pm_genpd_timing_fops = { + .open = pm_genpd_timing_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2268
Quoted 10 lines
drivers/base/power/domain.c +2
-3static int __init pm_genpd_debug_init (void ) { -struct dentry *d; +struct dentry *d =NULL ; pm_genpd_debugfs_dir =debugfs_create_dir ("pm_genpd" ,NULL ); -if (!pm_genpd_debugfs_dir) -return -ENOMEM ; +if (pm_genpd_debugfs_dir)
In case when CONFIG_DEBUG_FS is unset, this doesn't work very well, as pm_genpd_debugfs_dir will contain an error code.
Since you are anyway make quite big change to the debugfs support for genpd, perhaps you can try to fix that up as well!?
Quoted 15 lines
drivers/base/power/domain.c +13
-1+ d =debugfs_create_file ("summary" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_summary_fops); +if (d) + d =debugfs_create_file ("states" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_states_fops); +if (d) + d =debugfs_create_file ("timings" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_timing_fops); +if (d) +return 0 ; - d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2288
Quoted 10 lines
drivers/base/power/domain.c +2
-4- pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); -if (!d) -return -ENOMEM ; +debugfs_remove_recursive (pm_genpd_debugfs_dir/*can be null*/ ); -return 0 ; +return -ENOMEM ; }late_initcall (pm_genpd_debug_init);
Kind regards
Uffe
RFC PATCH 6/27 PM / Domains: add debugfs 'states' and 'timings' seq files
Quoted 1 line · Ulf Hansson
On 17 November 2015 at 23:37, Lina Iyer <[email protected]> wrote:
Hi Ulf,
thanks for the review, comments and questions below.
Best Regards,
Marc.
Quoted 38 lines
Quoted 5 lines
From: Marc Titinger <[email protected]>
This purpose of these debug seq-files is to help investigate generic power domain state transitions, based on device constraints. requires the "multiple states" patches from Axel Haslam.
This last sentence doesn't belong in the change-log, please remove it.
Quoted 28 lines
also rename 'summary' from '
pm_genpd_summary'sample output for 'states'
==========================Domain State name Eval = 2200nter + Exit =
Min_off_on(ns)
-----------------------------------------------------------------------a53_pd cluster-sleep-0 1500000+800000=2300000 a57_pd d1-retention 1000000+800000=1800000 a57_pd cluster-sleep-0 1500000+800000=2300000sample output for 'timings'
===========================Domain Devices, Timings in ns
Stop/Start Save/Restore, Effective
---------------------------------------------------- ---a53_pd/cpus/cpu at 100 1060 /660 1580 /1940 ,0 (cached stop) /cpus/cpu at 101 1060 /740 1520 /1600 ,0 (cached stop) /cpus/cpu at 102 880 /620 1380 /1780 ,0 (cached stop) /cpus/cpu at 103 1080 /640 1340 /1600 ,0 (cached stop) a57_pd /cpus/cpu at 0 1160 /740 3280 /1800 ,0 (cached stop) /cpus/cpu at 1 780 /1400 1440 /2080 ,0 (cached stop) /D1 600 /540 7140 /6420 ,2199460 (cached stop)Quoted 1 line
Signed-off-by Marc Titinger <[email protected]>
Quoted 8 lines
A general comment. Static functions in genpd shall start with one of the following prefix.
genpd_*
_genpd_*
__genpd_*Please change accordingly.
Many static routines were already prefixed like the exported functions with "pm_", shall I make a separate patch for this renaming ?
Quoted 25 lines
Quoted 12 lines
--- drivers/base/power/domain.c | 115 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-)drivers/base/power/domain.c +0
-0@@ -2169,21 +2169,120 @@ static const struct file_operations pm_genpd_summary_fops = { .release = single_release, };drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2180
Quoted 10 lines
drivers/base/power/domain.c +9
-0+static int pm_genpd_states_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s, +"\n Domain State name Enter + Exit = Min_off_on (ns)\n" ); +seq_puts (s, +"-----------------------------------------------------------------------\n" ); +You must hold the
gpd_list_lockwhile traversing the gpd list.
Quoted 8 lines
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2185
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { + +int i; + +for (i =0 ; i < genpd->state_count; i++) {To be sure you have valid data, you should hold the genpd lock here.
At some point while testing with calling suspend from the power_off handler, the cpu would go to sleep with the lock held, hence using this seq-file would not work.
while I agree, I think it is not super likely that a domain/child/devices/states are added or removed at this point (the DT is parsed already), would using list_for_each_entry_safe be safe enough ?
Quoted 100 lines
Quoted 28 lines
drivers/base/power/domain.c +28
-0+seq_printf (s,"%-20s %-20s %lld+%lld=%lld\n" , + genpd->name, + genpd->states[i].name, + genpd->states[i].power_on_latency_ns, + genpd->states[i].power_off_latency_ns, + genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns); + } + + } + +seq_puts (s,"\n" ); + +return 0 ; +} + +static int pm_genpd_states_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_states_show,NULL ); +} + +static const struct file_operations pm_genpd_states_fops = { + .open = pm_genpd_states_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2223
Quoted 10 lines
drivers/base/power/domain.c +10
-0+static int pm_genpd_timing_show (struct seq_file *s,void *data) +{ +struct generic_pm_domain *genpd; + +seq_puts (s,"\n Domain Devices, Timings in ns\n" ); +seq_puts (s, +" Stop/Start Save/Restore, Effective\n" ); +seq_puts (s, +"---------------------------------------------------- ---\n" ); +You must hold the
gpd_list_lockwhile traversing the gpd list.drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2228
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { +struct pm_domain_data *pm_data; + +seq_printf (s,"%-30s" , genpd->name); +You must hold the genpd lock while traversing the device list.
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2240
Quoted 12 lines
drivers/base/power/domain.c +12
-0+list_for_each_entry (pm_data, &genpd->dev_list, list_node) { +struct gpd_timing_data *td = &to_gpd_data (pm_data)->td; + +if (!pm_data->dev->of_node) +continue ; + + seq_printf(s, + "\n %-20s %-6lld/%-6lld %-6lld/%-6lld,%lld %s%s", + pm_data->dev->of_node->full_name, + td->stop_latency_ns, td->start_latency_ns, + td->save_state_latency_ns, + td->restore_state_latency_ns,This needs a re-base as these values have been merged.
Quoted 21 lines
drivers/base/power/domain.c +21
-0+ td->effective_constraint_ns, + td->cached_stop_ok ?"(cached stop) " :"" , + td->constraint_changed ? "(changed)" : ""); + } +seq_puts (s,"\n" ); + } +return 0 ; +} + +static int pm_genpd_timing_open (struct inode *inode,struct file *file) +{ +return single_open (file, pm_genpd_timing_show,NULL ); +} + +static const struct file_operations pm_genpd_timing_fops = { + .open = pm_genpd_timing_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2268
Quoted 10 lines
drivers/base/power/domain.c +2
-3static int __init pm_genpd_debug_init (void ) { -struct dentry *d; +struct dentry *d =NULL ; pm_genpd_debugfs_dir =debugfs_create_dir ("pm_genpd" ,NULL ); -if (!pm_genpd_debugfs_dir) -return -ENOMEM ; +if (pm_genpd_debugfs_dir)In case when
CONFIG_DEBUG_FSis unset, this doesn't work very well, aspm_genpd_debugfs_dirwill contain an error code.
Quoted 2 lines
Since you are anyway make quite big change to the debugfs support for genpd, perhaps you can try to fix that up as well!?
Ah yes ok, will do.
Quoted 30 lines
Quoted 16 lines
drivers/base/power/domain.c +13
-1+ d =debugfs_create_file ("summary" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_summary_fops); +if (d) + d =debugfs_create_file ("states" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_states_fops); +if (d) + d =debugfs_create_file ("timings" ,S_IRUGO , + pm_genpd_debugfs_dir,NULL , + &pm_genpd_timing_fops); +if (d) +return 0 ; - d = debugfs_create_file("pm_genpd_summary", S_IRUGO,drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2288
Quoted 10 lines
drivers/base/power/domain.c +2
-4- pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops); -if (!d) -return -ENOMEM ; +debugfs_remove_recursive (pm_genpd_debugfs_dir/*can be null*/ ); -return 0 ; +return -ENOMEM ; }late_initcall (pm_genpd_debug_init);Kind regards
Uffe
RFC PATCH 6/27 PM / Domains: add debugfs 'states' and 'timings' seq files
[...]
Quoted 8 lines
Quoted 8 lines
A general comment. Static functions in genpd shall start with one of the following prefix.
genpd_*
_genpd_*
__genpd_*
Quoted 5 lines
Quoted 1 line
Please change accordingly.
Many static routines were already prefixed like the exported functions with "
pm_", shall I make a separate patch for this renaming ?
My point is that I don't want it to becomes worse.
If you follow the above rule for new changes, I am happy.
Whether you want to send a separate patch fixing the other existing name to be consistent with above rule, I would also be happy. :-)
Quoted 42 lines
Quoted 32 lines
Quoted 18 lines
--- drivers/base/power/domain.c | 115 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-)drivers/base/power/domain.c +1
-0@@ -2169,21 +2169,120 @@ static const struct file_operations pm_genpd_summary_fops = { .release = single_release, }; +static int pm_genpd_states_show (struct seq_file *s,void *data)drivers/base/power/domain.c static const struct file_operations · line 2183
Quoted 10 lines
drivers/base/power/domain.c +8
-0+{ +struct generic_pm_domain *genpd; + +seq_puts (s, + "\n Domain State name Enter + Exit = Min_off_on (ns)\n"); +seq_puts (s, +"-----------------------------------------------------------------------\n" ); +You must hold the
gpd_list_lockwhile traversing the gpd list.Quoted 9 lines
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2185
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { + +int i; + +for (i =0 ; i < genpd->state_count; i++) {To be sure you have valid data, you should hold the genpd lock here.
Quoted 3 lines
At some point while testing with calling suspend from the
power_offhandler, the cpu would go to sleep with the lock held, hence using this seq-file would not work.
That seems like a bad behaviour during suspend. Why does it hold the lock?
On the other it shouldn't matter as userspace can't access the debugfs nodes, since its frozen at those times, right!?
Quoted 4 lines
while I agree, I think it is not super likely that a domain/child/devices/states are added or removed at this point (the DT is parsed already), would using
list_for_each_entry_safebe safe enough ?
No it's not.
The gpd_list is protected with the gdp_list_lock, which is needed because new genpds can be added at any point.
You also need the genpd lock here, as otherwise you may print the latency-values in the middle of when these are being updated.
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
drivers/base/power/domain.c +8
-0+seq_printf (s,"%-20s %-20s %lld+%lld=%lld\n" , + genpd->name, + genpd->states[i].name, + genpd->states[i].power_on_latency_ns, + genpd->states[i].power_off_latency_ns, + genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns); + }
Quoted 8 lines
Quoted 8 lines
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2201
Quoted 8 lines
drivers/base/power/domain.c +8
-0+ + } + +seq_puts (s,"\n" ); + +return 0 ; +} +
[...]
Kind regards
Uffe
RFC PATCH 6/27 PM / Domains: add debugfs 'states' and 'timings' seq files
Quoted 16 lines · Ulf Hansson
[...]
Quoted 8 lines
Quoted 8 lines
A general comment. Static functions in genpd shall start with one of the following prefix.
genpd_*
_genpd_*
__genpd_*Quoted 5 lines
Quoted 1 line
Please change accordingly.
Many static routines were already prefixed like the exported functions with "
pm_", shall I make a separate patch for this renaming ?
Quoted 6 lines
My point is that I don't want it to becomes worse.
If you follow the above rule for new changes, I am happy.
Whether you want to send a separate patch fixing the other existing name to be consistent with above rule, I would also be happy. :-)
Fair enough, I'll do a renaming patch :)
Quoted 43 lines
Quoted 43 lines
Quoted 42 lines
Quoted 19 lines
--- drivers/base/power/domain.c | 115 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 107 insertions(+), 8 deletions(-)drivers/base/power/domain.c +1
-0@@ -2169,21 +2169,120 @@ static const struct file_operations pm_genpd_summary_fops = { .release = single_release, }; +static int pm_genpd_states_show (struct seq_file *s,void *data)drivers/base/power/domain.c static const struct file_operations · line 2183
Quoted 10 lines
drivers/base/power/domain.c +8
-0+{ +struct generic_pm_domain *genpd; + +seq_puts (s, + "\n Domain State name Enter + Exit = Min_off_on (ns)\n"); +seq_puts (s, +"-----------------------------------------------------------------------\n" ); +You must hold the
gpd_list_lockwhile traversing the gpd list.drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2185
Quoted 5 lines
drivers/base/power/domain.c +5
-0+list_for_each_entry (genpd, &gpd_list, gpd_list_node) { + +int i; + +for (i =0 ; i < genpd->state_count; i++) {To be sure you have valid data, you should hold the genpd lock here.
Quoted 5 lines
Quoted 3 lines
At some point while testing with calling suspend from the
power_offhandler, the cpu would go to sleep with the lock held, hence using this seq-file would not work.That seems like a bad behaviour during suspend. Why does it hold the lock?
that was in the scenario where 2 or more cpus are devices of the same power domain. IIRC you would have something like:
arm_enter_idle_state(cpu)
pm_runtime_put_sync rpm_suspend __rpm_get_callback pm_genpd_runtime_suspend [Take lock] ->platform code to enter sleep...
and race condition with another cpu of the same cluster trying to suspend or resume at the same time. it is a bad behaviour, I cannot test the os-initiated mode here but I assume this is done differently and is no longer an issue (sorry for not being more specific).
Quoted 3 lines
On the other it shouldn't matter as userspace can't access the debugfs nodes, since its frozen at those times, right!?
you can have cpu_j off, and cpu_i running the shell, in the scenario above. But since this was while hacking calling psci from genpd.power_off, I'm not sure it's worth mentioning...
Quoted 6 lines
Quoted 5 lines
while I agree, I think it is not super likely that a domain/child/devices/states are added or removed at this point (the DT is parsed already), would using
list_for_each_entry_safebe safe enough ?
Quoted 7 lines
No it's not.
The
gpd_listis protected with thegdp_list_lock, which is needed because new genpds can be added at any point.You also need the genpd lock here, as otherwise you may print the latency-values in the middle of when these are being updated.
Understood, I'll put the lock back.
Quoted 26 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
drivers/base/power/domain.c +8
-0+seq_printf (s,"%-20s %-20s %lld+%lld=%lld\n" , + genpd->name, + genpd->states[i].name, + genpd->states[i].power_on_latency_ns, + genpd->states[i].power_off_latency_ns, + genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns); + }Quoted 8 lines
Quoted 8 lines
drivers/base/power/domain.c static const struct file_operations pm_genpd_summary_fops = { · line 2201
Quoted 8 lines
drivers/base/power/domain.c +8
-0+ + } + +seq_puts (s,"\n" ); + +return 0 ; +} +[...]
Kind regards
Uffe
RFC PATCH 7/27 PM / Domains: Read domain residency from DT
In reply to Lina IyerDomains that have expensive suspend resume operations, may require a certain amount of time be spent in an idle state to reap the power saving benefit. Such domains, may provide the residency requirement for a domain state.
Read the residency for a domain state from the DT. A domain governor may use this information in determining the state for the domain.
--- Documentation/devicetree/bindings/power/power_domain.txt | 7 +++++++ drivers/base/power/domain.c | 6 ++++++ include/linux/pm_domain.h | 1 + 3 files changed, 14 insertions(+)
Documentation/devicetree/bindings/power/power_domain.txt +7 -0
@@ -67,6 +67,13 @@ have the following properties - If omitted, this is assumed to be equal to: entry-latency-us + exit-latency-us + - residency-us: + Usage: Optional + Value type: <prop-encoded-array> + Definition: A u32 value representing the time for which a + domain must be idle in the state to reap power saving benefits + of entering the state. + - state-param: Usage: Optional Value type: <prop-encoded-array>
drivers/base/power/domain.c +6 -0
@@ -1245,6 +1245,7 @@ static int genpd_alloc_states_data(struct generic_pm_domain *genpd, st[i].power_on_latency_ns; genpd->states[i].power_off_latency_ns = st[i].power_off_latency_ns; + genpd->states[i].residency_ns = st[i].residency_ns; genpd->states[i].param = st[i].param; }
@@ -1513,6 +1514,7 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,const struct of_device_id *match_id;int err =0 ;u32 latency; +u32 residency;u32 param; match_id =of_match_node (power_state_match, state_node);
@@ -1554,6 +1556,10 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,return -EINVAL ; } + err =of_property_read_u32 (state_node,"residency-us" , &residency); +if (!err) + genpd_state->residency_ns =1000 * residency; + err =of_property_read_u32 (state_node,"state-param" , ¶m);if (!err) genpd_state->param = param;
include/linux/pm_domain.h +1 -0
@@ -41,6 +41,7 @@ struct genpd_power_state {char *name;s64 power_off_latency_ns;s64 power_on_latency_ns; +s64 residency_ns;u32 param; };
Signature · 1 line
2.1.4
RFC PATCH 7/27 PM / Domains: Read domain residency from DT
Documentation/devicetree/bindings/power/power_domain.txt have the following properties - · line 72
Quoted 11 lines · Lina Iyer
Documentation/devicetree/bindings/power/power_domain.txt +3
-0@@ -67,6 +67,13 @@ have the following properties - If omitted, this is assumed to be equal to: entry-latency-us + exit-latency-us + - residency-us: + Usage: Optional + Value type: <prop-encoded-array>
Is it an array or just a single u32? Some example would be good to add.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 7/27 PM / Domains: Read domain residency from DT
In reply to Lina IyerQuoted 5 lines · Lina Iyer
Domains that have expensive suspend resume operations, may require a certain amount of time be spent in an idle state to reap the power saving benefit. Such domains, may provide the residency requirement for a domain state.
Quoted 8 lines
Read the residency for a domain state from the DT. A domain governor may use this information in determining the state for the domain.
Cc Axel Haslam <[email protected]>Cc Marc Titinger <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- Documentation/devicetree/bindings/power/power_domain.txt | 7 +++++++
Please split DT documentation into a separate patch preceding $subject patch. Don't forget to send it to the DT maintainers as well.
Quoted 66 lines
drivers/base/power/domain.c | 6 ++++++ include/linux/pm_domain.h | 1 + 3 files changed, 14 insertions(+)Documentation/devicetree/bindings/power/power_domain.txt +7
-0@@ -67,6 +67,13 @@ have the following properties - If omitted, this is assumed to be equal to: entry-latency-us + exit-latency-us + - residency-us: + Usage: Optional + Value type: <prop-encoded-array> + Definition: A u32 value representing the time for which a + domain must be idle in the state to reap power saving benefits + of entering the state. + - state-param: Usage: Optional Value type: <prop-encoded-array>drivers/base/power/domain.c +6
-0@@ -1245,6 +1245,7 @@ static int genpd_alloc_states_data(struct generic_pm_domain *genpd, st[i].power_on_latency_ns; genpd->states[i].power_off_latency_ns = st[i].power_off_latency_ns; + genpd->states[i].residency_ns = st[i].residency_ns; genpd->states[i].param = st[i].param; }@@ -1513,6 +1514,7 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,const struct of_device_id *match_id;int err =0 ;u32 latency; +u32 residency;u32 param; match_id =of_match_node (power_state_match, state_node);@@ -1554,6 +1556,10 @@ static int of_get_genpd_power_state(struct genpd_power_state *genpd_state,return -EINVAL ; } + err =of_property_read_u32 (state_node,"residency-us" , &residency); +if (!err) + genpd_state->residency_ns =1000 * residency; + err =of_property_read_u32 (state_node,"state-param" , ¶m);if (!err) genpd_state->param = param;include/linux/pm_domain.h +1
-1@@ -41,6 +41,7 @@ struct genpd_power_state {char *name;s64 power_off_latency_ns;s64 power_on_latency_ns; +s64 residency_ns;u32 param; }; --
Quoted 2 lines
2.1.4
A general comment.
I would as step prior $subject patch, add the mechanism in genpd to deal with this new concept of residency values. When *all* that mechanism is in place (perhaps it needs more than one patch), then add the DT parts.
Kind regards
Uffe
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
In reply to Lina IyerGeneric Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
To achieve domain specific locking, set the domain's ->flag to GENPD_FLAG_IRQ_SAFE while defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted through genpd_lock() and genpd_unlock() functions that use the flag to determine the appropriate lock to be used for that domain. Domains that have lower latency to suspend and resume and can operate with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that sub-domains and all devices in the IRQ safe domain's hierarchy also have to be IRQ safe, so that we dont try to lock a mutex, while holding a spinlock. Non-IRQ safe domains may continue to have devices and sub-domains that may or may not be IRQ safe.
--- Documentation/power/devices.txt | 11 ++- drivers/base/power/domain.c | 197 ++++++++++++++++++++++++++++++++-------- include/linux/pm_domain.h | 12 ++- 3 files changed, 178 insertions(+), 42 deletions(-)
Documentation/power/devices.txt +10 -1
@@ -607,7 +607,16 @@ individually. Instead, a set of devices sharing a power resource can be put into a low-power state together at the same time by turning off the shared power resource. Of course, they also need to be put into the full-power state together, by turning the shared power resource on. A set of devices with this -property is often referred to as a power domain. +property is often referred to as a power domain. A power domain may also be +nested inside another power domain. + +Devices, by default, operate in process context and if a device can operate in +IRQ safe context, has to be explicitly set as IRQ safe. Power domains by +default, operate in process context but could have devices that are IRQ safe. +Such power domains cannot be powered on/off during runtime PM. On the other +hand, an IRQ safe PM domain that can be powered on/off and suspended or resumed +in an atomic context, may contain IRQ safe devices. Such domains may only +contain IRQ safe devices or IRQ safe sub-domains. Support for power domains is provided through the pm_domain field of struct device. This field is a pointer to an object of type struct dev_pm_domain,
drivers/base/power/domain.c +158 -39
389 lines not on this page · raw message
include/linux/pm_domain.h +10 -2
@@ -15,9 +15,11 @@#include <linux/err.h> #include <linux/of.h> #include <linux/notifier.h> +#include <linux/spinlock.h> /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */ +#define GENPD_FLAG_IRQ_SAFE (1U << 1)/* PM domain operates in atomic */ enum gpd_status {GPD_STATE_ACTIVE =0 ,/* PM domain is active */
@@ -51,7 +53,6 @@ struct generic_pm_domain {struct list_head master_links;/* Links with PM domain as a master */ struct list_head slave_links;/* Links with PM domain as a slave */ struct list_head dev_list;/* List of devices */ -struct mutex lock;struct dev_power_governor *gov;struct work_struct power_off_work;const char *name;
@@ -75,7 +76,14 @@ struct generic_pm_domain {struct genpd_power_state *states;unsigned int state_count;/* number of states */ unsigned int state_idx;/* state that genpd will go to when off */ - +bool irq_safe; +union { +struct mutex mlock; +struct { +spinlock_t slock; +unsigned long lock_flags; + }; + }; };static inline struct generic_pm_domain *pd_to_genpd (struct dev_pm_domain *pd)
Signature · 1 line
2.1.4
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
Quoted 12 lines · Lina Iyer
Generic Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
Quoted 5 lines
To achieve domain specific locking, set the domain's ->flag to
GENPD_FLAG_IRQ_SAFEwhile defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted throughgenpd_lock()andgenpd_unlock()functions that use the flag to determine the appropriate lock to be used for that domain.
Please add a newline here.
Quoted 1 line
Domains that have lower latency to suspend and resume and can operate
I believe I get the point, as we must not keep IRQs disabled for too long. Perhaps that can be a bit clarified?
Quoted 6 lines
with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
The restriction this imposes on the domain hierarchy is that sub-domains and all devices in the IRQ safe domain's hierarchy also have to be IRQ safe, so that we dont try to lock a mutex, while holding a spinlock.
Isn't it the opposite as you described here?
So holding a mutex while taking a spinlock should be okay, but not the other way around.
Regarding power on:
*)
If we get a request to power on the master, its spinlock will be taken, but its subdomain isn't touched.
**)
If we get a request to power on the subdomain, it will first try to power its master. That means, first we take the mutex of the subdomain, then as the master is IRQ safe we will take its spinlock. We power on the master, releases its spinlock, continues to power on the subdomain and then releases its mutex.
Regarding power off:
*)
Trying to power off the master will fail unless the atomic "sd_count" is tested for zero. In that execution path, its subdomain isn't touched.
**)
Trying to power off a subdomain, thus executing in non-atomic context, starts by taking its mutex then continue doing the power off things. When completed, the sd_count for its master will be decreased and then we schedule a power off work for it, to allow it to be powered off at some point later. Even if we wouldn't schedule a work, and instead tried to power off the master within the same context it should be safe to take a spinlock.
I haven't thought about the IRQ safe devices yet, but I guess similar applies to them.
Quoted 2 lines
Non-IRQ safe domains may continue to have devices and sub-domains that may or may not be IRQ safe.
According the my comments above, no I don't think so. Non-IRQ safe domains can't have subdomains that is IRQ safe.
Before I continue to review the code, let's align on the above first as I think it's important we get this right first. :-)
[...]
Kind regards
Uffe
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
Quoted 13 lines · Ulf Hansson
Quoted 12 lines · Lina Iyer
Generic Power Domains currently support turning on/off only in process context. This prevents the usage of PM domains for domains that could be powered on/off in a context where IRQs are disabled. Many such domains exist today and do not get powered off, when the IRQ safe devices in that domain are powered off, because of this limitation.
However, not all domains can operate in IRQ safe contexts. Genpd therefore, has to support both cases where the domain may or may not operate in IRQ safe contexts. Configuring genpd to use an appropriate lock for that domain, would allow domains that have IRQ safe devices to runtime suspend and resume, in atomic context.
Quoted 8 lines
Quoted 5 lines
To achieve domain specific locking, set the domain's ->flag to
GENPD_FLAG_IRQ_SAFEwhile defining the domain. This indicates that genpd should use a spinlock instead of a mutex for locking the domain. Locking is abstracted throughgenpd_lock()andgenpd_unlock()functions that use the flag to determine the appropriate lock to be used for that domain.Please add a newline here.
Sure.
Quoted 8 lines
Quoted 1 line
Domains that have lower latency to suspend and resume and can operate
I believe I get the point, as we must not keep IRQs disabled for too long. Perhaps that can be a bit clarified?
Quoted 3 lines
with IRQs disabled may now be able to save power, when the component devices and sub-domains are idle at runtime.
Quoted 9 lines
Quoted 3 lines
The restriction this imposes on the domain hierarchy is that sub-domains and all devices in the IRQ safe domain's hierarchy also have to be IRQ safe, so that we dont try to lock a mutex, while holding a spinlock.
Isn't it the opposite as you described here?
So holding a mutex while taking a spinlock should be okay, but not the other way around.
That's exactly what I describe. We *dont* try to lock a mutex while holding a spinlock.
Quoted 5 lines
Regarding power on:
*)
If we get a request to power on the master, its spinlock will be taken, but its subdomain isn't touched.
True. Powering on a domain should not power on its subdomains. So we are good here.
Quoted 7 lines
**)
If we get a request to power on the subdomain, it will first try to power its master. That means, first we take the mutex of the subdomain, then as the master is IRQ safe we will take its spinlock. We power on the master, releases its spinlock, continues to power on the subdomain and then releases its mutex.
The implied assumption in having a subdomain that is IRQ-safe while the parent domain is non-IRQ-safe is that the parent is never powered on/off in the context of the subdomain, it is assumed always ON.
Quoted 6 lines
Regarding power off:
*)
Trying to power off the master will fail unless the atomic "sd_count" is tested for zero. In that execution path, its subdomain isn't touched.
True for IRQ-safe parents with IRQ-safe subdomains as well as non-IRQ safe parents with IRQ-safe subdomains.
Quoted 9 lines
**)
Trying to power off a subdomain, thus executing in non-atomic context, starts by taking its mutex then continue doing the power off things. When completed, thesd_countfor its master will be decreased and then we schedule a power off work for it, to allow it to be powered off at some point later. Even if we wouldn't schedule a work, and instead tried to power off the master within the same context it should be safe to take a spinlock.
This is true as well for IRQ-safe subdomains that are attached to IRQ-safe parents. But if the parent is not IRQ-safe then the domains is left powered ON.
Quoted 3 lines
I haven't thought about the IRQ safe devices yet, but I guess similar applies to them.
The same rules apply to IRQ safe devices.
Here is an excerpt from pm_genpd_runtime_resume -
/* If power.irq_safe, the PM domain is never powered off. */
if (dev->power.irq_safe) {
timed = false;
goto out;
}Quoted 6 lines
Quoted 2 lines
Non-IRQ safe domains may continue to have devices and sub-domains that may or may not be IRQ safe.
According the my comments above, no I don't think so. Non-IRQ safe domains can't have subdomains that is IRQ safe.
Here is a summary of the combinations as I understand. Correct me if I am wrong, please.
*) Non-IRQ safe parent + IRQ-safe subdomain
Attach subdomain:
- parent mutex held and subdomain spinlocked.
Power-on:
- subdomain assumes parent is always ON or powered on before the subdomain is powered ON.
Power-off:
- parent is not powered off even if the subdomain was the last active domain.
**) IRQ-safe parent + IRQ-safe subdomain
Attach subdomain:
- parent spinlock held and subdomain spinlocked.
Power-on:
- subdomain may power on the parent.
Power-off:
- last active sub-domain will power off the parent in the same context.
***) IRQ-safe parent + non-IRQ-safe subdomain
Attach subdomain:
- parent spinlock held and subdomain **cannot** be mutex locked.
Power-on:
- subdomain may power on the parent.
Power-off:
- last active subdomain will be able to power off the domain
Except for the last case, we can support the others in addition to the currently available, with the restriction that
- IRQ-safe parents can only have IRQ-safe subdomains and devices
- Non-IRQ safe parents may have both IRQ-safe and non-IRQ-safe subdomains and devices and the assumption that
-- IRQ-safe subdomains and devices will not bother to power on/off their non-IRQ-safe parent.
Quoted 3 lines
Before I continue to review the code, let's align on the above first as I think it's important we get this right first. :-)
Absolutely.
Thank you for your review. Was expecting this detail a review of the concept from you :)
Thanks,
Lina
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
[...]
Quoted 6 lines
Quoted 6 lines
Quoted 4 lines
The restriction this imposes on the domain hierarchy is that sub-domains and all devices in the IRQ safe domain's hierarchy also have to be IRQ safe, so that we dont try to lock a mutex, while holding a spinlock.
Quoted 7 lines
Quoted 5 lines
Isn't it the opposite as you described here?
So holding a mutex while taking a spinlock should be okay, but not the other way around.
That's exactly what I describe. We *dont* try to lock a mutex while holding a spinlock.
Apologize, I wasn't referring to the mutex/spinlock as that part you state correctly.
I was reading the change log as: "*if* the parent domain is IRQ safe everything below it (devices and subdomains) also needs to be IRQ safe". Which I assume doesn't have to be the case.
Then below I was describing an example of an IRQ safe parent with a non-IRQ safe subdomain, which I think should be an okay configuration.
Quoted 16 lines
Quoted 6 lines
Regarding power on:
*)
If we get a request to power on the master, its spinlock will be taken, but its subdomain isn't touched.True. Powering on a domain should not power on its subdomains. So we are good here.
Quoted 7 lines
**)
If we get a request to power on the subdomain, it will first try to power its master. That means, first we take the mutex of the subdomain, then as the master is IRQ safe we will take its spinlock. We power on the master, releases its spinlock, continues to power on the subdomain and then releases its mutex.
Quoted 3 lines
The implied assumption in having a subdomain that is IRQ-safe while the parent domain is non-IRQ-safe is that the parent is never powered on/off in the context of the subdomain, it is assumed always ON.
Yes, that follows how gendp deals with IRQ safe devices. Although, I consider that as workaround and not a solution.
In the long run, I think IRQ safe devices should belong to IRQ safe domains.
Unless you have a specific need to apply the similar workaround for parents and subdomains, I am suggesting that genpd shouldn't allow such configurations.
[...]
Quoted 10 lines
Quoted 8 lines
Quoted 3 lines
Non-IRQ safe domains may continue to have devices and sub-domains that may or may not be IRQ safe.
According the my comments above, no I don't think so. Non-IRQ safe domains can't have subdomains that is IRQ safe.
Here is a summary of the combinations as I understand. Correct me if I am wrong, please.
Nice summary, it good that we discuss all possible scenarios!
Quoted 2 lines
*) Non-IRQ safe parent + IRQ-safe subdomain
Let's name this case 1.
Quoted 5 lines
Attach subdomain:
- parent mutex held and subdomain spinlocked.
Power-on:
- subdomain assumes parent is always ON or powered on before the subdomain is powered ON.
As stated above, what do you think of *not* dealing with this case as I think it's not a solution but a workaround!
Quoted 5 lines
Power-off:
- parent is not powered off even if the subdomain was the last active domain.**) IRQ-safe parent + IRQ-safe subdomain
Let's name this case 2.
Quoted 9 lines
Attach subdomain:
- parent spinlock held and subdomain spinlocked.
Power-on:
- subdomain may power on the parent.
Power-off:
- last active sub-domain will power off the parent in the same context.From a genpd locking point of view, there should be no reason to power
off in same context. We should be able to deal with this via a scheduled work for this case as well, right!?
Although, I realize that it's very useful to power off in the same context to minimize latencies in the full blown CPU Cluster Power Management solution.
Perhaps a separate genpd configuration flag could be added to enable this method, as I imagine that not *all* IRQ safe domains wants to use it.
What do you think?
Quoted 2 lines
***) IRQ-safe parent + non-IRQ-safe subdomain
Let's name this case 3.
Quoted 2 lines
Attach subdomain:
- parent spinlock held and subdomain **cannot** be mutex locked.
First, attaching shouldn't occur from atomic context.
Second, the important part is that we pick the locks in same order as elsewhere in genpd, and of course we must not pick a mutex while holding a spinlock.
In all cases when adding subdomains, we should start by fetching the subdomains lock *then* the lock for the parent. The current genpd code doesn't do that and additionally it uses nested locks.
I am going to send a patch the changes this behaviour, as I think it's wrong and may cause deadlocks!
Following this approach means that case 1 can't be supported, as that would mean holding a spinlock while fetching a mutex.
Quoted 5 lines
Power-on:
- subdomain may power on the parent.
Power-off:
- last active subdomain will be able to power off the domain
Quoted 9 lines
Except for the last case, we can support the others in addition to the currently available, with the restriction that
- IRQ-safe parents can only have IRQ-safe subdomains and devices- Non-IRQ safe parents may have both IRQ-safe and non-IRQ-safe subdomains and devices and the assumption that
-- IRQ-safe subdomains and devices will not bother to power on/off their non-IRQ-safe parent.
I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
Thoughts?
Kind regards
Uffe
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
On Fri, Jan 15 2016 at 01:55 -0700, Ulf Hansson wrote:
[...]
Quoted 30 lines
Quoted 2 lines
*) Non-IRQ safe parent + IRQ-safe subdomain
Let's name this case 1.
Quoted 5 lines
Attach subdomain:
- parent mutex held and subdomain spinlocked.
Power-on:
- subdomain assumes parent is always ON or powered on before the subdomain is powered ON.As stated above, what do you think of *not* dealing with this case as I think it's not a solution but a workaround!
Quoted 5 lines
Power-off:
- parent is not powered off even if the subdomain was the last active domain.**) IRQ-safe parent + IRQ-safe subdomain
Let's name this case 2.
Quoted 7 lines
Attach subdomain:
- parent spinlock held and subdomain spinlocked.
Power-on:
- subdomain may power on the parent.
Power-off:
- last active sub-domain will power off the parent in the same context.
Quoted 4 lines
From a genpd locking point of view, there should be no reason to power off in same context. We should be able to deal with this via a scheduled work for this case as well, right!?
I agree it need not be a requirement. May be we can have an option to schedule the work. In the case of CPU power management, scheduling a work is not an option, especially when the domain and the CPUs are collapsing.
Quoted 9 lines
Although, I realize that it's very useful to power off in the same context to minimize latencies in the full blown CPU Cluster Power Management solution.
Perhaps a separate genpd configuration flag could be added to enable this method, as I imagine that not *all* IRQ safe domains wants to use it.What do you think?
May be we can provide the is_async option as a genpd flag.
Quoted 14 lines
Quoted 2 lines
***) IRQ-safe parent + non-IRQ-safe subdomain
Let's name this case 3.
Quoted 2 lines
Attach subdomain:
- parent spinlock held and subdomain **cannot** be mutex locked.First, attaching shouldn't occur from atomic context.
Second, the important part is that we pick the locks in same order as elsewhere in genpd, and of course we must not pick a mutex while holding a spinlock.
Quoted 6 lines
In all cases when adding subdomains, we should start by fetching the subdomains lock *then* the lock for the parent. The current genpd code doesn't do that and additionally it uses nested locks.
I am going to send a patch the changes this behaviour, as I think it's wrong and may cause deadlocks!
Ah. Makes sense. I wish I had thought of it.
Quoted 12 lines
Following this approach means that case 1 can't be supported, as that would mean holding a spinlock while fetching a mutex.
Quoted 9 lines
Power-on:
- subdomain may power on the parent.
Power-off:
- last active subdomain will be able to power off the domainExcept for the last case, we can support the others in addition to the currently available, with the restriction that
- IRQ-safe parents can only have IRQ-safe subdomains and devices
Quoted 9 lines
Quoted 5 lines
- Non-IRQ safe parents may have both IRQ-safe and non-IRQ-safe subdomains and devices and the assumption that
-- IRQ-safe subdomains and devices will not bother to power on/off their non-IRQ-safe parent.I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
With you other patch, it does make sense to have 2 and 3 instead of 1 and 2.
My only concern is as I view the SoC as a whole, there would be cases where the parent domain could only be non-IRQ safe (turning off a big regulator may require some sleep) and the subdomains are quicker and faster IRQ-safe domains. Putting up this restriction, chops up the domain hierarchy. Imagine, if you could represent the whole power domain organization in DT, but because of this restriction, we may not be able to do that.
Thanks,
Lina
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
[...]
Quoted 5 lines
Quoted 3 lines
I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
With you other patch, it does make sense to have 2 and 3 instead of 1 and 2.
Okay!
Quoted 4 lines
My only concern is as I view the SoC as a whole, there would be cases where the parent domain could only be non-IRQ safe (turning off a big regulator may require some sleep) and the subdomains are quicker and faster IRQ-safe domains. Putting up this restriction, chops up the
Just because they are "quick" (don't sleep) doesn't mean they have to set the IRQ safe flag for the domain.
Let's compare how pm_runtime_irq_safe() is used for devices in drivers. Lots of corresponding runtime PM callbacks don't sleep, but that don't mean that the driver calls pm_runtime_irq_safe().
Drivers that are expecting to invoke pm_runtime_get_sync() (or other synchronous runtime PM API) from atomic context, requires the corresponding runtime PM callbacks to be IRQ safe. Only under these circumstances the pm_runtime_irq_safe() is used.
Quoted 3 lines
domain hierarchy. Imagine, if you could represent the whole power domain organization in DT, but because of this restriction, we may not be able to do that.
You do have a point. Considering my comment above, do you still think this may become an issue?
Kind regards
Uffe
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
Quoted 18 lines · Ulf Hansson
[...]
Quoted 5 lines
Quoted 3 lines
I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
With you other patch, it does make sense to have 2 and 3 instead of 1 and 2.
Okay!
Quoted 4 lines
My only concern is as I view the SoC as a whole, there would be cases where the parent domain could only be non-IRQ safe (turning off a big regulator may require some sleep) and the subdomains are quicker and faster IRQ-safe domains. Putting up this restriction, chops up the
Just because they are "quick" (don't sleep) doesn't mean they have to set the IRQ safe flag for the domain.
Quoted 9 lines
Let's compare how
pm_runtime_irq_safe()is used for devices in drivers. Lots of corresponding runtime PM callbacks don't sleep, but that don't mean that the driver callspm_runtime_irq_safe().Drivers that are expecting to invoke
pm_runtime_get_sync()(or other synchronous runtime PM API) from atomic context, requires the corresponding runtime PM callbacks to be IRQ safe. Only under these circumstances thepm_runtime_irq_safe()is used.
I agree.
The limitation and the interpretation could completely be s/w. There is nothing preventing that in hardware. So we could have a processor subsystem domain that can collapse faster and even during cpuidle. We would define that domain IRQ-safe because of its need to collapse and resume when IRQs are disabled, but its parent may not have that restriction, in fact, the parent could have another child that could only operate as non-IRQ safe.
By imposing the limitation that parents of IRQ-safe domains cannot be non-IRQ-safe, we have to chop off that relationship, because of another non-IRQ-safe sibling.
Quoted 7 lines
Quoted 3 lines
domain hierarchy. Imagine, if you could represent the whole power domain organization in DT, but because of this restriction, we may not be able to do that.
You do have a point. Considering my comment above, do you still think this may become an issue?
Yes. Pls see my example below.
That said, I probably won't encounter this limitation in my current series, but its not hard to foresee this corner case.
Thanks,
Lina
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
Quoted 43 lines · Lina Iyer
Quoted 18 lines · Ulf Hansson
[...]
Quoted 5 lines
Quoted 3 lines
I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
With you other patch, it does make sense to have 2 and 3 instead of 1 and 2.
Okay!
Quoted 4 lines
My only concern is as I view the SoC as a whole, there would be cases where the parent domain could only be non-IRQ safe (turning off a big regulator may require some sleep) and the subdomains are quicker and faster IRQ-safe domains. Putting up this restriction, chops up the
Just because they are "quick" (don't sleep) doesn't mean they have to set the IRQ safe flag for the domain.
Quoted 9 lines
Let's compare how
pm_runtime_irq_safe()is used for devices in drivers. Lots of corresponding runtime PM callbacks don't sleep, but that don't mean that the driver callspm_runtime_irq_safe().Drivers that are expecting to invoke
pm_runtime_get_sync()(or other synchronous runtime PM API) from atomic context, requires the corresponding runtime PM callbacks to be IRQ safe. Only under these circumstances thepm_runtime_irq_safe()is used.I agree.
The limitation and the interpretation could completely be s/w. There is nothing preventing that in hardware. So we could have a processor subsystem domain that can collapse faster and even during cpuidle. We would define that domain IRQ-safe because of its need to collapse and resume when IRQs are disabled, but its parent may not have that restriction, in fact, the parent could have another child that could only operate as non-IRQ safe.
By imposing the limitation that parents of IRQ-safe domains cannot be non-IRQ-safe, we have to chop off that relationship, because of another
non-IRQ-safe sibling.
Quoted 9 lines
Quoted 7 lines
Quoted 3 lines
domain hierarchy. Imagine, if you could represent the whole power domain organization in DT, but because of this restriction, we may not be able to do that.
You do have a point. Considering my comment above, do you still think this may become an issue?
Yes. Pls see my example below.
Sorry above ;)
Quoted 5 lines
That said, I probably won't encounter this limitation in my current series, but its not hard to foresee this corner case.
Thanks,
Lina
RFC PATCH 8/27 PM / Domains: Support IRQ safe PM domains
In reply to Lina IyerQuoted 50 lines · Lina Iyer
Quoted 21 lines · Ulf Hansson
[...]
Quoted 5 lines
Quoted 3 lines
I think case 2 and case 3 should be okay to support, but I am really questioning case 1.
With you other patch, it does make sense to have 2 and 3 instead of 1 and 2.
Okay!
Quoted 4 lines
My only concern is as I view the SoC as a whole, there would be cases where the parent domain could only be non-IRQ safe (turning off a big regulator may require some sleep) and the subdomains are quicker and faster IRQ-safe domains. Putting up this restriction, chops up the
Just because they are "quick" (don't sleep) doesn't mean they have to set the IRQ safe flag for the domain.
Quoted 9 lines
Let's compare how
pm_runtime_irq_safe()is used for devices in drivers. Lots of corresponding runtime PM callbacks don't sleep, but that don't mean that the driver callspm_runtime_irq_safe().Drivers that are expecting to invoke
pm_runtime_get_sync()(or other synchronous runtime PM API) from atomic context, requires the corresponding runtime PM callbacks to be IRQ safe. Only under these circumstances thepm_runtime_irq_safe()is used.I agree.
The limitation and the interpretation could completely be s/w. There is nothing preventing that in hardware. So we could have a processor subsystem domain that can collapse faster and even during cpuidle. We would define that domain IRQ-safe because of its need to collapse and resume when IRQs are disabled, but its parent may not have that restriction, in fact, the parent could have another child that could only operate as non-IRQ safe.
By imposing the limitation that parents of IRQ-safe domains cannot be non-IRQ-safe, we have to chop off that relationship, because of another non-IRQ-safe sibling.
Quoted 5 lines
Quoted 3 lines
domain hierarchy. Imagine, if you could represent the whole power domain organization in DT, but because of this restriction, we may not be able to do that.
Quoted 7 lines
Quoted 3 lines
You do have a point. Considering my comment above, do you still think this may become an issue?
Yes. Pls see my example below.
That said, I probably won't encounter this limitation in my current series, but its not hard to foresee this corner case.
Okay, thanks for sharing your thoughts.
For now, I suggest we leave case 1) as a non-supported option.
When we have a valid use case for case 1), we anyway will have to think of a better approach than what's suggested in $subject patch, since keeping the parent domain always powered on isn't good enough.
Kind regards
Uffe
RFC PATCH 9/27 PM / Domains: Attempt runtime suspend of IRQ safe parent domain
In reply to Lina IyerWhen a sub-domain is powered off, attempt powering off the parent domains to maximize power savings. A sub-domain that is IRQ safe may however have a parent that is not IRQ safe and therefore cannot be powered down in atomic context that the sub-domain may be powered off.
An IRQ safe sub-domain may attempt to power down the parent domain in a synchronous call, if the parent is also IRQ safe.
--- drivers/base/power/domain.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/base/power/domain.c +16 -0
@@ -44,6 +44,8 @@ static int pm_genpd_alloc_states_names(struct generic_pm_domain *genpd,static LIST_HEAD (gpd_list);static DEFINE_MUTEX (gpd_list_lock); +static int genpd_poweroff (struct generic_pm_domain *genpd,bool is_async); + static inline int genpd_lock_nosleep(struct generic_pm_domain *genpd, unsigned int subclass) __acquires(&genpd->slock)
@@ -298,6 +300,13 @@ static int __genpd_poweron(struct generic_pm_domain *genpd) &genpd->slave_links, slave_node) {genpd_sd_counter_dec (link->master); + +/* Assume masters that are non-irq safe are always-on */ +if (genpd->irq_safe && link->master->irq_safe) { +genpd_poweroff (link->master, false); +continue ; + } +genpd_queue_power_off_work (link->master); }
@@ -448,6 +457,13 @@ static int genpd_poweroff(struct generic_pm_domain *genpd, bool is_async)list_for_each_entry (link, &genpd->slave_links, slave_node) {genpd_sd_counter_dec (link->master); + +/* Assume masters that are non-irq safe are always-on */ +if (genpd->irq_safe && link->master->irq_safe) { +genpd_poweroff (link->master, false); +continue ; + } +genpd_queue_power_off_work (link->master); }
Signature · 1 line
2.1.4
RFC PATCH 10/27 drivers: power: Introduce PM domains for CPUs/clusters
In reply to Lina IyerDefine and add Generic PM domains (genpd) for CPU clusters. Many new SoCs group CPUs as clusters. Clusters share common resources like power rails, caches, VFP, Coresight etc. When all CPUs in the cluster are idle, these shared resources may also be put in their idle state.
The idle time between the last CPU entering idle and a CPU resuming execution is an opportunity for these shared resources to be powered down. Generic PM domain provides a framework for defining such power domains and attach devices to the domain. When the devices in the domain are idle at runtime, the domain would also be suspended and resumed before the first of the devices resume execution.
We define a generic PM domain for each cluster and attach CPU devices in the cluster to that PM domain. The DT definitions for the SoC describe this relationship. Genpd callbacks for power_on and power_off can then be used to power up/down the shared resources for the domain.
--- Documentation/arm/cpu-domains.txt | 52 +++++++++ drivers/base/power/Makefile | 1 + drivers/base/power/cpu-pd.c | 231 ++++++++++++++++++++++++++++++++++++++ include/linux/cpu-pd.h | 32 ++++++ 4 files changed, 316 insertions(+) create mode 100644 Documentation/arm/cpu-domains.txt create mode 100644 drivers/base/power/cpu-pd.c create mode 100644 include/linux/cpu-pd.h
Documentation/arm/cpu-domains.txt new file +52 -0
@@ -0,0 +1,52 @@ +CPU Clusters and PM domain + +Newer CPUs are grouped in a SoC as clusters. A cluster in addition to the +CPUs may have caches, GIC, VFP and architecture specific power controller to +power the cluster. A cluster may also be nested in another cluster, the +hierarchy of which is depicted in the device tree. CPUIdle frameworks enables +the CPUs to determine the sleep time and enter low power state to save power +during periods of idle. CPUs in a cluster may enter and exit idle state +independently. During the time when all the CPUs are in idle state, the +cluster can safely be in idle state as well. When the last of the CPUs is +powered off as a result of idle, the cluster may also be powered down, but the +domain must be powered on before the first of the CPUs in the cluster resumes +execution. + +SoCs can power down the CPU and resume execution in a few uSecs and the domain +that powers the CPU cluster also have comparable idle latencies. The CPU WFI +signal in ARM CPUs is used as a hardware trigger for the cluster hardware to +enter their idle state. The hardware can be programmed in advance to put the +cluster in the desired idle state befitting the wakeup latency requested by +the CPUs. When all the CPUs in a cluster have executed their WFI instruction, +the state machine for the power controller may put the cluster components in +their power down or idle state. Generally, the domains would power on with the +hardware sensing the CPU's interrupts. The domains may however, need to be +reconfigured by the CPU to remain active, until the last CPU is ready to enter +idle again. To power down a cluster, it is generally required to power down +all the CPUs. The caches would also need to be flushed. The hardware state of +some of the components may need to be saved and restored when powered back on. +SoC vendors may also have hardware specific configuration that must be done +before the cluster can be powered off. When the cluster is powered off, +notifications may be sent out to other SoC components to scale down or even +power off their resources. + +Power management domains represent relationship of devices and their power +controllers. They are represented in the DT as domain consumers and providers. +A device may have a domain provider and a domain provider may support multiple +domain consumers. Domains like clusters, may also be nested inside one +another. A domain that has no active consumer, may be powered off and any +resuming consumer would trigger the domain back to active. Parent domains may +be powered off when the child domains are powered off. The CPU cluster can be +fashioned as a PM domain. When the CPU devices are powered off, the PM domain +may be powered off. + +The code in Generic PM domains handles the hierarchy of devices, domains and +the reference counting of objects leading to last man down and first man up. +The CPU domains core code defines PM domains for each CPU cluster and attaches +the domains' CPU devices to as specified in the DT. Platform drivers may use +the following API to register their CPU PM domains. + +of_init_cpu_pm_domain() - +Provides a single step registration of the CPU PM domain and attach CPUs to +the genpd. Platform drivers may additionally register callbacks for power_on +and power_off operations.
drivers/base/power/Makefile +1 -0
@@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_OPP) += opp/ obj-$(CONFIG_PM_GENERIC_DOMAINS) += domain.o domain_governor.o +obj-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpu-pd.o obj-$(CONFIG_HAVE_CLK) += clock_ops.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/cpu-pd.c new file +231 -0
231 lines not on this page · raw message
include/linux/cpu-pd.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +* include/linux/cpu-pd.h +* +* Copyright (C) 2015 Linaro Ltd. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef __CPU_PD_H__ +#define __CPU_PD_H__ + +#include <linux/list.h> +#include <linux/of.h> +#include <linux/pm_domain.h> + +struct cpu_pd_ops { +int (*power_off)(struct generic_pm_domain *genpd); +int (*power_on)(struct generic_pm_domain *genpd); +}; + +struct cpu_pm_domain { +struct list_head link; +struct generic_pm_domain *genpd; +struct device_node *dn; +struct cpu_pd_ops plat_ops; +}; + +struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn, +const struct cpu_pd_ops *ops); +#endif /* __CPU_PD_H__ */
Signature · 1 line
2.1.4
RFC PATCH 10/27 drivers: power: Introduce PM domains for CPUs/clusters
Documentation/arm/cpu-domains.txt line 6
Quoted 12 lines · Lina Iyer
Documentation/arm/cpu-domains.txt new file +6
-0@@ -0,0 +1,52 @@ +CPU Clusters and PM domain + +Newer CPUs are grouped in a SoC as clusters. A cluster in addition to the +CPUs may have caches, GIC, VFP and architecture specific power controller to +power the cluster. A cluster may also be nested in another cluster, the +hierarchy of which is depicted in the device tree. CPUIdle frameworks enables
s/frameworks/framework/?
s/depicted/described/? Hopefully we aren't putting pictures or art in DT for this sort of stuff.
Quoted 16 lines
drivers/base/power/cpu-pd.c new file +10
-0@@ -0,0 +1,231 @@ +/* +* CPU Generic PM Domain. +* +* Copyright (C) 2015 Linaro Ltd. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ +
drivers/base/power/cpu-pd.c line 20
Quoted 10 lines
drivers/base/power/cpu-pd.c new file +10
-0+#define DEBUG + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/cpu.h> +#include <linux/cpu_pm.h> +#include <linux/cpu-pd.h> +#include <linux/device.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h>
Is this used?
Quoted 5 lines
drivers/base/power/cpu-pd.c new file +5
-0+#include <linux/rculist.h> +#include <linux/slab.h> + +#define CPU_PD_NAME_MAX 36 +
[...]
Quoted 12 lines
drivers/base/power/cpu-pd.c new file +12
-0+static int of_pm_domain_attach_cpus (struct device_node *dn) +{ +int cpuid, ret; + +/* Find any CPU nodes with a phandle to this power domain */ +for_each_possible_cpu (cpuid) { +struct device *cpu_dev; +struct device_node *cpu_pd; + + cpu_dev =get_cpu_device (cpuid); +if (!cpu_dev) { + pr_warn("%s: Unable to get device for CPU%d\n",
drivers/base/power/cpu-pd.c line 105
Quoted 10 lines
drivers/base/power/cpu-pd.c new file +10
-0+ __func__, cpuid); +return -ENODEV ; + } + +/* Only attach CPUs that are part of this domain */ + cpu_pd =of_parse_phandle (cpu_dev->of_node,"power-domains" ,0 ); +if (cpu_pd != dn) +continue ; + +if (cpu_online (cpuid)) {
I guess we don't care if hotplug is running in parallel to this code?
Quoted 20 lines
drivers/base/power/cpu-pd.c new file +20
-0+pm_runtime_set_active (cpu_dev); +/* +* Execute the below on that 'cpu' to ensure that the +* reference counting is correct. It's possible that +* while this code is executing, the 'cpu' may be +* powered down, but we may incorrectly increment the +* usage. By executing the get_cpu on the 'cpu', +* we can ensure that the 'cpu' and its usage count are +* matched. +*/ +smp_call_function_single (cpuid, run_cpu,NULL , true); + }else { +pm_runtime_set_suspended (cpu_dev); + } + + ret =genpd_dev_pm_attach (cpu_dev); +if (ret) { +dev_warn (cpu_dev, +"%s: Unable to attach to power-domain: %d\n" , + __func__, ret);
drivers/base/power/cpu-pd.c line 135
Quoted 10 lines
drivers/base/power/cpu-pd.c new file +10
-0+ }else { +pm_runtime_enable (cpu_dev); +dev_dbg (cpu_dev,"Attached CPU%d to domain\n" , cpuid); + } + } + +return 0 ; +} + +int of_register_cpu_pm_domain(struct device_node *dn,
static?
Quoted 43 lines
drivers/base/power/cpu-pd.c new file +43
-0+ struct cpu_pm_domain *pd) +{ +int ret; + +if (!pd || !pd->genpd) +return -EINVAL ; + +/* +* The platform should not set up the genpd callbacks. +* They should setup the pd->plat_ops instead. +*/ +WARN_ON (pd->genpd->power_off); +WARN_ON (pd->genpd->power_on); + + pd->genpd->power_off = cpu_pd_power_off; + pd->genpd->power_on = cpu_pd_power_on; + pd->genpd->flags |=GENPD_FLAG_IRQ_SAFE ; + +INIT_LIST_HEAD_RCU (&pd->link); +spin_lock (&cpu_pd_list_lock); +list_add_rcu (&pd->link, &of_cpu_pd_list); +spin_unlock (&cpu_pd_list_lock); + pd->dn = dn; + +/* Register the CPU genpd */ +pr_debug ("adding %s as CPU PM domain.\n" , pd->genpd->name); + ret =of_pm_genpd_init (dn, pd->genpd, &simple_qos_governor, false); +if (ret) { +pr_err ("Unable to initialize domain %s\n" , dn->full_name); +return ret; + } + + ret =of_genpd_add_provider_simple (dn, pd->genpd); +if (ret) +pr_warn ("Unable to add genpd %s as provider\n" , + pd->genpd->name); + +/* Attach the CPUs to the CPU PM domain */ + ret =of_pm_domain_attach_cpus (dn); +if (ret) +of_genpd_del_provider (dn); + +return ret;
drivers/base/power/cpu-pd.c line 188
Quoted 10 lines
drivers/base/power/cpu-pd.c new file +10
-0+} + +/** +* of_init_cpu_pm_domain() - Initialize a CPU PM domain using the CPU pd +* provided +* @dn: PM domain provider device node +* @ops: CPU PM domain platform specific ops for callback +* +* This is a single step initialize the CPU PM domain with defaults, +* also register the genpd and attach CPUs to the genpd.
Returns?
Quoted 28 lines
+ */
drivers/base/power/cpu-pd.c new file +27
-0+struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn, +const struct cpu_pd_ops *ops) +{ +struct cpu_pm_domain *pd; +int ret; + +if (!of_device_is_available (dn)) +return ERR_PTR (-ENODEV ); + + pd =kzalloc (sizeof (*pd),GFP_KERNEL ); +if (!pd) +return ERR_PTR (-ENOMEM ); + + pd->genpd =kzalloc (sizeof (*(pd->genpd)),GFP_KERNEL ); +if (!pd->genpd) { +kfree (pd); +return ERR_PTR (-ENOMEM ); + } + + pd->genpd->name =kstrndup (dn->full_name,CPU_PD_NAME_MAX ,GFP_KERNEL ); +if (!pd->genpd->name) { +kfree (pd->genpd); +kfree (pd); +return ERR_PTR (-ENOMEM ); + } + +if (ops) {
drivers/base/power/cpu-pd.c line 226
Quoted 10 lines
drivers/base/power/cpu-pd.c new file +10
-0+ pd->plat_ops.power_off = ops->power_off; + pd->plat_ops.power_on = ops->power_on; + } + + ret =of_register_cpu_pm_domain (dn, pd); +if (ret) { +kfree (pd->genpd->name); +kfree (pd->genpd); +kfree (pd); +return ERR_PTR (ret);
Maybe we can have a goto error path so that we don't duplicate these kfree calls a bunch of times.
Quoted 5 lines
drivers/base/power/cpu-pd.c new file +5
-0+ } + +return pd->genpd; +} +EXPORT_SYMBOL (of_init_cpu_pm_domain);
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 11/27 drivers: cpu: Define CPU devices as IRQ safe
In reply to Lina IyerCPUs may be powered off from CPUIdle or hotplug and are called with IRQ's disabled. Define CPU devices as IRQ safe, so they may be runtime suspended/resumed.
--- drivers/base/cpu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/base/cpu.c +4 -2
@@ -17,6 +17,7 @@#include <linux/of.h> #include <linux/cpufeature.h> #include <linux/tick.h> +#include <linux/pm_runtime.h> #include "base.h"
@@ -371,10 +372,11 @@ int register_cpu(struct cpu *cpu, int num)if (cpu->hotpluggable) cpu->dev.groups = hotplugable_cpu_attr_groups; error =device_register (&cpu->dev); -if (!error) +if (!error) { +pm_runtime_irq_safe (&cpu->dev);per_cpu (cpu_sys_devices, num) = &cpu->dev; -if (!error)register_cpu_under_node (num,cpu_to_node (num)); + }return error; }
Signature · 1 line
2.1.4
RFC PATCH 12/27 ARM: cpuidle: remove cpu parameter from the cpuidle_ops suspend hook
In reply to Lina IyerFrom: Lorenzo Pieralisi <[email protected]>
The suspend() hook in the cpuidle_ops struct is always called on the cpu entering idle, which means that the cpu parameter passed to the suspend hook always corresponds to the local cpu, making it somewhat redundant.
This patch removes the logical cpu parameter from the ARM cpuidle_ops.suspend hook and updates all the existing kernel implementations to reflect this change.
--- arch/arm/include/asm/cpuidle.h | 2 +- arch/arm/kernel/cpuidle.c | 2 +- drivers/soc/qcom/spm.c | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-)
arch/arm/include/asm/cpuidle.h +1 -1
@@ -30,7 +30,7 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev,struct device_node ;struct cpuidle_ops { -int (*suspend)(int cpu,unsigned long arg); +int (*suspend)(unsigned long arg);int (*init)(struct device_node *,int cpu); };
arch/arm/kernel/cpuidle.c +1 -1
@@ -56,7 +56,7 @@ int arm_cpuidle_suspend(int index)int cpu =smp_processor_id ();if (cpuidle_ops[cpu].suspend) - ret = cpuidle_ops[cpu].suspend (cpu, index); + ret = cpuidle_ops[cpu].suspend (index);return ret; }
drivers/soc/qcom/spm.c +5 -5
@@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = { static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv); -typedef int (*idle_fn )(int ); +typedef int (*idle_fn )(void ); static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops); static inline void spm_register_write(struct spm_driver_data *drv,
@@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused)return -1 ; } -static int qcom_cpu_spc (int cpu) +static int qcom_cpu_spc (void ) {int ret; -struct spm_driver_data *drv =per_cpu (cpu_spm_drv, cpu); +struct spm_driver_data *drv =__this_cpu_read (cpu_spm_drv);spm_set_low_power_mode (drv,PM_SLEEP_MODE_SPC ); ret =cpu_suspend (0 , qcom_pm_collapse);
@@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu)return ret; } -static int qcom_idle_enter (int cpu,unsigned long index) +static int qcom_idle_enter (unsigned long index) { -return per_cpu (qcom_idle_ops, cpu)[index](cpu); +return __this_cpu_read (qcom_idle_ops)[index](); }static const struct of_device_id qcom_idle_state_match[]__initconst = {
Signature · 1 line
2.1.4
RFC PATCH 13/27 ARM: cpuidle: Add runtime PM support for CPU idle
In reply to Lina IyerNotify runtime PM when the CPU is going to be powered off in the idle state. This allows for runtime PM suspend/resume of the CPU as well as its PM domain.
--- drivers/cpuidle/cpuidle-arm.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/cpuidle/cpuidle-arm.c +8 -0
@@ -11,13 +11,16 @@#define pr_fmt (fmt) "CPUidle arm: " fmt +#include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/rcupdate.h> #include <asm/cpuidle.h>
@@ -45,6 +48,10 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) { +struct device *cpu_dev =get_cpu_device (dev->cpu); + +RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev)); +/* * Pass idle state index to cpu_suspend which in turn will * call the CPU ops suspend protocol with idle index as a
@@ -52,6 +59,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,*/ arm_cpuidle_suspend (idx); +RCU_NONIDLE (pm_runtime_get_sync (cpu_dev));cpu_pm_exit (); }
Signature · 1 line
2.1.4
RFC PATCH 13/27 ARM: cpuidle: Add runtime PM support for CPU idle
Quoted 33 lines · Lina Iyer
Notify runtime PM when the CPU is going to be powered off in the idle state. This allows for runtime PM suspend/resume of the CPU as well as its PM domain.
Cc Daniel Lezcano <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 8 ++++++++ 1 file changed, 8 insertions(+)drivers/cpuidle/cpuidle-arm.c +3
-0@@ -11,13 +11,16 @@#define pr_fmt (fmt) "CPUidle arm: " fmt +#include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/rcupdate.h> #include <asm/cpuidle.h>
drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 53
Quoted 6 lines
drivers/cpuidle/cpuidle-arm.c +3
-0@@ -45,6 +48,10 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) { +struct device *cpu_dev =get_cpu_device (dev->cpu); + +RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));
By my test, it seems that the usage_count of cpu device will be decrease to -1 and the code never runs here. If it need a runtime_put somewhere?
Quoted 19 lines
drivers/cpuidle/cpuidle-arm.c +2
-1+/* * Pass idle state index to cpu_suspend which in turn will * call the CPU ops suspend protocol with idle index as a @@ -52,6 +59,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,*/ arm_cpuidle_suspend (idx); +RCU_NONIDLE (pm_runtime_get_sync (cpu_dev));cpu_pm_exit (); } --2.1.4
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC PATCH 13/27 ARM: cpuidle: Add runtime PM support for CPU idle
Quoted 35 lines · Zhaoyang Huang
Quoted 34 lines · Lina Iyer
Notify runtime PM when the CPU is going to be powered off in the idle state. This allows for runtime PM suspend/resume of the CPU as well as its PM domain.
Cc Daniel Lezcano <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 8 ++++++++ 1 file changed, 8 insertions(+)drivers/cpuidle/cpuidle-arm.c +3
-0@@ -11,13 +11,16 @@#define pr_fmt (fmt) "CPUidle arm: " fmt +#include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/rcupdate.h> #include <asm/cpuidle.h> @@ -45,6 +48,10 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
Quoted 8 lines
drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 53
Quoted 5 lines
drivers/cpuidle/cpuidle-arm.c +3
-0ret =cpu_pm_enter ();if (!ret) { +struct device *cpu_dev =get_cpu_device (dev->cpu); + +RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));By my test, it seems that the
usage_countof cpu device will be decrease to -1 and the code never runs here. If it need aruntime_putsomewhere?
You shouldn't have to. Pls make sure all the patches are pulled in and compiles successfully.
Let me know if you still run into issues.
Thanks,
Lina
Quoted 19 lines
Quoted 19 lines
drivers/cpuidle/cpuidle-arm.c +2
-1+/* * Pass idle state index to cpu_suspend which in turn will * call the CPU ops suspend protocol with idle index as a @@ -52,6 +59,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,*/ arm_cpuidle_suspend (idx); +RCU_NONIDLE (pm_runtime_get_sync (cpu_dev));cpu_pm_exit (); } --2.1.4
--
To unsubscribe from this list: send the line "unsubscribe linux-pm" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
RFC PATCH 13/27 ARM: cpuidle: Add runtime PM support for CPU idle
In reply to Lina IyerQuoted 33 lines · Lina Iyer
Notify runtime PM when the CPU is going to be powered off in the idle state. This allows for runtime PM suspend/resume of the CPU as well as its PM domain.
Cc Daniel Lezcano <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 8 ++++++++ 1 file changed, 8 insertions(+)drivers/cpuidle/cpuidle-arm.c +3
-0@@ -11,13 +11,16 @@#define pr_fmt (fmt) "CPUidle arm: " fmt +#include <linux/cpu.h> #include <linux/cpuidle.h> #include <linux/cpumask.h> #include <linux/cpu_pm.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +#include <linux/rcupdate.h> #include <asm/cpuidle.h>
drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 54
Quoted 7 lines
drivers/cpuidle/cpuidle-arm.c +4
-0@@ -45,6 +48,10 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) { +struct device *cpu_dev =get_cpu_device (dev->cpu); + +RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev)); +
A comment in the changelog about the use of _put_sync_suspend() instead of a normal _put_sync() would be helpful for this patch.
Kevin
RFC PATCH 14/27 tick: get next wakeup event for the CPU
In reply to Lina IyerCPUidle governors use the sleep time of the CPU to determine the idle state of the CPU. However, to determine a common sleep time between CPUs that enter idle at different times, the tick_nohz_get_sleep_length() API is not very helpful.
Add API to read the next event for the CPU. The value can be evaluated at any time to determine the remaining sleep time of the CPU.
--- include/linux/tick.h | 10 ++++++++++ kernel/time/tick-sched.c | 8 ++++++++ 2 files changed, 18 insertions(+)
include/linux/tick.h +10 -0
@@ -103,6 +103,7 @@ extern void tick_nohz_idle_enter(void);extern void tick_nohz_idle_exit (void );extern void tick_nohz_irq_exit (void );extern ktime_t tick_nohz_get_sleep_length (void ); +extern ktime_t tick_nohz_get_next_wakeup (void );extern u64 get_cpu_idle_time_us (int cpu,u64 *last_update_time);extern u64 get_cpu_iowait_time_us (int cpu,u64 *last_update_time);#else /* !CONFIG_NO_HZ_COMMON */
@@ -116,6 +117,15 @@ static inline ktime_t tick_nohz_get_sleep_length(void) return len; } + +static inline ktime_t tick_nohz_get_next_wakeup (void ) +{ +ktime_t len = { .tv64 =NSEC_PER_SEC /HZ }; + +/* Next wake up is the tick period, assume it starts now */ +return ktime_add (len,ktime_get ()); +} +static inline u64 get_cpu_idle_time_us (int cpu,u64 *unused) {return -1 ; }static inline u64 get_cpu_iowait_time_us (int cpu,u64 *unused) {return -1 ; }#endif /* !CONFIG_NO_HZ_COMMON */
kernel/time/tick-sched.c +8 -0
@@ -870,6 +870,14 @@ ktime_t tick_nohz_get_sleep_length(void)return ts->sleep_length; } +ktime_t tick_nohz_get_next_wakeup (void ) +{ +struct clock_event_device *dev = +__this_cpu_read (tick_cpu_device.evtdev); + +return dev->next_event; +} +static void tick_nohz_account_idle_ticks (struct tick_sched *ts) {#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
Signature · 1 line
2.1.4
RFC PATCH 15/27 PM / Domains: Add next_wakeup to device's timing data
In reply to Lina IyerAllow devices that know when their next wakeup event is, to record save it as part of timing data. A genpd governor may use this data to determine if suspending the domain is going to affect the QoS of its devices.
--- include/linux/pm_domain.h | 2 ++ 1 file changed, 2 insertions(+)
include/linux/pm_domain.h +2 -0
@@ -16,6 +16,7 @@#include <linux/of.h> #include <linux/notifier.h> #include <linux/spinlock.h> +#include <linux/ktime.h> /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */
@@ -104,6 +105,7 @@ struct gpd_timing_data {s64 effective_constraint_ns;bool constraint_changed;bool cached_stop_ok; +ktime_t next_wakeup; };struct pm_domain_data {
Signature · 1 line
2.1.4
RFC PATCH 15/27 PM / Domains: Add next_wakeup to device's timing data
Quoted 23 lines · Lina Iyer
Allow devices that know when their next wakeup event is, to record save it as part of timing data. A genpd governor may use this data to determine if suspending the domain is going to affect the QoS of its devices.
Signed-off-by Lina Iyer <[email protected]>--- include/linux/pm_domain.h | 2 ++ 1 file changed, 2 insertions(+)include/linux/pm_domain.h +1
-0@@ -16,6 +16,7 @@#include <linux/of.h> #include <linux/notifier.h> #include <linux/spinlock.h> +#include <linux/ktime.h> /* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */
include/linux/pm_domain.h struct gpd_timing_data { · line 109
Quoted 5 lines
include/linux/pm_domain.h +1
-0@@ -104,6 +105,7 @@ struct gpd_timing_data {s64 effective_constraint_ns;bool constraint_changed;bool cached_stop_ok; +ktime_t next_wakeup; };
The addition of a new field should be combined with the patch that uses it (e.g. PATCH 16/27)
Kevin
RFC PATCH 15/27 PM / Domains: Add next_wakeup to device's timing data
Quoted 22 lines · Kevin Hilman
Quoted 20 lines · Lina Iyer
Allow devices that know when their next wakeup event is, to record save it as part of timing data. A genpd governor may use this data to determine if suspending the domain is going to affect the QoS of its devices.
Signed-off-by Lina Iyer <[email protected]>--- include/linux/pm_domain.h | 2 ++ 1 file changed, 2 insertions(+)include/linux/pm_domain.h +1
-0@@ -16,6 +16,7 @@#include <linux/of.h> #include <linux/notifier.h> #include <linux/spinlock.h> +#include <linux/ktime.h>
Quoted 12 lines
include/linux/pm_domain.h struct gpd_timing_data { · line 109
Quoted 8 lines
include/linux/pm_domain.h +1
-0/* Defines used for the flags field in the struct generic_pm_domain */ #define GENPD_FLAG_PM_CLK (1U << 0)/* PM domain uses PM clk */ @@ -104,6 +105,7 @@ struct gpd_timing_data {s64 effective_constraint_ns;bool constraint_changed;bool cached_stop_ok; +ktime_t next_wakeup; };The addition of a new field should be combined with the patch that uses it (e.g. PATCH 16/27)
Okay. Will do.
Quoted 1 line
Kevin
RFC PATCH 16/27 ARM: cpuidle: Record the next wakeup event of the CPU
In reply to Lina IyerReading the next wakeup of the CPU can only be realiably done only from that CPU. In the idle enter path record the next wake up of the CPU. The information is useful to determine the sleep time left for the CPU.
--- drivers/cpuidle/cpuidle-arm.c | 5 +++++ 1 file changed, 5 insertions(+)
drivers/cpuidle/cpuidle-arm.c +5 -0
@@ -18,9 +18,11 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/rcupdate.h> +#include <linux/tick.h> #include <asm/cpuidle.h>
@@ -49,7 +51,9 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) {struct device *cpu_dev =get_cpu_device (dev->cpu); +struct generic_pm_domain_data *gpd =dev_gpd_data (cpu_dev); + gpd->td.next_wakeup =tick_nohz_get_next_wakeup ();RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));/*
@@ -60,6 +64,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,arm_cpuidle_suspend (idx);RCU_NONIDLE (pm_runtime_get_sync (cpu_dev)); + gpd->td.next_wakeup.tv64 =0 ;cpu_pm_exit (); }
Signature · 1 line
2.1.4
RFC PATCH 16/27 ARM: cpuidle: Record the next wakeup event of the CPU
Quoted 26 lines · Lina Iyer
Reading the next wakeup of the CPU can only be realiably done only from that CPU. In the idle enter path record the next wake up of the CPU. The information is useful to determine the sleep time left for the CPU.
Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 5 +++++ 1 file changed, 5 insertions(+)drivers/cpuidle/cpuidle-arm.c +2
-0@@ -18,9 +18,11 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/rcupdate.h> +#include <linux/tick.h> #include <asm/cpuidle.h>
drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 57
Quoted 7 lines
drivers/cpuidle/cpuidle-arm.c +2
-0@@ -49,7 +51,9 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) {struct device *cpu_dev =get_cpu_device (dev->cpu); +struct generic_pm_domain_data *gpd =dev_gpd_data (cpu_dev); + gpd->td.next_wakeup =tick_nohz_get_next_wakeup ();RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));
Maybe set this back to zero atomicaly, after wakeup?
Checking for non-zero that might be another way for the domain goveror that there haven't been any CPU wakeups since the CPUs have gone idle.
Kevin
RFC PATCH 16/27 ARM: cpuidle: Record the next wakeup event of the CPU
Quoted 33 lines · Kevin Hilman
Quoted 31 lines · Lina Iyer
Reading the next wakeup of the CPU can only be realiably done only from that CPU. In the idle enter path record the next wake up of the CPU. The information is useful to determine the sleep time left for the CPU.
Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 5 +++++ 1 file changed, 5 insertions(+)drivers/cpuidle/cpuidle-arm.c +3
-0@@ -18,9 +18,11 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/rcupdate.h> +#include <linux/tick.h> #include <asm/cpuidle.h> @@ -49,7 +51,9 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) {struct device *cpu_dev =get_cpu_device (dev->cpu); +struct generic_pm_domain_data *gpd =dev_gpd_data (cpu_dev);
Quoted 8 lines
drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 57
Quoted 2 lines
drivers/cpuidle/cpuidle-arm.c +1
-0+ gpd->td.next_wakeup =tick_nohz_get_next_wakeup ();RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));Maybe set this back to zero atomicaly, after wakeup?
Checking for non-zero that might be another way for the domain goveror that there haven't been any CPU wakeups since the CPUs have gone idle.
I set it to 0 below.
The reference counting by the GET_PUT tells the domain if a device is suspended. I see that as a redudant information. May be I am not getting your point.
-- Lina
Quoted 1 line
Kevin
RFC PATCH 16/27 ARM: cpuidle: Record the next wakeup event of the CPU
Quoted 39 lines · Lina Iyer
Quoted 38 lines · Kevin Hilman
Quoted 26 lines · Lina Iyer
Reading the next wakeup of the CPU can only be realiably done only from that CPU. In the idle enter path record the next wake up of the CPU. The information is useful to determine the sleep time left for the CPU.
Signed-off-by Lina Iyer <[email protected]>--- drivers/cpuidle/cpuidle-arm.c | 5 +++++ 1 file changed, 5 insertions(+)drivers/cpuidle/cpuidle-arm.c +2
-0@@ -18,9 +18,11 @@#include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/rcupdate.h> +#include <linux/tick.h> #include <asm/cpuidle.h> drivers/cpuidle/cpuidle-arm.c static int arm_enter_idle_state(struct cpuidle_device *dev, · line 57
Quoted 7 lines
drivers/cpuidle/cpuidle-arm.c +2
-0@@ -49,7 +51,9 @@ static int arm_enter_idle_state(struct cpuidle_device *dev, ret =cpu_pm_enter ();if (!ret) {struct device *cpu_dev =get_cpu_device (dev->cpu); +struct generic_pm_domain_data *gpd =dev_gpd_data (cpu_dev); + gpd->td.next_wakeup =tick_nohz_get_next_wakeup ();RCU_NONIDLE (pm_runtime_put_sync_suspend (cpu_dev));Maybe set this back to zero atomicaly, after wakeup?
Quoted 4 lines
Quoted 3 lines
Checking for non-zero that might be another way for the domain goveror that there haven't been any CPU wakeups since the CPUs have gone idle.
I set it to 0 below.
Oops, you're right. Not sure how I missed that.
Quoted 3 lines
The reference counting by the
GET_PUTtells the domain if a device is suspended. I see that as a redudant information. May be I am not getting your point.
It's redundant, but may be helpful in detecting races. e.g. domain is being powered off and CPU wakes up and sets this zero, but runtime PM layers haven't been called yet so dev->power->usage_count is still zero.
Kevin
RFC PATCH 17/27 drivers: cpu-pd: Record CPUs that are part of the domain
In reply to Lina IyerIn order to power down the heirarchy of the CPU domain, the domain needs information about the CPUs in that domain hierarchy . A domain that has sub-domains containing CPU devices, will need information about all the CPUs in the sub-domains as well.
Introduce of_attach_cpu_pm_domain() to allow CPU domains to be attached to its parent domain and the information on CPUs attached directly or indirectly be bubbled up the hierarchy.
--- drivers/base/power/cpu-pd.c | 96 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/cpu-pd.h | 3 ++ 2 files changed, 97 insertions(+), 2 deletions(-)
drivers/base/power/cpu-pd.c +94 -2
@@ -18,6 +18,7 @@#include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/pm_qos.h> #include <linux/rculist.h> #include <linux/slab.h>
@@ -44,6 +45,27 @@ struct cpu_pm_domain *to_cpu_pd(struct generic_pm_domain *d)return res; } +/** +* get_cpus_in_domain() - Recursively parse CPUs in a domain. +*/ +static void get_cpus_in_domain (struct generic_pm_domain *genpd, +struct cpumask *mask) +{ +struct gpd_link *link; +struct cpu_pm_domain *pd =to_cpu_pd (genpd); +struct generic_pm_domain *sd; + +if (!cpumask_empty (pd->cpus) && mask != pd->cpus) { +cpumask_or (mask, pd->cpus, mask); +return ; + } + +list_for_each_entry (link, &genpd->master_links, master_node) { + sd = link->slave; +get_cpus_in_domain (sd, mask); + } +} +static int cpu_pd_power_off (struct generic_pm_domain *genpd) {struct cpu_pm_domain *pd =to_cpu_pd (genpd);
@@ -81,7 +103,8 @@ static void run_cpu(void *unused)pm_runtime_get_noresume (cpu_dev); } -static int of_pm_domain_attach_cpus (struct device_node *dn) +static int of_pm_domain_attach_cpus (struct device_node *dn, +struct cpu_pm_domain *pd) {int cpuid, ret;
@@ -126,6 +149,7 @@ static int of_pm_domain_attach_cpus(struct device_node *dn) }else {pm_runtime_enable (cpu_dev);dev_dbg (cpu_dev,"Attached CPU%d to domain\n" , cpuid); +cpumask_set_cpu (cpuid, pd->cpus); } }
@@ -171,7 +195,7 @@ int of_register_cpu_pm_domain(struct device_node *dn, pd->genpd->name);/* Attach the CPUs to the CPU PM domain */ - ret =of_pm_domain_attach_cpus (dn); + ret =of_pm_domain_attach_cpus (dn, pd);if (ret)of_genpd_del_provider (dn);
@@ -213,6 +237,13 @@ struct generic_pm_domain *of_init_cpu_pm_domain(struct device_node *dn,return ERR_PTR (-ENOMEM ); } +if (!zalloc_cpumask_var (&pd->cpus,GFP_KERNEL )) { +kfree (pd->genpd->name); +kfree (pd->genpd); +kfree (pd); +return ERR_PTR (-ENOMEM ); + } +if (ops) { pd->plat_ops.power_off = ops->power_off; pd->plat_ops.power_on = ops->power_on;
@@ -222,6 +253,7 @@ struct generic_pm_domain *of_init_cpu_pm_domain(struct device_node *dn,if (ret) {kfree (pd->genpd->name);kfree (pd->genpd); +kfree (pd->cpus);kfree (pd);return ERR_PTR (ret); }
@@ -229,3 +261,63 @@ struct generic_pm_domain *of_init_cpu_pm_domain(struct device_node *dn,return pd->genpd; }EXPORT_SYMBOL (of_init_cpu_pm_domain); + +/** +* of_attach_cpu_pm_domain() - Attach a CPU PM domain to its parent +* @dn: The device node of the CPU PM domain that needs to be attached +* +* The platform code can use this simplfied function to parse the domain +* provider of this device node and attach the genpd associated with @dn +* to it parents. +* +* Note: Both @dn and its domain provider must be initialized using +* of_init_cpu_pm_domain. +*/ +int of_attach_cpu_pm_domain (struct device_node *dn) +{ +struct of_phandle_args args; +struct generic_pm_domain *genpd, *parent; +struct cpu_pm_domain *pd; +int ret; + + args.np = dn; + args.args_count =0 ; + + genpd =of_genpd_get_from_provider (&args); +if (IS_ERR (genpd)) +return -EINVAL ; + +if (!to_cpu_pd (genpd)) { +pr_warn ("%s: domain %s is not a CPU domain\n" , + __func__, genpd->name); +return -EINVAL ; + } + + ret =of_parse_phandle_with_args (dn,"power-domains" , +"#power-domain-cells" ,0 , &args); +if (ret <0 ) +return ret; + + parent =of_genpd_get_from_provider (&args); +if (IS_ERR (parent)) +return -EINVAL ; + + pd =to_cpu_pd (parent); +if (!pd) { +pr_warn ("%s: domain (%s) parent (%s) is non-CPU domain\n" , + __func__, genpd->name, parent->name); +return -EINVAL ; + } + + ret =pm_genpd_add_subdomain (genpd, parent); +if (ret) { +pr_err ("%s: Unable to add sub-domain (%s) to parent (%s)\n" , + __func__, genpd->name, parent->name); +return ret; + } + +get_cpus_in_domain (parent, pd->cpus); + +return 0 ; +} +EXPORT_SYMBOL (of_attach_cpu_pm_domain);
include/linux/cpu-pd.h +3 -0
@@ -11,6 +11,7 @@#ifndef __CPU_PD_H__#define __CPU_PD_H__ +#include <linux/cpumask.h> #include <linux/list.h> #include <linux/of.h> #include <linux/pm_domain.h>
@@ -25,8 +26,10 @@ struct cpu_pm_domain {struct generic_pm_domain *genpd;struct device_node *dn;struct cpu_pd_ops plat_ops; + cpumask_var_t cpus; };struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn,const struct cpu_pd_ops *ops); +int of_attach_cpu_pm_domain (struct device_node *dn);#endif /* __CPU_PD_H__ */
Signature · 1 line
2.1.4
RFC PATCH 17/27 drivers: cpu-pd: Record CPUs that are part of the domain
drivers/base/power/cpu-pd.c line 21
Quoted 9 lines · Lina Iyer
drivers/base/power/cpu-pd.c +1
-0@@ -18,6 +18,7 @@#include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/pm_qos.h>
What's this added for?
Quoted 2 lines
drivers/base/power/cpu-pd.c +0
-0#include <linux/rculist.h> #include <linux/slab.h>
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 17/27 drivers: cpu-pd: Record CPUs that are part of the domain
Quoted 13 lines · Stephen Boyd
drivers/base/power/cpu-pd.c line 21
Quoted 9 lines · Lina Iyer
drivers/base/power/cpu-pd.c +1
-0@@ -18,6 +18,7 @@#include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/pm_qos.h> What's this added for?
pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Getting the PM QoS for the CPUs.
-- Lina
Quoted 6 lines
Quoted 2 lines
drivers/base/power/cpu-pd.c +0
-0#include <linux/rculist.h> #include <linux/slab.h> Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 17/27 drivers: cpu-pd: Record CPUs that are part of the domain
Quoted 14 lines · Lina Iyer
Quoted 13 lines · Stephen Boyd
drivers/base/power/cpu-pd.c line 21
Quoted 9 lines · Lina Iyer
drivers/base/power/cpu-pd.c +1
-0@@ -18,6 +18,7 @@#include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/pm_qos.h> What's this added for?
Quoted 3 lines
pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Getting the PM QoS for the CPUs.
Ok. That function call is not added in this patch though.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 17/27 drivers: cpu-pd: Record CPUs that are part of the domain
Quoted 15 lines · Stephen Boyd
Quoted 14 lines · Lina Iyer
Quoted 13 lines · Stephen Boyd
drivers/base/power/cpu-pd.c line 21
Quoted 9 lines · Lina Iyer
drivers/base/power/cpu-pd.c +1
-0@@ -18,6 +18,7 @@#include <linux/device.h> #include <linux/pm_runtime.h> #include <linux/platform_device.h> +#include <linux/pm_qos.h> What's this added for?
Quoted 6 lines
Quoted 3 lines
pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
Getting the PM QoS for the CPUs.Ok. That function call is not added in this patch though.
Ah, right. Will fix.
Quoted 4 lines
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
In reply to Lina IyerA PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS CPU_DMA_LATENCY and the state's residency.
--- drivers/base/power/cpu-pd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-)
drivers/base/power/cpu-pd.c +82 -1
@@ -21,6 +21,7 @@#include <linux/pm_qos.h> #include <linux/rculist.h> #include <linux/slab.h> +#include <linux/tick.h> #define CPU_PD_NAME_MAX 36
@@ -66,6 +67,86 @@ static void get_cpus_in_domain(struct generic_pm_domain *genpd, } } +static bool cpu_pd_down_ok (struct dev_pm_domain *pd) +{ +struct generic_pm_domain *genpd =pd_to_genpd (pd); +struct cpu_pm_domain *cpu_pd =to_cpu_pd (genpd); +int qos =pm_qos_request (PM_QOS_CPU_DMA_LATENCY ); +u64 sleep_ns = ~0 ; +ktime_t earliest; +int cpu; +int i; + +/* Reset the last set genpd state, default to index 0 */ + genpd->state_idx =0 ; + +/* We dont want to power down, if QoS is 0 */ +if (!qos) +return false; + +/* +* Find the sleep time for the cluster. +* The time between now and the first wake up of any CPU that +* are in this domain hierarchy is the time available for the +* domain to be idle. +*/ + earliest.tv64 =KTIME_MAX ; +for_each_cpu_and (cpu, cpu_pd->cpus, cpu_online_mask) { +struct device *cpu_dev =get_cpu_device (cpu); +struct gpd_timing_data *td; + + td = &dev_gpd_data (cpu_dev)->td; + +if (earliest.tv64 < td->next_wakeup.tv64) + earliest = td->next_wakeup; + } + + sleep_ns =ktime_to_ns (ktime_sub (earliest,ktime_get ())); +if (sleep_ns <=0 ) +return false; + +/* +* Find the deepest sleep state that satisfies the residency +* requirement and the QoS constraint +*/ +for (i = genpd->state_count -1 ; i >0 ; i--) { +u64 state_sleep_ns; + + state_sleep_ns = genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns + + genpd->states[i].residency_ns; + +/* +* If we cant sleep to save power in the state, move on +* to the next lower idle state. +*/ +if (state_sleep_ns > sleep_ns) +continue ; + +/* +* We also dont want to sleep more than we should to +* gaurantee QoS. +*/ +if (state_sleep_ns < (qos *NSEC_PER_USEC )) +break ; + } + +if (i >=0 ) + genpd->state_idx = i; + +return (i >=0 ) ? true : false; +} + +static bool cpu_stop_ok (struct device *dev) +{ +return true; +} + +struct dev_power_governor cpu_pd_gov = { + .power_down_ok = cpu_pd_down_ok, + .stop_ok = cpu_stop_ok, +}; +static int cpu_pd_power_off (struct generic_pm_domain *genpd) {struct cpu_pm_domain *pd =to_cpu_pd (genpd);
@@ -183,7 +264,7 @@ int of_register_cpu_pm_domain(struct device_node *dn,/* Register the CPU genpd */ pr_debug ("adding %s as CPU PM domain.\n" , pd->genpd->name); - ret =of_pm_genpd_init (dn, pd->genpd, &simple_qos_governor, false); + ret =of_pm_genpd_init (dn, pd->genpd, &cpu_pd_gov, false);if (ret) {pr_err ("Unable to initialize domain %s\n" , dn->full_name);return ret;
Signature · 1 line
2.1.4
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.
To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
You could use the power domains information to detect states that are shared between cpus.
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Thoughts appreciated.
Lorenzo
Quoted 116 lines
Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/cpu-pd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-)drivers/base/power/cpu-pd.c +82
-1@@ -21,6 +21,7 @@#include <linux/pm_qos.h> #include <linux/rculist.h> #include <linux/slab.h> +#include <linux/tick.h> #define CPU_PD_NAME_MAX 36@@ -66,6 +67,86 @@ static void get_cpus_in_domain(struct generic_pm_domain *genpd, } } +static bool cpu_pd_down_ok (struct dev_pm_domain *pd) +{ +struct generic_pm_domain *genpd =pd_to_genpd (pd); +struct cpu_pm_domain *cpu_pd =to_cpu_pd (genpd); +int qos =pm_qos_request (PM_QOS_CPU_DMA_LATENCY ); +u64 sleep_ns = ~0 ; +ktime_t earliest; +int cpu; +int i; + +/* Reset the last set genpd state, default to index 0 */ + genpd->state_idx =0 ; + +/* We dont want to power down, if QoS is 0 */ +if (!qos) +return false; + +/* +* Find the sleep time for the cluster. +* The time between now and the first wake up of any CPU that +* are in this domain hierarchy is the time available for the +* domain to be idle. +*/ + earliest.tv64 =KTIME_MAX ; +for_each_cpu_and (cpu, cpu_pd->cpus, cpu_online_mask) { +struct device *cpu_dev =get_cpu_device (cpu); +struct gpd_timing_data *td; + + td = &dev_gpd_data (cpu_dev)->td; + +if (earliest.tv64 < td->next_wakeup.tv64) + earliest = td->next_wakeup; + } + + sleep_ns =ktime_to_ns (ktime_sub (earliest,ktime_get ())); +if (sleep_ns <=0 ) +return false; + +/* +* Find the deepest sleep state that satisfies the residency +* requirement and the QoS constraint +*/ +for (i = genpd->state_count -1 ; i >0 ; i--) { +u64 state_sleep_ns; + + state_sleep_ns = genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns + + genpd->states[i].residency_ns; + +/* +* If we cant sleep to save power in the state, move on +* to the next lower idle state. +*/ +if (state_sleep_ns > sleep_ns) +continue ; + +/* +* We also dont want to sleep more than we should to +* gaurantee QoS. +*/ +if (state_sleep_ns < (qos *NSEC_PER_USEC )) +break ; + } + +if (i >=0 ) + genpd->state_idx = i; + +return (i >=0 ) ? true : false; +} + +static bool cpu_stop_ok (struct device *dev) +{ +return true; +} + +struct dev_power_governor cpu_pd_gov = { + .power_down_ok = cpu_pd_down_ok, + .stop_ok = cpu_stop_ok, +}; +static int cpu_pd_power_off (struct generic_pm_domain *genpd) {struct cpu_pm_domain *pd =to_cpu_pd (genpd);@@ -183,7 +264,7 @@ int of_register_cpu_pm_domain(struct device_node *dn,/* Register the CPU genpd */ pr_debug ("adding %s as CPU PM domain.\n" , pd->genpd->name); - ret =of_pm_genpd_init (dn, pd->genpd, &simple_qos_governor, false); + ret =of_pm_genpd_init (dn, pd->genpd, &cpu_pd_gov, false);if (ret) {pr_err ("Unable to initialize domain %s\n" , dn->full_name);return ret;Signature · 1 line
2.1.4
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
Quoted 29 lines · Lorenzo Pieralisi
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
You could use the power domains information to detect states that are shared between cpus.
Quoted 6 lines
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Thoughts appreciated.
Maybe this is silly and not thought-through, but I wonder if the responsibilities could be split or instance with an outer control loop that has the heuristic to compute the next tick time, and the required cpu-power needed during that time slot, and an inner control loop (genpd) that has a per-domain QoS and can optimize power consumption.
Marc.
Quoted 119 lines
Lorenzo
Quoted 116 lines
Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/cpu-pd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-)drivers/base/power/cpu-pd.c +82
-2@@ -21,6 +21,7 @@#include <linux/pm_qos.h> #include <linux/rculist.h> #include <linux/slab.h> +#include <linux/tick.h> #define CPU_PD_NAME_MAX 36@@ -66,6 +67,86 @@ static void get_cpus_in_domain(struct generic_pm_domain *genpd, } } +static bool cpu_pd_down_ok (struct dev_pm_domain *pd) +{ +struct generic_pm_domain *genpd =pd_to_genpd (pd); +struct cpu_pm_domain *cpu_pd =to_cpu_pd (genpd); +int qos =pm_qos_request (PM_QOS_CPU_DMA_LATENCY ); +u64 sleep_ns = ~0 ; +ktime_t earliest; +int cpu; +int i; + +/* Reset the last set genpd state, default to index 0 */ + genpd->state_idx =0 ; + +/* We dont want to power down, if QoS is 0 */ +if (!qos) +return false; + +/* +* Find the sleep time for the cluster. +* The time between now and the first wake up of any CPU that +* are in this domain hierarchy is the time available for the +* domain to be idle. +*/ + earliest.tv64 =KTIME_MAX ; +for_each_cpu_and (cpu, cpu_pd->cpus, cpu_online_mask) { +struct device *cpu_dev =get_cpu_device (cpu); +struct gpd_timing_data *td; + + td = &dev_gpd_data (cpu_dev)->td; + +if (earliest.tv64 < td->next_wakeup.tv64) + earliest = td->next_wakeup; + } + + sleep_ns =ktime_to_ns (ktime_sub (earliest,ktime_get ())); +if (sleep_ns <=0 ) +return false; + +/* +* Find the deepest sleep state that satisfies the residency +* requirement and the QoS constraint +*/ +for (i = genpd->state_count -1 ; i >0 ; i--) { +u64 state_sleep_ns; + + state_sleep_ns = genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns + + genpd->states[i].residency_ns; + +/* +* If we cant sleep to save power in the state, move on +* to the next lower idle state. +*/ +if (state_sleep_ns > sleep_ns) +continue ; + +/* +* We also dont want to sleep more than we should to +* gaurantee QoS. +*/ +if (state_sleep_ns < (qos *NSEC_PER_USEC )) +break ; + } + +if (i >=0 ) + genpd->state_idx = i; + +return (i >=0 ) ? true : false; +} + +static bool cpu_stop_ok (struct device *dev) +{ +return true; +} + +struct dev_power_governor cpu_pd_gov = { + .power_down_ok = cpu_pd_down_ok, + .stop_ok = cpu_stop_ok, +}; +static int cpu_pd_power_off (struct generic_pm_domain *genpd) {struct cpu_pm_domain *pd =to_cpu_pd (genpd);@@ -183,7 +264,7 @@ int of_register_cpu_pm_domain(struct device_node *dn,/* Register the CPU genpd */ pr_debug ("adding %s as CPU PM domain.\n" , pd->genpd->name); - ret =of_pm_genpd_init (dn, pd->genpd, &simple_qos_governor, false); + ret =of_pm_genpd_init (dn, pd->genpd, &cpu_pd_gov, false);if (ret) {pr_err ("Unable to initialize domain %s\n" , dn->full_name);return ret; --2.1.4
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
Quoted 35 lines · Marc Titinger
Quoted 34 lines · Lorenzo Pieralisi
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
You could use the power domains information to detect states that are shared between cpus.
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Quoted 8 lines
Quoted 1 line
Thoughts appreciated.
Maybe this is silly and not thought-through, but I wonder if the responsibilities could be split or instance with an outer control loop that has the heuristic to compute the next tick time, and the required cpu-power needed during that time slot, and an inner control loop (genpd) that has a per-domain QoS and can optimize power consumption.
Not sure I understand everything you said, but the heuristics across a bunch of CPUs can be very erratic. Its hard enough for menu governor to determine heuristics on a per-cpu basis.
I governor in this patch already takes care of PM QoS, but does not do a per-cpu QoS.
We should discuss this more.
-- Lina
Quoted 122 lines
Marc.
Quoted 119 lines
Lorenzo
Quoted 116 lines
Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/cpu-pd.c | 83 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-)drivers/base/power/cpu-pd.c +82
-2@@ -21,6 +21,7 @@#include <linux/pm_qos.h> #include <linux/rculist.h> #include <linux/slab.h> +#include <linux/tick.h> #define CPU_PD_NAME_MAX 36@@ -66,6 +67,86 @@ static void get_cpus_in_domain(struct generic_pm_domain *genpd, } } +static bool cpu_pd_down_ok (struct dev_pm_domain *pd) +{ +struct generic_pm_domain *genpd =pd_to_genpd (pd); +struct cpu_pm_domain *cpu_pd =to_cpu_pd (genpd); +int qos =pm_qos_request (PM_QOS_CPU_DMA_LATENCY ); +u64 sleep_ns = ~0 ; +ktime_t earliest; +int cpu; +int i; + +/* Reset the last set genpd state, default to index 0 */ + genpd->state_idx =0 ; + +/* We dont want to power down, if QoS is 0 */ +if (!qos) +return false; + +/* +* Find the sleep time for the cluster. +* The time between now and the first wake up of any CPU that +* are in this domain hierarchy is the time available for the +* domain to be idle. +*/ + earliest.tv64 =KTIME_MAX ; +for_each_cpu_and (cpu, cpu_pd->cpus, cpu_online_mask) { +struct device *cpu_dev =get_cpu_device (cpu); +struct gpd_timing_data *td; + + td = &dev_gpd_data (cpu_dev)->td; + +if (earliest.tv64 < td->next_wakeup.tv64) + earliest = td->next_wakeup; + } + + sleep_ns =ktime_to_ns (ktime_sub (earliest,ktime_get ())); +if (sleep_ns <=0 ) +return false; + +/* +* Find the deepest sleep state that satisfies the residency +* requirement and the QoS constraint +*/ +for (i = genpd->state_count -1 ; i >0 ; i--) { +u64 state_sleep_ns; + + state_sleep_ns = genpd->states[i].power_off_latency_ns + + genpd->states[i].power_on_latency_ns + + genpd->states[i].residency_ns; + +/* +* If we cant sleep to save power in the state, move on +* to the next lower idle state. +*/ +if (state_sleep_ns > sleep_ns) +continue ; + +/* +* We also dont want to sleep more than we should to +* gaurantee QoS. +*/ +if (state_sleep_ns < (qos *NSEC_PER_USEC )) +break ; + } + +if (i >=0 ) + genpd->state_idx = i; + +return (i >=0 ) ? true : false; +} + +static bool cpu_stop_ok (struct device *dev) +{ +return true; +} + +struct dev_power_governor cpu_pd_gov = { + .power_down_ok = cpu_pd_down_ok, + .stop_ok = cpu_stop_ok, +}; +static int cpu_pd_power_off (struct generic_pm_domain *genpd) {struct cpu_pm_domain *pd =to_cpu_pd (genpd);@@ -183,7 +264,7 @@ int of_register_cpu_pm_domain(struct device_node *dn,/* Register the CPU genpd */ pr_debug ("adding %s as CPU PM domain.\n" , pd->genpd->name); - ret =of_pm_genpd_init (dn, pd->genpd, &simple_qos_governor, false); + ret =of_pm_genpd_init (dn, pd->genpd, &cpu_pd_gov, false);if (ret) {pr_err ("Unable to initialize domain %s\n" , dn->full_name);return ret; --2.1.4
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
In reply to Lorenzo PieralisiQuoted 26 lines · Lorenzo Pieralisi
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
Quoted 7 lines
You could use the power domains information to detect states that are shared between cpus.
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Hmm, considering "normal" devices in "normal" power domains, and following the same logic, the equivalent would be to say that the decision to gate the power domain belongs to the individual drivers in the domain instead of in the power domain layer. I disagree.
IMO, there are different decision layers because there are different hardware layers. Devices (including CPUs) are reponsible for handling device-local idle states, based on device-local conditions (e.g. local wakeups, timers, etc.) and domains are responsible for handling decisions based on conditions of the whole domain.
Kevin
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
Quoted 41 lines · Kevin Hilman
Quoted 26 lines · Lorenzo Pieralisi
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
Quoted 7 lines
You could use the power domains information to detect states that are shared between cpus.
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Hmm, considering "normal" devices in "normal" power domains, and following the same logic, the equivalent would be to say that the decision to gate the power domain belongs to the individual drivers in the domain instead of in the power domain layer. I disagree.
Quoted 5 lines
IMO, there are different decision layers because there are different hardware layers. Devices (including CPUs) are reponsible for handling device-local idle states, based on device-local conditions (e.g. local wakeups, timers, etc.) and domains are responsible for handling decisions based on conditions of the whole domain.
After going through the series for the second time (it is quite complex and should probably be split) I understood your point of view and I agree with it, I will review it more in-depth to understand the details.
One thing that is not clear to me is how we would end up handling cluster states in platform coordinated mode with this series (and I am actually referring to the data we would add in the idle-states, such as min-residency). I admit that data for cluster states at present is not extremely well defined, because we have to add latencies for the cluster state even if the state itself may be just a cpu one (by definition a cluster state is entered only if all cpus in the cluster enter it, otherwise FW or power controller demote them automatically).
I would like to take this series as an opportunity to improve the current situation in a clean way (and without changing the bindings, only augmenting them).
On a side note, I think we should give up the concept of cluster entirely, to me they are just a group of cpus, I do not see any reason why we should group cpus this way and I do not like the dependencies of this series on the cpu-map either, I do not see the reason but I will go through code again to make sure I am not missing anything.
To be clear, to me the cpumask should be created with all cpus belonging in a given power domain, no cluster dependency (and yes the CPU PM notifiers are not appropriate at present - eg on cpu_cluster_pm_{enter/exit} we save and restore the GIC distributor state even on multi-cluster systems, that's useless and has no connection with the real power domain topology at all, so the concept of cluster as it stands is shaky to say the least).
Thanks,
Lorenzo
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
Quoted 48 lines · Lorenzo Pieralisi
Quoted 41 lines · Kevin Hilman
Quoted 26 lines · Lorenzo Pieralisi
Quoted 8 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
Quoted 4 lines
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.To me this information should be part of the CPUidle governor (it is already there), we should not split the decision into multiple layers.
The problem you are facing is that the CPUidle governor(s) do not take cross cpus relationship into account, I do not think that adding another decision layer in the power domain subsystem helps, you are doing that just because adding it to the existing CPUidle governor(s) is invasive.
Why can't we use the power domain work you put together to eg disable idle states that share multiple cpus and make them "visible" only when the power domain that encompass them is actually going down ?
Quoted 7 lines
You could use the power domains information to detect states that are shared between cpus.
It is just an idea, what I am saying is that having another governor in the power domain subsytem does not make much sense, you split the decision in two layers while there is actually one, the existing CPUidle governor and that's where the decision should be taken.
Hmm, considering "normal" devices in "normal" power domains, and following the same logic, the equivalent would be to say that the decision to gate the power domain belongs to the individual drivers in the domain instead of in the power domain layer. I disagree.
Quoted 5 lines
IMO, there are different decision layers because there are different hardware layers. Devices (including CPUs) are reponsible for handling device-local idle states, based on device-local conditions (e.g. local wakeups, timers, etc.) and domains are responsible for handling decisions based on conditions of the whole domain.
Quoted 4 lines
After going through the series for the second time (it is quite complex and should probably be split) I understood your point of view and I agree with it, I will review it more in-depth to understand the details.
I have included patches from Axel and Marc, so as to get a complete picture. My core changes are in genpd, cpu-pd and psci.c
Quoted 6 lines
One thing that is not clear to me is how we would end up handling cluster states in platform coordinated mode with this series (and I am actually referring to the data we would add in the idle-states, such as min-residency).
From what I see, the platform coordinated mode, doesnt need any of this.
We are fine as it is today. CPUs vote for the cluster state they can enter and the f/w determines based on these votes. It makes sense and probably easier to flatten out the cluster states and attach them to cpuidle for that.
I couldnt find a symmetry with OS initated. May be it deserves more discussion and brain storming.
Quoted 11 lines
I admit that data for cluster states at present
is not extremely well defined, because we have to add latencies for the cluster state even if the state itself may be just a cpu one (by definition a cluster state is entered only if all cpus in the cluster enter it, otherwise FW or power controller demote them automatically).I would like to take this series as an opportunity to improve the current situation in a clean way (and without changing the bindings, only augmenting them).
Quoted 6 lines
On a side note, I think we should give up the concept of cluster entirely, to me they are just a group of cpus, I do not see any reason why we should group cpus this way and I do not like the dependencies of this series on the cpu-map either, I do not see the reason but I will go through code again to make sure I am not missing anything.
SoC's could have different organization of CPUs (clubbed as clusters) and power domains the power thesee clusters. This information has to come from the DT. Since there are no actual devices in linux for domain management (with PSCI), I have added them to cpu-map, which already builds up the cluster hierarchy. The only addition I had to make wa allow these cluster nodes to be tell the kernel that they are domain providers.
Quoted 8 lines
To be clear, to me the cpumask should be created with all cpus belonging in a given power domain, no cluster dependency (and yes the CPU PM notifiers are not appropriate at present - eg on cpu_cluster_pm_{enter/exit} we save and restore the GIC distributor state even on multi-cluster systems, that's useless and has no connection with the real power domain topology at all, so the concept of cluster as it stands is shaky to say the least).
Lets discuss this more. I am interested in what you are thinking, will let you go through the code.
Thanks for you time Lorenzo.
-- Lina
RFC PATCH 18/27 drivers: cpu-pd: Add PM Domain governor for CPUs
In reply to Lina IyerQuoted 14 lines · Lina Iyer
A PM domain comprising of CPUs may be powered off when all the CPUs in the domain are powered down. Powering down a CPU domain is generally a expensive operation and therefore the power performance trade offs should be considered. The time between the last CPU powering down and the first CPU powering up in a domain, is the time available for the domain to sleep. Ideally, the sleep time of the domain should fulfill the residency requirement of the domains' idle state.
To do this effectively, read the time before the wakeup of the cluster's CPUs and ensure that the domain's idle state sleep time guarantees the QoS requirements of each of the CPU, the PM QoS
CPU_DMA_LATENCYand the state's residency.Signed-off-by Lina Iyer <[email protected]>
[...]
drivers/base/power/cpu-pd.c static void get_cpus_in_domain(struct generic_pm_domain *genpd, · line 148
Quoted 9 lines
drivers/base/power/cpu-pd.c +9
-0+static bool cpu_stop_ok (struct device *dev) +{ +return true; +} + +struct dev_power_governor cpu_pd_gov = { + .power_down_ok = cpu_pd_down_ok, + .stop_ok = cpu_stop_ok, +};
If stop_ok is unconditionally true, it should probably just be removed (IOW cpu_pd_gov->stop_ok == NULL), and that will avoid an unnecessary function call.
Kevin
RFC PATCH 19/27 drivers: cpu-pd: Invoke CPU PM runtime on hotplug
In reply to Lina IyerWhen a CPU is hotplugged off invoke CPU runtime suspend to notify runtime PM of the CPU being powered down and opportunistically power down the domain as well. Do that independent of the architecture using hotplug notifiers.
--- drivers/base/power/cpu-pd.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-)
drivers/base/power/cpu-pd.c +29 -2
@@ -237,6 +237,30 @@ static int of_pm_domain_attach_cpus(struct device_node *dn,return 0 ; } +static int cpu_hotplug (struct notifier_block *nb, +unsigned long action,void *data) +{ +struct device *dev =get_cpu_device (smp_processor_id ()); + +/* Execute CPU runtime PM on that CPU */ +switch (action) { +case CPU_DYING : +case CPU_DYING_FROZEN : +pm_runtime_put_sync_suspend (dev); +pm_runtime_disable (dev); +break ; +case CPU_STARTING : +case CPU_STARTING_FROZEN : +pm_runtime_enable (dev); +pm_runtime_get_sync (dev); +break ; +default : +break ; + } + +return NOTIFY_OK ; +} +int of_register_cpu_pm_domain (struct device_node *dn,struct cpu_pm_domain *pd) {
@@ -277,10 +301,13 @@ int of_register_cpu_pm_domain(struct device_node *dn,/* Attach the CPUs to the CPU PM domain */ ret =of_pm_domain_attach_cpus (dn, pd); -if (ret) +if (ret) {of_genpd_del_provider (dn); +return ret; + } -return ret; + hotcpu_notifier(cpu_hotplug, 0) +return 0 ; }/**
Signature · 1 line
2.1.4
RFC PATCH 20/27 Documentation: ARM: topology: 'cluster' property for cluster nodes
In reply to Lina IyerAdd a place holder phandle for a cluster to define cluster specific properties like PM domain definitions etc.
--- Documentation/devicetree/bindings/arm/topology.txt | 8 ++++++++ 1 file changed, 8 insertions(+)
Documentation/devicetree/bindings/arm/topology.txt +8 -0
@@ -109,6 +109,14 @@ Bindings for cluster/cpu/thread nodes are defined as follows: The cluster node name must be "clusterN" as described in 2.1 above. A cluster node can not be a leaf node. + Properties for a cluster node are - + + -cluster + Usage: Optional + Value type: <phandle> + Definition: a phandle to a cluster node that corresponds to + this cluster. + A cluster node's child nodes must be: - one or more cluster nodes; or
Signature · 1 line
2.1.4
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
In reply to Lina IyerArchitectures that support CPU domain control in the firmware specify the domain heirarchy as part of the topology nodes. Parse and initialize domains from the topology node for such architectures.
--- drivers/base/power/cpu-pd.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/cpu-pd.h | 1 + 2 files changed, 77 insertions(+)
drivers/base/power/cpu-pd.c +76 -0
@@ -429,3 +429,79 @@ int of_attach_cpu_pm_domain(struct device_node *dn)return 0 ; }EXPORT_SYMBOL (of_attach_cpu_pm_domain); + +static int of_parse_cpu_pd (struct device_node *cluster, +const struct cpu_pd_ops *ops) +{ +struct device_node *domain_node; +struct generic_pm_domain *genpd; +char name[10 ]; +struct device_node *c; +int i, ret; + +for (i =0 ; ; i++) { +snprintf (name,sizeof (name),"cluster%d" , i); + c =of_get_child_by_name (cluster, name); +if (!c) +break ; + + domain_node =of_parse_phandle (c,"cluster" ,0 ); +if (!domain_node) +return -1 ; + +/* Initialize CPU PM domain domain at this level */ + genpd =of_init_cpu_pm_domain (domain_node, ops); +if (IS_ERR (genpd)) +return -1 ; + +/* Initialize and attach child domains */ + ret =of_parse_cpu_pd (c, ops); + +/* +* Attach the domain to its parent after reading +* the children, so the mask of CPUs in this domain +* are setup correctly. +*/ +if (!ret) +of_attach_cpu_pm_domain (domain_node); + +of_node_put (c); +if (ret !=0 ) +return ret; + } + +return 0 ; +} + +/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ; + + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" ); +if (!map) + goto out; + + ret =of_parse_cpu_pd (map, ops); +if (ret !=0 ) + goto out_map; + +out_map: +of_node_put (map); +out: +of_node_put (cn); +return ret; +} +EXPORT_SYMBOL (of_setup_cpu_domain_topology);
include/linux/cpu-pd.h +1 -0
@@ -32,4 +32,5 @@ struct cpu_pm_domain {struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn,const struct cpu_pd_ops *ops);int of_attach_cpu_pm_domain (struct device_node *dn); +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops);#endif /* __CPU_PD_H__ */
Signature · 1 line
2.1.4
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
Hi Lina,
Quoted 77 lines · Lina Iyer
Architectures that support CPU domain control in the firmware specify the domain heirarchy as part of the topology nodes. Parse and initialize domains from the topology node for such architectures.
Cc Rob Herring <[email protected]>Cc Stephen Boyd <[email protected]>Cc Kevin Hilman <[email protected]>Cc Ulf Hansson <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/cpu-pd.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/cpu-pd.h | 1 + 2 files changed, 77 insertions(+)drivers/base/power/cpu-pd.c +54
-0@@ -429,3 +429,79 @@ int of_attach_cpu_pm_domain(struct device_node *dn)return 0 ; }EXPORT_SYMBOL (of_attach_cpu_pm_domain); + +static int of_parse_cpu_pd (struct device_node *cluster, +const struct cpu_pd_ops *ops) +{ +struct device_node *domain_node; +struct generic_pm_domain *genpd; +char name[10 ]; +struct device_node *c; +int i, ret; + +for (i =0 ; ; i++) { +snprintf (name,sizeof (name),"cluster%d" , i); + c =of_get_child_by_name (cluster, name); +if (!c) +break ; + + domain_node =of_parse_phandle (c,"cluster" ,0 ); +if (!domain_node) +return -1 ; + +/* Initialize CPU PM domain domain at this level */ + genpd =of_init_cpu_pm_domain (domain_node, ops); +if (IS_ERR (genpd)) +return -1 ; + +/* Initialize and attach child domains */ + ret =of_parse_cpu_pd (c, ops); + +/* +* Attach the domain to its parent after reading +* the children, so the mask of CPUs in this domain +* are setup correctly. +*/ +if (!ret) +of_attach_cpu_pm_domain (domain_node); + +of_node_put (c); +if (ret !=0 ) +return ret; + } + +return 0 ; +} + +/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ;
drivers/base/power/cpu-pd.c int of_attach_cpu_pm_domain(struct device_node *dn) · line 495
Quoted 10 lines
drivers/base/power/cpu-pd.c +10
-0+ + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" ); +if (!map) + goto out;
I commented on this before, is this reliance on cpu-map necessary ? Could not you just rely on the "power-domains" phandle in the cpu nodes to build the cpumask for a specific power domain ? I think you should try to decouple the concept of power domain from the cpu-map cluster and I think this would also simplify your code in the process.
So to sum it up, I'd suggest you build the power domain cpumask by enumerating the cpus pointing at a specific power-domain node.
Lorenzo
Quoted 25 lines
drivers/base/power/cpu-pd.c +12
-0+ + ret =of_parse_cpu_pd (map, ops); +if (ret !=0 ) + goto out_map; + +out_map: +of_node_put (map); +out: +of_node_put (cn); +return ret; +} +EXPORT_SYMBOL (of_setup_cpu_domain_topology);include/linux/cpu-pd.h +1
-0@@ -32,4 +32,5 @@ struct cpu_pm_domain {struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn,const struct cpu_pd_ops *ops);int of_attach_cpu_pm_domain (struct device_node *dn); +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops);#endif /* __CPU_PD_H__ */ Signature · 1 line
2.1.4
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
Quoted 88 lines · Lorenzo Pieralisi
Hi Lina,
Quoted 85 lines · Lina Iyer
Architectures that support CPU domain control in the firmware specify the domain heirarchy as part of the topology nodes. Parse and initialize domains from the topology node for such architectures.
Cc Rob Herring <[email protected]>Cc Stephen Boyd <[email protected]>Cc Kevin Hilman <[email protected]>Cc Ulf Hansson <[email protected]>Cc Lorenzo Pieralisi <[email protected]>Signed-off-by Lina Iyer <[email protected]>--- drivers/base/power/cpu-pd.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/cpu-pd.h | 1 + 2 files changed, 77 insertions(+)drivers/base/power/cpu-pd.c +62
-0@@ -429,3 +429,79 @@ int of_attach_cpu_pm_domain(struct device_node *dn)return 0 ; }EXPORT_SYMBOL (of_attach_cpu_pm_domain); + +static int of_parse_cpu_pd (struct device_node *cluster, +const struct cpu_pd_ops *ops) +{ +struct device_node *domain_node; +struct generic_pm_domain *genpd; +char name[10 ]; +struct device_node *c; +int i, ret; + +for (i =0 ; ; i++) { +snprintf (name,sizeof (name),"cluster%d" , i); + c =of_get_child_by_name (cluster, name); +if (!c) +break ; + + domain_node =of_parse_phandle (c,"cluster" ,0 ); +if (!domain_node) +return -1 ; + +/* Initialize CPU PM domain domain at this level */ + genpd =of_init_cpu_pm_domain (domain_node, ops); +if (IS_ERR (genpd)) +return -1 ; + +/* Initialize and attach child domains */ + ret =of_parse_cpu_pd (c, ops); + +/* +* Attach the domain to its parent after reading +* the children, so the mask of CPUs in this domain +* are setup correctly. +*/ +if (!ret) +of_attach_cpu_pm_domain (domain_node); + +of_node_put (c); +if (ret !=0 ) +return ret; + } + +return 0 ; +} + +/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ; + + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" );
Quoted 9 lines
drivers/base/power/cpu-pd.c int of_attach_cpu_pm_domain(struct device_node *dn) · line 495
Quoted 2 lines
drivers/base/power/cpu-pd.c +2
-0+if (!map) + goto out;I commented on this before, is this reliance on cpu-map necessary ? Could not you just rely on the "power-domains" phandle in the cpu nodes to build the cpumask for a specific power domain ? I think you should try to decouple the concept of power domain from the cpu-map cluster and I think this would also simplify your code in the process.
Sorry, I missed seeing your comment on this earlier.
Hmm, it can be done, but I felt out of place to define just power domains that have no devices in Linux, since they are handled in PSCI, but also have no relation to PSCI. Hence, I felt cpu-map to be a good place for the cluster domain. But I am not strongly inclined. I can remove them from the cpu-map and keep them as separate nodes. However, it would be nice to have a way to say these nodes are PSCI controlled.
Thanks,
Lina
Quoted 29 lines
So to sum it up, I'd suggest you build the power domain cpumask by enumerating the cpus pointing at a specific power-domain node.
Quoted 25 lines
drivers/base/power/cpu-pd.c +12
-0+ + ret =of_parse_cpu_pd (map, ops); +if (ret !=0 ) + goto out_map; + +out_map: +of_node_put (map); +out: +of_node_put (cn); +return ret; +} +EXPORT_SYMBOL (of_setup_cpu_domain_topology);include/linux/cpu-pd.h +1
-1@@ -32,4 +32,5 @@ struct cpu_pm_domain {struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn,const struct cpu_pd_ops *ops);int of_attach_cpu_pm_domain (struct device_node *dn); +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops);#endif /* __CPU_PD_H__ */ --2.1.4
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
On Tue, Dec 08, 2015 at 11:05:36AM -0700, Lina Iyer wrote:
[...]
Quoted 28 lines · Lorenzo Pieralisi
Quoted 18 lines
Quoted 18 lines
drivers/base/power/cpu-pd.c +18
-0+/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ; + + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" );Quoted 9 lines
drivers/base/power/cpu-pd.c int of_attach_cpu_pm_domain(struct device_node *dn) · line 495
Quoted 2 lines
drivers/base/power/cpu-pd.c +2
-0+if (!map) + goto out;I commented on this before, is this reliance on cpu-map necessary ? Could not you just rely on the "power-domains" phandle in the cpu nodes to build the cpumask for a specific power domain ? I think you should try to decouple the concept of power domain from the cpu-map cluster and I think this would also simplify your code in the process.
Quoted 8 lines
Sorry, I missed seeing your comment on this earlier.
Hmm, it can be done, but I felt out of place to define just power domains that have no devices in Linux, since they are handled in PSCI, but also have no relation to PSCI. Hence, I felt cpu-map to be a good place for the cluster domain. But I am not strongly inclined. I can remove them from the cpu-map and keep them as separate nodes. However, it would be nice to have a way to say these nodes are PSCI controlled.
I do not agree with the way you described the system.
Here is how I see it. DT must model HW, and in HW your cpus will be part of a power domain(s) that of course can be hierarchical. Every cpus has a phandle to the power domain it belongs into, and to group them as "cluster" you should follow the power domains hierarchy (ie if you have a cluster power domain, it will contain the core power domains, through the hierarchy it is easy to detect what cpus are part of the cluster power domain by detecting which cpus are part of its children). It is much simpler than the current solution I think, you get rid of cpu-map dependency (honestly it is a bit weird what you did, with cpu-map containing a cluster phandle to a power domain and cpu phandles to cpu nodes, and the DT bindings you posted does not seem to match your dts).
For PSCI, nothing prevents you from defining the same bindings except that the power domain(s) is not explicitly controlled by the OS, still, the DT would always describe the HW and you can use it to detect the power domain topology and which cpus are part of a given power domain.
Please let me know what you think, thanks.
Lorenzo
Quoted 34 lines
Thanks,
LinaQuoted 29 lines
So to sum it up, I'd suggest you build the power domain cpumask by enumerating the cpus pointing at a specific power-domain node.
Quoted 25 lines
drivers/base/power/cpu-pd.c +12
-0+ + ret =of_parse_cpu_pd (map, ops); +if (ret !=0 ) + goto out_map; + +out_map: +of_node_put (map); +out: +of_node_put (cn); +return ret; +} +EXPORT_SYMBOL (of_setup_cpu_domain_topology);include/linux/cpu-pd.h +1
-1@@ -32,4 +32,5 @@ struct cpu_pm_domain {struct generic_pm_domain *of_init_cpu_pm_domain (struct device_node *dn,const struct cpu_pd_ops *ops);int of_attach_cpu_pm_domain (struct device_node *dn); +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops);#endif /* __CPU_PD_H__ */ --2.1.4
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
Quoted 56 lines · Lorenzo Pieralisi
On Tue, Dec 08, 2015 at 11:05:36AM -0700, Lina Iyer wrote:
[...]
Quoted 28 lines · Lorenzo Pieralisi
Quoted 18 lines
Quoted 18 lines
drivers/base/power/cpu-pd.c +18
-0+/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ; + + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" );Quoted 9 lines
drivers/base/power/cpu-pd.c int of_attach_cpu_pm_domain(struct device_node *dn) · line 495
Quoted 2 lines
drivers/base/power/cpu-pd.c +2
-0+if (!map) + goto out;I commented on this before, is this reliance on cpu-map necessary ? Could not you just rely on the "power-domains" phandle in the cpu nodes to build the cpumask for a specific power domain ? I think you should try to decouple the concept of power domain from the cpu-map cluster and I think this would also simplify your code in the process.
Quoted 8 lines
Sorry, I missed seeing your comment on this earlier.
Hmm, it can be done, but I felt out of place to define just power domains that have no devices in Linux, since they are handled in PSCI, but also have no relation to PSCI. Hence, I felt cpu-map to be a good place for the cluster domain. But I am not strongly inclined. I can remove them from the cpu-map and keep them as separate nodes. However, it would be nice to have a way to say these nodes are PSCI controlled.
I do not agree with the way you described the system.
Here is how I see it. DT must model HW, and in HW your cpus will be part of a power domain(s) that of course can be hierarchical. Every cpus has a phandle to the power domain it belongs into, and to group them as "cluster" you should follow the power domains hierarchy (ie if you have a cluster power domain, it will contain the core power domains, through the hierarchy it is easy to detect what cpus are part of the cluster power domain by detecting which cpus are part of its children). It is much simpler than the current solution I think, you get rid of cpu-map dependency (honestly it is a bit weird what you did, with cpu-map containing a cluster phandle to a power domain and cpu phandles to cpu nodes, and the DT bindings you posted does not seem to match your dts).
Quoted 6 lines
For PSCI, nothing prevents you from defining the same bindings except that the power domain(s) is not explicitly controlled by the OS, still, the DT would always describe the HW and you can use it to detect the power domain topology and which cpus are part of a given power domain.
Please let me know what you think, thanks.
+1
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
RFC PATCH 21/27 drivers: cpu-pd: Parse topology to setup CPU PM domains
Quoted 58 lines · Geert Uytterhoeven
Quoted 56 lines · Lorenzo Pieralisi
On Tue, Dec 08, 2015 at 11:05:36AM -0700, Lina Iyer wrote:
[...]
Quoted 28 lines · Lorenzo Pieralisi
Quoted 18 lines
Quoted 18 lines
drivers/base/power/cpu-pd.c +18
-0+/** +* of_setup_cpu_domain_topology() - Setup the CPU domains from the CPU +* topology node in DT. +* +* @ops: The PM domain suspend/resume ops for all the domains in the topology +*/ +int of_setup_cpu_domain_topology (const struct cpu_pd_ops *ops) +{ +struct device_node *cn, *map; +int ret =0 ; + + cn =of_find_node_by_path ("/cpus" ); +if (!cn) { +pr_err ("No CPU information found in DT\n" ); +return 0 ; + } + + map =of_get_child_by_name (cn,"cpu-map" );Quoted 9 lines
drivers/base/power/cpu-pd.c int of_attach_cpu_pm_domain(struct device_node *dn) · line 495
Quoted 2 lines
drivers/base/power/cpu-pd.c +2
-0+if (!map) + goto out;I commented on this before, is this reliance on cpu-map necessary ? Could not you just rely on the "power-domains" phandle in the cpu nodes to build the cpumask for a specific power domain ? I think you should try to decouple the concept of power domain from the cpu-map cluster and I think this would also simplify your code in the process.
Quoted 8 lines
Sorry, I missed seeing your comment on this earlier.
Hmm, it can be done, but I felt out of place to define just power domains that have no devices in Linux, since they are handled in PSCI, but also have no relation to PSCI. Hence, I felt cpu-map to be a good place for the cluster domain. But I am not strongly inclined. I can remove them from the cpu-map and keep them as separate nodes. However, it would be nice to have a way to say these nodes are PSCI controlled.
I do not agree with the way you described the system.
Here is how I see it. DT must model HW, and in HW your cpus will be part of a power domain(s) that of course can be hierarchical. Every cpus has a phandle to the power domain it belongs into, and to group them as "cluster" you should follow the power domains hierarchy (ie if you have a cluster power domain, it will contain the core power domains, through the hierarchy it is easy to detect what cpus are part of the cluster power domain by detecting which cpus are part of its children). It is much simpler than the current solution I think, you get rid of cpu-map dependency (honestly it is a bit weird what you did, with cpu-map containing a cluster phandle to a power domain and cpu phandles to cpu nodes, and the DT bindings you posted does not seem to match your dts).
Quoted 9 lines
Quoted 6 lines
For PSCI, nothing prevents you from defining the same bindings except that the power domain(s) is not explicitly controlled by the OS, still, the DT would always describe the HW and you can use it to detect the power domain topology and which cpus are part of a given power domain.
Please let me know what you think, thanks.
+1
I have a version written up that builds up the hierarchy from the CPU nodes. It doesnt use cpu-map. Will be part of the next submission.
Thanks,
Lina
RFC PATCH 22/27 drivers: firmware: PSCI: Export psci_has_ext_power_state()
In reply to Lina IyerExport psci_has_ext_power_state() to allow PSCI layers to determine and pass the correct state id in the format supported.
--- drivers/firmware/psci.c | 2 +- include/linux/psci.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-)
drivers/firmware/psci.c +1 -1
@@ -83,7 +83,7 @@ static u32 psci_function_id[PSCI_FN_MAX];static u32 psci_cpu_suspend_feature; -static inline bool psci_has_ext_power_state (void ) +bool psci_has_ext_power_state (void ) {return psci_cpu_suspend_feature &PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK ;
include/linux/psci.h +1 -0
@@ -23,6 +23,7 @@bool psci_tos_resident_on (int cpu);bool psci_power_state_loses_context (u32 state);bool psci_power_state_is_valid (u32 state); +bool psci_has_ext_power_state (void );struct psci_operations {int (*cpu_suspend)(u32 state,unsigned long entry_point);
Signature · 1 line
2.1.4
RFC PATCH 23/27 ARM64: psci: Support cluster idle states for OS-Initated
In reply to Lina IyerARMv8 PSCI firmware may allow Linux to determine the state of the CPU cluster and the cluster at coherency level to enter idle states when there are no active CPUs. Since Linux has a better idea of the QoS and the wakeup pattern of the CPUs, the cluster idle states may be better determined by the OS instead of the firmware.
The last CPU entering idle in a cluster, holds the responsibility of selecting the state of the cluster. Only one CPU in a cluster may provide the cluster idle state to the firmware. Similarly, the last CPU in the cluster of clusters may provide the state of the coherency domain.
The CPU PM domain framework facilitates registration of CPU PM domains and reference counting for the last man down and the first man up. But the organzation of CPUs into clusters must be provided. The cpu-map topology node provides the information. Call into the CPU PM framework to parse the CPU topology node and setup the PM domains. The state id for the cluster ids is available with the domain idle state.
The last CPU PSCI state id of the entire system would therefore be - Coherency State ID + Cluster State ID + CPU idle state ID, that is passed into the PSCI firmware when the CPU makes the PSCI enable-method.
--- arch/arm64/kernel/psci.c | 54 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-)
arch/arm64/kernel/psci.c +51 -3
@@ -15,10 +15,12 @@#define pr_fmt (fmt) "psci: " fmt +#include <linux/cpu-pd.h> #include <linux/init.h> #include <linux/of.h> #include <linux/smp.h> #include <linux/delay.h> +#include <linux/pm_domain.h> #include <linux/psci.h> #include <linux/slab.h>
@@ -31,6 +33,44 @@#include <asm/suspend.h> static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); +static DEFINE_PER_CPU(u32, cluster_state_id); + +static inline u32 psci_get_composite_state_id (u32 cpu_state) +{ +u32 val = cpu_state; + +if (psci_has_ext_power_state ()) + val +=this_cpu_read (cluster_state_id); + +return val; +} + +static inline void psci_reset_composite_state_id (void ) +{ +this_cpu_write (cluster_state_id,0 ); +} + +static int psci_pd_power_on (struct generic_pm_domain *genpd) +{ +return 0 ; +} + +static int psci_pd_power_off (struct generic_pm_domain *genpd) +{ +__this_cpu_add (cluster_state_id, genpd->states[genpd->state_idx].param); +return 0 ; +} + +static const struct cpu_pd_ops psci_pd_ops = { + .power_on = psci_pd_power_on, + .power_off = psci_pd_power_off, +}; + +static int __init psci_setup_cpu_domains (void ) +{ +return of_setup_cpu_domain_topology (&psci_pd_ops); +} +subsys_initcall (psci_setup_cpu_domains);static int __maybe_unused cpu_psci_cpu_init_idle (unsigned int cpu) {
@@ -117,6 +157,8 @@ static int cpu_psci_cpu_boot(unsigned int cpu)if (err)pr_err ("failed to boot CPU%d (%d)\n" , cpu, err); +/* Reset CPU cluster states */ +psci_reset_composite_state_id ();return err; }
@@ -181,15 +223,16 @@ static int cpu_psci_cpu_kill(unsigned int cpu)static int psci_suspend_finisher (unsigned long index) {u32 *state =__this_cpu_read (psci_power_state); +u32 ext_state =psci_get_composite_state_id (state[index -1 ]); -return psci_ops.cpu_suspend (state[index -1 ], -virt_to_phys (cpu_resume)); +return psci_ops.cpu_suspend (ext_state,virt_to_phys (cpu_resume)); }static int __maybe_unused cpu_psci_cpu_suspend (unsigned long index) {int ret;u32 *state =__this_cpu_read (psci_power_state); +u32 ext_state =psci_get_composite_state_id (state[index -1 ]);/* * idle state index 0 corresponds to wfi, should never be called * from the cpu_suspend operations
@@ -198,10 +241,15 @@ static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)return -EINVAL ;if (!psci_power_state_loses_context (state[index -1 ])) - ret = psci_ops.cpu_suspend (state[index -1 ],0 ); + ret = psci_ops.cpu_suspend (ext_state,0 );else ret =cpu_suspend (index, psci_suspend_finisher); +/* +* Clear the CPU's cluster states, we start afresh after coming +* out of idle. +*/ +psci_reset_composite_state_id ();return ret; }
Signature · 1 line
2.1.4
RFC PATCH 24/27 arm64: dts: Add Qualcomm MSM8916, MTP8916, APQ8016, SBC8016 ids
In reply to Lina IyerFrom: Kumar Gala <[email protected]>
Add qcom,msm-id and qcom,board-id to allow bootloader to identify which device tree to boot on the MTP8916 & SBC8016 boards.
--- arch/arm64/boot/dts/qcom/apq8016-sbc.dts | 2 ++ arch/arm64/boot/dts/qcom/msm8916-mtp.dts | 3 +++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 6 ++++++ 3 files changed, 11 insertions(+)
arch/arm64/boot/dts/qcom/apq8016-sbc.dts +2 -0
@@ -13,9 +13,11 @@ /dts-v1/; +#include <dt-bindings/arm/qcom-ids.h> #include "apq8016-sbc.dtsi" / { model = "Qualcomm Technologies, Inc. APQ 8016 SBC"; compatible = "qcom,apq8016-sbc", "qcom,apq8016", "qcom,sbc"; + qcom,board-id = <QCOM_BRD_ID(SBC, 1, 0) QCOM_BRD_SUBTYPE_DEFAULT>; };
arch/arm64/boot/dts/qcom/msm8916-mtp.dts +3 -0
@@ -13,10 +13,13 @@ /dts-v1/; +#include <dt-bindings/arm/qcom-ids.h> #include "msm8916-mtp.dtsi" / { model = "Qualcomm Technologies, Inc. MSM 8916 MTP"; compatible = "qcom,msm8916-mtp", "qcom,msm8916-mtp-smb1360", "qcom,msm8916", "qcom,mtp"; + qcom,board-id = <QCOM_BRD_ID(MTP, 1, 0) QCOM_BRD_SUBTYPE_DEFAULT>, + <QCOM_BRD_ID(MTP, 1, 0) QCOM_BRD_SUBTYPE_MTP8916_SMB1360>; };
arch/arm64/boot/dts/qcom/msm8916.dtsi +6 -0
@@ -14,10 +14,16 @@ #include <dt-bindings/interrupt-controller/arm-gic.h> #include <dt-bindings/clock/qcom,gcc-msm8916.h> #include <dt-bindings/reset/qcom,gcc-msm8916.h> +#include <dt-bindings/arm/qcom-ids.h> / { model = "Qualcomm Technologies, Inc. MSM8916"; compatible = "qcom,msm8916"; + qcom,msm-id = <QCOM_ID_MSM8916 0>, + <QCOM_ID_MSM8216 0>, + <QCOM_ID_MSM8116 0>, + <QCOM_ID_MSM8616 0>, + <QCOM_ID_APQ8016 0>; interrupt-parent = <&intc>;
Signature · 1 line
2.1.4
RFC PATCH 25/27 devicetree: bindings: Document qcom, msm-id and qcom, board-id
In reply to Lina IyerFrom: Kumar Gala <[email protected]>
The top level qcom,msm-id and qcom,board-id are utilized by bootloaders on Qualcomm MSM platforms to determine which device tree should be utilized and passed to the kernel.
--- Documentation/devicetree/bindings/arm/msm/ids.txt | 65 +++++++++++++++++++++++ include/dt-bindings/arm/qcom-ids.h | 33 ++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/msm/ids.txt create mode 100644 include/dt-bindings/arm/qcom-ids.h
Documentation/devicetree/bindings/arm/msm/ids.txt new file +65 -0
@@ -0,0 +1,65 @@ +* MSM-ID + +The qcom,msm-id entry specifies the MSM chipset and hardware revision. It can +optionally be an array of these to indicate multiple hardware that use the same +device tree. It is expected that the bootloader will use this information at +boot-up to decide which device tree to use when given multiple device trees, +some of which may not be compatible with the actual hardware. It is the +bootloader's responsibility to pass the correct device tree to the kernel. + +PROPERTIES + +- qcom,msm-id: + Usage: required + Value type: <prop-encoded-array> (<chipset_id, rev_id> [, <c2, r2> ..]) + Definition: + The "chipset_id" consists of three fields as below: + + bits 0-15 = The unique MSM chipset id. + bits 16-31 = Reserved. Should be 0 + + chipset_id is an exact match value + + The "rev_id" is a chipset specific 32-bit id that represents + the version of the chipset. + + The rev_id is a best match id. The bootloader will look for + the closest possible patch. + +* BOARD-ID + +The qcom,board-id entry specifies the board type and revision information. It +can optionally be an array of these to indicate multiple boards that use the +same device tree. It is expected that the bootloader will use this information +at boot-up to decide which device tree to use when given multiple device trees, +some of which may not be compatible with the actual hardware. It is the +bootloader's responsibility to pass the correct device tree to the kernel. + +PROPERTIES + +- qcom,board-id: + Usage: required + Value type: <prop-encoded-array> (<board_id, subtype_id> [, <b2, s2> ..]) + Definition: + The "board_id" consists of three fields as below: + + bits 31-24 = Unusued. + bits 23-16 = Platform Version Major + bits 15-8 = Platfrom Version Minor + bits 7-0 = Platform Type + + Platform Type field is an exact match value. The Platform + Major/Minor field is a best match. The bootloader will look + for the closest possible match. + + The "subtype_id" is unique to a Platform Type/Chipset ID. For + a given Platform Type, there will typically only be a single + board and the subtype_id will be 0. However in some cases board + variants may need to be distinquished by different subtype_id + values. + + subtype_id is an exact match value. + +EXAMPLE: + qcom,board-id = <15 2>; + qcom,msm-id = <0x1007e 0>;
include/dt-bindings/arm/qcom-ids.h new file +33 -0
@@ -0,0 +1,33 @@ +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +#ifndef __DT_BINDINGS_QCOM_IDS_H +#define __DT_BINDINGS_QCOM_IDS_H + +/* qcom,msm-id */ +#define QCOM_ID_MSM8916 206 +#define QCOM_ID_APQ8016 247 +#define QCOM_ID_MSM8216 248 +#define QCOM_ID_MSM8116 249 +#define QCOM_ID_MSM8616 250 + +/* qcom,board-id */ +#define QCOM_BRD_ID (a, major, minor) \ + (((major & 0xff) << 16) | ((minor & 0xff) << 8) | QCOM_BRD_ID_##a) + +#define QCOM_BRD_ID_MTP 8 +#define QCOM_BRD_ID_DRAGONBRD 10 +#define QCOM_BRD_ID_SBC 24 + +#define QCOM_BRD_SUBTYPE_DEFAULT 0 +#define QCOM_BRD_SUBTYPE_MTP8916_SMB1360 1 + +#endif
Signature · 1 line
2.1.4
Rob Herring
maintainerRFC PATCH 25/27 devicetree: bindings: Document qcom, msm-id and qcom, board-id
Quoted 5 lines · Lina Iyer
From: Kumar Gala <[email protected]>
The top level qcom,msm-id and qcom,board-id are utilized by bootloaders on Qualcomm MSM platforms to determine which device tree should be utilized and passed to the kernel.
See [1] and the link there to the prior version which is like this one.
Rob
RFC PATCH 25/27 devicetree: bindings: Document qcom, msm-id and qcom,board-id
Quoted 9 lines · Rob Herring
Quoted 5 lines · Lina Iyer
From: Kumar Gala <[email protected]>
The top level qcom,msm-id and qcom,board-id are utilized by bootloaders on Qualcomm MSM platforms to determine which device tree should be utilized and passed to the kernel.
See [1] and the link there to the prior version which is like this one.
Thanks Rob. Andy Gross sent me that link and I am waiting on the changes along with the dtb tool to make it. These patches will go away after Stephen's changes. I just put it in here for completeness.
Thanks,
Lina
Quoted 1 line
RFC PATCH 26/27 ARM64: dts: Add PSCI cpuidle support for MSM8916
In reply to Lina IyerAdd device bindings for CPUs to suspend using PSCI as the enable-method.
--- arch/arm64/boot/dts/qcom/msm8916.dtsi | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
arch/arm64/boot/dts/qcom/msm8916.dtsi +24 -0
@@ -51,25 +51,49 @@ device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SPC>; }; CPU1: cpu at 1 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x1>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SPC>; }; CPU2: cpu at 2 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x2>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SPC>; }; CPU3: cpu at 3 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x3>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SPC>; }; + + idle-states { + CPU_SPC: spc { + compatible = "qcom,idle-state-spc", + "arm,idle-state"; + arm,psci-suspend-param = <0x40000002>; + entry-latency-us = <130>; + exit-latency-us = <150>; + min-residency-us = <2000>; + }; + }; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; }; timer {
Signature · 1 line
2.1.4
RFC PATCH 27/27 ARM64: dts: Define CPU power domain for MSM8916
In reply to Lina IyerDefine power domain and the power states for the domain as defined by the PSCI firmware.
--- arch/arm64/boot/dts/qcom/msm8916.dtsi | 48 ++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-)
arch/arm64/boot/dts/qcom/msm8916.dtsi +47 -1
@@ -47,12 +47,32 @@ #address-cells = <1>; #size-cells = <0>; + cpu-map { + cluster0 { + cluster = <&CLUSTER0>; + + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + core2 { + cpu = <&CPU2>; + }; + core3 { + cpu = <&CPU3>; + }; + }; + }; + CPU0: cpu at 0 { device_type = "cpu"; compatible = "arm,cortex-a53", "arm,armv8"; reg = <0x0>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; + power-domains = <&CLUSTER0>; }; CPU1: cpu at 1 {
@@ -61,6 +81,7 @@ reg = <0x1>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; + power-domains = <&CLUSTER0>; }; CPU2: cpu at 2 {
@@ -69,6 +90,7 @@ reg = <0x2>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; + power-domains = <&CLUSTER0>; }; CPU3: cpu at 3 {
@@ -77,6 +99,7 @@ reg = <0x3>; enable-method = "psci"; cpu-idle-states = <&CPU_SPC>; + power-domains = <&CLUSTER0>; }; idle-states {
@@ -89,8 +112,31 @@ min-residency-us = <2000>; }; }; + + CLUSTER0: cluster at 0 { + #power-domain-cells = <0>; + power-states = <&CLUSTER_RET>, <&CLUSTER_PWR_DWN>; + }; + + cluster-power-states { + CLUSTER_RET: power-state at 1 { + compatible = "linux,domain-state"; + state-param = <0x1000010>; + entry-latency-us = <500>; + exit-latency-us = <500>; + residency-us = <2000>; + }; + + CLUSTER_PWR_DWN: power-state at 2 { + compatible = "linux,domain-state"; + state-param = <0x1000030>; + entry-latency-us = <2000>; + exit-latency-us = <2000>; + residency-us = <6000>; + }; + }; }; - + psci { compatible = "arm,psci-1.0"; method = "smc";
Signature · 1 line
2.1.4