xref: /OK3568_Linux_fs/u-boot/arch/powerpc/lib/bootm.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * (C) Copyright 2008 Semihalf
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * (C) Copyright 2000-2006
5*4882a593Smuzhiyun  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <common.h>
12*4882a593Smuzhiyun #include <watchdog.h>
13*4882a593Smuzhiyun #include <command.h>
14*4882a593Smuzhiyun #include <image.h>
15*4882a593Smuzhiyun #include <malloc.h>
16*4882a593Smuzhiyun #include <u-boot/zlib.h>
17*4882a593Smuzhiyun #include <bzlib.h>
18*4882a593Smuzhiyun #include <environment.h>
19*4882a593Smuzhiyun #include <asm/byteorder.h>
20*4882a593Smuzhiyun #include <asm/mp.h>
21*4882a593Smuzhiyun #include <bootm.h>
22*4882a593Smuzhiyun #include <vxworks.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #if defined(CONFIG_OF_LIBFDT)
25*4882a593Smuzhiyun #include <linux/libfdt.h>
26*4882a593Smuzhiyun #include <fdt_support.h>
27*4882a593Smuzhiyun #endif
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #ifdef CONFIG_SYS_INIT_RAM_LOCK
30*4882a593Smuzhiyun #include <asm/cache.h>
31*4882a593Smuzhiyun #endif
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun static ulong get_sp (void);
36*4882a593Smuzhiyun extern void ft_fixup_num_cores(void *blob);
37*4882a593Smuzhiyun static void set_clocks_in_mhz (bd_t *kbd);
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #ifndef CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE
40*4882a593Smuzhiyun #define CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE	(768*1024*1024)
41*4882a593Smuzhiyun #endif
42*4882a593Smuzhiyun 
boot_jump_linux(bootm_headers_t * images)43*4882a593Smuzhiyun static void boot_jump_linux(bootm_headers_t *images)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	void	(*kernel)(bd_t *, ulong r4, ulong r5, ulong r6,
46*4882a593Smuzhiyun 			  ulong r7, ulong r8, ulong r9);
47*4882a593Smuzhiyun #ifdef CONFIG_OF_LIBFDT
48*4882a593Smuzhiyun 	char *of_flat_tree = images->ft_addr;
49*4882a593Smuzhiyun #endif
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	kernel = (void (*)(bd_t *, ulong, ulong, ulong,
52*4882a593Smuzhiyun 			   ulong, ulong, ulong))images->ep;
53*4882a593Smuzhiyun 	debug ("## Transferring control to Linux (at address %08lx) ...\n",
54*4882a593Smuzhiyun 		(ulong)kernel);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	bootstage_mark(BOOTSTAGE_ID_RUN_OS);
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun #ifdef CONFIG_BOOTSTAGE_FDT
59*4882a593Smuzhiyun 	bootstage_fdt_add_report();
60*4882a593Smuzhiyun #endif
61*4882a593Smuzhiyun #ifdef CONFIG_BOOTSTAGE_REPORT
62*4882a593Smuzhiyun 	bootstage_report();
63*4882a593Smuzhiyun #endif
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun #if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500)
66*4882a593Smuzhiyun 	unlock_ram_in_cache();
67*4882a593Smuzhiyun #endif
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun #if defined(CONFIG_OF_LIBFDT)
70*4882a593Smuzhiyun 	if (of_flat_tree) {	/* device tree; boot new style */
71*4882a593Smuzhiyun 		/*
72*4882a593Smuzhiyun 		 * Linux Kernel Parameters (passing device tree):
73*4882a593Smuzhiyun 		 *   r3: pointer to the fdt
74*4882a593Smuzhiyun 		 *   r4: 0
75*4882a593Smuzhiyun 		 *   r5: 0
76*4882a593Smuzhiyun 		 *   r6: epapr magic
77*4882a593Smuzhiyun 		 *   r7: size of IMA in bytes
78*4882a593Smuzhiyun 		 *   r8: 0
79*4882a593Smuzhiyun 		 *   r9: 0
80*4882a593Smuzhiyun 		 */
81*4882a593Smuzhiyun 		debug ("   Booting using OF flat tree...\n");
82*4882a593Smuzhiyun 		WATCHDOG_RESET ();
83*4882a593Smuzhiyun 		(*kernel) ((bd_t *)of_flat_tree, 0, 0, EPAPR_MAGIC,
84*4882a593Smuzhiyun 			   env_get_bootm_mapsize(), 0, 0);
85*4882a593Smuzhiyun 		/* does not return */
86*4882a593Smuzhiyun 	} else
87*4882a593Smuzhiyun #endif
88*4882a593Smuzhiyun 	{
89*4882a593Smuzhiyun 		/*
90*4882a593Smuzhiyun 		 * Linux Kernel Parameters (passing board info data):
91*4882a593Smuzhiyun 		 *   r3: ptr to board info data
92*4882a593Smuzhiyun 		 *   r4: initrd_start or 0 if no initrd
93*4882a593Smuzhiyun 		 *   r5: initrd_end - unused if r4 is 0
94*4882a593Smuzhiyun 		 *   r6: Start of command line string
95*4882a593Smuzhiyun 		 *   r7: End   of command line string
96*4882a593Smuzhiyun 		 *   r8: 0
97*4882a593Smuzhiyun 		 *   r9: 0
98*4882a593Smuzhiyun 		 */
99*4882a593Smuzhiyun 		ulong cmd_start = images->cmdline_start;
100*4882a593Smuzhiyun 		ulong cmd_end = images->cmdline_end;
101*4882a593Smuzhiyun 		ulong initrd_start = images->initrd_start;
102*4882a593Smuzhiyun 		ulong initrd_end = images->initrd_end;
103*4882a593Smuzhiyun 		bd_t *kbd = images->kbd;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 		debug ("   Booting using board info...\n");
106*4882a593Smuzhiyun 		WATCHDOG_RESET ();
107*4882a593Smuzhiyun 		(*kernel) (kbd, initrd_start, initrd_end,
108*4882a593Smuzhiyun 			   cmd_start, cmd_end, 0, 0);
109*4882a593Smuzhiyun 		/* does not return */
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 	return ;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
arch_lmb_reserve(struct lmb * lmb)114*4882a593Smuzhiyun void arch_lmb_reserve(struct lmb *lmb)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	phys_size_t bootm_size;
117*4882a593Smuzhiyun 	ulong size, sp, bootmap_base;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	bootmap_base = env_get_bootm_low();
120*4882a593Smuzhiyun 	bootm_size = env_get_bootm_size();
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun #ifdef DEBUG
123*4882a593Smuzhiyun 	if (((u64)bootmap_base + bootm_size) >
124*4882a593Smuzhiyun 	    (CONFIG_SYS_SDRAM_BASE + (u64)gd->ram_size))
125*4882a593Smuzhiyun 		puts("WARNING: bootm_low + bootm_size exceed total memory\n");
126*4882a593Smuzhiyun 	if ((bootmap_base + bootm_size) > get_effective_memsize())
127*4882a593Smuzhiyun 		puts("WARNING: bootm_low + bootm_size exceed eff. memory\n");
128*4882a593Smuzhiyun #endif
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	size = min(bootm_size, get_effective_memsize());
131*4882a593Smuzhiyun 	size = min(size, (ulong)CONFIG_SYS_LINUX_LOWMEM_MAX_SIZE);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	if (size < bootm_size) {
134*4882a593Smuzhiyun 		ulong base = bootmap_base + size;
135*4882a593Smuzhiyun 		printf("WARNING: adjusting available memory to %lx\n", size);
136*4882a593Smuzhiyun 		lmb_reserve(lmb, base, bootm_size - size);
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	/*
140*4882a593Smuzhiyun 	 * Booting a (Linux) kernel image
141*4882a593Smuzhiyun 	 *
142*4882a593Smuzhiyun 	 * Allocate space for command line and board info - the
143*4882a593Smuzhiyun 	 * address should be as high as possible within the reach of
144*4882a593Smuzhiyun 	 * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
145*4882a593Smuzhiyun 	 * memory, which means far enough below the current stack
146*4882a593Smuzhiyun 	 * pointer.
147*4882a593Smuzhiyun 	 */
148*4882a593Smuzhiyun 	sp = get_sp();
149*4882a593Smuzhiyun 	debug ("## Current stack ends at 0x%08lx\n", sp);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	/* adjust sp by 4K to be safe */
152*4882a593Smuzhiyun 	sp -= 4096;
153*4882a593Smuzhiyun 	lmb_reserve(lmb, sp, (CONFIG_SYS_SDRAM_BASE + get_effective_memsize() - sp));
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun #ifdef CONFIG_MP
156*4882a593Smuzhiyun 	cpu_mp_lmb_reserve(lmb);
157*4882a593Smuzhiyun #endif
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	return ;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
boot_prep_linux(bootm_headers_t * images)162*4882a593Smuzhiyun static void boot_prep_linux(bootm_headers_t *images)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun #ifdef CONFIG_MP
165*4882a593Smuzhiyun 	/*
166*4882a593Smuzhiyun 	 * if we are MP make sure to flush the device tree so any changes are
167*4882a593Smuzhiyun 	 * made visibile to all other cores.  In AMP boot scenarios the cores
168*4882a593Smuzhiyun 	 * might not be HW cache coherent with each other.
169*4882a593Smuzhiyun 	 */
170*4882a593Smuzhiyun 	flush_cache((unsigned long)images->ft_addr, images->ft_len);
171*4882a593Smuzhiyun #endif
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
boot_cmdline_linux(bootm_headers_t * images)174*4882a593Smuzhiyun static int boot_cmdline_linux(bootm_headers_t *images)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun 	ulong of_size = images->ft_len;
177*4882a593Smuzhiyun 	struct lmb *lmb = &images->lmb;
178*4882a593Smuzhiyun 	ulong *cmd_start = &images->cmdline_start;
179*4882a593Smuzhiyun 	ulong *cmd_end = &images->cmdline_end;
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	int ret = 0;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 	if (!of_size) {
184*4882a593Smuzhiyun 		/* allocate space and init command line */
185*4882a593Smuzhiyun 		ret = boot_get_cmdline (lmb, cmd_start, cmd_end);
186*4882a593Smuzhiyun 		if (ret) {
187*4882a593Smuzhiyun 			puts("ERROR with allocation of cmdline\n");
188*4882a593Smuzhiyun 			return ret;
189*4882a593Smuzhiyun 		}
190*4882a593Smuzhiyun 	}
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	return ret;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun 
boot_bd_t_linux(bootm_headers_t * images)195*4882a593Smuzhiyun static int boot_bd_t_linux(bootm_headers_t *images)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun 	ulong of_size = images->ft_len;
198*4882a593Smuzhiyun 	struct lmb *lmb = &images->lmb;
199*4882a593Smuzhiyun 	bd_t **kbd = &images->kbd;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	int ret = 0;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (!of_size) {
204*4882a593Smuzhiyun 		/* allocate space for kernel copy of board info */
205*4882a593Smuzhiyun 		ret = boot_get_kbd (lmb, kbd);
206*4882a593Smuzhiyun 		if (ret) {
207*4882a593Smuzhiyun 			puts("ERROR with allocation of kernel bd\n");
208*4882a593Smuzhiyun 			return ret;
209*4882a593Smuzhiyun 		}
210*4882a593Smuzhiyun 		set_clocks_in_mhz(*kbd);
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	return ret;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
boot_body_linux(bootm_headers_t * images)216*4882a593Smuzhiyun static int boot_body_linux(bootm_headers_t *images)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun 	int ret;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	/* allocate space for kernel copy of board info */
221*4882a593Smuzhiyun 	ret = boot_bd_t_linux(images);
222*4882a593Smuzhiyun 	if (ret)
223*4882a593Smuzhiyun 		return ret;
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	ret = image_setup_linux(images);
226*4882a593Smuzhiyun 	if (ret)
227*4882a593Smuzhiyun 		return ret;
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	return 0;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun noinline
do_bootm_linux(int flag,int argc,char * const argv[],bootm_headers_t * images)233*4882a593Smuzhiyun int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	int	ret;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	if (flag & BOOTM_STATE_OS_CMDLINE) {
238*4882a593Smuzhiyun 		boot_cmdline_linux(images);
239*4882a593Smuzhiyun 		return 0;
240*4882a593Smuzhiyun 	}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	if (flag & BOOTM_STATE_OS_BD_T) {
243*4882a593Smuzhiyun 		boot_bd_t_linux(images);
244*4882a593Smuzhiyun 		return 0;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	if (flag & BOOTM_STATE_OS_PREP) {
248*4882a593Smuzhiyun 		boot_prep_linux(images);
249*4882a593Smuzhiyun 		return 0;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun 	boot_prep_linux(images);
253*4882a593Smuzhiyun 	ret = boot_body_linux(images);
254*4882a593Smuzhiyun 	if (ret)
255*4882a593Smuzhiyun 		return ret;
256*4882a593Smuzhiyun 	boot_jump_linux(images);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 	return 0;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
get_sp(void)261*4882a593Smuzhiyun static ulong get_sp (void)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	ulong sp;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	asm( "mr %0,1": "=r"(sp) : );
266*4882a593Smuzhiyun 	return sp;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun 
set_clocks_in_mhz(bd_t * kbd)269*4882a593Smuzhiyun static void set_clocks_in_mhz (bd_t *kbd)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	char	*s;
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun 	s = env_get("clocks_in_mhz");
274*4882a593Smuzhiyun 	if (s) {
275*4882a593Smuzhiyun 		/* convert all clock information to MHz */
276*4882a593Smuzhiyun 		kbd->bi_intfreq /= 1000000L;
277*4882a593Smuzhiyun 		kbd->bi_busfreq /= 1000000L;
278*4882a593Smuzhiyun #if defined(CONFIG_CPM2)
279*4882a593Smuzhiyun 		kbd->bi_cpmfreq /= 1000000L;
280*4882a593Smuzhiyun 		kbd->bi_brgfreq /= 1000000L;
281*4882a593Smuzhiyun 		kbd->bi_sccfreq /= 1000000L;
282*4882a593Smuzhiyun 		kbd->bi_vco	/= 1000000L;
283*4882a593Smuzhiyun #endif
284*4882a593Smuzhiyun 	}
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun #if defined(CONFIG_BOOTM_VXWORKS)
boot_prep_vxworks(bootm_headers_t * images)288*4882a593Smuzhiyun void boot_prep_vxworks(bootm_headers_t *images)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun #if defined(CONFIG_OF_LIBFDT)
291*4882a593Smuzhiyun 	int off;
292*4882a593Smuzhiyun 	u64 base, size;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	if (!images->ft_addr)
295*4882a593Smuzhiyun 		return;
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	base = (u64)gd->bd->bi_memstart;
298*4882a593Smuzhiyun 	size = (u64)gd->bd->bi_memsize;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	off = fdt_path_offset(images->ft_addr, "/memory");
301*4882a593Smuzhiyun 	if (off < 0)
302*4882a593Smuzhiyun 		fdt_fixup_memory(images->ft_addr, base, size);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun #if defined(CONFIG_MP)
305*4882a593Smuzhiyun #if defined(CONFIG_MPC85xx)
306*4882a593Smuzhiyun 	ft_fixup_cpu(images->ft_addr, base + size);
307*4882a593Smuzhiyun 	ft_fixup_num_cores(images->ft_addr);
308*4882a593Smuzhiyun #elif defined(CONFIG_MPC86xx)
309*4882a593Smuzhiyun 	off = fdt_add_mem_rsv(images->ft_addr,
310*4882a593Smuzhiyun 			determine_mp_bootpg(NULL), (u64)4096);
311*4882a593Smuzhiyun 	if (off < 0)
312*4882a593Smuzhiyun 		printf("## WARNING %s: %s\n", __func__, fdt_strerror(off));
313*4882a593Smuzhiyun 	ft_fixup_num_cores(images->ft_addr);
314*4882a593Smuzhiyun #endif
315*4882a593Smuzhiyun 	flush_cache((unsigned long)images->ft_addr, images->ft_len);
316*4882a593Smuzhiyun #endif
317*4882a593Smuzhiyun #endif
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
boot_jump_vxworks(bootm_headers_t * images)320*4882a593Smuzhiyun void boot_jump_vxworks(bootm_headers_t *images)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun 	/* PowerPC VxWorks boot interface conforms to the ePAPR standard
323*4882a593Smuzhiyun 	 * general purpuse registers:
324*4882a593Smuzhiyun 	 *
325*4882a593Smuzhiyun 	 *	r3: Effective address of the device tree image
326*4882a593Smuzhiyun 	 *	r4: 0
327*4882a593Smuzhiyun 	 *	r5: 0
328*4882a593Smuzhiyun 	 *	r6: ePAPR magic value
329*4882a593Smuzhiyun 	 *	r7: shall be the size of the boot IMA in bytes
330*4882a593Smuzhiyun 	 *	r8: 0
331*4882a593Smuzhiyun 	 *	r9: 0
332*4882a593Smuzhiyun 	 *	TCR: WRC = 0, no watchdog timer reset will occur
333*4882a593Smuzhiyun 	 */
334*4882a593Smuzhiyun 	WATCHDOG_RESET();
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	((void (*)(void *, ulong, ulong, ulong,
337*4882a593Smuzhiyun 		ulong, ulong, ulong))images->ep)(images->ft_addr,
338*4882a593Smuzhiyun 		0, 0, EPAPR_MAGIC, env_get_bootm_mapsize(), 0, 0);
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun #endif
341