blob: 841d89e07993dad0ff10e9002acfdc352c5aed83 [file] [log] [blame]
Sughosh Ganu29c7e442024-10-25 22:57:23 +05301// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2024, Linaro Limited
4 */
5
6#include <fdt_support.h>
7#include <fdtdec.h>
8#include <memtop.h>
9
10#include <asm/types.h>
11
12#define MEM_RGN_COUNT 16
13
14struct region {
15 phys_addr_t base;
16 phys_size_t size;
17};
18
19struct mem_region {
20 struct region rgn[MEM_RGN_COUNT];
21 uint count;
22};
23
24static void add_mem_region(struct mem_region *mem_rgn, phys_addr_t base,
25 phys_size_t size)
26{
27 long i;
28
29 for (i = mem_rgn->count; i >= 0; i--) {
30 if (i && base < mem_rgn->rgn[i - 1].base) {
31 mem_rgn->rgn[i] = mem_rgn->rgn[i - 1];
32 } else {
33 mem_rgn->rgn[i].base = base;
34 mem_rgn->rgn[i].size = size;
35 break;
36 }
37 }
38
39 mem_rgn->count++;
40}
41
42static void mem_regions_init(struct mem_region *mem)
43{
44 uint i;
45
46 mem->count = 0;
47 for (i = 0; i < MEM_RGN_COUNT; i++) {
48 mem->rgn[i].base = 0;
49 mem->rgn[i].size = 0;
50 }
51}
52
53static int fdt_add_reserved_regions(struct mem_region *free_mem,
54 struct mem_region *reserved_mem,
55 void *fdt_blob)
56{
57 u64 addr, size;
58 int i, total, ret;
59 int nodeoffset, subnode;
60 struct fdt_resource res;
61
62 if (fdt_check_header(fdt_blob) != 0)
63 return -1;
64
65 /* process memreserve sections */
66 total = fdt_num_mem_rsv(fdt_blob);
67 assert_noisy(total < MEM_RGN_COUNT);
68 for (i = 0; i < total; i++) {
69 if (fdt_get_mem_rsv(fdt_blob, i, &addr, &size) != 0)
70 continue;
71 add_mem_region(reserved_mem, addr, size);
72 }
73
74 i = 0;
75 /* process reserved-memory */
76 nodeoffset = fdt_subnode_offset(fdt_blob, 0, "reserved-memory");
77 if (nodeoffset >= 0) {
78 subnode = fdt_first_subnode(fdt_blob, nodeoffset);
79 while (subnode >= 0) {
80 /* check if this subnode has a reg property */
81 ret = fdt_get_resource(fdt_blob, subnode, "reg", 0,
82 &res);
83 if (!ret && fdtdec_get_is_enabled(fdt_blob, subnode)) {
84 addr = res.start;
85 size = res.end - res.start + 1;
86 assert_noisy(i < MEM_RGN_COUNT);
87 add_mem_region(reserved_mem, addr, size);
88 }
89
90 subnode = fdt_next_subnode(fdt_blob, subnode);
91 ++i;
92 }
93 }
94
95 return 0;
96}
97
98static long addrs_overlap(phys_addr_t base1, phys_size_t size1,
99 phys_addr_t base2, phys_size_t size2)
100{
101 const phys_addr_t base1_end = base1 + size1 - 1;
102 const phys_addr_t base2_end = base2 + size2 - 1;
103
104 return ((base1 <= base2_end) && (base2 <= base1_end));
105}
106
107static long region_overlap_check(struct mem_region *mem_rgn, phys_addr_t base,
108 phys_size_t size)
109{
110 unsigned long i;
111 struct region *rgn = mem_rgn->rgn;
112
113 for (i = 0; i < mem_rgn->count; i++) {
114 phys_addr_t rgnbase = rgn[i].base;
115 phys_size_t rgnsize = rgn[i].size;
116
117 if (addrs_overlap(base, size, rgnbase, rgnsize))
118 break;
119 }
120
121 return (i < mem_rgn->count) ? i : -1;
122}
123
124static int find_ram_top(struct mem_region *free_mem,
125 struct mem_region *reserved_mem, phys_size_t size)
126{
127 long i, rgn;
128 phys_addr_t base = 0;
129 phys_addr_t res_base;
130
131 for (i = free_mem->count - 1; i >= 0; i--) {
132 phys_addr_t rgnbase = free_mem->rgn[i].base;
133 phys_size_t rgnsize = free_mem->rgn[i].size;
134
135 if (rgnsize < size)
136 continue;
137
138 base = rgnbase + rgnsize - size;
139 while (base && rgnbase <= base) {
140 rgn = region_overlap_check(reserved_mem, base, size);
141 if (rgn < 0)
142 return base;
143
144 res_base = reserved_mem->rgn[rgn].base;
145 if (res_base < size)
146 break;
147 base = res_base - size;
148 }
149 }
150
151 return 0;
152}
153
154phys_addr_t get_mem_top(phys_addr_t ram_start, phys_size_t ram_size,
155 phys_size_t size, void *fdt)
156{
157 int i;
158 struct mem_region free_mem;
159 struct mem_region reserved_mem;
160
161 mem_regions_init(&free_mem);
162 mem_regions_init(&reserved_mem);
163
164 add_mem_region(&free_mem, ram_start, ram_size);
165
166 i = fdt_add_reserved_regions(&free_mem, &reserved_mem, fdt);
167 if (i < 0)
168 return 0;
169
170 return find_ram_top(&free_mem, &reserved_mem, size);
171}