From 79bb2f540a27af275a6c832029b13fbc024571b6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 16 Jan 2013 17:57:33 -0600 Subject: ARM: introduce common set_auxcr/get_auxcr functions Move the private set_auxcr/get_auxcr functions from drivers/cpuidle/cpuidle-calxeda.c so they can be used across platforms. Signed-off-by: Rob Herring Cc: Russell King Signed-off-by: Nicolas Pitre Acked-by: Tony Lindgren Acked-by: Santosh Shilimkar Reviewed-by: Will Deacon --- arch/arm/include/asm/cp15.h | 14 ++++++++++++++ drivers/cpuidle/cpuidle-calxeda.c | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm/include/asm/cp15.h b/arch/arm/include/asm/cp15.h index 5ef4d8015a6..ce4d01c03e6 100644 --- a/arch/arm/include/asm/cp15.h +++ b/arch/arm/include/asm/cp15.h @@ -59,6 +59,20 @@ static inline void set_cr(unsigned int val) isb(); } +static inline unsigned int get_auxcr(void) +{ + unsigned int val; + asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val)); + return val; +} + +static inline void set_auxcr(unsigned int val) +{ + asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR" + : : "r" (val)); + isb(); +} + #ifndef CONFIG_SMP extern void adjust_cr(unsigned long mask, unsigned long set); #endif diff --git a/drivers/cpuidle/cpuidle-calxeda.c b/drivers/cpuidle/cpuidle-calxeda.c index e1aab38c5a8..ece83d6e049 100644 --- a/drivers/cpuidle/cpuidle-calxeda.c +++ b/drivers/cpuidle/cpuidle-calxeda.c @@ -37,20 +37,6 @@ extern void *scu_base_addr; static struct cpuidle_device __percpu *calxeda_idle_cpuidle_devices; -static inline unsigned int get_auxcr(void) -{ - unsigned int val; - asm("mrc p15, 0, %0, c1, c0, 1 @ get AUXCR" : "=r" (val) : : "cc"); - return val; -} - -static inline void set_auxcr(unsigned int val) -{ - asm volatile("mcr p15, 0, %0, c1, c0, 1 @ set AUXCR" - : : "r" (val) : "cc"); - isb(); -} - static noinline void calxeda_idle_restore(void) { set_cr(get_cr() | CR_C); -- cgit v1.2.3 From 22744ffd55b1d0794c265679769d2308c0cb5360 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 2 May 2012 20:56:52 -0400 Subject: ARM: vexpress: introduce DCSCB support This adds basic CPU and cluster reset controls on RTSM for the A15x4-A7x4 model configuration using the Dual Cluster System Configuration Block (DCSCB). The cache coherency interconnect (CCI) is not handled yet. Signed-off-by: Nicolas Pitre Reviewed-by: Santosh Shilimkar Acked-by: Pawel Moll --- .../devicetree/bindings/arm/rtsm-dcscb.txt | 19 +++ arch/arm/mach-vexpress/Kconfig | 8 + arch/arm/mach-vexpress/Makefile | 1 + arch/arm/mach-vexpress/dcscb.c | 162 +++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/rtsm-dcscb.txt create mode 100644 arch/arm/mach-vexpress/dcscb.c diff --git a/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt b/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt new file mode 100644 index 00000000000..3b8fbf3c00c --- /dev/null +++ b/Documentation/devicetree/bindings/arm/rtsm-dcscb.txt @@ -0,0 +1,19 @@ +ARM Dual Cluster System Configuration Block +------------------------------------------- + +The Dual Cluster System Configuration Block (DCSCB) provides basic +functionality for controlling clocks, resets and configuration pins in +the Dual Cluster System implemented by the Real-Time System Model (RTSM). + +Required properties: + +- compatible : should be "arm,rtsm,dcscb" + +- reg : physical base address and the size of the registers window + +Example: + + dcscb@60000000 { + compatible = "arm,rtsm,dcscb"; + reg = <0x60000000 0x1000>; + }; diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 52d315b792c..82b22c26836 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -52,4 +52,12 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_CA9X4 bool "Versatile Express Cortex-A9x4 tile" +config ARCH_VEXPRESS_DCSCB + bool "Dual Cluster System Control Block (DCSCB) support" + depends on MCPM + help + Support for the Dual Cluster System Configuration Block (DCSCB). + This is needed to provide CPU and cluster power management + on RTSM. + endmenu diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index 80b64971fbd..2253644054d 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -6,5 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c new file mode 100644 index 00000000000..ad44881adae --- /dev/null +++ b/arch/arm/mach-vexpress/dcscb.c @@ -0,0 +1,162 @@ +/* + * arch/arm/mach-vexpress/dcscb.c - Dual Cluster System Configuration Block + * + * Created by: Nicolas Pitre, May 2012 + * Copyright: (C) 2012-2013 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 + + +#define RST_HOLD0 0x0 +#define RST_HOLD1 0x4 +#define SYS_SWRESET 0x8 +#define RST_STAT0 0xc +#define RST_STAT1 0x10 +#define EAG_CFG_R 0x20 +#define EAG_CFG_W 0x24 +#define KFC_CFG_R 0x28 +#define KFC_CFG_W 0x2c +#define DCS_CFG_R 0x30 + +/* + * We can't use regular spinlocks. In the switcher case, it is possible + * for an outbound CPU to call power_down() after its inbound counterpart + * is already live using the same logical CPU number which trips lockdep + * debugging. + */ +static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; + +static void __iomem *dcscb_base; + +static int dcscb_power_up(unsigned int cpu, unsigned int cluster) +{ + unsigned int rst_hold, cpumask = (1 << cpu); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + if (cpu >= 4 || cluster >= 2) + return -EINVAL; + + /* + * Since this is called with IRQs enabled, and no arch_spin_lock_irq + * variant exists, we need to disable IRQs manually here. + */ + local_irq_disable(); + arch_spin_lock(&dcscb_lock); + + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + if (rst_hold & (1 << 8)) { + /* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 << 8); + rst_hold |= 0xf; + } + rst_hold &= ~(cpumask | (cpumask << 4)); + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + + arch_spin_unlock(&dcscb_lock); + local_irq_enable(); + + return 0; +} + +static void dcscb_power_down(void) +{ + unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; + + mpidr = read_cpuid_mpidr(); + cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); + cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); + cpumask = (1 << cpu); + + pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); + BUG_ON(cpu >= 4 || cluster >= 2); + + arch_spin_lock(&dcscb_lock); + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + rst_hold |= cpumask; + if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf) + rst_hold |= (1 << 8); + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + arch_spin_unlock(&dcscb_lock); + last_man = (rst_hold & (1 << 8)); + + /* + * Now let's clean our L1 cache and shut ourself down. + * If we're the last CPU in this cluster then clean L2 too. + */ + + /* + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated).. + */ + flush_cache_louis(); + set_cr(get_cr() & ~CR_C); + + if (!last_man) { + flush_cache_louis(); + } else { + flush_cache_all(); + outer_flush_all(); + } + + /* Disable local coherency by clearing the ACTLR "SMP" bit: */ + set_auxcr(get_auxcr() & ~(1 << 6)); + + /* Now we are prepared for power-down, do it: */ + dsb(); + wfi(); + + /* Not dead at this point? Let our caller cope. */ +} + +static const struct mcpm_platform_ops dcscb_power_ops = { + .power_up = dcscb_power_up, + .power_down = dcscb_power_down, +}; + +static int __init dcscb_init(void) +{ + struct device_node *node; + int ret; + + node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb"); + if (!node) + return -ENODEV; + dcscb_base= of_iomap(node, 0); + if (!dcscb_base) + return -EADDRNOTAVAIL; + + ret = mcpm_platform_register(&dcscb_power_ops); + if (ret) { + iounmap(dcscb_base); + return ret; + } + + /* + * Future entries into the kernel can now go + * through the cluster entry vectors. + */ + vexpress_flags_set(virt_to_phys(mcpm_entry_point)); + + return 0; +} + +early_initcall(dcscb_init); -- cgit v1.2.3 From 0f80732f5d51be9c981a78cbcbecdc5b72f7a1e6 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Mon, 16 Jul 2012 22:07:10 -0400 Subject: ARM: vexpress/dcscb: add CPU use counts to the power up/down API implementation It is possible for a CPU to be told to power up before it managed to power itself down. Solve this race with a usage count as mandated by the API definition. Signed-off-by: nicolas Pitre Reviewed-by: Santosh Shilimkar Acked-by: Pawel Moll --- arch/arm/mach-vexpress/dcscb.c | 77 +++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index ad44881adae..946b332ab99 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -44,6 +44,7 @@ static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; static void __iomem *dcscb_base; +static int dcscb_use_count[4][2]; static int dcscb_power_up(unsigned int cpu, unsigned int cluster) { @@ -60,14 +61,27 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster) local_irq_disable(); arch_spin_lock(&dcscb_lock); - rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); - if (rst_hold & (1 << 8)) { - /* remove cluster reset and add individual CPU's reset */ - rst_hold &= ~(1 << 8); - rst_hold |= 0xf; + dcscb_use_count[cpu][cluster]++; + if (dcscb_use_count[cpu][cluster] == 1) { + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + if (rst_hold & (1 << 8)) { + /* remove cluster reset and add individual CPU's reset */ + rst_hold &= ~(1 << 8); + rst_hold |= 0xf; + } + rst_hold &= ~(cpumask | (cpumask << 4)); + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + } else if (dcscb_use_count[cpu][cluster] != 2) { + /* + * The only possible values are: + * 0 = CPU down + * 1 = CPU (still) up + * 2 = CPU requested to be up before it had a chance + * to actually make itself down. + * Any other value is a bug. + */ + BUG(); } - rst_hold &= ~(cpumask | (cpumask << 4)); - writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); arch_spin_unlock(&dcscb_lock); local_irq_enable(); @@ -77,7 +91,8 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster) static void dcscb_power_down(void) { - unsigned int mpidr, cpu, cluster, rst_hold, cpumask, last_man; + unsigned int mpidr, cpu, cluster, rst_hold, cpumask; + bool last_man = false, skip_wfi = false; mpidr = read_cpuid_mpidr(); cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); @@ -88,13 +103,26 @@ static void dcscb_power_down(void) BUG_ON(cpu >= 4 || cluster >= 2); arch_spin_lock(&dcscb_lock); - rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); - rst_hold |= cpumask; - if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf) - rst_hold |= (1 << 8); - writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + dcscb_use_count[cpu][cluster]--; + if (dcscb_use_count[cpu][cluster] == 0) { + rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); + rst_hold |= cpumask; + if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf) { + rst_hold |= (1 << 8); + last_man = true; + } + writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); + } else if (dcscb_use_count[cpu][cluster] == 1) { + /* + * A power_up request went ahead of us. + * Even if we do not want to shut this CPU down, + * the caller expects a certain state as if the WFI + * was aborted. So let's continue with cache cleaning. + */ + skip_wfi = true; + } else + BUG(); arch_spin_unlock(&dcscb_lock); - last_man = (rst_hold & (1 << 8)); /* * Now let's clean our L1 cache and shut ourself down. @@ -121,8 +149,10 @@ static void dcscb_power_down(void) set_auxcr(get_auxcr() & ~(1 << 6)); /* Now we are prepared for power-down, do it: */ - dsb(); - wfi(); + if (!skip_wfi) { + dsb(); + wfi(); + } /* Not dead at this point? Let our caller cope. */ } @@ -132,6 +162,19 @@ static const struct mcpm_platform_ops dcscb_power_ops = { .power_down = dcscb_power_down, }; +static void __init dcscb_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(cpu >= 4 || cluster >= 2); + dcscb_use_count[cpu][cluster] = 1; +} + static int __init dcscb_init(void) { struct device_node *node; @@ -144,6 +187,8 @@ static int __init dcscb_init(void) if (!dcscb_base) return -EADDRNOTAVAIL; + dcscb_usage_count_init(); + ret = mcpm_platform_register(&dcscb_power_ops); if (ret) { iounmap(dcscb_base); -- cgit v1.2.3 From 1cd802a0e99b2d4c8bde85be19748960efa6cacd Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 18 Jul 2012 16:41:16 -0400 Subject: ARM: vexpress/dcscb: do not hardcode number of CPUs per cluster If 4 CPUs are assumed, the A15x1-A7x1 model configuration would never shut down the initial cluster as the 0xf reset bit mask will never be observed. Let's construct this mask based on the provided information in the DCSCB config register for the number of CPUs per cluster. Signed-off-by: Nicolas Pitre Reviewed-by: Santosh Shilimkar Acked-by: Pawel Moll --- arch/arm/mach-vexpress/dcscb.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index 946b332ab99..dd9e80566f9 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -45,10 +45,12 @@ static arch_spinlock_t dcscb_lock = __ARCH_SPIN_LOCK_UNLOCKED; static void __iomem *dcscb_base; static int dcscb_use_count[4][2]; +static int dcscb_mcpm_cpu_mask[2]; static int dcscb_power_up(unsigned int cpu, unsigned int cluster) { unsigned int rst_hold, cpumask = (1 << cpu); + unsigned int mcpm_mask = dcscb_mcpm_cpu_mask[cluster]; pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); if (cpu >= 4 || cluster >= 2) @@ -67,7 +69,7 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster) if (rst_hold & (1 << 8)) { /* remove cluster reset and add individual CPU's reset */ rst_hold &= ~(1 << 8); - rst_hold |= 0xf; + rst_hold |= mcpm_mask; } rst_hold &= ~(cpumask | (cpumask << 4)); writel_relaxed(rst_hold, dcscb_base + RST_HOLD0 + cluster * 4); @@ -91,13 +93,14 @@ static int dcscb_power_up(unsigned int cpu, unsigned int cluster) static void dcscb_power_down(void) { - unsigned int mpidr, cpu, cluster, rst_hold, cpumask; + unsigned int mpidr, cpu, cluster, rst_hold, cpumask, mcpm_mask; bool last_man = false, skip_wfi = false; mpidr = read_cpuid_mpidr(); cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0); cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1); cpumask = (1 << cpu); + mcpm_mask = dcscb_mcpm_cpu_mask[cluster]; pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); BUG_ON(cpu >= 4 || cluster >= 2); @@ -107,7 +110,7 @@ static void dcscb_power_down(void) if (dcscb_use_count[cpu][cluster] == 0) { rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); rst_hold |= cpumask; - if (((rst_hold | (rst_hold >> 4)) & 0xf) == 0xf) { + if (((rst_hold | (rst_hold >> 4)) & mcpm_mask) == mcpm_mask) { rst_hold |= (1 << 8); last_man = true; } @@ -178,6 +181,7 @@ static void __init dcscb_usage_count_init(void) static int __init dcscb_init(void) { struct device_node *node; + unsigned int cfg; int ret; node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb"); @@ -186,7 +190,9 @@ static int __init dcscb_init(void) dcscb_base= of_iomap(node, 0); if (!dcscb_base) return -EADDRNOTAVAIL; - + cfg = readl_relaxed(dcscb_base + DCS_CFG_R); + dcscb_mcpm_cpu_mask[0] = (1 << (((cfg >> 16) >> (0 << 2)) & 0xf)) - 1; + dcscb_mcpm_cpu_mask[1] = (1 << (((cfg >> 16) >> (1 << 2)) & 0xf)) - 1; dcscb_usage_count_init(); ret = mcpm_platform_register(&dcscb_power_ops); -- cgit v1.2.3 From f40ffc6070d9583ed69126b876740cbd823c24af Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 17 Jul 2012 14:25:44 +0100 Subject: ARM: vexpress/dcscb: handle platform coherency exit/setup and CCI Add the required code to properly handle race free platform coherency exit to the DCSCB power down method. The power_up_setup callback is used to enable the CCI interface for the cluster being brought up. This must be done in assembly before the kernel environment is entered. Thanks to Achin Gupta and Nicolas Pitre for their help and contributions. Signed-off-by: Dave Martin Signed-off-by: Nicolas Pitre Reviewed-by: Santosh Shilimkar Acked-by: Pawel Moll --- arch/arm/mach-vexpress/Kconfig | 1 + arch/arm/mach-vexpress/Makefile | 2 +- arch/arm/mach-vexpress/dcscb.c | 74 ++++++++++++++++++++++++--------- arch/arm/mach-vexpress/dcscb_setup.S | 80 ++++++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 20 deletions(-) create mode 100644 arch/arm/mach-vexpress/dcscb_setup.S diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 82b22c26836..49e97ef3933 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -55,6 +55,7 @@ config ARCH_VEXPRESS_CA9X4 config ARCH_VEXPRESS_DCSCB bool "Dual Cluster System Control Block (DCSCB) support" depends on MCPM + select ARM_CCI help Support for the Dual Cluster System Configuration Block (DCSCB). This is needed to provide CPU and cluster power management diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index 2253644054d..f6e90f3272d 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -6,6 +6,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o -obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o +obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index dd9e80566f9..68c8fdddffc 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -105,7 +106,10 @@ static void dcscb_power_down(void) pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster); BUG_ON(cpu >= 4 || cluster >= 2); + __mcpm_cpu_going_down(cpu, cluster); + arch_spin_lock(&dcscb_lock); + BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP); dcscb_use_count[cpu][cluster]--; if (dcscb_use_count[cpu][cluster] == 0) { rst_hold = readl_relaxed(dcscb_base + RST_HOLD0 + cluster * 4); @@ -125,31 +129,59 @@ static void dcscb_power_down(void) skip_wfi = true; } else BUG(); - arch_spin_unlock(&dcscb_lock); - /* - * Now let's clean our L1 cache and shut ourself down. - * If we're the last CPU in this cluster then clean L2 too. - */ - - /* - * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need - * a preliminary flush here for those CPUs. At least, that's - * the theory -- without the extra flush, Linux explodes on - * RTSM (maybe not needed anymore, to be investigated).. - */ - flush_cache_louis(); - set_cr(get_cr() & ~CR_C); + if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) { + arch_spin_unlock(&dcscb_lock); - if (!last_man) { - flush_cache_louis(); - } else { + /* + * Flush all cache levels for this cluster. + * + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated). + */ flush_cache_all(); + set_cr(get_cr() & ~CR_C); + flush_cache_all(); + + /* + * This is a harmless no-op. On platforms with a real + * outer cache this might either be needed or not, + * depending on where the outer cache sits. + */ outer_flush_all(); + + /* Disable local coherency by clearing the ACTLR "SMP" bit: */ + set_auxcr(get_auxcr() & ~(1 << 6)); + + /* + * Disable cluster-level coherency by masking + * incoming snoops and DVM messages: + */ + disable_cci(cluster); + + __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN); + } else { + arch_spin_unlock(&dcscb_lock); + + /* + * Flush the local CPU cache. + * + * A15/A7 can hit in the cache with SCTLR.C=0, so we don't need + * a preliminary flush here for those CPUs. At least, that's + * the theory -- without the extra flush, Linux explodes on + * RTSM (maybe not needed anymore, to be investigated). + */ + flush_cache_louis(); + set_cr(get_cr() & ~CR_C); + flush_cache_louis(); + + /* Disable local coherency by clearing the ACTLR "SMP" bit: */ + set_auxcr(get_auxcr() & ~(1 << 6)); } - /* Disable local coherency by clearing the ACTLR "SMP" bit: */ - set_auxcr(get_auxcr() & ~(1 << 6)); + __mcpm_cpu_down(cpu, cluster); /* Now we are prepared for power-down, do it: */ if (!skip_wfi) { @@ -178,6 +210,8 @@ static void __init dcscb_usage_count_init(void) dcscb_use_count[cpu][cluster] = 1; } +extern void dcscb_power_up_setup(unsigned int affinity_level); + static int __init dcscb_init(void) { struct device_node *node; @@ -196,6 +230,8 @@ static int __init dcscb_init(void) dcscb_usage_count_init(); ret = mcpm_platform_register(&dcscb_power_ops); + if (!ret) + ret = mcpm_sync_init(dcscb_power_up_setup); if (ret) { iounmap(dcscb_base); return ret; diff --git a/arch/arm/mach-vexpress/dcscb_setup.S b/arch/arm/mach-vexpress/dcscb_setup.S new file mode 100644 index 00000000000..93bd13f458a --- /dev/null +++ b/arch/arm/mach-vexpress/dcscb_setup.S @@ -0,0 +1,80 @@ +/* + * arch/arm/include/asm/dcscb_setup.S + * + * Created by: Dave Martin, 2012-06-22 + * Copyright: (C) 2012-2013 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 + + +#define SLAVE_SNOOPCTL_OFFSET 0 +#define SNOOPCTL_SNOOP_ENABLE (1 << 0) +#define SNOOPCTL_DVM_ENABLE (1 << 1) + +#define CCI_STATUS_OFFSET 0xc +#define STATUS_CHANGE_PENDING (1 << 0) + +#define CCI_SLAVE_OFFSET(n) (0x1000 + 0x1000 * (n)) + +#define RTSM_CCI_PHYS_BASE 0x2c090000 +#define RTSM_CCI_SLAVE_A15 3 +#define RTSM_CCI_SLAVE_A7 4 + +#define RTSM_CCI_A15_OFFSET CCI_SLAVE_OFFSET(RTSM_CCI_SLAVE_A15) +#define RTSM_CCI_A7_OFFSET CCI_SLAVE_OFFSET(RTSM_CCI_SLAVE_A7) + + +ENTRY(dcscb_power_up_setup) + + cmp r0, #0 @ check affinity level + beq 2f + +/* + * Enable cluster-level coherency, in preparation for turning on the MMU. + * The ACTLR SMP bit does not need to be set here, because cpu_resume() + * already restores that. + */ + + mrc p15, 0, r0, c0, c0, 5 @ MPIDR + ubfx r0, r0, #8, #4 @ cluster + + @ A15/A7 may not require explicit L2 invalidation on reset, dependent + @ on hardware integration desicions. + @ For now, this code assumes that L2 is either already invalidated, or + @ invalidation is not required. + + ldr r3, =RTSM_CCI_PHYS_BASE + RTSM_CCI_A15_OFFSET + cmp r0, #0 @ A15 cluster? + addne r3, r3, #RTSM_CCI_A7_OFFSET - RTSM_CCI_A15_OFFSET + + @ r3 now points to the correct CCI slave register block + + ldr r0, [r3, #SLAVE_SNOOPCTL_OFFSET] + orr r0, r0, #SNOOPCTL_SNOOP_ENABLE | SNOOPCTL_DVM_ENABLE + str r0, [r3, #SLAVE_SNOOPCTL_OFFSET] @ enable CCI snoops + + @ Wait for snoop control change to complete: + + ldr r3, =RTSM_CCI_PHYS_BASE + +1: ldr r0, [r3, #CCI_STATUS_OFFSET] + tst r0, #STATUS_CHANGE_PENDING + bne 1b + + dsb @ Synchronise side-effects of enabling CCI + + bx lr + +2: @ Implementation-specific local CPU setup operations should go here, + @ if any. In this case, there is nothing to do. + + bx lr + +ENDPROC(dcscb_power_up_setup) -- cgit v1.2.3 From 0839ed53649d1153e7ba8fd8c3bccb48938e9ffd Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Mon, 17 Dec 2012 00:15:00 +0000 Subject: ARM: vexpress: allow native pm ops backends to probe for psci suppport This patch allows the vexpress 'rtsm' 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 Signed-off-by: Jon Medhurst --- arch/arm/mach-vexpress/dcscb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-vexpress/dcscb.c b/arch/arm/mach-vexpress/dcscb.c index 68c8fdddffc..0dc3caca227 100644 --- a/arch/arm/mach-vexpress/dcscb.c +++ b/arch/arm/mach-vexpress/dcscb.c @@ -23,6 +23,7 @@ #include #include #include +#include #define RST_HOLD0 0x0 @@ -218,6 +219,12 @@ static int __init dcscb_init(void) unsigned int cfg; int ret; + ret = psci_probe(); + if (!ret) { + pr_debug("psci found. Aborting native init\n"); + return -ENODEV; + } + node = of_find_compatible_node(NULL, NULL, "arm,rtsm,dcscb"); if (!node) return -ENODEV; -- cgit v1.2.3 From 23c5ac41ee7db68b1d64e75e641f026e8f1afe97 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Wed, 20 Feb 2013 17:34:20 +0000 Subject: ARM: mcpm: Make all mcpm functions notrace The functions in mcpm_entry.c are mostly intended for use during scary cache and coherency disabling sequences, or do other things which confuse trace ... like powering a CPU down and not returning. Similarly for the backend code. For simplicity, this patch just makes whole files notrace. There should be more than enough traceable points on the paths to these functions, but we can be more fine-grained later if there is a need for it. Signed-off-by: Dave Martin --- arch/arm/mach-vexpress/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-vexpress/Makefile b/arch/arm/mach-vexpress/Makefile index f6e90f3272d..f5b78d49246 100644 --- a/arch/arm/mach-vexpress/Makefile +++ b/arch/arm/mach-vexpress/Makefile @@ -7,5 +7,6 @@ ccflags-$(CONFIG_ARCH_MULTIPLATFORM) := -I$(srctree)/$(src)/include \ obj-y := v2m.o reset.o obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o +CFLAGS_REMOVE_dcscb.o = -pg obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -- cgit v1.2.3