1
2#include <linux/libfdt_env.h>
3#include <asm/setup.h>
4#include <libfdt.h>
5
6#if defined(CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND)
7#define do_extend_cmdline 1
8#else
9#define do_extend_cmdline 0
10#endif
11
12#define NR_BANKS 16
13
14static int node_offset(void *fdt, const char *node_path)
15{
16 int offset = fdt_path_offset(fdt, node_path);
17 if (offset == -FDT_ERR_NOTFOUND)
18
19 offset = fdt_add_subnode(fdt, 0, node_path + 1);
20 return offset;
21}
22
23static int setprop(void *fdt, const char *node_path, const char *property,
24 void *val_array, int size)
25{
26 int offset = node_offset(fdt, node_path);
27 if (offset < 0)
28 return offset;
29 return fdt_setprop(fdt, offset, property, val_array, size);
30}
31
32static int setprop_string(void *fdt, const char *node_path,
33 const char *property, const char *string)
34{
35 int offset = node_offset(fdt, node_path);
36 if (offset < 0)
37 return offset;
38 return fdt_setprop_string(fdt, offset, property, string);
39}
40
41static int setprop_cell(void *fdt, const char *node_path,
42 const char *property, uint32_t val)
43{
44 int offset = node_offset(fdt, node_path);
45 if (offset < 0)
46 return offset;
47 return fdt_setprop_cell(fdt, offset, property, val);
48}
49
50static const void *getprop(const void *fdt, const char *node_path,
51 const char *property, int *len)
52{
53 int offset = fdt_path_offset(fdt, node_path);
54
55 if (offset == -FDT_ERR_NOTFOUND)
56 return NULL;
57
58 return fdt_getprop(fdt, offset, property, len);
59}
60
61static uint32_t get_cell_size(const void *fdt)
62{
63 int len;
64 uint32_t cell_size = 1;
65 const __be32 *size_len = getprop(fdt, "/", "#size-cells", &len);
66
67 if (size_len)
68 cell_size = fdt32_to_cpu(*size_len);
69 return cell_size;
70}
71
72static void merge_fdt_bootargs(void *fdt, const char *fdt_cmdline)
73{
74 char cmdline[COMMAND_LINE_SIZE];
75 const char *fdt_bootargs;
76 char *ptr = cmdline;
77 int len = 0;
78
79
80 fdt_bootargs = getprop(fdt, "/chosen", "bootargs", &len);
81 if (fdt_bootargs)
82 if (len < COMMAND_LINE_SIZE) {
83 memcpy(ptr, fdt_bootargs, len);
84
85
86 ptr += len - 1;
87 }
88
89
90 if (fdt_cmdline) {
91 len = strlen(fdt_cmdline);
92 if (ptr - cmdline + len + 2 < COMMAND_LINE_SIZE) {
93 *ptr++ = ' ';
94 memcpy(ptr, fdt_cmdline, len);
95 ptr += len;
96 }
97 }
98 *ptr = '\0';
99
100 setprop_string(fdt, "/chosen", "bootargs", cmdline);
101}
102
103static void hex_str(char *out, uint32_t value)
104{
105 uint32_t digit;
106 int idx;
107
108 for (idx = 7; idx >= 0; idx--) {
109 digit = value >> 28;
110 value <<= 4;
111 digit &= 0xf;
112 if (digit < 10)
113 digit += '0';
114 else
115 digit += 'A'-10;
116 *out++ = digit;
117 }
118 *out = '\0';
119}
120
121
122
123
124
125
126
127
128
129int atags_to_fdt(void *atag_list, void *fdt, int total_space)
130{
131 struct tag *atag = atag_list;
132
133
134 __be32 mem_reg_property[2 * 2 * NR_BANKS];
135 int memcount = 0;
136 int ret, memsize;
137
138
139 if ((u32)atag_list & 0x3)
140 return 1;
141
142
143 if (*(__be32 *)atag_list == cpu_to_fdt32(FDT_MAGIC))
144 return 0;
145
146
147 if (atag->hdr.tag != ATAG_CORE ||
148 (atag->hdr.size != tag_size(tag_core) &&
149 atag->hdr.size != 2))
150 return 1;
151
152
153 ret = fdt_open_into(fdt, fdt, total_space);
154 if (ret < 0)
155 return ret;
156
157 for_each_tag(atag, atag_list) {
158 if (atag->hdr.tag == ATAG_CMDLINE) {
159
160
161
162
163
164
165 if (do_extend_cmdline)
166 merge_fdt_bootargs(fdt,
167 atag->u.cmdline.cmdline);
168 else
169 setprop_string(fdt, "/chosen", "bootargs",
170 atag->u.cmdline.cmdline);
171 } else if (atag->hdr.tag == ATAG_MEM) {
172 if (memcount >= sizeof(mem_reg_property)/4)
173 continue;
174 if (!atag->u.mem.size)
175 continue;
176 memsize = get_cell_size(fdt);
177
178 if (memsize == 2) {
179
180
181
182 __be64 *mem_reg_prop64 =
183 (__be64 *)mem_reg_property;
184 mem_reg_prop64[memcount++] =
185 cpu_to_fdt64(atag->u.mem.start);
186 mem_reg_prop64[memcount++] =
187 cpu_to_fdt64(atag->u.mem.size);
188 } else {
189 mem_reg_property[memcount++] =
190 cpu_to_fdt32(atag->u.mem.start);
191 mem_reg_property[memcount++] =
192 cpu_to_fdt32(atag->u.mem.size);
193 }
194
195 } else if (atag->hdr.tag == ATAG_INITRD2) {
196 uint32_t initrd_start, initrd_size;
197 initrd_start = atag->u.initrd.start;
198 initrd_size = atag->u.initrd.size;
199 setprop_cell(fdt, "/chosen", "linux,initrd-start",
200 initrd_start);
201 setprop_cell(fdt, "/chosen", "linux,initrd-end",
202 initrd_start + initrd_size);
203 } else if (atag->hdr.tag == ATAG_SERIAL) {
204 char serno[16+2];
205 hex_str(serno, atag->u.serialnr.high);
206 hex_str(serno+8, atag->u.serialnr.low);
207 setprop_string(fdt, "/", "serial-number", serno);
208 }
209 }
210
211 if (memcount) {
212 setprop(fdt, "/memory", "reg", mem_reg_property,
213 4 * memcount * memsize);
214 }
215
216 return fdt_pack(fdt);
217}
218