From mboxrd@z Thu Jan 1 00:00:00 1970 From: Lorenzo Pieralisi Subject: [PATCH v8 5/8] arm64: add PSCI CPU_SUSPEND based cpu_suspend support Date: Mon, 1 Sep 2014 16:28:41 +0100 Message-ID: <1409585324-3678-6-git-send-email-lorenzo.pieralisi@arm.com> References: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> Content-Type: text/plain; charset=WINDOWS-1252 Content-Transfer-Encoding: quoted-printable Return-path: In-Reply-To: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> Sender: linux-pm-owner@vger.kernel.org To: linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: devicetree@vger.kernel.org, Lorenzo Pieralisi , Mark Rutland , Sudeep Holla , Catalin Marinas , Charles Garcia Tobin , Nicolas Pitre , Rob Herring , Grant Likely , Peter De Schrijver , Santosh Shilimkar , Daniel Lezcano , Amit Kucheria , Vincent Guittot , Antti Miettinen , Stephen Boyd , Kevin Hilman , Sebastian Capella , Tomasz Figa , Mark Brown , Paul Walmsley , Chander Kashyap , Geoff List-Id: devicetree@vger.kernel.org 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 Reviewed-by: Catalin Marinas Signed-off-by: Lorenzo Pieralisi --- 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 #include #include +#include #include =20 #include @@ -28,6 +29,7 @@ #include #include #include +#include #include =20 #define PSCI_POWER_STATE_TYPE_STANDBY=09=090 @@ -65,6 +67,8 @@ enum psci_function { =09PSCI_FN_MAX, }; =20 +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_st= ate); + static u32 psci_function_id[PSCI_FN_MAX]; =20 static int psci_to_linux_errno(int errno) @@ -93,6 +97,18 @@ static u32 psci_power_state_pack(struct psci_power_state= state) =09=09 & PSCI_0_2_POWER_STATE_AFFL_MASK); } =20 +static void psci_power_state_unpack(u32 power_state, +=09=09=09=09 struct psci_power_state *state) +{ +=09state->id =3D (power_state & PSCI_0_2_POWER_STATE_ID_MASK) >> +=09=09=09PSCI_0_2_POWER_STATE_ID_SHIFT; +=09state->type =3D (power_state & PSCI_0_2_POWER_STATE_TYPE_MASK) >> +=09=09=09PSCI_0_2_POWER_STATE_TYPE_SHIFT; +=09state->affinity_level =3D +=09=09=09(power_state & PSCI_0_2_POWER_STATE_AFFL_MASK) >> +=09=09=09PSCI_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) =09return err; } =20 +static int __maybe_unused cpu_psci_cpu_init_idle(struct device_node *cpu_n= ode, +=09=09=09=09=09=09 unsigned int cpu) +{ +=09int i, ret, count =3D 0; +=09struct psci_power_state *psci_states; +=09struct device_node *state_node; + +=09/* +=09 * If the PSCI cpu_suspend function hook has not been initialized +=09 * idle states must not be enabled, so bail out +=09 */ +=09if (!psci_ops.cpu_suspend) +=09=09return -EOPNOTSUPP; + +=09/* Count idle states */ +=09while ((state_node =3D of_parse_phandle(cpu_node, "cpu-idle-states", +=09=09=09=09=09 count))) { +=09=09count++; +=09=09of_node_put(state_node); +=09} + +=09if (!count) +=09=09return -ENODEV; + +=09psci_states =3D kcalloc(count, sizeof(*psci_states), GFP_KERNEL); +=09if (!psci_states) +=09=09return -ENOMEM; + +=09for (i =3D 0; i < count; i++) { +=09=09u32 psci_power_state; + +=09=09state_node =3D of_parse_phandle(cpu_node, "cpu-idle-states", i); + +=09=09ret =3D of_property_read_u32(state_node, +=09=09=09=09=09 "arm,psci-suspend-param", +=09=09=09=09=09 &psci_power_state); +=09=09if (ret) { +=09=09=09pr_warn(" * %s missing arm,psci-suspend-param property\n", +=09=09=09=09state_node->full_name); +=09=09=09of_node_put(state_node); +=09=09=09goto free_mem; +=09=09} + +=09=09of_node_put(state_node); +=09=09pr_debug("psci-power-state %#x index %d\n", psci_power_state, +=09=09=09=09=09=09=09 i); +=09=09psci_power_state_unpack(psci_power_state, &psci_states[i]); +=09} +=09/* Idle states parsed correctly, initialize per-cpu pointer */ +=09per_cpu(psci_power_state, cpu) =3D psci_states; +=09return 0; + +free_mem: +=09kfree(psci_states); +=09return ret; +} + static int get_set_conduit_method(struct device_node *np) { =09const char *method; @@ -436,8 +509,39 @@ static int cpu_psci_cpu_kill(unsigned int cpu) #endif #endif =20 +static int psci_suspend_finisher(unsigned long index) +{ +=09struct psci_power_state *state =3D __get_cpu_var(psci_power_state); + +=09return psci_ops.cpu_suspend(state[index - 1], +=09=09=09=09 virt_to_phys(cpu_resume)); +} + +static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) +{ +=09int ret; +=09struct psci_power_state *state =3D __get_cpu_var(psci_power_state); +=09/* +=09 * idle state index 0 corresponds to wfi, should never be called +=09 * from the cpu_suspend operations +=09 */ +=09if (WARN_ON_ONCE(!index)) +=09=09return -EINVAL; + +=09if (state->type =3D=3D PSCI_POWER_STATE_TYPE_STANDBY) +=09=09ret =3D psci_ops.cpu_suspend(state[index - 1], 0); +=09else +=09=09ret =3D __cpu_suspend(index, psci_suspend_finisher); + +=09return ret; +} + const struct cpu_operations cpu_psci_ops =3D { =09.name=09=09=3D "psci", +#ifdef CONFIG_CPU_IDLE +=09.cpu_init_idle=09=3D cpu_psci_cpu_init_idle, +=09.cpu_suspend=09=3D cpu_psci_cpu_suspend, +#endif #ifdef CONFIG_SMP =09.cpu_init=09=3D cpu_psci_cpu_init, =09.cpu_prepare=09=3D cpu_psci_cpu_prepare, --=20 1.9.1 From mboxrd@z Thu Jan 1 00:00:00 1970 From: lorenzo.pieralisi@arm.com (Lorenzo Pieralisi) Date: Mon, 1 Sep 2014 16:28:41 +0100 Subject: [PATCH v8 5/8] arm64: add PSCI CPU_SUSPEND based cpu_suspend support In-Reply-To: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> References: <1409585324-3678-1-git-send-email-lorenzo.pieralisi@arm.com> Message-ID: <1409585324-3678-6-git-send-email-lorenzo.pieralisi@arm.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org 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 Reviewed-by: Catalin Marinas Signed-off-by: Lorenzo Pieralisi --- 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 #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #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