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