Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
b90b9a38 NP |
2 | #include <asm/setup.h> |
3 | #include <libfdt.h> | |
4 | ||
d0f34a11 GR |
5 | #if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND) |
6 | #define do_extend_cmdline 1 | |
7 | #else | |
8 | #define do_extend_cmdline 0 | |
9 | #endif | |
10 | ||
1c2f87c2 LA |
11 | #define NR_BANKS 16 |
12 | ||
b90b9a38 NP |
13 | static int node_offset(void *fdt, const char *node_path) |
14 | { | |
15 | int offset = fdt_path_offset(fdt, node_path); | |
16 | if (offset == -FDT_ERR_NOTFOUND) | |
17 | offset = fdt_add_subnode(fdt, 0, node_path); | |
18 | return offset; | |
19 | } | |
20 | ||
21 | static int setprop(void *fdt, const char *node_path, const char *property, | |
22 | uint32_t *val_array, int size) | |
23 | { | |
24 | int offset = node_offset(fdt, node_path); | |
25 | if (offset < 0) | |
26 | return offset; | |
27 | return fdt_setprop(fdt, offset, property, val_array, size); | |
28 | } | |
29 | ||
30 | static int setprop_string(void *fdt, const char *node_path, | |
31 | const char *property, const char *string) | |
32 | { | |
33 | int offset = node_offset(fdt, node_path); | |
34 | if (offset < 0) | |
35 | return offset; | |
36 | return fdt_setprop_string(fdt, offset, property, string); | |
37 | } | |
38 | ||
39 | static int setprop_cell(void *fdt, const char *node_path, | |
40 | const char *property, uint32_t val) | |
41 | { | |
42 | int offset = node_offset(fdt, node_path); | |
43 | if (offset < 0) | |
44 | return offset; | |
45 | return fdt_setprop_cell(fdt, offset, property, val); | |
46 | } | |
47 | ||
d0f34a11 GR |
48 | static const void *getprop(const void *fdt, const char *node_path, |
49 | const char *property, int *len) | |
50 | { | |
51 | int offset = fdt_path_offset(fdt, node_path); | |
52 | ||
53 | if (offset == -FDT_ERR_NOTFOUND) | |
54 | return NULL; | |
55 | ||
56 | return fdt_getprop(fdt, offset, property, len); | |
57 | } | |
58 | ||
faefd550 GC |
59 | static uint32_t get_cell_size(const void *fdt) |
60 | { | |
61 | int len; | |
62 | uint32_t cell_size = 1; | |
63 | const uint32_t *size_len = getprop(fdt, "/", "#size-cells", &len); | |
64 | ||
65 | if (size_len) | |
66 | cell_size = fdt32_to_cpu(*size_len); | |
67 | return cell_size; | |
68 | } | |
69 | ||
d0f34a11 GR |
70 | static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline) |
71 | { | |
72 | char cmdline[COMMAND_LINE_SIZE]; | |
73 | const char *fdt_bootargs; | |
74 | char *ptr = cmdline; | |
75 | int len = 0; | |
76 | ||
77 | /* copy the fdt command line into the buffer */ | |
78 | fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len); | |
79 | if (fdt_bootargs) | |
80 | if (len < COMMAND_LINE_SIZE) { | |
81 | memcpy(ptr, fdt_bootargs, len); | |
82 | /* len is the length of the string | |
83 | * including the NULL terminator */ | |
84 | ptr += len - 1; | |
85 | } | |
86 | ||
87 | /* and append the ATAG_CMDLINE */ | |
88 | if (fdt_cmdline) { | |
89 | len = strlen(fdt_cmdline); | |
90 | if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) { | |
91 | *ptr++ = ' '; | |
92 | memcpy(ptr, fdt_cmdline, len); | |
93 | ptr += len; | |
94 | } | |
95 | } | |
96 | *ptr = '\0'; | |
97 | ||
98 | setprop_string(fdt, "/chosen", "bootargs", cmdline); | |
99 | } | |
100 | ||
b90b9a38 NP |
101 | /* |
102 | * Convert and fold provided ATAGs into the provided FDT. | |
103 | * | |
104 | * REturn values: | |
105 | * = 0 -> pretend success | |
106 | * = 1 -> bad ATAG (may retry with another possible ATAG pointer) | |
107 | * < 0 -> error from libfdt | |
108 | */ | |
109 | int atags_to_fdt(void *atag_list, void *fdt, int total_space) | |
110 | { | |
111 | struct tag *atag = atag_list; | |
faefd550 GC |
112 | /* In the case of 64 bits memory size, need to reserve 2 cells for |
113 | * address and size for each bank */ | |
114 | uint32_t mem_reg_property[2 * 2 * NR_BANKS]; | |
b90b9a38 | 115 | int memcount = 0; |
faefd550 | 116 | int ret, memsize; |
b90b9a38 NP |
117 | |
118 | /* make sure we've got an aligned pointer */ | |
119 | if ((u32)atag_list & 0x3) | |
120 | return 1; | |
121 | ||
122 | /* if we get a DTB here we're done already */ | |
123 | if (*(u32 *)atag_list == fdt32_to_cpu(FDT_MAGIC)) | |
124 | return 0; | |
125 | ||
126 | /* validate the ATAG */ | |
127 | if (atag->hdr.tag != ATAG_CORE || | |
128 | (atag->hdr.size != tag_size(tag_core) && | |
129 | atag->hdr.size != 2)) | |
130 | return 1; | |
131 | ||
132 | /* let's give it all the room it could need */ | |
133 | ret = fdt_open_into(fdt, fdt, total_space); | |
134 | if (ret < 0) | |
135 | return ret; | |
136 | ||
137 | for_each_tag(atag, atag_list) { | |
138 | if (atag->hdr.tag == ATAG_CMDLINE) { | |
d0f34a11 GR |
139 | /* Append the ATAGS command line to the device tree |
140 | * command line. | |
141 | * NB: This means that if the same parameter is set in | |
142 | * the device tree and in the tags, the one from the | |
143 | * tags will be chosen. | |
144 | */ | |
145 | if (do_extend_cmdline) | |
146 | merge_fdt_bootargs(fdt, | |
147 | atag->u.cmdline.cmdline); | |
148 | else | |
149 | setprop_string(fdt, "/chosen", "bootargs", | |
150 | atag->u.cmdline.cmdline); | |
b90b9a38 NP |
151 | } else if (atag->hdr.tag == ATAG_MEM) { |
152 | if (memcount >= sizeof(mem_reg_property)/4) | |
153 | continue; | |
a106b21a MZ |
154 | if (!atag->u.mem.size) |
155 | continue; | |
faefd550 GC |
156 | memsize = get_cell_size(fdt); |
157 | ||
158 | if (memsize == 2) { | |
159 | /* if memsize is 2, that means that | |
160 | * each data needs 2 cells of 32 bits, | |
161 | * so the data are 64 bits */ | |
162 | uint64_t *mem_reg_prop64 = | |
163 | (uint64_t *)mem_reg_property; | |
164 | mem_reg_prop64[memcount++] = | |
165 | cpu_to_fdt64(atag->u.mem.start); | |
166 | mem_reg_prop64[memcount++] = | |
167 | cpu_to_fdt64(atag->u.mem.size); | |
168 | } else { | |
169 | mem_reg_property[memcount++] = | |
170 | cpu_to_fdt32(atag->u.mem.start); | |
171 | mem_reg_property[memcount++] = | |
172 | cpu_to_fdt32(atag->u.mem.size); | |
173 | } | |
174 | ||
b90b9a38 NP |
175 | } else if (atag->hdr.tag == ATAG_INITRD2) { |
176 | uint32_t initrd_start, initrd_size; | |
177 | initrd_start = atag->u.initrd.start; | |
178 | initrd_size = atag->u.initrd.size; | |
179 | setprop_cell(fdt, "/chosen", "linux,initrd-start", | |
180 | initrd_start); | |
181 | setprop_cell(fdt, "/chosen", "linux,initrd-end", | |
182 | initrd_start + initrd_size); | |
183 | } | |
184 | } | |
185 | ||
faefd550 GC |
186 | if (memcount) { |
187 | setprop(fdt, "/memory", "reg", mem_reg_property, | |
188 | 4 * memcount * memsize); | |
189 | } | |
b90b9a38 NP |
190 | |
191 | return fdt_pack(fdt); | |
192 | } |