]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - common/fdt_support.c
Add common memory fixup function
[karo-tx-uboot.git] / common / fdt_support.c
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
32 /*
33  * Global data (for the gd->bd)
34  */
35 DECLARE_GLOBAL_DATA_PTR;
36
37 /*
38  * fdt points to our working device tree.
39  */
40 struct fdt_header *fdt;
41
42 /********************************************************************/
43
44 /**
45  * fdt_find_and_setprop: Find a node and set it's property
46  *
47  * @fdt: ptr to device tree
48  * @node: path of node
49  * @prop: property name
50  * @val: ptr to new value
51  * @len: length of new property value
52  * @create: flag to create the property if it doesn't exist
53  *
54  * Convenience function to directly set a property given the path to the node.
55  */
56 int fdt_find_and_setprop(void *fdt, const char *node, const char *prop,
57                          const void *val, int len, int create)
58 {
59         int nodeoff = fdt_path_offset(fdt, node);
60
61         if (nodeoff < 0)
62                 return nodeoff;
63
64         if ((!create) && (fdt_get_property(fdt, nodeoff, prop, 0) == NULL))
65                 return 0; /* create flag not set; so exit quietly */
66
67         return fdt_setprop(fdt, nodeoff, prop, val, len);
68 }
69
70 int fdt_chosen(void *fdt, ulong initrd_start, ulong initrd_end, int force)
71 {
72         int   nodeoffset;
73         int   err;
74         u32   tmp;              /* used to set 32 bit integer properties */
75         char  *str;             /* used to set string properties */
76
77         err = fdt_check_header(fdt);
78         if (err < 0) {
79                 printf("fdt_chosen: %s\n", fdt_strerror(err));
80                 return err;
81         }
82
83         if (initrd_start && initrd_end) {
84                 uint64_t addr, size;
85                 int  total = fdt_num_mem_rsv(fdt);
86                 int  j;
87
88                 /*
89                  * Look for an existing entry and update it.  If we don't find
90                  * the entry, we will j be the next available slot.
91                  */
92                 for (j = 0; j < total; j++) {
93                         err = fdt_get_mem_rsv(fdt, j, &addr, &size);
94                         if (addr == initrd_start) {
95                                 fdt_del_mem_rsv(fdt, j);
96                                 break;
97                         }
98                 }
99
100                 err = fdt_add_mem_rsv(fdt, initrd_start, initrd_end - initrd_start + 1);
101                 if (err < 0) {
102                         printf("fdt_chosen: %s\n", fdt_strerror(err));
103                         return err;
104                 }
105         }
106
107         /*
108          * Find the "chosen" node.
109          */
110         nodeoffset = fdt_path_offset (fdt, "/chosen");
111
112         /*
113          * If we have a "chosen" node already the "force the writing"
114          * is not set, our job is done.
115          */
116         if ((nodeoffset >= 0) && !force)
117                 return 0;
118
119         /*
120          * No "chosen" node in the blob: create it.
121          */
122         if (nodeoffset < 0) {
123                 /*
124                  * Create a new node "/chosen" (offset 0 is root level)
125                  */
126                 nodeoffset = fdt_add_subnode(fdt, 0, "chosen");
127                 if (nodeoffset < 0) {
128                         printf("WARNING: could not create /chosen %s.\n",
129                                 fdt_strerror(nodeoffset));
130                         return nodeoffset;
131                 }
132         }
133
134         /*
135          * Update pre-existing properties, create them if non-existant.
136          */
137         str = getenv("bootargs");
138         if (str != NULL) {
139                 err = fdt_setprop(fdt, nodeoffset,
140                         "bootargs", str, strlen(str)+1);
141                 if (err < 0)
142                         printf("WARNING: could not set bootargs %s.\n",
143                                 fdt_strerror(err));
144         }
145         if (initrd_start && initrd_end) {
146                 tmp = __cpu_to_be32(initrd_start);
147                 err = fdt_setprop(fdt, nodeoffset,
148                          "linux,initrd-start", &tmp, sizeof(tmp));
149                 if (err < 0)
150                         printf("WARNING: "
151                                 "could not set linux,initrd-start %s.\n",
152                                 fdt_strerror(err));
153                 tmp = __cpu_to_be32(initrd_end);
154                 err = fdt_setprop(fdt, nodeoffset,
155                         "linux,initrd-end", &tmp, sizeof(tmp));
156                 if (err < 0)
157                         printf("WARNING: could not set linux,initrd-end %s.\n",
158                                 fdt_strerror(err));
159         }
160 #ifdef OF_STDOUT_PATH
161         err = fdt_setprop(fdt, nodeoffset,
162                 "linux,stdout-path", OF_STDOUT_PATH, strlen(OF_STDOUT_PATH)+1);
163         if (err < 0)
164                 printf("WARNING: could not set linux,stdout-path %s.\n",
165                         fdt_strerror(err));
166 #endif
167
168         return err;
169 }
170
171 /********************************************************************/
172
173 #ifdef CONFIG_OF_HAS_UBOOT_ENV
174
175 /* Function that returns a character from the environment */
176 extern uchar(*env_get_char) (int);
177
178
179 int fdt_env(void *fdt)
180 {
181         int   nodeoffset;
182         int   err;
183         int   k, nxt;
184         int i;
185         static char tmpenv[256];
186
187         err = fdt_check_header(fdt);
188         if (err < 0) {
189                 printf("fdt_env: %s\n", fdt_strerror(err));
190                 return err;
191         }
192
193         /*
194          * See if we already have a "u-boot-env" node, delete it if so.
195          * Then create a new empty node.
196          */
197         nodeoffset = fdt_path_offset (fdt, "/u-boot-env");
198         if (nodeoffset >= 0) {
199                 err = fdt_del_node(fdt, nodeoffset);
200                 if (err < 0) {
201                         printf("fdt_env: %s\n", fdt_strerror(err));
202                         return err;
203                 }
204         }
205         /*
206          * Create a new node "/u-boot-env" (offset 0 is root level)
207          */
208         nodeoffset = fdt_add_subnode(fdt, 0, "u-boot-env");
209         if (nodeoffset < 0) {
210                 printf("WARNING: could not create /u-boot-env %s.\n",
211                         fdt_strerror(nodeoffset));
212                 return nodeoffset;
213         }
214
215         for (i = 0; env_get_char(i) != '\0'; i = nxt + 1) {
216                 char *s, *lval, *rval;
217
218                 /*
219                  * Find the end of the name=definition
220                  */
221                 for (nxt = i; env_get_char(nxt) != '\0'; ++nxt)
222                         ;
223                 s = tmpenv;
224                 for (k = i; k < nxt && s < &tmpenv[sizeof(tmpenv) - 1]; ++k)
225                         *s++ = env_get_char(k);
226                 *s++ = '\0';
227                 lval = tmpenv;
228                 /*
229                  * Find the first '=': it separates the name from the value
230                  */
231                 s = strchr(tmpenv, '=');
232                 if (s != NULL) {
233                         *s++ = '\0';
234                         rval = s;
235                 } else
236                         continue;
237                 err = fdt_setprop(fdt, nodeoffset, lval, rval, strlen(rval)+1);
238                 if (err < 0) {
239                         printf("WARNING: could not set %s %s.\n",
240                                 lval, fdt_strerror(err));
241                         return err;
242                 }
243         }
244         return 0;
245 }
246 #endif /* ifdef CONFIG_OF_HAS_UBOOT_ENV */
247
248 /********************************************************************/
249
250 #ifdef CONFIG_OF_HAS_BD_T
251
252 #define BDM(x)  {       .name = #x, .offset = offsetof(bd_t, bi_ ##x ) }
253
254 static const struct {
255         const char *name;
256         int offset;
257 } bd_map[] = {
258         BDM(memstart),
259         BDM(memsize),
260         BDM(flashstart),
261         BDM(flashsize),
262         BDM(flashoffset),
263         BDM(sramstart),
264         BDM(sramsize),
265 #if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) \
266         || defined(CONFIG_E500)
267         BDM(immr_base),
268 #endif
269 #if defined(CONFIG_MPC5xxx)
270         BDM(mbar_base),
271 #endif
272 #if defined(CONFIG_MPC83XX)
273         BDM(immrbar),
274 #endif
275 #if defined(CONFIG_MPC8220)
276         BDM(mbar_base),
277         BDM(inpfreq),
278         BDM(pcifreq),
279         BDM(pevfreq),
280         BDM(flbfreq),
281         BDM(vcofreq),
282 #endif
283         BDM(bootflags),
284         BDM(ip_addr),
285         BDM(intfreq),
286         BDM(busfreq),
287 #ifdef CONFIG_CPM2
288         BDM(cpmfreq),
289         BDM(brgfreq),
290         BDM(sccfreq),
291         BDM(vco),
292 #endif
293 #if defined(CONFIG_MPC5xxx)
294         BDM(ipbfreq),
295         BDM(pcifreq),
296 #endif
297         BDM(baudrate),
298 };
299
300
301 int fdt_bd_t(void *fdt)
302 {
303         bd_t *bd = gd->bd;
304         int   nodeoffset;
305         int   err;
306         u32   tmp;              /* used to set 32 bit integer properties */
307         int i;
308
309         err = fdt_check_header(fdt);
310         if (err < 0) {
311                 printf("fdt_bd_t: %s\n", fdt_strerror(err));
312                 return err;
313         }
314
315         /*
316          * See if we already have a "bd_t" node, delete it if so.
317          * Then create a new empty node.
318          */
319         nodeoffset = fdt_path_offset (fdt, "/bd_t");
320         if (nodeoffset >= 0) {
321                 err = fdt_del_node(fdt, nodeoffset);
322                 if (err < 0) {
323                         printf("fdt_bd_t: %s\n", fdt_strerror(err));
324                         return err;
325                 }
326         }
327         /*
328          * Create a new node "/bd_t" (offset 0 is root level)
329          */
330         nodeoffset = fdt_add_subnode(fdt, 0, "bd_t");
331         if (nodeoffset < 0) {
332                 printf("WARNING: could not create /bd_t %s.\n",
333                         fdt_strerror(nodeoffset));
334                 printf("fdt_bd_t: %s\n", fdt_strerror(nodeoffset));
335                 return nodeoffset;
336         }
337         /*
338          * Use the string/pointer structure to create the entries...
339          */
340         for (i = 0; i < sizeof(bd_map)/sizeof(bd_map[0]); i++) {
341                 tmp = cpu_to_be32(getenv("bootargs"));
342                 err = fdt_setprop(fdt, nodeoffset,
343                         bd_map[i].name, &tmp, sizeof(tmp));
344                 if (err < 0)
345                         printf("WARNING: could not set %s %s.\n",
346                                 bd_map[i].name, fdt_strerror(err));
347         }
348         /*
349          * Add a couple of oddball entries...
350          */
351         err = fdt_setprop(fdt, nodeoffset, "enetaddr", &bd->bi_enetaddr, 6);
352         if (err < 0)
353                 printf("WARNING: could not set enetaddr %s.\n",
354                         fdt_strerror(err));
355         err = fdt_setprop(fdt, nodeoffset, "ethspeed", &bd->bi_ethspeed, 4);
356         if (err < 0)
357                 printf("WARNING: could not set ethspeed %s.\n",
358                         fdt_strerror(err));
359         return 0;
360 }
361 #endif /* ifdef CONFIG_OF_HAS_BD_T */
362
363 void do_fixup_by_path(void *fdt, const char *path, const char *prop,
364                       const void *val, int len, int create)
365 {
366 #if defined(DEBUG)
367         int i;
368         debug("Updating property '%s/%s' = ", node, prop);
369         for (i = 0; i < len; i++)
370                 debug(" %.2x", *(u8*)(val+i));
371         debug("\n");
372 #endif
373         int rc = fdt_find_and_setprop(fdt, path, prop, val, len, create);
374         if (rc)
375                 printf("Unable to update property %s:%s, err=%s\n",
376                         path, prop, fdt_strerror(rc));
377 }
378
379 void do_fixup_by_path_u32(void *fdt, const char *path, const char *prop,
380                           u32 val, int create)
381 {
382         val = cpu_to_fdt32(val);
383         do_fixup_by_path(fdt, path, prop, &val, sizeof(val), create);
384 }
385
386 void do_fixup_by_prop(void *fdt,
387                       const char *pname, const void *pval, int plen,
388                       const char *prop, const void *val, int len,
389                       int create)
390 {
391         int off;
392 #if defined(DEBUG)
393         int i;
394         debug("Updating property '%s/%s' = ", node, prop);
395         for (i = 0; i < len; i++)
396                 debug(" %.2x", *(u8*)(val+i));
397         debug("\n");
398 #endif
399         off = fdt_node_offset_by_prop_value(fdt, -1, pname, pval, plen);
400         while (off != -FDT_ERR_NOTFOUND) {
401                 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
402                         fdt_setprop(fdt, off, prop, val, len);
403                 off = fdt_node_offset_by_prop_value(fdt, off, pname, pval, plen);
404         }
405 }
406
407 void do_fixup_by_prop_u32(void *fdt,
408                           const char *pname, const void *pval, int plen,
409                           const char *prop, u32 val, int create)
410 {
411         val = cpu_to_fdt32(val);
412         do_fixup_by_prop(fdt, pname, pval, plen, prop, &val, 4, create);
413 }
414
415 void do_fixup_by_compat(void *fdt, const char *compat,
416                         const char *prop, const void *val, int len, int create)
417 {
418         int off = -1;
419 #if defined(DEBUG)
420         int i;
421         debug("Updating property '%s/%s' = ", node, prop);
422         for (i = 0; i < len; i++)
423                 debug(" %.2x", *(u8*)(val+i));
424         debug("\n");
425 #endif
426         off = fdt_node_offset_by_compatible(fdt, -1, compat);
427         while (off != -FDT_ERR_NOTFOUND) {
428                 if (create || (fdt_get_property(fdt, off, prop, 0) != NULL))
429                         fdt_setprop(fdt, off, prop, val, len);
430                 off = fdt_node_offset_by_compatible(fdt, off, compat);
431         }
432 }
433
434 void do_fixup_by_compat_u32(void *fdt, const char *compat,
435                             const char *prop, u32 val, int create)
436 {
437         val = cpu_to_fdt32(val);
438         do_fixup_by_compat(fdt, compat, prop, &val, 4, create);
439 }
440
441 int fdt_fixup_memory(void *blob, u64 start, u64 size)
442 {
443         int err, nodeoffset, len = 0;
444         u8 tmp[16];
445         const u32 *addrcell, *sizecell;
446
447         err = fdt_check_header(blob);
448         if (err < 0) {
449                 printf("%s: %s\n", __FUNCTION__, fdt_strerror(err));
450                 return err;
451         }
452
453         /* update, or add and update /memory node */
454         nodeoffset = fdt_path_offset(blob, "/memory");
455         if (nodeoffset < 0) {
456                 nodeoffset = fdt_add_subnode(blob, 0, "memory");
457                 if (nodeoffset < 0)
458                         printf("WARNING: could not create /memory: %s.\n",
459                                         fdt_strerror(nodeoffset));
460                 return nodeoffset;
461         }
462         err = fdt_setprop(blob, nodeoffset, "device_type", "memory",
463                         sizeof("memory"));
464         if (err < 0) {
465                 printf("WARNING: could not set %s %s.\n", "device_type",
466                                 fdt_strerror(err));
467                 return err;
468         }
469
470         addrcell = fdt_getprop(blob, 0, "#address-cells", NULL);
471         /* use shifts and mask to ensure endianness */
472         if ((addrcell) && (*addrcell == 2)) {
473                 tmp[0] = (start >> 56) & 0xff;
474                 tmp[1] = (start >> 48) & 0xff;
475                 tmp[2] = (start >> 40) & 0xff;
476                 tmp[3] = (start >> 32) & 0xff;
477                 tmp[4] = (start >> 24) & 0xff;
478                 tmp[5] = (start >> 16) & 0xff;
479                 tmp[6] = (start >>  8) & 0xff;
480                 tmp[7] = (start      ) & 0xff;
481                 len = 8;
482         } else {
483                 tmp[0] = (start >> 24) & 0xff;
484                 tmp[1] = (start >> 16) & 0xff;
485                 tmp[2] = (start >>  8) & 0xff;
486                 tmp[3] = (start      ) & 0xff;
487                 len = 4;
488         }
489
490         sizecell = fdt_getprop(blob, 0, "#size-cells", NULL);
491         /* use shifts and mask to ensure endianness */
492         if ((sizecell) && (*sizecell == 2)) {
493                 tmp[0+len] = (size >> 56) & 0xff;
494                 tmp[1+len] = (size >> 48) & 0xff;
495                 tmp[2+len] = (size >> 40) & 0xff;
496                 tmp[3+len] = (size >> 32) & 0xff;
497                 tmp[4+len] = (size >> 24) & 0xff;
498                 tmp[5+len] = (size >> 16) & 0xff;
499                 tmp[6+len] = (size >>  8) & 0xff;
500                 tmp[7+len] = (size      ) & 0xff;
501                 len += 8;
502         } else {
503                 tmp[0+len] = (size >> 24) & 0xff;
504                 tmp[1+len] = (size >> 16) & 0xff;
505                 tmp[2+len] = (size >>  8) & 0xff;
506                 tmp[3+len] = (size      ) & 0xff;
507                 len += 4;
508         }
509
510         err = fdt_setprop(blob, nodeoffset, "reg", tmp, len);
511         if (err < 0) {
512                 printf("WARNING: could not set %s %s.\n",
513                                 "reg", fdt_strerror(err));
514                 return err;
515         }
516         return 0;
517 }
518
519 void fdt_fixup_ethernet(void *fdt, bd_t *bd)
520 {
521         int node;
522         const char *path;
523
524         node = fdt_path_offset(fdt, "/aliases");
525         if (node >= 0) {
526 #if defined(CONFIG_HAS_ETH0)
527                 path = fdt_getprop(fdt, node, "ethernet0", NULL);
528                 if (path) {
529                         do_fixup_by_path(fdt, path, "mac-address",
530                                 bd->bi_enetaddr, 6, 0);
531                         do_fixup_by_path(fdt, path, "local-mac-address",
532                                 bd->bi_enetaddr, 6, 1);
533                 }
534 #endif
535 #if defined(CONFIG_HAS_ETH1)
536                 path = fdt_getprop(fdt, node, "ethernet1", NULL);
537                 if (path) {
538                         do_fixup_by_path(fdt, path, "mac-address",
539                                 bd->bi_enet1addr, 6, 0);
540                         do_fixup_by_path(fdt, path, "local-mac-address",
541                                 bd->bi_enet1addr, 6, 1);
542                 }
543 #endif
544 #if defined(CONFIG_HAS_ETH2)
545                 path = fdt_getprop(fdt, node, "ethernet2", NULL);
546                 if (path) {
547                         do_fixup_by_path(fdt, path, "mac-address",
548                                 bd->bi_enet2addr, 6, 0);
549                         do_fixup_by_path(fdt, path, "local-mac-address",
550                                 bd->bi_enet2addr, 6, 1);
551                 }
552 #endif
553 #if defined(CONFIG_HAS_ETH3)
554                 path = fdt_getprop(fdt, node, "ethernet3", NULL);
555                 if (path) {
556                         do_fixup_by_path(fdt, path, "mac-address",
557                                 bd->bi_enet3addr, 6, 0);
558                         do_fixup_by_path(fdt, path, "local-mac-address",
559                                 bd->bi_enet3addr, 6, 1);
560                 }
561 #endif
562         }
563 }