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