xref: /rk3399_rockchip-uboot/lib/sysmem.c (revision 6aa65bb1ee0951865e27da81dde1de76c6d4687e)
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_W("\"%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 	/* Already allocated ? */
270 	list_for_each(node, &sysmem->allocated_head) {
271 		prop = list_entry(node, struct sysmem_property, node);
272 		if (!strcmp(prop->name, name)) {
273 			SYSMEM_E("Failed to double alloc for existence \"%s\"\n", name);
274 			return NULL;
275 		} else if (sysmem_is_overlap(prop->base, prop->size, base, size)) {
276 			SYSMEM_E("\"%s\" (base=0x%08lx, size=0x%lx) alloc is "
277 				 "overlap with existence \"%s\" (base=0x%08lx, size=0x%lx)\n",
278 				 name, (ulong)base, (ulong)size,
279 				 prop->name, (ulong)prop->base,
280 				 (ulong)prop->size);
281 			return NULL;
282 		}
283 	}
284 
285 	alloc_size = size + sizeof(*check);
286 	if (base == SYSMEM_ALLOC_ANYWHERE)
287 		alloc_base = base;
288 	else
289 		alloc_base = base + alloc_size;	/* LMB is align down alloc mechanism */
290 
291 	paddr = lmb_alloc_base(&sysmem->lmb, alloc_size, align, alloc_base);
292 	if (paddr) {
293 		if  ((paddr == base) || (base == SYSMEM_ALLOC_ANYWHERE)) {
294 			prop = malloc(sizeof(*prop));
295 			if (!prop) {
296 				SYSMEM_E("No memory for \"%s\" alloc sysmem\n", name);
297 				return NULL;
298 			}
299 
300 			prop->name = name;
301 			prop->base = paddr;
302 			prop->size = alloc_size;
303 			sysmem->allocated_cnt++;
304 
305 			check = (struct sysmem_check *)(paddr + size);
306 			check->magic = SYSMEM_MAGIC;
307 
308 			list_add_tail(&prop->node, &sysmem->allocated_head);
309 		} else {
310 			SYSMEM_E("Failed to alloc \"%s\" at expect 0x%lx but "
311 				 "alloc at 0x%lx\n",
312 				 name, (ulong)base, (ulong)paddr);
313 			return NULL;
314 		}
315 	} else {
316 		SYSMEM_E("Failed to alloc \"%s\" at 0x%lx\n", name, (ulong)base);
317 	}
318 
319 	SYSMEM_D("Alloc: \"%s\", paddr=0x%lx, size=0x%lx, align=0x%x, anywhere=%d\n",
320 		 name, (ulong)paddr, (ulong)size, (u32)align, !base);
321 
322 	return (void *)paddr;
323 }
324 
325 void *sysmem_alloc_align(const char *name, phys_size_t size, ulong align)
326 {
327 	return sysmem_alloc_align_base(name,
328 				       SYSMEM_ALLOC_ANYWHERE,
329 				       size,
330 				       align);
331 }
332 
333 void *sysmem_alloc_base(const char *name, phys_addr_t base, phys_size_t size)
334 {
335 	return sysmem_alloc_align_base(name,
336 				       base,
337 				       size,
338 				       SYSMEM_ALLOC_NO_ALIGN);
339 }
340 
341 void *sysmem_alloc(const char *name, phys_size_t size)
342 {
343 	return sysmem_alloc_align_base(name,
344 				       SYSMEM_ALLOC_ANYWHERE,
345 				       size,
346 				       SYSMEM_ALLOC_NO_ALIGN);
347 }
348 
349 int sysmem_free(phys_addr_t base)
350 {
351 	struct sysmem *sysmem = &plat_sysmem;
352 	struct sysmem_property *prop;
353 	struct list_head *node;
354 	int found = 0;
355 	int ret;
356 
357 	if (!sysmem_has_init())
358 		return -ENOSYS;
359 
360 	/* Find existence */
361 	list_for_each(node, &sysmem->allocated_head) {
362 		prop = list_entry(node, struct sysmem_property, node);
363 		if (prop->base == base) {
364 			found = 1;
365 			break;
366 		}
367 	}
368 
369 	if (!found) {
370 		SYSMEM_E("Failed to free no allocated sysmem at 0x%lx\n", (ulong)base);
371 		return -EINVAL;
372 	}
373 
374 	ret = lmb_free(&sysmem->lmb, prop->base, prop->size);
375 	if (ret >= 0) {
376 		SYSMEM_I("Free: \"%s\", paddr=0x%lx, size=0x%lx\n",
377 			 prop->name, (ulong)prop->base, (ulong)prop->size);
378 		sysmem->allocated_cnt--;
379 		list_del(&prop->node);
380 		free(prop);
381 	} else {
382 		SYSMEM_E("Failed to free \"%s\" at 0x%lx\n", prop->name, (ulong)base);
383 	}
384 
385 	return (ret >= 0) ? 0 : ret;
386 }
387 
388 int sysmem_init(void)
389 {
390 	struct sysmem *sysmem = &plat_sysmem;
391 	struct sysmem_check *check;
392 	phys_addr_t mem_start;
393 	phys_size_t mem_size;
394 	int ret;
395 
396 	SYSMEM_I("init\n");
397 
398 	lmb_init(&sysmem->lmb);
399 	INIT_LIST_HEAD(&sysmem->allocated_head);
400 	INIT_LIST_HEAD(&sysmem->reserved_head);
401 	sysmem->allocated_cnt = 0;
402 	sysmem->has_init = true;
403 
404 	/* Add all available system memory */
405 #ifdef CONFIG_NR_DRAM_BANKS
406 	int i;
407 
408 	for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
409 		ret = sysmem_add(gd->bd->bi_dram[i].start,
410 				 gd->bd->bi_dram[i].size);
411 		if (ret) {
412 			SYSMEM_E("Failed to add sysmem from bi_dram[%d]\n", i);
413 			return ret;
414 		}
415 	}
416 #else
417 	mem_start = env_get_bootm_low();
418 	mem_size = env_get_bootm_size();
419 	ret = sysmem_add(mem_start, mem_size);
420 	if (ret) {
421 		SYSMEM_E("Failed to add sysmem from bootm_low/size\n");
422 		return ret;
423 	}
424 #endif
425 
426 	/* Reserved for arch */
427 	ret = arch_sysmem_reserve(sysmem);
428 	if (ret) {
429 		SYSMEM_E("Failed to reserve sysmem for arch\n");
430 		return ret;
431 	}
432 
433 	/* Reserved for board */
434 	ret = board_sysmem_reserve(sysmem);
435 	if (ret) {
436 		SYSMEM_E("Failed to reserve sysmem for board\n");
437 		return ret;
438 	}
439 
440 	/* Reserved for U-boot framework 'reserve_xxx()' */
441 	mem_start = gd->start_addr_sp - CONFIG_SYS_STACK_SIZE;
442 	mem_size = gd->ram_top - mem_start;
443 	check = (struct sysmem_check *)mem_start;
444 	check->magic = SYSMEM_MAGIC;
445 
446 	ret = sysmem_reserve("U-Boot", mem_start, mem_size);
447 	if (ret) {
448 		SYSMEM_E("Failed to reserve sysmem for U-Boot framework\n");
449 		return ret;
450 	}
451 
452 	sysmem_dump();
453 
454 	return 0;
455 }
456 
457 __weak int board_sysmem_reserve(struct sysmem *sysmem)
458 {
459 	/* please define platform specific board_sysmem_reserve() */
460 	return 0;
461 }
462 
463 __weak int arch_sysmem_reserve(struct sysmem *sysmem)
464 {
465 	/* please define platform specific arch_sysmem_reserve() */
466 	return 0;
467 }
468