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