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