]> git.sur5r.net Git - u-boot/blob - arch/arm/mach-tegra/tegra186/nvtboot_board.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / arch / arm / mach-tegra / tegra186 / nvtboot_board.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2016-2018, NVIDIA CORPORATION.
4  */
5
6 #include <stdlib.h>
7 #include <common.h>
8 #include <fdt_support.h>
9 #include <fdtdec.h>
10 #include <asm/arch/tegra.h>
11 #include <asm/armv8/mmu.h>
12
13 extern unsigned long nvtboot_boot_x0;
14
15 /*
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
26 extern struct mm_region tegra_mem_map[];
27
28 static 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
42 static 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
84 static 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
101 static 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
120 static 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
145 static 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
201 out_free_var_aliases:
202         free(var_aliases);
203 out_free_var_offset:
204         free(var_offset);
205 out_free_var_align:
206         free(var_align);
207 out_free_var_size:
208         free(var_size);
209 }
210
211 #ifdef DEBUG
212 static 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
229 static 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
273 static int set_fdt_addr(void)
274 {
275         int ret;
276
277         ret = env_set_hex("fdt_addr", nvtboot_boot_x0);
278         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
286 /*
287  * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's
288  * ethaddr environment variable if possible.
289  */
290 static 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 */
297         if (env_get("ethaddr"))
298                 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
311         ret = env_set("ethaddr", (void *)prop);
312         if (ret) {
313                 printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret);
314                 return ret;
315         }
316
317         return 0;
318 }
319
320 int tegra_soc_board_init_late(void)
321 {
322         set_calculated_env_vars();
323         /*
324          * Ignore errors here; the value may not be used depending on
325          * extlinux.conf or boot script content.
326          */
327         set_fdt_addr();
328         /* Ignore errors here; not all cases care about Ethernet addresses */
329         set_ethaddr_from_nvtboot();
330
331         return 0;
332 }