]> Git Repo - J-u-boot.git/blob - arch/arm/mach-snapdragon/board.c
Merge patch series "arm: Add Analog Devices SC5xx Machine Type"
[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 #include "time.h"
10 #include <asm/armv8/mmu.h>
11 #include <asm/gpio.h>
12 #include <asm/io.h>
13 #include <asm/psci.h>
14 #include <asm/system.h>
15 #include <dm/device.h>
16 #include <dm/pinctrl.h>
17 #include <dm/uclass-internal.h>
18 #include <dm/read.h>
19 #include <power/regulator.h>
20 #include <env.h>
21 #include <init.h>
22 #include <linux/arm-smccc.h>
23 #include <linux/bug.h>
24 #include <linux/psci.h>
25 #include <linux/sizes.h>
26 #include <lmb.h>
27 #include <malloc.h>
28 #include <fdt_support.h>
29 #include <usb.h>
30 #include <sort.h>
31
32 #include "qcom-priv.h"
33
34 DECLARE_GLOBAL_DATA_PTR;
35
36 static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
37
38 struct mm_region *mem_map = rbx_mem_map;
39
40 int dram_init(void)
41 {
42         return fdtdec_setup_mem_size_base();
43 }
44
45 static int ddr_bank_cmp(const void *v1, const void *v2)
46 {
47         const struct {
48                 phys_addr_t start;
49                 phys_size_t size;
50         } *res1 = v1, *res2 = v2;
51
52         if (!res1->size)
53                 return 1;
54         if (!res2->size)
55                 return -1;
56
57         return (res1->start >> 24) - (res2->start >> 24);
58 }
59
60 int dram_init_banksize(void)
61 {
62         int ret;
63
64         ret = fdtdec_setup_memory_banksize();
65         if (ret < 0)
66                 return ret;
67
68         if (CONFIG_NR_DRAM_BANKS < 2)
69                 return 0;
70
71         /* Sort our RAM banks -_- */
72         qsort(gd->bd->bi_dram, CONFIG_NR_DRAM_BANKS, sizeof(gd->bd->bi_dram[0]), ddr_bank_cmp);
73
74         return 0;
75 }
76
77 static void show_psci_version(void)
78 {
79         struct arm_smccc_res res;
80
81         arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
82
83         debug("PSCI:  v%ld.%ld\n",
84               PSCI_VERSION_MAJOR(res.a0),
85               PSCI_VERSION_MINOR(res.a0));
86 }
87
88 void *board_fdt_blob_setup(int *err)
89 {
90         phys_addr_t fdt;
91         /* Return DTB pointer passed by ABL */
92         *err = 0;
93         fdt = get_prev_bl_fdt_addr();
94
95         /*
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
99          */
100
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;
105         }
106
107         return (void *)fdt;
108 }
109
110 void reset_cpu(void)
111 {
112         psci_system_reset();
113 }
114
115 /*
116  * Some Qualcomm boards require GPIO configuration when switching USB modes.
117  * Support setting this configuration via pinctrl state.
118  */
119 int board_usb_init(int index, enum usb_init_type init)
120 {
121         struct udevice *usb;
122         int ret = 0;
123
124         /* USB device */
125         ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
126         if (ret) {
127                 printf("Cannot find USB device\n");
128                 return ret;
129         }
130
131         ret = dev_read_stringlist_search(usb, "pinctrl-names",
132                                          "device");
133         /* No "device" pinctrl state, so just bail */
134         if (ret < 0)
135                 return 0;
136
137         /* Select "default" or "device" pinctrl */
138         switch (init) {
139         case USB_INIT_HOST:
140                 pinctrl_select_state(usb, "default");
141                 break;
142         case USB_INIT_DEVICE:
143                 pinctrl_select_state(usb, "device");
144                 break;
145         default:
146                 debug("Unknown usb_init_type %d\n", init);
147                 break;
148         }
149
150         return 0;
151 }
152
153 /*
154  * Some boards still need board specific init code, they can implement that by
155  * overriding this function.
156  *
157  * FIXME: get rid of board specific init code
158  */
159 void __weak qcom_board_init(void)
160 {
161 }
162
163 int board_init(void)
164 {
165         regulators_enable_boot_on(false);
166         show_psci_version();
167         qcom_of_fixup_nodes();
168         qcom_board_init();
169         return 0;
170 }
171
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.
179  */
180 static void configure_env(void)
181 {
182         const char *first_compat, *last_compat;
183         char *tmp;
184         char buf[32] = { 0 };
185         /*
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
188          * db845c.
189          */
190         char dt_path[64] = { 0 };
191         int compat_count, ret;
192         ofnode root;
193
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.
197          */
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__);
201                 return;
202         }
203
204         /* The most specific device compatible (e.g. "thundercomm,db845c") */
205         ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
206         if (ret < 0) {
207                 log_warning("Can't read first compatible\n");
208                 return;
209         }
210
211         /* The last compatible is always the SoC compatible */
212         ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
213         if (ret < 0) {
214                 log_warning("Can't read second compatible\n");
215                 return;
216         }
217
218         /* Copy the second compat (e.g. "qcom,sdm845") into buf */
219         strlcpy(buf, last_compat, sizeof(buf) - 1);
220         tmp = buf;
221
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);
225                 return;
226         }
227
228         /* tmp now points to just the "sdm845" part of the string */
229         env_set("soc", tmp);
230
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);
234         tmp = buf;
235
236         /* The Qualcomm reference boards (RBx, HDK, etc)  */
237         if (!strncmp("qcom", buf, strlen("qcom"))) {
238                 /*
239                  * They all have the first compatible as "qcom,<soc>-<board>"
240                  * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
241                  * the dash.
242                  */
243                 if (!strsep(&tmp, "-")) {
244                         log_warning("compatible '%s' has no '-'\n", buf);
245                         return;
246                 }
247                 /* tmp is now "rb5" */
248                 env_set("board", tmp);
249         } else {
250                 if (!strsep(&tmp, ",")) {
251                         log_warning("compatible '%s' has no ','\n", buf);
252                         return;
253                 }
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")
257                  */
258                 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
259                         env_set("board", tmp);
260                 } else {
261                         *(tmp - 1) = '-';
262                         env_set("board", buf);
263                 }
264         }
265
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);
270 }
271
272 void __weak qcom_late_init(void)
273 {
274 }
275
276 #define KERNEL_COMP_SIZE        SZ_64M
277
278 #define addr_alloc(lmb, size) lmb_alloc(lmb, size, SZ_2M)
279
280 /* Stolen from arch/arm/mach-apple/board.c */
281 int board_late_init(void)
282 {
283         struct lmb lmb;
284         u32 status = 0;
285
286         lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
287
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));
296
297         if (status)
298                 log_warning("%s: Failed to set run time variables\n", __func__);
299
300         configure_env();
301         qcom_late_init();
302
303         return 0;
304 }
305
306 static void build_mem_map(void)
307 {
308         int i, j;
309
310         /*
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
316          */
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;
323
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;
330         }
331
332         mem_map[i].phys = UINT64_MAX;
333         mem_map[i].size = 0;
334
335 #ifdef DEBUG
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);
340 #endif
341 }
342
343 u64 get_page_table_size(void)
344 {
345         return SZ_64K;
346 }
347
348 static int fdt_cmp_res(const void *v1, const void *v2)
349 {
350         const struct fdt_resource *res1 = v1, *res2 = v2;
351
352         return res1->start - res2->start;
353 }
354
355 #define N_RESERVED_REGIONS 32
356
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
359  * to EL3.
360  */
361 static void carve_out_reserved_memory(void)
362 {
363         static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
364         int parent, rmem, count, i = 0;
365         phys_addr_t start;
366         size_t size;
367
368         /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
369          * attempt to access them, causing a security exception.
370          */
371         parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
372         if (parent <= 0) {
373                 log_err("No reserved memory regions found\n");
374                 return;
375         }
376
377         /* Collect the reserved memory regions */
378         fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
379                 const fdt32_t *ptr;
380                 int len;
381                 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
382                         continue;
383
384                 if (i == N_RESERVED_REGIONS) {
385                         log_err("Too many reserved regions!\n");
386                         break;
387                 }
388
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.
392                  */
393                 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
394                 if (ptr) {
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.
397                          */
398                         res[i].start = fdt32_to_cpu(ptr[1]);
399                         res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
400                         i++;
401                 }
402         }
403
404         /* Sort the reserved memory regions by address */
405         count = i;
406         qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
407
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
410          * regions.
411          */
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.
420                  */
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
426                          * out of bounds...
427                          */
428                         if (i == count)
429                                 break;
430                         start = ALIGN_DOWN(res[i].start, SZ_2M);
431                         size = ALIGN(res[i].end - start, SZ_2M);
432                 } else {
433                         /* Bump size if this region is immediately after the previous one */
434                         size = ALIGN(res[i].end - start, SZ_2M);
435                 }
436         }
437 }
438
439 /* This function open-codes setup_all_pgtables() so that we can
440  * insert additional mappings *before* turning on the MMU.
441  */
442 void enable_caches(void)
443 {
444         u64 tlb_addr = gd->arch.tlb_addr;
445         u64 tlb_size = gd->arch.tlb_size;
446         u64 pt_size;
447         ulong carveout_start;
448
449         gd->arch.tlb_fillptr = tlb_addr;
450
451         build_mem_map();
452
453         icache_enable();
454
455         /* Create normal system page tables */
456         setup_pgtables();
457
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);
461
462         /* Create emergency page tables */
463         gd->arch.tlb_size -= pt_size;
464         gd->arch.tlb_addr = gd->arch.tlb_fillptr;
465         setup_pgtables();
466         gd->arch.tlb_emerg = gd->arch.tlb_addr;
467         gd->arch.tlb_addr = tlb_addr;
468         gd->arch.tlb_size = tlb_size;
469
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));
474
475         dcache_enable();
476 }
This page took 0.051168 seconds and 4 git commands to generate.