blob: 75a880f093cabc73c3cd56fb61ca103f6b9f3730 [file] [log] [blame]
Caleb Connollyfe1694c2024-02-26 17:26:24 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Common initialisation for Qualcomm Snapdragon boards.
4 *
5 * Copyright (c) 2024 Linaro Ltd.
6 * Author: Caleb Connolly <caleb.connolly@linaro.org>
7 */
8
Caleb Connolly3b5faa52024-11-13 06:04:17 +01009#define LOG_CATEGORY LOGC_BOARD
10#define pr_fmt(fmt) "QCOM: " fmt
11
Caleb Connollyfe1694c2024-02-26 17:26:24 +000012#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>
Caleb Connolly5ecc90d2024-04-03 14:07:47 +020021#include <power/regulator.h>
Caleb Connollyfe1694c2024-02-26 17:26:24 +000022#include <env.h>
Caleb Connollyc0cd2942024-08-09 01:59:24 +020023#include <fdt_support.h>
Caleb Connollyfe1694c2024-02-26 17:26:24 +000024#include <init.h>
25#include <linux/arm-smccc.h>
26#include <linux/bug.h>
27#include <linux/psci.h>
28#include <linux/sizes.h>
Caleb Connolly31385662024-02-26 17:26:25 +000029#include <lmb.h>
Caleb Connollyfe1694c2024-02-26 17:26:24 +000030#include <malloc.h>
Volodymyr Babchukbdacfea2024-03-11 21:33:45 +000031#include <fdt_support.h>
Caleb Connollyfe1694c2024-02-26 17:26:24 +000032#include <usb.h>
Caleb Connolly81982462024-02-26 17:26:27 +000033#include <sort.h>
Caleb Connolly3b5faa52024-11-13 06:04:17 +010034#include <time.h>
Caleb Connollyfe1694c2024-02-26 17:26:24 +000035
Caleb Connolly2983ae82024-04-03 14:07:45 +020036#include "qcom-priv.h"
37
Caleb Connollyfe1694c2024-02-26 17:26:24 +000038DECLARE_GLOBAL_DATA_PTR;
39
40static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
41
42struct mm_region *mem_map = rbx_mem_map;
43
Caleb Connollye83b1cd2024-08-09 01:59:25 +020044static struct {
45 phys_addr_t start;
46 phys_size_t size;
47} prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
48
Caleb Connollyfe1694c2024-02-26 17:26:24 +000049int dram_init(void)
50{
Caleb Connollye83b1cd2024-08-09 01:59:25 +020051 /*
52 * gd->ram_base / ram_size have been setup already
53 * in qcom_parse_memory().
54 */
55 return 0;
Caleb Connollyfe1694c2024-02-26 17:26:24 +000056}
57
58static int ddr_bank_cmp(const void *v1, const void *v2)
59{
60 const struct {
61 phys_addr_t start;
62 phys_size_t size;
63 } *res1 = v1, *res2 = v2;
64
65 if (!res1->size)
66 return 1;
67 if (!res2->size)
68 return -1;
69
70 return (res1->start >> 24) - (res2->start >> 24);
71}
72
Caleb Connollye83b1cd2024-08-09 01:59:25 +020073/* This has to be done post-relocation since gd->bd isn't preserved */
74static void qcom_configure_bi_dram(void)
75{
76 int i;
77
78 for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
79 gd->bd->bi_dram[i].start = prevbl_ddr_banks[i].start;
80 gd->bd->bi_dram[i].size = prevbl_ddr_banks[i].size;
81 }
82}
83
Caleb Connollyfe1694c2024-02-26 17:26:24 +000084int dram_init_banksize(void)
85{
Caleb Connollye83b1cd2024-08-09 01:59:25 +020086 qcom_configure_bi_dram();
Caleb Connollyfe1694c2024-02-26 17:26:24 +000087
Caleb Connollye83b1cd2024-08-09 01:59:25 +020088 return 0;
89}
Caleb Connollyfe1694c2024-02-26 17:26:24 +000090
Caleb Connollye83b1cd2024-08-09 01:59:25 +020091static void qcom_parse_memory(void)
92{
93 ofnode node;
94 const fdt64_t *memory;
95 int memsize;
96 phys_addr_t ram_end = 0;
97 int i, j, banks;
98
99 node = ofnode_path("/memory");
100 if (!ofnode_valid(node)) {
101 log_err("No memory node found in device tree!\n");
102 return;
103 }
104 memory = ofnode_read_prop(node, "reg", &memsize);
105 if (!memory) {
106 log_err("No memory configuration was provided by the previous bootloader!\n");
107 return;
108 }
109
110 banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
111
112 if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
113 log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
114
115 if (banks > CONFIG_NR_DRAM_BANKS)
116 log_err("Provided more memory banks than we can handle\n");
117
118 for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
119 prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
120 prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
121 /* SM8650 boards sometimes have empty regions! */
122 if (!prevbl_ddr_banks[j].size) {
123 j--;
124 continue;
125 }
126 ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
127 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000128
129 /* Sort our RAM banks -_- */
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200130 qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000131
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200132 gd->ram_base = prevbl_ddr_banks[0].start;
133 gd->ram_size = ram_end - gd->ram_base;
134 debug("ram_base = %#011lx, ram_size = %#011llx, ram_end = %#011llx\n",
135 gd->ram_base, gd->ram_size, ram_end);
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000136}
137
138static void show_psci_version(void)
139{
140 struct arm_smccc_res res;
141
142 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
143
144 debug("PSCI: v%ld.%ld\n",
145 PSCI_VERSION_MAJOR(res.a0),
146 PSCI_VERSION_MINOR(res.a0));
147}
148
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200149/* We support booting U-Boot with an internal DT when running as a first-stage bootloader
150 * or for supporting quirky devices where it's easier to leave the downstream DT in place
151 * to improve ABL compatibility. Otherwise, we use the DT provided by ABL.
152 */
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000153void *board_fdt_blob_setup(int *err)
154{
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200155 struct fdt_header *fdt;
156 bool internal_valid, external_valid;
157
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000158 *err = 0;
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200159 fdt = (struct fdt_header *)get_prev_bl_fdt_addr();
160 external_valid = fdt && !fdt_check_header(fdt);
161 internal_valid = !fdt_check_header(gd->fdt_blob);
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000162
163 /*
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200164 * There is no point returning an error here, U-Boot can't do anything useful in this situation.
165 * Bail out while we can still print a useful error message.
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000166 */
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200167 if (!internal_valid && !external_valid)
168 panic("Internal FDT is invalid and no external FDT was provided! (fdt=%#llx)\n",
169 (phys_addr_t)fdt);
Volodymyr Babchukbdacfea2024-03-11 21:33:45 +0000170
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200171 if (internal_valid) {
172 debug("Using built in FDT\n");
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200173 } else {
174 debug("Using external FDT\n");
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200175 /* So we can use it before returning */
176 gd->fdt_blob = fdt;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000177 }
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200178
179 /*
180 * Parse the /memory node while we're here,
181 * this makes it easy to do other things early.
182 */
183 qcom_parse_memory();
184
185 return (void *)gd->fdt_blob;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000186}
187
188void reset_cpu(void)
189{
190 psci_system_reset();
191}
192
193/*
194 * Some Qualcomm boards require GPIO configuration when switching USB modes.
195 * Support setting this configuration via pinctrl state.
196 */
197int board_usb_init(int index, enum usb_init_type init)
198{
199 struct udevice *usb;
200 int ret = 0;
201
202 /* USB device */
203 ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
204 if (ret) {
205 printf("Cannot find USB device\n");
206 return ret;
207 }
208
209 ret = dev_read_stringlist_search(usb, "pinctrl-names",
210 "device");
211 /* No "device" pinctrl state, so just bail */
212 if (ret < 0)
213 return 0;
214
215 /* Select "default" or "device" pinctrl */
216 switch (init) {
217 case USB_INIT_HOST:
218 pinctrl_select_state(usb, "default");
219 break;
220 case USB_INIT_DEVICE:
221 pinctrl_select_state(usb, "device");
222 break;
223 default:
224 debug("Unknown usb_init_type %d\n", init);
225 break;
226 }
227
228 return 0;
229}
230
231/*
232 * Some boards still need board specific init code, they can implement that by
233 * overriding this function.
234 *
235 * FIXME: get rid of board specific init code
236 */
237void __weak qcom_board_init(void)
238{
239}
240
241int board_init(void)
242{
243 show_psci_version();
Caleb Connolly2983ae82024-04-03 14:07:45 +0200244 qcom_of_fixup_nodes();
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000245 qcom_board_init();
246 return 0;
247}
248
Caleb Connolly750a0fa2024-08-09 01:59:27 +0200249/**
250 * out_len includes the trailing null space
251 */
252static int get_cmdline_option(const char *cmdline, const char *key, char *out, int out_len)
253{
254 const char *p, *p_end;
255 int len;
256
257 p = strstr(cmdline, key);
258 if (!p)
259 return -ENOENT;
260
261 p += strlen(key);
262 p_end = strstr(p, " ");
263 if (!p_end)
264 return -ENOENT;
265
266 len = p_end - p;
267 if (len > out_len)
268 len = out_len;
269
270 strncpy(out, p, len);
271 out[len] = '\0';
272
273 return 0;
274}
275
276/* The bootargs are populated by the previous stage bootloader */
277static const char *get_cmdline(void)
278{
279 ofnode node;
280 static const char *cmdline = NULL;
281
282 if (cmdline)
283 return cmdline;
284
285 node = ofnode_path("/chosen");
286 if (!ofnode_valid(node))
287 return NULL;
288
289 cmdline = ofnode_read_string(node, "bootargs");
290
291 return cmdline;
292}
293
294void qcom_set_serialno(void)
295{
296 const char *cmdline = get_cmdline();
297 char serial[32];
298
299 if (!cmdline) {
300 log_debug("Failed to get bootargs\n");
301 return;
302 }
303
304 get_cmdline_option(cmdline, "androidboot.serialno=", serial, sizeof(serial));
305 if (serial[0] != '\0')
306 env_set("serial#", serial);
307}
308
Caleb Connollya0156902024-02-26 17:26:26 +0000309/* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
310 * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
311 * comprehensive solution to automatically picking the DTB, but aims to be correct for the
312 * majority case. For most devices it should be possible to make this algorithm work by
313 * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
314 * variants that are all supported by a single U-Boot image will require implementing device-
315 * specific detection.
316 */
317static void configure_env(void)
318{
319 const char *first_compat, *last_compat;
320 char *tmp;
321 char buf[32] = { 0 };
322 /*
323 * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
324 * The vendor is skipped when it's a Qualcomm reference board, or the
325 * db845c.
326 */
327 char dt_path[64] = { 0 };
328 int compat_count, ret;
329 ofnode root;
330
331 root = ofnode_root();
332 /* This is almost always 2, but be explicit that we want the first and last compatibles
333 * not the first and second.
334 */
335 compat_count = ofnode_read_string_count(root, "compatible");
336 if (compat_count < 2) {
337 log_warning("%s: only one root compatible bailing!\n", __func__);
338 return;
339 }
340
341 /* The most specific device compatible (e.g. "thundercomm,db845c") */
342 ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
343 if (ret < 0) {
344 log_warning("Can't read first compatible\n");
345 return;
346 }
347
348 /* The last compatible is always the SoC compatible */
349 ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
350 if (ret < 0) {
351 log_warning("Can't read second compatible\n");
352 return;
353 }
354
355 /* Copy the second compat (e.g. "qcom,sdm845") into buf */
356 strlcpy(buf, last_compat, sizeof(buf) - 1);
357 tmp = buf;
358
359 /* strsep() is destructive, it replaces the comma with a \0 */
360 if (!strsep(&tmp, ",")) {
361 log_warning("second compatible '%s' has no ','\n", buf);
362 return;
363 }
364
365 /* tmp now points to just the "sdm845" part of the string */
366 env_set("soc", tmp);
367
368 /* Now figure out the "board" part from the first compatible */
369 memset(buf, 0, sizeof(buf));
370 strlcpy(buf, first_compat, sizeof(buf) - 1);
371 tmp = buf;
372
373 /* The Qualcomm reference boards (RBx, HDK, etc) */
374 if (!strncmp("qcom", buf, strlen("qcom"))) {
375 /*
376 * They all have the first compatible as "qcom,<soc>-<board>"
377 * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
378 * the dash.
379 */
380 if (!strsep(&tmp, "-")) {
381 log_warning("compatible '%s' has no '-'\n", buf);
382 return;
383 }
384 /* tmp is now "rb5" */
385 env_set("board", tmp);
386 } else {
387 if (!strsep(&tmp, ",")) {
388 log_warning("compatible '%s' has no ','\n", buf);
389 return;
390 }
391 /* for thundercomm we just want the bit after the comma (e.g. "db845c"),
392 * for all other boards we replace the comma with a '-' and take both
393 * (e.g. "oneplus-enchilada")
394 */
395 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
396 env_set("board", tmp);
397 } else {
398 *(tmp - 1) = '-';
399 env_set("board", buf);
400 }
401 }
402
403 /* Now build the full path name */
404 snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
405 env_get("soc"), env_get("board"));
406 env_set("fdtfile", dt_path);
Caleb Connolly750a0fa2024-08-09 01:59:27 +0200407
408 qcom_set_serialno();
Caleb Connollya0156902024-02-26 17:26:26 +0000409}
410
Caleb Connolly31385662024-02-26 17:26:25 +0000411void __weak qcom_late_init(void)
412{
413}
414
415#define KERNEL_COMP_SIZE SZ_64M
Caleb Connolly94a50842024-08-09 01:59:28 +0200416#ifdef CONFIG_FASTBOOT_BUF_SIZE
417#define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
418#else
419#define FASTBOOT_BUF_SIZE 0
420#endif
Caleb Connolly31385662024-02-26 17:26:25 +0000421
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530422#define addr_alloc(size) lmb_alloc(size, SZ_2M)
Caleb Connolly31385662024-02-26 17:26:25 +0000423
424/* Stolen from arch/arm/mach-apple/board.c */
425int board_late_init(void)
426{
Caleb Connolly31385662024-02-26 17:26:25 +0000427 u32 status = 0;
Caleb Connolly39288732024-08-09 01:59:30 +0200428 phys_addr_t addr;
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200429 struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob;
Caleb Connolly31385662024-02-26 17:26:25 +0000430
Caleb Connolly31385662024-02-26 17:26:25 +0000431 /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
Caleb Connolly39288732024-08-09 01:59:30 +0200432 addr = addr_alloc(SZ_128M);
433 status |= env_set_hex("kernel_addr_r", addr);
434 status |= env_set_hex("loadaddr", addr);
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530435 status |= env_set_hex("ramdisk_addr_r", addr_alloc(SZ_128M));
436 status |= env_set_hex("kernel_comp_addr_r", addr_alloc(KERNEL_COMP_SIZE));
Caleb Connolly31385662024-02-26 17:26:25 +0000437 status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
Caleb Connolly94a50842024-08-09 01:59:28 +0200438 if (IS_ENABLED(CONFIG_FASTBOOT))
439 status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE));
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530440 status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M));
441 status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M));
Caleb Connolly39288732024-08-09 01:59:30 +0200442 addr = addr_alloc(SZ_2M);
443 status |= env_set_hex("fdt_addr_r", addr);
Caleb Connolly31385662024-02-26 17:26:25 +0000444
445 if (status)
446 log_warning("%s: Failed to set run time variables\n", __func__);
447
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200448 /* By default copy U-Boots FDT, it will be used as a fallback */
Caleb Connolly39288732024-08-09 01:59:30 +0200449 memcpy((void *)addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize));
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200450
Caleb Connollya0156902024-02-26 17:26:26 +0000451 configure_env();
Caleb Connolly31385662024-02-26 17:26:25 +0000452 qcom_late_init();
453
Caleb Connolly2bdb2522024-10-12 15:57:19 +0200454 /* Configure the dfu_string for capsule updates */
455 qcom_configure_capsule_updates();
456
Caleb Connolly31385662024-02-26 17:26:25 +0000457 return 0;
458}
459
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000460static void build_mem_map(void)
461{
Caleb Connolly81982462024-02-26 17:26:27 +0000462 int i, j;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000463
464 /*
465 * Ensure the peripheral block is sized to correctly cover the address range
466 * up to the first memory bank.
467 * Don't map the first page to ensure that we actually trigger an abort on a
468 * null pointer access rather than just hanging.
469 * FIXME: we should probably split this into more precise regions
470 */
471 mem_map[0].phys = 0x1000;
472 mem_map[0].virt = mem_map[0].phys;
473 mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
474 mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
475 PTE_BLOCK_NON_SHARE |
476 PTE_BLOCK_PXN | PTE_BLOCK_UXN;
477
Caleb Connolly81982462024-02-26 17:26:27 +0000478 for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
479 mem_map[i].phys = gd->bd->bi_dram[j].start;
480 mem_map[i].virt = mem_map[i].phys;
481 mem_map[i].size = gd->bd->bi_dram[j].size;
482 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
483 PTE_BLOCK_INNER_SHARE;
484 }
485
486 mem_map[i].phys = UINT64_MAX;
487 mem_map[i].size = 0;
488
489#ifdef DEBUG
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000490 debug("Configured memory map:\n");
Caleb Connolly81982462024-02-26 17:26:27 +0000491 for (i = 0; mem_map[i].size; i++)
492 debug(" 0x%016llx - 0x%016llx: entry %d\n",
493 mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
494#endif
495}
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000496
Caleb Connolly81982462024-02-26 17:26:27 +0000497u64 get_page_table_size(void)
498{
Neil Armstrong4086a1f2024-08-09 01:59:26 +0200499 return SZ_1M;
Caleb Connolly81982462024-02-26 17:26:27 +0000500}
501
502static int fdt_cmp_res(const void *v1, const void *v2)
503{
504 const struct fdt_resource *res1 = v1, *res2 = v2;
505
506 return res1->start - res2->start;
507}
508
509#define N_RESERVED_REGIONS 32
510
511/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
512 * On some platforms this is enough to trigger a security violation and trap
513 * to EL3.
514 */
515static void carve_out_reserved_memory(void)
516{
517 static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
518 int parent, rmem, count, i = 0;
519 phys_addr_t start;
520 size_t size;
521
522 /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
523 * attempt to access them, causing a security exception.
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000524 */
Caleb Connolly81982462024-02-26 17:26:27 +0000525 parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
526 if (parent <= 0) {
527 log_err("No reserved memory regions found\n");
528 return;
529 }
530
531 /* Collect the reserved memory regions */
532 fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
533 const fdt32_t *ptr;
534 int len;
535 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
536 continue;
537
538 if (i == N_RESERVED_REGIONS) {
539 log_err("Too many reserved regions!\n");
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000540 break;
541 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000542
Caleb Connolly81982462024-02-26 17:26:27 +0000543 /* Read the address and size out from the reg property. Doing this "properly" with
544 * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
545 * takes <1ms... Oh the woes of no dcache.
546 */
547 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
548 if (ptr) {
549 /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
550 * the 32-bit address space. So we can cheat here for speed.
551 */
552 res[i].start = fdt32_to_cpu(ptr[1]);
553 res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
554 i++;
555 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000556 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000557
Caleb Connolly81982462024-02-26 17:26:27 +0000558 /* Sort the reserved memory regions by address */
559 count = i;
560 qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
561
562 /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
563 * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
564 * regions.
565 */
566 start = ALIGN_DOWN(res[0].start, SZ_2M);
567 size = ALIGN(res[0].end - start, SZ_2M);
568 for (i = 1; i <= count; i++) {
569 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
570 * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
571 * compatible properties).
572 * If within 2M of the previous region, bump the size to include this region. Otherwise
573 * start a new region.
574 */
575 if (i == count || start + size < res[i].start - SZ_2M) {
576 debug(" 0x%016llx - 0x%016llx: reserved\n",
577 start, start + size);
578 mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
579 /* If this is the final region then quit here before we index
580 * out of bounds...
581 */
582 if (i == count)
583 break;
584 start = ALIGN_DOWN(res[i].start, SZ_2M);
585 size = ALIGN(res[i].end - start, SZ_2M);
586 } else {
587 /* Bump size if this region is immediately after the previous one */
588 size = ALIGN(res[i].end - start, SZ_2M);
589 }
590 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000591}
592
Caleb Connolly81982462024-02-26 17:26:27 +0000593/* This function open-codes setup_all_pgtables() so that we can
594 * insert additional mappings *before* turning on the MMU.
595 */
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000596void enable_caches(void)
597{
Caleb Connolly81982462024-02-26 17:26:27 +0000598 u64 tlb_addr = gd->arch.tlb_addr;
599 u64 tlb_size = gd->arch.tlb_size;
600 u64 pt_size;
601 ulong carveout_start;
602
603 gd->arch.tlb_fillptr = tlb_addr;
604
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000605 build_mem_map();
606
607 icache_enable();
Caleb Connolly81982462024-02-26 17:26:27 +0000608
609 /* Create normal system page tables */
610 setup_pgtables();
611
612 pt_size = (uintptr_t)gd->arch.tlb_fillptr -
613 (uintptr_t)gd->arch.tlb_addr;
614 debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
615
616 /* Create emergency page tables */
617 gd->arch.tlb_size -= pt_size;
618 gd->arch.tlb_addr = gd->arch.tlb_fillptr;
619 setup_pgtables();
620 gd->arch.tlb_emerg = gd->arch.tlb_addr;
621 gd->arch.tlb_addr = tlb_addr;
622 gd->arch.tlb_size = tlb_size;
623
Sam Day46760ba2024-05-07 18:41:23 +0000624 /* We do the carveouts only for QCS404, for now. */
625 if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
626 carveout_start = get_timer(0);
627 /* Takes ~20-50ms on SDM845 */
628 carve_out_reserved_memory();
629 debug("carveout time: %lums\n", get_timer(carveout_start));
630 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000631 dcache_enable();
632}