aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter De Schrijver <pdeschrijver@nvidia.com>2012-02-09 18:47:45 -0500
committerOlof Johansson <olof@lixom.net>2012-02-26 17:44:44 -0500
commitb36ab9754efbd7429d214b3b03dc9843882571bd (patch)
tree4829030d17f9c03d78b3d065bd04c4c63814c8e1
parent26fe681fdaa5c800f1f99e8181631866e50ed8a1 (diff)
ARM: tegra: rework Tegra secondary CPU core bringup
Prepare the Tegra secondary CPU core bringup code for other Tegra variants. The reset handler is also generalized to allow for future introduction of powersaving modes which turn off the CPU cores. Based on work by: Scott Williams <scwilliams@nvidia.com> Chris Johnson <cwj@nvidia.com> Colin Cross <ccross@android.com> Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com> Acked-by: Stephen Warren <swarren@nvidia.com> Tested-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r--arch/arm/mach-tegra/Makefile1
-rw-r--r--arch/arm/mach-tegra/headsmp.S135
-rw-r--r--arch/arm/mach-tegra/include/mach/iomap.h3
-rw-r--r--arch/arm/mach-tegra/platsmp.c97
-rw-r--r--arch/arm/mach-tegra/reset.c84
-rw-r--r--arch/arm/mach-tegra/reset.h50
6 files changed, 317 insertions, 53 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index c103008d960e..7c77a539f858 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-tegra30-tables.o
19obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o 19obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
20obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o 20obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o
21obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o 21obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
22obj-$(CONFIG_SMP) += reset.o
22obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o 23obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
23obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o apbio.o 24obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o apbio.o
24obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o 25obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index b5349b2f13d2..37d6dd96d62f 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -1,6 +1,23 @@
1#include <linux/linkage.h> 1#include <linux/linkage.h>
2#include <linux/init.h> 2#include <linux/init.h>
3 3
4#include <asm/cache.h>
5
6#include <mach/iomap.h>
7
8#include "flowctrl.h"
9#include "reset.h"
10
11#define APB_MISC_GP_HIDREV 0x804
12#define PMC_SCRATCH41 0x140
13
14#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
15
16 .macro mov32, reg, val
17 movw \reg, #:lower16:\val
18 movt \reg, #:upper16:\val
19 .endm
20
4 .section ".text.head", "ax" 21 .section ".text.head", "ax"
5 __CPUINIT 22 __CPUINIT
6 23
@@ -47,15 +64,117 @@ ENTRY(v7_invalidate_l1)
47 mov pc, lr 64 mov pc, lr
48ENDPROC(v7_invalidate_l1) 65ENDPROC(v7_invalidate_l1)
49 66
67
50ENTRY(tegra_secondary_startup) 68ENTRY(tegra_secondary_startup)
51 msr cpsr_fsxc, #0xd3
52 bl v7_invalidate_l1 69 bl v7_invalidate_l1
53 mrc p15, 0, r0, c0, c0, 5 70 /* Enable coresight */
54 and r0, r0, #15 71 mov32 r0, 0xC5ACCE55
55 ldr r1, =0x6000f100 72 mcr p14, 0, r0, c7, c12, 6
56 str r0, [r1]
571: ldr r2, [r1]
58 cmp r0, r2
59 beq 1b
60 b secondary_startup 73 b secondary_startup
61ENDPROC(tegra_secondary_startup) 74ENDPROC(tegra_secondary_startup)
75
76 .align L1_CACHE_SHIFT
77ENTRY(__tegra_cpu_reset_handler_start)
78
79/*
80 * __tegra_cpu_reset_handler:
81 *
82 * Common handler for all CPU reset events.
83 *
84 * Register usage within the reset handler:
85 *
86 * R7 = CPU present (to the OS) mask
87 * R8 = CPU in LP1 state mask
88 * R9 = CPU in LP2 state mask
89 * R10 = CPU number
90 * R11 = CPU mask
91 * R12 = pointer to reset handler data
92 *
93 * NOTE: This code is copied to IRAM. All code and data accesses
94 * must be position-independent.
95 */
96
97 .align L1_CACHE_SHIFT
98ENTRY(__tegra_cpu_reset_handler)
99
100 cpsid aif, 0x13 @ SVC mode, interrupts disabled
101 mrc p15, 0, r10, c0, c0, 5 @ MPIDR
102 and r10, r10, #0x3 @ R10 = CPU number
103 mov r11, #1
104 mov r11, r11, lsl r10 @ R11 = CPU mask
105 adr r12, __tegra_cpu_reset_handler_data
106
107#ifdef CONFIG_SMP
108 /* Does the OS know about this CPU? */
109 ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
110 tst r7, r11 @ if !present
111 bleq __die @ CPU not present (to OS)
112#endif
113
114#ifdef CONFIG_ARCH_TEGRA_2x_SOC
115 /* Are we on Tegra20? */
116 mov32 r6, TEGRA_APB_MISC_BASE
117 ldr r0, [r6, #APB_MISC_GP_HIDREV]
118 and r0, r0, #0xff00
119 cmp r0, #(0x20 << 8)
120 bne 1f
121 /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
122 mov32 r6, TEGRA_PMC_BASE
123 mov r0, #0
124 cmp r10, #0
125 strne r0, [r6, #PMC_SCRATCH41]
1261:
127#endif
128
129#ifdef CONFIG_SMP
130 /*
131 * Can only be secondary boot (initial or hotplug) but CPU 0
132 * cannot be here.
133 */
134 cmp r10, #0
135 bleq __die @ CPU0 cannot be here
136 ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
137 cmp lr, #0
138 bleq __die @ no secondary startup handler
139 bx lr
140#endif
141
142/*
143 * We don't know why the CPU reset. Just kill it.
144 * The LR register will contain the address we died at + 4.
145 */
146
147__die:
148 sub lr, lr, #4
149 mov32 r7, TEGRA_PMC_BASE
150 str lr, [r7, #PMC_SCRATCH41]
151
152 mov32 r7, TEGRA_CLK_RESET_BASE
153
154 /* Are we on Tegra20? */
155 mov32 r6, TEGRA_APB_MISC_BASE
156 ldr r0, [r6, #APB_MISC_GP_HIDREV]
157 and r0, r0, #0xff00
158 cmp r0, #(0x20 << 8)
159 bne 1f
160
161#ifdef CONFIG_ARCH_TEGRA_2x_SOC
162 mov32 r0, 0x1111
163 mov r1, r0, lsl r10
164 str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
165#endif
1661:
167 /* If the CPU still isn't dead, just spin here. */
168 b .
169ENDPROC(__tegra_cpu_reset_handler)
170
171 .align L1_CACHE_SHIFT
172 .type __tegra_cpu_reset_handler_data, %object
173 .globl __tegra_cpu_reset_handler_data
174__tegra_cpu_reset_handler_data:
175 .rept TEGRA_RESET_DATA_SIZE
176 .long 0
177 .endr
178 .align L1_CACHE_SHIFT
179
180ENTRY(__tegra_cpu_reset_handler_end)
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 67644c905d8e..cff672a344f4 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -113,6 +113,9 @@
113#define TEGRA_AHB_GIZMO_BASE 0x6000C004 113#define TEGRA_AHB_GIZMO_BASE 0x6000C004
114#define TEGRA_AHB_GIZMO_SIZE 0x10C 114#define TEGRA_AHB_GIZMO_SIZE 0x10C
115 115
116#define TEGRA_SB_BASE 0x6000C200
117#define TEGRA_SB_SIZE 256
118
116#define TEGRA_STATMON_BASE 0x6000C400 119#define TEGRA_STATMON_BASE 0x6000C400
117#define TEGRA_STATMON_SIZE SZ_1K 120#define TEGRA_STATMON_SIZE SZ_1K
118 121
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index 7d2b5d03c1df..79a241a6320f 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -26,18 +26,26 @@
26 26
27#include <mach/iomap.h> 27#include <mach/iomap.h>
28 28
29#include "fuse.h"
30#include "flowctrl.h"
31#include "reset.h"
32
29extern void tegra_secondary_startup(void); 33extern void tegra_secondary_startup(void);
30 34
31static DEFINE_SPINLOCK(boot_lock);
32static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); 35static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
33 36
34#define EVP_CPU_RESET_VECTOR \ 37#define EVP_CPU_RESET_VECTOR \
35 (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) 38 (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
36#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ 39#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
37 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) 40 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
41#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
42 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
38#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ 43#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
39 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) 44 (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
40 45
46#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
47#define CPU_RESET(cpu) (0x1111ul<<(cpu))
48
41void __cpuinit platform_secondary_init(unsigned int cpu) 49void __cpuinit platform_secondary_init(unsigned int cpu)
42{ 50{
43 /* 51 /*
@@ -47,63 +55,62 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
47 */ 55 */
48 gic_secondary_init(0); 56 gic_secondary_init(0);
49 57
50 /*
51 * Synchronise with the boot thread.
52 */
53 spin_lock(&boot_lock);
54 spin_unlock(&boot_lock);
55} 58}
56 59
57int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) 60static int tegra20_power_up_cpu(unsigned int cpu)
58{ 61{
59 unsigned long old_boot_vector;
60 unsigned long boot_vector;
61 unsigned long timeout;
62 u32 reg; 62 u32 reg;
63 63
64 /* 64 /* Enable the CPU clock. */
65 * set synchronisation state between this boot processor 65 reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
66 * and the secondary one 66 writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
67 */ 67 barrier();
68 spin_lock(&boot_lock);
69
70
71 /* set the reset vector to point to the secondary_startup routine */
72
73 boot_vector = virt_to_phys(tegra_secondary_startup);
74 old_boot_vector = readl(EVP_CPU_RESET_VECTOR);
75 writel(boot_vector, EVP_CPU_RESET_VECTOR);
76
77 /* enable cpu clock on cpu1 */
78 reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); 68 reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
79 writel(reg & ~(1<<9), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
80
81 reg = (1<<13) | (1<<9) | (1<<5) | (1<<1);
82 writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
83 69
84 smp_wmb(); 70 /* Clear flow controller CSR. */
85 flush_cache_all(); 71 flowctrl_write_cpu_csr(cpu, 0);
86 72
87 /* unhalt the cpu */ 73 return 0;
88 writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14); 74}
89 75
90 timeout = jiffies + (1 * HZ); 76int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
91 while (time_before(jiffies, timeout)) { 77{
92 if (readl(EVP_CPU_RESET_VECTOR) != boot_vector) 78 int status;
93 break;
94 udelay(10);
95 }
96 79
97 /* put the old boot vector back */ 80 /* Force the CPU into reset. The CPU must remain in reset when the
98 writel(old_boot_vector, EVP_CPU_RESET_VECTOR); 81 * flow controller state is cleared (which will cause the flow
82 * controller to stop driving reset if the CPU has been power-gated
83 * via the flow controller). This will have no effect on first boot
84 * of the CPU since it should already be in reset.
85 */
86 writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
87 dmb();
99 88
100 /* 89 /*
101 * now the secondary core is starting up let it run its 90 * Unhalt the CPU. If the flow controller was used to power-gate the
102 * calibrations, then wait for it to finish 91 * CPU this will cause the flow controller to stop driving reset.
92 * The CPU will remain in reset because the clock and reset block
93 * is now driving reset.
103 */ 94 */
104 spin_unlock(&boot_lock); 95 flowctrl_write_cpu_halt(cpu, 0);
96
97 switch (tegra_chip_id) {
98 case TEGRA20:
99 status = tegra20_power_up_cpu(cpu);
100 break;
101 default:
102 status = -EINVAL;
103 break;
104 }
105 105
106 return 0; 106 if (status)
107 goto done;
108
109 /* Take the CPU out of reset. */
110 writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
111 wmb();
112done:
113 return status;
107} 114}
108 115
109/* 116/*
@@ -128,6 +135,6 @@ void __init smp_init_cpus(void)
128 135
129void __init platform_smp_prepare_cpus(unsigned int max_cpus) 136void __init platform_smp_prepare_cpus(unsigned int max_cpus)
130{ 137{
131 138 tegra_cpu_reset_handler_init();
132 scu_enable(scu_base); 139 scu_enable(scu_base);
133} 140}
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
new file mode 100644
index 000000000000..4d6a2ee99c3b
--- /dev/null
+++ b/arch/arm/mach-tegra/reset.c
@@ -0,0 +1,84 @@
1/*
2 * arch/arm/mach-tegra/reset.c
3 *
4 * Copyright (C) 2011,2012 NVIDIA Corporation.
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/init.h>
18#include <linux/io.h>
19#include <linux/cpumask.h>
20#include <linux/bitops.h>
21
22#include <asm/cacheflush.h>
23#include <asm/hardware/cache-l2x0.h>
24
25#include <mach/iomap.h>
26#include <mach/irammap.h>
27
28#include "reset.h"
29#include "fuse.h"
30
31#define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
32 TEGRA_IRAM_RESET_HANDLER_OFFSET)
33
34static bool is_enabled;
35
36static void tegra_cpu_reset_handler_enable(void)
37{
38 void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_RESET_BASE);
39 void __iomem *evp_cpu_reset =
40 IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100);
41 void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE);
42 u32 reg;
43
44 BUG_ON(is_enabled);
45 BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE);
46
47 memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start,
48 tegra_cpu_reset_handler_size);
49
50 /*
51 * NOTE: This must be the one and only write to the EVP CPU reset
52 * vector in the entire system.
53 */
54 writel(TEGRA_IRAM_RESET_BASE + tegra_cpu_reset_handler_offset,
55 evp_cpu_reset);
56 wmb();
57 reg = readl(evp_cpu_reset);
58
59 /*
60 * Prevent further modifications to the physical reset vector.
61 * NOTE: Has no effect on chips prior to Tegra30.
62 */
63 if (tegra_chip_id != TEGRA20) {
64 reg = readl(sb_ctrl);
65 reg |= 2;
66 writel(reg, sb_ctrl);
67 wmb();
68 }
69
70 is_enabled = true;
71}
72
73void __init tegra_cpu_reset_handler_init(void)
74{
75
76#ifdef CONFIG_SMP
77 __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
78 *((u32 *)cpu_present_mask);
79 __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
80 virt_to_phys((void *)tegra_secondary_startup);
81#endif
82
83 tegra_cpu_reset_handler_enable();
84}
diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h
new file mode 100644
index 000000000000..de88bf851dd3
--- /dev/null
+++ b/arch/arm/mach-tegra/reset.h
@@ -0,0 +1,50 @@
1/*
2 * arch/arm/mach-tegra/reset.h
3 *
4 * CPU reset dispatcher.
5 *
6 * Copyright (c) 2011, NVIDIA Corporation.
7 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#ifndef __MACH_TEGRA_RESET_H
20#define __MACH_TEGRA_RESET_H
21
22#define TEGRA_RESET_MASK_PRESENT 0
23#define TEGRA_RESET_MASK_LP1 1
24#define TEGRA_RESET_MASK_LP2 2
25#define TEGRA_RESET_STARTUP_SECONDARY 3
26#define TEGRA_RESET_STARTUP_LP2 4
27#define TEGRA_RESET_STARTUP_LP1 5
28#define TEGRA_RESET_DATA_SIZE 6
29
30#ifndef __ASSEMBLY__
31
32extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
33
34void __tegra_cpu_reset_handler_start(void);
35void __tegra_cpu_reset_handler(void);
36void __tegra_cpu_reset_handler_end(void);
37void tegra_secondary_startup(void);
38
39#define tegra_cpu_reset_handler_offset \
40 ((u32)__tegra_cpu_reset_handler - \
41 (u32)__tegra_cpu_reset_handler_start)
42
43#define tegra_cpu_reset_handler_size \
44 (__tegra_cpu_reset_handler_end - \
45 __tegra_cpu_reset_handler_start)
46
47void __init tegra_cpu_reset_handler_init(void);
48
49#endif
50#endif