]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
Merge branch 'tx51-bugfix' into 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 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
241                         const char *prop)
242 {
243         int ret;
244         int offset;
245         const uint32_t *phandle;
246         uint32_t ph = 0;
247
248         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
249         if (offset <= 0)
250                 return;
251
252         phandle = fdt_getprop(blob, offset, prop, NULL);
253         if (phandle) {
254                 ph = fdt32_to_cpu(*phandle);
255         }
256
257         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
258         ret = fdt_delprop(blob, offset, prop);
259         if (ret == 0)
260                 karo_set_fdtsize(blob);
261
262         if (!ph)
263                 return;
264
265         offset = fdt_node_offset_by_phandle(blob, ph);
266         if (offset <= 0)
267                 return;
268
269         debug("Removing node @ %08x\n", offset);
270         fdt_del_node(blob, offset);
271         karo_set_fdtsize(blob);
272 }
273
274 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
275 {
276         const uint32_t *prop;
277
278         memset(fb_mode, 0, sizeof(*fb_mode));
279
280         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
281         if (prop)
282                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
283
284         prop = fdt_getprop(blob, off, "hactive", NULL);
285         if (prop)
286                 fb_mode->xres = fdt32_to_cpu(*prop);
287
288         prop = fdt_getprop(blob, off, "vactive", NULL);
289         if (prop)
290                 fb_mode->yres = fdt32_to_cpu(*prop);
291
292         prop = fdt_getprop(blob, off, "hback-porch", NULL);
293         if (prop)
294                 fb_mode->left_margin = fdt32_to_cpu(*prop);
295
296         prop = fdt_getprop(blob, off, "hsync-len", NULL);
297         if (prop)
298                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
299
300         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
301         if (prop)
302                 fb_mode->right_margin = fdt32_to_cpu(*prop);
303
304         prop = fdt_getprop(blob, off, "vback-porch", NULL);
305         if (prop)
306                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
307
308         prop = fdt_getprop(blob, off, "vsync-len", NULL);
309         if (prop)
310                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
311
312         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
313         if (prop)
314                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
315
316         prop = fdt_getprop(blob, off, "hsync-active", NULL);
317         if (prop)
318                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
319
320         prop = fdt_getprop(blob, off, "vsync-active", NULL);
321         if (prop)
322                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
323 #if defined(CONFIG_MX51) || defined(CONFIG_MX53) || defined(CONFIG_MX6)
324         prop = fdt_getprop(blob, off, "de-active", NULL);
325         if (prop)
326                 fb_mode->sync |= *prop ? 0 : FB_SYNC_OE_LOW_ACT;
327
328         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
329         if (prop)
330                 fb_mode->sync |= *prop ? 0 : FB_SYNC_CLK_LAT_FALL;
331 #endif
332         return 0;
333 }
334
335 static int fdt_update_native_fb_mode(void *blob, int off)
336 {
337         int ret;
338         uint32_t ph;
339
340         ret = fdt_increase_size(blob, 32);
341         if (ret) {
342                 printf("Warning: Failed to increase FDT size: %d\n", ret);
343         }
344         debug("Creating phandle at offset %d\n", off);
345         ph = fdt_create_phandle(blob, off);
346         if (!ph) {
347                 printf("Failed to create phandle for video timing\n");
348                 return -ENOMEM;
349         }
350
351         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
352                 off, ph);
353         off = fdt_parent_offset(blob, off);
354         if (off < 0)
355                 return off;
356         debug("parent offset=%06x\n", off);
357         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
358         if (ret)
359                 printf("Failed to set property 'native-mode': %d\n", ret);
360         karo_set_fdtsize(blob);
361         return ret;
362 }
363
364 static int karo_fdt_find_video_timings(void *blob)
365 {
366         int off = fdt_path_offset(blob, "display");
367         const char *subnode = "display-timings";
368
369         if (off < 0) {
370                 debug("Could not find node 'display' in FDT: %d\n", off);
371                 return off;
372         }
373
374         off = fdt_subnode_offset(blob, off, subnode);
375         if (off < 0) {
376                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
377         }
378         return off;
379 }
380
381 int karo_fdt_get_fb_mode(void *blob, const char *name, struct fb_videomode *fb_mode)
382 {
383         int off = karo_fdt_find_video_timings(blob);
384
385         if (off < 0)
386                 return off;
387         while (off > 0) {
388                 const char *n, *endp;
389                 int len, d = 1;
390
391                 off = fdt_next_node(blob, off, &d);
392                 if (off < 0)
393                         return off;
394                 if (d < 1)
395                         return -EINVAL;
396                 if (d > 2) {
397                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
398                         continue;
399                 }
400                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
401
402                 n = fdt_getprop(blob, off, "panel-name", &len);
403                 if (!n) {
404                         n = fdt_get_name(blob, off, NULL);
405                         if (strcasecmp(n, name) == 0) {
406                                 break;
407                         }
408                 } else {
409                         int found = 0;
410
411                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
412                                 debug("Checking panel-name '%s'\n", n);
413                                 if (strcasecmp(n, name) == 0) {
414                                         debug("Using node %s @ %04x\n",
415                                                 fdt_get_name(blob, off, NULL), off);
416                                         found = 1;
417                                         break;
418                                 }
419                         }
420                         if (found)
421                                 break;
422                 }
423         }
424         if (off > 0) {
425                 return fdt_init_fb_mode(blob, off, fb_mode);
426         }
427         return -EINVAL;
428 }
429
430 #define SET_FB_PROP(n, v) ({                                            \
431         int ret;                                                        \
432         ret = fdt_setprop_u32(blob, off, n, v);                         \
433         if (ret) {                                                      \
434                 printf("Failed to set property %s: %d\n", name, ret);   \
435         }                                                               \
436         ret;                                                            \
437 })
438
439
440 int karo_fdt_create_fb_mode(void *blob, const char *name,
441                         struct fb_videomode *fb_mode)
442 {
443         int off = fdt_path_offset(blob, "display");
444         int ret;
445         const char *subnode = "display-timings";
446
447         if (off < 0) {
448                 printf("'display' node not found in FDT\n");
449                 return off;
450         }
451
452         ret = fdt_increase_size(blob, 512);
453         if (ret) {
454                 printf("Failed to increase FDT size: %d\n", ret);
455                 return ret;
456         }
457
458         printf("node '%s' offset=%d\n", "display", off);
459         ret = fdt_subnode_offset(blob, off, subnode);
460         if (ret < 0) {
461                 debug("Could not find node '%s' in FDT: %d\n", subnode, ret);
462                 ret = fdt_add_subnode(blob, off, subnode);
463                 if (ret < 0) {
464                         printf("Failed to add %s subnode: %d\n", subnode, ret);
465                         return ret;
466                 }
467         }
468
469         printf("node '%s' offset=%d\n", subnode, ret);
470         ret = fdt_add_subnode(blob, ret, name);
471         if (ret < 0) {
472                 printf("Failed to add %s subnode: %d\n", name, ret);
473                 return ret;
474         }
475         off = ret;
476
477         ret = SET_FB_PROP("clock-frequency",
478                         PICOS2KHZ(fb_mode->pixclock) * 1000);
479         if (ret)
480                 goto out;
481         ret = SET_FB_PROP("hactive", fb_mode->xres);
482         if (ret)
483                 goto out;
484         ret = SET_FB_PROP("vactive", fb_mode->yres);
485         if (ret)
486                 goto out;
487         ret = SET_FB_PROP("hback-porch", fb_mode->left_margin);
488         if (ret)
489                 goto out;
490         ret = SET_FB_PROP("hsync-len", fb_mode->hsync_len);
491         if (ret)
492                 goto out;
493         ret = SET_FB_PROP("hfront-porch", fb_mode->right_margin);
494         if (ret)
495                 goto out;
496         ret = SET_FB_PROP("vback-porch", fb_mode->upper_margin);
497         if (ret)
498                 goto out;
499         ret = SET_FB_PROP("vsync-len", fb_mode->vsync_len);
500         if (ret)
501                 goto out;
502         ret = SET_FB_PROP("vfront-porch", fb_mode->lower_margin);
503         if (ret)
504                 goto out;
505         ret = SET_FB_PROP("hsync-active",
506                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
507         if (ret)
508                 goto out;
509         ret = SET_FB_PROP("vsync-active",
510                         fb_mode->sync & FB_SYNC_VERT_HIGH_ACT ? 1 : 0);
511         if (ret)
512                 goto out;
513
514 #if defined(CONFIG_MX51) || defined(CONFIG_MX53) || defined(CONFIG_MX6)
515         ret = SET_FB_PROP("de-active",
516                         !(fb_mode->sync & FB_SYNC_OE_LOW_ACT));
517         if (ret)
518                 goto out;
519         ret = SET_FB_PROP("pixelclk-active",
520                         !(fb_mode->sync & FB_SYNC_CLK_LAT_FALL));
521         if (ret)
522                 goto out;
523 #else
524         /* TODO: make these configurable */
525         ret = SET_FB_PROP("de-active", 1);
526         if (ret)
527                 goto out;
528         ret = SET_FB_PROP("pixelclk-active", 1);
529         if (ret)
530                 goto out;
531 #endif
532 out:
533         karo_set_fdtsize(blob);
534         return ret;
535 }
536
537 int karo_fdt_update_fb_mode(void *blob, const char *name)
538 {
539         int off = fdt_path_offset(blob, "display");
540         const char *subnode = "display-timings";
541
542         if (off < 0)
543                 return off;
544
545         if (name == NULL) {
546                 int parent = fdt_parent_offset(blob, off);
547                 int ret;
548
549                 if (parent < 0) {
550                         printf("Failed to find parent of node '%s'\n",
551                                 fdt_get_name(blob, off, NULL));
552                         return parent;
553                 }
554                 debug("parent offset=%06x\n", parent);
555                 ret = fdt_set_node_status(blob, parent, FDT_STATUS_DISABLED, 0);
556                 return ret;
557         }
558
559         off = fdt_subnode_offset(blob, off, subnode);
560         if (off < 0) {
561                 debug("Could not find node '%s' in FDT: %d\n", subnode, off);
562                 return off;
563         }
564         while (off > 0) {
565                 const char *n, *endp;
566                 int len, d = 1;
567
568                 off = fdt_next_node(blob, off, &d);
569                 if (off < 0)
570                         return off;
571                 if (d < 1)
572                         return -EINVAL;
573                 if (d > 2) {
574                         debug("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
575                         continue;
576                 }
577                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
578
579                 n = fdt_getprop(blob, off, "panel-name", &len);
580                 if (!n) {
581                         n = fdt_get_name(blob, off, NULL);
582                         if (strcasecmp(n, name) == 0) {
583                                 break;
584                         }
585                 } else {
586                         int found = 0;
587
588                         for (endp = n + len; n < endp; n += strlen(n) + 1) {
589                                 debug("Checking panel-name '%s'\n", n);
590                                 if (strcasecmp(n, name) == 0) {
591                                         debug("Using node %s @ %04x\n",
592                                                 fdt_get_name(blob, off, NULL), off);
593                                         found = 1;
594                                         break;
595                                 }
596                         }
597                         if (found)
598                                 break;
599                 }
600         }
601         if (off > 0)
602                 return fdt_update_native_fb_mode(blob, off);
603         return off;
604 }
605
606 static int karo_load_part(const char *part, void *addr, size_t len)
607 {
608         int ret;
609         struct mtd_device *dev;
610         struct part_info *part_info;
611         u8 part_num;
612         size_t actual;
613
614         debug("Initializing mtd_parts\n");
615         ret = mtdparts_init();
616         if (ret)
617                 return ret;
618
619         debug("Trying to find NAND partition '%s'\n", part);
620         ret = find_dev_and_part(part, &dev, &part_num,
621                                 &part_info);
622         if (ret) {
623                 printf("Failed to find flash partition '%s': %d\n",
624                         part, ret);
625
626                 return ret;
627         }
628         debug("Found partition '%s': offset=%08x size=%08x\n",
629                 part, part_info->offset, part_info->size);
630         if (part_info->size < len) {
631                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
632                         part, len, part_info->size);
633                 len = part_info->size;
634         }
635         debug("Reading NAND partition '%s' to %p\n", part, addr);
636         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len,
637                                 &actual, len, addr);
638         if (ret) {
639                 printf("Failed to load partition '%s' to %p\n", part, addr);
640                 return ret;
641         }
642         if (actual < len)
643                 printf("Read only %u of %u bytes due to bad blocks\n",
644                         actual, len);
645
646         debug("Read %u byte from partition '%s' @ offset %08x\n",
647                 len, part, part_info->offset);
648         return 0;
649 }
650
651 void *karo_fdt_load_dtb(void)
652 {
653         int ret;
654         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
655
656         if (tstc()) {
657                 debug("aborting DTB load\n");
658                 return NULL;
659         }
660
661         /* clear FDT header in memory */
662         memset(fdt, 0, 4);
663
664         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
665         if (ret) {
666                 printf("Failed to load dtb from flash: %d\n", ret);
667                 return NULL;
668         }
669
670         if (fdt_check_header(fdt)) {
671                 debug("No valid DTB in flash\n");
672                 return NULL;
673         }
674         debug("Using DTB from flash\n");
675         karo_set_fdtsize(fdt);
676         return fdt;
677 }