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 }
192