From: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> To: linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org, devicetree@vger.kernel.org Cc: 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 <chander.kashyap@linaro.org> Subject: [PATCH v4 4/6] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Date: Wed, 11 Jun 2014 17:18:38 +0100 [thread overview] Message-ID: <1402503520-8611-5-git-send-email-lorenzo.pieralisi@arm.com> (raw) In-Reply-To: <1402503520-8611-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 by the PSCI suspend backend. Entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset. Idle state indices missing a DT node description are initialized to power state standby WFI so that if called by the idle driver they provide the default behaviour. Reviewed-by: Sebastian Capella <sebcape@gmail.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> --- arch/arm64/include/asm/psci.h | 4 ++ arch/arm64/kernel/psci.c | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index e5312ea..16c1351 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,6 +14,10 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H +struct cpuidle_driver; int psci_init(void); +int __init psci_dt_register_idle_states(struct cpuidle_driver *, + struct device_node *[]); + #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 9e9798f..f708bcc 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -15,12 +15,14 @@ #define pr_fmt(fmt) "psci: " fmt +#include <linux/cpuidle.h> #include <linux/init.h> #include <linux/of.h> #include <linux/smp.h> #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 +30,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 +68,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 +98,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 +216,77 @@ static int psci_migrate_info_type(void) return err; } +int __init psci_dt_register_idle_states(struct cpuidle_driver *drv, + struct device_node *state_nodes[]) +{ + int cpu, i; + struct psci_power_state *psci_states; + const struct cpu_operations *cpu_ops_ptr; + + if (!state_nodes) + return -EINVAL; + /* + * This is belt-and-braces: make sure that if the idle + * specified protocol is psci, the cpu_ops have been + * initialized to psci operations. Anything else is + * a recipe for mayhem. + */ + for_each_cpu(cpu, drv->cpumask) { + cpu_ops_ptr = cpu_ops[cpu]; + if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci"))) + return -EOPNOTSUPP; + } + + psci_states = kcalloc(drv->state_count, sizeof(*psci_states), + GFP_KERNEL); + + if (!psci_states) { + pr_warn("psci idle state allocation failed\n"); + return -ENOMEM; + } + + for_each_cpu(cpu, drv->cpumask) { + if (per_cpu(psci_power_state, cpu)) { + pr_warn("idle states already initialized on cpu %u\n", + cpu); + continue; + } + per_cpu(psci_power_state, cpu) = psci_states; + } + + + for (i = 0; i < drv->state_count; i++) { + u32 psci_power_state; + + if (!state_nodes[i]) { + /* + * An index with a missing node pointer falls back to + * simple STANDBYWFI + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + if (of_property_read_u32(state_nodes[i], "entry-method-param", + &psci_power_state)) { + pr_warn(" * %s missing entry-method-param property\n", + state_nodes[i]->full_name); + /* + * If entry-method-param property is missing, fall + * back to STANDBYWFI state + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + pr_debug("psci-power-state %#x index %u\n", psci_power_state, + i); + psci_power_state_unpack(psci_power_state, &psci_states[i]); + } + + return 0; +} + static int get_set_conduit_method(struct device_node *np) { const char *method; @@ -435,6 +523,18 @@ static int cpu_psci_cpu_kill(unsigned int cpu) } #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +static int cpu_psci_cpu_suspend(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + if (!state) + return -EOPNOTSUPP; + + return psci_ops.cpu_suspend(state[index], virt_to_phys(cpu_resume)); +} +#endif + const struct cpu_operations cpu_psci_ops = { .name = "psci", .cpu_init = cpu_psci_cpu_init, @@ -445,6 +545,9 @@ const struct cpu_operations cpu_psci_ops = { .cpu_die = cpu_psci_cpu_die, .cpu_kill = cpu_psci_cpu_kill, #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND + .cpu_suspend = cpu_psci_cpu_suspend, +#endif }; #endif -- 1.8.4
WARNING: multiple messages have this Message-ID (diff)
From: lorenzo.pieralisi@arm.com (Lorenzo Pieralisi) To: linux-arm-kernel@lists.infradead.org Subject: [PATCH v4 4/6] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Date: Wed, 11 Jun 2014 17:18:38 +0100 [thread overview] Message-ID: <1402503520-8611-5-git-send-email-lorenzo.pieralisi@arm.com> (raw) In-Reply-To: <1402503520-8611-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 by the PSCI suspend backend. Entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset. Idle state indices missing a DT node description are initialized to power state standby WFI so that if called by the idle driver they provide the default behaviour. Reviewed-by: Sebastian Capella <sebcape@gmail.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> --- arch/arm64/include/asm/psci.h | 4 ++ arch/arm64/kernel/psci.c | 103 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index e5312ea..16c1351 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,6 +14,10 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H +struct cpuidle_driver; int psci_init(void); +int __init psci_dt_register_idle_states(struct cpuidle_driver *, + struct device_node *[]); + #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 9e9798f..f708bcc 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -15,12 +15,14 @@ #define pr_fmt(fmt) "psci: " fmt +#include <linux/cpuidle.h> #include <linux/init.h> #include <linux/of.h> #include <linux/smp.h> #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 +30,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 +68,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 +98,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 +216,77 @@ static int psci_migrate_info_type(void) return err; } +int __init psci_dt_register_idle_states(struct cpuidle_driver *drv, + struct device_node *state_nodes[]) +{ + int cpu, i; + struct psci_power_state *psci_states; + const struct cpu_operations *cpu_ops_ptr; + + if (!state_nodes) + return -EINVAL; + /* + * This is belt-and-braces: make sure that if the idle + * specified protocol is psci, the cpu_ops have been + * initialized to psci operations. Anything else is + * a recipe for mayhem. + */ + for_each_cpu(cpu, drv->cpumask) { + cpu_ops_ptr = cpu_ops[cpu]; + if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci"))) + return -EOPNOTSUPP; + } + + psci_states = kcalloc(drv->state_count, sizeof(*psci_states), + GFP_KERNEL); + + if (!psci_states) { + pr_warn("psci idle state allocation failed\n"); + return -ENOMEM; + } + + for_each_cpu(cpu, drv->cpumask) { + if (per_cpu(psci_power_state, cpu)) { + pr_warn("idle states already initialized on cpu %u\n", + cpu); + continue; + } + per_cpu(psci_power_state, cpu) = psci_states; + } + + + for (i = 0; i < drv->state_count; i++) { + u32 psci_power_state; + + if (!state_nodes[i]) { + /* + * An index with a missing node pointer falls back to + * simple STANDBYWFI + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + if (of_property_read_u32(state_nodes[i], "entry-method-param", + &psci_power_state)) { + pr_warn(" * %s missing entry-method-param property\n", + state_nodes[i]->full_name); + /* + * If entry-method-param property is missing, fall + * back to STANDBYWFI state + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + pr_debug("psci-power-state %#x index %u\n", psci_power_state, + i); + psci_power_state_unpack(psci_power_state, &psci_states[i]); + } + + return 0; +} + static int get_set_conduit_method(struct device_node *np) { const char *method; @@ -435,6 +523,18 @@ static int cpu_psci_cpu_kill(unsigned int cpu) } #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +static int cpu_psci_cpu_suspend(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + if (!state) + return -EOPNOTSUPP; + + return psci_ops.cpu_suspend(state[index], virt_to_phys(cpu_resume)); +} +#endif + const struct cpu_operations cpu_psci_ops = { .name = "psci", .cpu_init = cpu_psci_cpu_init, @@ -445,6 +545,9 @@ const struct cpu_operations cpu_psci_ops = { .cpu_die = cpu_psci_cpu_die, .cpu_kill = cpu_psci_cpu_kill, #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND + .cpu_suspend = cpu_psci_cpu_suspend, +#endif }; #endif -- 1.8.4
next prev parent reply other threads:[~2014-06-11 16:18 UTC|newest] Thread overview: 74+ messages / expand[flat|nested] mbox.gz Atom feed top 2014-06-11 16:18 [PATCH v4 0/6] ARM generic idle states Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi 2014-06-11 16:18 ` [PATCH v4 1/6] Documentation: arm: define DT idle states bindings Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi 2014-06-11 18:15 ` Nicolas Pitre 2014-06-11 18:15 ` Nicolas Pitre 2014-06-13 16:49 ` Lorenzo Pieralisi 2014-06-13 16:49 ` Lorenzo Pieralisi 2014-06-13 17:33 ` Nicolas Pitre 2014-06-13 17:33 ` Nicolas Pitre 2014-06-16 14:23 ` Lorenzo Pieralisi 2014-06-16 14:23 ` Lorenzo Pieralisi 2014-06-16 14:48 ` Nicolas Pitre 2014-06-16 14:48 ` Nicolas Pitre 2014-06-18 17:36 ` Lorenzo Pieralisi 2014-06-18 17:36 ` Lorenzo Pieralisi 2014-06-18 18:20 ` Sebastian Capella 2014-06-18 18:20 ` Sebastian Capella 2014-06-18 19:27 ` Santosh Shilimkar 2014-06-18 19:27 ` Santosh Shilimkar 2014-06-18 20:51 ` Nicolas Pitre 2014-06-18 20:51 ` Nicolas Pitre 2014-06-18 20:55 ` Santosh Shilimkar 2014-06-18 20:55 ` Santosh Shilimkar 2014-06-18 21:09 ` Nicolas Pitre 2014-06-18 21:09 ` Nicolas Pitre 2014-06-18 23:13 ` Santosh Shilimkar 2014-06-18 23:13 ` Santosh Shilimkar 2014-06-19 7:33 ` Charles Garcia-Tobin 2014-06-19 7:33 ` Charles Garcia-Tobin 2014-06-19 14:08 ` Santosh Shilimkar 2014-06-19 14:08 ` Santosh Shilimkar 2014-06-19 15:09 ` Charles Garcia-Tobin 2014-06-19 15:09 ` Charles Garcia-Tobin 2014-06-18 21:03 ` Nicolas Pitre 2014-06-18 21:03 ` Nicolas Pitre 2014-06-13 17:40 ` Sebastian Capella 2014-06-13 17:40 ` Sebastian Capella [not found] ` <1402503520-8611-1-git-send-email-lorenzo.pieralisi-5wv7dgnIgG8@public.gmane.org> 2014-06-11 16:18 ` [PATCH v4 2/6] Documentation: devicetree: psci: define CPU suspend parameter Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi 2014-06-11 16:18 ` [PATCH v4 5/6] drivers: cpuidle: CPU idle ARM64 driver Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi 2014-06-18 21:34 ` Daniel Lezcano 2014-06-18 21:34 ` Daniel Lezcano 2014-06-19 9:30 ` Lorenzo Pieralisi 2014-06-19 9:30 ` Lorenzo Pieralisi 2014-06-19 3:02 ` Rob Herring 2014-06-19 3:02 ` Rob Herring 2014-06-19 9:08 ` Lorenzo Pieralisi 2014-06-19 9:08 ` Lorenzo Pieralisi 2014-06-11 16:18 ` [PATCH v4 3/6] drivers: cpuidle: implement OF based idle states infrastructure Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi 2014-06-11 18:24 ` Nicolas Pitre 2014-06-11 18:24 ` Nicolas Pitre 2014-06-12 8:46 ` Lorenzo Pieralisi 2014-06-12 8:46 ` Lorenzo Pieralisi 2014-06-11 18:25 ` Rafael J. Wysocki 2014-06-11 18:25 ` Rafael J. Wysocki 2014-06-12 9:03 ` Lorenzo Pieralisi 2014-06-12 9:03 ` Lorenzo Pieralisi 2014-06-13 3:48 ` Preeti U Murthy 2014-06-13 3:48 ` Preeti U Murthy 2014-06-13 17:16 ` Lorenzo Pieralisi 2014-06-13 17:16 ` Lorenzo Pieralisi 2014-07-06 10:01 ` Paul Burton 2014-07-06 10:01 ` Paul Burton 2014-06-11 18:38 ` Nicolas Pitre 2014-06-11 18:38 ` Nicolas Pitre 2014-06-12 9:19 ` Lorenzo Pieralisi 2014-06-12 9:19 ` Lorenzo Pieralisi 2014-06-11 16:18 ` Lorenzo Pieralisi [this message] 2014-06-11 16:18 ` [PATCH v4 4/6] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Lorenzo Pieralisi 2014-06-11 16:18 ` [PATCH v4 6/6] arm64: boot: dts: update rtsm aemv8 dts with PSCI and idle states Lorenzo Pieralisi 2014-06-11 16:18 ` 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=1402503520-8611-5-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=chander.kashyap@linaro.org \ --cc=daniel.lezcano@linaro.org \ --cc=devicetree@vger.kernel.org \ --cc=grant.likely@linaro.org \ --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.