blob: ee0582d2d1f719ce06c9bce90724f1bfd50697f6 [file] [log] [blame]
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -08001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -07002 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of The Linux Foundation nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <libfdt.h>
30#include <dev_tree.h>
31#include <lib/ptable.h>
32#include <malloc.h>
33#include <qpic_nand.h>
34#include <stdlib.h>
35#include <string.h>
36#include <platform.h>
37#include <board.h>
38
Joel Kingaa335dc2013-06-03 16:11:08 -070039struct dt_entry_v1
40{
41 uint32_t platform_id;
42 uint32_t variant_id;
43 uint32_t soc_rev;
44 uint32_t offset;
45 uint32_t size;
46};
47
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070048extern int target_is_emmc_boot(void);
49extern uint32_t target_dev_tree_mem(void *fdt, uint32_t memory_node_offset);
Deepa Dinamanic55f01b2013-05-30 14:05:56 -070050/* TODO: This function needs to be moved to target layer to check violations
51 * against all the other regions as well.
52 */
53extern int check_aboot_addr_range_overlap(uint32_t start, uint32_t size);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -070054
Channagoud Kadabi11682e92013-02-28 11:21:46 -080055/*
Dima Zavin77e41f32013-03-06 16:10:43 -080056 * Will relocate the DTB to the tags addr if the device tree is found and return
57 * its address
58 *
59 * Arguments: kernel - Start address of the kernel loaded in RAM
60 * tags - Start address of the tags loaded in RAM
Channagoud Kadabi704cd562013-04-25 15:19:59 -070061 * kernel_size - Size of the kernel in bytes
62 *
Channagoud Kadabi11682e92013-02-28 11:21:46 -080063 * Return Value: DTB address : If appended device tree is found
Dima Zavin77e41f32013-03-06 16:10:43 -080064 * 'NULL' : Otherwise
Channagoud Kadabi11682e92013-02-28 11:21:46 -080065 */
Channagoud Kadabi704cd562013-04-25 15:19:59 -070066void *dev_tree_appended(void *kernel, void *tags, uint32_t kernel_size)
Channagoud Kadabi11682e92013-02-28 11:21:46 -080067{
68 uint32_t app_dtb_offset = 0;
Deepa Dinamanic55f01b2013-05-30 14:05:56 -070069 uint32_t size;
Channagoud Kadabi11682e92013-02-28 11:21:46 -080070
71 memcpy((void*) &app_dtb_offset, (void*) (kernel + DTB_OFFSET), sizeof(uint32_t));
Channagoud Kadabi11682e92013-02-28 11:21:46 -080072
Channagoud Kadabi704cd562013-04-25 15:19:59 -070073 /*
74 * Check if we have valid offset for the DTB, if not return error.
75 * If the kernel image does not have appeneded device tree, DTB offset
76 * might contain some random address which is not accessible & cause
77 * data abort. If kernel start + dtb offset address exceed the total
78 * size of the kernel, then we dont have an appeneded DTB.
79 */
80 if (app_dtb_offset < kernel_size)
81 {
82 if (!fdt_check_header((void*) (kernel + app_dtb_offset)))
83 {
84 void *dtb;
85 int rc;
Dima Zavin77e41f32013-03-06 16:10:43 -080086
Channagoud Kadabi704cd562013-04-25 15:19:59 -070087 dprintf(INFO, "Found Appeneded Flattened Device tree\n");
88 dtb = kernel + app_dtb_offset;
Deepa Dinamanic55f01b2013-05-30 14:05:56 -070089 size = fdt_totalsize(dtb);
90 if (check_aboot_addr_range_overlap(tags, size))
91 {
92 dprintf(CRITICAL, "Appended dtb aboot overlap check failed.\n");
93 return NULL;
94 }
95 rc = fdt_open_into(dtb, tags, size);
Channagoud Kadabi704cd562013-04-25 15:19:59 -070096 if (rc == 0)
97 {
98 /* clear out the old DTB magic so kernel doesn't find it */
99 *((uint32_t *)dtb) = 0;
100 return tags;
101 }
Dima Zavin77e41f32013-03-06 16:10:43 -0800102 }
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800103 }
Channagoud Kadabi704cd562013-04-25 15:19:59 -0700104 else
105 dprintf(CRITICAL, "DTB offset is incorrect, kernel image does not have appended DTB\n");
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800106
Dima Zavin77e41f32013-03-06 16:10:43 -0800107 return NULL;
Channagoud Kadabi11682e92013-02-28 11:21:46 -0800108}
109
Joel Kingaa335dc2013-06-03 16:11:08 -0700110/* Returns 0 if the device tree is valid. */
111int dev_tree_validate(struct dt_table *table, unsigned int page_size)
112{
113 int dt_entry_size;
114
115 /* Validate the device tree table header */
116 if(table->magic != DEV_TREE_MAGIC) {
117 dprintf(CRITICAL, "ERROR: Bad magic in device tree table \n");
118 return -1;
119 }
120
121 if (table->version == DEV_TREE_VERSION_V1) {
122 dt_entry_size = sizeof(struct dt_entry_v1);
123 } else if (table->version == DEV_TREE_VERSION_V2) {
124 dt_entry_size = sizeof(struct dt_entry);
125 } else {
126 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
127 table->version);
128 return -1;
129 }
130
131 /* Restriction that the device tree entry table should be less than a page*/
132 ASSERT(((table->num_entries * dt_entry_size)+ DEV_TREE_HEADER_SIZE) < page_size);
133
134 return 0;
135}
136
Maria Yu2e2d2c22013-07-03 19:20:33 +0800137static 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 -0700138{
Maria Yu2e2d2c22013-07-03 19:20:33 +0800139 /* 1. must match the platform_id, hardware_id, platform_version
140 * 2. soc rev number equal then return 0
141 * 3. dt soc rev number less than cdt return -1
142 * 4. otherwise return 1
143 */
144 uint32_t cur_dt_target_id ;
Maria Yuca51ee22013-06-27 21:45:24 +0800145
Maria Yu2e2d2c22013-07-03 19:20:33 +0800146 cur_dt_target_id = cur_dt_entry->variant_id | ((cur_dt_entry->board_hw_subtype & subtype_mask & 0xff) << 24);
147
148 if((cur_dt_entry->platform_id == board_platform_id()) &&
149 (cur_dt_target_id == target_variant_id)) {
150 if(cur_dt_entry->soc_rev == board_soc_version()) {
Maria Yuca51ee22013-06-27 21:45:24 +0800151 return 0;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800152 } else if(cur_dt_entry->soc_rev < board_soc_version()) {
153 return -1;
Maria Yuca51ee22013-06-27 21:45:24 +0800154 }
155 }
Maria Yuca51ee22013-06-27 21:45:24 +0800156
Maria Yu2e2d2c22013-07-03 19:20:33 +0800157 return 1;
Maria Yuca51ee22013-06-27 21:45:24 +0800158}
159
Maria Yu2e2d2c22013-07-03 19:20:33 +0800160static int __dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info,
161 uint32_t target_variant_id, uint32_t subtype_mask)
Maria Yuca51ee22013-06-27 21:45:24 +0800162{
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700163 uint32_t i;
Joel Kingaa335dc2013-06-03 16:11:08 -0700164 unsigned char *table_ptr;
165 struct dt_entry dt_entry_buf_1;
166 struct dt_entry dt_entry_buf_2;
167 struct dt_entry *cur_dt_entry;
168 struct dt_entry *best_match_dt_entry;
169 struct dt_entry_v1 *dt_entry_v1;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800170 uint32_t found = 0;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700171
Joel Kingaa335dc2013-06-03 16:11:08 -0700172 if (!dt_entry_info) {
173 dprintf(CRITICAL, "ERROR: Bad parameter passed to %s \n",
174 __func__);
175 return -1;
176 }
177
178 table_ptr = (unsigned char *)table + DEV_TREE_HEADER_SIZE;
179 cur_dt_entry = &dt_entry_buf_1;
180 best_match_dt_entry = NULL;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700181
Maria Yu2e2d2c22013-07-03 19:20:33 +0800182 for(i = 0; found == 0 && i < table->num_entries; i++)
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700183 {
Joel Kingaa335dc2013-06-03 16:11:08 -0700184 memset(cur_dt_entry, 0, sizeof(struct dt_entry));
185 switch(table->version) {
186 case DEV_TREE_VERSION_V1:
187 dt_entry_v1 = (struct dt_entry_v1 *)table_ptr;
188 cur_dt_entry->platform_id = dt_entry_v1->platform_id;
189 cur_dt_entry->variant_id = dt_entry_v1->variant_id;
190 cur_dt_entry->soc_rev = dt_entry_v1->soc_rev;
191 cur_dt_entry->board_hw_subtype = board_hardware_subtype();
192 cur_dt_entry->offset = dt_entry_v1->offset;
193 cur_dt_entry->size = dt_entry_v1->size;
194 table_ptr += sizeof(struct dt_entry_v1);
195 break;
196 case DEV_TREE_VERSION_V2:
197 memcpy(cur_dt_entry, (struct dt_entry *)table_ptr,
198 sizeof(struct dt_entry));
199 table_ptr += sizeof(struct dt_entry);
200 break;
201 default:
202 dprintf(CRITICAL, "ERROR: Unsupported version (%d) in DT table \n",
203 table->version);
204 return -1;
205 }
206
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800207 /* DTBs are stored in the ascending order of soc revision.
208 * For eg: Rev0..Rev1..Rev2 & so on.
209 * we pickup the DTB with highest soc rev number which is less
210 * than or equal to actual hardware
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700211 */
Maria Yu2e2d2c22013-07-03 19:20:33 +0800212 switch(platform_dt_match(cur_dt_entry, target_variant_id, subtype_mask)) {
213 case 0:
214 best_match_dt_entry = cur_dt_entry;
215 found = 1;
216 break;
217 case -1:
218 if (!best_match_dt_entry) {
219 /* copy structure */
David Ng618293a2013-06-25 12:29:03 -0700220 best_match_dt_entry = cur_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800221 cur_dt_entry = &dt_entry_buf_2;
222 } else {
223 /* Swap dt_entry buffers */
224 struct dt_entry *temp = cur_dt_entry;
225 cur_dt_entry = best_match_dt_entry;
226 best_match_dt_entry = temp;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700227 }
Maria Yu2e2d2c22013-07-03 19:20:33 +0800228 default:
229 break;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800230 }
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700231 }
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800232
Joel Kingaa335dc2013-06-03 16:11:08 -0700233 if (best_match_dt_entry) {
234 *dt_entry_info = *best_match_dt_entry;
Maria Yu2e2d2c22013-07-03 19:20:33 +0800235 found = 1;
236 }
237
238 if (found != 0) {
David Ng618293a2013-06-25 12:29:03 -0700239 dprintf(INFO, "Using DTB entry %u/%08x/%u/%u for device %u/%08x/%u/%u\n",
240 dt_entry_info->platform_id, dt_entry_info->soc_rev,
241 dt_entry_info->variant_id, dt_entry_info->board_hw_subtype,
242 board_platform_id(), board_soc_version(),
243 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700244 return 0;
Channagoud Kadabiafd62bf2013-01-08 20:32:52 -0800245 }
246
David Ng618293a2013-06-25 12:29:03 -0700247 dprintf(CRITICAL, "ERROR: Unable to find suitable device tree for device (%u/0x%08x/%u/%u)\n",
248 board_platform_id(), board_soc_version(),
249 board_hardware_id(), board_hardware_subtype());
Joel Kingaa335dc2013-06-03 16:11:08 -0700250 return -1;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700251}
252
Maria Yu2e2d2c22013-07-03 19:20:33 +0800253/* Function to obtain the index information for the correct device tree
254 * based on the platform data.
255 * If a matching device tree is found, the information is returned in the
256 * "dt_entry_info" out parameter and a function value of 0 is returned, otherwise
257 * a non-zero function value is returned.
258 */
259int dev_tree_get_entry_info(struct dt_table *table, struct dt_entry *dt_entry_info)
260{
261 uint32_t target_variant_id;
262
263 if(board_hardware_id() == HW_PLATFORM_QRD) {
264 target_variant_id = board_target_id();
265 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
266 return 0;
267 }
268 }
269 /*
270 * for compatible with version 1 and version 2 dtbtool
271 * will compare the subtype inside the variant id
272 */
273 target_variant_id = board_hardware_id() | ((board_hardware_subtype() & 0xff) << 24);
274 if (__dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0xff) == 0) {
275 return 0;
276 }
277
278 /*
279 * add compatible with old device selection method which don't compare subtype
280 */
281 target_variant_id = board_hardware_id();
282 return __dev_tree_get_entry_info(table, dt_entry_info, target_variant_id, 0);
283}
284
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700285/* Function to add the first RAM partition info to the device tree.
286 * Note: The function replaces the reg property in the "/memory" node
287 * with the addr and size provided.
288 */
289int dev_tree_add_first_mem_info(uint32_t *fdt, uint32_t offset, uint32_t addr, uint32_t size)
290{
291 int ret;
292
293 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
294
295 if (ret)
296 {
297 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
298 ret);
299 }
300
301
302 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
303
304 if (ret)
305 {
306 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
307 ret);
308 }
309
310 return ret;
311}
312
313/* Function to add the subsequent RAM partition info to the device tree. */
314int dev_tree_add_mem_info(void *fdt, uint32_t offset, uint32_t addr, uint32_t size)
315{
316 static int mem_info_cnt = 0;
317 int ret;
318
319 if (!mem_info_cnt)
320 {
321 /* Replace any other reg prop in the memory node. */
322 ret = fdt_setprop_u32(fdt, offset, "reg", addr);
323 mem_info_cnt = 1;
324 }
325 else
326 {
327 /* Append the mem info to the reg prop for subsequent nodes. */
328 ret = fdt_appendprop_u32(fdt, offset, "reg", addr);
329 }
330
331 if (ret)
332 {
333 dprintf(CRITICAL, "Failed to add the memory information addr: %d\n",
334 ret);
335 }
336
337
338 ret = fdt_appendprop_u32(fdt, offset, "reg", size);
339
340 if (ret)
341 {
342 dprintf(CRITICAL, "Failed to add the memory information size: %d\n",
343 ret);
344 }
345
346 return ret;
347}
348
349/* Top level function that updates the device tree. */
350int update_device_tree(void *fdt, const char *cmdline,
351 void *ramdisk, uint32_t ramdisk_size)
352{
353 int ret = 0;
354 uint32_t offset;
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700355
356 /* Check the device tree header */
357 ret = fdt_check_header(fdt);
358 if (ret)
359 {
360 dprintf(CRITICAL, "Invalid device tree header \n");
361 return ret;
362 }
363
Deepa Dinamani1c970732013-04-19 14:23:01 -0700364 /* Add padding to make space for new nodes and properties. */
365 ret = fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + DTB_PAD_SIZE);
366 if (ret!= 0)
367 {
368 dprintf(CRITICAL, "Failed to move/resize dtb buffer: %d\n", ret);
369 return ret;
370 }
371
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700372 /* Get offset of the memory node */
373 ret = fdt_path_offset(fdt, "/memory");
374 if (ret < 0)
375 {
376 dprintf(CRITICAL, "Could not find memory node.\n");
377 return ret;
378 }
379
380 offset = ret;
381
382 ret = target_dev_tree_mem(fdt, offset);
383 if(ret)
384 {
385 dprintf(CRITICAL, "ERROR: Cannot update memory node\n");
386 return ret;
387 }
388
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700389 /* Get offset of the chosen node */
390 ret = fdt_path_offset(fdt, "/chosen");
391 if (ret < 0)
392 {
393 dprintf(CRITICAL, "Could not find chosen node.\n");
394 return ret;
395 }
396
397 offset = ret;
398 /* Adding the cmdline to the chosen node */
Amol Jadi10c7d1c2013-01-25 13:24:29 -0800399 ret = fdt_setprop_string(fdt, offset, (const char*)"bootargs", (const void*)cmdline);
Deepa Dinamani28c0ffe2012-09-24 11:45:21 -0700400 if (ret)
401 {
402 dprintf(CRITICAL, "ERROR: Cannot update chosen node [bootargs]\n");
403 return ret;
404 }
405
406 /* Adding the initrd-start to the chosen node */
407 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start", (uint32_t)ramdisk);
408 if (ret)
409 {
410 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
411 return ret;
412 }
413
414 /* Adding the initrd-end to the chosen node */
415 ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end", ((uint32_t)ramdisk + ramdisk_size));
416 if (ret)
417 {
418 dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
419 return ret;
420 }
421
422 fdt_pack(fdt);
423
424 return ret;
425}
Maria Yuca51ee22013-06-27 21:45:24 +0800426