]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
karo: fdt: fix handling of multiple panel-names in karo_fdt_update_fb_mode()
[karo-tx-uboot.git] / board / karo / common / fdt.c
1 /*
2  * (C) Copyright 2012 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 <linux/list.h>
24 #include <linux/fb.h>
25 #include <jffs2/load_kernel.h>
26
27 #include "karo.h"
28
29 #ifdef CONFIG_MAX_DTB_SIZE
30 #define MAX_DTB_SIZE    CONFIG_MAX_DTB_SIZE
31 #else
32 #define MAX_DTB_SIZE    SZ_64K
33 #endif
34
35 DECLARE_GLOBAL_DATA_PTR;
36
37 static void karo_set_fdtsize(void *fdt)
38 {
39         char fdt_size[9];
40         size_t fdtsize = getenv_ulong("fdtsize", 16, 0);
41
42         if (fdtsize == fdt_totalsize(fdt)) {
43                 return;
44         }
45         debug("FDT size changed from %u to %u\n",
46                 fdtsize, fdt_totalsize(fdt));
47
48         snprintf(fdt_size, sizeof(fdt_size), "%08x", fdt_totalsize(fdt));
49         setenv("fdtsize", fdt_size);
50 }
51
52 void karo_fdt_move_fdt(void)
53 {
54         void *fdt;
55         unsigned long fdt_addr = getenv_ulong("fdtaddr", 16, 0);
56
57         if (working_fdt) {
58                 debug("DTB already loaded\n");
59                 return;
60         }
61
62         if (!fdt_addr) {
63                 fdt_addr = CONFIG_SYS_FDT_ADDR;
64                 printf("fdtaddr is not set; using default: %08lx\n",
65                         fdt_addr);
66         }
67
68         fdt = karo_fdt_load_dtb();
69         if (fdt == NULL) {
70                 fdt = (void *)gd->fdt_blob;
71                 if (fdt == NULL) {
72                         printf("Compiled in FDT not found\n");
73                         return;
74                 }
75                 debug("Checking FDT header @ %p\n", fdt);
76                 if (fdt_check_header(fdt)) {
77                         printf("ERROR: No valid DTB found at %p\n", fdt);
78                         return;
79                 }
80                 printf("No DTB in flash; using default DTB\n");
81                 debug("Moving FDT from %p..%p to %08lx..%08lx\n",
82                         fdt, fdt + fdt_totalsize(fdt) - 1,
83                         fdt_addr, fdt_addr + fdt_totalsize(fdt) - 1);
84                 memmove((void *)fdt_addr, fdt, fdt_totalsize(fdt));
85         }
86         set_working_fdt_addr((void *)fdt_addr);
87         gd->fdt_blob = fdt;
88         karo_set_fdtsize(fdt);
89 }
90
91 void karo_fdt_remove_node(void *blob, const char *node)
92 {
93         int off = fdt_path_offset(blob, node);
94         int ret;
95
96         debug("Removing node '%s' from DT\n", node);
97
98         if (off < 0) {
99                 printf("Could not find node '%s': %d\n", node, off);
100         } else {
101                 ret = fdt_del_node(blob, off);
102                 if (ret)
103                         printf("Failed to remove node '%s': %d\n",
104                                 node, ret);
105         }
106         karo_set_fdtsize(blob);
107 }
108
109 void karo_fdt_enable_node(void *blob, const char *node, int enable)
110 {
111         int off = fdt_path_offset(blob, node);
112
113         debug("%sabling node '%s'\n", enable ? "En" : "Dis", node);
114         if (off < 0) {
115                 printf("Could not find node '%s': %d\n", node, off);
116                 return;
117         }
118         fdt_set_node_status(blob, off,
119                         enable ? FDT_STATUS_OKAY : FDT_STATUS_DISABLED, 0);
120
121         karo_set_fdtsize(blob);
122 }
123
124 static const char *karo_touchpanels[] = {
125         "ti,tsc2007",
126         "edt,edt-ft5x06",
127 #ifdef CONFIG_MX28
128         "fsl,imx28-lradc",
129 #endif
130 };
131
132 static void fdt_del_tp_node(void *blob, const char *name)
133 {
134         int offs = fdt_node_offset_by_compatible(blob, -1, name);
135
136         if (offs < 0) {
137                 debug("node '%s' not found: %d\n", name, offs);
138                 return;
139         }
140
141         debug("Removing node '%s' from DT\n", name);
142         fdt_del_node(blob, offs);
143 }
144
145 void karo_fdt_fixup_touchpanel(void *blob)
146 {
147         int i;
148         const char *model = getenv("touchpanel");
149
150         for (i = 0; i < ARRAY_SIZE(karo_touchpanels); i++) {
151                 const char *tp = karo_touchpanels[i];
152
153                 if (model != NULL && strcmp(model, tp) == 0)
154                         continue;
155
156                 if (model != NULL) {
157                         if (strcmp(model, tp) == 0)
158                                 continue;
159                         tp = strchr(tp, ',');
160                         if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
161                                 continue;
162                 }
163                 fdt_del_tp_node(blob, karo_touchpanels[i]);
164                 karo_set_fdtsize(blob);
165         }
166 }
167
168 void karo_fdt_fixup_usb_otg(void *blob, const char *node, const char *phy)
169 {
170         const char *otg_mode = getenv("otg_mode");
171         int off;
172         int ret;
173         const uint32_t *ph;
174
175         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
176
177         off = fdt_path_offset(blob, node);
178         if (off < 0) {
179                 debug("Failed to find node %s\n", node);
180                 return;
181         }
182
183         if (otg_mode && (strcmp(otg_mode, "device") == 0 ||
184                                 strcmp(otg_mode, "gadget") == 0)) {
185                 ret = fdt_setprop_string(blob, off, "dr_mode", "peripheral");
186                 phy = NULL;
187         } else if (otg_mode && strcmp(otg_mode, "host") == 0) {
188                 ret = fdt_setprop_string(blob, off, "dr_mode", "host");
189                 phy = NULL;
190         } else {
191                 if (otg_mode && strcmp(otg_mode, "none") != 0)
192                         printf("Invalid 'otg_mode' setting '%s'; disabling usbotg port\n",
193                                 otg_mode);
194                 ret = fdt_setprop_string(blob, off, "status", "disabled");
195         }
196         if (ret)
197                 goto out;
198
199         if (phy == NULL)
200                 goto out;
201
202         ph = fdt_getprop(blob, off, phy, NULL);
203         if (ph == NULL) {
204                 printf("Failed to find '%s' phandle in node '%s'\n", phy,
205                         fdt_get_name(blob, off, NULL));
206                 goto out;
207         }
208
209         off = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*ph));
210         if (off <= 0) {
211                 printf("Failed to find '%s' node via phandle %04x\n",
212                         phy, fdt32_to_cpu(*ph));
213                 goto out;
214         }
215         ret = fdt_setprop_string(blob, off, "status", "disabled");
216
217 out:
218         if (ret)
219                 printf("Failed to update usbotg: %d\n", ret);
220         printf("node '%s' updated\n", node);
221         karo_set_fdtsize(blob);
222 }
223
224 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
225                         const char *prop)
226 {
227         int ret;
228         int offset;
229         const uint32_t *phandle;
230         uint32_t ph = 0;
231
232         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
233         if (offset <= 0)
234                 return;
235
236         phandle = fdt_getprop(blob, offset, prop, NULL);
237         if (phandle) {
238                 ph = fdt32_to_cpu(*phandle);
239         }
240
241         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
242         ret = fdt_delprop(blob, offset, prop);
243         if (ret == 0)
244                 karo_set_fdtsize(blob);
245
246         if (!ph)
247                 return;
248
249         offset = fdt_node_offset_by_phandle(blob, ph);
250         if (offset <= 0)
251                 return;
252
253         debug("Removing node @ %08x\n", offset);
254         fdt_del_node(blob, offset);
255         karo_set_fdtsize(blob);
256 }
257
258 static int fdt_init_fb_mode(const void *blob, int off, struct fb_videomode *fb_mode)
259 {
260         const uint32_t *prop;
261
262         memset(fb_mode, 0, sizeof(*fb_mode));
263
264         prop = fdt_getprop(blob, off, "clock-frequency", NULL);
265         if (prop)
266                 fb_mode->pixclock = KHZ2PICOS(fdt32_to_cpu(*prop) / 1000);
267
268         prop = fdt_getprop(blob, off, "hactive", NULL);
269         if (prop)
270                 fb_mode->xres = fdt32_to_cpu(*prop);
271
272         prop = fdt_getprop(blob, off, "vactive", NULL);
273         if (prop)
274                 fb_mode->yres = fdt32_to_cpu(*prop);
275
276         prop = fdt_getprop(blob, off, "hback-porch", NULL);
277         if (prop)
278                 fb_mode->left_margin = fdt32_to_cpu(*prop);
279
280         prop = fdt_getprop(blob, off, "hsync-len", NULL);
281         if (prop)
282                 fb_mode->hsync_len = fdt32_to_cpu(*prop);
283
284         prop = fdt_getprop(blob, off, "hfront-porch", NULL);
285         if (prop)
286                 fb_mode->right_margin = fdt32_to_cpu(*prop);
287
288         prop = fdt_getprop(blob, off, "vback-porch", NULL);
289         if (prop)
290                 fb_mode->upper_margin = fdt32_to_cpu(*prop);
291
292         prop = fdt_getprop(blob, off, "vsync-len", NULL);
293         if (prop)
294                 fb_mode->vsync_len = fdt32_to_cpu(*prop);
295
296         prop = fdt_getprop(blob, off, "vfront-porch", NULL);
297         if (prop)
298                 fb_mode->lower_margin = fdt32_to_cpu(*prop);
299
300         prop = fdt_getprop(blob, off, "hsync-active", NULL);
301         if (prop)
302                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
303
304         prop = fdt_getprop(blob, off, "vsync-active", NULL);
305         if (prop)
306                 fb_mode->sync |= *prop ? FB_SYNC_VERT_HIGH_ACT : 0;
307 #if 0
308         prop = fdt_getprop(blob, off, "de-active", NULL);
309         if (prop)
310                 fb_mode->sync |= *prop ? FB_SYNC_DATA_ENABLE_HIGH_ACT : 0;
311
312         prop = fdt_getprop(blob, off, "pixelclk-active", NULL);
313         if (prop)
314                 fb_mode->sync |= *prop ? FB_SYNC_DOTCLK_FALLING_ACT : 0;
315 #endif
316         return 0;
317 }
318
319 static int fdt_update_native_fb_mode(void *blob, int off)
320 {
321         int ret;
322         uint32_t ph;
323         int i;
324
325         for (i = 1; i < 16; i++) {
326                 fdt_set_totalsize(blob, fdt_totalsize(blob) + 8 * 4);
327                 karo_set_fdtsize(blob);
328                 ph = fdt_create_phandle(blob, off);
329                 if (ph)
330                         break;
331         }
332         if (ph == 0) {
333                 printf("Failed to create phandle for video timing\n");
334                 return -ENOMEM;
335         }
336
337         debug("phandle of %s @ %06x=%04x\n", fdt_get_name(blob, off, NULL),
338                 off, ph);
339         off = fdt_parent_offset(blob, off);
340         if (off < 0)
341                 return off;
342         debug("parent offset=%06x\n", off);
343         ret = fdt_setprop_cell(blob, off, "native-mode", ph);
344         return ret;
345 }
346
347 static int karo_fdt_find_video_timings(void *blob)
348 {
349         int off = fdt_path_offset(blob, "display");
350         const char *subnode = "display-timings";
351
352         if (off < 0) {
353                 printf("Could not find node 'display' in FDT: %d\n", off);
354                 return off;
355         }
356
357         off = fdt_subnode_offset(blob, off, subnode);
358         if (off < 0) {
359                 printf("Could not find node '%s' in FDT: %d\n", subnode, off);
360         }
361         return off;
362 }
363
364 int karo_fdt_get_fb_mode(void *blob, const char *name, struct fb_videomode *fb_mode)
365 {
366         int off = karo_fdt_find_video_timings(blob);
367
368         if (off < 0)
369                 return off;
370         do {
371                 const char *n, *endp;
372                 int len, d = 1;
373
374                 off = fdt_next_node(blob, off, &d);
375                 if (d > 2) {
376                         printf("Skipping node @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
377                         continue;
378                 }
379                 debug("parsing subnode @ %04x %s depth %d\n", off, fdt_get_name(blob, off, NULL), d);
380                 if (off < 0 || d < 1)
381                         break;
382
383                 n = fdt_getprop(blob, off, "panel-name", &len);
384                 if (!n) {
385                         printf("Missing 'panel-name' property in node '%s'\n",
386                                 fdt_get_name(blob, off, NULL));
387                         continue;
388                 }
389                 for (endp = n + len; n < endp; n += strlen(n) + 1) {
390                         debug("Checking panel-name '%s'\n", n);
391                         if (strcasecmp(n, name) == 0) {
392                                 fdt_init_fb_mode(blob, off, fb_mode);
393                                 return fdt_update_native_fb_mode(blob, off);
394                         }
395                 }
396         } while (off > 0);
397         return -EINVAL;
398 }
399
400 int karo_fdt_update_fb_mode(void *blob, const char *name)
401 {
402         int off = fdt_path_offset(blob, "display");
403         const char *subnode = "display-timings";
404
405         if (off < 0)
406                 return off;
407
408         if (name == NULL) {
409                 int parent = fdt_parent_offset(blob, off);
410                 int ret;
411
412                 if (parent < 0) {
413                         printf("Failed to find parent of node '%s'\n",
414                                 fdt_get_name(blob, off, NULL));
415                         return parent;
416                 }
417                 debug("parent offset=%06x\n", parent);
418                 ret = fdt_setprop_string(blob, parent, "status", "disabled");
419                 return ret;
420         }
421
422         off = fdt_subnode_offset(blob, off, subnode);
423         if (off < 0) {
424                 printf("Could not find node '%s' in FDT: %d\n", subnode, off);
425         }
426         do {
427                 const char *n, *endp;
428                 int len, d = 1;
429                 int node = fdt_next_node(blob, off, &d);
430                 int do_del;
431
432                 if (d > 2) {
433                         printf("Skipping node @ %04x %s depth %d\n", node, fdt_get_name(blob, node, NULL), d);
434                         continue;
435                 }
436                 debug("parsing subnode @ %04x %s depth %d\n", node, fdt_get_name(blob, node, NULL), d);
437                 if (node < 0 || d < 1)
438                         break;
439
440                 n = fdt_getprop(blob, node, "panel-name", &len);
441                 if (!n) {
442                         printf("Missing 'panel-name' property in node '%s'\n",
443                                 fdt_get_name(blob, node, NULL));
444                         continue;
445                 }
446                 do_del = 1;
447                 for (endp = n + len; n < endp; n += strlen(n) + 1) {
448                         debug("Checking panel-name '%s'\n", n);
449                         if (strcasecmp(n, name) == 0) {
450                                 debug("Keeping node %s @ %04x\n",
451                                         fdt_get_name(blob, node, NULL), node);
452                                 off = node;
453                                 do_del = 0;
454                                 break;
455                         }
456                 }
457                 if (do_del) {
458                         debug("Deleting node %s @ %04x\n",
459                                 fdt_get_name(blob, node, NULL), node);
460                         fdt_del_node(blob, node);
461                 }
462
463         } while (off > 0);
464
465         return 0;
466 }
467
468 static int karo_load_part(const char *part, void *addr, size_t len)
469 {
470         int ret;
471         struct mtd_device *dev;
472         struct part_info *part_info;
473         u8 part_num;
474
475         debug("Initializing mtd_parts\n");
476         ret = mtdparts_init();
477         if (ret)
478                 return ret;
479
480         debug("Trying to find NAND partition '%s'\n", part);
481         ret = find_dev_and_part(part, &dev, &part_num,
482                                 &part_info);
483         if (ret) {
484                 printf("Failed to find flash partition '%s': %d\n",
485                         part, ret);
486
487                 return ret;
488         }
489         debug("Found partition '%s': offset=%08x size=%08x\n",
490                 part, part_info->offset, part_info->size);
491         if (part_info->size < len) {
492                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
493                         part, len, part_info->size);
494                 len = part_info->size;
495         }
496         debug("Reading NAND partition '%s' to %p\n", part, addr);
497         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len, addr);
498         if (ret) {
499                 printf("Failed to load partition '%s' to %p\n", part, addr);
500                 return ret;
501         }
502         debug("Read %u byte from partition '%s' @ offset %08x\n",
503                 len, part, part_info->offset);
504         return 0;
505 }
506
507 void *karo_fdt_load_dtb(void)
508 {
509         int ret;
510         void *fdt = (void *)getenv_ulong("fdtaddr", 16, CONFIG_SYS_FDT_ADDR);
511
512         if (tstc()) {
513                 debug("aborting DTB load\n");
514                 return NULL;
515         }
516
517         /* clear FDT header in memory */
518         memset(fdt, 0, 4);
519
520         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
521         if (ret) {
522                 printf("Failed to load dtb from flash: %d\n", ret);
523                 return NULL;
524         }
525
526         if (fdt_check_header(fdt)) {
527                 debug("No valid DTB in flash\n");
528                 return NULL;
529         }
530         debug("Using DTB from flash\n");
531         karo_set_fdtsize(fdt);
532         return fdt;
533 }