1 /*
2  * Copyright (c) 2016-2018, NVIDIA CORPORATION.
3  *
4  * SPDX-License-Identifier: GPL-2.0+
5  */
6 
7 #include <stdlib.h>
8 #include <common.h>
9 #include <fdt_support.h>
10 #include <fdtdec.h>
11 #include <asm/arch/tegra.h>
12 #include <asm/armv8/mmu.h>
13 
14 DECLARE_GLOBAL_DATA_PTR;
15 
16 extern unsigned long nvtboot_boot_x0;
17 
18 /*
19  * The following few functions run late during the boot process and dynamically
20  * calculate the load address of various binaries. To keep track of multiple
21  * allocations, some writable list of RAM banks must be used. tegra_mem_map[]
22  * is used for this purpose to avoid making yet another copy of the list of RAM
23  * banks. This is safe because tegra_mem_map[] is only used once during very
24  * early boot to create U-Boot's page tables, long before this code runs. If
25  * this assumption becomes invalid later, we can just fix the code to copy the
26  * list of RAM banks into some private data structure before running.
27  */
28 
29 extern struct mm_region tegra_mem_map[];
30 
31 static char *gen_varname(const char *var, const char *ext)
32 {
33 	size_t len_var = strlen(var);
34 	size_t len_ext = strlen(ext);
35 	size_t len = len_var + len_ext + 1;
36 	char *varext = malloc(len);
37 
38 	if (!varext)
39 		return 0;
40 	strcpy(varext, var);
41 	strcpy(varext + len_var, ext);
42 	return varext;
43 }
44 
45 static void mark_ram_allocated(int bank, u64 allocated_start, u64 allocated_end)
46 {
47 	u64 bank_start = tegra_mem_map[bank].virt;
48 	u64 bank_size = tegra_mem_map[bank].size;
49 	u64 bank_end = bank_start + bank_size;
50 	bool keep_front = allocated_start != bank_start;
51 	bool keep_tail = allocated_end != bank_end;
52 
53 	if (keep_front && keep_tail) {
54 		/*
55 		 * There are CONFIG_NR_DRAM_BANKS DRAM entries in the array,
56 		 * starting at index 1 (index 0 is MMIO). So, we are at DRAM
57 		 * entry "bank" not "bank - 1" as for a typical 0-base array.
58 		 * The number of remaining DRAM entries is therefore
59 		 * "CONFIG_NR_DRAM_BANKS - bank". We want to duplicate the
60 		 * current entry and shift up the remaining entries, dropping
61 		 * the last one. Thus, we must copy one fewer entry than the
62 		 * number remaining.
63 		 */
64 		memmove(&tegra_mem_map[bank + 1], &tegra_mem_map[bank],
65 			CONFIG_NR_DRAM_BANKS - bank - 1);
66 		tegra_mem_map[bank].size = allocated_start - bank_start;
67 		bank++;
68 		tegra_mem_map[bank].virt = allocated_end;
69 		tegra_mem_map[bank].phys = allocated_end;
70 		tegra_mem_map[bank].size = bank_end - allocated_end;
71 	} else if (keep_front) {
72 		tegra_mem_map[bank].size = allocated_start - bank_start;
73 	} else if (keep_tail) {
74 		tegra_mem_map[bank].virt = allocated_end;
75 		tegra_mem_map[bank].phys = allocated_end;
76 		tegra_mem_map[bank].size = bank_end - allocated_end;
77 	} else {
78 		/*
79 		 * We could move all subsequent banks down in the array but
80 		 * that's not necessary for subsequent allocations to work, so
81 		 * we skip doing so.
82 		 */
83 		tegra_mem_map[bank].size = 0;
84 	}
85 }
86 
87 static void reserve_ram(u64 start, u64 size)
88 {
89 	int bank;
90 	u64 end = start + size;
91 
92 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
93 		u64 bank_start = tegra_mem_map[bank].virt;
94 		u64 bank_size = tegra_mem_map[bank].size;
95 		u64 bank_end = bank_start + bank_size;
96 
97 		if (end <= bank_start || start > bank_end)
98 			continue;
99 		mark_ram_allocated(bank, start, end);
100 		break;
101 	}
102 }
103 
104 static u64 alloc_ram(u64 size, u64 align, u64 offset)
105 {
106 	int bank;
107 
108 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
109 		u64 bank_start = tegra_mem_map[bank].virt;
110 		u64 bank_size = tegra_mem_map[bank].size;
111 		u64 bank_end = bank_start + bank_size;
112 		u64 allocated = ROUND(bank_start, align) + offset;
113 		u64 allocated_end = allocated + size;
114 
115 		if (allocated_end > bank_end)
116 			continue;
117 		mark_ram_allocated(bank, allocated, allocated_end);
118 		return allocated;
119 	}
120 	return 0;
121 }
122 
123 static void set_calculated_aliases(char *aliases, u64 address)
124 {
125 	char *tmp, *alias;
126 	int err;
127 
128 	aliases = strdup(aliases);
129 	if (!aliases) {
130 		pr_err("strdup(aliases) failed");
131 		return;
132 	}
133 
134 	tmp = aliases;
135 	while (true) {
136 		alias = strsep(&tmp, " ");
137 		if (!alias)
138 			break;
139 		debug("%s: alias: %s\n", __func__, alias);
140 		err = env_set_hex(alias, address);
141 		if (err)
142 			pr_err("Could not set %s\n", alias);
143 	}
144 
145 	free(aliases);
146 }
147 
148 static void set_calculated_env_var(const char *var)
149 {
150 	char *var_size;
151 	char *var_align;
152 	char *var_offset;
153 	char *var_aliases;
154 	u64 size;
155 	u64 align;
156 	u64 offset;
157 	char *aliases;
158 	u64 address;
159 	int err;
160 
161 	var_size = gen_varname(var, "_size");
162 	if (!var_size)
163 		return;
164 	var_align = gen_varname(var, "_align");
165 	if (!var_align)
166 		goto out_free_var_size;
167 	var_offset = gen_varname(var, "_offset");
168 	if (!var_offset)
169 		goto out_free_var_align;
170 	var_aliases = gen_varname(var, "_aliases");
171 	if (!var_aliases)
172 		goto out_free_var_offset;
173 
174 	size = env_get_hex(var_size, 0);
175 	if (!size) {
176 		pr_err("%s not set or zero\n", var_size);
177 		goto out_free_var_aliases;
178 	}
179 	align = env_get_hex(var_align, 1);
180 	/* Handle extant variables, but with a value of 0 */
181 	if (!align)
182 		align = 1;
183 	offset = env_get_hex(var_offset, 0);
184 	aliases = env_get(var_aliases);
185 
186 	debug("%s: Calc var %s; size=%llx, align=%llx, offset=%llx\n",
187 	      __func__, var, size, align, offset);
188 	if (aliases)
189 		debug("%s: Aliases: %s\n", __func__, aliases);
190 
191 	address = alloc_ram(size, align, offset);
192 	if (!address) {
193 		pr_err("Could not allocate %s\n", var);
194 		goto out_free_var_aliases;
195 	}
196 	debug("%s: Address %llx\n", __func__, address);
197 
198 	err = env_set_hex(var, address);
199 	if (err)
200 		pr_err("Could not set %s\n", var);
201 	if (aliases)
202 		set_calculated_aliases(aliases, address);
203 
204 out_free_var_aliases:
205 	free(var_aliases);
206 out_free_var_offset:
207 	free(var_offset);
208 out_free_var_align:
209 	free(var_align);
210 out_free_var_size:
211 	free(var_size);
212 }
213 
214 #ifdef DEBUG
215 static void dump_ram_banks(void)
216 {
217 	int bank;
218 
219 	for (bank = 1; bank <= CONFIG_NR_DRAM_BANKS; bank++) {
220 		u64 bank_start = tegra_mem_map[bank].virt;
221 		u64 bank_size = tegra_mem_map[bank].size;
222 		u64 bank_end = bank_start + bank_size;
223 
224 		if (!bank_size)
225 			continue;
226 		printf("%d: %010llx..%010llx (+%010llx)\n", bank - 1,
227 		       bank_start, bank_end, bank_size);
228 	}
229 }
230 #endif
231 
232 static void set_calculated_env_vars(void)
233 {
234 	char *vars, *tmp, *var;
235 
236 #ifdef DEBUG
237 	printf("RAM banks before any calculated env. var.s:\n");
238 	dump_ram_banks();
239 #endif
240 
241 	reserve_ram(nvtboot_boot_x0, fdt_totalsize(nvtboot_boot_x0));
242 
243 #ifdef DEBUG
244 	printf("RAM after reserving cboot DTB:\n");
245 	dump_ram_banks();
246 #endif
247 
248 	vars = env_get("calculated_vars");
249 	if (!vars) {
250 		debug("%s: No env var calculated_vars\n", __func__);
251 		return;
252 	}
253 
254 	vars = strdup(vars);
255 	if (!vars) {
256 		pr_err("strdup(calculated_vars) failed");
257 		return;
258 	}
259 
260 	tmp = vars;
261 	while (true) {
262 		var = strsep(&tmp, " ");
263 		if (!var)
264 			break;
265 		debug("%s: var: %s\n", __func__, var);
266 		set_calculated_env_var(var);
267 #ifdef DEBUG
268 		printf("RAM banks affter allocating %s:\n", var);
269 		dump_ram_banks();
270 #endif
271 	}
272 
273 	free(vars);
274 }
275 
276 static int set_fdt_addr(void)
277 {
278 	int ret;
279 
280 	ret = env_set_hex("fdt_addr", nvtboot_boot_x0);
281 	if (ret) {
282 		printf("Failed to set fdt_addr to point at DTB: %d\n", ret);
283 		return ret;
284 	}
285 
286 	return 0;
287 }
288 
289 /*
290  * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's
291  * ethaddr environment variable if possible.
292  */
293 static int set_ethaddr_from_nvtboot(void)
294 {
295 	const void *nvtboot_blob = (void *)nvtboot_boot_x0;
296 	int ret, node, len;
297 	const u32 *prop;
298 
299 	/* Already a valid address in the environment? If so, keep it */
300 	if (env_get("ethaddr"))
301 		return 0;
302 
303 	node = fdt_path_offset(nvtboot_blob, "/chosen");
304 	if (node < 0) {
305 		printf("Can't find /chosen node in nvtboot DTB\n");
306 		return node;
307 	}
308 	prop = fdt_getprop(nvtboot_blob, node, "nvidia,ether-mac", &len);
309 	if (!prop) {
310 		printf("Can't find nvidia,ether-mac property in nvtboot DTB\n");
311 		return -ENOENT;
312 	}
313 
314 	ret = env_set("ethaddr", (void *)prop);
315 	if (ret) {
316 		printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret);
317 		return ret;
318 	}
319 
320 	return 0;
321 }
322 
323 int tegra_soc_board_init_late(void)
324 {
325 	set_calculated_env_vars();
326 	/*
327 	 * Ignore errors here; the value may not be used depending on
328 	 * extlinux.conf or boot script content.
329 	 */
330 	set_fdt_addr();
331 	/* Ignore errors here; not all cases care about Ethernet addresses */
332 	set_ethaddr_from_nvtboot();
333 
334 	return 0;
335 }
336