]> Git Repo - J-u-boot.git/blob - arch/arm/mach-snapdragon/board.c
Merge patch series "board: beagle: Enable 32k and debounce configuration and fixups"
[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 <env.h>
20 #include <init.h>
21 #include <linux/arm-smccc.h>
22 #include <linux/bug.h>
23 #include <linux/psci.h>
24 #include <linux/sizes.h>
25 #include <lmb.h>
26 #include <malloc.h>
27 #include <usb.h>
28 #include <sort.h>
29
30 DECLARE_GLOBAL_DATA_PTR;
31
32 static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
33
34 struct mm_region *mem_map = rbx_mem_map;
35
36 int dram_init(void)
37 {
38         return fdtdec_setup_mem_size_base();
39 }
40
41 static int ddr_bank_cmp(const void *v1, const void *v2)
42 {
43         const struct {
44                 phys_addr_t start;
45                 phys_size_t size;
46         } *res1 = v1, *res2 = v2;
47
48         if (!res1->size)
49                 return 1;
50         if (!res2->size)
51                 return -1;
52
53         return (res1->start >> 24) - (res2->start >> 24);
54 }
55
56 int dram_init_banksize(void)
57 {
58         int ret;
59
60         ret = fdtdec_setup_memory_banksize();
61         if (ret < 0)
62                 return ret;
63
64         if (CONFIG_NR_DRAM_BANKS < 2)
65                 return 0;
66
67         /* Sort our RAM banks -_- */
68         qsort(gd->bd->bi_dram, CONFIG_NR_DRAM_BANKS, sizeof(gd->bd->bi_dram[0]), ddr_bank_cmp);
69
70         return 0;
71 }
72
73 static void show_psci_version(void)
74 {
75         struct arm_smccc_res res;
76
77         arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
78
79         debug("PSCI:  v%ld.%ld\n",
80               PSCI_VERSION_MAJOR(res.a0),
81               PSCI_VERSION_MINOR(res.a0));
82 }
83
84 void *board_fdt_blob_setup(int *err)
85 {
86         phys_addr_t fdt;
87         /* Return DTB pointer passed by ABL */
88         *err = 0;
89         fdt = get_prev_bl_fdt_addr();
90
91         /*
92          * If we bail then the board will simply not boot, instead let's
93          * try and use the FDT built into U-Boot if there is one...
94          * This avoids having a hard dependency on the previous stage bootloader
95          */
96         if (IS_ENABLED(CONFIG_OF_SEPARATE) && (!fdt || fdt != ALIGN(fdt, SZ_4K))) {
97                 debug("%s: Using built in FDT, bootloader gave us %#llx\n", __func__, fdt);
98                 return (void *)gd->fdt_blob;
99         }
100
101         return (void *)fdt;
102 }
103
104 void reset_cpu(void)
105 {
106         psci_system_reset();
107 }
108
109 /*
110  * Some Qualcomm boards require GPIO configuration when switching USB modes.
111  * Support setting this configuration via pinctrl state.
112  */
113 int board_usb_init(int index, enum usb_init_type init)
114 {
115         struct udevice *usb;
116         int ret = 0;
117
118         /* USB device */
119         ret = uclass_find_device_by_seq(UCLASS_USB, index, &usb);
120         if (ret) {
121                 printf("Cannot find USB device\n");
122                 return ret;
123         }
124
125         ret = dev_read_stringlist_search(usb, "pinctrl-names",
126                                          "device");
127         /* No "device" pinctrl state, so just bail */
128         if (ret < 0)
129                 return 0;
130
131         /* Select "default" or "device" pinctrl */
132         switch (init) {
133         case USB_INIT_HOST:
134                 pinctrl_select_state(usb, "default");
135                 break;
136         case USB_INIT_DEVICE:
137                 pinctrl_select_state(usb, "device");
138                 break;
139         default:
140                 debug("Unknown usb_init_type %d\n", init);
141                 break;
142         }
143
144         return 0;
145 }
146
147 /*
148  * Some boards still need board specific init code, they can implement that by
149  * overriding this function.
150  *
151  * FIXME: get rid of board specific init code
152  */
153 void __weak qcom_board_init(void)
154 {
155 }
156
157 int board_init(void)
158 {
159         show_psci_version();
160         qcom_board_init();
161         return 0;
162 }
163
164 /* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
165  * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
166  * comprehensive solution to automatically picking the DTB, but aims to be correct for the
167  * majority case. For most devices it should be possible to make this algorithm work by
168  * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
169  * variants that are all supported by a single U-Boot image will require implementing device-
170  * specific detection.
171  */
172 static void configure_env(void)
173 {
174         const char *first_compat, *last_compat;
175         char *tmp;
176         char buf[32] = { 0 };
177         /*
178          * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
179          * The vendor is skipped when it's a Qualcomm reference board, or the
180          * db845c.
181          */
182         char dt_path[64] = { 0 };
183         int compat_count, ret;
184         ofnode root;
185
186         root = ofnode_root();
187         /* This is almost always 2, but be explicit that we want the first and last compatibles
188          * not the first and second.
189          */
190         compat_count = ofnode_read_string_count(root, "compatible");
191         if (compat_count < 2) {
192                 log_warning("%s: only one root compatible bailing!\n", __func__);
193                 return;
194         }
195
196         /* The most specific device compatible (e.g. "thundercomm,db845c") */
197         ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
198         if (ret < 0) {
199                 log_warning("Can't read first compatible\n");
200                 return;
201         }
202
203         /* The last compatible is always the SoC compatible */
204         ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
205         if (ret < 0) {
206                 log_warning("Can't read second compatible\n");
207                 return;
208         }
209
210         /* Copy the second compat (e.g. "qcom,sdm845") into buf */
211         strlcpy(buf, last_compat, sizeof(buf) - 1);
212         tmp = buf;
213
214         /* strsep() is destructive, it replaces the comma with a \0 */
215         if (!strsep(&tmp, ",")) {
216                 log_warning("second compatible '%s' has no ','\n", buf);
217                 return;
218         }
219
220         /* tmp now points to just the "sdm845" part of the string */
221         env_set("soc", tmp);
222
223         /* Now figure out the "board" part from the first compatible */
224         memset(buf, 0, sizeof(buf));
225         strlcpy(buf, first_compat, sizeof(buf) - 1);
226         tmp = buf;
227
228         /* The Qualcomm reference boards (RBx, HDK, etc)  */
229         if (!strncmp("qcom", buf, strlen("qcom"))) {
230                 /*
231                  * They all have the first compatible as "qcom,<soc>-<board>"
232                  * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
233                  * the dash.
234                  */
235                 if (!strsep(&tmp, "-")) {
236                         log_warning("compatible '%s' has no '-'\n", buf);
237                         return;
238                 }
239                 /* tmp is now "rb5" */
240                 env_set("board", tmp);
241         } else {
242                 if (!strsep(&tmp, ",")) {
243                         log_warning("compatible '%s' has no ','\n", buf);
244                         return;
245                 }
246                 /* for thundercomm we just want the bit after the comma (e.g. "db845c"),
247                  * for all other boards we replace the comma with a '-' and take both
248                  * (e.g. "oneplus-enchilada")
249                  */
250                 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
251                         env_set("board", tmp);
252                 } else {
253                         *(tmp - 1) = '-';
254                         env_set("board", buf);
255                 }
256         }
257
258         /* Now build the full path name */
259         snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
260                  env_get("soc"), env_get("board"));
261         env_set("fdtfile", dt_path);
262 }
263
264 void __weak qcom_late_init(void)
265 {
266 }
267
268 #define KERNEL_COMP_SIZE        SZ_64M
269
270 #define addr_alloc(lmb, size) lmb_alloc(lmb, size, SZ_2M)
271
272 /* Stolen from arch/arm/mach-apple/board.c */
273 int board_late_init(void)
274 {
275         struct lmb lmb;
276         u32 status = 0;
277
278         lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
279
280         /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
281         status |= env_set_hex("kernel_addr_r", addr_alloc(&lmb, SZ_128M));
282         status |= env_set_hex("ramdisk_addr_r", addr_alloc(&lmb, SZ_128M));
283         status |= env_set_hex("kernel_comp_addr_r", addr_alloc(&lmb, KERNEL_COMP_SIZE));
284         status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
285         status |= env_set_hex("scriptaddr", addr_alloc(&lmb, SZ_4M));
286         status |= env_set_hex("pxefile_addr_r", addr_alloc(&lmb, SZ_4M));
287         status |= env_set_hex("fdt_addr_r", addr_alloc(&lmb, SZ_2M));
288
289         if (status)
290                 log_warning("%s: Failed to set run time variables\n", __func__);
291
292         configure_env();
293         qcom_late_init();
294
295         return 0;
296 }
297
298 static void build_mem_map(void)
299 {
300         int i, j;
301
302         /*
303          * Ensure the peripheral block is sized to correctly cover the address range
304          * up to the first memory bank.
305          * Don't map the first page to ensure that we actually trigger an abort on a
306          * null pointer access rather than just hanging.
307          * FIXME: we should probably split this into more precise regions
308          */
309         mem_map[0].phys = 0x1000;
310         mem_map[0].virt = mem_map[0].phys;
311         mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
312         mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
313                          PTE_BLOCK_NON_SHARE |
314                          PTE_BLOCK_PXN | PTE_BLOCK_UXN;
315
316         for (i = 1, j = 0; i < ARRAY_SIZE(rbx_mem_map) - 1 && gd->bd->bi_dram[j].size; i++, j++) {
317                 mem_map[i].phys = gd->bd->bi_dram[j].start;
318                 mem_map[i].virt = mem_map[i].phys;
319                 mem_map[i].size = gd->bd->bi_dram[j].size;
320                 mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | \
321                                    PTE_BLOCK_INNER_SHARE;
322         }
323
324         mem_map[i].phys = UINT64_MAX;
325         mem_map[i].size = 0;
326
327 #ifdef DEBUG
328         debug("Configured memory map:\n");
329         for (i = 0; mem_map[i].size; i++)
330                 debug("  0x%016llx - 0x%016llx: entry %d\n",
331                       mem_map[i].phys, mem_map[i].phys + mem_map[i].size, i);
332 #endif
333 }
334
335 u64 get_page_table_size(void)
336 {
337         return SZ_64K;
338 }
339
340 static int fdt_cmp_res(const void *v1, const void *v2)
341 {
342         const struct fdt_resource *res1 = v1, *res2 = v2;
343
344         return res1->start - res2->start;
345 }
346
347 #define N_RESERVED_REGIONS 32
348
349 /* Mark all no-map regions as PTE_TYPE_FAULT to prevent speculative access.
350  * On some platforms this is enough to trigger a security violation and trap
351  * to EL3.
352  */
353 static void carve_out_reserved_memory(void)
354 {
355         static struct fdt_resource res[N_RESERVED_REGIONS] = { 0 };
356         int parent, rmem, count, i = 0;
357         phys_addr_t start;
358         size_t size;
359
360         /* Some reserved nodes must be carved out, as the cache-prefetcher may otherwise
361          * attempt to access them, causing a security exception.
362          */
363         parent = fdt_path_offset(gd->fdt_blob, "/reserved-memory");
364         if (parent <= 0) {
365                 log_err("No reserved memory regions found\n");
366                 return;
367         }
368
369         /* Collect the reserved memory regions */
370         fdt_for_each_subnode(rmem, gd->fdt_blob, parent) {
371                 const fdt32_t *ptr;
372                 int len;
373                 if (!fdt_getprop(gd->fdt_blob, rmem, "no-map", NULL))
374                         continue;
375
376                 if (i == N_RESERVED_REGIONS) {
377                         log_err("Too many reserved regions!\n");
378                         break;
379                 }
380
381                 /* Read the address and size out from the reg property. Doing this "properly" with
382                  * fdt_get_resource() takes ~70ms on SDM845, but open-coding the happy path here
383                  * takes <1ms... Oh the woes of no dcache.
384                  */
385                 ptr = fdt_getprop(gd->fdt_blob, rmem, "reg", &len);
386                 if (ptr) {
387                         /* Qualcomm devices use #address/size-cells = <2> but all reserved regions are within
388                          * the 32-bit address space. So we can cheat here for speed.
389                          */
390                         res[i].start = fdt32_to_cpu(ptr[1]);
391                         res[i].end = res[i].start + fdt32_to_cpu(ptr[3]);
392                         i++;
393                 }
394         }
395
396         /* Sort the reserved memory regions by address */
397         count = i;
398         qsort(res, count, sizeof(struct fdt_resource), fdt_cmp_res);
399
400         /* Now set the right attributes for them. Often a lot of the regions are tightly packed together
401          * so we can optimise the number of calls to mmu_change_region_attr() by combining adjacent
402          * regions.
403          */
404         start = ALIGN_DOWN(res[0].start, SZ_2M);
405         size = ALIGN(res[0].end - start, SZ_2M);
406         for (i = 1; i <= count; i++) {
407                 /* We ideally want to 2M align everything for more efficient pagetables, but we must avoid
408                  * overwriting reserved memory regions which shouldn't be mapped as FAULT (like those with
409                  * compatible properties).
410                  * If within 2M of the previous region, bump the size to include this region. Otherwise
411                  * start a new region.
412                  */
413                 if (i == count || start + size < res[i].start - SZ_2M) {
414                         debug("  0x%016llx - 0x%016llx: reserved\n",
415                               start, start + size);
416                         mmu_change_region_attr(start, size, PTE_TYPE_FAULT);
417                         /* If this is the final region then quit here before we index
418                          * out of bounds...
419                          */
420                         if (i == count)
421                                 break;
422                         start = ALIGN_DOWN(res[i].start, SZ_2M);
423                         size = ALIGN(res[i].end - start, SZ_2M);
424                 } else {
425                         /* Bump size if this region is immediately after the previous one */
426                         size = ALIGN(res[i].end - start, SZ_2M);
427                 }
428         }
429 }
430
431 /* This function open-codes setup_all_pgtables() so that we can
432  * insert additional mappings *before* turning on the MMU.
433  */
434 void enable_caches(void)
435 {
436         u64 tlb_addr = gd->arch.tlb_addr;
437         u64 tlb_size = gd->arch.tlb_size;
438         u64 pt_size;
439         ulong carveout_start;
440
441         gd->arch.tlb_fillptr = tlb_addr;
442
443         build_mem_map();
444
445         icache_enable();
446
447         /* Create normal system page tables */
448         setup_pgtables();
449
450         pt_size = (uintptr_t)gd->arch.tlb_fillptr -
451                   (uintptr_t)gd->arch.tlb_addr;
452         debug("Primary pagetable size: %lluKiB\n", pt_size / 1024);
453
454         /* Create emergency page tables */
455         gd->arch.tlb_size -= pt_size;
456         gd->arch.tlb_addr = gd->arch.tlb_fillptr;
457         setup_pgtables();
458         gd->arch.tlb_emerg = gd->arch.tlb_addr;
459         gd->arch.tlb_addr = tlb_addr;
460         gd->arch.tlb_size = tlb_size;
461
462         carveout_start = get_timer(0);
463         /* Takes ~20-50ms on SDM845 */
464         carve_out_reserved_memory();
465         debug("carveout time: %lums\n", get_timer(carveout_start));
466
467         dcache_enable();
468 }
This page took 0.054926 seconds and 4 git commands to generate.