xref: /rk3399_rockchip-uboot/common/fdt_support.c (revision 521dcd30b9cc5b72cd27ae04104f19369251aa20)
1 /*
2  * (C) Copyright 2007
3  * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23 
24 #include <common.h>
25 #include <linux/ctype.h>
26 #include <linux/types.h>
27 
28 #ifdef CONFIG_OF_LIBFDT
29 
30 #include <asm/global_data.h>
31 #include <fdt.h>
32 #include <libfdt.h>
33 #include <fdt_support.h>
34 
35 /*
36  * Global data (for the gd->bd)
37  */
38 DECLARE_GLOBAL_DATA_PTR;
39 
40 /*
41  * fdt points to our working device tree.
42  */
43 struct fdt_header *fdt;
44 
45 /********************************************************************/
46 
47 /**
48  * fdt_find_and_setprop: Find a node and set it's property
49  *
50  * @fdt: ptr to device tree
51  * @node: path of node
52  * @prop: property name
53  * @val: ptr to new value
54  * @len: length of new property value
55  * @create: flag to create the property if it doesn't exist
56  *
57  * Convenience function to directly set a property given the path to the node.
58  */
59 int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
60 			 const void *val, int len, int create)
61 {
62 	int nodeoff = fdt_path_offset(fdt, node);
63 
64 	if (nodeoff < 0)
65 		return nodeoff;
66 
67 	if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL))
68 		return 0; /* create flag not set; so exit quietly */
69 
70 	return fdt_setprop(fdt, nodeoff, prop, val, len);
71 }
72 
73 int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force)
74 {
75 	int   nodeoffset;
76 	int   err;
77 	u32   tmp;		/* used to set 32 bit integer properties */
78 	char  *str;		/* used to set string properties */
79 
80 	err = fdt_check_header(fdt);
81 	if (err < 0) {
82 		printf("fdt_chosen: %s\n", fdt_strerror(err));
83 		return err;
84 	}
85 
86 	if (initrd_start && initrd_end) {
87 		uint64_t addr, size;
88 		int  total = fdt_num_mem_rsv(fdt);
89 		int  j;
90 
91 		/*
92 		 * Look for an existing entry and update it.  If we don't find
93 		 * the entry, we will j be the next available slot.
94 		 */
95 		for (j = 0; j < total; j++) {
96 			err = fdt_get_mem_rsv(fdt, j, &addr, &size);
97 			if (addr == initrd_start) {
98 				fdt_del_mem_rsv(fdt, j);
99 				break;
100 			}
101 		}
102 
103 		err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1);
104 		if (err < 0) {
105 			printf("fdt_chosen: %s\n", fdt_strerror(err));
106 			return err;
107 		}
108 	}
109 
110 	/*
111 	 * Find the "chosen" node.
112 	 */
113 	nodeoffset = fdt_path_offset (fdt, "/chosen");
114 
115 	/*
116 	 * If we have a "chosen" node already the "force the writing"
117 	 * is not set, our job is done.
118 	 */
119 	if ((nodeoffset >= 0) && !force)
120 		return 0;
121 
122 	/*
123 	 * No "chosen" node in the blob: create it.
124 	 */
125 	if (nodeoffset < 0) {
126 		/*
127 		 * Create a new node "/chosen" (offset 0 is root level)
128 		 */
129 		nodeoffset = fdt_add_subnode(fdt, 0, "chosen");
130 		if (nodeoffset < 0) {
131 			printf("WARNING: could not create /chosen %s.\n",
132 				fdt_strerror(nodeoffset));
133 			return nodeoffset;
134 		}
135 	}
136 
137 	/*
138 	 * Update pre-existing properties, create them if non-existant.
139 	 */
140 	str = getenv("bootargs");
141 	if (str != NULL) {
142 		err = fdt_setprop(fdt, nodeoffset,
143 			"bootargs", str, strlen(str)+1);
144 		if (err < 0)
145 			printf("WARNING: could not set bootargs %s.\n",
146 				fdt_strerror(err));
147 	}
148 	if (initrd_start && initrd_end) {
149 		tmp = __cpu_to_be32(initrd_start);
150 		err = fdt_setprop(fdt, nodeoffset,
151 			 "linux,initrd-start", &tmp, sizeof(tmp));
152 		if (err < 0)
153 			printf("WARNING: "
154 				"could not set linux,initrd-start %s.\n",
155 				fdt_strerror(err));
156 		tmp = __cpu_to_be32(initrd_end);
157 		err = fdt_setprop(fdt, nodeoffset,
158 			"linux,initrd-end", &tmp, sizeof(tmp));
159 		if (err < 0)
160 			printf("WARNING: could not set linux,initrd-end %s.\n",
161 				fdt_strerror(err));
162 	}
163 #ifdef OF_STDOUT_PATH
164 	err = fdt_setprop(fdt, nodeoffset,
165 		"linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1);
166 	if (err < 0)
167 		printf("WARNING: could not set linux,stdout-path %s.\n",
168 			fdt_strerror(err));
169 #endif
170 
171 	return err;
172 }
173 
174 /********************************************************************/
175 
176 #ifdef CONFIG_OF_HAS_UBOOT_ENV
177 
178 /* Function that returns a character from the environment */
179 extern uchar(*env_get_char) (int);
180 
181 
182 int fdt_env(void *fdt)
183 {
184 	int   nodeoffset;
185 	int   err;
186 	int   k, nxt;
187 	int i;
188 	static char tmpenv[256];
189 
190 	err = fdt_check_header(fdt);
191 	if (err < 0) {
192 		printf("fdt_env: %s\n", fdt_strerror(err));
193 		return err;
194 	}
195 
196 	/*
197 	 * See if we already have a "u-boot-env" node, delete it if so.
198 	 * Then create a new empty node.
199 	 */
200 	nodeoffset = fdt_path_offset (fdt, "/u-boot-env");
201 	if (nodeoffset >= 0) {
202 		err = fdt_del_node(fdt, nodeoffset);
203 		if (err < 0) {
204 			printf("fdt_env: %s\n", fdt_strerror(err));
205 			return err;
206 		}
207 	}
208 	/*
209 	 * Create a new node "/u-boot-env" (offset 0 is root level)
210 	 */
211 	nodeoffset = fdt_add_subnode(fdt, 0, "u-boot-env");
212 	if (nodeoffset < 0) {
213 		printf("WARNING: could not create /u-boot-env %s.\n",
214 			fdt_strerror(nodeoffset));
215 		return nodeoffset;
216 	}
217 
218 	for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
219 		char *s, *lval, *rval;
220 
221 		/*
222 		 * Find the end of the name=definition
223 		 */
224 		for (nxt = i; env_get_char(nxt) != '\0'; ++nxt)
225 			;
226 		s = tmpenv;
227 		for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k)
228 			*s++ = env_get_char(k);
229 		*s++ = '\0';
230 		lval = tmpenv;
231 		/*
232 		 * Find the first '=': it separates the name from the value
233 		 */
234 		s = strchr(tmpenv, '=');
235 		if (s != NULL) {
236 			*s++ = '\0';
237 			rval = s;
238 		} else
239 			continue;
240 		err = fdt_setprop(fdt, nodeoffset, lval, rval, strlen(rval)+1);
241 		if (err < 0) {
242 			printf("WARNING: could not set %s %s.\n",
243 				lval, fdt_strerror(err));
244 			return err;
245 		}
246 	}
247 	return 0;
248 }
249 #endif /* ifdef CONFIG_OF_HAS_UBOOT_ENV */
250 
251 /********************************************************************/
252 
253 #ifdef CONFIG_OF_HAS_BD_T
254 
255 #define BDM(x)	{	.name = #x, .offset = offsetof(bd_t, bi_ ##x ) }
256 
257 static const struct {
258 	const char *name;
259 	int offset;
260 } bd_map[] = {
261 	BDM(memstart),
262 	BDM(memsize),
263 	BDM(flashstart),
264 	BDM(flashsize),
265 	BDM(flashoffset),
266 	BDM(sramstart),
267 	BDM(sramsize),
268 #if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \
269 	|| defined(CONFIG_E500)
270 	BDM(immr_base),
271 #endif
272 #if defined(CONFIG_MPC5xxx)
273 	BDM(mbar_base),
274 #endif
275 #if defined(CONFIG_MPC83XX)
276 	BDM(immrbar),
277 #endif
278 #if defined(CONFIG_MPC8220)
279 	BDM(mbar_base),
280 	BDM(inpfreq),
281 	BDM(pcifreq),
282 	BDM(pevfreq),
283 	BDM(flbfreq),
284 	BDM(vcofreq),
285 #endif
286 	BDM(bootflags),
287 	BDM(ip_addr),
288 	BDM(intfreq),
289 	BDM(busfreq),
290 #ifdef CONFIG_CPM2
291 	BDM(cpmfreq),
292 	BDM(brgfreq),
293 	BDM(sccfreq),
294 	BDM(vco),
295 #endif
296 #if defined(CONFIG_MPC5xxx)
297 	BDM(ipbfreq),
298 	BDM(pcifreq),
299 #endif
300 	BDM(baudrate),
301 };
302 
303 
304 int fdt_bd_t(void *fdt)
305 {
306 	bd_t *bd = gd->bd;
307 	int   nodeoffset;
308 	int   err;
309 	u32   tmp;		/* used to set 32 bit integer properties */
310 	int i;
311 
312 	err = fdt_check_header(fdt);
313 	if (err < 0) {
314 		printf("fdt_bd_t: %s\n", fdt_strerror(err));
315 		return err;
316 	}
317 
318 	/*
319 	 * See if we already have a "bd_t" node, delete it if so.
320 	 * Then create a new empty node.
321 	 */
322 	nodeoffset = fdt_path_offset (fdt, "/bd_t");
323 	if (nodeoffset >= 0) {
324 		err = fdt_del_node(fdt, nodeoffset);
325 		if (err < 0) {
326 			printf("fdt_bd_t: %s\n", fdt_strerror(err));
327 			return err;
328 		}
329 	}
330 	/*
331 	 * Create a new node "/bd_t" (offset 0 is root level)
332 	 */
333 	nodeoffset = fdt_add_subnode(fdt, 0, "bd_t");
334 	if (nodeoffset < 0) {
335 		printf("WARNING: could not create /bd_t %s.\n",
336 			fdt_strerror(nodeoffset));
337 		printf("fdt_bd_t: %s\n", fdt_strerror(nodeoffset));
338 		return nodeoffset;
339 	}
340 	/*
341 	 * Use the string/pointer structure to create the entries...
342 	 */
343 	for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) {
344 		tmp = cpu_to_be32(getenv("bootargs"));
345 		err = fdt_setprop(fdt, nodeoffset,
346 			bd_map[i].name, &tmp, sizeof(tmp));
347 		if (err < 0)
348 			printf("WARNING: could not set %s %s.\n",
349 				bd_map[i].name, fdt_strerror(err));
350 	}
351 	/*
352 	 * Add a couple of oddball entries...
353 	 */
354 	err = fdt_setprop(fdt, nodeoffset, "enetaddr", &bd->bi_enetaddr, 6);
355 	if (err < 0)
356 		printf("WARNING: could not set enetaddr %s.\n",
357 			fdt_strerror(err));
358 	err = fdt_setprop(fdt, nodeoffset, "ethspeed", &bd->bi_ethspeed, 4);
359 	if (err < 0)
360 		printf("WARNING: could not set ethspeed %s.\n",
361 			fdt_strerror(err));
362 	return 0;
363 }
364 #endif /* ifdef CONFIG_OF_HAS_BD_T */
365 
366 void do_fixup_by_path(void *fdt, const char *path, const char *prop,
367 		      const void *val, int len, int create)
368 {
369 #if defined(DEBUG)
370 	int i;
371 	debug("Updating property '%s/%s' = ", node, prop);
372 	for (i = 0; i < len; i++)
373 		debug(" %.2x", *(u8*)(val+i));
374 	debug("\n");
375 #endif
376 	int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
377 	if (rc)
378 		printf("Unable to update property %s:%s, err=%s\n",
379 			path, prop, fdt_strerror(rc));
380 }
381 
382 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop,
383 			  u32 val, int create)
384 {
385 	val = cpu_to_fdt32(val);
386 	do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create);
387 }
388 
389 void do_fixup_by_prop(void *fdt,
390 		      const char *pname, const void *pval, int plen,
391 		      const char *prop, const void *val, int len,
392 		      int create)
393 {
394 	int off;
395 #if defined(DEBUG)
396 	int i;
397 	debug("Updating property '%s/%s' = ", node, prop);
398 	for (i = 0; i < len; i++)
399 		debug(" %.2x", *(u8*)(val+i));
400 	debug("\n");
401 #endif
402 	off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen);
403 	while (off != -FDT_ERR_NOTFOUND) {
404 		if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
405 			fdt_setprop(fdt, off, prop, val, len);
406 		off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen);
407 	}
408 }
409 
410 void do_fixup_by_prop_u32(void *fdt,
411 			  const char *pname, const void *pval, int plen,
412 			  const char *prop, u32 val, int create)
413 {
414 	val = cpu_to_fdt32(val);
415 	do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create);
416 }
417 
418 void do_fixup_by_compat(void *fdt, const char *compat,
419 			const char *prop, const void *val, int len, int create)
420 {
421 	int off = -1;
422 #if defined(DEBUG)
423 	int i;
424 	debug("Updating property '%s/%s' = ", node, prop);
425 	for (i = 0; i < len; i++)
426 		debug(" %.2x", *(u8*)(val+i));
427 	debug("\n");
428 #endif
429 	off = fdt_node_offset_by_compatible(fdt, -1, compat);
430 	while (off != -FDT_ERR_NOTFOUND) {
431 		if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
432 			fdt_setprop(fdt, off, prop, val, len);
433 		off = fdt_node_offset_by_compatible(fdt, off, compat);
434 	}
435 }
436 
437 void do_fixup_by_compat_u32(void *fdt, const char *compat,
438 			    const char *prop, u32 val, int create)
439 {
440 	val = cpu_to_fdt32(val);
441 	do_fixup_by_compat(fdt, compat, prop, &val, 4, create);
442 }
443 
444 void fdt_fixup_ethernet(void *fdt, bd_t *bd)
445 {
446 	int node;
447 	const char *path;
448 
449 	node = fdt_path_offset(fdt, "/aliases");
450 	if (node >= 0) {
451 #if defined(CONFIG_HAS_ETH0)
452 		path = fdt_getprop(fdt, node, "ethernet0", NULL);
453 		if (path) {
454 			do_fixup_by_path(fdt, path, "mac-address",
455 				bd->bi_enetaddr, 6, 0);
456 			do_fixup_by_path(fdt, path, "local-mac-address",
457 				bd->bi_enetaddr, 6, 1);
458 		}
459 #endif
460 #if defined(CONFIG_HAS_ETH1)
461 		path = fdt_getprop(fdt, node, "ethernet1", NULL);
462 		if (path) {
463 			do_fixup_by_path(fdt, path, "mac-address",
464 				bd->bi_enet1addr, 6, 0);
465 			do_fixup_by_path(fdt, path, "local-mac-address",
466 				bd->bi_enet1addr, 6, 1);
467 		}
468 #endif
469 #if defined(CONFIG_HAS_ETH2)
470 		path = fdt_getprop(fdt, node, "ethernet2", NULL);
471 		if (path) {
472 			do_fixup_by_path(fdt, path, "mac-address",
473 				bd->bi_enet2addr, 6, 0);
474 			do_fixup_by_path(fdt, path, "local-mac-address",
475 				bd->bi_enet2addr, 6, 1);
476 		}
477 #endif
478 #if defined(CONFIG_HAS_ETH3)
479 		path = fdt_getprop(fdt, node, "ethernet3", NULL);
480 		if (path) {
481 			do_fixup_by_path(fdt, path, "mac-address",
482 				bd->bi_enet3addr, 6, 0);
483 			do_fixup_by_path(fdt, path, "local-mac-address",
484 				bd->bi_enet3addr, 6, 1);
485 		}
486 #endif
487 	}
488 }
489 
490 #endif /* CONFIG_OF_LIBFDT */
491