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