]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
Merge remote branch 'remotes/kc/karo-tx6q' into karo-tx48
[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 <jffs2/load_kernel.h>
24
25 #include "karo.h"
26
27 #ifdef CONFIG_MAX_DTB_SIZE
28 #define MAX_DTB_SIZE    CONFIG_MAX_DTB_SIZE
29 #else
30 #define MAX_DTB_SIZE    SZ_64K
31 #endif
32
33 DECLARE_GLOBAL_DATA_PTR;
34
35 static void karo_set_fdtsize(void *fdt)
36 {
37         char fdt_size[9];
38         size_t fdtsize = getenv_ulong("fdtsize", 16, 0);
39
40         if (fdtsize == fdt_totalsize(fdt)) {
41                 return;
42         }
43         debug("FDT size changed from %u to %u\n",
44                 fdtsize, fdt_totalsize(fdt));
45
46         snprintf(fdt_size, sizeof(fdt_size), "%08x", fdt_totalsize(fdt));
47         setenv("fdtsize", fdt_size);
48 }
49
50 void karo_fdt_move_fdt(void)
51 {
52         void *fdt;
53         unsigned long fdt_addr = getenv_ulong("fdtaddr", 16, 0);
54
55         if (!fdt_addr) {
56                 fdt_addr = CONFIG_SYS_FDT_ADDR;
57                 printf("fdtaddr is not set; using default: %08lx\n",
58                         fdt_addr);
59         }
60
61         fdt = karo_fdt_load_dtb();
62         if (fdt == NULL) {
63                 fdt = (void *)gd->fdt_blob;
64                 if (fdt == NULL) {
65                         printf("Compiled in FDT not found\n");
66                         return;
67                 }
68                 debug("Checking FDT header @ %p\n", fdt);
69                 if (fdt_check_header(fdt)) {
70                         printf("ERROR: No valid DTB found at %p\n", fdt);
71                         return;
72                 }
73                 printf("No DTB in flash; using default DTB\n");
74                 debug("Moving FDT from %p..%p to %08lx..%08lx\n",
75                         fdt, fdt + fdt_totalsize(fdt) - 1,
76                         fdt_addr, fdt_addr + fdt_totalsize(fdt) - 1);
77                 memmove((void *)fdt_addr, fdt, fdt_totalsize(fdt));
78         }
79         set_working_fdt_addr((void *)fdt_addr);
80         karo_set_fdtsize(fdt);
81 }
82
83 void karo_fdt_remove_node(void *blob, const char *node)
84 {
85         debug("Removing node '%s' from DT\n", node);
86         int off = fdt_path_offset(blob, node);
87         int ret;
88
89         if (off < 0) {
90                 printf("Could not find node '%s': %d\n", node, off);
91         } else {
92                 ret = fdt_del_node(blob, off);
93                 if (ret)
94                         printf("Failed to remove node '%s': %d\n",
95                                 node, ret);
96         }
97         karo_set_fdtsize(blob);
98 }
99
100 void karo_fdt_enable_node(void *blob, const char *node, int enable)
101 {
102         int off = fdt_path_offset(blob, node);
103
104         debug("%sabling node '%s'\n", enable ? "En" : "Dis", node);
105         if (off < 0) {
106                 printf("Could not find node '%s': %d\n", node, off);
107                 return;
108         }
109         fdt_set_node_status(blob, off,
110                         enable ? FDT_STATUS_OKAY : FDT_STATUS_DISABLED, 0);
111
112         karo_set_fdtsize(blob);
113 }
114
115 static const char *karo_touchpanels[] = {
116         "ti,tsc2007",
117         "edt,edt-ft5x06",
118 };
119
120 static void fdt_del_tp_node(void *blob, const char *name)
121 {
122         int offs = fdt_node_offset_by_compatible(blob, -1, name);
123         uint32_t ph1 = 0, ph2 = 0;
124         const uint32_t *prop;
125
126         if (offs < 0) {
127                 debug("node '%s' not found: %d\n", name, offs);
128                 return;
129         }
130
131         prop = fdt_getprop(blob, offs, "reset-switch", NULL);
132         if (prop)
133                 ph1 = fdt32_to_cpu(*prop);
134
135         prop = fdt_getprop(blob, offs, "wake-switch", NULL);
136         if (prop)
137                 ph2 = fdt32_to_cpu(*prop);
138
139         debug("Removing node '%s' from DT\n", name);
140         fdt_del_node(blob, offs);
141
142         if (ph1) {
143                 offs = fdt_node_offset_by_phandle(blob, ph1);
144                 if (offs > 0) {
145                         debug("Removing node %08x @ %08x\n", ph1, offs);
146                         fdt_del_node(blob, offs);
147                 }
148         }
149         if (ph2) {
150                 offs = fdt_node_offset_by_phandle(blob, ph2);
151                 if (offs > 0) {
152                         debug("Removing node %08x @ %08x\n", ph2, offs);
153                         fdt_del_node(blob, offs);
154                 }
155         }
156 }
157
158 void karo_fdt_fixup_touchpanel(void *blob)
159 {
160         int i;
161         const char *model = getenv("touchpanel");
162
163         for (i = 0; i < ARRAY_SIZE(karo_touchpanels); i++) {
164                 const char *tp = karo_touchpanels[i];
165
166                 if (model != NULL && strcmp(model, tp) == 0)
167                         continue;
168
169                 if (model != NULL) {
170                         if (strcmp(model, tp) == 0)
171                                 continue;
172                         tp = strchr(tp, ',');
173                         if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
174                                 continue;
175                 }
176                 fdt_del_tp_node(blob, karo_touchpanels[i]);
177                 karo_set_fdtsize(blob);
178         }
179 }
180
181 void karo_fdt_fixup_usb_otg(void *blob, const char *compat, phys_addr_t offs)
182 {
183         const char *otg_mode = getenv("otg_mode");
184         int off;
185
186         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
187
188         off = fdt_node_offset_by_compat_reg(blob, compat, offs);
189         if (off < 0) {
190                 printf("Failed to find node %s@%08lx\n", compat, offs);
191                 return;
192         }
193
194         if (!otg_mode || strcmp(otg_mode, "device") != 0) {
195                 printf("Deleting property gadget-device-name from node %s@%08lx\n",
196                         compat, offs);
197                 fdt_delprop(blob, off, "gadget-device-name");
198         }
199         if (!otg_mode || strcmp(otg_mode, "host") != 0) {
200                 printf("Deleting property host-device-name from node %s@%08lx\n",
201                         compat, offs);
202                 fdt_delprop(blob, off, "host-device-name");
203         }
204         karo_set_fdtsize(blob);
205 }
206
207 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
208                         const char *prop)
209 {
210         int ret;
211         int offset;
212         const uint32_t *phandle;
213         uint32_t ph = 0;
214
215         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
216         if (offset <= 0)
217                 return;
218
219         phandle = fdt_getprop(blob, offset, prop, NULL);
220         if (phandle) {
221                 ph = fdt32_to_cpu(*phandle);
222         }
223
224         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
225         ret = fdt_delprop(blob, offset, prop);
226         if (ret == 0)
227                 karo_set_fdtsize(blob);
228
229         if (!ph)
230                 return;
231
232         offset = fdt_node_offset_by_phandle(blob, ph);
233         if (offset <= 0)
234                 return;
235
236         debug("Removing node @ %08x\n", offset);
237         fdt_del_node(blob, offset);
238         karo_set_fdtsize(blob);
239 }
240
241 static int karo_load_part(const char *part, void *addr, size_t len)
242 {
243         int ret;
244         struct mtd_device *dev;
245         struct part_info *part_info;
246         u8 part_num;
247
248         debug("Initializing mtd_parts\n");
249         ret = mtdparts_init();
250         if (ret)
251                 return ret;
252
253         debug("Trying to find NAND partition '%s'\n", part);
254         ret = find_dev_and_part(part, &dev, &part_num,
255                                 &part_info);
256         if (ret) {
257                 printf("Failed to find flash partition '%s': %d\n",
258                         part, ret);
259
260                 return ret;
261         }
262         debug("Found partition '%s': offset=%08x size=%08x\n",
263                 part, part_info->offset, part_info->size);
264         if (part_info->size < len) {
265                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
266                         part, len, part_info->size);
267                 len = part_info->size;
268         }
269         debug("Reading NAND partition '%s' to %p\n", part, addr);
270         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len, addr);
271         if (ret) {
272                 printf("Failed to load partition '%s' to %p\n", part, addr);
273                 return ret;
274         }
275         debug("Read %u byte from partition '%s' @ offset %08x\n",
276                 len, part, part_info->offset);
277         return 0;
278 }
279
280 void *karo_fdt_load_dtb(void)
281 {
282         int ret;
283         void *fdt = (void *)getenv_ulong("fdtaddr", 16, 0);
284
285         if (tstc() || !fdt) {
286                 debug("aborting DTB load\n");
287                 return NULL;
288         }
289
290         /* clear FDT header in memory */
291         memset(fdt, 0, 4);
292
293         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
294         if (ret) {
295                 printf("Failed to load dtb from flash: %d\n", ret);
296                 return NULL;
297         }
298
299         if (fdt_check_header(fdt)) {
300                 debug("No valid DTB in flash\n");
301                 return NULL;
302         }
303         debug("Using DTB from flash\n");
304         karo_set_fdtsize(fdt);
305         return fdt;
306 }