]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
fdt: karo: add support for LVDS selection via 'video_mode'
[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         printf("node '%s' offset=%d\n", "display", off);
538         ret = fdt_subnode_offset(blob, off, subnode);
539         if (ret < 0) {
540                 debug("Could not find node '%s' in FDT: %d\n", subnode, ret);
541                 ret = fdt_add_subnode(blob, off, subnode);
542                 if (ret < 0) {
543                         printf("Failed to add %s subnode: %d\n", subnode, ret);
544                         return ret;
545                 }
546         }
547
548         printf("node '%s' offset=%d\n", subnode, ret);
549         ret = fdt_add_subnode(blob, ret, name);
550         if (ret < 0) {
551                 printf("Failed to add %s subnode: %d\n", name, ret);
552                 return ret;
553         }
554         off = ret;
555
556         ret = SET_FB_PROP("clock-frequency",
557                         PICOS2KHZ(fb_mode->pixclock) * 1000);
558         if (ret)
559                 goto out;
560         ret = SET_FB_PROP("hactive", fb_mode->xres);
561         if (ret)
562                 goto out;
563         ret = SET_FB_PROP("vactive", fb_mode->yres);
564         if (ret)
565                 goto out;
566         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
567         if (ret)
568                 goto out;
569         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
570         if (ret)
571                 goto out;
572         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
573         if (ret)
574                 goto out;
575         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
576         if (ret)
577                 goto out;
578         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
579         if (ret)
580                 goto out;
581         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
582         if (ret)
583                 goto out;
584         ret = SET_FB_PROP("hsync-active",
585                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
586         if (ret)
587                 goto out;
588         ret = SET_FB_PROP("vsync-active",
589                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
590         if (ret)
591                 goto out;
592         ret = SET_FB_PROP("de-active",
593                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
594         if (ret)
595                 goto out;
596         ret = SET_FB_PROP("pixelclk-active",
597                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
598 out:
599         karo_set_fdtsize(blob);
600         return ret;
601 }
602
603 char *karo_fdt_set_display(char *video_mode, const char *lcd_path,
604                         const char *lvds_path)
605 {
606         char *vm;
607         int ret;
608         int off;
609         void *blob = (void *)gd->fdt_blob;
610
611         if (video_mode == NULL)
612                 return NULL;
613
614         off = fdt_path_offset(blob, "/aliases");
615         ret = fdt_resize(blob);
616         if (ret < 0) {
617                 printf("%s: Failed to resize FDT: %s\n",
618                         __func__, fdt_strerror(ret));
619         }
620         vm = strsep(&video_mode, ":");
621         if (off < 0) {
622                 off = fdt_add_subnode(blob, off, "aliases");
623                 if (off < 0) {
624                         printf("%s: Failed to create 'aliases' node: %s\n",
625                                 __func__, fdt_strerror(off));
626                         return video_mode ?: vm;
627                 }
628         }
629         if (video_mode != NULL) {
630                 char display[strlen(lvds_path) + sizeof("/lvds-channel@x")];
631
632                 strncpy(display, lvds_path, sizeof(display));
633                 if (strcmp(vm, "LVDS") == 0 ||
634                         strcmp(vm, "LVDS0") == 0) {
635                         strncat(display, "/lvds-channel@0", sizeof(display));
636                 } else if (strcmp(vm, "LVDS1") == 0) {
637                         strncat(display, "/lvds-channel@1", sizeof(display));
638                 } else {
639                         printf("%s: Syntax error in video_mode\n",
640                                 __func__);
641                         return video_mode;
642                 }
643                 ret = fdt_setprop_string(blob, off, "display", display);
644                 debug("setprop_string(display='%s') returned %d\n", display, ret);
645         } else {
646                 char display[strlen(lcd_path) + sizeof("/display@di0")];
647
648                 snprintf(display, sizeof(display), "%s/display@di0", lcd_path);
649
650                 ret = fdt_setprop_string(blob, off, "display",
651                                         display);
652
653                 off = fdt_path_offset(blob, "lvds0");
654                 if (off >= 0) {
655                         ret = fdt_set_node_status(blob, off,
656                                                 FDT_STATUS_DISABLED, 0);
657                 }
658                 off = fdt_path_offset(blob, "lvds1");
659                 if (off >= 0) {
660                         ret = fdt_set_node_status(blob, off,
661                                                 FDT_STATUS_DISABLED, 0);
662                 }
663                 video_mode = vm;
664         }
665         if (ret) {
666                 printf("%s: failed to set 'display' alias: %s\n",
667                         __func__, fdt_strerror(ret));
668         }
669         return video_mode;
670 }
671
672 int karo_fdt_update_fb_mode(void *blob, const char *name)
673 {
674         int off = fdt_path_offset(blob, "display");
675         const char *subnode = "display-timings";
676
677         if (off < 0)
678                 return off;
679
680         if (name == NULL) {
681                 int ret;
682
683                 debug("Disabling node '%s' at %03x\n",
684                         fdt_get_name(blob, off, NULL), off);
685                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
686                 return ret;
687         }
688
689         off = fdt_subnode_offset(blob, off, subnode);
690         if (off < 0) {
691                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
692                 return off;
693         }
694         while (off > 0) {
695                 const char *n, *endp;
696                 int len, d = 1;
697
698                 off = fdt_next_node(blob, off, &d);
699                 if (off < 0)
700                         return off;
701                 if (d < 1)
702                         return -EINVAL;
703                 if (d > 2) {
704                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
705                         continue;
706                 }
707                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
708
709                 n = fdt_getprop(blob, off, "panel-name", &len);
710                 if (!n) {
711                         n = fdt_get_name(blob, off, NULL);
712                         if (strcasecmp(n, name) == 0) {
713                                 break;
714                         }
715                 } else {
716                         int found = 0;
717
718                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
719                                 debug("Checking panel-name '%s'\n", n);
720                                 if (strcasecmp(n, name) == 0) {
721                                         debug("Using node %s @ %04x\n",
722                                                 fdt_get_name(blob, off, NULL), off);
723                                         found = 1;
724                                         break;
725                                 }
726                         }
727                         if (found)
728                                 break;
729                 }
730         }
731         if (off > 0)
732                 return fdt_update_native_fb_mode(blob, off);
733         return off;
734 }
735
736 static int karo_load_part(const char *part, void *addr, size_t len)
737 {
738         int ret;
739         struct mtd_device *dev;
740         struct part_info *part_info;
741         u8 part_num;
742         size_t actual;
743
744         debug("Initializing mtd_parts\n");
745         ret = mtdparts_init();
746         if (ret)
747                 return ret;
748
749         debug("Trying to find NAND partition '%s'\n", part);
750         ret = find_dev_and_part(part, &dev, &part_num, &part_info);
751         if (ret) {
752                 printf("Failed to find flash partition '%s': %d\n",
753                         part, ret);
754
755                 return ret;
756         }
757         debug("Found partition '%s': offset=%08x size=%08x\n",
758                 part, part_info->offset, part_info->size);
759         if (part_info->size < len) {
760                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
761                         part, len, part_info->size);
762                 len = part_info->size;
763         }
764         debug("Reading NAND partition '%s' to %p\n", part, addr);
765         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len,
766                                 &actual, len, addr);
767         if (ret) {
768                 printf("Failed to load partition '%s' to %p\n", part, addr);
769                 return ret;
770         }
771         if (actual < len)
772                 printf("Read only %u of %u bytes due to bad blocks\n",
773                         actual, len);
774
775         debug("Read %u byte from partition '%s' @ offset %08x\n",
776                 len, part, part_info->offset);
777         return 0;
778 }
779
780 void *karo_fdt_load_dtb(void)
781 {
782         int ret;
783         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
784
785         if (tstc()) {
786                 debug("aborting DTB load\n");
787                 return NULL;
788         }
789
790         /* clear FDT header in memory */
791         memset(fdt, 0, 4);
792
793         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
794         if (ret) {
795                 printf("Failed to load dtb from flash: %d\n", ret);
796                 return NULL;
797         }
798
799         if (fdt_check_header(fdt)) {
800                 debug("No valid DTB in flash\n");
801                 return NULL;
802         }
803         debug("Using DTB from flash\n");
804         karo_set_fdtsize(fdt);
805         return fdt;
806 }