blob: 07c99ffdd02552a30689a4170f2c0ac962be5092 [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
vijay kumare2a5ea82014-06-25 12:24:14 +0530308 if (((uintptr_t)kernel + (uintptr_t)app_dtb_offset) < (uintptr_t)kernel) {
309 return NULL;
310 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700311 dtb = kernel + app_dtb_offset;
vijay kumare2a5ea82014-06-25 12:24:14 +0530312 while (((uintptr_t)dtb + sizeof(struct fdt_header)) < (uintptr_t)kernel_end) {
Shashank Mittalc0f10282013-07-15 14:53:31 -0700313 uint32_t dtb_soc_rev_id;
Dima Zavinc46f8382013-05-03 12:23:06 -0700314 struct fdt_header dtb_hdr;
315 uint32_t dtb_size;
Dima Zavin77e41f32013-03-06 16:10:43 -0800316
Dima Zavinc46f8382013-05-03 12:23:06 -0700317 /* the DTB could be unaligned, so extract the header,
318 * and operate on it separately */
319 memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
320 if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
vijay kumare2a5ea82014-06-25 12:24:14 +0530321 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) < (uintptr_t)dtb) ||
322 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) > (uintptr_t)kernel_end))
Dima Zavinc46f8382013-05-03 12:23:06 -0700323 break;
324 dtb_size = fdt_totalsize(&dtb_hdr);
325
vijay kumarb05eed22014-06-24 16:30:18 +0530326 if (check_aboot_addr_range_overlap(tags, dtb_size)) {
327 dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
328 return NULL;
329 }
330
Dima Zavinc46f8382013-05-03 12:23:06 -0700331 /* now that we know we have a valid DTB, we need to copy
332 * it somewhere aligned, like tags */
333 memcpy(tags, dtb, dtb_size);
334
Shashank Mittalc0f10282013-07-15 14:53:31 -0700335 dtb_soc_rev_id = dev_tree_compatible(tags);
336 if (dtb_soc_rev_id == board_soc_version()) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700337 /* clear out the old DTB magic so kernel doesn't find it */
338 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
339 return tags;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700340 } else if ((dtb_soc_rev_id != INVALID_SOC_REV_ID) &&
341 (dtb_soc_rev_id < board_soc_version())) {
342 /* if current bestmatch is less than new dtb_soc_rev_id then update
343 bestmatch_tag */
344 if((bestmatch_soc_rev_id == INVALID_SOC_REV_ID) ||
345 (bestmatch_soc_rev_id < dtb_soc_rev_id)) {
346 bestmatch_tag = dtb;
347 bestmatch_tag_size = dtb_size;
348 bestmatch_soc_rev_id = dtb_soc_rev_id;
349 }
Dima Zavin77e41f32013-03-06 16:10:43 -0800350 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700351
352 /* goto the next device tree if any */
353 dtb += dtb_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800354 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700355
Shashank Mittalc0f10282013-07-15 14:53:31 -0700356 if(bestmatch_tag) {
357 dprintf(INFO,"DTB found with bestmatch soc rev id 0x%x.Board soc rev id 0x%x\n",
358 bestmatch_soc_rev_id, board_soc_version());
359 memcpy(tags, bestmatch_tag, bestmatch_tag_size);
360 /* clear out the old DTB magic so kernel doesn't find it */
361 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
362 return tags;
363 }
364
Dima Zavinc46f8382013-05-03 12:23:06 -0700365 dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800366
Dima Zavin77e41f32013-03-06 16:10:43 -0800367 return NULL;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800368}
369
Joel Kingaa335dc2013-06-03 16:11:08 -0700370/* Returns 0 if the device tree is valid. */
Deepa Dinamani87252952013-09-09 13:58:27 -0700371int dev_tree_validate(struct dt_table *table, unsigned int page_size, uint32_t *dt_hdr_size)
Joel Kingaa335dc2013-06-03 16:11:08 -0700372{
373 int dt_entry_size;
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700374 uint64_t hdr_size;
Joel Kingaa335dc2013-06-03 16:11:08 -0700375
376 /* Validate the device tree table header */
377 if(table->magic != DEV_TREE_MAGIC) {
378 dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
379 return -1;
380 }
381
382 if (table->version == DEV_TREE_VERSION_V1) {
383 dt_entry_size = sizeof(struct dt_entry_v1);
384 } else if (table->version == DEV_TREE_VERSION_V2) {
385 dt_entry_size = sizeof(struct dt_entry);
386 } else {
387 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
388 table->version);
389 return -1;
390 }
391
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700392 hdr_size = (uint64_t)table->num_entries * dt_entry_size + DEV_TREE_HEADER_SIZE;
393
Deepa Dinamani87252952013-09-09 13:58:27 -0700394 /* Roundup to page_size. */
395 hdr_size = ROUNDUP(hdr_size, page_size);
396
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700397 if (hdr_size > UINT_MAX)
398 return -1;
399 else
400 *dt_hdr_size = hdr_size & UINT_MAX;
Joel Kingaa335dc2013-06-03 16:11:08 -0700401
402 return 0;
403}
404
Maria Yu2e2d2c22013-07-03 19:20:33 +0800405static 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 -0700406{
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800407 /*
408 * 1. Check if cur_dt_entry has platform_hw_version major & minor present?
409 * 2. If present, calculate cur_dt_target_id for the current platform as:
410 * 3. bit no |31 24 | 23 16| 15 8 |7 0|
411 * 4. |subtype| major | minor |hw_platform|
412 */
413 uint32_t cur_dt_target_id ;
Aparna Mallavarapu6eb30902014-05-13 17:03:10 +0530414 uint32_t cur_dt_hlos_subtype;
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800415
416 /*
417 * if variant_id has platform_hw_ver has major = 0xff and minor = 0xff,
418 * ignore the major & minor versions from the DTB entry
419 */
420 if ((cur_dt_entry->variant_id & 0xffff00) == 0xffff00)
421 cur_dt_target_id = (cur_dt_entry->variant_id & 0xff0000ff) | (target_variant_id & 0xffff00);
422 /*
423 * We have a valid platform_hw_version major & minor numbers in the board-id, so
424 * use the board-id from the DTB.
425 * Note: For some QRD platforms the format used is qcom, board-id = <0xMVmVPT 0xPS>
426 * where: MV: platform major ver, mV: platform minor ver, PT: platform type
427 * PS: platform subtype, so we need to put PS @ bit 24-31 to be backward compatible.
428 */
429 else
430 cur_dt_target_id = cur_dt_entry->variant_id | ((cur_dt_entry->board_hw_subtype & subtype_mask & 0xff) << 24);
Aparna Mallavarapu6eb30902014-05-13 17:03:10 +0530431 /* Determine the bits 23:8 to check the DT with the DDR Size */
432 cur_dt_hlos_subtype = (cur_dt_entry->board_hw_subtype & 0xffff00);
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800433
434 /* 1. must match the platform_id, platform_hw_id, platform_version
Maria Yu2e2d2c22013-07-03 19:20:33 +0800435 * 2. soc rev number equal then return 0
436 * 3. dt soc rev number less than cdt return -1
437 * 4. otherwise return 1
438 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800439
440 if((cur_dt_entry->platform_id == board_platform_id()) &&
Aparna Mallavarapu6eb30902014-05-13 17:03:10 +0530441 (cur_dt_target_id == target_variant_id) &&
442 (cur_dt_hlos_subtype == target_get_hlos_subtype())) {
443
Maria Yu2e2d2c22013-07-03 19:20:33 +0800444 if(cur_dt_entry->soc_rev == board_soc_version()) {
Maria Yuca51ee22013-06-27 21:45:24 +0800445 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800446 } else if(cur_dt_entry->soc_rev < board_soc_version()) {
447 return -1;
Maria Yuca51ee22013-06-27 21:45:24 +0800448 }
449 }
Maria Yuca51ee22013-06-27 21:45:24 +0800450
Maria Yu2e2d2c22013-07-03 19:20:33 +0800451 return 1;
Maria Yuca51ee22013-06-27 21:45:24 +0800452}
453
Maria Yu2e2d2c22013-07-03 19:20:33 +0800454static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info,
455 uint32_t target_variant_id, uint32_t subtype_mask)
Maria Yuca51ee22013-06-27 21:45:24 +0800456{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700457 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700458 unsigned char *table_ptr;
459 struct dt_entry dt_entry_buf_1;
460 struct dt_entry dt_entry_buf_2;
461 struct dt_entry *cur_dt_entry;
462 struct dt_entry *best_match_dt_entry;
463 struct dt_entry_v1 *dt_entry_v1;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800464 uint32_t found = 0;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700465
Joel Kingaa335dc2013-06-03 16:11:08 -0700466 if (!dt_entry_info) {
467 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
468 __func__);
469 return -1;
470 }
471
472 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
473 cur_dt_entry = &dt_entry_buf_1;
474 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700475
Maria Yu2e2d2c22013-07-03 19:20:33 +0800476 for(i = 0; found == 0 && i < table->num_entries; i++)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700477 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700478 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
479 switch(table->version) {
480 case DEV_TREE_VERSION_V1:
481 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
482 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
483 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
484 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
485 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
486 cur_dt_entry->offset = dt_entry_v1->offset;
487 cur_dt_entry->size = dt_entry_v1->size;
488 table_ptr += sizeof(struct dt_entry_v1);
489 break;
490 case DEV_TREE_VERSION_V2:
491 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
492 sizeof(struct dt_entry));
493 table_ptr += sizeof(struct dt_entry);
494 break;
495 default:
496 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
497 table->version);
498 return -1;
499 }
500
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800501 /* DTBs are stored in the ascending order of soc revision.
502 * For eg: Rev0..Rev1..Rev2 & so on.
503 * we pickup the DTB with highest soc rev number which is less
504 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700505 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800506 switch(platform_dt_match(cur_dt_entry, target_variant_id, subtype_mask)) {
507 case 0:
508 best_match_dt_entry = cur_dt_entry;
509 found = 1;
510 break;
511 case -1:
512 if (!best_match_dt_entry) {
513 /* copy structure */
David Ng618293a2013-06-25 12:29:03 -0700514 best_match_dt_entry = cur_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800515 cur_dt_entry = &dt_entry_buf_2;
516 } else {
517 /* Swap dt_entry buffers */
518 struct dt_entry *temp = cur_dt_entry;
519 cur_dt_entry = best_match_dt_entry;
520 best_match_dt_entry = temp;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700521 }
Maria Yu2e2d2c22013-07-03 19:20:33 +0800522 default:
523 break;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800524 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700525 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800526
Joel Kingaa335dc2013-06-03 16:11:08 -0700527 if (best_match_dt_entry) {
528 *dt_entry_info = *best_match_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800529 found = 1;
530 }
531
532 if (found != 0) {
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800533 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 -0700534 dt_entry_info->platform_id, dt_entry_info->soc_rev,
535 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
536 board_platform_id(), board_soc_version(),
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800537 board_target_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700538 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800539 }
540
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800541 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 -0700542 board_platform_id(), board_soc_version(),
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800543 board_target_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700544 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700545}
546
Maria Yu2e2d2c22013-07-03 19:20:33 +0800547/* Function to obtain the index information for the correct device tree
548 * based on the platform data.
549 * If a matching device tree is found, the information is returned in the
550 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
551 * a non-zero function value is returned.
552 */
553int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
554{
555 uint32_t target_variant_id;
556
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800557 target_variant_id = board_target_id();
558 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
559 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800560 }
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800561
Maria Yu2e2d2c22013-07-03 19:20:33 +0800562 /*
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800563 * for compatible with version 1 and version 2 dtbtool
564 * will compare the subtype inside the variant id
565 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800566 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
567 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
568 return 0;
569 }
570
571 /*
572 * add compatible with old device selection method which don't compare subtype
573 */
574 target_variant_id = board_hardware_id();
575 return __dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0);
576}
577
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700578/* Function to add the first RAM partition info to the device tree.
579 * Note: The function replaces the reg property in the "/memory" node
580 * with the addr and size provided.
581 */
582int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
583{
584 int ret;
585
586 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
587
588 if (ret)
589 {
590 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
591 ret);
592 }
593
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700594 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
595
596 if (ret)
597 {
598 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
599 ret);
600 }
601
602 return ret;
603}
604
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700605static 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 -0700606{
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700607 int len;
608 uint32_t *valp;
609 int ret;
610 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700611
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700612 mem_node->offset = mem_node_offset;
613
614 /* Get offset of the root node */
615 ret = fdt_path_offset(fdt, "/");
616 if (ret < 0)
617 {
618 dprintf(CRITICAL, "Could not find memory node.\n");
619 return ret;
620 }
621
622 offset = ret;
623
624 /* Find the #address-cells size. */
625 valp = (uint32_t*)fdt_getprop(fdt, offset, "#address-cells", &len);
Lijuan Gaoae0927b2014-07-18 17:16:16 +0800626 if (len <= 0 || !valp)
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700627 {
628 if (len == -FDT_ERR_NOTFOUND)
629 {
630 /* Property not found.
631 * Assume standard sizes.
632 */
633 mem_node->addr_cell_size = 2;
634 dprintf(CRITICAL, "Using default #addr_cell_size: %u\n", mem_node->addr_cell_size);
635 }
636 else
637 {
638 dprintf(CRITICAL, "Error finding the #address-cells property\n");
639 return len;
640 }
641 }
642 else
643 mem_node->addr_cell_size = fdt32_to_cpu(*valp);
644
645 /* Find the #size-cells size. */
646 valp = (uint32_t*)fdt_getprop(fdt, offset, "#size-cells", &len);
Lijuan Gaoae0927b2014-07-18 17:16:16 +0800647 if (len <= 0 || !valp)
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700648 {
649 if (len == -FDT_ERR_NOTFOUND)
650 {
651 /* Property not found.
652 * Assume standard sizes.
653 */
654 mem_node->size_cell_size = 1;
655 dprintf(CRITICAL, "Using default #size_cell_size: %u\n", mem_node->size_cell_size);
656 }
657 else
658 {
659 dprintf(CRITICAL, "Error finding the #size-cells property\n");
660 return len;
661 }
662 }
663 else
664 mem_node->size_cell_size = fdt32_to_cpu(*valp);
665
666 return 0;
667}
668
669static void dev_tree_update_memory_node(uint32_t offset)
670{
671 mem_node.offset = offset;
672 mem_node.addr_cell_size = 1;
673 mem_node.size_cell_size = 1;
674}
675
676/* Function to add the subsequent RAM partition info to the device tree. */
677int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint64_t addr, uint64_t size)
678{
679 int ret = 0;
680
681 if(smem_get_ram_ptable_version() >= 1)
682 {
683 ret = dev_tree_query_memory_cell_sizes(fdt, &mem_node, offset);
684 if (ret < 0)
685 {
686 dprintf(CRITICAL, "Could not find #address-cells and #size-cells properties: ret %d\n", ret);
687 return ret;
688 }
689
690 }
691 else
692 {
693 dev_tree_update_memory_node(offset);
694 }
695
696 if (!(mem_node.mem_info_cnt))
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700697 {
698 /* Replace any other reg prop in the memory node. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700699
700 /* cell_size is the number of 32 bit words used to represent an address/length in the device tree.
701 * memory node in DT can be either 32-bit(cell-size = 1) or 64-bit(cell-size = 2).So when updating
702 * the memory node in the device tree, we write one word or two words based on cell_size = 1 or 2.
703 */
704
705 if(mem_node.addr_cell_size == 2)
706 {
707 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
708 if(ret)
709 {
710 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
711 return ret;
712 }
713
714 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
715 if(ret)
716 {
717 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
718 return ret;
719 }
720 }
721 else
722 {
723 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
724 if(ret)
725 {
726 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
727 return ret;
728 }
729 }
730
731 mem_node.mem_info_cnt = 1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700732 }
733 else
734 {
735 /* Append the mem info to the reg prop for subsequent nodes. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700736 if(mem_node.addr_cell_size == 2)
737 {
738 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
739 if(ret)
740 {
741 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
742 return ret;
743 }
744 }
745
746 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
747 if(ret)
748 {
749 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
750 return ret;
751 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700752 }
753
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700754 if(mem_node.size_cell_size == 2)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700755 {
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700756 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", size>>32);
757 if(ret)
758 {
759 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
760 return ret;
761 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700762 }
763
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700764 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700765
766 if (ret)
767 {
768 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
769 ret);
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700770 return ret;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700771 }
772
773 return ret;
774}
775
776/* Top level function that updates the device tree. */
777int update_device_tree(void *fdt, const char *cmdline,
778 void *ramdisk, uint32_t ramdisk_size)
779{
780 int ret = 0;
781 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700782
783 /* Check the device tree header */
784 ret = fdt_check_header(fdt);
785 if (ret)
786 {
787 dprintf(CRITICAL, "Invalid device tree header \n");
788 return ret;
789 }
790
Deepa Dinamani1c970732013-04-19 14:23:01 -0700791 /* Add padding to make space for new nodes and properties. */
792 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
793 if (ret!= 0)
794 {
795 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
796 return ret;
797 }
798
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700799 /* Get offset of the memory node */
800 ret = fdt_path_offset(fdt, "/memory");
801 if (ret < 0)
802 {
803 dprintf(CRITICAL, "Could not find memory node.\n");
804 return ret;
805 }
806
807 offset = ret;
808
809 ret = target_dev_tree_mem(fdt, offset);
810 if(ret)
811 {
812 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
813 return ret;
814 }
815
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700816 /* Get offset of the chosen node */
817 ret = fdt_path_offset(fdt, "/chosen");
818 if (ret < 0)
819 {
820 dprintf(CRITICAL, "Could not find chosen node.\n");
821 return ret;
822 }
823
824 offset = ret;
Maria Yuabde9542014-07-07 14:49:34 +0800825 if (cmdline)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700826 {
Maria Yuabde9542014-07-07 14:49:34 +0800827 /* Adding the cmdline to the chosen node */
828 ret = fdt_appendprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
829 if (ret)
830 {
831 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
832 return ret;
833 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700834 }
835
Joonwoo Parka5b5f492014-02-13 18:24:48 -0800836 if (ramdisk_size) {
837 /* Adding the initrd-start to the chosen node */
838 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start",
839 (uint32_t)ramdisk);
840 if (ret)
841 {
842 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
843 return ret;
844 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700845
Joonwoo Parka5b5f492014-02-13 18:24:48 -0800846 /* Adding the initrd-end to the chosen node */
847 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end",
848 ((uint32_t)ramdisk + ramdisk_size));
849 if (ret)
850 {
851 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
852 return ret;
853 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700854 }
855
856 fdt_pack(fdt);
857
858 return ret;
859}
Maria Yuca51ee22013-06-27 21:45:24 +0800860