summaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/head_32.S
blob: ee954d1b8cc68952a3e45a9a43c042b12e325abd (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
/* $Id: head.S,v 1.7 2003/09/01 17:58:19 lethal Exp $
 *
 *  arch/sh/kernel/head.S
 *
 *  Copyright (C) 1999, 2000  Niibe Yutaka & Kaz Kojima
 *  Copyright (C) 2010  Matt Fleming
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Head.S contains the SH exception handlers and startup code.
 */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/thread_info.h>
#include <asm/mmu.h>
#include <cpu/mmu_context.h>

#ifdef CONFIG_CPU_SH4A
#define SYNCO()		synco

#define PREFI(label, reg)	\
	mov.l	label, reg;	\
	prefi	@reg
#else
#define SYNCO()
#define PREFI(label, reg)
#endif

	.section	.empty_zero_page, "aw"
ENTRY(empty_zero_page)
	.long	1		/* MOUNT_ROOT_RDONLY */
	.long	0		/* RAMDISK_FLAGS */
	.long	0x0200		/* ORIG_ROOT_DEV */
	.long	1		/* LOADER_TYPE */
	.long	0x00000000	/* INITRD_START */
	.long	0x00000000	/* INITRD_SIZE */
#ifdef CONFIG_32BIT
	.long	0x53453f00 + 32	/* "SE?" = 32 bit */
#else
	.long	0x53453f00 + 29	/* "SE?" = 29 bit */
#endif
1:
	.skip	PAGE_SIZE - empty_zero_page - 1b

	__HEAD

/*
 * Condition at the entry of _stext:
 *
 *   BSC has already been initialized.
 *   INTC may or may not be initialized.
 *   VBR may or may not be initialized.
 *   MMU may or may not be initialized.
 *   Cache may or may not be initialized.
 *   Hardware (including on-chip modules) may or may not be initialized. 
 *
 */
ENTRY(_stext)
	!			Initialize Status Register
	mov.l	1f, r0		! MD=1, RB=0, BL=0, IMASK=0xF
	ldc	r0, sr
	!			Initialize global interrupt mask
#ifdef CONFIG_CPU_HAS_SR_RB
	mov	#0, r0
	ldc	r0, r6_bank
#endif
	
	/*
	 * Prefetch if possible to reduce cache miss penalty.
	 *
	 * We do this early on for SH-4A as a micro-optimization,
	 * as later on we will have speculative execution enabled
	 * and this will become less of an issue.
	 */
	PREFI(5f, r0)
	PREFI(6f, r0)

	!
	mov.l	2f, r0
	mov	r0, r15		! Set initial r15 (stack pointer)
#ifdef CONFIG_CPU_HAS_SR_RB
	mov.l	7f, r0
	ldc	r0, r7_bank	! ... and initial thread_info
#endif

#if defined(CONFIG_PMB) && !defined(CONFIG_PMB_LEGACY)

#define __PMB_ITER_BY_SIZE(size)			\
.L##size:						\
	mov	#(size >> 4), r6;			\
	shll16	r6;					\
	shll8	r6;					\
							\
	cmp/hi	r5, r6;					\
	bt	9999f;					\
							\
	mov	#(PMB_SZ_##size##M >> 2), r9;		\
	shll2	r9;					\
							\
	/*						\
	 * Cached mapping				\
	 */						\
	mov	#PMB_C, r8;				\
	or	r0, r8;					\
	or	r9, r8;					\
	mov.l	r8, @r1;				\
	mov.l	r2, @r3;				\
							\
	/* Increment to the next PMB_DATA entry */	\
	add	r4, r1;					\
	/* Increment to the next PMB_ADDR entry */	\
	add	r4, r3;					\
	/* Increment number of PMB entries */		\
	add	#1, r10;				\
							\
	/*						\
	 * Uncached mapping				\
	 */						\
	mov	#(PMB_UB >> 8), r8;			\
	shll8	r8;					\
							\
	or	r0, r8;					\
	or	r9, r8;					\
	mov.l	r8, @r1;				\
	mov	r2, r8;					\
	add	r7, r8;					\
	mov.l	r8, @r3;				\
							\
	/* Increment to the next PMB_DATA entry */	\
	add	r4, r1;					\
	/* Increment to the next PMB_ADDR entry */	\
	add	r4, r3;					\
	/* Increment number of PMB entries */		\
	add	#1, r10;				\
							\
	sub	r6, r5;					\
	add	r6, r0;					\
	add	r6, r2;					\
							\
	bra	.L##size;				\
9999:

	/*
	 * Reconfigure the initial PMB mappings setup by the hardware.
	 *
	 * When we boot in 32-bit MMU mode there are 2 PMB entries already
	 * setup for us.
	 *
	 * Entry       VPN	   PPN	    V	SZ	C	UB	WT
	 * ---------------------------------------------------------------
	 *   0	    0x80000000 0x00000000   1  512MB	1	0	1
	 *   1	    0xA0000000 0x00000000   1  512MB	0	0	0
	 *
	 * But we reprogram them here because we want complete control over
	 * our address space and the initial mappings may not map PAGE_OFFSET
	 * to __MEMORY_START (or even map all of our RAM).
	 *
	 * Once we've setup cached and uncached mappings for all of RAM we
	 * clear the rest of the PMB entries.
	 *
	 * This clearing also deals with the fact that PMB entries can persist
	 * across reboots. The PMB could have been left in any state when the
	 * reboot occurred, so to be safe we clear all entries and start with
	 * with a clean slate.
	 */

	mov.l	.LMMUCR, r1	/* Flush the TLB */
	mov.l	@r1, r0
	or	#MMUCR_TI, r0
	mov.l	r0, @r1

	mov.l	.LMEMORY_SIZE, r5
	mov	r5, r7

	mov	#PMB_E_SHIFT, r0
	mov	#0x1, r4
	shld	r0, r4

	mov.l	.LFIRST_DATA_ENTRY, r0
	mov.l	.LPMB_DATA, r1
	mov.l	.LFIRST_ADDR_ENTRY, r2
	mov.l	.LPMB_ADDR, r3

	mov	#0, r10

	/*
	 * r0 = PMB_DATA data field
	 * r1 = PMB_DATA address field
	 * r2 = PMB_ADDR data field
	 * r3 = PMB_ADDR address field
	 * r4 = PMB_E_SHIFT
	 * r5 = remaining amount of RAM to map
	 * r6 = PMB mapping size we're trying to use
	 * r7 = cached_to_uncached
	 * r8 = scratch register
	 * r9 = scratch register
	 * r10 = number of PMB entries we've setup
	 */
	__PMB_ITER_BY_SIZE(512)
	__PMB_ITER_BY_SIZE(128)
	__PMB_ITER_BY_SIZE(64)
	__PMB_ITER_BY_SIZE(16)

	/* Update cached_to_uncached */
	mov.l	.Lcached_to_uncached, r0
	mov.l	r7, @r0

	/*
	 * Clear the remaining PMB entries.
	 *
	 * r3 = entry to begin clearing from
	 * r10 = number of entries we've setup so far
	 */
	mov	#0, r1
	mov	#PMB_ENTRY_MAX, r0

.Lagain:
	mov.l	r1, @r3		/* Clear PMB_ADDR entry */
	add	#1, r10		/* Increment the loop counter */
	cmp/eq	r0, r10
	bf/s	.Lagain
	 add	r4, r3		/* Increment to the next PMB_ADDR entry */

	mov.l	6f, r0
	icbi	@r0

#endif /* !CONFIG_PMB_LEGACY */

#ifndef CONFIG_SH_NO_BSS_INIT
	/*
	 * Don't clear BSS if running on slow platforms such as an RTL simulation,
	 * remote memory via SHdebug link, etc.  For these the memory can be guaranteed
	 * to be all zero on boot anyway.
	 */
				! Clear BSS area
#ifdef CONFIG_SMP	
	mov.l	3f, r0
	cmp/eq	#0, r0		! skip clear if set to zero
	bt	10f
#endif
	
	mov.l	3f, r1
	add	#4, r1
	mov.l	4f, r2
	mov	#0, r0
9:	cmp/hs	r2, r1
	bf/s	9b		! while (r1 < r2)
	 mov.l	r0,@-r2

10:		
#endif

	!			Additional CPU initialization
	mov.l	6f, r0
	jsr	@r0
	 nop

	SYNCO()			! Wait for pending instructions..
	
	!			Start kernel
	mov.l	5f, r0
	jmp	@r0
	 nop

	.balign 4
#if defined(CONFIG_CPU_SH2)
1:	.long	0x000000F0		! IMASK=0xF
#else
1:	.long	0x400080F0		! MD=1, RB=0, BL=0, FD=1, IMASK=0xF
#endif
ENTRY(stack_start)
2:	.long	init_thread_union+THREAD_SIZE
3:	.long	__bss_start
4:	.long	_end
5:	.long	start_kernel
6:	.long	sh_cpu_init
7:	.long	init_thread_union

#if defined(CONFIG_PMB) && !defined(CONFIG_PMB_LEGACY)
.LPMB_ADDR:		.long	PMB_ADDR
.LPMB_DATA:		.long	PMB_DATA
.LFIRST_ADDR_ENTRY:	.long	PAGE_OFFSET | PMB_V
.LFIRST_DATA_ENTRY:	.long	__MEMORY_START | PMB_V
.LMMUCR:		.long	MMUCR
.Lcached_to_uncached:	.long	cached_to_uncached
.LMEMORY_SIZE:		.long	__MEMORY_SIZE
#endif