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