diff options
author | Joseph Lo <josephl@nvidia.com> | 2012-08-16 05:31:51 -0400 |
---|---|---|
committer | Stephen Warren <swarren@nvidia.com> | 2012-09-13 13:41:06 -0400 |
commit | 59b0f6825c15d24859e22b1024440ae2a094983e (patch) | |
tree | c0bfc0ebe5a4fc58318eb3a5045287fe452965fc /arch/arm | |
parent | c2be5bfcc986e688c0c9b427b9fb33be178437d8 (diff) |
ARM: tegra30: add CPU hotplug support
Hotplug function put CPUs in offline or online state at runtime.
When the CPU been put in the offline state, it was been clock and
power gated. Except primary CPU other CPUs can be hotplugged.
Based on the work by:
Scott Williams <scwilliams@nvidia.com>
Colin Cross <ccross@android.com>
Gary King <gking@nvidia.com>
Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/hotplug.c | 110 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-t30.S | 107 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 25 |
5 files changed, 159 insertions, 86 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f07f99452a98..efb2f90a8f77 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile | |||
@@ -17,6 +17,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o | |||
17 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o | 17 | obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o |
18 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o | 18 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o |
19 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o | 19 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o |
20 | obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-t30.o | ||
20 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o | 21 | obj-$(CONFIG_SMP) += platsmp.o headsmp.o |
21 | obj-$(CONFIG_SMP) += reset.o | 22 | obj-$(CONFIG_SMP) += reset.o |
22 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o | 23 | obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o |
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index f3654f830991..06520564d815 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include "fuse.h" | 34 | #include "fuse.h" |
35 | #include "pmc.h" | 35 | #include "pmc.h" |
36 | #include "apbio.h" | 36 | #include "apbio.h" |
37 | #include "sleep.h" | ||
37 | 38 | ||
38 | /* | 39 | /* |
39 | * Storage for debug-macro.S's state. | 40 | * Storage for debug-macro.S's state. |
@@ -147,6 +148,7 @@ void __init tegra30_init_early(void) | |||
147 | tegra_init_cache(0x441, 0x551); | 148 | tegra_init_cache(0x441, 0x551); |
148 | tegra_pmc_init(); | 149 | tegra_pmc_init(); |
149 | tegra_powergate_init(); | 150 | tegra_powergate_init(); |
151 | tegra30_hotplug_init(); | ||
150 | } | 152 | } |
151 | #endif | 153 | #endif |
152 | 154 | ||
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index d8dc9ddd6d18..be92d4c07606 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c | |||
@@ -1,91 +1,23 @@ | |||
1 | /* | 1 | /* |
2 | * linux/arch/arm/mach-realview/hotplug.c | ||
3 | * | 2 | * |
4 | * Copyright (C) 2002 ARM Ltd. | 3 | * Copyright (C) 2002 ARM Ltd. |
5 | * All Rights Reserved | 4 | * All Rights Reserved |
5 | * Copyright (c) 2010, 2012 NVIDIA Corporation. All rights reserved. | ||
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
10 | */ | 10 | */ |
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/errno.h> | ||
13 | #include <linux/smp.h> | 12 | #include <linux/smp.h> |
14 | 13 | ||
15 | #include <asm/cacheflush.h> | 14 | #include <asm/cacheflush.h> |
16 | #include <asm/cp15.h> | 15 | #include <asm/smp_plat.h> |
17 | 16 | ||
18 | static inline void cpu_enter_lowpower(void) | 17 | #include "sleep.h" |
19 | { | 18 | #include "tegra_cpu_car.h" |
20 | unsigned int v; | ||
21 | 19 | ||
22 | flush_cache_all(); | 20 | static void (*tegra_hotplug_shutdown)(void); |
23 | asm volatile( | ||
24 | " mcr p15, 0, %1, c7, c5, 0\n" | ||
25 | " mcr p15, 0, %1, c7, c10, 4\n" | ||
26 | /* | ||
27 | * Turn off coherency | ||
28 | */ | ||
29 | " mrc p15, 0, %0, c1, c0, 1\n" | ||
30 | " bic %0, %0, #0x20\n" | ||
31 | " mcr p15, 0, %0, c1, c0, 1\n" | ||
32 | " mrc p15, 0, %0, c1, c0, 0\n" | ||
33 | " bic %0, %0, %2\n" | ||
34 | " mcr p15, 0, %0, c1, c0, 0\n" | ||
35 | : "=&r" (v) | ||
36 | : "r" (0), "Ir" (CR_C) | ||
37 | : "cc"); | ||
38 | } | ||
39 | |||
40 | static inline void cpu_leave_lowpower(void) | ||
41 | { | ||
42 | unsigned int v; | ||
43 | |||
44 | asm volatile( | ||
45 | "mrc p15, 0, %0, c1, c0, 0\n" | ||
46 | " orr %0, %0, %1\n" | ||
47 | " mcr p15, 0, %0, c1, c0, 0\n" | ||
48 | " mrc p15, 0, %0, c1, c0, 1\n" | ||
49 | " orr %0, %0, #0x20\n" | ||
50 | " mcr p15, 0, %0, c1, c0, 1\n" | ||
51 | : "=&r" (v) | ||
52 | : "Ir" (CR_C) | ||
53 | : "cc"); | ||
54 | } | ||
55 | |||
56 | static inline void platform_do_lowpower(unsigned int cpu, int *spurious) | ||
57 | { | ||
58 | /* | ||
59 | * there is no power-control hardware on this platform, so all | ||
60 | * we can do is put the core into WFI; this is safe as the calling | ||
61 | * code will have already disabled interrupts | ||
62 | */ | ||
63 | for (;;) { | ||
64 | /* | ||
65 | * here's the WFI | ||
66 | */ | ||
67 | asm(".word 0xe320f003\n" | ||
68 | : | ||
69 | : | ||
70 | : "memory", "cc"); | ||
71 | |||
72 | /*if (pen_release == cpu) {*/ | ||
73 | /* | ||
74 | * OK, proper wakeup, we're done | ||
75 | */ | ||
76 | break; | ||
77 | /*}*/ | ||
78 | |||
79 | /* | ||
80 | * Getting here, means that we have come out of WFI without | ||
81 | * having been woken up - this shouldn't happen | ||
82 | * | ||
83 | * Just note it happening - when we're woken, we can report | ||
84 | * its occurrence. | ||
85 | */ | ||
86 | (*spurious)++; | ||
87 | } | ||
88 | } | ||
89 | 21 | ||
90 | int platform_cpu_kill(unsigned int cpu) | 22 | int platform_cpu_kill(unsigned int cpu) |
91 | { | 23 | { |
@@ -99,22 +31,20 @@ int platform_cpu_kill(unsigned int cpu) | |||
99 | */ | 31 | */ |
100 | void platform_cpu_die(unsigned int cpu) | 32 | void platform_cpu_die(unsigned int cpu) |
101 | { | 33 | { |
102 | int spurious = 0; | 34 | cpu = cpu_logical_map(cpu); |
103 | 35 | ||
104 | /* | 36 | /* Flush the L1 data cache. */ |
105 | * we're ready for shutdown now, so do it | 37 | flush_cache_all(); |
106 | */ | ||
107 | cpu_enter_lowpower(); | ||
108 | platform_do_lowpower(cpu, &spurious); | ||
109 | 38 | ||
110 | /* | 39 | /* Shut down the current CPU. */ |
111 | * bring this CPU back into the world of cache | 40 | tegra_hotplug_shutdown(); |
112 | * coherency, and then restore interrupts | 41 | |
113 | */ | 42 | /* Clock gate the CPU */ |
114 | cpu_leave_lowpower(); | 43 | tegra_wait_cpu_in_reset(cpu); |
44 | tegra_disable_cpu_clock(cpu); | ||
115 | 45 | ||
116 | if (spurious) | 46 | /* Should never return here. */ |
117 | pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); | 47 | BUG(); |
118 | } | 48 | } |
119 | 49 | ||
120 | int platform_cpu_disable(unsigned int cpu) | 50 | int platform_cpu_disable(unsigned int cpu) |
@@ -125,3 +55,11 @@ int platform_cpu_disable(unsigned int cpu) | |||
125 | */ | 55 | */ |
126 | return cpu == 0 ? -EPERM : 0; | 56 | return cpu == 0 ? -EPERM : 0; |
127 | } | 57 | } |
58 | |||
59 | #ifdef CONFIG_ARCH_TEGRA_3x_SOC | ||
60 | extern void tegra30_hotplug_shutdown(void); | ||
61 | void __init tegra30_hotplug_init(void) | ||
62 | { | ||
63 | tegra_hotplug_shutdown = tegra30_hotplug_shutdown; | ||
64 | } | ||
65 | #endif | ||
diff --git a/arch/arm/mach-tegra/sleep-t30.S b/arch/arm/mach-tegra/sleep-t30.S new file mode 100644 index 000000000000..777d9cee8b90 --- /dev/null +++ b/arch/arm/mach-tegra/sleep-t30.S | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012, NVIDIA Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <linux/linkage.h> | ||
18 | |||
19 | #include <asm/assembler.h> | ||
20 | |||
21 | #include <mach/iomap.h> | ||
22 | |||
23 | #include "sleep.h" | ||
24 | #include "flowctrl.h" | ||
25 | |||
26 | #define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */ | ||
27 | |||
28 | #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) | ||
29 | /* | ||
30 | * tegra30_hotplug_shutdown(void) | ||
31 | * | ||
32 | * Powergates the current CPU. | ||
33 | * Should never return. | ||
34 | */ | ||
35 | ENTRY(tegra30_hotplug_shutdown) | ||
36 | /* Turn off SMP coherency */ | ||
37 | exit_smp r4, r5 | ||
38 | |||
39 | /* Powergate this CPU */ | ||
40 | mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN | ||
41 | bl tegra30_cpu_shutdown | ||
42 | mov pc, lr @ should never get here | ||
43 | ENDPROC(tegra30_hotplug_shutdown) | ||
44 | |||
45 | /* | ||
46 | * tegra30_cpu_shutdown(unsigned long flags) | ||
47 | * | ||
48 | * Puts the current CPU in wait-for-event mode on the flow controller | ||
49 | * and powergates it -- flags (in R0) indicate the request type. | ||
50 | * Must never be called for CPU 0. | ||
51 | * | ||
52 | * corrupts r0-r4, r12 | ||
53 | */ | ||
54 | ENTRY(tegra30_cpu_shutdown) | ||
55 | cpu_id r3 | ||
56 | cmp r3, #0 | ||
57 | moveq pc, lr @ Must never be called for CPU 0 | ||
58 | |||
59 | ldr r12, =TEGRA_FLOW_CTRL_VIRT | ||
60 | cpu_to_csr_reg r1, r3 | ||
61 | add r1, r1, r12 @ virtual CSR address for this CPU | ||
62 | cpu_to_halt_reg r2, r3 | ||
63 | add r2, r2, r12 @ virtual HALT_EVENTS address for this CPU | ||
64 | |||
65 | /* | ||
66 | * Clear this CPU's "event" and "interrupt" flags and power gate | ||
67 | * it when halting but not before it is in the "WFE" state. | ||
68 | */ | ||
69 | movw r12, \ | ||
70 | FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | \ | ||
71 | FLOW_CTRL_CSR_ENABLE | ||
72 | mov r4, #(1 << 4) | ||
73 | orr r12, r12, r4, lsl r3 | ||
74 | str r12, [r1] | ||
75 | |||
76 | /* Halt this CPU. */ | ||
77 | mov r3, #0x400 | ||
78 | delay_1: | ||
79 | subs r3, r3, #1 @ delay as a part of wfe war. | ||
80 | bge delay_1; | ||
81 | cpsid a @ disable imprecise aborts. | ||
82 | ldr r3, [r1] @ read CSR | ||
83 | str r3, [r1] @ clear CSR | ||
84 | tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN | ||
85 | movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug | ||
86 | str r3, [r2] | ||
87 | ldr r0, [r2] | ||
88 | b wfe_war | ||
89 | |||
90 | __cpu_reset_again: | ||
91 | dsb | ||
92 | .align 5 | ||
93 | wfe @ CPU should be power gated here | ||
94 | wfe_war: | ||
95 | b __cpu_reset_again | ||
96 | |||
97 | /* | ||
98 | * 38 nop's, which fills reset of wfe cache line and | ||
99 | * 4 more cachelines with nop | ||
100 | */ | ||
101 | .rept 38 | ||
102 | nop | ||
103 | .endr | ||
104 | b . @ should never get here | ||
105 | |||
106 | ENDPROC(tegra30_cpu_shutdown) | ||
107 | #endif | ||
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index d0c7a8b1c885..0f047eb3ca2e 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h | |||
@@ -19,6 +19,8 @@ | |||
19 | 19 | ||
20 | #include <mach/iomap.h> | 20 | #include <mach/iomap.h> |
21 | 21 | ||
22 | #define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \ | ||
23 | + IO_CPU_VIRT) | ||
22 | #define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \ | 24 | #define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \ |
23 | + IO_PPSB_VIRT) | 25 | + IO_PPSB_VIRT) |
24 | 26 | ||
@@ -52,5 +54,28 @@ | |||
52 | movw \reg, #:lower16:\val | 54 | movw \reg, #:lower16:\val |
53 | movt \reg, #:upper16:\val | 55 | movt \reg, #:upper16:\val |
54 | .endm | 56 | .endm |
57 | |||
58 | /* Macro to exit SMP coherency. */ | ||
59 | .macro exit_smp, tmp1, tmp2 | ||
60 | mrc p15, 0, \tmp1, c1, c0, 1 @ ACTLR | ||
61 | bic \tmp1, \tmp1, #(1<<6) | (1<<0) @ clear ACTLR.SMP | ACTLR.FW | ||
62 | mcr p15, 0, \tmp1, c1, c0, 1 @ ACTLR | ||
63 | isb | ||
64 | cpu_id \tmp1 | ||
65 | mov \tmp1, \tmp1, lsl #2 | ||
66 | mov \tmp2, #0xf | ||
67 | mov \tmp2, \tmp2, lsl \tmp1 | ||
68 | mov32 \tmp1, TEGRA_ARM_PERIF_VIRT + 0xC | ||
69 | str \tmp2, [\tmp1] @ invalidate SCU tags for CPU | ||
70 | dsb | ||
71 | .endm | ||
72 | #else | ||
73 | |||
74 | #ifdef CONFIG_HOTPLUG_CPU | ||
75 | void tegra30_hotplug_init(void); | ||
76 | #else | ||
77 | static inline void tegra30_hotplug_init(void) {} | ||
78 | #endif | ||
79 | |||
55 | #endif | 80 | #endif |
56 | #endif | 81 | #endif |