xref: /rk3399_rockchip-uboot/lib/sysmem.c (revision 4454e90b43786bac3a0d4c3bfddeafddf794d6c0)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4  */
5 
6 #include <common.h>
7 #include <sysmem.h>
8 #include <lmb.h>
9 #include <malloc.h>
10 #include <asm/io.h>
11 
12 DECLARE_GLOBAL_DATA_PTR;
13 
14 #define SYSMEM_MAGIC		0x4D454D53	/* "SMEM" */
15 #define SYSMEM_ALLOC_ANYWHERE	0
16 #define SYSMEM_ALLOC_NO_ALIGN	1
17 
18 #ifndef CONFIG_SYS_STACK_SIZE
19 #define CONFIG_SYS_STACK_SIZE	SZ_2M
20 #endif
21 
22 #define SIZE_MB(len)		((len) >> 20)
23 #define SIZE_KB(len)		(((len) % (1 << 20)) >> 10)
24 
25 #define SYSMEM_I(fmt, args...)	printf("Sysmem: "fmt, ##args)
26 #define SYSMEM_W(fmt, args...)	printf("Sysmem Warn: "fmt, ##args)
27 #define SYSMEM_E(fmt, args...)	printf("Sysmem Error: "fmt, ##args)
28 #define SYSMEM_D(fmt, args...)	 debug("Sysmem Debug: "fmt, ##args)
29 
30 static struct sysmem plat_sysmem;	/* Global for platform */
31 
32 struct sysmem_check {
33 	uint32_t magic;
34 };
35 
36 static int sysmem_has_init(void)
37 {
38 	if (!plat_sysmem.has_init) {
39 		SYSMEM_E("Framework is not initialized\n");
40 		return 0;
41 	}
42 
43 	return 1;
44 }
45 
46 void sysmem_dump(void)
47 {
48 #ifdef DEBUG
49 	struct sysmem *sysmem = &plat_sysmem;
50 	struct lmb *lmb = &sysmem->lmb;
51 	struct sysmem_property *prop;
52 	struct sysmem_check *check;
53 	struct list_head *node;
54 	ulong memory_size = 0;
55 	ulong reserved_size = 0;
56 	ulong allocated_size = 0;
57 	ulong i;
58 
59 	if (!sysmem_has_init())
60 		return;
61 
62 	printf("\nsysmem_dump_all:\n");
63 
64 	/* Memory pool */
65 	printf("    ------------------------------------------------------\n");
66 	for (i = 0; i < lmb->memory.cnt; i++) {
67 		memory_size += lmb->memory.region[i].size;
68 		printf("    memory.rgn[%ld].base     = 0x%08lx\n", i,
69 		       (ulong)lmb->memory.region[i].base);
70 		printf("		 .size     = 0x%08lx\n",
71 		       (ulong)lmb->memory.region[i].size);
72 	}
73 	printf("\n    memory.total	   = 0x%08lx (%ld MiB. %ld KiB)\n",
74 	       (ulong)memory_size,
75 	       SIZE_MB((ulong)memory_size),
76 	       SIZE_KB((ulong)memory_size));
77 
78 	/* Reserved */
79 	i = 0;
80 	printf("    ------------------------------------------------------\n");
81 	list_for_each(node, &sysmem->reserved_head) {
82 		prop = list_entry(node, struct sysmem_property, node);
83 		reserved_size += prop->size;
84 		printf("    reserved.rgn[%ld].name   = \"%s\"\n", i, prop->name);
85 		printf("		   .base   = 0x%08lx\n",
86 		       (ulong)prop->base);
87 		printf("		   .size   = 0x%08lx\n",
88 		       (ulong)prop->size);
89 		i++;
90 	}
91 	printf("\n    reserved.total	   = 0x%08lx (%ld MiB. %ld KiB)\n",
92 	       (ulong)reserved_size,
93 	       SIZE_MB((ulong)reserved_size),
94 	       SIZE_KB((ulong)reserved_size));
95 
96 	/* Allocated */
97 	i = 0;
98 	printf("    ------------------------------------------------------\n");
99 	list_for_each(node, &sysmem->allocated_head) {
100 		prop = list_entry(node, struct sysmem_property, node);
101 		allocated_size += prop->size;
102 		check = (struct sysmem_check *)
103 				(prop->base + prop->size - sizeof(*check));
104 		printf("    allocated.rgn[%ld].name  = \"%s\"%s\n",
105 		       i, prop->name,
106 		       check->magic != SYSMEM_MAGIC ? "	(Overflow)" : "");
107 		printf("		    .base  = 0x%08lx\n",
108 		       (ulong)prop->base);
109 		printf("		    .size  = 0x%08lx\n",
110 		       (ulong)prop->size);
111 		i++;
112 	}
113 	printf("\n    allocated.total	   = 0x%08lx (%ld MiB. %ld KiB)\n",
114 	       (ulong)allocated_size,
115 	       SIZE_MB((ulong)allocated_size),
116 	       SIZE_KB((ulong)allocated_size));
117 
118 	/* LMB core reserved */
119 	printf("    ------------------------------------------------------\n");
120 	reserved_size = 0;
121 	for (i = 0; i < lmb->reserved.cnt; i++) {
122 		reserved_size += lmb->reserved.region[i].size;
123 		printf("    LMB.reserved[%ld].base   = 0x%08lx\n", i,
124 		       (ulong)lmb->reserved.region[i].base);
125 		printf("		   .size   = 0x%08lx\n",
126 		       (ulong)lmb->reserved.region[i].size);
127 	}
128 
129 	printf("\n    reserved.core.total	   = 0x%08lx (%ld MiB. %ld KiB)\n",
130 	       (ulong)reserved_size,
131 	       SIZE_MB((ulong)reserved_size),
132 	       SIZE_KB((ulong)reserved_size));
133 	printf("    ------------------------------------------------------\n\n");
134 #endif
135 }
136 
137 int sysmem_check(void)
138 {
139 	struct sysmem *sysmem = &plat_sysmem;
140 	struct sysmem_property *prop;
141 	struct sysmem_check *check;
142 	struct list_head *node;
143 	int ret = 0;
144 
145 	if (!sysmem_has_init())
146 		return -ENOSYS;
147 
148 	/* Check allocated */
149 	list_for_each(node, &sysmem->allocated_head) {
150 		prop = list_entry(node, struct sysmem_property, node);
151 		check = (struct sysmem_check *)
152 				(prop->base + prop->size - sizeof(*check));
153 		if (check->magic != SYSMEM_MAGIC) {
154 			ret = -EOVERFLOW;
155 			SYSMEM_E("\"%s\" (base=0x%08lx, size=0x%lx) is Overflow!\n",
156 				 prop->name, (ulong)prop->base, (ulong)prop->size);
157 		}
158 	}
159 
160 	/* Check stack */
161 	check = (struct sysmem_check *)(gd->start_addr_sp - CONFIG_SYS_STACK_SIZE);
162 	if (check->magic != SYSMEM_MAGIC) {
163 		ret = -EOVERFLOW;
164 		SYSMEM_E("Runtime stack is Overflow!\n");
165 	}
166 
167 	return ret;
168 }
169 
170 int sysmem_dump_check(void)
171 {
172 	sysmem_dump();
173 
174 	return sysmem_check();
175 }
176 
177 static int sysmem_is_overlap(phys_addr_t base1, phys_size_t size1,
178 			     phys_addr_t base2, phys_size_t size2)
179 {
180 	return ((base1 < (base2 + size2)) && (base2 < (base1 + size1)));
181 }
182 
183 int sysmem_add(phys_addr_t base, phys_size_t size)
184 {
185 	struct sysmem *sysmem = &plat_sysmem;
186 	int ret;
187 
188 	if (!sysmem_has_init())
189 		return -ENOSYS;
190 
191 	ret = lmb_add(&sysmem->lmb, base, size);
192 	if (ret < 0)
193 		SYSMEM_E("Failed to add sysmem at 0x%lx for 0x%lx size\n",
194 			 (ulong)base, (ulong)size);
195 
196 	return (ret >= 0) ? 0 : ret;
197 }
198 
199 int sysmem_reserve(const char *name, phys_addr_t base, phys_size_t size)
200 {
201 	struct sysmem *sysmem = &plat_sysmem;
202 	struct sysmem_property *prop;
203 	struct list_head *node;
204 	int ret = 0;
205 
206 	if (!sysmem_has_init())
207 		return -ENOSYS;
208 
209 	if (!name) {
210 		SYSMEM_E("NULL name for reserved sysmem\n");
211 		return -EINVAL;
212 	}
213 
214 	/* Check overlap */
215 	list_for_each(node, &sysmem->reserved_head) {
216 		prop = list_entry(node, struct sysmem_property, node);
217 		if (!strcmp(prop->name, name)) {
218 			SYSMEM_E("Failed to double reserve for existence \"%s\"\n", name);
219 			return -EEXIST;
220 		} else if (sysmem_is_overlap(prop->base, prop->size, base, size)) {
221 			SYSMEM_D("\"%s\" (base=0x%08lx, size=0x%lx) reserve is "
222 				 "overlap with existence \"%s\" (base=0x%08lx, size=0x%lx)\n",
223 				 name, (ulong)base, (ulong)size, prop->name,
224 				 (ulong)prop->base, (ulong)prop->size);
225 		}
226 	}
227 
228 	ret = lmb_reserve(&sysmem->lmb, base, size);
229 	if (ret >= 0) {
230 		prop = malloc(sizeof(*prop));
231 		if (!prop) {
232 			SYSMEM_E("No memory for \"%s\" reserve sysmem\n", name);
233 			return -ENOMEM;
234 		}
235 
236 		prop->name = name;
237 		prop->base = base;
238 		prop->size = size;
239 		list_add_tail(&prop->node, &sysmem->reserved_head);
240 	} else {
241 		SYSMEM_E("Failed to reserve \"%s\" at 0x%lx\n", name, (ulong)base);
242 		return -EINVAL;
243 	}
244 
245 	return 0;
246 }
247 
248 void *sysmem_alloc_align_base(const char *name,
249 			      phys_addr_t base,
250 			      phys_size_t size,
251 			      ulong align)
252 {
253 	struct sysmem *sysmem = &plat_sysmem;
254 	struct sysmem_property *prop;
255 	struct sysmem_check *check;
256 	struct list_head *node;
257 	phys_addr_t paddr;
258 	phys_addr_t alloc_base;
259 	phys_size_t alloc_size;
260 
261 	if (!sysmem_has_init())
262 		return NULL;
263 
264 	if (!name) {
265 		SYSMEM_E("NULL name for alloc sysmem\n");
266 		return NULL;
267 	}
268 
269 	if (!IS_ALIGNED(base, 4)) {
270 		SYSMEM_E("\"%s\" base=0x%08lx is not 4-byte aligned\n", name, (ulong)base);
271 		return NULL;
272 	}
273 
274 	/* Must be 4-byte aligned */
275 	size = ALIGN(size, 4);
276 
277 	/* Already allocated ? */
278 	list_for_each(node, &sysmem->allocated_head) {
279 		prop = list_entry(node, struct sysmem_property, node);
280 		if (!strcmp(prop->name, name)) {
281 			SYSMEM_E("Failed to double alloc for existence \"%s\"\n", name);
282 			return NULL;
283 		} else if (sysmem_is_overlap(prop->base, prop->size, base, size)) {
284 			SYSMEM_E("\"%s\" (base=0x%08lx, size=0x%lx) alloc is "
285 				 "overlap with existence \"%s\" (base=0x%08lx, size=0x%lx)\n",
286 				 name, (ulong)base, (ulong)size,
287 				 prop->name, (ulong)prop->base,
288 				 (ulong)prop->size);
289 			return NULL;
290 		}
291 	}
292 
293 	alloc_size = size + sizeof(*check);
294 	if (base == SYSMEM_ALLOC_ANYWHERE)
295 		alloc_base = base;
296 	else
297 		alloc_base = base + alloc_size;	/* LMB is align down alloc mechanism */
298 
299 	paddr = lmb_alloc_base(&sysmem->lmb, alloc_size, align, alloc_base);
300 	if (paddr) {
301 		if  ((paddr == base) || (base == SYSMEM_ALLOC_ANYWHERE)) {
302 			prop = malloc(sizeof(*prop));
303 			if (!prop) {
304 				SYSMEM_E("No memory for \"%s\" alloc sysmem\n", name);
305 				return NULL;
306 			}
307 
308 			prop->name = name;
309 			prop->base = paddr;
310 			prop->size = alloc_size;
311 			sysmem->allocated_cnt++;
312 
313 			check = (struct sysmem_check *)(paddr + size);
314 			check->magic = SYSMEM_MAGIC;
315 
316 			list_add_tail(&prop->node, &sysmem->allocated_head);
317 		} else {
318 			SYSMEM_E("Failed to alloc \"%s\" at expect 0x%lx but "
319 				 "alloc at 0x%lx\n",
320 				 name, (ulong)base, (ulong)paddr);
321 			return NULL;
322 		}
323 	} else {
324 		SYSMEM_E("Failed to alloc \"%s\" at 0x%lx\n", name, (ulong)base);
325 	}
326 
327 	SYSMEM_D("Alloc: \"%s\", paddr=0x%lx, size=0x%lx, align=0x%x, anywhere=%d\n",
328 		 name, (ulong)paddr, (ulong)size, (u32)align, !base);
329 
330 	return (void *)paddr;
331 }
332 
333 void *sysmem_alloc_align(const char *name, phys_size_t size, ulong align)
334 {
335 	return sysmem_alloc_align_base(name,
336 				       SYSMEM_ALLOC_ANYWHERE,
337 				       size,
338 				       align);
339 }
340 
341 void *sysmem_alloc_base(const char *name, phys_addr_t base, phys_size_t size)
342 {
343 	return sysmem_alloc_align_base(name,
344 				       base,
345 				       size,
346 				       SYSMEM_ALLOC_NO_ALIGN);
347 }
348 
349 void *sysmem_alloc(const char *name, phys_size_t size)
350 {
351 	return sysmem_alloc_align_base(name,
352 				       SYSMEM_ALLOC_ANYWHERE,
353 				       size,
354 				       SYSMEM_ALLOC_NO_ALIGN);
355 }
356 
357 int sysmem_free(phys_addr_t base)
358 {
359 	struct sysmem *sysmem = &plat_sysmem;
360 	struct sysmem_property *prop;
361 	struct list_head *node;
362 	int found = 0;
363 	int ret;
364 
365 	if (!sysmem_has_init())
366 		return -ENOSYS;
367 
368 	/* Find existence */
369 	list_for_each(node, &sysmem->allocated_head) {
370 		prop = list_entry(node, struct sysmem_property, node);
371 		if (prop->base == base) {
372 			found = 1;
373 			break;
374 		}
375 	}
376 
377 	if (!found) {
378 		SYSMEM_E("Failed to free no allocated sysmem at 0x%lx\n", (ulong)base);
379 		return -EINVAL;
380 	}
381 
382 	ret = lmb_free(&sysmem->lmb, prop->base, prop->size);
383 	if (ret >= 0) {
384 		SYSMEM_D("Free: \"%s\", paddr=0x%lx, size=0x%lx\n",
385 			 prop->name, (ulong)prop->base, (ulong)prop->size);
386 		sysmem->allocated_cnt--;
387 		list_del(&prop->node);
388 		free(prop);
389 	} else {
390 		SYSMEM_E("Failed to free \"%s\" at 0x%lx\n", prop->name, (ulong)base);
391 	}
392 
393 	return (ret >= 0) ? 0 : ret;
394 }
395 
396 int sysmem_init(void)
397 {
398 	struct sysmem *sysmem = &plat_sysmem;
399 	struct sysmem_check *check;
400 	phys_addr_t mem_start;
401 	phys_size_t mem_size;
402 	int ret;
403 
404 	SYSMEM_I("init\n");
405 
406 	lmb_init(&sysmem->lmb);
407 	INIT_LIST_HEAD(&sysmem->allocated_head);
408 	INIT_LIST_HEAD(&sysmem->reserved_head);
409 	sysmem->allocated_cnt = 0;
410 	sysmem->has_init = true;
411 
412 	/* Add all available system memory */
413 #ifdef CONFIG_NR_DRAM_BANKS
414 	int i;
415 
416 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
417 		ret = sysmem_add(gd->bd->bi_dram[i].start,
418 				 gd->bd->bi_dram[i].size);
419 		if (ret) {
420 			SYSMEM_E("Failed to add sysmem from bi_dram[%d]\n", i);
421 			return ret;
422 		}
423 	}
424 #else
425 	mem_start = env_get_bootm_low();
426 	mem_size = env_get_bootm_size();
427 	ret = sysmem_add(mem_start, mem_size);
428 	if (ret) {
429 		SYSMEM_E("Failed to add sysmem from bootm_low/size\n");
430 		return ret;
431 	}
432 #endif
433 
434 	/* Reserved for arch */
435 	ret = arch_sysmem_reserve(sysmem);
436 	if (ret) {
437 		SYSMEM_E("Failed to reserve sysmem for arch\n");
438 		return ret;
439 	}
440 
441 	/* Reserved for board */
442 	ret = board_sysmem_reserve(sysmem);
443 	if (ret) {
444 		SYSMEM_E("Failed to reserve sysmem for board\n");
445 		return ret;
446 	}
447 
448 	/* Reserved for U-boot framework 'reserve_xxx()' */
449 	mem_start = gd->start_addr_sp - CONFIG_SYS_STACK_SIZE;
450 	mem_size = gd->ram_top - mem_start;
451 	check = (struct sysmem_check *)mem_start;
452 	check->magic = SYSMEM_MAGIC;
453 
454 	ret = sysmem_reserve("U-Boot", mem_start, mem_size);
455 	if (ret) {
456 		SYSMEM_E("Failed to reserve sysmem for U-Boot framework\n");
457 		return ret;
458 	}
459 
460 	sysmem_dump();
461 
462 	return 0;
463 }
464 
465 __weak int board_sysmem_reserve(struct sysmem *sysmem)
466 {
467 	/* please define platform specific board_sysmem_reserve() */
468 	return 0;
469 }
470 
471 __weak int arch_sysmem_reserve(struct sysmem *sysmem)
472 {
473 	/* please define platform specific arch_sysmem_reserve() */
474 	return 0;
475 }
476