* [RFC 0/7] Managing cluser-level c-states with generic power domains
@ 2015-09-25 13:04 Marc Titinger
2015-09-25 13:04 ` [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64 Marc Titinger
` (6 more replies)
0 siblings, 7 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
-----------------------
Summary
1) DESCRIPTION
2) DEPENDENCIES
3) URL
------------------------
1) DESCRIPTION
This patch set's underlying idea is that cluster-level c-states can be managed
by the power domain, building upon Lina Iyers recent work on CPU-domain, and Axel Haslam's
genpd multiple states. The power domain may contain CPU devices and non-CPU devices.
Non-CPU Devices may expose latency constraints by registering intermediate power-states upon
probing, for instance shallower states than the deepest cluster-off state. The generic
power domain governor may chose a device retention state in place of the cluster-sleep
state demanded by the menu governor, and call the platform specific handling to enter/leave
that retention state.
power-states
-----------
The proposed way how cluster-level c-states are declared as manageable by the
power domain, rather than through the cpuidle-ops, relies on the introduction of
"power-states", consistent with c-states. Here is an example of the DT bindings,
the c-state CLUSTER_SLEEP_0 is exposed as a power-state in the compatible property:
juno.dts: idle-states {
entry-method = "arm,psci";
CPU_SLEEP_0: cpu-sleep-0 {
compatible = "arm,idle-state";
arm,psci-suspend-param = <0x0010000>;
local-timer-stop;
entry-latency-us = <100>;
exit-latency-us = <250>;
min-residency-us = <2000>;
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
compatible = "arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
exit-latency-us = <700>;
min-residency-us = <2500>;
};
}
This will tell cpuidle runtime_put/get the CPU devices for this c-state. Eventually, the
actual platform handlers may be called from the genpd platform ops (in place of cpuidle_ops).
"drivers/cpuidle/cpuidle-arm.c":
static const struct of_device_id arm_idle_state_match[] __initconst = {
{.compatible = "arm,idle-state",
.data = arm_enter_idle_state},
{.compatible = "arm,power-state",
.data = arm_enter_power_state},
};
In case of a power-state, arm_enter_power_state will only call pm_runtime_put/get_sync
The power doamin will handle the power off, currently this patch set lacks the final
call to the psci interface to have a fully fonctionnal setup
(and there are some genpd_lock'ing issues if put/get actually suspend the CPU device.)
Ultimately, we would like the Power Domain's simple governor to being able to chose
the cluster power-state based on the c-states defered to it (power-states) and constraints
added by the devices. Consequently, we need to "soak" those power-states into the
power-domain intermediate states from Axel. Since power-states are declared and handled
the same manner than c-states (idle-states in DT), these patches add a soaking used when
attaching to a genpd, where power-states are parsed from the DT into the genpd states:
"drivers/base/power/domain.c":
static const struct of_device_id power_state_match[] = {
{.compatible = "arm,power-state",
},
};
int of_genpd_device_parse_states(struct device_node *np,
struct generic_pm_domain *genpd)
debugfs addition
---------------
To easy debug, this patch set adds a seq-file names "states" to the pm_genpd debugfs:
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
And also a seq-file "timings", to help visualize the constrains of the non-CPU
devices in a cluster PD.
Domain Devices, Timings in ns
Stop/Start Save/Restore, Effective
---------------------------------------------------- ---
a57_pd
/cpus/cpu@0 800 /740 1320 /1720 ,0 (cached stop)
/cpus/cpu@1 800 /740 1420 /1780 ,0 (cached stop)
/D1 660 /580 16560 /6080 ,2199420 (cached stop)
Device power-states
-------------------
some devices, like L2 caches, may feature a shallower retention mode, between CPU_SLEEP_0
and CLUSTER_SLEEP_0, in which mode the L2 memory is not powered off, leading to faster
resume than CLUSTER_SLEEP_0.
One way to handle device constrains and retention features in the power-domain, is to
allow devices to register a new power-state (consistent with a c-state).
idle-states:
D1_RETENTION: d1-retention {
compatible = "arm,power-state";
/*leave the psci param, for demo/testing:
* the psci cpuidle driver will not currently
* understand that a c-state shall not have it's
* table entry with a firmware command.
* the actual .power_on/off would be registered
* by the DECLARE macro for a given domain*/
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
exit-latency-us = <200>;
min-residency-us = <2500>;
};
D1 {
compatible = "fake,fake-driver";
name = "D1";
constraint = <30000>;
power-domains = <&a53_pd>;
power-states =<&D1_RETENTION>;
};
The genpd simple governor can now upon suspend of the last-man CPU chose a shallower
retention state than CLUSTER_SLEEP_0.
In order to achieve this, this patch set added the power-state parsing during the
genpd_dev_pm_attach call. Multiple genpd states are now inserted in a sorted manner
according to their depth: see pm_genpd_insert_state in "drivers/base/power/domain.c".
2) DEPENDENCIES
This patch set applies over linux-4.2rc5 plus the following ordered dependencies:
* Ulf Hansson:
6637131 New [V4] PM / Domains: Remove intermediate states from the power off sequence
* Lina Iyer's patch series:
6945201 New [1/9] PM / Domains: Allocate memory outside domain locks
6945221 New [2/9] PM / Domains: Remove dev->driver check for runtime PM
6945231 New [3/9] PM / Domains: Support IRQ safe PM domains
6945211 New [4/9] kernel/cpu_pm: fix cpu_cluster_pm_exit comment
6945251 New [5/9] ARM: common: Introduce PM domains for CPUs/clusters
6945281 New [6/9] ARM: domain: Add platform handlers for CPU PM domains
6945261 New [7/9] ARM: cpuidle: Add runtime PM support for CPU idle
6945241 New [8/9] ARM64: smp: Add runtime PM support for CPU hotplug
6945271 New [9/9] ARM: smp: Add runtime PM support for CPU hotplug
* John Medhurst:
6303671 New arm64: dts: Add idle-states for Juno
* Axel Haslam:
7b4eda6 ARM: imx6: pm: declare pm domain latency on power_state struct.
e65789a PM / Domains: make governor select deepest state
8b22b90 PM / Domains: core changes for multiple states
84af098 PM / Domains: prepare for multiple states
2) URL
playable from https://github.com/mtitinger/linux-pm.git
by adding the "fake driver D1" and launching the test-dev-state.sh script.
this will show the power domain suspending to an intermediate state, based on the
device constraints.
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
a57_pd d1-retention
/devices/system/cpu/cpu1 suspended
/devices/system/cpu/cpu2 suspended
/devices/platform/D1
----------------------------------------------------------------------
Marc Titinger (7):
arm64: pm/domains: try mutualize CPU domains init between arm/arm64
arm64: Juno: declare generic power domains for both clusters.
PM / Domains: prepare for devices that might register a power state
PM / Domains: introduce power-states consistent with c-states.
PM / Domains: succeed & warn when attaching non-irqsafe devices to an
irq-safe domain.
arm: cpuidle: let genpd handle the cluster power transition with
'power-states'
PM / Domains: add debugfs 'states' and 'timings' seq files
.../devicetree/bindings/arm/idle-states.txt | 21 +-
.../devicetree/bindings/power/power_domain.txt | 29 ++
arch/arm/common/domains.c | 9 +-
arch/arm64/Kconfig | 1 +
arch/arm64/boot/dts/arm/juno.dts | 25 +-
arch/arm64/include/asm/arm-pd.h | 1 +
arch/arm64/kernel/Makefile | 6 +
arch/arm64/kernel/domains.c | 1 +
drivers/base/power/domain.c | 418 +++++++++++++++------
drivers/cpuidle/cpuidle-arm.c | 50 ++-
include/linux/pm_domain.h | 21 +-
11 files changed, 452 insertions(+), 130 deletions(-)
create mode 120000 arch/arm64/include/asm/arm-pd.h
create mode 120000 arch/arm64/kernel/domains.c
--
1.9.1
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-10-06 2:27 ` Lina Iyer
2015-09-25 13:04 ` [RFC 2/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
` (5 subsequent siblings)
6 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
fake path to start testing, eventually move this out of /arch/.
incidently enable PM_GENERIC_DOMAINS for VExpress.
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
arch/arm/common/domains.c | 4 ++--
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/arm-pd.h | 1 +
arch/arm64/kernel/Makefile | 6 ++++++
arch/arm64/kernel/domains.c | 1 +
5 files changed, 11 insertions(+), 2 deletions(-)
create mode 120000 arch/arm64/include/asm/arm-pd.h
create mode 120000 arch/arm64/kernel/domains.c
diff --git a/arch/arm/common/domains.c b/arch/arm/common/domains.c
index d3207da..68908d4 100644
--- a/arch/arm/common/domains.c
+++ b/arch/arm/common/domains.c
@@ -20,7 +20,7 @@
#include <asm/arm-pd.h>
-#define NAME_MAX 36
+#define GENPD_NAME_MAX 36
struct arm_pm_domain {
struct generic_pm_domain genpd;
@@ -182,7 +182,7 @@ static int __init arm_domain_init(void)
}
/* Initialize rest of CPU PM domain specifics */
- pd->genpd.name = kstrndup(np->name, NAME_MAX, GFP_KERNEL);
+ pd->genpd.name = kstrndup(np->name, GENPD_NAME_MAX, GFP_KERNEL);
pd->genpd.power_off = arm_pd_power_down;
pd->genpd.power_on = arm_pd_power_up;
pd->genpd.flags |= GENPD_FLAG_IRQ_SAFE;
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 7c55a63..d35f213 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -250,6 +250,7 @@ config ARCH_VEXPRESS
select COMMON_CLK_VERSATILE
select POWER_RESET_VEXPRESS
select VEXPRESS_CONFIG
+ select PM_GENERIC_DOMAINS if PM
help
This enables support for the ARMv8 software model (Versatile
Express).
diff --git a/arch/arm64/include/asm/arm-pd.h b/arch/arm64/include/asm/arm-pd.h
new file mode 120000
index 0000000..ecc5437
--- /dev/null
+++ b/arch/arm64/include/asm/arm-pd.h
@@ -0,0 +1 @@
+../../../arm/include/asm/arm-pd.h
\ No newline at end of file
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 426d076..4689565 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -30,6 +30,12 @@ arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
+
+# CPU domains: try mutualize for arm/arm64 in a first step.
+# Eventually make this more generic.
+#
+arm64-obj-$(CONFIG_PM_GENERIC_DOMAINS) += domains.o
+
arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
arm64-obj-$(CONFIG_KGDB) += kgdb.o
arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
diff --git a/arch/arm64/kernel/domains.c b/arch/arm64/kernel/domains.c
new file mode 120000
index 0000000..bf17c69
--- /dev/null
+++ b/arch/arm64/kernel/domains.c
@@ -0,0 +1 @@
+../../arm/common/domains.c
\ No newline at end of file
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 2/7] arm64: Juno: declare generic power domains for both clusters.
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
2015-09-25 13:04 ` [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64 Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-09-25 13:04 ` [RFC 3/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
` (4 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 342bb99..499f035 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -63,6 +63,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A57_1: cpu@1 {
@@ -72,6 +73,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A53_0: cpu@100 {
@@ -81,6 +83,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_1: cpu@101 {
@@ -90,6 +93,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_2: cpu@102 {
@@ -99,6 +103,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_3: cpu@103 {
@@ -108,6 +113,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A57_L2: l2-cache0 {
@@ -119,6 +125,19 @@
};
};
+ pm-domains {
+
+ a57_pd: a57_pd@ {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+
+ a53_pd: a53_pd@ {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+ };
+
pmu {
compatible = "arm,armv8-pmuv3";
interrupts = <GIC_SPI 02 IRQ_TYPE_LEVEL_HIGH>,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 3/7] PM / Domains: prepare for devices that might register a power state
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
2015-09-25 13:04 ` [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64 Marc Titinger
2015-09-25 13:04 ` [RFC 2/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-09-25 13:04 ` [RFC 4/7] PM / Domains: introduce power-states consistent with c-states Marc Titinger
` (3 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Devices may register an intermediate retention state into the domain upon
attaching. Currently generic domain would register an array of states upon
init. This patch prepares for later insertion (sort per depth, remove).
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
drivers/base/power/domain.c | 187 +++++++++++++++++++-------------------------
include/linux/pm_domain.h | 18 ++++-
2 files changed, 95 insertions(+), 110 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index aa8e842..8c8ea94 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/suspend.h>
#include <linux/export.h>
+#include <linux/sort.h>
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
@@ -50,12 +51,6 @@
__retval; \
})
-#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);
@@ -1353,46 +1348,6 @@ 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;
- }
-
- 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.
@@ -1963,6 +1918,73 @@ int pm_genpd_add_pstates(struct generic_pm_domain *genpd,
return 0;
}
+/*
+* state depth comparison function.
+*/
+static int state_cmp(const void *a, const void *b)
+{
+ struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
+ struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
+
+ s64 depth_a =
+ state_a->power_on_latency_ns + state_a->power_off_latency_ns;
+ s64 depth_b =
+ state_b->power_on_latency_ns + state_b->power_off_latency_ns;
+
+ return (depth_a > depth_b) ? 0 : -1;
+}
+
+/*
+ * TODO: antagonist routine.
+*/
+int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state)
+{
+ int ret = 0;
+ int state_count = genpd->state_count;
+
+ if (IS_ERR_OR_NULL(genpd) || (!state))
+ ret = -EINVAL;
+
+ if (state_count >= GENPD_POWER_STATES_MAX)
+ ret = -ENOMEM;
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ /* to save memory, Name allocation will happen if debug is enabled */
+ genpd->states[state_count].name = kstrndup(state->name,
+ GENPD_MAX_NAME_SIZE,
+ GFP_KERNEL);
+ if (!genpd->states[state_count].name) {
+ pr_err("%s Failed to allocate state '%s' name.\n",
+ genpd->name, state->name);
+ ret = -ENOMEM;
+ }
+#endif
+ genpd_lock(genpd);
+
+ if (!ret) {
+ genpd->states[state_count].power_on_latency_ns =
+ state->power_on_latency_ns;
+ genpd->states[state_count].power_off_latency_ns =
+ state->power_off_latency_ns;
+ genpd->state_count++;
+ }
+
+ /* sort from shallowest to deepest */
+ sort(genpd->states, genpd->state_count,
+ sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
+
+ /* Sanity check for current state index */
+ if (genpd->state_idx >= genpd->state_count) {
+ pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
+ genpd->state_idx = genpd->state_count - 1;
+ }
+
+ genpd_unlock(genpd);
+
+ return ret;
+}
+
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@@ -1976,36 +1998,24 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
const struct genpd_power_state *states,
unsigned int state_count, bool is_off)
{
- static const struct genpd_power_state genpd_default_state[] = {
- {
- .name = "OFF",
- .power_off_latency_ns = 0,
- .power_on_latency_ns = 0,
- },
- };
- int ret;
+ int i;
if (IS_ERR_OR_NULL(genpd))
return;
- /* If no states defined, use the default OFF state */
- if (!states || (state_count < 1))
- ret = genpd_alloc_states_data(genpd, genpd_default_state,
- ARRAY_SIZE(genpd_default_state));
- else
- ret = genpd_alloc_states_data(genpd, states, state_count);
-
- if (ret) {
- pr_err("Fail to allocate states for %s\n", genpd->name);
- return;
- }
+ /* simply use an array, we wish to add/remove new retention states
+ from later device init/exit. */
+ memset(genpd->states, 0, GENPD_POWER_STATES_MAX
+ * sizeof(struct genpd_power_state));
- /* Sanity check for initial state */
- if (genpd->state_idx >= genpd->state_count) {
- pr_warn("pm domain %s Invalid initial state.\n",
- genpd->name);
- genpd->state_idx = genpd->state_count - 1;
- }
+ if (!states || !state_count) {
+ /* require a provider for a default state */
+ genpd->state_count = 0;
+ genpd->state_idx = 0;
+ } else
+ for (i = 0; i < state_count; i++)
+ if (pm_genpd_insert_state(genpd, &states[i]))
+ return;
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
@@ -2364,33 +2374,6 @@ 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.
@@ -2529,12 +2512,4 @@ 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 */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index da3c789..39dda2c 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
struct cpuidle_state *idle_state;
};
+
+/* Arbitrary max number of devices registering a special
+ * retention state with the PD, to keep things simple.
+ */
+#define GENPD_POWER_STATES_MAX 12
+#define GENPD_MAX_NAME_SIZE 40
+
struct genpd_power_state {
char *name;
s64 power_off_latency_ns;
@@ -81,7 +88,8 @@ struct generic_pm_domain {
struct device *dev);
unsigned int flags; /* Bit field of configs for genpd */
- struct genpd_power_state *states;
+ struct genpd_power_state states[GENPD_POWER_STATES_MAX];
+
unsigned int state_count; /* number of states */
unsigned int state_idx; /* state that genpd will go to when off */
@@ -163,10 +171,12 @@ extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
extern int pm_genpd_name_detach_cpuidle(const char *name);
+extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state);
extern void pm_genpd_init(struct generic_pm_domain *genpd,
- struct dev_power_governor *gov,
- const struct genpd_power_state *states,
- unsigned int state_count, bool is_off);
+ struct dev_power_governor *gov,
+ const struct genpd_power_state *states,
+ unsigned int state_count, bool is_off);
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
extern int pm_genpd_name_poweron(const char *domain_name);
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 4/7] PM / Domains: introduce power-states consistent with c-states.
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
` (2 preceding siblings ...)
2015-09-25 13:04 ` [RFC 3/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-09-25 13:04 ` [RFC 5/7] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
` (2 subsequent siblings)
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
This patch allows cluster-level C-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 c-states.
With Juno, in this example the c-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
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
.../devicetree/bindings/arm/idle-states.txt | 21 ++++-
.../devicetree/bindings/power/power_domain.txt | 29 ++++++
arch/arm/common/domains.c | 5 ++
arch/arm64/boot/dts/arm/juno.dts | 10 ++-
drivers/base/power/domain.c | 100 +++++++++++++++++++++
include/linux/pm_domain.h | 3 +
6 files changed, 163 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt
index a8274ea..18fdeaf 100644
--- a/Documentation/devicetree/bindings/arm/idle-states.txt
+++ b/Documentation/devicetree/bindings/arm/idle-states.txt
@@ -270,7 +270,8 @@ follows:
- compatible
Usage: Required
Value type: <stringlist>
- Definition: Must be "arm,idle-state".
+ Definition: Must be "arm,idle-state",
+ or "arm,power-state" (see section 5)
- local-timer-stop
Usage: See definition
@@ -680,7 +681,23 @@ cpus {
};
===========================================
-5 - References
+5 - power state
+===========================================
+
+Device in a generic power domain may expose an intermediate retention
+state that can be opted to by the domain governor when the last-man
+CPU is powered off. Those power-states will not be entered by the
+cpuidle.ops based on a state index, but instead can be elected by the
+domain governor and entered to by the generic domain.
+
+ - compatible
+ Usage: Required
+ Value type: <stringlist>
+ Definition: Must be "arm,power-state".
+
+
+===========================================
+6 - References
===========================================
[1] ARM Linux Kernel documentation - CPUs bindings
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
index 0f8ed37..d437385 100644
--- a/Documentation/devicetree/bindings/power/power_domain.txt
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -29,6 +29,16 @@ 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.
+ CPU domains: Deep c-states that match a cluster power-off can be delegated to the
+ generic power domain. Device other than CPUs may have register intermediate
+ power states in the same domain. The domain governor can do a good job in
+ electing a power state when the last cpu is powered off as devices in the
+ same genpd may register intermediate states.
+ Devices : a device may register an intermediate c-state matching a memory
+ retention feature for instance.
+
Example:
power: power-controller@12340000 {
@@ -55,6 +65,25 @@ 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>;
+ };
+ };
+
+
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.
diff --git a/arch/arm/common/domains.c b/arch/arm/common/domains.c
index 68908d4..6684776 100644
--- a/arch/arm/common/domains.c
+++ b/arch/arm/common/domains.c
@@ -191,6 +191,11 @@ static int __init arm_domain_init(void)
pm_genpd_init(&pd->genpd, &simple_qos_governor, NULL, 0, false);
of_genpd_add_provider_simple(np, &pd->genpd);
+ /* if a cpuidle-state is declared in this domain node, it will
+ be the domain's job to enter/exit this state, if the defice
+ /subdomain constraints are compatible */
+ of_genpd_device_parse_states(np, &pd->genpd);
+
count++;
}
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 499f035..cadc5de 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -47,7 +47,7 @@
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
- compatible = "arm,idle-state";
+ compatible = "arm,idle-state","arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
@@ -128,13 +128,17 @@
pm-domains {
a57_pd: a57_pd@ {
- compatible = "arm,pd";
+ /* will have the a53 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@ {
- compatible = "arm,pd";
+ /* will have the a53 platform ARM_PD_METHOD_OF_DECLARE*/
+ compatible = "arm,pd","arm,cortex-a57";
#power-domain-cells = <0>;
+ power-states = <&CLUSTER_SLEEP_0>;
};
};
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 8c8ea94..8259654 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2285,6 +2285,104 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd);
}
+static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
+ *genpd_state,
+ const struct of_device_id *matches,
+ struct device_node *state_node)
+{
+ const struct of_device_id *match_id;
+ int err = 0;
+ u32 latency;
+
+ match_id = of_match_node(matches, 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 const struct of_device_id power_state_match[] = {
+ {.compatible = "arm,power-state",
+ },
+};
+
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd)
+{
+ 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;
+
+ err = dt_cpuidle_to_genpd_power_state(&genpd_state,
+ power_state_match,
+ 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->name,
+ GENPD_MAX_NAME_SIZE, GFP_KERNEL);
+ if (!genpd_state.name)
+ err = -ENOMEM;
+#endif
+ of_node_put(state_node);
+ err = pm_genpd_insert_state(genpd, &genpd_state);
+ if (err)
+ break;
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ kfree(genpd_state.name);
+#endif
+ }
+ return err;
+}
+
/**
* genpd_dev_pm_attach - Attach a device to its PM domain using DT.
* @dev: Device to attach.
@@ -2353,6 +2451,8 @@ int genpd_dev_pm_attach(struct device *dev)
return ret;
}
+ of_genpd_device_parse_states(dev->of_node, pd);
+
dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
pm_genpd_poweron(pd);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 39dda2c..b0bff93 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -306,6 +306,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
struct of_phandle_args *genpdspec,
void *data);
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd);
+
int genpd_dev_pm_attach(struct device *dev);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int __of_genpd_add_provider(struct device_node *np,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 5/7] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain.
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
` (3 preceding siblings ...)
2015-09-25 13:04 ` [RFC 4/7] PM / Domains: introduce power-states consistent with c-states Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-09-25 13:04 ` [RFC 6/7] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
2015-09-25 13:04 ` [RFC 7/7] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
This patch checks for irq-safe compatibility in suspend/resume instead of
failing the attach operation early on. Non-cpu devices attaching to an
irq-safe power domain will have to call pm_runtime_irq_safe from their
probe function.
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
drivers/base/power/domain.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 8259654..b747e9e 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -585,6 +585,14 @@ static int pm_genpd_runtime_suspend(struct device *dev)
if (dev->power.irq_safe && !genpd->irq_safe)
return 0;
+ /* The device probe may have missed calling pm_runtime_irq_safe.
+ */
+ if (!dev->power.irq_safe && genpd->irq_safe) {
+ dev_err(dev, "trying to %s a non-irqsafe device in an irq-safe domain\n",
+ __func__);
+ return -EINVAL;
+ }
+
genpd_lock(genpd);
genpd->in_progress++;
@@ -624,6 +632,14 @@ static int pm_genpd_runtime_resume(struct device *dev)
goto out;
}
+ /* The device probe may have missed calling pm_runtime_irq_safe.
+ */
+ if (!dev->power.irq_safe && genpd->irq_safe) {
+ dev_err(dev, "trying to %s a non-irqsafe device in an irq-safe domain\n",
+ __func__);
+ return -EINVAL;
+ }
+
genpd_lock(genpd);
ret = __pm_genpd_poweron(genpd);
genpd_unlock(genpd);
@@ -1365,11 +1381,11 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
return -EINVAL;
- if (genpd->irq_safe && !dev->power.irq_safe) {
- dev_err(dev,
+ /* Only issue a warning, runtime_irqsafe may be called later on
+ * from the driver probe. */
+ if (genpd->irq_safe && !dev->power.irq_safe)
+ dev_warn(dev,
"Devices in an IRQ safe domain have to be IRQ safe.\n");
- return -EINVAL;
- }
gpd_data = genpd_alloc_dev_data(dev, genpd, td);
if (IS_ERR(gpd_data))
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 6/7] arm: cpuidle: let genpd handle the cluster power transition with 'power-states'
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
` (4 preceding siblings ...)
2015-09-25 13:04 ` [RFC 5/7] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
2015-09-25 13:04 ` [RFC 7/7] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Cpuidle now handles c-states and power-states differently. c-states do not decrement
the reference count for the CPUs in the cluster, while power-states i.e.
cluster level states like 'CLUSTER_SLEEP_0' in the case of juno, will.
The 'D1' fake device also registers intermediate power-state,
for experimentation.
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 2 +-
drivers/cpuidle/cpuidle-arm.c | 50 ++++++++++++++++++++++++++++++++--------
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index cadc5de..0bb0dd7 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -47,7 +47,7 @@
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
- compatible = "arm,idle-state","arm,power-state";
+ compatible = "arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index ca118ed..592bb1cb 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -39,7 +39,6 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
int ret;
- struct device *cpu_dev = get_cpu_device(dev->cpu);
if (!idx) {
cpu_do_idle();
@@ -48,11 +47,6 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
ret = cpu_pm_enter();
if (!ret) {
- /*
- * Notify runtime PM as well of this cpu powering down
- * TODO: Merge CPU_PM and runtime PM.
- */
- pm_runtime_put_sync(cpu_dev);
/*
* Pass idle state index to cpu_suspend which in turn will
@@ -61,6 +55,43 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
*/
arm_cpuidle_suspend(idx);
+ cpu_pm_exit();
+ }
+
+ return ret ? -1 : idx;
+}
+
+/*
+ * arm_enter_power_state - delegate state trasition to genpd
+ *
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
+ *
+ * Called from the CPUidle framework to delegate a state transition
+ * to the generic domain. This will be a cluster poweroff state
+ * the Domain will chose to actually turn off the cluster based on
+ * the status of other CPUs, and devices and subdomains in the Cluster
+ * domain.
+ */
+static int arm_enter_power_state(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int idx)
+{
+ int ret;
+ struct device *cpu_dev = get_cpu_device(dev->cpu);
+
+ BUG_ON(idx == 0);
+
+ ret = cpu_pm_enter();
+ if (!ret) {
+ /*
+ * Notify runtime PM as well of this cpu powering down
+ * TODO: Merge CPU_PM and runtime PM.
+ */
+ pm_runtime_put_sync(cpu_dev);
+
+ arm_cpuidle_suspend(idx);
+
pm_runtime_get_sync(cpu_dev);
cpu_pm_exit();
}
@@ -89,9 +120,10 @@ static struct cpuidle_driver arm_idle_driver = {
};
static const struct of_device_id arm_idle_state_match[] __initconst = {
- { .compatible = "arm,idle-state",
- .data = arm_enter_idle_state },
- { },
+ {.compatible = "arm,idle-state",
+ .data = arm_enter_idle_state},
+ {.compatible = "arm,power-state",
+ .data = arm_enter_power_state},
};
/*
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC 7/7] PM / Domains: add debugfs 'states' and 'timings' seq files
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
` (5 preceding siblings ...)
2015-09-25 13:04 ` [RFC 6/7] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
@ 2015-09-25 13:04 ` Marc Titinger
6 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-09-25 13:04 UTC (permalink / raw)
To: khilman, rjw
Cc: linux-pm, linux-kernel, ahaslam, bcousson, lina.iyer, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
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@100 1060 /660 1580 /1940 ,0 (cached stop)
/cpus/cpu@101 1060 /740 1520 /1600 ,0 (cached stop)
/cpus/cpu@102 880 /620 1380 /1780 ,0 (cached stop)
/cpus/cpu@103 1080 /640 1340 /1600 ,0 (cached stop)
a57_pd
/cpus/cpu@0 1160 /740 3280 /1800 ,0 (cached stop)
/cpus/cpu@1 780 /1400 1440 /2080 ,0 (cached stop)
/D1 600 /540 7140 /6420 ,2199460 (cached stop)
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
drivers/base/power/domain.c | 107 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 105 insertions(+), 2 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index b747e9e..59ccd92 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2606,6 +2606,96 @@ 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;
@@ -2615,8 +2705,21 @@ static int __init pm_genpd_debug_init(void)
if (!pm_genpd_debugfs_dir)
return -ENOMEM;
- d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
- pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops);
+ d = debugfs_create_file("summary", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_summary_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("states", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_states_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("timings", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_timing_fops);
if (!d)
return -ENOMEM;
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* Re: [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64
2015-09-25 13:04 ` [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64 Marc Titinger
@ 2015-10-06 2:27 ` Lina Iyer
2015-10-06 8:52 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
0 siblings, 2 replies; 39+ messages in thread
From: Lina Iyer @ 2015-10-06 2:27 UTC (permalink / raw)
To: Marc Titinger; +Cc: khilman, rjw, linux-pm, linux-kernel, ahaslam, bcousson
On Fri, Sep 25 2015 at 07:04 -0600, Marc Titinger wrote:
>From: Marc Titinger <mtitinger@baylibre.com>
>
>fake path to start testing, eventually move this out of /arch/.
>incidently enable PM_GENERIC_DOMAINS for VExpress.
>
In fact, this could be moved out of ARM. My last series moved it to
drivers/base/power/.
-- Lina
>Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
>---
> arch/arm/common/domains.c | 4 ++--
> arch/arm64/Kconfig | 1 +
> arch/arm64/include/asm/arm-pd.h | 1 +
> arch/arm64/kernel/Makefile | 6 ++++++
> arch/arm64/kernel/domains.c | 1 +
> 5 files changed, 11 insertions(+), 2 deletions(-)
> create mode 120000 arch/arm64/include/asm/arm-pd.h
> create mode 120000 arch/arm64/kernel/domains.c
>
>diff --git a/arch/arm/common/domains.c b/arch/arm/common/domains.c
>index d3207da..68908d4 100644
>--- a/arch/arm/common/domains.c
>+++ b/arch/arm/common/domains.c
>@@ -20,7 +20,7 @@
>
> #include <asm/arm-pd.h>
>
>-#define NAME_MAX 36
>+#define GENPD_NAME_MAX 36
>
> struct arm_pm_domain {
> struct generic_pm_domain genpd;
>@@ -182,7 +182,7 @@ static int __init arm_domain_init(void)
> }
>
> /* Initialize rest of CPU PM domain specifics */
>- pd->genpd.name = kstrndup(np->name, NAME_MAX, GFP_KERNEL);
>+ pd->genpd.name = kstrndup(np->name, GENPD_NAME_MAX, GFP_KERNEL);
> pd->genpd.power_off = arm_pd_power_down;
> pd->genpd.power_on = arm_pd_power_up;
> pd->genpd.flags |= GENPD_FLAG_IRQ_SAFE;
>diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
>index 7c55a63..d35f213 100644
>--- a/arch/arm64/Kconfig
>+++ b/arch/arm64/Kconfig
>@@ -250,6 +250,7 @@ config ARCH_VEXPRESS
> select COMMON_CLK_VERSATILE
> select POWER_RESET_VEXPRESS
> select VEXPRESS_CONFIG
>+ select PM_GENERIC_DOMAINS if PM
> help
> This enables support for the ARMv8 software model (Versatile
> Express).
>diff --git a/arch/arm64/include/asm/arm-pd.h b/arch/arm64/include/asm/arm-pd.h
>new file mode 120000
>index 0000000..ecc5437
>--- /dev/null
>+++ b/arch/arm64/include/asm/arm-pd.h
>@@ -0,0 +1 @@
>+../../../arm/include/asm/arm-pd.h
>\ No newline at end of file
>diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
>index 426d076..4689565 100644
>--- a/arch/arm64/kernel/Makefile
>+++ b/arch/arm64/kernel/Makefile
>@@ -30,6 +30,12 @@ arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
> arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
> arm64-obj-$(CONFIG_CPU_PM) += sleep.o suspend.o
> arm64-obj-$(CONFIG_CPU_IDLE) += cpuidle.o
>+
>+# CPU domains: try mutualize for arm/arm64 in a first step.
>+# Eventually make this more generic.
>+#
>+arm64-obj-$(CONFIG_PM_GENERIC_DOMAINS) += domains.o
>+
> arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o
> arm64-obj-$(CONFIG_KGDB) += kgdb.o
> arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o
>diff --git a/arch/arm64/kernel/domains.c b/arch/arm64/kernel/domains.c
>new file mode 120000
>index 0000000..bf17c69
>--- /dev/null
>+++ b/arch/arm64/kernel/domains.c
>@@ -0,0 +1 @@
>+../../arm/common/domains.c
>\ No newline at end of file
>--
>1.9.1
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64
2015-10-06 2:27 ` Lina Iyer
@ 2015-10-06 8:52 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
1 sibling, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 8:52 UTC (permalink / raw)
To: Lina Iyer; +Cc: khilman, rjw, linux-pm, linux-kernel, ahaslam, bcousson
On 06/10/2015 04:27, Lina Iyer wrote:
> On Fri, Sep 25 2015 at 07:04 -0600, Marc Titinger wrote:
>> From: Marc Titinger <mtitinger@baylibre.com>
>>
>> fake path to start testing, eventually move this out of /arch/.
>> incidently enable PM_GENERIC_DOMAINS for VExpress.
>>
> In fact, this could be moved out of ARM. My last series moved it to
> drivers/base/power/.
>
> -- Lina
>
Hi Lina, thanks for the heads-up, rebase of my patch-set in progress :)
Marc.
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC v2 0/6] Managing cluser-level c-states with generic power domains
2015-10-06 2:27 ` Lina Iyer
2015-10-06 8:52 ` Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 1/6] arm64: Juno: declare generic power domains for both clusters Marc Titinger
` (7 more replies)
1 sibling, 8 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
v2:
- rebase on Lina Iyer's latest series
- remove unnecessary dependency on perf-state patches from Axel Haslam
-----------------------
Summary
1) DESCRIPTION
2) DEPENDENCIES
3) URL
------------------------
1) DESCRIPTION
This patch set's underlying idea is that cluster-level c-states can be managed
by the power domain, building upon Lina Iyers recent work on CPU-domain, and Axel Haslam's
genpd multiple states. The power domain may contain CPU devices and non-CPU devices.
Non-CPU Devices may expose latency constraints by registering intermediate power-states upon
probing, for instance shallower states than the deepest cluster-off state. The generic
power domain governor may chose a device retention state in place of the cluster-sleep
state demanded by the menu governor, and call the platform specific handling to enter/leave
that retention state.
power-states
-----------
The proposed way how cluster-level c-states are declared as manageable by the
power domain, rather than through the cpuidle-ops, relies on the introduction of
"power-states", consistent with c-states. Here is an example of the DT bindings,
the c-state CLUSTER_SLEEP_0 is exposed as a power-state in the compatible property:
juno.dts: idle-states {
entry-method = "arm,psci";
CPU_SLEEP_0: cpu-sleep-0 {
compatible = "arm,idle-state";
arm,psci-suspend-param = <0x0010000>;
local-timer-stop;
entry-latency-us = <100>;
exit-latency-us = <250>;
min-residency-us = <2000>;
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
compatible = "arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
exit-latency-us = <700>;
min-residency-us = <2500>;
};
}
This will tell cpuidle runtime_put/get the CPU devices for this c-state. Eventually, the
actual platform handlers may be called from the genpd platform ops (in place of cpuidle_ops).
"drivers/cpuidle/cpuidle-arm.c":
static const struct of_device_id arm_idle_state_match[] __initconst = {
{.compatible = "arm,idle-state",
.data = arm_enter_idle_state},
{.compatible = "arm,power-state",
.data = arm_enter_power_state},
};
In case of a power-state, arm_enter_power_state will only call pm_runtime_put/get_sync
The power doamin will handle the power off, currently this patch set lacks the final
call to the psci interface to have a fully fonctionnal setup
(and there are some genpd_lock'ing issues if put/get actually suspend the CPU device.)
Ultimately, we would like the Power Domain's simple governor to being able to chose
the cluster power-state based on the c-states defered to it (power-states) and constraints
added by the devices. Consequently, we need to "soak" those power-states into the
power-domain intermediate states from Axel. Since power-states are declared and handled
the same manner than c-states (idle-states in DT), these patches add a soaking used when
attaching to a genpd, where power-states are parsed from the DT into the genpd states:
"drivers/base/power/domain.c":
static const struct of_device_id power_state_match[] = {
{.compatible = "arm,power-state",
},
};
int of_genpd_device_parse_states(struct device_node *np,
struct generic_pm_domain *genpd)
debugfs addition
---------------
To easy debug, this patch set adds a seq-file names "states" to the pm_genpd debugfs:
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
And also a seq-file "timings", to help visualize the constrains of the non-CPU
devices in a cluster PD.
Domain Devices, Timings in ns
Stop/Start Save/Restore, Effective
---------------------------------------------------- ---
a57_pd
/cpus/cpu@0 800 /740 1320 /1720 ,0 (cached stop)
/cpus/cpu@1 800 /740 1420 /1780 ,0 (cached stop)
/D1 660 /580 16560 /6080 ,2199420 (cached stop)
Device power-states
-------------------
some devices, like L2 caches, may feature a shallower retention mode, between CPU_SLEEP_0
and CLUSTER_SLEEP_0, in which mode the L2 memory is not powered off, leading to faster
resume than CLUSTER_SLEEP_0.
One way to handle device constrains and retention features in the power-domain, is to
allow devices to register a new power-state (consistent with a c-state).
idle-states:
D1_RETENTION: d1-retention {
compatible = "arm,power-state";
/*leave the psci param, for demo/testing:
* the psci cpuidle driver will not currently
* understand that a c-state shall not have it's
* table entry with a firmware command.
* the actual .power_on/off would be registered
* by the DECLARE macro for a given domain*/
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
exit-latency-us = <200>;
min-residency-us = <2500>;
};
D1 {
compatible = "fake,fake-driver";
name = "D1";
constraint = <30000>;
power-domains = <&a53_pd>;
power-states =<&D1_RETENTION>;
};
The genpd simple governor can now upon suspend of the last-man CPU chose a shallower
retention state than CLUSTER_SLEEP_0.
In order to achieve this, this patch set added the power-state parsing during the
genpd_dev_pm_attach call. Multiple genpd states are now inserted in a sorted manner
according to their depth: see pm_genpd_insert_state in "drivers/base/power/domain.c".
2) DEPENDENCIES
This patch set applies over linux-4.2rc5 plus the following ordered dependencies:
* Ulf Hansson:
6637131 New [V4] PM / Domains: Remove intermediate states from the power off sequence
* Lina Iyer's patch series:
7118981 Not Applicable [v2,1/7] PM / Domains: Allocate memory outside domain locks
7118991 Not Applicable [v2,2/7] PM / Domains: Support IRQ safe PM domains
7119001 Not Applicable [v2,3/7] drivers: cpu: Define CPU devices as IRQ safe
7119011 Not Applicable [v2,4/7] PM / Domains: Introduce PM domains for CPUs/clusters
7119021 Not Applicable [v2,5/7] ARM: cpuidle: Add runtime PM support for CPU idle
7119031 Not Applicable [v2,6/7] ARM64: smp: Add runtime PM support for CPU hotplug
7119041 Not Applicable [v2,7/7] ARM: smp: Add runtime PM support for CPU hotplug
* John Medhurst:
6303671 New arm64: dts: Add idle-states for Juno
* Axel Haslam:
6301741 Not Applicable [v7,1/5] PM / Domains: prepare for multiple states
6301751 Not Applicable [v7,2/5] PM / Domains: core changes for multiple states
6301781 Not Applicable [v7,3/5] PM / Domains: make governor select deepest state
6301771 Not Applicable [v7,4/5] ARM: imx6: pm: declare pm domain latency on power_state struct.
6301761 Not Applicable [v7,5/5] PM / Domains: remove old power on/off latencies.
2) URL
playable from https://github.com/mtitinger/linux-pm.git
by adding the "fake driver D1" and launching the test-dev-state.sh script.
this will show the power domain suspending to an intermediate state, based on the
device constraints.
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
a57_pd d1-retention
/devices/system/cpu/cpu1 suspended
/devices/system/cpu/cpu2 suspended
/devices/platform/D1
----------------------------------------------------------------------
Marc Titinger (6):
arm64: Juno: declare generic power domains for both clusters.
PM / Domains: prepare for devices that might register a power state
PM / Domains: introduce power-states consistent with c-states.
PM / Domains: succeed & warn when attaching non-irqsafe devices to an
irq-safe domain.
arm: cpuidle: let genpd handle the cluster power transition with
'power-states'
PM / Domains: add debugfs 'states' and 'timings' seq files
.../devicetree/bindings/arm/idle-states.txt | 21 +-
.../devicetree/bindings/power/power_domain.txt | 29 ++
arch/arm64/boot/dts/arm/juno.dts | 25 +-
drivers/base/power/cpu-pd.c | 5 +
drivers/base/power/domain.c | 415 +++++++++++++++------
drivers/cpuidle/cpuidle-arm.c | 52 ++-
include/linux/pm_domain.h | 21 +-
7 files changed, 437 insertions(+), 131 deletions(-)
--
1.9.1
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC v2 1/6] arm64: Juno: declare generic power domains for both clusters.
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state Marc Titinger
` (6 subsequent siblings)
7 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 342bb99..499f035 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -63,6 +63,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A57_1: cpu@1 {
@@ -72,6 +73,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A53_0: cpu@100 {
@@ -81,6 +83,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_1: cpu@101 {
@@ -90,6 +93,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_2: cpu@102 {
@@ -99,6 +103,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_3: cpu@103 {
@@ -108,6 +113,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A57_L2: l2-cache0 {
@@ -119,6 +125,19 @@
};
};
+ pm-domains {
+
+ a57_pd: a57_pd@ {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+
+ a53_pd: a53_pd@ {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+ };
+
pmu {
compatible = "arm,armv8-pmuv3";
interrupts = <GIC_SPI 02 IRQ_TYPE_LEVEL_HIGH>,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
2015-10-06 14:27 ` [RFC v2 1/6] arm64: Juno: declare generic power domains for both clusters Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-08 16:11 ` Lina Iyer
2015-10-06 14:27 ` [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states Marc Titinger
` (5 subsequent siblings)
7 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Devices may register an intermediate retention state into the domain upon
attaching. Currently generic domain would register an array of states upon
init. This patch prepares for later insertion (sort per depth, remove).
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
drivers/base/power/domain.c | 189 +++++++++++++++++++-------------------------
include/linux/pm_domain.h | 18 ++++-
2 files changed, 97 insertions(+), 110 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 3e27a2b..e5f4c00b 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/suspend.h>
#include <linux/export.h>
+#include <linux/sort.h>
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
@@ -50,12 +51,6 @@
__retval; \
})
-#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);
@@ -1364,46 +1359,6 @@ 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;
- }
-
- 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.
@@ -1833,6 +1788,75 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
}
}
+
+/*
+* state depth comparison function.
+*/
+static int state_cmp(const void *a, const void *b)
+{
+ struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
+ struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
+
+ s64 depth_a =
+ state_a->power_on_latency_ns + state_a->power_off_latency_ns;
+ s64 depth_b =
+ state_b->power_on_latency_ns + state_b->power_off_latency_ns;
+
+ return (depth_a > depth_b) ? 0 : -1;
+}
+
+/*
+* TODO: antagonist routine.
+*/
+int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state)
+{
+ int ret = 0;
+ int state_count = genpd->state_count;
+
+ if (IS_ERR_OR_NULL(genpd) || (!state))
+ ret = -EINVAL;
+
+ if (state_count >= GENPD_POWER_STATES_MAX)
+ ret = -ENOMEM;
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ /* to save memory, Name allocation will happen if debug is enabled */
+ genpd->states[state_count].name = kstrndup(state->name,
+ GENPD_MAX_NAME_SIZE,
+ GFP_KERNEL);
+ if (!genpd->states[state_count].name) {
+ pr_err("%s Failed to allocate state '%s' name.\n",
+ genpd->name, state->name);
+ ret = -ENOMEM;
+ }
+#endif
+ genpd_lock(genpd);
+
+ if (!ret) {
+ genpd->states[state_count].power_on_latency_ns =
+ state->power_on_latency_ns;
+ genpd->states[state_count].power_off_latency_ns =
+ state->power_off_latency_ns;
+ genpd->state_count++;
+ }
+
+ /* sort from shallowest to deepest */
+ sort(genpd->states, genpd->state_count,
+ sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
+
+ /* Sanity check for current state index */
+ if (genpd->state_idx >= genpd->state_count) {
+ pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
+ genpd->state_idx = genpd->state_count - 1;
+ }
+
+ genpd_unlock(genpd);
+
+ return ret;
+}
+
+
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@@ -1846,36 +1870,24 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
const struct genpd_power_state *states,
unsigned int state_count, bool is_off)
{
- static const struct genpd_power_state genpd_default_state[] = {
- {
- .name = "OFF",
- .power_off_latency_ns = 0,
- .power_on_latency_ns = 0,
- },
- };
- int ret;
+ int i;
if (IS_ERR_OR_NULL(genpd))
return;
- /* If no states defined, use the default OFF state */
- if (!states || (state_count < 1))
- ret = genpd_alloc_states_data(genpd, genpd_default_state,
- ARRAY_SIZE(genpd_default_state));
- else
- ret = genpd_alloc_states_data(genpd, states, state_count);
-
- if (ret) {
- pr_err("Fail to allocate states for %s\n", genpd->name);
- return;
- }
+ /* simply use an array, we wish to add/remove new retention states
+ from later device init/exit. */
+ memset(genpd->states, 0, GENPD_POWER_STATES_MAX
+ * sizeof(struct genpd_power_state));
- /* Sanity check for initial state */
- if (genpd->state_idx >= genpd->state_count) {
- pr_warn("pm domain %s Invalid initial state.\n",
- genpd->name);
- genpd->state_idx = genpd->state_count - 1;
- }
+ if (!states || !state_count) {
+ /* require a provider for a default state */
+ genpd->state_count = 0;
+ genpd->state_idx = 0;
+ } else
+ for (i = 0; i < state_count; i++)
+ if (pm_genpd_insert_state(genpd, &states[i]))
+ return;
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
@@ -2233,33 +2245,6 @@ 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.
@@ -2398,12 +2383,4 @@ 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 */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 9d37292..8a4eab0 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
struct cpuidle_state *idle_state;
};
+
+/* Arbitrary max number of devices registering a special
+ * retention state with the PD, to keep things simple.
+ */
+#define GENPD_POWER_STATES_MAX 12
+#define GENPD_MAX_NAME_SIZE 40
+
struct genpd_power_state {
char *name;
s64 power_off_latency_ns;
@@ -80,7 +87,8 @@ struct generic_pm_domain {
struct device *dev);
unsigned int flags; /* Bit field of configs for genpd */
- struct genpd_power_state *states;
+ struct genpd_power_state states[GENPD_POWER_STATES_MAX];
+
unsigned int state_count; /* number of states */
unsigned int state_idx; /* state that genpd will go to when off */
@@ -159,10 +167,12 @@ extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
extern int pm_genpd_name_detach_cpuidle(const char *name);
+extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state);
extern void pm_genpd_init(struct generic_pm_domain *genpd,
- struct dev_power_governor *gov,
- const struct genpd_power_state *states,
- unsigned int state_count, bool is_off);
+ struct dev_power_governor *gov,
+ const struct genpd_power_state *states,
+ unsigned int state_count, bool is_off);
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
extern int pm_genpd_name_poweron(const char *domain_name);
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states.
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
2015-10-06 14:27 ` [RFC v2 1/6] arm64: Juno: declare generic power domains for both clusters Marc Titinger
2015-10-06 14:27 ` [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-08 16:27 ` Lina Iyer
2015-10-06 14:27 ` [RFC v2 4/6] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
` (4 subsequent siblings)
7 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
This patch allows cluster-level C-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 c-states.
With Juno, in this example the c-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
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
.../devicetree/bindings/arm/idle-states.txt | 21 ++++-
.../devicetree/bindings/power/power_domain.txt | 29 ++++++
arch/arm64/boot/dts/arm/juno.dts | 10 ++-
drivers/base/power/cpu-pd.c | 5 ++
drivers/base/power/domain.c | 100 +++++++++++++++++++++
include/linux/pm_domain.h | 3 +
6 files changed, 163 insertions(+), 5 deletions(-)
diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt
index a8274ea..18fdeaf 100644
--- a/Documentation/devicetree/bindings/arm/idle-states.txt
+++ b/Documentation/devicetree/bindings/arm/idle-states.txt
@@ -270,7 +270,8 @@ follows:
- compatible
Usage: Required
Value type: <stringlist>
- Definition: Must be "arm,idle-state".
+ Definition: Must be "arm,idle-state",
+ or "arm,power-state" (see section 5)
- local-timer-stop
Usage: See definition
@@ -680,7 +681,23 @@ cpus {
};
===========================================
-5 - References
+5 - power state
+===========================================
+
+Device in a generic power domain may expose an intermediate retention
+state that can be opted to by the domain governor when the last-man
+CPU is powered off. Those power-states will not be entered by the
+cpuidle.ops based on a state index, but instead can be elected by the
+domain governor and entered to by the generic domain.
+
+ - compatible
+ Usage: Required
+ Value type: <stringlist>
+ Definition: Must be "arm,power-state".
+
+
+===========================================
+6 - References
===========================================
[1] ARM Linux Kernel documentation - CPUs bindings
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
index 0f8ed37..d437385 100644
--- a/Documentation/devicetree/bindings/power/power_domain.txt
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -29,6 +29,16 @@ 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.
+ CPU domains: Deep c-states that match a cluster power-off can be delegated to the
+ generic power domain. Device other than CPUs may have register intermediate
+ power states in the same domain. The domain governor can do a good job in
+ electing a power state when the last cpu is powered off as devices in the
+ same genpd may register intermediate states.
+ Devices : a device may register an intermediate c-state matching a memory
+ retention feature for instance.
+
Example:
power: power-controller@12340000 {
@@ -55,6 +65,25 @@ 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>;
+ };
+ };
+
+
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.
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 499f035..cadc5de 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -47,7 +47,7 @@
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
- compatible = "arm,idle-state";
+ compatible = "arm,idle-state","arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
@@ -128,13 +128,17 @@
pm-domains {
a57_pd: a57_pd@ {
- compatible = "arm,pd";
+ /* will have the a53 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@ {
- compatible = "arm,pd";
+ /* will have the a53 platform ARM_PD_METHOD_OF_DECLARE*/
+ compatible = "arm,pd","arm,cortex-a57";
#power-domain-cells = <0>;
+ power-states = <&CLUSTER_SLEEP_0>;
};
};
diff --git a/drivers/base/power/cpu-pd.c b/drivers/base/power/cpu-pd.c
index 5f30025..3c4cb12 100644
--- a/drivers/base/power/cpu-pd.c
+++ b/drivers/base/power/cpu-pd.c
@@ -163,6 +163,11 @@ int of_register_cpu_pm_domain(struct device_node *dn,
pm_genpd_init(pd->genpd, &simple_qos_governor, false);
of_genpd_add_provider_simple(dn, pd->genpd);
+ /* if a cpuidle-state is declared in this domain node, it will
+ * be the domain's job to enter/exit this state, if the device
+ * subdomain constraints are compatible */
+ of_genpd_device_parse_states(dn, &pd->genpd);
+
/* Attach the CPUs to the CPU PM domain */
return of_pm_domain_attach_cpus();
}
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index e5f4c00b..1ca28a2 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2156,6 +2156,104 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd);
}
+static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
+ *genpd_state,
+ const struct of_device_id *matches,
+ struct device_node *state_node)
+{
+ const struct of_device_id *match_id;
+ int err = 0;
+ u32 latency;
+
+ match_id = of_match_node(matches, 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 const struct of_device_id power_state_match[] = {
+ {.compatible = "arm,power-state",
+ },
+};
+
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd)
+{
+ 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;
+
+ err = dt_cpuidle_to_genpd_power_state(&genpd_state,
+ power_state_match,
+ 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->name,
+ GENPD_MAX_NAME_SIZE, GFP_KERNEL);
+ if (!genpd_state.name)
+ err = -ENOMEM;
+#endif
+ of_node_put(state_node);
+ err = pm_genpd_insert_state(genpd, &genpd_state);
+ if (err)
+ break;
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ kfree(genpd_state.name);
+#endif
+ }
+ return err;
+}
+
/**
* genpd_dev_pm_attach - Attach a device to its PM domain using DT.
* @dev: Device to attach.
@@ -2224,6 +2322,8 @@ int genpd_dev_pm_attach(struct device *dev)
return ret;
}
+ of_genpd_device_parse_states(dev->of_node, pd);
+
dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
pm_genpd_poweron(pd);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 8a4eab0..791a8ac 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -302,6 +302,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
struct of_phandle_args *genpdspec,
void *data);
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd);
+
int genpd_dev_pm_attach(struct device *dev);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int __of_genpd_add_provider(struct device_node *np,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v2 4/6] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain.
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
` (2 preceding siblings ...)
2015-10-06 14:27 ` [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
` (3 subsequent siblings)
7 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
This patch checks for irq-safe compatibility in suspend/resume instead of
failing the attach operation early on. Non-cpu devices attaching to an
irq-safe power domain will have to call pm_runtime_irq_safe from their
probe function.
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
drivers/base/power/domain.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 1ca28a2..ebf1299 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -635,6 +635,14 @@ static int pm_genpd_runtime_resume(struct device *dev)
goto out;
}
+ /* The device probe may have missed calling pm_runtime_irq_safe.
+ */
+ if (!dev->power.irq_safe && genpd->irq_safe) {
+ dev_err(dev, "trying to %s a non-irqsafe device in an irq-safe domain\n",
+ __func__);
+ return -EINVAL;
+ }
+
genpd_lock(genpd);
ret = __pm_genpd_poweron(genpd);
genpd_unlock(genpd);
@@ -1376,12 +1384,11 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
return -EINVAL;
- if (genpd->irq_safe && !dev->power.irq_safe) {
- dev_err(dev,
- "PM Domain %s is IRQ safe; device has to IRQ safe.\n",
- genpd->name);
- return -EINVAL;
- }
+ /* Only issue a warning, runtime_irqsafe may be called later on
+ * from the driver probe. */
+ if (genpd->irq_safe && !dev->power.irq_safe)
+ dev_warn(dev,
+ "Devices in an IRQ safe domain have to be IRQ safe.\n");
gpd_data = genpd_alloc_dev_data(dev, genpd, td);
if (IS_ERR(gpd_data))
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states'
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
` (3 preceding siblings ...)
2015-10-06 14:27 ` [RFC v2 4/6] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-11-11 9:10 ` Zhaoyang Huang
2015-10-06 14:27 ` [RFC v2 6/6] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
` (2 subsequent siblings)
7 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Cpuidle now handles c-states and power-states differently. c-states do not decrement
the reference count for the CPUs in the cluster, while power-states i.e.
cluster level states like 'CLUSTER_SLEEP_0' in the case of juno, will.
The 'D1' fake device also registers intermediate power-state,
for experimentation.
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 2 +-
drivers/cpuidle/cpuidle-arm.c | 52 ++++++++++++++++++++++++++++++++--------
2 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index cadc5de..0bb0dd7 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -47,7 +47,7 @@
};
CLUSTER_SLEEP_0: cluster-sleep-0 {
- compatible = "arm,idle-state","arm,power-state";
+ compatible = "arm,power-state";
arm,psci-suspend-param = <0x1010000>;
local-timer-stop;
entry-latency-us = <800>;
diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
index 7c791f9..8dd5dc3 100644
--- a/drivers/cpuidle/cpuidle-arm.c
+++ b/drivers/cpuidle/cpuidle-arm.c
@@ -40,7 +40,6 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int idx)
{
int ret;
- struct device *cpu_dev = get_cpu_device(dev->cpu);
if (!idx) {
cpu_do_idle();
@@ -50,18 +49,49 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
ret = cpu_pm_enter();
if (!ret) {
/*
- * Notify runtime PM as well of this cpu powering down
- * TODO: Merge CPU_PM and runtime PM.
- */
- RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
-
- /*
* Pass idle state index to cpu_suspend which in turn will
* call the CPU ops suspend protocol with idle index as a
* parameter.
*/
arm_cpuidle_suspend(idx);
+ cpu_pm_exit();
+ }
+
+ return ret ? -1 : idx;
+}
+
+/*
+ * arm_enter_power_state - delegate state trasition to genpd
+ *
+ * dev: cpuidle device
+ * drv: cpuidle driver
+ * idx: state index
+ *
+ * Called from the CPUidle framework to delegate a state transition
+ * to the generic domain. This will be a cluster poweroff state
+ * the Domain will chose to actually turn off the cluster based on
+ * the status of other CPUs, and devices and subdomains in the Cluster
+ * domain.
+*/
+static int arm_enter_power_state(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv, int idx)
+{
+ int ret;
+ struct device *cpu_dev = get_cpu_device(dev->cpu);
+
+ BUG_ON(idx == 0);
+
+ ret = cpu_pm_enter();
+ if (!ret) {
+ /*
+ * Notify runtime PM as well of this cpu powering down
+ * TODO: Merge CPU_PM and runtime PM.
+ */
+ RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
+
+ arm_cpuidle_suspend(idx);
+
RCU_NONIDLE(pm_runtime_get_sync(cpu_dev));
cpu_pm_exit();
}
@@ -69,6 +99,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
return ret ? -1 : idx;
}
+
static struct cpuidle_driver arm_idle_driver = {
.name = "arm_idle",
.owner = THIS_MODULE,
@@ -90,9 +121,10 @@ static struct cpuidle_driver arm_idle_driver = {
};
static const struct of_device_id arm_idle_state_match[] __initconst = {
- { .compatible = "arm,idle-state",
- .data = arm_enter_idle_state },
- { },
+ {.compatible = "arm,idle-state",
+ .data = arm_enter_idle_state},
+ {.compatible = "arm,power-state",
+ .data = arm_enter_power_state},
};
/*
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v2 6/6] PM / Domains: add debugfs 'states' and 'timings' seq files
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
` (4 preceding siblings ...)
2015-10-06 14:27 ` [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
@ 2015-10-06 14:27 ` Marc Titinger
2015-10-13 23:10 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Kevin Hilman
2015-10-19 20:58 ` Lina Iyer
7 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-06 14:27 UTC (permalink / raw)
To: khilman, rjw
Cc: lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
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@100 1060 /660 1580 /1940 ,0 (cached stop)
/cpus/cpu@101 1060 /740 1520 /1600 ,0 (cached stop)
/cpus/cpu@102 880 /620 1380 /1780 ,0 (cached stop)
/cpus/cpu@103 1080 /640 1340 /1600 ,0 (cached stop)
a57_pd
/cpus/cpu@0 1160 /740 3280 /1800 ,0 (cached stop)
/cpus/cpu@1 780 /1400 1440 /2080 ,0 (cached stop)
/D1 600 /540 7140 /6420 ,2199460 (cached stop)
Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
---
drivers/base/power/domain.c | 107 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 105 insertions(+), 2 deletions(-)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index ebf1299..5de2112 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -2468,6 +2468,96 @@ 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;
@@ -2477,8 +2567,21 @@ static int __init pm_genpd_debug_init(void)
if (!pm_genpd_debugfs_dir)
return -ENOMEM;
- d = debugfs_create_file("pm_genpd_summary", S_IRUGO,
- pm_genpd_debugfs_dir, NULL, &pm_genpd_summary_fops);
+ d = debugfs_create_file("summary", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_summary_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("states", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_states_fops);
+ if (!d)
+ return -ENOMEM;
+
+ d = debugfs_create_file("timings", S_IRUGO,
+ pm_genpd_debugfs_dir, NULL,
+ &pm_genpd_timing_fops);
if (!d)
return -ENOMEM;
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* Re: [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-06 14:27 ` [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state Marc Titinger
@ 2015-10-08 16:11 ` Lina Iyer
2015-10-09 9:39 ` Marc Titinger
0 siblings, 1 reply; 39+ messages in thread
From: Lina Iyer @ 2015-10-08 16:11 UTC (permalink / raw)
To: Marc Titinger
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Hi Marc,
Thanks for rebasing on top of my latest series.
On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>Devices may register an intermediate retention state into the domain upon
>
I may agree with the usability of dynamic adding a state to the domain,
but I dont see why a device attaching to a domain should bring about a
new domain state.
A domain should define its power states, independent of the devices that
may attach. The way I see it, devices have their own idle states and
domains have their own. I do see a relationship between possible domain
states depending on the states of the individual devices in the domain.
For ex, a CPU domain can only be in a retention state (low voltage,
memory retained), if its CPU devices are in retention state, i.e, the
domain cannot be powered off; alternately, the domain may be in
retention or power down if the CPU devices are in power down state.
Could you elaborate on why this is a need?
Thanks,
Lina
>attaching. Currently generic domain would register an array of states upon
>init. This patch prepares for later insertion (sort per depth, remove).
>
>Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>---
> drivers/base/power/domain.c | 189 +++++++++++++++++++-------------------------
> include/linux/pm_domain.h | 18 ++++-
> 2 files changed, 97 insertions(+), 110 deletions(-)
>
>diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>index 3e27a2b..e5f4c00b 100644
>--- a/drivers/base/power/domain.c
>+++ b/drivers/base/power/domain.c
>@@ -19,6 +19,7 @@
> #include <linux/sched.h>
> #include <linux/suspend.h>
> #include <linux/export.h>
>+#include <linux/sort.h>
>
> #define GENPD_RETRY_MAX_MS 250 /* Approximate */
>
>@@ -50,12 +51,6 @@
> __retval; \
> })
>
>-#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);
>
>@@ -1364,46 +1359,6 @@ 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;
>- }
>-
>- 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.
>@@ -1833,6 +1788,75 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
> }
> }
>
>+
>+/*
>+* state depth comparison function.
>+*/
>+static int state_cmp(const void *a, const void *b)
>+{
>+ struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
>+ struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
>+
>+ s64 depth_a =
>+ state_a->power_on_latency_ns + state_a->power_off_latency_ns;
>+ s64 depth_b =
>+ state_b->power_on_latency_ns + state_b->power_off_latency_ns;
>+
>+ return (depth_a > depth_b) ? 0 : -1;
>+}
>+
>+/*
>+* TODO: antagonist routine.
>+*/
>+int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>+ const struct genpd_power_state *state)
>+{
>+ int ret = 0;
>+ int state_count = genpd->state_count;
>+
>+ if (IS_ERR_OR_NULL(genpd) || (!state))
>+ ret = -EINVAL;
>+
>+ if (state_count >= GENPD_POWER_STATES_MAX)
>+ ret = -ENOMEM;
>+
>+#ifdef CONFIG_PM_ADVANCED_DEBUG
>+ /* to save memory, Name allocation will happen if debug is enabled */
>+ genpd->states[state_count].name = kstrndup(state->name,
>+ GENPD_MAX_NAME_SIZE,
>+ GFP_KERNEL);
>+ if (!genpd->states[state_count].name) {
>+ pr_err("%s Failed to allocate state '%s' name.\n",
>+ genpd->name, state->name);
>+ ret = -ENOMEM;
>+ }
>+#endif
>+ genpd_lock(genpd);
>+
>+ if (!ret) {
>+ genpd->states[state_count].power_on_latency_ns =
>+ state->power_on_latency_ns;
>+ genpd->states[state_count].power_off_latency_ns =
>+ state->power_off_latency_ns;
>+ genpd->state_count++;
>+ }
>+
>+ /* sort from shallowest to deepest */
>+ sort(genpd->states, genpd->state_count,
>+ sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
>+
>+ /* Sanity check for current state index */
>+ if (genpd->state_idx >= genpd->state_count) {
>+ pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
>+ genpd->state_idx = genpd->state_count - 1;
>+ }
>+
>+ genpd_unlock(genpd);
>+
>+ return ret;
>+}
>+
>+
> /**
> * pm_genpd_init - Initialize a generic I/O PM domain object.
> * @genpd: PM domain object to initialize.
>@@ -1846,36 +1870,24 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
> const struct genpd_power_state *states,
> unsigned int state_count, bool is_off)
> {
>- static const struct genpd_power_state genpd_default_state[] = {
>- {
>- .name = "OFF",
>- .power_off_latency_ns = 0,
>- .power_on_latency_ns = 0,
>- },
>- };
>- int ret;
>+ int i;
>
> if (IS_ERR_OR_NULL(genpd))
> return;
>
>- /* If no states defined, use the default OFF state */
>- if (!states || (state_count < 1))
>- ret = genpd_alloc_states_data(genpd, genpd_default_state,
>- ARRAY_SIZE(genpd_default_state));
>- else
>- ret = genpd_alloc_states_data(genpd, states, state_count);
>-
>- if (ret) {
>- pr_err("Fail to allocate states for %s\n", genpd->name);
>- return;
>- }
>+ /* simply use an array, we wish to add/remove new retention states
>+ from later device init/exit. */
>+ memset(genpd->states, 0, GENPD_POWER_STATES_MAX
>+ * sizeof(struct genpd_power_state));
>
>- /* Sanity check for initial state */
>- if (genpd->state_idx >= genpd->state_count) {
>- pr_warn("pm domain %s Invalid initial state.\n",
>- genpd->name);
>- genpd->state_idx = genpd->state_count - 1;
>- }
>+ if (!states || !state_count) {
>+ /* require a provider for a default state */
>+ genpd->state_count = 0;
>+ genpd->state_idx = 0;
>+ } else
>+ for (i = 0; i < state_count; i++)
>+ if (pm_genpd_insert_state(genpd, &states[i]))
>+ return;
>
> INIT_LIST_HEAD(&genpd->master_links);
> INIT_LIST_HEAD(&genpd->slave_links);
>@@ -2233,33 +2245,6 @@ 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.
>@@ -2398,12 +2383,4 @@ 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 */
>diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>index 9d37292..8a4eab0 100644
>--- a/include/linux/pm_domain.h
>+++ b/include/linux/pm_domain.h
>@@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
> struct cpuidle_state *idle_state;
> };
>
>+
>+/* Arbitrary max number of devices registering a special
>+ * retention state with the PD, to keep things simple.
>+ */
>+#define GENPD_POWER_STATES_MAX 12
>+#define GENPD_MAX_NAME_SIZE 40
>+
> struct genpd_power_state {
> char *name;
> s64 power_off_latency_ns;
>@@ -80,7 +87,8 @@ struct generic_pm_domain {
> struct device *dev);
> unsigned int flags; /* Bit field of configs for genpd */
>
>- struct genpd_power_state *states;
>+ struct genpd_power_state states[GENPD_POWER_STATES_MAX];
>+
> unsigned int state_count; /* number of states */
> unsigned int state_idx; /* state that genpd will go to when off */
>
>@@ -159,10 +167,12 @@ extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
> extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
> extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
> extern int pm_genpd_name_detach_cpuidle(const char *name);
>+extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>+ const struct genpd_power_state *state);
> extern void pm_genpd_init(struct generic_pm_domain *genpd,
>- struct dev_power_governor *gov,
>- const struct genpd_power_state *states,
>- unsigned int state_count, bool is_off);
>+ struct dev_power_governor *gov,
>+ const struct genpd_power_state *states,
>+ unsigned int state_count, bool is_off);
>
> extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
> extern int pm_genpd_name_poweron(const char *domain_name);
>--
>1.9.1
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states.
2015-10-06 14:27 ` [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states Marc Titinger
@ 2015-10-08 16:27 ` Lina Iyer
2015-10-09 10:04 ` Marc Titinger
0 siblings, 1 reply; 39+ messages in thread
From: Lina Iyer @ 2015-10-08 16:27 UTC (permalink / raw)
To: Marc Titinger
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>This patch allows cluster-level C-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 c-states.
>
A domain power state as depicted in the DT is no different than the CPU
idle state. I think you can achieve this without adding another
compatible - arm,power-state.
I think I am still at loss trying to understand why a device is
populating the domain's power states.
>With Juno, in this example the c-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
>
>Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>---
> .../devicetree/bindings/arm/idle-states.txt | 21 ++++-
> .../devicetree/bindings/power/power_domain.txt | 29 ++++++
> arch/arm64/boot/dts/arm/juno.dts | 10 ++-
> drivers/base/power/cpu-pd.c | 5 ++
> drivers/base/power/domain.c | 100 +++++++++++++++++++++
> include/linux/pm_domain.h | 3 +
> 6 files changed, 163 insertions(+), 5 deletions(-)
>
>diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt b/Documentation/devicetree/bindings/arm/idle-states.txt
>index a8274ea..18fdeaf 100644
>--- a/Documentation/devicetree/bindings/arm/idle-states.txt
>+++ b/Documentation/devicetree/bindings/arm/idle-states.txt
>@@ -270,7 +270,8 @@ follows:
> - compatible
> Usage: Required
> Value type: <stringlist>
>- Definition: Must be "arm,idle-state".
>+ Definition: Must be "arm,idle-state",
>+ or "arm,power-state" (see section 5)
>
> - local-timer-stop
> Usage: See definition
>@@ -680,7 +681,23 @@ cpus {
> };
>
> ===========================================
>-5 - References
>+5 - power state
>+===========================================
>+
>+Device in a generic power domain may expose an intermediate retention
>+state that can be opted to by the domain governor when the last-man
>+CPU is powered off. Those power-states will not be entered by the
>+cpuidle.ops based on a state index, but instead can be elected by the
>+domain governor and entered to by the generic domain.
>+
Agreed.
>+ - compatible
>+ Usage: Required
>+ Value type: <stringlist>
>+ Definition: Must be "arm,power-state".
>+
>+
>+===========================================
>+6 - References
> ===========================================
>
> [1] ARM Linux Kernel documentation - CPUs bindings
>diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
>index 0f8ed37..d437385 100644
>--- a/Documentation/devicetree/bindings/power/power_domain.txt
>+++ b/Documentation/devicetree/bindings/power/power_domain.txt
>@@ -29,6 +29,16 @@ 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.
>+ CPU domains: Deep c-states that match a cluster power-off can be delegated to the
>+ generic power domain. Device other than CPUs may have register intermediate
>+ power states in the same domain. The domain governor can do a good job in
>+ electing a power state when the last cpu is powered off as devices in the
>+ same genpd may register intermediate states.
>
Devices may enable a certain domain state, but should not be defining
the domain state. The domain its state and the domain governor may
choose to enter that state on a vote from its devices.
>+ Devices : a device may register an intermediate c-state matching a memory
>+ retention feature for instance.
>
This point onwards is where I need clarity.
Thanks,
Lina
>+
> Example:
>
> power: power-controller@12340000 {
>@@ -55,6 +65,25 @@ 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>;
>+ };
>+ };
>+
>+
> 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.
>diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
>index 499f035..cadc5de 100644
>--- a/arch/arm64/boot/dts/arm/juno.dts
>+++ b/arch/arm64/boot/dts/arm/juno.dts
>@@ -47,7 +47,7 @@
> };
>
> CLUSTER_SLEEP_0: cluster-sleep-0 {
>- compatible = "arm,idle-state";
>+ compatible = "arm,idle-state","arm,power-state";
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
>@@ -128,13 +128,17 @@
> pm-domains {
>
> a57_pd: a57_pd@ {
>- compatible = "arm,pd";
>+ /* will have the a53 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@ {
>- compatible = "arm,pd";
>+ /* will have the a53 platform ARM_PD_METHOD_OF_DECLARE*/
>+ compatible = "arm,pd","arm,cortex-a57";
> #power-domain-cells = <0>;
>+ power-states = <&CLUSTER_SLEEP_0>;
> };
> };
>
>diff --git a/drivers/base/power/cpu-pd.c b/drivers/base/power/cpu-pd.c
>index 5f30025..3c4cb12 100644
>--- a/drivers/base/power/cpu-pd.c
>+++ b/drivers/base/power/cpu-pd.c
>@@ -163,6 +163,11 @@ int of_register_cpu_pm_domain(struct device_node *dn,
> pm_genpd_init(pd->genpd, &simple_qos_governor, false);
> of_genpd_add_provider_simple(dn, pd->genpd);
>
>+ /* if a cpuidle-state is declared in this domain node, it will
>+ * be the domain's job to enter/exit this state, if the device
>+ * subdomain constraints are compatible */
>+ of_genpd_device_parse_states(dn, &pd->genpd);
>+
> /* Attach the CPUs to the CPU PM domain */
> return of_pm_domain_attach_cpus();
> }
>diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>index e5f4c00b..1ca28a2 100644
>--- a/drivers/base/power/domain.c
>+++ b/drivers/base/power/domain.c
>@@ -2156,6 +2156,104 @@ static void genpd_dev_pm_sync(struct device *dev)
> genpd_queue_power_off_work(pd);
> }
>
>+static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
>+ *genpd_state,
>+ const struct of_device_id *matches,
>+ struct device_node *state_node)
>+{
>+ const struct of_device_id *match_id;
>+ int err = 0;
>+ u32 latency;
>+
>+ match_id = of_match_node(matches, 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 const struct of_device_id power_state_match[] = {
>+ {.compatible = "arm,power-state",
>+ },
>+};
>+
>+int of_genpd_device_parse_states(struct device_node *np,
>+ struct generic_pm_domain *genpd)
>+{
>+ 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;
>+
>+ err = dt_cpuidle_to_genpd_power_state(&genpd_state,
>+ power_state_match,
>+ 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->name,
>+ GENPD_MAX_NAME_SIZE, GFP_KERNEL);
>+ if (!genpd_state.name)
>+ err = -ENOMEM;
>+#endif
>+ of_node_put(state_node);
>+ err = pm_genpd_insert_state(genpd, &genpd_state);
>+ if (err)
>+ break;
>+#ifdef CONFIG_PM_ADVANCED_DEBUG
>+ kfree(genpd_state.name);
>+#endif
>+ }
>+ return err;
>+}
>+
> /**
> * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
> * @dev: Device to attach.
>@@ -2224,6 +2322,8 @@ int genpd_dev_pm_attach(struct device *dev)
> return ret;
> }
>
>+ of_genpd_device_parse_states(dev->of_node, pd);
>+
> dev->pm_domain->detach = genpd_dev_pm_detach;
> dev->pm_domain->sync = genpd_dev_pm_sync;
> pm_genpd_poweron(pd);
>diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>index 8a4eab0..791a8ac 100644
>--- a/include/linux/pm_domain.h
>+++ b/include/linux/pm_domain.h
>@@ -302,6 +302,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
> struct of_phandle_args *genpdspec,
> void *data);
>
>+int of_genpd_device_parse_states(struct device_node *np,
>+ struct generic_pm_domain *genpd);
>+
> int genpd_dev_pm_attach(struct device *dev);
> #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
> static inline int __of_genpd_add_provider(struct device_node *np,
>--
>1.9.1
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-08 16:11 ` Lina Iyer
@ 2015-10-09 9:39 ` Marc Titinger
2015-10-09 18:22 ` Lina Iyer
0 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-09 9:39 UTC (permalink / raw)
To: Lina Iyer
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On 08/10/2015 18:11, Lina Iyer wrote:
> Hi Marc,
>
> Thanks for rebasing on top of my latest series.
>
> On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>> Devices may register an intermediate retention state into the domain upon
>>
> I may agree with the usability of dynamic adding a state to the domain,
> but I dont see why a device attaching to a domain should bring about a
> new domain state.
Hi Lina,
thanks a lot for taking the time to look into this. The initial
discussion behind it was about to see how a device like a PMU, FPU,
cache, or a Healthcheck IP in the same power domain than CPUs, with
special retention states can be handled in a way 'unified' with CPUs.
Also I admit it is partly an attempt from us to create something useful
out of the "collision" of Axel's multiple states and your
CPUs-as-generic-power-domain-devices, hence the RFC!
Looking at Juno for instance, she currently has a platform-initiated
mode implemented in the arm-trusted-firmware through psci as a
cpuidle-driver. the menu governor will select a possibly deep c-state,
but the last-man CPU and actual power state is known to ATF. Similarly
my idea was to have a genpd-initiated mode so to say, where the actual
power state results from the cpu-domain's governor choice based on
possible retention states, and their latency.
A Health-check IP or Cache will not (to my current knowledge) have a
driver calling runtime_put, but may have a retention state "D1_RET" with
a off/on latency between CPU_SLEEP and CLUSTER_SLEEP, so that
CLUSTER_SLEEP may be ruled out by the governor, but D1_RET is ok given
the time slot available. Some platform code can be called so that the
cluster goes to D1_RET in that case, upon the last-man CPU
waiting-for-interrupt. Note that in the case of a Health-Check HIP, the
state my actually be a working state (all CPUs power down, and time slot
OK for sampling stuff).
>
> A domain should define its power states, independent of the devices that
> may attach. The way I see it, devices have their own idle states and
> domains have their own. I do see a relationship between possible domain
> states depending on the states of the individual devices in the domain.
> For ex, a CPU domain can only be in a retention state (low voltage,
> memory retained), if its CPU devices are in retention state, i.e, the
> domain cannot be powered off; alternately, the domain may be in
> retention or power down if the CPU devices are in power down state.
>
> Could you elaborate on why this is a need?
Well, it may not be a need TBH, it is an attempt to have cluster-level
devices act like hotplugged CPUs but with heterogeneous c-states and
latencies. I hope it makes some sense :)
Thanks,
Marc.
>
> Thanks,
> Lina
>
>> attaching. Currently generic domain would register an array of states
>> upon
>> init. This patch prepares for later insertion (sort per depth, remove).
>>
>> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>> ---
>> drivers/base/power/domain.c | 189
>> +++++++++++++++++++-------------------------
>> include/linux/pm_domain.h | 18 ++++-
>> 2 files changed, 97 insertions(+), 110 deletions(-)
>>
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index 3e27a2b..e5f4c00b 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -19,6 +19,7 @@
>> #include <linux/sched.h>
>> #include <linux/suspend.h>
>> #include <linux/export.h>
>> +#include <linux/sort.h>
>>
>> #define GENPD_RETRY_MAX_MS 250 /* Approximate */
>>
>> @@ -50,12 +51,6 @@
>> __retval; \
>> })
>>
>> -#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);
>>
>> @@ -1364,46 +1359,6 @@ 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;
>> - }
>> -
>> - 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.
>> @@ -1833,6 +1788,75 @@ static void genpd_lock_init(struct
>> generic_pm_domain *genpd)
>> }
>> }
>>
>> +
>> +/*
>> +* state depth comparison function.
>> +*/
>> +static int state_cmp(const void *a, const void *b)
>> +{
>> + struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
>> + struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
>> +
>> + s64 depth_a =
>> + state_a->power_on_latency_ns + state_a->power_off_latency_ns;
>> + s64 depth_b =
>> + state_b->power_on_latency_ns + state_b->power_off_latency_ns;
>> +
>> + return (depth_a > depth_b) ? 0 : -1;
>> +}
>> +
>> +/*
>> +* TODO: antagonist routine.
>> +*/
>> +int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>> + const struct genpd_power_state *state)
>> +{
>> + int ret = 0;
>> + int state_count = genpd->state_count;
>> +
>> + if (IS_ERR_OR_NULL(genpd) || (!state))
>> + ret = -EINVAL;
>> +
>> + if (state_count >= GENPD_POWER_STATES_MAX)
>> + ret = -ENOMEM;
>> +
>> +#ifdef CONFIG_PM_ADVANCED_DEBUG
>> + /* to save memory, Name allocation will happen if debug is
>> enabled */
>> + genpd->states[state_count].name = kstrndup(state->name,
>> + GENPD_MAX_NAME_SIZE,
>> + GFP_KERNEL);
>> + if (!genpd->states[state_count].name) {
>> + pr_err("%s Failed to allocate state '%s' name.\n",
>> + genpd->name, state->name);
>> + ret = -ENOMEM;
>> + }
>> +#endif
>> + genpd_lock(genpd);
>> +
>> + if (!ret) {
>> + genpd->states[state_count].power_on_latency_ns =
>> + state->power_on_latency_ns;
>> + genpd->states[state_count].power_off_latency_ns =
>> + state->power_off_latency_ns;
>> + genpd->state_count++;
>> + }
>> +
>> + /* sort from shallowest to deepest */
>> + sort(genpd->states, genpd->state_count,
>> + sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
>> +
>> + /* Sanity check for current state index */
>> + if (genpd->state_idx >= genpd->state_count) {
>> + pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
>> + genpd->state_idx = genpd->state_count - 1;
>> + }
>> +
>> + genpd_unlock(genpd);
>> +
>> + return ret;
>> +}
>> +
>> +
>> /**
>> * pm_genpd_init - Initialize a generic I/O PM domain object.
>> * @genpd: PM domain object to initialize.
>> @@ -1846,36 +1870,24 @@ void pm_genpd_init(struct generic_pm_domain
>> *genpd,
>> const struct genpd_power_state *states,
>> unsigned int state_count, bool is_off)
>> {
>> - static const struct genpd_power_state genpd_default_state[] = {
>> - {
>> - .name = "OFF",
>> - .power_off_latency_ns = 0,
>> - .power_on_latency_ns = 0,
>> - },
>> - };
>> - int ret;
>> + int i;
>>
>> if (IS_ERR_OR_NULL(genpd))
>> return;
>>
>> - /* If no states defined, use the default OFF state */
>> - if (!states || (state_count < 1))
>> - ret = genpd_alloc_states_data(genpd, genpd_default_state,
>> - ARRAY_SIZE(genpd_default_state));
>> - else
>> - ret = genpd_alloc_states_data(genpd, states, state_count);
>> -
>> - if (ret) {
>> - pr_err("Fail to allocate states for %s\n", genpd->name);
>> - return;
>> - }
>> + /* simply use an array, we wish to add/remove new retention states
>> + from later device init/exit. */
>> + memset(genpd->states, 0, GENPD_POWER_STATES_MAX
>> + * sizeof(struct genpd_power_state));
>>
>> - /* Sanity check for initial state */
>> - if (genpd->state_idx >= genpd->state_count) {
>> - pr_warn("pm domain %s Invalid initial state.\n",
>> - genpd->name);
>> - genpd->state_idx = genpd->state_count - 1;
>> - }
>> + if (!states || !state_count) {
>> + /* require a provider for a default state */
>> + genpd->state_count = 0;
>> + genpd->state_idx = 0;
>> + } else
>> + for (i = 0; i < state_count; i++)
>> + if (pm_genpd_insert_state(genpd, &states[i]))
>> + return;
>>
>> INIT_LIST_HEAD(&genpd->master_links);
>> INIT_LIST_HEAD(&genpd->slave_links);
>> @@ -2233,33 +2245,6 @@ 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.
>> @@ -2398,12 +2383,4 @@ 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 */
>> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>> index 9d37292..8a4eab0 100644
>> --- a/include/linux/pm_domain.h
>> +++ b/include/linux/pm_domain.h
>> @@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
>> struct cpuidle_state *idle_state;
>> };
>>
>> +
>> +/* Arbitrary max number of devices registering a special
>> + * retention state with the PD, to keep things simple.
>> + */
>> +#define GENPD_POWER_STATES_MAX 12
>> +#define GENPD_MAX_NAME_SIZE 40
>> +
>> struct genpd_power_state {
>> char *name;
>> s64 power_off_latency_ns;
>> @@ -80,7 +87,8 @@ struct generic_pm_domain {
>> struct device *dev);
>> unsigned int flags; /* Bit field of configs for genpd */
>>
>> - struct genpd_power_state *states;
>> + struct genpd_power_state states[GENPD_POWER_STATES_MAX];
>> +
>> unsigned int state_count; /* number of states */
>> unsigned int state_idx; /* state that genpd will go to when off */
>>
>> @@ -159,10 +167,12 @@ extern int pm_genpd_attach_cpuidle(struct
>> generic_pm_domain *genpd, int state);
>> extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
>> extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
>> extern int pm_genpd_name_detach_cpuidle(const char *name);
>> +extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>> + const struct genpd_power_state *state);
>> extern void pm_genpd_init(struct generic_pm_domain *genpd,
>> - struct dev_power_governor *gov,
>> - const struct genpd_power_state *states,
>> - unsigned int state_count, bool is_off);
>> + struct dev_power_governor *gov,
>> + const struct genpd_power_state *states,
>> + unsigned int state_count, bool is_off);
>>
>> extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
>> extern int pm_genpd_name_poweron(const char *domain_name);
>> --
>> 1.9.1
>>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states.
2015-10-08 16:27 ` Lina Iyer
@ 2015-10-09 10:04 ` Marc Titinger
0 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-09 10:04 UTC (permalink / raw)
To: Lina Iyer
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On 08/10/2015 18:27, Lina Iyer wrote:
> On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>> This patch allows cluster-level C-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 c-states.
>>
> A domain power state as depicted in the DT is no different than the CPU
> idle state. I think you can achieve this without adding another
> compatible - arm,power-state.
>
yes, thats'a bit hacky, I was facing the genpd_lock'ing issue I believe
you mentioned recently and lead to your simplification work in the case
of CPUs. the power-state discrimination was to allow cpu-idle to call
runtime_put/get only for those states where genpd is to call the
platform code and select the optimal c-state. Eventually this
discrimination would be useless. Alternatively I've been wondering if we
could have one domain (and one lock) per CPU, and have a parent domain
for the cluster.
> I think I am still at loss trying to understand why a device is
> populating the domain's power states.
>
>> With Juno, in this example the c-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
>>
>> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>> ---
>> .../devicetree/bindings/arm/idle-states.txt | 21 ++++-
>> .../devicetree/bindings/power/power_domain.txt | 29 ++++++
>> arch/arm64/boot/dts/arm/juno.dts | 10 ++-
>> drivers/base/power/cpu-pd.c | 5 ++
>> drivers/base/power/domain.c | 100
>> +++++++++++++++++++++
>> include/linux/pm_domain.h | 3 +
>> 6 files changed, 163 insertions(+), 5 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/idle-states.txt
>> b/Documentation/devicetree/bindings/arm/idle-states.txt
>> index a8274ea..18fdeaf 100644
>> --- a/Documentation/devicetree/bindings/arm/idle-states.txt
>> +++ b/Documentation/devicetree/bindings/arm/idle-states.txt
>> @@ -270,7 +270,8 @@ follows:
>> - compatible
>> Usage: Required
>> Value type: <stringlist>
>> - Definition: Must be "arm,idle-state".
>> + Definition: Must be "arm,idle-state",
>> + or "arm,power-state" (see section 5)
>>
>> - local-timer-stop
>> Usage: See definition
>> @@ -680,7 +681,23 @@ cpus {
>> };
>>
>> ===========================================
>> -5 - References
>> +5 - power state
>> +===========================================
>> +
>> +Device in a generic power domain may expose an intermediate retention
>> +state that can be opted to by the domain governor when the last-man
>> +CPU is powered off. Those power-states will not be entered by the
>> +cpuidle.ops based on a state index, but instead can be elected by the
>> +domain governor and entered to by the generic domain.
>> +
> Agreed.
>
>> + - compatible
>> + Usage: Required
>> + Value type: <stringlist>
>> + Definition: Must be "arm,power-state".
>> +
>> +
>> +===========================================
>> +6 - References
>> ===========================================
>>
>> [1] ARM Linux Kernel documentation - CPUs bindings
>> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt
>> b/Documentation/devicetree/bindings/power/power_domain.txt
>> index 0f8ed37..d437385 100644
>> --- a/Documentation/devicetree/bindings/power/power_domain.txt
>> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
>> @@ -29,6 +29,16 @@ 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.
>> + CPU domains: Deep c-states that match a cluster power-off can be
>> delegated to the
>> + generic power domain. Device other than CPUs may have register
>> intermediate
>> + power states in the same domain. The domain governor can do a good
>> job in
>> + electing a power state when the last cpu is powered off as devices
>> in the
>> + same genpd may register intermediate states.
>>
> Devices may enable a certain domain state, but should not be defining
> the domain state. The domain its state and the domain governor may
> choose to enter that state on a vote from its devices.
>
>> + Devices : a device may register an intermediate c-state matching a
>> memory
>> + retention feature for instance.
>>
> This point onwards is where I need clarity.
>
> Thanks,
> Lina
>
>> +
>> Example:
>>
>> power: power-controller@12340000 {
>> @@ -55,6 +65,25 @@ 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>;
>> + };
>> + };
>> +
>> +
>> 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.
>> diff --git a/arch/arm64/boot/dts/arm/juno.dts
>> b/arch/arm64/boot/dts/arm/juno.dts
>> index 499f035..cadc5de 100644
>> --- a/arch/arm64/boot/dts/arm/juno.dts
>> +++ b/arch/arm64/boot/dts/arm/juno.dts
>> @@ -47,7 +47,7 @@
>> };
>>
>> CLUSTER_SLEEP_0: cluster-sleep-0 {
>> - compatible = "arm,idle-state";
>> + compatible = "arm,idle-state","arm,power-state";
>> arm,psci-suspend-param = <0x1010000>;
>> local-timer-stop;
>> entry-latency-us = <800>;
>> @@ -128,13 +128,17 @@
>> pm-domains {
>>
>> a57_pd: a57_pd@ {
>> - compatible = "arm,pd";
>> + /* will have the a53 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@ {
>> - compatible = "arm,pd";
>> + /* will have the a53 platform ARM_PD_METHOD_OF_DECLARE*/
>> + compatible = "arm,pd","arm,cortex-a57";
>> #power-domain-cells = <0>;
>> + power-states = <&CLUSTER_SLEEP_0>;
>> };
>> };
>>
>> diff --git a/drivers/base/power/cpu-pd.c b/drivers/base/power/cpu-pd.c
>> index 5f30025..3c4cb12 100644
>> --- a/drivers/base/power/cpu-pd.c
>> +++ b/drivers/base/power/cpu-pd.c
>> @@ -163,6 +163,11 @@ int of_register_cpu_pm_domain(struct device_node
>> *dn,
>> pm_genpd_init(pd->genpd, &simple_qos_governor, false);
>> of_genpd_add_provider_simple(dn, pd->genpd);
>>
>> + /* if a cpuidle-state is declared in this domain node, it will
>> + * be the domain's job to enter/exit this state, if the device
>> + * subdomain constraints are compatible */
>> + of_genpd_device_parse_states(dn, &pd->genpd);
>> +
>> /* Attach the CPUs to the CPU PM domain */
>> return of_pm_domain_attach_cpus();
>> }
>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>> index e5f4c00b..1ca28a2 100644
>> --- a/drivers/base/power/domain.c
>> +++ b/drivers/base/power/domain.c
>> @@ -2156,6 +2156,104 @@ static void genpd_dev_pm_sync(struct device *dev)
>> genpd_queue_power_off_work(pd);
>> }
>>
>> +static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
>> + *genpd_state,
>> + const struct of_device_id *matches,
>> + struct device_node *state_node)
>> +{
>> + const struct of_device_id *match_id;
>> + int err = 0;
>> + u32 latency;
>> +
>> + match_id = of_match_node(matches, 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 const struct of_device_id power_state_match[] = {
>> + {.compatible = "arm,power-state",
>> + },
>> +};
>> +
>> +int of_genpd_device_parse_states(struct device_node *np,
>> + struct generic_pm_domain *genpd)
>> +{
>> + 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;
>> +
>> + err = dt_cpuidle_to_genpd_power_state(&genpd_state,
>> + power_state_match,
>> + 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->name,
>> + GENPD_MAX_NAME_SIZE, GFP_KERNEL);
>> + if (!genpd_state.name)
>> + err = -ENOMEM;
>> +#endif
>> + of_node_put(state_node);
>> + err = pm_genpd_insert_state(genpd, &genpd_state);
>> + if (err)
>> + break;
>> +#ifdef CONFIG_PM_ADVANCED_DEBUG
>> + kfree(genpd_state.name);
>> +#endif
>> + }
>> + return err;
>> +}
>> +
>> /**
>> * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
>> * @dev: Device to attach.
>> @@ -2224,6 +2322,8 @@ int genpd_dev_pm_attach(struct device *dev)
>> return ret;
>> }
>>
>> + of_genpd_device_parse_states(dev->of_node, pd);
>> +
>> dev->pm_domain->detach = genpd_dev_pm_detach;
>> dev->pm_domain->sync = genpd_dev_pm_sync;
>> pm_genpd_poweron(pd);
>> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>> index 8a4eab0..791a8ac 100644
>> --- a/include/linux/pm_domain.h
>> +++ b/include/linux/pm_domain.h
>> @@ -302,6 +302,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
>> struct of_phandle_args *genpdspec,
>> void *data);
>>
>> +int of_genpd_device_parse_states(struct device_node *np,
>> + struct generic_pm_domain *genpd);
>> +
>> int genpd_dev_pm_attach(struct device *dev);
>> #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
>> static inline int __of_genpd_add_provider(struct device_node *np,
>> --
>> 1.9.1
>>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-09 9:39 ` Marc Titinger
@ 2015-10-09 18:22 ` Lina Iyer
2015-10-13 10:29 ` Marc Titinger
0 siblings, 1 reply; 39+ messages in thread
From: Lina Iyer @ 2015-10-09 18:22 UTC (permalink / raw)
To: Marc Titinger
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On Fri, Oct 09 2015 at 03:39 -0600, Marc Titinger wrote:
>On 08/10/2015 18:11, Lina Iyer wrote:
>>Hi Marc,
>>
>>Thanks for rebasing on top of my latest series.
>>
>>On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>>>Devices may register an intermediate retention state into the domain upon
>>>
>>I may agree with the usability of dynamic adding a state to the domain,
>>but I dont see why a device attaching to a domain should bring about a
>>new domain state.
>
>Hi Lina,
>
>thanks a lot for taking the time to look into this. The initial
>discussion behind it was about to see how a device like a PMU, FPU,
>cache, or a Healthcheck IP in the same power domain than CPUs, with
>special retention states can be handled in a way 'unified' with CPUs.
>Also I admit it is partly an attempt from us to create something
>useful out of the "collision" of Axel's multiple states and your
>CPUs-as-generic-power-domain-devices, hence the RFC!
>
>Looking at Juno for instance, she currently has a platform-initiated
>mode implemented in the arm-trusted-firmware through psci as a
>cpuidle-driver. the menu governor will select a possibly deep c-state,
>but the last-man CPU and actual power state is known to ATF. Similarly
>my idea was to have a genpd-initiated mode so to say, where the actual
>power state results from the cpu-domain's governor choice based on
>possible retention states, and their latency.
>
Okay, I must admit that my ideas are quite partial to OS-initiated PSCI
(v1.0 onwards). So you have C-States that allow domains to enter idle as
well. Fair.
>A Health-check IP or Cache will not (to my current knowledge) have a
>driver calling runtime_put, but may have a retention state "D1_RET"
>with a off/on latency between CPU_SLEEP and CLUSTER_SLEEP, so that
>CLUSTER_SLEEP may be ruled out by the governor, but D1_RET is ok given
>the time slot available.
>
A couple of questions here.
You say there is no driver for HIP, is there a device for it atleast?
How is the CPU/Domain going to know if the HIP is running or not?
To me this seems like you want to set a QoS on the PM domain here.
>Some platform code can be called so that the
>cluster goes to D1_RET in that case, upon the last-man CPU
>waiting-for-interrupt. Note that in the case of a Health-Check HIP,
>the state my actually be a working state (all CPUs power down, and
>time slot OK for sampling stuff).
>
Building on my previous statement, if you have a QoS for a domain and a
domain governor, it could consider the QoS requirement and choose to do
retention. However that needs a driver or some entity that know the HIP
is requesting a D1_RET mode only.
>>
>>A domain should define its power states, independent of the devices that
>>may attach. The way I see it, devices have their own idle states and
>>domains have their own. I do see a relationship between possible domain
>>states depending on the states of the individual devices in the domain.
>>For ex, a CPU domain can only be in a retention state (low voltage,
>>memory retained), if its CPU devices are in retention state, i.e, the
>>domain cannot be powered off; alternately, the domain may be in
>>retention or power down if the CPU devices are in power down state.
>>
>>Could you elaborate on why this is a need?
>
>Well, it may not be a need TBH, it is an attempt to have cluster-level
>devices act like hotplugged CPUs but with heterogeneous c-states and
>latencies. I hope it makes some sense :)
>
Hmm.. Let me think about it.
What would be a difference between a cluster-level device and a CPU in
the same domain?
-- Lina
>Thanks,
>Marc.
>
>
>>
>>Thanks,
>>Lina
>>
>>>attaching. Currently generic domain would register an array of states
>>>upon
>>>init. This patch prepares for later insertion (sort per depth, remove).
>>>
>>>Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>>>---
>>>drivers/base/power/domain.c | 189
>>>+++++++++++++++++++-------------------------
>>>include/linux/pm_domain.h | 18 ++++-
>>>2 files changed, 97 insertions(+), 110 deletions(-)
>>>
>>>diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>>>index 3e27a2b..e5f4c00b 100644
>>>--- a/drivers/base/power/domain.c
>>>+++ b/drivers/base/power/domain.c
>>>@@ -19,6 +19,7 @@
>>>#include <linux/sched.h>
>>>#include <linux/suspend.h>
>>>#include <linux/export.h>
>>>+#include <linux/sort.h>
>>>
>>>#define GENPD_RETRY_MAX_MS 250 /* Approximate */
>>>
>>>@@ -50,12 +51,6 @@
>>> __retval; \
>>>})
>>>
>>>-#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);
>>>
>>>@@ -1364,46 +1359,6 @@ 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;
>>>- }
>>>-
>>>- 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.
>>>@@ -1833,6 +1788,75 @@ static void genpd_lock_init(struct
>>>generic_pm_domain *genpd)
>>> }
>>>}
>>>
>>>+
>>>+/*
>>>+* state depth comparison function.
>>>+*/
>>>+static int state_cmp(const void *a, const void *b)
>>>+{
>>>+ struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
>>>+ struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
>>>+
>>>+ s64 depth_a =
>>>+ state_a->power_on_latency_ns + state_a->power_off_latency_ns;
>>>+ s64 depth_b =
>>>+ state_b->power_on_latency_ns + state_b->power_off_latency_ns;
>>>+
>>>+ return (depth_a > depth_b) ? 0 : -1;
>>>+}
>>>+
>>>+/*
>>>+* TODO: antagonist routine.
>>>+*/
>>>+int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>>>+ const struct genpd_power_state *state)
>>>+{
>>>+ int ret = 0;
>>>+ int state_count = genpd->state_count;
>>>+
>>>+ if (IS_ERR_OR_NULL(genpd) || (!state))
>>>+ ret = -EINVAL;
>>>+
>>>+ if (state_count >= GENPD_POWER_STATES_MAX)
>>>+ ret = -ENOMEM;
>>>+
>>>+#ifdef CONFIG_PM_ADVANCED_DEBUG
>>>+ /* to save memory, Name allocation will happen if debug is
>>>enabled */
>>>+ genpd->states[state_count].name = kstrndup(state->name,
>>>+ GENPD_MAX_NAME_SIZE,
>>>+ GFP_KERNEL);
>>>+ if (!genpd->states[state_count].name) {
>>>+ pr_err("%s Failed to allocate state '%s' name.\n",
>>>+ genpd->name, state->name);
>>>+ ret = -ENOMEM;
>>>+ }
>>>+#endif
>>>+ genpd_lock(genpd);
>>>+
>>>+ if (!ret) {
>>>+ genpd->states[state_count].power_on_latency_ns =
>>>+ state->power_on_latency_ns;
>>>+ genpd->states[state_count].power_off_latency_ns =
>>>+ state->power_off_latency_ns;
>>>+ genpd->state_count++;
>>>+ }
>>>+
>>>+ /* sort from shallowest to deepest */
>>>+ sort(genpd->states, genpd->state_count,
>>>+ sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
>>>+
>>>+ /* Sanity check for current state index */
>>>+ if (genpd->state_idx >= genpd->state_count) {
>>>+ pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
>>>+ genpd->state_idx = genpd->state_count - 1;
>>>+ }
>>>+
>>>+ genpd_unlock(genpd);
>>>+
>>>+ return ret;
>>>+}
>>>+
>>>+
>>>/**
>>> * pm_genpd_init - Initialize a generic I/O PM domain object.
>>> * @genpd: PM domain object to initialize.
>>>@@ -1846,36 +1870,24 @@ void pm_genpd_init(struct generic_pm_domain
>>>*genpd,
>>> const struct genpd_power_state *states,
>>> unsigned int state_count, bool is_off)
>>>{
>>>- static const struct genpd_power_state genpd_default_state[] = {
>>>- {
>>>- .name = "OFF",
>>>- .power_off_latency_ns = 0,
>>>- .power_on_latency_ns = 0,
>>>- },
>>>- };
>>>- int ret;
>>>+ int i;
>>>
>>> if (IS_ERR_OR_NULL(genpd))
>>> return;
>>>
>>>- /* If no states defined, use the default OFF state */
>>>- if (!states || (state_count < 1))
>>>- ret = genpd_alloc_states_data(genpd, genpd_default_state,
>>>- ARRAY_SIZE(genpd_default_state));
>>>- else
>>>- ret = genpd_alloc_states_data(genpd, states, state_count);
>>>-
>>>- if (ret) {
>>>- pr_err("Fail to allocate states for %s\n", genpd->name);
>>>- return;
>>>- }
>>>+ /* simply use an array, we wish to add/remove new retention states
>>>+ from later device init/exit. */
>>>+ memset(genpd->states, 0, GENPD_POWER_STATES_MAX
>>>+ * sizeof(struct genpd_power_state));
>>>
>>>- /* Sanity check for initial state */
>>>- if (genpd->state_idx >= genpd->state_count) {
>>>- pr_warn("pm domain %s Invalid initial state.\n",
>>>- genpd->name);
>>>- genpd->state_idx = genpd->state_count - 1;
>>>- }
>>>+ if (!states || !state_count) {
>>>+ /* require a provider for a default state */
>>>+ genpd->state_count = 0;
>>>+ genpd->state_idx = 0;
>>>+ } else
>>>+ for (i = 0; i < state_count; i++)
>>>+ if (pm_genpd_insert_state(genpd, &states[i]))
>>>+ return;
>>>
>>> INIT_LIST_HEAD(&genpd->master_links);
>>> INIT_LIST_HEAD(&genpd->slave_links);
>>>@@ -2233,33 +2245,6 @@ 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.
>>>@@ -2398,12 +2383,4 @@ 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 */
>>>diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>>>index 9d37292..8a4eab0 100644
>>>--- a/include/linux/pm_domain.h
>>>+++ b/include/linux/pm_domain.h
>>>@@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
>>> struct cpuidle_state *idle_state;
>>>};
>>>
>>>+
>>>+/* Arbitrary max number of devices registering a special
>>>+ * retention state with the PD, to keep things simple.
>>>+ */
>>>+#define GENPD_POWER_STATES_MAX 12
>>>+#define GENPD_MAX_NAME_SIZE 40
>>>+
>>>struct genpd_power_state {
>>> char *name;
>>> s64 power_off_latency_ns;
>>>@@ -80,7 +87,8 @@ struct generic_pm_domain {
>>> struct device *dev);
>>> unsigned int flags; /* Bit field of configs for genpd */
>>>
>>>- struct genpd_power_state *states;
>>>+ struct genpd_power_state states[GENPD_POWER_STATES_MAX];
>>>+
>>> unsigned int state_count; /* number of states */
>>> unsigned int state_idx; /* state that genpd will go to when off */
>>>
>>>@@ -159,10 +167,12 @@ extern int pm_genpd_attach_cpuidle(struct
>>>generic_pm_domain *genpd, int state);
>>>extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
>>>extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
>>>extern int pm_genpd_name_detach_cpuidle(const char *name);
>>>+extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>>>+ const struct genpd_power_state *state);
>>>extern void pm_genpd_init(struct generic_pm_domain *genpd,
>>>- struct dev_power_governor *gov,
>>>- const struct genpd_power_state *states,
>>>- unsigned int state_count, bool is_off);
>>>+ struct dev_power_governor *gov,
>>>+ const struct genpd_power_state *states,
>>>+ unsigned int state_count, bool is_off);
>>>
>>>extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
>>>extern int pm_genpd_name_poweron(const char *domain_name);
>>>--
>>>1.9.1
>>>
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-09 18:22 ` Lina Iyer
@ 2015-10-13 10:29 ` Marc Titinger
2015-10-13 21:03 ` Kevin Hilman
0 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-13 10:29 UTC (permalink / raw)
To: Lina Iyer
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On 09/10/2015 20:22, Lina Iyer wrote:
> On Fri, Oct 09 2015 at 03:39 -0600, Marc Titinger wrote:
>> On 08/10/2015 18:11, Lina Iyer wrote:
>>> Hi Marc,
>>>
>>> Thanks for rebasing on top of my latest series.
>>>
>>> On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>>>> Devices may register an intermediate retention state into the domain
>>>> upon
>>>>
>>> I may agree with the usability of dynamic adding a state to the domain,
>>> but I dont see why a device attaching to a domain should bring about a
>>> new domain state.
>>
>> Hi Lina,
>>
>> thanks a lot for taking the time to look into this. The initial
>> discussion behind it was about to see how a device like a PMU, FPU,
>> cache, or a Healthcheck IP in the same power domain than CPUs, with
>> special retention states can be handled in a way 'unified' with CPUs.
>> Also I admit it is partly an attempt from us to create something
>> useful out of the "collision" of Axel's multiple states and your
>> CPUs-as-generic-power-domain-devices, hence the RFC!
>>
>> Looking at Juno for instance, she currently has a platform-initiated
>> mode implemented in the arm-trusted-firmware through psci as a
>> cpuidle-driver. the menu governor will select a possibly deep c-state,
>> but the last-man CPU and actual power state is known to ATF. Similarly
>> my idea was to have a genpd-initiated mode so to say, where the actual
>> power state results from the cpu-domain's governor choice based on
>> possible retention states, and their latency.
>>
> Okay, I must admit that my ideas are quite partial to OS-initiated PSCI
> (v1.0 onwards). So you have C-States that allow domains to enter idle as
> well. Fair.
>
>> A Health-check IP or Cache will not (to my current knowledge) have a
>> driver calling runtime_put, but may have a retention state "D1_RET"
>> with a off/on latency between CPU_SLEEP and CLUSTER_SLEEP, so that
>> CLUSTER_SLEEP may be ruled out by the governor, but D1_RET is ok given
>> the time slot available.
> A couple of questions here.
>
> You say there is no driver for HIP, is there a device for it atleast?
> How is the CPU/Domain going to know if the HIP is running or not?
>
> To me this seems like you want to set a QoS on the PM domain here.
>
>> Some platform code can be called so that the cluster goes to D1_RET in
>> that case, upon the last-man CPU waiting-for-interrupt. Note that in
>> the case of a Health-Check HIP, the state my actually be a working
>> state (all CPUs power down, and time slot OK for sampling stuff).
>>
> Building on my previous statement, if you have a QoS for a domain and a
> domain governor, it could consider the QoS requirement and choose to do
> retention. However that needs a driver or some entity that know the HIP
> is requesting a D1_RET mode only.
lets' consider a device like an L2-cache with a RAM retention state,
(for instance looking at
http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500f/DDI0500F_cortex_a53_r0p4_trm.pdf
page 41). the platform code and suspend sequence that will allow for
setup of this L2 RAM Retention state will be partly common to handling
deep c-states like 'CLUSTER_SLEEP'. Typically for a53, the manual
describes a parent power domain PDCORTEXA53, child CPU domains PDCPUn
and a child domain PDL2 that allows for L2 RAM retention. We can have
all CPUs 'off' and PDL2 in retention.
In terms of 'multiple states', each CPU as a genpd device can
independently set a constraint for the domain 'simple governor', ok it's
not done through pm_qos_add_request, but through runtime_put, but since
the c-states are soaked into the power domain as possible power-domain
states, the domain will chose for the deepest possible c-state based on :
- cpus runtime_put (for c-states deeper than state0 "WFI")
- qos_requests from regular devices in the domain, or subdomains
- .. what about L2 or devices with their own power domain, that will not
hook to pm_runtime ?
Beyond L2 controllers, you could have hard IPs for debug, monitoring,
that will have a child power domain like above, but not necessarily hook
to pm_runtime. Since the platform code for handling the CPU constraints
on the domain QoS is the one for handling c-states, and the same for the
L2-retention state, why not expose those constraints as all-c-states ?
I reckon that alternatively, it could be interesting to model L2-cc as a
regular peripheral on its own, and hook to pm_runtime instead, but then
eventually will call the same monitor code code that handles the
cpu-suspend. But it's maybe less architecture dependent and more in the
initial spirit of "statement-of-work" motivating this series ?
M.
>
>>>
>>> A domain should define its power states, independent of the devices that
>>> may attach. The way I see it, devices have their own idle states and
>>> domains have their own. I do see a relationship between possible domain
>>> states depending on the states of the individual devices in the domain.
>>> For ex, a CPU domain can only be in a retention state (low voltage,
>>> memory retained), if its CPU devices are in retention state, i.e, the
>>> domain cannot be powered off; alternately, the domain may be in
>>> retention or power down if the CPU devices are in power down state.
>>>
>>> Could you elaborate on why this is a need?
>>
>> Well, it may not be a need TBH, it is an attempt to have cluster-level
>> devices act like hotplugged CPUs but with heterogeneous c-states and
>> latencies. I hope it makes some sense :)
>>
> Hmm.. Let me think about it.
>
> What would be a difference between a cluster-level device and a CPU in
> the same domain?
>
> -- Lina
>
>> Thanks,
>> Marc.
>>
>>
>>>
>>> Thanks,
>>> Lina
>>>
>>>> attaching. Currently generic domain would register an array of states
>>>> upon
>>>> init. This patch prepares for later insertion (sort per depth, remove).
>>>>
>>>> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
>>>> ---
>>>> drivers/base/power/domain.c | 189
>>>> +++++++++++++++++++-------------------------
>>>> include/linux/pm_domain.h | 18 ++++-
>>>> 2 files changed, 97 insertions(+), 110 deletions(-)
>>>>
>>>> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
>>>> index 3e27a2b..e5f4c00b 100644
>>>> --- a/drivers/base/power/domain.c
>>>> +++ b/drivers/base/power/domain.c
>>>> @@ -19,6 +19,7 @@
>>>> #include <linux/sched.h>
>>>> #include <linux/suspend.h>
>>>> #include <linux/export.h>
>>>> +#include <linux/sort.h>
>>>>
>>>> #define GENPD_RETRY_MAX_MS 250 /* Approximate */
>>>>
>>>> @@ -50,12 +51,6 @@
>>>> __retval; \
>>>> })
>>>>
>>>> -#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);
>>>>
>>>> @@ -1364,46 +1359,6 @@ 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;
>>>> - }
>>>> -
>>>> - 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.
>>>> @@ -1833,6 +1788,75 @@ static void genpd_lock_init(struct
>>>> generic_pm_domain *genpd)
>>>> }
>>>> }
>>>>
>>>> +
>>>> +/*
>>>> +* state depth comparison function.
>>>> +*/
>>>> +static int state_cmp(const void *a, const void *b)
>>>> +{
>>>> + struct genpd_power_state *state_a = (struct genpd_power_state
>>>> *)(a);
>>>> + struct genpd_power_state *state_b = (struct genpd_power_state
>>>> *)(b);
>>>> +
>>>> + s64 depth_a =
>>>> + state_a->power_on_latency_ns + state_a->power_off_latency_ns;
>>>> + s64 depth_b =
>>>> + state_b->power_on_latency_ns + state_b->power_off_latency_ns;
>>>> +
>>>> + return (depth_a > depth_b) ? 0 : -1;
>>>> +}
>>>> +
>>>> +/*
>>>> +* TODO: antagonist routine.
>>>> +*/
>>>> +int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>>>> + const struct genpd_power_state *state)
>>>> +{
>>>> + int ret = 0;
>>>> + int state_count = genpd->state_count;
>>>> +
>>>> + if (IS_ERR_OR_NULL(genpd) || (!state))
>>>> + ret = -EINVAL;
>>>> +
>>>> + if (state_count >= GENPD_POWER_STATES_MAX)
>>>> + ret = -ENOMEM;
>>>> +
>>>> +#ifdef CONFIG_PM_ADVANCED_DEBUG
>>>> + /* to save memory, Name allocation will happen if debug is
>>>> enabled */
>>>> + genpd->states[state_count].name = kstrndup(state->name,
>>>> + GENPD_MAX_NAME_SIZE,
>>>> + GFP_KERNEL);
>>>> + if (!genpd->states[state_count].name) {
>>>> + pr_err("%s Failed to allocate state '%s' name.\n",
>>>> + genpd->name, state->name);
>>>> + ret = -ENOMEM;
>>>> + }
>>>> +#endif
>>>> + genpd_lock(genpd);
>>>> +
>>>> + if (!ret) {
>>>> + genpd->states[state_count].power_on_latency_ns =
>>>> + state->power_on_latency_ns;
>>>> + genpd->states[state_count].power_off_latency_ns =
>>>> + state->power_off_latency_ns;
>>>> + genpd->state_count++;
>>>> + }
>>>> +
>>>> + /* sort from shallowest to deepest */
>>>> + sort(genpd->states, genpd->state_count,
>>>> + sizeof(genpd->states[0]), state_cmp, NULL /*generic swap */);
>>>> +
>>>> + /* Sanity check for current state index */
>>>> + if (genpd->state_idx >= genpd->state_count) {
>>>> + pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
>>>> + genpd->state_idx = genpd->state_count - 1;
>>>> + }
>>>> +
>>>> + genpd_unlock(genpd);
>>>> +
>>>> + return ret;
>>>> +}
>>>> +
>>>> +
>>>> /**
>>>> * pm_genpd_init - Initialize a generic I/O PM domain object.
>>>> * @genpd: PM domain object to initialize.
>>>> @@ -1846,36 +1870,24 @@ void pm_genpd_init(struct generic_pm_domain
>>>> *genpd,
>>>> const struct genpd_power_state *states,
>>>> unsigned int state_count, bool is_off)
>>>> {
>>>> - static const struct genpd_power_state genpd_default_state[] = {
>>>> - {
>>>> - .name = "OFF",
>>>> - .power_off_latency_ns = 0,
>>>> - .power_on_latency_ns = 0,
>>>> - },
>>>> - };
>>>> - int ret;
>>>> + int i;
>>>>
>>>> if (IS_ERR_OR_NULL(genpd))
>>>> return;
>>>>
>>>> - /* If no states defined, use the default OFF state */
>>>> - if (!states || (state_count < 1))
>>>> - ret = genpd_alloc_states_data(genpd, genpd_default_state,
>>>> - ARRAY_SIZE(genpd_default_state));
>>>> - else
>>>> - ret = genpd_alloc_states_data(genpd, states, state_count);
>>>> -
>>>> - if (ret) {
>>>> - pr_err("Fail to allocate states for %s\n", genpd->name);
>>>> - return;
>>>> - }
>>>> + /* simply use an array, we wish to add/remove new retention states
>>>> + from later device init/exit. */
>>>> + memset(genpd->states, 0, GENPD_POWER_STATES_MAX
>>>> + * sizeof(struct genpd_power_state));
>>>>
>>>> - /* Sanity check for initial state */
>>>> - if (genpd->state_idx >= genpd->state_count) {
>>>> - pr_warn("pm domain %s Invalid initial state.\n",
>>>> - genpd->name);
>>>> - genpd->state_idx = genpd->state_count - 1;
>>>> - }
>>>> + if (!states || !state_count) {
>>>> + /* require a provider for a default state */
>>>> + genpd->state_count = 0;
>>>> + genpd->state_idx = 0;
>>>> + } else
>>>> + for (i = 0; i < state_count; i++)
>>>> + if (pm_genpd_insert_state(genpd, &states[i]))
>>>> + return;
>>>>
>>>> INIT_LIST_HEAD(&genpd->master_links);
>>>> INIT_LIST_HEAD(&genpd->slave_links);
>>>> @@ -2233,33 +2245,6 @@ 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.
>>>> @@ -2398,12 +2383,4 @@ 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 */
>>>> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
>>>> index 9d37292..8a4eab0 100644
>>>> --- a/include/linux/pm_domain.h
>>>> +++ b/include/linux/pm_domain.h
>>>> @@ -45,6 +45,13 @@ struct gpd_cpuidle_data {
>>>> struct cpuidle_state *idle_state;
>>>> };
>>>>
>>>> +
>>>> +/* Arbitrary max number of devices registering a special
>>>> + * retention state with the PD, to keep things simple.
>>>> + */
>>>> +#define GENPD_POWER_STATES_MAX 12
>>>> +#define GENPD_MAX_NAME_SIZE 40
>>>> +
>>>> struct genpd_power_state {
>>>> char *name;
>>>> s64 power_off_latency_ns;
>>>> @@ -80,7 +87,8 @@ struct generic_pm_domain {
>>>> struct device *dev);
>>>> unsigned int flags; /* Bit field of configs for genpd */
>>>>
>>>> - struct genpd_power_state *states;
>>>> + struct genpd_power_state states[GENPD_POWER_STATES_MAX];
>>>> +
>>>> unsigned int state_count; /* number of states */
>>>> unsigned int state_idx; /* state that genpd will go to when off */
>>>>
>>>> @@ -159,10 +167,12 @@ extern int pm_genpd_attach_cpuidle(struct
>>>> generic_pm_domain *genpd, int state);
>>>> extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
>>>> extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
>>>> extern int pm_genpd_name_detach_cpuidle(const char *name);
>>>> +extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
>>>> + const struct genpd_power_state *state);
>>>> extern void pm_genpd_init(struct generic_pm_domain *genpd,
>>>> - struct dev_power_governor *gov,
>>>> - const struct genpd_power_state *states,
>>>> - unsigned int state_count, bool is_off);
>>>> + struct dev_power_governor *gov,
>>>> + const struct genpd_power_state *states,
>>>> + unsigned int state_count, bool is_off);
>>>>
>>>> extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
>>>> extern int pm_genpd_name_poweron(const char *domain_name);
>>>> --
>>>> 1.9.1
>>>>
>>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state
2015-10-13 10:29 ` Marc Titinger
@ 2015-10-13 21:03 ` Kevin Hilman
0 siblings, 0 replies; 39+ messages in thread
From: Kevin Hilman @ 2015-10-13 21:03 UTC (permalink / raw)
To: Marc Titinger
Cc: Lina Iyer, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Marc Titinger <mtitinger@baylibre.com> writes:
> On 09/10/2015 20:22, Lina Iyer wrote:
>> On Fri, Oct 09 2015 at 03:39 -0600, Marc Titinger wrote:
>>> On 08/10/2015 18:11, Lina Iyer wrote:
>>>> Hi Marc,
>>>>
>>>> Thanks for rebasing on top of my latest series.
>>>>
>>>> On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>>>>> Devices may register an intermediate retention state into the domain
>>>>> upon
>>>>>
>>>> I may agree with the usability of dynamic adding a state to the domain,
>>>> but I dont see why a device attaching to a domain should bring about a
>>>> new domain state.
>>>
>>> Hi Lina,
>>>
>>> thanks a lot for taking the time to look into this. The initial
>>> discussion behind it was about to see how a device like a PMU, FPU,
>>> cache, or a Healthcheck IP in the same power domain than CPUs, with
>>> special retention states can be handled in a way 'unified' with CPUs.
>>> Also I admit it is partly an attempt from us to create something
>>> useful out of the "collision" of Axel's multiple states and your
>>> CPUs-as-generic-power-domain-devices, hence the RFC!
>>>
>>> Looking at Juno for instance, she currently has a platform-initiated
>>> mode implemented in the arm-trusted-firmware through psci as a
>>> cpuidle-driver. the menu governor will select a possibly deep c-state,
>>> but the last-man CPU and actual power state is known to ATF. Similarly
>>> my idea was to have a genpd-initiated mode so to say, where the actual
>>> power state results from the cpu-domain's governor choice based on
>>> possible retention states, and their latency.
>>>
>> Okay, I must admit that my ideas are quite partial to OS-initiated PSCI
>> (v1.0 onwards). So you have C-States that allow domains to enter idle as
>> well. Fair.
>>
>>> A Health-check IP or Cache will not (to my current knowledge) have a
>>> driver calling runtime_put, but may have a retention state "D1_RET"
>>> with a off/on latency between CPU_SLEEP and CLUSTER_SLEEP, so that
>>> CLUSTER_SLEEP may be ruled out by the governor, but D1_RET is ok given
>>> the time slot available.
>> A couple of questions here.
>>
>> You say there is no driver for HIP, is there a device for it atleast?
>> How is the CPU/Domain going to know if the HIP is running or not?
>>
>> To me this seems like you want to set a QoS on the PM domain here.
>>
>>> Some platform code can be called so that the cluster goes to D1_RET in
>>> that case, upon the last-man CPU waiting-for-interrupt. Note that in
>>> the case of a Health-Check HIP, the state my actually be a working
>>> state (all CPUs power down, and time slot OK for sampling stuff).
>>>
>> Building on my previous statement, if you have a QoS for a domain and a
>> domain governor, it could consider the QoS requirement and choose to do
>> retention. However that needs a driver or some entity that know the HIP
>> is requesting a D1_RET mode only.
>
> lets' consider a device like an L2-cache with a RAM retention state,
> (for instance looking at
> http://infocenter.arm.com/help/topic/com.arm.doc.ddi0500f/DDI0500F_cortex_a53_r0p4_trm.pdf
> page 41). the platform code and suspend sequence that will allow for
> setup of this L2 RAM Retention state will be partly common to
> handling deep c-states like 'CLUSTER_SLEEP'. Typically for a53, the
> manual describes a parent power domain PDCORTEXA53, child CPU domains
> PDCPUn and a child domain PDL2 that allows for L2 RAM retention. We
> can have all CPUs 'off' and PDL2 in retention.
>
> In terms of 'multiple states', each CPU as a genpd device can
> independently set a constraint for the domain 'simple governor', ok
> it's not done through pm_qos_add_request, but through runtime_put, but
> since the c-states are soaked into the power domain as possible
> power-domain states, the domain will chose for the deepest possible
> c-state based on :
>
> - cpus runtime_put (for c-states deeper than state0 "WFI")
> - qos_requests from regular devices in the domain, or subdomains
> - .. what about L2 or devices with their own power domain, that will
> not hook to pm_runtime ?
>
> Beyond L2 controllers, you could have hard IPs for debug, monitoring,
> that will have a child power domain like above, but not necessarily
> hook to pm_runtime. Since the platform code for handling the CPU
> constraints on the domain QoS is the one for handling c-states, and
> the same for the L2-retention state, why not expose those constraints
> as all-c-states ?
>
> I reckon that alternatively, it could be interesting to model L2-cc as
> a regular peripheral on its own, and hook to pm_runtime instead, but
> then eventually will call the same monitor code code that handles the
> cpu-suspend. But it's maybe less architecture dependent and more in
> the initial spirit of "statement-of-work" motivating this series ?
Correct.
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.
Kevin
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 0/6] Managing cluser-level c-states with generic power domains
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
` (5 preceding siblings ...)
2015-10-06 14:27 ` [RFC v2 6/6] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
@ 2015-10-13 23:10 ` Kevin Hilman
2015-10-14 8:10 ` Axel Haslam
2015-10-19 20:58 ` Lina Iyer
7 siblings, 1 reply; 39+ messages in thread
From: Kevin Hilman @ 2015-10-13 23:10 UTC (permalink / raw)
To: Marc Titinger
Cc: rjw, lina.iyer, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Hi Marc,
Sorry for the lag in reviewing this. I've been on the road for a couple
weeks of conference related travel. I did have a look through this on
the plane, but didn't have time to reply until now, and then had to
rework my thoughts a bit since Lina already said some of the things I
was thinking. Anyways, some overall thoughts below...
Marc Titinger <mtitinger@baylibre.com> writes:
> v2:
> - rebase on Lina Iyer's latest series
> - remove unnecessary dependency on perf-state patches from Axel Haslam
>
> -----------------------
>
> Summary
>
> 1) DESCRIPTION
> 2) DEPENDENCIES
> 3) URL
> ------------------------
>
>
> 1) DESCRIPTION
>
>
> This patch set's underlying idea is that cluster-level c-states can be managed
nit: can we drop the use of C-states throughout and just refer to idle
states. C-states is technically an ACPI specific term.
> by the power domain, building upon Lina Iyers recent work on CPU-domain, and Axel Haslam's
> genpd multiple states. The power domain may contain CPU devices and non-CPU devices.
@Axel: any plans to repost your series now that we have a little more
momentum here?
> Non-CPU Devices may expose latency constraints by registering intermediate power-states upon
> probing, for instance shallower states than the deepest cluster-off state. The generic
> power domain governor may chose a device retention state in place of the cluster-sleep
> state demanded by the menu governor, and call the platform specific handling to enter/leave
> that retention state.
>
>
> power-states
> -----------
>
>
> The proposed way how cluster-level c-states are declared as manageable by the
> power domain, rather than through the cpuidle-ops, relies on the introduction of
> "power-states", consistent with c-states. Here is an example of the DT bindings,
> the c-state CLUSTER_SLEEP_0 is exposed as a power-state in the compatible property:
>
> juno.dts: idle-states {
> entry-method = "arm,psci";
>
> CPU_SLEEP_0: cpu-sleep-0 {
> compatible = "arm,idle-state";
> arm,psci-suspend-param = <0x0010000>;
> local-timer-stop;
> entry-latency-us = <100>;
> exit-latency-us = <250>;
> min-residency-us = <2000>;
> };
>
> CLUSTER_SLEEP_0: cluster-sleep-0 {
> compatible = "arm,power-state";
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
> exit-latency-us = <700>;
> min-residency-us = <2500>;
> };
> }
>
> This will tell cpuidle runtime_put/get the CPU devices for this c-state. Eventually, the
> actual platform handlers may be called from the genpd platform ops (in place of cpuidle_ops).
>
> "drivers/cpuidle/cpuidle-arm.c":
>
> static const struct of_device_id arm_idle_state_match[] __initconst = {
> {.compatible = "arm,idle-state",
> .data = arm_enter_idle_state},
> {.compatible = "arm,power-state",
> .data = arm_enter_power_state},
> };
>
>
> In case of a power-state, arm_enter_power_state will only call pm_runtime_put/get_sync
> The power doamin will handle the power off,
I'm not crazy about this part of the design. Essentially it delegates
the cluster level decision making to the CPUidle driver. IMO, all of
the decision making should be at the domain level.
> currently this patch set lacks the final
> call to the psci interface to have a fully fonctionnal setup
> (and there are some genpd_lock'ing issues if put/get actually suspend the CPU device.)
> Ultimately, we would like the Power Domain's simple governor to being able to chose
> the cluster power-state based on the c-states defered to it (power-states) and constraints
> added by the devices. Consequently, we need to "soak" those power-states into the
> power-domain intermediate states from Axel. Since power-states are declared and handled
> the same manner than c-states (idle-states in DT), these patches add a soaking used when
> attaching to a genpd, where power-states are parsed from the DT into the genpd states:
>
>
> "drivers/base/power/domain.c":
>
> static const struct of_device_id power_state_match[] = {
> {.compatible = "arm,power-state",
> },
> };
>
> int of_genpd_device_parse_states(struct device_node *np,
> struct generic_pm_domain *genpd)
This approach results in some strange (IMO) sharing of the
responsibility, as can be seen because both the genpd and the cpuidle
driver end up parsing the new arm,power-state states.
IMO, the CPUidle driver should not have to be aware of what cluster
states are availble, nor what other devices may or may not be in the
same domain as a given CPU.
Maybe another way to differentiate between CPU idle states and cluster
idle states is to associate them with a power-domain node in the DT
(instead of a CPU node as is done currently.) Then there would also not
be a need for another compatible string. As Lina pointed out, your
arm,power-state has identical properties to an arm,idle-state so doesn't
really justify a new compatible.
> debugfs addition
> ---------------
>
> To easy debug, this patch set adds a seq-file names "states" to the pm_genpd debugfs:
>
> 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
I guess this should probably be part of Axel's "multiple state" series.
> And also a seq-file "timings", to help visualize the constrains of the non-CPU
> devices in a cluster PD.
>
> Domain Devices, Timings in ns
> Stop/Start Save/Restore, Effective
> ---------------------------------------------------- ---
> a57_pd
> /cpus/cpu@0 800 /740 1320 /1720 ,0 (cached stop)
> /cpus/cpu@1 800 /740 1420 /1780 ,0 (cached stop)
> /D1 660 /580 16560 /6080 ,2199420 (cached stop)
Nice!
> Device power-states
> -------------------
>
> some devices, like L2 caches, may feature a shallower retention mode, between CPU_SLEEP_0
> and CLUSTER_SLEEP_0, in which mode the L2 memory is not powered off, leading to faster
> resume than CLUSTER_SLEEP_0.
>
> One way to handle device constrains and retention features in the power-domain, is to
> allow devices to register a new power-state (consistent with a c-state).
The problem with this is that the power-state is not technically a
property of the device. It's a property of the power domain.
> idle-states:
>
> D1_RETENTION: d1-retention {
> compatible = "arm,power-state";
> /*leave the psci param, for demo/testing:
> * the psci cpuidle driver will not currently
> * understand that a c-state shall not have it's
> * table entry with a firmware command.
> * the actual .power_on/off would be registered
> * by the DECLARE macro for a given domain*/
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
> exit-latency-us = <200>;
> min-residency-us = <2500>;
> };
>
>
> D1 {
> compatible = "fake,fake-driver";
> name = "D1";
> constraint = <30000>;
> power-domains = <&a53_pd>;
> power-states =<&D1_RETENTION>;
> };
>
>
> The genpd simple governor can now upon suspend of the last-man CPU chose a shallower
> retention state than CLUSTER_SLEEP_0.
>
> In order to achieve this, this patch set added the power-state parsing during the
> genpd_dev_pm_attach call. Multiple genpd states are now inserted in a sorted manner
> according to their depth: see pm_genpd_insert_state in "drivers/base/power/domain.c".
As mentioned elsewhere, I think the ability to dynamically add (and
presumably remove) genpd states is a bit overkill right now. I think
the list of power states should be statically defined along with the
power-domain itself, and then it should simply rely on the runtime PM
usage of all the devices in the domain (as well as the genpd governors)
to select the right state.
Kevin
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 0/6] Managing cluser-level c-states with generic power domains
2015-10-13 23:10 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Kevin Hilman
@ 2015-10-14 8:10 ` Axel Haslam
0 siblings, 0 replies; 39+ messages in thread
From: Axel Haslam @ 2015-10-14 8:10 UTC (permalink / raw)
To: Kevin Hilman
Cc: Marc Titinger, Rafael J. Wysocki, Lina Iyer, Benoit Cousson,
Linux PM list, linux-kernel, Marc Titinger
> @Axel: any plans to repost your series now that we have a little more
> momentum here?
>
sure, no problem, ill rebase adding Marc's patch for the debugfs file
and re-post them.
Regards,
Axel
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 0/6] Managing cluser-level c-states with generic power domains
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
` (6 preceding siblings ...)
2015-10-13 23:10 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Kevin Hilman
@ 2015-10-19 20:58 ` Lina Iyer
2015-10-20 9:10 ` Marc Titinger
` (8 more replies)
7 siblings, 9 replies; 39+ messages in thread
From: Lina Iyer @ 2015-10-19 20:58 UTC (permalink / raw)
To: Marc Titinger
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Hi Marc,
I am trying to apply this on top of Axel's patches on linux-next (after
fixing issues I saw with his v9), and running to issues applying your
patches. Could you rebase on top of his v10 (he said he would send to
the ML soon) ?
Thanks,
Lina
On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>v2:
> - rebase on Lina Iyer's latest series
> - remove unnecessary dependency on perf-state patches from Axel Haslam
>
>-----------------------
>
>Summary
>
>1) DESCRIPTION
>2) DEPENDENCIES
>3) URL
>------------------------
>
>
>1) DESCRIPTION
>
>
> This patch set's underlying idea is that cluster-level c-states can be managed
>by the power domain, building upon Lina Iyers recent work on CPU-domain, and Axel Haslam's
>genpd multiple states. The power domain may contain CPU devices and non-CPU devices.
>
>Non-CPU Devices may expose latency constraints by registering intermediate power-states upon
>probing, for instance shallower states than the deepest cluster-off state. The generic
>power domain governor may chose a device retention state in place of the cluster-sleep
>state demanded by the menu governor, and call the platform specific handling to enter/leave
>that retention state.
>
>
>power-states
>-----------
>
>
>The proposed way how cluster-level c-states are declared as manageable by the
>power domain, rather than through the cpuidle-ops, relies on the introduction of
>"power-states", consistent with c-states. Here is an example of the DT bindings,
>the c-state CLUSTER_SLEEP_0 is exposed as a power-state in the compatible property:
>
>juno.dts: idle-states {
> entry-method = "arm,psci";
>
> CPU_SLEEP_0: cpu-sleep-0 {
> compatible = "arm,idle-state";
> arm,psci-suspend-param = <0x0010000>;
> local-timer-stop;
> entry-latency-us = <100>;
> exit-latency-us = <250>;
> min-residency-us = <2000>;
> };
>
> CLUSTER_SLEEP_0: cluster-sleep-0 {
> compatible = "arm,power-state";
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
> exit-latency-us = <700>;
> min-residency-us = <2500>;
> };
> }
>
>This will tell cpuidle runtime_put/get the CPU devices for this c-state. Eventually, the
>actual platform handlers may be called from the genpd platform ops (in place of cpuidle_ops).
>
>"drivers/cpuidle/cpuidle-arm.c":
>
>static const struct of_device_id arm_idle_state_match[] __initconst = {
> {.compatible = "arm,idle-state",
> .data = arm_enter_idle_state},
> {.compatible = "arm,power-state",
> .data = arm_enter_power_state},
>};
>
>
>In case of a power-state, arm_enter_power_state will only call pm_runtime_put/get_sync
>The power doamin will handle the power off, currently this patch set lacks the final
>call to the psci interface to have a fully fonctionnal setup
>(and there are some genpd_lock'ing issues if put/get actually suspend the CPU device.)
>
>Ultimately, we would like the Power Domain's simple governor to being able to chose
>the cluster power-state based on the c-states defered to it (power-states) and constraints
>added by the devices. Consequently, we need to "soak" those power-states into the
>power-domain intermediate states from Axel. Since power-states are declared and handled
>the same manner than c-states (idle-states in DT), these patches add a soaking used when
>attaching to a genpd, where power-states are parsed from the DT into the genpd states:
>
>
>"drivers/base/power/domain.c":
>
>static const struct of_device_id power_state_match[] = {
> {.compatible = "arm,power-state",
> },
>};
>
>int of_genpd_device_parse_states(struct device_node *np,
> struct generic_pm_domain *genpd)
>
>debugfs addition
>---------------
>
>To easy debug, this patch set adds a seq-file names "states" to the pm_genpd debugfs:
>
> 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
>
>And also a seq-file "timings", to help visualize the constrains of the non-CPU
>devices in a cluster PD.
>
> Domain Devices, Timings in ns
> Stop/Start Save/Restore, Effective
>---------------------------------------------------- ---
>a57_pd
> /cpus/cpu@0 800 /740 1320 /1720 ,0 (cached stop)
> /cpus/cpu@1 800 /740 1420 /1780 ,0 (cached stop)
> /D1 660 /580 16560 /6080 ,2199420 (cached stop)
>
>
>Device power-states
>-------------------
>
>some devices, like L2 caches, may feature a shallower retention mode, between CPU_SLEEP_0
>and CLUSTER_SLEEP_0, in which mode the L2 memory is not powered off, leading to faster
>resume than CLUSTER_SLEEP_0.
>
>One way to handle device constrains and retention features in the power-domain, is to
>allow devices to register a new power-state (consistent with a c-state).
>
>idle-states:
>
> D1_RETENTION: d1-retention {
> compatible = "arm,power-state";
> /*leave the psci param, for demo/testing:
> * the psci cpuidle driver will not currently
> * understand that a c-state shall not have it's
> * table entry with a firmware command.
> * the actual .power_on/off would be registered
> * by the DECLARE macro for a given domain*/
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
> exit-latency-us = <200>;
> min-residency-us = <2500>;
> };
>
>
> D1 {
> compatible = "fake,fake-driver";
> name = "D1";
> constraint = <30000>;
> power-domains = <&a53_pd>;
> power-states =<&D1_RETENTION>;
> };
>
>
>The genpd simple governor can now upon suspend of the last-man CPU chose a shallower
>retention state than CLUSTER_SLEEP_0.
>
>In order to achieve this, this patch set added the power-state parsing during the
>genpd_dev_pm_attach call. Multiple genpd states are now inserted in a sorted manner
>according to their depth: see pm_genpd_insert_state in "drivers/base/power/domain.c".
>
>
>
>2) DEPENDENCIES
>
> This patch set applies over linux-4.2rc5 plus the following ordered dependencies:
>
> * Ulf Hansson:
>
>6637131 New [V4] PM / Domains: Remove intermediate states from the power off sequence
>
> * Lina Iyer's patch series:
>
>7118981 Not Applicable [v2,1/7] PM / Domains: Allocate memory outside domain locks
>7118991 Not Applicable [v2,2/7] PM / Domains: Support IRQ safe PM domains
>7119001 Not Applicable [v2,3/7] drivers: cpu: Define CPU devices as IRQ safe
>7119011 Not Applicable [v2,4/7] PM / Domains: Introduce PM domains for CPUs/clusters
>7119021 Not Applicable [v2,5/7] ARM: cpuidle: Add runtime PM support for CPU idle
>7119031 Not Applicable [v2,6/7] ARM64: smp: Add runtime PM support for CPU hotplug
>7119041 Not Applicable [v2,7/7] ARM: smp: Add runtime PM support for CPU hotplug
>
> * John Medhurst:
>
>6303671 New arm64: dts: Add idle-states for Juno
>
> * Axel Haslam:
>
>6301741 Not Applicable [v7,1/5] PM / Domains: prepare for multiple states
>6301751 Not Applicable [v7,2/5] PM / Domains: core changes for multiple states
>6301781 Not Applicable [v7,3/5] PM / Domains: make governor select deepest state
>6301771 Not Applicable [v7,4/5] ARM: imx6: pm: declare pm domain latency on power_state struct.
>6301761 Not Applicable [v7,5/5] PM / Domains: remove old power on/off latencies.
>
>2) URL
>
>playable from https://github.com/mtitinger/linux-pm.git
>
>by adding the "fake driver D1" and launching the test-dev-state.sh script.
>this will show the power domain suspending to an intermediate state, based on the
>device constraints.
>
> 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
>a57_pd d1-retention
> /devices/system/cpu/cpu1 suspended
> /devices/system/cpu/cpu2 suspended
> /devices/platform/D1
>
>----------------------------------------------------------------------
>
>Marc Titinger (6):
> arm64: Juno: declare generic power domains for both clusters.
> PM / Domains: prepare for devices that might register a power state
> PM / Domains: introduce power-states consistent with c-states.
> PM / Domains: succeed & warn when attaching non-irqsafe devices to an
> irq-safe domain.
> arm: cpuidle: let genpd handle the cluster power transition with
> 'power-states'
> PM / Domains: add debugfs 'states' and 'timings' seq files
>
> .../devicetree/bindings/arm/idle-states.txt | 21 +-
> .../devicetree/bindings/power/power_domain.txt | 29 ++
> arch/arm64/boot/dts/arm/juno.dts | 25 +-
> drivers/base/power/cpu-pd.c | 5 +
> drivers/base/power/domain.c | 415 +++++++++++++++------
> drivers/cpuidle/cpuidle-arm.c | 52 ++-
> include/linux/pm_domain.h | 21 +-
> 7 files changed, 437 insertions(+), 131 deletions(-)
>
>--
>1.9.1
>
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 0/6] Managing cluser-level c-states with generic power domains
2015-10-19 20:58 ` Lina Iyer
@ 2015-10-20 9:10 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 0/7] Managing cluser-level idle-states " Marc Titinger
` (7 subsequent siblings)
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-20 9:10 UTC (permalink / raw)
To: Lina Iyer
Cc: khilman, rjw, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
On 19/10/2015 22:58, Lina Iyer wrote:
> Hi Marc,
>
> I am trying to apply this on top of Axel's patches on linux-next (after
> fixing issues I saw with his v9), and running to issues applying your
> patches. Could you rebase on top of his v10 (he said he would send to
> the ML soon) ?
>
Hi Lina,
I want to replay this with Juno this afternoon first, I'll post ASAP.
Also, based on Kevin's comment I was wondering if I should drop this
path already and try the other way as discussed (hook l2 devices to
runtime-pm, through the CPU device), but I still need to think about
this first.
Cheers,
Marc.
> Thanks,
> Lina
>
> On Tue, Oct 06 2015 at 08:27 -0600, Marc Titinger wrote:
>> v2:
>> - rebase on Lina Iyer's latest series
>> - remove unnecessary dependency on perf-state patches from Axel Haslam
>>
>> -----------------------
>>
>> Summary
>>
>> 1) DESCRIPTION
>> 2) DEPENDENCIES
>> 3) URL
>> ------------------------
>>
>>
>> 1) DESCRIPTION
>>
>>
>> This patch set's underlying idea is that cluster-level c-states
>> can be managed
>> by the power domain, building upon Lina Iyers recent work on
>> CPU-domain, and Axel Haslam's
>> genpd multiple states. The power domain may contain CPU devices and
>> non-CPU devices.
>>
>> Non-CPU Devices may expose latency constraints by registering
>> intermediate power-states upon
>> probing, for instance shallower states than the deepest cluster-off
>> state. The generic
>> power domain governor may chose a device retention state in place of
>> the cluster-sleep
>> state demanded by the menu governor, and call the platform specific
>> handling to enter/leave
>> that retention state.
>>
>>
>> power-states
>> -----------
>>
>>
>> The proposed way how cluster-level c-states are declared as manageable
>> by the
>> power domain, rather than through the cpuidle-ops, relies on the
>> introduction of
>> "power-states", consistent with c-states. Here is an example of the DT
>> bindings,
>> the c-state CLUSTER_SLEEP_0 is exposed as a power-state in the
>> compatible property:
>>
>> juno.dts: idle-states {
>> entry-method = "arm,psci";
>>
>> CPU_SLEEP_0: cpu-sleep-0 {
>> compatible = "arm,idle-state";
>> arm,psci-suspend-param = <0x0010000>;
>> local-timer-stop;
>> entry-latency-us = <100>;
>> exit-latency-us = <250>;
>> min-residency-us = <2000>;
>> };
>>
>> CLUSTER_SLEEP_0: cluster-sleep-0 {
>> compatible = "arm,power-state";
>> arm,psci-suspend-param = <0x1010000>;
>> local-timer-stop;
>> entry-latency-us = <800>;
>> exit-latency-us = <700>;
>> min-residency-us = <2500>;
>> };
>> }
>>
>> This will tell cpuidle runtime_put/get the CPU devices for this
>> c-state. Eventually, the
>> actual platform handlers may be called from the genpd platform ops (in
>> place of cpuidle_ops).
>>
>> "drivers/cpuidle/cpuidle-arm.c":
>>
>> static const struct of_device_id arm_idle_state_match[] __initconst = {
>> {.compatible = "arm,idle-state",
>> .data = arm_enter_idle_state},
>> {.compatible = "arm,power-state",
>> .data = arm_enter_power_state},
>> };
>>
>>
>> In case of a power-state, arm_enter_power_state will only call
>> pm_runtime_put/get_sync
>> The power doamin will handle the power off, currently this patch set
>> lacks the final
>> call to the psci interface to have a fully fonctionnal setup
>> (and there are some genpd_lock'ing issues if put/get actually suspend
>> the CPU device.)
>>
>> Ultimately, we would like the Power Domain's simple governor to being
>> able to chose
>> the cluster power-state based on the c-states defered to it
>> (power-states) and constraints
>> added by the devices. Consequently, we need to "soak" those
>> power-states into the
>> power-domain intermediate states from Axel. Since power-states are
>> declared and handled
>> the same manner than c-states (idle-states in DT), these patches add a
>> soaking used when
>> attaching to a genpd, where power-states are parsed from the DT into
>> the genpd states:
>>
>>
>> "drivers/base/power/domain.c":
>>
>> static const struct of_device_id power_state_match[] = {
>> {.compatible = "arm,power-state",
>> },
>> };
>>
>> int of_genpd_device_parse_states(struct device_node *np,
>> struct generic_pm_domain *genpd)
>>
>> debugfs addition
>> ---------------
>>
>> To easy debug, this patch set adds a seq-file names "states" to the
>> pm_genpd debugfs:
>>
>> 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
>>
>> And also a seq-file "timings", to help visualize the constrains of the
>> non-CPU
>> devices in a cluster PD.
>>
>> Domain Devices, Timings in ns
>> Stop/Start Save/Restore, Effective
>> ---------------------------------------------------- ---
>> a57_pd
>> /cpus/cpu@0 800 /740 1320 /1720 ,0 (cached stop)
>> /cpus/cpu@1 800 /740 1420 /1780 ,0 (cached stop)
>> /D1 660 /580 16560 /6080 ,2199420 (cached stop)
>>
>>
>> Device power-states
>> -------------------
>>
>> some devices, like L2 caches, may feature a shallower retention mode,
>> between CPU_SLEEP_0
>> and CLUSTER_SLEEP_0, in which mode the L2 memory is not powered off,
>> leading to faster
>> resume than CLUSTER_SLEEP_0.
>>
>> One way to handle device constrains and retention features in the
>> power-domain, is to
>> allow devices to register a new power-state (consistent with a c-state).
>>
>> idle-states:
>>
>> D1_RETENTION: d1-retention {
>> compatible = "arm,power-state";
>> /*leave the psci param, for demo/testing:
>> * the psci cpuidle driver will not
>> currently
>> * understand that a c-state shall not
>> have it's
>> * table entry with a firmware command.
>> * the actual .power_on/off would be
>> registered
>> * by the DECLARE macro for a given
>> domain*/
>> arm,psci-suspend-param = <0x1010000>;
>> local-timer-stop;
>> entry-latency-us = <800>;
>> exit-latency-us = <200>;
>> min-residency-us = <2500>;
>> };
>>
>>
>> D1 {
>> compatible = "fake,fake-driver";
>> name = "D1";
>> constraint = <30000>;
>> power-domains = <&a53_pd>;
>> power-states =<&D1_RETENTION>;
>> };
>>
>>
>> The genpd simple governor can now upon suspend of the last-man CPU
>> chose a shallower
>> retention state than CLUSTER_SLEEP_0.
>>
>> In order to achieve this, this patch set added the power-state parsing
>> during the
>> genpd_dev_pm_attach call. Multiple genpd states are now inserted in a
>> sorted manner
>> according to their depth: see pm_genpd_insert_state in
>> "drivers/base/power/domain.c".
>>
>>
>>
>> 2) DEPENDENCIES
>>
>> This patch set applies over linux-4.2rc5 plus the following
>> ordered dependencies:
>>
>> * Ulf Hansson:
>>
>> 6637131 New [V4] PM / Domains: Remove intermediate states
>> from the power off sequence
>>
>> * Lina Iyer's patch series:
>>
>> 7118981 Not Applicable [v2,1/7] PM / Domains: Allocate memory outside
>> domain locks
>> 7118991 Not Applicable [v2,2/7] PM / Domains: Support IRQ safe PM domains
>> 7119001 Not Applicable [v2,3/7] drivers: cpu: Define CPU devices as
>> IRQ safe
>> 7119011 Not Applicable [v2,4/7] PM / Domains: Introduce PM domains for
>> CPUs/clusters
>> 7119021 Not Applicable [v2,5/7] ARM: cpuidle: Add runtime PM support
>> for CPU idle
>> 7119031 Not Applicable [v2,6/7] ARM64: smp: Add runtime PM support for
>> CPU hotplug
>> 7119041 Not Applicable [v2,7/7] ARM: smp: Add runtime PM support for
>> CPU hotplug
>>
>> * John Medhurst:
>>
>> 6303671 New arm64: dts: Add idle-states for Juno
>>
>> * Axel Haslam:
>>
>> 6301741 Not Applicable [v7,1/5] PM / Domains: prepare for multiple states
>> 6301751 Not Applicable [v7,2/5] PM / Domains: core changes for
>> multiple states
>> 6301781 Not Applicable [v7,3/5] PM / Domains: make governor select
>> deepest state
>> 6301771 Not Applicable [v7,4/5] ARM: imx6: pm: declare pm domain
>> latency on power_state struct.
>> 6301761 Not Applicable [v7,5/5] PM / Domains: remove old power on/off
>> latencies.
>>
>> 2) URL
>>
>> playable from https://github.com/mtitinger/linux-pm.git
>>
>> by adding the "fake driver D1" and launching the test-dev-state.sh
>> script.
>> this will show the power domain suspending to an intermediate state,
>> based on the
>> device constraints.
>>
>> 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
>> a57_pd d1-retention
>> /devices/system/cpu/cpu1 suspended
>> /devices/system/cpu/cpu2 suspended
>> /devices/platform/D1
>>
>> ----------------------------------------------------------------------
>>
>> Marc Titinger (6):
>> arm64: Juno: declare generic power domains for both clusters.
>> PM / Domains: prepare for devices that might register a power state
>> PM / Domains: introduce power-states consistent with c-states.
>> PM / Domains: succeed & warn when attaching non-irqsafe devices to an
>> irq-safe domain.
>> arm: cpuidle: let genpd handle the cluster power transition with
>> 'power-states'
>> PM / Domains: add debugfs 'states' and 'timings' seq files
>>
>> .../devicetree/bindings/arm/idle-states.txt | 21 +-
>> .../devicetree/bindings/power/power_domain.txt | 29 ++
>> arch/arm64/boot/dts/arm/juno.dts | 25 +-
>> drivers/base/power/cpu-pd.c | 5 +
>> drivers/base/power/domain.c | 415
>> +++++++++++++++------
>> drivers/cpuidle/cpuidle-arm.c | 52 ++-
>> include/linux/pm_domain.h | 21 +-
>> 7 files changed, 437 insertions(+), 131 deletions(-)
>>
>> --
>> 1.9.1
>>
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC v3 0/7] Managing cluser-level idle-states with generic power domains
2015-10-19 20:58 ` Lina Iyer
2015-10-20 9:10 ` Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 1/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
` (6 subsequent siblings)
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel, Marc Titinger
Hi Lina,
this is the rebased version of my patches to handle cluster sleep with genpd.
I rebased over your current patches in the linaro git, and Axels v10, and
finally linux-next.
The arm64/Juno stuff is mainly for testing, it may not be 100% up-to-date with
the CPU-pd discussion outcomes, and I've left the patch for dynamic states
registration by devices in this series for now (will do things differently
using the CPU devices as suggested by Kevin).
Changes in v3 (based on comments from you and Kevin)
- removed the "power-states" binding. Instead, look for a ppty called
cpu-idle-states in each PD node. this is a list of idle-states that will be
mapped as domain states.
- quit referring to c-states, but refer to idle-states instead.
Jon Medhurst (1):
arm64: dts: Add idle-states for Juno
Marc Titinger (6):
PM / Domains: prepare for devices that might register a power state
PM / Domains: support idle-states as genpd multiple-state.
arm64: Juno: declare generic power domains for both clusters.
drivers: cpu-pd: allow calling of_cpu_pd_init from platform code.
arm64: PM /Domains: Initialize CPU-domains from DT.
arm64: Juno: declare idle-state cluster-sleep-0 as genpd state
.../devicetree/bindings/power/power_domain.txt | 29 +++
arch/arm64/boot/dts/arm/juno.dts | 50 ++++
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/cpu_domain.c | 19 ++
drivers/base/power/cpu-pd.c | 31 +--
drivers/base/power/domain.c | 278 ++++++++++++++-------
include/linux/cpu-pd.h | 2 +
include/linux/pm_domain.h | 13 +-
8 files changed, 308 insertions(+), 115 deletions(-)
create mode 100644 arch/arm64/kernel/cpu_domain.c
--
1.9.1
^ permalink raw reply [flat|nested] 39+ messages in thread
* [RFC v3 1/7] PM / Domains: prepare for devices that might register a power state
2015-10-19 20:58 ` Lina Iyer
2015-10-20 9:10 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 0/7] Managing cluser-level idle-states " Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state Marc Titinger
` (5 subsequent siblings)
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Devices may register an intermediate retention state into the domain
upon attaching. Currently generic domain would register an array of
states upon init. This patch prepares for later insertion (sort per
depth, remove).
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
Signed-off-by: Lina Iyer <lina.iyer@linaro.org>
- Lina: Rebase related changes]
- Lina: Initialize domain states after the domain is initalized to
ensure the domain lock is initialized before calling the function
to add states dynamically.
---
drivers/base/power/cpu-pd.c | 2 -
drivers/base/power/domain.c | 178 +++++++++++++++++++++-----------------------
include/linux/pm_domain.h | 10 ++-
3 files changed, 94 insertions(+), 96 deletions(-)
diff --git a/drivers/base/power/cpu-pd.c b/drivers/base/power/cpu-pd.c
index aa276fc..eddee98 100644
--- a/drivers/base/power/cpu-pd.c
+++ b/drivers/base/power/cpu-pd.c
@@ -21,8 +21,6 @@
#include <linux/rculist.h>
#include <linux/slab.h>
-#define NAME_MAX 36
-
/* List of CPU PM domains we care about */
static LIST_HEAD(of_cpu_pd_list);
static DEFINE_SPINLOCK(cpu_pd_list_lock);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index cc8134d..6b2d771 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/suspend.h>
#include <linux/export.h>
+#include <linux/sort.h>
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
@@ -50,12 +51,6 @@
__retval; \
})
-#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);
@@ -1297,45 +1292,6 @@ 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;
- }
-
- 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.
@@ -1614,6 +1570,73 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
}
}
+/*
+ * state depth comparison function.
+ */
+static int state_cmp(const void *a, const void *b)
+{
+ struct genpd_power_state *state_a = (struct genpd_power_state *)(a);
+ struct genpd_power_state *state_b = (struct genpd_power_state *)(b);
+
+ s64 depth_a =
+ state_a->power_on_latency_ns + state_a->power_off_latency_ns;
+ s64 depth_b =
+ state_b->power_on_latency_ns + state_b->power_off_latency_ns;
+
+ return (depth_a > depth_b) ? 0 : -1;
+}
+
+/*
+ * TODO: antagonist routine.
+ */
+int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state)
+{
+ int ret = 0;
+ int state_count = genpd->state_count;
+
+ if (IS_ERR_OR_NULL(genpd) || (!state))
+ ret = -EINVAL;
+
+ if (state_count >= GENPD_POWER_STATES_MAX)
+ ret = -ENOMEM;
+
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ /* to save memory, Name allocation will happen if debug is enabled */
+ genpd->states[state_count].name = kstrndup(state->name,
+ GENPD_MAX_NAME_SIZE,
+ GFP_KERNEL);
+ if (!genpd->states[state_count].name) {
+ pr_err("%s Failed to allocate state '%s' name.\n",
+ genpd->name, state->name);
+ ret = -ENOMEM;
+ }
+#endif
+ genpd_lock(genpd);
+
+ if (!ret) {
+ genpd->states[state_count].power_on_latency_ns =
+ state->power_on_latency_ns;
+ genpd->states[state_count].power_off_latency_ns =
+ state->power_off_latency_ns;
+ genpd->state_count++;
+ }
+
+ /* sort from shallowest to deepest */
+ sort(genpd->states, genpd->state_count,
+ sizeof(genpd->states[0]), state_cmp, NULL);
+
+ /* Sanity check for current state index */
+ if (genpd->state_idx >= genpd->state_count) {
+ pr_warn("pm domain %s Invalid initial state.\n", genpd->name);
+ genpd->state_idx = genpd->state_count - 1;
+ }
+
+ genpd_unlock(genpd);
+
+ return ret;
+}
+
/**
* pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize.
@@ -1627,23 +1650,11 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
const struct genpd_power_state *states,
unsigned int state_count, bool is_off)
{
- int ret;
+ int i;
if (IS_ERR_OR_NULL(genpd))
return;
- /* State data should be provided */
- if (!states || (state_count < 1)) {
- pr_err("Invalid state data\n");
- return;
- }
-
- ret = genpd_alloc_states_data(genpd, states, state_count);
- if (ret) {
- pr_err("Failed to allocate states for %s\n", genpd->name);
- return;
- }
-
INIT_LIST_HEAD(&genpd->master_links);
INIT_LIST_HEAD(&genpd->slave_links);
INIT_LIST_HEAD(&genpd->dev_list);
@@ -1688,9 +1699,25 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
genpd->dev_ops.start = pm_clk_resume;
}
+ /* simply use an array, we wish to add/remove new retention states
+ * from later device init/exit.
+ */
+ memset(genpd->states, 0, GENPD_POWER_STATES_MAX
+ * sizeof(struct genpd_power_state));
+
+ if (!states || !state_count) {
+ /* require a provider for a default state */
+ genpd->state_count = 0;
+ genpd->state_idx = 0;
+ } else
+ for (i = 0; i < state_count; i++)
+ if (pm_genpd_insert_state(genpd, &states[i]))
+ return;
+
mutex_lock(&gpd_list_lock);
list_add(&genpd->gpd_list_node, &gpd_list);
mutex_unlock(&gpd_list_lock);
+
}
EXPORT_SYMBOL_GPL(pm_genpd_init);
@@ -2029,33 +2056,6 @@ 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.
@@ -2292,12 +2292,4 @@ 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 */
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 57ee8d8..24621be 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -44,6 +44,12 @@ struct gpd_cpuidle_data {
struct cpuidle_state *idle_state;
};
+/* Arbitrary max number of devices registering a special
+ * retention state with the PD, to keep things simple.
+ */
+#define GENPD_POWER_STATES_MAX 12
+#define GENPD_MAX_NAME_SIZE 40
+
struct genpd_power_state {
char *name;
s64 power_off_latency_ns;
@@ -77,7 +83,7 @@ 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;
+ struct genpd_power_state states[GENPD_POWER_STATES_MAX];
unsigned int state_count; /* number of states */
unsigned int state_idx; /* state that genpd will go to when off */
bool irq_safe;
@@ -154,6 +160,8 @@ extern void pm_genpd_init_simple(struct generic_pm_domain *genpd,
extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
extern int pm_genpd_name_poweron(const char *domain_name);
extern void pm_genpd_poweroff_unused(void);
+extern int pm_genpd_insert_state(struct generic_pm_domain *genpd,
+ const struct genpd_power_state *state);
extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state.
2015-10-19 20:58 ` Lina Iyer
` (2 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 1/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-11-13 5:56 ` Zhaoyang Huang
2015-10-27 17:40 ` [RFC v3 3/7] arm64: dts: Add idle-states for Juno Marc Titinger
` (4 subsequent siblings)
8 siblings, 1 reply; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
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
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
.../devicetree/bindings/power/power_domain.txt | 29 ++++++
drivers/base/power/domain.c | 102 ++++++++++++++++++++-
include/linux/pm_domain.h | 3 +
3 files changed, 131 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
index 025b5e7..2657e19 100644
--- a/Documentation/devicetree/bindings/power/power_domain.txt
+++ b/Documentation/devicetree/bindings/power/power_domain.txt
@@ -29,6 +29,16 @@ Optional properties:
specified by this binding. More details about power domain specifier are
available in the next section.
+ - cpu-idle-states : a phandle of an idle-state that shall be soaked into a
+ generic domain power state.
+ CPU domains: Deep c-states that match a cluster power-off can be delegated to the
+ generic power domain. Device other than CPUs may have register intermediate
+ power states in the same domain. The domain governor can do a good job in
+ electing a power state when the last cpu is powered off as devices in the
+ same genpd may register intermediate states.
+ Devices : a device may register an intermediate c-state matching a memory
+ retention feature for instance.
+
Example:
power: power-controller@12340000 {
@@ -55,6 +65,25 @@ 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>;
+ cpu-idle-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>;
+ cpu-idle-states = <&CLUSTER_SLEEP_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.
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 6b2d771..8512e28 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1345,7 +1345,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
else {
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
atomic_inc(&genpd->usage_count);
- printk("Add device %d\n", atomic_read(&genpd->usage_count));
+ dev_info(dev, "Add device %d\n",
+ atomic_read(&genpd->usage_count));
}
return ret;
}
@@ -1592,7 +1593,7 @@ static int state_cmp(const void *a, const void *b)
int pm_genpd_insert_state(struct generic_pm_domain *genpd,
const struct genpd_power_state *state)
{
- int ret = 0;
+ int i, ret = 0;
int state_count = genpd->state_count;
if (IS_ERR_OR_NULL(genpd) || (!state))
@@ -1601,11 +1602,18 @@ int pm_genpd_insert_state(struct generic_pm_domain *genpd,
if (state_count >= GENPD_POWER_STATES_MAX)
ret = -ENOMEM;
+ /* Bail out, this state was already registered.*/
+ for (i = 0; i < state_count; i++)
+ if (!strncmp(state->name, genpd->states[i].name,
+ GENPD_MAX_NAME_SIZE))
+ return 0;
+
#ifdef CONFIG_PM_ADVANCED_DEBUG
/* to save memory, Name allocation will happen if debug is enabled */
genpd->states[state_count].name = kstrndup(state->name,
GENPD_MAX_NAME_SIZE,
GFP_KERNEL);
+
if (!genpd->states[state_count].name) {
pr_err("%s Failed to allocate state '%s' name.\n",
genpd->name, state->name);
@@ -1963,6 +1971,93 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd);
}
+
+static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
+ *genpd_state,
+ struct device_node *state_node)
+{
+ int err = 0;
+ u32 latency;
+
+ 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;
+}
+
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd)
+{
+ 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, "domain-idle-states", i);
+ if (!state_node)
+ break;
+
+ err = dt_cpuidle_to_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->name,
+ GENPD_MAX_NAME_SIZE, GFP_KERNEL);
+ if (!genpd_state.name)
+ err = -ENOMEM;
+#endif
+ of_node_put(state_node);
+ err = pm_genpd_insert_state(genpd, &genpd_state);
+ if (err)
+ break;
+#ifdef CONFIG_PM_ADVANCED_DEBUG
+ kfree(genpd_state.name);
+#endif
+ }
+ return err;
+}
+
/**
* genpd_dev_pm_attach - Attach a device to its PM domain using DT.
* @dev: Device to attach.
@@ -1996,7 +2091,6 @@ int genpd_dev_pm_attach(struct device *dev)
if (ret < 0) {
if (ret != -ENOENT)
return ret;
-
/*
* Try legacy Samsung-specific bindings
* (for backwards compatibility of DT ABI)
@@ -2034,6 +2128,8 @@ int genpd_dev_pm_attach(struct device *dev)
goto out;
}
+ of_genpd_device_parse_states(pd_args.np, pd);
+
dev->pm_domain->detach = genpd_dev_pm_detach;
dev->pm_domain->sync = genpd_dev_pm_sync;
ret = genpd_poweron(pd);
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 24621be..48ab3b1 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -248,6 +248,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
struct of_phandle_args *genpdspec,
void *data);
+int of_genpd_device_parse_states(struct device_node *np,
+ struct generic_pm_domain *genpd);
+
int genpd_dev_pm_attach(struct device *dev);
#else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
static inline int __of_genpd_add_provider(struct device_node *np,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 3/7] arm64: dts: Add idle-states for Juno
2015-10-19 20:58 ` Lina Iyer
` (3 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 4/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
` (3 subsequent siblings)
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel, Jon Medhurst
From: Jon Medhurst <tixy@linaro.org>
Signed-off-by: Jon Medhurst <tixy@linaro.org>
---
arch/arm64/boot/dts/arm/juno.dts | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 53442b5..b3fcee8 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -60,6 +60,29 @@
};
};
+ idle-states {
+
+ entry-method = "arm,psci";
+
+ CPU_SLEEP_0: cpu-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x0010000>;
+ local-timer-stop;
+ entry-latency-us = <100>;
+ exit-latency-us = <250>;
+ min-residency-us = <2000>;
+ };
+
+ CLUSTER_SLEEP_0: cluster-sleep-0 {
+ compatible = "arm,idle-state";
+ arm,psci-suspend-param = <0x1010000>;
+ local-timer-stop;
+ entry-latency-us = <800>;
+ exit-latency-us = <700>;
+ min-residency-us = <2500>;
+ };
+ };
+
A57_0: cpu@0 {
compatible = "arm,cortex-a57","arm,armv8";
reg = <0x0 0x0>;
@@ -67,6 +90,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A57_1: cpu@1 {
@@ -76,6 +100,7 @@
enable-method = "psci";
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_0: cpu@100 {
@@ -85,6 +110,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_1: cpu@101 {
@@ -94,6 +120,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_2: cpu@102 {
@@ -103,6 +130,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A53_3: cpu@103 {
@@ -112,6 +140,7 @@
enable-method = "psci";
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
+ cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
};
A57_L2: l2-cache0 {
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 4/7] arm64: Juno: declare generic power domains for both clusters.
2015-10-19 20:58 ` Lina Iyer
` (4 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 3/7] arm64: dts: Add idle-states for Juno Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 5/7] drivers: cpu-pd: allow calling of_cpu_pd_init from platform code Marc Titinger
` (2 subsequent siblings)
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index b3fcee8..0a72c07 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -91,6 +91,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A57_1: cpu@1 {
@@ -101,6 +102,7 @@
next-level-cache = <&A57_L2>;
clocks = <&scpi_dvfs 0>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a57_pd>;
};
A53_0: cpu@100 {
@@ -111,6 +113,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_1: cpu@101 {
@@ -121,6 +124,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_2: cpu@102 {
@@ -131,6 +135,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A53_3: cpu@103 {
@@ -141,6 +146,7 @@
next-level-cache = <&A53_L2>;
clocks = <&scpi_dvfs 1>;
cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>;
+ power-domains = <&a53_pd>;
};
A57_L2: l2-cache0 {
@@ -152,6 +158,19 @@
};
};
+ CPU_PD: cpu-domains {
+
+ a57_pd: a57_pd@ {
+ compatible = "arm,cpu-pd";
+ #power-domain-cells = <0>;
+ };
+
+ a53_pd: a53_pd@ {
+ compatible = "arm,cpu-pd";
+ #power-domain-cells = <0>;
+ };
+ };
+
pmu_a57 {
compatible = "arm,cortex-a57-pmu";
interrupts = <GIC_SPI 02 IRQ_TYPE_LEVEL_HIGH>,
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 5/7] drivers: cpu-pd: allow calling of_cpu_pd_init from platform code.
2015-10-19 20:58 ` Lina Iyer
` (5 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 4/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 6/7] arm64: PM /Domains: Initialize CPU-domains from DT Marc Titinger
2015-10-27 17:40 ` [RFC v3 7/7] arm64: Juno: declare idle-state cluster-sleep-0 as genpd state Marc Titinger
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
re-instate a two step init, first register the CPU-power domains, then
attach the CPUs, because CPU ordering does not match cluster/PD boundaries.
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
drivers/base/power/cpu-pd.c | 29 ++++++++++++-----------------
include/linux/cpu-pd.h | 2 ++
2 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/drivers/base/power/cpu-pd.c b/drivers/base/power/cpu-pd.c
index eddee98..701a68f 100644
--- a/drivers/base/power/cpu-pd.c
+++ b/drivers/base/power/cpu-pd.c
@@ -8,8 +8,6 @@
* published by the Free Software Foundation.
*/
-#define DEBUG
-
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/cpu.h>
@@ -160,8 +158,6 @@ static int cpu_hotplug(struct notifier_block *nb,
int of_register_cpu_pm_domain(struct device_node *dn,
struct cpu_pm_domain *pd)
{
- int ret;
-
if (!pd || !pd->genpd)
return -EINVAL;
@@ -187,14 +183,6 @@ int of_register_cpu_pm_domain(struct device_node *dn,
pm_genpd_init_simple(pd->genpd, &simple_qos_governor, false);
of_genpd_add_provider_simple(dn, pd->genpd);
- /* Attach the CPUs to the CPU PM domain */
- ret = of_pm_domain_attach_cpus();
- if (ret) {
- of_genpd_del_provider(dn);
- return ret;
- }
-
- hotcpu_notifier(cpu_hotplug, 0)
return 0;
}
EXPORT_SYMBOL(of_register_cpu_pm_domain);
@@ -245,21 +233,28 @@ int of_init_cpu_pm_domain(struct device_node *dn, struct cpu_pm_ops *ops)
EXPORT_SYMBOL(of_init_cpu_pm_domain);
-static int __init of_cpu_pd_init(void)
+int __init of_cpu_pd_init(const char *compatible)
{
struct device_node *dn;
int ret;
- for_each_compatible_node(dn, NULL, "cpu,pd") {
+ if (!compatible)
+ return -EINVAL;
+
+ for_each_compatible_node(dn, NULL, compatible) {
if (!of_device_is_available(dn))
continue;
ret = of_init_cpu_pm_domain(dn, NULL);
- if (!ret)
+ if (ret)
return ret;
}
- return 0;
+
+ ret = of_pm_domain_attach_cpus();
+ if (!ret)
+ hotcpu_notifier(cpu_hotplug, 0);
+
+ return ret;
}
-device_initcall(of_cpu_pd_init);
diff --git a/include/linux/cpu-pd.h b/include/linux/cpu-pd.h
index 9ae6f5b..f3066d0 100644
--- a/include/linux/cpu-pd.h
+++ b/include/linux/cpu-pd.h
@@ -32,4 +32,6 @@ extern int of_register_cpu_pm_domain(struct device_node *dn,
extern int of_init_cpu_pm_domain(struct device_node *dn,
struct cpu_pm_ops *ops);
+extern int __init of_cpu_pd_init(const char *compatible);
+
#endif /* __CPU_PD_H__ */
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 6/7] arm64: PM /Domains: Initialize CPU-domains from DT.
2015-10-19 20:58 ` Lina Iyer
` (6 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 5/7] drivers: cpu-pd: allow calling of_cpu_pd_init from platform code Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 7/7] arm64: Juno: declare idle-state cluster-sleep-0 as genpd state Marc Titinger
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
arch/arm64/kernel/Makefile | 1 +
arch/arm64/kernel/cpu_domain.c | 19 +++++++++++++++++++
2 files changed, 20 insertions(+)
create mode 100644 arch/arm64/kernel/cpu_domain.c
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 1b6bda2..5f8b59f 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -46,6 +46,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o $(stub-obj)
arm64-obj-$(CONFIG_PCI) += pci.o
arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
arm64-obj-$(CONFIG_ACPI) += acpi.o
+arm64-obj-$(CONFIG_PM_GENERIC_DOMAINS) += cpu_domain.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/cpu_domain.c b/arch/arm64/kernel/cpu_domain.c
new file mode 100644
index 0000000..06a3949
--- /dev/null
+++ b/arch/arm64/kernel/cpu_domain.c
@@ -0,0 +1,19 @@
+/*
+ * 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 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.
+ */
+#include <linux/cpu-pd.h>
+
+static int __init arm64_cpu_pd_init(void)
+{
+ return of_cpu_pd_init("arm,cpu-pd");
+}
+device_initcall(arm64_cpu_pd_init);
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* [RFC v3 7/7] arm64: Juno: declare idle-state cluster-sleep-0 as genpd state
2015-10-19 20:58 ` Lina Iyer
` (7 preceding siblings ...)
2015-10-27 17:40 ` [RFC v3 6/7] arm64: PM /Domains: Initialize CPU-domains from DT Marc Titinger
@ 2015-10-27 17:40 ` Marc Titinger
8 siblings, 0 replies; 39+ messages in thread
From: Marc Titinger @ 2015-10-27 17:40 UTC (permalink / raw)
To: lina.iyer
Cc: rjw, khilman, ahaslam, bcousson, linux-pm, linux-kernel,
Marc Titinger, Marc Titinger
From: Marc Titinger <mtitinger@baylibre.com>
Using Juno to exercise the code that prepares for OS-initiated idle-state
handling, using genpd platform callbacks. In opposition to platform-
initiated mode as currently with pcsi/ATF.
Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
---
arch/arm64/boot/dts/arm/juno.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
index 0a72c07..f42d5f9 100644
--- a/arch/arm64/boot/dts/arm/juno.dts
+++ b/arch/arm64/boot/dts/arm/juno.dts
@@ -163,11 +163,13 @@
a57_pd: a57_pd@ {
compatible = "arm,cpu-pd";
#power-domain-cells = <0>;
+ domain-idle-states = <&CLUSTER_SLEEP_0>;
};
a53_pd: a53_pd@ {
compatible = "arm,cpu-pd";
#power-domain-cells = <0>;
+ domain-idle-states = <&CLUSTER_SLEEP_0>;
};
};
--
1.9.1
^ permalink raw reply related [flat|nested] 39+ messages in thread
* Re: [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states'
2015-10-06 14:27 ` [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
@ 2015-11-11 9:10 ` Zhaoyang Huang
2015-11-11 17:27 ` Lina Iyer
0 siblings, 1 reply; 39+ messages in thread
From: Zhaoyang Huang @ 2015-11-11 9:10 UTC (permalink / raw)
To: Marc Titinger
Cc: khilman, rjw, Lina Iyer, Axel Haslam, Benoit Cousson, linux-pm,
linux-kernel
On 6 October 2015 at 22:27, Marc Titinger <mtitinger@baylibre.com> wrote:
> From: Marc Titinger <mtitinger@baylibre.com>
>
> Cpuidle now handles c-states and power-states differently. c-states do not decrement
> the reference count for the CPUs in the cluster, while power-states i.e.
> cluster level states like 'CLUSTER_SLEEP_0' in the case of juno, will.
>
> The 'D1' fake device also registers intermediate power-state,
> for experimentation.
>
> Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
> ---
> arch/arm64/boot/dts/arm/juno.dts | 2 +-
> drivers/cpuidle/cpuidle-arm.c | 52 ++++++++++++++++++++++++++++++++--------
> 2 files changed, 43 insertions(+), 11 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
> index cadc5de..0bb0dd7 100644
> --- a/arch/arm64/boot/dts/arm/juno.dts
> +++ b/arch/arm64/boot/dts/arm/juno.dts
> @@ -47,7 +47,7 @@
> };
>
> CLUSTER_SLEEP_0: cluster-sleep-0 {
> - compatible = "arm,idle-state","arm,power-state";
> + compatible = "arm,power-state";
> arm,psci-suspend-param = <0x1010000>;
> local-timer-stop;
> entry-latency-us = <800>;
> diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
> index 7c791f9..8dd5dc3 100644
> --- a/drivers/cpuidle/cpuidle-arm.c
> +++ b/drivers/cpuidle/cpuidle-arm.c
> @@ -40,7 +40,6 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
> struct cpuidle_driver *drv, int idx)
> {
> int ret;
> - struct device *cpu_dev = get_cpu_device(dev->cpu);
>
> if (!idx) {
> cpu_do_idle();
> @@ -50,18 +49,49 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
> ret = cpu_pm_enter();
> if (!ret) {
> /*
> - * Notify runtime PM as well of this cpu powering down
> - * TODO: Merge CPU_PM and runtime PM.
> - */
> - RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
> -
> - /*
> * Pass idle state index to cpu_suspend which in turn will
> * call the CPU ops suspend protocol with idle index as a
> * parameter.
> */
> arm_cpuidle_suspend(idx);
>
> + cpu_pm_exit();
> + }
> +
> + return ret ? -1 : idx;
> +}
> +
> +/*
> + * arm_enter_power_state - delegate state trasition to genpd
> + *
> + * dev: cpuidle device
> + * drv: cpuidle driver
> + * idx: state index
> + *
> + * Called from the CPUidle framework to delegate a state transition
> + * to the generic domain. This will be a cluster poweroff state
> + * the Domain will chose to actually turn off the cluster based on
> + * the status of other CPUs, and devices and subdomains in the Cluster
> + * domain.
> +*/
> +static int arm_enter_power_state(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv, int idx)
> +{
> + int ret;
> + struct device *cpu_dev = get_cpu_device(dev->cpu);
> +
> + BUG_ON(idx == 0);
> +
> + ret = cpu_pm_enter();
> + if (!ret) {
> + /*
> + * Notify runtime PM as well of this cpu powering down
> + * TODO: Merge CPU_PM and runtime PM.
> + */
> + RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
[question]: Does it mean that above function will use the gpd->rpm->idle?
> +
> + arm_cpuidle_suspend(idx);
> +
> RCU_NONIDLE(pm_runtime_get_sync(cpu_dev));
> cpu_pm_exit();
> }
> @@ -69,6 +99,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
> return ret ? -1 : idx;
> }
>
> +
> static struct cpuidle_driver arm_idle_driver = {
> .name = "arm_idle",
> .owner = THIS_MODULE,
> @@ -90,9 +121,10 @@ static struct cpuidle_driver arm_idle_driver = {
> };
>
> static const struct of_device_id arm_idle_state_match[] __initconst = {
> - { .compatible = "arm,idle-state",
> - .data = arm_enter_idle_state },
> - { },
> + {.compatible = "arm,idle-state",
> + .data = arm_enter_idle_state},
> + {.compatible = "arm,power-state",
> + .data = arm_enter_power_state},
> };
>
> /*
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states'
2015-11-11 9:10 ` Zhaoyang Huang
@ 2015-11-11 17:27 ` Lina Iyer
0 siblings, 0 replies; 39+ messages in thread
From: Lina Iyer @ 2015-11-11 17:27 UTC (permalink / raw)
To: Zhaoyang Huang
Cc: Marc Titinger, khilman, rjw, Axel Haslam, Benoit Cousson,
linux-pm, linux-kernel
On Wed, Nov 11 2015 at 02:10 -0700, Zhaoyang Huang wrote:
>On 6 October 2015 at 22:27, Marc Titinger <mtitinger@baylibre.com> wrote:
>> From: Marc Titinger <mtitinger@baylibre.com>
>>
>> Cpuidle now handles c-states and power-states differently. c-states do not decrement
>> the reference count for the CPUs in the cluster, while power-states i.e.
>> cluster level states like 'CLUSTER_SLEEP_0' in the case of juno, will.
>>
>> The 'D1' fake device also registers intermediate power-state,
>> for experimentation.
>>
>> Signed-off-by: Marc Titinger <mtitinger@baylibre.com>
>> ---
>> arch/arm64/boot/dts/arm/juno.dts | 2 +-
>> drivers/cpuidle/cpuidle-arm.c | 52 ++++++++++++++++++++++++++++++++--------
>> 2 files changed, 43 insertions(+), 11 deletions(-)
>>
>> diff --git a/arch/arm64/boot/dts/arm/juno.dts b/arch/arm64/boot/dts/arm/juno.dts
>> index cadc5de..0bb0dd7 100644
>> --- a/arch/arm64/boot/dts/arm/juno.dts
>> +++ b/arch/arm64/boot/dts/arm/juno.dts
>> @@ -47,7 +47,7 @@
>> };
>>
>> CLUSTER_SLEEP_0: cluster-sleep-0 {
>> - compatible = "arm,idle-state","arm,power-state";
>> + compatible = "arm,power-state";
>> arm,psci-suspend-param = <0x1010000>;
>> local-timer-stop;
>> entry-latency-us = <800>;
>> diff --git a/drivers/cpuidle/cpuidle-arm.c b/drivers/cpuidle/cpuidle-arm.c
>> index 7c791f9..8dd5dc3 100644
>> --- a/drivers/cpuidle/cpuidle-arm.c
>> +++ b/drivers/cpuidle/cpuidle-arm.c
>> @@ -40,7 +40,6 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
>> struct cpuidle_driver *drv, int idx)
>> {
>> int ret;
>> - struct device *cpu_dev = get_cpu_device(dev->cpu);
>>
>> if (!idx) {
>> cpu_do_idle();
>> @@ -50,18 +49,49 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
>> ret = cpu_pm_enter();
>> if (!ret) {
>> /*
>> - * Notify runtime PM as well of this cpu powering down
>> - * TODO: Merge CPU_PM and runtime PM.
>> - */
>> - RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
>> -
>> - /*
>> * Pass idle state index to cpu_suspend which in turn will
>> * call the CPU ops suspend protocol with idle index as a
>> * parameter.
>> */
>> arm_cpuidle_suspend(idx);
>>
>> + cpu_pm_exit();
>> + }
>> +
>> + return ret ? -1 : idx;
>> +}
>> +
>> +/*
>> + * arm_enter_power_state - delegate state trasition to genpd
>> + *
>> + * dev: cpuidle device
>> + * drv: cpuidle driver
>> + * idx: state index
>> + *
>> + * Called from the CPUidle framework to delegate a state transition
>> + * to the generic domain. This will be a cluster poweroff state
>> + * the Domain will chose to actually turn off the cluster based on
>> + * the status of other CPUs, and devices and subdomains in the Cluster
>> + * domain.
>> +*/
>> +static int arm_enter_power_state(struct cpuidle_device *dev,
>> + struct cpuidle_driver *drv, int idx)
>> +{
>> + int ret;
>> + struct device *cpu_dev = get_cpu_device(dev->cpu);
>> +
>> + BUG_ON(idx == 0);
>> +
>> + ret = cpu_pm_enter();
>> + if (!ret) {
>> + /*
>> + * Notify runtime PM as well of this cpu powering down
>> + * TODO: Merge CPU_PM and runtime PM.
>> + */
>> + RCU_NONIDLE(pm_runtime_put_sync(cpu_dev));
>[question]: Does it mean that above function will use the gpd->rpm->idle?
Will end up at rpm_suspend() in runtime.c which will callinto genpd. Its
not a direct call to genpd.
Thanks,
Lina
>> +
>> + arm_cpuidle_suspend(idx);
>> +
>> RCU_NONIDLE(pm_runtime_get_sync(cpu_dev));
>> cpu_pm_exit();
>> }
>> @@ -69,6 +99,7 @@ static int arm_enter_idle_state(struct cpuidle_device *dev,
>> return ret ? -1 : idx;
>> }
>>
>> +
>> static struct cpuidle_driver arm_idle_driver = {
>> .name = "arm_idle",
>> .owner = THIS_MODULE,
>> @@ -90,9 +121,10 @@ static struct cpuidle_driver arm_idle_driver = {
>> };
>>
>> static const struct of_device_id arm_idle_state_match[] __initconst = {
>> - { .compatible = "arm,idle-state",
>> - .data = arm_enter_idle_state },
>> - { },
>> + {.compatible = "arm,idle-state",
>> + .data = arm_enter_idle_state},
>> + {.compatible = "arm,power-state",
>> + .data = arm_enter_power_state},
>> };
>>
>> /*
>> --
>> 1.9.1
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state.
2015-10-27 17:40 ` [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state Marc Titinger
@ 2015-11-13 5:56 ` Zhaoyang Huang
0 siblings, 0 replies; 39+ messages in thread
From: Zhaoyang Huang @ 2015-11-13 5:56 UTC (permalink / raw)
To: Marc Titinger
Cc: Lina Iyer, rjw, khilman, Axel Haslam, Benoit Cousson, linux-pm,
linux-kernel, Marc Titinger
On 28 October 2015 at 01:40, Marc Titinger <mtitinger@baylibre.com> wrote:
> From: Marc Titinger <mtitinger@baylibre.com>
>
> 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
>
> Signed-off-by: Marc Titinger <mtitinger+renesas@baylibre.com>
> ---
> .../devicetree/bindings/power/power_domain.txt | 29 ++++++
> drivers/base/power/domain.c | 102 ++++++++++++++++++++-
> include/linux/pm_domain.h | 3 +
> 3 files changed, 131 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/power/power_domain.txt b/Documentation/devicetree/bindings/power/power_domain.txt
> index 025b5e7..2657e19 100644
> --- a/Documentation/devicetree/bindings/power/power_domain.txt
> +++ b/Documentation/devicetree/bindings/power/power_domain.txt
> @@ -29,6 +29,16 @@ Optional properties:
> specified by this binding. More details about power domain specifier are
> available in the next section.
>
> + - cpu-idle-states : a phandle of an idle-state that shall be soaked into a
> + generic domain power state.
> + CPU domains: Deep c-states that match a cluster power-off can be delegated to the
> + generic power domain. Device other than CPUs may have register intermediate
> + power states in the same domain. The domain governor can do a good job in
> + electing a power state when the last cpu is powered off as devices in the
> + same genpd may register intermediate states.
> + Devices : a device may register an intermediate c-state matching a memory
> + retention feature for instance.
> +
> Example:
>
> power: power-controller@12340000 {
> @@ -55,6 +65,25 @@ 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>;
> + cpu-idle-states = <&CLUSTER_SLEEP_0>;
Should the "cpu_idle_states" be "domain-idle-states", which is
consistent to the following code.
> + };
> +
> + a53_pd: a53_pd@ {
> + /* will have a a53 platform ARM_PD_METHOD_OF_DECLARE*/
> + compatible = "arm,pd","arm,cortex-a53";
> + #power-domain-cells = <0>;
> + cpu-idle-states = <&CLUSTER_SLEEP_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.
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index 6b2d771..8512e28 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -1345,7 +1345,8 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
> else {
> dev_pm_qos_add_notifier(dev, &gpd_data->nb);
> atomic_inc(&genpd->usage_count);
> - printk("Add device %d\n", atomic_read(&genpd->usage_count));
> + dev_info(dev, "Add device %d\n",
> + atomic_read(&genpd->usage_count));
> }
> return ret;
> }
> @@ -1592,7 +1593,7 @@ static int state_cmp(const void *a, const void *b)
> int pm_genpd_insert_state(struct generic_pm_domain *genpd,
> const struct genpd_power_state *state)
> {
> - int ret = 0;
> + int i, ret = 0;
> int state_count = genpd->state_count;
>
> if (IS_ERR_OR_NULL(genpd) || (!state))
> @@ -1601,11 +1602,18 @@ int pm_genpd_insert_state(struct generic_pm_domain *genpd,
> if (state_count >= GENPD_POWER_STATES_MAX)
> ret = -ENOMEM;
>
> + /* Bail out, this state was already registered.*/
> + for (i = 0; i < state_count; i++)
> + if (!strncmp(state->name, genpd->states[i].name,
> + GENPD_MAX_NAME_SIZE))
> + return 0;
> +
> #ifdef CONFIG_PM_ADVANCED_DEBUG
> /* to save memory, Name allocation will happen if debug is enabled */
> genpd->states[state_count].name = kstrndup(state->name,
> GENPD_MAX_NAME_SIZE,
> GFP_KERNEL);
> +
> if (!genpd->states[state_count].name) {
> pr_err("%s Failed to allocate state '%s' name.\n",
> genpd->name, state->name);
> @@ -1963,6 +1971,93 @@ static void genpd_dev_pm_sync(struct device *dev)
> genpd_queue_power_off_work(pd);
> }
>
> +
> +static int dt_cpuidle_to_genpd_power_state(struct genpd_power_state
> + *genpd_state,
> + struct device_node *state_node)
> +{
> + int err = 0;
> + u32 latency;
> +
> + 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;
> +}
> +
> +int of_genpd_device_parse_states(struct device_node *np,
> + struct generic_pm_domain *genpd)
> +{
> + 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, "domain-idle-states", i);
> + if (!state_node)
> + break;
> +
> + err = dt_cpuidle_to_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->name,
> + GENPD_MAX_NAME_SIZE, GFP_KERNEL);
> + if (!genpd_state.name)
> + err = -ENOMEM;
> +#endif
> + of_node_put(state_node);
> + err = pm_genpd_insert_state(genpd, &genpd_state);
> + if (err)
> + break;
> +#ifdef CONFIG_PM_ADVANCED_DEBUG
> + kfree(genpd_state.name);
> +#endif
> + }
> + return err;
> +}
> +
> /**
> * genpd_dev_pm_attach - Attach a device to its PM domain using DT.
> * @dev: Device to attach.
> @@ -1996,7 +2091,6 @@ int genpd_dev_pm_attach(struct device *dev)
> if (ret < 0) {
> if (ret != -ENOENT)
> return ret;
> -
> /*
> * Try legacy Samsung-specific bindings
> * (for backwards compatibility of DT ABI)
> @@ -2034,6 +2128,8 @@ int genpd_dev_pm_attach(struct device *dev)
> goto out;
> }
>
> + of_genpd_device_parse_states(pd_args.np, pd);
> +
> dev->pm_domain->detach = genpd_dev_pm_detach;
> dev->pm_domain->sync = genpd_dev_pm_sync;
> ret = genpd_poweron(pd);
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 24621be..48ab3b1 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -248,6 +248,9 @@ struct generic_pm_domain *__of_genpd_xlate_onecell(
> struct of_phandle_args *genpdspec,
> void *data);
>
> +int of_genpd_device_parse_states(struct device_node *np,
> + struct generic_pm_domain *genpd);
> +
> int genpd_dev_pm_attach(struct device *dev);
> #else /* !CONFIG_PM_GENERIC_DOMAINS_OF */
> static inline int __of_genpd_add_provider(struct device_node *np,
> --
> 1.9.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 39+ messages in thread
end of thread, other threads:[~2015-11-13 5:56 UTC | newest]
Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-25 13:04 [RFC 0/7] Managing cluser-level c-states with generic power domains Marc Titinger
2015-09-25 13:04 ` [RFC 1/7] arm64: pm/domains: try mutualize CPU domains init between arm/arm64 Marc Titinger
2015-10-06 2:27 ` Lina Iyer
2015-10-06 8:52 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Marc Titinger
2015-10-06 14:27 ` [RFC v2 1/6] arm64: Juno: declare generic power domains for both clusters Marc Titinger
2015-10-06 14:27 ` [RFC v2 2/6] PM / Domains: prepare for devices that might register a power state Marc Titinger
2015-10-08 16:11 ` Lina Iyer
2015-10-09 9:39 ` Marc Titinger
2015-10-09 18:22 ` Lina Iyer
2015-10-13 10:29 ` Marc Titinger
2015-10-13 21:03 ` Kevin Hilman
2015-10-06 14:27 ` [RFC v2 3/6] PM / Domains: introduce power-states consistent with c-states Marc Titinger
2015-10-08 16:27 ` Lina Iyer
2015-10-09 10:04 ` Marc Titinger
2015-10-06 14:27 ` [RFC v2 4/6] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
2015-10-06 14:27 ` [RFC v2 5/6] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
2015-11-11 9:10 ` Zhaoyang Huang
2015-11-11 17:27 ` Lina Iyer
2015-10-06 14:27 ` [RFC v2 6/6] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
2015-10-13 23:10 ` [RFC v2 0/6] Managing cluser-level c-states with generic power domains Kevin Hilman
2015-10-14 8:10 ` Axel Haslam
2015-10-19 20:58 ` Lina Iyer
2015-10-20 9:10 ` Marc Titinger
2015-10-27 17:40 ` [RFC v3 0/7] Managing cluser-level idle-states " Marc Titinger
2015-10-27 17:40 ` [RFC v3 1/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
2015-10-27 17:40 ` [RFC v3 2/7] PM / Domains: support idle-states as genpd multiple-state Marc Titinger
2015-11-13 5:56 ` Zhaoyang Huang
2015-10-27 17:40 ` [RFC v3 3/7] arm64: dts: Add idle-states for Juno Marc Titinger
2015-10-27 17:40 ` [RFC v3 4/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
2015-10-27 17:40 ` [RFC v3 5/7] drivers: cpu-pd: allow calling of_cpu_pd_init from platform code Marc Titinger
2015-10-27 17:40 ` [RFC v3 6/7] arm64: PM /Domains: Initialize CPU-domains from DT Marc Titinger
2015-10-27 17:40 ` [RFC v3 7/7] arm64: Juno: declare idle-state cluster-sleep-0 as genpd state Marc Titinger
2015-09-25 13:04 ` [RFC 2/7] arm64: Juno: declare generic power domains for both clusters Marc Titinger
2015-09-25 13:04 ` [RFC 3/7] PM / Domains: prepare for devices that might register a power state Marc Titinger
2015-09-25 13:04 ` [RFC 4/7] PM / Domains: introduce power-states consistent with c-states Marc Titinger
2015-09-25 13:04 ` [RFC 5/7] PM / Domains: succeed & warn when attaching non-irqsafe devices to an irq-safe domain Marc Titinger
2015-09-25 13:04 ` [RFC 6/7] arm: cpuidle: let genpd handle the cluster power transition with 'power-states' Marc Titinger
2015-09-25 13:04 ` [RFC 7/7] PM / Domains: add debugfs 'states' and 'timings' seq files Marc Titinger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).