diff options
-rw-r--r-- | arch/arm/mach-exynos/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-exynos/common.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/hotplug.c | 91 | ||||
-rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 113 | ||||
-rw-r--r-- | arch/arm/mach-exynos/regs-pmu.h | 3 |
5 files changed, 116 insertions, 96 deletions
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 775ee35cb277..bcefb5473ee4 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -17,9 +17,6 @@ obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o -obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -CFLAGS_hotplug.o += -march=armv7-a - plus_sec := $(call as-instr,.arch_extension sec,+sec) AFLAGS_exynos-smc.o :=-Wa,-march=armv7-a$(plus_sec) AFLAGS_sleep.o :=-Wa,-march=armv7-a$(plus_sec) diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index ef95cb1cc00a..d4d09bc1e48c 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -139,8 +139,6 @@ extern void exynos_cpu_resume_ns(void); extern struct smp_operations exynos_smp_ops; -extern void exynos_cpu_die(unsigned int cpu); - /* PMU(Power Management Unit) support */ #define PMU_TABLE_END (-1U) diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c deleted file mode 100644 index 4d86961a7957..000000000000 --- a/arch/arm/mach-exynos/hotplug.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Cloned from linux/arch/arm/mach-realview/hotplug.c - * - * Copyright (C) 2002 ARM Ltd. - * All Rights Reserved - * - * 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 <linux/kernel.h> -#include <linux/errno.h> -#include <linux/smp.h> -#include <linux/io.h> - -#include <asm/cacheflush.h> -#include <asm/cp15.h> -#include <asm/smp_plat.h> - -#include "common.h" -#include "regs-pmu.h" - -static inline void cpu_leave_lowpower(void) -{ - unsigned int v; - - asm volatile( - "mrc p15, 0, %0, c1, c0, 0\n" - " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (CR_C), "Ir" (0x40) - : "cc"); -} - -static inline void platform_do_lowpower(unsigned int cpu, int *spurious) -{ - u32 mpidr = cpu_logical_map(cpu); - u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); - - for (;;) { - - /* Turn the CPU off on next WFI instruction. */ - exynos_cpu_power_down(core_id); - - wfi(); - - if (pen_release == core_id) { - /* - * OK, proper wakeup, we're done - */ - break; - } - - /* - * Getting here, means that we have come out of WFI without - * having been woken up - this shouldn't happen - * - * Just note it happening - when we're woken, we can report - * its occurrence. - */ - (*spurious)++; - } -} - -/* - * platform-specific code to shutdown a CPU - * - * Called with IRQs disabled - */ -void __ref exynos_cpu_die(unsigned int cpu) -{ - int spurious = 0; - - v7_exit_coherency_flush(louis); - - platform_do_lowpower(cpu, &spurious); - - /* - * bring this CPU back into the world of cache - * coherency, and then restore interrupts - */ - cpu_leave_lowpower(); - - if (spurious) - pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); -} diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 8543064dc445..adb36a8e8785 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -22,6 +22,7 @@ #include <linux/of_address.h> #include <asm/cacheflush.h> +#include <asm/cp15.h> #include <asm/smp_plat.h> #include <asm/smp_scu.h> #include <asm/firmware.h> @@ -33,6 +34,88 @@ extern void exynos4_secondary_startup(void); +/* + * Set or clear the USE_DELAYED_RESET_ASSERTION option, set on Exynos4 SoCs + * during hot-(un)plugging CPUx. + * + * The feature can be cleared safely during first boot of secondary CPU. + * + * Exynos4 SoCs require setting USE_DELAYED_RESET_ASSERTION during powering + * down a CPU so the CPU idle clock down feature could properly detect global + * idle state when CPUx is off. + */ +static void exynos_set_delayed_reset_assertion(u32 core_id, bool enable) +{ + if (soc_is_exynos4()) { + unsigned int tmp; + + tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id)); + if (enable) + tmp |= S5P_USE_DELAYED_RESET_ASSERTION; + else + tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION); + pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id)); + } +} + +#ifdef CONFIG_HOTPLUG_CPU +static inline void cpu_leave_lowpower(u32 core_id) +{ + unsigned int v; + + asm volatile( + "mrc p15, 0, %0, c1, c0, 0\n" + " orr %0, %0, %1\n" + " mcr p15, 0, %0, c1, c0, 0\n" + " mrc p15, 0, %0, c1, c0, 1\n" + " orr %0, %0, %2\n" + " mcr p15, 0, %0, c1, c0, 1\n" + : "=&r" (v) + : "Ir" (CR_C), "Ir" (0x40) + : "cc"); + + exynos_set_delayed_reset_assertion(core_id, false); +} + +static inline void platform_do_lowpower(unsigned int cpu, int *spurious) +{ + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + + for (;;) { + + /* Turn the CPU off on next WFI instruction. */ + exynos_cpu_power_down(core_id); + + /* + * Exynos4 SoCs require setting + * USE_DELAYED_RESET_ASSERTION so the CPU idle + * clock down feature could properly detect + * global idle state when CPUx is off. + */ + exynos_set_delayed_reset_assertion(core_id, true); + + wfi(); + + if (pen_release == core_id) { + /* + * OK, proper wakeup, we're done + */ + break; + } + + /* + * Getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * Just note it happening - when we're woken, we can report + * its occurrence. + */ + (*spurious)++; + } +} +#endif /* CONFIG_HOTPLUG_CPU */ + /** * exynos_core_power_down : power down the specified cpu * @cpu : the cpu to power down @@ -260,6 +343,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) udelay(10); } + /* No harm if this is called during first boot of secondary CPU */ + exynos_set_delayed_reset_assertion(core_id, false); + /* * now the secondary core is starting up let it run its * calibrations, then wait for it to finish @@ -341,6 +427,33 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) } } +#ifdef CONFIG_HOTPLUG_CPU +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +static void exynos_cpu_die(unsigned int cpu) +{ + int spurious = 0; + u32 mpidr = cpu_logical_map(cpu); + u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); + + v7_exit_coherency_flush(louis); + + platform_do_lowpower(cpu, &spurious); + + /* + * bring this CPU back into the world of cache + * coherency, and then restore interrupts + */ + cpu_leave_lowpower(core_id); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +} +#endif /* CONFIG_HOTPLUG_CPU */ + struct smp_operations exynos_smp_ops __initdata = { .smp_init_cpus = exynos_smp_init_cpus, .smp_prepare_cpus = exynos_smp_prepare_cpus, diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h index 4ea5e320c6d1..322f13277c3b 100644 --- a/arch/arm/mach-exynos/regs-pmu.h +++ b/arch/arm/mach-exynos/regs-pmu.h @@ -20,6 +20,7 @@ #define S5P_USE_STANDBY_WFI0 (1 << 16) #define S5P_USE_STANDBY_WFE0 (1 << 24) +#define S5P_USE_DELAYED_RESET_ASSERTION BIT(12) #define EXYNOS_CORE_PO_RESET(n) ((1 << 4) << n) #define EXYNOS_WAKEUP_FROM_LOWPWR (1 << 28) @@ -108,6 +109,8 @@ (EXYNOS_ARM_CORE0_CONFIGURATION + (0x80 * (_nr))) #define EXYNOS_ARM_CORE_STATUS(_nr) \ (EXYNOS_ARM_CORE_CONFIGURATION(_nr) + 0x4) +#define EXYNOS_ARM_CORE_OPTION(_nr) \ + (EXYNOS_ARM_CORE_CONFIGURATION(_nr) + 0x8) #define EXYNOS_ARM_COMMON_CONFIGURATION 0x2500 #define EXYNOS_COMMON_CONFIGURATION(_nr) \ |