]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
Merge branch 'karo-tx-merge'
[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                         printf("Compiled in FDT not found\n");
74                         return;
75                 }
76                 debug("Checking FDT header @ %p\n", fdt);
77                 if (fdt_check_header(fdt)) {
78                         printf("ERROR: No valid DTB found at %p\n", fdt);
79                         return;
80                 }
81                 printf("No DTB in flash; using default DTB\n");
82                 debug("Moving FDT from %p..%p to %08lx..%08lx\n",
83                         fdt, fdt + fdt_totalsize(fdt) - 1,
84                         fdt_addr, fdt_addr + fdt_totalsize(fdt) - 1);
85                 memmove((void *)fdt_addr, fdt, fdt_totalsize(fdt));
86         }
87         set_working_fdt_addr((void *)fdt_addr);
88         gd->fdt_blob = fdt;
89         karo_set_fdtsize(fdt);
90 }
91
92 void karo_fdt_remove_node(void *blob, const char *node)
93 {
94         int off = fdt_path_offset(blob, node);
95         int ret;
96
97         debug("Removing node '%s' from DT\n", node);
98
99         if (off < 0) {
100                 printf("Could not find node '%s': %d\n", node, off);
101         } else {
102                 ret = fdt_del_node(blob, off);
103                 if (ret)
104                         printf("Failed to remove node '%s': %d\n",
105                                 node, ret);
106         }
107         karo_set_fdtsize(blob);
108 }
109
110 void karo_fdt_enable_node(void *blob, const char *node, int enable)
111 {
112         int off = fdt_path_offset(blob, node);
113
114         debug("%sabling node '%s'\n", enable ? "En" : "Dis", node);
115         if (off < 0) {
116                 printf("Could not find node '%s': %d\n", node, off);
117                 return;
118         }
119         fdt_set_node_status(blob, off,
120                         enable ? FDT_STATUS_OKAY : FDT_STATUS_DISABLED, 0);
121
122         karo_set_fdtsize(blob);
123 }
124
125 static const char *karo_touchpanels[] = {
126         "ti,tsc2007",
127         "edt,edt-ft5x06",
128 #ifdef CONFIG_MX28
129         "fsl,imx28-lradc",
130 #endif
131 };
132
133 static void fdt_disable_tp_node(void *blob, const char *name)
134 {
135         int offs = fdt_node_offset_by_compatible(blob, -1, name);
136
137         if (offs < 0) {
138                 debug("node '%s' not found: %d\n", name, offs);
139                 return;
140         }
141
142         debug("Disabling node '%s'\n", name);
143         fdt_set_node_status(blob, offs, FDT_STATUS_DISABLED, 0);
144 }
145
146 void karo_fdt_fixup_touchpanel(void *blob)
147 {
148         int i;
149         const char *model = getenv("touchpanel");
150
151         for (i = 0; i < ARRAY_SIZE(karo_touchpanels); i++) {
152                 const char *tp = karo_touchpanels[i];
153
154                 if (model != NULL && strcmp(model, tp) == 0)
155                         continue;
156
157                 if (model != NULL) {
158                         if (strcmp(model, tp) == 0)
159                                 continue;
160                         tp = strchr(tp, ',');
161                         if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
162                                 continue;
163                 }
164                 fdt_disable_tp_node(blob, karo_touchpanels[i]);
165         }
166         karo_set_fdtsize(blob);
167 }
168
169 void karo_fdt_fixup_usb_otg(void *blob, const char *node, const char *phy)
170 {
171         const char *otg_mode = getenv("otg_mode");
172         int off;
173         int ret;
174         const uint32_t *ph;
175         int disable_otg = 0;
176         int disable_phy_pins = 1;
177
178         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
179
180         off = fdt_path_offset(blob, node);
181         if (off < 0) {
182                 debug("Failed to find node %s\n", node);
183                 return;
184         }
185
186         if (otg_mode && (strcmp(otg_mode, "device") == 0 ||
187                                 strcmp(otg_mode, "gadget") == 0)) {
188                 debug("Setting dr_mode to 'peripheral'\n");
189                 ret = fdt_setprop_string(blob, off, "dr_mode", "peripheral");
190         } else if (otg_mode && strcmp(otg_mode, "host") == 0) {
191                 debug("Setting dr_mode to 'host'\n");
192                 ret = fdt_setprop_string(blob, off, "dr_mode", "host");
193                 disable_phy_pins = 0;
194         } else if (otg_mode && strcmp(otg_mode, "otg") == 0) {
195                 debug("Setting dr_mode to 'host'\n");
196                 ret = fdt_setprop_string(blob, off, "dr_mode", "otg");
197         } else {
198                 if (otg_mode && strcmp(otg_mode, "none") != 0)
199                         printf("Invalid 'otg_mode' setting '%s'; disabling usbotg port\n",
200                                 otg_mode);
201                 disable_otg = 1;
202                 ret = 0;
203         }
204
205         if ((!disable_phy_pins && !disable_otg) || ret)
206                 goto out;
207
208         if (disable_otg) {
209                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
210                 if (ret)
211                         goto out;
212         }
213
214         ph = fdt_getprop(blob, off, phy, NULL);
215         if (ph == NULL) {
216                 printf("Failed to find '%s' phandle in node '%s'\n", phy,
217                         fdt_get_name(blob, off, NULL));
218                 goto out;
219         }
220
221         off = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*ph));
222         if (off <= 0) {
223                 printf("Failed to find '%s' node via phandle %04x\n",
224                         phy, fdt32_to_cpu(*ph));
225                 goto out;
226         }
227
228         if (disable_otg) {
229                 debug("Disabling usbphy\n");
230                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
231         }
232 out:
233         if (ret)
234                 printf("Failed to update usbotg: %d\n", ret);
235         else
236                 debug("node '%s' updated\n", node);
237         karo_set_fdtsize(blob);
238 }
239
240 static int karo_fdt_flexcan_enabled(void *blob)
241 {
242         const char *can_ifs[] = {
243                 "can0",
244                 "can1",
245         };
246         size_t i;
247
248         for (i = 0; i < ARRAY_SIZE(can_ifs); i++) {
249                 const char *status;
250                 int off = fdt_path_offset(blob, can_ifs[i]);
251
252                 if (off < 0) {
253                         debug("node '%s' not found\n", can_ifs[i]);
254                         continue;
255                 }
256                 status = fdt_getprop(blob, off, "status", NULL);
257                 if (status && strcmp(status, "okay") == 0) {
258                         debug("%s is enabled\n", can_ifs[i]);
259                         return 1;
260                 }
261         }
262         debug("can driver is disabled\n");
263         return 0;
264 }
265
266 static void karo_fdt_set_lcd_pins(void *blob, const char *name)
267 {
268         int off = fdt_path_offset(blob, name);
269         u32 ph;
270         const struct fdt_property *pc;
271         int len;
272
273         if (off < 0)
274                 return;
275
276         ph = fdt_create_phandle(blob, off);
277         if (!ph)
278                 return;
279
280         off = fdt_path_offset(blob, "display");
281         if (off < 0)
282                 return;
283
284         pc = fdt_get_property(blob, off, "pinctrl-0", &len);
285         if (!pc || len < sizeof(ph))
286                 return;
287
288         memcpy((void *)pc->data, &ph, sizeof(ph));
289         fdt_setprop_cell(blob, off, "pinctrl-0", ph);
290 }
291
292 void karo_fdt_fixup_flexcan(void *blob, int xcvr_present)
293 {
294         const char *xcvr_status = "disabled";
295
296         if (xcvr_present) {
297                 if (karo_fdt_flexcan_enabled(blob)) {
298                         karo_fdt_set_lcd_pins(blob, "lcdif_23bit_pins_a");
299                         xcvr_status = "okay";
300                 } else {
301                         karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
302                 }
303         } else {
304                 const char *otg_mode = getenv("otg_mode");
305
306                 if (otg_mode && (strcmp(otg_mode, "host") == 0))
307                         karo_fdt_enable_node(blob, "can1", 0);
308
309                 karo_fdt_set_lcd_pins(blob, "lcdif_24bit_pins_a");
310         }
311         fdt_find_and_setprop(blob, "/regulators/can-xcvr", "status",
312                         xcvr_status, strlen(xcvr_status) + 1, 1);
313 }
314
315 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
316                         const char *prop)
317 {
318         int ret;
319         int offset;
320         const uint32_t *phandle;
321         uint32_t ph = 0;
322
323         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
324         if (offset <= 0)
325                 return;
326
327         phandle = fdt_getprop(blob, offset, prop, NULL);
328         if (phandle) {
329                 ph = fdt32_to_cpu(*phandle);
330         }
331
332         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
333         ret = fdt_delprop(blob, offset, prop);
334         if (ret == 0)
335                 karo_set_fdtsize(blob);
336
337         if (!ph)
338                 return;
339
340         offset = fdt_node_offset_by_phandle(blob, ph);
341         if (offset <= 0)
342                 return;
343
344         debug("Removing node @ %08x\n", offset);
345         fdt_del_node(blob, offset);
346         karo_set_fdtsize(blob);
347 }
348
349 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
350 {
351         const uint32_t *prop;
352
353         memset(fb_mode, 0, sizeof(*fb_mode));
354
355         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
356         if (prop)
357                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
358
359         prop = fdt_getprop(blob, off, "hactive", NULL);
360         if (prop)
361                 fb_mode->xres = fdt32_to_cpu(*prop);
362
363         prop = fdt_getprop(blob, off, "vactive", NULL);
364         if (prop)
365                 fb_mode->yres = fdt32_to_cpu(*prop);
366
367         prop = fdt_getprop(blob, off, "hback-porch", NULL);
368         if (prop)
369                 fb_mode->left_margin = fdt32_to_cpu(*prop);
370
371         prop = fdt_getprop(blob, off, "hsync-len", NULL);
372         if (prop)
373                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
374
375         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
376         if (prop)
377                 fb_mode->right_margin = fdt32_to_cpu(*prop);
378
379         prop = fdt_getprop(blob, off, "vback-porch", NULL);
380         if (prop)
381                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
382
383         prop = fdt_getprop(blob, off, "vsync-len", NULL);
384         if (prop)
385                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
386
387         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
388         if (prop)
389                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
390
391         prop = fdt_getprop(blob, off, "hsync-active", NULL);
392         if (prop)
393                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
394
395         prop = fdt_getprop(blob, off, "vsync-active", NULL);
396         if (prop)
397                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
398
399         prop = fdt_getprop(blob, off, "de-active", NULL);
400         if (prop)
401                 fb_mode->sync |= *prop ? 0 : FB_SYNC_OE_LOW_ACT;
402
403         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
404         if (prop)
405                 fb_mode->sync |= *prop ? 0 : FB_SYNC_CLK_LAT_FALL;
406
407         return 0;
408 }
409
410 static int fdt_update_native_fb_mode(void *blob, int off)
411 {
412         int ret;
413         uint32_t ph;
414
415         ret = fdt_increase_size(blob, 64);
416         if (ret) {
417                 printf("Warning: Failed to increase FDT size: %d\n", ret);
418         }
419         debug("Creating phandle at offset %d\n", off);
420         ph = fdt_create_phandle(blob, off);
421         if (!ph) {
422                 printf("Failed to create phandle for video timing\n");
423                 return -ENOMEM;
424         }
425
426         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
427                 off, ph);
428         off = fdt_parent_offset(blob, off);
429         if (off < 0)
430                 return off;
431         debug("parent offset=%06x\n", off);
432         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
433         if (ret)
434                 printf("Failed to set property 'native-mode': %d\n", ret);
435         karo_set_fdtsize(blob);
436         return ret;
437 }
438
439 static int karo_fdt_find_video_timings(void *blob)
440 {
441         int off = fdt_path_offset(blob, "display");
442         const char *subnode = "display-timings";
443
444         if (off < 0) {
445                 debug("Could not find node 'display' in FDT: %d\n", off);
446                 return off;
447         }
448
449         off = fdt_subnode_offset(blob, off, subnode);
450         if (off < 0) {
451                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
452         }
453         return off;
454 }
455
456 int karo_fdt_get_fb_mode(void *blob, const char *name, struct fb_videomode *fb_mode)
457 {
458         int off = karo_fdt_find_video_timings(blob);
459
460         if (off < 0)
461                 return off;
462         while (off > 0) {
463                 const char *n, *endp;
464                 int len, d = 1;
465
466                 off = fdt_next_node(blob, off, &d);
467                 if (off < 0)
468                         return off;
469                 if (d < 1)
470                         return -EINVAL;
471                 if (d > 2) {
472                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
473                         continue;
474                 }
475                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
476
477                 n = fdt_getprop(blob, off, "panel-name", &len);
478                 if (!n) {
479                         n = fdt_get_name(blob, off, NULL);
480                         if (strcasecmp(n, name) == 0) {
481                                 break;
482                         }
483                 } else {
484                         int found = 0;
485
486                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
487                                 debug("Checking panel-name '%s'\n", n);
488                                 if (strcasecmp(n, name) == 0) {
489                                         debug("Using node %s @ %04x\n",
490                                                 fdt_get_name(blob, off, NULL), off);
491                                         found = 1;
492                                         break;
493                                 }
494                         }
495                         if (found)
496                                 break;
497                 }
498         }
499         if (off > 0) {
500                 return fdt_init_fb_mode(blob, off, fb_mode);
501         }
502         return -EINVAL;
503 }
504
505 #define SET_FB_PROP(n, v) ({                                            \
506         int ret;                                                        \
507         ret = fdt_setprop_u32(blob, off, n, v);                         \
508         if (ret) {                                                      \
509                 printf("Failed to set property %s: %d\n", name, ret);   \
510         }                                                               \
511         ret;                                                            \
512 })
513
514
515 int karo_fdt_create_fb_mode(void *blob, const char *name,
516                         struct fb_videomode *fb_mode)
517 {
518         int off = fdt_path_offset(blob, "display");
519         int ret;
520         const char *subnode = "display-timings";
521
522         if (off < 0) {
523                 printf("'display' node not found in FDT\n");
524                 return off;
525         }
526
527         ret = fdt_increase_size(blob, 512);
528         if (ret) {
529                 printf("Failed to increase FDT size: %d\n", ret);
530                 return ret;
531         }
532
533         printf("node '%s' offset=%d\n", "display", off);
534         ret = fdt_subnode_offset(blob, off, subnode);
535         if (ret < 0) {
536                 debug("Could not find node '%s' in FDT: %d\n", subnode, ret);
537                 ret = fdt_add_subnode(blob, off, subnode);
538                 if (ret < 0) {
539                         printf("Failed to add %s subnode: %d\n", subnode, ret);
540                         return ret;
541                 }
542         }
543
544         printf("node '%s' offset=%d\n", subnode, ret);
545         ret = fdt_add_subnode(blob, ret, name);
546         if (ret < 0) {
547                 printf("Failed to add %s subnode: %d\n", name, ret);
548                 return ret;
549         }
550         off = ret;
551
552         ret = SET_FB_PROP("clock-frequency",
553                         PICOS2KHZ(fb_mode->pixclock) * 1000);
554         if (ret)
555                 goto out;
556         ret = SET_FB_PROP("hactive", fb_mode->xres);
557         if (ret)
558                 goto out;
559         ret = SET_FB_PROP("vactive", fb_mode->yres);
560         if (ret)
561                 goto out;
562         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
563         if (ret)
564                 goto out;
565         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
566         if (ret)
567                 goto out;
568         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
569         if (ret)
570                 goto out;
571         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
572         if (ret)
573                 goto out;
574         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
575         if (ret)
576                 goto out;
577         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
578         if (ret)
579                 goto out;
580         ret = SET_FB_PROP("hsync-active",
581                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
582         if (ret)
583                 goto out;
584         ret = SET_FB_PROP("vsync-active",
585                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
586         if (ret)
587                 goto out;
588         ret = SET_FB_PROP("de-active",
589                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
590         if (ret)
591                 goto out;
592         ret = SET_FB_PROP("pixelclk-active",
593                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
594 out:
595         karo_set_fdtsize(blob);
596         return ret;
597 }
598
599 int karo_fdt_update_fb_mode(void *blob, const char *name)
600 {
601         int off = fdt_path_offset(blob, "display");
602         const char *subnode = "display-timings";
603
604         if (off < 0)
605                 return off;
606
607         if (name == NULL) {
608                 int ret;
609
610                 debug("Disabling node '%s' at %03x\n",
611                         fdt_get_name(blob, off, NULL), off);
612                 ret = fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0);
613                 return ret;
614         }
615
616         off = fdt_subnode_offset(blob, off, subnode);
617         if (off < 0) {
618                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
619                 return off;
620         }
621         while (off > 0) {
622                 const char *n, *endp;
623                 int len, d = 1;
624
625                 off = fdt_next_node(blob, off, &d);
626                 if (off < 0)
627                         return off;
628                 if (d < 1)
629                         return -EINVAL;
630                 if (d > 2) {
631                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
632                         continue;
633                 }
634                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
635
636                 n = fdt_getprop(blob, off, "panel-name", &len);
637                 if (!n) {
638                         n = fdt_get_name(blob, off, NULL);
639                         if (strcasecmp(n, name) == 0) {
640                                 break;
641                         }
642                 } else {
643                         int found = 0;
644
645                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
646                                 debug("Checking panel-name '%s'\n", n);
647                                 if (strcasecmp(n, name) == 0) {
648                                         debug("Using node %s @ %04x\n",
649                                                 fdt_get_name(blob, off, NULL), off);
650                                         found = 1;
651                                         break;
652                                 }
653                         }
654                         if (found)
655                                 break;
656                 }
657         }
658         if (off > 0)
659                 return fdt_update_native_fb_mode(blob, off);
660         return off;
661 }
662
663 static int karo_load_part(const char *part, void *addr, size_t len)
664 {
665         int ret;
666         struct mtd_device *dev;
667         struct part_info *part_info;
668         u8 part_num;
669         size_t actual;
670
671         debug("Initializing mtd_parts\n");
672         ret = mtdparts_init();
673         if (ret)
674                 return ret;
675
676         debug("Trying to find NAND partition '%s'\n", part);
677         ret = find_dev_and_part(part, &dev, &part_num, &part_info);
678         if (ret) {
679                 printf("Failed to find flash partition '%s': %d\n",
680                         part, ret);
681
682                 return ret;
683         }
684         debug("Found partition '%s': offset=%08x size=%08x\n",
685                 part, part_info->offset, part_info->size);
686         if (part_info->size < len) {
687                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
688                         part, len, part_info->size);
689                 len = part_info->size;
690         }
691         debug("Reading NAND partition '%s' to %p\n", part, addr);
692         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len,
693                                 &actual, len, addr);
694         if (ret) {
695                 printf("Failed to load partition '%s' to %p\n", part, addr);
696                 return ret;
697         }
698         if (actual < len)
699                 printf("Read only %u of %u bytes due to bad blocks\n",
700                         actual, len);
701
702         debug("Read %u byte from partition '%s' @ offset %08x\n",
703                 len, part, part_info->offset);
704         return 0;
705 }
706
707 void *karo_fdt_load_dtb(void)
708 {
709         int ret;
710         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
711
712         if (tstc()) {
713                 debug("aborting DTB load\n");
714                 return NULL;
715         }
716
717         /* clear FDT header in memory */
718         memset(fdt, 0, 4);
719
720         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
721         if (ret) {
722                 printf("Failed to load dtb from flash: %d\n", ret);
723                 return NULL;
724         }
725
726         if (fdt_check_header(fdt)) {
727                 debug("No valid DTB in flash\n");
728                 return NULL;
729         }
730         debug("Using DTB from flash\n");
731         karo_set_fdtsize(fdt);
732         return fdt;
733 }