blob: bdba71d5caecee2e98ece9b9e56d743ede289025 [file] [log] [blame]
Channagoud Kadabi571193a2014-02-05 13:58:49 -08001/* Copyright (c) 2012-2014, 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
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -070048static struct dt_mem_node_info mem_node;
49
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070050static 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 -070051extern int target_is_emmc_boot(void);
52extern uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset);
Deepa Dinamanic55f01b2013-05-30 14:05:56 -070053/* TODO: This function needs to be moved to target layer to check violations
54 * against all the other regions as well.
55 */
56extern int check_aboot_addr_range_overlap(uint32_t start, uint32_t size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070057
Shashank Mittalc0f10282013-07-15 14:53:31 -070058/* Returns soc version if platform id and hardware id matches
59 otherwise return 0xFFFFFFFF */
60#define INVALID_SOC_REV_ID 0XFFFFFFFF
61static uint32_t dev_tree_compatible(void *dtb)
Dima Zavinc46f8382013-05-03 12:23:06 -070062{
63 int root_offset;
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070064 const void *prop = NULL;
65 const char *plat_prop = NULL;
66 const char *board_prop = NULL;
67 char *model = NULL;
68 struct dt_entry cur_dt_entry;
69 struct dt_entry *dt_entry_v2 = NULL;
70 struct board_id *board_data = NULL;
71 struct plat_id *platform_data = NULL;
Dima Zavinc46f8382013-05-03 12:23:06 -070072 int len;
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070073 int len_board_id;
74 int len_plat_id;
75 int min_plat_id_len = 0;
76 uint32_t target_variant_id;
77 uint32_t dtb_ver;
78 uint32_t num_entries = 0;
79 uint32_t i, j, k;
80 uint32_t found = 0;
81 uint32_t msm_data_count;
82 uint32_t board_data_count;
83 uint32_t soc_rev;
Dima Zavinc46f8382013-05-03 12:23:06 -070084
85 root_offset = fdt_path_offset(dtb, "/");
86 if (root_offset < 0)
87 return false;
88
89 prop = fdt_getprop(dtb, root_offset, "model", &len);
90 if (prop && len > 0) {
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070091 model = (char *) malloc(sizeof(char) * len);
92 ASSERT(model);
93 strlcpy(model, prop, len);
Dima Zavinc46f8382013-05-03 12:23:06 -070094 } else {
95 model[0] = '\0';
96 }
97
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070098 /* Find the board-id prop from DTB , if board-id is present then
99 * the DTB is version 2 */
100 board_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,board-id", &len_board_id);
101 if (board_prop)
102 {
103 dtb_ver = DEV_TREE_VERSION_V2;
104 min_plat_id_len = PLAT_ID_SIZE;
105 }
106 else
107 {
108 dtb_ver = DEV_TREE_VERSION_V1;
109 min_plat_id_len = DT_ENTRY_V1_SIZE;
110 }
111
112 /* Get the msm-id prop from DTB */
113 plat_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,msm-id", &len_plat_id);
114 if (!plat_prop || len_plat_id <= 0) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700115 dprintf(INFO, "qcom,msm-id entry not found\n");
116 return false;
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700117 } else if (len_plat_id % min_plat_id_len) {
118 dprintf(INFO, "qcom,msm-id in device tree is (%d) not a multiple of (%d)\n",
119 len_plat_id, min_plat_id_len);
Dima Zavinc46f8382013-05-03 12:23:06 -0700120 return false;
121 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700122
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700123 /*
124 * If DTB version is '1' look for <x y z> pair in the DTB
125 * x: platform_id
126 * y: variant_id
127 * z: SOC rev
128 */
129 if (dtb_ver == DEV_TREE_VERSION_V1)
130 {
131 while (len_plat_id)
132 {
133 cur_dt_entry.platform_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->platform_id);
134 cur_dt_entry.variant_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->variant_id);
135 cur_dt_entry.soc_rev = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->soc_rev);
136 cur_dt_entry.board_hw_subtype = board_hardware_subtype();
Dima Zavinc46f8382013-05-03 12:23:06 -0700137
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700138 target_variant_id = board_hardware_id();
139
140 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u 0x%x)\n",
141 *model ? model : "unknown",
142 cur_dt_entry.platform_id, cur_dt_entry.variant_id, cur_dt_entry.soc_rev);
143
144 if (platform_dt_match(&cur_dt_entry, target_variant_id, 0) == 1)
145 {
146 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u 0x%x> != <%u %u 0x%x>\n",
147 cur_dt_entry.platform_id,
148 cur_dt_entry.variant_id,
149 cur_dt_entry.soc_rev,
150 board_platform_id(),
151 board_hardware_id(),
152 board_soc_version());
153 plat_prop += DT_ENTRY_V1_SIZE;
154 len_plat_id -= DT_ENTRY_V1_SIZE;
155 continue;
156 }
157 else
158 {
159 found = 1;
160 break;
161 }
162 }
163 }
164 /*
165 * If DTB Version is '2' then we have split DTB with board & msm data
166 * populated saperately in board-id & msm-id prop respectively.
167 * Extract the data & prepare a look up table
168 */
169 else if (dtb_ver == DEV_TREE_VERSION_V2)
170 {
171 board_data_count = (len_board_id / BOARD_ID_SIZE);
172 msm_data_count = (len_plat_id / PLAT_ID_SIZE);
173
174 /* If we are using dtb v2.0, then we have split board & msm data in the DTB */
175 board_data = (struct board_id *) malloc(sizeof(struct board_id) * (len_board_id / BOARD_ID_SIZE));
176 ASSERT(board_data);
177 platform_data = (struct plat_id *) malloc(sizeof(struct plat_id) * (len_plat_id / PLAT_ID_SIZE));
178 ASSERT(platform_data);
179 i = 0;
180
181 /* Extract board data from DTB */
182 for(i = 0 ; i < board_data_count; i++)
183 {
184 board_data[i].variant_id = fdt32_to_cpu(((struct board_id *)board_prop)->variant_id);
185 board_data[i].platform_subtype = fdt32_to_cpu(((struct board_id *)board_prop)->platform_subtype);
186 len_board_id -= sizeof(struct board_id);
187 board_prop += sizeof(struct board_id);
188 }
189
190 /* Extract platform data from DTB */
191 for(i = 0 ; i < msm_data_count; i++)
192 {
193 platform_data[i].platform_id = fdt32_to_cpu(((struct plat_id *)plat_prop)->platform_id);
194 platform_data[i].soc_rev = fdt32_to_cpu(((struct plat_id *)plat_prop)->soc_rev);
195 len_plat_id -= sizeof(struct plat_id);
196 plat_prop += sizeof(struct plat_id);
197 }
198
199 /* We need to merge board & platform data into dt entry structure */
200 num_entries = msm_data_count * board_data_count;
201 dt_entry_v2 = (struct dt_entry*) malloc(sizeof(struct dt_entry) * num_entries);
202 ASSERT(dt_entry_v2);
203
204 /* If we have '<X>; <Y>; <Z>' as platform data & '<A>; <B>; <C>' as board data.
205 * Then dt entry should look like
206 * <X ,A >;<X, B>;<X, C>;
207 * <Y ,A >;<Y, B>;<Y, C>;
208 * <Z ,A >;<Z, B>;<Z, C>;
209 */
210 i = 0;
211 k = 0;
212 for (i = 0; i < msm_data_count; i++)
213 {
214 for (j = 0; j < board_data_count; j++)
215 {
216 dt_entry_v2[k].platform_id = platform_data[i].platform_id;
217 dt_entry_v2[k].soc_rev = platform_data[i].soc_rev;
218 dt_entry_v2[k].variant_id = board_data[j].variant_id;
219 dt_entry_v2[k].board_hw_subtype = board_data[j].platform_subtype;
220 k++;
221 }
222 }
223
224 /* Now find the matching entry in the merged list */
225 if (board_hardware_id() == HW_PLATFORM_QRD)
226 target_variant_id = board_target_id();
227 else
228 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
229
230 for (i=0 ;i < num_entries; i++)
231 {
232 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u %u 0x%x)\n",
233 *model ? model : "unknown",
234 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);
235
236 if (platform_dt_match(&dt_entry_v2[i], target_variant_id, 0xff) == 1)
237 {
238 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u %u 0x%x> != <%u %u %u 0x%x>\n",
239 dt_entry_v2[i].platform_id,
240 dt_entry_v2[i].variant_id,
241 dt_entry_v2[i].soc_rev,
242 dt_entry_v2[i].board_hw_subtype,
243 board_platform_id(),
244 board_hardware_id(),
245 board_hardware_subtype(),
246 board_soc_version());
247 continue;
248 }
249 else
250 {
251 /* If found a match, return the cur_dt_entry */
252 found = 1;
253 cur_dt_entry = dt_entry_v2[i];
254 break;
255 }
256 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700257 }
258
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700259 if (!found)
260 {
261 soc_rev = INVALID_SOC_REV_ID;
262 goto end;
263 }
264 else
265 soc_rev = cur_dt_entry.soc_rev;
266
267 dprintf(INFO, "Device tree's msm_id matches the board: <%u %u %u 0x%x> == <%u %u %u 0x%x>\n",
268 cur_dt_entry.platform_id,
269 cur_dt_entry.variant_id,
270 cur_dt_entry.board_hw_subtype,
271 cur_dt_entry.soc_rev,
Shashank Mittalc0f10282013-07-15 14:53:31 -0700272 board_platform_id(),
273 board_hardware_id(),
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700274 board_hardware_subtype(),
Shashank Mittalc0f10282013-07-15 14:53:31 -0700275 board_soc_version());
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700276
277end:
278 free(board_data);
279 free(platform_data);
280 free(dt_entry_v2);
281 free(model);
282
283 return soc_rev;
Dima Zavinc46f8382013-05-03 12:23:06 -0700284}
285
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800286/*
Dima Zavin77e41f32013-03-06 16:10:43 -0800287 * Will relocate the DTB to the tags addr if the device tree is found and return
288 * its address
289 *
290 * Arguments: kernel - Start address of the kernel loaded in RAM
291 * tags - Start address of the tags loaded in RAM
Channagoud Kadabi704cd562013-04-25 15:19:59 -0700292 * kernel_size - Size of the kernel in bytes
293 *
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800294 * Return Value: DTB address : If appended device tree is found
Dima Zavin77e41f32013-03-06 16:10:43 -0800295 * 'NULL' : Otherwise
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800296 */
Dima Zavinc46f8382013-05-03 12:23:06 -0700297void *dev_tree_appended(void *kernel, uint32_t kernel_size, void *tags)
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800298{
Dima Zavinc46f8382013-05-03 12:23:06 -0700299 void *kernel_end = kernel + kernel_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800300 uint32_t app_dtb_offset = 0;
Dima Zavinc46f8382013-05-03 12:23:06 -0700301 void *dtb;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700302 void *bestmatch_tag = NULL;
303 uint32_t bestmatch_tag_size;
304 uint32_t bestmatch_soc_rev_id = INVALID_SOC_REV_ID;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800305
306 memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800307
Dima Zavinc46f8382013-05-03 12:23:06 -0700308 dtb = kernel + app_dtb_offset;
309 while (dtb + sizeof(struct fdt_header) < kernel_end) {
Shashank Mittalc0f10282013-07-15 14:53:31 -0700310 uint32_t dtb_soc_rev_id;
Dima Zavinc46f8382013-05-03 12:23:06 -0700311 struct fdt_header dtb_hdr;
312 uint32_t dtb_size;
Dima Zavin77e41f32013-03-06 16:10:43 -0800313
Dima Zavinc46f8382013-05-03 12:23:06 -0700314 /* the DTB could be unaligned, so extract the header,
315 * and operate on it separately */
316 memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
317 if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
318 (dtb + fdt_totalsize((const void *)&dtb_hdr) > kernel_end))
319 break;
320 dtb_size = fdt_totalsize(&dtb_hdr);
321
322 /* now that we know we have a valid DTB, we need to copy
323 * it somewhere aligned, like tags */
324 memcpy(tags, dtb, dtb_size);
325
Shashank Mittalc0f10282013-07-15 14:53:31 -0700326 dtb_soc_rev_id = dev_tree_compatible(tags);
327 if (dtb_soc_rev_id == board_soc_version()) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700328 /* clear out the old DTB magic so kernel doesn't find it */
329 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
330 return tags;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700331 } else if ((dtb_soc_rev_id != INVALID_SOC_REV_ID) &&
332 (dtb_soc_rev_id < board_soc_version())) {
333 /* if current bestmatch is less than new dtb_soc_rev_id then update
334 bestmatch_tag */
335 if((bestmatch_soc_rev_id == INVALID_SOC_REV_ID) ||
336 (bestmatch_soc_rev_id < dtb_soc_rev_id)) {
337 bestmatch_tag = dtb;
338 bestmatch_tag_size = dtb_size;
339 bestmatch_soc_rev_id = dtb_soc_rev_id;
340 }
Dima Zavin77e41f32013-03-06 16:10:43 -0800341 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700342
343 /* goto the next device tree if any */
344 dtb += dtb_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800345 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700346
Shashank Mittalc0f10282013-07-15 14:53:31 -0700347 if(bestmatch_tag) {
348 dprintf(INFO,"DTB found with bestmatch soc rev id 0x%x.Board soc rev id 0x%x\n",
349 bestmatch_soc_rev_id, board_soc_version());
350 memcpy(tags, bestmatch_tag, bestmatch_tag_size);
351 /* clear out the old DTB magic so kernel doesn't find it */
352 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
353 return tags;
354 }
355
Dima Zavinc46f8382013-05-03 12:23:06 -0700356 dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800357
Dima Zavin77e41f32013-03-06 16:10:43 -0800358 return NULL;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800359}
360
Joel Kingaa335dc2013-06-03 16:11:08 -0700361/* Returns 0 if the device tree is valid. */
Deepa Dinamani87252952013-09-09 13:58:27 -0700362int dev_tree_validate(struct dt_table *table, unsigned int page_size, uint32_t *dt_hdr_size)
Joel Kingaa335dc2013-06-03 16:11:08 -0700363{
364 int dt_entry_size;
Deepa Dinamani87252952013-09-09 13:58:27 -0700365 uint32_t hdr_size;
Joel Kingaa335dc2013-06-03 16:11:08 -0700366
367 /* Validate the device tree table header */
368 if(table->magic != DEV_TREE_MAGIC) {
369 dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
370 return -1;
371 }
372
373 if (table->version == DEV_TREE_VERSION_V1) {
374 dt_entry_size = sizeof(struct dt_entry_v1);
375 } else if (table->version == DEV_TREE_VERSION_V2) {
376 dt_entry_size = sizeof(struct dt_entry);
377 } else {
378 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
379 table->version);
380 return -1;
381 }
382
Deepa Dinamani87252952013-09-09 13:58:27 -0700383 hdr_size = table->num_entries * dt_entry_size + DEV_TREE_HEADER_SIZE;
384 /* Roundup to page_size. */
385 hdr_size = ROUNDUP(hdr_size, page_size);
386
387 *dt_hdr_size = hdr_size;
Joel Kingaa335dc2013-06-03 16:11:08 -0700388
389 return 0;
390}
391
Maria Yu2e2d2c22013-07-03 19:20:33 +0800392static 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 -0700393{
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800394 /*
395 * 1. Check if cur_dt_entry has platform_hw_version major & minor present?
396 * 2. If present, calculate cur_dt_target_id for the current platform as:
397 * 3. bit no |31 24 | 23 16| 15 8 |7 0|
398 * 4. |subtype| major | minor |hw_platform|
399 */
400 uint32_t cur_dt_target_id ;
401
402 /*
403 * if variant_id has platform_hw_ver has major = 0xff and minor = 0xff,
404 * ignore the major & minor versions from the DTB entry
405 */
406 if ((cur_dt_entry->variant_id & 0xffff00) == 0xffff00)
407 cur_dt_target_id = (cur_dt_entry->variant_id & 0xff0000ff) | (target_variant_id & 0xffff00);
408 /*
409 * We have a valid platform_hw_version major & minor numbers in the board-id, so
410 * use the board-id from the DTB.
411 * Note: For some QRD platforms the format used is qcom, board-id = <0xMVmVPT 0xPS>
412 * where: MV: platform major ver, mV: platform minor ver, PT: platform type
413 * PS: platform subtype, so we need to put PS @ bit 24-31 to be backward compatible.
414 */
415 else
416 cur_dt_target_id = cur_dt_entry->variant_id | ((cur_dt_entry->board_hw_subtype & subtype_mask & 0xff) << 24);
417
418 /* 1. must match the platform_id, platform_hw_id, platform_version
Maria Yu2e2d2c22013-07-03 19:20:33 +0800419 * 2. soc rev number equal then return 0
420 * 3. dt soc rev number less than cdt return -1
421 * 4. otherwise return 1
422 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800423
424 if((cur_dt_entry->platform_id == board_platform_id()) &&
425 (cur_dt_target_id == target_variant_id)) {
426 if(cur_dt_entry->soc_rev == board_soc_version()) {
Maria Yuca51ee22013-06-27 21:45:24 +0800427 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800428 } else if(cur_dt_entry->soc_rev < board_soc_version()) {
429 return -1;
Maria Yuca51ee22013-06-27 21:45:24 +0800430 }
431 }
Maria Yuca51ee22013-06-27 21:45:24 +0800432
Maria Yu2e2d2c22013-07-03 19:20:33 +0800433 return 1;
Maria Yuca51ee22013-06-27 21:45:24 +0800434}
435
Maria Yu2e2d2c22013-07-03 19:20:33 +0800436static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info,
437 uint32_t target_variant_id, uint32_t subtype_mask)
Maria Yuca51ee22013-06-27 21:45:24 +0800438{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700439 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700440 unsigned char *table_ptr;
441 struct dt_entry dt_entry_buf_1;
442 struct dt_entry dt_entry_buf_2;
443 struct dt_entry *cur_dt_entry;
444 struct dt_entry *best_match_dt_entry;
445 struct dt_entry_v1 *dt_entry_v1;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800446 uint32_t found = 0;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700447
Joel Kingaa335dc2013-06-03 16:11:08 -0700448 if (!dt_entry_info) {
449 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
450 __func__);
451 return -1;
452 }
453
454 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
455 cur_dt_entry = &dt_entry_buf_1;
456 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700457
Maria Yu2e2d2c22013-07-03 19:20:33 +0800458 for(i = 0; found == 0 && i < table->num_entries; i++)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700459 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700460 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
461 switch(table->version) {
462 case DEV_TREE_VERSION_V1:
463 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
464 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
465 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
466 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
467 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
468 cur_dt_entry->offset = dt_entry_v1->offset;
469 cur_dt_entry->size = dt_entry_v1->size;
470 table_ptr += sizeof(struct dt_entry_v1);
471 break;
472 case DEV_TREE_VERSION_V2:
473 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
474 sizeof(struct dt_entry));
475 table_ptr += sizeof(struct dt_entry);
476 break;
477 default:
478 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
479 table->version);
480 return -1;
481 }
482
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800483 /* DTBs are stored in the ascending order of soc revision.
484 * For eg: Rev0..Rev1..Rev2 & so on.
485 * we pickup the DTB with highest soc rev number which is less
486 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700487 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800488 switch(platform_dt_match(cur_dt_entry, target_variant_id, subtype_mask)) {
489 case 0:
490 best_match_dt_entry = cur_dt_entry;
491 found = 1;
492 break;
493 case -1:
494 if (!best_match_dt_entry) {
495 /* copy structure */
David Ng618293a2013-06-25 12:29:03 -0700496 best_match_dt_entry = cur_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800497 cur_dt_entry = &dt_entry_buf_2;
498 } else {
499 /* Swap dt_entry buffers */
500 struct dt_entry *temp = cur_dt_entry;
501 cur_dt_entry = best_match_dt_entry;
502 best_match_dt_entry = temp;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700503 }
Maria Yu2e2d2c22013-07-03 19:20:33 +0800504 default:
505 break;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800506 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700507 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800508
Joel Kingaa335dc2013-06-03 16:11:08 -0700509 if (best_match_dt_entry) {
510 *dt_entry_info = *best_match_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800511 found = 1;
512 }
513
514 if (found != 0) {
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800515 dprintf(INFO, "Using DTB entry %u/%08x/0x%08x/%u for device %u/%08x/0x%08x/%u\n",
David Ng618293a2013-06-25 12:29:03 -0700516 dt_entry_info->platform_id, dt_entry_info->soc_rev,
517 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
518 board_platform_id(), board_soc_version(),
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800519 board_target_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700520 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800521 }
522
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800523 dprintf(CRITICAL, "ERROR: Unable to find suitable device tree for device (%u/0x%08x/0x%08x/%u)\n",
David Ng618293a2013-06-25 12:29:03 -0700524 board_platform_id(), board_soc_version(),
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800525 board_target_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700526 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700527}
528
Maria Yu2e2d2c22013-07-03 19:20:33 +0800529/* Function to obtain the index information for the correct device tree
530 * based on the platform data.
531 * If a matching device tree is found, the information is returned in the
532 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
533 * a non-zero function value is returned.
534 */
535int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
536{
537 uint32_t target_variant_id;
538
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800539 target_variant_id = board_target_id();
540 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
541 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800542 }
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800543
Maria Yu2e2d2c22013-07-03 19:20:33 +0800544 /*
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800545 * for compatible with version 1 and version 2 dtbtool
546 * will compare the subtype inside the variant id
547 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800548 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
549 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
550 return 0;
551 }
552
553 /*
554 * add compatible with old device selection method which don't compare subtype
555 */
556 target_variant_id = board_hardware_id();
557 return __dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0);
558}
559
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700560/* Function to add the first RAM partition info to the device tree.
561 * Note: The function replaces the reg property in the "/memory" node
562 * with the addr and size provided.
563 */
564int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
565{
566 int ret;
567
568 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
569
570 if (ret)
571 {
572 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
573 ret);
574 }
575
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700576 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
577
578 if (ret)
579 {
580 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
581 ret);
582 }
583
584 return ret;
585}
586
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700587static int dev_tree_query_memory_cell_sizes(void *fdt, struct dt_mem_node_info *mem_node, uint32_t mem_node_offset)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700588{
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700589 int len;
590 uint32_t *valp;
591 int ret;
592 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700593
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700594 mem_node->offset = mem_node_offset;
595
596 /* Get offset of the root node */
597 ret = fdt_path_offset(fdt, "/");
598 if (ret < 0)
599 {
600 dprintf(CRITICAL, "Could not find memory node.\n");
601 return ret;
602 }
603
604 offset = ret;
605
606 /* Find the #address-cells size. */
607 valp = (uint32_t*)fdt_getprop(fdt, offset, "#address-cells", &len);
608 if (len <= 0)
609 {
610 if (len == -FDT_ERR_NOTFOUND)
611 {
612 /* Property not found.
613 * Assume standard sizes.
614 */
615 mem_node->addr_cell_size = 2;
616 dprintf(CRITICAL, "Using default #addr_cell_size: %u\n", mem_node->addr_cell_size);
617 }
618 else
619 {
620 dprintf(CRITICAL, "Error finding the #address-cells property\n");
621 return len;
622 }
623 }
624 else
625 mem_node->addr_cell_size = fdt32_to_cpu(*valp);
626
627 /* Find the #size-cells size. */
628 valp = (uint32_t*)fdt_getprop(fdt, offset, "#size-cells", &len);
629 if (len <= 0)
630 {
631 if (len == -FDT_ERR_NOTFOUND)
632 {
633 /* Property not found.
634 * Assume standard sizes.
635 */
636 mem_node->size_cell_size = 1;
637 dprintf(CRITICAL, "Using default #size_cell_size: %u\n", mem_node->size_cell_size);
638 }
639 else
640 {
641 dprintf(CRITICAL, "Error finding the #size-cells property\n");
642 return len;
643 }
644 }
645 else
646 mem_node->size_cell_size = fdt32_to_cpu(*valp);
647
648 return 0;
649}
650
651static void dev_tree_update_memory_node(uint32_t offset)
652{
653 mem_node.offset = offset;
654 mem_node.addr_cell_size = 1;
655 mem_node.size_cell_size = 1;
656}
657
658/* Function to add the subsequent RAM partition info to the device tree. */
659int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint64_t addr, uint64_t size)
660{
661 int ret = 0;
662
663 if(smem_get_ram_ptable_version() >= 1)
664 {
665 ret = dev_tree_query_memory_cell_sizes(fdt, &mem_node, offset);
666 if (ret < 0)
667 {
668 dprintf(CRITICAL, "Could not find #address-cells and #size-cells properties: ret %d\n", ret);
669 return ret;
670 }
671
672 }
673 else
674 {
675 dev_tree_update_memory_node(offset);
676 }
677
678 if (!(mem_node.mem_info_cnt))
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700679 {
680 /* Replace any other reg prop in the memory node. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700681
682 /* cell_size is the number of 32 bit words used to represent an address/length in the device tree.
683 * memory node in DT can be either 32-bit(cell-size = 1) or 64-bit(cell-size = 2).So when updating
684 * the memory node in the device tree, we write one word or two words based on cell_size = 1 or 2.
685 */
686
687 if(mem_node.addr_cell_size == 2)
688 {
689 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
690 if(ret)
691 {
692 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
693 return ret;
694 }
695
696 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
697 if(ret)
698 {
699 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
700 return ret;
701 }
702 }
703 else
704 {
705 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
706 if(ret)
707 {
708 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
709 return ret;
710 }
711 }
712
713 mem_node.mem_info_cnt = 1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700714 }
715 else
716 {
717 /* Append the mem info to the reg prop for subsequent nodes. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700718 if(mem_node.addr_cell_size == 2)
719 {
720 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
721 if(ret)
722 {
723 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
724 return ret;
725 }
726 }
727
728 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
729 if(ret)
730 {
731 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
732 return ret;
733 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700734 }
735
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700736 if(mem_node.size_cell_size == 2)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700737 {
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700738 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", size>>32);
739 if(ret)
740 {
741 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
742 return ret;
743 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700744 }
745
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700746 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700747
748 if (ret)
749 {
750 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
751 ret);
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700752 return ret;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700753 }
754
755 return ret;
756}
757
758/* Top level function that updates the device tree. */
759int update_device_tree(void *fdt, const char *cmdline,
760 void *ramdisk, uint32_t ramdisk_size)
761{
762 int ret = 0;
763 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700764
765 /* Check the device tree header */
766 ret = fdt_check_header(fdt);
767 if (ret)
768 {
769 dprintf(CRITICAL, "Invalid device tree header \n");
770 return ret;
771 }
772
Deepa Dinamani1c970732013-04-19 14:23:01 -0700773 /* Add padding to make space for new nodes and properties. */
774 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
775 if (ret!= 0)
776 {
777 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
778 return ret;
779 }
780
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700781 /* Get offset of the memory node */
782 ret = fdt_path_offset(fdt, "/memory");
783 if (ret < 0)
784 {
785 dprintf(CRITICAL, "Could not find memory node.\n");
786 return ret;
787 }
788
789 offset = ret;
790
791 ret = target_dev_tree_mem(fdt, offset);
792 if(ret)
793 {
794 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
795 return ret;
796 }
797
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700798 /* Get offset of the chosen node */
799 ret = fdt_path_offset(fdt, "/chosen");
800 if (ret < 0)
801 {
802 dprintf(CRITICAL, "Could not find chosen node.\n");
803 return ret;
804 }
805
806 offset = ret;
807 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800808 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700809 if (ret)
810 {
811 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
812 return ret;
813 }
814
815 /* Adding the initrd-start to the chosen node */
816 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
817 if (ret)
818 {
819 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
820 return ret;
821 }
822
823 /* Adding the initrd-end to the chosen node */
824 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
825 if (ret)
826 {
827 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
828 return ret;
829 }
830
831 fdt_pack(fdt);
832
833 return ret;
834}
Maria Yuca51ee22013-06-27 21:45:24 +0800835