1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2008 - 2013 Tensilica Inc.
4 * (C) Copyright 2014 Cadence Design Systems Inc.
5 */
6
7#include <common.h>
8#include <bootm.h>
9#include <bootstage.h>
10#include <command.h>
11#include <cpu_func.h>
12#include <env.h>
13#include <asm/global_data.h>
14#include <u-boot/zlib.h>
15#include <asm/byteorder.h>
16#include <asm/addrspace.h>
17#include <asm/bootparam.h>
18#include <asm/cache.h>
19#include <image.h>
20
21DECLARE_GLOBAL_DATA_PTR;
22
23/*
24 * Setup boot-parameters.
25 */
26
27static struct bp_tag *setup_first_tag(struct bp_tag *params)
28{
29	params->id = BP_TAG_FIRST;
30	params->size = sizeof(long);
31	*(unsigned long *)&params->data = BP_VERSION;
32
33	return bp_tag_next(params);
34}
35
36static struct bp_tag *setup_last_tag(struct bp_tag *params)
37{
38	params->id = BP_TAG_LAST;
39	params->size = 0;
40
41	return bp_tag_next(params);
42}
43
44static struct bp_tag *setup_memory_tag(struct bp_tag *params)
45{
46	struct meminfo *mem;
47
48	params->id = BP_TAG_MEMORY;
49	params->size = sizeof(struct meminfo);
50	mem = (struct meminfo *)params->data;
51	mem->type = MEMORY_TYPE_CONVENTIONAL;
52	mem->start = PHYSADDR(gd->ram_base);
53	mem->end = PHYSADDR(gd->ram_base + gd->ram_size);
54
55	printf("   MEMORY:          tag:0x%04x, type:0X%lx, start:0X%lx, end:0X%lx\n",
56	       BP_TAG_MEMORY, mem->type, mem->start, mem->end);
57
58	return bp_tag_next(params);
59}
60
61static struct bp_tag *setup_commandline_tag(struct bp_tag *params,
62					    char *cmdline)
63{
64	int len;
65
66	if (!cmdline)
67		return params;
68
69	len = strlen(cmdline);
70
71	params->id = BP_TAG_COMMAND_LINE;
72	params->size = (len + 3) & -4;
73	strcpy((char *)params->data, cmdline);
74
75	printf("   COMMAND_LINE:    tag:0x%04x, size:%u, data:'%s'\n",
76	       BP_TAG_COMMAND_LINE, params->size, cmdline);
77
78	return bp_tag_next(params);
79}
80
81static struct bp_tag *setup_ramdisk_tag(struct bp_tag *params,
82					unsigned long rd_start,
83					unsigned long rd_end)
84{
85	struct meminfo *mem;
86
87	if (rd_start == rd_end)
88		return params;
89
90	/* Add a single banked memory */
91
92	params->id = BP_TAG_INITRD;
93	params->size = sizeof(struct meminfo);
94
95	mem = (struct meminfo *)params->data;
96	mem->type =  MEMORY_TYPE_CONVENTIONAL;
97	mem->start = PHYSADDR(rd_start);
98	mem->end = PHYSADDR(rd_end);
99
100	printf("   INITRD:          tag:0x%x, type:0X%04lx, start:0X%lx, end:0X%lx\n",
101	       BP_TAG_INITRD, mem->type, mem->start, mem->end);
102
103	return bp_tag_next(params);
104}
105
106static struct bp_tag *setup_serial_tag(struct bp_tag *params)
107{
108	params->id = BP_TAG_SERIAL_BAUDRATE;
109	params->size = sizeof(unsigned long);
110	params->data[0] = gd->baudrate;
111
112	printf("   SERIAL_BAUDRATE: tag:0x%04x, size:%u, baudrate:%lu\n",
113	       BP_TAG_SERIAL_BAUDRATE, params->size, params->data[0]);
114
115	return bp_tag_next(params);
116}
117
118#ifdef CONFIG_OF_LIBFDT
119
120static struct bp_tag *setup_fdt_tag(struct bp_tag *params, void *fdt_start)
121{
122	params->id = BP_TAG_FDT;
123	params->size = sizeof(unsigned long);
124	params->data[0] = (unsigned long)fdt_start;
125
126	printf("   FDT:             tag:0x%04x, size:%u, start:0x%lx\n",
127	       BP_TAG_FDT, params->size, params->data[0]);
128
129	return bp_tag_next(params);
130}
131
132#endif
133
134/*
135 * Boot Linux.
136 */
137
138int do_bootm_linux(int flag, struct bootm_info *bmi)
139{
140	struct bootm_headers *images = bmi->images;
141	struct bp_tag *params, *params_start;
142	ulong initrd_start, initrd_end;
143	char *commandline = env_get("bootargs");
144
145	if (!(flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)))
146		return 0;
147
148	show_boot_progress(15);
149
150	if (images->rd_start) {
151		initrd_start = images->rd_start;
152		initrd_end = images->rd_end;
153	} else {
154		initrd_start = 0;
155		initrd_end = 0;
156	}
157
158	params_start = (struct bp_tag *)gd->bd->bi_boot_params;
159	params = params_start;
160	params = setup_first_tag(params);
161	params = setup_memory_tag(params);
162	params = setup_commandline_tag(params, commandline);
163	params = setup_serial_tag(params);
164
165	if (initrd_start)
166		params = setup_ramdisk_tag(params, initrd_start, initrd_end);
167
168#ifdef CONFIG_OF_LIBFDT
169	if (images->ft_addr)
170		params = setup_fdt_tag(params, images->ft_addr);
171#endif
172
173	printf("\n");
174
175	params = setup_last_tag(params);
176
177	show_boot_progress(15);
178
179	printf("Transferring Control to Linux @0x%08lx ...\n\n",
180	       (ulong)images->ep);
181
182	flush_dcache_range((unsigned long)params_start, (unsigned long)params);
183
184	if (flag & BOOTM_STATE_OS_FAKE_GO)
185		return 0;
186
187	/*
188	 * _start() in vmlinux expects boot params in register a2.
189	 * NOTE:
190	 *    Disable/delete your u-boot breakpoints before stepping into linux.
191	 */
192	asm volatile ("mov	a2, %0\n\t"
193		      "jx	%1\n\t"
194		      : : "a" (params_start), "a" (images->ep)
195		      : "a2");
196
197	/* Does not return */
198
199	return 1;
200}
201
202static ulong get_sp(void)
203{
204	ulong ret;
205
206	asm("mov %0, a1" : "=r"(ret) : );
207	return ret;
208}
209
210void arch_lmb_reserve(struct lmb *lmb)
211{
212	arch_lmb_reserve_generic(lmb, get_sp(), gd->ram_top, 4096);
213}
214