ARM: tegra: Unify Tegra186 builds
[oweals/u-boot.git] / arch / arm / mach-tegra / cboot.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2016-2018, NVIDIA CORPORATION.
4  */
5
6 #include <common.h>
7 #include <fdt_support.h>
8 #include <fdtdec.h>
9 #include <stdlib.h>
10
11 #include <linux/sizes.h>
12
13 #include <asm/arch/tegra.h>
14 #include <asm/arch-tegra/cboot.h>
15 #include <asm/armv8/mmu.h>
16
17 /*
18  * Size of a region that's large enough to hold the relocated U-Boot and all
19  * other allocations made around it (stack, heap, page tables, etc.)
20  * In practice, running "bdinfo" at the shell prompt, the stack reaches about
21  * 5MB from the address selected for ram_top as of the time of writing,
22  * so a 16MB region should be plenty.
23  */
24 #define MIN_USABLE_RAM_SIZE SZ_16M
25 /*
26  * The amount of space we expect to require for stack usage. Used to validate
27  * that all reservations fit into the region selected for the relocation target
28  */
29 #define MIN_USABLE_STACK_SIZE SZ_1M
30
31 DECLARE_GLOBAL_DATA_PTR;
32
33 extern struct mm_region tegra_mem_map[];
34
35 /*
36  * These variables are written to before relocation, and hence cannot be
37  * in.bss, since .bss overlaps the DTB that's appended to the U-Boot binary.
38  * The section attribute forces this into .data and avoids this issue. This
39  * also has the nice side-effect of the content being valid after relocation.
40  */
41
42 /* The number of valid entries in ram_banks[] */
43 static int ram_bank_count __attribute__((section(".data")));
44
45 /*
46  * The usable top-of-RAM for U-Boot. This is both:
47  * a) Below 4GB to avoid issues with peripherals that use 32-bit addressing.
48  * b) At the end of a region that has enough space to hold the relocated U-Boot
49  *    and all other allocations made around it (stack, heap, page tables, etc.)
50  */
51 static u64 ram_top __attribute__((section(".data")));
52 /* The base address of the region of RAM that ends at ram_top */
53 static u64 region_base __attribute__((section(".data")));
54
55 int cboot_dram_init(void)
56 {
57         unsigned int na, ns;
58         const void *cboot_blob = (void *)cboot_boot_x0;
59         int node, len, i;
60         const u32 *prop;
61
62         if (!cboot_blob)
63                 return -EINVAL;
64
65         na = fdtdec_get_uint(cboot_blob, 0, "#address-cells", 2);
66         ns = fdtdec_get_uint(cboot_blob, 0, "#size-cells", 2);
67
68         node = fdt_path_offset(cboot_blob, "/memory");
69         if (node < 0) {
70                 pr_err("Can't find /memory node in cboot DTB");
71                 hang();
72         }
73         prop = fdt_getprop(cboot_blob, node, "reg", &len);
74         if (!prop) {
75                 pr_err("Can't find /memory/reg property in cboot DTB");
76                 hang();
77         }
78
79         /* Calculate the true # of base/size pairs to read */
80         len /= 4;               /* Convert bytes to number of cells */
81         len /= (na + ns);       /* Convert cells to number of banks */
82         if (len > CONFIG_NR_DRAM_BANKS)
83                 len = CONFIG_NR_DRAM_BANKS;
84
85         /* Parse the /memory node, and save useful entries */
86         gd->ram_size = 0;
87         ram_bank_count = 0;
88         for (i = 0; i < len; i++) {
89                 u64 bank_start, bank_end, bank_size, usable_bank_size;
90
91                 /* Extract raw memory region data from DTB */
92                 bank_start = fdt_read_number(prop, na);
93                 prop += na;
94                 bank_size = fdt_read_number(prop, ns);
95                 prop += ns;
96                 gd->ram_size += bank_size;
97                 bank_end = bank_start + bank_size;
98                 debug("Bank %d: %llx..%llx (+%llx)\n", i,
99                       bank_start, bank_end, bank_size);
100
101                 /*
102                  * Align the bank to MMU section size. This is not strictly
103                  * necessary, since the translation table construction code
104                  * handles page granularity without issue. However, aligning
105                  * the MMU entries reduces the size and number of levels in the
106                  * page table, so is worth it.
107                  */
108                 bank_start = ROUND(bank_start, SZ_2M);
109                 bank_end = bank_end & ~(SZ_2M - 1);
110                 bank_size = bank_end - bank_start;
111                 debug("  aligned: %llx..%llx (+%llx)\n",
112                       bank_start, bank_end, bank_size);
113                 if (bank_end <= bank_start)
114                         continue;
115
116                 /* Record data used to create MMU translation tables */
117                 ram_bank_count++;
118                 /* Index below is deliberately 1-based to skip MMIO entry */
119                 tegra_mem_map[ram_bank_count].virt = bank_start;
120                 tegra_mem_map[ram_bank_count].phys = bank_start;
121                 tegra_mem_map[ram_bank_count].size = bank_size;
122                 tegra_mem_map[ram_bank_count].attrs =
123                         PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE;
124
125                 /* Determine best bank to relocate U-Boot into */
126                 if (bank_end > SZ_4G)
127                         bank_end = SZ_4G;
128                 debug("  end  %llx (usable)\n", bank_end);
129                 usable_bank_size = bank_end - bank_start;
130                 debug("  size %llx (usable)\n", usable_bank_size);
131                 if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) &&
132                     (bank_end > ram_top)) {
133                         ram_top = bank_end;
134                         region_base = bank_start;
135                         debug("ram top now %llx\n", ram_top);
136                 }
137         }
138
139         /* Ensure memory map contains the desired sentinel entry */
140         tegra_mem_map[ram_bank_count + 1].virt = 0;
141         tegra_mem_map[ram_bank_count + 1].phys = 0;
142         tegra_mem_map[ram_bank_count + 1].size = 0;
143         tegra_mem_map[ram_bank_count + 1].attrs = 0;
144
145         /* Error out if a relocation target couldn't be found */
146         if (!ram_top) {
147                 pr_err("Can't find a usable RAM top");
148                 hang();
149         }
150
151         return 0;
152 }
153
154 int cboot_dram_init_banksize(void)
155 {
156         int i;
157
158         if (ram_bank_count == 0)
159                 return -EINVAL;
160
161         if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) {
162                 pr_err("Reservations exceed chosen region size");
163                 hang();
164         }
165
166         for (i = 0; i < ram_bank_count; i++) {
167                 gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt;
168                 gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size;
169         }
170
171 #ifdef CONFIG_PCI
172         gd->pci_ram_top = ram_top;
173 #endif
174
175         return 0;
176 }
177
178 ulong cboot_get_usable_ram_top(ulong total_size)
179 {
180         return ram_top;
181 }
182
183 /*
184  * The following few functions run late during the boot process and dynamically
185  * calculate the load address of various binaries. To keep track of multiple
186  * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
187  * is used for this purpose to avoid making yet another copy of the list of RAM
188  * banks. This is safe because tegra_mem_map[] is only used once during very
189  * early boot to create U-Boot's page tables, long before this code runs. If
190  * this assumption becomes invalid later, we can just fix the code to copy the
191  * list of RAM banks into some private data structure before running.
192  */
193
194 static char *gen_varname(const char *var, const char *ext)
195 {
196         size_t len_var = strlen(var);
197         size_t len_ext = strlen(ext);
198         size_t len = len_var + len_ext + 1;
199         char *varext = malloc(len);
200
201         if (!varext)
202                 return 0;
203         strcpy(varext, var);
204         strcpy(varext + len_var, ext);
205         return varext;
206 }
207
208 static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
209 {
210         u64 bank_start = tegra_mem_map[bank].virt;
211         u64 bank_size = tegra_mem_map[bank].size;
212         u64 bank_end = bank_start + bank_size;
213         bool keep_front = allocated_start != bank_start;
214         bool keep_tail = allocated_end != bank_end;
215
216         if (keep_front && keep_tail) {
217                 /*
218                  * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
219                  * starting at index 1 (index 0 is MMIO). So, we are at DRAM
220                  * entry "bank" not "bank - 1" as for a typical 0-base array.
221                  * The number of remaining DRAM entries is therefore
222                  * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
223                  * current entry and shift up the remaining entries, dropping
224                  * the last one. Thus, we must copy one fewer entry than the
225                  * number remaining.
226                  */
227                 memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
228                         CONFIG_NR_DRAM_BANKS - bank - 1);
229                 tegra_mem_map[bank].size = allocated_start - bank_start;
230                 bank++;
231                 tegra_mem_map[bank].virt = allocated_end;
232                 tegra_mem_map[bank].phys = allocated_end;
233                 tegra_mem_map[bank].size = bank_end - allocated_end;
234         } else if (keep_front) {
235                 tegra_mem_map[bank].size = allocated_start - bank_start;
236         } else if (keep_tail) {
237                 tegra_mem_map[bank].virt = allocated_end;
238                 tegra_mem_map[bank].phys = allocated_end;
239                 tegra_mem_map[bank].size = bank_end - allocated_end;
240         } else {
241                 /*
242                  * We could move all subsequent banks down in the array but
243                  * that's not necessary for subsequent allocations to work, so
244                  * we skip doing so.
245                  */
246                 tegra_mem_map[bank].size = 0;
247         }
248 }
249
250 static void reserve_ram(u64 start, u64 size)
251 {
252         int bank;
253         u64 end = start + size;
254
255         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
256                 u64 bank_start = tegra_mem_map[bank].virt;
257                 u64 bank_size = tegra_mem_map[bank].size;
258                 u64 bank_end = bank_start + bank_size;
259
260                 if (end <= bank_start || start > bank_end)
261                         continue;
262                 mark_ram_allocated(bank, start, end);
263                 break;
264         }
265 }
266
267 static u64 alloc_ram(u64 size, u64 align, u64 offset)
268 {
269         int bank;
270
271         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
272                 u64 bank_start = tegra_mem_map[bank].virt;
273                 u64 bank_size = tegra_mem_map[bank].size;
274                 u64 bank_end = bank_start + bank_size;
275                 u64 allocated = ROUND(bank_start, align) + offset;
276                 u64 allocated_end = allocated + size;
277
278                 if (allocated_end > bank_end)
279                         continue;
280                 mark_ram_allocated(bank, allocated, allocated_end);
281                 return allocated;
282         }
283         return 0;
284 }
285
286 static void set_calculated_aliases(char *aliases, u64 address)
287 {
288         char *tmp, *alias;
289         int err;
290
291         aliases = strdup(aliases);
292         if (!aliases) {
293                 pr_err("strdup(aliases) failed");
294                 return;
295         }
296
297         tmp = aliases;
298         while (true) {
299                 alias = strsep(&tmp, " ");
300                 if (!alias)
301                         break;
302                 debug("%s: alias: %s\n", __func__, alias);
303                 err = env_set_hex(alias, address);
304                 if (err)
305                         pr_err("Could not set %s\n", alias);
306         }
307
308         free(aliases);
309 }
310
311 static void set_calculated_env_var(const char *var)
312 {
313         char *var_size;
314         char *var_align;
315         char *var_offset;
316         char *var_aliases;
317         u64 size;
318         u64 align;
319         u64 offset;
320         char *aliases;
321         u64 address;
322         int err;
323
324         var_size = gen_varname(var, "_size");
325         if (!var_size)
326                 return;
327         var_align = gen_varname(var, "_align");
328         if (!var_align)
329                 goto out_free_var_size;
330         var_offset = gen_varname(var, "_offset");
331         if (!var_offset)
332                 goto out_free_var_align;
333         var_aliases = gen_varname(var, "_aliases");
334         if (!var_aliases)
335                 goto out_free_var_offset;
336
337         size = env_get_hex(var_size, 0);
338         if (!size) {
339                 pr_err("%s not set or zero\n", var_size);
340                 goto out_free_var_aliases;
341         }
342         align = env_get_hex(var_align, 1);
343         /* Handle extant variables, but with a value of 0 */
344         if (!align)
345                 align = 1;
346         offset = env_get_hex(var_offset, 0);
347         aliases = env_get(var_aliases);
348
349         debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
350               __func__, var, size, align, offset);
351         if (aliases)
352                 debug("%s: Aliases: %s\n", __func__, aliases);
353
354         address = alloc_ram(size, align, offset);
355         if (!address) {
356                 pr_err("Could not allocate %s\n", var);
357                 goto out_free_var_aliases;
358         }
359         debug("%s: Address %llx\n", __func__, address);
360
361         err = env_set_hex(var, address);
362         if (err)
363                 pr_err("Could not set %s\n", var);
364         if (aliases)
365                 set_calculated_aliases(aliases, address);
366
367 out_free_var_aliases:
368         free(var_aliases);
369 out_free_var_offset:
370         free(var_offset);
371 out_free_var_align:
372         free(var_align);
373 out_free_var_size:
374         free(var_size);
375 }
376
377 #ifdef DEBUG
378 static void dump_ram_banks(void)
379 {
380         int bank;
381
382         for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
383                 u64 bank_start = tegra_mem_map[bank].virt;
384                 u64 bank_size = tegra_mem_map[bank].size;
385                 u64 bank_end = bank_start + bank_size;
386
387                 if (!bank_size)
388                         continue;
389                 printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
390                        bank_start, bank_end, bank_size);
391         }
392 }
393 #endif
394
395 static void set_calculated_env_vars(void)
396 {
397         char *vars, *tmp, *var;
398
399 #ifdef DEBUG
400         printf("RAM banks before any calculated env. var.s:\n");
401         dump_ram_banks();
402 #endif
403
404         reserve_ram(cboot_boot_x0, fdt_totalsize(cboot_boot_x0));
405
406 #ifdef DEBUG
407         printf("RAM after reserving cboot DTB:\n");
408         dump_ram_banks();
409 #endif
410
411         vars = env_get("calculated_vars");
412         if (!vars) {
413                 debug("%s: No env var calculated_vars\n", __func__);
414                 return;
415         }
416
417         vars = strdup(vars);
418         if (!vars) {
419                 pr_err("strdup(calculated_vars) failed");
420                 return;
421         }
422
423         tmp = vars;
424         while (true) {
425                 var = strsep(&tmp, " ");
426                 if (!var)
427                         break;
428                 debug("%s: var: %s\n", __func__, var);
429                 set_calculated_env_var(var);
430 #ifdef DEBUG
431                 printf("RAM banks after allocating %s:\n", var);
432                 dump_ram_banks();
433 #endif
434         }
435
436         free(vars);
437 }
438
439 static int set_fdt_addr(void)
440 {
441         int ret;
442
443         ret = env_set_hex("fdt_addr", cboot_boot_x0);
444         if (ret) {
445                 printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
446                 return ret;
447         }
448
449         return 0;
450 }
451
452 /*
453  * Attempt to use /chosen/nvidia,ether-mac in the cboot DTB to U-Boot's
454  * ethaddr environment variable if possible.
455  */
456 static int set_ethaddr_from_cboot(void)
457 {
458         const void *cboot_blob = (void *)cboot_boot_x0;
459         int ret, node, len;
460         const u32 *prop;
461
462         /* Already a valid address in the environment? If so, keep it */
463         if (env_get("ethaddr"))
464                 return 0;
465
466         node = fdt_path_offset(cboot_blob, "/chosen");
467         if (node < 0) {
468                 printf("Can't find /chosen node in cboot DTB\n");
469                 return node;
470         }
471         prop = fdt_getprop(cboot_blob, node, "nvidia,ether-mac", &len);
472         if (!prop) {
473                 printf("Can't find nvidia,ether-mac property in cboot DTB\n");
474                 return -ENOENT;
475         }
476
477         ret = env_set("ethaddr", (void *)prop);
478         if (ret) {
479                 printf("Failed to set ethaddr from cboot DTB: %d\n", ret);
480                 return ret;
481         }
482
483         return 0;
484 }
485
486 int cboot_late_init(void)
487 {
488         set_calculated_env_vars();
489         /*
490          * Ignore errors here; the value may not be used depending on
491          * extlinux.conf or boot script content.
492          */
493         set_fdt_addr();
494         /* Ignore errors here; not all cases care about Ethernet addresses */
495         set_ethaddr_from_cboot();
496
497         return 0;
498 }