1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Common initialisation for Qualcomm Snapdragon boards.
4 *
5 * Copyright (c) 2024 Linaro Ltd.
6 * Author: Casey Connolly <casey.connolly@linaro.org>
7 */
8
9 #define LOG_CATEGORY LOGC_BOARD
10 #define pr_fmt(fmt) "QCOM: " fmt
11
12 #include <asm/armv8/mmu.h>
13 #include <asm/gpio.h>
14 #include <asm/io.h>
15 #include <asm/psci.h>
16 #include <asm/system.h>
17 #include <dm/device.h>
18 #include <dm/pinctrl.h>
19 #include <dm/uclass-internal.h>
20 #include <dm/read.h>
21 #include <power/regulator.h>
22 #include <env.h>
23 #include <fdt_support.h>
24 #include <init.h>
25 #include <linux/arm-smccc.h>
26 #include <linux/bug.h>
27 #include <linux/psci.h>
28 #include <linux/sizes.h>
29 #include <lmb.h>
30 #include <malloc.h>
31 #include <fdt_support.h>
32 #include <usb.h>
33 #include <sort.h>
34 #include <time.h>
35
36 #include "qcom-priv.h"
37
38 DECLARE_GLOBAL_DATA_PTR;
39
40 enum qcom_boot_source qcom_boot_source __section(".data") = 0;
41
42 static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
43
44 struct mm_region *mem_map = rbx_mem_map;
45
46 static struct {
47 phys_addr_t start;
48 phys_size_t size;
49 } prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
50
dram_init(void)51 int dram_init(void)
52 {
53 /*
54 * gd->ram_base / ram_size have been setup already
55 * in qcom_parse_memory().
56 */
57 return 0;
58 }
59
ddr_bank_cmp(const void * v1,const void * v2)60 static int ddr_bank_cmp(const void *v1, const void *v2)
61 {
62 const struct {
63 phys_addr_t start;
64 phys_size_t size;
65 } *res1 = v1, *res2 = v2;
66
67 if (!res1->size)
68 return 1;
69 if (!res2->size)
70 return -1;
71
72 return (res1->start >> 24) - (res2->start >> 24);
73 }
74
75 /* This has to be done post-relocation since gd->bd isn't preserved */
qcom_configure_bi_dram(void)76 static void qcom_configure_bi_dram(void)
77 {
78 int i;
79
80 for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
81 gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
82 gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
83 }
84 }
85
dram_init_banksize(void)86 int dram_init_banksize(void)
87 {
88 qcom_configure_bi_dram();
89
90 return 0;
91 }
92
93 /**
94 * The generic memory parsing code in U-Boot lacks a few things that we
95 * need on Qualcomm:
96 *
97 * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
98 * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
99 * of that first memory block.
100 *
101 * This results in all memory beyond U-Boot being unusable in Linux when booting
102 * with EFI.
103 *
104 * Since the ranges in the memory node may be out of order, the only way for us
105 * to correctly determine the relocation address for U-Boot is to parse all
106 * memory regions and find the highest valid address.
107 *
108 * We can't use fdtdec_setup_memory_banksize() since it stores the result in
109 * gd->bd, which is not yet allocated.
110 *
111 * @fdt: FDT blob to parse /memory node from
112 *
113 * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
114 */
qcom_parse_memory(const void * fdt)115 static int qcom_parse_memory(const void *fdt)
116 {
117 int offset;
118 const fdt64_t *memory;
119 int memsize;
120 phys_addr_t ram_end = 0;
121 int i, j, banks;
122
123 offset = fdt_path_offset(fdt, "/memory");
124 if (offset < 0)
125 return -ENODATA;
126
127 memory = fdt_getprop(fdt, offset, "reg", &memsize);
128 if (!memory)
129 return -ENODATA;
130
131 banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
132
133 if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
134 log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
135
136 if (banks > CONFIG_NR_DRAM_BANKS)
137 log_err("Provided more memory banks than we can handle\n");
138
139 for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
140 prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
141 prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
142 if (!prevbl_ddr_banks[j].size) {
143 j--;
144 continue;
145 }
146 ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
147 }
148
149 if (!banks || !prevbl_ddr_banks[0].size)
150 return -ENODATA;
151
152 /* Sort our RAM banks -_- */
153 qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
154
155 gd->ram_base = prevbl_ddr_banks[0].start;
156 gd->ram_size = ram_end - gd->ram_base;
157
158 return 0;
159 }
160
show_psci_version(void)161 static void show_psci_version(void)
162 {
163 struct arm_smccc_res res;
164
165 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
166
167 /* Some older SoCs like MSM8916 don't always support PSCI */
168 if ((int)res.a0 == PSCI_RET_NOT_SUPPORTED)
169 return;
170
171 debug("PSCI: v%ld.%ld\n",
172 PSCI_VERSION_MAJOR(res.a0),
173 PSCI_VERSION_MINOR(res.a0));
174 }
175
176 /**
177 * Most MSM8916 devices in the wild shipped without PSCI support, but the
178 * upstream DTs pretend that PSCI exists. If that situation is detected here,
179 * the /psci node is deleted. This is done very early to ensure the PSCI
180 * firmware driver doesn't bind (which then binds a sysreset driver that won't
181 * work).
182 */
qcom_psci_fixup(void * fdt)183 static void qcom_psci_fixup(void *fdt)
184 {
185 int offset, ret;
186 struct arm_smccc_res res;
187
188 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
189
190 if ((int)res.a0 != PSCI_RET_NOT_SUPPORTED)
191 return;
192
193 offset = fdt_path_offset(fdt, "/psci");
194 if (offset < 0)
195 return;
196
197 debug("Found /psci DT node on device with no PSCI. Deleting.\n");
198 ret = fdt_del_node(fdt, offset);
199 if (ret)
200 log_err("Failed to delete /psci node: %d\n", ret);
201 }
202
203 /* We support booting U-Boot with an internal DT when running as a first-stage bootloader
204 * or for supporting quirky devices where it's easier to leave the downstream DT in place
205 * to improve ABL compatibility. Otherwise, we use the DT provided by ABL.
206 */
board_fdt_blob_setup(void ** fdtp)207 int board_fdt_blob_setup(void **fdtp)
208 {
209 struct fdt_header *external_fdt, *internal_fdt;
210 bool internal_valid, external_valid;
211 int ret = -ENODATA;
212
213 internal_fdt = (struct fdt_header *)*fdtp;
214 external_fdt = (struct fdt_header *)get_prev_bl_fdt_addr();
215 external_valid = external_fdt && !fdt_check_header(external_fdt);
216 internal_valid = !fdt_check_header(internal_fdt);
217
218 /*
219 * There is no point returning an error here, U-Boot can't do anything useful in this situation.
220 * Bail out while we can still print a useful error message.
221 */
222 if (!internal_valid && !external_valid)
223 panic("Internal FDT is invalid and no external FDT was provided! (fdt=%#llx)\n",
224 (phys_addr_t)external_fdt);
225
226 /* Prefer memory information from internal DT if it's present */
227 if (internal_valid)
228 ret = qcom_parse_memory(internal_fdt);
229
230 if (ret < 0 && external_valid) {
231 /* No internal FDT or it lacks a proper /memory node.
232 * The previous bootloader handed us something, let's try that.
233 */
234 if (internal_valid)
235 debug("No memory info in internal FDT, falling back to external\n");
236
237 ret = qcom_parse_memory(external_fdt);
238 }
239
240 if (ret < 0)
241 panic("No valid memory ranges found!\n");
242
243 /* If we have an external FDT, it can only have come from the Android bootloader. */
244 if (external_valid)
245 qcom_boot_source = QCOM_BOOT_SOURCE_ANDROID;
246 else
247 qcom_boot_source = QCOM_BOOT_SOURCE_XBL;
248
249 debug("ram_base = %#011lx, ram_size = %#011llx\n",
250 gd->ram_base, gd->ram_size);
251
252 if (internal_valid) {
253 debug("Using built in FDT\n");
254 ret = -EEXIST;
255 } else {
256 debug("Using external FDT\n");
257 *fdtp = external_fdt;
258 ret = 0;
259 }
260
261 qcom_psci_fixup(*fdtp);
262
263 return ret;
264 }
265
266 /*
267 * Some Qualcomm boards require GPIO configuration when switching USB modes.
268 * Support setting this configuration via pinctrl state.
269 */
board_usb_init(int index,enum usb_init_type init)270 int board_usb_init(int index, enum usb_init_type init)
271 {
272 struct udevice *usb;
273 int ret = 0;
274
275 /* USB device */
276 ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
277 if (ret) {
278 printf("Cannot find USB device\n");
279 return ret;
280 }
281
282 ret = dev_read_stringlist_search(usb, "pinctrl-names",
283 "device");
284 /* No "device" pinctrl state, so just bail */
285 if (ret < 0)
286 return 0;
287
288 /* Select "default" or "device" pinctrl */
289 switch (init) {
290 case USB_INIT_HOST:
291 pinctrl_select_state(usb, "default");
292 break;
293 case USB_INIT_DEVICE:
294 pinctrl_select_state(usb, "device");
295 break;
296 default:
297 debug("Unknown usb_init_type %d\n", init);
298 break;
299 }
300
301 return 0;
302 }
303
304 /*
305 * Some boards still need board specific init code, they can implement that by
306 * overriding this function.
307 *
308 * FIXME: get rid of board specific init code
309 */
qcom_board_init(void)310 void __weak qcom_board_init(void)
311 {
312 }
313
board_init(void)314 int board_init(void)
315 {
316 show_psci_version();
317 qcom_board_init();
318 return 0;
319 }
320
321 /**
322 * out_len includes the trailing null space
323 */
get_cmdline_option(const char * cmdline,const char * key,char * out,int out_len)324 static int get_cmdline_option(const char *cmdline, const char *key, char *out, int out_len)
325 {
326 const char *p, *p_end;
327 int len;
328
329 p = strstr(cmdline, key);
330 if (!p)
331 return -ENOENT;
332
333 p += strlen(key);
334 p_end = strstr(p, " ");
335 if (!p_end)
336 return -ENOENT;
337
338 len = p_end - p;
339 if (len > out_len)
340 len = out_len;
341
342 strncpy(out, p, len);
343 out[len] = '\0';
344
345 return 0;
346 }
347
348 /* The bootargs are populated by the previous stage bootloader */
get_cmdline(void)349 static const char *get_cmdline(void)
350 {
351 ofnode node;
352 static const char *cmdline = NULL;
353
354 if (cmdline)
355 return cmdline;
356
357 node = ofnode_path("/chosen");
358 if (!ofnode_valid(node))
359 return NULL;
360
361 cmdline = ofnode_read_string(node, "bootargs");
362
363 return cmdline;
364 }
365
qcom_set_serialno(void)366 void qcom_set_serialno(void)
367 {
368 const char *cmdline = get_cmdline();
369 char serial[32];
370
371 if (!cmdline) {
372 log_debug("Failed to get bootargs\n");
373 return;
374 }
375
376 get_cmdline_option(cmdline, "androidboot.serialno=", serial, sizeof(serial));
377 if (serial[0] != '\0')
378 env_set("serial#", serial);
379 }
380
381 /* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
382 * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
383 * comprehensive solution to automatically picking the DTB, but aims to be correct for the
384 * majority case. For most devices it should be possible to make this algorithm work by
385 * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
386 * variants that are all supported by a single U-Boot image will require implementing device-
387 * specific detection.
388 */
configure_env(void)389 static void configure_env(void)
390 {
391 const char *first_compat, *last_compat;
392 char *tmp;
393 char buf[32] = { 0 };
394 /*
395 * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
396 * The vendor is skipped when it's a Qualcomm reference board, or the
397 * db845c.
398 */
399 char dt_path[64] = { 0 };
400 int compat_count, ret;
401 ofnode root;
402
403 root = ofnode_root();
404 /* This is almost always 2, but be explicit that we want the first and last compatibles
405 * not the first and second.
406 */
407 compat_count = ofnode_read_string_count(root, "compatible");
408 if (compat_count < 2) {
409 log_warning("%s: only one root compatible bailing!\n", __func__);
410 return;
411 }
412
413 /* The most specific device compatible (e.g. "thundercomm,db845c") */
414 ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
415 if (ret < 0) {
416 log_warning("Can't read first compatible\n");
417 return;
418 }
419
420 strlcpy(buf, first_compat, sizeof(buf) - 1);
421 tmp = buf;
422
423 /* The Qualcomm reference boards (RBx, HDK, etc) */
424 if (!strncmp("qcom", buf, strlen("qcom"))) {
425 char *soc;
426
427 /*
428 * They all have the first compatible as "qcom,<soc>-<board>"
429 * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
430 * the dash.
431 */
432 if (!strsep(&tmp, ",")) {
433 log_warning("compatible '%s' has no ','\n", buf);
434 return;
435 }
436 soc = strsep(&tmp, "-");
437 if (!soc) {
438 log_warning("compatible '%s' has no '-'\n", buf);
439 return;
440 }
441
442 env_set("soc", soc);
443 env_set("board", tmp);
444 } else {
445 if (!strsep(&tmp, ",")) {
446 log_warning("compatible '%s' has no ','\n", buf);
447 return;
448 }
449 /*
450 * For thundercomm we just want the bit after the comma
451 * (e.g. "db845c"), for all other boards we replace the comma
452 * with a '-' and take both (e.g. "oneplus-enchilada")
453 */
454 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
455 env_set("board", tmp);
456 } else {
457 *(tmp - 1) = '-';
458 env_set("board", buf);
459 }
460
461 /* The last compatible is always the SoC compatible */
462 ret = ofnode_read_string_index(root, "compatible",
463 compat_count - 1, &last_compat);
464 if (ret < 0) {
465 log_warning("Can't read second compatible\n");
466 return;
467 }
468
469 /* Copy the last compat (e.g. "qcom,sdm845") into buf */
470 memset(buf, 0, sizeof(buf));
471 strlcpy(buf, last_compat, sizeof(buf) - 1);
472 tmp = buf;
473
474 /* strsep() is destructive, it replaces the comma with a \0 */
475 if (!strsep(&tmp, ",")) {
476 log_warning("second compatible '%s' has no ','\n", buf);
477 return;
478 }
479
480 /* tmp now points to just the "sdm845" part of the string */
481 env_set("soc", tmp);
482 }
483
484 /* Now build the full path name */
485 snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
486 env_get("soc"), env_get("board"));
487 env_set("fdtfile", dt_path);
488
489 qcom_set_serialno();
490 }
491
qcom_show_boot_source(void)492 void qcom_show_boot_source(void)
493 {
494 const char *name = "UNKNOWN";
495
496 switch (qcom_boot_source) {
497 case QCOM_BOOT_SOURCE_ANDROID:
498 name = "ABL";
499 break;
500 case QCOM_BOOT_SOURCE_XBL:
501 name = "XBL";
502 break;
503 }
504
505 log_info("U-Boot loaded from %s\n", name);
506 env_set("boot_source", name);
507 }
508
qcom_late_init(void)509 void __weak qcom_late_init(void)
510 {
511 }
512
513 #define KERNEL_COMP_SIZE SZ_64M
514 #ifdef CONFIG_FASTBOOT_BUF_SIZE
515 #define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
516 #else
517 #define FASTBOOT_BUF_SIZE 0
518 #endif
519
520 #define lmb_alloc(size, addr) lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, addr, size, LMB_NONE)
521
522 /* Stolen from arch/arm/mach-apple/board.c */
board_late_init(void)523 int board_late_init(void)
524 {
525 u32 status = 0, fdt_status = 0;
526 phys_addr_t addr;
527 struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob;
528
529 /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
530 status |= !lmb_alloc(SZ_128M, &addr) ?
531 env_set_hex("loadaddr", addr) : 1;
532 status |= env_set_hex("kernel_addr_r", addr);
533 status |= !lmb_alloc(SZ_128M, &addr) ?
534 env_set_hex("ramdisk_addr_r", addr) : 1;
535 status |= !lmb_alloc(KERNEL_COMP_SIZE, &addr) ?
536 env_set_hex("kernel_comp_addr_r", addr) : 1;
537 status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
538 status |= !lmb_alloc(SZ_4M, &addr) ?
539 env_set_hex("scriptaddr", addr) : 1;
540 status |= !lmb_alloc(SZ_4M, &addr) ?
541 env_set_hex("pxefile_addr_r", addr) : 1;
542
543 if (IS_ENABLED(CONFIG_FASTBOOT)) {
544 status |= !lmb_alloc(FASTBOOT_BUF_SIZE, &addr) ?
545 env_set_hex("fastboot_addr_r", addr) : 1;
546 /*
547 * Override loadaddr for memory rich soc since ${loadaddr} and
548 * ${kernel_addr_r} need to be different for the Android boot image
549 * flow. It's typically safe for ${loadaddr} to be the same address
550 * as the fastboot buffer.
551 */
552 status |= env_set_hex("loadaddr", addr);
553 }
554
555 fdt_status |= !lmb_alloc(SZ_2M, &addr) ?
556 env_set_hex("fdt_addr_r", addr) : 1;
557
558 if (status || fdt_status)
559 log_warning("%s: Failed to set run time variables\n", __func__);
560
561 /* By default copy U-Boots FDT, it will be used as a fallback */
562 if (fdt_status)
563 log_warning("%s: Failed to reserve memory for copying FDT\n",
564 __func__);
565 else
566 memcpy((void *)addr, (void *)gd->fdt_blob,
567 fdt32_to_cpu(fdt_blob->totalsize));
568
569 configure_env();
570 qcom_late_init();
571
572 qcom_show_boot_source();
573 /* Configure the dfu_string for capsule updates */
574 qcom_configure_capsule_updates();
575
576 return 0;
577 }
578
build_mem_map(void)579 static void build_mem_map(void)
580 {
581 int i, j;
582
583 /*
584 * Ensure the peripheral block is sized to correctly cover the address range
585 * up to the first memory bank.
586 * Don't map the first page to ensure that we actually trigger an abort on a
587 * null pointer access rather than just hanging.
588 * FIXME: we should probably split this into more precise regions
589 */
590 mem_map[0].phys = 0x1000;
591 mem_map[0].virt = mem_map[0].phys;
592 mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
593 mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
594 PTE_BLOCK_NON_SHARE |
595 PTE_BLOCK_PXN | PTE_BLOCK_UXN;
596
597 for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
598 mem_map[i].phys = gd->bd->bi_dram[j].start;
599 mem_map[i].virt = mem_map[i].phys;
600 mem_map[i].size = gd->bd->bi_dram[j].size;
601 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
602 PTE_BLOCK_INNER_SHARE;
603 }
604
605 mem_map[i].phys = UINT64_MAX;
606 mem_map[i].size = 0;
607
608 #ifdef DEBUG
609 debug("Configured memory map:\n");
610 for (i = 0; mem_map[i].size; i++)
611 debug(" 0x%016llx - 0x%016llx: entry %d\n",
612 mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
613 #endif
614 }
615
get_page_table_size(void)616 u64 get_page_table_size(void)
617 {
618 return SZ_1M;
619 }
620
fdt_cmp_res(const void * v1,const void * v2)621 static int fdt_cmp_res(const void *v1, const void *v2)
622 {
623 const struct fdt_resource *res1 = v1, *res2 = v2;
624
625 return res1->start - res2->start;
626 }
627
628 #define N_RESERVED_REGIONS 32
629
630 /* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
631 * On some platforms this is enough to trigger a security violation and trap
632 * to EL3.
633 */
carve_out_reserved_memory(void)634 static void carve_out_reserved_memory(void)
635 {
636 static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
637 int parent, rmem, count, i = 0;
638 phys_addr_t start;
639 size_t size;
640
641 /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
642 * attempt to access them, causing a security exception.
643 */
644 parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
645 if (parent <= 0) {
646 log_err("No reserved memory regions found\n");
647 return;
648 }
649
650 /* Collect the reserved memory regions */
651 fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
652 const fdt32_t *ptr;
653 int len;
654 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
655 continue;
656
657 if (i == N_RESERVED_REGIONS) {
658 log_err("Too many reserved regions!\n");
659 break;
660 }
661
662 /* Read the address and size out from the reg property. Doing this "properly" with
663 * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
664 * takes <1ms... Oh the woes of no dcache.
665 */
666 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
667 if (ptr) {
668 /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
669 * the 32-bit address space. So we can cheat here for speed.
670 */
671 res[i].start = fdt32_to_cpu(ptr[1]);
672 res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
673 i++;
674 }
675 }
676
677 /* Sort the reserved memory regions by address */
678 count = i;
679 qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
680
681 /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
682 * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
683 * regions.
684 */
685 start = ALIGN_DOWN(res[0].start, SZ_2M);
686 size = ALIGN(res[0].end - start, SZ_2M);
687 for (i = 1; i <= count; i++) {
688 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
689 * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
690 * compatible properties).
691 * If within 2M of the previous region, bump the size to include this region. Otherwise
692 * start a new region.
693 */
694 if (i == count || start + size < res[i].start - SZ_2M) {
695 debug(" 0x%016llx - 0x%016llx: reserved\n",
696 start, start + size);
697 mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
698 /* If this is the final region then quit here before we index
699 * out of bounds...
700 */
701 if (i == count)
702 break;
703 start = ALIGN_DOWN(res[i].start, SZ_2M);
704 size = ALIGN(res[i].end - start, SZ_2M);
705 } else {
706 /* Bump size if this region is immediately after the previous one */
707 size = ALIGN(res[i].end - start, SZ_2M);
708 }
709 }
710 }
711
712 /* This function open-codes setup_all_pgtables() so that we can
713 * insert additional mappings *before* turning on the MMU.
714 */
enable_caches(void)715 void enable_caches(void)
716 {
717 u64 tlb_addr = gd->arch.tlb_addr;
718 u64 tlb_size = gd->arch.tlb_size;
719 u64 pt_size;
720 ulong carveout_start;
721
722 gd->arch.tlb_fillptr = tlb_addr;
723
724 build_mem_map();
725
726 icache_enable();
727
728 /* Create normal system page tables */
729 setup_pgtables();
730
731 pt_size = (uintptr_t)gd->arch.tlb_fillptr -
732 (uintptr_t)gd->arch.tlb_addr;
733 debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
734
735 /* Create emergency page tables */
736 gd->arch.tlb_size -= pt_size;
737 gd->arch.tlb_addr = gd->arch.tlb_fillptr;
738 setup_pgtables();
739 gd->arch.tlb_emerg = gd->arch.tlb_addr;
740 gd->arch.tlb_addr = tlb_addr;
741 gd->arch.tlb_size = tlb_size;
742
743 /* We do the carveouts only for QCS404, for now. */
744 if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
745 carveout_start = get_timer(0);
746 /* Takes ~20-50ms on SDM845 */
747 carve_out_reserved_memory();
748 debug("carveout time: %lums\n", get_timer(carveout_start));
749 }
750 dcache_enable();
751 }
752