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