blob: 5547d6d054f2cdb7081f5dfcb78f9e27c58c268b [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
Sam Day72546b02025-01-23 12:12:14 +000091/**
92 * The generic memory parsing code in U-Boot lacks a few things that we
93 * need on Qualcomm:
94 *
95 * 1. It sets gd->ram_size and gd->ram_base to represent a single memory block
96 * 2. setup_dest_addr() later relocates U-Boot to ram_base + ram_size, the end
97 * of that first memory block.
98 *
99 * This results in all memory beyond U-Boot being unusable in Linux when booting
100 * with EFI.
101 *
102 * Since the ranges in the memory node may be out of order, the only way for us
103 * to correctly determine the relocation address for U-Boot is to parse all
104 * memory regions and find the highest valid address.
105 *
106 * We can't use fdtdec_setup_memory_banksize() since it stores the result in
107 * gd->bd, which is not yet allocated.
108 *
109 * @fdt: FDT blob to parse /memory node from
110 *
111 * Return: 0 on success or -ENODATA if /memory node is missing or incomplete
112 */
113static int qcom_parse_memory(const void *fdt)
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200114{
Sam Day798847f2025-01-22 10:26:59 +0000115 int offset;
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200116 const fdt64_t *memory;
117 int memsize;
118 phys_addr_t ram_end = 0;
119 int i, j, banks;
120
Sam Day798847f2025-01-22 10:26:59 +0000121 offset = fdt_path_offset(fdt, "/memory");
Sam Day72546b02025-01-23 12:12:14 +0000122 if (offset < 0)
123 return -ENODATA;
Sam Day798847f2025-01-22 10:26:59 +0000124
125 memory = fdt_getprop(fdt, offset, "reg", &memsize);
Sam Day72546b02025-01-23 12:12:14 +0000126 if (!memory)
127 return -ENODATA;
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200128
129 banks = min(memsize / (2 * sizeof(u64)), (ulong)CONFIG_NR_DRAM_BANKS);
130
131 if (memsize / sizeof(u64) > CONFIG_NR_DRAM_BANKS * 2)
132 log_err("Provided more than the max of %d memory banks\n", CONFIG_NR_DRAM_BANKS);
133
134 if (banks > CONFIG_NR_DRAM_BANKS)
135 log_err("Provided more memory banks than we can handle\n");
136
137 for (i = 0, j = 0; i < banks * 2; i += 2, j++) {
138 prevbl_ddr_banks[j].start = get_unaligned_be64(&memory[i]);
139 prevbl_ddr_banks[j].size = get_unaligned_be64(&memory[i + 1]);
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200140 if (!prevbl_ddr_banks[j].size) {
141 j--;
142 continue;
143 }
144 ram_end = max(ram_end, prevbl_ddr_banks[j].start + prevbl_ddr_banks[j].size);
145 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000146
Sam Day72546b02025-01-23 12:12:14 +0000147 if (!banks || !prevbl_ddr_banks[0].size)
148 return -ENODATA;
149
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000150 /* Sort our RAM banks -_- */
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200151 qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000152
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200153 gd->ram_base = prevbl_ddr_banks[0].start;
154 gd->ram_size = ram_end - gd->ram_base;
Sam Day72546b02025-01-23 12:12:14 +0000155
156 return 0;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000157}
158
159static void show_psci_version(void)
160{
161 struct arm_smccc_res res;
162
163 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
164
Sam Daycc789db2025-01-27 14:48:55 +0000165 /* Some older SoCs like MSM8916 don't always support PSCI */
166 if ((int)res.a0 == PSCI_RET_NOT_SUPPORTED)
167 return;
168
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000169 debug("PSCI: v%ld.%ld\n",
170 PSCI_VERSION_MAJOR(res.a0),
171 PSCI_VERSION_MINOR(res.a0));
172}
173
Sam Daycc789db2025-01-27 14:48:55 +0000174/**
175 * Most MSM8916 devices in the wild shipped without PSCI support, but the
176 * upstream DTs pretend that PSCI exists. If that situation is detected here,
177 * the /psci node is deleted. This is done very early to ensure the PSCI
178 * firmware driver doesn't bind (which then binds a sysreset driver that won't
179 * work).
180 */
181static void qcom_psci_fixup(void *fdt)
182{
183 int offset, ret;
184 struct arm_smccc_res res;
185
186 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
187
188 if ((int)res.a0 != PSCI_RET_NOT_SUPPORTED)
189 return;
190
191 offset = fdt_path_offset(fdt, "/psci");
192 if (offset < 0)
193 return;
194
195 debug("Found /psci DT node on device with no PSCI. Deleting.\n");
196 ret = fdt_del_node(fdt, offset);
197 if (ret)
198 log_err("Failed to delete /psci node: %d\n", ret);
199}
200
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200201/* We support booting U-Boot with an internal DT when running as a first-stage bootloader
202 * or for supporting quirky devices where it's easier to leave the downstream DT in place
203 * to improve ABL compatibility. Otherwise, we use the DT provided by ABL.
204 */
Simon Glass94086b22024-11-02 11:49:42 -0600205int board_fdt_blob_setup(void **fdtp)
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000206{
Sam Day72546b02025-01-23 12:12:14 +0000207 struct fdt_header *external_fdt, *internal_fdt;
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200208 bool internal_valid, external_valid;
Sam Day72546b02025-01-23 12:12:14 +0000209 int ret = -ENODATA;
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200210
Sam Day72546b02025-01-23 12:12:14 +0000211 internal_fdt = (struct fdt_header *)*fdtp;
212 external_fdt = (struct fdt_header *)get_prev_bl_fdt_addr();
213 external_valid = external_fdt && !fdt_check_header(external_fdt);
214 internal_valid = !fdt_check_header(internal_fdt);
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000215
216 /*
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200217 * There is no point returning an error here, U-Boot can't do anything useful in this situation.
218 * Bail out while we can still print a useful error message.
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000219 */
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200220 if (!internal_valid && !external_valid)
221 panic("Internal FDT is invalid and no external FDT was provided! (fdt=%#llx)\n",
Sam Day72546b02025-01-23 12:12:14 +0000222 (phys_addr_t)external_fdt);
223
224 /* Prefer memory information from internal DT if it's present */
225 if (internal_valid)
226 ret = qcom_parse_memory(internal_fdt);
227
228 if (ret < 0 && external_valid) {
229 /* No internal FDT or it lacks a proper /memory node.
230 * The previous bootloader handed us something, let's try that.
231 */
232 if (internal_valid)
233 debug("No memory info in internal FDT, falling back to external\n");
234
235 ret = qcom_parse_memory(external_fdt);
236 }
237
238 if (ret < 0)
239 panic("No valid memory ranges found!\n");
240
241 debug("ram_base = %#011lx, ram_size = %#011llx\n",
242 gd->ram_base, gd->ram_size);
Volodymyr Babchukbdacfea2024-03-11 21:33:45 +0000243
Caleb Connollyc0cd2942024-08-09 01:59:24 +0200244 if (internal_valid) {
245 debug("Using built in FDT\n");
Sam Daycc789db2025-01-27 14:48:55 +0000246 ret = -EEXIST;
247 } else {
248 debug("Using external FDT\n");
249 *fdtp = external_fdt;
250 ret = 0;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000251 }
Caleb Connollye83b1cd2024-08-09 01:59:25 +0200252
Sam Daycc789db2025-01-27 14:48:55 +0000253 qcom_psci_fixup(*fdtp);
254
255 return ret;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000256}
257
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000258/*
259 * Some Qualcomm boards require GPIO configuration when switching USB modes.
260 * Support setting this configuration via pinctrl state.
261 */
262int board_usb_init(int index, enum usb_init_type init)
263{
264 struct udevice *usb;
265 int ret = 0;
266
267 /* USB device */
268 ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
269 if (ret) {
270 printf("Cannot find USB device\n");
271 return ret;
272 }
273
274 ret = dev_read_stringlist_search(usb, "pinctrl-names",
275 "device");
276 /* No "device" pinctrl state, so just bail */
277 if (ret < 0)
278 return 0;
279
280 /* Select "default" or "device" pinctrl */
281 switch (init) {
282 case USB_INIT_HOST:
283 pinctrl_select_state(usb, "default");
284 break;
285 case USB_INIT_DEVICE:
286 pinctrl_select_state(usb, "device");
287 break;
288 default:
289 debug("Unknown usb_init_type %d\n", init);
290 break;
291 }
292
293 return 0;
294}
295
296/*
297 * Some boards still need board specific init code, they can implement that by
298 * overriding this function.
299 *
300 * FIXME: get rid of board specific init code
301 */
302void __weak qcom_board_init(void)
303{
304}
305
306int board_init(void)
307{
308 show_psci_version();
309 qcom_board_init();
310 return 0;
311}
312
Caleb Connolly750a0fa2024-08-09 01:59:27 +0200313/**
314 * out_len includes the trailing null space
315 */
316static int get_cmdline_option(const char *cmdline, const char *key, char *out, int out_len)
317{
318 const char *p, *p_end;
319 int len;
320
321 p = strstr(cmdline, key);
322 if (!p)
323 return -ENOENT;
324
325 p += strlen(key);
326 p_end = strstr(p, " ");
327 if (!p_end)
328 return -ENOENT;
329
330 len = p_end - p;
331 if (len > out_len)
332 len = out_len;
333
334 strncpy(out, p, len);
335 out[len] = '\0';
336
337 return 0;
338}
339
340/* The bootargs are populated by the previous stage bootloader */
341static const char *get_cmdline(void)
342{
343 ofnode node;
344 static const char *cmdline = NULL;
345
346 if (cmdline)
347 return cmdline;
348
349 node = ofnode_path("/chosen");
350 if (!ofnode_valid(node))
351 return NULL;
352
353 cmdline = ofnode_read_string(node, "bootargs");
354
355 return cmdline;
356}
357
358void qcom_set_serialno(void)
359{
360 const char *cmdline = get_cmdline();
361 char serial[32];
362
363 if (!cmdline) {
364 log_debug("Failed to get bootargs\n");
365 return;
366 }
367
368 get_cmdline_option(cmdline, "androidboot.serialno=", serial, sizeof(serial));
369 if (serial[0] != '\0')
370 env_set("serial#", serial);
371}
372
Caleb Connollya0156902024-02-26 17:26:26 +0000373/* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
374 * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
375 * comprehensive solution to automatically picking the DTB, but aims to be correct for the
376 * majority case. For most devices it should be possible to make this algorithm work by
377 * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
378 * variants that are all supported by a single U-Boot image will require implementing device-
379 * specific detection.
380 */
381static void configure_env(void)
382{
383 const char *first_compat, *last_compat;
384 char *tmp;
385 char buf[32] = { 0 };
386 /*
387 * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
388 * The vendor is skipped when it's a Qualcomm reference board, or the
389 * db845c.
390 */
391 char dt_path[64] = { 0 };
392 int compat_count, ret;
393 ofnode root;
394
395 root = ofnode_root();
396 /* This is almost always 2, but be explicit that we want the first and last compatibles
397 * not the first and second.
398 */
399 compat_count = ofnode_read_string_count(root, "compatible");
400 if (compat_count < 2) {
401 log_warning("%s: only one root compatible bailing!\n", __func__);
402 return;
403 }
404
405 /* The most specific device compatible (e.g. "thundercomm,db845c") */
406 ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
407 if (ret < 0) {
408 log_warning("Can't read first compatible\n");
409 return;
410 }
411
Caleb Connollya0156902024-02-26 17:26:26 +0000412 strlcpy(buf, first_compat, sizeof(buf) - 1);
413 tmp = buf;
414
415 /* The Qualcomm reference boards (RBx, HDK, etc) */
416 if (!strncmp("qcom", buf, strlen("qcom"))) {
Sumit Gargfdd436b2025-05-05 18:13:33 +0530417 char *soc;
418
Caleb Connollya0156902024-02-26 17:26:26 +0000419 /*
420 * They all have the first compatible as "qcom,<soc>-<board>"
421 * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
422 * the dash.
423 */
Sumit Gargfdd436b2025-05-05 18:13:33 +0530424 if (!strsep(&tmp, ",")) {
425 log_warning("compatible '%s' has no ','\n", buf);
426 return;
427 }
428 soc = strsep(&tmp, "-");
429 if (!soc) {
Caleb Connollya0156902024-02-26 17:26:26 +0000430 log_warning("compatible '%s' has no '-'\n", buf);
431 return;
432 }
Sumit Gargfdd436b2025-05-05 18:13:33 +0530433
434 env_set("soc", soc);
Caleb Connollya0156902024-02-26 17:26:26 +0000435 env_set("board", tmp);
436 } else {
437 if (!strsep(&tmp, ",")) {
438 log_warning("compatible '%s' has no ','\n", buf);
439 return;
440 }
Sumit Gargfdd436b2025-05-05 18:13:33 +0530441 /*
442 * For thundercomm we just want the bit after the comma
443 * (e.g. "db845c"), for all other boards we replace the comma
444 * with a '-' and take both (e.g. "oneplus-enchilada")
Caleb Connollya0156902024-02-26 17:26:26 +0000445 */
446 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
447 env_set("board", tmp);
448 } else {
449 *(tmp - 1) = '-';
450 env_set("board", buf);
451 }
Sumit Gargfdd436b2025-05-05 18:13:33 +0530452
453 /* The last compatible is always the SoC compatible */
454 ret = ofnode_read_string_index(root, "compatible",
455 compat_count - 1, &last_compat);
456 if (ret < 0) {
457 log_warning("Can't read second compatible\n");
458 return;
459 }
460
461 /* Copy the last compat (e.g. "qcom,sdm845") into buf */
462 memset(buf, 0, sizeof(buf));
463 strlcpy(buf, last_compat, sizeof(buf) - 1);
464 tmp = buf;
465
466 /* strsep() is destructive, it replaces the comma with a \0 */
467 if (!strsep(&tmp, ",")) {
468 log_warning("second compatible '%s' has no ','\n", buf);
469 return;
470 }
471
472 /* tmp now points to just the "sdm845" part of the string */
473 env_set("soc", tmp);
Caleb Connollya0156902024-02-26 17:26:26 +0000474 }
475
476 /* Now build the full path name */
477 snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
478 env_get("soc"), env_get("board"));
479 env_set("fdtfile", dt_path);
Caleb Connolly750a0fa2024-08-09 01:59:27 +0200480
481 qcom_set_serialno();
Caleb Connollya0156902024-02-26 17:26:26 +0000482}
483
Caleb Connolly31385662024-02-26 17:26:25 +0000484void __weak qcom_late_init(void)
485{
486}
487
488#define KERNEL_COMP_SIZE SZ_64M
Caleb Connolly94a50842024-08-09 01:59:28 +0200489#ifdef CONFIG_FASTBOOT_BUF_SIZE
490#define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
491#else
492#define FASTBOOT_BUF_SIZE 0
493#endif
Caleb Connolly31385662024-02-26 17:26:25 +0000494
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530495#define addr_alloc(size) lmb_alloc(size, SZ_2M)
Caleb Connolly31385662024-02-26 17:26:25 +0000496
497/* Stolen from arch/arm/mach-apple/board.c */
498int board_late_init(void)
499{
Caleb Connolly31385662024-02-26 17:26:25 +0000500 u32 status = 0;
Caleb Connolly39288732024-08-09 01:59:30 +0200501 phys_addr_t addr;
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200502 struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob;
Caleb Connolly31385662024-02-26 17:26:25 +0000503
Caleb Connolly31385662024-02-26 17:26:25 +0000504 /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
Caleb Connolly39288732024-08-09 01:59:30 +0200505 addr = addr_alloc(SZ_128M);
506 status |= env_set_hex("kernel_addr_r", addr);
507 status |= env_set_hex("loadaddr", addr);
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530508 status |= env_set_hex("ramdisk_addr_r", addr_alloc(SZ_128M));
509 status |= env_set_hex("kernel_comp_addr_r", addr_alloc(KERNEL_COMP_SIZE));
Caleb Connolly31385662024-02-26 17:26:25 +0000510 status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
Caleb Connolly94a50842024-08-09 01:59:28 +0200511 if (IS_ENABLED(CONFIG_FASTBOOT))
512 status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE));
Sughosh Ganu291bf9c2024-08-26 17:29:18 +0530513 status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M));
514 status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M));
Caleb Connolly39288732024-08-09 01:59:30 +0200515 addr = addr_alloc(SZ_2M);
516 status |= env_set_hex("fdt_addr_r", addr);
Caleb Connolly31385662024-02-26 17:26:25 +0000517
518 if (status)
519 log_warning("%s: Failed to set run time variables\n", __func__);
520
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200521 /* By default copy U-Boots FDT, it will be used as a fallback */
Caleb Connolly39288732024-08-09 01:59:30 +0200522 memcpy((void *)addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize));
Caleb Connollyeee2b6f2024-08-09 01:59:29 +0200523
Caleb Connollya0156902024-02-26 17:26:26 +0000524 configure_env();
Caleb Connolly31385662024-02-26 17:26:25 +0000525 qcom_late_init();
526
Caleb Connolly2bdb2522024-10-12 15:57:19 +0200527 /* Configure the dfu_string for capsule updates */
528 qcom_configure_capsule_updates();
529
Caleb Connolly31385662024-02-26 17:26:25 +0000530 return 0;
531}
532
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000533static void build_mem_map(void)
534{
Caleb Connolly81982462024-02-26 17:26:27 +0000535 int i, j;
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000536
537 /*
538 * Ensure the peripheral block is sized to correctly cover the address range
539 * up to the first memory bank.
540 * Don't map the first page to ensure that we actually trigger an abort on a
541 * null pointer access rather than just hanging.
542 * FIXME: we should probably split this into more precise regions
543 */
544 mem_map[0].phys = 0x1000;
545 mem_map[0].virt = mem_map[0].phys;
546 mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
547 mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
548 PTE_BLOCK_NON_SHARE |
549 PTE_BLOCK_PXN | PTE_BLOCK_UXN;
550
Caleb Connolly81982462024-02-26 17:26:27 +0000551 for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
552 mem_map[i].phys = gd->bd->bi_dram[j].start;
553 mem_map[i].virt = mem_map[i].phys;
554 mem_map[i].size = gd->bd->bi_dram[j].size;
555 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
556 PTE_BLOCK_INNER_SHARE;
557 }
558
559 mem_map[i].phys = UINT64_MAX;
560 mem_map[i].size = 0;
561
562#ifdef DEBUG
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000563 debug("Configured memory map:\n");
Caleb Connolly81982462024-02-26 17:26:27 +0000564 for (i = 0; mem_map[i].size; i++)
565 debug(" 0x%016llx - 0x%016llx: entry %d\n",
566 mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
567#endif
568}
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000569
Caleb Connolly81982462024-02-26 17:26:27 +0000570u64 get_page_table_size(void)
571{
Neil Armstrong4086a1f2024-08-09 01:59:26 +0200572 return SZ_1M;
Caleb Connolly81982462024-02-26 17:26:27 +0000573}
574
575static int fdt_cmp_res(const void *v1, const void *v2)
576{
577 const struct fdt_resource *res1 = v1, *res2 = v2;
578
579 return res1->start - res2->start;
580}
581
582#define N_RESERVED_REGIONS 32
583
584/* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
585 * On some platforms this is enough to trigger a security violation and trap
586 * to EL3.
587 */
588static void carve_out_reserved_memory(void)
589{
590 static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
591 int parent, rmem, count, i = 0;
592 phys_addr_t start;
593 size_t size;
594
595 /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
596 * attempt to access them, causing a security exception.
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000597 */
Caleb Connolly81982462024-02-26 17:26:27 +0000598 parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
599 if (parent <= 0) {
600 log_err("No reserved memory regions found\n");
601 return;
602 }
603
604 /* Collect the reserved memory regions */
605 fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
606 const fdt32_t *ptr;
607 int len;
608 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
609 continue;
610
611 if (i == N_RESERVED_REGIONS) {
612 log_err("Too many reserved regions!\n");
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000613 break;
614 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000615
Caleb Connolly81982462024-02-26 17:26:27 +0000616 /* Read the address and size out from the reg property. Doing this "properly" with
617 * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
618 * takes <1ms... Oh the woes of no dcache.
619 */
620 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
621 if (ptr) {
622 /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
623 * the 32-bit address space. So we can cheat here for speed.
624 */
625 res[i].start = fdt32_to_cpu(ptr[1]);
626 res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
627 i++;
628 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000629 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000630
Caleb Connolly81982462024-02-26 17:26:27 +0000631 /* Sort the reserved memory regions by address */
632 count = i;
633 qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
634
635 /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
636 * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
637 * regions.
638 */
639 start = ALIGN_DOWN(res[0].start, SZ_2M);
640 size = ALIGN(res[0].end - start, SZ_2M);
641 for (i = 1; i <= count; i++) {
642 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
643 * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
644 * compatible properties).
645 * If within 2M of the previous region, bump the size to include this region. Otherwise
646 * start a new region.
647 */
648 if (i == count || start + size < res[i].start - SZ_2M) {
649 debug(" 0x%016llx - 0x%016llx: reserved\n",
650 start, start + size);
651 mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
652 /* If this is the final region then quit here before we index
653 * out of bounds...
654 */
655 if (i == count)
656 break;
657 start = ALIGN_DOWN(res[i].start, SZ_2M);
658 size = ALIGN(res[i].end - start, SZ_2M);
659 } else {
660 /* Bump size if this region is immediately after the previous one */
661 size = ALIGN(res[i].end - start, SZ_2M);
662 }
663 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000664}
665
Caleb Connolly81982462024-02-26 17:26:27 +0000666/* This function open-codes setup_all_pgtables() so that we can
667 * insert additional mappings *before* turning on the MMU.
668 */
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000669void enable_caches(void)
670{
Caleb Connolly81982462024-02-26 17:26:27 +0000671 u64 tlb_addr = gd->arch.tlb_addr;
672 u64 tlb_size = gd->arch.tlb_size;
673 u64 pt_size;
674 ulong carveout_start;
675
676 gd->arch.tlb_fillptr = tlb_addr;
677
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000678 build_mem_map();
679
680 icache_enable();
Caleb Connolly81982462024-02-26 17:26:27 +0000681
682 /* Create normal system page tables */
683 setup_pgtables();
684
685 pt_size = (uintptr_t)gd->arch.tlb_fillptr -
686 (uintptr_t)gd->arch.tlb_addr;
687 debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
688
689 /* Create emergency page tables */
690 gd->arch.tlb_size -= pt_size;
691 gd->arch.tlb_addr = gd->arch.tlb_fillptr;
692 setup_pgtables();
693 gd->arch.tlb_emerg = gd->arch.tlb_addr;
694 gd->arch.tlb_addr = tlb_addr;
695 gd->arch.tlb_size = tlb_size;
696
Sam Day46760ba2024-05-07 18:41:23 +0000697 /* We do the carveouts only for QCS404, for now. */
698 if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
699 carveout_start = get_timer(0);
700 /* Takes ~20-50ms on SDM845 */
701 carve_out_reserved_memory();
702 debug("carveout time: %lums\n", get_timer(carveout_start));
703 }
Caleb Connollyfe1694c2024-02-26 17:26:24 +0000704 dcache_enable();
705}