From 922b78c7094e1db6fcb3a0e39c91c5a4cdde8957 Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Sun, 10 Mar 2013 22:36:51 +0000 Subject: [PATCH 1/7] ARM: TC2: replace hard coded cluster and cpu values with constants This patch adds constants in a tc2 specific header file to prevent use of hard coded values for specifying the number of cpus and clusters. Signed-off-by: Achin Gupta Signed-off-by: Liviu Dudau --- arch/arm/mach-vexpress/include/mach/tc2.h | 10 ++++++++++ arch/arm/mach-vexpress/tc2_pm.c | 16 +++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 arch/arm/mach-vexpress/include/mach/tc2.h diff --git a/arch/arm/mach-vexpress/include/mach/tc2.h b/arch/arm/mach-vexpress/include/mach/tc2.h new file mode 100644 index 000000000000..d3b5a2225a0e --- /dev/null +++ b/arch/arm/mach-vexpress/include/mach/tc2.h @@ -0,0 +1,10 @@ +#ifndef __MACH_TC2_H +#define __MACH_TC2_H + +/* + * cpu and cluster limits + */ +#define TC2_MAX_CPUS 3 +#define TC2_MAX_CLUSTERS 2 + +#endif diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c index aa866e5f9eac..fddea7c3a2a0 100644 --- a/arch/arm/mach-vexpress/tc2_pm.c +++ b/arch/arm/mach-vexpress/tc2_pm.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -37,12 +38,13 @@ */ static arch_spinlock_t tc2_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED; -static int tc2_pm_use_count[3][2]; +static int tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS]; static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster) { pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); - if (cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster)) + if (cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)) return -EINVAL; /* @@ -90,7 +92,8 @@ static void tc2_pm_down(u64 residency) cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); - BUG_ON(cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster)); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); __mcpm_cpu_going_down(cpu, cluster); @@ -194,7 +197,8 @@ static void tc2_pm_powered_up(void) cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); - BUG_ON(cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster)); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); local_irq_save(flags); arch_spin_lock(&tc2_pm_lock); @@ -232,7 +236,9 @@ static void __init tc2_pm_usage_count_init(void) cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); - BUG_ON(cpu >= 3 || cluster >= 2); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + tc2_pm_use_count[cpu][cluster] = 1; } From 58068c45b899873906a6dccc94715a3e72387d42 Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Mon, 17 Dec 2012 00:15:00 +0000 Subject: [PATCH 2/7] ARM: vexpress: allow native pm ops backends to probe for psci suppport This patch allows the vexpress 'tc2' native backend to probe the dt for presence of the psci backend. If present then the native implementation of the 'bL_platform_power_ops' is not used. Signed-off-by: Achin Gupta Signed-off-by: Liviu Dudau --- arch/arm/mach-vexpress/tc2_pm.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-vexpress/tc2_pm.c b/arch/arm/mach-vexpress/tc2_pm.c index fddea7c3a2a0..9c742edb7ae6 100644 --- a/arch/arm/mach-vexpress/tc2_pm.c +++ b/arch/arm/mach-vexpress/tc2_pm.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -248,6 +249,12 @@ static int __init tc2_pm_init(void) { int ret; + ret = psci_probe(); + if (!ret) { + pr_debug("psci found. Aborting native init\n"); + return -ENODEV; + } + if (!vexpress_spc_check_loaded()) return -ENODEV; From dfd6f0c31471ceb6438f054922aadba39c2888ff Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Tue, 26 Feb 2013 15:33:25 +0000 Subject: [PATCH 3/7] ARM: vexpress: add shim layer for psci backend on TC2 This patch introduces a shim layer for the TC2 platform which converts 'bL_platform_power_ops' routines to their psci counterparts. The psci counterparts are implemented by the secure firmware. The shim layer is used only when Linux is running in non-secure world and the secure firmware implements psci. It also introduces the use of a reference count to allow a power up call to go ahead of a power down call. Signed-off-by: Achin Gupta Signed-off-by: Liviu Dudau --- arch/arm/mach-vexpress/Makefile | 4 + arch/arm/mach-vexpress/tc2_pm_psci.c | 179 +++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 arch/arm/mach-vexpress/tc2_pm_psci.c diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index fd9bcfd74316..59cdec9cbd7e 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -8,5 +8,9 @@ obj-y := v2m.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o CFLAGS_REMOVE_tc2_pm.o = -pg +ifeq ($(CONFIG_ARCH_VEXPRESS_TC2),y) +obj-$(CONFIG_ARM_PSCI) += tc2_pm_psci.o +CFLAGS_REMOVE_tc2_pm_psci.o = -pg +endif obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-vexpress/tc2_pm_psci.c b/arch/arm/mach-vexpress/tc2_pm_psci.c new file mode 100644 index 000000000000..c9715b8137e3 --- /dev/null +++ b/arch/arm/mach-vexpress/tc2_pm_psci.c @@ -0,0 +1,179 @@ +/* + * arch/arm/mach-vexpress/tc2_pm_psci.c - TC2 PSCI support + * + * Created by: Achin Gupta, December 2012 + * Copyright: (C) 2012 ARM Limited + * + * Some portions of this file were originally written by Nicolas Pitre + * Copyright: (C) 2012 Linaro Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * Platform specific state id understood by the firmware and used to + * program the power controller + */ +#define PSCI_POWER_STATE_ID 0 + +static atomic_t tc2_pm_use_count[TC2_MAX_CPUS][TC2_MAX_CLUSTERS]; + +static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned int mpidr = (cluster << 8) | cpu; + int ret = 0; + + BUG_ON(!psci_ops.cpu_on); + + switch (atomic_inc_return(&tc2_pm_use_count[cpu][cluster])) { + case 1: + /* + * This is a request to power up a cpu that linux thinks has + * been powered down. Retries are needed if the firmware has + * seen the power down request as yet. + */ + do + ret = psci_ops.cpu_on(mpidr, + virt_to_phys(bL_entry_point)); + while (ret == -EAGAIN); + + return ret; + case 2: + /* This power up request has overtaken a power down request */ + return ret; + default: + /* Any other value is a bug */ + BUG(); + } +} + +static void tc2_pm_psci_power_down(void) +{ + struct psci_power_state power_state; + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + BUG_ON(!psci_ops.cpu_off); + + switch (atomic_dec_return(&tc2_pm_use_count[cpu][cluster])) { + case 1: + /* + * Overtaken by a power up. Flush caches, exit coherency, + * return & fake a reset + */ + asm volatile ( + "mrc p15, 0, ip, c1, c0, 0 \n\t" + "bic ip, ip, #(1 << 2) @ clear C bit \n\t" + "mcr p15, 0, ip, c1, c0, 0 \n\t" + "dsb \n\t" + "isb" + : : : "ip" ); + + flush_cache_louis(); + + asm volatile ( + "clrex \n\t" + "mrc p15, 0, ip, c1, c0, 1 \n\t" + "bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" + "mcr p15, 0, ip, c1, c0, 1 \n\t" + "isb \n\t" + "dsb" + : : : "ip" ); + + return; + case 0: + /* A normal request to possibly power down the cluster */ + power_state.id = PSCI_POWER_STATE_ID; + power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; + power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; + + psci_ops.cpu_off(power_state); + + /* On success this function never returns */ + default: + /* Any other value is a bug */ + BUG(); + } +} + +static void tc2_pm_psci_suspend(u64 unused) +{ + struct psci_power_state power_state; + + BUG_ON(!psci_ops.cpu_suspend); + + /* On TC2 always attempt to power down the cluster */ + power_state.id = PSCI_POWER_STATE_ID; + power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; + power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; + + psci_ops.cpu_suspend(power_state, virt_to_phys(bL_entry_point)); + + /* On success this function never returns */ + BUG(); +} + +static const struct bL_platform_power_ops tc2_pm_power_ops = { + .power_up = tc2_pm_psci_power_up, + .power_down = tc2_pm_psci_power_down, + .suspend = tc2_pm_psci_suspend, +}; + +static void __init tc2_pm_usage_count_init(void) +{ + unsigned int mpidr, cpu, cluster; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cluster >= TC2_MAX_CLUSTERS || + cpu >= vexpress_spc_get_nb_cpus(cluster)); + + atomic_set(&tc2_pm_use_count[cpu][cluster], 1); +} + +static int __init tc2_pm_psci_init(void) +{ + int ret; + + ret = psci_probe(); + if (ret) { + pr_debug("psci not found. Aborting psci init\n"); + return -ENODEV; + } + + tc2_pm_usage_count_init(); + + ret = bL_platform_power_register(&tc2_pm_power_ops); + if (!ret) + ret = bL_cluster_sync_init(NULL); + if (!ret) + pr_info("TC2 power management initialized\n"); + return ret; +} + +early_initcall(tc2_pm_psci_init); From 4997791921e168d5bd8dd707deb360f6d32f5d7e Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Sun, 16 Dec 2012 22:41:02 +0000 Subject: [PATCH 4/7] ARM: vexpress: add psci support in TC2 device tree This patch adds a psci device node to allow the ospm subsystems on the TC2 to work with a psci backend implemented in the secure firmware. The function offsets start from 1 instead of 0 as thats whats the current secure firmware implements. Signed-off-by: Achin Gupta Signed-off-by: Liviu Dudau --- arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts index 1abef13f9e97..cd621a42b0da 100644 --- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts +++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts @@ -147,6 +147,15 @@ clock-output-names = "oscclk6a"; }; + psci { + compatible = "arm,psci"; + method = "smc"; + cpu_suspend = <0x80100001>; + cpu_off = <0x80100002>; + cpu_on = <0x80100003>; + migrate = <0x80100004>; + }; + dcc { compatible = "arm,vexpress,config-bus"; arm,vexpress,config-bridge = <&v2m_sysreg>; From e64a97aa17f483b616b9adc93e74daee1eabf4a0 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 21 Mar 2013 11:15:56 +0000 Subject: [PATCH 5/7] ARM: vexpress: Fixup tc2_pm_psci.c for mcpm APIs Signed-off-by: Jon Medhurst --- arch/arm/mach-vexpress/tc2_pm_psci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-vexpress/tc2_pm_psci.c b/arch/arm/mach-vexpress/tc2_pm_psci.c index c9715b8137e3..04d83d2965f2 100644 --- a/arch/arm/mach-vexpress/tc2_pm_psci.c +++ b/arch/arm/mach-vexpress/tc2_pm_psci.c @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include @@ -53,7 +53,7 @@ static int tc2_pm_psci_power_up(unsigned int cpu, unsigned int cluster) */ do ret = psci_ops.cpu_on(mpidr, - virt_to_phys(bL_entry_point)); + virt_to_phys(mcpm_entry_point)); while (ret == -EAGAIN); return ret; @@ -129,13 +129,13 @@ static void tc2_pm_psci_suspend(u64 unused) power_state.type = PSCI_POWER_STATE_TYPE_POWER_DOWN; power_state.affinity_level = PSCI_POWER_STATE_AFFINITY_LEVEL1; - psci_ops.cpu_suspend(power_state, virt_to_phys(bL_entry_point)); + psci_ops.cpu_suspend(power_state, virt_to_phys(mcpm_entry_point)); /* On success this function never returns */ BUG(); } -static const struct bL_platform_power_ops tc2_pm_power_ops = { +static const struct mcpm_platform_ops tc2_pm_power_ops = { .power_up = tc2_pm_psci_power_up, .power_down = tc2_pm_psci_power_down, .suspend = tc2_pm_psci_suspend, @@ -168,9 +168,9 @@ static int __init tc2_pm_psci_init(void) tc2_pm_usage_count_init(); - ret = bL_platform_power_register(&tc2_pm_power_ops); + ret = mcpm_platform_register(&tc2_pm_power_ops); if (!ret) - ret = bL_cluster_sync_init(NULL); + ret = mcpm_sync_init(NULL); if (!ret) pr_info("TC2 power management initialized\n"); return ret; From b62c31f5262bdba8171287cfe92df7bdeed1af31 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 21 Mar 2013 12:00:09 +0000 Subject: [PATCH 6/7] ARM: vexpress: Get tc2_pm_psci.c to use common CP15 accessor functions Signed-off-by: Jon Medhurst --- arch/arm/mach-vexpress/tc2_pm_psci.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-vexpress/tc2_pm_psci.c b/arch/arm/mach-vexpress/tc2_pm_psci.c index 04d83d2965f2..5a5e4f568497 100644 --- a/arch/arm/mach-vexpress/tc2_pm_psci.c +++ b/arch/arm/mach-vexpress/tc2_pm_psci.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -83,24 +84,12 @@ static void tc2_pm_psci_power_down(void) * Overtaken by a power up. Flush caches, exit coherency, * return & fake a reset */ - asm volatile ( - "mrc p15, 0, ip, c1, c0, 0 \n\t" - "bic ip, ip, #(1 << 2) @ clear C bit \n\t" - "mcr p15, 0, ip, c1, c0, 0 \n\t" - "dsb \n\t" - "isb" - : : : "ip" ); + set_cr(get_cr() & ~CR_C); flush_cache_louis(); - asm volatile ( - "clrex \n\t" - "mrc p15, 0, ip, c1, c0, 1 \n\t" - "bic ip, ip, #(1 << 6) @ clear SMP bit \n\t" - "mcr p15, 0, ip, c1, c0, 1 \n\t" - "isb \n\t" - "dsb" - : : : "ip" ); + asm volatile ("clrex"); + set_auxcr(get_auxcr() & ~(1 << 6)); return; case 0: From 521caf641e76b48016e25b66e37799a9333ede39 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Wed, 19 Jun 2013 10:17:00 +0100 Subject: [PATCH 7/7] ARM: vexpress: Ensure SPC driver is loaded before using it in PSCI init Otherwise we get a nullptr dereference calling vexpress_spc_get_nb_cpus. Signed-off-by: Jon Medhurst --- arch/arm/mach-vexpress/tc2_pm_psci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-vexpress/tc2_pm_psci.c b/arch/arm/mach-vexpress/tc2_pm_psci.c index 5a5e4f568497..c2fdc22e4c06 100644 --- a/arch/arm/mach-vexpress/tc2_pm_psci.c +++ b/arch/arm/mach-vexpress/tc2_pm_psci.c @@ -155,6 +155,11 @@ static int __init tc2_pm_psci_init(void) return -ENODEV; } + if (!vexpress_spc_check_loaded()) { + pr_debug("spc not found. Aborting psci init\n"); + return -ENODEV; + } + tc2_pm_usage_count_init(); ret = mcpm_platform_register(&tc2_pm_power_ops);