aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-02-21 20:46:23 -0500
committerErik Gilling <konkers@android.com>2010-08-05 17:57:01 -0400
commit1cea7326b3fff97d17d33fb8f33163409a84431b (patch)
tree6f8964633b9ca49a7a86921007d0e8a24654e5ad /arch/arm
parentd861196163e30c07add471562b45dce38517c9b2 (diff)
[ARM] tegra: SMP support
Signed-off-by: Colin Cross <ccross@android.com> Signed-off-by: Erik Gilling <konkers@android.com>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/Kconfig10
-rw-r--r--arch/arm/mach-tegra/Makefile2
-rw-r--r--arch/arm/mach-tegra/headsmp.S61
-rw-r--r--arch/arm/mach-tegra/hotplug.c140
-rw-r--r--arch/arm/mach-tegra/include/mach/smp.h30
-rw-r--r--arch/arm/mach-tegra/localtimer.c25
-rw-r--r--arch/arm/mach-tegra/platsmp.c156
7 files changed, 420 insertions, 4 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 43aad7a0207a..0ca4a94204df 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1112,10 +1112,11 @@ config SMP
1112 bool "Symmetric Multi-Processing (EXPERIMENTAL)" 1112 bool "Symmetric Multi-Processing (EXPERIMENTAL)"
1113 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\ 1113 depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\
1114 MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\ 1114 MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\
1115 ARCH_U8500 || ARCH_VEXPRESS_CA9X4) 1115 ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
1116 depends on GENERIC_CLOCKEVENTS 1116 depends on GENERIC_CLOCKEVENTS
1117 select USE_GENERIC_SMP_HELPERS 1117 select USE_GENERIC_SMP_HELPERS
1118 select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || ARCH_VEXPRESS_CA9X4) 1118 select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || \
1119 ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
1119 help 1120 help
1120 This enables support for systems with more than one CPU. If you have 1121 This enables support for systems with more than one CPU. If you have
1121 a system with only one CPU, like most personal computers, say N. If 1122 a system with only one CPU, like most personal computers, say N. If
@@ -1185,9 +1186,10 @@ config LOCAL_TIMERS
1185 bool "Use local timer interrupts" 1186 bool "Use local timer interrupts"
1186 depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \ 1187 depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \
1187 REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \ 1188 REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \
1188 ARCH_U8500 || ARCH_VEXPRESS_CA9X4) 1189 ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
1189 default y 1190 default y
1190 select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_OMAP4 || ARCH_U8500) 1191 select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_OMAP4 || \\
1192 ARCH_U8500 || ARCH_TEGRA
1191 help 1193 help
1192 Enable support for local timers on SMP platforms, rather then the 1194 Enable support for local timers on SMP platforms, rather then the
1193 legacy IPI broadcast method. Local timers allows the system 1195 legacy IPI broadcast method. Local timers allows the system
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index e20546ab2f5f..f339559ca161 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -3,3 +3,5 @@ obj-y += io.o
3obj-y += irq.o 3obj-y += irq.o
4obj-y += clock.o 4obj-y += clock.o
5obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o 5obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
6obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
7obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
new file mode 100644
index 000000000000..b5349b2f13d2
--- /dev/null
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -0,0 +1,61 @@
1#include <linux/linkage.h>
2#include <linux/init.h>
3
4 .section ".text.head", "ax"
5 __CPUINIT
6
7/*
8 * Tegra specific entry point for secondary CPUs.
9 * The secondary kernel init calls v7_flush_dcache_all before it enables
10 * the L1; however, the L1 comes out of reset in an undefined state, so
11 * the clean + invalidate performed by v7_flush_dcache_all causes a bunch
12 * of cache lines with uninitialized data and uninitialized tags to get
13 * written out to memory, which does really unpleasant things to the main
14 * processor. We fix this by performing an invalidate, rather than a
15 * clean + invalidate, before jumping into the kernel.
16 */
17ENTRY(v7_invalidate_l1)
18 mov r0, #0
19 mcr p15, 2, r0, c0, c0, 0
20 mrc p15, 1, r0, c0, c0, 0
21
22 ldr r1, =0x7fff
23 and r2, r1, r0, lsr #13
24
25 ldr r1, =0x3ff
26
27 and r3, r1, r0, lsr #3 @ NumWays - 1
28 add r2, r2, #1 @ NumSets
29
30 and r0, r0, #0x7
31 add r0, r0, #4 @ SetShift
32
33 clz r1, r3 @ WayShift
34 add r4, r3, #1 @ NumWays
351: sub r2, r2, #1 @ NumSets--
36 mov r3, r4 @ Temp = NumWays
372: subs r3, r3, #1 @ Temp--
38 mov r5, r3, lsl r1
39 mov r6, r2, lsl r0
40 orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
41 mcr p15, 0, r5, c7, c6, 2
42 bgt 2b
43 cmp r2, #0
44 bgt 1b
45 dsb
46 isb
47 mov pc, lr
48ENDPROC(v7_invalidate_l1)
49
50ENTRY(tegra_secondary_startup)
51 msr cpsr_fsxc, #0xd3
52 bl v7_invalidate_l1
53 mrc p15, 0, r0, c0, c0, 5
54 and r0, r0, #15
55 ldr r1, =0x6000f100
56 str r0, [r1]
571: ldr r2, [r1]
58 cmp r0, r2
59 beq 1b
60 b secondary_startup
61ENDPROC(tegra_secondary_startup)
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c
new file mode 100644
index 000000000000..8e7f115aa21e
--- /dev/null
+++ b/arch/arm/mach-tegra/hotplug.c
@@ -0,0 +1,140 @@
1/*
2 * linux/arch/arm/mach-realview/hotplug.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
6 *
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
9 * published by the Free Software Foundation.
10 */
11#include <linux/kernel.h>
12#include <linux/errno.h>
13#include <linux/smp.h>
14#include <linux/completion.h>
15
16#include <asm/cacheflush.h>
17
18static DECLARE_COMPLETION(cpu_killed);
19
20static inline void cpu_enter_lowpower(void)
21{
22 unsigned int v;
23
24 flush_cache_all();
25 asm volatile(
26 " mcr p15, 0, %1, c7, c5, 0\n"
27 " mcr p15, 0, %1, c7, c10, 4\n"
28 /*
29 * Turn off coherency
30 */
31 " mrc p15, 0, %0, c1, c0, 1\n"
32 " bic %0, %0, #0x20\n"
33 " mcr p15, 0, %0, c1, c0, 1\n"
34 " mrc p15, 0, %0, c1, c0, 0\n"
35 " bic %0, %0, #0x04\n"
36 " mcr p15, 0, %0, c1, c0, 0\n"
37 : "=&r" (v)
38 : "r" (0)
39 : "cc");
40}
41
42static inline void cpu_leave_lowpower(void)
43{
44 unsigned int v;
45
46 asm volatile(
47 "mrc p15, 0, %0, c1, c0, 0\n"
48 " orr %0, %0, #0x04\n"
49 " mcr p15, 0, %0, c1, c0, 0\n"
50 " mrc p15, 0, %0, c1, c0, 1\n"
51 " orr %0, %0, #0x20\n"
52 " mcr p15, 0, %0, c1, c0, 1\n"
53 : "=&r" (v)
54 :
55 : "cc");
56}
57
58static inline void platform_do_lowpower(unsigned int cpu)
59{
60 /*
61 * there is no power-control hardware on this platform, so all
62 * we can do is put the core into WFI; this is safe as the calling
63 * code will have already disabled interrupts
64 */
65 for (;;) {
66 /*
67 * here's the WFI
68 */
69 asm(".word 0xe320f003\n"
70 :
71 :
72 : "memory", "cc");
73
74 /*if (pen_release == cpu) {*/
75 /*
76 * OK, proper wakeup, we're done
77 */
78 break;
79 /*}*/
80
81 /*
82 * getting here, means that we have come out of WFI without
83 * having been woken up - this shouldn't happen
84 *
85 * The trouble is, letting people know about this is not really
86 * possible, since we are currently running incoherently, and
87 * therefore cannot safely call printk() or anything else
88 */
89#ifdef DEBUG
90 printk(KERN_WARN "CPU%u: spurious wakeup call\n", cpu);
91#endif
92 }
93}
94
95int platform_cpu_kill(unsigned int cpu)
96{
97 return wait_for_completion_timeout(&cpu_killed, 5000);
98}
99
100/*
101 * platform-specific code to shutdown a CPU
102 *
103 * Called with IRQs disabled
104 */
105void platform_cpu_die(unsigned int cpu)
106{
107#ifdef DEBUG
108 unsigned int this_cpu = hard_smp_processor_id();
109
110 if (cpu != this_cpu) {
111 printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
112 this_cpu, cpu);
113 BUG();
114 }
115#endif
116
117 printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
118 complete(&cpu_killed);
119
120 /*
121 * we're ready for shutdown now, so do it
122 */
123 cpu_enter_lowpower();
124 platform_do_lowpower(cpu);
125
126 /*
127 * bring this CPU back into the world of cache
128 * coherency, and then restore interrupts
129 */
130 cpu_leave_lowpower();
131}
132
133int platform_cpu_disable(unsigned int cpu)
134{
135 /*
136 * we don't allow CPU 0 to be shutdown (it is still too special
137 * e.g. clock tick interrupts)
138 */
139 return cpu == 0 ? -EPERM : 0;
140}
diff --git a/arch/arm/mach-tegra/include/mach/smp.h b/arch/arm/mach-tegra/include/mach/smp.h
new file mode 100644
index 000000000000..8b42dab79a70
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/smp.h
@@ -0,0 +1,30 @@
1#ifndef ASMARM_ARCH_SMP_H
2#define ASMARM_ARCH_SMP_H
3
4
5#include <asm/hardware/gic.h>
6
7#define hard_smp_processor_id() \
8 ({ \
9 unsigned int cpunum; \
10 __asm__("mrc p15, 0, %0, c0, c0, 5" \
11 : "=r" (cpunum)); \
12 cpunum &= 0x0F; \
13 })
14
15/*
16 * We use IRQ1 as the IPI
17 */
18static inline void smp_cross_call(const struct cpumask *mask)
19{
20 gic_raise_softirq(mask, 1);
21}
22
23/*
24 * Do nothing on MPcore.
25 */
26static inline void smp_cross_call_done(cpumask_t callmap)
27{
28}
29
30#endif
diff --git a/arch/arm/mach-tegra/localtimer.c b/arch/arm/mach-tegra/localtimer.c
new file mode 100644
index 000000000000..f81ca7cbbc1f
--- /dev/null
+++ b/arch/arm/mach-tegra/localtimer.c
@@ -0,0 +1,25 @@
1/*
2 * arch/arm/mach-tegra/localtimer.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
6 *
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
9 * published by the Free Software Foundation.
10 */
11#include <linux/init.h>
12#include <linux/smp.h>
13#include <linux/clockchips.h>
14#include <asm/irq.h>
15#include <asm/smp_twd.h>
16#include <asm/localtimer.h>
17
18/*
19 * Setup the local clock events for a CPU.
20 */
21void __cpuinit local_timer_setup(struct clock_event_device *evt)
22{
23 evt->irq = IRQ_LOCALTIMER;
24 twd_timer_setup(evt);
25}
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
new file mode 100644
index 000000000000..1c0fd92cab39
--- /dev/null
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -0,0 +1,156 @@
1/*
2 * linux/arch/arm/mach-tegra/platsmp.c
3 *
4 * Copyright (C) 2002 ARM Ltd.
5 * All Rights Reserved
6 *
7 * Copyright (C) 2009 Palm
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14#include <linux/init.h>
15#include <linux/errno.h>
16#include <linux/delay.h>
17#include <linux/device.h>
18#include <linux/jiffies.h>
19#include <linux/smp.h>
20#include <linux/io.h>
21
22#include <asm/cacheflush.h>
23#include <mach/hardware.h>
24#include <asm/mach-types.h>
25#include <asm/localtimer.h>
26#include <asm/smp_scu.h>
27
28#include <mach/iomap.h>
29
30extern void tegra_secondary_startup(void);
31
32static DEFINE_SPINLOCK(boot_lock);
33static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
34
35#define EVP_CPU_RESET_VECTOR \
36 (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
37#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
38 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
39#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
40 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
41
42void __cpuinit platform_secondary_init(unsigned int cpu)
43{
44 trace_hardirqs_off();
45
46 /*
47 * if any interrupts are already enabled for the primary
48 * core (e.g. timer irq), then they will not have been enabled
49 * for us: do so
50 */
51 gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x100);
52
53 /*
54 * Synchronise with the boot thread.
55 */
56 spin_lock(&boot_lock);
57 spin_unlock(&boot_lock);
58}
59
60int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
61{
62 unsigned long old_boot_vector;
63 unsigned long boot_vector;
64 unsigned long timeout;
65 u32 reg;
66
67 /*
68 * set synchronisation state between this boot processor
69 * and the secondary one
70 */
71 spin_lock(&boot_lock);
72
73
74 /* set the reset vector to point to the secondary_startup routine */
75
76 boot_vector = virt_to_phys(tegra_secondary_startup);
77 old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
78 writel(boot_vector, EVP_CPU_RESET_VECTOR);
79
80 /* enable cpu clock on cpu1 */
81 reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
82 writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
83
84 reg = (1<<13) | (1<<9) | (1<<5) | (1<<1);
85 writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
86
87 smp_wmb();
88 flush_cache_all();
89
90 /* unhalt the cpu */
91 writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14);
92
93 timeout = jiffies + (1 * HZ);
94 while (time_before(jiffies, timeout)) {
95 if (readl(EVP_CPU_RESET_VECTOR) != boot_vector)
96 break;
97 udelay(10);
98 }
99
100 /* put the old boot vector back */
101 writel(old_boot_vector, EVP_CPU_RESET_VECTOR);
102
103 /*
104 * now the secondary core is starting up let it run its
105 * calibrations, then wait for it to finish
106 */
107 spin_unlock(&boot_lock);
108
109 return 0;
110}
111
112/*
113 * Initialise the CPU possible map early - this describes the CPUs
114 * which may be present or become present in the system.
115 */
116void __init smp_init_cpus(void)
117{
118 unsigned int i, ncores = scu_get_core_count(scu_base);
119
120 for (i = 0; i < ncores; i++)
121 cpu_set(i, cpu_possible_map);
122}
123
124void __init smp_prepare_cpus(unsigned int max_cpus)
125{
126 unsigned int ncores = scu_get_core_count(scu_base);
127 unsigned int cpu = smp_processor_id();
128 int i;
129
130 smp_store_cpu_info(cpu);
131
132 /*
133 * are we trying to boot more cores than exist?
134 */
135 if (max_cpus > ncores)
136 max_cpus = ncores;
137
138 /*
139 * Initialise the present map, which describes the set of CPUs
140 * actually populated at the present time.
141 */
142 for (i = 0; i < max_cpus; i++)
143 set_cpu_present(i, true);
144
145 /*
146 * Initialise the SCU if there are more than one CPU and let
147 * them know where to start. Note that, on modern versions of
148 * MILO, the "poke" doesn't actually do anything until each
149 * individual core is sent a soft interrupt to get it out of
150 * WFI
151 */
152 if (max_cpus > 1) {
153 percpu_timer_setup();
154 scu_enable(scu_base);
155 }
156}