1 // SPDX-License-Identifier: GPL-2.0+
3 * Common initialisation for Qualcomm Snapdragon boards.
5 * Copyright (c) 2024 Linaro Ltd.
10 #include <asm/armv8/mmu.h>
14 #include <asm/system.h>
15 #include <dm/device.h>
16 #include <dm/pinctrl.h>
17 #include <dm/uclass-internal.h>
19 #include <power/regulator.h>
22 #include <linux/arm-smccc.h>
23 #include <linux/bug.h>
24 #include <linux/psci.h>
25 #include <linux/sizes.h>
28 #include <fdt_support.h>
32 #include "qcom-priv.h"
34 DECLARE_GLOBAL_DATA_PTR;
36 static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
38 struct mm_region *mem_map = rbx_mem_map;
42 return fdtdec_setup_mem_size_base();
45 static int ddr_bank_cmp(const void *v1, const void *v2)
50 } *res1 = v1, *res2 = v2;
57 return (res1->start >> 24) - (res2->start >> 24);
60 int dram_init_banksize(void)
64 ret = fdtdec_setup_memory_banksize();
68 if (CONFIG_NR_DRAM_BANKS < 2)
71 /* Sort our RAM banks -_- */
72 qsort(gd->bd->bi_dram, CONFIG_NR_DRAM_BANKS, sizeof(gd->bd->bi_dram[0]), ddr_bank_cmp);
77 static void show_psci_version(void)
79 struct arm_smccc_res res;
81 arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
83 debug("PSCI: v%ld.%ld\n",
84 PSCI_VERSION_MAJOR(res.a0),
85 PSCI_VERSION_MINOR(res.a0));
88 void *board_fdt_blob_setup(int *err)
91 /* Return DTB pointer passed by ABL */
93 fdt = get_prev_bl_fdt_addr();
96 * If we bail then the board will simply not boot, instead let's
97 * try and use the FDT built into U-Boot if there is one...
98 * This avoids having a hard dependency on the previous stage bootloader
101 if (IS_ENABLED(CONFIG_OF_SEPARATE) && (!fdt || fdt != ALIGN(fdt, SZ_4K) ||
102 fdt_check_header((void *)fdt))) {
103 debug("%s: Using built in FDT, bootloader gave us %#llx\n", __func__, fdt);
104 return (void *)gd->fdt_blob;
116 * Some Qualcomm boards require GPIO configuration when switching USB modes.
117 * Support setting this configuration via pinctrl state.
119 int board_usb_init(int index, enum usb_init_type init)
125 ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
127 printf("Cannot find USB device\n");
131 ret = dev_read_stringlist_search(usb, "pinctrl-names",
133 /* No "device" pinctrl state, so just bail */
137 /* Select "default" or "device" pinctrl */
140 pinctrl_select_state(usb, "default");
142 case USB_INIT_DEVICE:
143 pinctrl_select_state(usb, "device");
146 debug("Unknown usb_init_type %d\n", init);
154 * Some boards still need board specific init code, they can implement that by
155 * overriding this function.
157 * FIXME: get rid of board specific init code
159 void __weak qcom_board_init(void)
165 regulators_enable_boot_on(false);
167 qcom_of_fixup_nodes();
172 /* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
173 * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
174 * comprehensive solution to automatically picking the DTB, but aims to be correct for the
175 * majority case. For most devices it should be possible to make this algorithm work by
176 * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
177 * variants that are all supported by a single U-Boot image will require implementing device-
178 * specific detection.
180 static void configure_env(void)
182 const char *first_compat, *last_compat;
184 char buf[32] = { 0 };
186 * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
187 * The vendor is skipped when it's a Qualcomm reference board, or the
190 char dt_path[64] = { 0 };
191 int compat_count, ret;
194 root = ofnode_root();
195 /* This is almost always 2, but be explicit that we want the first and last compatibles
196 * not the first and second.
198 compat_count = ofnode_read_string_count(root, "compatible");
199 if (compat_count < 2) {
200 log_warning("%s: only one root compatible bailing!\n", __func__);
204 /* The most specific device compatible (e.g. "thundercomm,db845c") */
205 ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
207 log_warning("Can't read first compatible\n");
211 /* The last compatible is always the SoC compatible */
212 ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
214 log_warning("Can't read second compatible\n");
218 /* Copy the second compat (e.g. "qcom,sdm845") into buf */
219 strlcpy(buf, last_compat, sizeof(buf) - 1);
222 /* strsep() is destructive, it replaces the comma with a \0 */
223 if (!strsep(&tmp, ",")) {
224 log_warning("second compatible '%s' has no ','\n", buf);
228 /* tmp now points to just the "sdm845" part of the string */
231 /* Now figure out the "board" part from the first compatible */
232 memset(buf, 0, sizeof(buf));
233 strlcpy(buf, first_compat, sizeof(buf) - 1);
236 /* The Qualcomm reference boards (RBx, HDK, etc) */
237 if (!strncmp("qcom", buf, strlen("qcom"))) {
239 * They all have the first compatible as "qcom,<soc>-<board>"
240 * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
243 if (!strsep(&tmp, "-")) {
244 log_warning("compatible '%s' has no '-'\n", buf);
247 /* tmp is now "rb5" */
248 env_set("board", tmp);
250 if (!strsep(&tmp, ",")) {
251 log_warning("compatible '%s' has no ','\n", buf);
254 /* for thundercomm we just want the bit after the comma (e.g. "db845c"),
255 * for all other boards we replace the comma with a '-' and take both
256 * (e.g. "oneplus-enchilada")
258 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
259 env_set("board", tmp);
262 env_set("board", buf);
266 /* Now build the full path name */
267 snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
268 env_get("soc"), env_get("board"));
269 env_set("fdtfile", dt_path);
272 void __weak qcom_late_init(void)
276 #define KERNEL_COMP_SIZE SZ_64M
278 #define addr_alloc(lmb, size) lmb_alloc(lmb, size, SZ_2M)
280 /* Stolen from arch/arm/mach-apple/board.c */
281 int board_late_init(void)
286 lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
288 /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
289 status |= env_set_hex("kernel_addr_r", addr_alloc(&lmb, SZ_128M));
290 status |= env_set_hex("ramdisk_addr_r", addr_alloc(&lmb, SZ_128M));
291 status |= env_set_hex("kernel_comp_addr_r", addr_alloc(&lmb, KERNEL_COMP_SIZE));
292 status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
293 status |= env_set_hex("scriptaddr", addr_alloc(&lmb, SZ_4M));
294 status |= env_set_hex("pxefile_addr_r", addr_alloc(&lmb, SZ_4M));
295 status |= env_set_hex("fdt_addr_r", addr_alloc(&lmb, SZ_2M));
298 log_warning("%s: Failed to set run time variables\n", __func__);
306 static void build_mem_map(void)
311 * Ensure the peripheral block is sized to correctly cover the address range
312 * up to the first memory bank.
313 * Don't map the first page to ensure that we actually trigger an abort on a
314 * null pointer access rather than just hanging.
315 * FIXME: we should probably split this into more precise regions
317 mem_map[0].phys = 0x1000;
318 mem_map[0].virt = mem_map[0].phys;
319 mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
320 mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
321 PTE_BLOCK_NON_SHARE |
322 PTE_BLOCK_PXN | PTE_BLOCK_UXN;
324 for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
325 mem_map[i].phys = gd->bd->bi_dram[j].start;
326 mem_map[i].virt = mem_map[i].phys;
327 mem_map[i].size = gd->bd->bi_dram[j].size;
328 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
329 PTE_BLOCK_INNER_SHARE;
332 mem_map[i].phys = UINT64_MAX;
336 debug("Configured memory map:\n");
337 for (i = 0; mem_map[i].size; i++)
338 debug(" 0x%016llx - 0x%016llx: entry %d\n",
339 mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
343 u64 get_page_table_size(void)
348 static int fdt_cmp_res(const void *v1, const void *v2)
350 const struct fdt_resource *res1 = v1, *res2 = v2;
352 return res1->start - res2->start;
355 #define N_RESERVED_REGIONS 32
357 /* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
358 * On some platforms this is enough to trigger a security violation and trap
361 static void carve_out_reserved_memory(void)
363 static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
364 int parent, rmem, count, i = 0;
368 /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
369 * attempt to access them, causing a security exception.
371 parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
373 log_err("No reserved memory regions found\n");
377 /* Collect the reserved memory regions */
378 fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
381 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
384 if (i == N_RESERVED_REGIONS) {
385 log_err("Too many reserved regions!\n");
389 /* Read the address and size out from the reg property. Doing this "properly" with
390 * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
391 * takes <1ms... Oh the woes of no dcache.
393 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
395 /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
396 * the 32-bit address space. So we can cheat here for speed.
398 res[i].start = fdt32_to_cpu(ptr[1]);
399 res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
404 /* Sort the reserved memory regions by address */
406 qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
408 /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
409 * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
412 start = ALIGN_DOWN(res[0].start, SZ_2M);
413 size = ALIGN(res[0].end - start, SZ_2M);
414 for (i = 1; i <= count; i++) {
415 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
416 * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
417 * compatible properties).
418 * If within 2M of the previous region, bump the size to include this region. Otherwise
419 * start a new region.
421 if (i == count || start + size < res[i].start - SZ_2M) {
422 debug(" 0x%016llx - 0x%016llx: reserved\n",
423 start, start + size);
424 mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
425 /* If this is the final region then quit here before we index
430 start = ALIGN_DOWN(res[i].start, SZ_2M);
431 size = ALIGN(res[i].end - start, SZ_2M);
433 /* Bump size if this region is immediately after the previous one */
434 size = ALIGN(res[i].end - start, SZ_2M);
439 /* This function open-codes setup_all_pgtables() so that we can
440 * insert additional mappings *before* turning on the MMU.
442 void enable_caches(void)
444 u64 tlb_addr = gd->arch.tlb_addr;
445 u64 tlb_size = gd->arch.tlb_size;
447 ulong carveout_start;
449 gd->arch.tlb_fillptr = tlb_addr;
455 /* Create normal system page tables */
458 pt_size = (uintptr_t)gd->arch.tlb_fillptr -
459 (uintptr_t)gd->arch.tlb_addr;
460 debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
462 /* Create emergency page tables */
463 gd->arch.tlb_size -= pt_size;
464 gd->arch.tlb_addr = gd->arch.tlb_fillptr;
466 gd->arch.tlb_emerg = gd->arch.tlb_addr;
467 gd->arch.tlb_addr = tlb_addr;
468 gd->arch.tlb_size = tlb_size;
470 carveout_start = get_timer(0);
471 /* Takes ~20-50ms on SDM845 */
472 carve_out_reserved_memory();
473 debug("carveout time: %lums\n", get_timer(carveout_start));