blob: 0ec55a5e3edffda37ad6f4408b9f8aa3ffc0fb79 [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
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -070050static int platform_dt_match(struct dt_entry *cur_dt_entry, struct board_dt_entry *board_dt_data, 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;
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -070084 struct board_dt_entry board_dt_data;
Dima Zavinc46f8382013-05-03 12:23:06 -070085
86 root_offset = fdt_path_offset(dtb, "/");
87 if (root_offset < 0)
88 return false;
89
90 prop = fdt_getprop(dtb, root_offset, "model", &len);
91 if (prop && len > 0) {
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070092 model = (char *) malloc(sizeof(char) * len);
93 ASSERT(model);
94 strlcpy(model, prop, len);
Dima Zavinc46f8382013-05-03 12:23:06 -070095 } else {
96 model[0] = '\0';
97 }
98
Channagoud Kadabia4dbe332013-09-05 17:44:11 -070099 /* Find the board-id prop from DTB , if board-id is present then
100 * the DTB is version 2 */
101 board_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,board-id", &len_board_id);
102 if (board_prop)
103 {
104 dtb_ver = DEV_TREE_VERSION_V2;
105 min_plat_id_len = PLAT_ID_SIZE;
106 }
107 else
108 {
109 dtb_ver = DEV_TREE_VERSION_V1;
110 min_plat_id_len = DT_ENTRY_V1_SIZE;
111 }
112
113 /* Get the msm-id prop from DTB */
114 plat_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,msm-id", &len_plat_id);
115 if (!plat_prop || len_plat_id <= 0) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700116 dprintf(INFO, "qcom,msm-id entry not found\n");
117 return false;
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700118 } else if (len_plat_id % min_plat_id_len) {
119 dprintf(INFO, "qcom,msm-id in device tree is (%d) not a multiple of (%d)\n",
120 len_plat_id, min_plat_id_len);
Dima Zavinc46f8382013-05-03 12:23:06 -0700121 return false;
122 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700123
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700124 /*
125 * If DTB version is '1' look for <x y z> pair in the DTB
126 * x: platform_id
127 * y: variant_id
128 * z: SOC rev
129 */
130 if (dtb_ver == DEV_TREE_VERSION_V1)
131 {
132 while (len_plat_id)
133 {
134 cur_dt_entry.platform_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->platform_id);
135 cur_dt_entry.variant_id = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->variant_id);
136 cur_dt_entry.soc_rev = fdt32_to_cpu(((const struct dt_entry_v1 *)plat_prop)->soc_rev);
137 cur_dt_entry.board_hw_subtype = board_hardware_subtype();
Dima Zavinc46f8382013-05-03 12:23:06 -0700138
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700139 board_dt_data.target_variant_id = board_hardware_id();
140 board_dt_data.platform_variant_id = board_platform_id();
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700141
142 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u 0x%x)\n",
143 *model ? model : "unknown",
144 cur_dt_entry.platform_id, cur_dt_entry.variant_id, cur_dt_entry.soc_rev);
145
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700146 if (platform_dt_match(&cur_dt_entry, &board_dt_data, 0) == 1)
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700147 {
148 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u 0x%x> != <%u %u 0x%x>\n",
149 cur_dt_entry.platform_id,
150 cur_dt_entry.variant_id,
151 cur_dt_entry.soc_rev,
152 board_platform_id(),
153 board_hardware_id(),
154 board_soc_version());
155 plat_prop += DT_ENTRY_V1_SIZE;
156 len_plat_id -= DT_ENTRY_V1_SIZE;
157 continue;
158 }
159 else
160 {
161 found = 1;
162 break;
163 }
164 }
165 }
166 /*
167 * If DTB Version is '2' then we have split DTB with board & msm data
168 * populated saperately in board-id & msm-id prop respectively.
169 * Extract the data & prepare a look up table
170 */
171 else if (dtb_ver == DEV_TREE_VERSION_V2)
172 {
173 board_data_count = (len_board_id / BOARD_ID_SIZE);
174 msm_data_count = (len_plat_id / PLAT_ID_SIZE);
175
176 /* If we are using dtb v2.0, then we have split board & msm data in the DTB */
177 board_data = (struct board_id *) malloc(sizeof(struct board_id) * (len_board_id / BOARD_ID_SIZE));
178 ASSERT(board_data);
179 platform_data = (struct plat_id *) malloc(sizeof(struct plat_id) * (len_plat_id / PLAT_ID_SIZE));
180 ASSERT(platform_data);
181 i = 0;
182
183 /* Extract board data from DTB */
184 for(i = 0 ; i < board_data_count; i++)
185 {
186 board_data[i].variant_id = fdt32_to_cpu(((struct board_id *)board_prop)->variant_id);
187 board_data[i].platform_subtype = fdt32_to_cpu(((struct board_id *)board_prop)->platform_subtype);
188 len_board_id -= sizeof(struct board_id);
189 board_prop += sizeof(struct board_id);
190 }
191
192 /* Extract platform data from DTB */
193 for(i = 0 ; i < msm_data_count; i++)
194 {
195 platform_data[i].platform_id = fdt32_to_cpu(((struct plat_id *)plat_prop)->platform_id);
196 platform_data[i].soc_rev = fdt32_to_cpu(((struct plat_id *)plat_prop)->soc_rev);
197 len_plat_id -= sizeof(struct plat_id);
198 plat_prop += sizeof(struct plat_id);
199 }
200
201 /* We need to merge board & platform data into dt entry structure */
202 num_entries = msm_data_count * board_data_count;
203 dt_entry_v2 = (struct dt_entry*) malloc(sizeof(struct dt_entry) * num_entries);
204 ASSERT(dt_entry_v2);
205
206 /* If we have '<X>; <Y>; <Z>' as platform data & '<A>; <B>; <C>' as board data.
207 * Then dt entry should look like
208 * <X ,A >;<X, B>;<X, C>;
209 * <Y ,A >;<Y, B>;<Y, C>;
210 * <Z ,A >;<Z, B>;<Z, C>;
211 */
212 i = 0;
213 k = 0;
214 for (i = 0; i < msm_data_count; i++)
215 {
216 for (j = 0; j < board_data_count; j++)
217 {
218 dt_entry_v2[k].platform_id = platform_data[i].platform_id;
219 dt_entry_v2[k].soc_rev = platform_data[i].soc_rev;
220 dt_entry_v2[k].variant_id = board_data[j].variant_id;
221 dt_entry_v2[k].board_hw_subtype = board_data[j].platform_subtype;
222 k++;
223 }
224 }
225
226 /* Now find the matching entry in the merged list */
227 if (board_hardware_id() == HW_PLATFORM_QRD)
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700228 {
229 board_dt_data.target_variant_id = board_target_id();
230 board_dt_data.platform_variant_id = board_platform_id();
231 }
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700232 else
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700233 {
234 board_dt_data.target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
235 board_dt_data.platform_variant_id = board_platform_id();
236 }
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700237
238 for (i=0 ;i < num_entries; i++)
239 {
240 dprintf(SPEW, "Found an appended flattened device tree (%s - %u %u %u 0x%x)\n",
241 *model ? model : "unknown",
242 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);
243
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700244 if (platform_dt_match(&dt_entry_v2[i], &board_dt_data, 0xff) == 1)
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700245 {
246 dprintf(SPEW, "Device tree's msm_id doesn't match the board: <%u %u %u 0x%x> != <%u %u %u 0x%x>\n",
247 dt_entry_v2[i].platform_id,
248 dt_entry_v2[i].variant_id,
249 dt_entry_v2[i].soc_rev,
250 dt_entry_v2[i].board_hw_subtype,
251 board_platform_id(),
252 board_hardware_id(),
253 board_hardware_subtype(),
254 board_soc_version());
255 continue;
256 }
257 else
258 {
259 /* If found a match, return the cur_dt_entry */
260 found = 1;
261 cur_dt_entry = dt_entry_v2[i];
262 break;
263 }
264 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700265 }
266
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700267 if (!found)
268 {
269 soc_rev = INVALID_SOC_REV_ID;
270 goto end;
271 }
272 else
273 soc_rev = cur_dt_entry.soc_rev;
274
275 dprintf(INFO, "Device tree's msm_id matches the board: <%u %u %u 0x%x> == <%u %u %u 0x%x>\n",
276 cur_dt_entry.platform_id,
277 cur_dt_entry.variant_id,
278 cur_dt_entry.board_hw_subtype,
279 cur_dt_entry.soc_rev,
Shashank Mittalc0f10282013-07-15 14:53:31 -0700280 board_platform_id(),
281 board_hardware_id(),
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700282 board_hardware_subtype(),
Shashank Mittalc0f10282013-07-15 14:53:31 -0700283 board_soc_version());
Channagoud Kadabia4dbe332013-09-05 17:44:11 -0700284
285end:
286 free(board_data);
287 free(platform_data);
288 free(dt_entry_v2);
289 free(model);
290
291 return soc_rev;
Dima Zavinc46f8382013-05-03 12:23:06 -0700292}
293
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800294/*
Dima Zavin77e41f32013-03-06 16:10:43 -0800295 * Will relocate the DTB to the tags addr if the device tree is found and return
296 * its address
297 *
298 * Arguments: kernel - Start address of the kernel loaded in RAM
299 * tags - Start address of the tags loaded in RAM
Channagoud Kadabi704cd562013-04-25 15:19:59 -0700300 * kernel_size - Size of the kernel in bytes
301 *
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800302 * Return Value: DTB address : If appended device tree is found
Dima Zavin77e41f32013-03-06 16:10:43 -0800303 * 'NULL' : Otherwise
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800304 */
Dima Zavinc46f8382013-05-03 12:23:06 -0700305void *dev_tree_appended(void *kernel, uint32_t kernel_size, void *tags)
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800306{
Dima Zavinc46f8382013-05-03 12:23:06 -0700307 void *kernel_end = kernel + kernel_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800308 uint32_t app_dtb_offset = 0;
Dima Zavinc46f8382013-05-03 12:23:06 -0700309 void *dtb;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700310 void *bestmatch_tag = NULL;
311 uint32_t bestmatch_tag_size;
312 uint32_t bestmatch_soc_rev_id = INVALID_SOC_REV_ID;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800313
314 memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800315
vijay kumare2a5ea82014-06-25 12:24:14 +0530316 if (((uintptr_t)kernel + (uintptr_t)app_dtb_offset) < (uintptr_t)kernel) {
317 return NULL;
318 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700319 dtb = kernel + app_dtb_offset;
vijay kumare2a5ea82014-06-25 12:24:14 +0530320 while (((uintptr_t)dtb + sizeof(struct fdt_header)) < (uintptr_t)kernel_end) {
Shashank Mittalc0f10282013-07-15 14:53:31 -0700321 uint32_t dtb_soc_rev_id;
Dima Zavinc46f8382013-05-03 12:23:06 -0700322 struct fdt_header dtb_hdr;
323 uint32_t dtb_size;
Dima Zavin77e41f32013-03-06 16:10:43 -0800324
Dima Zavinc46f8382013-05-03 12:23:06 -0700325 /* the DTB could be unaligned, so extract the header,
326 * and operate on it separately */
327 memcpy(&dtb_hdr, dtb, sizeof(struct fdt_header));
328 if (fdt_check_header((const void *)&dtb_hdr) != 0 ||
vijay kumare2a5ea82014-06-25 12:24:14 +0530329 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) < (uintptr_t)dtb) ||
330 ((uintptr_t)dtb + (uintptr_t)fdt_totalsize((const void *)&dtb_hdr) > (uintptr_t)kernel_end))
Dima Zavinc46f8382013-05-03 12:23:06 -0700331 break;
332 dtb_size = fdt_totalsize(&dtb_hdr);
333
vijay kumarb05eed22014-06-24 16:30:18 +0530334 if (check_aboot_addr_range_overlap(tags, dtb_size)) {
335 dprintf(CRITICAL, "Tags addresses overlap with aboot addresses.\n");
336 return NULL;
337 }
338
Dima Zavinc46f8382013-05-03 12:23:06 -0700339 /* now that we know we have a valid DTB, we need to copy
340 * it somewhere aligned, like tags */
341 memcpy(tags, dtb, dtb_size);
342
Shashank Mittalc0f10282013-07-15 14:53:31 -0700343 dtb_soc_rev_id = dev_tree_compatible(tags);
344 if (dtb_soc_rev_id == board_soc_version()) {
Dima Zavinc46f8382013-05-03 12:23:06 -0700345 /* clear out the old DTB magic so kernel doesn't find it */
346 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
347 return tags;
Shashank Mittalc0f10282013-07-15 14:53:31 -0700348 } else if ((dtb_soc_rev_id != INVALID_SOC_REV_ID) &&
349 (dtb_soc_rev_id < board_soc_version())) {
350 /* if current bestmatch is less than new dtb_soc_rev_id then update
351 bestmatch_tag */
352 if((bestmatch_soc_rev_id == INVALID_SOC_REV_ID) ||
353 (bestmatch_soc_rev_id < dtb_soc_rev_id)) {
354 bestmatch_tag = dtb;
355 bestmatch_tag_size = dtb_size;
356 bestmatch_soc_rev_id = dtb_soc_rev_id;
357 }
Dima Zavin77e41f32013-03-06 16:10:43 -0800358 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700359
360 /* goto the next device tree if any */
361 dtb += dtb_size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800362 }
Dima Zavinc46f8382013-05-03 12:23:06 -0700363
Shashank Mittalc0f10282013-07-15 14:53:31 -0700364 if(bestmatch_tag) {
365 dprintf(INFO,"DTB found with bestmatch soc rev id 0x%x.Board soc rev id 0x%x\n",
366 bestmatch_soc_rev_id, board_soc_version());
367 memcpy(tags, bestmatch_tag, bestmatch_tag_size);
368 /* clear out the old DTB magic so kernel doesn't find it */
369 *((uint32_t *)(kernel + app_dtb_offset)) = 0;
370 return tags;
371 }
372
Dima Zavinc46f8382013-05-03 12:23:06 -0700373 dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800374
Dima Zavin77e41f32013-03-06 16:10:43 -0800375 return NULL;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800376}
377
Joel Kingaa335dc2013-06-03 16:11:08 -0700378/* Returns 0 if the device tree is valid. */
Deepa Dinamani87252952013-09-09 13:58:27 -0700379int dev_tree_validate(struct dt_table *table, unsigned int page_size, uint32_t *dt_hdr_size)
Joel Kingaa335dc2013-06-03 16:11:08 -0700380{
381 int dt_entry_size;
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700382 uint64_t hdr_size;
Joel Kingaa335dc2013-06-03 16:11:08 -0700383
384 /* Validate the device tree table header */
385 if(table->magic != DEV_TREE_MAGIC) {
386 dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
387 return -1;
388 }
389
390 if (table->version == DEV_TREE_VERSION_V1) {
391 dt_entry_size = sizeof(struct dt_entry_v1);
392 } else if (table->version == DEV_TREE_VERSION_V2) {
393 dt_entry_size = sizeof(struct dt_entry);
394 } else {
395 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
396 table->version);
397 return -1;
398 }
399
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700400 hdr_size = (uint64_t)table->num_entries * dt_entry_size + DEV_TREE_HEADER_SIZE;
401
Deepa Dinamani87252952013-09-09 13:58:27 -0700402 /* Roundup to page_size. */
403 hdr_size = ROUNDUP(hdr_size, page_size);
404
Channagoud Kadabid87c2772014-06-20 15:41:55 -0700405 if (hdr_size > UINT_MAX)
406 return -1;
407 else
408 *dt_hdr_size = hdr_size & UINT_MAX;
Joel Kingaa335dc2013-06-03 16:11:08 -0700409
410 return 0;
411}
412
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700413static int platform_dt_match(struct dt_entry *cur_dt_entry, struct board_dt_entry *board_dt_data, uint32_t subtype_mask)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700414{
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800415 /*
416 * 1. Check if cur_dt_entry has platform_hw_version major & minor present?
417 * 2. If present, calculate cur_dt_target_id for the current platform as:
418 * 3. bit no |31 24 | 23 16| 15 8 |7 0|
419 * 4. |subtype| major | minor |hw_platform|
420 */
421 uint32_t cur_dt_target_id ;
Aparna Mallavarapu6eb30902014-05-13 17:03:10 +0530422 uint32_t cur_dt_hlos_subtype;
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800423
424 /*
425 * if variant_id has platform_hw_ver has major = 0xff and minor = 0xff,
426 * ignore the major & minor versions from the DTB entry
427 */
428 if ((cur_dt_entry->variant_id & 0xffff00) == 0xffff00)
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700429 cur_dt_target_id = (cur_dt_entry->variant_id & 0xff0000ff) | (board_dt_data->target_variant_id & 0xffff00);
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800430 /*
431 * We have a valid platform_hw_version major & minor numbers in the board-id, so
432 * use the board-id from the DTB.
433 * Note: For some QRD platforms the format used is qcom, board-id = <0xMVmVPT 0xPS>
434 * where: MV: platform major ver, mV: platform minor ver, PT: platform type
435 * PS: platform subtype, so we need to put PS @ bit 24-31 to be backward compatible.
436 */
437 else
438 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 +0530439 /* Determine the bits 23:8 to check the DT with the DDR Size */
440 cur_dt_hlos_subtype = (cur_dt_entry->board_hw_subtype & 0xffff00);
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800441
442 /* 1. must match the platform_id, platform_hw_id, platform_version
Maria Yu2e2d2c22013-07-03 19:20:33 +0800443 * 2. soc rev number equal then return 0
444 * 3. dt soc rev number less than cdt return -1
445 * 4. otherwise return 1
446 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800447
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700448 if((cur_dt_entry->platform_id == board_dt_data->platform_variant_id) &&
449 (cur_dt_target_id == board_dt_data->target_variant_id) &&
Aparna Mallavarapu6eb30902014-05-13 17:03:10 +0530450 (cur_dt_hlos_subtype == target_get_hlos_subtype())) {
Maria Yu2e2d2c22013-07-03 19:20:33 +0800451 if(cur_dt_entry->soc_rev == board_soc_version()) {
Maria Yuca51ee22013-06-27 21:45:24 +0800452 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800453 } else if(cur_dt_entry->soc_rev < board_soc_version()) {
454 return -1;
Maria Yuca51ee22013-06-27 21:45:24 +0800455 }
456 }
Maria Yuca51ee22013-06-27 21:45:24 +0800457
Maria Yu2e2d2c22013-07-03 19:20:33 +0800458 return 1;
Maria Yuca51ee22013-06-27 21:45:24 +0800459}
460
Maria Yu2e2d2c22013-07-03 19:20:33 +0800461static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info,
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700462 struct board_dt_entry *board_dt_data, uint32_t subtype_mask)
Maria Yuca51ee22013-06-27 21:45:24 +0800463{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700464 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700465 unsigned char *table_ptr;
466 struct dt_entry dt_entry_buf_1;
467 struct dt_entry dt_entry_buf_2;
468 struct dt_entry *cur_dt_entry;
469 struct dt_entry *best_match_dt_entry;
470 struct dt_entry_v1 *dt_entry_v1;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800471 uint32_t found = 0;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700472
Joel Kingaa335dc2013-06-03 16:11:08 -0700473 if (!dt_entry_info) {
474 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
475 __func__);
476 return -1;
477 }
478
479 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
480 cur_dt_entry = &dt_entry_buf_1;
481 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700482
Maria Yu2e2d2c22013-07-03 19:20:33 +0800483 for(i = 0; found == 0 && i < table->num_entries; i++)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700484 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700485 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
486 switch(table->version) {
487 case DEV_TREE_VERSION_V1:
488 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
489 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
490 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
491 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
492 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
493 cur_dt_entry->offset = dt_entry_v1->offset;
494 cur_dt_entry->size = dt_entry_v1->size;
495 table_ptr += sizeof(struct dt_entry_v1);
496 break;
497 case DEV_TREE_VERSION_V2:
498 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
499 sizeof(struct dt_entry));
500 table_ptr += sizeof(struct dt_entry);
501 break;
502 default:
503 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
504 table->version);
505 return -1;
506 }
507
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800508 /* DTBs are stored in the ascending order of soc revision.
509 * For eg: Rev0..Rev1..Rev2 & so on.
510 * we pickup the DTB with highest soc rev number which is less
511 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700512 */
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700513 switch(platform_dt_match(cur_dt_entry, board_dt_data, subtype_mask)) {
Maria Yu2e2d2c22013-07-03 19:20:33 +0800514 case 0:
515 best_match_dt_entry = cur_dt_entry;
516 found = 1;
517 break;
518 case -1:
519 if (!best_match_dt_entry) {
520 /* copy structure */
David Ng618293a2013-06-25 12:29:03 -0700521 best_match_dt_entry = cur_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800522 cur_dt_entry = &dt_entry_buf_2;
523 } else {
524 /* Swap dt_entry buffers */
525 struct dt_entry *temp = cur_dt_entry;
526 cur_dt_entry = best_match_dt_entry;
527 best_match_dt_entry = temp;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700528 }
Maria Yu2e2d2c22013-07-03 19:20:33 +0800529 default:
530 break;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800531 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700532 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800533
Joel Kingaa335dc2013-06-03 16:11:08 -0700534 if (best_match_dt_entry) {
535 *dt_entry_info = *best_match_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800536 found = 1;
537 }
538
539 if (found != 0) {
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700540 dprintf(INFO, "Using DTB entry 0x%08x/%08x/0x%08x/%u for device 0x%08x/%08x/0x%08x/%u\n",
David Ng618293a2013-06-25 12:29:03 -0700541 dt_entry_info->platform_id, dt_entry_info->soc_rev,
542 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700543 board_dt_data->platform_variant_id, board_soc_version(),
544 board_dt_data->target_variant_id, board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700545 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800546 }
547
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700548 dprintf(CRITICAL, "INFO: Unable to find suitable device tree for device (0x%08x/0x%08x/0x%08x/%u)\n",
549 board_dt_data->platform_variant_id, board_soc_version(),
550 board_dt_data->target_variant_id, board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700551 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700552}
553
Maria Yu2e2d2c22013-07-03 19:20:33 +0800554/* Function to obtain the index information for the correct device tree
555 * based on the platform data.
556 * If a matching device tree is found, the information is returned in the
557 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
558 * a non-zero function value is returned.
559 */
560int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
561{
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700562 struct board_dt_entry board_dt_data;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800563
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700564 /* 1. Look for new board-id (platform version + hw + subtype) & new msm-id (soc ver + soc id + foundry-id) */
565 board_dt_data.target_variant_id = board_target_id();
566 /* Platform-id
567 * bit no |31 24|23 16|15 0|
568 * |reserved|foundry-id|msm-id|
569 */
570 board_dt_data.platform_variant_id = board_platform_id() | (board_foundry_id() << 16);
571 if (__dev_tree_get_entry_info(table, dt_entry_info, &board_dt_data, 0xff) == 0) {
572 return 0;
573 }
574
575 /* 2. Look for new board-id & old msm-id (no foundry-id) */
576 board_dt_data.target_variant_id = board_target_id();
577 board_dt_data.platform_variant_id = board_platform_id();
578 if (__dev_tree_get_entry_info(table, dt_entry_info, &board_dt_data, 0xff) == 0) {
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800579 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800580 }
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800581
Maria Yu2e2d2c22013-07-03 19:20:33 +0800582 /*
Channagoud Kadabi571193a2014-02-05 13:58:49 -0800583 * for compatible with version 1 and version 2 dtbtool
584 * will compare the subtype inside the variant id
585 */
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700586
587 /* 3. Look for old board-id (no platform version) & new msm-id (with foundry-id) */
588 board_dt_data.target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
589 board_dt_data.platform_variant_id = board_platform_id() | (board_foundry_id() << 16);
590 if (__dev_tree_get_entry_info(table, dt_entry_info, &board_dt_data, 0xff) == 0) {
591 return 0;
592 }
593 /* 4. Look for old board-id (no platform versions) & old msm-id(no foundry-id) */
594 board_dt_data.target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
595 board_dt_data.platform_variant_id = board_platform_id();
596 if (__dev_tree_get_entry_info(table, dt_entry_info, &board_dt_data, 0xff) == 0) {
Maria Yu2e2d2c22013-07-03 19:20:33 +0800597 return 0;
598 }
599
600 /*
601 * add compatible with old device selection method which don't compare subtype
602 */
Sundarajan Srinivasan763c0db2014-05-20 17:08:36 -0700603 board_dt_data.target_variant_id = board_hardware_id();
604 board_dt_data.platform_variant_id = board_platform_id();
605 return __dev_tree_get_entry_info(table, dt_entry_info, &board_dt_data, 0);
Maria Yu2e2d2c22013-07-03 19:20:33 +0800606}
607
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700608/* Function to add the first RAM partition info to the device tree.
609 * Note: The function replaces the reg property in the "/memory" node
610 * with the addr and size provided.
611 */
612int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
613{
614 int ret;
615
616 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
617
618 if (ret)
619 {
620 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
621 ret);
622 }
623
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700624 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
625
626 if (ret)
627 {
628 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
629 ret);
630 }
631
632 return ret;
633}
634
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700635static 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 -0700636{
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700637 int len;
638 uint32_t *valp;
639 int ret;
640 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700641
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700642 mem_node->offset = mem_node_offset;
643
644 /* Get offset of the root node */
645 ret = fdt_path_offset(fdt, "/");
646 if (ret < 0)
647 {
648 dprintf(CRITICAL, "Could not find memory node.\n");
649 return ret;
650 }
651
652 offset = ret;
653
654 /* Find the #address-cells size. */
655 valp = (uint32_t*)fdt_getprop(fdt, offset, "#address-cells", &len);
Lijuan Gaoae0927b2014-07-18 17:16:16 +0800656 if (len <= 0 || !valp)
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700657 {
658 if (len == -FDT_ERR_NOTFOUND)
659 {
660 /* Property not found.
661 * Assume standard sizes.
662 */
663 mem_node->addr_cell_size = 2;
664 dprintf(CRITICAL, "Using default #addr_cell_size: %u\n", mem_node->addr_cell_size);
665 }
666 else
667 {
668 dprintf(CRITICAL, "Error finding the #address-cells property\n");
669 return len;
670 }
671 }
672 else
673 mem_node->addr_cell_size = fdt32_to_cpu(*valp);
674
675 /* Find the #size-cells size. */
676 valp = (uint32_t*)fdt_getprop(fdt, offset, "#size-cells", &len);
Lijuan Gaoae0927b2014-07-18 17:16:16 +0800677 if (len <= 0 || !valp)
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700678 {
679 if (len == -FDT_ERR_NOTFOUND)
680 {
681 /* Property not found.
682 * Assume standard sizes.
683 */
684 mem_node->size_cell_size = 1;
685 dprintf(CRITICAL, "Using default #size_cell_size: %u\n", mem_node->size_cell_size);
686 }
687 else
688 {
689 dprintf(CRITICAL, "Error finding the #size-cells property\n");
690 return len;
691 }
692 }
693 else
694 mem_node->size_cell_size = fdt32_to_cpu(*valp);
695
696 return 0;
697}
698
699static void dev_tree_update_memory_node(uint32_t offset)
700{
701 mem_node.offset = offset;
702 mem_node.addr_cell_size = 1;
703 mem_node.size_cell_size = 1;
704}
705
706/* Function to add the subsequent RAM partition info to the device tree. */
707int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint64_t addr, uint64_t size)
708{
709 int ret = 0;
710
711 if(smem_get_ram_ptable_version() >= 1)
712 {
713 ret = dev_tree_query_memory_cell_sizes(fdt, &mem_node, offset);
714 if (ret < 0)
715 {
716 dprintf(CRITICAL, "Could not find #address-cells and #size-cells properties: ret %d\n", ret);
717 return ret;
718 }
719
720 }
721 else
722 {
723 dev_tree_update_memory_node(offset);
724 }
725
726 if (!(mem_node.mem_info_cnt))
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700727 {
728 /* Replace any other reg prop in the memory node. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700729
730 /* cell_size is the number of 32 bit words used to represent an address/length in the device tree.
731 * memory node in DT can be either 32-bit(cell-size = 1) or 64-bit(cell-size = 2).So when updating
732 * the memory node in the device tree, we write one word or two words based on cell_size = 1 or 2.
733 */
734
735 if(mem_node.addr_cell_size == 2)
736 {
737 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
738 if(ret)
739 {
740 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
741 return ret;
742 }
743
744 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
745 if(ret)
746 {
747 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
748 return ret;
749 }
750 }
751 else
752 {
753 ret = fdt_setprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
754 if(ret)
755 {
756 dprintf(CRITICAL, "ERROR: Could not set prop reg for memory node\n");
757 return ret;
758 }
759 }
760
761 mem_node.mem_info_cnt = 1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700762 }
763 else
764 {
765 /* Append the mem info to the reg prop for subsequent nodes. */
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700766 if(mem_node.addr_cell_size == 2)
767 {
768 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", addr >> 32);
769 if(ret)
770 {
771 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
772 return ret;
773 }
774 }
775
776 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)addr);
777 if(ret)
778 {
779 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
780 return ret;
781 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700782 }
783
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700784 if(mem_node.size_cell_size == 2)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700785 {
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700786 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", size>>32);
787 if(ret)
788 {
789 dprintf(CRITICAL, "ERROR: Could not append prop reg for memory node\n");
790 return ret;
791 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700792 }
793
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700794 ret = fdt_appendprop_u32(fdt, mem_node.offset, "reg", (uint32_t)size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700795
796 if (ret)
797 {
798 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
799 ret);
Sundarajan Srinivasan44de4342013-07-08 14:47:13 -0700800 return ret;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700801 }
802
803 return ret;
804}
805
806/* Top level function that updates the device tree. */
807int update_device_tree(void *fdt, const char *cmdline,
808 void *ramdisk, uint32_t ramdisk_size)
809{
810 int ret = 0;
811 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700812
813 /* Check the device tree header */
814 ret = fdt_check_header(fdt);
815 if (ret)
816 {
817 dprintf(CRITICAL, "Invalid device tree header \n");
818 return ret;
819 }
820
Deepa Dinamani1c970732013-04-19 14:23:01 -0700821 /* Add padding to make space for new nodes and properties. */
822 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
823 if (ret!= 0)
824 {
825 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
826 return ret;
827 }
828
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700829 /* Get offset of the memory node */
830 ret = fdt_path_offset(fdt, "/memory");
831 if (ret < 0)
832 {
833 dprintf(CRITICAL, "Could not find memory node.\n");
834 return ret;
835 }
836
837 offset = ret;
838
839 ret = target_dev_tree_mem(fdt, offset);
840 if(ret)
841 {
842 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
843 return ret;
844 }
845
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700846 /* Get offset of the chosen node */
847 ret = fdt_path_offset(fdt, "/chosen");
848 if (ret < 0)
849 {
850 dprintf(CRITICAL, "Could not find chosen node.\n");
851 return ret;
852 }
853
854 offset = ret;
855 /* Adding the cmdline to the chosen node */
Aparna Mallavarapu8fdf9312014-07-11 21:03:47 +0530856 ret = fdt_appendprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700857 if (ret)
858 {
859 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
860 return ret;
861 }
862
Joonwoo Parka5b5f492014-02-13 18:24:48 -0800863 if (ramdisk_size) {
864 /* Adding the initrd-start to the chosen node */
865 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start",
866 (uint32_t)ramdisk);
867 if (ret)
868 {
869 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
870 return ret;
871 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700872
Joonwoo Parka5b5f492014-02-13 18:24:48 -0800873 /* Adding the initrd-end to the chosen node */
874 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end",
875 ((uint32_t)ramdisk + ramdisk_size));
876 if (ret)
877 {
878 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
879 return ret;
880 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700881 }
882
883 fdt_pack(fdt);
884
885 return ret;
886}
Maria Yuca51ee22013-06-27 21:45:24 +0800887