summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/headsmp.S
blob: 0f1e52d89edce76c6e7f693eea60cde34956e908 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
 * arch/arm/mach-tegra/headsmp.S
 *
 * SMP initialization routines for Tegra SoCs
 *
 * Copyright (c) 2009-2011, NVIDIA Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>
#include <asm/cache.h>

#include <mach/iomap.h>
#include <mach/io.h>

#include "power.h"
#include "power-macros.S"
#include "reset.h"

#define DEBUG_CPU_RESET_HANDLER	0	/* Non-zero enables debug code */

#define RST_DEVICES_U		0xc
#define PMC_SCRATCH41		0x140
#define TWD_BASE		(TEGRA_ARM_PERIF_BASE + 0x600)

#define RESET_DATA(x)		((TEGRA_RESET_##x)*4)


/*        .section ".cpuinit.text", "ax"*/

.macro	poke_ev, val, tmp
	mov32	\tmp, (TEGRA_EXCEPTION_VECTORS_BASE + 0x114)
	str	\val, [\tmp]
.endm

/*
 * __tegra_cpu_reset_handler_halt_failed:
 *
 * Alternate entry point for reset handler for cases where the
 * WFI halt failed to take effect.
 *
 */
	.align PAGE_SHIFT
ENTRY(__tegra_cpu_reset_handler_start)
ENTRY(__tegra_cpu_reset_handler_halt_failed)

#if DEBUG_CPU_RESET_HANDLER
	b	.				@ Debug spin
