1
2
3
4
5
6#include <common.h>
7#include <env.h>
8#include <fdt_support.h>
9#include <fdtdec.h>
10#include <hang.h>
11#include <init.h>
12#include <log.h>
13#include <malloc.h>
14#include <net.h>
15#include <stdlib.h>
16#include <string.h>
17#include <asm/global_data.h>
18
19#include <linux/ctype.h>
20#include <linux/sizes.h>
21
22#include <asm/arch/tegra.h>
23#include <asm/arch-tegra/cboot.h>
24#include <asm/armv8/mmu.h>
25
26
27
28
29
30
31
32
33#define MIN_USABLE_RAM_SIZE SZ_16M
34
35
36
37
38#define MIN_USABLE_STACK_SIZE SZ_1M
39
40DECLARE_GLOBAL_DATA_PTR;
41
42extern struct mm_region tegra_mem_map[];
43
44
45
46
47
48
49
50
51
52static int ram_bank_count __section(".data");
53
54
55
56
57
58
59
60static u64 ram_top __section(".data");
61
62static u64 region_base __section(".data");
63
64
65
66
67
68unsigned long cboot_boot_x0 __section(".data");
69
70void cboot_save_boot_params(unsigned long x0, unsigned long x1,
71 unsigned long x2, unsigned long x3)
72{
73 cboot_boot_x0 = x0;
74}
75
76int cboot_dram_init(void)
77{
78 unsigned int na, ns;
79 const void *cboot_blob = (void *)cboot_boot_x0;
80 int node, len, i;
81 const u32 *prop;
82
83 if (!cboot_blob)
84 return -EINVAL;
85
86 na = fdtdec_get_uint(cboot_blob, 0, "#address-cells", 2);
87 ns = fdtdec_get_uint(cboot_blob, 0, "#size-cells", 2);
88
89 node = fdt_path_offset(cboot_blob, "/memory");
90 if (node < 0) {
91 pr_err("Can't find /memory node in cboot DTB");
92 hang();
93 }
94 prop = fdt_getprop(cboot_blob, node, "reg", &len);
95 if (!prop) {
96 pr_err("Can't find /memory/reg property in cboot DTB");
97 hang();
98 }
99
100
101 len /= 4;
102 len /= (na + ns);
103 if (len > CONFIG_NR_DRAM_BANKS)
104 len = CONFIG_NR_DRAM_BANKS;
105
106
107 gd->ram_size = 0;
108 ram_bank_count = 0;
109 for (i = 0; i < len; i++) {
110 u64 bank_start, bank_end, bank_size, usable_bank_size;
111
112
113 bank_start = fdt_read_number(prop, na);
114 prop += na;
115 bank_size = fdt_read_number(prop, ns);
116 prop += ns;
117 gd->ram_size += bank_size;
118 bank_end = bank_start + bank_size;
119 debug("Bank %d: %llx..%llx (+%llx)\n", i,
120 bank_start, bank_end, bank_size);
121
122
123
124
125
126
127
128
129 bank_start = ROUND(bank_start, SZ_2M);
130 bank_end = bank_end & ~(SZ_2M - 1);
131 bank_size = bank_end - bank_start;
132 debug(" aligned: %llx..%llx (+%llx)\n",
133 bank_start, bank_end, bank_size);
134 if (bank_end <= bank_start)
135 continue;
136
137
138 ram_bank_count++;
139
140 tegra_mem_map[ram_bank_count].virt = bank_start;
141 tegra_mem_map[ram_bank_count].phys = bank_start;
142 tegra_mem_map[ram_bank_count].size = bank_size;
143 tegra_mem_map[ram_bank_count].attrs =
144 PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE;
145
146
147 if (bank_end > SZ_4G)
148 bank_end = SZ_4G;
149 debug(" end %llx (usable)\n", bank_end);
150 usable_bank_size = bank_end - bank_start;
151 debug(" size %llx (usable)\n", usable_bank_size);
152 if ((usable_bank_size >= MIN_USABLE_RAM_SIZE) &&
153 (bank_end > ram_top)) {
154 ram_top = bank_end;
155 region_base = bank_start;
156 debug("ram top now %llx\n", ram_top);
157 }
158 }
159
160
161 tegra_mem_map[ram_bank_count + 1].virt = 0;
162 tegra_mem_map[ram_bank_count + 1].phys = 0;
163 tegra_mem_map[ram_bank_count + 1].size = 0;
164 tegra_mem_map[ram_bank_count + 1].attrs = 0;
165
166
167 if (!ram_top) {
168 pr_err("Can't find a usable RAM top");
169 hang();
170 }
171
172 return 0;
173}
174
175int cboot_dram_init_banksize(void)
176{
177 int i;
178
179 if (ram_bank_count == 0)
180 return -EINVAL;
181
182 if ((gd->start_addr_sp - region_base) < MIN_USABLE_STACK_SIZE) {
183 pr_err("Reservations exceed chosen region size");
184 hang();
185 }
186
187 for (i = 0; i < ram_bank_count; i++) {
188 gd->bd->bi_dram[i].start = tegra_mem_map[1 + i].virt;
189 gd->bd->bi_dram[i].size = tegra_mem_map[1 + i].size;
190 }
191
192#ifdef CONFIG_PCI
193 gd->pci_ram_top = ram_top;
194#endif
195
196 return 0;
197}
198
199ulong cboot_get_usable_ram_top(ulong total_size)
200{
201 return ram_top;
202}
203
204
205
206
207
208
209
210
211
212
213
214
215static char *gen_varname(const char *var, const char *ext)
216{
217 size_t len_var = strlen(var);
218 size_t len_ext = strlen(ext);
219 size_t len = len_var + len_ext + 1;
220 char *varext = malloc(len);
221
222 if (!varext)
223 return 0;
224 strcpy(varext, var);
225 strcpy(varext + len_var, ext);
226 return varext;
227}
228
229static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
230{
231 u64 bank_start = tegra_mem_map[bank].virt;
232 u64 bank_size = tegra_mem_map[bank].size;
233 u64 bank_end = bank_start + bank_size;
234 bool keep_front = allocated_start != bank_start;
235 bool keep_tail = allocated_end != bank_end;
236
237 if (keep_front && keep_tail) {
238
239
240
241
242
243
244
245
246
247
248 memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
249 CONFIG_NR_DRAM_BANKS - bank - 1);
250 tegra_mem_map[bank].size = allocated_start - bank_start;
251 bank++;
252 tegra_mem_map[bank].virt = allocated_end;
253 tegra_mem_map[bank].phys = allocated_end;
254 tegra_mem_map[bank].size = bank_end - allocated_end;
255 } else if (keep_front) {
256 tegra_mem_map[bank].size = allocated_start - bank_start;
257 } else if (keep_tail) {
258 tegra_mem_map[bank].virt = allocated_end;
259 tegra_mem_map[bank].phys = allocated_end;
260 tegra_mem_map[bank].size = bank_end - allocated_end;
261 } else {
262
263
264
265
266
267 tegra_mem_map[bank].size = 0;
268 }
269}
270
271static void reserve_ram(u64 start, u64 size)
272{
273 int bank;
274 u64 end = start + size;
275
276 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
277 u64 bank_start = tegra_mem_map[bank].virt;
278 u64 bank_size = tegra_mem_map[bank].size;
279 u64 bank_end = bank_start + bank_size;
280
281 if (end <= bank_start || start > bank_end)
282 continue;
283 mark_ram_allocated(bank, start, end);
284 break;
285 }
286}
287
288static u64 alloc_ram(u64 size, u64 align, u64 offset)
289{
290 int bank;
291
292 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
293 u64 bank_start = tegra_mem_map[bank].virt;
294 u64 bank_size = tegra_mem_map[bank].size;
295 u64 bank_end = bank_start + bank_size;
296 u64 allocated = ROUND(bank_start, align) + offset;
297 u64 allocated_end = allocated + size;
298
299 if (allocated_end > bank_end)
300 continue;
301 mark_ram_allocated(bank, allocated, allocated_end);
302 return allocated;
303 }
304 return 0;
305}
306
307static void set_calculated_aliases(char *aliases, u64 address)
308{
309 char *tmp, *alias;
310 int err;
311
312 aliases = strdup(aliases);
313 if (!aliases) {
314 pr_err("strdup(aliases) failed");
315 return;
316 }
317
318 tmp = aliases;
319 while (true) {
320 alias = strsep(&tmp, " ");
321 if (!alias)
322 break;
323 debug("%s: alias: %s\n", __func__, alias);
324 err = env_set_hex(alias, address);
325 if (err)
326 pr_err("Could not set %s\n", alias);
327 }
328
329 free(aliases);
330}
331
332static void set_calculated_env_var(const char *var)
333{
334 char *var_size;
335 char *var_align;
336 char *var_offset;
337 char *var_aliases;
338 u64 size;
339 u64 align;
340 u64 offset;
341 char *aliases;
342 u64 address;
343 int err;
344
345 var_size = gen_varname(var, "_size");
346 if (!var_size)
347 return;
348 var_align = gen_varname(var, "_align");
349 if (!var_align)
350 goto out_free_var_size;
351 var_offset = gen_varname(var, "_offset");
352 if (!var_offset)
353 goto out_free_var_align;
354 var_aliases = gen_varname(var, "_aliases");
355 if (!var_aliases)
356 goto out_free_var_offset;
357
358 size = env_get_hex(var_size, 0);
359 if (!size) {
360 pr_err("%s not set or zero\n", var_size);
361 goto out_free_var_aliases;
362 }
363 align = env_get_hex(var_align, 1);
364
365 if (!align)
366 align = 1;
367 offset = env_get_hex(var_offset, 0);
368 aliases = env_get(var_aliases);
369
370 debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
371 __func__, var, size, align, offset);
372 if (aliases)
373 debug("%s: Aliases: %s\n", __func__, aliases);
374
375 address = alloc_ram(size, align, offset);
376 if (!address) {
377 pr_err("Could not allocate %s\n", var);
378 goto out_free_var_aliases;
379 }
380 debug("%s: Address %llx\n", __func__, address);
381
382 err = env_set_hex(var, address);
383 if (err)
384 pr_err("Could not set %s\n", var);
385 if (aliases)
386 set_calculated_aliases(aliases, address);
387
388out_free_var_aliases:
389 free(var_aliases);
390out_free_var_offset:
391 free(var_offset);
392out_free_var_align:
393 free(var_align);
394out_free_var_size:
395 free(var_size);
396}
397
398#ifdef DEBUG
399static void dump_ram_banks(void)
400{
401 int bank;
402
403 for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
404 u64 bank_start = tegra_mem_map[bank].virt;
405 u64 bank_size = tegra_mem_map[bank].size;
406 u64 bank_end = bank_start + bank_size;
407
408 if (!bank_size)
409 continue;
410 printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
411 bank_start, bank_end, bank_size);
412 }
413}
414#endif
415
416static void set_calculated_env_vars(void)
417{
418 char *vars, *tmp, *var;
419
420#ifdef DEBUG
421 printf("RAM banks before any calculated env. var.s:\n");
422 dump_ram_banks();
423#endif
424
425 reserve_ram(cboot_boot_x0, fdt_totalsize(cboot_boot_x0));
426
427#ifdef DEBUG
428 printf("RAM after reserving cboot DTB:\n");
429 dump_ram_banks();
430#endif
431
432 vars = env_get("calculated_vars");
433 if (!vars) {
434 debug("%s: No env var calculated_vars\n", __func__);
435 return;
436 }
437
438 vars = strdup(vars);
439 if (!vars) {
440 pr_err("strdup(calculated_vars) failed");
441 return;
442 }
443
444 tmp = vars;
445 while (true) {
446 var = strsep(&tmp, " ");
447 if (!var)
448 break;
449 debug("%s: var: %s\n", __func__, var);
450 set_calculated_env_var(var);
451#ifdef DEBUG
452 printf("RAM banks after allocating %s:\n", var);
453 dump_ram_banks();
454#endif
455 }
456
457 free(vars);
458}
459
460static int set_fdt_addr(void)
461{
462 int ret;
463
464 ret = env_set_hex("fdt_addr", cboot_boot_x0);
465 if (ret) {
466 printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
467 return ret;
468 }
469
470 return 0;
471}
472
473
474
475
476
477static int cboot_get_ethaddr_legacy(const void *fdt, uint8_t mac[ETH_ALEN])
478{
479 const char *const properties[] = {
480 "nvidia,ethernet-mac",
481 "nvidia,ether-mac",
482 };
483 const char *prop;
484 unsigned int i;
485 int node, len;
486
487 node = fdt_path_offset(fdt, "/chosen");
488 if (node < 0) {
489 printf("Can't find /chosen node in cboot DTB\n");
490 return node;
491 }
492
493 for (i = 0; i < ARRAY_SIZE(properties); i++) {
494 prop = fdt_getprop(fdt, node, properties[i], &len);
495 if (prop)
496 break;
497 }
498
499 if (!prop) {
500 printf("Can't find Ethernet MAC address in cboot DTB\n");
501 return -ENOENT;
502 }
503
504 string_to_enetaddr(prop, mac);
505
506 if (!is_valid_ethaddr(mac)) {
507 printf("Invalid MAC address: %s\n", prop);
508 return -EINVAL;
509 }
510
511 debug("Legacy MAC address: %pM\n", mac);
512
513 return 0;
514}
515
516int cboot_get_ethaddr(const void *fdt, uint8_t mac[ETH_ALEN])
517{
518 int node, len, err = 0;
519 const uchar *prop;
520 const char *path;
521
522 path = fdt_get_alias(fdt, "ethernet");
523 if (!path) {
524 err = -ENOENT;
525 goto out;
526 }
527
528 debug("ethernet alias found: %s\n", path);
529
530 node = fdt_path_offset(fdt, path);
531 if (node < 0) {
532 err = -ENOENT;
533 goto out;
534 }
535
536 prop = fdt_getprop(fdt, node, "local-mac-address", &len);
537 if (!prop) {
538 err = -ENOENT;
539 goto out;
540 }
541
542 if (len != ETH_ALEN) {
543 err = -EINVAL;
544 goto out;
545 }
546
547 debug("MAC address: %pM\n", prop);
548 memcpy(mac, prop, ETH_ALEN);
549
550out:
551 if (err < 0)
552 err = cboot_get_ethaddr_legacy(fdt, mac);
553
554 return err;
555}
556
557static char *strip(const char *ptr)
558{
559 const char *end;
560
561 while (*ptr && isblank(*ptr))
562 ptr++;
563
564
565 if (*ptr == '\0')
566 return strdup(ptr);
567
568 end = ptr;
569
570 while (end[1])
571 end++;
572
573 while (isblank(*end))
574 end--;
575
576 return strndup(ptr, end - ptr + 1);
577}
578
579static char *cboot_get_bootargs(const void *fdt)
580{
581 const char *args;
582 int offset, len;
583
584 offset = fdt_path_offset(fdt, "/chosen");
585 if (offset < 0)
586 return NULL;
587
588 args = fdt_getprop(fdt, offset, "bootargs", &len);
589 if (!args)
590 return NULL;
591
592 return strip(args);
593}
594
595int cboot_late_init(void)
596{
597 const void *fdt = (const void *)cboot_boot_x0;
598 uint8_t mac[ETH_ALEN];
599 char *bootargs;
600 int err;
601
602 set_calculated_env_vars();
603
604
605
606
607 set_fdt_addr();
608
609
610 err = cboot_get_ethaddr(fdt, mac);
611 if (!err) {
612 void *blob = (void *)gd->fdt_blob;
613
614 err = fdtdec_set_ethernet_mac_address(blob, mac, sizeof(mac));
615 if (err < 0)
616 printf("failed to set MAC address %pM: %d\n", mac, err);
617 }
618
619 bootargs = cboot_get_bootargs(fdt);
620 if (bootargs) {
621 env_set("cbootargs", bootargs);
622 free(bootargs);
623 }
624
625 return 0;
626}
627