xref: /openbmc/u-boot/common/fdt_support.c (revision 6d0f6bcf)
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 #include <asm/global_data.h>
28 #include <fdt.h>
29 #include <libfdt.h>
30 #include <fdt_support.h>
31 #include <exports.h>
32 
33 /*
34  * Global data (for the gd->bd)
35  */
36 DECLARE_GLOBAL_DATA_PTR;
37 
38 
39 /**
40  * fdt_find_and_setprop: Find a node and set it's property
41  *
42  * @fdt: ptr to device tree
43  * @node: path of node
44  * @prop: property name
45  * @val: ptr to new value
46  * @len: length of new property value
47  * @create: flag to create the property if it doesn't exist
48  *
49  * Convenience function to directly set a property given the path to the node.
50  */
51 int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
52 			 const void *val, int len, int create)
53 {
54 	int nodeoff = fdt_path_offset(fdt, node);
55 
56 	if (nodeoff < 0)
57 		return nodeoff;
58 
59 	if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL))
60 		return 0; /* create flag not set; so exit quietly */
61 
62 	return fdt_setprop(fdt, nodeoff, prop, val, len);
63 }
64 
65 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
66 static int fdt_fixup_stdout(void *fdt, int chosenoff)
67 {
68 	int err = 0;
69 #ifdef CONFIG_CONS_INDEX
70 	int node;
71 	char sername[9] = { 0 };
72 	const char *path;
73 
74 	sprintf(sername, "serial%d", CONFIG_CONS_INDEX - 1);
75 
76 	err = node = fdt_path_offset(fdt, "/aliases");
77 	if (node >= 0) {
78 		int len;
79 		path = fdt_getprop(fdt, node, sername, &len);
80 		if (path) {
81 			char *p = malloc(len);
82 			err = -FDT_ERR_NOSPACE;
83 			if (p) {
84 				memcpy(p, path, len);
85 				err = fdt_setprop(fdt, chosenoff,
86 					"linux,stdout-path", p, len);
87 				free(p);
88 			}
89 		} else {
90 			err = len;
91 		}
92 	}
93 #endif
94 	if (err < 0)
95 		printf("WARNING: could not set linux,stdout-path %s.\n",
96 				fdt_strerror(err));
97 
98 	return err;
99 }
100 #endif
101 
102 int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end, int force)
103 {
104 	int   nodeoffset;
105 	int   err, j, total;
106 	u32   tmp;
107 	const char *path;
108 	uint64_t addr, size;
109 
110 	/* Find the "chosen" node.  */
111 	nodeoffset = fdt_path_offset (fdt, "/chosen");
112 
113 	/* If there is no "chosen" node in the blob return */
114 	if (nodeoffset < 0) {
115 		printf("fdt_initrd: %s\n", fdt_strerror(nodeoffset));
116 		return nodeoffset;
117 	}
118 
119 	/* just return if initrd_start/end aren't valid */
120 	if ((initrd_start == 0) || (initrd_end == 0))
121 		return 0;
122 
123 	total = fdt_num_mem_rsv(fdt);
124 
125 	/*
126 	 * Look for an existing entry and update it.  If we don't find
127 	 * the entry, we will j be the next available slot.
128 	 */
129 	for (j = 0; j < total; j++) {
130 		err = fdt_get_mem_rsv(fdt, j, &addr, &size);
131 		if (addr == initrd_start) {
132 			fdt_del_mem_rsv(fdt, j);
133 			break;
134 		}
135 	}
136 
137 	err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1);
138 	if (err < 0) {
139 		printf("fdt_initrd: %s\n", fdt_strerror(err));
140 		return err;
141 	}
142 
143 	path = fdt_getprop(fdt, nodeoffset, "linux,initrd-start", NULL);
144 	if ((path == NULL) || force) {
145 		tmp = __cpu_to_be32(initrd_start);
146 		err = fdt_setprop(fdt, nodeoffset,
147 			"linux,initrd-start", &tmp, sizeof(tmp));
148 		if (err < 0) {
149 			printf("WARNING: "
150 				"could not set linux,initrd-start %s.\n",
151 				fdt_strerror(err));
152 			return err;
153 		}
154 		tmp = __cpu_to_be32(initrd_end);
155 		err = fdt_setprop(fdt, nodeoffset,
156 			"linux,initrd-end", &tmp, sizeof(tmp));
157 		if (err < 0) {
158 			printf("WARNING: could not set linux,initrd-end %s.\n",
159 				fdt_strerror(err));
160 
161 			return err;
162 		}
163 	}
164 
165 	return 0;
166 }
167 
168 int fdt_chosen(void *fdt, int force)
169 {
170 	int   nodeoffset;
171 	int   err;
172 	char  *str;		/* used to set string properties */
173 	const char *path;
174 
175 	err = fdt_check_header(fdt);
176 	if (err < 0) {
177 		printf("fdt_chosen: %s\n", fdt_strerror(err));
178 		return err;
179 	}
180 
181 	/*
182 	 * Find the "chosen" node.
183 	 */
184 	nodeoffset = fdt_path_offset (fdt, "/chosen");
185 
186 	/*
187 	 * If there is no "chosen" node in the blob, create it.
188 	 */
189 	if (nodeoffset < 0) {
190 		/*
191 		 * Create a new node "/chosen" (offset 0 is root level)
192 		 */
193 		nodeoffset = fdt_add_subnode(fdt, 0, "chosen");
194 		if (nodeoffset < 0) {
195 			printf("WARNING: could not create /chosen %s.\n",
196 				fdt_strerror(nodeoffset));
197 			return nodeoffset;
198 		}
199 	}
200 
201 	/*
202 	 * Create /chosen properites that don't exist in the fdt.
203 	 * If the property exists, update it only if the "force" parameter
204 	 * is true.
205 	 */
206 	str = getenv("bootargs");
207 	if (str != NULL) {
208 		path = fdt_getprop(fdt, nodeoffset, "bootargs", NULL);
209 		if ((path == NULL) || force) {
210 			err = fdt_setprop(fdt, nodeoffset,
211 				"bootargs", str, strlen(str)+1);
212 			if (err < 0)
213 				printf("WARNING: could not set bootargs %s.\n",
214 					fdt_strerror(err));
215 		}
216 	}
217 
218 #ifdef CONFIG_OF_STDOUT_VIA_ALIAS
219 	path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL);
220 	if ((path == NULL) || force)
221 		err = fdt_fixup_stdout(fdt, nodeoffset);
222 #endif
223 
224 #ifdef OF_STDOUT_PATH
225 	path = fdt_getprop(fdt, nodeoffset, "linux,stdout-path", NULL);
226 	if ((path == NULL) || force) {
227 		err = fdt_setprop(fdt, nodeoffset,
228 			"linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1);
229 		if (err < 0)
230 			printf("WARNING: could not set linux,stdout-path %s.\n",
231 				fdt_strerror(err));
232 	}
233 #endif
234 
235 	return err;
236 }
237 
238 void do_fixup_by_path(void *fdt, const char *path, const char *prop,
239 		      const void *val, int len, int create)
240 {
241 #if defined(DEBUG)
242 	int i;
243 	debug("Updating property '%s/%s' = ", path, prop);
244 	for (i = 0; i < len; i++)
245 		debug(" %.2x", *(u8*)(val+i));
246 	debug("\n");
247 #endif
248 	int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
249 	if (rc)
250 		printf("Unable to update property %s:%s, err=%s\n",
251 			path, prop, fdt_strerror(rc));
252 }
253 
254 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop,
255 			  u32 val, int create)
256 {
257 	val = cpu_to_fdt32(val);
258 	do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create);
259 }
260 
261 void do_fixup_by_prop(void *fdt,
262 		      const char *pname, const void *pval, int plen,
263 		      const char *prop, const void *val, int len,
264 		      int create)
265 {
266 	int off;
267 #if defined(DEBUG)
268 	int i;
269 	debug("Updating property '%s' = ", prop);
270 	for (i = 0; i < len; i++)
271 		debug(" %.2x", *(u8*)(val+i));
272 	debug("\n");
273 #endif
274 	off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen);
275 	while (off != -FDT_ERR_NOTFOUND) {
276 		if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
277 			fdt_setprop(fdt, off, prop, val, len);
278 		off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen);
279 	}
280 }
281 
282 void do_fixup_by_prop_u32(void *fdt,
283 			  const char *pname, const void *pval, int plen,
284 			  const char *prop, u32 val, int create)
285 {
286 	val = cpu_to_fdt32(val);
287 	do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create);
288 }
289 
290 void do_fixup_by_compat(void *fdt, const char *compat,
291 			const char *prop, const void *val, int len, int create)
292 {
293 	int off = -1;
294 #if defined(DEBUG)
295 	int i;
296 	debug("Updating property '%s' = ", prop);
297 	for (i = 0; i < len; i++)
298 		debug(" %.2x", *(u8*)(val+i));
299 	debug("\n");
300 #endif
301 	off = fdt_node_offset_by_compatible(fdt, -1, compat);
302 	while (off != -FDT_ERR_NOTFOUND) {
303 		if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
304 			fdt_setprop(fdt, off, prop, val, len);
305 		off = fdt_node_offset_by_compatible(fdt, off, compat);
306 	}
307 }
308 
309 void do_fixup_by_compat_u32(void *fdt, const char *compat,
310 			    const char *prop, u32 val, int create)
311 {
312 	val = cpu_to_fdt32(val);
313 	do_fixup_by_compat(fdt, compat, prop, &val, 4, create);
314 }
315 
316 int fdt_fixup_memory(void *blob, u64 start, u64 size)
317 {
318 	int err, nodeoffset, len = 0;
319 	u8 tmp[16];
320 	const u32 *addrcell, *sizecell;
321 
322 	err = fdt_check_header(blob);
323 	if (err < 0) {
324 		printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
325 		return err;
326 	}
327 
328 	/* update, or add and update /memory node */
329 	nodeoffset = fdt_path_offset(blob, "/memory");
330 	if (nodeoffset < 0) {
331 		nodeoffset = fdt_add_subnode(blob, 0, "memory");
332 		if (nodeoffset < 0)
333 			printf("WARNING: could not create /memory: %s.\n",
334 					fdt_strerror(nodeoffset));
335 		return nodeoffset;
336 	}
337 	err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
338 			sizeof("memory"));
339 	if (err < 0) {
340 		printf("WARNING: could not set %s %s.\n", "device_type",
341 				fdt_strerror(err));
342 		return err;
343 	}
344 
345 	addrcell = fdt_getprop(blob, 0, "#address-cells", NULL);
346 	/* use shifts and mask to ensure endianness */
347 	if ((addrcell) && (*addrcell == 2)) {
348 		tmp[0] = (start >> 56) & 0xff;
349 		tmp[1] = (start >> 48) & 0xff;
350 		tmp[2] = (start >> 40) & 0xff;
351 		tmp[3] = (start >> 32) & 0xff;
352 		tmp[4] = (start >> 24) & 0xff;
353 		tmp[5] = (start >> 16) & 0xff;
354 		tmp[6] = (start >>  8) & 0xff;
355 		tmp[7] = (start      ) & 0xff;
356 		len = 8;
357 	} else {
358 		tmp[0] = (start >> 24) & 0xff;
359 		tmp[1] = (start >> 16) & 0xff;
360 		tmp[2] = (start >>  8) & 0xff;
361 		tmp[3] = (start      ) & 0xff;
362 		len = 4;
363 	}
364 
365 	sizecell = fdt_getprop(blob, 0, "#size-cells", NULL);
366 	/* use shifts and mask to ensure endianness */
367 	if ((sizecell) && (*sizecell == 2)) {
368 		tmp[0+len] = (size >> 56) & 0xff;
369 		tmp[1+len] = (size >> 48) & 0xff;
370 		tmp[2+len] = (size >> 40) & 0xff;
371 		tmp[3+len] = (size >> 32) & 0xff;
372 		tmp[4+len] = (size >> 24) & 0xff;
373 		tmp[5+len] = (size >> 16) & 0xff;
374 		tmp[6+len] = (size >>  8) & 0xff;
375 		tmp[7+len] = (size      ) & 0xff;
376 		len += 8;
377 	} else {
378 		tmp[0+len] = (size >> 24) & 0xff;
379 		tmp[1+len] = (size >> 16) & 0xff;
380 		tmp[2+len] = (size >>  8) & 0xff;
381 		tmp[3+len] = (size      ) & 0xff;
382 		len += 4;
383 	}
384 
385 	err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
386 	if (err < 0) {
387 		printf("WARNING: could not set %s %s.\n",
388 				"reg", fdt_strerror(err));
389 		return err;
390 	}
391 	return 0;
392 }
393 
394 void fdt_fixup_ethernet(void *fdt)
395 {
396 	int node, i, j;
397 	char enet[16], *tmp, *end;
398 	char mac[16] = "ethaddr";
399 	const char *path;
400 	unsigned char mac_addr[6];
401 
402 	node = fdt_path_offset(fdt, "/aliases");
403 	if (node < 0)
404 		return;
405 
406 	i = 0;
407 	while ((tmp = getenv(mac)) != NULL) {
408 		sprintf(enet, "ethernet%d", i);
409 		path = fdt_getprop(fdt, node, enet, NULL);
410 		if (!path) {
411 			debug("No alias for %s\n", enet);
412 			sprintf(mac, "eth%daddr", ++i);
413 			continue;
414 		}
415 
416 		for (j = 0; j < 6; j++) {
417 			mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
418 			if (tmp)
419 				tmp = (*end) ? end+1 : end;
420 		}
421 
422 		do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0);
423 		do_fixup_by_path(fdt, path, "local-mac-address",
424 				&mac_addr, 6, 1);
425 
426 		sprintf(mac, "eth%daddr", ++i);
427 	}
428 }
429 
430 #ifdef CONFIG_HAS_FSL_DR_USB
431 void fdt_fixup_dr_usb(void *blob, bd_t *bd)
432 {
433 	char *mode;
434 	char *type;
435 	const char *compat = "fsl-usb2-dr";
436 	const char *prop_mode = "dr_mode";
437 	const char *prop_type = "phy_type";
438 	int node_offset;
439 	int err;
440 
441 	mode = getenv("usb_dr_mode");
442 	type = getenv("usb_phy_type");
443 	if (!mode && !type)
444 		return;
445 
446 	node_offset = fdt_node_offset_by_compatible(blob, 0, compat);
447 	if (node_offset < 0) {
448 		printf("WARNING: could not find compatible node %s: %s.\n",
449 			compat, fdt_strerror(node_offset));
450 		return;
451 	}
452 
453 	if (mode) {
454 		err = fdt_setprop(blob, node_offset, prop_mode, mode,
455 				  strlen(mode) + 1);
456 		if (err < 0)
457 			printf("WARNING: could not set %s for %s: %s.\n",
458 			       prop_mode, compat, fdt_strerror(err));
459 	}
460 
461 	if (type) {
462 		err = fdt_setprop(blob, node_offset, prop_type, type,
463 				  strlen(type) + 1);
464 		if (err < 0)
465 			printf("WARNING: could not set %s for %s: %s.\n",
466 			       prop_type, compat, fdt_strerror(err));
467 	}
468 }
469 #endif /* CONFIG_HAS_FSL_DR_USB */
470 
471 #if defined(CONFIG_MPC83XX) || defined(CONFIG_MPC85xx)
472 /*
473  * update crypto node properties to a specified revision of the SEC
474  * called with sec_rev == 0 if not on an mpc8xxxE processor
475  */
476 void fdt_fixup_crypto_node(void *blob, int sec_rev)
477 {
478 	const struct sec_rev_prop {
479 		u32 sec_rev;
480 		u32 num_channels;
481 		u32 channel_fifo_len;
482 		u32 exec_units_mask;
483 		u32 descriptor_types_mask;
484 	} sec_rev_prop_list [] = {
485 		{ 0x0200, 4, 24, 0x07e, 0x01010ebf }, /* SEC 2.0 */
486 		{ 0x0201, 4, 24, 0x0fe, 0x012b0ebf }, /* SEC 2.1 */
487 		{ 0x0202, 1, 24, 0x04c, 0x0122003f }, /* SEC 2.2 */
488 		{ 0x0204, 4, 24, 0x07e, 0x012b0ebf }, /* SEC 2.4 */
489 		{ 0x0300, 4, 24, 0x9fe, 0x03ab0ebf }, /* SEC 3.0 */
490 		{ 0x0303, 4, 24, 0x97c, 0x03ab0abf }, /* SEC 3.3 */
491 	};
492 	char compat_strlist[ARRAY_SIZE(sec_rev_prop_list) *
493 			    sizeof("fsl,secX.Y")];
494 	int crypto_node, sec_idx, err;
495 	char *p;
496 	u32 val;
497 
498 	/* locate crypto node based on lowest common compatible */
499 	crypto_node = fdt_node_offset_by_compatible(blob, -1, "fsl,sec2.0");
500 	if (crypto_node == -FDT_ERR_NOTFOUND)
501 		return;
502 
503 	/* delete it if not on an E-processor */
504 	if (crypto_node > 0 && !sec_rev) {
505 		fdt_del_node(blob, crypto_node);
506 		return;
507 	}
508 
509 	/* else we got called for possible uprev */
510 	for (sec_idx = 0; sec_idx < ARRAY_SIZE(sec_rev_prop_list); sec_idx++)
511 		if (sec_rev_prop_list[sec_idx].sec_rev == sec_rev)
512 			break;
513 
514 	if (sec_idx == ARRAY_SIZE(sec_rev_prop_list)) {
515 		puts("warning: unknown SEC revision number\n");
516 		return;
517 	}
518 
519 	val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].num_channels);
520 	err = fdt_setprop(blob, crypto_node, "fsl,num-channels", &val, 4);
521 	if (err < 0)
522 		printf("WARNING: could not set crypto property: %s\n",
523 		       fdt_strerror(err));
524 
525 	val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].descriptor_types_mask);
526 	err = fdt_setprop(blob, crypto_node, "fsl,descriptor-types-mask", &val, 4);
527 	if (err < 0)
528 		printf("WARNING: could not set crypto property: %s\n",
529 		       fdt_strerror(err));
530 
531 	val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].exec_units_mask);
532 	err = fdt_setprop(blob, crypto_node, "fsl,exec-units-mask", &val, 4);
533 	if (err < 0)
534 		printf("WARNING: could not set crypto property: %s\n",
535 		       fdt_strerror(err));
536 
537 	val = cpu_to_fdt32(sec_rev_prop_list[sec_idx].channel_fifo_len);
538 	err = fdt_setprop(blob, crypto_node, "fsl,channel-fifo-len", &val, 4);
539 	if (err < 0)
540 		printf("WARNING: could not set crypto property: %s\n",
541 		       fdt_strerror(err));
542 
543 	val = 0;
544 	while (sec_idx >= 0) {
545 		p = compat_strlist + val;
546 		val += sprintf(p, "fsl,sec%d.%d",
547 			(sec_rev_prop_list[sec_idx].sec_rev & 0xff00) >> 8,
548 			sec_rev_prop_list[sec_idx].sec_rev & 0x00ff) + 1;
549 		sec_idx--;
550 	}
551 	err = fdt_setprop(blob, crypto_node, "compatible", &compat_strlist, val);
552 	if (err < 0)
553 		printf("WARNING: could not set crypto property: %s\n",
554 		       fdt_strerror(err));
555 }
556 #endif /* defined(CONFIG_MPC83XX) || defined(CONFIG_MPC85xx) */
557 
558 /* Resize the fdt to its actual size + a bit of padding */
559 int fdt_resize(void *blob)
560 {
561 	int i;
562 	uint64_t addr, size;
563 	int total, ret;
564 	uint actualsize;
565 
566 	if (!blob)
567 		return 0;
568 
569 	total = fdt_num_mem_rsv(blob);
570 	for (i = 0; i < total; i++) {
571 		fdt_get_mem_rsv(blob, i, &addr, &size);
572 		if (addr == (uint64_t)(u32)blob) {
573 			fdt_del_mem_rsv(blob, i);
574 			break;
575 		}
576 	}
577 
578 	/* Calculate the actual size of the fdt */
579 	actualsize = fdt_off_dt_strings(blob) +
580 		fdt_size_dt_strings(blob);
581 
582 	/* Make it so the fdt ends on a page boundary */
583 	actualsize = ALIGN(actualsize, 0x1000);
584 	actualsize = actualsize - ((uint)blob & 0xfff);
585 
586 	/* Change the fdt header to reflect the correct size */
587 	fdt_set_totalsize(blob, actualsize);
588 
589 	/* Add the new reservation */
590 	ret = fdt_add_mem_rsv(blob, (uint)blob, actualsize);
591 	if (ret < 0)
592 		return ret;
593 
594 	return actualsize;
595 }
596