]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/karo/common/fdt.c
TX6 Release 2013-04-22
[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                 tp = strchr(tp, ',');
170                 if (tp != NULL && *tp != '\0' && strcmp(model, tp + 1) == 0)
171                         continue;
172
173                 fdt_del_tp_node(blob, karo_touchpanels[i]);
174                 karo_set_fdtsize(blob);
175         }
176 }
177
178 void karo_fdt_fixup_usb_otg(void *blob, const char *compat, phys_addr_t offs)
179 {
180         const char *otg_mode = getenv("otg_mode");
181         int off;
182
183         debug("OTG mode is '%s'\n", otg_mode ? otg_mode : "<UNSET>");
184
185         off = fdt_node_offset_by_compat_reg(blob, compat, offs);
186         if (off < 0) {
187                 printf("Failed to find node %s@%08lx\n", compat, offs);
188                 return;
189         }
190
191         if (!otg_mode || strcmp(otg_mode, "device") != 0) {
192                 printf("Deleting property gadget-device-name from node %s@%08lx\n",
193                         compat, offs);
194                 fdt_delprop(blob, off, "gadget-device-name");
195         }
196         if (!otg_mode || strcmp(otg_mode, "host") != 0) {
197                 printf("Deleting property host-device-name from node %s@%08lx\n",
198                         compat, offs);
199                 fdt_delprop(blob, off, "host-device-name");
200         }
201         karo_set_fdtsize(blob);
202 }
203
204 void karo_fdt_del_prop(void *blob, const char *compat, phys_addr_t offs,
205                         const char *prop)
206 {
207         int ret;
208         int offset;
209         const uint32_t *phandle;
210         uint32_t ph = 0;
211
212         offset = fdt_node_offset_by_compat_reg(blob, compat, offs);
213         if (offset <= 0)
214                 return;
215
216         phandle = fdt_getprop(blob, offset, prop, NULL);
217         if (phandle) {
218                 ph = fdt32_to_cpu(*phandle);
219         }
220
221         debug("Removing property '%s' from node %s@%08lx\n", prop, compat, offs);
222         ret = fdt_delprop(blob, offset, prop);
223         if (ret == 0)
224                 karo_set_fdtsize(blob);
225
226         if (!ph)
227                 return;
228
229         offset = fdt_node_offset_by_phandle(blob, ph);
230         if (offset <= 0)
231                 return;
232
233         debug("Removing node @ %08x\n", offset);
234         fdt_del_node(blob, offset);
235         karo_set_fdtsize(blob);
236 }
237
238 static int karo_load_part(const char *part, void *addr, size_t len)
239 {
240         int ret;
241         struct mtd_device *dev;
242         struct part_info *part_info;
243         u8 part_num;
244
245         debug("Initializing mtd_parts\n");
246         ret = mtdparts_init();
247         if (ret)
248                 return ret;
249
250         debug("Trying to find NAND partition '%s'\n", part);
251         ret = find_dev_and_part(part, &dev, &part_num,
252                                 &part_info);
253         if (ret) {
254                 printf("Failed to find flash partition '%s': %d\n",
255                         part, ret);
256
257                 return ret;
258         }
259         debug("Found partition '%s': offset=%08x size=%08x\n",
260                 part, part_info->offset, part_info->size);
261         if (part_info->size < len) {
262                 printf("Warning: partition '%s' smaller than requested size: %u; truncating data to %u byte\n",
263                         part, len, part_info->size);
264                 len = part_info->size;
265         }
266         debug("Reading NAND partition '%s' to %p\n", part, addr);
267         ret = nand_read_skip_bad(&nand_info[0], part_info->offset, &len, addr);
268         if (ret) {
269                 printf("Failed to load partition '%s' to %p\n", part, addr);
270                 return ret;
271         }
272         debug("Read %u byte from partition '%s' @ offset %08x\n",
273                 len, part, part_info->offset);
274         return 0;
275 }
276
277 void *karo_fdt_load_dtb(void)
278 {
279         int ret;
280         void *fdt = (void *)getenv_ulong("fdtaddr", 16, 0);
281
282         if (tstc() || !fdt) {
283                 debug("aborting DTB load\n");
284                 return NULL;
285         }
286
287         /* clear FDT header in memory */
288         memset(fdt, 0, 4);
289
290         ret = karo_load_part("dtb", fdt, MAX_DTB_SIZE);
291         if (ret) {
292                 printf("Failed to load dtb from flash: %d\n", ret);
293                 return NULL;
294         }
295
296         if (fdt_check_header(fdt)) {
297                 debug("No valid DTB in flash\n");
298                 return NULL;
299         }
300         debug("Using DTB from flash\n");
301         karo_set_fdtsize(fdt);
302         return fdt;
303 }