blob: 83c0e931ea248992cd5788d435974be21777a004 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Stephen Warrenfdb32692016-09-12 11:51:13 -06002/*
Stephen Warren07ba4982018-01-08 17:41:24 -07003 * Copyright (c) 2016-2018, NVIDIA CORPORATION.
Stephen Warrenfdb32692016-09-12 11:51:13 -06004 */
5
Stephen Warren07ba4982018-01-08 17:41:24 -07006#include <stdlib.h>
Stephen Warrenfdb32692016-09-12 11:51:13 -06007#include <common.h>
8#include <fdt_support.h>
9#include <fdtdec.h>
10#include <asm/arch/tegra.h>
Stephen Warren07ba4982018-01-08 17:41:24 -070011#include <asm/armv8/mmu.h>
12
Stephen Warrenfdb32692016-09-12 11:51:13 -060013extern unsigned long nvtboot_boot_x0;
14
Stephen Warren07ba4982018-01-08 17:41:24 -070015/*
16 * The following few functions run late during the boot process and dynamically
17 * calculate the load address of various binaries. To keep track of multiple
18 * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
19 * is used for this purpose to avoid making yet another copy of the list of RAM
20 * banks. This is safe because tegra_mem_map[] is only used once during very
21 * early boot to create U-Boot's page tables, long before this code runs. If
22 * this assumption becomes invalid later, we can just fix the code to copy the
23 * list of RAM banks into some private data structure before running.
24 */
25
26extern struct mm_region tegra_mem_map[];
27
28static char *gen_varname(const char *var, const char *ext)
29{
30 size_t len_var = strlen(var);
31 size_t len_ext = strlen(ext);
32 size_t len = len_var + len_ext + 1;
33 char *varext = malloc(len);
34
35 if (!varext)
36 return 0;
37 strcpy(varext, var);
38 strcpy(varext + len_var, ext);
39 return varext;
40}
41
42static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
43{
44 u64 bank_start = tegra_mem_map[bank].virt;
45 u64 bank_size = tegra_mem_map[bank].size;
46 u64 bank_end = bank_start + bank_size;
47 bool keep_front = allocated_start != bank_start;
48 bool keep_tail = allocated_end != bank_end;
49
50 if (keep_front && keep_tail) {
51 /*
52 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
53 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
54 * entry "bank" not "bank - 1" as for a typical 0-base array.
55 * The number of remaining DRAM entries is therefore
56 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
57 * current entry and shift up the remaining entries, dropping
58 * the last one. Thus, we must copy one fewer entry than the
59 * number remaining.
60 */
61 memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
62 CONFIG_NR_DRAM_BANKS - bank - 1);
63 tegra_mem_map[bank].size = allocated_start - bank_start;
64 bank++;
65 tegra_mem_map[bank].virt = allocated_end;
66 tegra_mem_map[bank].phys = allocated_end;
67 tegra_mem_map[bank].size = bank_end - allocated_end;
68 } else if (keep_front) {
69 tegra_mem_map[bank].size = allocated_start - bank_start;
70 } else if (keep_tail) {
71 tegra_mem_map[bank].virt = allocated_end;
72 tegra_mem_map[bank].phys = allocated_end;
73 tegra_mem_map[bank].size = bank_end - allocated_end;
74 } else {
75 /*
76 * We could move all subsequent banks down in the array but
77 * that's not necessary for subsequent allocations to work, so
78 * we skip doing so.
79 */
80 tegra_mem_map[bank].size = 0;
81 }
82}
83
84static void reserve_ram(u64 start, u64 size)
85{
86 int bank;
87 u64 end = start + size;
88
89 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
90 u64 bank_start = tegra_mem_map[bank].virt;
91 u64 bank_size = tegra_mem_map[bank].size;
92 u64 bank_end = bank_start + bank_size;
93
94 if (end <= bank_start || start > bank_end)
95 continue;
96 mark_ram_allocated(bank, start, end);
97 break;
98 }
99}
100
101static u64 alloc_ram(u64 size, u64 align, u64 offset)
102{
103 int bank;
104
105 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
106 u64 bank_start = tegra_mem_map[bank].virt;
107 u64 bank_size = tegra_mem_map[bank].size;
108 u64 bank_end = bank_start + bank_size;
109 u64 allocated = ROUND(bank_start, align) + offset;
110 u64 allocated_end = allocated + size;
111
112 if (allocated_end > bank_end)
113 continue;
114 mark_ram_allocated(bank, allocated, allocated_end);
115 return allocated;
116 }
117 return 0;
118}
119
120static void set_calculated_aliases(char *aliases, u64 address)
121{
122 char *tmp, *alias;
123 int err;
124
125 aliases = strdup(aliases);
126 if (!aliases) {
127 pr_err("strdup(aliases) failed");
128 return;
129 }
130
131 tmp = aliases;
132 while (true) {
133 alias = strsep(&tmp, " ");
134 if (!alias)
135 break;
136 debug("%s: alias: %s\n", __func__, alias);
137 err = env_set_hex(alias, address);
138 if (err)
139 pr_err("Could not set %s\n", alias);
140 }
141
142 free(aliases);
143}
144
145static void set_calculated_env_var(const char *var)
146{
147 char *var_size;
148 char *var_align;
149 char *var_offset;
150 char *var_aliases;
151 u64 size;
152 u64 align;
153 u64 offset;
154 char *aliases;
155 u64 address;
156 int err;
157
158 var_size = gen_varname(var, "_size");
159 if (!var_size)
160 return;
161 var_align = gen_varname(var, "_align");
162 if (!var_align)
163 goto out_free_var_size;
164 var_offset = gen_varname(var, "_offset");
165 if (!var_offset)
166 goto out_free_var_align;
167 var_aliases = gen_varname(var, "_aliases");
168 if (!var_aliases)
169 goto out_free_var_offset;
170
171 size = env_get_hex(var_size, 0);
172 if (!size) {
173 pr_err("%s not set or zero\n", var_size);
174 goto out_free_var_aliases;
175 }
176 align = env_get_hex(var_align, 1);
177 /* Handle extant variables, but with a value of 0 */
178 if (!align)
179 align = 1;
180 offset = env_get_hex(var_offset, 0);
181 aliases = env_get(var_aliases);
182
183 debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
184 __func__, var, size, align, offset);
185 if (aliases)
186 debug("%s: Aliases: %s\n", __func__, aliases);
187
188 address = alloc_ram(size, align, offset);
189 if (!address) {
190 pr_err("Could not allocate %s\n", var);
191 goto out_free_var_aliases;
192 }
193 debug("%s: Address %llx\n", __func__, address);
194
195 err = env_set_hex(var, address);
196 if (err)
197 pr_err("Could not set %s\n", var);
198 if (aliases)
199 set_calculated_aliases(aliases, address);
200
201out_free_var_aliases:
202 free(var_aliases);
203out_free_var_offset:
204 free(var_offset);
205out_free_var_align:
206 free(var_align);
207out_free_var_size:
208 free(var_size);
209}
210
211#ifdef DEBUG
212static void dump_ram_banks(void)
213{
214 int bank;
215
216 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
217 u64 bank_start = tegra_mem_map[bank].virt;
218 u64 bank_size = tegra_mem_map[bank].size;
219 u64 bank_end = bank_start + bank_size;
220
221 if (!bank_size)
222 continue;
223 printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
224 bank_start, bank_end, bank_size);
225 }
226}
227#endif
228
229static void set_calculated_env_vars(void)
230{
231 char *vars, *tmp, *var;
232
233#ifdef DEBUG
234 printf("RAM banks before any calculated env. var.s:\n");
235 dump_ram_banks();
236#endif
237
238 reserve_ram(nvtboot_boot_x0, fdt_totalsize(nvtboot_boot_x0));
239
240#ifdef DEBUG
241 printf("RAM after reserving cboot DTB:\n");
242 dump_ram_banks();
243#endif
244
245 vars = env_get("calculated_vars");
246 if (!vars) {
247 debug("%s: No env var calculated_vars\n", __func__);
248 return;
249 }
250
251 vars = strdup(vars);
252 if (!vars) {
253 pr_err("strdup(calculated_vars) failed");
254 return;
255 }
256
257 tmp = vars;
258 while (true) {
259 var = strsep(&tmp, " ");
260 if (!var)
261 break;
262 debug("%s: var: %s\n", __func__, var);
263 set_calculated_env_var(var);
264#ifdef DEBUG
265 printf("RAM banks affter allocating %s:\n", var);
266 dump_ram_banks();
267#endif
268 }
269
270 free(vars);
271}
272
Stephen Warren66c3e192016-12-02 12:26:42 -0700273static int set_fdt_addr(void)
274{
275 int ret;
276
Simon Glass4d949a22017-08-03 12:22:10 -0600277 ret = env_set_hex("fdt_addr", nvtboot_boot_x0);
Stephen Warren66c3e192016-12-02 12:26:42 -0700278 if (ret) {
279 printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
280 return ret;
281 }
282
283 return 0;
284}
285
Stephen Warrenfdb32692016-09-12 11:51:13 -0600286/*
287 * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's
288 * ethaddr environment variable if possible.
289 */
290static int set_ethaddr_from_nvtboot(void)
291{
292 const void *nvtboot_blob = (void *)nvtboot_boot_x0;
293 int ret, node, len;
294 const u32 *prop;
295
296 /* Already a valid address in the environment? If so, keep it */
Simon Glass64b723f2017-08-03 12:22:12 -0600297 if (env_get("ethaddr"))
Stephen Warrenfdb32692016-09-12 11:51:13 -0600298 return 0;
299
300 node = fdt_path_offset(nvtboot_blob, "/chosen");
301 if (node < 0) {
302 printf("Can't find /chosen node in nvtboot DTB\n");
303 return node;
304 }
305 prop = fdt_getprop(nvtboot_blob, node, "nvidia,ether-mac", &len);
306 if (!prop) {
307 printf("Can't find nvidia,ether-mac property in nvtboot DTB\n");
308 return -ENOENT;
309 }
310
Simon Glass6a38e412017-08-03 12:22:09 -0600311 ret = env_set("ethaddr", (void *)prop);
Stephen Warrenfdb32692016-09-12 11:51:13 -0600312 if (ret) {
313 printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret);
314 return ret;
315 }
316
317 return 0;
318}
319
320int tegra_soc_board_init_late(void)
321{
Stephen Warren07ba4982018-01-08 17:41:24 -0700322 set_calculated_env_vars();
Stephen Warren66c3e192016-12-02 12:26:42 -0700323 /*
324 * Ignore errors here; the value may not be used depending on
325 * extlinux.conf or boot script content.
326 */
327 set_fdt_addr();
Stephen Warrenfdb32692016-09-12 11:51:13 -0600328 /* Ignore errors here; not all cases care about Ethernet addresses */
329 set_ethaddr_from_nvtboot();
330
331 return 0;
332}