]> Git Repo - J-u-boot.git/blob - arch/arm/mach-snapdragon/board.c
fdt: Swap the signature for board_fdt_blob_setup()
[J-u-boot.git] / arch / arm / mach-snapdragon / board.c
1 // 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 <[email protected]>
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 static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
41
42 struct mm_region *mem_map = rbx_mem_map;
43
44 static struct {
45         phys_addr_t start;
46         phys_size_t size;
47 } prevbl_ddr_banks[CONFIG_NR_DRAM_BANKS] __section(".data") = { 0 };
48
49 int dram_init(void)
50 {
51         /*
52          * gd->ram_base / ram_size have been setup already
53          * in qcom_parse_memory().
54          */
55         return 0;
56 }
57
58 static 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
73 /* This has to be done post-relocation since gd->bd isn't preserved */
74 static 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
84 int dram_init_banksize(void)
85 {
86         qcom_configure_bi_dram();
87
88         return 0;
89 }
90
91 static 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         }
128
129         /* Sort our RAM banks -_- */
130         qsort(prevbl_ddr_banks, banks, sizeof(prevbl_ddr_banks[0]), ddr_bank_cmp);
131
132         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);
136 }
137
138 static 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
149 /* 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  */
153 int board_fdt_blob_setup(void **fdtp)
154 {
155         struct fdt_header *fdt;
156         bool internal_valid, external_valid;
157         int ret = 0;
158
159         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);
162
163         /*
164          * 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.
166          */
167         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);
170
171         if (internal_valid) {
172                 debug("Using built in FDT\n");
173                 ret = -EEXIST;
174         } else {
175                 debug("Using external FDT\n");
176                 /* So we can use it before returning */
177                 *fdtp = fdt;
178         }
179
180         /*
181          * Parse the /memory node while we're here,
182          * this makes it easy to do other things early.
183          */
184         qcom_parse_memory();
185
186         return ret;
187 }
188
189 void reset_cpu(void)
190 {
191         psci_system_reset();
192 }
193
194 /*
195  * Some Qualcomm boards require GPIO configuration when switching USB modes.
196  * Support setting this configuration via pinctrl state.
197  */
198 int board_usb_init(int index, enum usb_init_type init)
199 {
200         struct udevice *usb;
201         int ret = 0;
202
203         /* USB device */
204         ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
205         if (ret) {
206                 printf("Cannot find USB device\n");
207                 return ret;
208         }
209
210         ret = dev_read_stringlist_search(usb, "pinctrl-names",
211                                          "device");
212         /* No "device" pinctrl state, so just bail */
213         if (ret < 0)
214                 return 0;
215
216         /* Select "default" or "device" pinctrl */
217         switch (init) {
218         case USB_INIT_HOST:
219                 pinctrl_select_state(usb, "default");
220                 break;
221         case USB_INIT_DEVICE:
222                 pinctrl_select_state(usb, "device");
223                 break;
224         default:
225                 debug("Unknown usb_init_type %d\n", init);
226                 break;
227         }
228
229         return 0;
230 }
231
232 /*
233  * Some boards still need board specific init code, they can implement that by
234  * overriding this function.
235  *
236  * FIXME: get rid of board specific init code
237  */
238 void __weak qcom_board_init(void)
239 {
240 }
241
242 int board_init(void)
243 {
244         show_psci_version();
245         qcom_of_fixup_nodes();
246         qcom_board_init();
247         return 0;
248 }
249
250 /**
251  * out_len includes the trailing null space
252  */
253 static int get_cmdline_option(const char *cmdline, const char *key, char *out, int out_len)
254 {
255         const char *p, *p_end;
256         int len;
257
258         p = strstr(cmdline, key);
259         if (!p)
260                 return -ENOENT;
261
262         p += strlen(key);
263         p_end = strstr(p, " ");
264         if (!p_end)
265                 return -ENOENT;
266
267         len = p_end - p;
268         if (len > out_len)
269                 len = out_len;
270
271         strncpy(out, p, len);
272         out[len] = '\0';
273
274         return 0;
275 }
276
277 /* The bootargs are populated by the previous stage bootloader */
278 static const char *get_cmdline(void)
279 {
280         ofnode node;
281         static const char *cmdline = NULL;
282
283         if (cmdline)
284                 return cmdline;
285
286         node = ofnode_path("/chosen");
287         if (!ofnode_valid(node))
288                 return NULL;
289
290         cmdline = ofnode_read_string(node, "bootargs");
291
292         return cmdline;
293 }
294
295 void qcom_set_serialno(void)
296 {
297         const char *cmdline = get_cmdline();
298         char serial[32];
299
300         if (!cmdline) {
301                 log_debug("Failed to get bootargs\n");
302                 return;
303         }
304
305         get_cmdline_option(cmdline, "androidboot.serialno=", serial, sizeof(serial));
306         if (serial[0] != '\0')
307                 env_set("serial#", serial);
308 }
309
310 /* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
311  * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
312  * comprehensive solution to automatically picking the DTB, but aims to be correct for the
313  * majority case. For most devices it should be possible to make this algorithm work by
314  * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
315  * variants that are all supported by a single U-Boot image will require implementing device-
316  * specific detection.
317  */
318 static void configure_env(void)
319 {
320         const char *first_compat, *last_compat;
321         char *tmp;
322         char buf[32] = { 0 };
323         /*
324          * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
325          * The vendor is skipped when it's a Qualcomm reference board, or the
326          * db845c.
327          */
328         char dt_path[64] = { 0 };
329         int compat_count, ret;
330         ofnode root;
331
332         root = ofnode_root();
333         /* This is almost always 2, but be explicit that we want the first and last compatibles
334          * not the first and second.
335          */
336         compat_count = ofnode_read_string_count(root, "compatible");
337         if (compat_count < 2) {
338                 log_warning("%s: only one root compatible bailing!\n", __func__);
339                 return;
340         }
341
342         /* The most specific device compatible (e.g. "thundercomm,db845c") */
343         ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
344         if (ret < 0) {
345                 log_warning("Can't read first compatible\n");
346                 return;
347         }
348
349         /* The last compatible is always the SoC compatible */
350         ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
351         if (ret < 0) {
352                 log_warning("Can't read second compatible\n");
353                 return;
354         }
355
356         /* Copy the second compat (e.g. "qcom,sdm845") into buf */
357         strlcpy(buf, last_compat, sizeof(buf) - 1);
358         tmp = buf;
359
360         /* strsep() is destructive, it replaces the comma with a \0 */
361         if (!strsep(&tmp, ",")) {
362                 log_warning("second compatible '%s' has no ','\n", buf);
363                 return;
364         }
365
366         /* tmp now points to just the "sdm845" part of the string */
367         env_set("soc", tmp);
368
369         /* Now figure out the "board" part from the first compatible */
370         memset(buf, 0, sizeof(buf));
371         strlcpy(buf, first_compat, sizeof(buf) - 1);
372         tmp = buf;
373
374         /* The Qualcomm reference boards (RBx, HDK, etc)  */
375         if (!strncmp("qcom", buf, strlen("qcom"))) {
376                 /*
377                  * They all have the first compatible as "qcom,<soc>-<board>"
378                  * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
379                  * the dash.
380                  */
381                 if (!strsep(&tmp, "-")) {
382                         log_warning("compatible '%s' has no '-'\n", buf);
383                         return;
384                 }
385                 /* tmp is now "rb5" */
386                 env_set("board", tmp);
387         } else {
388                 if (!strsep(&tmp, ",")) {
389                         log_warning("compatible '%s' has no ','\n", buf);
390                         return;
391                 }
392                 /* for thundercomm we just want the bit after the comma (e.g. "db845c"),
393                  * for all other boards we replace the comma with a '-' and take both
394                  * (e.g. "oneplus-enchilada")
395                  */
396                 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
397                         env_set("board", tmp);
398                 } else {
399                         *(tmp - 1) = '-';
400                         env_set("board", buf);
401                 }
402         }
403
404         /* Now build the full path name */
405         snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
406                  env_get("soc"), env_get("board"));
407         env_set("fdtfile", dt_path);
408
409         qcom_set_serialno();
410 }
411
412 void __weak qcom_late_init(void)
413 {
414 }
415
416 #define KERNEL_COMP_SIZE        SZ_64M
417 #ifdef CONFIG_FASTBOOT_BUF_SIZE
418 #define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
419 #else
420 #define FASTBOOT_BUF_SIZE 0
421 #endif
422
423 #define addr_alloc(size) lmb_alloc(size, SZ_2M)
424
425 /* Stolen from arch/arm/mach-apple/board.c */
426 int board_late_init(void)
427 {
428         u32 status = 0;
429         phys_addr_t addr;
430         struct fdt_header *fdt_blob = (struct fdt_header *)gd->fdt_blob;
431
432         /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
433         addr = addr_alloc(SZ_128M);
434         status |= env_set_hex("kernel_addr_r", addr);
435         status |= env_set_hex("loadaddr", addr);
436         status |= env_set_hex("ramdisk_addr_r", addr_alloc(SZ_128M));
437         status |= env_set_hex("kernel_comp_addr_r", addr_alloc(KERNEL_COMP_SIZE));
438         status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
439         if (IS_ENABLED(CONFIG_FASTBOOT))
440                 status |= env_set_hex("fastboot_addr_r", addr_alloc(FASTBOOT_BUF_SIZE));
441         status |= env_set_hex("scriptaddr", addr_alloc(SZ_4M));
442         status |= env_set_hex("pxefile_addr_r", addr_alloc(SZ_4M));
443         addr = addr_alloc(SZ_2M);
444         status |= env_set_hex("fdt_addr_r", addr);
445
446         if (status)
447                 log_warning("%s: Failed to set run time variables\n", __func__);
448
449         /* By default copy U-Boots FDT, it will be used as a fallback */
450         memcpy((void *)addr, (void *)gd->fdt_blob, fdt32_to_cpu(fdt_blob->totalsize));
451
452         configure_env();
453         qcom_late_init();
454
455         /* Configure the dfu_string for capsule updates */
456         qcom_configure_capsule_updates();
457
458         return 0;
459 }
460
461 static void build_mem_map(void)
462 {
463         int i, j;
464
465         /*
466          * Ensure the peripheral block is sized to correctly cover the address range
467          * up to the first memory bank.
468          * Don't map the first page to ensure that we actually trigger an abort on a
469          * null pointer access rather than just hanging.
470          * FIXME: we should probably split this into more precise regions
471          */
472         mem_map[0].phys = 0x1000;
473         mem_map[0].virt = mem_map[0].phys;
474         mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
475         mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
476                          PTE_BLOCK_NON_SHARE |
477                          PTE_BLOCK_PXN | PTE_BLOCK_UXN;
478
479         for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
480                 mem_map[i].phys = gd->bd->bi_dram[j].start;
481                 mem_map[i].virt = mem_map[i].phys;
482                 mem_map[i].size = gd->bd->bi_dram[j].size;
483                 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
484                                    PTE_BLOCK_INNER_SHARE;
485         }
486
487         mem_map[i].phys = UINT64_MAX;
488         mem_map[i].size = 0;
489
490 #ifdef DEBUG
491         debug("Configured memory map:\n");
492         for (i = 0; mem_map[i].size; i++)
493                 debug("  0x%016llx - 0x%016llx: entry %d\n",
494                       mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
495 #endif
496 }
497
498 u64 get_page_table_size(void)
499 {
500         return SZ_1M;
501 }
502
503 static int fdt_cmp_res(const void *v1, const void *v2)
504 {
505         const struct fdt_resource *res1 = v1, *res2 = v2;
506
507         return res1->start - res2->start;
508 }
509
510 #define N_RESERVED_REGIONS 32
511
512 /* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
513  * On some platforms this is enough to trigger a security violation and trap
514  * to EL3.
515  */
516 static void carve_out_reserved_memory(void)
517 {
518         static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
519         int parent, rmem, count, i = 0;
520         phys_addr_t start;
521         size_t size;
522
523         /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
524          * attempt to access them, causing a security exception.
525          */
526         parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
527         if (parent <= 0) {
528                 log_err("No reserved memory regions found\n");
529                 return;
530         }
531
532         /* Collect the reserved memory regions */
533         fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
534                 const fdt32_t *ptr;
535                 int len;
536                 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
537                         continue;
538
539                 if (i == N_RESERVED_REGIONS) {
540                         log_err("Too many reserved regions!\n");
541                         break;
542                 }
543
544                 /* Read the address and size out from the reg property. Doing this "properly" with
545                  * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
546                  * takes <1ms... Oh the woes of no dcache.
547                  */
548                 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
549                 if (ptr) {
550                         /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
551                          * the 32-bit address space. So we can cheat here for speed.
552                          */
553                         res[i].start = fdt32_to_cpu(ptr[1]);
554                         res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
555                         i++;
556                 }
557         }
558
559         /* Sort the reserved memory regions by address */
560         count = i;
561         qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
562
563         /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
564          * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
565          * regions.
566          */
567         start = ALIGN_DOWN(res[0].start, SZ_2M);
568         size = ALIGN(res[0].end - start, SZ_2M);
569         for (i = 1; i <= count; i++) {
570                 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
571                  * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
572                  * compatible properties).
573                  * If within 2M of the previous region, bump the size to include this region. Otherwise
574                  * start a new region.
575                  */
576                 if (i == count || start + size < res[i].start - SZ_2M) {
577                         debug("  0x%016llx - 0x%016llx: reserved\n",
578                               start, start + size);
579                         mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
580                         /* If this is the final region then quit here before we index
581                          * out of bounds...
582                          */
583                         if (i == count)
584                                 break;
585                         start = ALIGN_DOWN(res[i].start, SZ_2M);
586                         size = ALIGN(res[i].end - start, SZ_2M);
587                 } else {
588                         /* Bump size if this region is immediately after the previous one */
589                         size = ALIGN(res[i].end - start, SZ_2M);
590                 }
591         }
592 }
593
594 /* This function open-codes setup_all_pgtables() so that we can
595  * insert additional mappings *before* turning on the MMU.
596  */
597 void enable_caches(void)
598 {
599         u64 tlb_addr = gd->arch.tlb_addr;
600         u64 tlb_size = gd->arch.tlb_size;
601         u64 pt_size;
602         ulong carveout_start;
603
604         gd->arch.tlb_fillptr = tlb_addr;
605
606         build_mem_map();
607
608         icache_enable();
609
610         /* Create normal system page tables */
611         setup_pgtables();
612
613         pt_size = (uintptr_t)gd->arch.tlb_fillptr -
614                   (uintptr_t)gd->arch.tlb_addr;
615         debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
616
617         /* Create emergency page tables */
618         gd->arch.tlb_size -= pt_size;
619         gd->arch.tlb_addr = gd->arch.tlb_fillptr;
620         setup_pgtables();
621         gd->arch.tlb_emerg = gd->arch.tlb_addr;
622         gd->arch.tlb_addr = tlb_addr;
623         gd->arch.tlb_size = tlb_size;
624
625         /* We do the carveouts only for QCS404, for now. */
626         if (fdt_node_check_compatible(gd->fdt_blob, 0, "qcom,qcs404") == 0) {
627                 carveout_start = get_timer(0);
628                 /* Takes ~20-50ms on SDM845 */
629                 carve_out_reserved_memory();
630                 debug("carveout time: %lums\n", get_timer(carveout_start));
631         }
632         dcache_enable();
633 }
This page took 0.060897 seconds and 4 git commands to generate.