#endif
#ifdef CONFIG_HAVE_ARM_TWD
	cpsid	aif, 0x13			@ SVC mode, interrupts disabled
	mrc	p15, 0, r10, c0, c0, 5		@ MPIDR
	and	r10, r10, #0x3			@ R10 = CPU number
	mov32	r1, 0xBAD00000
	orr	r1, r1, r10
	mov	r0, #0
	mov32	r2, TWD_BASE			@ Local timer base
	str	r0, [r2, #8]			@ Disable timer in case it's running
	str	r1, [r2, #0]			@ Save halt fail status
	b	__tegra_cpu_reset_handler
#endif
ENDPROC(__tegra_cpu_reset_handler_halt_failed)

/*
 * __tegra_cpu_reset_handler:
 *
 * Common handler for all CPU reset events.
 *
 * Register usage within the reset handler:
 *
 *	R5  = CPU present (to the OS) mask
 *	R6  = CPU initialized mask
 *	R7  = CPU online mask
 *	R8  = CPU in LP1 state mask
 *	R9  = CPU in LP2 state mask
 *	R10 = CPU number
 *	R11 = CPU mask
 *	R12 = pointer to reset handler data
 */

	.align L1_CACHE_SHIFT
	b	__tegra_cpu_reset_handler_halt_failed
ENTRY(__tegra_cpu_reset_handler)

#if DEBUG_CPU_RESET_HANDLER
	b	.
#endif
	cpsid	aif, 0x13			@ SVC mode, interrupts disabled
	mrc	p15, 0, r0, c0, c0, 0		@ read main ID register
	and	r5, r0, #0x00f00000		@ variant
	and	r6, r0, #0x0000000f		@ revision
	orr	r6, r6, r5, lsr #20-4		@ combine variant and revision
#ifndef CONFIG_TRUSTED_FOUNDATIONS
#ifdef CONFIG_ARM_ERRATA_743622
	teq	r6, #0x20			@ present in r2p0
	teqne	r6, #0x21			@ present in r2p1
	teqne	r6, #0x22			@ present in r2p2
	teqne	r6, #0x27			@ present in r2p7
	teqne	r6, #0x29			@ present in r2p9
	mrceq	p15, 0, r10, c15, c0, 1		@ read diagnostic register
	orreq	r10, r10, #1 << 6		@ set bit #6
	mcreq	p15, 0, r10, c15, c0, 1		@ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
	cmp	r6, #0x30			@ present prior to r3p0
	mrclt	p15, 0, r10, c15, c0, 1		@ read diagnostic register
	orrlt	r10, r10, #1 << 11		@ set bit #11
	mcrlt	p15, 0, r10, c15, c0, 1		@ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_752520
	cmp	r6, #0x29			@ present prior to r2p9
	mrclt	p15, 0, r10, c15, c0, 1		@ read diagnostic register
	orrlt	r10, r10, #1 << 20		@ set bit #20
	mcrlt	p15, 0, r10, c15, c0, 1		@ write diagnostic register
#endif
#endif
	mrc	p15, 0, r10, c0, c0, 5		@ MPIDR
	and	r10, r10, #0x3			@ R10 = CPU number
	mov	r11, #1
	mov	r11, r11, lsl r10  		@ R11 = CPU mask
	add	r12, pc, #__tegra_cpu_reset_handler_data-(.+8)

#ifdef CONFIG_PM
	ldr	r8, [r12, #RESET_DATA(MASK_LP1)]
#else
	mov	r8, #0
#endif

#ifdef CONFIG_PM
	/* Waking up from LP1? */
	tst	r8, r11				@ if in_lp1
	beq	__is_not_lp1
	cmp	r10, #0
	bne	__die				@ only CPU0 can be here
	ldr	lr, [r12, #RESET_DATA(STARTUP_LP1)]
	cmp	lr, #0
	bleq	__die				@ no LP1 startup handler
	bx	lr
__is_not_lp1:
#endif

#ifdef CONFIG_SMP
	ldr	r5, [r12, #RESET_DATA(MASK_PRESENT_PTR)]
	cmp	r5, #0
	bleq	__die				@ present mask not initialized
	ldr	r5, [r5]

	ldr	r6, [r12, #RESET_DATA(MASK_INIT)]

	ldr	r7, [r12, #RESET_DATA(MASK_ONLINE_PTR)]
	cmp	r7, #0
	bleq	__die				@ online mask not initialized
	ldr	r7, [r7]
#else
	mov	r5, #0
	mov	r6, #0
	mov	r7, #0
#endif

#ifdef CONFIG_SMP
	/* Does the OS know about this CPU? */
	tst	r5, r11 			@ if !present
	bleq	__die				@ CPU not present (to OS)

	/* Has this CPU ever been booted (and not CPU0)? */
	tst	r6, r11				@ if initialized
	bne	__is_initialized
	cmp	r10, #0
	bleq	__die				@ CPU0 cannot be here
	ldr	lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
	cmp	lr, #0
	bleq	__die				@ no secondary startup handler
	bx	lr
__is_initialized:

#ifdef CONFIG_HOTPLUG
	/* Is this CPU currently online to to the OS? */
	tst 	r7, r11				@ if online
	bne	__is_online
	cmp	r10, #0
	bleq	__die				@ CPU0 cannot be here
	ldr	lr, [r12, #RESET_DATA(STARTUP_HOTPLUG)]
	cmp	lr, #0
	bleq	__die				@ no hotplug startup handler
	bx	lr
__is_online:
#endif
#endif
	ldr	r9, [r12, #RESET_DATA(MASK_LP2)]

	/* Waking up from LP2? */
	tst	r9, r11				@ if in_lp2
	beq	__is_not_lp2
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
	cmp	r10, #1
	bne	__is_not_cpu1
	/* Tegra2 CPU1 LP2 wakeup uses the hotplug startup handler */
	ldr	lr, [r12, #RESET_DATA(STARTUP_HOTPLUG)]
	cmp	lr, #0
	bleq	__die				@ no hotplug startup handler
	bx	lr
__is_not_cpu1:
#endif
	ldr	lr, [r12, #RESET_DATA(STARTUP_LP2)]
	cmp	lr, #0
	bleq	__die				@ no LP2 startup handler
	bx	lr

__is_not_lp2:

	blne	__die				@ bad news
/*
 * We don't know why the CPU reset. Just kill it.
 * The LR register will contain the address we died at + 4.
 */

__die:
	sub	lr, lr, #4
	mov32	r5, TEGRA_PMC_BASE
	str	lr, [r5, #PMC_SCRATCH41]

	mov32	r5, TEGRA_CLK_RESET_BASE

#ifdef CONFIG_ARCH_TEGRA_2x_SOC
	mov32	r0, 0x1111
	mov	r1, r0, lsl r10
	str	r1, [r5, #0x340]		@ CLK_RST_CPU_CMPLX_SET
#else
	mov32	r6, TEGRA_FLOW_CTRL_BASE

	cmp	r10, #0
	moveq	r1, #FLOW_CTRL_HALT_CPU_EVENTS
	moveq	r2, #FLOW_CTLR_CPU_CSR
	movne	r1, r10, lsl #3
	addne	r2, r1, #(FLOW_CTLR_CPU1_CSR-8)
	addne	r1, r1, #(FLOW_CTLR_HALT_CPU1_EVENTS-8)

	/* Clear CPU "event" and "interrupt" flags and power gate
	   it when halting but not before it is in the "WFI" state. */
	ldr	r0, [r6, +r2]
	orr	r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
	orr	r0, r0, #FLOW_CTRL_CSR_ENABLE
	str	r0, [r6, +r2]

	/* Unconditionally halt this CPU */
	mov	r0, #FLOW_CTRL_WAITEVENT
	str	r0, [r6, +r1]
	ldr	r0, [r6, +r1]			@ memory barrier

	dsb
	isb
	wfi					@ CPU should be power gated here

	/* If the CPU didn't power gate above just kill it's clock. */

	mov	r0, r11, lsl #8
	str	r0, [r5, #348]			@ CLK_CPU_CMPLX_SET
#endif
	/* If the CPU still isn't dead, just spin here. */
	b	.

ENDPROC(__tegra_cpu_reset_handler)
	.align L1_CACHE_SHIFT
	.type	__tegra_cpu_reset_handler_data, %object
	.globl	__tegra_cpu_reset_handler_data
__tegra_cpu_reset_handler_data:
	.rept	TEGRA_RESET_DATA_SIZE
	.long	0
	.endr
	.size	__tegra_cpu_reset_handler_data, .-tegra_cpu_reset_handler_data
	.align L1_CACHE_SHIFT
ENTRY(__tegra_cpu_reset_handler_end)


#ifdef CONFIG_SMP
/*
 *	tegra_secondary_startup
 *
 *	 Initial secondary processor boot vector; jumps to kernel's
 *	 secondary_startup routine
 */
	.align L1_CACHE_SHIFT
ENTRY(tegra_secondary_startup)
	msr	cpsr_fsxc, #0xd3
	bl	__invalidate_cpu_state
	cpu_id	r0
	enable_coresite r1
	poke_ev	r0, r1
	b	secondary_startup
ENDPROC(tegra_secondary_startup)
#endif

/*
 *	__enable_coresite_access
 *
 *	  Called to take the CoreSight debug interface out of reset.
 *	  Called with MMU disabled.
 */
	.align L1_CACHE_SHIFT
ENTRY(__enable_coresite_access)
	/* Only assert CoreSight reset when CPU0 comes up. */
	cpu_id	r0
	cmp	r0, #0
	bne	no_coresight_reset

	mov32	r0, (TEGRA_CLK_RESET_BASE + RST_DEVICES_U)
	mov32	r2, (TEGRA_TMRUS_BASE)

	/* assert reset for 2usec */
	ldr	r1, [r0]
#ifdef CONFIG_TEGRA_SILICON_PLATFORM
	orr	r1, #(1<<9)
	str	r1, [r0]
#endif
	wait_for_us r3, r2, r4
	add	r3, r3, #2
	bic	r1, r1, #(1<<9)
	wait_until r3, r2, r4
	str	r1, [r0]

no_coresight_reset:
	/* Enable CoreSight */
	enable_coresite r3
	bx	lr
ENDPROC(__enable_coresite_access)