blob: 6b5ce421f8a1b0a2cd11090fdfb403ab73fa8289 [file] [log] [blame]
Caleb Connollye6c284b2023-11-20 20:48:00 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Common initialisation for Qualcomm Snapdragon boards.
4 *
5 * Copyright (c) 2023 Linaro Ltd.
6 * Author: Caleb Connolly <caleb.connolly@linaro.org>
7 */
8
9#define LOG_DEBUG
10
11#include <asm/armv8/mmu.h>
12#include <asm/gpio.h>
13#include <asm/io.h>
14#include <asm/psci.h>
15#include <asm/system.h>
16#include <dm/device.h>
17#include <env.h>
18#include <init.h>
19#include <linux/arm-smccc.h>
20#include <linux/bug.h>
21#include <linux/psci.h>
22#include <linux/sizes.h>
Caleb Connolly16da8c72023-10-03 11:35:40 +010023#include <lmb.h>
Caleb Connollye6c284b2023-11-20 20:48:00 +000024#include <malloc.h>
25
Caleb Connollyb55c0f82023-12-07 00:13:07 +000026#include "qcom-priv.h"
27
Caleb Connollye6c284b2023-11-20 20:48:00 +000028DECLARE_GLOBAL_DATA_PTR;
29
30static struct mm_region rbx_mem_map[CONFIG_NR_DRAM_BANKS + 2] = { { 0 } };
31
32struct mm_region *mem_map = rbx_mem_map;
33
34int dram_init(void)
35{
36 return fdtdec_setup_mem_size_base();
37}
38
39int dram_init_banksize(void)
40{
Caleb Connolly00bd7312023-12-11 16:02:20 +000041 int ret, i;
Caleb Connollye6c284b2023-11-20 20:48:00 +000042 phys_addr_t start, size;
43
44 ret = fdtdec_setup_memory_banksize();
45 if (ret < 0)
46 return ret;
47
Caleb Connollye6c284b2023-11-20 20:48:00 +000048 /* Some bootloaders populate the RAM banks in the wrong order -_- */
49 start = gd->bd->bi_dram[1].start;
50 size = gd->bd->bi_dram[1].size;
51 if (size && start < gd->bd->bi_dram[0].start) {
52 debug("Swapping DRAM banks\n");
53 gd->bd->bi_dram[1].start = gd->bd->bi_dram[0].start;
54 gd->bd->bi_dram[1].size = gd->bd->bi_dram[0].size;
55 gd->bd->bi_dram[0].start = start;
56 gd->bd->bi_dram[0].size = size;
57 }
58
Caleb Connolly00bd7312023-12-11 16:02:20 +000059 for (i = 0; gd->bd->bi_dram[i].size; i++) {
60 size = ALIGN(gd->bd->bi_dram[i].size, SZ_4K);
61 /*
62 * If you're filling out the /memory node manually, the size value should
63 * end in 3 zeros. Better yet, let ABL populate it for you.
64 */
65 WARN_ONCE(gd->bd->bi_dram[i].size != size,
66 "\nDRAM bank %d size not aligned to 4K! Patching things up...\n", i);
67 gd->bd->bi_dram[i].size = size;
68 }
69
Caleb Connollye6c284b2023-11-20 20:48:00 +000070 return 0;
71}
72
73static 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
84void *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
Caleb Connollye6c284b2023-11-20 20:48:00 +0000104/*
105 * Some boards still need board specific init code, they can implement that by
106 * overriding this function.
107 *
108 * FIXME: get rid of board specific init code
109 */
110void __weak qcom_board_init(void)
111{
112}
113
114int board_init(void)
115{
116 show_psci_version();
117 qcom_board_init();
118 return 0;
119}
120
Caleb Connolly28a30542023-10-17 13:03:32 +0100121/* Sets up the "board", and "soc" environment variables as well as constructing the devicetree
122 * path, with a few quirks to handle non-standard dtb filenames. This is not meant to be a
123 * comprehensive solution to automatically picking the DTB, but aims to be correct for the
124 * majority case. For most devices it should be possible to make this algorithm work by
125 * adjusting the root compatible property in the U-Boot DTS. Handling devices with multiple
126 * variants that are all supported by a single U-Boot image will require implementing device-
127 * specific detection.
128 */
129static void configure_env(void)
130{
131 const char *first_compat, *last_compat;
132 char *tmp;
133 char buf[32] = { 0 };
134 /*
135 * Most DTB filenames follow the scheme: qcom/<soc>-[vendor]-<board>.dtb
136 * The vendor is skipped when it's a Qualcomm reference board, or the
137 * db845c.
138 */
139 char dt_path[64] = { 0 };
140 int compat_count, ret;
141 ofnode root;
142
143 root = ofnode_root();
144 /* This is almost always 2, but be explicit that we want the first and last compatibles
145 * not the first and second.
146 */
147 compat_count = ofnode_read_string_count(root, "compatible");
148 if (compat_count < 2) {
149 log_warning("%s: only one root compatible bailing!\n", __func__);
150 return;
151 }
152
153 /* The most specific device compatible (e.g. "thundercomm,db845c") */
154 ret = ofnode_read_string_index(root, "compatible", 0, &first_compat);
155 if (ret < 0) {
156 log_warning("Can't read first compatible\n");
157 return;
158 }
159
160 /* The last compatible is always the SoC compatible */
161 ret = ofnode_read_string_index(root, "compatible", compat_count - 1, &last_compat);
162 if (ret < 0) {
163 log_warning("Can't read second compatible\n");
164 return;
165 }
166
167 /* Copy the second compat (e.g. "qcom,sdm845") into buf */
168 strlcpy(buf, last_compat, sizeof(buf) - 1);
169 tmp = buf;
170
171 /* strsep() is destructive, it replaces the comma with a \0 */
172 if (!strsep(&tmp, ",")) {
173 log_warning("second compatible '%s' has no ','\n", buf);
174 return;
175 }
176
177 /* tmp now points to just the "sdm845" part of the string */
178 env_set("soc", tmp);
179
180 /* Now figure out the "board" part from the first compatible */
181 memset(buf, 0, sizeof(buf));
182 strlcpy(buf, first_compat, sizeof(buf) - 1);
183 tmp = buf;
184
185 /* The Qualcomm reference boards (RBx, HDK, etc) */
186 if (!strncmp("qcom", buf, strlen("qcom"))) {
187 /*
188 * They all have the first compatible as "qcom,<soc>-<board>"
189 * (e.g. "qcom,qrb5165-rb5"). We extract just the part after
190 * the dash.
191 */
192 if (!strsep(&tmp, "-")) {
193 log_warning("compatible '%s' has no '-'\n", buf);
194 return;
195 }
196 /* tmp is now "rb5" */
197 env_set("board", tmp);
198 } else {
199 if (!strsep(&tmp, ",")) {
200 log_warning("compatible '%s' has no ','\n", buf);
201 return;
202 }
203 /* for thundercomm we just want the bit after the comma (e.g. "db845c"),
204 * for all other boards we replace the comma with a '-' and take both
205 * (e.g. "oneplus-enchilada")
206 */
207 if (!strncmp("thundercomm", buf, strlen("thundercomm"))) {
208 env_set("board", tmp);
209 } else {
210 *(tmp - 1) = '-';
211 env_set("board", buf);
212 }
213 }
214
215 /* Now build the full path name */
216 snprintf(dt_path, sizeof(dt_path), "qcom/%s-%s.dtb",
217 env_get("soc"), env_get("board"));
218 env_set("fdtfile", dt_path);
Caleb Connollye5baf282024-02-23 11:39:26 +0000219
220 qcom_set_serialno();
Caleb Connolly28a30542023-10-17 13:03:32 +0100221}
222
Caleb Connolly16da8c72023-10-03 11:35:40 +0100223void __weak qcom_late_init(void)
224{
225}
226
227#define KERNEL_COMP_SIZE SZ_64M
228#define SZ_96M (SZ_64M + SZ_32M)
Caleb Connollydcca2dd2023-12-04 15:00:37 +0000229#ifdef CONFIG_FASTBOOT_BUF_SIZE
230#define FASTBOOT_BUF_SIZE CONFIG_FASTBOOT_BUF_SIZE
231#else
232#define FASTBOOT_BUF_SIZE 0
233#endif
Caleb Connolly16da8c72023-10-03 11:35:40 +0100234
235#define addr_alloc(lmb, size) lmb_alloc(lmb, size, SZ_2M)
236
237/* Stolen from arch/arm/mach-apple/board.c */
238int board_late_init(void)
239{
240 struct lmb lmb;
241 u32 status = 0;
242
243 lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
244
245 /* We need to be fairly conservative here as we support boards with just 1G of TOTAL RAM */
246 status |= env_set_hex("kernel_addr_r", addr_alloc(&lmb, SZ_128M));
247 status |= env_set_hex("ramdisk_addr_r", addr_alloc(&lmb, SZ_96M));
248 status |= env_set_hex("kernel_comp_addr_r", addr_alloc(&lmb, KERNEL_COMP_SIZE));
249 status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE);
Caleb Connollydcca2dd2023-12-04 15:00:37 +0000250 if (IS_ENABLED(CONFIG_FASTBOOT))
251 status |= env_set_hex("fastboot_addr_r", addr_alloc(&lmb, FASTBOOT_BUF_SIZE));
Caleb Connolly16da8c72023-10-03 11:35:40 +0100252 status |= env_set_hex("scriptaddr", addr_alloc(&lmb, SZ_4M));
253 status |= env_set_hex("pxefile_addr_r", addr_alloc(&lmb, SZ_4M));
254 status |= env_set_hex("fdt_addr_r", addr_alloc(&lmb, SZ_2M));
255
256 if (status)
257 log_warning("%s: Failed to set run time variables\n", __func__);
258
Caleb Connolly28a30542023-10-17 13:03:32 +0100259 configure_env();
Caleb Connolly16da8c72023-10-03 11:35:40 +0100260 qcom_late_init();
261
Caleb Connollyb55c0f82023-12-07 00:13:07 +0000262 /* Configure the dfu_string for capsule updates */
263 qcom_configure_capsule_updates();
264
Caleb Connolly16da8c72023-10-03 11:35:40 +0100265 return 0;
266}
267
Caleb Connollye6c284b2023-11-20 20:48:00 +0000268static void build_mem_map(void)
269{
270 int i;
271
272 /*
273 * Ensure the peripheral block is sized to correctly cover the address range
274 * up to the first memory bank.
275 * Don't map the first page to ensure that we actually trigger an abort on a
276 * null pointer access rather than just hanging.
277 * FIXME: we should probably split this into more precise regions
278 */
279 mem_map[0].phys = 0x1000;
280 mem_map[0].virt = mem_map[0].phys;
281 mem_map[0].size = gd->bd->bi_dram[0].start - mem_map[0].phys;
282 mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
283 PTE_BLOCK_NON_SHARE |
284 PTE_BLOCK_PXN | PTE_BLOCK_UXN;
285
286 debug("Configured memory map:\n");
287 debug(" 0x%016llx - 0x%016llx: Peripheral block\n",
288 mem_map[0].phys, mem_map[0].phys + mem_map[0].size);
289
290 /*
291 * Now add memory map entries for each DRAM bank, ensuring we don't
292 * overwrite the list terminator
293 */
294 for (i = 0; i < ARRAY_SIZE(rbx_mem_map) - 2 && gd->bd->bi_dram[i].size; i++) {
295 if (i == ARRAY_SIZE(rbx_mem_map) - 1) {
296 log_warning("Too many DRAM banks!\n");
297 break;
298 }
299 mem_map[i + 1].phys = gd->bd->bi_dram[i].start;
300 mem_map[i + 1].virt = mem_map[i + 1].phys;
301 mem_map[i + 1].size = gd->bd->bi_dram[i].size;
302 mem_map[i + 1].attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
303 PTE_BLOCK_INNER_SHARE;
304
305 debug(" 0x%016llx - 0x%016llx: DDR bank %d\n",
306 mem_map[i + 1].phys, mem_map[i + 1].phys + mem_map[i + 1].size, i);
307 }
308}
309
310u64 get_page_table_size(void)
311{
312 return SZ_64K;
313}
314
315void enable_caches(void)
316{
317 build_mem_map();
318
319 icache_enable();
320 dcache_enable();
321}