]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
karo: prevent video_mode variable from being clobbered by karo_fdt_set_display()
[karo-tx-uboot.git] / board / karo / common / fdt.c
1 /*
2  * (C) Copyright 2012,2013 Lothar Waßmann <LW@KARO-electronics.de>
3  *
4  * See file CREDITS for list of people who contributed to this
5  * project.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16 */
17
18 #include <common.h>
19 #include <errno.h>
20 #include <libfdt.h>
21 #include <fdt_support.h>
22 #include <nand.h>
23 #include <mxcfb.h>
24 #include <linux/list.h>
25 #include <linux/fb.h>
26 #include <jffs2/load_kernel.h>
27
28 #include "karo.h"
29
30 #ifdef CONFIG_MAX_DTB_SIZE
31 #define MAX_DTB_SIZE    CONFIG_MAX_DTB_SIZE
32 #else
33 #define MAX_DTB_SIZE    SZ_64K
34 #endif
35
36 DECLARE_GLOBAL_DATA_PTR;
37
38 static void karo_set_fdtsize(void *fdt)
39 {
40         char fdt_size[9];
41         size_t fdtsize = getenv_ulong("fdtsize", 16, 0);
42
43         if (fdtsize == fdt_totalsize(fdt)) {
44                 return;
45         }
46         debug("FDT size changed from %u to %u\n",
47                 fdtsize, fdt_totalsize(fdt));
48
49         snprintf(fdt_size, sizeof(fdt_size), "%08x", fdt_totalsize(fdt));
50         setenv("fdtsize", fdt_size);
51 }
52
53 void karo_fdt_move_fdt(void)
54 {
55         void *fdt;
56         unsigned long fdt_addr = getenv_ulong("fdtaddr", 16, 0);
57
58         if (working_fdt) {
59                 debug("DTB already loaded\n");
60                 return;
61         }
62
63         if (!fdt_addr) {
64                 fdt_addr = CONFIG_SYS_FDT_ADDR;
65                 printf("fdtaddr is not set; using default: %08lx\n",
66                         fdt_addr);
67         }
68
69         fdt = karo_fdt_load_dtb();
70         if (fdt == NULL) {
71                 fdt = (void *)gd->fdt_blob;
72                 if (fdt == NULL) {
73 #ifdef CONFIG_OF_EMBED
74                         printf("Compiled in FDT not found\n");
75 #else
76                         printf("No FDT found\n");
77 #endif
78                         return;
79                 }
80                 debug("Checking FDT header @ %p\n", fdt);
81                 if (fdt_check_header(fdt)) {
82                         printf("ERROR: No valid DTB found at %p\n", fdt);
83                         return;
84                 }
85                 printf("No DTB in flash; using default DTB\n");
86                 debug("Moving FDT from %p..%p to %08lx..%08lx\n",
87                         fdt, fdt + fdt_totalsize(fdt) - 1,
88                         fdt_addr, fdt_addr + fdt_totalsize(fdt) - 1);
89                 memmove((void *)fdt_addr, fdt, fdt_totalsize(fdt));
90         }
91         set_working_fdt_addr((void *)fdt_addr);
92         gd->fdt_blob = fdt;
93         karo_set_fdtsize(fdt);
94 }
95
96 void karo_fdt_remove_node(void *blob, const char *node)
97 {
98         int off = fdt_path_offset(blob, node);
99         int ret;
100
101         debug("Removing node '%s' from DT\n", node);
102
103         if (off < 0) {
104                 printf("Could not find node '%s': %d\n", node, off);
105         } else {
106                 ret = fdt_del_node(blob, off);
107                 if (ret)
108                         printf("Failed to remove node '%s': %d\n",
109                                 node, ret);
110         }
111         karo_set_fdtsize(blob);
112 }
113
114 void karo_fdt_enable_node(void *blob, const char *node, int enable)
115 {
116         int off = fdt_path_offset(blob, node);
117
118         debug("%sabling node '%s'\n", enable ? "En" : "Dis", node);
119         if (off < 0) {
120                 printf("Could not find node '%s': %d\n", node, off);
121                 return;
122         }
123         fdt_set_node_status(blob, off,
124                         enable ? FDT_STATUS_OKAY : FDT_STATUS_DISABLED, 0);
125
126         karo_set_fdtsize(blob);
127 }
128
129 static const char *karo_touchpanels[] = {
130         "ti,tsc2007",
131         "edt,edt-ft5x06",
132 #ifdef CONFIG_MX28
133         "fsl,imx28-lradc",
134 #endif
135 };
136
137 static void fdt_disable_tp_node(void *blob, const char *name)
138 {
139         int offs = fdt_node_offset_by_compatible(blob, -1, name);
140
141         if (offs < 0) {
142                 debug("node '%s' not found: %d\n", name, offs);
143                 return;
144         }
145
146         debug("Disabling node '%s'\n", name);
147         fdt_set_node_status(blob, offs, FDT_STATUS_DISABLED, 0);
148 }
149
150 void karo_fdt_fixup_touchpanel(void *blob)
151 {
152         int i;
153         const char *model = getenv("touchpanel");
154
155         for (i = 0; i < ARRAY_SIZE(karo_touchpanels); i++) {
156                 const char *tp = karo_touchpanels[i];
157
158                 if (model != NULL && strcmp(model, tp) == 0)
159                         continue;
160
161                 if (model != NULL) {
162                         if (strcmp(model, tp) == 0)
163                                 continue;
164                         tp = strchr(tp, ',');
165                         if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
166                                 continue;
167                 }
168                 fdt_disable_tp_node(blob, karo_touchpanels[i]);
169         }
170         karo_set_fdtsize(blob);
171 }
172
173 void karo_fdt_fixup_usb_otg(void *blob, const char *node, const char *phy)
174 {
175         const char *otg_mode = getenv("otg_mode");
176         int off;
177         int ret;
178         const uint32_t *ph;
179         int disable_otg = 0;
180         int disable_phy_pins = 1;
181
182         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
183
184         off = fdt_path_offset(blob, node);
185         if (off < 0) {
186                 debug("Failed to find node %s\n", node);
187                 return;
188         }
189
190         if (otg_mode && (strcmp(otg_mode, "device") == 0 ||
191                                 strcmp(otg_mode, "gadget") == 0)) {
192                 debug("Setting dr_mode to 'peripheral'\n");
193                 ret = fdt_setprop_string(blob, off, "dr_mode", "peripheral");
194         } else if (otg_mode && strcmp(otg_mode, "host") == 0) {
195                 debug("Setting dr_mode to 'host'\n");
196                 ret = fdt_setprop_string(blob, off, "dr_mode", "host");
197                 disable_phy_pins = 0;
198         } else if (otg_mode && strcmp(otg_mode, "otg") == 0) {
199                 debug("Setting dr_mode to 'host'\n");
200                 ret = fdt_setprop_string(blob, off, "dr_mode", "otg");
201         } else {
202                 if (otg_mode && strcmp(otg_mode, "none") != 0)
203                         printf("Invalid 'otg_mode' setting '%s'; disabling usbotg port\n",
204                                 otg_mode);
205                 disable_otg = 1;
206                 ret = 0;
207         }
208
209         if ((!disable_phy_pins && !disable_otg) || ret)
210                 goto out;
211
212         if (disable_otg) {
213                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
214                 if (ret)
215                         goto out;
216         }
217
218         ph = fdt_getprop(blob, off, phy, NULL);
219         if (ph == NULL) {
220                 printf("Failed to find '%s' phandle in node '%s'\n", phy,
221                         fdt_get_name(blob, off, NULL));
222                 goto out;
223         }
224
225         off = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*ph));
226         if (off <= 0) {
227                 printf("Failed to find '%s' node via phandle %04x\n",
228                         phy, fdt32_to_cpu(*ph));
229                 goto out;
230         }
231
232         if (disable_otg) {
233                 debug("Disabling usbphy\n");
234                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
235         }
236 out:
237         if (ret)
238                 printf("Failed to update usbotg: %d\n", ret);
239         else
240                 debug("node '%s' updated\n", node);
241         karo_set_fdtsize(blob);
242 }
243
244 static int karo_fdt_flexcan_enabled(void *blob)
245 {
246         const char *can_ifs[] = {
247                 "can0",
248                 "can1",
249         };
250         size_t i;
251
252         for (i = 0; i < ARRAY_SIZE(can_ifs); i++) {
253                 const char *status;
254                 int off = fdt_path_offset(blob, can_ifs[i]);
255
256                 if (off < 0) {
257                         debug("node '%s' not found\n", can_ifs[i]);
258                         continue;
259                 }
260                 status = fdt_getprop(blob, off, "status", NULL);
261                 if (status && strcmp(status, "okay") == 0) {
262                         debug("%s is enabled\n", can_ifs[i]);
263                         return 1;
264                 }
265         }
266         debug("can driver is disabled\n");
267         return 0;
268 }
269
270 static void karo_fdt_set_lcd_pins(void *blob, const char *name)
271 {
272         int off = fdt_path_offset(blob, name);
273         u32 ph;
274         const struct fdt_property *pc;
275         int len;
276
277         if (off < 0)
278                 return;
279
280         ph = fdt_create_phandle(blob, off);
281         if (!ph)
282                 return;
283
284         off = fdt_path_offset(blob, "display");
285         if (off < 0)
286                 return;
287
288         pc = fdt_get_property(blob, off, "pinctrl-0", &len);
289         if (!pc || len < sizeof(ph))
290                 return;
291
292         memcpy((void *)pc->data, &ph, sizeof(ph));
293         fdt_setprop_cell(blob, off, "pinctrl-0", ph);
294 }
295
296 void karo_fdt_fixup_flexcan(void *blob, int xcvr_present)
297 {
298         const char *xcvr_status = "disabled";
299
300         if (xcvr_present) {
301                 if (karo_fdt_flexcan_enabled(blob)) {
302                         karo_fdt_set_lcd_pins(blob, "lcdif_23bit_pins_a");
303                         xcvr_status = "okay";
304                 } else {
305                         karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
306                 }
307         } else {
308                 const char *otg_mode = getenv("otg_mode");
309
310                 if (otg_mode && (strcmp(otg_mode, "host") == 0))
311                         karo_fdt_enable_node(blob, "can1", 0);
312
313                 karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
314         }
315         fdt_find_and_setprop(blob, "/regulators/can-xcvr", "status",
316                         xcvr_status, strlen(xcvr_status) + 1, 1);
317 }
318
319 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
320                         const char *prop)
321 {
322         int ret;
323         int offset;
324         const uint32_t *phandle;
325         uint32_t ph = 0;
326
327         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
328         if (offset <= 0)
329                 return;
330
331         phandle = fdt_getprop(blob, offset, prop, NULL);
332         if (phandle) {
333                 ph = fdt32_to_cpu(*phandle);
334         }
335
336         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
337         ret = fdt_delprop(blob, offset, prop);
338         if (ret == 0)
339                 karo_set_fdtsize(blob);
340
341         if (!ph)
342                 return;
343
344         offset = fdt_node_offset_by_phandle(blob, ph);
345         if (offset <= 0)
346                 return;
347
348         debug("Removing node @ %08x\n", offset);
349         fdt_del_node(blob, offset);
350         karo_set_fdtsize(blob);
351 }
352
353 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
354 {
355         const uint32_t *prop;
356
357         memset(fb_mode, 0, sizeof(*fb_mode));
358
359         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
360         if (prop)
361                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
362
363         prop = fdt_getprop(blob, off, "hactive", NULL);
364         if (prop)
365                 fb_mode->xres = fdt32_to_cpu(*prop);
366
367         prop = fdt_getprop(blob, off, "vactive", NULL);
368         if (prop)
369                 fb_mode->yres = fdt32_to_cpu(*prop);
370
371         prop = fdt_getprop(blob, off, "hback-porch", NULL);
372         if (prop)
373                 fb_mode->left_margin = fdt32_to_cpu(*prop);
374
375         prop = fdt_getprop(blob, off, "hsync-len", NULL);
376         if (prop)
377                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
378
379         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
380         if (prop)
381                 fb_mode->right_margin = fdt32_to_cpu(*prop);
382
383         prop = fdt_getprop(blob, off, "vback-porch", NULL);
384         if (prop)
385                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
386
387         prop = fdt_getprop(blob, off, "vsync-len", NULL);
388         if (prop)
389                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
390
391         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
392         if (prop)
393                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
394
395         prop = fdt_getprop(blob, off, "hsync-active", NULL);
396         if (prop)
397                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
398
399         prop = fdt_getprop(blob, off, "vsync-active", NULL);
400         if (prop)
401                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
402
403         prop = fdt_getprop(blob, off, "de-active", NULL);
404         if (prop)
405                 fb_mode->sync |= *prop ? 0 : FB_SYNC_OE_LOW_ACT;
406
407         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
408         if (prop)
409                 fb_mode->sync |= *prop ? 0 : FB_SYNC_CLK_LAT_FALL;
410
411         return 0;
412 }
413
414 static int fdt_update_native_fb_mode(void *blob, int off)
415 {
416         int ret;
417         uint32_t ph;
418
419         ret = fdt_increase_size(blob, 64);
420         if (ret) {
421                 printf("Warning: Failed to increase FDT size: %d\n", ret);
422         }
423         debug("Creating phandle at offset %d\n", off);
424         ph = fdt_create_phandle(blob, off);
425         if (!ph) {
426                 printf("Failed to create phandle for video timing\n");
427                 return -ENOMEM;
428         }
429
430         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
431                 off, ph);
432         off = fdt_parent_offset(blob, off);
433         if (off < 0)
434                 return off;
435         debug("parent offset=%06x\n", off);
436         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
437         if (ret)
438                 printf("Failed to set property 'native-mode': %d\n", ret);
439         karo_set_fdtsize(blob);
440         return ret;
441 }
442
443 static int karo_fdt_find_video_timings(void *blob)
444 {
445         int off = fdt_path_offset(blob, "display");
446         const char *subnode = "display-timings";
447
448         if (off < 0) {
449                 debug("Could not find node 'display' in FDT: %d\n", off);
450                 return off;
451         }
452
453         off = fdt_subnode_offset(blob, off, subnode);
454         if (off < 0) {
455                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
456         }
457         return off;
458 }
459
460 int karo_fdt_get_fb_mode(void *blob, const char *name, struct fb_videomode *fb_mode)
461 {
462         int off = karo_fdt_find_video_timings(blob);
463
464         if (off < 0)
465                 return off;
466         while (off > 0) {
467                 const char *n, *endp;
468                 int len, d = 1;
469
470                 off = fdt_next_node(blob, off, &d);
471                 if (off < 0)
472                         return off;
473                 if (d < 1)
474                         return -EINVAL;
475                 if (d > 2) {
476                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
477                         continue;
478                 }
479                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
480
481                 n = fdt_getprop(blob, off, "panel-name", &len);
482                 if (!n) {
483                         n = fdt_get_name(blob, off, NULL);
484                         if (strcasecmp(n, name) == 0) {
485                                 break;
486                         }
487                 } else {
488                         int found = 0;
489
490                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
491                                 debug("Checking panel-name '%s'\n", n);
492                                 if (strcasecmp(n, name) == 0) {
493                                         debug("Using node %s @ %04x\n",
494                                                 fdt_get_name(blob, off, NULL), off);
495                                         found = 1;
496                                         break;
497                                 }
498                         }
499                         if (found)
500                                 break;
501                 }
502         }
503         if (off > 0) {
504                 return fdt_init_fb_mode(blob, off, fb_mode);
505         }
506         return -EINVAL;
507 }
508
509 #define SET_FB_PROP(n, v) ({                                            \
510         int ret;                                                        \
511         ret = fdt_setprop_u32(blob, off, n, v);                         \
512         if (ret) {                                                      \
513                 printf("Failed to set property %s: %d\n", name, ret);   \
514         }                                                               \
515         ret;                                                            \
516 })
517
518
519 int karo_fdt_create_fb_mode(void *blob, const char *name,
520                         struct fb_videomode *fb_mode)
521 {
522         int off = fdt_path_offset(blob, "display");
523         int ret;
524         const char *subnode = "display-timings";
525
526         if (off < 0) {
527                 printf("'display' node not found in FDT\n");
528                 return off;
529         }
530
531         ret = fdt_increase_size(blob, 512);
532         if (ret) {
533                 printf("Failed to increase FDT size: %d\n", ret);
534                 return ret;
535         }
536
537         ret = fdt_subnode_offset(blob, off, subnode);
538         if (ret < 0) {
539                 debug("Could not find node '%s' in FDT: %d\n", subnode, ret);
540                 ret = fdt_add_subnode(blob, off, subnode);
541                 if (ret < 0) {
542                         printf("Failed to add %s subnode: %d\n", subnode, ret);
543                         return ret;
544                 }
545         }
546
547         ret = fdt_add_subnode(blob, ret, name);
548         if (ret < 0) {
549                 printf("Failed to add %s subnode: %d\n", name, ret);
550                 return ret;
551         }
552         off = ret;
553
554         ret = SET_FB_PROP("clock-frequency",
555                         PICOS2KHZ(fb_mode->pixclock) * 1000);
556         if (ret)
557                 goto out;
558         ret = SET_FB_PROP("hactive", fb_mode->xres);
559         if (ret)
560                 goto out;
561         ret = SET_FB_PROP("vactive", fb_mode->yres);
562         if (ret)
563                 goto out;
564         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
565         if (ret)
566                 goto out;
567         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
568         if (ret)
569                 goto out;
570         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
571         if (ret)
572                 goto out;
573         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
574         if (ret)
575                 goto out;
576         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
577         if (ret)
578                 goto out;
579         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
580         if (ret)
581                 goto out;
582         ret = SET_FB_PROP("hsync-active",
583                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
584         if (ret)
585                 goto out;
586         ret = SET_FB_PROP("vsync-active",
587                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
588         if (ret)
589                 goto out;
590         ret = SET_FB_PROP("de-active",
591                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
592         if (ret)
593                 goto out;
594         ret = SET_FB_PROP("pixelclk-active",
595                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
596 out:
597         karo_set_fdtsize(blob);
598         return ret;
599 }
600
601 const char *karo_fdt_set_display(const char *video_mode, const char *lcd_path,
602                                 const char *lvds_path)
603 {
604         static char buffer[128];
605         char *vm, *vmode = buffer;
606         int ret;
607         int off;
608         void *blob = (void *)gd->fdt_blob;
609
610         if (video_mode == NULL || strlen(video_mode) == 0)
611                 return NULL;
612
613         if (strlen(video_mode) >= sizeof(buffer)) {
614                 printf("video_mode string too long\n");
615                 return NULL;
616         }
617         strcpy(buffer, video_mode);
618
619         off = fdt_path_offset(blob, "/aliases");
620         ret = fdt_resize(blob);
621         if (ret < 0) {
622                 printf("%s: Failed to resize FDT: %s\n",
623                         __func__, fdt_strerror(ret));
624         }
625         vm = strsep(&vmode, ":");
626         if (off < 0) {
627                 off = fdt_add_subnode(blob, off, "aliases");
628                 if (off < 0) {
629                         printf("%s: Failed to create 'aliases' node: %s\n",
630                                 __func__, fdt_strerror(off));
631                         return vmode ?: vm;
632                 }
633         }
634         if (vmode != NULL) {
635                 char display[strlen(lvds_path) + sizeof("/lvds-channel@x")];
636
637                 strncpy(display, lvds_path, sizeof(display));
638                 if (strcmp(vm, "LVDS") == 0 ||
639                         strcmp(vm, "LVDS0") == 0) {
640                         strncat(display, "/lvds-channel@0", sizeof(display));
641                 } else if (strcmp(vm, "LVDS1") == 0) {
642                         strncat(display, "/lvds-channel@1", sizeof(display));
643                 } else {
644                         debug("%s: Syntax error in video_mode\n", __func__);
645                         return vmode;
646                 }
647                 ret = fdt_setprop_string(blob, off, "display", display);
648                 debug("setprop_string(display='%s') returned %d\n", display, ret);
649         } else {
650                 char display[strlen(lcd_path) + sizeof("/display@di0")];
651
652                 snprintf(display, sizeof(display), "%s/display@di0", lcd_path);
653
654                 ret = fdt_setprop_string(blob, off, "display",
655                                         display);
656
657                 off = fdt_path_offset(blob, "lvds0");
658                 if (off >= 0) {
659                         ret = fdt_set_node_status(blob, off,
660                                                 FDT_STATUS_DISABLED, 0);
661                 }
662                 off = fdt_path_offset(blob, "lvds1");
663                 if (off >= 0) {
664                         ret = fdt_set_node_status(blob, off,
665                                                 FDT_STATUS_DISABLED, 0);
666                 }
667                 vmode = vm;
668         }
669         if (ret) {
670                 printf("%s: failed to set 'display' alias: %s\n",
671                         __func__, fdt_strerror(ret));
672         }
673         return vmode;
674 }
675
676 int karo_fdt_update_fb_mode(void *blob, const char *name)
677 {
678         int off = fdt_path_offset(blob, "display");
679         const char *subnode = "display-timings";
680
681         if (off < 0)
682                 return off;
683
684         if (name == NULL) {
685                 int ret;
686
687                 debug("Disabling node '%s' at %03x\n",
688                         fdt_get_name(blob, off, NULL), off);
689                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
690                 return ret;
691         }
692
693         off = fdt_subnode_offset(blob, off, subnode);
694         if (off < 0) {
695                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
696                 return off;
697         }
698         while (off > 0) {
699                 const char *n, *endp;
700                 int len, d = 1;
701
702                 off = fdt_next_node(blob, off, &d);
703                 if (off < 0)
704                         return off;
705                 if (d < 1)
706                         return -EINVAL;
707                 if (d > 2) {
708                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
709                         continue;
710                 }
711                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
712
713                 n = fdt_getprop(blob, off, "panel-name", &len);
714                 if (!n) {
715                         n = fdt_get_name(blob, off, NULL);
716                         if (strcasecmp(n, name) == 0) {
717                                 break;
718                         }
719                 } else {
720                         int found = 0;
721
722                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
723                                 debug("Checking panel-name '%s'\n", n);
724                                 if (strcasecmp(n, name) == 0) {
725                                         debug("Using node %s @ %04x\n",
726                                                 fdt_get_name(blob, off, NULL), off);
727                                         found = 1;
728                                         break;
729                                 }
730                         }
731                         if (found)
732                                 break;
733                 }
734         }
735         if (off > 0)
736                 return fdt_update_native_fb_mode(blob, off);
737         return off;
738 }
739
740 static int karo_load_part(const char *part, void *addr, size_t len)
741 {
742         int ret;
743         struct mtd_device *dev;
744         struct part_info *part_info;
745         u8 part_num;
746         size_t actual;
747
748         debug("Initializing mtd_parts\n");
749         ret = mtdparts_init();
750         if (ret)
751                 return ret;
752
753         debug("Trying to find NAND partition '%s'\n", part);
754         ret = find_dev_and_part(part, &dev, &part_num, &part_info);
755         if (ret) {
756                 printf("Failed to find flash partition '%s': %d\n",
757                         part, ret);
758
759                 return ret;
760         }
761         debug("Found partition '%s': offset=%08x size=%08x\n",
762                 part, part_info->offset, part_info->size);
763         if (part_info->size < len) {
764                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
765                         part, len, part_info->size);
766                 len = part_info->size;
767         }
768         debug("Reading NAND partition '%s' to %p\n", part, addr);
769         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len,
770                                 &actual, len, addr);
771         if (ret) {
772                 printf("Failed to load partition '%s' to %p\n", part, addr);
773                 return ret;
774         }
775         if (actual < len)
776                 printf("Read only %u of %u bytes due to bad blocks\n",
777                         actual, len);
778
779         debug("Read %u byte from partition '%s' @ offset %08x\n",
780                 len, part, part_info->offset);
781         return 0;
782 }
783
784 void *karo_fdt_load_dtb(void)
785 {
786         int ret;
787         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
788
789         if (tstc()) {
790                 debug("aborting DTB load\n");
791                 return NULL;
792         }
793
794         /* clear FDT header in memory */
795         memset(fdt, 0, 4);
796
797         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
798         if (ret) {
799                 printf("Failed to load dtb from flash: %d\n", ret);
800                 return NULL;
801         }
802
803         if (fdt_check_header(fdt)) {
804                 debug("No valid DTB in flash\n");
805                 return NULL;
806         }
807         debug("Using DTB from flash\n");
808         karo_set_fdtsize(fdt);
809         return fdt;
810 }