blob: 796c3861893fa1b013eb05fd63fad0c2ff9fe4d3 [file] [log] [blame]
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -07002 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <libfdt.h>
30#include <dev_tree.h>
31#include <lib/ptable.h>
32#include <malloc.h>
33#include <qpic_nand.h>
34#include <stdlib.h>
35#include <string.h>
36#include <platform.h>
37#include <board.h>
38
Joel Kingaa335dc2013-06-03 16:11:08 -070039struct dt_entry_v1
40{
41 uint32_t platform_id;
42 uint32_t variant_id;
43 uint32_t soc_rev;
44 uint32_t offset;
45 uint32_t size;
46};
47
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -070048static int platform_dt_match(struct dt_entry *cur_dt_entry, uint32_t target_variant_id, uint32_t subtype_mask);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070049extern int target_is_emmc_boot(void);
50extern uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset);
Deepa Dinamanic55f01b2013-05-30 14:05:56 -070051/* TODO: This function needs to be moved to target layer to check violations
52 * against all the other regions as well.
53 */
54extern int check_aboot_addr_range_overlap(uint32_t start, uint32_t size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070055
Shashank Mittalc0f10282013-07-15 14:53:31 -070056/* Returns soc version if platform id and hardware id matches
57 otherwise return 0xFFFFFFFF */
58#define INVALID_SOC_REV_ID 0XFFFFFFFF
59static uint32_t dev_tree_compatible(void *dtb)
Dima Zavinc46f8382013-05-03 12:23:06 -070060{
61 int root_offset;
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -070062 const void *prop = NULL;
63 const char *plat_prop = NULL;
64 const char *board_prop = NULL;
65 char *model = NULL;
66 struct dt_entry cur_dt_entry;
67 struct dt_entry *dt_entry_v2 = NULL;
68 struct board_id *board_data = NULL;
69 struct plat_id *platform_data = NULL;
Dima Zavinc46f8382013-05-03 12:23:06 -070070 int len;
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -070071 int len_board_id;
72 int len_plat_id;
73 int min_plat_id_len = 0;
74 uint32_t target_variant_id;
75 uint32_t dtb_ver;
76 uint32_t num_entries = 0;
77 uint32_t i, j, k;
78 uint32_t found = 0;
79 uint32_t msm_data_count;
80 uint32_t board_data_count;
81 uint32_t soc_rev;
Dima Zavinc46f8382013-05-03 12:23:06 -070082
83 root_offset = fdt_path_offset(dtb, "/");
84 if (root_offset < 0)
85 return false;
86
87 prop = fdt_getprop(dtb, root_offset, "model", &len);
88 if (prop && len > 0) {
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -070089 model = (char *) malloc(sizeof(char) * len);
90 ASSERT(model);
91 strlcpy(model, prop, len);
Dima Zavinc46f8382013-05-03 12:23:06 -070092 } else {
93 model[0] = '\0';
94 }
95
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -070096 /* Find the board-id prop from DTB , if board-id is present then
97 * the DTB is version 2 */
98 board_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,board-id", &len_board_id);
99 if (board_prop)
100 {
101 dtb_ver = DEV_TREE_VERSION_V2;
102 min_plat_id_len = PLAT_ID_SIZE;
103 }
104 else
105 {
106 dtb_ver = DEV_TREE_VERSION_V1;
107 min_plat_id_len = DT_ENTRY_V1_SIZE;
108 }
109
110 /* Get the msm-id prop from DTB */
111 plat_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,msm-id", &len_plat_id);
112 if (!plat_prop || len_plat_id <= 0) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700113 dprintf(INFO, "qcom,msm-id entry not found\n");
114 return false;
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700115 } else if (len_plat_id % min_plat_id_len) {
116 dprintf(INFO, "qcom,msm-id in device tree is (%d) not a multiple of (%d)\n",
117 len_plat_id, min_plat_id_len);
Dima Zavinc46f8382013-05-03 12:23:06 -0700118 return false;
119 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700120
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700121 /*
122 * If DTB version is '1' look for <x y z> pair in the DTB
123 * x: platform_id
124 * y: variant_id
125 * z: SOC rev
126 */
127 if (dtb_ver == DEV_TREE_VERSION_V1)
128 {
129 while (len_plat_id)
130 {
131 cur_dt_entry.platform_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->platform_id);
132 cur_dt_entry.variant_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->variant_id);
133 cur_dt_entry.soc_rev = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->soc_rev);
134 cur_dt_entry.board_hw_subtype = board_hardware_subtype();
Dima Zavinc46f8382013-05-03 12:23:06 -0700135
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700136 target_variant_id = board_hardware_id();
137
138 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u 0x%x)\n",
139 *model ? model : "unknown",
140 cur_dt_entry.platform_id, cur_dt_entry.variant_id, cur_dt_entry.soc_rev);
141
142 if (platform_dt_match(&cur_dt_entry, target_variant_id, 0) == 1)
143 {
144 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u 0x%x> != <%u %u 0x%x>\n",
145 cur_dt_entry.platform_id,
146 cur_dt_entry.variant_id,
147 cur_dt_entry.soc_rev,
148 board_platform_id(),
149 board_hardware_id(),
150 board_soc_version());
151 plat_prop += DT_ENTRY_V1_SIZE;
152 len_plat_id -= DT_ENTRY_V1_SIZE;
153 continue;
154 }
155 else
156 {
157 found = 1;
158 break;
159 }
160 }
161 }
162 /*
163 * If DTB Version is '2' then we have split DTB with board & msm data
164 * populated saperately in board-id & msm-id prop respectively.
165 * Extract the data & prepare a look up table
166 */
167 else if (dtb_ver == DEV_TREE_VERSION_V2)
168 {
169 board_data_count = (len_board_id / BOARD_ID_SIZE);
170 msm_data_count = (len_plat_id / PLAT_ID_SIZE);
171
172 /* If we are using dtb v2.0, then we have split board & msm data in the DTB */
173 board_data = (struct board_id *) malloc(sizeof(struct board_id) * (len_board_id / BOARD_ID_SIZE));
174 ASSERT(board_data);
175 platform_data = (struct plat_id *) malloc(sizeof(struct plat_id) * (len_plat_id / PLAT_ID_SIZE));
176 ASSERT(platform_data);
177 i = 0;
178
179 /* Extract board data from DTB */
180 for(i = 0 ; i < board_data_count; i++)
181 {
182 board_data[i].variant_id = fdt32_to_cpu(((struct board_id *)board_prop)->variant_id);
183 board_data[i].platform_subtype = fdt32_to_cpu(((struct board_id *)board_prop)->platform_subtype);
184 len_board_id -= sizeof(struct board_id);
185 board_prop += sizeof(struct board_id);
186 }
187
188 /* Extract platform data from DTB */
189 for(i = 0 ; i < msm_data_count; i++)
190 {
191 platform_data[i].platform_id = fdt32_to_cpu(((struct plat_id *)plat_prop)->platform_id);
192 platform_data[i].soc_rev = fdt32_to_cpu(((struct plat_id *)plat_prop)->soc_rev);
193 len_plat_id -= sizeof(struct plat_id);
194 plat_prop += sizeof(struct plat_id);
195 }
196
197 /* We need to merge board & platform data into dt entry structure */
198 num_entries = msm_data_count * board_data_count;
199 dt_entry_v2 = (struct dt_entry*) malloc(sizeof(struct dt_entry) * num_entries);
200 ASSERT(dt_entry_v2);
201
202 /* If we have '<X>; <Y>; <Z>' as platform data & '<A>; <B>; <C>' as board data.
203 * Then dt entry should look like
204 * <X ,A >;<X, B>;<X, C>;
205 * <Y ,A >;<Y, B>;<Y, C>;
206 * <Z ,A >;<Z, B>;<Z, C>;
207 */
208 i = 0;
209 k = 0;
210 for (i = 0; i < msm_data_count; i++)
211 {
212 for (j = 0; j < board_data_count; j++)
213 {
214 dt_entry_v2[k].platform_id = platform_data[i].platform_id;
215 dt_entry_v2[k].soc_rev = platform_data[i].soc_rev;
216 dt_entry_v2[k].variant_id = board_data[j].variant_id;
217 dt_entry_v2[k].board_hw_subtype = board_data[j].platform_subtype;
218 k++;
219 }
220 }
221
222 /* Now find the matching entry in the merged list */
223 if (board_hardware_id() == HW_PLATFORM_QRD)
224 target_variant_id = board_target_id();
225 else
226 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
227
228 for (i=0 ;i < num_entries; i++)
229 {
230 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u %u 0x%x)\n",
231 *model ? model : "unknown",
232 dt_entry_v2[i].platform_id, dt_entry_v2[i].variant_id, dt_entry_v2[i].board_hw_subtype, dt_entry_v2[i].soc_rev);
233
234 if (platform_dt_match(&dt_entry_v2[i], target_variant_id, 0xff) == 1)
235 {
236 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u %u 0x%x> != <%u %u %u 0x%x>\n",
237 dt_entry_v2[i].platform_id,
238 dt_entry_v2[i].variant_id,
239 dt_entry_v2[i].soc_rev,
240 dt_entry_v2[i].board_hw_subtype,
241 board_platform_id(),
242 board_hardware_id(),
243 board_hardware_subtype(),
244 board_soc_version());
245 continue;
246 }
247 else
248 {
249 /* If found a match, return the cur_dt_entry */
250 found = 1;
251 cur_dt_entry = dt_entry_v2[i];
252 break;
253 }
254 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700255 }
256
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700257 if (!found)
258 {
259 soc_rev = INVALID_SOC_REV_ID;
260 goto end;
261 }
262 else
263 soc_rev = cur_dt_entry.soc_rev;
264
265 dprintf(INFO, "Device tree's msm_id matches the board: <%u %u %u 0x%x> == <%u %u %u 0x%x>\n",
266 cur_dt_entry.platform_id,
267 cur_dt_entry.variant_id,
268 cur_dt_entry.board_hw_subtype,
269 cur_dt_entry.soc_rev,
Shashank Mittalc0f10282013-07-15 14:53:31 -0700270 board_platform_id(),
271 board_hardware_id(),
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700272 board_hardware_subtype(),
Shashank Mittalc0f10282013-07-15 14:53:31 -0700273 board_soc_version());
Channagoud Kadabif2c88dd2013-09-05 17:44:11 -0700274
275end:
276 free(board_data);
277 free(platform_data);
278 free(dt_entry_v2);
279 free(model);
280
281 return soc_rev;
Dima Zavinc46f8382013-05-03 12:23:06 -0700282}
283
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800284/*
Dima Zavin77e41f32013-03-06 16:10:43 -0800285 * Will relocate the DTB to the tags addr if the device tree is found and return
286 * its address
287 *
288 * Arguments: kernel - Start address of the kernel loaded in RAM
289 * tags - Start address of the tags loaded in RAM
Channagoud Kadabi704cd562013-04-25 15:19:59 -0700290 * kernel_size - Size of the kernel in bytes
291 *
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800292 * Return Value: DTB address : If appended device tree is found
Dima Zavin77e41f32013-03-06 16:10:43 -0800293 * 'NULL' : Otherwise
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800294 */
Dima Zavinc46f8382013-05-03 12:23:06 -0700295void *dev_tree_appended(void *kernel, uint32_t kernel_size, void *tags)
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800296{
Dima Zavinc46f8382013-05-03 12:23:06 -0700297 void *kernel_end = kernel + kernel_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800298 uint32_t app_dtb_offset = 0;
Dima Zavinc46f8382013-05-03 12:23:06 -0700299 void *dtb;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700300 void *bestmatch_tag = NULL;
301 uint32_t bestmatch_tag_size;
302 uint32_t bestmatch_soc_rev_id = INVALID_SOC_REV_ID;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800303
304 memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800305
vijay kumarb3898ea2014-06-25 12:24:14 +0530306 if (((uintptr_t)kernel + (uintptr_t)app_dtb_offset) < (uintptr_t)kernel) {
307 return NULL;
308 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700309 dtb = kernel + app_dtb_offset;
vijay kumarb3898ea2014-06-25 12:24:14 +0530310 while (((uintptr_t)dtb + sizeof(struct fdt_header)) < (uintptr_t)kernel_end) {
Shashank Mittalc0f10282013-07-15 14:53:31 -0700311 uint32_t dtb_soc_rev_id;
Dima Zavinc46f8382013-05-03 12:23:06 -0700312 struct fdt_header dtb_hdr;
313 uint32_t dtb_size;
Dima Zavin77e41f32013-03-06 16:10:43 -0800314
Dima Zavinc46f8382013-05-03 12:23:06 -0700315 /* the DTB could be unaligned, so extract the header,
316 * and operate on it separately */
317 memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
318 if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
vijay kumarb3898ea2014-06-25 12:24:14 +0530319 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) < (uintptr_t)dtb) ||
320 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) > (uintptr_t)kernel_end))
Dima Zavinc46f8382013-05-03 12:23:06 -0700321 break;
322 dtb_size = fdt_totalsize(&dtb_hdr);
323
vijay kumard3ba56f2014-06-24 16:30:18 +0530324 if (check_aboot_addr_range_overlap(tags, dtb_size)) {
325 dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
326 return NULL;
327 }
328
Dima Zavinc46f8382013-05-03 12:23:06 -0700329 /* now that we know we have a valid DTB, we need to copy
330 * it somewhere aligned, like tags */
331 memcpy(tags, dtb, dtb_size);
332
Shashank Mittalc0f10282013-07-15 14:53:31 -0700333 dtb_soc_rev_id = dev_tree_compatible(tags);
334 if (dtb_soc_rev_id == board_soc_version()) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700335 /* clear out the old DTB magic so kernel doesn't find it */
336 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
337 return tags;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700338 } else if ((dtb_soc_rev_id != INVALID_SOC_REV_ID) &&
339 (dtb_soc_rev_id < board_soc_version())) {
340 /* if current bestmatch is less than new dtb_soc_rev_id then update
341 bestmatch_tag */
342 if((bestmatch_soc_rev_id == INVALID_SOC_REV_ID) ||
343 (bestmatch_soc_rev_id < dtb_soc_rev_id)) {
344 bestmatch_tag = dtb;
345 bestmatch_tag_size = dtb_size;
346 bestmatch_soc_rev_id = dtb_soc_rev_id;
347 }
Dima Zavin77e41f32013-03-06 16:10:43 -0800348 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700349
350 /* goto the next device tree if any */
351 dtb += dtb_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800352 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700353
Shashank Mittalc0f10282013-07-15 14:53:31 -0700354 if(bestmatch_tag) {
355 dprintf(INFO,"DTB found with bestmatch soc rev id 0x%x.Board soc rev id 0x%x\n",
356 bestmatch_soc_rev_id, board_soc_version());
357 memcpy(tags, bestmatch_tag, bestmatch_tag_size);
358 /* clear out the old DTB magic so kernel doesn't find it */
359 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
360 return tags;
361 }
362
Dima Zavinc46f8382013-05-03 12:23:06 -0700363 dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800364
Dima Zavin77e41f32013-03-06 16:10:43 -0800365 return NULL;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800366}
367
Joel Kingaa335dc2013-06-03 16:11:08 -0700368/* Returns 0 if the device tree is valid. */
Deepa Dinamani87252952013-09-09 13:58:27 -0700369int dev_tree_validate(struct dt_table *table, unsigned int page_size, uint32_t *dt_hdr_size)
Joel Kingaa335dc2013-06-03 16:11:08 -0700370{
371 int dt_entry_size;
Channagoud Kadabi49b69332014-06-20 15:41:55 -0700372 uint64_t hdr_size;
Joel Kingaa335dc2013-06-03 16:11:08 -0700373
374 /* Validate the device tree table header */
375 if(table->magic != DEV_TREE_MAGIC) {
376 dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
377 return -1;
378 }
379
380 if (table->version == DEV_TREE_VERSION_V1) {
381 dt_entry_size = sizeof(struct dt_entry_v1);
382 } else if (table->version == DEV_TREE_VERSION_V2) {
383 dt_entry_size = sizeof(struct dt_entry);
384 } else {
385 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
386 table->version);
387 return -1;
388 }
389
Channagoud Kadabi49b69332014-06-20 15:41:55 -0700390 hdr_size = (uint64_t)table->num_entries * dt_entry_size + DEV_TREE_HEADER_SIZE;
391
Deepa Dinamani87252952013-09-09 13:58:27 -0700392 /* Roundup to page_size. */
393 hdr_size = ROUNDUP(hdr_size, page_size);
394
Channagoud Kadabi49b69332014-06-20 15:41:55 -0700395 if (hdr_size > UINT_MAX)
396 return -1;
397 else
398 *dt_hdr_size = hdr_size & UINT_MAX;
Joel Kingaa335dc2013-06-03 16:11:08 -0700399
400 return 0;
401}
402
Maria Yu2e2d2c22013-07-03 19:20:33 +0800403static int platform_dt_match(struct dt_entry *cur_dt_entry, uint32_t target_variant_id, uint32_t subtype_mask)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700404{
Maria Yu2e2d2c22013-07-03 19:20:33 +0800405 /* 1. must match the platform_id, hardware_id, platform_version
406 * 2. soc rev number equal then return 0
407 * 3. dt soc rev number less than cdt return -1
408 * 4. otherwise return 1
409 */
410 uint32_t cur_dt_target_id ;
Maria Yuca51ee22013-06-27 21:45:24 +0800411
Maria Yu2e2d2c22013-07-03 19:20:33 +0800412 cur_dt_target_id = cur_dt_entry->variant_id | ((cur_dt_entry->board_hw_subtype & subtype_mask & 0xff) << 24);
413
414 if((cur_dt_entry->platform_id == board_platform_id()) &&
415 (cur_dt_target_id == target_variant_id)) {
416 if(cur_dt_entry->soc_rev == board_soc_version()) {
Maria Yuca51ee22013-06-27 21:45:24 +0800417 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800418 } else if(cur_dt_entry->soc_rev < board_soc_version()) {
419 return -1;
Maria Yuca51ee22013-06-27 21:45:24 +0800420 }
421 }
Maria Yuca51ee22013-06-27 21:45:24 +0800422
Maria Yu2e2d2c22013-07-03 19:20:33 +0800423 return 1;
Maria Yuca51ee22013-06-27 21:45:24 +0800424}
425
Maria Yu2e2d2c22013-07-03 19:20:33 +0800426static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info,
427 uint32_t target_variant_id, uint32_t subtype_mask)
Maria Yuca51ee22013-06-27 21:45:24 +0800428{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700429 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700430 unsigned char *table_ptr;
431 struct dt_entry dt_entry_buf_1;
432 struct dt_entry dt_entry_buf_2;
433 struct dt_entry *cur_dt_entry;
434 struct dt_entry *best_match_dt_entry;
435 struct dt_entry_v1 *dt_entry_v1;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800436 uint32_t found = 0;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700437
Joel Kingaa335dc2013-06-03 16:11:08 -0700438 if (!dt_entry_info) {
439 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
440 __func__);
441 return -1;
442 }
443
444 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
445 cur_dt_entry = &dt_entry_buf_1;
446 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700447
Maria Yu2e2d2c22013-07-03 19:20:33 +0800448 for(i = 0; found == 0 && i < table->num_entries; i++)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700449 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700450 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
451 switch(table->version) {
452 case DEV_TREE_VERSION_V1:
453 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
454 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
455 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
456 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
457 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
458 cur_dt_entry->offset = dt_entry_v1->offset;
459 cur_dt_entry->size = dt_entry_v1->size;
460 table_ptr += sizeof(struct dt_entry_v1);
461 break;
462 case DEV_TREE_VERSION_V2:
463 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
464 sizeof(struct dt_entry));
465 table_ptr += sizeof(struct dt_entry);
466 break;
467 default:
468 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
469 table->version);
470 return -1;
471 }
472
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800473 /* DTBs are stored in the ascending order of soc revision.
474 * For eg: Rev0..Rev1..Rev2 & so on.
475 * we pickup the DTB with highest soc rev number which is less
476 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700477 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800478 switch(platform_dt_match(cur_dt_entry, target_variant_id, subtype_mask)) {
479 case 0:
480 best_match_dt_entry = cur_dt_entry;
481 found = 1;
482 break;
483 case -1:
484 if (!best_match_dt_entry) {
485 /* copy structure */
David Ng618293a2013-06-25 12:29:03 -0700486 best_match_dt_entry = cur_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800487 cur_dt_entry = &dt_entry_buf_2;
488 } else {
489 /* Swap dt_entry buffers */
490 struct dt_entry *temp = cur_dt_entry;
491 cur_dt_entry = best_match_dt_entry;
492 best_match_dt_entry = temp;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700493 }
Maria Yu2e2d2c22013-07-03 19:20:33 +0800494 default:
495 break;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800496 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700497 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800498
Joel Kingaa335dc2013-06-03 16:11:08 -0700499 if (best_match_dt_entry) {
500 *dt_entry_info = *best_match_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800501 found = 1;
502 }
503
504 if (found != 0) {
David Ng618293a2013-06-25 12:29:03 -0700505 dprintf(INFO, "Using DTB entry %u/%08x/%u/%u for device %u/%08x/%u/%u\n",
506 dt_entry_info->platform_id, dt_entry_info->soc_rev,
507 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
508 board_platform_id(), board_soc_version(),
509 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700510 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800511 }
512
David Ng618293a2013-06-25 12:29:03 -0700513 dprintf(CRITICAL, "ERROR: Unable to find suitable device tree for device (%u/0x%08x/%u/%u)\n",
514 board_platform_id(), board_soc_version(),
515 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700516 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700517}
518
Maria Yu2e2d2c22013-07-03 19:20:33 +0800519/* Function to obtain the index information for the correct device tree
520 * based on the platform data.
521 * If a matching device tree is found, the information is returned in the
522 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
523 * a non-zero function value is returned.
524 */
525int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
526{
527 uint32_t target_variant_id;
528
529 if(board_hardware_id() == HW_PLATFORM_QRD) {
530 target_variant_id = board_target_id();
531 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
532 return 0;
533 }
534 }
535 /*
536 * for compatible with version 1 and version 2 dtbtool
537 * will compare the subtype inside the variant id
538 */
539 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
540 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
541 return 0;
542 }
543
544 /*
545 * add compatible with old device selection method which don't compare subtype
546 */
547 target_variant_id = board_hardware_id();
548 return __dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0);
549}
550
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700551/* Function to add the first RAM partition info to the device tree.
552 * Note: The function replaces the reg property in the "/memory" node
553 * with the addr and size provided.
554 */
555int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
556{
557 int ret;
558
559 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
560
561 if (ret)
562 {
563 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
564 ret);
565 }
566
567
568 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
569
570 if (ret)
571 {
572 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
573 ret);
574 }
575
576 return ret;
577}
578
579/* Function to add the subsequent RAM partition info to the device tree. */
580int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t addr, uint32_t size)
581{
582 static int mem_info_cnt = 0;
583 int ret;
584
585 if (!mem_info_cnt)
586 {
587 /* Replace any other reg prop in the memory node. */
588 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
589 mem_info_cnt = 1;
590 }
591 else
592 {
593 /* Append the mem info to the reg prop for subsequent nodes. */
594 ret = fdt_appendprop_u32(fdt, offset, "reg", addr);
595 }
596
597 if (ret)
598 {
599 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
600 ret);
601 }
602
603
604 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
605
606 if (ret)
607 {
608 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
609 ret);
610 }
611
612 return ret;
613}
614
615/* Top level function that updates the device tree. */
616int update_device_tree(void *fdt, const char *cmdline,
617 void *ramdisk, uint32_t ramdisk_size)
618{
619 int ret = 0;
620 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700621
622 /* Check the device tree header */
623 ret = fdt_check_header(fdt);
624 if (ret)
625 {
626 dprintf(CRITICAL, "Invalid device tree header \n");
627 return ret;
628 }
629
Deepa Dinamani1c970732013-04-19 14:23:01 -0700630 /* Add padding to make space for new nodes and properties. */
631 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
632 if (ret!= 0)
633 {
634 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
635 return ret;
636 }
637
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700638 /* Get offset of the memory node */
639 ret = fdt_path_offset(fdt, "/memory");
640 if (ret < 0)
641 {
642 dprintf(CRITICAL, "Could not find memory node.\n");
643 return ret;
644 }
645
646 offset = ret;
647
648 ret = target_dev_tree_mem(fdt, offset);
649 if(ret)
650 {
651 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
652 return ret;
653 }
654
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700655 /* Get offset of the chosen node */
656 ret = fdt_path_offset(fdt, "/chosen");
657 if (ret < 0)
658 {
659 dprintf(CRITICAL, "Could not find chosen node.\n");
660 return ret;
661 }
662
663 offset = ret;
664 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800665 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700666 if (ret)
667 {
668 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
669 return ret;
670 }
671
672 /* Adding the initrd-start to the chosen node */
673 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
674 if (ret)
675 {
676 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
677 return ret;
678 }
679
680 /* Adding the initrd-end to the chosen node */
681 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
682 if (ret)
683 {
684 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
685 return ret;
686 }
687
688 fdt_pack(fdt);
689
690 return ret;
691}
Maria Yuca51ee22013-06-27 21:45:24 +0800692