From: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> To: linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: devicetree@vger.kernel.org, Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>, Mark Rutland <mark.rutland@arm.com>, Sudeep Holla <sudeep.holla@arm.com>, Catalin Marinas <catalin.marinas@arm.com>, Charles Garcia Tobin <Charles.Garcia-Tobin@arm.com>, Nicolas Pitre <nico@linaro.org>, Rob Herring <robh+dt@kernel.org>, Grant Likely <grant.likely@linaro.org>, Peter De Schrijver <pdeschrijver@nvidia.com>, Santosh Shilimkar <santosh.shilimkar@ti.com>, Daniel Lezcano <daniel.lezcano@linaro.org>, Amit Kucheria <amit.kucheria@linaro.org>, Vincent Guittot <vincent.guittot@linaro.org>, Antti Miettinen <ananaza@iki.fi>, Stephen Boyd <sboyd@codeaurora.org>, Kevin Hilman <khilman@linaro.org>, Sebastian Capella <sebcape@gmail.com>, Tomasz Figa <t.figa@samsung.com>, Mark Brown <broonie@kernel.org>, Paul Walmsley <paul@pwsan.com>, Chander Kashyap <k.chander@samsung.com>, Geoff Subject: [PATCH v8 5/8] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Date: Mon, 1 Sep 2014 16:28:41 +0100 [thread overview] Message-ID: <1409585324-3678-6-git-send-email-lorenzo.pieralisi@arm.com> (raw) In-Reply-To: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> This patch implements the cpu_suspend cpu operations method through the PSCI CPU SUSPEND API. The PSCI implementation translates the idle state index passed by the cpu_suspend core call into a valid PSCI state according to the PSCI states initialized at boot through the cpu_init_idle() CPU operations hook. The PSCI CPU suspend operation hook checks if the PSCI state is a standby state. If it is, it calls the PSCI suspend implementation straight away, without saving any context. If the state is a power down state the kernel calls the __cpu_suspend API (that saves the CPU context) and passed the PSCI suspend finisher as a parameter so that PSCI can be called by the __cpu_suspend implementation after saving and flushing the context as last function before power down. For power down states, entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset. Reviewed-by: Ashwin Chaugule <ashwin.chaugule@linaro.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> --- arch/arm64/kernel/psci.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 5539547..866c1c8 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -21,6 +21,7 @@ #include <linux/reboot.h> #include <linux/pm.h> #include <linux/delay.h> +#include <linux/slab.h> #include <uapi/linux/psci.h> #include <asm/compiler.h> @@ -28,6 +29,7 @@ #include <asm/errno.h> #include <asm/psci.h> #include <asm/smp_plat.h> +#include <asm/suspend.h> #include <asm/system_misc.h> #define PSCI_POWER_STATE_TYPE_STANDBY 0 @@ -65,6 +67,8 @@ enum psci_function { PSCI_FN_MAX, }; +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); + static u32 psci_function_id[PSCI_FN_MAX]; static int psci_to_linux_errno(int errno) @@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state) & PSCI_0_2_POWER_STATE_AFFL_MASK); } +static void psci_power_state_unpack(u32 power_state, + struct psci_power_state *state) +{ + state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >> + PSCI_0_2_POWER_STATE_ID_SHIFT; + state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >> + PSCI_0_2_POWER_STATE_TYPE_SHIFT; + state->affinity_level = + (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >> + PSCI_0_2_POWER_STATE_AFFL_SHIFT; +} + /* * The following two functions are invoked via the invoke_psci_fn pointer * and will not be inlined, allowing us to piggyback on the AAPCS. @@ -199,6 +215,63 @@ static int psci_migrate_info_type(void) return err; } +static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node, + unsigned int cpu) +{ + int i, ret, count = 0; + struct psci_power_state *psci_states; + struct device_node *state_node; + + /* + * If the PSCI cpu_suspend function hook has not been initialized + * idle states must not be enabled, so bail out + */ + if (!psci_ops.cpu_suspend) + return -EOPNOTSUPP; + + /* Count idle states */ + while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", + count))) { + count++; + of_node_put(state_node); + } + + if (!count) + return -ENODEV; + + psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); + if (!psci_states) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u32 psci_power_state; + + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + + ret = of_property_read_u32(state_node, + "arm,psci-suspend-param", + &psci_power_state); + if (ret) { + pr_warn(" * %s missing arm,psci-suspend-param property\n", + state_node->full_name); + of_node_put(state_node); + goto free_mem; + } + + of_node_put(state_node); + pr_debug("psci-power-state %#x index %d\n", psci_power_state, + i); + psci_power_state_unpack(psci_power_state, &psci_states[i]); + } + /* Idle states parsed correctly, initialize per-cpu pointer */ + per_cpu(psci_power_state, cpu) = psci_states; + return 0; + +free_mem: + kfree(psci_states); + return ret; +} + static int get_set_conduit_method(struct device_node *np) { const char *method; @@ -436,8 +509,39 @@ static int cpu_psci_cpu_kill(unsigned int cpu) #endif #endif +static int psci_suspend_finisher(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + return psci_ops.cpu_suspend(state[index - 1], + virt_to_phys(cpu_resume)); +} + +static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) +{ + int ret; + struct psci_power_state *state = __get_cpu_var(psci_power_state); + /* + * idle state index 0 corresponds to wfi, should never be called + * from the cpu_suspend operations + */ + if (WARN_ON_ONCE(!index)) + return -EINVAL; + + if (state->type == PSCI_POWER_STATE_TYPE_STANDBY) + ret = psci_ops.cpu_suspend(state[index - 1], 0); + else + ret = __cpu_suspend(index, psci_suspend_finisher); + + return ret; +} + const struct cpu_operations cpu_psci_ops = { .name = "psci", +#ifdef CONFIG_CPU_IDLE + .cpu_init_idle = cpu_psci_cpu_init_idle, + .cpu_suspend = cpu_psci_cpu_suspend, +#endif #ifdef CONFIG_SMP .cpu_init = cpu_psci_cpu_init, .cpu_prepare = cpu_psci_cpu_prepare, -- 1.9.1
WARNING: multiple messages have this Message-ID (diff)
From: lorenzo.pieralisi@arm.com (Lorenzo Pieralisi) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v8 5/8] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Date: Mon, 1 Sep 2014 16:28:41 +0100 [thread overview] Message-ID: <1409585324-3678-6-git-send-email-lorenzo.pieralisi@arm.com> (raw) In-Reply-To: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> This patch implements the cpu_suspend cpu operations method through the PSCI CPU SUSPEND API. The PSCI implementation translates the idle state index passed by the cpu_suspend core call into a valid PSCI state according to the PSCI states initialized at boot through the cpu_init_idle() CPU operations hook. The PSCI CPU suspend operation hook checks if the PSCI state is a standby state. If it is, it calls the PSCI suspend implementation straight away, without saving any context. If the state is a power down state the kernel calls the __cpu_suspend API (that saves the CPU context) and passed the PSCI suspend finisher as a parameter so that PSCI can be called by the __cpu_suspend implementation after saving and flushing the context as last function before power down. For power down states, entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset. Reviewed-by: Ashwin Chaugule <ashwin.chaugule@linaro.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> --- arch/arm64/kernel/psci.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 5539547..866c1c8 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -21,6 +21,7 @@ #include <linux/reboot.h> #include <linux/pm.h> #include <linux/delay.h> +#include <linux/slab.h> #include <uapi/linux/psci.h> #include <asm/compiler.h> @@ -28,6 +29,7 @@ #include <asm/errno.h> #include <asm/psci.h> #include <asm/smp_plat.h> +#include <asm/suspend.h> #include <asm/system_misc.h> #define PSCI_POWER_STATE_TYPE_STANDBY 0 @@ -65,6 +67,8 @@ enum psci_function { PSCI_FN_MAX, }; +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); + static u32 psci_function_id[PSCI_FN_MAX]; static int psci_to_linux_errno(int errno) @@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state state) & PSCI_0_2_POWER_STATE_AFFL_MASK); } +static void psci_power_state_unpack(u32 power_state, + struct psci_power_state *state) +{ + state->id = (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >> + PSCI_0_2_POWER_STATE_ID_SHIFT; + state->type = (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >> + PSCI_0_2_POWER_STATE_TYPE_SHIFT; + state->affinity_level = + (power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >> + PSCI_0_2_POWER_STATE_AFFL_SHIFT; +} + /* * The following two functions are invoked via the invoke_psci_fn pointer * and will not be inlined, allowing us to piggyback on the AAPCS. @@ -199,6 +215,63 @@ static int psci_migrate_info_type(void) return err; } +static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_node, + unsigned int cpu) +{ + int i, ret, count = 0; + struct psci_power_state *psci_states; + struct device_node *state_node; + + /* + * If the PSCI cpu_suspend function hook has not been initialized + * idle states must not be enabled, so bail out + */ + if (!psci_ops.cpu_suspend) + return -EOPNOTSUPP; + + /* Count idle states */ + while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", + count))) { + count++; + of_node_put(state_node); + } + + if (!count) + return -ENODEV; + + psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); + if (!psci_states) + return -ENOMEM; + + for (i = 0; i < count; i++) { + u32 psci_power_state; + + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + + ret = of_property_read_u32(state_node, + "arm,psci-suspend-param", + &psci_power_state); + if (ret) { + pr_warn(" * %s missing arm,psci-suspend-param property\n", + state_node->full_name); + of_node_put(state_node); + goto free_mem; + } + + of_node_put(state_node); + pr_debug("psci-power-state %#x index %d\n", psci_power_state, + i); + psci_power_state_unpack(psci_power_state, &psci_states[i]); + } + /* Idle states parsed correctly, initialize per-cpu pointer */ + per_cpu(psci_power_state, cpu) = psci_states; + return 0; + +free_mem: + kfree(psci_states); + return ret; +} + static int get_set_conduit_method(struct device_node *np) { const char *method; @@ -436,8 +509,39 @@ static int cpu_psci_cpu_kill(unsigned int cpu) #endif #endif +static int psci_suspend_finisher(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + return psci_ops.cpu_suspend(state[index - 1], + virt_to_phys(cpu_resume)); +} + +static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) +{ + int ret; + struct psci_power_state *state = __get_cpu_var(psci_power_state); + /* + * idle state index 0 corresponds to wfi, should never be called + * from the cpu_suspend operations + */ + if (WARN_ON_ONCE(!index)) + return -EINVAL; + + if (state->type == PSCI_POWER_STATE_TYPE_STANDBY) + ret = psci_ops.cpu_suspend(state[index - 1], 0); + else + ret = __cpu_suspend(index, psci_suspend_finisher); + + return ret; +} + const struct cpu_operations cpu_psci_ops = { .name = "psci", +#ifdef CONFIG_CPU_IDLE + .cpu_init_idle = cpu_psci_cpu_init_idle, + .cpu_suspend = cpu_psci_cpu_suspend, +#endif #ifdef CONFIG_SMP .cpu_init = cpu_psci_cpu_init, .cpu_prepare = cpu_psci_cpu_prepare, -- 1.9.1
next prev parent reply other threads:[~2014-09-01 15:28 UTC|newest] Thread overview: 66+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-09-01 15:28 [PATCH v8 0/8] ARM generic idle states Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-01 15:28 ` [PATCH v8 1/8] arm64: kernel: refactor the CPU suspend API for retention states Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi [not found] ` <1409585324-3678-1-git-send-email-lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org> 2014-09-01 15:28 ` [PATCH v8 2/8] Documentation: arm: define DT idle states bindings Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-01 15:28 ` [PATCH v8 3/8] drivers: cpuidle: implement DT based idle states infrastructure Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-03 13:25 ` Daniel Lezcano 2014-09-03 13:25 ` Daniel Lezcano 2014-09-03 17:30 ` Lorenzo Pieralisi 2014-09-03 17:30 ` Lorenzo Pieralisi 2014-09-01 15:28 ` [PATCH v8 4/8] arm64: kernel: introduce cpu_init_idle CPU operation Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-03 17:34 ` Lina Iyer 2014-09-03 17:34 ` Lina Iyer 2014-09-03 17:46 ` Lorenzo Pieralisi 2014-09-03 17:46 ` Lorenzo Pieralisi 2014-09-03 19:16 ` Lina Iyer 2014-09-03 19:16 ` Lina Iyer 2014-09-01 15:28 ` Lorenzo Pieralisi [this message] 2014-09-01 15:28 ` [PATCH v8 5/8] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Lorenzo Pieralisi 2014-09-01 15:28 ` [PATCH v8 6/8] drivers: cpuidle: CPU idle ARM64 driver Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-03 17:37 ` Lorenzo Pieralisi 2014-09-03 17:37 ` Lorenzo Pieralisi 2014-09-04 16:03 ` Catalin Marinas 2014-09-04 16:03 ` Catalin Marinas 2014-09-04 17:29 ` Lorenzo Pieralisi 2014-09-04 17:29 ` Lorenzo Pieralisi 2014-09-05 9:21 ` Will Deacon 2014-09-05 9:21 ` Will Deacon 2014-09-05 15:34 ` Lorenzo Pieralisi 2014-09-05 15:34 ` Lorenzo Pieralisi 2014-09-11 8:28 ` Daniel Lezcano 2014-09-11 8:28 ` Daniel Lezcano 2014-09-11 8:57 ` Lorenzo Pieralisi 2014-09-11 8:57 ` Lorenzo Pieralisi [not found] ` <20140911085727.GA25773-7AyDDHkRsp3ZROr8t4l/smS4ubULX0JqMm0uRHvK7Nw@public.gmane.org> 2014-09-11 9:32 ` Daniel Lezcano 2014-09-11 9:32 ` Daniel Lezcano 2014-09-12 10:32 ` Catalin Marinas 2014-09-12 10:32 ` Catalin Marinas 2014-09-12 11:26 ` Lorenzo Pieralisi 2014-09-12 11:26 ` Lorenzo Pieralisi 2014-09-23 13:35 ` Bartlomiej Zolnierkiewicz 2014-09-23 13:35 ` Bartlomiej Zolnierkiewicz 2014-09-23 18:14 ` Lorenzo Pieralisi 2014-09-23 18:14 ` Lorenzo Pieralisi 2014-09-14 16:59 ` Tomasz Figa 2014-09-14 16:59 ` Tomasz Figa 2014-09-23 13:42 ` Bartlomiej Zolnierkiewicz 2014-09-23 13:42 ` Bartlomiej Zolnierkiewicz 2014-09-23 18:16 ` Lorenzo Pieralisi 2014-09-23 18:16 ` Lorenzo Pieralisi 2014-09-29 11:08 ` Lorenzo Pieralisi 2014-09-29 11:08 ` Lorenzo Pieralisi 2014-09-01 15:28 ` [PATCH v8 7/8] drivers: cpuidle: initialize big.LITTLE driver through DT Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-03 13:29 ` Daniel Lezcano 2014-09-03 13:29 ` Daniel Lezcano 2014-09-01 15:28 ` [PATCH v8 8/8] drivers: cpuidle: initialize Exynos " Lorenzo Pieralisi 2014-09-01 15:28 ` Lorenzo Pieralisi 2014-09-03 13:32 ` Daniel Lezcano 2014-09-03 13:32 ` Daniel Lezcano 2014-09-03 17:29 ` Lorenzo Pieralisi 2014-09-03 17:29 ` Lorenzo Pieralisi
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1409585324-3678-6-git-send-email-lorenzo.pieralisi@arm.com \ --to=lorenzo.pieralisi@arm.com \ --cc=Charles.Garcia-Tobin@arm.com \ --cc=amit.kucheria@linaro.org \ --cc=ananaza@iki.fi \ --cc=broonie@kernel.org \ --cc=catalin.marinas@arm.com \ --cc=daniel.lezcano@linaro.org \ --cc=devicetree@vger.kernel.org \ --cc=grant.likely@linaro.org \ --cc=k.chander@samsung.com \ --cc=khilman@linaro.org \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-pm@vger.kernel.org \ --cc=mark.rutland@arm.com \ --cc=nico@linaro.org \ --cc=paul@pwsan.com \ --cc=pdeschrijver@nvidia.com \ --cc=robh+dt@kernel.org \ --cc=santosh.shilimkar@ti.com \ --cc=sboyd@codeaurora.org \ --cc=sebcape@gmail.com \ --cc=sudeep.holla@arm.com \ --cc=t.figa@samsung.com \ --cc=vincent.guittot@linaro.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.